Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:14:12

0001 // This file is part of VecGeom and is distributed under the
0002 // conditions in the file LICENSE.txt in the top directory.
0003 // For the full list of authors see CONTRIBUTORS.txt and `git log`.
0004 
0005 /// \brief Declaration of the data structure for the tessellated shape.
0006 /// \file volumes/TessellatedStruct.h
0007 /// \author First version created by Mihaela Gheata (CERN/ISS)
0008 
0009 #ifndef VECGEOM_VOLUMES_TESSELLATEDSTRUCT_H_
0010 #define VECGEOM_VOLUMES_TESSELLATEDSTRUCT_H_
0011 
0012 #include "TessellatedCluster.h"
0013 #include "VecGeom/base/BitSet.h"
0014 #include "VecGeom/base/RNG.h"
0015 #include "VecGeom/base/Config.h"
0016 #include <vector>
0017 
0018 #include "VecGeom/base/Stopwatch.h"
0019 #include "VecGeom/management/HybridManager2.h"
0020 #include "VecGeom/navigation/HybridNavigator2.h"
0021 
0022 #ifdef VECGEOM_EMBREE
0023 #include "VecGeom/management/EmbreeManager.h"
0024 #include "VecGeom/navigation/EmbreeNavigator.h"
0025 #endif
0026 
0027 #include "VecGeom/management/ABBoxManager.h"
0028 
0029 namespace vecgeom {
0030 
0031 // VECGEOM_DEVICE_DECLARE_CONV_TEMPLATE(class, TessellatedStruct, typename);
0032 
0033 inline namespace VECGEOM_IMPL_NAMESPACE {
0034 
0035 // Structure used for vectorizing queries on groups of triangles
0036 
0037 #ifdef VECGEOM_EMBREE
0038 #define USEEMBREE 1
0039 #endif
0040 
0041 /** Templated class holding the data structures for the tessellated solid.
0042 
0043   The class is templated on the number of edges for the composing facets and on the floating precision type
0044   used to represent the data and perform all calculations. It provides API for:
0045   - Adding triangular or quadrilateral facets
0046   - Initializing internally all data structures after adding all the facets that compose a closed tessellated
0047   surface. This creates a temporary helper data structure of the type GridHelper, used to clusterize facets in
0048   groups having as many elements as the vector size. The clusters are then used to construct a special navigation
0049   acceleration structure.
0050   - retrieving the hit clusters and the hit facets selected during navigation
0051 */
0052 template <size_t NVERT, typename T = double>
0053 class TessellatedStruct {
0054 
0055 #ifndef VECGEOM_ENABLE_CUDA
0056   using Real_v = vecgeom::VectorBackend::Real_v;
0057 #else
0058   using Real_v        = vecgeom::ScalarBackend::Real_v;
0059 #endif
0060   using Facet_t = Tile<NVERT, T>;
0061 
0062   // Here we should be able to use vecgeom::Vector
0063   template <typename U>
0064   using vector_t = vecgeom::Vector<U>;
0065 
0066   using BVHStructure = HybridManager2::HybridBoxAccelerationStructure;
0067 
0068 #ifdef USEEMBREE
0069   using BVHStructure2 = EmbreeManager::EmbreeAccelerationStructure;
0070 #else
0071   using BVHStructure2 = HybridManager2::HybridBoxAccelerationStructure; // EmbreeManager::EmbreeAccelerationStructure;
0072 #endif
0073 
0074   /** Structure representing a cell of a uniform grid embedding the tessellated solid bounding volume.
0075 
0076   The cell just stores an array of indices for the facets intersecting it. The cell coordinates are known by the
0077   GridHelper data structure owning it.
0078   */
0079   struct GridCell {
0080     vector_t<int> fArray; ///< Array of facet indices
0081     bool fUsed = false;   ///< Flag for cell usage
0082 
0083     /// Default constructor for a grid cell.
0084     VECCORE_ATT_HOST_DEVICE
0085     GridCell()
0086     { /* fArray.reserve(4); */
0087     }
0088   };
0089 
0090   /** Helper structure representing a grid of equal cells dividing the bounding box of a tessellated solid.
0091 
0092   The grid helper is defined by the number of cells in X/Y/Z. It provides methods to retrieve the cell
0093   containing a space point, or associated to a triplet of indices on (x, y, z). The helper is used by the
0094   TessellatedStruct for the facet clusterization.
0095   */
0096   //__________________________________________________________________________
0097   struct GridHelper {
0098     int fNgrid       = 0;           ///< Grid size
0099     int fNcells      = 0;           ///< Number of cells in the grid
0100     int fNcached     = 0;           ///< number of cached cells
0101     GridCell **fGrid = nullptr;     ///< Grid for clustering facets
0102     Vector3D<T> fMinExtent;         ///< Minimum extent
0103     Vector3D<T> fMaxExtent;         ///< Maximum extent
0104     Vector3D<T> fInvExtSize;        ///< Inverse extent size
0105     vector_t<Vector3D<T>> fAllVert; ///< Full list of vertices
0106 
0107     /// Default constructor for the grid helper structure.
0108     GridHelper() {}
0109 
0110     /// Destructor of the grid helper, deleting the cells and their content.
0111     ~GridHelper()
0112     {
0113       if (fGrid) {
0114         for (int i = 0; i < fNcells; ++i)
0115           delete fGrid[i];
0116         delete[] fGrid;
0117       }
0118     }
0119 
0120     /// Create all the cells corresponding to a given grid division number.
0121     /** @param ngrid Number of cells on each axis.*/
0122     void CreateCells(int ngrid)
0123     {
0124       if (fNgrid) return;
0125       fNgrid  = ngrid;
0126       fNcells = ngrid * ngrid * ngrid;
0127       fGrid   = new GridCell *[fNcells];
0128       for (int i = 0; i < fNcells; ++i)
0129         fGrid[i] = new GridCell();
0130     }
0131 
0132     /// Clears the content of all cells.
0133     VECCORE_ATT_HOST_DEVICE
0134     VECGEOM_FORCE_INLINE
0135     void ClearCells()
0136     {
0137       for (int icell = 0; icell < fNcells; ++icell)
0138         fGrid[icell]->fArray.clear();
0139     }
0140 
0141     /// Retrive a cell by its index triplet on (x,y,z).
0142     /** @param ind array of cell indices on (x,y,z)
0143         @return GridCell corresponding to the given indices
0144     */
0145     VECCORE_ATT_HOST_DEVICE
0146     VECGEOM_FORCE_INLINE
0147     GridCell *GetCell(int ind[3]) { return fGrid[fNgrid * fNgrid * ind[0] + fNgrid * ind[1] + ind[2]]; }
0148 
0149     /// Retreive a cell containing a space point and fill its indices triplet.
0150     /** @param[in]  point Space point
0151         @param[out] ind   Triplet of indices of the cell containing the point
0152         @return           Grid cell containing the space point
0153     */
0154     VECCORE_ATT_HOST_DEVICE
0155     VECGEOM_FORCE_INLINE
0156     GridCell *GetCell(Vector3D<T> const &point, int ind[3])
0157     {
0158       Vector3D<T> ratios = (point - fMinExtent) * fInvExtSize;
0159       for (int i = 0; i < 3; ++i) {
0160         ind[i] = ratios[i] * fNgrid;
0161         ind[i] = vecCore::math::Max(ind[i], 0);
0162         ind[i] = vecCore::math::Min(ind[i], fNgrid - 1);
0163       }
0164       return (GetCell(ind));
0165     }
0166   };
0167 
0168 public:
0169   bool fSolidClosed   = false;          ///< Closure of the solid
0170   T fCubicVolume      = 0;              ///< Cubic volume
0171   T fSurfaceArea      = 0;              ///< Surface area
0172   GridHelper *fHelper = nullptr;        ///< Grid helper
0173   Vector3D<T> fMinExtent;               ///< Minimum extent
0174   Vector3D<T> fMaxExtent;               ///< Maximum extent
0175   Vector3D<T> fInvExtSize;              ///< Inverse extent size
0176   Vector3D<T> fTestDir;                 ///< Test direction for Inside function
0177   BVHStructure *fNavHelper   = nullptr; ///< Navigation helper using bounding boxes
0178   BVHStructure2 *fNavHelper2 = nullptr; ///< Navigation helper using bounding boxes
0179 
0180   // Here we have a pointer to the aligned bbox structure
0181   // ABBoxanager *fABBoxManager;
0182 
0183   vector_t<int> fCluster;                                  ///< Cluster of facets storing just the indices
0184   vector_t<int> fCandidates;                               ///< Candidates for the current cluster
0185   vector_t<Vector3D<T>> fVertices;                         ///< Vector of unique vertices
0186   vector_t<Facet_t *> fFacets;                             ///< Vector of triangular facets
0187   vector_t<TessellatedCluster<NVERT, Real_v> *> fClusters; ///< Vector of facet clusters
0188   BitSet *fSelected          = nullptr;                    ///< Facets already in clusters
0189   int fNcldist[kVecSize + 1] = {0};                        ///< Distribution of number of cluster size
0190 
0191 private:
0192   /// Creates the navigation acceleration structure based ob the pre-computed clusters of facets.
0193   void CreateABBoxes()
0194   {
0195     using Boxes_t           = ABBoxManager::ABBoxContainer_t;
0196     using BoxCorner_t       = ABBoxManager::ABBox_s;
0197     int nclusters           = fClusters.size();
0198     BoxCorner_t *boxcorners = new BoxCorner_t[2 * nclusters];
0199     for (int i = 0; i < nclusters; ++i) {
0200       boxcorners[2 * i]     = fClusters[i]->fMinExtent;
0201       boxcorners[2 * i + 1] = fClusters[i]->fMaxExtent;
0202     }
0203     Boxes_t boxes = &boxcorners[0];
0204     fNavHelper    = HybridManager2::Instance().BuildStructure(boxes, nclusters);
0205 #ifdef USEEMBREE
0206     fNavHelper2 = EmbreeManager::Instance().BuildStructureFromBoundingBoxes(boxes, nclusters);
0207 #else
0208     fNavHelper2 = fNavHelper;
0209 #endif
0210   }
0211 
0212 public:
0213   /// Default constructor.
0214   VECCORE_ATT_HOST_DEVICE
0215   VECGEOM_FORCE_INLINE
0216   TessellatedStruct()
0217   {
0218     fMinExtent = InfinityLength<T>();
0219     fMaxExtent = -InfinityLength<T>();
0220     fHelper    = new GridHelper();
0221   }
0222 
0223   /// Destructor.
0224   VECCORE_ATT_HOST_DEVICE
0225   VECGEOM_FORCE_INLINE
0226   ~TessellatedStruct()
0227   {
0228     delete fHelper;
0229     if (fSelected) BitSet::ReleaseInstance(fSelected);
0230   }
0231 
0232   /// Adds a pre-defined facet and re-computes the extent.
0233   /** The vertices are added to the list of all vertices (including duplications) and the extent
0234     is re-adjusted.
0235     @param facet Pre-computed facet to be added
0236   */
0237   VECCORE_ATT_HOST_DEVICE
0238   VECGEOM_FORCE_INLINE
0239   void AddFacet(Facet_t *facet)
0240   {
0241     using vecCore::math::Max;
0242     using vecCore::math::Min;
0243 
0244     fFacets.push_back(facet);
0245     int ind = fHelper->fAllVert.size();
0246     // Add the vertices
0247     for (size_t i = 0; i < NVERT; ++i) {
0248       fHelper->fAllVert.push_back(facet->fVertices[i]);
0249       facet->fIndices[i] = ind + i;
0250       fMinExtent[0]      = Min(fMinExtent[0], facet->fVertices[i].x());
0251       fMinExtent[1]      = Min(fMinExtent[1], facet->fVertices[i].y());
0252       fMinExtent[2]      = Min(fMinExtent[2], facet->fVertices[i].z());
0253       fMaxExtent[0]      = Max(fMaxExtent[0], facet->fVertices[i].x());
0254       fMaxExtent[1]      = Max(fMaxExtent[1], facet->fVertices[i].y());
0255       fMaxExtent[2]      = Max(fMaxExtent[2], facet->fVertices[i].z());
0256     }
0257   }
0258 
0259   /// Add a non-duplicated vertex to the solid.
0260   /** Duplications are only checked in the grid cell containing the vertex. An index to the unique vertex is
0261     added to the cell, while the vertex position is added to the list fVertices.
0262     @param  vertex Space point to be added as vertex.
0263     @return Unique vertex id after removing duplicates.
0264   */
0265   VECCORE_ATT_HOST_DEVICE
0266   VECGEOM_FORCE_INLINE
0267   int AddVertex(Vector3D<T> const &vertex)
0268   {
0269     // Get the cell in which to add the vertex
0270     int ind[3];
0271     GridCell *cell = fHelper->GetCell(vertex, ind);
0272     // Loop existing vertices in the cell and check for a duplicate
0273     constexpr Precision tolerancesq = kTolerance * kTolerance;
0274     for (int ivert : cell->fArray) {
0275       // existing vertex?
0276       if ((fVertices[ivert] - vertex).Mag2() < tolerancesq) return ivert;
0277     }
0278     // Push new vertex into the tessellated structure
0279     int ivertnew = fVertices.size();
0280     fVertices.push_back(vertex);
0281     // Update the cell with the new vertex index
0282     cell->fArray.push_back(ivertnew);
0283     return ivertnew;
0284   }
0285 
0286   /// Retrieval of the extent of the tessellated structure.
0287   /** @param[out] amin Box corner having minimum coordinates
0288       @param[out] amax Box corner having maximum coordinates
0289   */
0290   VECCORE_ATT_HOST_DEVICE
0291   VECGEOM_FORCE_INLINE
0292   void Extent(Vector3D<T> &amin, Vector3D<T> &amax) const
0293   {
0294     amin = fMinExtent;
0295     amax = fMaxExtent;
0296   }
0297 
0298   /// Method performing all tasks for initializing and closing the data structures.
0299 
0300   /** The following sequence of operations is executed:
0301     - Creation of the grid of cells
0302     - Removal of duplicate facet indices and update of facets with  the unique vertex indices
0303     - Filling the cells with facet indices crossing them and creation of clusters
0304     - Creation of the navigation acceleration structures
0305   */
0306   void Close()
0307   {
0308     int ind[3];
0309     fInvExtSize = fMaxExtent - fMinExtent;
0310     if (fInvExtSize[0] * fInvExtSize[1] * fInvExtSize[2] < kTolerance) {
0311       std::cout << "Tessellated structure is flat - not allowed\n";
0312       return;
0313     }
0314     fInvExtSize          = 1. / fInvExtSize;
0315     fHelper->fMinExtent  = fMinExtent;
0316     fHelper->fMaxExtent  = fMaxExtent;
0317     fHelper->fInvExtSize = fInvExtSize;
0318     // Make a grid with ~kVecSize facets per cell
0319     int ngrid = 1 + size_t(vecCore::math::Pow<T>(T(fFacets.size()), 1. / 3.));
0320     // Stopwatch timer;
0321     // timer.Start();
0322     fHelper->CreateCells(ngrid);
0323     // auto time = timer.Stop();
0324     // std::cout << "CreateCells: " << time << " sec\n";
0325 
0326     // Loop over facets and their vertices, fill list of vertices free of
0327     // duplications.
0328     // timer.Start();
0329     for (auto facet : fFacets) {
0330       for (int ivert = 0; ivert < 3; ++ivert) {
0331         facet->fIndices[ivert] = AddVertex(facet->fVertices[ivert]);
0332       }
0333     }
0334     // time = timer.Stop();
0335     // std::cout << "Remove duplicates: " << time << " sec\n";
0336 
0337     // Clear vertices and store facet indices in the grid helper
0338     // timer.Start();
0339     fHelper->ClearCells();
0340     unsigned ifacet = 0;
0341     for (auto facet : fFacets) {
0342       fHelper->GetCell(facet->fCenter, ind)->fArray.push_back(ifacet);
0343       //      for (int ivert = 0; ivert < 3; ++ivert) {
0344       //        fHelper->GetCell(facet->fVertices[ivert], ind)->fArray.push_back(ifacet);
0345       //      }
0346       ifacet++;
0347     }
0348     // time = timer.Stop();
0349     // std::cout << "Store facets into grid: " << time << " sec\n";
0350 
0351     // Make clusters
0352     // timer.Start();
0353     //    std::cout << "=== Using dummy clusters\n";
0354     //    CreateDummyClusters();
0355 
0356     const int nfacets = fFacets.size();
0357     fSelected         = BitSet::MakeInstance(nfacets);
0358     fSelected->ResetAllBits();
0359     fCandidates.clear();
0360     ifacet = 0;
0361     fCandidates.push_back(ifacet);
0362     fSelected->SetBitNumber(ifacet);
0363     TessellatedCluster<NVERT, Real_v> *cluster;
0364     while (fCandidates.size()) {
0365       // Use existing candidates in fCandidates to create the cluster
0366       cluster = CreateCluster();
0367       if (!cluster) cluster = MakePartialCluster();
0368       fClusters.push_back(cluster);
0369       // Fill cluster from the same cell or from a neighbor cell
0370       if (!fCandidates.size()) {
0371         ifacet = fSelected->FirstNullBit();
0372         if (ifacet == fFacets.size()) break;
0373         fCandidates.push_back(ifacet);
0374         fSelected->SetBitNumber(ifacet);
0375       }
0376     }
0377 
0378     // time = timer.Stop();
0379     // std::cout << "Clusterizer: " << time << " sec\n";
0380 
0381     // Create navigation helper to be used in TessellatedImplementation
0382     // timer.Start();
0383     CreateABBoxes(); // to navigate, see: TestHybridBVH.cpp/HybridNavigator2.h/HybridSafetyEstimator.h
0384     // time = timer.Stop();
0385     // std::cout << "Create AABoxes: " << time << " sec\n";
0386     // Generate random direction non-parallel to any of the surfaces
0387     constexpr T tolerance(1.e-8);
0388     while (1) {
0389       RandomDirection(fTestDir);
0390       // Loop over triangles and check that dot product is not close to 0
0391       for (auto facet : fFacets) {
0392         if (vecCore::math::Abs(facet->fNormal.Dot(fTestDir)) < tolerance) break;
0393       }
0394       break;
0395     }
0396     fSolidClosed = true;
0397   }
0398 
0399   /// Generate and store a random direction used for Contains and Inside navigation queries.
0400   /** @param[out] direction Random direction generated */
0401   void RandomDirection(Vector3D<Precision> &direction)
0402   {
0403     Precision phi   = RNG::Instance().uniform(0., 2. * kPi);
0404     Precision theta = std::acos(1. - 2. * RNG::Instance().uniform(0, 1));
0405     direction.x()   = std::sin(theta) * std::cos(phi);
0406     direction.y()   = std::sin(theta) * std::sin(phi);
0407     direction.z()   = std::cos(theta);
0408   }
0409 
0410   /// Loop over facets and group them in clusters in the order of definition.
0411   void CreateDummyClusters()
0412   {
0413     TessellatedCluster<NVERT, Real_v> *tcl = nullptr;
0414     int i                                  = 0;
0415     int j                                  = 0;
0416     for (auto facet : fFacets) {
0417       i = i % kVecSize;
0418       if (i == 0) {
0419         if (tcl) fClusters.push_back(tcl);
0420         tcl = new TessellatedCluster<NVERT, Real_v>();
0421       }
0422       tcl->AddFacet(i++, facet, j++);
0423     }
0424     // The last cluster may not be yet full
0425     for (; i < kVecSize; ++i)
0426       tcl->AddFacet(i, tcl->fFacets[0], tcl->fIfacets[0]);
0427   }
0428 
0429   /// Create the next cluster of closest neighbour facets using the GridHelper structure.
0430   /** @return Created cluster of neighbour facets */
0431   TessellatedCluster<NVERT, Real_v> *CreateCluster()
0432   {
0433     // Create cluster starting from fCandidates list
0434     unsigned nfacets = 0;
0435     assert(fCandidates.size() > 0); // call the method with at least one candidate in the list
0436     constexpr int rankmax = 3;      // ??? how to determine an appropriate value ???
0437     int rank              = 0;
0438     fCluster.clear();
0439     int ifacet = fCandidates[0];
0440     fCluster.push_back(ifacet);
0441     while (fCandidates.size() < kVecSize && rank < rankmax) {
0442       GatherNeighborCandidates(fCandidates[0], rank++);
0443     }
0444     if (fCandidates.size() < kVecSize) return nullptr;
0445     fCandidates.erase(fCandidates.begin());
0446     // Add facets with maximum neighborhood weight to existing cluster
0447     int iref = 0;
0448     while (nfacets < kVecSize) {
0449       nfacets = AddCandidatesToCluster(4 >> iref++); // 4 common vertices, 2, 1 or none
0450     }
0451 
0452     // The cluster is now complete, create a tessellated cluster object
0453     TessellatedCluster<NVERT, Real_v> *tcl = new TessellatedCluster<NVERT, Real_v>();
0454     int i                                  = 0;
0455     for (auto ifct : fCluster) {
0456       Facet_t *facet = fFacets[ifct];
0457       tcl->AddFacet(i++, facet, ifct);
0458     }
0459     fNcldist[fCluster.size()]++;
0460     return tcl;
0461   }
0462 
0463   /// Create partial cluster starting from fCandidates list
0464   /** @return Created cluster*/
0465   TessellatedCluster<NVERT, Real_v> *MakePartialCluster()
0466   {
0467     for (auto ifacet : fCandidates) {
0468       fCluster.push_back(ifacet);
0469     }
0470     fNcldist[fCluster.size()]++;
0471     fCandidates.clear();
0472     while (fCluster.size() < kVecSize)
0473       fCluster.push_back(fCluster[0]);
0474 
0475     // The cluster is now complete, create a tessellated cluster object
0476     TessellatedCluster<NVERT, Real_v> *tcl = new TessellatedCluster<NVERT, Real_v>();
0477     int i                                  = 0;
0478     for (auto ifacet : fCluster) {
0479       Facet_t *facet = fFacets[ifacet];
0480       tcl->AddFacet(i++, facet, ifacet);
0481     }
0482     return tcl;
0483   }
0484 
0485   /// Add candidates to and existing cluster.
0486   /** A neighbourhood weight is computed for each facet candidate according the number of vertices
0487     contained in grid cells already occupied by the cluster.
0488     @param  weightmin Minimum accepted weight
0489     @return New size of the cluster
0490   */
0491   int AddCandidatesToCluster(int weightmin)
0492   {
0493     // Add all candidate having the required weight to the cluster, until cluster
0494     // is complete
0495     for (auto it = fCandidates.begin(); it != fCandidates.end() && fCluster.size() < kVecSize;) {
0496       int weight = NeighborToCluster(*it);
0497       if (weight >= weightmin) {
0498         fCluster.push_back(*it);
0499         it = fCandidates.erase(it);
0500       } else {
0501         ++it;
0502       }
0503     }
0504     return fCluster.size();
0505   }
0506 
0507   /// Compute the weight of neighbourhood of a facet with respect to a cluster.
0508   /** @param ifacet Facet index
0509       @return Number of facet vertices contained by grid cells occupied by the cluster
0510   */
0511   int NeighborToCluster(int ifacet)
0512   {
0513     Facet_t *facet = fFacets[ifacet];
0514     int weight     = 0;
0515     for (auto icand : fCluster) {
0516       Facet_t *other = fFacets[icand];
0517       weight += facet->IsNeighbor(*other);
0518     }
0519     return weight;
0520   }
0521 
0522   /// Add all facets touching a cell to the list of candidates to be added to the next cluster.
0523   /** @param ind Triplet of cell indices on (x,y,z) */
0524   VECCORE_ATT_HOST_DEVICE
0525   VECGEOM_FORCE_INLINE
0526   void AddCandidatesFromCell(int ind[3])
0527   {
0528     GridCell *cell = fHelper->GetCell(ind);
0529     if (cell->fUsed) return;
0530     for (auto neighbor : cell->fArray) {
0531       if (!fSelected->TestBitNumber(neighbor)) {
0532         fSelected->SetBitNumber(neighbor);
0533         fCandidates.push_back(neighbor);
0534       }
0535     }
0536     cell->fUsed = true;
0537   }
0538 
0539   /// Gather candidates from cells neighboring the cell containing the center of the facet
0540   /** @param  ifacet Facet index for which neighbours are searched
0541       @param  rank   Maximum distance between the reference cell containing the center of the facet
0542         and the other cells where neighbours are looked for
0543       @return Size of the list of candidates.
0544   */
0545   int GatherNeighborCandidates(int ifacet, int rank)
0546   {
0547     int ind0[3], ind[3];
0548     const Facet_t *facet = fFacets[ifacet];
0549     fHelper->GetCell(facet->fCenter, ind0);
0550     if (rank == 0) {
0551       AddCandidatesFromCell(ind0);
0552       return (fCandidates.size());
0553     }
0554 
0555     // Establish cell index limits for the requested rank
0556     int limits[6];
0557     bool limited[6] = {false};
0558     for (int i = 0; i < 3; ++i) {
0559       limits[2 * i] = ind0[i] - rank;
0560       if (limits[2 * i] < 0) {
0561         limits[2 * i]  = 0;
0562         limited[2 * i] = true;
0563       }
0564       limits[2 * i + 1] = ind0[i] + rank;
0565       if (limits[2 * i + 1] > fHelper->fNgrid - 1) {
0566         limits[2 * i + 1]  = fHelper->fNgrid - 1;
0567         limited[2 * i + 1] = true;
0568       }
0569     }
0570     // Gather all cells for the given rank
0571     for (int iax1 = 0; iax1 < 3; ++iax1) {
0572       int iax2 = (iax1 + 1) % 3;
0573       int iax3 = (iax1 + 2) % 3;
0574       for (int iside = 0; iside < 2; ++iside) {
0575         if (limited[2 * iax1 + iside]) continue;
0576         ind[iax1] = limits[2 * iax1 + iside];
0577         for (ind[iax2] = limits[2 * iax2]; ind[iax2] <= limits[2 * iax2 + 1]; ind[iax2]++) {
0578           for (ind[iax3] = limits[2 * iax3]; ind[iax3] <= limits[2 * iax3 + 1]; ind[iax3]++) {
0579             AddCandidatesFromCell(ind);
0580           }
0581         }
0582       }
0583     }
0584     return (fCandidates.size());
0585   }
0586 
0587   /// Method for adding a new triangular facet
0588   /** @param vt0      First vertex
0589       @param vt1      Second vertex
0590       @param vt2      Third vertex
0591       @param absolute If true then vt0, vt1 and vt2 are the vertices to be added in
0592         anti-clockwise order looking from the outsider. If false the vertices are relative
0593         to the first: vt0, vt0+vt1, vt0+vt2, in anti-clockwise order when looking from the outsider.
0594   */
0595   VECCORE_ATT_HOST_DEVICE
0596   bool AddTriangularFacet(Vector3D<T> const &vt0, Vector3D<T> const &vt1, Vector3D<T> const &vt2, bool absolute = true)
0597   {
0598     assert(NVERT == 3);
0599     Facet_t *facet = new Facet_t;
0600     bool added     = false;
0601     if (absolute)
0602       added = facet->SetVertices(vt0, vt1, vt2);
0603     else
0604       added = facet->SetVertices(vt0, vt0 + vt1, vt0 + vt1 + vt2);
0605     if (!added) {
0606       delete facet;
0607       return false;
0608     }
0609     AddFacet(facet);
0610     return true;
0611   }
0612 
0613   /// Method for adding a new quadrilateral facet
0614   /** @param vt0      First vertex
0615       @param vt1      Second vertex
0616       @param vt2      Third vertex
0617       @param vt3      Fourth vertex
0618       @param absolute If true then vt0, vt1, vt2 and vt3 are the vertices to be added in
0619         anti-clockwise order looking from the outsider. If false the vertices are relative
0620         to the first: vt0, vt0+vt1, vt0+vt2, vt0+vt3 in anti-clockwise order when looking from the
0621         outsider.
0622   */
0623   VECCORE_ATT_HOST_DEVICE
0624   bool AddQuadrilateralFacet(Vector3D<T> const &vt0, Vector3D<T> const &vt1, Vector3D<T> const &vt2,
0625                              Vector3D<T> const &vt3, bool absolute = true)
0626   {
0627     // We should check the quadrilateral convexity to correctly define the
0628     // triangle facets
0629     // CheckConvexity()vt0, vt1, vt2, vt3, absolute);
0630     assert(NVERT <= 4);
0631     Facet_t *facet = new Facet_t;
0632     if (NVERT == 3) {
0633       if (absolute) {
0634         if (!facet->SetVertices(vt0, vt1, vt2)) {
0635           delete facet;
0636           return false;
0637         }
0638       } else {
0639         if (!facet->SetVertices(vt0, vt0 + vt1, vt0 + vt1 + vt2)) {
0640           delete facet;
0641           return false;
0642         }
0643       }
0644       AddFacet(facet);
0645       facet = new Facet_t;
0646       if (absolute) {
0647         if (!facet->SetVertices(vt0, vt2, vt3)) {
0648           delete facet;
0649           return false;
0650         }
0651       } else {
0652         if (!facet->SetVertices(vt0, vt0 + vt2, vt0 + vt2 + vt3)) {
0653           delete facet;
0654           return false;
0655         }
0656       }
0657       AddFacet(facet);
0658       return true;
0659     } else if (NVERT == 4) {
0660       // Add a single facet
0661       if (absolute) {
0662         if (!facet->SetVertices(vt0, vt1, vt2, vt3)) {
0663           delete facet;
0664           return false;
0665         }
0666       } else {
0667         if (!facet->SetVertices(vt0, vt0 + vt1, vt0 + vt1 + vt2, vt0 + vt1 + vt2 + vt3)) {
0668           delete facet;
0669           return false;
0670         }
0671       }
0672       AddFacet(facet);
0673       return true;
0674     }
0675     return false;
0676   }
0677 
0678 }; // end class
0679 } // namespace VECGEOM_IMPL_NAMESPACE
0680 } // namespace vecgeom
0681 
0682 #endif