Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-07 09:23: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 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/BoundTrackParameters.hpp"
0015 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0016 #include "Acts/MagneticField/NullBField.hpp"
0017 #include "Acts/Propagator/ConstrainedStep.hpp"
0018 #include "Acts/Propagator/NavigationTarget.hpp"
0019 #include "Acts/Propagator/PropagatorTraits.hpp"
0020 #include "Acts/Propagator/StepperOptions.hpp"
0021 #include "Acts/Propagator/StepperStatistics.hpp"
0022 #include "Acts/Propagator/detail/SteppingHelper.hpp"
0023 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0024 #include "Acts/Surfaces/Surface.hpp"
0025 #include "Acts/Utilities/Intersection.hpp"
0026 #include "Acts/Utilities/Logger.hpp"
0027 #include "Acts/Utilities/MathHelpers.hpp"
0028 #include "Acts/Utilities/Result.hpp"
0029 
0030 #include <cmath>
0031 #include <stdexcept>
0032 #include <string>
0033 #include <tuple>
0034 
0035 namespace Acts {
0036 
0037 class IVolumeMaterial;
0038 
0039 /// @brief straight line stepper based on Surface intersection
0040 ///
0041 /// The straight line stepper is a simple navigation stepper
0042 /// to be used to navigate through the tracking geometry. It can be
0043 /// used for simple material mapping, navigation validation
0044 class StraightLineStepper final {
0045  public:
0046   /// Type alias for bound track parameters
0047   using BoundParameters = BoundTrackParameters;
0048   /// Type alias for transport jacobian matrix
0049   using Jacobian = BoundMatrix;
0050   /// Type alias for covariance matrix
0051   using Covariance = BoundMatrix;
0052   /// Type alias for magnetic field (null field for straight line propagation)
0053   using BField = NullBField;
0054 
0055   struct Config {};
0056 
0057   /// Configuration options for straight line propagation.
0058   struct Options : public StepperPlainOptions {
0059     /// Constructor from geometry and magnetic field contexts
0060     /// @param gctx The geometry context
0061     /// @param mctx The magnetic field context
0062     Options(const GeometryContext& gctx, const MagneticFieldContext& mctx)
0063         : StepperPlainOptions(gctx, mctx) {}
0064 
0065     /// Set plain stepper options
0066     /// @param options The plain options to set
0067     void setPlainOptions(const StepperPlainOptions& options) {
0068       static_cast<StepperPlainOptions&>(*this) = options;
0069     }
0070   };
0071 
0072   /// State for track parameter propagation
0073   ///
0074   struct State {
0075     /// Constructor from the initial bound track parameters
0076     ///
0077     /// @param [in] optionsIn The options for the stepper
0078     ///
0079     /// @note the covariance matrix is copied when needed
0080     explicit State(const Options& optionsIn) : options(optionsIn) {}
0081 
0082     /// Configuration options for the stepper
0083     Options options;
0084 
0085     /// Jacobian from local to the global frame
0086     BoundToFreeMatrix jacToGlobal = BoundToFreeMatrix::Zero();
0087 
0088     /// Pure transport jacobian part from runge kutta integration
0089     FreeMatrix jacTransport = FreeMatrix::Identity();
0090 
0091     /// The propagation derivative
0092     FreeVector derivative = FreeVector::Zero();
0093 
0094     /// Internal free vector parameters
0095     FreeVector pars = FreeVector::Zero();
0096 
0097     /// Particle hypothesis
0098     ParticleHypothesis particleHypothesis = ParticleHypothesis::pion();
0099 
0100     /// Covariance matrix for track parameter uncertainties, set if the
0101     /// covariance is transported
0102     std::optional<Covariance> cov;
0103 
0104     /// accumulated path length state
0105     double pathAccumulated = 0.;
0106 
0107     /// Total number of performed steps
0108     std::size_t nSteps = 0;
0109 
0110     /// adaptive step size of the runge-kutta integration
0111     ConstrainedStep stepSize;
0112 
0113     /// Previous step size for overstep estimation (ignored for straight line
0114     /// stepper)
0115     double previousStepSize = 0.;
0116 
0117     /// Statistics of the stepper
0118     StepperStatistics statistics;
0119   };
0120 
0121   /// Create a stepper state from propagation options
0122   /// @param options The propagation options
0123   /// @return A new stepper state initialized with the provided options
0124   State makeState(const Options& options) const;
0125 
0126   /// Initialize the stepper state from bound track parameters
0127   /// @param state The stepper state to initialize
0128   /// @param par The bound track parameters to initialize from
0129   void initialize(State& state, const BoundParameters& par) const;
0130 
0131   /// Initialize the stepper state from bound parameters and components
0132   /// @param state The stepper state to initialize
0133   /// @param boundParams The bound parameter vector
0134   /// @param cov Optional covariance matrix
0135   /// @param particleHypothesis The particle hypothesis (mass, charge, etc.)
0136   /// @param surface The reference surface
0137   void initialize(State& state, const BoundVector& boundParams,
0138                   const std::optional<BoundMatrix>& cov,
0139                   ParticleHypothesis particleHypothesis,
0140                   const Surface& surface) const;
0141 
0142   /// Get the field for the stepping, this gives back a zero field
0143   /// @return Always returns zero magnetic field vector for straight-line propagation
0144   Result<Vector3> getField(State& /*state*/, const Vector3& /*pos*/) const {
0145     // get the field from the cell
0146     return Result<Vector3>::success({0., 0., 0.});
0147   }
0148 
0149   /// Current global geometric curvature d(direction)/ds in inverse length.
0150   /// Unchanged under propagation reversal; reflects current momentum and field.
0151   /// @param state Stepping state; the field cache may be updated
0152   /// @return Curvature vector, or a field lookup error
0153   Result<Vector3> curvature([[maybe_unused]] State& state) const {
0154     return Result<Vector3>::success(Vector3::Zero());
0155   }
0156 
0157   /// Global particle position accessor
0158   ///
0159   /// @param state [in] The stepping state (thread-local cache)
0160   /// @return Current global position vector
0161   Vector3 position(const State& state) const {
0162     return state.pars.template segment<3>(eFreePos0);
0163   }
0164 
0165   /// Momentum direction accessor
0166   ///
0167   /// @param state [in] The stepping state (thread-local cache)
0168   /// @return Current normalized direction vector
0169   Vector3 direction(const State& state) const {
0170     return state.pars.template segment<3>(eFreeDir0);
0171   }
0172 
0173   /// QoP direction accessor
0174   ///
0175   /// @param state [in] The stepping state (thread-local cache)
0176   /// @return Charge over momentum (q/p) value
0177   double qOverP(const State& state) const { return state.pars[eFreeQOverP]; }
0178 
0179   /// Absolute momentum accessor
0180   ///
0181   /// @param state [in] The stepping state (thread-local cache)
0182   /// @return Absolute momentum magnitude
0183   double absoluteMomentum(const State& state) const {
0184     return particleHypothesis(state).extractMomentum(qOverP(state));
0185   }
0186 
0187   /// Momentum accessor
0188   ///
0189   /// @param state [in] The stepping state (thread-local cache)
0190   /// @return Current momentum vector
0191   Vector3 momentum(const State& state) const {
0192     return absoluteMomentum(state) * direction(state);
0193   }
0194 
0195   /// Charge access
0196   ///
0197   /// @param state [in] The stepping state (thread-local cache)
0198   /// @return Electric charge of the particle
0199   double charge(const State& state) const {
0200     return particleHypothesis(state).extractCharge(qOverP(state));
0201   }
0202 
0203   /// Particle hypothesis
0204   ///
0205   /// @param state [in] The stepping state (thread-local cache)
0206   /// @return Reference to the particle hypothesis used
0207   const ParticleHypothesis& particleHypothesis(const State& state) const {
0208     return state.particleHypothesis;
0209   }
0210 
0211   /// Time access
0212   ///
0213   /// @param state [in] The stepping state (thread-local cache)
0214   /// @return The time coordinate from the free parameters vector
0215   double time(const State& state) const { return state.pars[eFreeTime]; }
0216 
0217   /// Update surface status
0218   ///
0219   /// This method intersects the provided surface and update the navigation
0220   /// step estimation accordingly (hence it changes the state). It also
0221   /// returns the status of the intersection to trigger onSurface in case
0222   /// the surface is reached.
0223   ///
0224   /// @param [in,out] state The stepping state (thread-local cache)
0225   /// @param [in] surface The surface provided
0226   /// @param [in] index The surface intersection index
0227   /// @param [in] navDir The navigation direction
0228   /// @param [in] boundaryTolerance The boundary check for this status update
0229   /// @param [in] surfaceTolerance Surface tolerance used for intersection
0230   /// @param [in] stype The step size type to be set
0231   /// @param [in] logger A logger instance
0232   /// @param curvature Global geometric curvature for the crossing estimate
0233   /// @return Status of the intersection indicating whether surface was reached
0234   IntersectionStatus updateSurfaceStatus(
0235       State& state, const Surface& surface, std::uint8_t index,
0236       Direction navDir, const BoundaryTolerance& boundaryTolerance,
0237       double surfaceTolerance, ConstrainedStep::Type stype,
0238       const Logger& logger = getDummyLogger(),
0239       const Vector3& curvature = Vector3::Zero()) const {
0240     return detail::updateSingleSurfaceStatus<StraightLineStepper>(
0241         *this, state, surface, index, navDir, boundaryTolerance,
0242         surfaceTolerance, stype, logger, curvature);
0243   }
0244 
0245   /// Update step size
0246   ///
0247   /// It checks the status to the reference surface & updates
0248   /// the step size accordingly
0249   ///
0250   /// @param state [in,out] The stepping state (thread-local cache)
0251   /// @param target [in] The NavigationTarget
0252   /// @param direction [in] The propagation direction
0253   /// @param stype [in] The step size type to be set
0254   void updateStepSize(State& state, const NavigationTarget& target,
0255                       Direction direction, ConstrainedStep::Type stype) const {
0256     static_cast<void>(direction);
0257     double stepSize = target.pathLength();
0258     updateStepSize(state, stepSize, stype);
0259   }
0260 
0261   /// Update step size - explicitly with a double
0262   ///
0263   /// @param state [in,out] The stepping state (thread-local cache)
0264   /// @param stepSize [in] The step size value
0265   /// @param stype [in] The step size type to be set
0266   void updateStepSize(State& state, double stepSize,
0267                       ConstrainedStep::Type stype) const {
0268     state.previousStepSize = state.stepSize.value();
0269     state.stepSize.update(stepSize, stype);
0270   }
0271 
0272   /// Get the step size
0273   ///
0274   /// @param state [in] The stepping state (thread-local cache)
0275   /// @param stype [in] The step size type to be returned
0276   /// @return Current step size for the specified constraint type
0277   double getStepSize(const State& state, ConstrainedStep::Type stype) const {
0278     return state.stepSize.value(stype);
0279   }
0280 
0281   /// Release the Step size
0282   ///
0283   /// @param [in,out] state The stepping state (thread-local cache)
0284   /// @param [in] stype The step size type to be released
0285   void releaseStepSize(State& state, ConstrainedStep::Type stype) const {
0286     state.stepSize.release(stype);
0287   }
0288 
0289   /// Output the Step Size - single component
0290   ///
0291   /// @param state [in,out] The stepping state (thread-local cache)
0292   /// @return String representation of the current step size
0293   std::string outputStepSize(const State& state) const {
0294     return state.stepSize.toString();
0295   }
0296 
0297   /// Get the step size constraints
0298   ///
0299   /// @param state [in] The stepping state (thread-local cache)
0300   /// @return The step size constraints
0301   const ConstrainedStep& stepSize(const State& state) const {
0302     return state.stepSize;
0303   }
0304 
0305   /// Get the stepper statistics
0306   ///
0307   /// @param state [in] The stepping state (thread-local cache)
0308   /// @return The statistics since the last initialization
0309   const StepperStatistics& statistics(const State& state) const {
0310     return state.statistics;
0311   }
0312 
0313   /// Get the path length
0314   ///
0315   /// @param state [in] The stepping state (thread-local cache)
0316   /// @return The path length since the last initialization
0317   double pathLength(const State& state) const { return state.pathAccumulated; }
0318 
0319   /// Check if the state carries a covariance
0320   ///
0321   /// @param state [in] The stepping state (thread-local cache)
0322   /// @return True if the covariance is transported
0323   bool hasCovariance(const State& state) const { return state.cov.has_value(); }
0324 
0325   /// Get the covariance at the anchor
0326   ///
0327   /// The anchor is the frame of the last initialization, transport or update.
0328   ///
0329   /// @param state [in] The stepping state (thread-local cache)
0330   /// @return The covariance at the anchor, or no value if the state does not
0331   ///         carry a covariance
0332   const std::optional<Covariance>& covariance(const State& state) const {
0333     return state.cov;
0334   }
0335 
0336   /// Set the covariance at the anchor
0337   ///
0338   /// The state must already carry a covariance, because the stepper only
0339   /// transports the Jacobian while it has one.
0340   ///
0341   /// @param [in,out] state The stepping state (thread-local cache)
0342   /// @param [in] covariance The new covariance at the anchor
0343   /// @throws std::logic_error if the state does not carry a covariance
0344   void setCovariance(State& state, const Covariance& covariance) const {
0345     if (!state.cov.has_value()) {
0346       throw std::logic_error(
0347           "Cannot set the covariance of a state without a covariance");
0348     }
0349     state.cov = covariance;
0350   }
0351 
0352   /// Get the bound parameters at the current position
0353   ///
0354   /// The parameters carry the covariance at the anchor if the state has one.
0355   ///
0356   /// @note It does not check if the state is on @p surface or anchored on it
0357   ///
0358   /// @param [in] state The stepping state (thread-local cache)
0359   /// @param [in] surface The surface of the parameters
0360   /// @return The bound parameters, or a failure if the position cannot be
0361   ///         expressed on @p surface
0362   Result<BoundParameters> boundParameters(const State& state,
0363                                           const Surface& surface) const;
0364 
0365   /// @brief If necessary fill additional members needed for
0366   /// transportToCurvilinear
0367   ///
0368   /// Compute path length derivatives in case they have not been computed
0369   /// yet, which is the case if no step has been executed yet.
0370   ///
0371   /// @param [in, out] state The stepping state (thread-local cache)
0372   /// @return true if nothing is missing after this call, false otherwise.
0373   bool prepareCurvilinearState(State& state) const {
0374     // test whether the accumulated path has still its initial value.
0375     if (state.pathAccumulated != 0) {
0376       return true;
0377     }
0378 
0379     // dr/ds :
0380     state.derivative.template head<3>() = direction(state);
0381     // dt / ds
0382     state.derivative(eFreeTime) = fastHypot(
0383         1., state.particleHypothesis.mass() / absoluteMomentum(state));
0384     // d (dr/ds) / ds : == 0
0385     state.derivative.template segment<3>(4) = Acts::Vector3::Zero().transpose();
0386     // d qop / ds  == 0
0387     state.derivative(eFreeQOverP) = 0.;
0388 
0389     return true;
0390   }
0391 
0392   /// Get the curvilinear parameters at the current position
0393   ///
0394   /// The parameters carry the covariance at the anchor if the state has one.
0395   ///
0396   /// @param [in] state The stepping state (thread-local cache)
0397   /// @return The curvilinear parameters
0398   BoundParameters curvilinearParameters(const State& state) const;
0399 
0400   /// Method to update a stepper state to the some parameters
0401   ///
0402   /// This anchors the state on @p surface.
0403   ///
0404   /// @param [in,out] state State object that will be updated
0405   /// @param [in] freeParams Free parameters that will be written into @p state
0406   /// @param [in] boundParams Corresponding bound parameters used to update jacToGlobal in @p state
0407   /// @param [in] covariance Covariance that will be written into @p state if
0408   ///                        the state carries a covariance
0409   /// @param [in] surface The surface used to update the jacToGlobal
0410   void update(State& state, const FreeVector& freeParams,
0411               const BoundVector& boundParams, const Covariance& covariance,
0412               const Surface& surface) const;
0413 
0414   /// Method to update the stepper state
0415   ///
0416   /// @param [in,out] state State object that will be updated
0417   /// @param [in] uposition the updated position
0418   /// @param [in] udirection the updated direction
0419   /// @param [in] qop the updated qop value
0420   /// @param [in] time the updated time value
0421   void update(State& state, const Vector3& uposition, const Vector3& udirection,
0422               double qop, double time) const;
0423 
0424   /// Transport the covariance to the curvilinear frame at the current position
0425   ///
0426   /// This anchors the state on the curvilinear frame. Without a covariance
0427   /// the state does not change.
0428   ///
0429   /// @param [in,out] state State of the stepper
0430   /// @return The jacobian from the previous anchor to the curvilinear frame
0431   Jacobian transportToCurvilinear(State& state) const;
0432 
0433   /// Transport the covariance to a surface at the current position
0434   ///
0435   /// This anchors the state on @p surface. Without a covariance the state
0436   /// does not change. The state must be on @p surface, and it stays unchanged
0437   /// if it is not.
0438   ///
0439   /// @param [in,out] state State of the stepper
0440   /// @param [in] surface The surface to transport the covariance to
0441   /// @param [in] freeToBoundCorrection Correction for non-linearity effect during transform from free to bound
0442   /// @return The jacobian from the previous anchor to @p surface, or a failure
0443   ///         if the state is not on @p surface
0444   Result<Jacobian> transportToBound(
0445       State& state, const Surface& surface,
0446       const FreeToBoundCorrection& freeToBoundCorrection =
0447           FreeToBoundCorrection(false)) const;
0448 
0449   /// Perform a straight line propagation step
0450   ///
0451   /// @param [in,out] state State of the stepper
0452   /// @param propDir is the direction of propagation
0453   /// @param material is the optional volume material we are stepping through.
0454   //         This is simply ignored if `nullptr`.
0455   /// @return the result of the step
0456   ///
0457   /// @note The state contains the desired step size. It can be negative during
0458   ///       backwards track propagation.
0459   Result<double> step(State& state, Direction propDir,
0460                       const IVolumeMaterial* material) const {
0461     static_cast<void>(material);
0462 
0463     // use the adjusted step size
0464     const auto h = state.stepSize.value() * propDir;
0465     const auto m = state.particleHypothesis.mass();
0466     const auto p = absoluteMomentum(state);
0467     // time propagates along distance as 1/b = sqrt(1 + m²/p²)
0468     const auto dtds = fastHypot(1, m / p);
0469     // Update the track parameters according to the equations of motion
0470     Vector3 dir = direction(state);
0471     state.pars.template segment<3>(eFreePos0) += h * dir;
0472     state.pars[eFreeTime] += h * dtds;
0473 
0474     // Propagate the jacobian
0475     if (state.cov.has_value()) {
0476       // The step transport matrix in global coordinates
0477       FreeMatrix D = FreeMatrix::Identity();
0478       D.block<3, 3>(0, 4) = SquareMatrix<3>::Identity() * h;
0479       // Extend the calculation by the time propagation
0480       // d(t)/d(q/p) = h m^2 (q/p) / (q^2 dt/ds), with the q^2 folded into p
0481       // via p = |q| / |q/p|.
0482       D(3, 7) = h * m * m / (p * p * state.pars[eFreeQOverP] * dtds);
0483       // Set the derivative factor the time
0484       state.derivative(3) = dtds;
0485       // Update jacobian and derivative
0486       state.jacTransport = D * state.jacTransport;
0487       state.derivative.template head<3>() = dir;
0488     }
0489 
0490     // state the path length
0491     state.pathAccumulated += h;
0492     ++state.nSteps;
0493 
0494     ++state.statistics.nAttemptedSteps;
0495     ++state.statistics.nSuccessfulSteps;
0496     if (propDir != Direction::fromScalarZeroAsPositive(h)) {
0497       ++state.statistics.nReverseSteps;
0498     }
0499     state.statistics.pathLength += h;
0500     state.statistics.absolutePathLength += std::abs(h);
0501 
0502     return h;
0503   }
0504 };
0505 
0506 template <>
0507 struct SupportsBoundParameters<StraightLineStepper> : public std::true_type {};
0508 
0509 }  // namespace Acts