Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-29 08:44:59

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file geocel/vg/VecgeomTrackView.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <VecGeom/base/Config.h>
0010 #include <VecGeom/base/Cuda.h>
0011 #include <VecGeom/base/Version.h>
0012 #include <VecGeom/navigation/NavStateFwd.h>
0013 #include <VecGeom/navigation/NavigationState.h>
0014 #include <VecGeom/volumes/LogicalVolume.h>
0015 #include <VecGeom/volumes/PlacedVolume.h>
0016 
0017 #include "corecel/Config.hh"
0018 
0019 #include "corecel/Macros.hh"
0020 #include "corecel/math/Algorithms.hh"
0021 #include "corecel/math/ArrayUtils.hh"
0022 #include "corecel/math/SoftEqual.hh"
0023 #include "corecel/sys/ThreadId.hh"
0024 #include "geocel/Types.hh"
0025 
0026 #include "VecgeomData.hh"
0027 
0028 #include "detail/VecgeomCompatibility.hh"
0029 
0030 #ifdef VECGEOM_USE_SURF
0031 #    include "detail/SurfNavigator.hh"
0032 #elif VECGEOM_VERSION >= 0x020000
0033 #    include <VecGeom/navigation/BVHNavigator.h>
0034 #else
0035 #    include "detail/BVHNavigator.hh"
0036 #endif
0037 
0038 namespace celeritas
0039 {
0040 //---------------------------------------------------------------------------//
0041 /*!
0042  * Navigate through a VecGeom geometry on a single thread.
0043  *
0044  * For a description of ordering requirements, see:
0045  * \sa OrangeTrackView
0046  *
0047  * \code
0048     VecgeomTrackView geom(vg_params_ref, vg_state_ref, trackslot_id);
0049    \endcode
0050  *
0051  * The "next distance" is cached as part of `find_next_step`, but it is only
0052  * used when the immediate next call is `move_to_boundary`.
0053  */
0054 class VecgeomTrackView
0055 {
0056   public:
0057     //!@{
0058     //! \name Type aliases
0059     using Initializer_t = GeoTrackInitializer;
0060     using ParamsRef = NativeCRef<VecgeomParamsData>;
0061     using StateRef = NativeRef<VecgeomStateData>;
0062 #ifdef VECGEOM_USE_SURF
0063     using Navigator = celeritas::detail::SurfNavigator;
0064 #elif VECGEOM_VERSION >= 0x020000
0065     using Navigator = vecgeom::BVHNavigator;
0066 #else
0067     using Navigator = celeritas::detail::BVHNavigator;
0068 #endif
0069     //!@}
0070 
0071     //! Helper struct for initializing from an existing geometry state
0072     struct DetailedInitializer
0073     {
0074         VecgeomTrackView const& other;  //!< Existing geometry
0075         Real3 const& dir;  //!< New direction
0076     };
0077 
0078   public:
0079     // Construct from persistent and state data
0080     inline CELER_FUNCTION VecgeomTrackView(ParamsRef const& data,
0081                                            StateRef const& stateview,
0082                                            TrackSlotId tid);
0083 
0084     // Initialize the state
0085     inline CELER_FUNCTION VecgeomTrackView&
0086     operator=(Initializer_t const& init);
0087     // Initialize the state from a parent state and new direction
0088     inline CELER_FUNCTION VecgeomTrackView&
0089     operator=(DetailedInitializer const& init);
0090 
0091     //// STATIC ACCESSORS ////
0092 
0093     //! A tiny push to make sure tracks do not get stuck at boundaries
0094     static CELER_CONSTEXPR_FUNCTION real_type extra_push() { return 1e-13; }
0095 
0096     //// ACCESSORS ////
0097 
0098     //!@{
0099     //! State accessors
0100     CELER_FORCEINLINE_FUNCTION Real3 const& pos() const { return pos_; }
0101     CELER_FORCEINLINE_FUNCTION Real3 const& dir() const { return dir_; }
0102     //!@}
0103 
0104     // Get the current volume's ID
0105     inline CELER_FUNCTION VolumeId volume_id() const;
0106     // Get the ID of the current volume instance
0107     inline CELER_FUNCTION VolumeInstanceId volume_instance_id() const;
0108     // Get the depth in the geometry hierarchy
0109     inline CELER_FUNCTION LevelId level() const;
0110     // Get the volume instance ID for all levels
0111     inline CELER_FUNCTION void
0112     volume_instance_id(Span<VolumeInstanceId> levels) const;
0113 
0114     //!@{
0115     //! VecGeom states are never "on" a surface
0116     CELER_FUNCTION SurfaceId surface_id() const { return {}; }
0117     //!@}
0118 
0119     // Whether the track is outside the valid geometry region
0120     CELER_FORCEINLINE_FUNCTION bool is_outside() const;
0121     // Whether the track is exactly on a surface
0122     CELER_FORCEINLINE_FUNCTION bool is_on_boundary() const;
0123     //! Whether the last operation resulted in an error
0124     CELER_FORCEINLINE_FUNCTION bool failed() const { return false; }
0125 
0126     //// OPERATIONS ////
0127 
0128     // Find the distance to the next boundary (infinite max)
0129     inline CELER_FUNCTION Propagation find_next_step();
0130 
0131     // Find the distance to the next boundary, up to and including a step
0132     inline CELER_FUNCTION Propagation find_next_step(real_type max_step);
0133 
0134     // Find the safety at the current position (infinite max)
0135     inline CELER_FUNCTION real_type find_safety();
0136 
0137     // Find the safety at the current position up to a maximum step distance
0138     inline CELER_FUNCTION real_type find_safety(real_type max_step);
0139 
0140     // Move to the boundary in preparation for crossing it
0141     inline CELER_FUNCTION void move_to_boundary();
0142 
0143     // Move within the volume
0144     inline CELER_FUNCTION void move_internal(real_type step);
0145 
0146     // Move within the volume to a specific point
0147     inline CELER_FUNCTION void move_internal(Real3 const& pos);
0148 
0149     // Cross from one side of the current surface to the other
0150     inline CELER_FUNCTION void cross_boundary();
0151 
0152     // Change direction
0153     inline CELER_FUNCTION void set_dir(Real3 const& newdir);
0154 
0155   private:
0156     //// TYPES ////
0157 
0158     using Volume = vecgeom::LogicalVolume;
0159     using NavState = vecgeom::NavigationState;
0160 
0161     //// DATA ////
0162 
0163     //! Shared/persistent geometry data
0164     ParamsRef const& params_;
0165 
0166     //!@{
0167     //! Referenced thread-local data
0168     NavState& vgstate_;
0169     NavState& vgnext_;
0170     Real3& pos_;
0171     Real3& dir_;
0172 #ifdef VECGEOM_USE_SURF
0173     long& next_surface_;
0174 #endif
0175     //!@}
0176 
0177     // Temporary data
0178     real_type next_step_{0};
0179 
0180     //// HELPER FUNCTIONS ////
0181 
0182     // Whether any next distance-to-boundary has been found
0183     inline CELER_FUNCTION bool has_next_step() const;
0184 
0185     // Whether the next distance-to-boundary is to a surface
0186     inline CELER_FUNCTION bool is_next_boundary() const;
0187 
0188     //! Get a reference to the current volume
0189     inline CELER_FUNCTION Volume const& volume() const;
0190 
0191 #ifdef VECGEOM_USE_SURF
0192     static CELER_CONSTEXPR_FUNCTION long null_surface() { return -1; }
0193 #endif
0194 };
0195 
0196 //---------------------------------------------------------------------------//
0197 // INLINE DEFINITIONS
0198 //---------------------------------------------------------------------------//
0199 /*!
0200  * Construct from persistent and state data.
0201  */
0202 CELER_FUNCTION
0203 VecgeomTrackView::VecgeomTrackView(ParamsRef const& params,
0204                                    StateRef const& states,
0205                                    TrackSlotId tid)
0206     : params_(params)
0207     , vgstate_(states.vgstate.at(params_.max_depth, tid))
0208     , vgnext_(states.vgnext.at(params_.max_depth, tid))
0209     , pos_(states.pos[tid])
0210     , dir_(states.dir[tid])
0211 #ifdef VECGEOM_USE_SURF
0212     , next_surface_{states.next_surface[tid]}
0213 #endif
0214 {
0215 }
0216 
0217 //---------------------------------------------------------------------------//
0218 /*!
0219  * Construct the state.
0220  *
0221  * Expensive. This function should only be called to initialize an event from a
0222  * starting location and direction, but excess secondaries will also be
0223  * initialized this way.
0224  */
0225 CELER_FUNCTION VecgeomTrackView&
0226 VecgeomTrackView::operator=(Initializer_t const& init)
0227 {
0228     CELER_EXPECT(is_soft_unit_vector(init.dir));
0229 
0230     // Initialize position/direction
0231     pos_ = init.pos;
0232     dir_ = init.dir;
0233 #ifdef VECGEOM_USE_SURF
0234     next_surface_ = null_surface();
0235 #endif
0236 
0237     // Set up current state and locate daughter volume.
0238     vgstate_.Clear();
0239     constexpr bool contains_point = true;
0240 #ifdef VECGEOM_USE_SURF
0241     auto world = vecgeom::NavigationState::WorldId();
0242 #else
0243     vecgeom::VPlacedVolume const* world = params_.world_volume;
0244 #endif
0245     // LocatePointIn sets `vgstate_`
0246     Navigator::LocatePointIn(
0247         world, detail::to_vector(pos_), vgstate_, contains_point);
0248     return *this;
0249 }
0250 
0251 //---------------------------------------------------------------------------//
0252 /*!
0253  * Construct the state from a direction and a copy of the parent state.
0254  *
0255  * This is a faster method of creating secondaries from a parent that has just
0256  * been absorbed, or when filling in an empty track from a parent that is still
0257  * alive.
0258  */
0259 CELER_FUNCTION
0260 VecgeomTrackView& VecgeomTrackView::operator=(DetailedInitializer const& init)
0261 {
0262     CELER_EXPECT(is_soft_unit_vector(init.dir));
0263 
0264     if (this != &init.other)
0265     {
0266         // Copy the navigation state and position from the parent state
0267         init.other.vgstate_.CopyTo(&vgstate_);
0268         pos_ = init.other.pos_;
0269     }
0270 
0271     // Set up the next state and initialize the direction
0272     vgnext_ = vgstate_;
0273     dir_ = init.dir;
0274 
0275     CELER_ENSURE(!this->has_next_step());
0276     return *this;
0277 }
0278 
0279 //---------------------------------------------------------------------------//
0280 /*!
0281  * Get the volume ID in the current cell.
0282  */
0283 CELER_FORCEINLINE_FUNCTION VolumeId VecgeomTrackView::volume_id() const
0284 {
0285     CELER_EXPECT(!this->is_outside());
0286     return id_cast<VolumeId>(this->volume().id());
0287 }
0288 
0289 //---------------------------------------------------------------------------//
0290 /*!
0291  * Get the physical volume ID in the current cell.
0292  */
0293 CELER_FUNCTION VolumeInstanceId VecgeomTrackView::volume_instance_id() const
0294 {
0295     CELER_EXPECT(!this->is_outside());
0296     vecgeom::VPlacedVolume const* top = vgstate_.Top();
0297     CELER_ASSERT(top);
0298     return id_cast<VolumeInstanceId>(top->id());
0299 }
0300 
0301 //---------------------------------------------------------------------------//
0302 /*!
0303  * Get the depth in the geometry hierarchy.
0304  */
0305 CELER_FUNCTION LevelId VecgeomTrackView::level() const
0306 {
0307     return id_cast<LevelId>(vgstate_.GetLevel());
0308 }
0309 
0310 //---------------------------------------------------------------------------//
0311 /*!
0312  * Get the volume instance ID for all levels>
0313  */
0314 CELER_FUNCTION void
0315 VecgeomTrackView::volume_instance_id(Span<VolumeInstanceId> levels) const
0316 {
0317     CELER_EXPECT(levels.size() == this->level().get() + 1);
0318     for (auto lev : range(levels.size()))
0319     {
0320         vecgeom::VPlacedVolume const* pv = vgstate_.At(lev);
0321         CELER_ASSERT(pv);
0322         levels[lev] = id_cast<VolumeInstanceId>(vgstate_.At(lev)->id());
0323     }
0324 }
0325 
0326 //---------------------------------------------------------------------------//
0327 /*!
0328  * Whether the track is outside the valid geometry region.
0329  */
0330 CELER_FUNCTION bool VecgeomTrackView::is_outside() const
0331 {
0332     return vgstate_.IsOutside();
0333 }
0334 
0335 //---------------------------------------------------------------------------//
0336 /*!
0337  * Whether the track is on the boundary of a volume.
0338  */
0339 CELER_FUNCTION bool VecgeomTrackView::is_on_boundary() const
0340 {
0341     return vgstate_.IsOnBoundary();
0342 }
0343 
0344 //---------------------------------------------------------------------------//
0345 /*!
0346  * Find the distance to the next geometric boundary.
0347  *
0348  * This function is allowed to be called from the exterior for ray tracing.
0349  */
0350 CELER_FUNCTION Propagation VecgeomTrackView::find_next_step()
0351 {
0352     if (this->is_outside())
0353     {
0354         // SPECIAL CASE: find distance to interior from outside world volume
0355         auto* pplvol = params_.world_volume;
0356         next_step_ = pplvol->DistanceToIn(detail::to_vector(pos_),
0357                                           detail::to_vector(dir_),
0358                                           vecgeom::kInfLength);
0359 
0360         next_step_ = max(next_step_, this->extra_push());
0361         vgnext_.Clear();
0362         Propagation result;
0363         result.distance = next_step_;
0364         result.boundary = next_step_ < vecgeom::kInfLength;
0365 
0366         if (result.boundary)
0367         {
0368             vgnext_.Push(pplvol);
0369             vgnext_.SetBoundaryState(true);
0370         }
0371 
0372         return result;
0373     }
0374 
0375     return this->find_next_step(vecgeom::kInfLength);
0376 }
0377 
0378 //---------------------------------------------------------------------------//
0379 /*!
0380  * Find the distance to the next geometric boundary.
0381  */
0382 CELER_FUNCTION Propagation VecgeomTrackView::find_next_step(real_type max_step)
0383 {
0384     CELER_EXPECT(!this->is_outside());
0385     CELER_EXPECT(max_step > 0);
0386 
0387 #ifdef VECGEOM_USE_SURF
0388     // Use the navigator to find internal distance
0389     next_surface_ = null_surface();
0390 #endif
0391     // TODO: vgnext is simply copied and the boundary flag optionally set
0392     next_step_ = Navigator::ComputeStepAndNextVolume(detail::to_vector(pos_),
0393                                                      detail::to_vector(dir_),
0394                                                      max_step,
0395                                                      vgstate_,
0396                                                      vgnext_
0397 #ifdef VECGEOM_USE_SURF
0398                                                      ,
0399                                                      next_surface_
0400 #endif
0401     );
0402     next_step_ = max(next_step_, this->extra_push());
0403 
0404     if (!this->is_next_boundary())
0405     {
0406         // Soft equivalence between distance and max step is because the
0407         // BVH navigator subtracts and then re-adds a bump distance to the
0408         // step
0409         CELER_ASSERT(soft_equal(next_step_, max_step));
0410         next_step_ = max_step;
0411     }
0412 
0413     Propagation result;
0414     result.distance = next_step_;
0415     result.boundary = this->is_next_boundary();
0416 
0417     CELER_ENSURE(this->has_next_step());
0418     CELER_ENSURE(result.distance > 0);
0419     CELER_ENSURE(result.distance <= max(max_step, this->extra_push()));
0420     CELER_ENSURE(result.boundary || result.distance == max_step
0421                  || max_step < this->extra_push());
0422     return result;
0423 }
0424 
0425 //---------------------------------------------------------------------------//
0426 /*!
0427  * Find the safety at the current position.
0428  */
0429 CELER_FUNCTION real_type VecgeomTrackView::find_safety()
0430 {
0431     return this->find_safety(vecgeom::kInfLength);
0432 }
0433 
0434 //---------------------------------------------------------------------------//
0435 /*!
0436  * Find the safety at the current position up to a maximum distance.
0437  *
0438  * The safety within a step is only needed up to the end of the physics step
0439  * length.
0440  */
0441 CELER_FUNCTION real_type VecgeomTrackView::find_safety(real_type max_radius)
0442 {
0443     CELER_EXPECT(!this->is_outside());
0444     CELER_EXPECT(!this->is_on_boundary());
0445     CELER_EXPECT(max_radius > 0);
0446 
0447     real_type safety = Navigator::ComputeSafety(detail::to_vector(this->pos()),
0448                                                 vgstate_
0449 #if VECGEOM_VERSION >= 0x200000
0450                                                 ,
0451                                                 max_radius
0452 #endif
0453     );
0454     safety = min<real_type>(safety, max_radius);
0455 
0456     // Since the reported "safety" is negative if we've moved slightly beyond
0457     // the boundary of a solid without crossing it, we must clamp to zero.
0458     return max<real_type>(safety, 0);
0459 }
0460 
0461 //---------------------------------------------------------------------------//
0462 /*!
0463  * Move to the next boundary but don't cross yet.
0464  */
0465 CELER_FUNCTION void VecgeomTrackView::move_to_boundary()
0466 {
0467     CELER_EXPECT(this->has_next_step());
0468     CELER_EXPECT(this->is_next_boundary());
0469 
0470     // Move next step
0471     axpy(next_step_, dir_, &pos_);
0472     next_step_ = 0;
0473     vgstate_.SetBoundaryState(true);
0474 
0475     CELER_ENSURE(this->is_on_boundary());
0476 }
0477 
0478 //---------------------------------------------------------------------------//
0479 /*!
0480  * Cross from one side of the current surface to the other.
0481  *
0482  * The position *must* be on the boundary following a move-to-boundary.
0483  */
0484 CELER_FUNCTION void VecgeomTrackView::cross_boundary()
0485 {
0486     CELER_EXPECT(this->is_on_boundary());
0487     CELER_EXPECT(this->is_next_boundary());
0488 
0489     // Relocate to next tracking volume (maybe across multiple boundaries)
0490     if (vgnext_.Top() != nullptr)
0491     {
0492 #ifdef VECGEOM_USE_SURF
0493         if (!vgstate_.IsOutside())
0494         {
0495             // In surf model, relocation does not work from [OUTSIDE]
0496             // (no need to call this function) as vgnext_ is already set
0497             Navigator::RelocateToNextVolume(detail::to_vector(this->pos_),
0498                                             detail::to_vector(this->dir_),
0499                                             next_surface_,
0500                                             vgnext_);
0501         }
0502 #else
0503         // Some navigators require an lvalue temp_pos
0504         auto temp_pos = detail::to_vector(this->pos_);
0505         Navigator::RelocateToNextVolume(
0506             temp_pos, detail::to_vector(this->dir_), vgnext_);
0507 #endif
0508     }
0509 
0510     vgstate_ = vgnext_;
0511 
0512     CELER_ENSURE(this->is_on_boundary());
0513 }
0514 
0515 //---------------------------------------------------------------------------//
0516 /*!
0517  * Move within the current volume.
0518  *
0519  * The straight-line distance *must* be less than the distance to the
0520  * boundary.
0521  */
0522 CELER_FUNCTION void VecgeomTrackView::move_internal(real_type dist)
0523 {
0524     CELER_EXPECT(this->has_next_step());
0525     CELER_EXPECT(dist > 0 && dist <= next_step_);
0526     CELER_EXPECT(dist != next_step_ || !this->is_next_boundary());
0527 
0528     // Move and update next_step_
0529     axpy(dist, dir_, &pos_);
0530     next_step_ -= dist;
0531     vgstate_.SetBoundaryState(false);
0532 
0533     CELER_ENSURE(!this->is_on_boundary());
0534 }
0535 
0536 //---------------------------------------------------------------------------//
0537 /*!
0538  * Move within the current volume to a nearby point.
0539  *
0540  * \warning It's up to the caller to make sure that the position is
0541  * "nearby" and within the same volume.
0542  */
0543 CELER_FUNCTION void VecgeomTrackView::move_internal(Real3 const& pos)
0544 {
0545     pos_ = pos;
0546     next_step_ = 0;
0547     vgstate_.SetBoundaryState(false);
0548 
0549     CELER_ENSURE(!this->is_on_boundary());
0550 }
0551 
0552 //---------------------------------------------------------------------------//
0553 /*!
0554  * Change the track's direction.
0555  *
0556  * This happens after a scattering event or movement inside a magnetic field.
0557  * It resets the calculated distance-to-boundary.
0558  */
0559 CELER_FUNCTION void VecgeomTrackView::set_dir(Real3 const& newdir)
0560 {
0561     CELER_EXPECT(is_soft_unit_vector(newdir));
0562     dir_ = newdir;
0563     next_step_ = 0;
0564 }
0565 
0566 //---------------------------------------------------------------------------//
0567 // PRIVATE MEMBER FUNCTIONS
0568 //---------------------------------------------------------------------------//
0569 /*!
0570  * Whether a next step has been calculated.
0571  */
0572 CELER_FUNCTION bool VecgeomTrackView::has_next_step() const
0573 {
0574     return next_step_ != 0;
0575 }
0576 
0577 //---------------------------------------------------------------------------//
0578 /*!
0579  * Whether the calculated next step will take track to next boundary.
0580  */
0581 CELER_FUNCTION bool VecgeomTrackView::is_next_boundary() const
0582 {
0583     CELER_EXPECT(this->has_next_step() || this->is_on_boundary());
0584     return vgnext_.IsOnBoundary();
0585 }
0586 
0587 //---------------------------------------------------------------------------//
0588 /*!
0589  * Get a reference to the current volume, or to world volume if outside.
0590  */
0591 CELER_FUNCTION auto VecgeomTrackView::volume() const -> Volume const&
0592 {
0593     vecgeom::VPlacedVolume const* physvol_ptr = vgstate_.Top();
0594     CELER_ENSURE(physvol_ptr);
0595     return *physvol_ptr->GetLogicalVolume();
0596 }
0597 
0598 //---------------------------------------------------------------------------//
0599 }  // namespace celeritas