Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-03 08:44:45

0001 // SPDX-FileCopyrightText: 2020 CERN
0002 // SPDX-License-Identifier: Apache-2.0
0003 
0004 /**
0005  * @file BVHNavigator.h
0006  * @brief Navigation methods for geometry.
0007  */
0008 
0009 #ifndef BVH_NAVIGATOR_H
0010 #define BVH_NAVIGATOR_H
0011 
0012 #include <VecGeom/base/Global.h>
0013 #include <VecGeom/base/Vector3D.h>
0014 #include <VecGeom/navigation/NavigationState.h>
0015 #include <VecGeom/volumes/LogicalVolume.h>
0016 #include <VecGeom/management/BVHManager.h>
0017 #include <VecGeom/management/GeoManager.h>
0018 
0019 #ifdef VECGEOM_ENABLE_CUDA
0020 #include <VecGeom/backend/cuda/Interface.h>
0021 #endif
0022 
0023 namespace vecgeom {
0024 
0025 class BVHNavigator {
0026 
0027 // to avoid changing the user code via templates, the precision is chosen at compile-time in the BVHManager and
0028 // BVHNavigator
0029 #ifdef VECGEOM_BVH_SINGLE
0030   using Real_t = float;
0031 #else
0032   using Real_t = double;
0033 #endif
0034 
0035 public:
0036   static constexpr Precision kBoundaryPush = 10 * vecgeom::kTolerance;
0037 
0038   /*
0039    * @param[in] aLVIndex Global index of a LogicalVolume
0040    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0041    * @returns The PlacedVolume defined by @p aLVIndex and @p index
0042    */
0043   VECCORE_ATT_HOST_DEVICE
0044   static VECGEOM_FORCE_INLINE Daughter GetPlacedVolume(int aLVIndex, int index)
0045   {
0046 #ifdef VECCORE_CUDA_DEVICE_COMPILATION
0047     VECGEOM_VALIDATE(vecgeom::globaldevicegeomdata::gDeviceLogicalVolumes != nullptr,
0048                      << "Logical volumes not copied to device");
0049     return vecgeom::globaldevicegeomdata::gDeviceLogicalVolumes[aLVIndex].GetDaughters()[index];
0050 #else
0051 #ifndef VECCORE_CUDA
0052     return vecgeom::GeoManager::Instance().GetLogicalVolume(aLVIndex)->GetDaughters()[index];
0053 #else
0054     // this is the case when we compile with nvcc for host side
0055     VECGEOM_VALIDATE(false, << "reached unimplement code");
0056     (void)index; // avoid unused parameter warning.
0057     (void)aLVIndex;
0058     return nullptr;
0059 #endif
0060 #endif
0061   }
0062 
0063   /*
0064    * @param[in] global_index Global index of a PlacedVolume
0065    * @returns The PlacedVolume with global index @p global_index
0066    */
0067   VECCORE_ATT_HOST_DEVICE
0068   static VECGEOM_FORCE_INLINE vecgeom::VPlacedVolume *GetPlacedVolume(int global_index)
0069   {
0070 #ifdef VECCORE_CUDA_DEVICE_COMPILATION
0071     VECGEOM_VALIDATE(vecgeom::globaldevicegeomdata::gCompactPlacedVolBuffer != nullptr,
0072                      << "Placed volumes not copied to device");
0073     return &vecgeom::globaldevicegeomdata::gCompactPlacedVolBuffer[global_index];
0074 #else
0075 #ifndef VECCORE_CUDA
0076     return vecgeom::GeoManager::Instance().GetPlacedVolume(global_index);
0077 #else
0078     // this is the case when we compile with nvcc for host side
0079     VECGEOM_VALIDATE(false, << "reached unimplement code");
0080     (void)global_index; // avoid unused parameter warning.
0081     return nullptr;
0082 #endif
0083 #endif
0084   }
0085 
0086   /*
0087    * @param[in] aLVIndex Global index of a LogicalVolume
0088    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0089    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0090    * @returns The safety to in to the PlacedVolume defined by @p aLVIndex and @p index for the point @p localpoint
0091    */
0092   VECCORE_ATT_HOST_DEVICE
0093   static Precision CandidateSafetyToIn(int aLVIndex, int index, Vector3D<Precision> localpoint)
0094   {
0095     return GetPlacedVolume(aLVIndex, index)->SafetyToIn(localpoint);
0096   };
0097 
0098   /*
0099    * @param[in] aLVIndex Global index of a LogicalVolume
0100    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0101    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0102    * @param[in] localdir Direction in the local coordinates of the LV specified by @aLVIndex
0103    * @param[in] step Maximum step length
0104    * @returns The distance to in to the PlacedVolume defined by @p aLVIndex and @p index for the point @p localpoint
0105    * and direction @p localdir
0106    */
0107   VECCORE_ATT_HOST_DEVICE
0108   static Precision CandidateDistanceToIn(int aLVIndex, int index, Vector3D<Precision> localpoint,
0109                                          Vector3D<Precision> localdir, Precision step)
0110   {
0111     Daughter vol = GetPlacedVolume(aLVIndex, index);
0112     return vol->DistanceToIn(localpoint, localdir, step);
0113   };
0114 
0115   /*
0116    * @param[in] aLVIndex Global index of a LogicalVolume
0117    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0118    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0119    * @param[out] daughterlocalpoint Point in the local coordinates of the PlacedVolume defined by
0120    * @p aLVIndex and @p index
0121    * @returns Whether @localpoint falls within the PlacedVolume defined by @p aLVIndex and @p index
0122    */
0123   VECCORE_ATT_HOST_DEVICE
0124   static vecgeom::Inside_t CandidateInside(int aLVIndex, int index, Vector3D<Precision> const &localpoint,
0125                                            Vector3D<Precision> &daughterlocalpoint)
0126   {
0127     auto daughter      = GetPlacedVolume(aLVIndex, index);
0128     daughterlocalpoint = daughter->GetTransformation()->Transform<Precision>(localpoint);
0129     return daughter->GetUnplacedVolume()->Inside(daughterlocalpoint);
0130   };
0131 
0132   /*
0133    * @param[in] aLVIndex Global index of a LogicalVolume
0134    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0135    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0136    * @param[out] daughterlocalpoint Point in the local coordinates of the PlacedVolume defined by
0137    * @p aLVIndex and @p index
0138    * @returns Whether @localpoint falls within the PlacedVolume defined by @p aLVIndex and @p index
0139    */
0140   VECCORE_ATT_HOST_DEVICE
0141   static bool CandidateContains(int aLVIndex, int index, Vector3D<Precision> const &localpoint,
0142                                 Vector3D<Precision> &daughterlocalpoint)
0143   {
0144     auto inside = CandidateInside(aLVIndex, index, localpoint, daughterlocalpoint);
0145     return inside != EnumInside::kOutside;
0146   };
0147 
0148   /*
0149    * @param[in] aLVIndex Global index of a LogicalVolume
0150    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0151    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0152    * @param[in] localdir Direction in the local coordinates of the LV specified by @aLVIndex
0153    * @returns The distance to in to the Bounding Box of the PlacedVolume defined by @p aLVIndex
0154    * and @p index for the point @p localpoint and direction @p localdir
0155    */
0156   VECCORE_ATT_HOST_DEVICE
0157   static Precision CandidateApproachSolid(int aLVIndex, int index, Vector3D<Precision> localpoint,
0158                                           Vector3D<Precision> localdir)
0159   {
0160     auto vol                            = GetPlacedVolume(aLVIndex, index);
0161     vecgeom::Transformation3D const *tr = vol->GetTransformation();
0162     Vector3D<Precision> pv_localpoint   = tr->Transform(localpoint);
0163     Vector3D<Precision> pv_localdir     = tr->TransformDirection(localdir);
0164     Vector3D<Precision> pv_invlocaldir(1.0 / vecgeom::NonZero(pv_localdir[0]), 1.0 / vecgeom::NonZero(pv_localdir[1]),
0165                                        1.0 / vecgeom::NonZero(pv_localdir[2]));
0166     return vol->GetUnplacedVolume()->ApproachSolid(pv_localpoint, pv_invlocaldir);
0167   };
0168 
0169   /*
0170    * Used by the BVH to determine if it needs to skip checking a placed volume. The global index of the volume
0171    * defined by @p aLVIndex and @p index can only be accessed from the navigator
0172    * @param[in] aLVIndex Global index of a LogicalVolume
0173    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0174    * @param[in] global_id Global id of a PLacedVolume
0175    * @returns Whether the global id of the PlacedVolume defined by @p aLVIndex and @p index is the same as @p global_id
0176    */
0177   VECCORE_ATT_HOST_DEVICE
0178   static VECGEOM_FORCE_INLINE bool SkipItem(int aLVIndex, int index, long const global_id)
0179   {
0180     return (global_id == GetPlacedVolume(aLVIndex, index)->id());
0181   }
0182 
0183   VECCORE_ATT_HOST_DEVICE
0184   static long TestBVHCheckDaughterIntersections(const vecgeom::BVH<Real_t> &bvh, Vector3D<Precision> &localpoint,
0185                                                 Vector3D<Precision> &localdir, Precision &bvhstep)
0186   {
0187     long hitcandidate_index = -1;
0188     long last_exited_id     = -1;
0189     bvh.CheckDaughterIntersections<BVHNavigator, Precision>(localpoint, localdir, bvhstep, last_exited_id,
0190                                                             hitcandidate_index);
0191     return hitcandidate_index;
0192   }
0193 
0194   /*
0195    * @param[in] aLVIndex Global index of a LogicalVolume
0196    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0197    * @returns The global id of the PlacedVolume defined by @p aLVIndex and @p index
0198    */
0199   VECCORE_ATT_HOST_DEVICE
0200   static uint ItemId(int aLVIndex, int index) { return GetPlacedVolume(aLVIndex, index)->id(); }
0201 
0202   /**
0203    * @brief Locate the deepest daughter volume that contains a point, starting from a given placed volume.
0204    *
0205    * ## Coordinate frames
0206    * - **Input point** @p point is in the **parent frame of @p vol** (i.e. the same frame expected by
0207    *   `VPlacedVolume::Inside` for @p vol).
0208    * - The NavigationState @p path should point to the **parent frame of @p vol**.
0209    * @param[in]  vol
0210    *   The starting placed volume (the mother at which the descent begins). Must be non-null if @p top is true.
0211    * @param[in]  point
0212    *   Query point **in the parent frame of @p vol**.
0213    * @param[in,out] path
0214    *   Navigation state to be filled. Must start at the  **parent frame of @p vol**.
0215    * @param[in]  top
0216    *   If true, the function first validates that the point is inside @p vol. If false, a nullptr is returned
0217    * @param[in]  exclude
0218    *   Optional placed volume to exclude once during the descent (useful to avoid immediately
0219    *   re-entering a volume you just exited). The exclusion is applied only to the first BVH
0220    *   query and then cleared.
0221    * @return
0222    *   The deepest placed volume that contains the point, or `nullptr` if @p top is true and
0223    *   `vol->Inside(point)` reports outside. When the descent stops at @p vol (i.e. no daughter
0224    *   contains the point), returns @p vol.
0225    */
0226   VECCORE_ATT_HOST_DEVICE
0227   static Daughter LocatePointIn(vecgeom::VPlacedVolume const *vol, Vector3D<Precision> const &point,
0228                                 vecgeom::NavigationState &path, bool top,
0229                                 vecgeom::VPlacedVolume const *exclude = nullptr)
0230   {
0231 
0232     // optional check whether the point is inside the provided placed volume vol
0233     if (top) {
0234       // Must check the provided volume
0235       VECGEOM_ASSERT(vol != nullptr);
0236       auto inside = vol->Inside(point);
0237       if (inside == kOutside) return nullptr;
0238       // Set the boundary state to the path
0239       if (inside == kSurface) path.SetBoundaryState(true);
0240     }
0241 
0242     path.Push(vol);
0243 
0244     // transform the point into the reference frame of the `vol`
0245     Vector3D<Precision> currentpoint(vol->GetTransformation()->Transform<Precision>(point));
0246     Vector3D<Precision> daughterlocalpoint;
0247     long exclude_id = -1;
0248     long vol_id     = -1;
0249 
0250     for (auto v = vol; v->GetDaughters().size() > 0;) {
0251       auto bvh = vecgeom::BVHManager::GetBVH(v->GetLogicalVolume()->id());
0252 
0253       exclude_id = -1;
0254       if (exclude != nullptr) {
0255         exclude_id = exclude->id();
0256       }
0257       vol_id = -1;
0258 
0259       auto inside = bvh->LevelInside<BVHNavigator>(exclude_id, currentpoint, vol_id, daughterlocalpoint);
0260       if (inside == kOutside) break;
0261       if (inside == kSurface) path.SetBoundaryState(true);
0262 
0263       currentpoint = daughterlocalpoint;
0264       // Update the current volume v
0265       v = GetPlacedVolume(vol_id);
0266       path.Push(v);
0267 
0268       // Only exclude the placed volume once since we could enter it again via a
0269       // different volume history.
0270       exclude = nullptr;
0271     }
0272 
0273     return path.Top();
0274   }
0275 
0276   /**
0277    * @brief Find new final NavigationState after a NavigationState has just been left (i.e., after exiting its corresponding top volume)
0278    * The function will climb up the NavigationStates until the point is inside, and then descend again to find the
0279    * deepest volume containing the point. The just exited volume will be skipped
0280    * @param[in]  localpoint   Point in the **local frame of the navigation state (path.Top())**
0281    * @param[in,out] path      Navigation state of the volume that was just left; updated to the new deepest containing path.
0282    * @return The deepest placed volume that contains the point (after re-location), or nullptr if no mother exists.
0283    */
0284   VECCORE_ATT_HOST_DEVICE
0285   static Daughter RelocatePoint(Vector3D<Precision> const &localpoint, vecgeom::NavigationState &path)
0286   {
0287     vecgeom::VPlacedVolume const *currentmother = path.Top();
0288     Daughter skip                               = nullptr;
0289     Vector3D<Precision> transformed             = localpoint;
0290     // continue to climb up the Navigation tree until a volume is found which contains the volume
0291     do {
0292       skip = currentmother;
0293       path.Pop();
0294       transformed   = currentmother->GetTransformation()->InverseTransform(transformed);
0295       currentmother = path.Top();
0296     } while (currentmother && (currentmother->IsAssembly() || !currentmother->UnplacedContains(transformed)));
0297 
0298     // first volume up the hierarchy is found that contains the point. Now descend to find the deepest state that
0299     // contains the point
0300     if (currentmother) {
0301       return LocatePointInNavState(transformed, path, false, skip);
0302     }
0303     return currentmother;
0304   }
0305 
0306   /**
0307    * @brief Update NavState path to the deepest state that contains given point starting from the given path.
0308    * @param[in]  localpoint
0309    *   localpoint **in the reference frame of @p path.**.
0310    * @param[in,out] path
0311    *   Starting NavigationState; output of the final NavigationState
0312    * @param[in]  top
0313    *   If true, the function first validates that the point is inside @p path. If false, a nullptr is returned
0314    * @param[in]  exclude
0315    *   Optional placed volume to exclude once during the descent
0316    * @return
0317    *   The final top-volume of the NavState after finding the deepest NavState that contains the point
0318    */
0319   VECCORE_ATT_HOST_DEVICE
0320   static Daughter LocatePointInNavState(Vector3D<Precision> const &localpoint, vecgeom::NavigationState &path, bool top,
0321                                         vecgeom::VPlacedVolume const *exclude = nullptr)
0322   {
0323 
0324     VECGEOM_ASSERT(path.Top() != nullptr);
0325 
0326     auto currentLogical = path.Top()->GetLogicalVolume();
0327     VECGEOM_ASSERT(currentLogical != nullptr);
0328 
0329     // optional check if point is inside current NavState's top volume
0330     if (top) {
0331       auto inside = currentLogical->GetUnplacedVolume()->Inside(localpoint);
0332       if (inside == kOutside) return nullptr;
0333       // Set the boundary state to the path
0334       if (inside == kSurface) path.SetBoundaryState(true);
0335     }
0336 
0337     Vector3D<Precision> currentpoint(localpoint);
0338     Vector3D<Precision> daughterlocalpoint;
0339     // exclude->id() returns unsigned int, but LevelInside takes a long as an input
0340     long exclude_id = exclude ? static_cast<long>(exclude->id()) : -1;
0341     long pvol_id    = -1;
0342 
0343     while (currentLogical->GetDaughters().size() > 0) {
0344       auto *bvh = vecgeom::BVHManager::GetBVH(currentLogical->id());
0345 
0346       auto inside = bvh->LevelInside<BVHNavigator>(exclude_id, currentpoint, pvol_id, daughterlocalpoint);
0347 
0348       if (inside == kOutside) break; // no containing daughter here
0349       if (inside == kSurface) path.SetBoundaryState(true);
0350 
0351       auto *placed = GetPlacedVolume(pvol_id);
0352       path.Push(placed);
0353 
0354       // Prepare for next level: switch to daughter's logical volume + daughter-local point
0355       currentpoint   = daughterlocalpoint;
0356       currentLogical = placed->GetLogicalVolume();
0357 
0358       // Only exclude the placed volume once since we could enter it again via a
0359       // different volume history.
0360       exclude_id = -1;
0361       exclude    = nullptr;
0362     }
0363 
0364     return path.Top();
0365   }
0366 
0367 private:
0368   // Computes a step in the current volume from the localpoint into localdir,
0369   // taking step_limit into account. If a volume is hit, the function calls
0370   // out_state.SetBoundaryState(true) and hitcandidate is set to the hit
0371   // daughter volume, or kept unchanged if the current volume is left.
0372   VECCORE_ATT_HOST_DEVICE
0373   static Precision ComputeStepAndHit(Vector3D<Precision> const &localpoint, Vector3D<Precision> const &localdir,
0374                                      Precision step_limit, vecgeom::NavigationState const &in_state,
0375                                      vecgeom::NavigationState &out_state, Daughter &hitcandidate)
0376   {
0377     in_state.CopyTo(&out_state);
0378     // Just return unchanged current state if the step limit is null or invalid
0379     if (step_limit <= 0) return 0.;
0380 
0381     Precision step = step_limit;
0382     Daughter pvol  = in_state.Top();
0383     // Daughter last_exited = in_state.GetLastExited();
0384     long hitcandidate_index = -1;
0385     long last_exited_id     = -1;
0386 
0387     // need to calc DistanceToOut first
0388     step = pvol->DistanceToOut(localpoint, localdir, step_limit);
0389     // This should never happen as DistanceToOut should never return infinity (TBC)
0390     if (step == vecgeom::kInfLength) step = 0.;
0391     // Most solids ignore step_limit
0392     step = Min(step, step_limit);
0393     // Boundary very close or already outside
0394     if (step < kTolerance) {
0395       step = Max(0., step);
0396       // Even if the point is outside the volume, we have to set the boundary flag to force relocation
0397       out_state.SetBoundaryState(true);
0398       return step;
0399     }
0400 
0401     // Now distance to children
0402     if (pvol->GetDaughters().size() > 0) {
0403       auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0404 
0405       hitcandidate_index = -1;
0406       // id is an uint, however we use a long in order to be able to fit the full uint range, and -1 in case there is no
0407       // last exited volume in the navigation state.
0408       last_exited_id = -1;
0409       // if (last_exited != nullptr) last_exited_id = last_exited->id();
0410 
0411       bvh->CheckDaughterIntersections<BVHNavigator, Precision>(localpoint, localdir, step, last_exited_id,
0412                                                                hitcandidate_index);
0413 
0414       if (hitcandidate_index >= 0) {
0415         // A child was hit within the step_limit
0416         step         = Max(0., step);
0417         hitcandidate = pvol->GetLogicalVolume()->GetDaughters()[hitcandidate_index];
0418         out_state.SetBoundaryState(true);
0419         return step;
0420       }
0421     }
0422 
0423     // Is geometry further away than physics step?
0424     if (step >= step_limit) {
0425       // Then this is a phyics step and we don't need to do anything.
0426       out_state.SetBoundaryState(false);
0427       return step_limit;
0428     }
0429 
0430     // Otherwise it is a geometry step and we push the point to the boundary.
0431     out_state.SetBoundaryState(true);
0432     return step;
0433   }
0434 
0435   // Computes a step in the current volume from the localpoint into localdir,
0436   // until the next daughter bounding box, taking step_limit into account.
0437   VECCORE_ATT_HOST_DEVICE
0438   static Precision ApproachNextVolume(Vector3D<Precision> const &localpoint, Vector3D<Precision> const &localdir,
0439                                       Precision step_limit, vecgeom::NavigationState const &in_state)
0440   {
0441     Precision step = step_limit;
0442     Daughter pvol  = in_state.Top();
0443     // Daughter last_exited = in_state.GetLastExited();
0444 
0445     if (pvol->GetDaughters().size() > 0) {
0446       auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0447 
0448       // id is an uint, however we use a long in order to be able to fit the full uint range, and -1 in case there is no
0449       // last exited volume in the navigation state.
0450       long last_exited_id = -1;
0451       // if (last_exited != nullptr) last_exited_id = last_exited->id();
0452 
0453       bvh->ApproachNextDaughter<BVHNavigator>(localpoint, localdir, step, last_exited_id);
0454       // Make sure we don't "step" on next boundary
0455       step -= 10 * vecgeom::kTolerance;
0456     }
0457 
0458     if (step == vecgeom::kInfLength && step_limit > 0) return 0;
0459 
0460     // Is geometry further away than physics step?
0461     if (step > step_limit) {
0462       // Then this is a phyics step and we don't need to do anything.
0463       return step_limit;
0464     }
0465 
0466     if (step < 0) {
0467       step = 0;
0468     }
0469 
0470     return step;
0471   }
0472 
0473 public:
0474   // Computes the isotropic safety from the globalpoint. The safety must be accurate only below the provided limit.
0475   VECCORE_ATT_HOST_DEVICE
0476   static Precision ComputeSafety(Vector3D<Precision> const &globalpoint, vecgeom::NavigationState const &state,
0477                                  Precision limit = InfinityLength<Precision>())
0478   {
0479     Daughter pvol = state.Top();
0480     if (pvol == nullptr) return kInfLength;
0481     vecgeom::Transformation3D m;
0482     state.TopMatrix(m);
0483     Vector3D<Precision> localpoint = m.Transform(globalpoint);
0484 
0485     // need to calc DistanceToOut first
0486     Precision safety = pvol->SafetyToOut(localpoint);
0487     limit            = Min(safety, limit);
0488 
0489     if (safety > 0 && pvol->GetDaughters().size() > 0) {
0490       auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0491       safety   = bvh->ComputeSafety<BVHNavigator>(localpoint, safety, limit);
0492     }
0493 
0494     return safety;
0495   }
0496 
0497   // Computes a step from the globalpoint (which must be in the current volume)
0498   // into globaldir, taking step_limit into account. If a volume is hit, the
0499   // function calls out_state.SetBoundaryState(true) and relocates the state to
0500   // the next volume.
0501   VECCORE_ATT_HOST_DEVICE
0502   static Precision ComputeStepAndPropagatedState(Vector3D<Precision> const &globalpoint,
0503                                                  Vector3D<Precision> const &globaldir, Precision step_limit,
0504                                                  vecgeom::NavigationState const &in_state,
0505                                                  vecgeom::NavigationState &out_state, Precision push = 0)
0506   {
0507     if (in_state.Top() == nullptr) return kInfLength;
0508     // If we are on the boundary, push a bit more.
0509     if (in_state.IsOnBoundary()) {
0510       push += kBoundaryPush;
0511     }
0512     if (step_limit < push) {
0513       // Go as far as the step limit says, assuming there is no boundary.
0514       // TODO: Does this make sense?
0515       in_state.CopyTo(&out_state);
0516       out_state.SetBoundaryState(false);
0517       return step_limit;
0518     }
0519     step_limit -= push;
0520 
0521     // calculate local point/dir from global point/dir
0522     Vector3D<Precision> localpoint;
0523     Vector3D<Precision> localdir;
0524     // Impl::DoGlobalToLocalTransformation(in_state, globalpoint, globaldir, localpoint, localdir);
0525     vecgeom::Transformation3D m;
0526     in_state.TopMatrix(m);
0527     localpoint = m.Transform(globalpoint);
0528     localdir   = m.TransformDirection(globaldir);
0529     // The user may want to move point from boundary before computing the step
0530     localpoint += push * localdir;
0531 
0532     Daughter hitcandidate = nullptr;
0533     Precision step        = ComputeStepAndHit(localpoint, localdir, step_limit, in_state, out_state, hitcandidate);
0534     step += push;
0535 
0536     if (out_state.IsOnBoundary()) {
0537       // Relocate the point after the step to refine out_state.
0538       localpoint += (step + kBoundaryPush) * localdir;
0539 
0540       if (!hitcandidate) {
0541         // We didn't hit a daughter but instead we're exiting the current volume.
0542         RelocatePoint(localpoint, out_state);
0543       } else {
0544         // Otherwise check if we're directly entering other daughters transitively.
0545         localpoint = hitcandidate->GetTransformation()->Transform(localpoint);
0546         LocatePointIn(hitcandidate, localpoint, out_state, false);
0547       }
0548 
0549       if (out_state.Top() != nullptr) {
0550         while (out_state.Top()->IsAssembly() || out_state.HasSamePathAsOther(in_state)) {
0551           out_state.Pop();
0552         }
0553         VECGEOM_ASSERT(!out_state.Top()->GetLogicalVolume()->GetUnplacedVolume()->IsAssembly());
0554       }
0555     }
0556 
0557     return step;
0558   }
0559 
0560   // Computes a step from the globalpoint (which must be in the current volume)
0561   // into globaldir, taking step_limit into account. If a volume is hit, the
0562   // function calls out_state.SetBoundaryState(true) and
0563   //  - removes all volumes from out_state if the current volume is left, or
0564   //  - adds the hit daughter volume to out_state if one is hit.
0565   // However the function does _NOT_ relocate the state to the next volume,
0566   // that is entering multiple volumes that share a boundary.
0567   VECCORE_ATT_HOST_DEVICE
0568   static Precision ComputeStepAndNextVolume(Vector3D<Precision> const &globalpoint,
0569                                             Vector3D<Precision> const &globaldir, Precision step_limit,
0570                                             vecgeom::NavigationState const &in_state,
0571                                             vecgeom::NavigationState &out_state, Precision push = 0)
0572   {
0573     if (in_state.Top() == nullptr) return kInfLength;
0574     // If we are on the boundary, push a bit more.
0575     if (in_state.IsOnBoundary()) {
0576       push += kBoundaryPush;
0577     }
0578     if (step_limit < push) {
0579       // Go as far as the step limit says, assuming there is no boundary.
0580       // TODO: Does this make sense?
0581       in_state.CopyTo(&out_state);
0582       if (step_limit > kTolerance) out_state.SetBoundaryState(false);
0583       return step_limit;
0584     }
0585     step_limit -= push;
0586 
0587     // calculate local point/dir from global point/dir
0588     Vector3D<Precision> localpoint;
0589     Vector3D<Precision> localdir;
0590     // Impl::DoGlobalToLocalTransformation(in_state, globalpoint, globaldir, localpoint, localdir);
0591     vecgeom::Transformation3D m;
0592     in_state.TopMatrix(m);
0593     localpoint = m.Transform(globalpoint);
0594     localdir   = m.TransformDirection(globaldir);
0595 
0596     Daughter hitcandidate = nullptr;
0597     // Avoid computing the distance from boundary by pushing the point
0598     Precision step =
0599         ComputeStepAndHit(localpoint + push * localdir, localdir, step_limit, in_state, out_state, hitcandidate);
0600     // step correction with the push distance
0601     step += (step > 0.) * push;
0602 
0603     if (out_state.IsOnBoundary()) {
0604       if (!hitcandidate) {
0605         vecgeom::VPlacedVolume const *currentmother = out_state.Top();
0606         Vector3D<Precision> transformed             = localpoint;
0607         // Push the point inside the next volume.
0608         transformed += (step + kBoundaryPush) * localdir;
0609         do {
0610           out_state.SetLastExited();
0611           out_state.Pop();
0612           transformed   = currentmother->GetTransformation()->InverseTransform(transformed);
0613           currentmother = out_state.Top();
0614         } while (currentmother &&
0615                  (currentmother->IsAssembly() || currentmother->GetUnplacedVolume()->Inside(transformed) != kInside));
0616       } else {
0617         out_state.Push(hitcandidate);
0618       }
0619     }
0620 
0621     return step;
0622   }
0623 
0624   // Computes a step from the globalpoint (which must be in the current volume)
0625   // into globaldir, taking step_limit into account.
0626   VECCORE_ATT_HOST_DEVICE
0627   static Precision ComputeStepToApproachNextVolume(Vector3D<Precision> const &globalpoint,
0628                                                    Vector3D<Precision> const &globaldir, Precision step_limit,
0629                                                    vecgeom::NavigationState const &in_state)
0630   {
0631     if (in_state.Top() == nullptr) return kInfLength;
0632     // calculate local point/dir from global point/dir
0633     Vector3D<Precision> localpoint;
0634     Vector3D<Precision> localdir;
0635     // Impl::DoGlobalToLocalTransformation(in_state, globalpoint, globaldir, localpoint, localdir);
0636     vecgeom::Transformation3D m;
0637     in_state.TopMatrix(m);
0638     localpoint = m.Transform(globalpoint);
0639     localdir   = m.TransformDirection(globaldir);
0640 
0641     Precision step = ApproachNextVolume(localpoint, localdir, step_limit, in_state);
0642 
0643     return step;
0644   }
0645 
0646   // Relocate a state that was returned from ComputeStepAndNextVolume: It
0647   // recursively locates the pushed point in the containing volume.
0648   VECCORE_ATT_HOST_DEVICE
0649   static void RelocateToNextVolume(Vector3D<Precision> const &globalpoint, Vector3D<Precision> const &globaldir,
0650                                    vecgeom::NavigationState &state)
0651   {
0652     // if already outside, don't do anything
0653     if (state.IsOutside()) return;
0654 
0655     // Push the point inside the next volume.
0656     // A.G. This should not be needed now since LocatePointIn is boundary-aware
0657     Vector3D<Precision> pushed = globalpoint /* + kBoundaryPush * globaldir*/;
0658 
0659     // Calculate local point from global point.
0660     vecgeom::Transformation3D m;
0661     state.TopMatrix(m);
0662     Vector3D<Precision> localpoint = m.Transform(pushed);
0663 
0664     // passing the state to check in + the local point in the reference frame of the state
0665     LocatePointInNavState(localpoint, state, false, state.GetLastExited());
0666 
0667     if (state.Top() != nullptr) {
0668       while (state.Top()->IsAssembly()) {
0669         state.Pop();
0670       }
0671       VECGEOM_ASSERT(!state.Top()->GetLogicalVolume()->GetUnplacedVolume()->IsAssembly());
0672     }
0673   }
0674 };
0675 
0676 } // namespace vecgeom
0677 
0678 #endif // BVH_NAVIGATOR_H