Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:16:18

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // SPDX-FileCopyrightText: 2020 CERN
0003 // SPDX-License-Identifier: Apache-2.0
0004 //---------------------------------------------------------------------------//
0005 /*!
0006  * \file BVHNavigator.hh
0007  * \brief Bounding Volume Hierarchy navigator directly derived from AdePT
0008  *
0009  * Original source:
0010  * https://github.com/apt-sim/AdePT/blob/bafab78519faafde0b8e5055128c2a3610d43d77/base/inc/AdePT/BVHNavigator.h
0011  */
0012 //---------------------------------------------------------------------------//
0013 #pragma once
0014 
0015 #include <limits>
0016 #include <VecGeom/base/Global.h>
0017 #include <VecGeom/base/Vector3D.h>
0018 #include <VecGeom/management/BVHManager.h>
0019 #include <VecGeom/navigation/NavStateFwd.h>
0020 #include <VecGeom/navigation/NavigationState.h>
0021 
0022 #ifdef VECGEOM_ENABLE_CUDA
0023 #    include <VecGeom/backend/cuda/Interface.h>
0024 #endif
0025 
0026 #if VECGEOM_VERSION >= 0x020000
0027 #    error "Use the built-in VecGeom BVH navigator since it is available"
0028 #endif
0029 
0030 #include "corecel/Macros.hh"
0031 
0032 namespace celeritas
0033 {
0034 namespace detail
0035 {
0036 //---------------------------------------------------------------------------//
0037 class BVHNavigator
0038 {
0039   public:
0040     using Precision = vecgeom::Precision;
0041     using Vector3D = vecgeom::Vector3D<vecgeom::Precision>;
0042     using VPlacedVolumePtr_t = vecgeom::VPlacedVolume const*;
0043 
0044     static constexpr Precision kBoundaryPush = 10 * vecgeom::kTolerance;
0045 
0046     CELER_FUNCTION static VPlacedVolumePtr_t
0047     LocatePointIn(vecgeom::VPlacedVolume const* vol,
0048                   Vector3D const& point,
0049                   vecgeom::NavigationState& path,
0050                   bool top,
0051                   vecgeom::VPlacedVolume const* exclude = nullptr)
0052     {
0053         if (top)
0054         {
0055             assert(vol != nullptr);
0056             if (!vol->UnplacedContains(point))
0057                 return nullptr;
0058         }
0059 
0060         path.Push(vol);
0061 
0062         Vector3D currentpoint(point);
0063         Vector3D daughterlocalpoint;
0064 
0065         while (vol->GetDaughters().size() > 0)
0066         {
0067             auto* bvh
0068                 = vecgeom::BVHManager::GetBVH(vol->GetLogicalVolume()->id());
0069             CELER_ASSERT(bvh);
0070 
0071             // Note: vol is updated by this call
0072             if (!bvh->LevelLocate(
0073                     exclude, currentpoint, vol, daughterlocalpoint))
0074             {
0075                 // Not inside any daughter
0076                 break;
0077             }
0078 
0079             currentpoint = daughterlocalpoint;
0080             path.Push(vol);
0081             // Only exclude the placed volume once since we could enter it
0082             // again via a different volume history.
0083             exclude = nullptr;
0084         }
0085 
0086         return path.Top();
0087     }
0088 
0089     CELER_FUNCTION static VPlacedVolumePtr_t
0090     RelocatePoint(Vector3D const& localpoint, vecgeom::NavigationState& path)
0091     {
0092         vecgeom::VPlacedVolume const* currentmother = path.Top();
0093         Vector3D transformed = localpoint;
0094         do
0095         {
0096             path.Pop();
0097             transformed = currentmother->GetTransformation()->InverseTransform(
0098                 transformed);
0099             currentmother = path.Top();
0100         } while (currentmother
0101                  && (currentmother->IsAssembly()
0102                      || !currentmother->UnplacedContains(transformed)));
0103 
0104         if (currentmother)
0105         {
0106             path.Pop();
0107             return LocatePointIn(currentmother, transformed, path, false);
0108         }
0109         return currentmother;
0110     }
0111 
0112   private:
0113     // Computes a step in the current volume from the localpoint into localdir,
0114     // taking step_limit into account. If a volume is hit, the function calls
0115     // out_state.SetBoundaryState(true) and hitcandidate is set to the hit
0116     // daughter volume, or kept unchanged if the current volume is left.
0117     CELER_FUNCTION static double
0118     ComputeStepAndHit(Vector3D const& localpoint,
0119                       Vector3D const& localdir,
0120                       Precision step_limit,
0121                       vecgeom::NavigationState const& in_state,
0122                       vecgeom::NavigationState& out_state,
0123                       VPlacedVolumePtr_t& hitcandidate)
0124     {
0125         if (step_limit <= 0)
0126         {
0127             // We don't need to ask any solid, step not limited by geometry.
0128             in_state.CopyTo(&out_state);
0129             out_state.SetBoundaryState(false);
0130             return 0;
0131         }
0132 
0133         Precision step = step_limit;
0134         VPlacedVolumePtr_t pvol = in_state.Top();
0135 
0136         // need to calc DistanceToOut first
0137         step = pvol->DistanceToOut(localpoint, localdir, step_limit);
0138 
0139         if (step < 0)
0140             step = 0;
0141 
0142         if (pvol->GetDaughters().size() > 0)
0143         {
0144             auto bvh
0145                 = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0146             bvh->CheckDaughterIntersections(
0147                 localpoint, localdir, step, pvol, hitcandidate);
0148         }
0149 
0150         // now we have the candidates and we prepare the out_state
0151         in_state.CopyTo(&out_state);
0152         if (step == vecgeom::kInfLength && step_limit > 0)
0153         {
0154             out_state.SetBoundaryState(true);
0155             do
0156             {
0157                 out_state.Pop();
0158             } while (out_state.Top()->IsAssembly());
0159 
0160             return vecgeom::kTolerance;
0161         }
0162 
0163         // Is geometry further away than physics step?
0164         if (step > step_limit)
0165         {
0166             // Then this is a phyics step and we don't need to do anything.
0167             out_state.SetBoundaryState(false);
0168             return step_limit;
0169         }
0170 
0171         // Otherwise it is a geometry step and we push the point to the
0172         // boundary.
0173         out_state.SetBoundaryState(true);
0174 
0175         if (step < 0)
0176         {
0177             step = 0;
0178         }
0179 
0180         return step;
0181     }
0182 
0183     // Computes a step in the current volume from the localpoint into localdir,
0184     // until the next daughter bounding box, taking step_limit into account.
0185     CELER_FUNCTION static double
0186     ApproachNextVolume(Vector3D const& localpoint,
0187                        Vector3D const& localdir,
0188                        Precision step_limit,
0189                        vecgeom::NavigationState const& in_state)
0190     {
0191         Precision step = step_limit;
0192         VPlacedVolumePtr_t pvol = in_state.Top();
0193 
0194         if (pvol->GetDaughters().size() > 0)
0195         {
0196             auto bvh
0197                 = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0198             // bvh->CheckDaughterIntersections(localpoint, localdir, step,
0199             // pvol, hitcandidate);
0200             bvh->ApproachNextDaughter(localpoint, localdir, step, pvol);
0201             // Make sure we don't "step" on next boundary
0202             step -= 10 * vecgeom::kTolerance;
0203         }
0204 
0205         if (step == vecgeom::kInfLength && step_limit > 0)
0206             return 0;
0207 
0208         // Is geometry further away than physics step?
0209         if (step > step_limit)
0210         {
0211             // Then this is a phyics step and we don't need to do anything.
0212             return step_limit;
0213         }
0214 
0215         if (step < 0)
0216         {
0217             step = 0;
0218         }
0219 
0220         return step;
0221     }
0222 
0223   public:
0224     // Computes the isotropic safety from the globalpoint.
0225     CELER_FUNCTION static double
0226     ComputeSafety(Vector3D const& globalpoint,
0227                   vecgeom::NavigationState const& state,
0228                   Precision safety = std::numeric_limits<Precision>::infinity())
0229     {
0230         VPlacedVolumePtr_t pvol = state.Top();
0231         vecgeom::Transformation3D m;
0232         state.TopMatrix(m);
0233         Vector3D localpoint = m.Transform(globalpoint);
0234 
0235         // need to calc DistanceToOut first
0236         safety = min(safety, pvol->SafetyToOut(localpoint));
0237 
0238         if (safety > 0 && pvol->GetDaughters().size() > 0)
0239         {
0240             auto bvh
0241                 = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0242             safety = bvh->ComputeSafety(localpoint, safety);
0243         }
0244 
0245         return safety;
0246     }
0247 
0248     // Computes a step from the globalpoint (which must be in the current
0249     // volume) into globaldir, taking step_limit into account. If a volume is
0250     // hit, the function calls out_state.SetBoundaryState(true) and relocates
0251     // the state to the next volume.
0252     CELER_FUNCTION static double
0253     ComputeStepAndPropagatedState(Vector3D const& globalpoint,
0254                                   Vector3D const& globaldir,
0255                                   Precision step_limit,
0256                                   vecgeom::NavigationState const& in_state,
0257                                   vecgeom::NavigationState& out_state,
0258                                   Precision push = 0)
0259     {
0260         // If we are on the boundary, push a bit more
0261         if (in_state.IsOnBoundary())
0262         {
0263             push += kBoundaryPush;
0264         }
0265         if (step_limit < push)
0266         {
0267             // Go as far as the step limit says, assuming there is no boundary.
0268             // TODO: Does this make sense?
0269             in_state.CopyTo(&out_state);
0270             out_state.SetBoundaryState(false);
0271             return step_limit;
0272         }
0273         step_limit -= push;
0274 
0275         // calculate local point/dir from global point/dir
0276         Vector3D localpoint;
0277         Vector3D localdir;
0278         // Impl::DoGlobalToLocalTransformation(in_state, globalpoint,
0279         // globaldir, localpoint, localdir);
0280         vecgeom::Transformation3D m;
0281         in_state.TopMatrix(m);
0282         localpoint = m.Transform(globalpoint);
0283         localdir = m.TransformDirection(globaldir);
0284         // The user may want to move point from boundary before computing the
0285         // step
0286         localpoint += push * localdir;
0287 
0288         VPlacedVolumePtr_t hitcandidate = nullptr;
0289         Precision step = ComputeStepAndHit(
0290             localpoint, localdir, step_limit, in_state, out_state, hitcandidate);
0291         step += push;
0292 
0293         if (out_state.IsOnBoundary())
0294         {
0295             // Relocate the point after the step to refine out_state.
0296             localpoint += (step + kBoundaryPush) * localdir;
0297 
0298             if (!hitcandidate)
0299             {
0300                 // We didn't hit a daughter but instead we're exiting the
0301                 // current volume.
0302                 RelocatePoint(localpoint, out_state);
0303             }
0304             else
0305             {
0306                 // Otherwise check if we're directly entering other daughters
0307                 // transitively.
0308                 localpoint
0309                     = hitcandidate->GetTransformation()->Transform(localpoint);
0310                 LocatePointIn(hitcandidate, localpoint, out_state, false);
0311             }
0312 
0313             if (out_state.Top() != nullptr)
0314             {
0315                 while (out_state.Top()->IsAssembly()
0316                        || out_state.HasSamePathAsOther(in_state))
0317                 {
0318                     out_state.Pop();
0319                 }
0320                 assert(!out_state.Top()
0321                             ->GetLogicalVolume()
0322                             ->GetUnplacedVolume()
0323                             ->IsAssembly());
0324             }
0325         }
0326 
0327         return step;
0328     }
0329 
0330     // Computes a step from the globalpoint (which must be in the current
0331     // volume) into globaldir, taking step_limit into account. If a volume is
0332     // hit, the function calls out_state.SetBoundaryState(true) and
0333     //  - removes all volumes from out_state if the current volume is left, or
0334     //  - adds the hit daughter volume to out_state if one is hit.
0335     // However the function does _NOT_ relocate the state to the next volume,
0336     // that is entering multiple volumes that share a boundary.
0337     CELER_FUNCTION static double
0338     ComputeStepAndNextVolume(Vector3D const& globalpoint,
0339                              Vector3D const& globaldir,
0340                              Precision step_limit,
0341                              vecgeom::NavigationState const& in_state,
0342                              vecgeom::NavigationState& out_state,
0343                              Precision push = 0)
0344     {
0345         // If we are on the boundary, push a bit more
0346         if (in_state.IsOnBoundary())
0347         {
0348             push += kBoundaryPush;
0349         }
0350         if (step_limit < push)
0351         {
0352             // Go as far as the step limit says, assuming there is no boundary.
0353             // TODO: Does this make sense?
0354             in_state.CopyTo(&out_state);
0355             out_state.SetBoundaryState(false);
0356             return step_limit;
0357         }
0358         step_limit -= push;
0359 
0360         // calculate local point/dir from global point/dir
0361         Vector3D localpoint;
0362         Vector3D localdir;
0363         // Impl::DoGlobalToLocalTransformation(in_state, globalpoint,
0364         // globaldir, localpoint, localdir);
0365         vecgeom::Transformation3D m;
0366         in_state.TopMatrix(m);
0367         localpoint = m.Transform(globalpoint);
0368         localdir = m.TransformDirection(globaldir);
0369         // The user may want to move point from boundary before computing the
0370         // step
0371         localpoint += push * localdir;
0372 
0373         VPlacedVolumePtr_t hitcandidate = nullptr;
0374         Precision step = ComputeStepAndHit(
0375             localpoint, localdir, step_limit, in_state, out_state, hitcandidate);
0376         step += push;
0377 
0378         if (out_state.IsOnBoundary())
0379         {
0380             if (!hitcandidate)
0381             {
0382                 vecgeom::VPlacedVolume const* currentmother = out_state.Top();
0383                 Vector3D transformed = localpoint;
0384                 // Push the point inside the next volume.
0385                 transformed += (step + kBoundaryPush) * localdir;
0386 
0387                 do
0388                 {
0389                     out_state.SetLastExited();
0390                     out_state.Pop();
0391                     transformed
0392                         = currentmother->GetTransformation()->InverseTransform(
0393                             transformed);
0394                     currentmother = out_state.Top();
0395                 } while (currentmother
0396                          && (currentmother->IsAssembly()
0397                              || !currentmother->UnplacedContains(transformed)));
0398             }
0399             else
0400             {
0401                 out_state.Push(hitcandidate);
0402             }
0403         }
0404 
0405         return step;
0406     }
0407 
0408     // Computes a step from the globalpoint (which must be in the current
0409     // volume) into globaldir, taking step_limit into account.
0410     CELER_FUNCTION static vecgeom::Precision
0411     ComputeStepToApproachNextVolume(Vector3D const& globalpoint,
0412                                     Vector3D const& globaldir,
0413                                     Precision step_limit,
0414                                     vecgeom::NavigationState const& in_state)
0415     {
0416         // calculate local point/dir from global point/dir
0417         Vector3D localpoint;
0418         Vector3D localdir;
0419         // Impl::DoGlobalToLocalTransformation(in_state, globalpoint,
0420         // globaldir, localpoint, localdir);
0421         vecgeom::Transformation3D m;
0422         in_state.TopMatrix(m);
0423         localpoint = m.Transform(globalpoint);
0424         localdir = m.TransformDirection(globaldir);
0425 
0426         Precision step
0427             = ApproachNextVolume(localpoint, localdir, step_limit, in_state);
0428 
0429         return step;
0430     }
0431 
0432     // Relocate a state that was returned from ComputeStepAndNextVolume: It
0433     // recursively locates the pushed point in the containing volume.
0434     CELER_FUNCTION static void
0435     RelocateToNextVolume(Vector3D const& globalpoint,
0436                          Vector3D const& globaldir,
0437                          vecgeom::NavigationState& state)
0438     {
0439         // Push the point inside the next volume.
0440         Vector3D pushed = globalpoint + kBoundaryPush * globaldir;
0441 
0442         // Calculate local point from global point.
0443         vecgeom::Transformation3D m;
0444         state.TopMatrix(m);
0445         Vector3D localpoint = m.Transform(pushed);
0446 
0447         VPlacedVolumePtr_t pvol = state.Top();
0448 
0449         state.Pop();
0450         LocatePointIn(pvol, localpoint, state, false, state.GetLastExited());
0451 
0452         if (state.Top() != nullptr)
0453         {
0454             while (state.Top()->IsAssembly())
0455             {
0456                 state.Pop();
0457             }
0458             assert(!state.Top()
0459                         ->GetLogicalVolume()
0460                         ->GetUnplacedVolume()
0461                         ->IsAssembly());
0462         }
0463     }
0464 };
0465 //---------------------------------------------------------------------------//
0466 }  // namespace detail
0467 }  // namespace celeritas