Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:13:53

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
0110   VECCORE_ATT_HOST_DEVICE
0111   void DistanceToPoint(Vector3D<Type2> const &point, Type2 *distances) const
0112   {
0113     for (int i = 0; i < N; ++i) {
0114       distances[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0115     }
0116   }
0117 
0118   /// \return the projection of a (Vector3D) direction into each plane's normal vector.
0119   /// The type returned is float, double, or various SIMD vector types.
0120   template <typename Type2>
0121   VECGEOM_FORCE_INLINE
0122   VECCORE_ATT_HOST_DEVICE
0123   void ProjectionToNormal(Vector3D<Type2> const &dir, Type2 *projection) const
0124   {
0125     for (int i = 0; i < N; ++i) {
0126       projection[i] = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0127     }
0128   }
0129 
0130   template <typename Real_v, bool ForInside>
0131   VECGEOM_FORCE_INLINE
0132   VECCORE_ATT_HOST_DEVICE
0133   void GenericKernelForContainsAndInside(Vector3D<Real_v> const &point, vecCore::Mask_v<Real_v> &completelyInside,
0134                                          vecCore::Mask_v<Real_v> &completelyOutside) const
0135   {
0136     // auto-vectorizable loop for Backend==scalar
0137     Real_v dist[N];
0138     for (unsigned int i = 0; i < N; ++i) {
0139       dist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0140     }
0141 
0142     // analysis loop - not auto-vectorizable
0143     for (unsigned int i = 0; i < N; ++i) {
0144       // is it outside of this side plane?
0145       completelyOutside = completelyOutside || (dist[i] > Real_v(MakePlusTolerant<ForInside>(0.)));
0146       if (ForInside) {
0147         completelyInside = completelyInside && (dist[i] < Real_v(MakeMinusTolerant<ForInside>(0.)));
0148       }
0149       // if (vecCore::EarlyReturnMaxLength(completelyOutside,1) && vecCore::MaskFull(completelyOutside)) return;
0150     }
0151   }
0152 
0153   /// \return the distance to the planar shell when the point is located outside.
0154   /// The type returned is the type corresponding to the backend given.
0155   /// For some special cases, the value returned is:
0156   ///     (1) +inf, if point+dir is outside & moving AWAY FROM OR PARALLEL TO any plane,
0157   ///     (2) -1, if point+dir crosses out a plane BEFORE crossing in ALL other planes (wrong-side)
0158   ///
0159   /// Note: smin,smax parameters are needed here, to flag shape-missing tracks.
0160   ///
0161   template <typename Real_v>
0162   VECGEOM_FORCE_INLINE
0163   VECCORE_ATT_HOST_DEVICE
0164   Real_v DistanceToIn(Vector3D<Real_v> const &point, Vector3D<Real_v> const &dir, Real_v &smin, Real_v &smax) const
0165   {
0166     using Bool_v = vecCore::Mask_v<Real_v>;
0167     Bool_v done(false);
0168     Real_v distIn(kInfLength); // set for earlier returns
0169 
0170     // hope for a vectorization of this part for Backend==scalar!!
0171     Real_v pdist[N];
0172     Real_v proj[N];
0173     Real_v vdist[N];
0174     // vectorizable part
0175     for (int i = 0; i < N; ++i) {
0176       pdist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0177       proj[i]  = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0178 
0179       // note(SW): on my machine it was better to keep vdist[N] instead of a local variable vdist below
0180       vdist[i] = -pdist[i] / NonZero(proj[i]);
0181     }
0182 
0183     // wrong-side check: if (inside && smin<0) return -1
0184     for (int i = 0; i < N; ++i) {
0185       done = done || (pdist[i] > Real_v(MakePlusTolerant<true>(0.)) && proj[i] >= Real_v(0.));
0186       done = done || (pdist[i] > Real_v(MakeMinusTolerant<true>(0.)) && proj[i] > Real_v(0.));
0187     }
0188     if (vecCore::EarlyReturnMaxLength(done, 1) && vecCore::MaskFull(done)) return distIn;
0189 
0190     // analysis loop
0191     for (int i = 0; i < N; ++i) {
0192       // if outside and moving away, return infinity
0193       Bool_v posPoint = pdist[i] > Real_v(MakeMinusTolerant<true>(0.));
0194       Bool_v posDir   = proj[i] > 0;
0195 
0196       // check if trajectory will intercept plane within current range (smin,smax)
0197       Bool_v interceptFromInside = (!posPoint && posDir);
0198       done                       = done || (interceptFromInside && vdist[i] < smin);
0199 
0200       Bool_v interceptFromOutside = (posPoint && !posDir);
0201       done                        = done || (interceptFromOutside && vdist[i] > smax);
0202       if (vecCore::EarlyReturnMaxLength(done, 1) && vecCore::MaskFull(done)) return distIn;
0203 
0204       // update smin,smax
0205       vecCore__MaskedAssignFunc(smin, interceptFromOutside && vdist[i] > smin, vdist[i]);
0206       vecCore__MaskedAssignFunc(smax, interceptFromInside && vdist[i] < smax, vdist[i]);
0207     }
0208 
0209     // Survivors will return smin, which is the maximum distance in an interceptFromOutside situation
0210     // (SW: not sure this is true since smin is initialized from outside and can have any arbitrary value)
0211     vecCore::MaskedAssign(distIn, !done && smin <= smax, smin);
0212     return distIn;
0213   }
0214 
0215   /// \return the distance to the planar shell when the point is located within the shell itself.
0216   /// The type returned is the type corresponding to the backend given.
0217   /// For some special cases, the value returned is:
0218   ///     (1) -1, if point is outside (wrong-side)
0219   template <typename Real_v>
0220   VECGEOM_FORCE_INLINE
0221   VECCORE_ATT_HOST_DEVICE
0222   Real_v DistanceToOut(Vector3D<Real_v> const &point, Vector3D<Real_v> const &dir) const
0223   {
0224     // using Bool_v = vecCore::Mask_v<Real_v>;
0225     // Bool_v done(false);
0226     Real_v distOut(kInfLength);
0227     // Real_v distOut1(kInfLength);
0228 
0229     // hope for a vectorization of this part for Backend==scalar !!
0230     // the idea is to put vectorizable things into this loop
0231     // and separate the analysis into a separate loop if need be
0232     Real_v pdist[N];
0233     Real_v proj[N];
0234     Real_v vdist[N];
0235     for (int i = 0; i < N; ++i) {
0236       pdist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0237       proj[i]  = this->fA[i] * dir.x() + this->fB[i] * dir.y() + this->fC[i] * dir.z();
0238       vdist[i] = -pdist[i] / NonZero(proj[i]);
0239     }
0240 
0241     // early return if point is outside of plane
0242     // for (int i = 0; i < N; ++i) {
0243     //   done = done || (pdist[i] > kHalfTolerance);
0244     // }
0245     // vecCore__MaskedAssignFunc(distOut, done, Real_v(-1.0));
0246     // // if (vecCore::EarlyReturnMaxLength(done,1) && vecCore::MaskFull(done)) return distOut;
0247 
0248     // std::cout<<"=== point="<< point <<", dir="<< dir <<"\n";
0249     for (int i = 0; i < N; ++i) {
0250       vecCore__MaskedAssignFunc(distOut, pdist[i] > kHalfTolerance, Real_v(-1.));
0251       vecCore__MaskedAssignFunc(distOut, proj[i] > Real_v(0.) && vdist[i] < distOut, vdist[i]);
0252       // std::cout<<"i="<< i <<", pdist="<< pdist[i] <<", proj="<< proj[i] <<", vdist="<< vdist[i] <<" "<< vdist1[i] <<"
0253       // --> dist="<< distOut <<", "<< distOut1 <<"\n";
0254     }
0255 
0256     return distOut;
0257   }
0258 
0259   /// \return the safety distance to the planar shell when the point is located within the shell itself.
0260   template <typename Real_v>
0261   VECGEOM_FORCE_INLINE
0262   VECCORE_ATT_HOST_DEVICE
0263   void SafetyToIn(Vector3D<Real_v> const &point, Real_v &safety) const
0264   {
0265     // vectorizable loop
0266     Real_v dist[N];
0267     for (int i = 0; i < N; ++i) {
0268       dist[i] = this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i];
0269     }
0270 
0271     // non-vectorizable part
0272     for (int i = 0; i < N; ++i) {
0273       vecCore__MaskedAssignFunc(safety, dist[i] > safety, dist[i]);
0274     }
0275   }
0276 
0277   /// \return the distance to the planar shell when the point is located within the shell itself.
0278   template <typename Real_v>
0279   VECGEOM_FORCE_INLINE
0280   VECCORE_ATT_HOST_DEVICE
0281   void SafetyToOut(Vector3D<Real_v> const &point, Real_v &safety) const
0282   {
0283     // vectorizable loop
0284     Real_v dist[N];
0285     for (int i = 0; i < N; ++i) {
0286       dist[i] = -(this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0287     }
0288 
0289     // non-vectorizable part
0290     for (int i = 0; i < N; ++i) {
0291       vecCore__MaskedAssignFunc(safety, dist[i] < safety, dist[i]);
0292     }
0293   }
0294 
0295   template <typename Real_v>
0296   VECGEOM_FORCE_INLINE
0297   VECCORE_ATT_HOST_DEVICE
0298   size_t ClosestFace(Vector3D<Real_v> const &point, Real_v &safety) const
0299   {
0300     // vectorizable loop
0301     Real_v dist[N];
0302     for (int i = 0; i < N; ++i) {
0303       dist[i] = Abs(this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0304     }
0305 
0306     // non-vectorizable part
0307     using Bool_v    = vecCore::Mask_v<Real_v>;
0308     using Index_v   = vecCore::Index<Real_v>;
0309     Index_v closest = static_cast<Index_v>(-1);
0310     for (size_t i = 0; i < N; ++i) {
0311       Bool_v closer = dist[i] < safety;
0312       vecCore__MaskedAssignFunc(safety, closer, dist[i]);
0313       vecCore::MaskedAssign(closest, closer, i);
0314     }
0315 
0316     return closest;
0317   }
0318 
0319   /// \return a *non-normalized* vector normal to the plane containing (or really close) to point.
0320   /// In most cases the resulting normal is either (0,0,0) when point is not close to any plane,
0321   /// or the normal of that single plane really close to the point (in this case, it *is* normalized).
0322   ///
0323   /// Note: If the point is really close to more than one plane, those planes' normals are added,
0324   /// and in this case the vector returned *is not* normalized.  This can be used as a flag, so that
0325   /// the callee knows that nsurf==2 (the maximum value possible).
0326   ///
0327   template <typename Real_v>
0328   VECGEOM_FORCE_INLINE
0329   VECCORE_ATT_HOST_DEVICE
0330   Real_v NormalKernel(Vector3D<Real_v> const &point, Vector3D<Real_v> &normal) const
0331   {
0332     Real_v safety = -InfinityLength<Real_v>();
0333 
0334     // vectorizable loop
0335     Real_v dist[N];
0336     for (int i = 0; i < N; ++i) {
0337       dist[i] = (this->fA[i] * point.x() + this->fB[i] * point.y() + this->fC[i] * point.z() + this->fD[i]);
0338     }
0339 
0340     // non-vectorizable part
0341     for (int i = 0; i < 4; ++i) {
0342       Real_v saf_i = dist[i] - safety;
0343 
0344       // if more planes found as far (within tolerance) as the best one so far *and not fully inside*, add its normal
0345       vecCore__MaskedAssignFunc(normal, Abs(saf_i) < kHalfTolerance && dist[i] >= -kHalfTolerance,
0346                                 normal + Vector3D<Real_v>(this->fA[i], this->fB[i], this->fC[i]));
0347 
0348       // this one is farther than our previous one -- update safety and normal
0349       vecCore__MaskedAssignFunc(normal, saf_i > Real_v(0.), Vector3D<Real_v>(this->fA[i], this->fB[i], this->fC[i]));
0350       vecCore__MaskedAssignFunc(safety, saf_i > Real_v(0.), dist[i]);
0351       // std::cout<<"dist["<< i <<"]="<< dist[i] <<", saf_i="<< saf_i <<", safety="<< safety <<", normal="<< normal
0352       // <<"\n";
0353     }
0354 
0355     // Note: this could be (rarely) a non-normalized normal vector (when point is close to 2 planes)
0356     // std::cout<<"Return from PlaneShell::Normal: safety="<< safety <<", normal="<< normal <<"\n";
0357     return safety;
0358   }
0359 };
0360 
0361 } // namespace VECGEOM_IMPL_NAMESPACE
0362 } // namespace vecgeom
0363 
0364 #endif // VECGEOM_BASE_SIDEPLANES_H_