From 6fef03256df2361dd757006d88a54c0eba942e74 Mon Sep 17 00:00:00 2001 From: Joana Niermann Date: Tue, 14 Jan 2025 10:51:37 +0100 Subject: [PATCH] Non caching navigator --- .../detray/navigation/caching_navigator.hpp | 25 +- .../navigation/detail/navigation_helpers.hpp | 220 +++++++++++++ core/include/detray/navigation/navigator.hpp | 298 ++++++++++++++++++ 3 files changed, 521 insertions(+), 22 deletions(-) create mode 100644 core/include/detray/navigation/detail/navigation_helpers.hpp create mode 100644 core/include/detray/navigation/navigator.hpp diff --git a/core/include/detray/navigation/caching_navigator.hpp b/core/include/detray/navigation/caching_navigator.hpp index fe7c77cc4..8654228f7 100644 --- a/core/include/detray/navigation/caching_navigator.hpp +++ b/core/include/detray/navigation/caching_navigator.hpp @@ -14,6 +14,7 @@ #include "detray/definitions/detail/qualifiers.hpp" #include "detray/definitions/units.hpp" #include "detray/navigation/detail/intersection_kernel.hpp" +#include "detray/navigation/detail/navigation_helpers.hpp" #include "detray/navigation/detail/ray.hpp" #include "detray/navigation/intersection/intersection.hpp" #include "detray/navigation/intersection/ray_intersector.hpp" @@ -323,28 +324,8 @@ class caching_navigator { DETRAY_HOST_DEVICE inline void init(const track_t &track, state &navigation, const navigation::config &cfg, const context_type &ctx) const { - const auto &det = navigation.detector(); - const auto volume = tracking_volume{det, navigation.volume()}; - - // Clean up state - navigation.clear(); - navigation.heartbeat(true); - - // Search for neighboring surfaces and fill candidates into cache - volume.template visit_neighborhood( - track, cfg, ctx, det, ctx, track, navigation, - darray{cfg.min_mask_tolerance, - cfg.max_mask_tolerance}, - static_cast(cfg.mask_tolerance_scalor), - static_cast(cfg.overstep_tolerance)); - - // Determine overall state of the navigation after updating the cache - update_navigation_state(navigation, cfg); - - // If init was not successful, the propagation setup is broken - if (navigation.trust_level() != navigation::trust_level::e_full) { - navigation.heartbeat(false); - } + // Run local navigation in the current volume + navigation::local_navigation(track, navigation, cfg, ctx); navigation.run_inspector(cfg, track.pos(), track.dir(), "Init complete: "); diff --git a/core/include/detray/navigation/detail/navigation_helpers.hpp b/core/include/detray/navigation/detail/navigation_helpers.hpp new file mode 100644 index 000000000..e3f195253 --- /dev/null +++ b/core/include/detray/navigation/detail/navigation_helpers.hpp @@ -0,0 +1,220 @@ +/** Detray library, part of the ACTS project (R&D line) + * + * (c) 2025 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Project include(s) +#include "detray/definitions/detail/qualifiers.hpp" +#include "detray/definitions/units.hpp" +#include "detray/navigation/detail/intersection_kernel.hpp" +#include "detray/navigation/detail/ray.hpp" +#include "detray/navigation/intersection/ray_intersector.hpp" +#include "detray/navigation/navigation_config.hpp" +#include "detray/navigation/navigation_state.hpp" + +namespace detray::navigation { + +/// A functor that fills the navigation candidates cache by intersecting +/// the surfaces in the volume neighborhood +struct candidate_search { + + /// Test the volume links + template + DETRAY_HOST_DEVICE void operator()( + const typename detector_t::surface_type &sf_descr, + const detector_t &det, const typename detector_t::geometry_context &ctx, + const track_t &track, navigation_state_t &nav_state, + const darray mask_tol, + const typename detector_t::scalar_type mask_tol_scalor, + const typename detector_t::scalar_type overstep_tol) const { + + using algebra_t = typename detector_t::algebra_type; + using scalar_t = dscalar; + + const auto sf = tracking_surface{det, sf_descr}; + + sf.template visit_mask>( + nav_state, + detail::ray( + track.pos(), + static_cast(nav_state.direction()) * track.dir()), + sf_descr, det.transform_store(), ctx, + sf.is_portal() ? darray{0.f, 0.f} : mask_tol, + mask_tol_scalor, overstep_tol); + } +}; + +/// @brief Helper method that re-establishes the navigation state after an +/// update. +/// +/// It checks wether the track has reached a surface or is still moving +/// towards the next surface candidate. If no new next candidate can be +// found, it flags 'no trust' in order to trigger a volume initialization. +/// +/// @param state the current navigation state +/// @param cfg the navigation configuration +template +DETRAY_HOST_DEVICE inline void update_navigation_state( + navigation_state_t &navigation, const navigation::config &cfg) { + + // Check whether the track reached the current candidate. Might be a + // portal, in which case the navigation needs to be re-initialized + if (navigation.target_reachable() && + navigation.has_reached_surface(navigation.target(), cfg)) { + // Set the next object that we want to reach (this function is only + // called once the cache has been updated to a full trust state). + // Might lead to exhausted cache. + navigation.advance(); + navigation.status((navigation.current().sf_desc.is_portal()) + ? navigation::status::e_on_portal + : navigation::status::e_on_object); + } else { + // Otherwise the track is moving towards a surface + navigation.status(navigation::status::e_towards_object); + } + // Exhaustion happens when after an update no next candidate in the + // cache is reachable anymore -> triggers init of [new] volume + // In backwards navigation or with strongly bent tracks, the cache may + // not be exhausted when trying to exit the volume (the ray is seeing + // the opposite side of the volume) + navigation.trust_level(!navigation.target_reachable() || + navigation.is_on_portal() + ? navigation::trust_level::e_no_trust + : navigation::trust_level::e_full); +} + +/// @brief Helper method to initialize a navigation state in a given volume. +/// +/// Calls the volumes accelerator structure for local navigation, then tests +/// the surfaces for intersection and keeps the clostest one(s). +/// The closest candidate is set as 'next candidate' or 'target'. +/// +/// @tparam track_t type of track, needs to provide pos() and dir() methods +/// +/// @param track access to the track parameters +/// @param state the current navigation state +/// @param cfg the navigation configuration +template +DETRAY_HOST_DEVICE inline void local_navigation(const track_t &track, + navigation_state_t &navigation, + const navigation::config &cfg, + const context_type &ctx) { + const auto &det = navigation.detector(); + const auto volume = tracking_volume{det, navigation.volume()}; + + // Clean up state + navigation.clear(); + navigation.heartbeat(true); + + // Search for neighboring surfaces and fill candidates into cache + volume.template visit_neighborhood( + track, cfg, ctx, det, ctx, track, navigation, + darray{cfg.min_mask_tolerance, cfg.max_mask_tolerance}, + static_cast(cfg.mask_tolerance_scalor), + static_cast(cfg.overstep_tolerance)); + + // Determine overall state of the navigation after updating the cache + update_navigation_state(navigation, cfg); + + // If init was not successful, the propagation setup is broken + if (navigation.trust_level() != navigation::trust_level::e_full) { + navigation.heartbeat(false); + } +} + +/// @brief Complete update of the navigation flow. +/// +/// Restores 'full trust' state to the cadidates cache and checks whether +/// the track stepped onto a portal and a volume switch is due. If so, or +/// when the previous update according to the given trust level +/// failed to restore trust, it performs a complete reinitialization of the +/// navigation. +/// +/// @tparam track_t type of track, needs to provide pos() and dir() methods +/// +/// @param track access to the track parameters +/// @param state the current navigation state +/// @param cfg the navigation configuration +/// +/// @returns a boolean that indicates wheather the volume was (re-)initialized +template +DETRAY_HOST_DEVICE inline bool volume_switch(const track_t &track, + navigation_state_t &navigation, + const navigation::config &cfg, + const context_type &ctx = {}) { + + // Set volume index to the next volume provided by the portal + navigation.set_volume(navigation.current().volume_link); + + // Either end of world or valid volume index + assert(detail::is_invalid_value(navigation.volume()) || + navigation.volume() < navigation.detector().volumes().size()); + + // Navigation reached the end of the detector world + if (detail::is_invalid_value(navigation.volume())) { + navigation.exit(); + return false; + } + + // Run inspection when needed (keep for debugging) + // navigation.run_inspector(cfg, track.pos(), track.dir(), "Volume + // switch: "); + + // Local navigation in the new volume + navigation::local_navigation(track, navigation, cfg, ctx); + + // Fresh initialization, reset trust and hearbeat even though we are + // on inner portal + navigation.trust_level(navigation::trust_level::e_full); + navigation.heartbeat(navigation.target_reachable()); + + return true; +} + +/// @brief Helper method that updates the intersection of a single candidate +/// and checks reachability +/// +/// @tparam candidate_t type of navigation candidate (intersection result) +/// @tparam track_t type of track, needs to provide pos() and dir() methods +/// @tparam context_t type of geometry context +/// +/// @param candidate the candidate intersection to be updated +/// @param track access to the track parameters +/// @param det access to the detector (geometry) +/// @param cfg the navigation configuration +/// +/// @returns whether the track can reach this candidate. +template +DETRAY_HOST_DEVICE inline bool update_candidate( + const navigation::direction nav_dir, candidate_t &candidate, + const track_t &track, const detector_t &det, const navigation::config &cfg, + const context_t &ctx) { + + using algebra_t = typename detector_t::algebra_type; + using scalar_t = dscalar; + + if (candidate.sf_desc.barcode().is_invalid()) { + return false; + } + + const auto sf = tracking_surface{det, candidate.sf_desc}; + + // Check whether this candidate is reachable by the track + return sf.template visit_mask>( + detail::ray(track.pos(), + static_cast(nav_dir) * track.dir()), + candidate, det.transform_store(), ctx, + sf.is_portal() ? darray{0.f, 0.f} + : darray{cfg.min_mask_tolerance, + cfg.max_mask_tolerance}, + static_cast(cfg.mask_tolerance_scalor), + static_cast(cfg.overstep_tolerance)); +} + +} // namespace detray::navigation diff --git a/core/include/detray/navigation/navigator.hpp b/core/include/detray/navigation/navigator.hpp new file mode 100644 index 000000000..ff116c57d --- /dev/null +++ b/core/include/detray/navigation/navigator.hpp @@ -0,0 +1,298 @@ +/** Detray library, part of the ACTS project (R&D line) + * + * (c) 2025 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Project include(s) +#include "detray/core/detector.hpp" +#include "detray/definitions/detail/indexing.hpp" +#include "detray/definitions/detail/qualifiers.hpp" +#include "detray/definitions/units.hpp" +#include "detray/navigation/detail/intersection_kernel.hpp" +#include "detray/navigation/intersection/intersection.hpp" +#include "detray/navigation/intersection/ray_intersector.hpp" +#include "detray/navigation/navigation_config.hpp" +#include "detray/navigation/navigation_state.hpp" +#include "detray/utils/ranges.hpp" + +namespace detray { + +/// @brief The geometry navigation class. +/// +/// The navigator is initialized around a detector object, but is itself +/// agnostic to the detectors's object/primitive types. +/// Within a detector volume, the navigatior will perform a local navigation +/// based on the geometry acceleration structure(s) that are provided by the +/// volume. Once the local navigation is resolved, it moves to the next volume +/// by a portal. +/// To this end, it requires a link to the [next] navigation volume in every +/// candidate that is computed by intersection from the detector objects: +/// A module surface must link back to its mother volume, while a portal surface +/// links to the next volume in the direction of the track. +/// +/// This navigator always updates the target surface that is closest to the +/// current track position in the current track direction. Once the surface is +/// reached, the current surface will be kept in a second cache slot. If the +/// target cannot be reached anymore, the navigation stream is re-initialized. +/// +/// The navigation state is set up by an init() call and then follows a +/// sequence of +/// - step() (stepper) +/// - update() (navigator) +/// - run_actors() (actor chain) +/// - update() (navigator) +/// calls, which are handled by the propagator class. +/// +/// The navigation heartbeat indicates, that the navigation is still running +/// and in a valid state. +/// +/// @tparam detector_t the detector to navigate +/// @tparam inspector_t is a validation inspector that can record information +/// about the navigation state at different points of the nav. flow. +/// @tparam intersection_t candidate type +template > +class navigator { + + public: + using detector_type = detector_t; + using context_type = detector_type::geometry_context; + + using algebra_type = typename detector_type::algebra_type; + using scalar_type = dscalar; + using point3_type = dpoint3D; + using vector3_type = dvector3D; + + using intersection_type = intersection_t; + using inspector_type = inspector_t; + + /// @brief Navigation state that contains the current and next candidate + /// + /// Once a surface is reached, the corresponding candidate is moved to the + /// position at index zero in the cache. The cache can hold two candidates: + /// the target (pos 1) and the current surface (pos 0). + class state + : public detray::ranges::view_interface, + navigation::state { + + friend class navigator; + + // Allow the filling/updating of candidates + friend struct intersection_initialize; + friend struct intersection_update; + + using base_type = + navigation::state; + + // Result of a geometry object evaluation + using candidate_t = typename base_type::candidate_t; + using candidate_cache_t = typename base_type::candidate_cache_t; + using candidate_itr_t = typename base_type::candidate_itr_t; + using candidate_const_itr_t = typename base_type::candidate_const_itr_t; + using dist_t = typename base_type::dist_t; + + public: + /// Use common methods of contructing a nvaigation state + using base_type::base_type; + + /// @returns start position of the candidate range - const + DETRAY_HOST_DEVICE + constexpr auto begin() const -> candidate_const_itr_t { + candidate_const_itr_t itr = this->candidates().begin(); + const dist_t next_idx{this->next_index()}; + + detray::ranges::advance( + itr, this->is_on_surface() ? next_idx - 1 : next_idx); + + return itr; + } + + /// @returns sentinel of the candidate range. + DETRAY_HOST_DEVICE + constexpr auto end() const -> candidate_const_itr_t { + return m_candidates.end(); + } + + /// Navigation state that cannot be recovered from. Leave the state + /// data for inspection. + /// + /// @returns navigation heartbeat (dead) + DETRAY_HOST_DEVICE + constexpr bool abort() { + base_type::abort(); + + run_inspector({}, point3_type{0.f, 0.f, 0.f}, + vector3_type{0.f, 0.f, 0.f}, "Aborted: "); + + return this->is_alive(); + } + + /// Navigation reaches final target or leaves detector world. Stop + /// navigation. + /// + /// @returns navigation heartbeat (dead) + DETRAY_HOST_DEVICE + constexpr bool exit() { + base_type::exit(); + + run_inspector({}, point3_type{0.f, 0.f, 0.f}, + vector3_type{0.f, 0.f, 0.f}, "Exited: "); + + return this->is_alive(); + } + + private: + /// @returns start position of valid candidate range. + DETRAY_HOST_DEVICE + constexpr auto begin() -> candidate_itr_t { + return m_candidates.begin(); + } + + /// @returns sentinel of the valid candidate range. + DETRAY_HOST_DEVICE + constexpr auto end() -> candidate_itr_t { return m_candidates.end(); } + + /// Insert a new element @param new_cadidate if it is closer that the + /// current next candidate. This is the cache update scheme that is + /// called during 'local_navigation' + DETRAY_HOST_DEVICE + constexpr void insert(candidate_itr_t, + const intersection_type &new_cadidate) { + + assert(detail::is_invalid_value(new_cadidate.volume_link) || + new_cadidate.volume_link < + this->detector().volumes().size()); + + // Insert the first candidate + if (new_cadidate < this->candidates()[1]) { + this->candidates()[1] = new_cadidate; + } + } + + /// Call the navigation inspector + DETRAY_HOST_DEVICE + inline void run_inspector( + [[maybe_unused]] const navigation::config &cfg, + [[maybe_unused]] const point3_type &track_pos, + [[maybe_unused]] const vector3_type &track_dir, + [[maybe_unused]] const char *message) { + if constexpr (!std::is_same_v) { + m_inspector(*this, cfg, track_pos, track_dir, message); + } + } + }; + + public: + /// @brief Helper method to initialize a volume. + /// + /// Calls the volumes accelerator structure for local navigation, then tests + /// the surfaces for intersection and keeps the clostest one. + /// The closest candidate is set as 'next candidate' or 'target'. + /// + /// @tparam track_t type of track, needs to provide pos() and dir() methods + /// + /// @param track access to the track parameters + /// @param state the current navigation state + /// @param cfg the navigation configuration + template + DETRAY_HOST_DEVICE inline void init(const track_t &track, state &navigation, + const navigation::config &cfg, + const context_type &ctx) const { + // Run local navigation in the current volume + navigation::local_navigation(track, navigation, cfg, ctx); + + navigation.run_inspector(cfg, track.pos(), track.dir(), + "Init complete: "); + } + + /// @brief Complete update of the navigation flow. + /// + /// Restores 'full trust' state to the cadidates cache and checks whether + /// the track stepped onto a portal and a volume switch is due. If so, or + /// when the previous update according to the given trust level + /// failed to restore trust, it performs a complete reinitialization of the + /// navigation. + /// + /// @tparam track_t type of track, needs to provide pos() and dir() methods + /// + /// @param track access to the track parameters + /// @param state the current navigation state + /// @param cfg the navigation configuration + /// + /// @returns a heartbeat to indicate if the navigation is still alive + template + DETRAY_HOST_DEVICE inline bool update(const track_t &track, + state &navigation, + const navigation::config &cfg, + const context_type &ctx = {}) const { + // Candidates are re-evaluated based on the current trust level. + // Should result in 'full trust' + bool is_init = update_kernel(track, navigation, cfg, ctx); + + // Update was completely successful (most likely case) + if (navigation.trust_level() == navigation::trust_level::e_full) { + return is_init; + } + // Otherwise: did we run into a portal? + else if (navigation.is_on_portal()) { + navigation::volume_switch(); + } + + // Sanity check: Should never be the case after complete update call + // If no trust could be restored for the current state, try again with + // relaxed tolerances + if (navigation.trust_level() != navigation::trust_level::e_full) { + is_init = true; + + // Try to save the navigation flow: Look further behind the + // track + auto loose_cfg{cfg}; + // Use the max mask tolerance in case a track leaves the volume + // when a sf is 'sticking' out of the portals due to the tol + loose_cfg.overstep_tolerance = math::min( + 100.f * cfg.overstep_tolerance, -10.f * cfg.max_mask_tolerance); + + navigation::local_navigation(track, navigation, loose_cfg, ctx); + + // Unrecoverable + if (navigation.trust_level() != navigation::trust_level::e_full) { + navigation.abort(); + } + } + + return is_init; + } + + private: + /// Helper method to update the candidates (surface intersections) + /// based on an externally provided trust level. Will (re-)initialize the + /// navigation if there is no trust. + /// + /// @tparam track_t type of track, needs to provide pos() and dir() methods + /// + /// @param track access to the track parameters + /// @param state the current navigation state + /// @param cfg the navigation configuration + template + DETRAY_HOST_DEVICE inline bool update_kernel( + const track_t &track, state &navigation, const navigation::config &cfg, + const context_type &ctx) const { + + const auto &det = navigation.detector(); + + // Current candidates are up to date, nothing left to do + if (navigation.trust_level() == navigation::trust_level::e_full) { + return false; + } + } +}; + +} // namespace detray