Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:09:32

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