Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-18 09:32:38

0001 //===-- base/PlaneShell.h ----------------------------*- C++ -*-===//
0002 /// \file PlaneShell.h
0003 /// \author Guilherme Lima (lima at fnal dot gov)
0004 
0005 #ifndef VECGEOM_BASE_SIDEPLANES_H_
0006 #define VECGEOM_BASE_SIDEPLANES_H_
0007 
0008 #include "VecGeom/base/Global.h"
0009 #include "VecGeom/volumes/kernel/GenericKernels.h"
0010 
0011 // namespace vecgeom::cuda { template <typename Real_v, int N> class PlaneShell; }
0012 #include <VecCore/VecCore>
0013 
0014 namespace vecgeom {
0015 inline namespace VECGEOM_IMPL_NAMESPACE {
0016 
0017 /**
0018  * @brief Uses SoA layout to store arrays of N (plane) parameters,
0019  *        representing a set of planes defining a volume or shape.
0020  *
0021  * For some volumes, e.g. trapezoid, when two of the planes are
0022  * parallel, they should be set perpendicular to the Z-axis, and then
0023  * the inside/outside calculations become trivial.  Therefore those planes
0024  * should NOT be included in this class.
0025  *
0026  * @details If vector acceleration is enabled, the scalar template
0027  *        instantiation will use vector instructions for operations
0028  *        when possible.
0029  */
0030 
0031 template <int N, typename Type>
0032 struct PlaneShell {
0033 
0034   // Using a SOA-like data structure for vectorization
0035   Precision fA[N];
0036   Precision fB[N];
0037   Precision fC[N];
0038   Precision fD[N];
0039 
0040 public:
0041   /**
0042    * Initializes the SOA with existing data arrays, performing no allocation.
0043    */
0044   VECCORE_ATT_HOST_DEVICE
0045   PlaneShell(Precision *const a, Precision *const b, Precision *const c, Precision *const d)
0046   {
0047     memcpy(&(this->fA), a, N * sizeof(Type));
0048     memcpy(&(this->fB), b, N * sizeof(Type));
0049     memcpy(&(this->fC), c, N * sizeof(Type));
0050     memcpy(&(this->fD), d, N * sizeof(Type));
0051   }
0052 
0053   /**
0054    * Initializes the SOA with a fixed size, allocating an aligned array for each
0055    * coordinate of the specified size.
0056    */
0057   VECCORE_ATT_HOST_DEVICE
0058   PlaneShell()
0059   {
0060     memset(&(this->fA), 0, N * sizeof(Type));
0061     memset(&(this->fB), 0, N * sizeof(Type));
0062     memset(&(this->fC), 0, N * sizeof(Type));
0063     memset(&(this->fD), 0, N * sizeof(Type));
0064   }
0065 
0066   /**
0067    * Copy constructor
0068    */
0069   VECCORE_ATT_HOST_DEVICE
0070   PlaneShell(PlaneShell const &other)
0071   {
0072     memcpy(&(this->fA), &(other.fA), N * sizeof(Type));
0073     memcpy(&(this->fB), &(other.fB), N * sizeof(Type));
0074     memcpy(&(this->fC), &(other.fC), N * sizeof(Type));
0075     memcpy(&(this->fD), &(other.fD), N * sizeof(Type));
0076   }
0077 
0078   /**
0079    * assignment operator
0080    */
0081   VECCORE_ATT_HOST_DEVICE
0082   PlaneShell &operator=(PlaneShell const &other)
0083   {
0084     memcpy(this->fA, other.fA, N * sizeof(Type));
0085     memcpy(this->fB, other.fB, N * sizeof(Type));
0086     memcpy(this->fC, other.fC, N * sizeof(Type));
0087     memcpy(this->fD, other.fD, N * sizeof(Type));
0088     return *this;
0089   }
0090 
0091   VECCORE_ATT_HOST_DEVICE
0092   void Set(int i, Precision a, Precision b, Precision c, Precision d)
0093   {
0094     fA[i] = a;
0095     fB[i] = b;
0096     fC[i] = c;
0097     fD[i] = d;
0098   }
0099 
0100   VECCORE_ATT_HOST_DEVICE
0101   unsigned int size() { return N; }
0102 
0103   VECCORE_ATT_HOST_DEVICE
0104   ~PlaneShell() {}
0105 
0106   /// \return the distance from point to each plane.  The type returned is float, double, or various SIMD vector types.
0107   /// Distances are negative (positive) for points in same (opposite) side from plane as the normal vector.
0108   template <typename Type2>
0109   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void DistanceToPoint(Vector3D<Type2> const &point,
0110                                                                     Type2 *distances) const
0111   {
0112     for (int i = 0; i < N; ++i) {
0113       distances[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0114     }
0115   }
0116 
0117   /// \return the projection of a (Vector3D) direction into each plane's normal vector.
0118   /// The type returned is float, double, or various SIMD vector types.
0119   template <typename Type2>
0120   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void ProjectionToNormal(Vector3D<Type2> const &dir,
0121                                                                        Type2 *projection) const
0122   {
0123     for (int i = 0; i < N; ++i) {
0124       projection[i] = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0125     }
0126   }
0127 
0128   template <typename Real_v, bool ForInside>
0129   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void GenericKernelForContainsAndInside(
0130       Vector3D<Real_v> const &point, vecCore::Mask_v<Real_v> &completelyInside,
0131       vecCore::Mask_v<Real_v> &completelyOutside) const
0132   {
0133     // auto-vectorizable loop for Backend==scalar
0134     Real_v dist[N];
0135     for (unsigned int i = 0; i < N; ++i) {
0136       dist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0137     }
0138 
0139     // analysis loop - not auto-vectorizable
0140     for (unsigned int i = 0; i < N; ++i) {
0141       // is it outside of this side plane?
0142       completelyOutside = completelyOutside || (dist[i] > Real_v(MakePlusTolerant<ForInside>(0.)));
0143       if (ForInside) {
0144         completelyInside = completelyInside && (dist[i] < Real_v(MakeMinusTolerant<ForInside>(0.)));
0145       }
0146       // if (vecCore::EarlyReturnMaxLength(completelyOutside,1) && vecCore::MaskFull(completelyOutside)) return;
0147     }
0148   }
0149 
0150   /// \return the distance to the planar shell when the point is located outside.
0151   /// The type returned is the type corresponding to the backend given.
0152   /// For some special cases, the value returned is:
0153   ///     (1) +inf, if point+dir is outside & moving AWAY FROM OR PARALLEL TO any plane,
0154   ///     (2) -1, if point+dir crosses out a plane BEFORE crossing in ALL other planes (wrong-side)
0155   ///
0156   /// Note: smin,smax parameters are needed here, to flag shape-missing tracks.
0157   ///
0158   template <typename Real_v>
0159   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v DistanceToIn(Vector3D<Real_v> const &point,
0160                                                                    Vector3D<Real_v> const &dir, Real_v &smin,
0161                                                                    Real_v &smax) const
0162   {
0163     using Bool_v = vecCore::Mask_v<Real_v>;
0164     Bool_v done(false);
0165     Real_v distIn(kInfLength); // set for earlier returns
0166 
0167     // hope for a vectorization of this part for Backend==scalar!!
0168     Real_v pdist[N];
0169     Real_v proj[N];
0170     Real_v vdist[N];
0171     // vectorizable part
0172     for (int i = 0; i < N; ++i) {
0173       pdist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0174       proj[i]  = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0175 
0176       // note(SW): on my machine it was better to keep vdist[N] instead of a local variable vdist below
0177       vdist[i] = -pdist[i] / NonZero(proj[i]);
0178     }
0179 
0180     // wrong-side check: if (inside && smin<0) return -1
0181     for (int i = 0; i < N; ++i) {
0182       done = done || (pdist[i] > Real_v(MakePlusTolerant<true>(0.)) && proj[i] >= Real_v(0.));
0183       done = done || (pdist[i] > Real_v(MakeMinusTolerant<true>(0.)) && proj[i] > Real_v(0.));
0184     }
0185     if (vecCore::EarlyReturnMaxLength(done, 1) && vecCore::MaskFull(done)) return distIn;
0186 
0187     // analysis loop
0188     for (int i = 0; i < N; ++i) {
0189       // if outside and moving away, return infinity
0190       Bool_v posPoint = pdist[i] > Real_v(MakeMinusTolerant<true>(0.));
0191       Bool_v posDir   = proj[i] > 0;
0192 
0193       // check if trajectory will intercept plane within current range (smin,smax)
0194       Bool_v interceptFromInside = (!posPoint && posDir);
0195       done                       = done || (interceptFromInside && vdist[i] < smin);
0196 
0197       Bool_v interceptFromOutside = (posPoint && !posDir);
0198       done                        = done || (interceptFromOutside && vdist[i] > smax);
0199       if (vecCore::EarlyReturnMaxLength(done, 1) && vecCore::MaskFull(done)) return distIn;
0200 
0201       // update smin,smax
0202       vecCore__MaskedAssignFunc(smin, interceptFromOutside && vdist[i] > smin, vdist[i]);
0203       vecCore__MaskedAssignFunc(smax, interceptFromInside && vdist[i] < smax, vdist[i]);
0204     }
0205 
0206     // Survivors will return smin, which is the maximum distance in an interceptFromOutside situation
0207     // (SW: not sure this is true since smin is initialized from outside and can have any arbitrary value)
0208     vecCore::MaskedAssign(distIn, !done && smin <= smax, smin);
0209     return distIn;
0210   }
0211 
0212   /// \return the distance to the planar shell when the point is located within the shell itself.
0213   /// The type returned is the type corresponding to the backend given.
0214   /// For some special cases, the value returned is:
0215   ///     (1) -1, if point is outside (wrong-side)
0216   template <typename Real_v>
0217   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v DistanceToOut(Vector3D<Real_v> const &point,
0218                                                                     Vector3D<Real_v> const &dir) const
0219   {
0220     // using Bool_v = vecCore::Mask_v<Real_v>;
0221     // Bool_v done(false);
0222     Real_v distOut(kInfLength);
0223     // Real_v distOut1(kInfLength);
0224 
0225     // hope for a vectorization of this part for Backend==scalar !!
0226     // the idea is to put vectorizable things into this loop
0227     // and separate the analysis into a separate loop if need be
0228     Real_v pdist[N];
0229     Real_v proj[N];
0230     Real_v vdist[N];
0231     for (int i = 0; i < N; ++i) {
0232       pdist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0233       proj[i]  = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0234       vdist[i] = -pdist[i] / NonZero(proj[i]);
0235     }
0236 
0237     // early return if point is outside of plane
0238     // for (int i = 0; i < N; ++i) {
0239     //   done = done || (pdist[i] > kHalfTolerance);
0240     // }
0241     // vecCore__MaskedAssignFunc(distOut, done, Real_v(-1.0));
0242     // // if (vecCore::EarlyReturnMaxLength(done,1) && vecCore::MaskFull(done)) return distOut;
0243 
0244     // std::cerr<<"=== point="<< point <<", dir="<< dir <<"\n";
0245     for (int i = 0; i < N; ++i) {
0246       vecCore__MaskedAssignFunc(distOut, pdist[i] > kHalfTolerance, Real_v(-1.));
0247       vecCore__MaskedAssignFunc(distOut, proj[i] > kTolerance && vdist[i] < distOut, vdist[i]);
0248       // std::cerr<<"i="<< i <<", pdist="<< pdist[i] <<", proj="<< proj[i] <<", vdist="<< vdist[i] <<" "<< vdist1[i] <<"
0249       // --> dist="<< distOut <<", "<< distOut1 <<"\n";
0250     }
0251 
0252     return distOut;
0253   }
0254 
0255   /// \return the safety distance to the planar shell when the point is located within the shell itself.
0256   template <typename Real_v>
0257   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void SafetyToIn(Vector3D<Real_v> const &point, Real_v &safety) const
0258   {
0259     // vectorizable loop
0260     Real_v dist[N];
0261     for (int i = 0; i < N; ++i) {
0262       dist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0263     }
0264 
0265     // non-vectorizable part
0266     for (int i = 0; i < N; ++i) {
0267       vecCore__MaskedAssignFunc(safety, dist[i] > safety, dist[i]);
0268     }
0269   }
0270 
0271   /// \return the distance to the planar shell when the point is located within the shell itself.
0272   template <typename Real_v>
0273   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void SafetyToOut(Vector3D<Real_v> const &point, Real_v &safety) const
0274   {
0275     // vectorizable loop
0276     Real_v dist[N];
0277     for (int i = 0; i < N; ++i) {
0278       dist[i] = -(this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0279     }
0280 
0281     // non-vectorizable part
0282     for (int i = 0; i < N; ++i) {
0283       vecCore__MaskedAssignFunc(safety, dist[i] < safety, dist[i]);
0284     }
0285   }
0286 
0287   template <typename Real_v>
0288   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE size_t ClosestFace(Vector3D<Real_v> const &point, Real_v &safety) const
0289   {
0290     // vectorizable loop
0291     Real_v dist[N];
0292     for (int i = 0; i < N; ++i) {
0293       dist[i] = Abs(this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0294     }
0295 
0296     // non-vectorizable part
0297     using Bool_v    = vecCore::Mask_v<Real_v>;
0298     using Index_v   = vecCore::Index<Real_v>;
0299     Index_v closest = static_cast<Index_v>(-1);
0300     for (size_t i = 0; i < N; ++i) {
0301       Bool_v closer = dist[i] < safety;
0302       vecCore__MaskedAssignFunc(safety, closer, dist[i]);
0303       vecCore::MaskedAssign(closest, closer, i);
0304     }
0305 
0306     return closest;
0307   }
0308 
0309   /// @param point Point position in local coordinates
0310   /// @param[out] normal A vector normal to the plane closest to point.
0311   /// If the point has kSurface condition for more than one plane, the un-normalized sum is returned
0312   /// @param[out] edge Point on edge condition. The normal vector needs to be normalized by the user
0313   /// @return Distance to closest surface
0314   template <typename Real_v>
0315   VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v NormalKernel(Vector3D<Real_v> const &point,
0316                                                                    Vector3D<Real_v> &normal, bool &edge) const
0317   {
0318     Real_v safety = InfinityLength<Real_v>();
0319 
0320     // vectorizable loop
0321     Real_v dist[N];
0322     Vector3D<Real_v> cornerNormal;
0323     unsigned char surfaces = 0;
0324     edge                   = false;
0325     for (int i = 0; i < N; ++i) {
0326       dist[i] = Abs(this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0327       // If closest update normal
0328       if (dist[i] < safety) {
0329         normal.Set(this->fA[i], this->fB[i], this->fC[i]);
0330         safety = dist[i];
0331       }
0332       // If on surface add to separate vector
0333       if (dist[i] < kTolerance) {
0334         surfaces++;
0335         cornerNormal += Vector3D<Real_v>(this->fA[i], this->fB[i], this->fC[i]);
0336       }
0337     }
0338     if (surfaces > 1) {
0339       // The point is on the edge - do not normalize the vector
0340       normal = cornerNormal;
0341       edge   = true;
0342     }
0343 
0344     return safety;
0345   }
0346 };
0347 
0348 } // namespace VECGEOM_IMPL_NAMESPACE
0349 } // namespace vecgeom
0350 
0351 #endif // VECGEOM_BASE_SIDEPLANES_H_