Back to home page

EIC code displayed by LXR

 
 

    


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

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2023-2024 UT-Battelle, LLC, and other Celeritas developers.
0003 // See the top-level COPYRIGHT file for details.
0004 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0005 //---------------------------------------------------------------------------//
0006 //! \file geocel/g4/GeantGeoTrackView.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <algorithm>
0011 #include <type_traits>
0012 #include <G4LogicalVolume.hh>
0013 #include <G4Navigator.hh>
0014 #include <G4TouchableHandle.hh>
0015 #include <G4TouchableHistory.hh>
0016 
0017 #include "corecel/Macros.hh"
0018 #include "corecel/math/Algorithms.hh"
0019 #include "corecel/math/ArrayUtils.hh"
0020 #include "corecel/math/SoftEqual.hh"
0021 #include "geocel/Types.hh"
0022 
0023 #include "Convert.geant.hh"
0024 #include "GeantGeoData.hh"
0025 
0026 namespace celeritas
0027 {
0028 //---------------------------------------------------------------------------//
0029 /*!
0030  * Navigate through a Geant4 geometry on a single thread.
0031  *
0032  * This wraps a Geant4 geometry navigator and volume hierarchy state with the
0033  * same Celeritas tracker interface. It's not going to be the most efficient
0034  * code since the \c G4Navigator includes a lot of helper functions for
0035  * managing safety distance, tracking through a field, etc. We also
0036  * independently store a "celeritas" native position and direction, as well as
0037  * duplicating the "geant4" position and direction that are also stored under
0038  * the hood in the heavyweight navigator.
0039  *
0040  * For a description of ordering requirements, see: \sa OrangeTrackView .
0041  */
0042 class GeantGeoTrackView
0043 {
0044   public:
0045     //!@{
0046     //! \name Type aliases
0047     using Initializer_t = GeoTrackInitializer;
0048     using ParamsRef = NativeCRef<GeantGeoParamsData>;
0049     using StateRef = NativeRef<GeantGeoStateData>;
0050     using real_type = double;
0051     using Real3 = Array<real_type, 3>;
0052     //!@}
0053 
0054     //! Helper struct for initializing from an existing geometry state
0055     struct DetailedInitializer
0056     {
0057         GeantGeoTrackView const& other;  //!< Existing geometry
0058         Real3 dir;  //!< New direction
0059     };
0060 
0061   public:
0062     // Construct from params and state data
0063     inline GeantGeoTrackView(ParamsRef const& params,
0064                              StateRef const& state,
0065                              TrackSlotId tid);
0066 
0067     // Initialize the state
0068     inline GeantGeoTrackView& operator=(Initializer_t const& init);
0069     // Initialize the state from a parent state and new direction
0070     inline GeantGeoTrackView& operator=(DetailedInitializer const& init);
0071 
0072     //// STATIC ACCESSORS ////
0073 
0074     //! A tiny push to make sure tracks do not get stuck at boundaries
0075     static constexpr real_type extra_push()
0076     {
0077         return 1e-12 * lengthunits::millimeter;
0078     }
0079 
0080     //// ACCESSORS ////
0081 
0082     //!@{
0083     //! State accessors
0084     CELER_FORCEINLINE Real3 const& pos() const { return pos_; }
0085     CELER_FORCEINLINE Real3 const& dir() const { return dir_; }
0086     //!@}
0087 
0088     // Get the volume ID in the current cell.
0089     CELER_FORCEINLINE VolumeId volume_id() const;
0090     CELER_FORCEINLINE int volume_physid() const;
0091 
0092     //!@{
0093     //! VecGeom states are never "on" a surface
0094     SurfaceId surface_id() const { return {}; }
0095     SurfaceId next_surface_id() const { return {}; }
0096     //!@}
0097 
0098     // Whether the track is outside the valid geometry region
0099     CELER_FORCEINLINE bool is_outside() const;
0100     // Whether the track is exactly on a surface
0101     CELER_FORCEINLINE bool is_on_boundary() const;
0102     //! Whether the last operation resulted in an error
0103     CELER_FORCEINLINE bool failed() const { return false; }
0104 
0105     //// OPERATIONS ////
0106 
0107     // Find the distance to the next boundary (infinite max)
0108     inline Propagation find_next_step();
0109 
0110     // Find the distance to the next boundary, up to and including a step
0111     inline Propagation find_next_step(real_type max_step);
0112 
0113     // Find the safety at the current position
0114     inline real_type find_safety();
0115 
0116     // Find the safety at the current position up to a maximum step distance
0117     inline CELER_FUNCTION real_type find_safety(real_type max_step);
0118 
0119     // Move to the boundary in preparation for crossing it
0120     inline void move_to_boundary();
0121 
0122     // Move within the volume
0123     inline void move_internal(real_type step);
0124 
0125     // Move within the volume to a specific point
0126     inline void move_internal(Real3 const& pos);
0127 
0128     // Cross from one side of the current surface to the other
0129     inline void cross_boundary();
0130 
0131     // Change direction
0132     inline void set_dir(Real3 const& newdir);
0133 
0134   private:
0135     //// DATA ////
0136 
0137     //!@{
0138     //! Referenced thread-local data
0139     Real3& pos_;
0140     Real3& dir_;
0141     real_type& next_step_;
0142     real_type& safety_radius_;
0143     G4TouchableHandle& touch_handle_;
0144     G4Navigator& navi_;
0145     //!@}
0146 
0147     // Temporary data
0148     G4ThreeVector g4pos_;
0149     G4ThreeVector g4dir_;  // [mm]
0150     real_type g4safety_;  // [mm]
0151 
0152     //// HELPER FUNCTIONS ////
0153 
0154     // Whether any next distance-to-boundary has been found
0155     inline bool has_next_step() const;
0156 
0157     //! Get a pointer to the current volume; null if outside
0158     inline G4LogicalVolume const* volume() const;
0159 };
0160 
0161 //---------------------------------------------------------------------------//
0162 // INLINE DEFINITIONS
0163 //---------------------------------------------------------------------------//
0164 /*!
0165  * Construct from params and state data.
0166  */
0167 GeantGeoTrackView::GeantGeoTrackView(ParamsRef const&,
0168                                      StateRef const& states,
0169                                      TrackSlotId tid)
0170     : pos_(states.pos[tid])
0171     , dir_(states.dir[tid])
0172     , next_step_(states.next_step[tid])
0173     , safety_radius_(states.safety_radius[tid])
0174     , touch_handle_(states.nav_state.touch_handle(tid))
0175     , navi_(states.nav_state.navigator(tid))
0176 {
0177     g4pos_ = convert_to_geant(pos_, clhep_length);
0178     g4dir_ = convert_to_geant(dir_, 1);
0179     g4safety_ = convert_to_geant(safety_radius_, clhep_length);
0180 }
0181 
0182 //---------------------------------------------------------------------------//
0183 /*!
0184  * Construct the state.
0185  */
0186 GeantGeoTrackView& GeantGeoTrackView::operator=(Initializer_t const& init)
0187 {
0188     CELER_EXPECT(is_soft_unit_vector(init.dir));
0189 
0190     // Initialize position/direction
0191     std::copy(init.pos.begin(), init.pos.end(), pos_.begin());
0192     std::copy(init.dir.begin(), init.dir.end(), dir_.begin());
0193     next_step_ = 0;
0194     safety_radius_ = -1;  // Assume *not* on a boundary
0195 
0196     g4pos_ = convert_to_geant(pos_, clhep_length);
0197     g4dir_ = convert_to_geant(dir_, 1);
0198     g4safety_ = -1;
0199 
0200     navi_.LocateGlobalPointAndUpdateTouchable(g4pos_,
0201                                               g4dir_,
0202                                               touch_handle_(),
0203                                               /* relative_search = */ false);
0204 
0205     CELER_ENSURE(!this->has_next_step());
0206     return *this;
0207 }
0208 
0209 //---------------------------------------------------------------------------//
0210 /*!
0211  * Construct the state from a direction and a copy of the parent state.
0212  *
0213  * \c G4Track::SetTouchableHandle from \c G4VEmProcess::PostStepDoIt
0214  *
0215  * maybe see \c G4SteppingManager::Stepping
0216  */
0217 GeantGeoTrackView& GeantGeoTrackView::operator=(DetailedInitializer const& init)
0218 {
0219     CELER_EXPECT(is_soft_unit_vector(init.dir));
0220 
0221     if (this != &init.other)
0222     {
0223         // Copy values from the parent state
0224         pos_ = init.other.pos_;
0225         safety_radius_ = init.other.safety_radius_;
0226         g4pos_ = init.other.g4pos_;
0227         g4dir_ = init.other.g4dir_;
0228         g4safety_ = init.other.g4safety_;
0229 
0230         // Update the touchable and navigator
0231         touch_handle_ = init.other.touch_handle_;
0232         navi_.ResetHierarchyAndLocate(
0233             g4pos_, g4dir_, dynamic_cast<G4TouchableHistory&>(*touch_handle_()));
0234     }
0235 
0236     // Set up the next state and initialize the direction
0237     dir_ = init.dir;
0238     next_step_ = 0;
0239 
0240     CELER_ENSURE(!this->has_next_step());
0241     return *this;
0242 }
0243 
0244 //---------------------------------------------------------------------------//
0245 /*!
0246  * Get the volume ID in the current cell.
0247  */
0248 VolumeId GeantGeoTrackView::volume_id() const
0249 {
0250     CELER_EXPECT(!this->is_outside());
0251     auto inst_id = this->volume()->GetInstanceID();
0252     CELER_ENSURE(inst_id >= 0);
0253     return VolumeId{static_cast<VolumeId::size_type>(inst_id)};
0254 }
0255 
0256 //---------------------------------------------------------------------------//
0257 /*!
0258  * Get the physical volume ID in the current cell.
0259  */
0260 int GeantGeoTrackView::volume_physid() const
0261 {
0262     CELER_EXPECT(!this->is_outside());
0263     G4VPhysicalVolume* pv = touch_handle_()->GetVolume(0);
0264     if (!pv)
0265         return -1;
0266     return pv->GetInstanceID();
0267 }
0268 
0269 //---------------------------------------------------------------------------//
0270 /*!
0271  * Whether the track is outside the valid geometry region.
0272  */
0273 bool GeantGeoTrackView::is_outside() const
0274 {
0275     return this->volume() == nullptr;
0276 }
0277 
0278 //---------------------------------------------------------------------------//
0279 /*!
0280  * Whether the track is on the boundary of a volume.
0281  */
0282 bool GeantGeoTrackView::is_on_boundary() const
0283 {
0284     return safety_radius_ == 0.0;
0285 }
0286 
0287 //---------------------------------------------------------------------------//
0288 /*!
0289  * Find the distance to the next geometric boundary.
0290  */
0291 Propagation GeantGeoTrackView::find_next_step()
0292 {
0293     return this->find_next_step(numeric_limits<real_type>::infinity());
0294 }
0295 
0296 //---------------------------------------------------------------------------//
0297 /*!
0298  * Find the distance to the next geometric boundary.
0299  *
0300  * It seems that ComputeStep cannot be called twice in a row without an
0301  * intermediate call to \c LocateGlobalPointWithinVolume: the safety will be
0302  * set to zero.
0303  */
0304 Propagation GeantGeoTrackView::find_next_step(real_type max_step)
0305 {
0306     CELER_EXPECT(!this->is_outside());
0307     CELER_EXPECT(max_step > 0);
0308 
0309     // Compute the step
0310     real_type g4step = convert_to_geant(max_step, clhep_length);
0311     g4step = navi_.ComputeStep(g4pos_, g4dir_, g4step, g4safety_);
0312 
0313     if (g4safety_ != 0 && !this->is_on_boundary())
0314     {
0315         // Save the resulting safety distance if computed: allow to be
0316         // "negative" to prevent accidentally changing the boundary state
0317         safety_radius_ = convert_from_geant(g4safety_, clhep_length);
0318         CELER_ASSERT(!this->is_on_boundary());
0319     }
0320 
0321     // Update result
0322     Propagation result;
0323     result.distance = convert_from_geant(g4step, clhep_length);
0324     if (result.distance <= max_step)
0325     {
0326         result.boundary = true;
0327         result.distance
0328             = celeritas::max<real_type>(result.distance, this->extra_push());
0329         CELER_ENSURE(result.distance > 0);
0330     }
0331     else
0332     {
0333         // No intersection in range -> G4Navigator returns kInfinity
0334         result.distance = max_step;
0335         CELER_ENSURE(result.distance > 0);
0336     }
0337 
0338     // Save the next step
0339     next_step_ = result.distance;
0340 
0341     CELER_ENSURE(result.distance > 0);
0342     CELER_ENSURE(result.distance <= max(max_step, this->extra_push()));
0343     CELER_ENSURE(result.boundary || result.distance == max_step
0344                  || max_step < this->extra_push());
0345     CELER_ENSURE(this->has_next_step());
0346     return result;
0347 }
0348 
0349 //---------------------------------------------------------------------------//
0350 /*!
0351  * Find the safety at the current position.
0352  */
0353 auto GeantGeoTrackView::find_safety() -> real_type
0354 {
0355     return this->find_safety(numeric_limits<real_type>::infinity());
0356 }
0357 
0358 //---------------------------------------------------------------------------//
0359 /*!
0360  * Find the safety at the current position.
0361  *
0362  * \warning This can change the boundary state if the track was moved to or
0363  * initialized a point on the boundary.
0364  */
0365 auto GeantGeoTrackView::find_safety(real_type max_step) -> real_type
0366 {
0367     CELER_EXPECT(max_step > 0);
0368     if (!this->is_on_boundary() && (safety_radius_ < max_step))
0369     {
0370         real_type g4step = convert_to_geant(max_step, clhep_length);
0371         g4safety_ = navi_.ComputeSafety(g4pos_, g4step);
0372         safety_radius_ = max(convert_from_geant(g4safety_, clhep_length), 0.0);
0373     }
0374 
0375     return safety_radius_;
0376 }
0377 
0378 //---------------------------------------------------------------------------//
0379 /*!
0380  * Move to the next boundary but don't cross yet.
0381  */
0382 void GeantGeoTrackView::move_to_boundary()
0383 {
0384     CELER_EXPECT(this->has_next_step());
0385 
0386     // Move next step
0387     axpy(next_step_, dir_, &pos_);
0388     axpy(convert_to_geant(next_step_, clhep_length), g4dir_, &g4pos_);
0389     next_step_ = 0;
0390     safety_radius_ = 0;
0391     g4safety_ = 0;
0392     navi_.SetGeometricallyLimitedStep();
0393 
0394     CELER_ENSURE(this->is_on_boundary());
0395 }
0396 
0397 //---------------------------------------------------------------------------//
0398 /*!
0399  * Cross from one side of the current surface to the other.
0400  *
0401  * The position *must* be on the boundary following a move-to-boundary.
0402  */
0403 void GeantGeoTrackView::cross_boundary()
0404 {
0405     CELER_EXPECT(this->is_on_boundary());
0406 
0407     navi_.LocateGlobalPointAndUpdateTouchableHandle(
0408         g4pos_,
0409         g4dir_,
0410         touch_handle_,
0411         /* relative_search = */ true);
0412 
0413     CELER_ENSURE(this->is_on_boundary());
0414 }
0415 
0416 //---------------------------------------------------------------------------//
0417 /*!
0418  * Move within the current volume.
0419  *
0420  * The straight-line distance *must* be less than the distance to the
0421  * boundary.
0422  */
0423 void GeantGeoTrackView::move_internal(real_type dist)
0424 {
0425     CELER_EXPECT(this->has_next_step());
0426     CELER_EXPECT(dist > 0 && dist <= next_step_);
0427 
0428     // Move and update next_step
0429     axpy(dist, dir_, &pos_);
0430     axpy(convert_to_geant(dist, clhep_length), g4dir_, &g4pos_);
0431     next_step_ -= dist;
0432     navi_.LocateGlobalPointWithinVolume(g4pos_);
0433 
0434     safety_radius_ = -1;
0435     g4safety_ = 0;
0436 }
0437 
0438 //---------------------------------------------------------------------------//
0439 /*!
0440  * Move within the current volume to a nearby point.
0441  *
0442  * See \c G4PathFinder::ReLocate from \c G4SafetyHelper::ReLocateWithinVolume
0443  * from \c G4VMultipleScattering::AlongStepDoIt .
0444  */
0445 void GeantGeoTrackView::move_internal(Real3 const& pos)
0446 {
0447     pos_ = pos;
0448     g4pos_ = convert_to_geant(pos_, clhep_length);
0449     next_step_ = 0;
0450     navi_.LocateGlobalPointWithinVolume(g4pos_);
0451 
0452     safety_radius_ = -1;
0453     g4safety_ = 0;
0454 }
0455 
0456 //---------------------------------------------------------------------------//
0457 /*!
0458  * Change the track's direction.
0459  *
0460  * This happens after a scattering event or movement inside a magnetic field.
0461  * It resets the calculated distance-to-boundary.
0462  */
0463 void GeantGeoTrackView::set_dir(Real3 const& newdir)
0464 {
0465     CELER_EXPECT(is_soft_unit_vector(newdir));
0466     dir_ = newdir;
0467     g4dir_ = convert_to_geant(newdir, 1);
0468     next_step_ = 0;
0469 }
0470 
0471 //---------------------------------------------------------------------------//
0472 // PRIVATE MEMBER FUNCTIONS
0473 //---------------------------------------------------------------------------//
0474 /*!
0475  * Whether a next step has been calculated.
0476  */
0477 bool GeantGeoTrackView::has_next_step() const
0478 {
0479     return next_step_ != 0;
0480 }
0481 
0482 //---------------------------------------------------------------------------//
0483 /*!
0484  * Get a reference to the current volume, or to world volume if outside.
0485  */
0486 auto GeantGeoTrackView::volume() const -> G4LogicalVolume const*
0487 {
0488     CELER_EXPECT(touch_handle_());
0489     G4VPhysicalVolume* pv = touch_handle_()->GetVolume(0);
0490     if (!pv)
0491         return nullptr;
0492 
0493     return pv->GetLogicalVolume();
0494 }
0495 
0496 //---------------------------------------------------------------------------//
0497 }  // namespace celeritas