Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-20 07:46:27

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 #include "Acts/Geometry/GeometryContext.hpp"
0012 #include "Acts/Navigation/NavigationDelegate.hpp"
0013 #include "Acts/Navigation/NavigationStream.hpp"
0014 #include "Acts/Utilities/Any.hpp"
0015 
0016 #include <type_traits>
0017 
0018 namespace Acts {
0019 
0020 class TrackingVolume;
0021 class INavigationPolicy;
0022 class Surface;
0023 class Navigator;
0024 
0025 class NavigationPolicyStateManager;
0026 
0027 /// Wrapper class for a navigation policy state stored in the state manager.
0028 /// This class provides type-safe access to the underlying state through
0029 /// the `as<T>()` method. The state is stored as a type-erased std::any
0030 /// in the NavigationPolicyStateManager.
0031 class NavigationPolicyState {
0032  public:
0033   /// Cast the state to a specific type
0034   /// @tparam T The type to cast to
0035   /// @return Reference to the state as type T
0036   template <typename T>
0037   T& as() {
0038     return std::any_cast<T&>(payload());
0039   }
0040 
0041   /// Cast the state to a specific type (const version)
0042   /// @tparam T The type to cast to
0043   /// @return Const reference to the state as type T
0044   template <typename T>
0045   const T& as() const {
0046     return std::any_cast<T&>(payload());
0047   }
0048 
0049   NavigationPolicyState() = default;
0050 
0051   /// Check if the state is empty (not associated with a manager)
0052   /// @return True if the state is empty, false otherwise
0053   bool empty() const { return m_manager == nullptr; }
0054 
0055  private:
0056   NavigationPolicyState(NavigationPolicyStateManager& manager,
0057                         std::size_t index)
0058       : m_manager(&manager), m_index(index) {}
0059 
0060   std::any& payload();
0061   const std::any& payload() const;
0062 
0063   NavigationPolicyStateManager* m_manager = nullptr;
0064   std::size_t m_index = 0;
0065 
0066   friend class NavigationPolicyStateManager;
0067 };
0068 
0069 /// Manager class for navigation policy states. Maintains a stack of
0070 /// type-erased states and provides access to the current state.
0071 /// Navigation policies use this manager to push and pop their states
0072 /// during navigation.
0073 class NavigationPolicyStateManager {
0074  public:
0075   /// Push a new state onto the stack and construct it in-place
0076   /// @tparam T The type of state to create
0077   /// @tparam Args The types of the constructor arguments
0078   /// @param args Arguments to forward to the state constructor
0079   /// @return Reference to the newly created state
0080   template <typename T, typename... Args>
0081   T& pushState(Args&&... args) {
0082     std::any& state = m_stateStack.emplace_back();
0083     return state.emplace<T>(std::forward<Args>(args)...);
0084   }
0085 
0086   friend class Navigator;
0087 
0088   /// Get the current (top) state from the stack
0089   /// @return NavigationPolicyState wrapper for the current state, or empty state if stack is empty
0090   NavigationPolicyState currentState() {
0091     if (m_stateStack.empty()) {
0092       return {};  // Empty state as sentinel
0093     }
0094     return NavigationPolicyState{*this, m_stateStack.size() - 1};
0095   }
0096 
0097   /// Remove the current state from the stack
0098   /// @throws std::runtime_error if the stack is empty
0099   void popState() {
0100     if (m_stateStack.empty()) {
0101       throw std::runtime_error(
0102           "NavigationPolicyStateManager: Attempt to pop from empty stack");
0103     }
0104     m_stateStack.pop_back();
0105   }
0106 
0107   /// Completely reset the state manager by clearing the stack
0108   void reset() { m_stateStack.clear(); }
0109 
0110  private:
0111   friend class NavigationPolicyState;
0112 
0113   // We might want to extend this to a stack
0114   std::vector<std::any> m_stateStack;
0115 };
0116 
0117 inline std::any& NavigationPolicyState::payload() {
0118   if (m_manager == nullptr) {
0119     throw std::runtime_error(
0120         "NavigationPolicyState: Attempt to access empty payload");
0121   }
0122   return m_manager->m_stateStack.at(m_index);
0123 }
0124 
0125 inline const std::any& NavigationPolicyState::payload() const {
0126   if (m_manager == nullptr) {
0127     throw std::runtime_error(
0128         "NavigationPolicyState: Attempt to access empty payload");
0129   }
0130   return m_manager->m_stateStack.at(m_index);
0131 }
0132 
0133 namespace detail {
0134 template <typename T>
0135 concept HasOldInitializeCandidates = requires {
0136   requires requires(T policy, const GeometryContext& gctx,
0137                     const NavigationArguments& args,
0138                     AppendOnlyNavigationStream& stream, const Logger& logger) {
0139     policy.initializeCandidates(gctx, args, stream, logger);
0140   };
0141 };
0142 
0143 template <typename T>
0144 concept HasNewInitializeCandidates = requires {
0145   requires requires(T policy, const GeometryContext& gctx,
0146                     const NavigationArguments& args,
0147                     NavigationPolicyState& state,
0148                     AppendOnlyNavigationStream& stream, const Logger& logger) {
0149     policy.initializeCandidates(gctx, args, state, stream, logger);
0150   };
0151 };
0152 
0153 template <detail::HasOldInitializeCandidates T>
0154 void oldToNewSignatureAdapter(const void* instance, const GeometryContext& gctx,
0155                               const NavigationArguments& args,
0156                               NavigationPolicyState& /*state*/,
0157                               AppendOnlyNavigationStream& stream,
0158                               const Logger& logger) {
0159   const auto* policy = static_cast<const T*>(instance);
0160   policy->initializeCandidates(gctx, args, stream, logger);
0161 }
0162 }  // namespace detail
0163 
0164 /// Concept for a navigation policy
0165 /// This exists so `initializeCandidates` can be a non-virtual method and we
0166 /// still have a way to enforce it exists.
0167 template <typename T>
0168 concept NavigationPolicyConcept = requires {
0169   requires std::is_base_of_v<INavigationPolicy, T>;
0170 
0171   // Require either of the signatures to allow backwards compatibility
0172   requires(detail::HasOldInitializeCandidates<T> ||
0173            detail::HasNewInitializeCandidates<T>);
0174 };
0175 
0176 /// Base class for all navigation policies. The policy needs to be *connected*
0177 /// to a delegate via a virtual method for it to become active. The update
0178 /// method is not part of the class interface. The conventional `updateState`
0179 /// method is only required for use with the navigation policy factory,
0180 /// otherwise `connect` is free to connect any function.
0181 class INavigationPolicy {
0182  public:
0183   /// Noop update function that is suitable as a default for default navigation
0184   /// delegates.
0185   static void noopInitializeCandidates(
0186       const GeometryContext& /*unused*/, const NavigationArguments& /*unused*/,
0187       NavigationPolicyState& /*unused*/,
0188       const AppendOnlyNavigationStream& /*unused*/, const Logger& /*unused*/) {
0189     // This is a noop
0190   }
0191 
0192   /// Virtual destructor so policies can be held through this base class.
0193   virtual ~INavigationPolicy() = default;
0194 
0195   /// Connect a policy with a delegate (usually a member of a volume).
0196   /// This method exists to allow a policy to ensure a non-virtual function is
0197   /// registered with the delegate.
0198   /// @param delegate The delegate to connect to
0199   virtual void connect(NavigationDelegate& delegate) const = 0;
0200 
0201   /// Convenience function to walk over all navigation policies. The default
0202   /// implementation just calls this on itself, while the @ref
0203   /// MultiNavigationPolicy will call it on all it's children.
0204   /// @param visitor The visitor function to call for each policy
0205   virtual void visit(
0206       const std::function<void(const INavigationPolicy&)>& visitor) const {
0207     visitor(*this);
0208   }
0209 
0210   /// Check if the policy is in a valid state for navigation
0211   /// @param gctx The geometry context
0212   /// @param args The navigation arguments
0213   /// @param state The navigation policy state to check
0214   /// @param logger Logger for debug output
0215   /// @return True if the policy state is valid, false otherwise
0216   virtual bool isValid([[maybe_unused]] const GeometryContext& gctx,
0217                        [[maybe_unused]] const NavigationArguments& args,
0218                        [[maybe_unused]] NavigationPolicyState& state,
0219                        const Logger& logger) const {
0220     ACTS_VERBOSE("Default navigation policy isValid check. (always true)");
0221     return true;
0222   }
0223 
0224   struct EmptyState {};
0225 
0226   /// Create and initialize the state for this policy
0227   /// @param gctx The geometry context
0228   /// @param args The navigation arguments
0229   /// @param stateManager The state manager to push the new state onto
0230   /// @param logger Logger for debug output
0231   virtual void createState([[maybe_unused]] const GeometryContext& gctx,
0232                            [[maybe_unused]] const NavigationArguments& args,
0233                            NavigationPolicyStateManager& stateManager,
0234                            const Logger& logger) const {
0235     ACTS_VERBOSE(
0236         "Default navigation policy state initialization. (empty state)");
0237     stateManager.pushState<EmptyState>();
0238   }
0239 
0240   /// Remove the state for this policy from the state manager
0241   /// @param stateManager The state manager to pop the state from
0242   /// @param logger Logger for debug output
0243   virtual void popState(NavigationPolicyStateManager& stateManager,
0244                         const Logger& logger) const {
0245     // By default, we didn't push anything, so we don't need to poop anything
0246     ACTS_VERBOSE("Default navigation policy pop state. (pops empty state)");
0247     stateManager.popState();  // This will be correct regardless of if a derived
0248                               // class pushed a concrete state type that's
0249                               // different from the default EmptyState.
0250   }
0251 
0252  protected:
0253   /// Internal helper function for derived classes that conform to the concept
0254   /// and have a conventional `updateState` method. Mainly used to save some
0255   /// boilerplate.
0256   /// @tparam T The type of the navigation policy
0257   /// @param delegate The delegate to connect to
0258   template <NavigationPolicyConcept T>
0259   void connectDefault(NavigationDelegate& delegate) const {
0260     // This cannot be a concept because we use it in CRTP below
0261     const auto* self = static_cast<const T*>(this);
0262 
0263     if constexpr (detail::HasNewInitializeCandidates<T>) {
0264       delegate.template connect<&T::initializeCandidates>(self);
0265     } else {
0266       // @TODO: Remove navigation policy signature compatibility eventually
0267       delegate.connect(&detail::oldToNewSignatureAdapter<T>, self);
0268     }
0269   }
0270 };
0271 
0272 }  // namespace Acts