Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 10:33:37

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 a tessellated section.
0006 /// \file volumes/TessellatedStruct.h
0007 /// \author Mihaela Gheata (CERN/ISS)
0008 
0009 #ifndef VECGEOM_VOLUMES_TESSELLATEDSECTION_H_
0010 #define VECGEOM_VOLUMES_TESSELLATEDSECTION_H_
0011 
0012 #include "VecGeom/volumes/TessellatedCluster.h"
0013 
0014 #include "VecGeom/management/HybridManager2.h"
0015 #include "VecGeom/navigation/HybridNavigator2.h"
0016 #include "VecGeom/management/ABBoxManager.h"
0017 
0018 namespace vecgeom {
0019 
0020 inline namespace VECGEOM_IMPL_NAMESPACE {
0021 
0022 /// Navigation helper for adjacent quadrilateral facets forming a closed
0023 /// convex section in between 2 Z planes. A base class defines the scalar navigation
0024 /// interfaces.
0025 template <typename T>
0026 class TessellatedSection {
0027   // Here we should be able to use vecgeom::Vector
0028   template <typename U>
0029   /// Generic vector type
0030   using vector_t = std::vector<U>;
0031   /// SIMD real type
0032   using Real_v = vecgeom::VectorBackend::Real_v;
0033   /// Quadrilateral facets for the section
0034   using Facet_t = QuadrilateralFacet<T>;
0035   /// A cluster of several quadrilateral facets
0036   using Cluster_t = TessellatedCluster<4, Real_v>;
0037 
0038 private:
0039   size_t fNfacets = 0;    ///< Number of triangle facets on the section
0040   T fZ            = 0;    ///< Z position of the section
0041   T fDz           = 0;    ///< Half-length in Z
0042   T fCubicVolume  = 0;    ///< Cubic volume
0043   T fSurfaceArea  = 0;    ///< Surface area
0044   Vector3D<T> fMinExtent; ///< Minimum extent
0045   Vector3D<T> fMaxExtent; ///< Maximum extent
0046   bool fSameZ = false;    ///< All facets are at same Z
0047   T fUpNorm   = 0;        ///< Up normal in case of sameZ (+1 or -1)
0048 
0049   vector_t<Vector3D<T>> fVertices; ///< Vector of unique vertices
0050   vector_t<Facet_t *> fFacets;     ///< Vector of quadrilateral convex facets
0051   vector_t<Cluster_t *> fClusters; ///< Vector of facet clusters
0052 
0053 protected:
0054   /// Method adding a facet to the structure.
0055 
0056   /** The vertices are added to the list of all vertices (including duplications)
0057   and the extent is re-adjusted.*/
0058   /// @param facet Facet to be added
0059   VECCORE_ATT_HOST_DEVICE
0060   void AddFacet(Facet_t *facet)
0061   {
0062     // Method adding a facet to the structure. The vertices are added to the
0063     // list of all vertices (including duplications) and the extent is re-adjusted.
0064     if (fSameZ) {
0065       Vector3D<T> const &normal = facet->GetNormal();
0066       assert(normal.Perp() < kTolerance);
0067       if (fUpNorm == 0.) fUpNorm = vecCore::math::CopySign(T(1.), normal.z());
0068       assert(fUpNorm * normal.z() > 0);
0069     }
0070     fFacets.push_back(facet);
0071     // Adjust extent
0072     using vecCore::math::Max;
0073     using vecCore::math::Min;
0074     T xmin        = Min(Min(facet->fVertices[0].x(), facet->fVertices[1].x()),
0075                  Min(facet->fVertices[2].x(), facet->fVertices[3].x()));
0076     T ymin        = Min(Min(facet->fVertices[0].y(), facet->fVertices[1].y()),
0077                  Min(facet->fVertices[2].y(), facet->fVertices[3].y()));
0078     fMinExtent[0] = Min(fMinExtent[0], xmin);
0079     fMinExtent[1] = Min(fMinExtent[1], ymin);
0080     fMinExtent[2] = fZ - fDz;
0081     T xmax        = Max(Max(facet->fVertices[0].x(), facet->fVertices[1].x()),
0082                  Max(facet->fVertices[2].x(), facet->fVertices[3].x()));
0083     T ymax        = Max(Min(facet->fVertices[0].y(), facet->fVertices[1].y()),
0084                  Max(facet->fVertices[2].y(), facet->fVertices[3].y()));
0085     fMaxExtent[0] = Max(fMaxExtent[0], xmax);
0086     fMaxExtent[1] = Max(fMaxExtent[1], ymax);
0087     fMaxExtent[2] = fZ + fDz;
0088     // Check if we can create a Tessellated cluster
0089     size_t nfacets = fFacets.size();
0090     assert(nfacets <= fNfacets && "Cannot add extra facets to section");
0091     if (nfacets % kVecSize == 0 || nfacets == fNfacets) {
0092       size_t istart      = nfacets - (nfacets - 1) % kVecSize - 1;
0093       size_t i           = 0;
0094       Cluster_t *cluster = new Cluster_t();
0095       for (; istart < nfacets; ++istart) {
0096         cluster->AddFacet(i++, fFacets[istart], istart);
0097       }
0098       // The last cluster may not be yet full: fill with last facet
0099       for (; i < kVecSize; ++i)
0100         cluster->AddFacet(i, facet, nfacets - 1);
0101       fClusters.push_back(cluster);
0102     }
0103     if (nfacets == fNfacets) {
0104       assert(CalculateConvexity() == true);
0105     }
0106   }
0107 
0108   /// Calculate convexity of the section with respect to itself
0109   VECCORE_ATT_HOST_DEVICE
0110   bool CalculateConvexity()
0111   {
0112     size_t nconvex = 0;
0113     for (size_t i = 0; i < fNfacets; ++i) {
0114       Facet_t *facet = fFacets[i];
0115       bool convex    = true;
0116       for (size_t j = 0; j < fNfacets; ++j) {
0117         if (j == i) continue;
0118         for (size_t ivert = 0; ivert < 4; ++ivert) {
0119           convex &= facet->DistPlane(fFacets[j]->fVertices[ivert]) < kTolerance;
0120         }
0121         if (!convex) continue;
0122       }
0123       facet->fConvex = convex;
0124       if (convex) nconvex++;
0125     }
0126     for (auto cluster : fClusters)
0127       cluster->CalculateConvexity();
0128     if (nconvex == fNfacets) return true;
0129     return false;
0130   }
0131 
0132 public:
0133   /// Constructor:
0134   /// @param nfacets Number of facets in the section
0135   /// @param zmin Minimum Z position
0136   /// @param zmax Maximum Z position
0137   VECCORE_ATT_HOST_DEVICE
0138   TessellatedSection(int nfacets, T zmin, T zmax) : fNfacets(nfacets), fZ(0.5 * (zmin + zmax)), fDz(0.5 * (zmax - zmin))
0139   {
0140     assert(zmax >= zmin && "zmin is greater than zmax");
0141     if (fDz < kTolerance) {
0142       fSameZ = true;
0143       fDz    = 0.;
0144     }
0145     fMinExtent.Set(InfinityLength<T>());
0146     fMaxExtent.Set(-InfinityLength<T>());
0147   }
0148 
0149   /// Method for adding a new quadrilateral facet
0150   /** @param vt0      First vertex
0151       @param vt1      Second vertex
0152       @param vt2      Third vertex
0153       @param vt3      Fourth vertex
0154       @param absolute If true then vt0, vt1, vt2 and vt3 are the vertices to be added in
0155         anti-clockwise order looking from the outsider. If false the vertices are relative
0156         to the first: vt0, vt0+vt1, vt0+vt2, vt0+vt3 in anti-clockwise order when looking from the
0157         outsider.
0158   */
0159   VECCORE_ATT_HOST_DEVICE
0160   bool AddQuadrilateralFacet(Vector3D<T> const &vt0, Vector3D<T> const &vt1, Vector3D<T> const &vt2,
0161                              Vector3D<T> const &vt3, bool absolute = true)
0162   {
0163     // Quadrilateral facet, normal pointing outside
0164     Facet_t *facet = new Facet_t;
0165     if (absolute) {
0166       if (!facet->SetVertices(vt0, vt1, vt2, vt3)) {
0167         delete facet;
0168         return false;
0169       }
0170       AddFacet(facet);
0171     } else {
0172       if (!facet->SetVertices(vt0, vt0 + vt1, vt0 + vt1 + vt2, vt0 + vt1 + vt2 + vt3)) {
0173         delete facet;
0174         return false;
0175       }
0176       AddFacet(facet);
0177     }
0178     return true;
0179   }
0180 
0181   /// Fast check using the extent if the point is outside
0182   /// @param point Point position
0183   /// @return True if point is outside
0184   VECCORE_ATT_HOST_DEVICE
0185   VECGEOM_FORCE_INLINE
0186   bool IsOutside(Vector3D<T> const &point)
0187   {
0188     return ((point - fMinExtent).Min() < -kTolerance || (point - fMaxExtent).Max() > kTolerance);
0189   }
0190 
0191   /// Check if a point is inside the section
0192   /// @param point Point position
0193   /// @return Enumeration inside/outside/surface
0194   VECCORE_ATT_HOST_DEVICE
0195   VECGEOM_FORCE_INLINE
0196   Inside_t Inside(Vector3D<Real_v> const &point) const
0197   {
0198     // All lanes of point contain the same scalar point
0199     using Bool_v = vecCore::Mask<Real_v>;
0200 
0201     // Assuming the fast check on extent was already done using the scalar point
0202     size_t nclusters = fClusters.size();
0203     // Convex polygone on top/bottom
0204     Real_v distPlanes;
0205     Bool_v inside(true), outside(false);
0206     for (size_t i = 0; i < nclusters; ++i) {
0207       distPlanes = fClusters[i]->DistPlanes(point);
0208       outside |= distPlanes > Real_v(kTolerance);
0209       //        if (!vecCore::MaskEmpty(outside)) return kOutside;
0210       inside &= distPlanes < -kTolerance;
0211     }
0212     if (!vecCore::MaskEmpty(outside)) return kOutside;
0213     if (vecCore::MaskFull(inside)) return kInside;
0214     return kSurface;
0215   }
0216 
0217   /// Getter for the number of facets of the section
0218   VECCORE_ATT_HOST_DEVICE
0219   VECGEOM_FORCE_INLINE
0220   size_t GetNfacets() const { return fFacets.size(); }
0221 
0222   /// Getter for the number of clusters of the section
0223   VECCORE_ATT_HOST_DEVICE
0224   VECGEOM_FORCE_INLINE
0225   size_t GetNclusters() const { return fClusters.size(); }
0226 
0227   /// Getter for the cluster at a given index
0228   VECCORE_ATT_HOST_DEVICE
0229   VECGEOM_FORCE_INLINE
0230   Cluster_t const &GetCluster(size_t i) const { return *fClusters[i]; }
0231 
0232   /// Getter for the facet at a given index
0233   VECCORE_ATT_HOST_DEVICE
0234   VECGEOM_FORCE_INLINE
0235   Facet_t const &GetFacet(size_t i) const { return *fFacets[i]; }
0236 
0237   /// Check if point is inside the section. Note that the Z range is not checked.
0238   /// @param point Point position
0239   // @return True if point is inside or on the surface
0240   VECCORE_ATT_HOST_DEVICE
0241   VECGEOM_FORCE_INLINE
0242   bool Contains(Vector3D<Real_v> const &point) const
0243   {
0244     using Bool_v = vecCore::Mask<Real_v>;
0245 
0246     // Check if point is in the bounding box
0247     // if ((point - fMinExtent).Min() < 0. || (point - fMaxExtent).Max() > 0.) return kOutside;
0248 
0249     size_t nclusters = fClusters.size();
0250     // Convex polygone on top/bottom
0251     Bool_v outside(false);
0252 
0253     for (size_t i = 0; i < nclusters; ++i) {
0254       Real_v distPlanes = fClusters[i]->DistPlanes(point);
0255       outside |= distPlanes > Real_v(0);
0256       if (!vecCore::MaskEmpty(outside)) return false;
0257     }
0258     return true;
0259   }
0260 
0261   /// Computes distance to in for the cluster, but may ignore Z checks
0262   /// @param [in]  point Point position
0263   /// @param [in]  direction Direction for the distance computation
0264   /// @param [in]  invdirz Inverse of direction on Z
0265   /// @param [in]  stepmax Search limit for the distance
0266   /// @return Computed distance
0267   template <bool skipZ = true>
0268   VECGEOM_FORCE_INLINE
0269   VECCORE_ATT_HOST_DEVICE
0270   T DistanceToIn(Vector3D<T> const &point, Vector3D<T> const &direction, T invdirz, T stepmax) const
0271   {
0272     // Compute distance to segment from outside point.
0273     if (fSameZ) {
0274       // All facets are on the plane at fZ
0275       // Distance to plane
0276       T pz = point.z() - fZ;
0277       // If wrong direction or opposite side, no hit
0278       if (fUpNorm * direction.z() > 0 || pz * fUpNorm < -kTolerance) return InfinityLength<T>();
0279       T distance = -pz * invdirz;
0280       // Still need to check that the propagated point is in the section
0281       Vector3D<T> propagated(point + distance * direction);
0282       const int nclusters = fClusters.size();
0283       for (int i = 0; i < nclusters; ++i) {
0284         if (fClusters[i]->Contains(propagated)) return distance;
0285       }
0286       return InfinityLength<T>();
0287     }
0288 
0289     T pz = point.z() - fZ;
0290     if (!skipZ) {
0291       if ((vecCore::math::Abs(pz) - fDz) > -kTolerance && pz * direction.z() >= 0) return InfinityLength<T>();
0292     }
0293     const T ddz   = vecCore::math::CopySign(fDz, invdirz);
0294     const T distz = -(pz + ddz) * invdirz;
0295     T distance    = distz;
0296     T limit       = vecCore::math::Min(-(pz - ddz) * invdirz, stepmax);
0297 
0298     const int nclusters = fClusters.size();
0299     for (int i = 0; i < nclusters; ++i) {
0300       bool canhit =
0301           (fClusters[i]->DistanceToInConvex(point, direction, distance, limit)) && (distance < limit - kTolerance);
0302       if (!canhit) return InfinityLength<T>();
0303     }
0304     if (skipZ) {
0305       if (distance > distz) return distance;
0306       return InfinityLength<T>();
0307     }
0308     return distance;
0309   }
0310 
0311   /// Computes distance to out for the cluster, but may ignore Z checks
0312   /// @param [in]  point Point position
0313   /// @param [in]  direction Direction for the distance computation
0314   /// @return Computed distance
0315   template <bool skipZ = true>
0316   VECGEOM_FORCE_INLINE
0317   VECCORE_ATT_HOST_DEVICE
0318   T DistanceToOut(Vector3D<T> const &point, Vector3D<T> const &direction) const
0319   {
0320     // Compute distance to segment from point inside, returning also the crossed
0321     // facet.
0322     T pz         = point.z() - fZ;
0323     const T safz = vecCore::math::Abs(pz) - fDz;
0324     if (safz > kTolerance) return -kTolerance;
0325     const T vz = direction.z();
0326     T distance = (vecCore::math::CopySign(fDz, vz) - pz) / NonZero(vz);
0327     T dist;
0328     const int nclusters = fClusters.size();
0329     for (int i = 0; i < nclusters; ++i) {
0330       if (!fClusters[i]->DistanceToOutConvex(point, direction, dist)) return dist;
0331       if (dist < distance) distance = dist;
0332     }
0333     return distance;
0334   }
0335 
0336   /// Compute distance to segment from point inside, returning also the crossed facet.
0337   /// @param [in]  point Point position
0338   /// @param [in]  direction Direction for the distance computation
0339   /// @param [in]  invdirz Inverse of direction on Z
0340   /// @return Computed distance
0341   VECCORE_ATT_HOST_DEVICE
0342   VECGEOM_FORCE_INLINE
0343   T DistanceToOutRange(Vector3D<T> const &point, Vector3D<T> const &direction, T invdirz) const
0344   {
0345     // Compute distance to segment from point inside, returning also the crossed
0346     // facet.
0347     if (fSameZ) {
0348       // All facets are on the plane at z = fZ
0349       // Distance to plane
0350       T pz = point.z() - fZ;
0351       // If wrong direction or opposite side, no hit
0352       if (fUpNorm * direction.z() < 0 || pz * fUpNorm > kTolerance) return InfinityLength<T>();
0353       T distance = -pz * invdirz;
0354       // Still need to check that the propagated point is in the section
0355       Vector3D<T> propagated(point + distance * direction);
0356       const int nclusters = fClusters.size();
0357       for (int i = 0; i < nclusters; ++i) {
0358         if (fClusters[i]->Contains(propagated)) return distance;
0359       }
0360       return InfinityLength<T>();
0361     }
0362     T pz                = point.z() - fZ;
0363     T dmax              = (vecCore::math::CopySign(fDz, invdirz) - pz) * invdirz;
0364     T dmin              = (-vecCore::math::CopySign(fDz, invdirz) - pz) * invdirz;
0365     T dtoin             = dmin;                // will be reduced Max for all clusters
0366     T dtoout            = InfinityLength<T>(); // will be reduced Min for all clusters
0367     const int nclusters = fClusters.size();
0368     for (int i = 0; i < nclusters; ++i) {
0369       bool hit = fClusters[i]->DistanceToInOut(point, direction, dtoin, dtoout);
0370       if (!hit) return InfinityLength<T>();
0371     }
0372 
0373     if (dtoout > dtoin - kTolerance && dtoout < dmax) return dtoout;
0374     return InfinityLength<T>();
0375   }
0376 
0377   /// Computes distance to out for the cluster, returning also the crossed surface index
0378   /// @param [in]  point Point position
0379   /// @param [in]  direction Direction for the distance computation
0380   /// @param [out] isurf Crossed surface
0381   /// @return Computed distance
0382   VECCORE_ATT_HOST_DEVICE
0383   VECGEOM_FORCE_INLINE
0384   T DistanceToOut(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, int &isurf) const
0385   {
0386     // Compute distance to segment from point inside, returning also the crossed
0387     // facet.
0388     isurf               = -1;
0389     T distance          = InfinityLength<T>();
0390     T stepmax           = InfinityLength<T>();
0391     const int nclusters = fClusters.size();
0392     for (int i = 0; i < nclusters; ++i) {
0393       int isurfcrt = -1;
0394       T distcrt;
0395       fClusters[i]->DistanceToOut(point, direction, stepmax, distcrt, isurfcrt);
0396       if (distcrt < distance) {
0397         distance = distcrt;
0398         isurf    = isurfcrt;
0399       }
0400     }
0401     return distance;
0402   }
0403 
0404   /// Compute safety from outside point
0405   /// @param [in] point Point position
0406   /// @return Safety value
0407   VECCORE_ATT_HOST_DEVICE
0408   VECGEOM_FORCE_INLINE
0409   T SafetyToIn(Vector3D<T> const &point) const
0410   {
0411     // Compute approximate safety for the convex case
0412     T safety            = vecCore::math::Max(fZ - fDz - point.z(), point.z() - fZ - fDz);
0413     const int nclusters = fClusters.size();
0414     for (int i = 0; i < nclusters; ++i) {
0415       const Real_v safcl = fClusters[i]->DistPlanes(point);
0416       const T saf        = vecCore::ReduceMax(safcl);
0417       if (saf > safety) safety = saf;
0418     }
0419     return safety;
0420   }
0421 
0422   /// Compute safety squared from outside point and closest surface
0423   /// @param [in] point Point position
0424   /// @param [out] isurf Closest surface
0425   /// @return Safety squared value
0426   VECCORE_ATT_HOST_DEVICE
0427   VECGEOM_FORCE_INLINE
0428   T SafetyToInSq(Vector3D<Real_v> const &point, int &isurf) const
0429   {
0430     // Compute safety squared to segment from point outside, returning also the crossed
0431     // facet.
0432     isurf               = -1;
0433     T safetysq          = InfinityLength<T>();
0434     const int nclusters = fClusters.size();
0435     for (int i = 0; i < nclusters; ++i) {
0436       int isurfcrt     = -1;
0437       const T safsqcrt = fClusters[i]->template SafetySq<true>(point, isurfcrt);
0438       if (safsqcrt < safetysq) {
0439         safetysq = safsqcrt;
0440         isurf    = isurfcrt;
0441       }
0442     }
0443     return safetysq;
0444   }
0445 
0446   /// Compute safety from point inside
0447   /// @param [in] point Point position
0448   /// @return Safety value
0449   VECCORE_ATT_HOST_DEVICE
0450   VECGEOM_FORCE_INLINE
0451   T SafetyToOut(Vector3D<T> const &point) const
0452   {
0453     // Compute approximate safety for the convex case
0454     T safety            = vecCore::math::Max(fZ - fDz - point.z(), point.z() - fZ - fDz);
0455     const int nclusters = fClusters.size();
0456     for (int i = 0; i < nclusters; ++i) {
0457       const Real_v safcl = fClusters[i]->DistPlanes(point);
0458       const T saf        = vecCore::ReduceMax(safcl);
0459       if (saf > safety) safety = saf;
0460     }
0461     return -safety;
0462   }
0463 
0464   /// Compute safety squared from point inside and closest surface
0465   /// @param [in] point Point position
0466   /// @param [out] isurf Closest surface
0467   /// @return Safety squared value
0468   VECCORE_ATT_HOST_DEVICE
0469   VECGEOM_FORCE_INLINE
0470   T SafetyToOutSq(Vector3D<Real_v> const &point, int &isurf) const
0471   {
0472     // Compute safety squared to segment from point inside, returning also the crossed
0473     // facet.
0474     isurf               = -1;
0475     T safetysq          = InfinityLength<T>();
0476     const int nclusters = fClusters.size();
0477     for (int i = 0; i < nclusters; ++i) {
0478       int isurfcrt     = -1;
0479       const T safsqcrt = fClusters[i]->template SafetySq<false>(point, isurfcrt);
0480       if (safsqcrt < safetysq) {
0481         safetysq = safsqcrt;
0482         isurf    = isurfcrt;
0483       }
0484     }
0485     return safetysq;
0486   }
0487 
0488   /// Compute normal to segment surface in given point near surface.
0489   VECCORE_ATT_HOST_DEVICE
0490   VECGEOM_FORCE_INLINE
0491   void Normal(Vector3D<T> const & /*point*/, Vector3D<T> & /*normal*/, bool & /*valid*/) const {}
0492 };
0493 
0494 std::ostream &operator<<(std::ostream &os, TessellatedSection<double> const &ts);
0495 
0496 } // namespace VECGEOM_IMPL_NAMESPACE
0497 } // end namespace vecgeom
0498 
0499 #endif