Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-29 09:46: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   public:
0072     // Construct from persistent and state data
0073     inline CELER_FUNCTION VecgeomTrackView(ParamsRef const& data,
0074                                            StateRef const& stateview,
0075                                            TrackSlotId tid);
0076 
0077     // Initialize the state
0078     inline CELER_FUNCTION VecgeomTrackView&
0079     operator=(Initializer_t const& init);
0080 
0081     //// STATIC ACCESSORS ////
0082 
0083     //! A tiny push to make sure tracks do not get stuck at boundaries
0084     static CELER_CONSTEXPR_FUNCTION real_type extra_push() { return 1e-13; }
0085 
0086     //// ACCESSORS ////
0087 
0088     //!@{
0089     //! State accessors
0090     CELER_FORCEINLINE_FUNCTION Real3 const& pos() const { return pos_; }
0091     CELER_FORCEINLINE_FUNCTION Real3 const& dir() const { return dir_; }
0092     //!@}
0093 
0094     // Get the current volume's ID
0095     inline CELER_FUNCTION VolumeId volume_id() const;
0096     // Get the ID of the current volume instance
0097     inline CELER_FUNCTION VolumeInstanceId volume_instance_id() const;
0098     // Get the depth in the geometry hierarchy
0099     inline CELER_FUNCTION LevelId level() const;
0100     // Get the volume instance ID for all levels
0101     inline CELER_FUNCTION void
0102     volume_instance_id(Span<VolumeInstanceId> levels) const;
0103 
0104     // The current surface ID
0105     inline CELER_FUNCTION ImplSurfaceId impl_surface_id() const;
0106     // After 'find_next_step', the next straight-line surface
0107     inline CELER_FUNCTION ImplSurfaceId next_impl_surface_id() const;
0108 
0109     // Whether the track is outside the valid geometry region
0110     CELER_FORCEINLINE_FUNCTION bool is_outside() const;
0111     // Whether the track is exactly on a surface
0112     CELER_FORCEINLINE_FUNCTION bool is_on_boundary() const;
0113     //! Whether the last operation resulted in an error
0114     CELER_FORCEINLINE_FUNCTION bool failed() const { return false; }
0115     // Get the normal vector of the current surface
0116     inline CELER_FUNCTION Real3 normal() const;
0117 
0118     //// OPERATIONS ////
0119 
0120     // Find the distance to the next boundary (infinite max)
0121     inline CELER_FUNCTION Propagation find_next_step();
0122 
0123     // Find the distance to the next boundary, up to and including a step
0124     inline CELER_FUNCTION Propagation find_next_step(real_type max_step);
0125 
0126     // Find the safety at the current position (infinite max)
0127     inline CELER_FUNCTION real_type find_safety();
0128 
0129     // Find the safety at the current position up to a maximum step distance
0130     inline CELER_FUNCTION real_type find_safety(real_type max_step);
0131 
0132     // Move to the boundary in preparation for crossing it
0133     inline CELER_FUNCTION void move_to_boundary();
0134 
0135     // Move within the volume
0136     inline CELER_FUNCTION void move_internal(real_type step);
0137 
0138     // Move within the volume to a specific point
0139     inline CELER_FUNCTION void move_internal(Real3 const& pos);
0140 
0141     // Cross from one side of the current surface to the other
0142     inline CELER_FUNCTION void cross_boundary();
0143 
0144     // Change direction
0145     inline CELER_FUNCTION void set_dir(Real3 const& newdir);
0146 
0147   private:
0148     //// TYPES ////
0149 
0150     using Volume = vecgeom::LogicalVolume;
0151     using NavState = vecgeom::NavigationState;
0152 
0153     //// DATA ////
0154 
0155     //! Shared/persistent geometry data
0156     ParamsRef const& params_;
0157     StateRef const& state_;
0158     TrackSlotId tid_;
0159 
0160     //!@{
0161     //! Referenced thread-local data
0162     NavState& vgstate_;
0163     NavState& vgnext_;
0164     Real3& pos_;
0165     Real3& dir_;
0166 #ifdef VECGEOM_USE_SURF
0167     long& next_surface_;
0168 #endif
0169     //!@}
0170 
0171     // Temporary data
0172     real_type next_step_{0};
0173 
0174     //// HELPER FUNCTIONS ////
0175 
0176     // Whether any next distance-to-boundary has been found
0177     inline CELER_FUNCTION bool has_next_step() const;
0178 
0179     // Whether the next distance-to-boundary is to a surface
0180     inline CELER_FUNCTION bool is_next_boundary() const;
0181 
0182     //! Get a reference to the current volume
0183     inline CELER_FUNCTION Volume const& volume() const;
0184 
0185 #ifdef VECGEOM_USE_SURF
0186     static CELER_CONSTEXPR_FUNCTION long null_surface() { return -1; }
0187 #endif
0188 };
0189 
0190 //---------------------------------------------------------------------------//
0191 // INLINE DEFINITIONS
0192 //---------------------------------------------------------------------------//
0193 /*!
0194  * Construct from persistent and state data.
0195  */
0196 CELER_FUNCTION
0197 VecgeomTrackView::VecgeomTrackView(ParamsRef const& params,
0198                                    StateRef const& states,
0199                                    TrackSlotId tid)
0200     : params_(params)
0201     , state_(states)
0202     , tid_(tid)
0203     , vgstate_(states.vgstate.at(params_.max_depth, tid))
0204     , vgnext_(states.vgnext.at(params_.max_depth, tid))
0205     , pos_(states.pos[tid])
0206     , dir_(states.dir[tid])
0207 #ifdef VECGEOM_USE_SURF
0208     , next_surface_{states.next_surface[tid]}
0209 #endif
0210 {
0211 }
0212 
0213 //---------------------------------------------------------------------------//
0214 /*!
0215  * Construct the state.
0216  *
0217  * If a valid parent ID is provided, the state is constructed from a direction
0218  * and a copy of the parent state.  This is a faster method of creating
0219  * secondaries from a parent that has just been absorbed, or when filling in an
0220  * empty track from a parent that is still alive.
0221  *
0222  * Otherwise, the state is initialized from a starting location and direction,
0223  * which is expensive.
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 direction
0231     dir_ = init.dir;
0232 
0233     if (init.parent)
0234     {
0235         // Copy the navigation state and position from the parent state
0236         if (tid_ != init.parent)
0237         {
0238             VecgeomTrackView other(params_, state_, init.parent);
0239             other.vgstate_.CopyTo(&vgstate_);
0240             pos_ = other.pos_;
0241         }
0242         // Set up the next state and initialize the direction
0243         vgnext_ = vgstate_;
0244 
0245         CELER_ENSURE(this->pos() == init.pos);
0246         CELER_ENSURE(!this->has_next_step());
0247         return *this;
0248     }
0249 
0250     // Initialize the state from a position
0251     pos_ = init.pos;
0252 #ifdef VECGEOM_USE_SURF
0253     next_surface_ = null_surface();
0254 #endif
0255 
0256     // Set up current state and locate daughter volume.
0257     vgstate_.Clear();
0258     constexpr bool contains_point = true;
0259 #ifdef VECGEOM_USE_SURF
0260     auto world = vecgeom::NavigationState::WorldId();
0261 #else
0262     vecgeom::VPlacedVolume const* world = params_.world_volume;
0263 #endif
0264     // LocatePointIn sets `vgstate_`
0265     Navigator::LocatePointIn(
0266         world, detail::to_vector(pos_), vgstate_, contains_point);
0267     return *this;
0268 }
0269 
0270 //---------------------------------------------------------------------------//
0271 /*!
0272  * The current surface frame ID.
0273  */
0274 CELER_FUNCTION ImplSurfaceId VecgeomTrackView::impl_surface_id() const
0275 {
0276 #ifdef VECGEOM_USE_SURF
0277     if (this->is_on_boundary())
0278     {
0279         return id_cast<ImplSurfaceId>(next_surface_);
0280     }
0281     return {};
0282 #else
0283     CELER_ASSERT_UNREACHABLE();
0284 #endif
0285 }
0286 
0287 //---------------------------------------------------------------------------//
0288 /*!
0289  * After 'find_next_step', the next straight-line surface.
0290  */
0291 CELER_FUNCTION ImplSurfaceId VecgeomTrackView::next_impl_surface_id() const
0292 {
0293 #ifdef VECGEOM_USE_SURF
0294     if (!this->is_on_boundary())
0295     {
0296         return id_cast<ImplSurfaceId>(next_surface_);
0297     }
0298     return {};
0299 #else
0300     CELER_ASSERT_UNREACHABLE();
0301 #endif
0302 }
0303 
0304 //---------------------------------------------------------------------------//
0305 /*!
0306  * Get the volume ID in the current cell.
0307  */
0308 CELER_FORCEINLINE_FUNCTION VolumeId VecgeomTrackView::volume_id() const
0309 {
0310     CELER_EXPECT(!this->is_outside());
0311     return id_cast<VolumeId>(this->volume().id());
0312 }
0313 
0314 //---------------------------------------------------------------------------//
0315 /*!
0316  * Get the physical volume ID in the current cell.
0317  */
0318 CELER_FUNCTION VolumeInstanceId VecgeomTrackView::volume_instance_id() const
0319 {
0320     CELER_EXPECT(!this->is_outside());
0321     vecgeom::VPlacedVolume const* top = vgstate_.Top();
0322     CELER_ASSERT(top);
0323     return id_cast<VolumeInstanceId>(top->id());
0324 }
0325 
0326 //---------------------------------------------------------------------------//
0327 /*!
0328  * Get the depth in the geometry hierarchy.
0329  */
0330 CELER_FUNCTION LevelId VecgeomTrackView::level() const
0331 {
0332     return id_cast<LevelId>(vgstate_.GetLevel());
0333 }
0334 
0335 //---------------------------------------------------------------------------//
0336 /*!
0337  * Get the volume instance ID for all levels>
0338  */
0339 CELER_FUNCTION void
0340 VecgeomTrackView::volume_instance_id(Span<VolumeInstanceId> levels) const
0341 {
0342     CELER_EXPECT(levels.size() == this->level().get() + 1);
0343     for (auto lev : range(levels.size()))
0344     {
0345         vecgeom::VPlacedVolume const* pv = vgstate_.At(lev);
0346         CELER_ASSERT(pv);
0347         levels[lev] = id_cast<VolumeInstanceId>(vgstate_.At(lev)->id());
0348     }
0349 }
0350 
0351 //---------------------------------------------------------------------------//
0352 /*!
0353  * Whether the track is outside the valid geometry region.
0354  */
0355 CELER_FUNCTION bool VecgeomTrackView::is_outside() const
0356 {
0357     return vgstate_.IsOutside();
0358 }
0359 
0360 //---------------------------------------------------------------------------//
0361 /*!
0362  * Whether the track is on the boundary of a volume.
0363  */
0364 CELER_FUNCTION bool VecgeomTrackView::is_on_boundary() const
0365 {
0366     return vgstate_.IsOnBoundary();
0367 }
0368 
0369 //---------------------------------------------------------------------------//
0370 /*!
0371  * Get the surface normal of the boundary the track is currently on.
0372  */
0373 CELER_FUNCTION Real3 VecgeomTrackView::normal() const
0374 {
0375     CELER_NOT_IMPLEMENTED("VecgeomTrackView::normal");
0376 }
0377 
0378 //---------------------------------------------------------------------------//
0379 /*!
0380  * Find the distance to the next geometric boundary.
0381  *
0382  * This function is allowed to be called from the exterior for ray tracing.
0383  */
0384 CELER_FUNCTION Propagation VecgeomTrackView::find_next_step()
0385 {
0386     if (this->is_outside())
0387     {
0388         // SPECIAL CASE: find distance to interior from outside world volume
0389         auto* pplvol = params_.world_volume;
0390         next_step_ = pplvol->DistanceToIn(detail::to_vector(pos_),
0391                                           detail::to_vector(dir_),
0392                                           vecgeom::kInfLength);
0393 
0394         next_step_ = max(next_step_, this->extra_push());
0395         vgnext_.Clear();
0396         Propagation result;
0397         result.distance = next_step_;
0398         result.boundary = next_step_ < vecgeom::kInfLength;
0399 
0400         if (result.boundary)
0401         {
0402             vgnext_.Push(pplvol);
0403             vgnext_.SetBoundaryState(true);
0404         }
0405 
0406         return result;
0407     }
0408 
0409     return this->find_next_step(vecgeom::kInfLength);
0410 }
0411 
0412 //---------------------------------------------------------------------------//
0413 /*!
0414  * Find the distance to the next geometric boundary.
0415  */
0416 CELER_FUNCTION Propagation VecgeomTrackView::find_next_step(real_type max_step)
0417 {
0418     CELER_EXPECT(!this->is_outside());
0419     CELER_EXPECT(max_step > 0);
0420 
0421 #ifdef VECGEOM_USE_SURF
0422     // Use the navigator to find internal distance
0423     next_surface_ = null_surface();
0424 #endif
0425     // TODO: vgnext is simply copied and the boundary flag optionally set
0426     next_step_ = Navigator::ComputeStepAndNextVolume(detail::to_vector(pos_),
0427                                                      detail::to_vector(dir_),
0428                                                      max_step,
0429                                                      vgstate_,
0430                                                      vgnext_
0431 #ifdef VECGEOM_USE_SURF
0432                                                      ,
0433                                                      next_surface_
0434 #endif
0435     );
0436     next_step_ = max(next_step_, this->extra_push());
0437 
0438     if (!this->is_next_boundary())
0439     {
0440         // Soft equivalence between distance and max step is because the
0441         // BVH navigator subtracts and then re-adds a bump distance to the
0442         // step
0443         CELER_ASSERT(soft_equal(next_step_, max_step));
0444         next_step_ = max_step;
0445     }
0446 
0447     Propagation result;
0448     result.distance = next_step_;
0449     result.boundary = this->is_next_boundary();
0450 
0451     CELER_ENSURE(this->has_next_step());
0452     CELER_ENSURE(result.distance > 0);
0453     CELER_ENSURE(result.distance <= max(max_step, this->extra_push()));
0454     CELER_ENSURE(result.boundary || result.distance == max_step
0455                  || max_step < this->extra_push());
0456     return result;
0457 }
0458 
0459 //---------------------------------------------------------------------------//
0460 /*!
0461  * Find the safety at the current position.
0462  */
0463 CELER_FUNCTION real_type VecgeomTrackView::find_safety()
0464 {
0465     return this->find_safety(vecgeom::kInfLength);
0466 }
0467 
0468 //---------------------------------------------------------------------------//
0469 /*!
0470  * Find the safety at the current position up to a maximum distance.
0471  *
0472  * The safety within a step is only needed up to the end of the physics step
0473  * length.
0474  */
0475 CELER_FUNCTION real_type VecgeomTrackView::find_safety(real_type max_radius)
0476 {
0477     CELER_EXPECT(!this->is_outside());
0478     CELER_EXPECT(!this->is_on_boundary());
0479     CELER_EXPECT(max_radius > 0);
0480 
0481     real_type safety = Navigator::ComputeSafety(detail::to_vector(this->pos()),
0482                                                 vgstate_
0483 #if VECGEOM_VERSION >= 0x200000
0484                                                 ,
0485                                                 max_radius
0486 #endif
0487     );
0488     safety = min<real_type>(safety, max_radius);
0489 
0490     // Since the reported "safety" is negative if we've moved slightly beyond
0491     // the boundary of a solid without crossing it, we must clamp to zero.
0492     return max<real_type>(safety, 0);
0493 }
0494 
0495 //---------------------------------------------------------------------------//
0496 /*!
0497  * Move to the next boundary but don't cross yet.
0498  */
0499 CELER_FUNCTION void VecgeomTrackView::move_to_boundary()
0500 {
0501     CELER_EXPECT(this->has_next_step());
0502     CELER_EXPECT(this->is_next_boundary());
0503 
0504     // Move next step
0505     axpy(next_step_, dir_, &pos_);
0506     next_step_ = 0;
0507     vgstate_.SetBoundaryState(true);
0508 
0509     CELER_ENSURE(this->is_on_boundary());
0510 }
0511 
0512 //---------------------------------------------------------------------------//
0513 /*!
0514  * Cross from one side of the current surface to the other.
0515  *
0516  * The position *must* be on the boundary following a move-to-boundary.
0517  */
0518 CELER_FUNCTION void VecgeomTrackView::cross_boundary()
0519 {
0520     CELER_EXPECT(this->is_on_boundary());
0521     CELER_EXPECT(this->is_next_boundary());
0522 
0523     // Relocate to next tracking volume (maybe across multiple boundaries)
0524     if (vgnext_.Top() != nullptr)
0525     {
0526 #ifdef VECGEOM_USE_SURF
0527         if (!vgstate_.IsOutside())
0528         {
0529             // In surf model, relocation does not work from [OUTSIDE]
0530             // (no need to call this function) as vgnext_ is already set
0531             Navigator::RelocateToNextVolume(detail::to_vector(this->pos_),
0532                                             detail::to_vector(this->dir_),
0533                                             next_surface_,
0534                                             vgnext_);
0535         }
0536 #else
0537         // Some navigators require an lvalue temp_pos
0538         auto temp_pos = detail::to_vector(this->pos_);
0539         Navigator::RelocateToNextVolume(
0540             temp_pos, detail::to_vector(this->dir_), vgnext_);
0541 #endif
0542     }
0543 
0544     vgstate_ = vgnext_;
0545 
0546     CELER_ENSURE(this->is_on_boundary());
0547 }
0548 
0549 //---------------------------------------------------------------------------//
0550 /*!
0551  * Move within the current volume.
0552  *
0553  * The straight-line distance *must* be less than the distance to the
0554  * boundary.
0555  */
0556 CELER_FUNCTION void VecgeomTrackView::move_internal(real_type dist)
0557 {
0558     CELER_EXPECT(this->has_next_step());
0559     CELER_EXPECT(dist > 0 && dist <= next_step_);
0560     CELER_EXPECT(dist != next_step_ || !this->is_next_boundary());
0561 
0562     // Move and update next_step_
0563     axpy(dist, dir_, &pos_);
0564     next_step_ -= dist;
0565     vgstate_.SetBoundaryState(false);
0566 
0567     CELER_ENSURE(!this->is_on_boundary());
0568 }
0569 
0570 //---------------------------------------------------------------------------//
0571 /*!
0572  * Move within the current volume to a nearby point.
0573  *
0574  * \warning It's up to the caller to make sure that the position is
0575  * "nearby" and within the same volume.
0576  */
0577 CELER_FUNCTION void VecgeomTrackView::move_internal(Real3 const& pos)
0578 {
0579     pos_ = pos;
0580     next_step_ = 0;
0581     vgstate_.SetBoundaryState(false);
0582 
0583     CELER_ENSURE(!this->is_on_boundary());
0584 }
0585 
0586 //---------------------------------------------------------------------------//
0587 /*!
0588  * Change the track's direction.
0589  *
0590  * This happens after a scattering event or movement inside a magnetic field.
0591  * It resets the calculated distance-to-boundary.
0592  */
0593 CELER_FUNCTION void VecgeomTrackView::set_dir(Real3 const& newdir)
0594 {
0595     CELER_EXPECT(is_soft_unit_vector(newdir));
0596     dir_ = newdir;
0597     next_step_ = 0;
0598 }
0599 
0600 //---------------------------------------------------------------------------//
0601 // PRIVATE MEMBER FUNCTIONS
0602 //---------------------------------------------------------------------------//
0603 /*!
0604  * Whether a next step has been calculated.
0605  */
0606 CELER_FUNCTION bool VecgeomTrackView::has_next_step() const
0607 {
0608     return next_step_ != 0;
0609 }
0610 
0611 //---------------------------------------------------------------------------//
0612 /*!
0613  * Whether the calculated next step will take track to next boundary.
0614  */
0615 CELER_FUNCTION bool VecgeomTrackView::is_next_boundary() const
0616 {
0617     CELER_EXPECT(this->has_next_step() || this->is_on_boundary());
0618     return vgnext_.IsOnBoundary();
0619 }
0620 
0621 //---------------------------------------------------------------------------//
0622 /*!
0623  * Get a reference to the current volume, or to world volume if outside.
0624  */
0625 CELER_FUNCTION auto VecgeomTrackView::volume() const -> Volume const&
0626 {
0627     vecgeom::VPlacedVolume const* physvol_ptr = vgstate_.Top();
0628     CELER_ENSURE(physvol_ptr);
0629     return *physvol_ptr->GetLogicalVolume();
0630 }
0631 
0632 //---------------------------------------------------------------------------//
0633 }  // namespace celeritas