Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-27 07:24:01

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 // Project include(s)
0012 #include "detray/core/detail/container_views.hpp"
0013 #include "detray/definitions/containers.hpp"
0014 #include "detray/definitions/detail/qualifiers.hpp"
0015 #include "detray/definitions/indexing.hpp"
0016 #include "detray/definitions/navigation.hpp"
0017 #include "detray/geometry/identifier.hpp"
0018 #include "detray/geometry/tracking_surface.hpp"
0019 #include "detray/geometry/tracking_volume.hpp"
0020 #include "detray/navigation/detail/intersection_kernel.hpp"
0021 #include "detray/navigation/detail/print_state.hpp"
0022 #include "detray/navigation/intersection/intersection.hpp"
0023 #include "detray/navigation/intersection/ray_intersector.hpp"
0024 #include "detray/navigation/navigation_config.hpp"
0025 #include "detray/utils/invalid_values.hpp"
0026 #include "detray/utils/logging.hpp"
0027 #include "detray/utils/ranges.hpp"
0028 
0029 namespace detray::navigation {
0030 
0031 /// @brief A void inpector that does nothing.
0032 ///
0033 /// Inspectors can be plugged in to understand the current navigation state.
0034 struct void_inspector {
0035   struct void_view : public detray::detail::dbase_view {};
0036 
0037   using view_type = void_view;
0038   using const_view_type = const void_view;
0039 
0040   constexpr void_inspector() = default;
0041 
0042   DETRAY_HOST_DEVICE
0043   constexpr explicit void_inspector(
0044       const void_view & /*ignored*/) { /*Do nothing*/ }
0045 
0046   template <typename state_t>
0047   DETRAY_HOST_DEVICE constexpr void operator()(const state_t & /*ignored*/,
0048                                                const char * /*ignored*/) const {
0049     /*Do nothing*/
0050   }
0051 };
0052 
0053 /// @brief A navigation state object used to cache the information of the
0054 /// current navigation stream.
0055 ///
0056 /// The state is passed between navigation calls and is accessible to the
0057 /// actors in the propagation, for which it defines the public interface
0058 /// towards the navigation. The navigator class is responsible for updating the
0059 /// elements in the navigation state with every navigation call, establishing
0060 /// 'full trust' after changes to the track state reduced the trust level.
0061 ///
0062 /// @tparam detector_t the type of the detector that is being navigated
0063 /// @tparam k_cache_capacity number of object candidates the state can hold
0064 /// @tparam inspector_t is a validation inspector that can record information
0065 ///         about the navigation state at different points of the nav. flow.
0066 /// @tparam intersection_t result of an intersection operation
0067 template <typename derived_t, typename detector_t, std::size_t k_cache_capacity,
0068           typename inspector_t, typename intersection_t>
0069 class base_state : public detray::ranges::view_interface<
0070                        base_state<derived_t, detector_t, k_cache_capacity,
0071                                   inspector_t, intersection_t>> {
0072   /// Need at least two slots for the caching to work
0073   static_assert(k_cache_capacity >= 2u,
0074                 "Navigation cache needs to have a capacity larger than 1");
0075 
0076   /// Cast to the navigation state type to access its methods
0077   /// @{
0078   DETRAY_HOST_DEVICE
0079   constexpr auto cast_impl() -> derived_t & {
0080     return static_cast<derived_t &>(*this);
0081   }
0082   DETRAY_HOST_DEVICE
0083   constexpr auto cast_impl() const -> const derived_t & {
0084     return static_cast<const derived_t &>(*this);
0085   }
0086   /// @}
0087 
0088  protected:
0089   // Linear algebra types
0090   using algebra_t = typename detector_t::algebra_type;
0091   using scalar_t = dscalar<algebra_t>;
0092   using point3_t = dpoint3D<algebra_t>;
0093   using vector3_t = dvector3D<algebra_t>;
0094 
0095   // Result of a geometry object evaluation
0096   using candidate_t = intersection_t;
0097   using candidate_cache_t = darray<candidate_t, k_cache_capacity>;
0098   using candidate_itr_t = typename candidate_cache_t::iterator;
0099   using candidate_const_itr_t = typename candidate_cache_t::const_iterator;
0100   using dist_t = std::int_least8_t;
0101 
0102   // Link type to the next detector volume
0103   using nav_link_t = typename detector_t::surface_type::navigation_link;
0104 
0105  public:
0106   using detector_type = detector_t;
0107 
0108   /// Default constructor (needs a detector)
0109   base_state() = delete;
0110 
0111   /// Constructor using a given detector @param det
0112   DETRAY_HOST_DEVICE
0113   constexpr explicit base_state(const detector_t &det) : m_detector(&det) {}
0114 
0115   /// Construct from detector @param det and inspector view @param view
0116   template <concepts::device_view view_t>
0117   DETRAY_HOST_DEVICE constexpr base_state(const detector_t &det, view_t view)
0118       : m_detector(&det), m_inspector(view) {}
0119 
0120   /// @returns a reference to the detector
0121   DETRAY_HOST_DEVICE
0122   constexpr auto detector() const -> const detector_t & {
0123     return (*m_detector);
0124   }
0125 
0126   /// @return start position of the valid candidate range - const
0127   DETRAY_HOST_DEVICE
0128   constexpr auto begin() const -> candidate_const_itr_t {
0129     candidate_const_itr_t itr = m_candidates.cbegin();
0130     const dist_t idx{next_index()};
0131     detray::ranges::advance(itr,
0132                             (is_on_surface() && (idx >= 1)) ? idx - 1 : idx);
0133     return itr;
0134   }
0135 
0136   DETRAY_HOST_DEVICE
0137   constexpr auto cbegin() const -> candidate_const_itr_t {
0138     return std::as_const(*this).begin();
0139   }
0140 
0141   /// @return sentinel of the valid candidate range.
0142   DETRAY_HOST_DEVICE
0143   constexpr auto end() const -> candidate_const_itr_t {
0144     candidate_const_itr_t itr = m_candidates.cbegin();
0145     detray::ranges::advance(itr, m_last + 1);
0146     return itr;
0147   }
0148 
0149   DETRAY_HOST_DEVICE
0150   constexpr auto cend() const -> candidate_const_itr_t {
0151     return std::as_const(*this).end();
0152   }
0153 
0154   /// @returns last valid candidate (by position in the cache) - const
0155   DETRAY_HOST_DEVICE
0156   constexpr auto last() const -> const candidate_t & {
0157     assert(!cache_exhausted());
0158     assert(next_index() >= 0);
0159     return m_candidates[static_cast<std::size_t>(next_index())];
0160   }
0161 
0162   /// @returns the capacity of the internal candidate storage
0163   static consteval std::size_t capacity() { return k_cache_capacity; }
0164 
0165   /// @returns current navigation status - const
0166   DETRAY_HOST_DEVICE
0167   constexpr auto status() const -> navigation::status { return m_status; }
0168 
0169   /// @returns the navigation heartbeat (is this stream still being updated?)
0170   DETRAY_HOST_DEVICE
0171   constexpr bool is_alive() const {
0172     return cast_impl().status() > navigation::status::e_unknown;
0173   }
0174 
0175   /// @returns current navigation direction - const
0176   DETRAY_HOST_DEVICE
0177   constexpr auto direction() const -> navigation::direction {
0178     return m_direction;
0179   }
0180 
0181   /// Set direction in which the navigator should search for candidates
0182   DETRAY_HOST_DEVICE
0183   constexpr void set_direction(const navigation::direction dir) {
0184     if (m_direction != dir) {
0185       // Force renavigation after direction change
0186       cast_impl().set_no_trust();
0187       DETRAY_VERBOSE_HOST_DEVICE("Re-navigate after explicit direction change");
0188 
0189       m_direction = dir;
0190     }
0191   }
0192 
0193   /// @returns the externally provided mask tolerance - const
0194   DETRAY_HOST_DEVICE
0195   constexpr scalar_t external_tol() const { return m_external_mask_tol; }
0196 
0197   /// Set externally provided mask tolerance according to noise prediction
0198   DETRAY_HOST_DEVICE
0199   constexpr void set_external_tol(const scalar_t tol) {
0200     DETRAY_VERBOSE_HOST("Setting external mask tolerance: " << tol);
0201     m_external_mask_tol = tol;
0202   }
0203 
0204   /// @returns navigation trust level - const
0205   DETRAY_HOST_DEVICE
0206   constexpr auto trust_level() const -> navigation::trust_level {
0207     return m_trust_level;
0208   }
0209 
0210   /// Update navigation trust level to 'no trust'
0211   /// @{
0212   DETRAY_HOST_DEVICE
0213   constexpr void set_no_trust() {
0214     DETRAY_VERBOSE_HOST_DEVICE("Flagging re-navigation: \"no trust\"");
0215     m_trust_level = navigation::trust_level::e_no_trust;
0216   }
0217 
0218   /// @note only two trust levels for basic navigation implementation
0219   DETRAY_HOST_DEVICE
0220   constexpr void set_high_trust() { cast_impl().set_no_trust(); }
0221 
0222   /// @note only two trust levels for basic navigation implementation
0223   DETRAY_HOST_DEVICE
0224   constexpr void set_fair_trust() { cast_impl().set_no_trust(); }
0225   /// @}
0226 
0227   /// Helper method to check the track has reached a module surface
0228   DETRAY_HOST_DEVICE
0229   constexpr auto is_on_surface() const -> bool {
0230     return (m_status == navigation::status::e_on_object ||
0231             m_status == navigation::status::e_on_portal);
0232   }
0233 
0234   /// Helper method to check the track has reached a sensitive surface
0235   DETRAY_HOST_DEVICE
0236   constexpr auto is_on_sensitive() const -> bool {
0237     return (m_status == navigation::status::e_on_object) &&
0238            (cast_impl().geometry_identifier().id() == surface_id::e_sensitive);
0239   }
0240 
0241   /// Helper method to check the track has reached a passive surface
0242   DETRAY_HOST_DEVICE
0243   constexpr auto is_on_passive() const -> bool {
0244     return (m_status == navigation::status::e_on_object) &&
0245            (cast_impl().geometry_identifier().id() == surface_id::e_passive);
0246   }
0247 
0248   /// Helper method to check the track has reached a portal surface
0249   DETRAY_HOST_DEVICE
0250   constexpr auto is_on_portal() const -> bool {
0251     return m_status == navigation::status::e_on_portal;
0252   }
0253 
0254   /// Helper method to check the track has encountered material
0255   DETRAY_HOST_DEVICE
0256   constexpr auto encountered_sf_material() const -> bool {
0257     return (cast_impl().is_on_surface()) &&
0258            (cast_impl().current().surface().has_material());
0259   }
0260 
0261   /// @returns flag that indicates whether navigation was successful
0262   DETRAY_HOST_DEVICE
0263   constexpr bool finished() const {
0264     // Normal exit for this navigation?
0265     return (m_status == navigation::status::e_exit);
0266   }
0267 
0268   /// @returns current volume index - const
0269   DETRAY_HOST_DEVICE
0270   constexpr auto volume() const -> nav_link_t { return m_volume_index; }
0271 
0272   /// Set start/new volume from volume index @param v
0273   DETRAY_HOST_DEVICE
0274   constexpr void set_volume(dindex v) {
0275     assert(detray::detail::is_invalid_value(static_cast<nav_link_t>(v)) ||
0276            v < cast_impl().detector().volumes().size());
0277     if (v != m_volume_index) {
0278       DETRAY_VERBOSE_HOST_DEVICE("Setting new volume %d", v);
0279       // Make sure the new volume is properly initialized
0280       cast_impl().set_no_trust();
0281     }
0282     m_volume_index = static_cast<nav_link_t>(v);
0283   }
0284 
0285   /// @returns currently cached candidates - const
0286   DETRAY_HOST_DEVICE
0287   constexpr auto candidates() const -> const candidate_cache_t & {
0288     return m_candidates;
0289   }
0290 
0291   /// @returns number of currently cached (reachable) candidates - const
0292   DETRAY_HOST_DEVICE
0293   constexpr auto n_candidates() const -> dindex {
0294     assert(m_last - cast_impl().next_index() + 1 >= 0);
0295     return static_cast<dindex>(m_last - cast_impl().next_index() + 1);
0296   }
0297 
0298   /// @returns true if there are no cached candidates left - const
0299   DETRAY_HOST_DEVICE
0300   constexpr bool cache_exhausted() const {
0301     return cast_impl().n_candidates() == 0u;
0302   }
0303 
0304   /// @returns current/previous object that was reached - const
0305   DETRAY_HOST_DEVICE
0306   constexpr auto current() const -> const candidate_t & {
0307     assert(cast_impl().is_on_surface());
0308     assert(m_next > 0);
0309     return m_candidates[static_cast<std::size_t>(m_next - 1)];
0310   }
0311 
0312   /// @returns next object that we want to reach (current target) - const
0313   DETRAY_HOST_DEVICE
0314   constexpr auto target() const -> const candidate_t & {
0315     assert(m_next >= 0);
0316     return m_candidates[static_cast<std::size_t>(m_next)];
0317   }
0318 
0319   /// @returns identifier of the detector surface the navigator is on
0320   /// (invalid when not on surface) - const
0321   DETRAY_HOST_DEVICE
0322   constexpr auto geometry_identifier() const -> geometry::identifier {
0323     return cast_impl().current().surface().identifier();
0324   }
0325 
0326   /// @returns true if the current candidate lies on the surface edge
0327   DETRAY_HOST_DEVICE
0328   constexpr bool is_edge_candidate() const {
0329     assert(cast_impl().is_on_surface());
0330     return cast_impl().current().is_edge();
0331   }
0332 
0333   /// @returns true if the current candidate lies on the surface
0334   DETRAY_HOST_DEVICE
0335   constexpr bool is_good_candidate() const {
0336     assert(cast_impl().is_on_surface());
0337     return cast_impl().current().is_inside();
0338   }
0339 
0340   /// @returns true if the current candidate lies on the surface,
0341   /// including its edge
0342   DETRAY_HOST_DEVICE
0343   constexpr bool is_probably_candidate() const {
0344     assert(cast_impl().is_on_surface());
0345     return cast_impl().current().is_probably_inside();
0346   }
0347 
0348   /// Scalar representation of the navigation state,
0349   /// @returns distance to next target
0350   DETRAY_HOST_DEVICE
0351   constexpr scalar_t operator()() const {
0352     assert(math::isfinite(cast_impl().target().path()));
0353     return static_cast<scalar_t>(cast_impl().direction()) *
0354            cast_impl().target().path();
0355   }
0356 
0357   /// @param surface_t the surface interface type that is required
0358   /// @returns current detector surface the navigator is on - const
0359   template <template <typename> class surface_t = tracking_surface>
0360   DETRAY_HOST_DEVICE constexpr auto current_surface() const {
0361     assert(cast_impl().is_on_surface());
0362     return surface_t<detector_t>{*m_detector, current().surface()};
0363   }
0364 
0365   /// @param volume_t the volume interface type that is required
0366   /// @returns current detector volume of the navigation stream
0367   template <template <typename> class volume_t = tracking_volume>
0368   DETRAY_HOST_DEVICE constexpr auto current_volume() const {
0369     return volume_t<detector_t>{*m_detector, m_volume_index};
0370   }
0371 
0372   /// @param surface_t the surface interface type that is required
0373   /// @returns the next surface the navigator intends to reach
0374   template <template <typename> class surface_t = tracking_surface>
0375   DETRAY_HOST_DEVICE constexpr auto next_surface() const {
0376     return surface_t<detector_t>{*m_detector, cast_impl().target().surface()};
0377   }
0378 
0379   /// @param volume_t the volume interface type that is required
0380   /// @returns the next volume the navigator intends to reach
0381   template <template <typename> class volume_t = tracking_volume>
0382   DETRAY_HOST_DEVICE constexpr auto next_volume() const {
0383     return volume_t<detector_t>{*m_detector,
0384                                 cast_impl().target().surface().volume()};
0385   }
0386 
0387   /// Navigation reaches final target or leaves detector world. Stop
0388   /// navigation.
0389   DETRAY_HOST_DEVICE constexpr void exit() {
0390     m_status = navigation::status::e_exit;
0391     DETRAY_VERBOSE_HOST_DEVICE("Exited");
0392     cast_impl().run_inspector({}, point3_t{0.f, 0.f, 0.f},
0393                               vector3_t{0.f, 0.f, 0.f}, "Exited: ");
0394   }
0395 
0396   /// Navigation is being paused by actor: Maintain the navigation state
0397   /// and resume later
0398   DETRAY_HOST_DEVICE constexpr void pause() {
0399     DETRAY_VERBOSE_HOST_DEVICE("Paused by actor");
0400     cast_impl().run_inspector({}, point3_t{0.f, 0.f, 0.f},
0401                               vector3_t{0.f, 0.f, 0.f}, "Paused by actor: ");
0402   }
0403 
0404   /// Navigation state that cannot be recovered from. Leave the other
0405   /// data for inspection.
0406   ///
0407   /// @param custom_msg additional information on the reason for the error
0408   DETRAY_HOST_DEVICE constexpr void abort(
0409       const char *custom_msg = "Navigator (unknown reason)") {
0410     m_status = navigation::status::e_abort;
0411 
0412     /// Wrapper around the custom message that a print inspector can
0413     /// understand
0414     struct message_wrapper {
0415       const char *const m_msg{nullptr};
0416 
0417       DETRAY_HOST_DEVICE
0418       constexpr const char *operator()() const { return m_msg; }
0419     };
0420 
0421     assert(custom_msg != nullptr);
0422     DETRAY_ERROR_HOST("Aborted: " << custom_msg);
0423 
0424     cast_impl().run_inspector({}, point3_t{0.f, 0.f, 0.f},
0425                               vector3_t{0.f, 0.f, 0.f},
0426                               "Aborted: ", message_wrapper{custom_msg});
0427   }
0428 
0429   /// Navigation state that cannot be recovered from. Leave the other
0430   /// data for inspection.
0431   ///
0432   /// @param debug_msg_generator functor that returns additional
0433   ///                            information on the reason for the error
0434   template <typename debug_msg_generator_t>
0435     requires(!std::same_as<char *, debug_msg_generator_t>)
0436   DETRAY_HOST_DEVICE constexpr void abort(
0437       const debug_msg_generator_t &debug_msg_generator) {
0438     m_status = navigation::status::e_abort;
0439 
0440     DETRAY_ERROR_HOST("Aborted: " << debug_msg_generator());
0441 
0442     cast_impl().run_inspector({}, point3_t{0.f, 0.f, 0.f},
0443                               vector3_t{0.f, 0.f, 0.f},
0444                               "Aborted: ", debug_msg_generator);
0445   }
0446 
0447   /// @returns the navigation inspector - const
0448   DETRAY_HOST_DEVICE
0449   constexpr const auto &inspector() const { return m_inspector; }
0450 
0451   /// @returns the navigation inspector
0452   DETRAY_HOST_DEVICE
0453   constexpr auto &inspector() { return m_inspector; }
0454 
0455  protected:
0456   /// @return start position of valid candidate range.
0457   DETRAY_HOST_DEVICE
0458   constexpr auto begin() -> candidate_itr_t {
0459     candidate_itr_t itr = m_candidates.begin();
0460     const dist_t idx{cast_impl().next_index()};
0461     detray::ranges::advance(
0462         itr, (cast_impl().is_on_surface() && (idx >= 1)) ? idx - 1 : idx);
0463     return itr;
0464   }
0465 
0466   /// @return sentinel of the valid candidate range.
0467   DETRAY_HOST_DEVICE
0468   constexpr auto end() -> candidate_itr_t {
0469     candidate_itr_t itr = m_candidates.begin();
0470     detray::ranges::advance(itr, m_last + 1);
0471     return itr;
0472   }
0473 
0474   /// @returns last valid candidate (by position in the cache)
0475   DETRAY_HOST_DEVICE
0476   constexpr auto last() -> candidate_t & {
0477     assert(static_cast<std::size_t>(m_last) < m_candidates.size());
0478     return m_candidates[static_cast<std::size_t>(m_last)];
0479   }
0480 
0481   /// Set the next surface that we want to reach (update target)
0482   DETRAY_HOST_DEVICE
0483   constexpr void set_next(candidate_const_itr_t new_next) {
0484     const auto new_idx{
0485         detray::ranges::distance(m_candidates.cbegin(), new_next)};
0486     cast_impl().next_index(static_cast<dist_t>(new_idx));
0487     assert(cast_impl().next_index() <= m_last + 1);
0488   }
0489 
0490   /// Updates the position of the last valid candidate
0491   DETRAY_HOST_DEVICE
0492   constexpr void set_last(candidate_const_itr_t new_last) {
0493     const auto new_idx{
0494         detray::ranges::distance(m_candidates.cbegin(), new_last) - 1};
0495     last_index(static_cast<dist_t>(new_idx));
0496     assert(m_last < static_cast<dist_t>(k_cache_capacity));
0497   }
0498 
0499   /// @returns the index to the target surface
0500   DETRAY_HOST_DEVICE
0501   constexpr dist_t next_index() const { return m_next; }
0502 
0503   /// @returns the index to the target surface
0504   DETRAY_HOST_DEVICE
0505   constexpr dist_t last_index() const { return m_last; }
0506 
0507   /// Set the next surface that we want to reach (update target)
0508   DETRAY_HOST_DEVICE
0509   constexpr void next_index(dist_t pos) {
0510     m_next = pos;
0511     assert(m_next >= 0);
0512     assert(m_next < static_cast<dist_t>(k_cache_capacity) + 1);
0513   }
0514 
0515   /// Set the next surface that we want to reach (update target)
0516   DETRAY_HOST_DEVICE
0517   constexpr void last_index(dist_t pos) {
0518     m_last = pos;
0519     assert(m_last >= -1);
0520     assert(m_last < static_cast<dist_t>(k_cache_capacity));
0521   }
0522 
0523   /// Set the next surface that we want to reach (update target)
0524   DETRAY_HOST_DEVICE
0525   constexpr auto advance() -> void {
0526     ++m_next;
0527     assert(m_next < static_cast<dist_t>(k_cache_capacity) + 1);
0528     assert(cast_impl().next_index() <= cast_impl().last_index() + 1);
0529   }
0530 
0531   /// @returns currently cached candidates
0532   DETRAY_HOST_DEVICE
0533   constexpr auto candidates() -> candidate_cache_t & { return m_candidates; }
0534 
0535   /// @returns current/previous object that was reached
0536   DETRAY_HOST_DEVICE
0537   constexpr auto current() -> candidate_t & {
0538     assert(cast_impl().is_on_surface());
0539     assert(m_next > 0);
0540     return m_candidates[static_cast<std::size_t>(m_next - 1)];
0541   }
0542 
0543   /// @returns next object that we want to reach (current target)
0544   DETRAY_HOST_DEVICE
0545   constexpr auto target() -> candidate_t & {
0546     assert(static_cast<std::size_t>(m_next) < m_candidates.size());
0547     return m_candidates[static_cast<std::size_t>(m_next)];
0548   }
0549 
0550   /// Set the status to @param s
0551   DETRAY_HOST_DEVICE
0552   constexpr void status(navigation::status s) {
0553     DETRAY_DEBUG_HOST("Setting nav. status: " << s);
0554     m_status = s;
0555   }
0556 
0557   /// Reset the trustlevel to @param t
0558   DETRAY_HOST_DEVICE
0559   constexpr void trust_level(navigation::trust_level t) {
0560     DETRAY_DEBUG_HOST("Setting trust level: " << t);
0561     m_trust_level = t;
0562   }
0563 
0564   /// Clear the state
0565   DETRAY_HOST_DEVICE
0566   constexpr void clear_cache() {
0567     // Mark all data in the cache as unreachable
0568     for (std::size_t i = 0u; i < k_cache_capacity; ++i) {
0569       m_candidates[i].set_path(std::numeric_limits<scalar_t>::max());
0570     }
0571     m_next = 0;
0572     m_last = -1;
0573   }
0574 
0575   /// Call the navigation inspector
0576   DETRAY_HOST_DEVICE constexpr void run_inspector(
0577       [[maybe_unused]] const navigation::config &cfg,
0578       [[maybe_unused]] const point3_t &track_pos,
0579       [[maybe_unused]] const vector3_t &track_dir,
0580       [[maybe_unused]] const char *message) {
0581     [[maybe_unused]] auto derived = static_cast<const derived_t &>(*this);
0582 
0583     if constexpr (!std::is_same_v<inspector_t, navigation::void_inspector>) {
0584       cast_impl().inspector()(derived, cfg, track_pos, track_dir, message);
0585     }
0586 
0587     DETRAY_DEBUG_HOST("" << message << "\n"
0588                          << detray::navigation::print_state(derived)
0589                          << detray::navigation::print_candidates(
0590                                 derived, cfg, track_pos, track_dir));
0591   }
0592 
0593   /// Call the navigation inspector
0594   template <typename debug_msg_generator_t>
0595   DETRAY_HOST_DEVICE constexpr void run_inspector(
0596       [[maybe_unused]] const navigation::config &cfg,
0597       [[maybe_unused]] const point3_t &track_pos,
0598       [[maybe_unused]] const vector3_t &track_dir,
0599       [[maybe_unused]] const char *message,
0600       [[maybe_unused]] const debug_msg_generator_t &msg_gen) {
0601     [[maybe_unused]] auto derived = static_cast<const derived_t &>(*this);
0602 
0603     if constexpr (!std::is_same_v<inspector_t, navigation::void_inspector>) {
0604       cast_impl().inspector()(derived, cfg, track_pos, track_dir, message,
0605                               msg_gen);
0606     }
0607 
0608     DETRAY_DEBUG_HOST("" << message << msg_gen() << "\n"
0609                          << detray::navigation::print_state(derived)
0610                          << detray::navigation::print_candidates(
0611                                 derived, cfg, track_pos, track_dir));
0612   }
0613 
0614  private:
0615   /// @returns a string stream that prints the navigation state details
0616   DETRAY_HOST
0617   friend std::ostream &operator<<(std::ostream &os, const derived_t &s) {
0618     os << detray::navigation::print_state(s)
0619        << detray::navigation::print_candidates(s, {}, point3_t{0.f, 0.f, 0.f},
0620                                                vector3_t{0.f, 0.f, 0.f});
0621     return os;
0622   }
0623 
0624   /// Our cache of candidates (intersections with any kind of surface)
0625   candidate_cache_t m_candidates;
0626 
0627   /// Detector pointer
0628   const detector_t *m_detector{nullptr};
0629 
0630   /// The navigation status
0631   navigation::status m_status{navigation::status::e_unknown};
0632 
0633   /// The navigation direction
0634   navigation::direction m_direction{navigation::direction::e_forward};
0635 
0636   /// The navigation trust level determines how this states cache is to
0637   /// be updated in the current navigation call
0638   navigation::trust_level m_trust_level{navigation::trust_level::e_no_trust};
0639 
0640   /// External mask tolerance, that models noise during track transport
0641   scalar_t m_external_mask_tol{0.f * unit<scalar_t>::mm};
0642 
0643   /// The next best candidate (target): m_next <= m_last + 1.
0644   /// m_next can be larger than m_last when the cache is exhausted
0645   dist_t m_next{0};
0646 
0647   /// The last reachable candidate: m_last < k_cache_capacity
0648   /// Can never be advanced beyond the last element
0649   dist_t m_last{-1};
0650 
0651   /// Index in the detector volume container of current navigation volume
0652   nav_link_t m_volume_index{0u};
0653 
0654   /// The inspector type of this navigation engine
0655   DETRAY_NO_UNIQUE_ADDRESS inspector_t m_inspector;
0656 };
0657 
0658 }  // namespace detray::navigation