|
|
|||
File indexing completed on 2026-11-13 11:00:10
0001 //------------------------------- -*- C++ -*- -------------------------------// 0002 // Copyright VecGeom contributors: see top-level LICENSE file for details 0003 // SPDX-License-Identifier: Apache-2.0 0004 //---------------------------------------------------------------------------// 0005 //! \file VecGeom/navigation/NavView.h 0006 //---------------------------------------------------------------------------// 0007 #pragma once 0008 0009 #include <cmath> 0010 0011 #include "VecGeom/base/Assert.h" 0012 #include "VecGeom/base/Math.h" 0013 #include "VecGeom/base/Macros.h" 0014 #include "VecGeom/base/MemSpace.h" 0015 #include "VecGeom/base/Vector3D.h" 0016 #include "VecGeom/navigation/BVHNavigator.h" 0017 #include "VecGeom/volumes/PlacedVolume.h" 0018 #include "NavFindResult.h" 0019 #include "NavPath.h" 0020 #include "NavParams.h" 0021 0022 //---------------------------------------------------------------------------// 0023 /*! For testing and development (and required by unit tests, always check navigation inputs/outputs. 0024 * When switching to production use, these will be moved to a wrapper class. 0025 * For now, define a VECGEOM_NAV_DEBUG macro that can be disabled by downstream code. 0026 */ 0027 #ifndef VECGEOM_NAV_DEBUG 0028 #if VECGEOM_DEVICE_COMPILE 0029 // Never use runtime assertions in CUDA code 0030 #define VECGEOM_NAV_DEBUG 0 0031 #else 0032 // Default assertions to on 0033 #define VECGEOM_NAV_DEBUG 1 0034 #endif 0035 #endif 0036 0037 #if VECGEOM_NAV_DEBUG 0038 #define VECGEOM_NAV_VALIDATE(COND, MSG) VECGEOM_VALIDATE(COND, MSG) 0039 #else 0040 #define VECGEOM_NAV_VALIDATE(COND, MSG) VECGEOM_DISCARD(COND) 0041 #endif 0042 0043 namespace vecgeom { 0044 0045 //---------------------------------------------------------------------------// 0046 0047 // Note that vectors are different types on host and device... 0048 VECGEOM_HOST_FORWARD_DECLARE(template <class T> class Vector3D;); 0049 VECGEOM_DEVICE_FORWARD_DECLARE(template <class T> class Vector3D;); 0050 template <class T, MemSpace M> 0051 using Vector3 = MemSpaceCond_t<M, vecgeom::cxx::Vector3D<T>, vecgeom::cuda::Vector3D<T>>; 0052 0053 using Real3 = Vector3<Precision, MemSpace::native>; 0054 0055 //---------------------------------------------------------------------------// 0056 0057 /*! 0058 * Fundamental VecGeom navigation interface for a single thread. 0059 * 0060 * \c NavView is an ephemeral wrapper around externally owned VecGeom 0061 * navigation state. It provides direct access to VecGeom navigator 0062 * primitives regardless of storage, track-slot management, execution 0063 * architecture, or implementation navigator/state. 0064 * 0065 * The view holds non-owning references to: 0066 * - Shared \c NavParams: stores world volume and possibly other 0067 * thread-independent navigation data. (Currently the NavParams::world member 0068 * is only required for initializing from \c UnknownPath and may be left as nullptr.) 0069 * - Current \c NavState: encodes the current + previous navigation path 0070 * and surface-crossing flag. 0071 * - Next \c NavState: scratch buffer used for half-relocation. 0072 * - Global position as a \c Span<Real,3>. 0073 * - Global direction as a \c Span<Real,3>. 0074 * 0075 * The caller is responsible for keeping track of the on-boundary flag and the 0076 * straight-line distance between operations. 0077 * 0078 * The \c OpaquePath is a low-level, POD-compatible representation of a 0079 * navigation path that is selected at 0080 * build time: 0081 * - \c NavIndex when \c VECGEOM_USE_NAVINDEX is set. 0082 * - \c NavTuple when \c VECGEOM_USE_NAVTUPLE is set. 0083 * G4VG converts between Geant4 touchable paths and \c OpaquePath. 0084 * 0085 * \par Initialization 0086 * 0087 * Initializing the state wrapped by a view is done in one of two ways: 0088 * - <em>Directly from a full navigation path.</em> This use case is for 0089 * converting directly from a Geant4 "touchable history" or for cloning a 0090 * navigation state. 0091 * - <em>By performing a full point-in-volume search based on a starting 0092 * position.</em> This is needed to initialize "primary" particles which have 0093 * no geometric metadata. 0094 * 0095 * \par Typical straight-tracking usage 0096 * \code 0097 * NavView view(setup, cur_state, nxt_state, pos_span, dir_span); 0098 * view.Initialize(unknown_path, pos_span, dir_span); 0099 * NavFindResult nfr = view.FindNextBoundary(max_dist); 0100 * if (nfr.GetKind() == NavFindResultKind::hit) { 0101 * view.MoveToBoundary(nfr.GetDistance()); 0102 * auto normal = view.CalcNextNormal(); // retain before crossing if needed 0103 * view.CrossBoundary(); 0104 * elseif (nfr.GetKind() == NavFindResultKind::reentrant) { 0105 * auto normal = view.CalcNextNormal(); // may be needed, or may use previous retained 0106 * view.CrossBoundary(); 0107 * } else if (nfr.GetKind() == NavFindResultKind::miss) { 0108 * view.MoveInternal(max_dist); 0109 * } else { 0110 * // Handle an invalid boundary result 0111 * } 0112 * \endcode 0113 * 0114 * \note See \c navigation.md for additional high-level documentation. 0115 * 0116 * \note All points are in the "global" (world) coordinate system unless otherwise specified. 0117 */ 0118 class NavView { 0119 public: 0120 //!@{ 0121 //! \name Type aliases 0122 using Real = ::vecgeom::Precision; 0123 using Real3 = ::vecgeom::Real3; 0124 using SpanReal3 = Real3 &; 0125 using SpanConstReal3 = Real3 const &; 0126 using NavState = ::vecgeom::NavigationState; 0127 using LogicalVolumeId = unsigned int; 0128 using PlacedVolumeId = unsigned int; 0129 using OpaquePath = OpaqueNavPath; 0130 using NavSetupT = NavParams<MemSpace::native>; 0131 //!@} 0132 0133 // Construct a view to externally owned navigation state and position/direction 0134 inline VECGEOM_FUNCTION NavView(NavSetupT const &setup, NavState &cur_state, NavState &nxt_state, SpanReal3 pos, 0135 SpanReal3 dir); 0136 0137 //// STATE-INDEPENDENT FUNCTIONS //// 0138 0139 //! Opaque path indicating "outside" 0140 static VECGEOM_FUNCTION OpaquePath GetOutsidePath() { return NavIndex_t{0}; } 0141 0142 //! Opaque path indicating "world" 0143 static VECGEOM_FUNCTION OpaquePath GetWorldPath() { return NavIndex_t{1}; } 0144 0145 //// INITIALIZATION //// 0146 0147 // Initialize navigation from an existing opaque path and boundary state 0148 inline VECGEOM_FUNCTION void Initialize(FullPath fp, SpanConstReal3 pos, SpanConstReal3 dir); 0149 0150 // Initialize navigation from position and direction within the world 0151 inline VECGEOM_FUNCTION void Initialize(UnknownPath, SpanConstReal3 pos, SpanConstReal3 dir); 0152 0153 //// OPERATIONS //// 0154 0155 // Compute a conservative isotropic distance to the nearest boundary 0156 inline VECGEOM_FUNCTION Real FindSafety(Real max); 0157 0158 // Find the straight-line distance to the next boundary 0159 inline VECGEOM_FUNCTION NavFindResult FindNextBoundary(Real max); 0160 0161 // Change the current track direction 0162 inline VECGEOM_FUNCTION void ChangeDirection(SpanConstReal3 dir); 0163 0164 // Move to an arbitrary position within the current safety sphere 0165 inline VECGEOM_FUNCTION void MoveInternal(SpanConstReal3 pos); 0166 0167 // Move along the current direction without reaching a boundary 0168 inline VECGEOM_FUNCTION void MoveInternal(Real step); 0169 0170 // Advance to the boundary at the exact distance returned b 0171 inline VECGEOM_FUNCTION void MoveToBoundary(Real step); 0172 0173 // Compute the global boundary normal pointing from current to next state 0174 inline VECGEOM_FUNCTION Real3 CalcNextNormal() const; 0175 0176 // Cross the current boundary and update the navigation state 0177 inline VECGEOM_FUNCTION void CrossBoundary(); 0178 0179 //// ACCESSORS //// 0180 0181 // Whether the navigation state is outside the world or uninitialized 0182 inline VECGEOM_FUNCTION bool IsOutside() const; 0183 0184 // Whether the navigation state is on the boundary of a volume 0185 inline VECGEOM_FUNCTION bool IsOnBoundary() const; 0186 0187 // VecGeom logical volume ID of the current volume (NOT outside) 0188 inline VECGEOM_FUNCTION LogicalVolumeId GetLogicalVolumeId() const; 0189 0190 // VecGeom placed volume ID of the current volume instance (NOT outside) 0191 inline VECGEOM_FUNCTION PlacedVolumeId GetPlacedVolumeId() const; 0192 0193 // Access the current navigation path as an opaque value 0194 inline VECGEOM_FUNCTION OpaquePath const &GetOpaquePath() const; 0195 0196 //! Current global position. 0197 VECGEOM_FUNCTION SpanConstReal3 GetPosition() const { return pos_; } 0198 0199 //! Current global direction (unit vector). 0200 VECGEOM_FUNCTION SpanConstReal3 GetDirection() const { return dir_; } 0201 0202 private: 0203 //// TYPES //// 0204 0205 using Navigator = ::vecgeom::BVHNavigator; 0206 0207 //// DATA //// 0208 0209 NavSetupT const &setup_; //!< Navigation setup 0210 NavState &cur_state_; //!< Current navigation path 0211 NavState &nxt_state_; //!< Scratch "next" navigation path 0212 SpanReal3 pos_; //!< Global position [cm] 0213 SpanReal3 dir_; //!< Global direction (unit vector) 0214 0215 //// HELPER FUNCTIONS //// 0216 0217 // Move a straight-line distance without error checking 0218 inline VECGEOM_FUNCTION void MoveInternalImpl(Real distance); 0219 0220 //! Copy data from a const span to a span: this will change based on the definition of Span3 0221 static VECGEOM_FUNCTION void CopyFrom(SpanReal3 dst, SpanConstReal3 src) { dst = src; } 0222 0223 // Validate normalized directions: will be replaced by corecel::ArraySoftUnit 0224 inline static VECGEOM_FUNCTION bool IsUnitNormal(SpanConstReal3 v); 0225 }; 0226 0227 //---------------------------------------------------------------------------// 0228 // CONSTRUCTOR 0229 //---------------------------------------------------------------------------// 0230 0231 /*! 0232 * Construct from externally owned navigation state and position/direction. 0233 * 0234 * All arguments may be borrowed references; the caller must ensure they 0235 * outlive this view. 0236 * 0237 * \param setup Global (shared) data about the VecGeom navigation definition 0238 * \param cur_state Current navigation state (encodes path + boundary 0239 * flag). 0240 * \param nxt_state Scratch navigation state used as a "next" buffer for 0241 * half-relocation. 0242 * \param pos Mutable pointer to the global position 0243 * \param dir Mutable pointer to the global direction 0244 */ 0245 VECGEOM_FUNCTION NavView::NavView(NavSetupT const &setup, NavState &cur_state, NavState &nxt_state, SpanReal3 pos, 0246 SpanReal3 dir) 0247 : setup_{setup}, cur_state_{cur_state}, nxt_state_{nxt_state}, pos_{pos}, dir_{dir} 0248 { 0249 VECGEOM_NAV_VALIDATE(&cur_state_ != &nxt_state_, << "current and next must point to different objects"); 0250 } 0251 0252 //---------------------------------------------------------------------------// 0253 // INITIALIZATION 0254 //---------------------------------------------------------------------------// 0255 0256 /*! 0257 * Initialize navigation from an existing opaque path (CPU only). 0258 * 0259 * Intended for ADePT-style use where a Geant4 touchable path has already 0260 * been converted to an \c OpaquePath by G4VG. 0261 * 0262 * \pre \c pos must lie inside or on the boundary of the volume encoded by 0263 * \c path, and must not belong to a child volume of it. 0264 * \pre \c dir must be normalised to unit length within machine precision. 0265 */ 0266 VECGEOM_FUNCTION void NavView::Initialize(FullPath fp, SpanConstReal3 pos, SpanConstReal3 dir) 0267 { 0268 VECGEOM_NAV_VALIDATE(IsUnitNormal(dir), << "direction " << dir << " is not a unit vector"); 0269 0270 // Copy position and direction into the externally owned storage 0271 CopyFrom(pos_, pos); 0272 CopyFrom(dir_, dir); 0273 0274 // Clear current and next states 0275 cur_state_.Clear(); 0276 nxt_state_.Clear(); 0277 0278 // NOTE: SetNavIndex can actually take a tuple when VECGEOM_USE_NAVTUPLE 0279 cur_state_.SetNavIndex(fp.path); 0280 cur_state_.SetBoundaryState(fp.boundary); 0281 } 0282 0283 /*! 0284 * Initialize navigation by searching for the given position and direction. 0285 * 0286 * Performs a full geometry location to find the enclosing volume. Works 0287 * on both host and device, and is the preferred initialisation path for 0288 * generating primary particles from spatial distributions (e.g., uniform 0289 * box) on device. It is allowed for the position to be outside or on the 0290 * boundary of the world. 0291 * 0292 * \pre \c dir must be normalised to unit length within machine precision. 0293 */ 0294 VECGEOM_FUNCTION void NavView::Initialize(UnknownPath, SpanConstReal3 pos, SpanConstReal3 dir) 0295 { 0296 VECGEOM_NAV_VALIDATE(IsUnitNormal(dir), << "direction " << dir << " is not a unit vector"); 0297 // Copy position and direction into the externally owned storage 0298 CopyFrom(pos_, pos); 0299 CopyFrom(dir_, dir); 0300 0301 // Clear current and nexdt states 0302 cur_state_.Clear(); 0303 nxt_state_.Clear(); 0304 0305 auto const *world = setup_.world; 0306 VECGEOM_NAV_VALIDATE(world != nullptr, << "world is required for unknown-path initialization"); 0307 constexpr bool check_top = true; 0308 0309 auto *top_pv = Navigator::LocatePointIn(world, pos_, cur_state_, check_top); 0310 VECGEOM_NAV_VALIDATE((top_pv == nullptr) == this->IsOutside(), << "inconsistent initialization result"); 0311 } 0312 0313 //---------------------------------------------------------------------------// 0314 // OPERATIONS 0315 //---------------------------------------------------------------------------// 0316 0317 /*! 0318 * Compute a conservative isotropic distance to the nearest boundary. 0319 * 0320 * Returns a safety radius \em s satisfying the following constraints for 0321 * the maximum input distance \em d and the true inscribed-sphere radius 0322 * \em t: 0323 * - \f$ s > 0 \f$ 0324 * - \f$ s \le d \f$. 0325 * - \f$ s \le t \f$ (conservative upper bound on the inscribed sphere). 0326 * - Ideally \f$ s \ge C\,t \f$ for a geometry-independent constant \em C 0327 * (bounded relative error), though this cannot always be guaranteed. 0328 * 0329 * \pre The track is not on a boundary. 0330 * \pre \c max is strictly positive. 0331 * 0332 * \param max Maximum search distance. 0333 * \return Conservative safety radius. 0334 */ 0335 VECGEOM_FUNCTION auto NavView::FindSafety(Real max_dist) -> Real 0336 { 0337 VECGEOM_NAV_VALIDATE(!this->IsOnBoundary() && !this->IsOutside(), 0338 << "cannot search for safety when on a boundary or outside"); 0339 VECGEOM_NAV_VALIDATE(max_dist > 0, << "safety max " << max_dist << " is nonpositive"); 0340 0341 Real result = Navigator::ComputeSafety(pos_, cur_state_, max_dist); 0342 // TODO: some shapes return a limit greater than max_dist; fix for each one 0343 result = Min(max_dist, result); 0344 // TODO: when on the wrong side of the boundary, a *negative* distance is returned 0345 result = Max(Real(0), result); 0346 0347 VECGEOM_NAV_VALIDATE(0 <= result && result <= max_dist, 0348 << "returned safety " << result << " is out of bounds (0, " << max_dist << "]"); 0349 return result; 0350 } 0351 0352 /*! 0353 * Find the straight-line distance to the next boundary. 0354 * 0355 * The returned \c NavFindResultKind enumeration informs potential next actions: 0356 * - \c hit: The next state is cached internally. 0357 * Call \c MoveToBoundary . 0358 * - \c miss: No boundary exists along the current direction up to \c 0359 * max_dist. Call \c MoveInternal. 0360 * - \c reentrant: The track is already on a boundary headed outward from the 0361 * current volume (indicating a reentrant volume change). 0362 * This likely corresponds to the call sequence <code>{MoveToBoundary, 0363 * ChangeDirection, CrossBoundary}</code> where the new direction points 0364 * back into the previous volume. 0365 * This can be handled as a degenerate step (\c MoveInternal away from the 0366 * boundary) \em or crossing back into the previous boundary with \c 0367 * CrossBoundary . 0368 * - \c error: a negative solid distance indicates a geometry overlap. 0369 * 0370 * \pre The track position is inside the current volume or on a boundary. 0371 * \pre \c max_dist is strictly positive. 0372 * 0373 * \param max_dist Maximum search distance. 0374 * \return \c NavFindResult containing distance and boundary flag. 0375 * 0376 * \post Internal next state is cached (current implementation). 0377 * \post The next distance is \em not cached; the caller must store it if 0378 * \c MoveToBoundary will need it. 0379 */ 0380 VECGEOM_FUNCTION auto NavView::FindNextBoundary(Real max_dist) -> NavFindResult 0381 { 0382 VECGEOM_NAV_VALIDATE(!this->IsOutside(), << "cannot search while outside"); 0383 VECGEOM_NAV_VALIDATE(max_dist > 0, << "max_dist=" << max_dist << " must be positive"); 0384 0385 // Note: search distance may need to be bumped slightly to capture boundaries at maximum distance 0386 auto next_distance = Navigator::ComputeStepAndNextVolume(pos_, dir_, max_dist, cur_state_, nxt_state_); 0387 // Next distance should only be non-positive if a boundary is hit 0388 VECGEOM_ASSERT(nxt_state_.IsOnBoundary() || next_distance > 0); 0389 0390 VECGEOM_NAV_VALIDATE(next_distance <= max_dist, 0391 << "returned distance " << next_distance << " is beyond maximum " << max_dist); 0392 0393 if (!nxt_state_.IsOnBoundary()) 0394 return NavFindResult::Miss(); 0395 else if (next_distance > 0) 0396 return NavFindResult{next_distance}; // Hit 0397 else if (next_distance == 0) 0398 return NavFindResult::Reentrant(); // Already on a boundary and exiting 0399 else 0400 return NavFindResult::Error(); // Backward or NaN (error) 0401 } 0402 0403 /*! 0404 * Change the current track direction. 0405 * 0406 * May be called while on a boundary (e.g., before EM boundary crossing or 0407 * after optical reflection). 0408 * 0409 * \pre \c dir is normalised to unit length within machine epsilon. 0410 * 0411 * \param dir New direction unit vector. 0412 * 0413 * \post If in the interior of a volume, the next state is no longer valid, and 0414 * \c FindNextBoundary \em must be called before further movement. 0415 * If on a boundary, the next state \em may be invalid: if the new direction 0416 * points toward the interior, then \c FindNextBoundary \em should be called to 0417 * avoid two unnecessary crossings. 0418 * 0419 * \note When on a boundary, changing direction will only invalidate the next 0420 * state if the sign of \f$ \vec \Omega \cdot \vec n \f$ changes (i.e., if the 0421 * direction was previously outward and with the new direction is inward). In 0422 * the spirit of the zero-overhead principle, we do not do this for 0423 * production code. 0424 */ 0425 VECGEOM_FUNCTION void NavView::ChangeDirection(SpanConstReal3 dir) 0426 { 0427 VECGEOM_NAV_VALIDATE(IsUnitNormal(dir), << "direction " << dir << " is not a unit vector"); 0428 dir_ = dir; 0429 if (!cur_state_.IsOnBoundary()) { 0430 // NOTE: this is here for error checking purposes and should be removed for production use 0431 nxt_state_.Clear(); 0432 } 0433 } 0434 0435 /*! 0436 * Move to an arbitrary position within the current safety sphere. 0437 * 0438 * Used for charged tracks whose position is displaced transversely inside 0439 * the safety bubble (e.g.\ by magnetic-field or multiple-scattering 0440 * correction), where the displacement is guaranteed not to cross a 0441 * boundary. 0442 * 0443 * \pre \c pos lies within the current safety sphere and is not on a boundary. 0444 * 0445 * \param pos New global position. 0446 * 0447 * \post Boundary flag is false. 0448 */ 0449 VECGEOM_FUNCTION void NavView::MoveInternal(SpanConstReal3 pos) 0450 { 0451 // TODO: check against safety? 0452 VECGEOM_NAV_VALIDATE(!this->IsOutside(), << "cannot move while outside"); 0453 pos_ = pos; 0454 cur_state_.SetBoundaryState(false); 0455 nxt_state_.SetBoundaryState(false); 0456 // TODO: validate the volume hasn't changed 0457 } 0458 0459 /*! 0460 * Move along the current direction without reaching a boundary. 0461 * 0462 * Used for neutral tracks (photons) and for charged tracks near a 0463 * boundary where the safety is less than the sagitta or miss distance. 0464 * Unlike Geant4, no re-location call is needed after this move. 0465 * 0466 * \pre \c step is strictly positive. 0467 * \pre \c step is strictly less than the distance to the next boundary. 0468 * 0469 * \param step Distance to advance along the current direction. 0470 * 0471 * \post Boundary flag is false. 0472 */ 0473 VECGEOM_FUNCTION void NavView::MoveInternal(Real step) 0474 { 0475 VECGEOM_NAV_VALIDATE(!this->IsOutside(), << "cannot move while outside"); 0476 this->MoveInternalImpl(step); 0477 cur_state_.SetBoundaryState(false); 0478 } 0479 0480 /*! 0481 * Advance to the boundary at the exact distance returned by 0482 * \c FindNextBoundary. 0483 * 0484 * \pre \c FindNextBoundary returned \c hit or \c reentrant. 0485 * \pre \c ChangeDirection has \em not been called since the last 0486 * \c FindNextBoundary. 0487 * 0488 * \param step Exact distance returned by the preceding \c FindNextBoundary 0489 * call. 0490 * 0491 * \post The track is on the boundary (distance from true surface less than 0492 * a small tolerance, but potentially more than machine epsilon). 0493 * \post Boundary state is true. 0494 */ 0495 VECGEOM_FUNCTION void NavView::MoveToBoundary(Real step) 0496 { 0497 VECGEOM_NAV_VALIDATE(step > 0, << "boundary must be a positive distance away"); 0498 // TODO: validate input step is equal (within tolerance) to the last 0499 // NavFindResult that returned a hit (before direction change) 0500 // TODO: validate that a direction change on the boundary has not occurred 0501 VECGEOM_NAV_VALIDATE(nxt_state_.IsOnBoundary(), << "next state is not on a boundary"); 0502 this->MoveInternalImpl(step); 0503 cur_state_.SetBoundaryState(true); 0504 // TODO: verify endpoint is on boundary of current or next volume? 0505 } 0506 0507 /*! 0508 * @brief Compute the global boundary normal pointing from current to next state. 0509 * 0510 * "Next" is the state prepared by FindNextBoundary, not the particle direction. 0511 * Changing direction at the boundary preserves that state and this normal. 0512 * Leaving the current solid uses its outward normal; entering a daughter uses 0513 * the negated daughter normal. BVHNavigator::ComputeNextNormal performs the 0514 * calculation, including conversion to global coordinates. 0515 * 0516 * Retain the returned value in the caller if needed after CrossBoundary, 0517 * which adopts next as current and clears next. No normal is cached by the view. 0518 * Edges and corners follow the selected solid's Normal convention. 0519 * 0520 * @pre FindNextBoundary returned hit or reentrant; MoveToBoundary placed the 0521 * track at that boundary. Current and next represent distinct paths. 0522 * @pre CrossBoundary has not yet been called for this transition. 0523 * 0524 * @return Unit global normal from current toward next. 0525 * @throws std::runtime_error On an invalid workflow or unavailable solid normal 0526 * (e.g. an unresolved assembly); asserts instead on device. 0527 */ 0528 VECGEOM_FUNCTION auto NavView::CalcNextNormal() const -> Real3 0529 { 0530 VECGEOM_NAV_VALIDATE(nxt_state_.IsOnBoundary(), << "FindNextBoundary not yet called"); 0531 VECGEOM_NAV_VALIDATE(cur_state_.IsOnBoundary(), << "MoveToBoundary not yet called"); 0532 VECGEOM_NAV_VALIDATE(!cur_state_.IsOutside(), << "cannot query a normal while outside"); 0533 VECGEOM_NAV_VALIDATE(!cur_state_.HasSamePathAsOther(nxt_state_), << "current and next paths must differ"); 0534 Real3 normal; 0535 VECGEOM_NAV_VALIDATE(Navigator::ComputeNextNormal(pos_, cur_state_, nxt_state_, normal), 0536 << "boundary normal is unavailable"); 0537 return normal; 0538 } 0539 0540 /*! 0541 * Cross the current boundary and update the navigation state. 0542 * 0543 * Performs relocation: the "next" half-cooked state becomes the fully 0544 * correct current state. The "next" state is then invalidated, so 0545 * \c FindNextBoundary must be called again before the next 0546 * \c MoveToBoundary or \c CrossBoundary. 0547 * 0548 * \pre The track is on a boundary (i.e.\ \c FindNextBoundary and 0549 * \c MoveToBoundary have been called in order). 0550 * 0551 * \post The current navigation path reflects the new volume; calls to 0552 * \c GetLogicalVolumeId and \c GetPlacedVolumeId will return updated 0553 * values. 0554 * \post The internal next state is invalidated. 0555 * 0556 * \todo Should we \em swap current and next state so that we can move 0557 * back and forth cheaply, and then how would that affect FindNextBoundary? 0558 * What about a method ReenterBoundary()? 0559 */ 0560 VECGEOM_FUNCTION void NavView::CrossBoundary() 0561 { 0562 VECGEOM_NAV_VALIDATE(nxt_state_.IsOnBoundary(), << "FindNextBoundary not yet called"); 0563 VECGEOM_NAV_VALIDATE(cur_state_.IsOnBoundary(), << "MoveToBoundary not yet called"); 0564 0565 cur_state_ = nxt_state_; 0566 if (!cur_state_.IsOutside()) { 0567 Navigator::RelocateToNextVolume(pos_, dir_, cur_state_); 0568 } 0569 VECGEOM_ASSERT(cur_state_.IsOnBoundary()); 0570 0571 nxt_state_.Clear(); 0572 } 0573 0574 //---------------------------------------------------------------------------// 0575 // ACCESSORS 0576 //---------------------------------------------------------------------------// 0577 0578 /*! 0579 * Whether the navigation state is outside. 0580 * 0581 * This should be "true" for uninitialized states as well. 0582 */ 0583 VECGEOM_FUNCTION bool NavView::IsOutside() const { return cur_state_.IsOutside(); } 0584 0585 /*! 0586 * Whether the navigation state is on the boundary of a volume. 0587 * 0588 * This should always be \c false if outside. 0589 */ 0590 VECGEOM_FUNCTION bool NavView::IsOnBoundary() const { return cur_state_.IsOnBoundary(); } 0591 0592 /*! 0593 * VecGeom logical volume ID of the current volume. 0594 * 0595 * On host this can be mapped to \c vecgeom::LogicalVolume* and from there 0596 * to Adept/Celeritas materials, sensitive-detector regions, or Geant4 logical 0597 * volume pointers. 0598 * 0599 * \pre \c IsOutside must be \c false 0600 */ 0601 VECGEOM_FUNCTION auto NavView::GetLogicalVolumeId() const -> LogicalVolumeId 0602 { 0603 VECGEOM_NAV_VALIDATE(!cur_state_.IsOutside(), << "cannot query ID while outside"); 0604 return cur_state_.GetLogicalId(); 0605 } 0606 0607 /*! 0608 * VecGeom placed volume ID of the current volume instance. 0609 * 0610 * On host this can be mapped to \c vecgeom::VPlacedVolume* and from there 0611 * to a \c G4VPhysicalVolume*. 0612 */ 0613 VECGEOM_FUNCTION auto NavView::GetPlacedVolumeId() const -> PlacedVolumeId 0614 { 0615 VECGEOM_NAV_VALIDATE(!cur_state_.IsOutside(), << "cannot query ID while outside"); 0616 auto *pv = cur_state_.Top(); 0617 VECGEOM_ASSERT(pv); 0618 return pv->id(); 0619 } 0620 0621 /*! 0622 * Access the current navigation path as an opaque value. 0623 * 0624 * The path encodes the full touchable hierarchy and is only meaningful in 0625 * the context of the VecGeom geometry from which this view was 0626 * constructed. 0627 */ 0628 VECGEOM_FUNCTION auto NavView::GetOpaquePath() const -> OpaquePath const & { return cur_state_.GetState(); } 0629 0630 /*! 0631 * Move a straight-line distance without error checking. 0632 * 0633 * This is optimized to avoid aliasing issues and to use fused multiply-add for higher precision. 0634 */ 0635 VECGEOM_FUNCTION void NavView::MoveInternalImpl(Real distance) 0636 { 0637 Real3 newpos; 0638 for (int i = 0; i < 3; ++i) { 0639 newpos[i] = std::fma(distance, dir_[i], pos_[i]); 0640 } 0641 pos_ = newpos; 0642 } 0643 /*! 0644 * Validate directions have unit normal. 0645 * 0646 * This will be replaced by corecel::ArraySoftUnit : see 0647 * https://celeritas-project.github.io/celeritas/user/implementation/corecel/numerics.html#_CPPv4I0EN9celeritas13ArraySoftUnitE 0648 * . 0649 */ 0650 VECGEOM_FUNCTION bool NavView::IsUnitNormal(SpanConstReal3 v) 0651 { 0652 constexpr Real epsilon{1e-14}; 0653 Real length_sq{0}; 0654 for (int i = 0; i < 3; ++i) { 0655 length_sq = std::fma(v[i], v[i], length_sq); 0656 } 0657 // Factor of 3 is a loose bound of triangle inequality 0658 return Abs(length_sq - 1) < 3 * epsilon; 0659 } 0660 0661 //---------------------------------------------------------------------------// 0662 } // namespace vecgeom
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|