Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/VecGeom/volumes/utilities/VolumeUtilities.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * volume_utilities.h
0003  *
0004  *  Created on: Mar 24, 2014
0005  *      Author: swenzel
0006  */
0007 
0008 #ifndef VOLUME_UTILITIES_H_
0009 #define VOLUME_UTILITIES_H_
0010 
0011 #include "VecGeom/base/Vector3D.h"
0012 #include "VecGeom/base/Global.h"
0013 #include "VecGeom/base/RNG.h"
0014 #include "VecGeom/volumes/PlacedBox.h"
0015 #include "VecGeom/volumes/LogicalVolume.h"
0016 #include "VecGeom/navigation/NavigationState.h"
0017 #include "VecGeom/navigation/VNavigator.h"
0018 #include "VecGeom/navigation/GlobalLocator.h"
0019 #include "VecGeom/management/GeoManager.h"
0020 
0021 #include "VecGeom/management/Logger.h"
0022 
0023 #include <cstdio>
0024 #include <random>
0025 #include <vector>
0026 #include <random>
0027 
0028 namespace vecgeom {
0029 inline namespace VECGEOM_IMPL_NAMESPACE {
0030 namespace volumeUtilities {
0031 
0032 /**
0033  * @brief Is the trajectory through a point along a direction hitting a volume?
0034  * @details If ROOT is available and ??? is set, use
0035  *    ROOT to calculate it, otherwise use VecGeom utilities.
0036  * @param point is the starting point
0037  * @param dir is the direction of the trajectory
0038  * @param volume is the shape under test
0039  * @return true/false whether the trajectory hits the volume
0040  */
0041 VECGEOM_FORCE_INLINE
0042 bool IsHittingVolume(Vector3D<Precision> const &point, Vector3D<Precision> const &dir, VPlacedVolume const &volume)
0043 {
0044   VECGEOM_ASSERT(!volume.Contains(point));
0045   return volume.DistanceToIn(point, dir, vecgeom::kInfLength) < vecgeom::kInfLength;
0046 }
0047 
0048 VECGEOM_FORCE_INLINE
0049 bool IsHittingLogicalVolume(Vector3D<Precision> const &point, Vector3D<Precision> const &dir,
0050                             LogicalVolume const &volume)
0051 {
0052   VECGEOM_ASSERT(!volume.GetUnplacedVolume()->Contains(point));
0053   return volume.GetUnplacedVolume()->DistanceToIn(point, dir, vecgeom::kInfLength) < vecgeom::kInfLength;
0054 }
0055 
0056 // utility function to check if track hits any daughter of input logical volume
0057 inline bool IsHittingAnyDaughter(Vector3D<Precision> const &point, Vector3D<Precision> const &dir,
0058                                  LogicalVolume const &volume)
0059 {
0060   for (size_t daughter = 0; daughter < volume.GetDaughters().size(); ++daughter) {
0061     if (IsHittingVolume(point, dir, *volume.GetDaughters()[daughter])) {
0062       return true;
0063     }
0064   }
0065   return false;
0066 }
0067 
0068 /**
0069  * @brief Returns a random point, based on a sampling rectangular volume.
0070  * @details Mostly used for benchmarks and navigation tests
0071  * @param size is a Vector3D containing the rectangular dimensions of the sampling volume
0072  * @param scale an optional scale factor (default is 1)
0073  * @return a random output point
0074  */
0075 inline Vector3D<Precision> SamplePoint(Vector3D<Precision> const &size, const Precision scale = 1)
0076 {
0077   const Vector3D<Precision> ret(scale * (1. - 2. * RNG::Instance().uniform()) * size[0],
0078                                 scale * (1. - 2. * RNG::Instance().uniform()) * size[1],
0079                                 scale * (1. - 2. * RNG::Instance().uniform()) * size[2]);
0080   return ret;
0081 }
0082 
0083 /**
0084  * @brief Returns a random point, based on a sampling rectangular volume.
0085  * @details Mostly used for benchmarks and navigation tests
0086  * @param size is a Vector3D containing the rectangular dimensions of the sampling volume
0087  * @param scale an optional scale factor (default is 1)
0088  * @return a random output point
0089  */
0090 template <typename RngEngine>
0091 VECGEOM_FORCE_INLINE Vector3D<Precision> SamplePoint(Vector3D<Precision> const &size, RngEngine &rngengine,
0092                                                      const Precision scale = 1)
0093 {
0094   std::uniform_real_distribution<double> dist(0, 2.);
0095   const Vector3D<Precision> ret(scale * (1. - dist(rngengine)) * size[0], scale * (1. - dist(rngengine)) * size[1],
0096                                 scale * (1. - dist(rngengine)) * size[2]);
0097   return ret;
0098 }
0099 
0100 /**
0101  *  @brief Returns a random, normalized, but non-isotropic direction vector.
0102  *  @details Mostly used for benchmarks, when a direction is needed.
0103  *  @return a random, normalized direction vector
0104  */
0105 inline Vector3D<Precision> SampleDirection()
0106 {
0107 
0108   Vector3D<Precision> dir((1. - 2. * RNG::Instance().uniform()), (1. - 2. * RNG::Instance().uniform()),
0109                           (1. - 2. * RNG::Instance().uniform()));
0110 
0111   const Precision inverse_norm = 1. / std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
0112   dir *= inverse_norm;
0113 
0114   return dir;
0115 }
0116 
0117 /**
0118  *  @brief Returns a random, normalized, and isotropic direction vector.
0119  *  @details Mostly used for benchmarks, when a direction is needed.
0120  *  @return a random, normalized direction vector
0121  */
0122 inline Vector3D<Precision> SampleDirectionIsotropic()
0123 {
0124 
0125   Precision phi  = 2 * kPi * RNG::Instance().uniform();
0126   Precision sphi = vecCore::math::Sin(phi);
0127   Precision cphi = vecCore::math::Cos(phi);
0128   Precision the  = vecCore::math::ACos(1 - 2 * RNG::Instance().uniform());
0129   Precision sthe = vecCore::math::Sin(the);
0130   Precision cthe = vecCore::math::Cos(the);
0131   // This is normalized by construction
0132   Vector3D<Precision> dir(sthe * cphi, sthe * sphi, cthe);
0133   return dir;
0134 }
0135 
0136 /**
0137  *  @brief Fills a container with random normalized directions.
0138  *  @param dirs is the output container, provided by the caller
0139  */
0140 template <typename TrackContainer>
0141 VECGEOM_FORCE_INLINE void FillRandomDirections(TrackContainer &dirs)
0142 {
0143   dirs.resize(dirs.capacity());
0144   for (int i = 0, iMax = dirs.capacity(); i < iMax; ++i) {
0145     dirs.set(i, SampleDirection());
0146   }
0147 }
0148 
0149 /**
0150  *  @brief Fills a C array with random normalized directions.
0151  *  @param dirs is the output container, provided by the caller with the right size
0152  *  @param size is the number of directions to be filled
0153  */
0154 VECGEOM_FORCE_INLINE
0155 void FillRandomDirections(Vector3D<Precision> *dirs, int size)
0156 {
0157   for (int i = 0; i < size; ++i) {
0158     dirs[i] = SampleDirectionIsotropic();
0159   }
0160 }
0161 
0162 /**
0163  * @brief Fills a container with biased normalized directions.
0164  * @details Directions are randomly assigned first, and then the
0165  *    fraction of hits is measured and compared to suggested bias.
0166  *    Then some directions will be modified as needed, to force the
0167  *    sample as a whole to have the suggested hit bias (@see bias).
0168  * @param volume provided must have daughter volumes.  Those daughters
0169  *    are used to determine the hit bias (@see bias).
0170  * @param points provided, and not modified.
0171  * @param bias is a real number in the range [0,1], which suggests the
0172  *    fraction of points hitting any of the daughter volumes.
0173  * @param dirs is the output directions container, provided by the
0174  *    caller.
0175  */
0176 template <typename TrackContainer>
0177 VECGEOM_FORCE_INLINE void FillBiasedDirections(VPlacedVolume const &volume, TrackContainer const &points,
0178                                                Precision bias, TrackContainer &dirs, const bool motherOnly = false)
0179 {
0180   VECGEOM_ASSERT(bias >= 0. && bias <= 1.);
0181 
0182   if (bias > 0. && !motherOnly && volume.GetDaughters().size() == 0) {
0183     VECGEOM_LOG(error) << "nFillBiasedDirections: bias=" << bias << " requested, but no daughter volumes found";
0184     //// should throw exception, but for now just abort
0185     // printf("FillBiasedDirections: aborting...\n");
0186     // exit(1);
0187     ///== temporary: reset bias to zero
0188     bias = 0.0;
0189   }
0190 
0191   const int size = dirs.capacity();
0192   int n_hits     = 0;
0193   std::vector<bool> hit(size, false);
0194 
0195   // Randomize directions
0196   FillRandomDirections(dirs);
0197 
0198   // Check hits
0199   for (int track = 0; track < size; ++track) {
0200     bool isHitting = motherOnly ? IsHittingLogicalVolume(points[track], dirs[track], *volume.GetLogicalVolume())
0201                                 : IsHittingAnyDaughter(points[track], dirs[track], *volume.GetLogicalVolume());
0202     if (isHitting) {
0203       n_hits++;
0204       hit[track] = true;
0205     }
0206   }
0207 
0208   // Remove hits until threshold
0209   printf("VolumeUtilities: FillBiasedDirs: nhits/size = %i/%i and requested bias=%f\n", n_hits, size, bias);
0210   int tries    = 0;
0211   int maxtries = 10000 * size;
0212   while (static_cast<Precision>(n_hits) / static_cast<Precision>(size) > bias) {
0213     // while (n_hits > 0) {
0214     tries++;
0215     if (tries % 1000000 == 0) {
0216       printf("%s line %i: Warning: %i tries to reduce bias... volume=%s. Please check.\n", __FILE__, __LINE__, tries,
0217              volume.GetLabel().c_str());
0218     }
0219 
0220     int track         = static_cast<int>(static_cast<Precision>(size) * RNG::Instance().uniform());
0221     int internaltries = 0;
0222     while (hit[track]) {
0223       if (internaltries % 2) {
0224         dirs.set(track, SampleDirection());
0225       } else {
0226         // try inversing direction
0227         dirs.set(track, -dirs[track]);
0228       }
0229       internaltries++;
0230       bool isHitting = motherOnly ? IsHittingLogicalVolume(points[track], dirs[track], *volume.GetLogicalVolume())
0231                                   : IsHittingAnyDaughter(points[track], dirs[track], *volume.GetLogicalVolume());
0232 
0233       if (!isHitting) {
0234         n_hits--;
0235         hit[track] = false;
0236         //    tries = 0;
0237       }
0238       if (internaltries % 100 == 0) {
0239         // printf("%s line %i: Warning: %i tries to reduce bias... current bias %d volume=%s. Please check.\n",
0240         // __FILE__,
0241         //       __LINE__, internaltries, n_hits, volume.GetLabel().c_str());
0242         // try another track
0243         break;
0244       }
0245     }
0246   }
0247 
0248   // crosscheck
0249   {
0250     int crosscheckhits = 0;
0251     for (int track = 0; track < size; ++track) {
0252       bool isHitting = motherOnly ? IsHittingLogicalVolume(points[track], dirs[track], *volume.GetLogicalVolume())
0253                                   : IsHittingAnyDaughter(points[track], dirs[track], *volume.GetLogicalVolume());
0254       if (isHitting) crosscheckhits++;
0255     }
0256     VECGEOM_VALIDATE(crosscheckhits == n_hits, << "problem with hit count == 0");
0257     (void)crosscheckhits; // silence set but not unused warnings when asserts are disabled
0258   }
0259 
0260   // Add hits until threshold
0261   tries = 0;
0262   while (static_cast<Precision>(n_hits) / static_cast<Precision>(size) < bias && tries < maxtries) {
0263     int track = static_cast<int>(static_cast<Precision>(size) * RNG::Instance().uniform());
0264     while (!hit[track] && tries < maxtries) {
0265       ++tries;
0266       if (tries % 1000000 == 0) {
0267         printf("%s line %i: Warning: %i tries to increase bias... volume=%s, current bias=%i/%i=%f.  Please check.\n",
0268                __FILE__, __LINE__, tries, volume.GetLabel().c_str(), n_hits, size,
0269                static_cast<Precision>(n_hits) / static_cast<Precision>(size));
0270       }
0271 
0272       if (motherOnly) {
0273         int internaltries = 0;
0274         while (!hit[track]) {
0275           if (internaltries % 2) {
0276             dirs.set(track, SampleDirection());
0277           } else {
0278             // try inversing direction
0279             dirs.set(track, -dirs[track]);
0280           }
0281           internaltries++;
0282 
0283           if (IsHittingLogicalVolume(points[track], dirs[track], *volume.GetLogicalVolume())) {
0284             n_hits++;
0285             hit[track] = true;
0286           }
0287           if (internaltries % 100 == 0) {
0288             // printf("%s line %i: Warning: %i tries to reduce bias... current bias %d volume=%s. Please check.\n",
0289             // __FILE__,
0290             //       __LINE__, internaltries, n_hits, volume.GetLabel().c_str());
0291             // try another track
0292             break;
0293           }
0294         }
0295         continue;
0296       }
0297 
0298       // SW: a potentially much faster algorithm is the following:
0299       // sample a daughter to hit ( we can adjust the sampling probability according to Capacity or something; then
0300       // generate point on surface of daughter )
0301       // set direction accordingly
0302       uint selecteddaughter              = (uint)RNG::Instance().uniform() * volume.GetDaughters().size();
0303       VPlacedVolume const *daughter      = volume.GetDaughters()[selecteddaughter];
0304       Vector3D<Precision> pointonsurface = daughter->GetUnplacedVolume()->SamplePointOnSurface();
0305       // point is in reference frame of daughter so need to transform it back
0306       Vector3D<Precision> dirtosurfacepoint =
0307           daughter->GetTransformation()->InverseTransform(pointonsurface) - points[track];
0308       dirtosurfacepoint.Normalize();
0309       dirs.set(track, dirtosurfacepoint);
0310 
0311       // the brute force and simple sampling technique is the following
0312       // dirs.set(h, SampleDirection());
0313       if (IsHittingAnyDaughter(points[track], dirs[track], *volume.GetLogicalVolume())) {
0314         n_hits++;
0315         hit[track] = true;
0316         tries      = 0;
0317       }
0318     }
0319   }
0320 
0321   // crosscheck
0322   {
0323     int crosscheckhits = 0;
0324     for (int p = 0; p < size; ++p) {
0325       bool isHitting = motherOnly ? IsHittingLogicalVolume(points[p], dirs[p], *volume.GetLogicalVolume())
0326                                   : IsHittingAnyDaughter(points[p], dirs[p], *volume.GetLogicalVolume());
0327       if (isHitting) crosscheckhits++;
0328     }
0329     VECGEOM_VALIDATE(crosscheckhits == n_hits, << "problem with hit count");
0330     (void)crosscheckhits; // silence set but not unused warnings when asserts are disabled
0331   }
0332 
0333   if (tries == maxtries) {
0334     printf("WARNING: NUMBER OF DIRECTORY SAMPLING TRIES EXCEEDED MAXIMUM; N_HITS %d; ACHIEVED BIAS %lf \n", n_hits,
0335            n_hits / (1. * size));
0336   }
0337 }
0338 
0339 /**
0340  * @brief Same as previous function, but now taking a LogicalVolume as input.
0341  * @detail Delegates the filling to the other function (@see FillBiasedDirections).
0342  */
0343 template <typename TrackContainer>
0344 VECGEOM_FORCE_INLINE void FillBiasedDirections(LogicalVolume const &volume, TrackContainer const &points,
0345                                                const Precision bias, TrackContainer &dirs,
0346                                                const bool motherOnly = false)
0347 {
0348   VPlacedVolume const *const placed = volume.Place();
0349   FillBiasedDirections(*placed, points, bias, dirs, motherOnly);
0350   delete placed;
0351 }
0352 
0353 VECGEOM_FORCE_INLINE
0354 Precision UncontainedCapacity(VPlacedVolume const &volume)
0355 {
0356   Precision momCapacity = const_cast<VPlacedVolume &>(volume).Capacity();
0357   Precision dauCapacity = 0.;
0358   unsigned int kk       = 0;
0359   for (Vector<Daughter>::const_iterator j = volume.GetDaughters().cbegin(), jEnd = volume.GetDaughters().cend();
0360        j != jEnd; ++j, ++kk) {
0361     dauCapacity += const_cast<VPlacedVolume *>(*j)->Capacity();
0362   }
0363   return momCapacity - dauCapacity;
0364 }
0365 
0366 /**
0367  * @brief Fills the volume with 3D points which are _not_ contained in
0368  *    any daughters of the input mother volume.
0369  * @details Requires a proper bounding box from the input volume.
0370  *    Point coordinates are local to input mother volume.
0371  * @param volume is the input mother volume containing all output points.
0372  * @param points is the output container, provided by the caller.
0373  */
0374 template <typename TrackContainer>
0375 VECGEOM_FORCE_INLINE bool FillUncontainedPoints(VPlacedVolume const &volume, TrackContainer &points,
0376                                                 const bool motherOnly = false)
0377 {
0378   static double lastUncontCap = 0.0;
0379   double uncontainedCapacity  = UncontainedCapacity(volume);
0380   if (uncontainedCapacity != lastUncontCap) {
0381     VECGEOM_LOG(info) << "Uncontained capacity for " << volume.GetLabel() << ":" << uncontainedCapacity << " units\n";
0382     lastUncontCap = uncontainedCapacity;
0383   }
0384   if (uncontainedCapacity <= 1000 * vecgeom::kTolerance) {
0385     VECGEOM_LOG(warning) << "Volume provided <" << volume.GetLabel() << "> does not have uncontained capacity";
0386     return false;
0387   }
0388 
0389   const int size = points.capacity();
0390   points.resize(points.capacity());
0391 
0392   Vector3D<Precision> lower, upper, offset;
0393   volume.GetUnplacedVolume()->Extent(lower, upper);
0394   offset                  = 0.5 * (upper + lower);
0395   Vector3D<Precision> dim = 0.5 * (upper - lower);
0396   if (motherOnly) dim = 1.3 * dim;
0397 
0398   int totaltries = 0;
0399   for (int i = 0; i < size; ++i) {
0400     bool contained, retry;
0401     Vector3D<Precision> point;
0402     totaltries = 0;
0403     do {
0404       // ensure that point is contained in mother volume
0405       do {
0406         ++totaltries;
0407         if (totaltries % 10000 == 0) {
0408           VECGEOM_LOG(warning) << totaltries << " attempts to find uncontained points in volume " << volume.GetLabel();
0409         }
0410         if (totaltries % 5000000 == 0) {
0411           double ratio = 1.0 * i / totaltries;
0412           printf("Progress : %i tries ( succeeded = %i , ratio %f %% ) to find uncontained points... volume=%s.\n",
0413                  totaltries, i, 100. * ratio, volume.GetLabel().c_str());
0414         }
0415 
0416         point = offset + SamplePoint(dim);
0417         retry = !volume.UnplacedContains(point);
0418         if (motherOnly) retry = !retry;
0419       } while (retry);
0420       points.set(i, point);
0421       if (motherOnly) break;
0422 
0423       contained = false;
0424       int kk    = 0;
0425       for (Vector<Daughter>::const_iterator j = volume.GetDaughters().cbegin(), jEnd = volume.GetDaughters().cend();
0426            j != jEnd; ++j, ++kk) {
0427         if ((*j)->Contains(points[i])) {
0428           contained = true;
0429           break;
0430         }
0431       }
0432     } while (contained);
0433   }
0434   return true;
0435 }
0436 
0437 template <typename TrackContainer>
0438 VECGEOM_FORCE_INLINE bool FillUncontainedPoints(LogicalVolume const &volume, TrackContainer &points)
0439 {
0440   VPlacedVolume const *const placed = volume.Place();
0441   bool good                         = FillUncontainedPoints(*placed, points);
0442   delete placed;
0443 
0444   return good;
0445 }
0446 
0447 // *** The following functions allow to give an external generator
0448 // *** which should make these functions usable in parallel
0449 
0450 /**
0451  * @brief Fills the volume with 3D points which are _not_ contained in
0452  *    any daughters of the input mother volume.
0453  * @details Requires a proper bounding box from the input volume.
0454  *    Point coordinates are local to input mother volume.
0455  * @param volume is the input mother volume containing all output points.
0456  * @param points is the output container, provided by the caller.
0457  */
0458 template <typename RandomEngine, typename TrackContainer>
0459 VECGEOM_FORCE_INLINE bool FillUncontainedPoints(VPlacedVolume const &volume, RandomEngine &rngengine,
0460                                                 TrackContainer &points, const bool verbose= false)
0461 {
0462   static double lastUncontCap = 0.0;
0463   double uncontainedCapacity  = UncontainedCapacity(volume);
0464   if (uncontainedCapacity != lastUncontCap) {
0465     printf("Uncontained capacity for %s: %g units\n", volume.GetLabel().c_str(), uncontainedCapacity);
0466     lastUncontCap = uncontainedCapacity;
0467   }
0468   double totalcapacity = const_cast<VPlacedVolume &>(volume).Capacity();
0469 
0470   if (verbose)
0471   {
0472     VECGEOM_LOG(info) << "Volume <" << volume.GetLabel() << "> capacities: total =  " << totalcapacity
0473                        << ", uncontained = " << uncontainedCapacity << "\n";
0474   }
0475 
0476 #ifndef VECCORE_CUDA
0477   if (verbose && uncontainedCapacity <= 0.0 ) {
0478     VECGEOM_LOG(info) << " VolUtil: FillUncontPts: Volume " << volume.GetLabel() << "  capacities: "
0479                       << " total =  " << totalcapacity
0480                       << " uncontained = " << uncontainedCapacity << "\n";
0481   }
0482 #endif
0483 
0484   if (uncontainedCapacity <= 1000 * vecgeom::kTolerance)
0485   {
0486     VECGEOM_LOG(warning) << "\nVolUtil: FillUncontPts: ERROR: Volume provided <" << volume.GetLabel()
0487                           << "> does not have uncontained capacity!  "
0488                           << "    Value = " << uncontainedCapacity
0489                           << "      total = " << totalcapacity;
0490     return false;
0491     // TODO --- try to find points anyway, and decide if real points were found
0492   }
0493 
0494   const int size = points.capacity();
0495   points.resize(points.capacity());
0496 
0497   Vector3D<Precision> lower, upper, offset;
0498   volume.GetUnplacedVolume()->Extent(lower, upper);
0499   offset                        = 0.5 * (upper + lower);
0500   const Vector3D<Precision> dim = 0.5 * (upper - lower);
0501 
0502   const int maxtries = 100 * 1000 * 1000;
0503 
0504   int tries = 0; // count total trials ...
0505   int i;
0506   for (i = 0; i < size; ++i) {
0507     bool contained;
0508     Vector3D<Precision> point;
0509     do {
0510       // ensure that point is contained in mother volume
0511       int onego = 0;
0512       do {
0513         ++tries;
0514         point = offset + SamplePoint(dim, rngengine);
0515 
0516         onego++;
0517         if (onego % 100000 == 0) {
0518           VECGEOM_LOG(status) << "Warning: " <<  tries << " tries without another good point. "
0519                               << " Total successful # " << i << " ) "
0520                               << " to find uncontained points in volume= " << volume.GetLabel();
0521         }
0522 
0523         if ( verbose && tries % 5000000 == 0) {
0524           double ratio = ( 1.0 * i ) / tries;
0525           VECGEOM_LOG(status) << "Progress : " << tries << "tries (in this task) succeeded = " << i
0526                               << ", ratio = " << 100.0 * ratio << " % "
0527                               <<  " towards finding uncontained points... volume=" << volume.GetLabel() << " . ";
0528         }
0529       } while (!volume.UnplacedContains(point));
0530       points.set(i, point);
0531 
0532       contained = false;
0533       int kk    = 0;
0534       for (Vector<Daughter>::const_iterator j = volume.GetDaughters().cbegin(), jEnd = volume.GetDaughters().cend();
0535            j != jEnd; ++j, ++kk) {
0536         if ((*j)->Contains(points[i])) {
0537           contained = true;
0538           break;
0539         }
0540       }
0541     } while (contained && tries < maxtries);
0542 
0543     if (tries >= maxtries) break;
0544   }
0545   constexpr double too_small= 0.03; // --- Lots of work for each point
0546   // if(verbose || ratio < too_small )
0547   if(verbose || i < too_small * tries ) {
0548      double ratio = (i * 1.0) / tries;
0549      VECGEOM_LOG(info)  << " trials " << tries << " found " << i << " points "
0550                         << " ( out of " << size << " requested - success ratio = " << ratio
0551                         << " ) for Volume <" << volume.GetLabel() << "\n";
0552   }
0553   return (i > 0);
0554 }
0555 
0556 template <typename RandomEngine, typename TrackContainer>
0557 VECGEOM_FORCE_INLINE bool FillUncontainedPoints(LogicalVolume const &volume, RandomEngine &rngengine,
0558                                                 TrackContainer &points)
0559 {
0560   VPlacedVolume const *const placed = volume.Place();
0561   bool good                         = FillUncontainedPoints(*placed, rngengine, points);
0562   delete placed;
0563 
0564   return good;
0565 }
0566 
0567 /**
0568  * @brief Fill a container structure (SOA3D or AOS3D) with random
0569  *    points contained in a volume. Points are returned in the reference
0570  *    frame of the volume (and not in the mother containing this volume)
0571  * @details Input volume must have a valid bounding box, which is used
0572  *    for sampling.
0573  * @param volume containing all points
0574  * @param points is the output container, provided by the caller.
0575  * returns if successful or not
0576  */
0577 template <typename TrackContainer>
0578 VECGEOM_FORCE_INLINE bool FillRandomPoints(VPlacedVolume const &volume, TrackContainer &points)
0579 {
0580   const int size = points.capacity();
0581   points.resize(points.capacity());
0582 
0583   int tries = 0;
0584 
0585   Vector3D<Precision> lower, upper, offset;
0586   volume.GetUnplacedVolume()->Extent(lower, upper);
0587   offset                        = 0.5 * (upper + lower);
0588   const Vector3D<Precision> dim = 0.5 * (upper - lower);
0589 
0590   for (int i = 0; i < size; ++i) {
0591     Vector3D<Precision> point;
0592     do {
0593       ++tries;
0594       if (tries % 1000000 == 0) {
0595         printf("%s line %i: Warning: %i tries to find contained points... volume=%s.  Please check.\n", __FILE__,
0596                __LINE__, tries, volume.GetLabel().c_str());
0597       }
0598       if (tries > 100000000) {
0599         printf("%s line %i: giving up\n", __FILE__, __LINE__);
0600         return false;
0601       }
0602       point = offset + SamplePoint(dim);
0603     } while (!volume.UnplacedContains(point));
0604     points.set(i, point);
0605   }
0606   return true;
0607 }
0608 
0609 /**
0610  * @brief Fill a container structure (SOA3D or AOS3D) with random
0611  *    points contained in a volume. Points are returned in the reference
0612  *    frame of the volume (and not in the mother containing this volume)
0613  * @details Input volume must have a valid bounding box, which is used
0614  *    for sampling.
0615  * @param volume containing all points
0616  * @param points is the output container, provided by the caller.
0617  * returns if successful or not
0618  */
0619 template <typename TrackContainer>
0620 VECGEOM_FORCE_INLINE bool FillRandomPoints(VUnplacedVolume const &volume, TrackContainer &points)
0621 {
0622   const int size = points.capacity();
0623   points.resize(points.capacity());
0624 
0625   int tries = 0;
0626 
0627   Vector3D<Precision> lower, upper, offset;
0628   volume.Extent(lower, upper);
0629   offset                        = 0.5 * (upper + lower);
0630   const Vector3D<Precision> dim = 0.5 * (upper - lower);
0631 
0632   for (int i = 0; i < size; ++i) {
0633     Vector3D<Precision> point;
0634     do {
0635       ++tries;
0636       if (tries % 1000000 == 0) {
0637         printf("%s line %i: Warning: %i tries to find contained points... in UnplacedVolume. Please check.\n", __FILE__,
0638                __LINE__, tries);
0639       }
0640       if (tries > 100000000) {
0641         printf("%s line %i: giving up\n", __FILE__, __LINE__);
0642         return false;
0643       }
0644       point = offset + SamplePoint(dim);
0645     } while (!volume.Contains(point));
0646     points.set(i, point);
0647   }
0648   return true;
0649 }
0650 
0651 /**
0652  * @brief Fills the volume with 3D points which are to be contained in
0653  *    any daughters of the input mother volume.
0654  * @details Requires a proper bounding box from the input volume.
0655  * @param volume is the input mother volume containing all output points.
0656  * @param points is the output container, provided by the caller.
0657  */
0658 template <typename TrackContainer>
0659 VECGEOM_FORCE_INLINE void FillContainedPoints(VPlacedVolume const &volume, const double bias, TrackContainer &points,
0660                                               const bool placed = true, const bool motherOnly = false)
0661 {
0662 
0663   const int size = points.capacity();
0664   points.resize(points.capacity());
0665 
0666   Vector3D<Precision> lower, upper, offset;
0667   if (motherOnly)
0668     volume.GetUnplacedVolume()->Extent(lower, upper);
0669   else
0670     volume.Extent(lower, upper);
0671   offset                  = 0.5 * (upper + lower);
0672   Vector3D<Precision> dim = 0.5 * (upper - lower);
0673   if (motherOnly) dim = 1.3 * dim;
0674 
0675   int insideCount = 0;
0676   std::vector<bool> insideVector(size, false);
0677   for (int i = 0; i < size; ++i) {
0678     points.set(i, offset + SamplePoint(dim));
0679     // measure bias, which is the fraction of points contained in daughters
0680     if (motherOnly) {
0681       bool inside = volume.UnplacedContains(points[i]);
0682       if (inside) {
0683         ++insideCount;
0684         insideVector[i] = true;
0685       }
0686       continue;
0687     }
0688     for (Vector<Daughter>::const_iterator v = volume.GetDaughters().cbegin(), v_end = volume.GetDaughters().cend();
0689          v != v_end; ++v) {
0690       bool inside = (placed) ? (*v)->Contains(points[i]) : (*v)->UnplacedContains(points[i]);
0691       if (inside) {
0692         ++insideCount;
0693         insideVector[i] = true;
0694         continue; // if contained in one daughter, no need to check the others
0695       }
0696     }
0697   }
0698 
0699   // remove contained points to reduce bias as needed
0700   int i          = 0;
0701   int totaltries = 0;
0702   while (static_cast<double>(insideCount) / static_cast<double>(size) > bias) {
0703     while (!insideVector[i])
0704       ++i;
0705     bool contained;
0706     do {
0707       ++totaltries;
0708       if (totaltries % 1000000 == 0) {
0709         printf("%s line %i: Warning: %i totaltries to reduce bias... volume=%s.  Please check.\n", __FILE__, __LINE__,
0710                totaltries, volume.GetLabel().c_str());
0711       }
0712 
0713       points.set(i, offset + SamplePoint(dim));
0714       contained = false;
0715       if (motherOnly) {
0716         contained = volume.UnplacedContains(points[i]);
0717         continue;
0718       }
0719       for (Vector<Daughter>::const_iterator v = volume.GetDaughters().cbegin(), v_end = volume.GetDaughters().end();
0720            v != v_end; ++v) {
0721         contained = (placed) ? (*v)->Contains(points[i]) : (*v)->UnplacedContains(points[i]);
0722         if (contained) break;
0723       }
0724     } while (contained);
0725     insideVector[i] = false;
0726     // tries           = 0;
0727     --insideCount;
0728     ++i;
0729   }
0730 
0731   int tries;
0732   // add contained points to increase bias as needed
0733   i     = 0;
0734   tries = 0;
0735   SOA3D<Precision> daughterpoint(1); // a "container" to be reused;
0736   while (static_cast<double>(insideCount) / static_cast<double>(size) < bias) {
0737     while (insideVector[i])
0738       ++i;
0739     bool contained = false;
0740     do {
0741       ++tries;
0742       if (tries % 1000000 == 0) {
0743         printf("%s line %i: Warning: %i tries to increase bias... volume=%s.  Please check.\n", __FILE__, __LINE__,
0744                tries, volume.GetLabel().c_str());
0745       }
0746 
0747       if (motherOnly) {
0748         points.set(i, offset + SamplePoint(dim));
0749         contained = volume.UnplacedContains(points[i]);
0750         continue;
0751       }
0752       auto ndaughters = volume.GetDaughters().size();
0753       if (ndaughters == 1) {
0754         // a faster procedure for just 1 daughter --> can directly sample in daughter
0755         auto daughter = volume.GetDaughters().operator[](0);
0756         FillRandomPoints(*daughter, daughterpoint);
0757         points.set(i, placed ? volume.GetTransformation()->InverseTransform(daughterpoint[0]) : daughterpoint[0]);
0758         contained = true;
0759       } else {
0760         const Vector3D<Precision> sample = offset + SamplePoint(dim);
0761         for (Vector<Daughter>::const_iterator v = volume.GetDaughters().cbegin(), v_end = volume.GetDaughters().cend();
0762              v != v_end; ++v) {
0763           bool inside = (placed) ? (*v)->Contains(sample) : (*v)->UnplacedContains(sample);
0764           if (inside) {
0765             points.set(i, sample);
0766             contained = true;
0767             break;
0768           }
0769         }
0770       }
0771 
0772     } while (!contained);
0773     insideVector[i] = true;
0774     tries           = 0;
0775     ++insideCount;
0776     ++i;
0777   }
0778 }
0779 
0780 template <typename TrackContainer>
0781 VECGEOM_FORCE_INLINE void FillContainedPoints(VPlacedVolume const &volume, TrackContainer &points,
0782                                               const bool placed = true, const bool motherOnly = false)
0783 {
0784   FillContainedPoints<TrackContainer>(volume, 1, points, placed, motherOnly);
0785 }
0786 
0787 /**
0788  * @brief Fills a container structure (SOA3D or AOS3D) with random
0789  *    points contained inside a box defined by the two input corners.
0790  * @param lowercorner, uppercorner define the sampling box
0791  * @param points is the output container, provided by the caller.
0792  */
0793 template <typename TrackContainer>
0794 VECGEOM_FORCE_INLINE void FillRandomPoints(Vector3D<Precision> const &lowercorner,
0795                                            Vector3D<Precision> const &uppercorner, TrackContainer &points)
0796 {
0797   const int size = points.capacity();
0798   points.resize(points.capacity());
0799   Vector3D<Precision> dim    = (uppercorner - lowercorner) / 2.;
0800   Vector3D<Precision> offset = (uppercorner + lowercorner) / 2.;
0801   for (int i = 0; i < size; ++i) {
0802     points.set(i, offset + SamplePoint(dim));
0803   }
0804 }
0805 
0806 /**
0807  * @brief Fills a C array with random
0808  *    points contained inside a box defined by the two input corners.
0809  * @param lowercorner Lower corner of the sampling box
0810  * @param uppercorner Upper corner of the sampling box
0811  * @param points The output container, provided with the right size by the caller.
0812  */
0813 VECGEOM_FORCE_INLINE
0814 void FillRandomPoints(Vector3D<Precision> const &lowercorner, Vector3D<Precision> const &uppercorner,
0815                       Vector3D<Precision> *points, int size)
0816 {
0817   Vector3D<Precision> dim    = (uppercorner - lowercorner) / 2.;
0818   Vector3D<Precision> offset = (uppercorner + lowercorner) / 2.;
0819   for (int i = 0; i < size; ++i) {
0820     points[i] = offset + SamplePoint(dim);
0821   }
0822 }
0823 
0824 /**
0825  * @brief Fills a container structure (SOA3D or AOS3D) with random
0826  *    points contained inside a box defined by the two input corners, but
0827  *    not contained in an ecluded volume. This can be useful to sample
0828  *    the space in a bounding box not pertaining to the volume.
0829  * @param lowercorner, uppercorner define the sampling box
0830  * @param points is the output container, provided by the caller.
0831  */
0832 template <typename TrackContainer, typename ExcludedVol, bool exlu = true>
0833 VECGEOM_FORCE_INLINE void FillRandomPoints(Vector3D<Precision> const &lowercorner,
0834                                            Vector3D<Precision> const &uppercorner, ExcludedVol const &vol,
0835                                            TrackContainer &points)
0836 {
0837   const int size = points.capacity();
0838   points.resize(points.capacity());
0839   Vector3D<Precision> dim    = (uppercorner - lowercorner) / 2.;
0840   Vector3D<Precision> offset = (uppercorner + lowercorner) / 2.;
0841   for (int i = 0; i < size; ++i) {
0842     Vector3D<Precision> p;
0843     do {
0844       p = offset + SamplePoint(dim);
0845     } while (!(exlu ^ vol.Contains(p))); // XNOR
0846     points.set(i, p);
0847   }
0848 }
0849 
0850 /**
0851  * @brief Fills a (SOA3D or AOS3D) container with random points inside
0852  *    a box at the origin
0853  * @param dim is a Vector3D with w,y,z half-lengths defining the sampling box
0854  * @param points is the output container, provided by the caller.
0855  */
0856 template <typename TrackContainer>
0857 VECGEOM_FORCE_INLINE void FillRandomPoints(Vector3D<Precision> const &dim, TrackContainer &points)
0858 {
0859   FillRandomPoints(Vector3D<Precision>(-dim.x(), -dim.y(), -dim.z()), Vector3D<Precision>(dim.x(), dim.y(), dim.z()),
0860                    points);
0861 }
0862 
0863 /**
0864  * @brief Generates _uncontained_ global points and directions based
0865  *   on a logical volume.
0866  *
0867  * @details Points and direction coordinates are based on the global
0868  *   reference frame.  The positions have to be within a given logical
0869  *   volume, and not within any daughters of that logical volume.
0870  *
0871  * The function also returns the generated points in local reference
0872  *   frame of the logical volume.
0873  *
0874  * @param fraction: is the fraction with which the directions should
0875  *   hit a daughtervolume
0876  * @param np: number of particles
0877  *
0878  */
0879 template <typename TrackContainer>
0880 inline void FillGlobalPointsAndDirectionsForLogicalVolume(LogicalVolume const *lvol, TrackContainer &localpoints,
0881                                                           TrackContainer &globalpoints, TrackContainer &directions,
0882                                                           Precision fraction, int np)
0883 {
0884 
0885   // we need to generate a list of all the paths ( or placements ) which reference
0886   // the logical volume as their deepest node
0887 
0888   std::list<NavigationState *> allpaths;
0889   GeoManager::Instance().getAllPathForLogicalVolume(lvol, allpaths);
0890 
0891   NavigationState *s1       = NavigationState::MakeInstance(GeoManager::Instance().getMaxDepth());
0892   NavigationState *s2       = NavigationState::MakeInstance(GeoManager::Instance().getMaxDepth());
0893   int virtuallyhitsdaughter = 0;
0894   int reallyhitsdaughter    = 0;
0895   if (allpaths.size() > 0) {
0896     // get one representative of such a logical volume
0897     VPlacedVolume const *pvol = allpaths.front()->Top();
0898 
0899     // generate points which are in lvol but not in its daughters
0900     bool good = FillUncontainedPoints(*pvol, localpoints);
0901     // VECGEOM_ASSERT(good);
0902     if (!good) {
0903       std::cerr << "FATAL ERROR> FillUncontainedPoints failed for volume " << pvol->GetName() << std::endl;
0904       exit(1);
0905     }
0906 
0907     // now have the points in the local reference frame of the logical volume
0908     FillBiasedDirections(*lvol, localpoints, fraction, directions);
0909 
0910     // transform points to global frame
0911     globalpoints.resize(globalpoints.capacity());
0912     int placedcount = 0;
0913 
0914     while (placedcount < np) {
0915       std::list<NavigationState *>::iterator iter = allpaths.begin();
0916       while (placedcount < np && iter != allpaths.end()) {
0917         // this is matrix linking local and global reference frame
0918         Transformation3D m;
0919         (*iter)->TopMatrix(m);
0920 
0921         bool hitsdaughter = IsHittingAnyDaughter(localpoints[placedcount], directions[placedcount], *lvol);
0922         if (hitsdaughter) virtuallyhitsdaughter++;
0923         globalpoints.set(placedcount, m.InverseTransform(localpoints[placedcount]));
0924         directions.set(placedcount, m.InverseTransformDirection(directions[placedcount]));
0925 
0926         // do extensive cross tests
0927         s1->Clear();
0928         s2->Clear();
0929         GlobalLocator::LocateGlobalPoint(GeoManager::Instance().GetWorld(), globalpoints[placedcount], *s1, true);
0930         VECGEOM_ASSERT(s1->Top()->GetLogicalVolume() == lvol);
0931         Precision step = vecgeom::kInfLength;
0932         auto nav       = s1->Top()->GetLogicalVolume()->GetNavigator();
0933         nav->FindNextBoundaryAndStep(globalpoints[placedcount], directions[placedcount], *s1, *s2, vecgeom::kInfLength,
0934                                      step);
0935 #ifdef DEBUG
0936         if (!hitsdaughter) VECGEOM_ASSERT(s1->Distance(*s2) > s2->GetCurrentLevel() - s1->GetCurrentLevel());
0937 #endif
0938         if (hitsdaughter)
0939           if (s1->Distance(*s2) == s2->GetCurrentLevel() - s1->GetCurrentLevel()) {
0940             reallyhitsdaughter++;
0941           }
0942 
0943         placedcount++;
0944         iter++;
0945       }
0946     }
0947   } else {
0948     // an error message
0949       VECGEOM_LOG(error) << "FillGlobalPointsAndDirectionsForLogicalVolume()... ERROR condition detected";
0950   }
0951   printf(" really hits %d, virtually hits %d ", reallyhitsdaughter, virtuallyhitsdaughter);
0952   NavigationState::ReleaseInstance(s1);
0953   NavigationState::ReleaseInstance(s2);
0954   std::list<NavigationState *>::iterator iter = allpaths.begin();
0955   while (iter != allpaths.end()) {
0956     NavigationState::ReleaseInstance(*iter);
0957     ++iter;
0958   }
0959 }
0960 
0961 // same as above; logical volume is given by name
0962 template <typename TrackContainer>
0963 inline void FillGlobalPointsAndDirectionsForLogicalVolume(std::string const &name, TrackContainer &localpoints,
0964                                                           TrackContainer &globalpoints, TrackContainer &directions,
0965                                                           Precision fraction, int np)
0966 {
0967 
0968   LogicalVolume const *vol = GeoManager::Instance().FindLogicalVolume(name.c_str());
0969   if (vol != NULL)
0970     FillGlobalPointsAndDirectionsForLogicalVolume(vol, localpoints, globalpoints, directions, fraction, np);
0971 }
0972 
0973 // same as above; logical volume is given by id
0974 template <typename TrackContainer>
0975 inline void FillGlobalPointsAndDirectionsForLogicalVolume(int id, TrackContainer &localpoints,
0976                                                           TrackContainer &globalpoints, TrackContainer &directions,
0977                                                           Precision fraction, int np)
0978 {
0979 
0980   LogicalVolume const *vol = GeoManager::Instance().FindLogicalVolume(id);
0981   if (vol != NULL)
0982     FillGlobalPointsAndDirectionsForLogicalVolume(vol, localpoints, globalpoints, directions, fraction, np);
0983 }
0984 
0985 /**
0986  * @brief Generates _uncontained_ global points based
0987  *   on a logical volume.
0988  *
0989  * @details Points coordinates are based on the global
0990  *   reference frame.  The positions have to be within a given logical
0991  *   volume, and optionally not within any daughters of that logical volume.
0992  *
0993  * * @param np: number of particles
0994  *
0995  */
0996 template <typename TrackContainer>
0997 inline void FillGlobalPointsForLogicalVolume(LogicalVolume const *lvol, TrackContainer &localpoints,
0998                                              TrackContainer &globalpoints, int np, bool maybeindaughters = false)
0999 {
1000 
1001   // we need to generate a list of all the paths ( or placements ) which reference
1002   // the logical volume as their deepest node
1003 
1004   std::list<NavigationState *> allpaths;
1005   GeoManager::Instance().getAllPathForLogicalVolume(lvol, allpaths);
1006 
1007   if (allpaths.size() > 0) {
1008     // get one representative of such a logical volume
1009     VPlacedVolume const *pvol = allpaths.front()->Top();
1010 
1011     if (maybeindaughters) {
1012       FillContainedPoints(*pvol, localpoints);
1013     } else {
1014       // generate points which are in lvol but not in its daughters
1015       bool good = FillUncontainedPoints(*pvol, localpoints);
1016       // VECGEOM_ASSERT(good);
1017       if (!good) {
1018         std::cerr << "FATAL ERROR> FillUncontainedPoints failed for volume " << pvol->GetName() << std::endl;
1019         exit(1);
1020       }
1021     }
1022 
1023     // transform points to global frame
1024     globalpoints.resize(globalpoints.capacity());
1025     int placedcount = 0;
1026 
1027     while (placedcount < np) {
1028       std::list<NavigationState *>::iterator iter = allpaths.begin();
1029       while (placedcount < np && iter != allpaths.end()) {
1030         // this is matrix linking local and global reference frame
1031         Transformation3D m;
1032         (*iter)->TopMatrix(m);
1033 
1034         globalpoints.set(placedcount, m.InverseTransform(localpoints[placedcount]));
1035 
1036         placedcount++;
1037         iter++;
1038       }
1039     }
1040   } else {
1041       VECGEOM_LOG(error) << "FillGlobalPointsForLogicalVolume()... ERROR condition detected";
1042   }
1043 
1044   std::list<NavigationState *>::iterator iter = allpaths.begin();
1045   while (iter != allpaths.end()) {
1046     NavigationState::ReleaseInstance(*iter);
1047     ++iter;
1048   }
1049 }
1050 
1051 // same as above; logical volume is given by name
1052 template <typename TrackContainer>
1053 inline void FillGlobalPointsForLogicalVolume(std::string const &name, TrackContainer &localpoints,
1054                                              TrackContainer &globalpoints, int np)
1055 {
1056 
1057   LogicalVolume const *vol = GeoManager::Instance().FindLogicalVolume(name.c_str());
1058   if (vol != NULL) FillGlobalPointsForLogicalVolume(vol, localpoints, globalpoints, np);
1059 }
1060 
1061 // same as above; logical volume is given by id
1062 template <typename TrackContainer>
1063 inline void FillGlobalPointsForLogicalVolume(int id, TrackContainer &localpoints, TrackContainer &globalpoints, int np)
1064 {
1065 
1066   LogicalVolume const *vol = GeoManager::Instance().FindLogicalVolume(id);
1067   if (vol != NULL) FillGlobalPointsForLogicalVolume(vol, localpoints, globalpoints, np);
1068 }
1069 
1070 inline Precision GetRadiusInRing(Precision rmin, Precision rmax)
1071 {
1072 
1073   // Generate radius in annular ring according to uniform area
1074   if (rmin <= 0.) {
1075     return rmax * std::sqrt(RNG::Instance().uniform());
1076   }
1077   if (rmin != rmax) {
1078     Precision rmin2 = rmin * rmin;
1079     Precision rmax2 = rmax * rmax;
1080     return std::sqrt(rmin2 + RNG::Instance().uniform() * (rmax2 - rmin2));
1081   }
1082   return rmin;
1083 }
1084 
1085 /** This function will detect whether two aligned boxes intersects or not.
1086  *  returns a boolean, true if intersection exist, else false
1087  *
1088  *  Since the boxes are already aligned so we don't need Transformation matrices
1089  *  for the intersection detection algorithm.
1090  *                                  _
1091  *  input : 1. lowercornerFirstBox   |__ Extent of First Aligned UnplacedBox in mother's reference frame.
1092  *          2. uppercornerFirstBox  _|
1093  *                                  _
1094  *          3. lowercornerSecondBox  |__ Extent of Second Aligned UnplacedBox in mother's reference frame.
1095  *          4. uppercornerSecondBox _|
1096  *
1097  *  output :  Return a boolean, true if intersection exists, otherwise false.
1098  *
1099  */
1100 VECGEOM_FORCE_INLINE
1101 bool IntersectionExist(Vector3D<Precision> const lowercornerFirstBox, Vector3D<Precision> const uppercornerFirstBox,
1102                        Vector3D<Precision> const lowercornerSecondBox, Vector3D<Precision> const uppercornerSecondBox)
1103 {
1104 
1105   // Simplest algorithm
1106   // Needs to handle a total of 6 cases
1107 
1108   // Case 1: First Box is on left of Second Box
1109   if (uppercornerFirstBox.x() < lowercornerSecondBox.x()) return false;
1110 
1111   // Case 2: First Box is on right of Second Box
1112   if (lowercornerFirstBox.x() > uppercornerSecondBox.x()) return false;
1113 
1114   // Case 3: First Box is back side
1115   if (uppercornerFirstBox.y() < lowercornerSecondBox.y()) return false;
1116 
1117   // Case 4: First Box is front side
1118   if (lowercornerFirstBox.y() > uppercornerSecondBox.y()) return false;
1119 
1120   // Case 5: First Box is below the Second Box
1121   if (uppercornerFirstBox.z() < lowercornerSecondBox.z()) return false;
1122 
1123   // Case 6: First Box is above the Second Box
1124   if (lowercornerFirstBox.z() > uppercornerSecondBox.z()) return false;
1125 
1126   return true; // boxes overlap
1127 }
1128 
1129 /** This function will detect whether two boxes in arbitrary orientation intersects or not.
1130  *  returns a boolean, true if intersection exist, else false
1131  *
1132  *  Logic is implemented using Separation Axis Theorem (SAT) for 3D
1133  *                                  _
1134  *  input : 1. lowercornerFirstBox   |__ Extent of First UnplacedBox in mother's reference frame.
1135  *          2. uppercornerFirstBox  _|
1136  *                                  _
1137  *          3. lowercornerSecondBox  |__ Extent of Second UnplacedBox in mother's reference frame.
1138  *          4. uppercornerSecondBox _|
1139  *                                  _
1140  *          5. transformFirstBox     |__ Transformation matrix of First and Second Unplaced Box
1141  *          6. transformSecondBox   _|
1142  *
1143  *  output :  Return a boolean, true if intersection exists, otherwise false.
1144  */
1145 VECGEOM_FORCE_INLINE
1146 bool IntersectionExist(Vector3D<Precision> const lowercornerFirstBox, Vector3D<Precision> const uppercornerFirstBox,
1147                        Vector3D<Precision> const lowercornerSecondBox, Vector3D<Precision> const uppercornerSecondBox,
1148                        Transformation3D const *transformFirstBox, Transformation3D const *transformSecondBox, bool aux)
1149 {
1150 
1151   // Required variables
1152   Precision halfAx, halfAy, halfAz; // Half lengths of box A
1153   Precision halfBx, halfBy, halfBz; // Half lengths of box B
1154 
1155   halfAx = std::fabs(uppercornerFirstBox.x() - lowercornerFirstBox.x()) / 2.;
1156   halfAy = std::fabs(uppercornerFirstBox.y() - lowercornerFirstBox.y()) / 2.;
1157   halfAz = std::fabs(uppercornerFirstBox.z() - lowercornerFirstBox.z()) / 2.;
1158 
1159   halfBx = std::fabs(uppercornerSecondBox.x() - lowercornerSecondBox.x()) / 2.;
1160   halfBy = std::fabs(uppercornerSecondBox.y() - lowercornerSecondBox.y()) / 2.;
1161   halfBz = std::fabs(uppercornerSecondBox.z() - lowercornerSecondBox.z()) / 2.;
1162 
1163   Vector3D<Precision> pA = transformFirstBox->InverseTransform(Vector3D<Precision>(0, 0, 0));
1164   Vector3D<Precision> pB = transformSecondBox->InverseTransform(Vector3D<Precision>(0, 0, 0));
1165   Vector3D<Precision> T  = pB - pA;
1166 
1167   Vector3D<Precision> Ax = transformFirstBox->InverseTransformDirection(Vector3D<Precision>(1., 0., 0.));
1168   Vector3D<Precision> Ay = transformFirstBox->InverseTransformDirection(Vector3D<Precision>(0., 1., 0.));
1169   Vector3D<Precision> Az = transformFirstBox->InverseTransformDirection(Vector3D<Precision>(0., 0., 1.));
1170 
1171   Vector3D<Precision> Bx = transformSecondBox->InverseTransformDirection(Vector3D<Precision>(1., 0., 0.));
1172   Vector3D<Precision> By = transformSecondBox->InverseTransformDirection(Vector3D<Precision>(0., 1., 0.));
1173   Vector3D<Precision> Bz = transformSecondBox->InverseTransformDirection(Vector3D<Precision>(0., 0., 1.));
1174 
1175   /** Needs to handle total 15 cases for 3D.
1176    *   Literature can be found at following link
1177    *   http://www.jkh.me/files/tutorials/Separating%20Axis%20Theorem%20for%20Oriented%20Bounding%20Boxes.pdf
1178    */
1179 
1180   // Case 1:
1181   // L = Ax
1182   // std::cerr<<" 1 : "<<std::fabs(T.Dot(Ax))<<" :: 2 : "<<(halfAx + std::fabs(halfBx*Ax.Dot(Bx)) +
1183   // std::fabs(halfBy*Ax.Dot(By)) + std::fabs(halfBz*Ax.Dot(Bz)) )<<std::endl;
1184   if (std::fabs(T.Dot(Ax)) >
1185       (halfAx + std::fabs(halfBx * Ax.Dot(Bx)) + std::fabs(halfBy * Ax.Dot(By)) + std::fabs(halfBz * Ax.Dot(Bz)))) {
1186     return false;
1187   }
1188 
1189   // Case 2:
1190   // L = Ay
1191   if (std::fabs(T.Dot(Ay)) >
1192       (halfAy + std::fabs(halfBx * Ay.Dot(Bx)) + std::fabs(halfBy * Ay.Dot(By)) + std::fabs(halfBz * Ay.Dot(Bz)))) {
1193     return false;
1194   }
1195 
1196   // Case 3:
1197   // L = Az
1198   if (std::fabs(T.Dot(Az)) >
1199       (halfAz + std::fabs(halfBx * Az.Dot(Bx)) + std::fabs(halfBy * Az.Dot(By)) + std::fabs(halfBz * Az.Dot(Bz)))) {
1200     return false;
1201   }
1202 
1203   // Case 4:
1204   // L = Bx
1205   if (std::fabs(T.Dot(Bx)) >
1206       (halfBx + std::fabs(halfAx * Ax.Dot(Bx)) + std::fabs(halfAy * Ay.Dot(Bx)) + std::fabs(halfAz * Az.Dot(Bx)))) {
1207     return false;
1208   }
1209 
1210   // Case 5:
1211   // L = By
1212   if (std::fabs(T.Dot(By)) >
1213       (halfBy + std::fabs(halfAx * Ax.Dot(By)) + std::fabs(halfAy * Ay.Dot(By)) + std::fabs(halfAz * Az.Dot(By)))) {
1214     return false;
1215   }
1216 
1217   // Case 6:
1218   // L = Bz
1219   if (std::fabs(T.Dot(Bz)) >
1220       (halfBz + std::fabs(halfAx * Ax.Dot(Bz)) + std::fabs(halfAy * Ay.Dot(Bz)) + std::fabs(halfAz * Az.Dot(Bz)))) {
1221     return false;
1222   }
1223 
1224   // Case 7:
1225   // L = Ax X Bx
1226   if ((std::fabs(T.Dot(Az) * Ay.Dot(Bx) - T.Dot(Ay) * Az.Dot(Bx))) >
1227       (std::fabs(halfAy * Az.Dot(Bx)) + std::fabs(halfAz * Ay.Dot(Bx)) + std::fabs(halfBy * Ax.Dot(Bz)) +
1228        std::fabs(halfBz * Ax.Dot(By)))) {
1229     return false;
1230   }
1231 
1232   // Case 8:
1233   // L = Ax X By
1234   if ((std::fabs(T.Dot(Az) * Ay.Dot(By) - T.Dot(Ay) * Az.Dot(By))) >
1235       (std::fabs(halfAy * Az.Dot(By)) + std::fabs(halfAz * Ay.Dot(By)) + std::fabs(halfBx * Ax.Dot(Bz)) +
1236        std::fabs(halfBz * Ax.Dot(Bx)))) {
1237     return false;
1238   }
1239 
1240   // Case 9:
1241   // L = Ax X Bz
1242   if ((std::fabs(T.Dot(Az) * Ay.Dot(Bz) - T.Dot(Ay) * Az.Dot(Bz))) >
1243       (std::fabs(halfAy * Az.Dot(Bz)) + std::fabs(halfAz * Ay.Dot(Bz)) + std::fabs(halfBx * Ax.Dot(By)) +
1244        std::fabs(halfBy * Ax.Dot(Bx)))) {
1245     return false;
1246   }
1247 
1248   // Case 10:
1249   // L = Ay X Bx
1250   if ((std::fabs(T.Dot(Ax) * Az.Dot(Bx) - T.Dot(Az) * Ax.Dot(Bx))) >
1251       (std::fabs(halfAx * Az.Dot(Bx)) + std::fabs(halfAz * Ax.Dot(Bx)) + std::fabs(halfBy * Ay.Dot(Bz)) +
1252        std::fabs(halfBz * Ay.Dot(By)))) {
1253     return false;
1254   }
1255 
1256   // Case 11:
1257   // L = Ay X By
1258   if ((std::fabs(T.Dot(Ax) * Az.Dot(By) - T.Dot(Az) * Ax.Dot(By))) >
1259       (std::fabs(halfAx * Az.Dot(By)) + std::fabs(halfAz * Ax.Dot(By)) + std::fabs(halfBx * Ay.Dot(Bz)) +
1260        std::fabs(halfBz * Ay.Dot(Bx)))) {
1261     return false;
1262   }
1263 
1264   // Case 12:
1265   // L = Ay X Bz
1266   if ((std::fabs(T.Dot(Ax) * Az.Dot(Bz) - T.Dot(Az) * Ax.Dot(Bz))) >
1267       (std::fabs(halfAx * Az.Dot(Bz)) + std::fabs(halfAz * Ax.Dot(Bz)) + std::fabs(halfBx * Ay.Dot(By)) +
1268        std::fabs(halfBy * Ay.Dot(Bx)))) {
1269     return false;
1270   }
1271 
1272   // Case 13:
1273   // L = Az X Bx
1274   if ((std::fabs(T.Dot(Ay) * Ax.Dot(Bx) - T.Dot(Ax) * Ay.Dot(Bx))) >
1275       (std::fabs(halfAx * Ay.Dot(Bx)) + std::fabs(halfAy * Ax.Dot(Bx)) + std::fabs(halfBy * Az.Dot(Bz)) +
1276        std::fabs(halfBz * Az.Dot(By)))) {
1277     return false;
1278   }
1279 
1280   // Case 14:
1281   // L = Az X By
1282   if ((std::fabs(T.Dot(Ay) * Ax.Dot(By) - T.Dot(Ax) * Ay.Dot(By))) >
1283       (std::fabs(halfAx * Ay.Dot(By)) + std::fabs(halfAy * Ax.Dot(By)) + std::fabs(halfBx * Az.Dot(Bz)) +
1284        std::fabs(halfBz * Az.Dot(Bx)))) {
1285     return false;
1286   }
1287 
1288   // Case 15:
1289   // L = Az X Bz
1290   if ((std::fabs(T.Dot(Ay) * Ax.Dot(Bz) - T.Dot(Ax) * Ay.Dot(Bz))) >
1291       (std::fabs(halfAx * Ay.Dot(Bz)) + std::fabs(halfAy * Ax.Dot(Bz)) + std::fabs(halfBx * Az.Dot(By)) +
1292        std::fabs(halfBy * Az.Dot(Bx)))) {
1293     return false;
1294   }
1295 
1296   return true;
1297 }
1298 
1299 /// generates regularly spaced surface points on each face of a box
1300 /// npointsperline : number of points on each 1D line (there will be a total of
1301 /// 6 * pointsperline * pointsperline + 1 non-degenerate points with the corner points being
1302 /// included degenerate
1303 template <typename T>
1304 void GenerateRegularSurfacePointsOnBox(Vector3D<T> const &lower, Vector3D<T> const &upper, int pointsperline,
1305                                        std::vector<Vector3D<T>> &points)
1306 {
1307   const auto lengthvector = upper - lower;
1308   const auto delta        = lengthvector / (1. * pointsperline);
1309 
1310   // face y-z at x =y -L and x = +L
1311   for (int ny = 0; ny < pointsperline; ++ny) {
1312     const auto y = lower.y() + delta.y() * ny;
1313     for (int nz = 0; nz < pointsperline; ++nz) {
1314       const auto z = lower.z() + delta.z() * nz;
1315       Vector3D<T> p1(lower.x(), y, z);
1316       Vector3D<T> p2(upper.x(), y, z);
1317       points.push_back(p1);
1318       points.push_back(p2);
1319     }
1320   }
1321   // face x-z at y=-L and y=+L
1322   for (int nx = 0; nx < pointsperline; ++nx) {
1323     const auto x = lower.x() + delta.x() * nx;
1324     for (int nz = 0; nz < pointsperline; ++nz) {
1325       const auto z = lower.z() + delta.z() * nz;
1326       Vector3D<T> p1(x, lower.y(), z);
1327       Vector3D<T> p2(x, upper.y(), z);
1328       points.push_back(p1);
1329       points.push_back(p2);
1330     }
1331   }
1332   // face x-y at z=-L and z=+L
1333   for (int nx = 0; nx < pointsperline; ++nx) {
1334     const auto x = lower.x() + delta.x() * nx;
1335     for (int ny = 0; ny < pointsperline; ++ny) {
1336       const auto y = lower.y() + delta.y() * ny;
1337       Vector3D<T> p1(x, y, lower.z());
1338       Vector3D<T> p2(x, y, upper.z());
1339       points.push_back(p1);
1340       points.push_back(p2);
1341     }
1342   }
1343   points.push_back(upper);
1344 }
1345 
1346 } // end namespace volumeUtilities
1347 } // namespace VECGEOM_IMPL_NAMESPACE
1348 } // namespace vecgeom
1349 
1350 #endif /* VOLUME_UTILITIES_H_ */