Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-26 09:15:36

0001 /*
0002  * ABBoxManager.h
0003  *
0004  *  Created on: 24.04.2015
0005  *      Author: swenzel
0006  */
0007 
0008 #ifndef ABBOX_MANAGER_H
0009 #define ABBOX_MANAGER_H
0010 
0011 #pragma once
0012 
0013 #include "VecGeom/base/Global.h"
0014 
0015 #include "VecGeom/volumes/PlacedVolume.h"
0016 #include "VecGeom/volumes/UnplacedBox.h"
0017 #include "VecGeom/base/Vector3D.h"
0018 #include "VecGeom/management/GeoManager.h"
0019 #include "VecGeom/navigation/NavigationState.h"
0020 #include "VecGeom/base/Transformation3D.h"
0021 #include "VecGeom/volumes/kernel/BoxImplementation.h"
0022 
0023 #ifdef VECGEOM_USE_SURF
0024 #include "VecGeom/surfaces/SurfData.h" // still need this for the init bvh function, then we can cut this
0025 #include "VecGeom/surfaces/base/CpuTypes.h"
0026 #endif
0027 
0028 #include <map>
0029 #include <vector>
0030 
0031 namespace vecgeom {
0032 
0033 // Singleton class for ABBox manager
0034 // keeps a (centralized) map of volume pointers to vectors of aligned bounding boxes
0035 // the alternative would be to include such a thing into logical volumes
0036 template <typename Real_b>
0037 class ABBoxManager {
0038 public:
0039   typedef float Real_s;
0040   using Float_v = vecgeom::VectorBackend::Float_v;
0041 
0042   typedef Vector3D<Float_v> ABBox_v;
0043   // scalar
0044   typedef Vector3D<Real_b> ABBox_s;
0045 
0046   // use old style arrays here as std::vector has some problems
0047   // with Vector3D<kVc::Double_t>
0048   typedef ABBox_s *ABBoxContainer_t;
0049   typedef ABBox_v *ABBoxContainer_v;
0050 
0051   typedef std::pair<unsigned int, double> BoxIdDistancePair_t;
0052 
0053   // build an abstraction of sort to sort vectors and lists portably
0054   template <typename C, typename Compare>
0055   static void sort(C &v, Compare cmp)
0056   {
0057     std::sort(v.begin(), v.end(), cmp);
0058   }
0059 
0060   struct HitBoxComparatorFunctor {
0061     bool operator()(BoxIdDistancePair_t const &left, BoxIdDistancePair_t const &right)
0062     {
0063       return left.second < right.second;
0064     }
0065   };
0066 
0067   using FP_t = HitBoxComparatorFunctor;
0068 
0069   std::vector<ABBoxContainer_t> fVolToSurfaceABBoxesMap;
0070 
0071 private:
0072   std::vector<ABBoxContainer_t> fVolToABBoxesMap;
0073   std::vector<ABBoxContainer_v> fVolToABBoxesMap_v;
0074 
0075 public:
0076   // computes the aligned bounding box for a certain placed volume
0077   static void ComputeABBox(VPlacedVolume const *pvol, ABBox_s *lowerc, ABBox_s *upperc)
0078   {
0079     // idea: take the 8 corners of the bounding box in the reference frame of pvol
0080     // transform those corners and keep track of minimum and maximum extent
0081     // TODO: could make this code shorter with a more complex Vector3D class
0082     Vector3D<Precision> lower, upper;
0083     pvol->GetUnplacedVolume()->Extent(lower, upper);
0084 
0085     auto transformation = pvol->GetTransformation();
0086     TransformBoundingBox<Transformation3D>(lower, upper, *transformation);
0087     *lowerc = Vector3D<Precision>(lower.x() - 1E-3, lower.y() - 1E-3, lower.z() - 1E-3);
0088     *upperc = Vector3D<Precision>(upper.x() + 1E-3, upper.y() + 1E-3, upper.z() + 1E-3);
0089   }
0090 
0091   /** Splitted Aligned bounding boxes
0092    *
0093    *  This function will calculate the "numOfSlices" num of aligned bounding
0094    *  boxes of "numOfSlices" divisions of Bounding box of Placed Volume
0095    *
0096    *  input : 1. *pvol : A pointer to the Placed Volume.
0097    *        2. numOfSlices : that user want
0098    *
0099    *  output : lowerc : A STL vector containing the lower extent of the newly
0100    *                  calculated "numOfSlices" num of Aligned Bounding boxes
0101    *
0102    *           upperc : A STL vector containing the upper extent of the newly
0103    *                  calculated "numOfSlices" num of Aligned Bounding boxes
0104    *
0105    */
0106   static void ComputeSplittedABBox(VPlacedVolume const *pvol, std::vector<ABBox_s> &lowerc,
0107                                    std::vector<ABBox_s> &upperc, int numOfSlices)
0108   {
0109 
0110     // idea: Split the Placed Bounding Box of volume into the numOfSlices.
0111     //        Then pass each placed slice to the ComputABBox function,
0112     //        Get the coordinates of lower and upper corner of splittedABBox,
0113     //        store these coordinates into the vector of coordinates provided
0114     //        by the calling function.
0115 
0116     Vector3D<Precision> tmpLower, tmpUpper;
0117     pvol->GetUnplacedVolume()->Extent(tmpLower, tmpUpper);
0118     Vector3D<Precision> delta = tmpUpper - tmpLower;
0119     // chose the largest dimension for splitting
0120     int dim = 0;                                        // 0 for x, 1 for y,  2 for z //default considering X is largest
0121     if (delta.y() > delta.x() && delta.y() > delta.z()) // if y is largest
0122       dim = 1;
0123     if (delta.z() > delta.x() && delta.z() > delta.y()) // if z is largest
0124       dim = 2;
0125 
0126     Precision splitDx = 0., splitDy = 0., splitDz = 0.;
0127     splitDx = delta.x();
0128     splitDy = delta.y();
0129     splitDz = delta.z();
0130 
0131     // Only one will execute, considering slicing only in one dimension
0132     Precision val = 0.;
0133 
0134     if (dim == 0) {
0135       splitDx = delta.x() / numOfSlices;
0136       val     = -delta.x() / 2 + splitDx / 2;
0137     }
0138     if (dim == 1) {
0139       splitDy = delta.y() / numOfSlices;
0140       val     = -delta.y() / 2 + splitDy / 2;
0141     }
0142     if (dim == 2) {
0143       splitDz = delta.z() / numOfSlices;
0144       val     = -delta.z() / 2 + splitDz / 2;
0145     }
0146 
0147     // Precision minx, miny, minz, maxx, maxy, maxz;
0148     Transformation3D const *transf = pvol->GetTransformation();
0149 
0150     // Actual Stuff of slicing
0151     for (int i = 0; i < numOfSlices; i++) {
0152       // TODO :  Try to create sliced placed box.
0153       // Needs to modifiy translation parameters, without touching rotation
0154       // parameters
0155 
0156       Transformation3D transf2;
0157       Vector3D<Precision> transVec(0., 0., 0.);
0158       if (dim == 0) {
0159         transVec = transf->InverseTransform(Vector3D<Precision>(val, 0., 0.));
0160         val += splitDx;
0161       }
0162       if (dim == 1) {
0163         transVec = transf->InverseTransform(Vector3D<Precision>(0., val, 0.));
0164         val += splitDy;
0165       }
0166       if (dim == 2) {
0167         transVec = transf->InverseTransform(Vector3D<Precision>(0., 0., val));
0168         val += splitDz;
0169       }
0170 
0171       transf2.SetTranslation(transVec);
0172       transf2.SetRotation(transf->Rotation()[0], transf->Rotation()[1], transf->Rotation()[2], transf->Rotation()[3],
0173                           transf->Rotation()[4], transf->Rotation()[5], transf->Rotation()[6], transf->Rotation()[7],
0174                           transf->Rotation()[8]);
0175       transf2.SetProperties();
0176 
0177       Vector3D<Precision> lower1(0., 0., 0.), upper1(0., 0., 0.);
0178       UnplacedBox newBox2(splitDx / 2., splitDy / 2., splitDz / 2.);
0179       VPlacedVolume const *newBoxPlaced2 = LogicalVolume("", &newBox2).Place(&transf2);
0180       ABBoxManager<Precision>::Instance().ComputeABBox(newBoxPlaced2, &lower1, &upper1);
0181       lowerc.push_back(lower1);
0182       upperc.push_back(upper1);
0183     }
0184   }
0185 
0186   template <typename Transformation>
0187   static void TransformBoundingBox(Vector3D<Precision> &lower, Vector3D<Precision> &upper, Transformation const &transf)
0188   {
0189     auto delta = upper - lower;
0190     Precision minx, miny, minz, maxx, maxy, maxz;
0191     minx = kInfLength;
0192     miny = kInfLength;
0193     minz = kInfLength;
0194     maxx = -kInfLength;
0195     maxy = -kInfLength;
0196     maxz = -kInfLength;
0197     for (int x = 0; x <= 1; ++x)
0198       for (int y = 0; y <= 1; ++y)
0199         for (int z = 0; z <= 1; ++z) {
0200           Vector3D<Precision> corner;
0201           corner.x()                            = lower.x() + x * delta.x();
0202           corner.y()                            = lower.y() + y * delta.y();
0203           corner.z()                            = lower.z() + z * delta.z();
0204           Vector3D<Precision> transformedcorner = transf.InverseTransform(corner);
0205           minx                                  = std::min(minx, transformedcorner.x());
0206           miny                                  = std::min(miny, transformedcorner.y());
0207           minz                                  = std::min(minz, transformedcorner.z());
0208           maxx                                  = std::max(maxx, transformedcorner.x());
0209           maxy                                  = std::max(maxy, transformedcorner.y());
0210           maxz                                  = std::max(maxz, transformedcorner.z());
0211         }
0212     lower.Set(minx, miny, minz);
0213     upper.Set(maxx, maxy, maxz);
0214   }
0215 
0216   static ABBoxManager<Real_b> &Instance()
0217   {
0218     static ABBoxManager<Real_b> instance;
0219     return instance;
0220   }
0221 
0222   // initialize ABBoxes for a certain logical volume
0223   // very first version that just creates as many boxes as there are daughters
0224   // in reality we might have a lot more boxes than daughters (but not less)
0225   void InitABBoxes(LogicalVolume const *lvol)
0226   {
0227     if (fVolToABBoxesMap[lvol->id()] != nullptr) {
0228       // remove old boxes first
0229       RemoveABBoxes(lvol);
0230     }
0231     uint ndaughters              = lvol->GetDaughtersp()->size();
0232     ABBox_s *boxes               = new ABBox_s[2 * ndaughters];
0233     fVolToABBoxesMap[lvol->id()] = boxes;
0234 
0235     // same for the vector part
0236     int extra                      = (ndaughters % vecCore::VectorSize<Float_v>() > 0) ? 1 : 0;
0237     int size                       = 2 * (ndaughters / vecCore::VectorSize<Float_v>() + extra);
0238     ABBox_v *vectorboxes           = new ABBox_v[size];
0239     fVolToABBoxesMap_v[lvol->id()] = vectorboxes;
0240 
0241     // calculate boxes by iterating over daughters
0242     for (uint d = 0; d < ndaughters; ++d) {
0243       auto pvol = lvol->GetDaughtersp()->operator[](d);
0244       ComputeABBox(pvol, &boxes[2 * d], &boxes[2 * d + 1]);
0245     }
0246 
0247     // initialize vector version of Container
0248     int index                          = 0;
0249     unsigned int assignedscalarvectors = 0;
0250     for (uint i = 0; i < ndaughters; i += vecCore::VectorSize<Float_v>()) {
0251       Vector3D<Float_v> lower;
0252       Vector3D<Float_v> upper;
0253       // assign by components ( using generic VecCore API )
0254       for (uint k = 0; k < vecCore::VectorSize<Float_v>(); ++k) {
0255         if (2 * (i + k) < 2 * ndaughters) {
0256           vecCore::Set(lower.x(), k, boxes[2 * (i + k)].x());
0257           vecCore::Set(lower.y(), k, boxes[2 * (i + k)].y());
0258           vecCore::Set(lower.z(), k, boxes[2 * (i + k)].z());
0259           vecCore::Set(upper.x(), k, boxes[2 * (i + k) + 1].x());
0260           vecCore::Set(upper.y(), k, boxes[2 * (i + k) + 1].y());
0261           vecCore::Set(upper.z(), k, boxes[2 * (i + k) + 1].z());
0262           assignedscalarvectors += 2;
0263         } else {
0264           // filling in bounding boxes of zero size
0265           // better to put some irrational number than 0?
0266           vecCore::Scalar<Float_v> neginf = -InfinityLength<vecCore::Scalar<Float_v>>();
0267           vecCore::Set(lower.x(), k, neginf);
0268           vecCore::Set(lower.y(), k, neginf);
0269           vecCore::Set(lower.z(), k, neginf);
0270           vecCore::Set(upper.x(), k, neginf);
0271           vecCore::Set(upper.y(), k, neginf);
0272           vecCore::Set(upper.z(), k, neginf);
0273         }
0274       }
0275       vectorboxes[index++] = lower;
0276       vectorboxes[index++] = upper;
0277     }
0278     VECGEOM_ASSERT(index == size);
0279     VECGEOM_ASSERT(assignedscalarvectors == 2 * ndaughters);
0280     (void)assignedscalarvectors; // silence compiler warnings
0281   }
0282 
0283   // doing the same for many logical volumes
0284   template <typename Container>
0285   void InitABBoxes(Container const &lvolumes)
0286   {
0287     for (auto lvol : lvolumes) {
0288       InitABBoxes(lvol);
0289     }
0290   }
0291 
0292 #ifdef VECGEOM_USE_SURF
0293   static void ComputeSurfaceABBox(vgbrep::FramedSurface<Precision, Transformation3DMP<Precision>> const &framedSurface,
0294                                   Transformation3D const &volumeTransform, ABBox_s &lowerc, ABBox_s &upperc,
0295                                   vgbrep::CPUsurfData<Precision> const &cpudata, LogicalVolume const *lvol,
0296                                   const bool crop)
0297   {
0298     Vector3D<Precision> lowert, uppert;
0299 
0300     // bounding box of volume that the surface belongs to
0301     Vector3D<Precision> lower_vol, upper_vol;
0302     if (crop) lvol->GetUnplacedVolume()->Extent(lower_vol, upper_vol);
0303 
0304     // Get the frame bounding box
0305     framedSurface.Extent3D(lowert, uppert, cpudata);
0306     Vector3D<Precision> lower(lowert[0], lowert[1], lowert[2]);
0307     Vector3D<Precision> upper(uppert[0], uppert[1], uppert[2]);
0308 
0309     // Apply the local transformation
0310     TransformBoundingBox<Transformation3DMP<Precision>>(lower, upper, framedSurface.fTrans);
0311 
0312     // Apply the transformation with respect to the mother LV
0313     TransformBoundingBox<Transformation3D>(lower, upper, volumeTransform);
0314     if (crop) TransformBoundingBox<Transformation3D>(lower_vol, upper_vol, volumeTransform);
0315 
0316     if (!crop) {
0317       lowerc.Set(lower.x(), lower.y(), lower.z());
0318       upperc.Set(upper.x(), upper.y(), upper.z());
0319     } else {
0320       lowerc.Set(std::max(lower_vol.x() - 1e-3, lower.x()), std::max(lower_vol.y() - 1e-3, lower.y()),
0321                  std::max(lower_vol.z() - 1e-3, lower.z()));
0322       upperc.Set(std::min(upper_vol.x() + 1e-3, upper.x()), std::min(upper_vol.y() + 1e-3, upper.y()),
0323                  std::min(upper_vol.z() + 1e-3, upper.z()));
0324 
0325       // if surface bounding box is outside of volume bounding box, remove it entirely
0326       if (lower.x() > upper_vol.x() + vecgeom::kTolerance || upper.x() < lower_vol.x() - vecgeom::kTolerance ||
0327           lower.y() > upper_vol.y() + vecgeom::kTolerance || upper.y() < lower_vol.y() - vecgeom::kTolerance ||
0328           lower.z() > upper_vol.z() + vecgeom::kTolerance || upper.z() < lower_vol.z() - vecgeom::kTolerance) {
0329         lowerc.Set(0., 0., 0.);
0330         upperc.Set(0., 0., 0.);
0331       }
0332     }
0333   }
0334 
0335   // Initialize AABoxes for the surfaces of a LogicalVolume and those of its daughters
0336   void InitSurfaceABBoxesVol(LogicalVolume const *lvol, vgbrep::CPUsurfData<Precision> &cpudata, bool crop = false)
0337   {
0338     if (fVolToSurfaceABBoxesMap[lvol->id()] != nullptr && !crop) {
0339       // remove old boxes first
0340       RemoveSurfaceABBoxes(lvol);
0341     }
0342 
0343     // Get the shell of the root LV
0344     auto &rootShell = cpudata.fShells[lvol->id()];
0345     if (rootShell.fSurfaces.size() == 0) return;
0346 
0347     ABBox_s *boxes;
0348     if (!crop) {
0349       // Allocate space for the AABBs (2 corners per surface)
0350       boxes = new ABBox_s[2 * rootShell.fExitingSurfaces.size() + 2 * rootShell.fEnteringSurfaces.size()];
0351       fVolToSurfaceABBoxesMap[lvol->id()] = boxes;
0352     } else {
0353       boxes = fVolToSurfaceABBoxesMap[lvol->id()];
0354     }
0355 
0356     auto const identityTransform = new Transformation3D();
0357 
0358     // Create AABBs for the Exiting surfaces of this volume
0359     for (auto motherSurfIndex = 0u; motherSurfIndex < rootShell.fExitingSurfaces.size(); motherSurfIndex++) {
0360       // Get the surface
0361       auto exiting_ind        = rootShell.fExitingSurfaces[motherSurfIndex];
0362       auto const localSurface = cpudata.fLocalSurfaces[rootShell.fSurfaces[exiting_ind]];
0363 
0364       ComputeSurfaceABBox(localSurface, *identityTransform, boxes[2 * motherSurfIndex], boxes[2 * motherSurfIndex + 1],
0365                           cpudata, lvol, crop);
0366     }
0367 
0368     // Now, iterate again over the daughters, and fill the array of AABBs
0369     // We need to go over the daughters since we need to know their transformation
0370     // Also initialize the local visible surfaces list in cpudata
0371     int localSurfIndex = 0;
0372     for (auto pvol : lvol->GetDaughters()) {
0373       // Get the shell
0374       auto shell = cpudata.fShells[pvol->GetLogicalVolume()->id()];
0375       // Iterate over the local surfaces in this shell
0376       for (auto i = 0u; i < shell.fExitingSurfaces.size(); i++) {
0377         auto exiting_ind         = shell.fExitingSurfaces[i];
0378         auto const &localSurface = cpudata.fLocalSurfaces[shell.fSurfaces[exiting_ind]];
0379         // Transformation of this daughter volume with respect to its mother
0380         auto daughterTransform = pvol->GetTransformation();
0381         ComputeSurfaceABBox(localSurface, *daughterTransform,
0382                             boxes[2 * (localSurfIndex + rootShell.fExitingSurfaces.size())],
0383                             boxes[2 * (localSurfIndex + rootShell.fExitingSurfaces.size()) + 1], cpudata,
0384                             pvol->GetLogicalVolume(), crop);
0385         localSurfIndex++;
0386       }
0387     }
0388   }
0389 
0390   // Initialize AABoxes for the surfaces of a list of LogicalVolumes and those of their daughters
0391   template <typename Container>
0392   void InitSurfaceABBoxes(Container const &lvolumes, vgbrep::CPUsurfData<Precision> &cpudata, bool crop = false)
0393   {
0394     for (auto lvol : lvolumes) {
0395       InitSurfaceABBoxesVol(lvol, cpudata, crop);
0396     }
0397   }
0398 
0399   // Initialize ABBoxes for all registered LogicalVolumes
0400   void InitABBoxesForSurfaces(vgbrep::CPUsurfData<Precision> &cpudata, bool crop = false)
0401   {
0402     auto &container = GeoManager::Instance().GetLogicalVolumesMap();
0403     std::vector<LogicalVolume const *> logicalvolumes;
0404     if (!crop) {
0405       fVolToSurfaceABBoxesMap.resize(container.size(), nullptr);
0406       logicalvolumes.reserve(container.size());
0407       for (const auto &p : container) {
0408         logicalvolumes.push_back(p.second);
0409       }
0410     } else {
0411       GeoManager::Instance().GetAllLogicalVolumes(logicalvolumes);
0412     }
0413     InitSurfaceABBoxes(logicalvolumes, cpudata, crop);
0414   }
0415 
0416   void RemoveSurfaceABBoxes(LogicalVolume const *lvol)
0417   {
0418     if (fVolToSurfaceABBoxesMap[lvol->id()] != nullptr) delete[] fVolToSurfaceABBoxesMap[lvol->id()];
0419   }
0420 
0421   // Returns the list of AABBs associated to a LogicalVolume
0422   ABBoxContainer_t GetSurfaceABBoxes(int ivol, int &size, vgbrep::CPUsurfData<Precision> const &cpudata)
0423   {
0424     size = cpudata.fShells[ivol].fExitingSurfaces.size() + cpudata.fShells[ivol].fEnteringSurfaces.size();
0425     return fVolToSurfaceABBoxesMap[ivol];
0426   }
0427 #endif
0428 
0429   void InitABBoxesForCompleteGeometry()
0430   {
0431     auto &container = GeoManager::Instance().GetLogicalVolumesMap();
0432     fVolToABBoxesMap.resize(container.size(), nullptr);
0433     fVolToABBoxesMap_v.resize(container.size(), nullptr);
0434     std::vector<LogicalVolume const *> logicalvolumes(container.size());
0435     logicalvolumes.resize(0);
0436     for (auto p : container) {
0437       logicalvolumes.push_back(p.second);
0438     }
0439     InitABBoxes(logicalvolumes);
0440   }
0441 
0442   // remove the boxes from the list
0443   void RemoveABBoxes(LogicalVolume const *lvol)
0444   {
0445     if (fVolToABBoxesMap[lvol->id()] != nullptr) delete[] fVolToABBoxesMap[lvol->id()];
0446   }
0447 
0448   // returns the Container for a given logical volume or nullptr if
0449   // it does not exist
0450   ABBoxContainer_t GetABBoxes(LogicalVolume const *lvol, int &size)
0451   {
0452     size = lvol->GetDaughtersp()->size();
0453     return fVolToABBoxesMap[lvol->id()];
0454   }
0455 
0456   // returns the Container for a given logical volume or nullptr if
0457   // it does not exist
0458   ABBoxContainer_v GetABBoxes_v(LogicalVolume const *lvol, int &size)
0459   {
0460     int ndaughters = lvol->GetDaughtersp()->size();
0461     int extra      = (ndaughters % vecCore::VectorSize<Float_v>() > 0) ? 1 : 0;
0462     size           = ndaughters / vecCore::VectorSize<Float_v>() + extra;
0463     return fVolToABBoxesMap_v[lvol->id()];
0464   }
0465 };
0466 
0467 // Alias for ABBoxManager for forward compatibility with a templated version
0468 using ABBoxManager_t = ABBoxManager<vecgeom::Precision>;
0469 
0470 // output for hitboxes
0471 template <typename stream, typename Real_b>
0472 stream &operator<<(stream &s, std::vector<std::pair<unsigned int, double>> const &list)
0473 {
0474   for (auto i : list) {
0475     s << "(" << i.first << "," << i.second << ")" << " ";
0476   }
0477   return s;
0478 }
0479 } // namespace vecgeom
0480 
0481 #endif