Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-30 07:58:42

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/Definitions/Algebra.hpp"
0012 #include "Acts/EventData/BoundTrackParameters.hpp"
0013 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0014 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0015 #include "Acts/Propagator/ConstrainedStep.hpp"
0016 #include "Acts/Propagator/EigenStepperDefaultExtension.hpp"
0017 #include "Acts/Propagator/NavigationTarget.hpp"
0018 #include "Acts/Propagator/PropagatorTraits.hpp"
0019 #include "Acts/Propagator/StepperOptions.hpp"
0020 #include "Acts/Propagator/StepperStatistics.hpp"
0021 #include "Acts/Propagator/detail/SteppingHelper.hpp"
0022 #include "Acts/Surfaces/Surface.hpp"
0023 #include "Acts/Utilities/Intersection.hpp"
0024 #include "Acts/Utilities/Result.hpp"
0025 
0026 #include <type_traits>
0027 
0028 namespace Acts {
0029 
0030 class IVolumeMaterial;
0031 
0032 /// @brief Runge-Kutta-Nystroem stepper based on Eigen implementation
0033 /// for the following ODE:
0034 ///
0035 /// r = (x,y,z)    ... global position
0036 /// T = (Ax,Ay,Az) ... momentum direction (normalized)
0037 ///
0038 /// dr/ds = T
0039 /// dT/ds = q/p * (T x B)
0040 ///
0041 /// with s being the arc length of the track, q the charge of the particle,
0042 /// p the momentum magnitude and B the magnetic field
0043 ///
0044 template <typename extension_t = EigenStepperDefaultExtension>
0045 class EigenStepper final {
0046  public:
0047   /// Type alias for bound track parameters
0048   using BoundParameters = BoundTrackParameters;
0049   /// Type alias for jacobian matrix
0050   using Jacobian = BoundMatrix;
0051   /// Type alias for covariance matrix
0052   using Covariance = BoundMatrix;
0053   /// Bound state tuple containing parameters, Jacobian, and path length
0054   using BoundState = std::tuple<BoundParameters, Jacobian, double>;
0055 
0056   /// Configuration for the Eigen stepper.
0057   struct Config {
0058     /// Magnetic field provider
0059     std::shared_ptr<const MagneticFieldProvider> bField;
0060   };
0061 
0062   /// Stepper options including geometry and magnetic field contexts.
0063   struct Options : public StepperPlainOptions {
0064     /// Constructor from geometry and magnetic field contexts
0065     /// @param gctx The geometry context
0066     /// @param mctx The magnetic field context
0067     Options(const GeometryContext& gctx, const MagneticFieldContext& mctx)
0068         : StepperPlainOptions(gctx, mctx) {}
0069 
0070     /// Set plain options
0071     /// @param options The plain options to set
0072     void setPlainOptions(const StepperPlainOptions& options) {
0073       static_cast<StepperPlainOptions&>(*this) = options;
0074     }
0075   };
0076 
0077   /// @brief State for track parameter propagation
0078   ///
0079   /// It contains the stepping information and is provided thread local
0080   /// by the propagator
0081   struct State {
0082     /// Constructor from the initial bound track parameters
0083     ///
0084     /// @param [in] optionsIn is the options object for the stepper
0085     /// @param [in] fieldCacheIn is the cache object for the magnetic field
0086     ///
0087     /// @note the covariance matrix is copied when needed
0088     State(const Options& optionsIn, MagneticFieldProvider::Cache fieldCacheIn)
0089         : options(optionsIn), fieldCache(std::move(fieldCacheIn)) {}
0090 
0091     /// Configuration options for the Eigen stepper
0092     Options options;
0093 
0094     /// Internal free vector parameters
0095     FreeVector pars = FreeVector::Zero();
0096 
0097     /// Particle hypothesis
0098     ParticleHypothesis particleHypothesis = ParticleHypothesis::pion();
0099 
0100     /// Covariance matrix (and indicator)
0101     /// associated with the initial error on track parameters
0102     bool covTransport = false;
0103     /// Covariance matrix for track parameter uncertainties
0104     Covariance cov = Covariance::Zero();
0105 
0106     /// The full jacobian of the transport entire transport
0107     Jacobian jacobian = Jacobian::Identity();
0108 
0109     /// Jacobian from local to the global frame
0110     BoundToFreeMatrix jacToGlobal = BoundToFreeMatrix::Zero();
0111 
0112     /// Pure transport jacobian part from runge kutta integration
0113     FreeMatrix jacTransport = FreeMatrix::Identity();
0114 
0115     /// The propagation derivative
0116     FreeVector derivative = FreeVector::Zero();
0117 
0118     /// Accumulated path length state
0119     double pathAccumulated = 0.;
0120 
0121     /// Total number of performed steps
0122     std::size_t nSteps = 0;
0123 
0124     /// Totoal number of attempted steps
0125     std::size_t nStepTrials = 0;
0126 
0127     /// Adaptive step size of the runge-kutta integration
0128     ConstrainedStep stepSize;
0129 
0130     /// Last performed step (for overstep limit calculation)
0131     double previousStepSize = 0.;
0132 
0133     /// This caches the current magnetic field cell and stays
0134     /// (and interpolates) within it as long as this is valid.
0135     /// See step() code for details.
0136     MagneticFieldProvider::Cache fieldCache;
0137 
0138     /// Algorithmic extension
0139     extension_t extension;
0140 
0141     /// @brief Storage of magnetic field and the sub steps during a RKN4 step
0142     struct {
0143       /// Magnetic field evaluations
0144       Vector3 B_first{}, B_middle{}, B_last{};
0145       /// k_i of the RKN4 algorithm
0146       Vector3 k1{}, k2{}, k3{}, k4{};
0147       /// k_i elements of the momenta
0148       std::array<double, 4> kQoP{};
0149     } stepData;
0150 
0151     /// Statistics of the stepper
0152     StepperStatistics statistics;
0153   };
0154 
0155   /// Constructor requires knowledge of the detector's magnetic field
0156   /// @param bField The magnetic field provider
0157   explicit EigenStepper(std::shared_ptr<const MagneticFieldProvider> bField);
0158 
0159   /// @brief Constructor with configuration
0160   ///
0161   /// @param [in] config The configuration of the stepper
0162   explicit EigenStepper(const Config& config) : m_bField(config.bField) {}
0163 
0164   /// Create a stepper state from given options
0165   /// @param options Configuration options for the stepper state
0166   /// @return Initialized stepper state object
0167   State makeState(const Options& options) const;
0168 
0169   /// Initialize the stepper state from bound track parameters
0170   /// @param state Stepper state to initialize
0171   /// @param par Bound track parameters to initialize from
0172   void initialize(State& state, const BoundParameters& par) const;
0173 
0174   /// Initialize the stepper state from bound parameters and surface
0175   /// @param state Stepper state to initialize
0176   /// @param boundParams Vector of bound track parameters
0177   /// @param cov Optional covariance matrix
0178   /// @param particleHypothesis Particle hypothesis for the track
0179   /// @param surface Surface associated with the parameters
0180   void initialize(State& state, const BoundVector& boundParams,
0181                   const std::optional<BoundMatrix>& cov,
0182                   ParticleHypothesis particleHypothesis,
0183                   const Surface& surface) const;
0184 
0185   /// Get the field for the stepping, it checks first if the access is still
0186   /// within the Cell, and updates the cell if necessary.
0187   ///
0188   /// @param [in,out] state is the propagation state associated with the track
0189   ///                 the magnetic field cell is used (and potentially updated)
0190   /// @param [in] pos is the field position
0191   /// @return Magnetic field vector at the given position or error
0192   Result<Vector3> getField(State& state, const Vector3& pos) const {
0193     // get the field from the cell
0194     return m_bField->getField(pos, state.fieldCache);
0195   }
0196 
0197   /// Global particle position accessor
0198   ///
0199   /// @param state [in] The stepping state (thread-local cache)
0200   /// @return Current global position vector
0201   Vector3 position(const State& state) const {
0202     return state.pars.template segment<3>(eFreePos0);
0203   }
0204 
0205   /// Momentum direction accessor
0206   ///
0207   /// @param state [in] The stepping state (thread-local cache)
0208   /// @return Current normalized direction vector
0209   Vector3 direction(const State& state) const {
0210     return state.pars.template segment<3>(eFreeDir0);
0211   }
0212 
0213   /// QoP direction accessor
0214   ///
0215   /// @param state [in] The stepping state (thread-local cache)
0216   /// @return Charge over momentum (q/p) value
0217   double qOverP(const State& state) const { return state.pars[eFreeQOverP]; }
0218 
0219   /// Absolute momentum accessor
0220   ///
0221   /// @param state [in] The stepping state (thread-local cache)
0222   /// @return Absolute momentum magnitude
0223   double absoluteMomentum(const State& state) const {
0224     return particleHypothesis(state).extractMomentum(qOverP(state));
0225   }
0226 
0227   /// Momentum accessor
0228   ///
0229   /// @param state [in] The stepping state (thread-local cache)
0230   /// @return Current momentum vector
0231   Vector3 momentum(const State& state) const {
0232     return absoluteMomentum(state) * direction(state);
0233   }
0234 
0235   /// Charge access
0236   ///
0237   /// @param state [in] The stepping state (thread-local cache)
0238   /// @return Electric charge of the particle
0239   double charge(const State& state) const {
0240     return particleHypothesis(state).extractCharge(qOverP(state));
0241   }
0242 
0243   /// Particle hypothesis
0244   ///
0245   /// @param state [in] The stepping state (thread-local cache)
0246   /// @return Reference to the particle hypothesis used
0247   const ParticleHypothesis& particleHypothesis(const State& state) const {
0248     return state.particleHypothesis;
0249   }
0250 
0251   /// Time access
0252   ///
0253   /// @param state [in] The stepping state (thread-local cache)
0254   /// @return The time coordinate from the free parameters vector
0255   double time(const State& state) const { return state.pars[eFreeTime]; }
0256 
0257   /// Update surface status
0258   ///
0259   /// It checks the status to the reference surface & updates
0260   /// the step size accordingly
0261   ///
0262   /// @param [in,out] state The stepping state (thread-local cache)
0263   /// @param [in] surface The surface provided
0264   /// @param [in] index The surface intersection index
0265   /// @param [in] propDir The propagation direction
0266   /// @param [in] boundaryTolerance The boundary check for this status update
0267   /// @param [in] surfaceTolerance Surface tolerance used for intersection
0268   /// @param [in] stype The step size type to be set
0269   /// @param [in] logger A @c Logger instance
0270   /// @return Status of the intersection indicating whether surface was reached
0271   IntersectionStatus updateSurfaceStatus(
0272       State& state, const Surface& surface, std::uint8_t index,
0273       Direction propDir, const BoundaryTolerance& boundaryTolerance,
0274       double surfaceTolerance, ConstrainedStep::Type stype,
0275       const Logger& logger = getDummyLogger()) const {
0276     return detail::updateSingleSurfaceStatus<EigenStepper>(
0277         *this, state, surface, index, propDir, boundaryTolerance,
0278         surfaceTolerance, stype, logger);
0279   }
0280 
0281   /// Update step size
0282   ///
0283   /// This method intersects the provided surface and update the navigation
0284   /// step estimation accordingly (hence it changes the state). It also
0285   /// returns the status of the intersection to trigger onSurface in case
0286   /// the surface is reached.
0287   ///
0288   /// @param state [in,out] The stepping state (thread-local cache)
0289   /// @param target [in] The NavigationTarget
0290   /// @param direction [in] The propagation direction
0291   /// @param stype [in] The step size type to be set
0292   void updateStepSize(State& state, const NavigationTarget& target,
0293                       Direction direction, ConstrainedStep::Type stype) const {
0294     static_cast<void>(direction);
0295     double stepSize = target.pathLength();
0296     updateStepSize(state, stepSize, stype);
0297   }
0298 
0299   /// Update step size - explicitly with a double
0300   ///
0301   /// @param state [in,out] The stepping state (thread-local cache)
0302   /// @param stepSize [in] The step size value
0303   /// @param stype [in] The step size type to be set
0304   void updateStepSize(State& state, double stepSize,
0305                       ConstrainedStep::Type stype) const {
0306     state.previousStepSize = state.stepSize.value();
0307     state.stepSize.update(stepSize, stype);
0308   }
0309 
0310   /// Get the step size
0311   ///
0312   /// @param state [in] The stepping state (thread-local cache)
0313   /// @param stype [in] The step size type to be returned
0314   /// @return Current step size for the specified constraint type
0315   double getStepSize(const State& state, ConstrainedStep::Type stype) const {
0316     return state.stepSize.value(stype);
0317   }
0318 
0319   /// Release the Step size
0320   ///
0321   /// @param state [in,out] The stepping state (thread-local cache)
0322   /// @param [in] stype The step size type to be released
0323   void releaseStepSize(State& state, ConstrainedStep::Type stype) const {
0324     state.stepSize.release(stype);
0325   }
0326 
0327   /// Output the Step Size - single component
0328   ///
0329   /// @param state [in,out] The stepping state (thread-local cache)
0330   /// @return String representation of the current step size
0331   std::string outputStepSize(const State& state) const {
0332     return state.stepSize.toString();
0333   }
0334 
0335   /// Create and return the bound state at the current position
0336   ///
0337   /// @brief This transports (if necessary) the covariance
0338   /// to the surface and creates a bound state. It does not check
0339   /// if the transported state is at the surface, this needs to
0340   /// be guaranteed by the propagator
0341   ///
0342   /// @param [in] state State that will be presented as @c BoundState
0343   /// @param [in] surface The surface to which we bind the state
0344   /// @param [in] transportCov Flag steering covariance transport
0345   /// @param [in] freeToBoundCorrection Correction for non-linearity effect during transform from free to bound
0346   ///
0347   /// @return A bound state:
0348   ///   - the parameters at the surface
0349   ///   - the stepwise jacobian towards it (from last bound)
0350   ///   - and the path length (from start - for ordering)
0351   Result<BoundState> boundState(
0352       State& state, const Surface& surface, bool transportCov = true,
0353       const FreeToBoundCorrection& freeToBoundCorrection =
0354           FreeToBoundCorrection(false)) const;
0355 
0356   /// @brief If necessary fill additional members needed for curvilinearState
0357   ///
0358   /// Compute path length derivatives in case they have not been computed
0359   /// yet, which is the case if no step has been executed yet.
0360   ///
0361   /// @param [in, out] state The state of the stepper
0362   /// @return true if nothing is missing after this call, false otherwise.
0363   bool prepareCurvilinearState(State& state) const;
0364 
0365   /// Create and return a curvilinear state at the current position
0366   ///
0367   /// @brief This transports (if necessary) the covariance
0368   /// to the current position and creates a curvilinear state.
0369   ///
0370   /// @param [in] state State that will be presented as @c CurvilinearState
0371   /// @param [in] transportCov Flag steering covariance transport
0372   ///
0373   /// @return A curvilinear state:
0374   ///   - the curvilinear parameters at given position
0375   ///   - the stepweise jacobian towards it (from last bound)
0376   ///   - and the path length (from start - for ordering)
0377   BoundState curvilinearState(State& state, bool transportCov = true) const;
0378 
0379   /// Method to update a stepper state to the some parameters
0380   ///
0381   /// @param [in,out] state State object that will be updated
0382   /// @param [in] freeParams Free parameters that will be written into @p state
0383   /// @param [in] boundParams Corresponding bound parameters used to update jacToGlobal in @p state
0384   /// @param [in] covariance The covariance that will be written into @p state
0385   /// @param [in] surface The surface used to update the jacToGlobal
0386   void update(State& state, const FreeVector& freeParams,
0387               const BoundVector& boundParams, const Covariance& covariance,
0388               const Surface& surface) const;
0389 
0390   /// Method to update the stepper state
0391   ///
0392   /// @param [in,out] state State object that will be updated
0393   /// @param [in] uposition the updated position
0394   /// @param [in] udirection the updated direction
0395   /// @param [in] qOverP the updated qOverP value
0396   /// @param [in] time the updated time value
0397   void update(State& state, const Vector3& uposition, const Vector3& udirection,
0398               double qOverP, double time) const;
0399 
0400   /// Method for on-demand transport of the covariance
0401   /// to a new curvilinear frame at current  position,
0402   /// or direction of the state
0403   ///
0404   /// @param [in,out] state State of the stepper
0405   void transportCovarianceToCurvilinear(State& state) const;
0406 
0407   /// Method for on-demand transport of the covariance
0408   /// to a new curvilinear frame at current position,
0409   /// or direction of the state
0410   ///
0411   /// @tparam surface_t the Surface type
0412   ///
0413   /// @param [in,out] state State of the stepper
0414   /// @param [in] surface is the surface to which the covariance is forwarded to
0415   /// @param [in] freeToBoundCorrection Correction for non-linearity effect during transform from free to bound
0416   /// @note no check is done if the position is actually on the surface
0417   void transportCovarianceToBound(
0418       State& state, const Surface& surface,
0419       const FreeToBoundCorrection& freeToBoundCorrection =
0420           FreeToBoundCorrection(false)) const;
0421 
0422   /// Perform a Runge-Kutta track parameter propagation step
0423   ///
0424   /// @param [in,out] state State of the stepper
0425   /// @param propDir is the direction of propagation
0426   /// @param material is the optional volume material we are stepping through.
0427   //         This is simply ignored if `nullptr`.
0428   /// @return the result of the step
0429   ///
0430   /// @note The state contains the desired step size. It can be negative during
0431   ///       backwards track propagation, and since we're using an adaptive
0432   ///       algorithm, it can be modified by the stepper class during
0433   ///       propagation.
0434   Result<double> step(State& state, Direction propDir,
0435                       const IVolumeMaterial* material) const;
0436 
0437  protected:
0438   /// Magnetic field inside of the detector
0439   std::shared_ptr<const MagneticFieldProvider> m_bField;
0440 };
0441 
0442 template <>
0443 struct SupportsBoundParameters<EigenStepper<>> : public std::true_type {};
0444 
0445 }  // namespace Acts
0446 
0447 #include "Acts/Propagator/EigenStepper.ipp"