Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-17 08:35:36

0001 #ifndef BVH_SURF_NAVIGATOR_H
0002 #define BVH_SURF_NAVIGATOR_H
0003 
0004 #include <VecGeom/base/Vector3D.h>
0005 #include <VecGeom/base/BVH.h>
0006 #include <VecGeom/surfaces/Navigator.h>
0007 
0008 namespace vgbrep {
0009 namespace protonav {
0010 
0011 // Forward declaration
0012 // This is temporary, just as long as we need to call the BVH from the loop navigator for testing
0013 template <typename Real_t>
0014 VECCORE_ATT_HOST_DEVICE Real_t DistanceToLocalFS(vecgeom::Vector3D<Real_t> const &local,
0015                                                  vecgeom::Vector3D<Real_t> const &localdir, int volId,
0016                                                  SurfData<Real_t> const &surfdata,
0017                                                  FramedSurface<Real_t, TransformationMP<Real_t>> const &framedsurf,
0018                                                  bool exiting, bool &surfhit, Real_t &safety);
0019 
0020 template <typename Real_t>
0021 VECCORE_ATT_HOST_DEVICE VECGEOM_FORCE_INLINE Real_t LocalLogicSafety(vecgeom::Vector3D<Real_t> const &localpoint,
0022                                                                      bool exiting, int lvol_id,
0023                                                                      SurfData<Real_t> const &surfdata,
0024                                                                      Real_t safe_max); // Temporarily non default
0025 
0026 template <typename Real_t>
0027 VECCORE_ATT_HOST_DEVICE bool EnterCS(FSlocator &hit_frame, Vector3D<Real_t> const &point,
0028                                      Vector3D<Real_t> const &direction, Real_t hit_dist,
0029                                      Vector3D<Real_t> const &onsurf_local, FSlocator &out_frame);
0030 
0031 template <typename Real_t>
0032 VECCORE_ATT_HOST_DEVICE bool ExitCS(FSlocator &hit_frame, bool is_hit, Vector3D<Real_t> const &point,
0033                                     Vector3D<Real_t> const &direction, Real_t hit_dist,
0034                                     Vector3D<Real_t> const &onsurf_local, FSlocator &hit_FS, FSlocator &out_frame);
0035 
0036 template <typename Real_t>
0037 class BVHSurfNavigator {
0038 public:
0039   BVHSurfNavigator()  = default;
0040   ~BVHSurfNavigator() = default;
0041 
0042   /*
0043    * @param[in] lv_index Global index of a LogicalVolume
0044    * @param[in] index Index within the list of visible entering surfaces of the specified LogicalVolume
0045    * @param[in] localpoint Point in the local coordinates of the LV specified by @lv_index
0046    * @param[in] localdir Direction in the local coordinates of the LV specified by @lv_index
0047    * @param[in] step Maximum step length
0048    * @returns The distance to in to the Framed surface defined by @p lv_index and @p index for the point @p localpoint
0049    * and direction @p localdir
0050    */
0051   VECCORE_ATT_HOST_DEVICE
0052   static Real_t CandidateDistanceToIn(int lv_index, int index, Vector3D<Real_t> localpoint, Vector3D<Real_t> localdir,
0053                                       Real_t step)
0054   {
0055     auto const &surfdata = vgbrep::SurfData<Real_t>::Instance();
0056     // Get the shell for this volume
0057     auto const &shell = surfdata.fShells[lv_index];
0058 
0059     FramedSurface<Real_t, TransformationMP<Real_t>> *framed_surface;
0060     bool exiting{false};
0061 
0062     // Retrieve the candidate local surface
0063     // Different treatment for entering and exiting surfaces
0064     if (index >= shell.fNExitingSurfaces) // Entering surfaces
0065     {
0066       int entering_index = index - shell.fNExitingSurfaces;
0067       framed_surface     = &(surfdata.fLocalSurf[shell.fEnteringSurfaces[entering_index]]);
0068 
0069       // Get the transformation
0070       auto const &pvol_trans = surfdata.fPVolTrans[shell.fEnteringSurfacesPvolTrans[entering_index]];
0071 
0072       // Transform the points to the pvol frame
0073       localpoint = pvol_trans.Transform(localpoint);
0074       localdir   = pvol_trans.TransformDirection(localdir);
0075 
0076       // Update the LV index to that of the daughter
0077       lv_index = shell.fEnteringSurfacesLvolIds[entering_index];
0078 
0079     } else { // Exiting surfaces
0080       exiting            = true;
0081       auto exiting_index = shell.fExitingSurfaces[index];
0082       framed_surface     = &(surfdata.fLocalSurf[shell.fSurfaces[exiting_index]]);
0083     }
0084 
0085     // Common part for entering and exiting surfaces
0086 
0087     // Check if we intersect the unplaced and the distance
0088     Real_t intersect_distance{0};
0089     bool surfhit{false};
0090     Real_t safety;
0091     intersect_distance =
0092         DistanceToLocalFS(localpoint, localdir, lv_index, surfdata, *framed_surface, exiting, surfhit, safety);
0093     if (surfhit &&
0094         (intersect_distance > -vecgeom::kToleranceStrict<Real_t> || Abs(safety) < vecgeom::kToleranceStrict<Real_t>)) {
0095       return intersect_distance;
0096     } else {
0097       return vecgeom::InfinityLength<Real_t>();
0098     }
0099   }
0100 
0101   VECCORE_ATT_HOST_DEVICE
0102   static int GetLogicalId(int lv_index, int surfindex, bool &isBoolean)
0103   {
0104     auto const &surfdata = vgbrep::SurfData<Real_t>::Instance();
0105     auto const &shell    = surfdata.fShells[lv_index];
0106     if (surfindex >= shell.fNExitingSurfaces) {
0107       auto entering_index = surfindex - shell.fNExitingSurfaces;
0108       lv_index            = shell.fEnteringSurfacesLvolIds[entering_index];
0109       isBoolean           = surfdata.fLocalSurf[shell.fEnteringSurfaces[entering_index]].fLogicId > 0;
0110       return lv_index;
0111     }
0112     isBoolean = surfdata.fLocalSurf[shell.fSurfaces[shell.fExitingSurfaces[surfindex]]].fLogicId > 0;
0113     return lv_index;
0114   }
0115 
0116   VECCORE_ATT_HOST_DEVICE
0117   static bool IsNegated(int lv_index, int surfindex)
0118   {
0119     auto const &surfdata = vgbrep::SurfData<Real_t>::Instance();
0120     auto const &shell    = surfdata.fShells[lv_index];
0121     if (surfindex >= shell.fNExitingSurfaces)
0122       return (surfdata.fLocalSurf[shell.fEnteringSurfaces[surfindex - shell.fNExitingSurfaces]].fLogicId < 0);
0123     else
0124       return (surfdata.fLocalSurf[shell.fSurfaces[shell.fExitingSurfaces[surfindex]]].fLogicId < 0);
0125   }
0126 
0127   /*
0128    * @param[in] lv_index Global index of a LogicalVolume
0129    * @param[in] index Index within the list of visible entering surfaces of the specified LogicalVolume
0130    * @param[in] localpoint Point in the local coordinates of the LV specified by @lv_index
0131    * @param[in] limit Upper limit for the search in the minimization process (e.g. previous computed safety candidate)
0132    * @returns The safety to in to the Framed surface defined by @p lv_index and @p index for the point @p localpoint
0133    */
0134   VECCORE_ATT_HOST_DEVICE
0135   static Real_t CandidateSafetyToIn(int lv_index, int index, Vector3D<Real_t> localpoint,
0136                                     Real_t limit = vecgeom::InfinityLength<Real_t>())
0137   {
0138     auto const &surfdata = vgbrep::SurfData<Real_t>::Instance();
0139     // Get the shell for this volume
0140     auto const &shell = surfdata.fShells[lv_index];
0141 
0142     Vector3D<Real_t> surface_point;
0143     Vector3D<Real_t> surface_dir;
0144     FramedSurface<Real_t, TransformationMP<Real_t>> *framed_surface;
0145     bool exiting{false};
0146 
0147     // Retrieve the candidate local surface
0148     // Different treatment for entering and exiting surfaces
0149     if (index >= shell.fNExitingSurfaces) // Entering surfaces
0150     {
0151       int entering_index = index - shell.fNExitingSurfaces;
0152       framed_surface     = &(surfdata.fLocalSurf[shell.fEnteringSurfaces[entering_index]]);
0153       // Update the LV index to that of the daughter
0154       lv_index = shell.fEnteringSurfacesLvolIds[entering_index];
0155 
0156       // Get the transformation
0157       auto const &pvol_trans = surfdata.fPVolTrans[shell.fEnteringSurfacesPvolTrans[entering_index]];
0158       // Compute the transformation to the surface reference frame
0159       auto const &surf_trans = framed_surface->fTrans;
0160       auto volume_trans      = surf_trans * pvol_trans;
0161 
0162       // Convert the point to surface coordinates
0163       // surface_point = volume_trans.Transform(localpoint);
0164       surface_point = pvol_trans.Transform(localpoint);
0165       surface_point = surf_trans.Transform(surface_point);
0166 
0167     } else { // Exiting surfaces
0168       exiting            = true;
0169       auto exiting_index = shell.fExitingSurfaces[index];
0170       framed_surface     = &(surfdata.fLocalSurf[shell.fSurfaces[exiting_index]]);
0171       // Get the local transformation of the surface
0172       TransformationMP<Real_t> &local_trans = framed_surface->fTrans;
0173       // In the case of exiting surfaces we only need to apply this transformation
0174       surface_point = local_trans.Transform(localpoint);
0175     }
0176 
0177     // Get the unplaced
0178     UnplacedSurface<Real_t> &unplaced_surface = framed_surface->fSurface;
0179 
0180     // Compute the safety
0181     // First check coming from left side
0182     Vector3D<Real_t> onsurf_crt;
0183     Real_t safety_surf{0}, safety_frame{0};
0184     bool valid_safety{true};
0185     bool can_compute{false};
0186 
0187     bool flipped = framed_surface->fLogicId < 0;
0188     can_compute  = unplaced_surface.Safety(surface_point, exiting ^ flipped, surfdata, safety_surf, onsurf_crt);
0189 
0190     if (!can_compute || safety_surf >= limit) return safety_surf;
0191 
0192     // Now compute the safety from the projection of the point on the surface to the frame
0193     safety_frame = safety_surf;
0194     // This function returns either the maximum of safety_surf and safety_frame, or the accurate safety
0195     // computed using both
0196     if (!framed_surface->fNeverCheck)
0197       safety_frame = framed_surface->LocalSafetyFrame(onsurf_crt, safety_surf, surfdata, valid_safety);
0198 
0199     if (valid_safety) return safety_frame;
0200     return limit;
0201   }
0202 
0203   /*
0204    * Used by the BVH to determine if it needs to skip checking a framed surface. The global index of the surface
0205    * defined by @p lv_index and @p index can only be accessed from the navigator
0206    * @param[in] lv_index Global index of a LogicalVolume
0207    * @param[in] index Index within the list of visible entering surfaces of the specified LogicalVolume
0208    * @param[in] global_id Global id of a FramedSurface
0209    * @returns Whether the global id of the FramedSurface defined by @p lv_index and @p index is the same as @p global_id
0210    */
0211   VECCORE_ATT_HOST_DEVICE
0212   static VECGEOM_FORCE_INLINE bool SkipItem(int lv_index, int index, long const global_id)
0213   {
0214     // There are no global IDs for framed surfaces, return false
0215     return false;
0216   }
0217 
0218   template <typename BVH_t>
0219   VECCORE_ATT_HOST_DEVICE static long TestBVHCheckDaughterIntersections(BVH_t &bvh, Vector3D<Real_t> &localpoint,
0220                                                                         Vector3D<Real_t> &localdir, double &bvhstep)
0221   {
0222     long hitcandidate_index = -1;
0223     long last_exited_id     = -1;
0224     bvh.template CheckDaughterIntersections<BVHSurfNavigator>(localpoint, localdir, bvhstep, last_exited_id,
0225                                                               hitcandidate_index);
0226     return hitcandidate_index;
0227   }
0228 
0229   template <typename BVH_t>
0230   VECCORE_ATT_HOST_DEVICE static double TestBVHComputeSafety(BVH_t &bvh, Vector3D<Real_t> &localpoint, Real_t safety)
0231   {
0232     return bvh.template ComputeSafety<BVHSurfNavigator>(localpoint, safety);
0233   }
0234 
0235   VECCORE_ATT_HOST_DEVICE
0236   static Precision ComputeSafety(Vector3D<Precision> const &point_i, vecgeom::NavigationState const &in_state,
0237                                  Precision limit = vecgeom::InfinityLength<Precision>())
0238   {
0239     auto in_navind = in_state.GetNavIndex();
0240     if (in_navind == 0) return vecgeom::InfinityLength<Precision>();
0241 
0242     // Get the SurfData instance
0243     auto const &surfdata = SurfData<Real_t>::Instance();
0244     vecgeom::Vector3D<Real_t> point(point_i);
0245 
0246     // Get the shell
0247     auto lv_index     = in_state.GetLogicalId();
0248     auto const &shell = surfdata.fShells[lv_index];
0249 
0250     // Get the BVH
0251     auto &bvh = surfdata.fBVH[shell.fBVH];
0252 
0253     vecgeom::Transformation3D lv_trans;
0254     in_state.TopMatrix(lv_trans);
0255     auto localpoint = lv_trans.Transform(point);
0256 
0257     auto safety = bvh.template ComputeSafety<BVHSurfNavigator<Real_t>>(localpoint, limit);
0258     // Safety is rounded from float, so round down with float relative tolerance
0259     safety = vecgeom::MakeMinusTolerantRel<float>(safety);
0260     return static_cast<Real_t>(safety);
0261   }
0262 
0263   /*
0264    * @param[in] aLVIndex Global index of a LogicalVolume
0265    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0266    * @returns The global id of the PlacedVolume defined by @p aLVIndex and @p index
0267    */
0268   VECCORE_ATT_HOST_DEVICE
0269   static uint ItemId(int lv_index, int index)
0270   {
0271     auto const &surfdata = vgbrep::SurfData<Real_t>::Instance();
0272     // Get the shell for this volume
0273     auto const &shell = surfdata.fShells[lv_index];
0274     return shell.fDaughterPvolIds[index];
0275   }
0276 
0277   /*
0278    * @param[in] lv_index Global index of a LogicalVolume
0279    * @param[in] index Index within the list of daughters of the specified LogicalVolume
0280    * @param[in] localpoint Point in the local coordinates of the LV specified by @aLVIndex
0281    * @returns Whether @localpoint falls within the PlacedVolume defined by @p aLVIndex and @p index
0282    */
0283   VECCORE_ATT_HOST_DEVICE
0284   static bool CandidateContains(int lv_index, int index, Vector3D<Real_t> const &localpoint,
0285                                 vecgeom::NavigationState &path)
0286   {
0287     // Get the SurfData instance
0288     auto const &surfdata = SurfData<Real_t>::Instance();
0289     // Get the shell
0290     auto const &shell = surfdata.fShells[lv_index];
0291 
0292     Vector3D<Real_t> daughterlocalpoint;
0293     auto const &trans = surfdata.fPVolTrans[shell.fDaughterPvolTrans[index]];
0294     trans.Transform(localpoint, daughterlocalpoint);
0295 
0296     // Push to path
0297     path.PushDaughter(index);
0298 
0299     // Call LogicInside
0300     auto inside = vgbrep::protonav::LogicInsideLocal(daughterlocalpoint, path.GetLogicalId(), surfdata);
0301 
0302     // Pop from the path
0303     path.Pop();
0304 
0305     return inside;
0306   };
0307 
0308   VECCORE_ATT_HOST_DEVICE
0309   static int LocatePointIn(int pvol_id, Vector3D<Precision> const &point_i, vecgeom::NavigationState &path, bool top,
0310                            int *exclude = nullptr)
0311   {
0312     using NavigationState = vecgeom::NavigationState;
0313     vecgeom::Vector3D<Real_t> point(point_i);
0314 
0315     // Get the SurfData instance
0316     auto const &surfdata = SurfData<Real_t>::Instance();
0317 
0318     if (top) {
0319       VECGEOM_ASSERT(pvol_id >= 0);
0320       auto ivol   = NavigationState::ToPlacedId(pvol_id).fVolume.fId;
0321       auto inside = vgbrep::protonav::LogicInsideLocal(point, ivol, surfdata);
0322       if (!inside) return -1;
0323     }
0324 
0325     path.Push(pvol_id);
0326     Vector3D<Real_t> currentpoint(point);
0327     Vector3D<Real_t> daughterlocalpoint;
0328     int exclude_id  = -1;
0329     int daughter_id = -1;
0330 
0331     // Get the shell
0332     auto shell = surfdata.fShells[path.GetLogicalId()]; // Assuming path corresponds to pvol_id
0333 
0334     // Keep going down as long as the current volume has daughters
0335     while (shell.fNEnteringSurfaces > 0) {
0336       // Get the BVH
0337       auto &bvh = surfdata.fBVHSolids[shell.fBVH];
0338 
0339       exclude_id = -1;
0340       if (exclude != nullptr) {
0341         exclude_id = *exclude;
0342       }
0343       daughter_id = -1;
0344 
0345       // daughter_id will be the global id of the placed volume
0346       if (!bvh.template LevelLocate<BVHSurfNavigator<Real_t>>(exclude_id, currentpoint, daughter_id, path)) break;
0347 
0348       path.Push(daughter_id);
0349 
0350       // Compute the transformed point
0351       // TODO: This can be done cheaper if we use the transformation stored in the shell
0352       // This saves computing the full global transformation in TopMatrix, but for now we don't have
0353       // the index of the daughter from the BVH
0354       vecgeom::Transformation3DMP<Real_t> trans;
0355       path.TopMatrix(trans);
0356       trans.Transform(point, daughterlocalpoint);
0357 
0358       // And update the current point
0359       currentpoint = daughterlocalpoint;
0360 
0361       // Update the current volume shell
0362       shell = surfdata.fShells[path.GetLogicalId()];
0363 
0364       // Only exclude the placed volume once since we could enter it again via a
0365       // different volume history.
0366       if (exclude != nullptr) {
0367         *exclude = -1;
0368       }
0369     }
0370 
0371     return path.TopId();
0372   }
0373 
0374   /// @brief Method computing the distance to the next surface.
0375   /// @details The method returns the same ouy_state as the in_state, but alters the boundary condition.
0376   /// @tparam Real_i Input floating point type
0377   /// @param point_i Global point
0378   /// @param direction_i Global direction
0379   /// @param in_state Input navigation state before crossing
0380   /// @param out_state Output navigation state after crossing
0381   /// @param hitsurf_index Hit surface index
0382   /// @param stepmax Maximum allowed step
0383   /// @return Distance to next surface.
0384   template <typename Real_i>
0385   VECCORE_ATT_HOST_DEVICE static Real_i ComputeStepAndNextSurface(vecgeom::Vector3D<Real_i> const &point_i,
0386                                                                   vecgeom::Vector3D<Real_i> const &direction_i,
0387                                                                   vecgeom::NavigationState const &in_state,
0388                                                                   vecgeom::NavigationState &out_state,
0389                                                                   long &hitsurf_index,
0390                                                                   Real_i stepmax = vecgeom::InfinityLength<Real_i>())
0391   {
0392     out_state      = in_state;
0393     auto in_navind = in_state.GetNavIndex();
0394     // For the moment we only support a starting state within the setup
0395     if (in_navind == 0) return vecgeom::InfinityLength<Real_i>();
0396     auto const &surfdata = SurfData<Real_t>::Instance();
0397 
0398     auto ivol = in_state.GetLogicalId();
0399     auto &bvh = surfdata.fBVH[surfdata.fShells[ivol].fBVH];
0400 
0401     // casting point into Real_t (float in mixed precision) and using Real_t transformation for local points.
0402     // Since this is for a frame check, float is sufficient.
0403     vecgeom::Vector3D<Real_t> point(point_i);
0404     vecgeom::Vector3D<Real_t> direction(direction_i);
0405     vecgeom::Transformation3DMP<Real_t> lv_trans;
0406     in_state.TopMatrix(lv_trans);
0407     auto localpoint = lv_trans.Transform(point);
0408     auto localdir   = lv_trans.TransformDirection(direction);
0409 
0410     // Draft: potentially one can still transform in double precision and then convert that point to single precision
0411     // (or even propagate the point and do the conversion later). To be tested in the future
0412     // if (localpoint.Length2() > Real_t(1e8)) {
0413     //   vecgeom::Transformation3DMP<Real_i> lv_trans_DP;
0414     //   in_state.TopMatrix(lv_trans_DP);
0415     //   // precise point in Real_i (double) since this may be large values where rounding errors propagate to our
0416     //   distance calculation auto localpoint = lv_trans_DP.Transform(point); auto localdir   =
0417     //   lv_trans_DP.TransformDirection(direction);
0418 
0419     //   localpoint = {static_cast<Real_t>(localpoint[0]), static_cast<Real_t>(localpoint[1]),
0420     //   static_cast<Real_t>(localpoint[2])}; localdir = {static_cast<Real_t>(localdir[0]),
0421     //   static_cast<Real_t>(localdir[1]), static_cast<Real_t>(localdir[2])};
0422     // }
0423 
0424     auto bvhstep = stepmax;
0425     // long last_exited_id     = -1;
0426     bvh.template CheckDaughterIntersections<BVHSurfNavigator>(localpoint, localdir, bvhstep, -1, hitsurf_index);
0427     // If there is no physics step limitation, a surface must be found
0428     if (hitsurf_index < 0) {
0429       // Nothing is hit within the step limit
0430       out_state.SetBoundaryState(false);
0431       return stepmax;
0432     }
0433 
0434     // By default we hit a boundary and exit the in_state volume
0435     out_state.SetBoundaryState(true);
0436     out_state.SetLastExited();
0437 
0438     // Return the step to the hit surface
0439     return bvhstep;
0440   }
0441 
0442   /// @brief Method that relocates the point after CS traversal
0443   /// @tparam Real_i Input floating point type
0444   /// @param point_i Global point
0445   /// @param direction_i Global direction
0446   /// @param step Step to the next boundary
0447   /// @param out_state Input state before crossing and output state after crossing
0448   /// @param hit_FS Hit surface information
0449   template <typename Real_i>
0450   VECCORE_ATT_HOST_DEVICE static void RelocateToNextVolume(vecgeom::Vector3D<Real_i> const &point_i,
0451                                                            vecgeom::Vector3D<Real_i> const &direction_i, Real_i step,
0452                                                            long hitsurf_index, vecgeom::NavigationState &out_state,
0453                                                            CrossedSurface &hit_FS)
0454   {
0455     auto const &surfdata = SurfData<Real_t>::Instance();
0456     auto const in_state  = out_state;
0457     // out_state            = in_state;
0458     Real_t bvhstep_RT = Real_t(step); // cast only once and use in later functions
0459     vecgeom::Vector3D<Real_t> point(point_i);
0460     vecgeom::Vector3D<Real_t> direction(direction_i);
0461     vecgeom::Transformation3DMP<Real_t> scene_trans;
0462     in_state.SceneMatrix(scene_trans);
0463     auto local_scene    = scene_trans.Transform(point);
0464     auto localdir_scene = scene_trans.TransformDirection(direction);
0465     // Identify the common surface
0466     auto const &currentShell = surfdata.fShells[in_state.GetLogicalId()];
0467     Vector3D<Real_t> CS_local, CS_localdir, onsurf_crt;
0468     if (hitsurf_index < currentShell.fNExitingSurfaces) {
0469       // If the hit candidate is an exiting surface
0470       auto exiting_index = currentShell.fExitingSurfaces[hitsurf_index];
0471       FSlocator hit_FS_tmp;
0472       surfdata.SceneToTouchableLocator(in_state, exiting_index, hit_FS_tmp);
0473       FSlocator out_frame;
0474       hit_FS_tmp.state = in_state;
0475       // Get the onsurf point in CS coordinates
0476       auto const &surf     = surfdata.fCommonSurfaces[hit_FS_tmp.GetCSindex()];
0477       auto const &CS_trans = surf.fTrans;
0478       CS_local             = CS_trans.Transform(local_scene);
0479       CS_localdir          = CS_trans.TransformDirection(localdir_scene);
0480       onsurf_crt           = CS_local + bvhstep_RT * CS_localdir;
0481       ExitCS(hit_FS_tmp, /*is_hit=*/true, point, direction, bvhstep_RT, onsurf_crt, hit_FS.hit_surf, out_frame);
0482       hit_FS.exit_surf = hit_FS.hit_surf;
0483       out_state        = out_frame.state;
0484 
0485     } else {
0486 
0487       auto entering_index        = hitsurf_index - currentShell.fNExitingSurfaces;
0488       auto local_surface_id      = currentShell.fEnteringSurfaces[entering_index];
0489       auto const &framed_surface = surfdata.fLocalSurf[local_surface_id];
0490       auto pvol_id               = currentShell.fEnteringSurfacesPvol[entering_index];
0491       // Create a copy of the navigation state
0492       auto pvol_navstate(in_state);
0493       // Get the navigation state of the daughter
0494       // pvol_navstate.Push(pvol);
0495       pvol_navstate.Push(pvol_id);
0496       // set hit_FS.hit_surf
0497       surfdata.SceneToTouchableLocator(pvol_navstate, framed_surface.fSurfIndex, hit_FS.hit_surf);
0498       hit_FS.hit_surf.state = in_state;
0499       int traversal         = surfdata.GetFramedSurface(hit_FS.hit_surf).fTraversal;
0500       // Get the onsurf point in CS coordinates
0501       auto const &surf     = surfdata.fCommonSurfaces[hit_FS.hit_surf.GetCSindex()];
0502       auto const &CS_trans = surf.fTrans;
0503 
0504       unsigned short scene_id = 0, newscene_id = 0;
0505       bool is_scene = in_state.GetSceneId(scene_id, newscene_id);
0506 
0507       if (!is_scene) {
0508         CS_local    = CS_trans.Transform(local_scene);
0509         CS_localdir = CS_trans.TransformDirection(localdir_scene);
0510       } else {
0511         vecgeom::Transformation3DMP<Real_t> lv_trans;
0512         // local_scene is already transformed by SceneMatrix, therefore now TopInSceneMatrix is sufficient
0513         in_state.TopInSceneMatrix(lv_trans);
0514         auto localpoint = lv_trans.Transform(local_scene);
0515         auto localdir   = lv_trans.TransformDirection(localdir_scene);
0516         CS_local        = CS_trans.Transform(localpoint);
0517         CS_localdir     = CS_trans.TransformDirection(localdir);
0518       }
0519       onsurf_crt = CS_local + bvhstep_RT * CS_localdir;
0520 
0521       // Seek and cross entering frames
0522       FSlocator out_frame;
0523       EnterCS(hit_FS.hit_surf, point, direction, bvhstep_RT, onsurf_crt, out_frame, traversal);
0524       out_state = out_frame.state;
0525     }
0526 
0527     // Fix the out_state if pointing to a 0 scene
0528     if (out_state.GetSceneLevel() > 0 && out_state.GetNavIndex() == 0) out_state.PopScene();
0529     out_state.SetBoundaryState(true);
0530   }
0531 
0532   /// @brief Method computing the distance to the next surface and state after crossing it
0533   /// @tparam Real_i Input floating point type
0534   /// @param point_i Global point
0535   /// @param direction_i Global direction
0536   /// @param in_state Input navigation state before crossing
0537   /// @param out_state Output navigation state after crossing
0538   /// @param surfdata Surface data storage
0539   /// @param exit_surf data container storing the exited common surface, the side, the frame id, and whether there is an overlap
0540   /// @param stepmax maximum step
0541   /// @return Distance to next surface.
0542   template <typename Real_i>
0543   VECCORE_ATT_HOST_DEVICE static Real_i ComputeStepAndHit(vecgeom::Vector3D<Real_i> const &point_i,
0544                                                           vecgeom::Vector3D<Real_i> const &direction_i,
0545                                                           vecgeom::NavigationState const &in_state,
0546                                                           vecgeom::NavigationState &out_state, CrossedSurface &hit_FS,
0547                                                           Real_i stepmax = vecgeom::InfinityLength<Real_i>())
0548   {
0549     long hitsurf_index = -1;
0550     auto bvhstep       = ComputeStepAndNextSurface(point_i, direction_i, in_state, out_state, hitsurf_index, stepmax);
0551     // If there is no physics step limitation, a surface must be found
0552     if (hitsurf_index < 0) {
0553       if (in_state.GetNavIndex() > 0 && stepmax == vecgeom::InfinityLength<Real_i>())
0554         hit_FS.Set(0, -1, 0); // frame_id = -1 indicates extruding overlap
0555       // This can happen if the exit point is outside the mother volume (extrusion)
0556       // To recover, one can return the mother state as output and a zero distance
0557       return stepmax;
0558     }
0559     RelocateToNextVolume(point_i, direction_i, bvhstep, hitsurf_index, out_state, hit_FS);
0560     return bvhstep;
0561   }
0562 };
0563 
0564 } // namespace protonav
0565 } // namespace vgbrep
0566 
0567 #endif