Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-07 10:14:33

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 orange/OrangeTrackView.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include "corecel/Macros.hh"
0010 #include "corecel/Types.hh"
0011 #include "corecel/cont/Array.hh"
0012 #include "corecel/math/Algorithms.hh"
0013 #include "corecel/sys/ThreadId.hh"
0014 
0015 #include "LevelStateAccessor.hh"
0016 #include "OrangeData.hh"
0017 #include "OrangeTypes.hh"
0018 #include "transform/TransformVisitor.hh"
0019 #include "univ/SimpleUnitTracker.hh"
0020 #include "univ/TrackerVisitor.hh"
0021 #include "univ/UniverseTypeTraits.hh"
0022 #include "univ/detail/Types.hh"
0023 
0024 #include "detail/UniverseIndexer.hh"
0025 
0026 #if !CELER_DEVICE_COMPILE
0027 #    include "corecel/io/Logger.hh"
0028 #    include "corecel/io/Repr.hh"
0029 #endif
0030 
0031 namespace celeritas
0032 {
0033 //---------------------------------------------------------------------------//
0034 /*!
0035  * Navigate through an ORANGE geometry on a single thread.
0036  *
0037  * Since the navigation relies on computationally expensive calls and must
0038  * ensure a consistent state between physical and logical boundaries, there is
0039  * an ordering followed by Celeritas' internal calls to each track's state.
0040  * Access (\c pos, \c dir, \c volume/surface/is_outside/is_on_boundary) is
0041  * valid at any time.
0042  *
0043  * The required ordering is:
0044  *
0045  * - Initialization, via the assignment operator
0046  * - Locating the boundary crossing along the current direction, \c
0047  *   find_next_step
0048  * - Locating the closest point on the boundary in any direction, \c
0049  *   find_safety
0050  * - Movement within a volume, not crossing a boundary: \c move_internal or \c
0051  *   move_to_boundary
0052  * - If on a boundary, logically moving to the adjacent volume ("relocation")
0053  *   with \c cross_boundary
0054  *
0055  * At any time, \c set_dir may be called, but then \c find_next_step must again
0056  * be called before any subsequent \c move or \c cross action above .
0057  *
0058  * The main point is that \c find_next_step depends on the current
0059  * straight-line direction, \c move_to_boundary and \c move_internal (with
0060  * a step length) depends on that distance, and
0061  * \c cross_boundary depends on being on the boundary with a knowledge of the
0062  * post-boundary state.
0063  *
0064  * The direction of \c normal is set to always point out of the volume the
0065  * track is currently in. On the boundary this is determined by the sense
0066  * of the track rather than its direction.
0067  *
0068  * \todo \c move_internal with a position \em should depend on the safety
0069  * distance, but that check is not yet implemented.
0070  */
0071 class OrangeTrackView
0072 {
0073   public:
0074     //!@{
0075     //! \name Type aliases
0076     using ParamsRef = NativeCRef<OrangeParamsData>;
0077     using StateRef = NativeRef<OrangeStateData>;
0078     using Initializer_t = GeoTrackInitializer;
0079     //!@}
0080 
0081   public:
0082     // Construct from params and state
0083     inline CELER_FUNCTION OrangeTrackView(ParamsRef const& params,
0084                                           StateRef const& states,
0085                                           TrackSlotId tid);
0086 
0087     // Initialize the state
0088     inline CELER_FUNCTION OrangeTrackView& operator=(Initializer_t const& init);
0089 
0090     //// ACCESSORS ////
0091 
0092     // The current position
0093     inline CELER_FUNCTION Real3 const& pos() const;
0094     // The current direction
0095     inline CELER_FUNCTION Real3 const& dir() const;
0096 
0097     // The current volume ID (null if outside)
0098     inline CELER_FUNCTION VolumeId volume_id() const;
0099     // Get the physical volume ID in the current cell
0100     inline CELER_FUNCTION VolumeInstanceId volume_instance_id() const;
0101     // The current level
0102     inline CELER_FUNCTION LevelId const& level() const;
0103     // Get the volume instance ID for all levels
0104     inline CELER_FUNCTION void volume_instance_id(Span<VolumeInstanceId>) const;
0105 
0106     // The current implementation volume ID
0107     inline CELER_FUNCTION ImplVolumeId impl_volume_id() const;
0108     // The current surface ID
0109     inline CELER_FUNCTION ImplSurfaceId impl_surface_id() const;
0110     // After 'find_next_step', the next straight-line surface
0111     inline CELER_FUNCTION ImplSurfaceId next_impl_surface_id() const;
0112 
0113     // Whether the track is outside the valid geometry region
0114     inline CELER_FUNCTION bool is_outside() const;
0115     // Whether the track is exactly on a surface
0116     inline CELER_FUNCTION bool is_on_boundary() const;
0117     //! Whether the last operation resulted in an error
0118     CELER_FORCEINLINE_FUNCTION bool failed() const { return failed_; }
0119     // Get the normal vector pointing out of the current volume
0120     inline CELER_FUNCTION Real3 normal() const;
0121 
0122     //// OPERATIONS ////
0123 
0124     // Find the distance to the next boundary
0125     inline CELER_FUNCTION Propagation find_next_step();
0126 
0127     // Find the distance to the next boundary, up to and including a step
0128     inline CELER_FUNCTION Propagation find_next_step(real_type max_step);
0129 
0130     // Find the distance to the nearest boundary in any direction
0131     inline CELER_FUNCTION real_type find_safety();
0132 
0133     // Find the distance to the nearest nearby boundary in any direction
0134     inline CELER_FUNCTION real_type find_safety(real_type max_step);
0135 
0136     // Move to the boundary in preparation for crossing it
0137     inline CELER_FUNCTION void move_to_boundary();
0138 
0139     // Move within the volume
0140     inline CELER_FUNCTION void move_internal(real_type step);
0141 
0142     // Move within the volume to a specific point
0143     inline CELER_FUNCTION void move_internal(Real3 const& pos);
0144 
0145     // Cross from one side of the current surface to the other
0146     inline CELER_FUNCTION void cross_boundary();
0147 
0148     // Change direction
0149     inline CELER_FUNCTION void set_dir(Real3 const& newdir);
0150 
0151   private:
0152     //// TYPES ////
0153 
0154     using LSA = LevelStateAccessor;
0155 
0156     //! Helper struct for initializing from an existing geometry state
0157     struct DetailedInitializer
0158     {
0159         TrackSlotId parent;  //!< Parent track with existing geometry
0160         Real3 const& dir;  //!< New direction
0161     };
0162 
0163     //// DATA ////
0164 
0165     ParamsRef const& params_;
0166     StateRef const& states_;
0167     TrackSlotId track_slot_;
0168     bool failed_{false};
0169 
0170     //// PRIVATE STATE MUTATORS ////
0171 
0172     // The current level
0173     inline CELER_FUNCTION void level(LevelId);
0174 
0175     // The boundary on the current surface level
0176     inline CELER_FUNCTION void boundary(BoundaryResult);
0177 
0178     // The next step distance, as stored on the state
0179     inline CELER_FUNCTION void next_step(real_type dist);
0180 
0181     // The next surface to be encountered
0182     inline CELER_FUNCTION void next_surf(detail::OnLocalSurface const&);
0183 
0184     // The level of the next surface to be encountered
0185     inline CELER_FUNCTION void next_surface_level(LevelId);
0186 
0187     //// PRIVATE STATE ACCESSORS ////
0188 
0189     // The current surface level
0190     inline CELER_FUNCTION LevelId const& surface_level() const;
0191 
0192     // The local surface on the current surface level
0193     inline CELER_FUNCTION LocalSurfaceId const& surf() const;
0194 
0195     // The sense on the current surface level
0196     inline CELER_FUNCTION Sense const& sense() const;
0197 
0198     // The boundary on the current surface level
0199     inline CELER_FUNCTION BoundaryResult const& boundary() const;
0200 
0201     // The next step distance, as stored on the state
0202     inline CELER_FUNCTION real_type const& next_step() const;
0203 
0204     // The next surface to be encountered
0205     inline CELER_FUNCTION detail::OnLocalSurface next_surf() const;
0206 
0207     // The level of the next surface to be encountered
0208     inline CELER_FUNCTION LevelId const& next_surface_level() const;
0209 
0210     //// HELPER FUNCTIONS ////
0211 
0212     // Initialize the state from a parent state and new direction
0213     inline CELER_FUNCTION OrangeTrackView&
0214     operator=(DetailedInitializer const& init);
0215 
0216     // Iterate over lower levels to find the next step
0217     inline CELER_FUNCTION Propagation
0218     find_next_step_impl(detail::Intersection isect);
0219 
0220     // Create local sense reference
0221     inline CELER_FUNCTION Span<SenseValue> make_temp_sense() const;
0222 
0223     // Create local distance
0224     inline CELER_FUNCTION detail::TempNextFace make_temp_next() const;
0225 
0226     inline CELER_FUNCTION detail::LocalState
0227     make_local_state(LevelId level) const;
0228 
0229     // Whether the next distance-to-boundary has been found
0230     inline CELER_FUNCTION bool has_next_step() const;
0231 
0232     // Whether the next surface has been found
0233     inline CELER_FUNCTION bool has_next_surface() const;
0234 
0235     // Invalidate the next distance-to-boundary and surface
0236     inline CELER_FUNCTION void clear_next();
0237 
0238     // Assign the surface on the current level
0239     inline CELER_FUNCTION void
0240     surface(LevelId level, detail::OnLocalSurface surf);
0241 
0242     // Clear the surface on the current level
0243     inline CELER_FUNCTION void clear_surface();
0244 
0245     // Make a LevelStateAccessor for the current thread and level
0246     inline CELER_FUNCTION LSA make_lsa() const;
0247 
0248     // Make a LevelStateAccessor for the current thread and a given level
0249     inline CELER_FUNCTION LSA make_lsa(LevelId level) const;
0250 
0251     // Get the daughter ID for the volume in the universe (or null)
0252     inline CELER_FUNCTION DaughterId get_daughter(LSA const& lsa) const;
0253 
0254     // Get the transform ID for the given daughter.
0255     inline CELER_FUNCTION TransformId get_transform(DaughterId daughter_id) const;
0256 
0257     // Get the transform ID to move from this level to the one below.
0258     inline CELER_FUNCTION TransformId get_transform(LevelId lev) const;
0259 
0260     // Get the surface normal as defined by the geometry
0261     inline CELER_FUNCTION Real3 geo_normal() const;
0262 };
0263 
0264 //---------------------------------------------------------------------------//
0265 // MEMBER FUNCTIONS
0266 //---------------------------------------------------------------------------//
0267 /*!
0268  * Construct from persistent and state data.
0269  */
0270 CELER_FUNCTION
0271 OrangeTrackView::OrangeTrackView(ParamsRef const& params,
0272                                  StateRef const& states,
0273                                  TrackSlotId tid)
0274     : params_(params), states_(states), track_slot_(tid)
0275 {
0276     CELER_EXPECT(params_);
0277     CELER_EXPECT(states_);
0278     CELER_EXPECT(track_slot_ < states.size());
0279 }
0280 
0281 //---------------------------------------------------------------------------//
0282 /*!
0283  * Construct the state.
0284  *
0285  * Expensive. This function should only be called to initialize an event from a
0286  * starting location and direction. Secondaries will initialize their states
0287  * from a copy of the parent.
0288  */
0289 CELER_FUNCTION OrangeTrackView&
0290 OrangeTrackView::operator=(Initializer_t const& init)
0291 {
0292     CELER_EXPECT(is_soft_unit_vector(init.dir));
0293 
0294     if (init.parent)
0295     {
0296         // Initialize from direction and copy of parent state
0297         *this = {init.parent, init.dir};
0298         CELER_ENSURE(this->pos() == init.pos);
0299         return *this;
0300     }
0301 
0302     failed_ = false;
0303 
0304     // Create local state
0305     detail::LocalState local;
0306     local.pos = init.pos;
0307     local.dir = init.dir;
0308     local.volume = {};
0309     local.surface = {};
0310     local.temp_sense = this->make_temp_sense();
0311 
0312     // Helpers for applying parent-to-daughter transformations
0313     TransformVisitor apply_transform{params_};
0314     auto transform_down_local = [&local](auto&& t) {
0315         local.pos = t.transform_down(local.pos);
0316         local.dir = t.rotate_down(local.dir);
0317     };
0318 
0319     // Recurse into daughter universes starting with the outermost universe
0320     UniverseId univ_id = top_universe_id();
0321     DaughterId daughter_id;
0322     LevelId level{0};
0323     do
0324     {
0325         TrackerVisitor visit_tracker{params_};
0326         auto tinit = visit_tracker(
0327             [&local](auto&& t) { return t.initialize(local); }, univ_id);
0328 
0329         if (!tinit.volume || tinit.surface)
0330         {
0331 #if !CELER_DEVICE_COMPILE
0332             auto msg = CELER_LOG_LOCAL(error);
0333             msg << "Failed to initialize geometry state: ";
0334             if (!tinit.volume)
0335             {
0336                 msg << "could not find associated volume";
0337             }
0338             else
0339             {
0340                 msg << "started on a surface ("
0341                     << tinit.surface.id().unchecked_get() << ")";
0342             }
0343             msg << " in universe " << univ_id.unchecked_get()
0344                 << " at local position " << repr(local.pos);
0345 #endif
0346             // Mark as failed and place in local "exterior" to end the search
0347             // but preserve the current level information
0348             failed_ = true;
0349             tinit.volume = orange_exterior_volume;
0350         }
0351 
0352         auto lsa = this->make_lsa(level);
0353         lsa.vol() = tinit.volume;
0354         lsa.pos() = local.pos;
0355         lsa.dir() = local.dir;
0356         lsa.universe() = univ_id;
0357 
0358         daughter_id = visit_tracker(
0359             [&tinit](auto&& t) { return t.daughter(tinit.volume); }, univ_id);
0360 
0361         if (daughter_id)
0362         {
0363             auto const& daughter = params_.daughters[daughter_id];
0364             // Apply "transform down" based on stored transform
0365             apply_transform(transform_down_local, daughter.trans_id);
0366             // Update universe and increase level depth
0367             univ_id = daughter.universe_id;
0368             ++level;
0369         }
0370 
0371     } while (daughter_id);
0372 
0373     // Save found level
0374     this->level(level);
0375 
0376     // Reset surface/boundary information
0377     this->boundary(BoundaryResult::exiting);
0378     this->clear_surface();
0379     this->clear_next();
0380 
0381     CELER_ENSURE(!this->has_next_step());
0382     return *this;
0383 }
0384 
0385 //---------------------------------------------------------------------------//
0386 /*!
0387  * Construct the state from a direction and a copy of the parent state.
0388  */
0389 CELER_FUNCTION
0390 OrangeTrackView& OrangeTrackView::operator=(DetailedInitializer const& init)
0391 {
0392     CELER_EXPECT(is_soft_unit_vector(init.dir));
0393 
0394     failed_ = false;
0395 
0396     if (track_slot_ != init.parent)
0397     {
0398         // Copy init track's position and logical state
0399         OrangeTrackView other(params_, states_, init.parent);
0400         this->level(states_.level[other.track_slot_]);
0401         this->surface(other.surface_level(), {other.surf(), other.sense()});
0402         this->boundary(other.boundary());
0403 
0404         for (auto lev : range(LevelId{this->level() + 1}))
0405         {
0406             // Copy all data accessed via LSA
0407             auto lsa = this->make_lsa(lev);
0408             lsa = other.make_lsa(lev);
0409         }
0410     }
0411 
0412     // Clear the next step information since we're changing direction or
0413     // initializing a new state
0414     this->clear_next();
0415 
0416     // Transform direction from global to local
0417     Real3 localdir = init.dir;
0418     auto apply_transform = TransformVisitor{params_};
0419     auto rotate_down
0420         = [&localdir](auto&& t) { localdir = t.rotate_down(localdir); };
0421     for (auto lev : range(LevelId{this->level()}))
0422     {
0423         auto lsa = this->make_lsa(lev);
0424         lsa.dir() = localdir;
0425         apply_transform(rotate_down,
0426                         this->get_transform(this->get_daughter(lsa)));
0427     }
0428 
0429     // Save final level
0430     this->make_lsa().dir() = localdir;
0431 
0432     CELER_ENSURE(!this->has_next_step());
0433     return *this;
0434 }
0435 
0436 //---------------------------------------------------------------------------//
0437 /*!
0438  * The current position.
0439  */
0440 CELER_FUNCTION Real3 const& OrangeTrackView::pos() const
0441 {
0442     return this->make_lsa(LevelId{0}).pos();
0443 }
0444 
0445 //---------------------------------------------------------------------------//
0446 /*!
0447  * The current direction.
0448  */
0449 CELER_FUNCTION Real3 const& OrangeTrackView::dir() const
0450 {
0451     return this->make_lsa(LevelId{0}).dir();
0452 }
0453 
0454 //---------------------------------------------------------------------------//
0455 /*!
0456  * The current volume ID.
0457  *
0458  * \note It is allowable to call this function when "outside", because the
0459  * outside in ORANGE is just a special volume. Other geometries may not have
0460  * that behavior.
0461  */
0462 CELER_FUNCTION VolumeId OrangeTrackView::volume_id() const
0463 {
0464     ImplVolumeId impl_id = this->impl_volume_id();
0465     return impl_id;
0466 }
0467 
0468 //---------------------------------------------------------------------------//
0469 /*!
0470  * The current volume instance.
0471  *
0472  * \todo not implemented; ImplVolumeId is already halfway between a
0473  * "reusable volume" and a "volume instance" anyway...
0474  */
0475 CELER_FUNCTION VolumeInstanceId OrangeTrackView::volume_instance_id() const
0476 {
0477     return {};
0478 }
0479 
0480 //---------------------------------------------------------------------------//
0481 /*!
0482  * The current level.
0483  */
0484 CELER_FORCEINLINE_FUNCTION LevelId const& OrangeTrackView::level() const
0485 {
0486     return states_.level[track_slot_];
0487 }
0488 
0489 //---------------------------------------------------------------------------//
0490 /*!
0491  * Get the volume instance ID at every level.
0492  *
0493  * The input span size must be equal to the value of "level" plus one. The
0494  * top-most level ("world" or level zero) starts at index zero and moves
0495  * downward. Note that Geant4 uses the \em reverse nomenclature.
0496  */
0497 CELER_FUNCTION void
0498 OrangeTrackView::volume_instance_id(Span<VolumeInstanceId> levels) const
0499 {
0500     CELER_EXPECT(levels.size() == this->level().get() + 1);
0501     for (auto lev : range(levels.size()))
0502     {
0503         levels[lev] = {};
0504     }
0505 }
0506 
0507 //---------------------------------------------------------------------------//
0508 /*!
0509  * The current "global" volume ID.
0510  *
0511  * \note It is allowable to call this function when "outside", because the
0512  * outside in ORANGE is just a special volume. Other geometries may not have
0513  * that behavior.
0514  */
0515 CELER_FUNCTION ImplVolumeId OrangeTrackView::impl_volume_id() const
0516 {
0517     auto lsa = this->make_lsa();
0518     detail::UniverseIndexer ui(params_.universe_indexer_data);
0519     return ui.global_volume(lsa.universe(), lsa.vol());
0520 }
0521 
0522 //---------------------------------------------------------------------------//
0523 /*!
0524  * The current surface ID.
0525  */
0526 CELER_FUNCTION ImplSurfaceId OrangeTrackView::impl_surface_id() const
0527 {
0528     if (this->is_on_boundary())
0529     {
0530         auto lsa = this->make_lsa(this->surface_level());
0531         detail::UniverseIndexer ui{params_.universe_indexer_data};
0532         return ui.global_surface(lsa.universe(), this->surf());
0533     }
0534     else
0535     {
0536         return ImplSurfaceId{};
0537     }
0538 }
0539 
0540 //---------------------------------------------------------------------------//
0541 /*!
0542  * After 'find_next_step', the next straight-line surface.
0543  */
0544 CELER_FUNCTION ImplSurfaceId OrangeTrackView::next_impl_surface_id() const
0545 {
0546     CELER_EXPECT(this->has_next_surface());
0547     auto lsa = this->make_lsa(this->next_surface_level());
0548     detail::UniverseIndexer ui{params_.universe_indexer_data};
0549     return ui.global_surface(lsa.universe(), this->next_surf().id());
0550 }
0551 
0552 //---------------------------------------------------------------------------//
0553 /*!
0554  * Whether the track is outside the valid geometry region.
0555  */
0556 CELER_FUNCTION bool OrangeTrackView::is_outside() const
0557 {
0558     // Zeroth volume in outermost universe is always the exterior by
0559     // construction in ORANGE
0560     auto lsa = this->make_lsa(LevelId{0});
0561     return lsa.vol() == orange_exterior_volume;
0562 }
0563 
0564 //---------------------------------------------------------------------------//
0565 /*!
0566  * Whether the track is exactly on a surface.
0567  */
0568 CELER_FORCEINLINE_FUNCTION bool OrangeTrackView::is_on_boundary() const
0569 {
0570     return static_cast<bool>(this->surface_level());
0571 }
0572 
0573 //---------------------------------------------------------------------------//
0574 /*!
0575  * Get the normal vector of the current surface.
0576  *
0577  * The direction of the normal is determined by the sense of the track such
0578  * that the normal always points out of the volume that the track is currently
0579  * in.
0580  */
0581 CELER_FUNCTION Real3 OrangeTrackView::normal() const
0582 {
0583     CELER_EXPECT(this->is_on_boundary());
0584 
0585     auto normal = this->geo_normal();
0586     // Flip direction if on the outside of the surface
0587     if (this->sense() == Sense::outside)
0588     {
0589         normal = negate(normal);
0590     }
0591 
0592     return normal;
0593 }
0594 
0595 //---------------------------------------------------------------------------//
0596 /*!
0597  * Find the distance to the next geometric boundary.
0598  */
0599 CELER_FUNCTION Propagation OrangeTrackView::find_next_step()
0600 {
0601     if (CELER_UNLIKELY(this->boundary() == BoundaryResult::reentrant))
0602     {
0603         // On a boundary, headed back in: next step is zero
0604         return {0, true};
0605     }
0606 
0607     // Find intersection at the top level: always the first simple unit
0608     auto global_isect = [this] {
0609         SimpleUnitTracker t{params_, SimpleUnitId{0}};
0610         return t.intersect(this->make_local_state(LevelId{0}));
0611     }();
0612     // Find intersection for all lower levels
0613     return this->find_next_step_impl(global_isect);
0614 }
0615 
0616 //---------------------------------------------------------------------------//
0617 /*!
0618  * Find a nearby distance to the next geometric boundary up to a distance.
0619  *
0620  * This may reduce the number of surfaces needed to check, sort, or write to
0621  * temporary memory, thereby speeding up transport.
0622  */
0623 CELER_FUNCTION Propagation OrangeTrackView::find_next_step(real_type max_step)
0624 {
0625     CELER_EXPECT(max_step > 0);
0626 
0627     if (CELER_UNLIKELY(this->boundary() == BoundaryResult::reentrant))
0628     {
0629         // On a boundary, headed back in: next step is zero
0630         return {0, true};
0631     }
0632 
0633     // Find intersection at the top level: always the first simple unit
0634     auto global_isect = [this, &max_step] {
0635         SimpleUnitTracker t{params_, SimpleUnitId{0}};
0636         return t.intersect(this->make_local_state(LevelId{0}), max_step);
0637     }();
0638     // Find intersection for all lower levels
0639     auto result = this->find_next_step_impl(global_isect);
0640     CELER_ENSURE(result.distance <= max_step);
0641     return result;
0642 }
0643 
0644 //---------------------------------------------------------------------------//
0645 /*!
0646  * Move to the next straight-line boundary but do not change volume.
0647  */
0648 CELER_FUNCTION void OrangeTrackView::move_to_boundary()
0649 {
0650     CELER_EXPECT(this->boundary() != BoundaryResult::reentrant);
0651     CELER_EXPECT(this->has_next_step());
0652     CELER_EXPECT(this->has_next_surface());
0653 
0654     // Physically move next step
0655     real_type const dist = this->next_step();
0656     for (auto i : range(this->level() + 1))
0657     {
0658         auto lsa = this->make_lsa(LevelId{i});
0659         axpy(dist, lsa.dir(), &lsa.pos());
0660     }
0661 
0662     this->surface(this->next_surface_level(), this->next_surf());
0663     this->clear_next();
0664     CELER_ENSURE(this->is_on_boundary());
0665 }
0666 
0667 //---------------------------------------------------------------------------//
0668 /*!
0669  * Move within the current volume.
0670  *
0671  * The straight-line distance *must* be less than the distance to the
0672  * boundary.
0673  */
0674 CELER_FUNCTION void OrangeTrackView::move_internal(real_type dist)
0675 {
0676     CELER_EXPECT(this->has_next_step());
0677     CELER_EXPECT(dist > 0 && dist <= this->next_step());
0678     CELER_EXPECT(dist != this->next_step() || !this->has_next_surface());
0679 
0680     // Move and update the next step
0681     for (auto i : range(this->level() + 1))
0682     {
0683         auto lsa = this->make_lsa(LevelId{i});
0684         axpy(dist, lsa.dir(), &lsa.pos());
0685     }
0686     this->next_step(this->next_step() - dist);
0687     this->clear_surface();
0688 }
0689 
0690 //---------------------------------------------------------------------------//
0691 /*!
0692  * Move within the current volume to a nearby point.
0693  *
0694  * \todo Currently it's up to the caller to make sure that the position is
0695  * "nearby". We should actually test this with an "is inside" call.
0696  */
0697 CELER_FUNCTION void OrangeTrackView::move_internal(Real3 const& pos)
0698 {
0699     // Transform all nonlocal levels
0700     auto local_pos = pos;
0701     auto apply_transform = TransformVisitor{params_};
0702     auto translate_down
0703         = [&local_pos](auto&& t) { local_pos = t.transform_down(local_pos); };
0704     for (auto lev : range(LevelId{this->level()}))
0705     {
0706         auto lsa = this->make_lsa(lev);
0707         lsa.pos() = local_pos;
0708 
0709         // Apply "transform down" based on stored transform
0710         apply_transform(translate_down,
0711                         this->get_transform(this->get_daughter(lsa)));
0712     }
0713 
0714     // Save final level
0715     auto lsa = this->make_lsa();
0716     lsa.pos() = local_pos;
0717 
0718     // Clear surface state and next-step info
0719     this->clear_surface();
0720     this->clear_next();
0721 }
0722 
0723 //---------------------------------------------------------------------------//
0724 /*!
0725  * Cross from one side of the current surface to the other.
0726  *
0727  * The position *must* be on the boundary following a move-to-boundary. This
0728  * should only be called once per boundary crossing.
0729  */
0730 CELER_FUNCTION void OrangeTrackView::cross_boundary()
0731 {
0732     CELER_EXPECT(this->is_on_boundary());
0733     CELER_EXPECT(!this->has_next_step());
0734 
0735     if (CELER_UNLIKELY(this->boundary() == BoundaryResult::reentrant))
0736     {
0737         // Direction changed while on boundary leading to no change in
0738         // volume/surface. This is logically equivalent to a reflection.
0739         this->boundary(BoundaryResult::exiting);
0740         return;
0741     }
0742 
0743     // Cross surface by flipping the sense
0744     states_.sense[track_slot_] = flip_sense(this->sense());
0745     this->boundary(BoundaryResult::exiting);
0746 
0747     // Create local state from post-crossing level and updated sense
0748     LevelId level{this->surface_level()};
0749     UniverseId universe;
0750     LocalVolumeId volume;
0751     detail::LocalState local;
0752     {
0753         auto lsa = this->make_lsa(level);
0754         universe = lsa.universe();
0755         local.pos = lsa.pos();
0756         local.dir = lsa.dir();
0757         local.volume = lsa.vol();
0758         local.surface = {this->surf(), this->sense()};
0759         local.temp_sense = this->make_temp_sense();
0760     }
0761 
0762     TrackerVisitor visit_tracker{params_};
0763 
0764     // Update the post-crossing volume by crossing the boundary of the "surface
0765     // crossing" level universe
0766     volume = visit_tracker(
0767         [&local](auto&& t) { return t.cross_boundary(local).volume; },
0768         universe);
0769     if (CELER_UNLIKELY(!volume))
0770     {
0771         // Boundary crossing failure
0772 #if !CELER_DEVICE_COMPILE
0773         CELER_LOG_LOCAL(error)
0774             << "track failed to cross local surface "
0775             << this->surf().unchecked_get() << " in universe "
0776             << universe.unchecked_get() << " at local position "
0777             << repr(local.pos) << " along local direction " << repr(local.dir);
0778 #endif
0779         // Mark as failed and place in local "exterior" to end the search
0780         // but preserve the current level information
0781         failed_ = true;
0782         volume = orange_exterior_volume;
0783     }
0784     make_lsa(level).vol() = volume;
0785 
0786     // Clear local surface before diving into daughters
0787     // TODO: this is where we'd do inter-universe mapping
0788     local.volume = {};
0789     local.surface = {};
0790 
0791     // Starting with the current level (i.e., next_surface_level), iterate
0792     // down into the deepest level: *initializing* not *crossing*
0793     auto daughter_id = visit_tracker(
0794         [volume](auto&& t) { return t.daughter(volume); }, universe);
0795     while (daughter_id)
0796     {
0797         ++level;
0798         {
0799             // Update universe, local position/direction
0800             auto const& daughter = params_.daughters[daughter_id];
0801             TransformVisitor apply_transform{params_};
0802             auto transform_down_local = [&local](auto&& t) {
0803                 local.pos = t.transform_down(local.pos);
0804                 local.dir = t.rotate_down(local.dir);
0805             };
0806             apply_transform(transform_down_local, daughter.trans_id);
0807             universe = daughter.universe_id;
0808         }
0809 
0810         // Initialize in daughter and get IDs of volume and potential daughter
0811         volume = visit_tracker(
0812             [&local](auto&& t) { return t.initialize(local).volume; },
0813             universe);
0814 
0815         if (!volume)
0816         {
0817 #if !CELER_DEVICE_COMPILE
0818             auto msg = CELER_LOG_LOCAL(error);
0819             msg << "track failed to cross boundary: could not find associated "
0820                    "volume in universe "
0821                 << universe.unchecked_get() << " at local position "
0822                 << repr(local.pos);
0823 #endif
0824             // Mark as failed and place in local "exterior" to end the search
0825             // but preserve the current level information
0826             failed_ = true;
0827             volume = orange_exterior_volume;
0828         }
0829         daughter_id = visit_tracker(
0830             [volume](auto&& t) { return t.daughter(volume); }, universe);
0831 
0832         auto lsa = make_lsa(level);
0833         lsa.vol() = volume;
0834         lsa.pos() = local.pos;
0835         lsa.dir() = local.dir;
0836         lsa.universe() = universe;
0837     }
0838 
0839     // Save final level
0840     this->level(level);
0841 
0842     CELER_ENSURE(this->is_on_boundary());
0843 }
0844 
0845 //---------------------------------------------------------------------------//
0846 /*!
0847  * Change the track's direction.
0848  *
0849  * This happens after a scattering event or movement inside a magnetic field.
0850  * It resets the calculated distance-to-boundary. It is allowed to happen on
0851  * the boundary, but changing direction so that it goes from pointing outward
0852  * to inward (or vice versa) will mean that \c cross_boundary will be a
0853  * null-op.
0854  *
0855  * TODO: This needs to be updated to handle reflections through levels
0856  */
0857 CELER_FUNCTION void OrangeTrackView::set_dir(Real3 const& newdir)
0858 {
0859     CELER_EXPECT(is_soft_unit_vector(newdir));
0860 
0861     if (this->is_on_boundary())
0862     {
0863         // Changing direction on a boundary, which may result in not leaving
0864         // current volume upon the cross_surface call
0865         auto normal = this->geo_normal();
0866 
0867         // Evaluate whether the direction dotted with the surface normal
0868         // changes (i.e. heading from inside to outside or vice versa).
0869         if ((dot_product(normal, newdir) >= 0)
0870             != (dot_product(normal, this->dir()) >= 0))
0871         {
0872             // The boundary crossing direction has changed! Reverse our
0873             // plans to change the logical state and move to a new volume.
0874             this->boundary(flip_boundary(this->boundary()));
0875         }
0876     }
0877 
0878     // Complete direction setting by transforming direction all the way down
0879     Real3 localdir = newdir;
0880     auto apply_transform = TransformVisitor{params_};
0881     auto rotate_down
0882         = [&localdir](auto&& t) { localdir = t.rotate_down(localdir); };
0883     for (auto lev : range(this->level()))
0884     {
0885         auto lsa = this->make_lsa(lev);
0886         lsa.dir() = localdir;
0887         apply_transform(rotate_down,
0888                         this->get_transform(this->get_daughter(lsa)));
0889     }
0890     // Save final level
0891     this->make_lsa().dir() = localdir;
0892 
0893     this->clear_next();
0894 }
0895 
0896 //---------------------------------------------------------------------------//
0897 // STATE ACCESSORS
0898 //---------------------------------------------------------------------------//
0899 /*!
0900  * The current level.
0901  */
0902 CELER_FORCEINLINE_FUNCTION void OrangeTrackView::level(LevelId lev)
0903 {
0904     states_.level[track_slot_] = lev;
0905 }
0906 
0907 /*!
0908  * The boundary on the current surface level.
0909  */
0910 CELER_FORCEINLINE_FUNCTION void OrangeTrackView::boundary(BoundaryResult br)
0911 {
0912     states_.boundary[track_slot_] = br;
0913 }
0914 
0915 /*!
0916  * The next step distance.
0917  */
0918 CELER_FORCEINLINE_FUNCTION void OrangeTrackView::next_step(real_type dist)
0919 {
0920     states_.next_step[track_slot_] = dist;
0921 }
0922 
0923 /*!
0924  * The next surface to be encountered.
0925  */
0926 CELER_FORCEINLINE_FUNCTION void
0927 OrangeTrackView::next_surf(detail::OnLocalSurface const& s)
0928 {
0929     states_.next_surf[track_slot_] = s.id();
0930     states_.next_sense[track_slot_] = s.unchecked_sense();
0931 }
0932 
0933 /*!
0934  * The level of the next surface to be encountered.
0935  */
0936 CELER_FORCEINLINE_FUNCTION void OrangeTrackView::next_surface_level(LevelId lev)
0937 {
0938     states_.next_level[track_slot_] = lev;
0939 }
0940 
0941 //---------------------------------------------------------------------------//
0942 // CONST STATE ACCESSORS
0943 /*!
0944  * The current surface level.
0945  */
0946 CELER_FORCEINLINE_FUNCTION LevelId const& OrangeTrackView::surface_level() const
0947 {
0948     return states_.surface_level[track_slot_];
0949 }
0950 
0951 /*!
0952  * The local surface on the current surface level.
0953  */
0954 CELER_FORCEINLINE_FUNCTION LocalSurfaceId const& OrangeTrackView::surf() const
0955 {
0956     return states_.surf[track_slot_];
0957 }
0958 
0959 /*!
0960  * The sense on the current surface level.
0961  */
0962 CELER_FORCEINLINE_FUNCTION Sense const& OrangeTrackView::sense() const
0963 {
0964     return states_.sense[track_slot_];
0965 }
0966 
0967 /*!
0968  * The boundary on the current surface level.
0969  */
0970 CELER_FORCEINLINE_FUNCTION BoundaryResult const&
0971 OrangeTrackView::boundary() const
0972 {
0973     return states_.boundary[track_slot_];
0974 }
0975 
0976 /*!
0977  * The next step distance.
0978  */
0979 CELER_FORCEINLINE_FUNCTION real_type const& OrangeTrackView::next_step() const
0980 {
0981     return states_.next_step[track_slot_];
0982 }
0983 
0984 /*!
0985  * The next surface to be encountered.
0986  */
0987 CELER_FORCEINLINE_FUNCTION detail::OnLocalSurface
0988 OrangeTrackView::next_surf() const
0989 {
0990     return {states_.next_surf[track_slot_], states_.next_sense[track_slot_]};
0991 }
0992 
0993 /*!
0994  * The level of the next surface to be encountered.
0995  */
0996 CELER_FORCEINLINE_FUNCTION LevelId const&
0997 OrangeTrackView::next_surface_level() const
0998 {
0999     return states_.next_level[track_slot_];
1000 }
1001 
1002 //---------------------------------------------------------------------------//
1003 // HELPER FUNCTIONS
1004 //---------------------------------------------------------------------------//
1005 /*!
1006  * Iterate over levels 1 to N to find the next step.
1007  *
1008  * Caller is responsible for finding the candidate next step on level 0, and
1009  * passing the resultant Intersection object as an argument.
1010  */
1011 CELER_FUNCTION Propagation
1012 OrangeTrackView::find_next_step_impl(detail::Intersection isect)
1013 {
1014     TrackerVisitor visit_tracker{params_};
1015 
1016     // The level with minimum distance to intersection
1017     LevelId min_level{0};
1018 
1019     // Find the nearest intersection from level 0 to current level
1020     // inclusive, preferring the shallowest level (i.e., lowest univ_id)
1021     for (auto levelid : range(LevelId{1}, this->level() + 1))
1022     {
1023         auto univ_id = this->make_lsa(levelid).universe();
1024         auto local_isect = visit_tracker(
1025             [local_state = this->make_local_state(levelid), &isect](auto&& t) {
1026                 return t.intersect(local_state, isect.distance);
1027             },
1028             univ_id);
1029 
1030         if (local_isect.distance < isect.distance)
1031         {
1032             isect = local_isect;
1033             min_level = levelid;
1034         }
1035     }
1036 
1037     this->next_step(isect.distance);
1038     this->next_surf(isect.surface);
1039     if (isect)
1040     {
1041         // Save level corresponding to the intersection
1042         this->next_surface_level(min_level);
1043     }
1044 
1045     Propagation result;
1046     result.distance = isect.distance;
1047     result.boundary = static_cast<bool>(isect);
1048     return result;
1049 }
1050 
1051 //---------------------------------------------------------------------------//
1052 /*!
1053  * Find the distance to the nearest boundary in any direction.
1054  *
1055  * The safety distance at a given point is the minimum safety distance over all
1056  * levels, since surface deduplication can potentionally elide bounding
1057  * surfaces at more deeply embedded levels.
1058  */
1059 CELER_FUNCTION real_type OrangeTrackView::find_safety()
1060 {
1061     CELER_EXPECT(!this->is_on_boundary());
1062 
1063     TrackerVisitor visit_tracker{params_};
1064 
1065     real_type min_safety_dist = numeric_limits<real_type>::infinity();
1066 
1067     for (auto lev : range(LevelId{this->level() + 1}))
1068     {
1069         auto lsa = this->make_lsa(lev);
1070         auto sd = visit_tracker(
1071             [&lsa](auto&& t) { return t.safety(lsa.pos(), lsa.vol()); },
1072             lsa.universe());
1073         min_safety_dist = celeritas::min(min_safety_dist, sd);
1074     }
1075     return min_safety_dist;
1076 }
1077 
1078 //---------------------------------------------------------------------------//
1079 /*!
1080  * Find the distance to the nearest nearby boundary.
1081  *
1082  * Since we currently support only "simple" safety distances, we can't
1083  * eliminate anything by checking only nearby surfaces.
1084  */
1085 CELER_FUNCTION real_type OrangeTrackView::find_safety(real_type)
1086 {
1087     return this->find_safety();
1088 }
1089 
1090 //---------------------------------------------------------------------------//
1091 /*!
1092  * Get a reference to the current volume, or to world volume if outside.
1093  */
1094 CELER_FUNCTION Span<SenseValue> OrangeTrackView::make_temp_sense() const
1095 {
1096     auto const max_faces = params_.scalars.max_faces;
1097     auto offset = track_slot_.get() * max_faces;
1098     return states_.temp_sense[AllItems<SenseValue, MemSpace::native>{}].subspan(
1099         offset, max_faces);
1100 }
1101 
1102 //---------------------------------------------------------------------------//
1103 /*!
1104  * Set up intersection scratch space.
1105  */
1106 CELER_FUNCTION detail::TempNextFace OrangeTrackView::make_temp_next() const
1107 {
1108     auto const max_isect = params_.scalars.max_intersections;
1109     auto offset = track_slot_.get() * max_isect;
1110 
1111     detail::TempNextFace result;
1112     result.face = states_.temp_face[AllItems<FaceId>{}].data() + offset;
1113     result.distance = states_.temp_distance[AllItems<real_type>{}].data()
1114                       + offset;
1115     result.isect = states_.temp_isect[AllItems<size_type>{}].data() + offset;
1116     result.size = max_isect;
1117 
1118     return result;
1119 }
1120 
1121 //---------------------------------------------------------------------------//
1122 /*!
1123  * Create a local state.
1124  */
1125 CELER_FUNCTION detail::LocalState
1126 OrangeTrackView::make_local_state(LevelId level) const
1127 {
1128     detail::LocalState local;
1129 
1130     auto lsa = this->make_lsa(level);
1131 
1132     local.pos = lsa.pos();
1133     local.dir = lsa.dir();
1134     local.volume = lsa.vol();
1135     if (level == this->surface_level())
1136     {
1137         local.surface = {this->surf(), this->sense()};
1138     }
1139     else
1140     {
1141         local.surface = {};
1142     }
1143     local.temp_sense = this->make_temp_sense();
1144     local.temp_next = this->make_temp_next();
1145     return local;
1146 }
1147 
1148 //---------------------------------------------------------------------------//
1149 /*!
1150  * Whether any next step has been calculated.
1151  */
1152 CELER_FORCEINLINE_FUNCTION bool OrangeTrackView::has_next_step() const
1153 {
1154     return this->next_step() != 0;
1155 }
1156 
1157 //---------------------------------------------------------------------------//
1158 /*!
1159  * Whether the next intersecting surface has been found.
1160  */
1161 CELER_FORCEINLINE_FUNCTION bool OrangeTrackView::has_next_surface() const
1162 {
1163     return static_cast<bool>(states_.next_surf[track_slot_]);
1164 }
1165 
1166 //---------------------------------------------------------------------------//
1167 /*!
1168  * Reset the next distance-to-boundary and surface.
1169  */
1170 CELER_FUNCTION void OrangeTrackView::clear_next()
1171 {
1172     this->next_step(0);
1173     states_.next_surf[track_slot_] = {};
1174     CELER_ENSURE(!this->has_next_step() && !this->has_next_surface());
1175 }
1176 
1177 //---------------------------------------------------------------------------//
1178 /*!
1179  * Assign the surface on the current level.
1180  */
1181 CELER_FUNCTION void
1182 OrangeTrackView::surface(LevelId level, detail::OnLocalSurface surf)
1183 {
1184     states_.surface_level[track_slot_] = level;
1185     states_.surf[track_slot_] = surf.id();
1186     states_.sense[track_slot_] = surf.unchecked_sense();
1187 }
1188 
1189 //---------------------------------------------------------------------------//
1190 /*!
1191  * Clear the surface on the current level.
1192  */
1193 CELER_FUNCTION void OrangeTrackView::clear_surface()
1194 {
1195     states_.surface_level[track_slot_] = {};
1196     CELER_ENSURE(!this->is_on_boundary());
1197 }
1198 
1199 //---------------------------------------------------------------------------//
1200 /*!
1201  * Make a LevelStateAccessor for the current thread and level.
1202  */
1203 CELER_FORCEINLINE_FUNCTION auto OrangeTrackView::make_lsa() const -> LSA
1204 {
1205     return this->make_lsa(this->level());
1206 }
1207 
1208 //---------------------------------------------------------------------------//
1209 /*!
1210  * Make a LevelStateAccessor for the current thread and a given level.
1211  */
1212 CELER_FORCEINLINE_FUNCTION auto OrangeTrackView::make_lsa(LevelId level) const
1213     -> LSA
1214 {
1215     return LSA(&states_, track_slot_, level);
1216 }
1217 
1218 //---------------------------------------------------------------------------//
1219 /*!
1220  * Get the daughter ID for the given volume in the given universe.
1221  *
1222  * \return DaughterId or {} if the current volume is a leaf.
1223  */
1224 CELER_FUNCTION DaughterId OrangeTrackView::get_daughter(LSA const& lsa) const
1225 {
1226     TrackerVisitor visit_tracker{params_};
1227     return visit_tracker([&lsa](auto&& t) { return t.daughter(lsa.vol()); },
1228                          lsa.universe());
1229 }
1230 
1231 //---------------------------------------------------------------------------//
1232 /*!
1233  * Get the transform ID for the given daughter.
1234  */
1235 CELER_FUNCTION TransformId
1236 OrangeTrackView::get_transform(DaughterId daughter_id) const
1237 {
1238     CELER_EXPECT(daughter_id);
1239     return params_.daughters[daughter_id].trans_id;
1240 }
1241 
1242 //---------------------------------------------------------------------------//
1243 /*!
1244  * Get the transform ID for the given daughter.
1245  */
1246 CELER_FUNCTION TransformId OrangeTrackView::get_transform(LevelId lev) const
1247 {
1248     CELER_EXPECT(lev < this->level());
1249     LSA lsa(&states_, track_slot_, lev);
1250     return this->get_transform(this->get_daughter(lsa));
1251 }
1252 
1253 //---------------------------------------------------------------------------//
1254 /*!
1255  * Get the normal vector of the current surface as defined by the geometry.
1256  */
1257 CELER_FUNCTION Real3 OrangeTrackView::geo_normal() const
1258 {
1259     CELER_EXPECT(this->is_on_boundary());
1260 
1261     auto lsa = this->make_lsa(this->surface_level());
1262     TrackerVisitor visit_tracker{params_};
1263     auto normal = visit_tracker(
1264         [pos = lsa.pos(), local_surface = this->surf()](auto&& t) {
1265             return t.normal(pos, local_surface);
1266         },
1267         lsa.universe());
1268 
1269     // Rotate normal up to global coordinates
1270     auto apply_transform = TransformVisitor{params_};
1271     auto rotate_up = [&normal](auto&& t) { normal = t.rotate_up(normal); };
1272     for (auto level : range<int>(this->level().unchecked_get()).step(-1))
1273     {
1274         apply_transform(rotate_up, this->get_transform(LevelId(level)));
1275     }
1276 
1277     CELER_ENSURE(is_soft_unit_vector(normal));
1278 
1279     return normal;
1280 }
1281 
1282 //---------------------------------------------------------------------------//
1283 }  // namespace celeritas