Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:26:16

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