Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:51:49

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