Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-11-03 08:57:05

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/Common.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/EventData/TrackParameters.hpp"
0016 #include "Acts/EventData/TransformationHelpers.hpp"
0017 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0020 #include "Acts/Propagator/ConstrainedStep.hpp"
0021 #include "Acts/Propagator/NavigationTarget.hpp"
0022 #include "Acts/Propagator/StepperOptions.hpp"
0023 #include "Acts/Propagator/StepperStatistics.hpp"
0024 #include "Acts/Propagator/detail/SteppingHelper.hpp"
0025 #include "Acts/Surfaces/Surface.hpp"
0026 #include "Acts/Utilities/Intersection.hpp"
0027 #include "Acts/Utilities/Result.hpp"
0028 
0029 #include <cmath>
0030 
0031 namespace Acts {
0032 
0033 class IVolumeMaterial;
0034 
0035 /// @brief the AtlasStepper implementation for the
0036 ///
0037 /// This is based original stepper code from the ATLAS RungeKuttaPropagator
0038 class AtlasStepper {
0039  public:
0040   /// Type alias for Jacobian matrix
0041   using Jacobian = BoundMatrix;
0042   /// Type alias for covariance matrix
0043   using Covariance = BoundSquareMatrix;
0044   /// Type alias for bound state (parameters, jacobian, path length)
0045   using BoundState = std::tuple<BoundTrackParameters, Jacobian, double>;
0046 
0047   struct Config {
0048     std::shared_ptr<const MagneticFieldProvider> bField;
0049   };
0050 
0051   struct Options : public StepperPlainOptions {
0052     Options(const GeometryContext& gctx, const MagneticFieldContext& mctx)
0053         : StepperPlainOptions(gctx, mctx) {}
0054 
0055     void setPlainOptions(const StepperPlainOptions& options) {
0056       static_cast<StepperPlainOptions&>(*this) = options;
0057     }
0058   };
0059 
0060   /// @brief Nested State struct for the local caching
0061   struct State {
0062     /// Constructor
0063     ///
0064     /// @tparam Type of TrackParameters
0065     ///
0066     /// @param [in] optionsIn The options for the stepper
0067     /// @param [in] fieldCacheIn The magnetic field cache
0068     State(const Options& optionsIn, MagneticFieldProvider::Cache fieldCacheIn)
0069         : options(optionsIn), fieldCache(std::move(fieldCacheIn)) {}
0070 
0071     /// Stepper options for this propagation
0072     Options options;
0073 
0074     /// Particle hypothesis used for energy loss calculations
0075     ParticleHypothesis particleHypothesis = ParticleHypothesis::pion();
0076 
0077     // optimisation that init is not called twice
0078     /// Flag indicating whether the state is ready for stepping
0079     bool state_ready = false;
0080     // configuration
0081     /// Flag indicating whether Jacobian calculation is enabled
0082     bool useJacobian = false;
0083     /// Current step size
0084     double step = 0;
0085     /// Maximum path length for this propagation
0086     double maxPathLength = 0;
0087     /// Flag indicating magnetic field boundary conditions
0088     bool mcondition = false;
0089     /// Flag indicating whether magnetic field gradients are needed
0090     bool needgradient = false;
0091     /// Flag indicating new magnetic field evaluation needed
0092     bool newfield = true;
0093     // internal parameters to be used
0094     /// Current magnetic field vector
0095     Vector3 field = Vector3::Zero();
0096     /// Parameter vector for track state and derivatives
0097     std::array<double, 60> pVector{};
0098 
0099     /// Storage pattern of pVector
0100     ///                   /dL0    /dL1    /dPhi   /the   /dCM   /dT
0101     /// X  ->P[0]  dX /   P[ 8]   P[16]   P[24]   P[32]   P[40]  P[48]
0102     /// Y  ->P[1]  dY /   P[ 9]   P[17]   P[25]   P[33]   P[41]  P[49]
0103     /// Z  ->P[2]  dZ /   P[10]   P[18]   P[26]   P[34]   P[42]  P[50]
0104     /// T  ->P[3]  dT/    P[11]   P[19]   P[27]   P[35]   P[43]  P[51]
0105     /// Ax ->P[4]  dAx/   P[12]   P[20]   P[28]   P[36]   P[44]  P[52]
0106     /// Ay ->P[5]  dAy/   P[13]   P[21]   P[29]   P[37]   P[45]  P[53]
0107     /// Az ->P[6]  dAz/   P[14]   P[22]   P[30]   P[38]   P[46]  P[54]
0108     /// CM ->P[7]  dCM/   P[15]   P[23]   P[31]   P[39]   P[47]  P[55]
0109     /// Cache: P[56] - P[59]
0110 
0111     // result
0112     double parameters[eBoundSize] = {0., 0., 0., 0., 0., 0.};
0113     /// Pointer to external covariance matrix
0114     const Covariance* covariance = nullptr;
0115     /// Local covariance matrix storage
0116     Covariance cov = Covariance::Zero();
0117     /// Flag indicating whether covariance transport is enabled
0118     bool covTransport = false;
0119     /// Jacobian matrix storage for parameter derivatives
0120     double jacobian[eBoundSize * eBoundSize] = {};
0121 
0122     // accummulated path length cache
0123     /// Accumulated path length during propagation
0124     double pathAccumulated = 0.;
0125 
0126     /// Total number of performed steps
0127     std::size_t nSteps = 0;
0128 
0129     /// Totoal number of attempted steps
0130     std::size_t nStepTrials = 0;
0131 
0132     // Adaptive step size of the runge-kutta integration
0133     /// Constrained step size for adaptive integration
0134     ConstrainedStep stepSize;
0135 
0136     // Previous step size for overstep estimation
0137     /// Previous step size for overstep estimation
0138     double previousStepSize = 0.;
0139 
0140     /// It caches the current magnetic field cell and stays (and interpolates)
0141     ///  within as long as this is valid. See step() code for details.
0142     MagneticFieldProvider::Cache fieldCache;
0143 
0144     /// Debug output
0145     /// the string where debug messages are stored (optionally)
0146     bool debug = false;
0147     /// String buffer for debug output messages
0148     std::string debugString = "";
0149     /// buffer & formatting for consistent output
0150     std::size_t debugPfxWidth = 30;
0151     /// Width for debug message formatting
0152     std::size_t debugMsgWidth = 50;
0153 
0154     /// The statistics of the stepper
0155     StepperStatistics statistics;
0156   };
0157 
0158   /// Construct AtlasStepper with magnetic field provider
0159   /// @param bField Shared pointer to magnetic field provider
0160   explicit AtlasStepper(std::shared_ptr<const MagneticFieldProvider> bField)
0161       : m_bField(std::move(bField)) {}
0162 
0163   /// Construct AtlasStepper with configuration
0164   /// @param config Configuration containing magnetic field provider and other settings
0165   explicit AtlasStepper(const Config& config) : m_bField(config.bField) {}
0166 
0167   /// Create stepper state with given options
0168   /// @param options Stepper options including magnetic field context
0169   /// @return Stepper state initialized with options and magnetic field cache
0170   State makeState(const Options& options) const {
0171     State state{options, m_bField->makeCache(options.magFieldContext)};
0172     return state;
0173   }
0174 
0175   /// Initialize stepper state from bound track parameters
0176   /// @param state Stepper state to initialize
0177   /// @param par Bound track parameters containing initial conditions
0178   void initialize(State& state, const BoundTrackParameters& par) const {
0179     initialize(state, par.parameters(), par.covariance(),
0180                par.particleHypothesis(), par.referenceSurface());
0181   }
0182 
0183   /// Initialize stepper state from individual parameters
0184   /// @param state Stepper state to initialize
0185   /// @param boundParams Bound track parameter vector
0186   /// @param cov Optional covariance matrix
0187   /// @param particleHypothesis Particle hypothesis (mass, charge)
0188   /// @param surface Reference surface for bound parameters
0189   void initialize(State& state, const BoundVector& boundParams,
0190                   const std::optional<BoundMatrix>& cov,
0191                   ParticleHypothesis particleHypothesis,
0192                   const Surface& surface) const {
0193     state.particleHypothesis = particleHypothesis;
0194 
0195     state.pathAccumulated = 0;
0196     state.nSteps = 0;
0197     state.nStepTrials = 0;
0198     state.stepSize = ConstrainedStep();
0199     state.stepSize.setAccuracy(state.options.initialStepSize);
0200     state.stepSize.setUser(state.options.maxStepSize);
0201     state.previousStepSize = 0;
0202     state.statistics = StepperStatistics();
0203 
0204     // The rest of this constructor is copy&paste of AtlasStepper::update() -
0205     // this is a nasty but working solution for the stepper state without
0206     // functions
0207 
0208     const auto& Vp = boundParams;
0209 
0210     double Sf = std::sin(Vp[eBoundPhi]);
0211     double Cf = std::cos(Vp[eBoundPhi]);
0212     double Se = std::sin(Vp[eBoundTheta]);
0213     double Ce = std::cos(Vp[eBoundTheta]);
0214 
0215     const Vector3 dir = {Cf * Se, Sf * Se, Ce};
0216     const auto pos = surface.localToGlobal(
0217         state.options.geoContext, boundParams.segment<2>(eBoundLoc0), dir);
0218 
0219     double* pVector = state.pVector.data();
0220 
0221     pVector[0] = pos[ePos0];
0222     pVector[1] = pos[ePos1];
0223     pVector[2] = pos[ePos2];
0224     pVector[3] = boundParams[eBoundTime];
0225     pVector[4] = dir[ePos0];
0226     pVector[5] = dir[ePos1];
0227     pVector[6] = dir[ePos2];
0228     pVector[7] = Vp[eBoundQOverP];
0229 
0230     // @todo: remove magic numbers - is that the charge ?
0231     if (std::abs(pVector[7]) < .000000000000001) {
0232       pVector[7] < 0. ? pVector[7] = -.000000000000001
0233                       : pVector[7] = .000000000000001;
0234     }
0235 
0236     // prepare the jacobian if we have a covariance
0237     state.covTransport = cov.has_value();
0238     if (state.covTransport) {
0239       // copy the covariance matrix
0240       state.covariance = new BoundSquareMatrix(*cov);
0241       state.useJacobian = true;
0242       const auto transform =
0243           surface.referenceFrame(state.options.geoContext, pos, dir);
0244 
0245       pVector[8] = transform(0, eBoundLoc0);
0246       pVector[16] = transform(0, eBoundLoc1);
0247       pVector[24] = 0.;
0248       pVector[32] = 0.;
0249       pVector[40] = 0.;
0250       pVector[48] = 0.;  // dX /
0251 
0252       pVector[9] = transform(1, eBoundLoc0);
0253       pVector[17] = transform(1, eBoundLoc1);
0254       pVector[25] = 0.;
0255       pVector[33] = 0.;
0256       pVector[41] = 0.;
0257       pVector[49] = 0.;  // dY /
0258 
0259       pVector[10] = transform(2, eBoundLoc0);
0260       pVector[18] = transform(2, eBoundLoc1);
0261       pVector[26] = 0.;
0262       pVector[34] = 0.;
0263       pVector[42] = 0.;
0264       pVector[50] = 0.;  // dZ /
0265 
0266       pVector[11] = 0.;
0267       pVector[19] = 0.;
0268       pVector[27] = 0.;
0269       pVector[35] = 0.;
0270       pVector[43] = 0.;
0271       pVector[51] = 1.;  // dT/
0272 
0273       pVector[12] = 0.;
0274       pVector[20] = 0.;
0275       pVector[28] = -Sf * Se;  // - sin(phi) * cos(theta)
0276       pVector[36] = Cf * Ce;   // cos(phi) * cos(theta)
0277       pVector[44] = 0.;
0278       pVector[52] = 0.;  // dAx/
0279 
0280       pVector[13] = 0.;
0281       pVector[21] = 0.;
0282       pVector[29] = Cf * Se;  // cos(phi) * sin(theta)
0283       pVector[37] = Sf * Ce;  // sin(phi) * cos(theta)
0284       pVector[45] = 0.;
0285       pVector[53] = 0.;  // dAy/
0286 
0287       pVector[14] = 0.;
0288       pVector[22] = 0.;
0289       pVector[30] = 0.;
0290       pVector[38] = -Se;  // - sin(theta)
0291       pVector[46] = 0.;
0292       pVector[54] = 0.;  // dAz/
0293 
0294       pVector[15] = 0.;
0295       pVector[23] = 0.;
0296       pVector[31] = 0.;
0297       pVector[39] = 0.;
0298       pVector[47] = 1.;
0299       pVector[55] = 0.;  // dCM/
0300 
0301       pVector[56] = 0.;
0302       pVector[57] = 0.;
0303       pVector[58] = 0.;
0304       pVector[59] = 0.;
0305 
0306       // special treatment for surface types
0307       // the disc needs polar coordinate adaptations
0308       if (surface.type() == Surface::Disc) {
0309         double lCf = std::cos(Vp[1]);
0310         double lSf = std::sin(Vp[1]);
0311         double Ax[3] = {transform(0, 0), transform(1, 0), transform(2, 0)};
0312         double Ay[3] = {transform(0, 1), transform(1, 1), transform(2, 1)};
0313         double d0 = lCf * Ax[0] + lSf * Ay[0];
0314         double d1 = lCf * Ax[1] + lSf * Ay[1];
0315         double d2 = lCf * Ax[2] + lSf * Ay[2];
0316         pVector[8] = d0;
0317         pVector[9] = d1;
0318         pVector[10] = d2;
0319         pVector[16] = Vp[0] * (lCf * Ay[0] - lSf * Ax[0]);
0320         pVector[17] = Vp[0] * (lCf * Ay[1] - lSf * Ax[1]);
0321         pVector[18] = Vp[0] * (lCf * Ay[2] - lSf * Ax[2]);
0322       }
0323       // the line needs components that relate direction change
0324       // with global frame change
0325       if (surface.type() == Surface::Perigee ||
0326           surface.type() == Surface::Straw) {
0327         // sticking to the nomenclature of the original RkPropagator
0328         // - axis pointing along the drift/transverse direction
0329         double B[3] = {transform(0, 0), transform(1, 0), transform(2, 0)};
0330         // - axis along the straw
0331         double A[3] = {transform(0, 1), transform(1, 1), transform(2, 1)};
0332         // - normal vector of the reference frame
0333         double C[3] = {transform(0, 2), transform(1, 2), transform(2, 2)};
0334 
0335         // projection of direction onto normal vector of reference frame
0336         double PC = pVector[4] * C[0] + pVector[5] * C[1] + pVector[6] * C[2];
0337         double Bn = 1. / PC;
0338 
0339         double Bx2 = -A[2] * pVector[29];
0340         double Bx3 = A[1] * pVector[38] - A[2] * pVector[37];
0341 
0342         double By2 = A[2] * pVector[28];
0343         double By3 = A[2] * pVector[36] - A[0] * pVector[38];
0344 
0345         double Bz2 = A[0] * pVector[29] - A[1] * pVector[28];
0346         double Bz3 = A[0] * pVector[37] - A[1] * pVector[36];
0347 
0348         double B2 = B[0] * Bx2 + B[1] * By2 + B[2] * Bz2;
0349         double B3 = B[0] * Bx3 + B[1] * By3 + B[2] * Bz3;
0350 
0351         Bx2 = (Bx2 - B[0] * B2) * Bn;
0352         Bx3 = (Bx3 - B[0] * B3) * Bn;
0353         By2 = (By2 - B[1] * B2) * Bn;
0354         By3 = (By3 - B[1] * B3) * Bn;
0355         Bz2 = (Bz2 - B[2] * B2) * Bn;
0356         Bz3 = (Bz3 - B[2] * B3) * Bn;
0357 
0358         //  /dPhi      |     /dThe       |
0359         pVector[24] = Bx2 * Vp[0];
0360         pVector[32] = Bx3 * Vp[0];  // dX/
0361         pVector[25] = By2 * Vp[0];
0362         pVector[33] = By3 * Vp[0];  // dY/
0363         pVector[26] = Bz2 * Vp[0];
0364         pVector[34] = Bz3 * Vp[0];  // dZ/
0365       }
0366     }
0367 
0368     // now declare the state as ready
0369     state.state_ready = true;
0370   }
0371 
0372   /// Get the field for the stepping
0373   /// It checks first if the access is still within the Cell,
0374   /// and updates the cell if necessary, then it takes the field
0375   /// from the cell
0376   /// @param [in,out] state is the stepper state associated with the track
0377   ///                 the magnetic field cell is used (and potentially updated)
0378   /// @param [in] pos is the field position
0379   /// @return Magnetic field vector at the given position or error
0380   Result<Vector3> getField(State& state, const Vector3& pos) const {
0381     // get the field from the cell
0382     auto res = m_bField->getField(pos, state.fieldCache);
0383     if (res.ok()) {
0384       state.field = *res;
0385     }
0386     return res;
0387   }
0388 
0389   /// Get particle position from stepper state
0390   /// @param state Stepper state containing track information
0391   /// @return 3D position vector
0392   Vector3 position(const State& state) const {
0393     return Vector3(state.pVector[0], state.pVector[1], state.pVector[2]);
0394   }
0395 
0396   /// Get particle direction from stepper state
0397   /// @param state Stepper state containing track information
0398   /// @return 3D unit direction vector
0399   Vector3 direction(const State& state) const {
0400     return Vector3(state.pVector[4], state.pVector[5], state.pVector[6]);
0401   }
0402 
0403   /// Get charge over momentum from stepper state
0404   /// @param state Stepper state containing track information
0405   /// @return Charge over momentum (q/p)
0406   double qOverP(const State& state) const { return state.pVector[7]; }
0407 
0408   /// Absolute momentum accessor
0409   ///
0410   /// @param state [in] The stepping state (thread-local cache)
0411   /// @return Absolute momentum magnitude
0412   double absoluteMomentum(const State& state) const {
0413     return particleHypothesis(state).extractMomentum(qOverP(state));
0414   }
0415 
0416   /// Get 3D momentum vector from stepper state
0417   /// @param state Stepper state containing track information
0418   /// @return 3D momentum vector
0419   Vector3 momentum(const State& state) const {
0420     return absoluteMomentum(state) * direction(state);
0421   }
0422 
0423   /// Charge access
0424   ///
0425   /// @param state [in] The stepping state (thread-local cache)
0426   /// @return Electric charge of the particle
0427   double charge(const State& state) const {
0428     return particleHypothesis(state).extractCharge(qOverP(state));
0429   }
0430 
0431   /// Particle hypothesis
0432   ///
0433   /// @param state [in] The stepping state (thread-local cache)
0434   /// @return Reference to the particle hypothesis used
0435   const ParticleHypothesis& particleHypothesis(const State& state) const {
0436     return state.particleHypothesis;
0437   }
0438 
0439   /// Overstep limit
0440   /// @return Overstep limit for this stepper (negative value)
0441   double overstepLimit(const State& /*state*/) const {
0442     return -m_overstepLimit;
0443   }
0444 
0445   /// Time access
0446   /// @param state The stepping state to get time from
0447   /// @return Current time coordinate
0448   double time(const State& state) const { return state.pVector[3]; }
0449 
0450   /// Update surface status
0451   ///
0452   /// This method intersect the provided surface and update the navigation
0453   /// step estimation accordingly (hence it changes the state). It also
0454   /// returns the status of the intersection to trigger onSurface in case
0455   /// the surface is reached.
0456   ///
0457   /// @param [in,out] state The stepping state (thread-local cache)
0458   /// @param [in] surface The surface provided
0459   /// @param [in] index The surface intersection index
0460   /// @param [in] navDir The navigation direction
0461   /// @param [in] boundaryTolerance The boundary check for this status update
0462   /// @param [in] surfaceTolerance Surface tolerance used for intersection
0463   /// @param [in] stype The step size type to be set
0464   /// @param [in] logger Logger instance to use
0465   /// @return IntersectionStatus indicating how the surface was encountered
0466   IntersectionStatus updateSurfaceStatus(
0467       State& state, const Surface& surface, std::uint8_t index,
0468       Direction navDir, const BoundaryTolerance& boundaryTolerance,
0469       double surfaceTolerance, ConstrainedStep::Type stype,
0470       const Logger& logger = getDummyLogger()) const {
0471     return detail::updateSingleSurfaceStatus<AtlasStepper>(
0472         *this, state, surface, index, navDir, boundaryTolerance,
0473         surfaceTolerance, stype, logger);
0474   }
0475 
0476   /// Update step size
0477   ///
0478   /// It checks the status to the reference surface & updates
0479   /// the step size accordingly
0480   ///
0481   /// @param state [in,out] The stepping state (thread-local cache)
0482   /// @param target [in] The NavigationTarget
0483   /// @param direction [in] The propagation direction
0484   /// @param stype [in] The step size type to be set
0485   void updateStepSize(State& state, const NavigationTarget& target,
0486                       Direction direction, ConstrainedStep::Type stype) const {
0487     (void)direction;
0488     double stepSize = target.pathLength();
0489     updateStepSize(state, stepSize, stype);
0490   }
0491 
0492   /// Update step size - explicitly with a double
0493   ///
0494   /// @param [in,out] state The stepping state (thread-local cache)
0495   /// @param [in] stepSize The step size value
0496   /// @param [in] stype The step size type to be set
0497   void updateStepSize(State& state, double stepSize,
0498                       ConstrainedStep::Type stype) const {
0499     state.previousStepSize = state.stepSize.value();
0500     state.stepSize.update(stepSize, stype);
0501   }
0502 
0503   /// Release the Step size
0504   ///
0505   /// @param [in,out] state The stepping state (thread-local cache)
0506   /// @param [in] stype The step size type to be released
0507   void releaseStepSize(State& state, ConstrainedStep::Type stype) const {
0508     state.stepSize.release(stype);
0509   }
0510 
0511   /// Get the step size
0512   ///
0513   /// @param state [in] The stepping state (thread-local cache)
0514   /// @param stype [in] The step size type to be returned
0515   /// @return Current step size for the specified constraint type
0516   double getStepSize(const State& state, ConstrainedStep::Type stype) const {
0517     return state.stepSize.value(stype);
0518   }
0519 
0520   /// Output the Step Size - single component
0521   ///
0522   /// @param [in,out] state The stepping state (thread-local cache)
0523   /// @return String representation of the current step size
0524   std::string outputStepSize(const State& state) const {
0525     return state.stepSize.toString();
0526   }
0527 
0528   /// Create and return the bound state at the current position
0529   ///
0530   ///
0531   /// @param [in] state State that will be presented as @c BoundState
0532   /// @param [in] surface The surface to which we bind the state
0533   /// @param [in] transportCov Flag steering covariance transport
0534   /// @param [in] freeToBoundCorrection Correction for non-linearity effect during transform from free to bound
0535   ///
0536   /// @return A bound state:
0537   ///   - the parameters at the surface
0538   ///   - the stepwise jacobian towards it
0539   ///   - and the path length (from start - for ordering)
0540   Result<BoundState> boundState(
0541       State& state, const Surface& surface, bool transportCov = true,
0542       const FreeToBoundCorrection& freeToBoundCorrection =
0543           FreeToBoundCorrection(false)) const {
0544     // the convert method invalidates the state (in case it's reused)
0545     state.state_ready = false;
0546     // extract state information
0547     Acts::Vector4 pos4;
0548     pos4[ePos0] = state.pVector[0];
0549     pos4[ePos1] = state.pVector[1];
0550     pos4[ePos2] = state.pVector[2];
0551     pos4[eTime] = state.pVector[3];
0552     Acts::Vector3 dir;
0553     dir[eMom0] = state.pVector[4];
0554     dir[eMom1] = state.pVector[5];
0555     dir[eMom2] = state.pVector[6];
0556     const auto qOverP = state.pVector[7];
0557 
0558     // The transport of the covariance
0559     std::optional<Covariance> covOpt = std::nullopt;
0560     if (state.covTransport && transportCov) {
0561       transportCovarianceToBound(state, surface, freeToBoundCorrection);
0562     }
0563     if (state.cov != Covariance::Zero()) {
0564       covOpt = state.cov;
0565     }
0566 
0567     // Fill the end parameters
0568     auto parameters = BoundTrackParameters::create(
0569         state.options.geoContext, surface.getSharedPtr(), pos4, dir, qOverP,
0570         std::move(covOpt), state.particleHypothesis);
0571     if (!parameters.ok()) {
0572       return parameters.error();
0573     }
0574 
0575     Jacobian jacobian(state.jacobian);
0576 
0577     return BoundState(std::move(*parameters), jacobian.transpose(),
0578                       state.pathAccumulated);
0579   }
0580 
0581   /// @brief If necessary fill additional members needed for curvilinearState
0582   ///
0583   /// Compute path length derivatives in case they have not been computed
0584   /// yet, which is the case if no step has been executed yet.
0585   ///
0586   /// @param [in, out] state The stepping state (thread-local cache)
0587   /// @return true if nothing is missing after this call, false otherwise.
0588   bool prepareCurvilinearState(State& state) const {
0589     (void)state;
0590     return true;
0591   }
0592 
0593   /// Create and return a curvilinear state at the current position
0594   ///
0595   ///
0596   /// @param [in] state State that will be presented as @c CurvilinearState
0597   /// @param [in] transportCov Flag steering covariance transport
0598   ///
0599   /// @return A curvilinear state:
0600   ///   - the curvilinear parameters at given position
0601   ///   - the stepweise jacobian towards it
0602   ///   - and the path length (from start - for ordering)
0603   BoundState curvilinearState(State& state, bool transportCov = true) const {
0604     // the convert method invalidates the state (in case it's reused)
0605     state.state_ready = false;
0606     // extract state information
0607     Acts::Vector4 pos4;
0608     pos4[ePos0] = state.pVector[0];
0609     pos4[ePos1] = state.pVector[1];
0610     pos4[ePos2] = state.pVector[2];
0611     pos4[eTime] = state.pVector[3];
0612     Acts::Vector3 dir;
0613     dir[eMom0] = state.pVector[4];
0614     dir[eMom1] = state.pVector[5];
0615     dir[eMom2] = state.pVector[6];
0616     const auto qOverP = state.pVector[7];
0617 
0618     std::optional<Covariance> covOpt = std::nullopt;
0619     if (state.covTransport && transportCov) {
0620       transportCovarianceToCurvilinear(state);
0621     }
0622     if (state.cov != Covariance::Zero()) {
0623       covOpt = state.cov;
0624     }
0625 
0626     BoundTrackParameters parameters = BoundTrackParameters::createCurvilinear(
0627         pos4, dir, qOverP, std::move(covOpt), state.particleHypothesis);
0628 
0629     Jacobian jacobian(state.jacobian);
0630 
0631     return BoundState(std::move(parameters), jacobian.transpose(),
0632                       state.pathAccumulated);
0633   }
0634 
0635   /// The state update method
0636   ///
0637   /// @param [in,out] state The stepper state for
0638   /// @param [in] parameters The new free track parameters at start
0639   /// @param [in] boundParams Corresponding bound parameters
0640   /// @param [in] covariance The updated covariance matrix
0641   /// @param [in] surface The surface used to update the pVector
0642   void update(State& state, const FreeVector& parameters,
0643               const BoundVector& boundParams, const Covariance& covariance,
0644               const Surface& surface) const {
0645     Vector3 direction = parameters.template segment<3>(eFreeDir0).normalized();
0646     state.pVector[0] = parameters[eFreePos0];
0647     state.pVector[1] = parameters[eFreePos1];
0648     state.pVector[2] = parameters[eFreePos2];
0649     state.pVector[3] = parameters[eFreeTime];
0650     state.pVector[4] = direction.x();
0651     state.pVector[5] = direction.y();
0652     state.pVector[6] = direction.z();
0653     state.pVector[7] = std::copysign(parameters[eFreeQOverP], state.pVector[7]);
0654 
0655     // @todo: remove magic numbers - is that the charge ?
0656     if (std::abs(state.pVector[7]) < .000000000000001) {
0657       state.pVector[7] < 0. ? state.pVector[7] = -.000000000000001
0658                             : state.pVector[7] = .000000000000001;
0659     }
0660 
0661     // prepare the jacobian if we have a covariance
0662     // copy the covariance matrix
0663 
0664     Vector3 pos(state.pVector[0], state.pVector[1], state.pVector[2]);
0665     Vector3 mom(state.pVector[4], state.pVector[5], state.pVector[6]);
0666 
0667     double Sf = std::sin(boundParams[eBoundPhi]);
0668     double Cf = std::cos(boundParams[eBoundPhi]);
0669     double Se = std::sin(boundParams[eBoundTheta]);
0670     double Ce = std::cos(boundParams[eBoundTheta]);
0671 
0672     const auto transform =
0673         surface.referenceFrame(state.options.geoContext, pos, mom);
0674 
0675     state.pVector[8] = transform(0, eBoundLoc0);
0676     state.pVector[16] = transform(0, eBoundLoc1);
0677     state.pVector[24] = 0.;
0678     state.pVector[32] = 0.;
0679     state.pVector[40] = 0.;
0680     state.pVector[48] = 0.;  // dX /
0681 
0682     state.pVector[9] = transform(1, eBoundLoc0);
0683     state.pVector[17] = transform(1, eBoundLoc1);
0684     state.pVector[25] = 0.;
0685     state.pVector[33] = 0.;
0686     state.pVector[41] = 0.;
0687     state.pVector[49] = 0.;  // dY /
0688 
0689     state.pVector[10] = transform(2, eBoundLoc0);
0690     state.pVector[18] = transform(2, eBoundLoc1);
0691     state.pVector[26] = 0.;
0692     state.pVector[34] = 0.;
0693     state.pVector[42] = 0.;
0694     state.pVector[50] = 0.;  // dZ /
0695 
0696     state.pVector[11] = 0.;
0697     state.pVector[19] = 0.;
0698     state.pVector[27] = 0.;
0699     state.pVector[35] = 0.;
0700     state.pVector[43] = 0.;
0701     state.pVector[51] = 1.;  // dT/
0702 
0703     state.pVector[12] = 0.;
0704     state.pVector[20] = 0.;
0705     state.pVector[28] = -Sf * Se;  // - sin(phi) * cos(theta)
0706     state.pVector[36] = Cf * Ce;   // cos(phi) * cos(theta)
0707     state.pVector[44] = 0.;
0708     state.pVector[52] = 0.;  // dAx/
0709 
0710     state.pVector[13] = 0.;
0711     state.pVector[21] = 0.;
0712     state.pVector[29] = Cf * Se;  // cos(phi) * sin(theta)
0713     state.pVector[37] = Sf * Ce;  // sin(phi) * cos(theta)
0714     state.pVector[45] = 0.;
0715     state.pVector[53] = 0.;  // dAy/
0716 
0717     state.pVector[14] = 0.;
0718     state.pVector[22] = 0.;
0719     state.pVector[30] = 0.;
0720     state.pVector[38] = -Se;  // - sin(theta)
0721     state.pVector[46] = 0.;
0722     state.pVector[54] = 0.;  // dAz/
0723 
0724     state.pVector[15] = 0.;
0725     state.pVector[23] = 0.;
0726     state.pVector[31] = 0.;
0727     state.pVector[39] = 0.;
0728     state.pVector[47] = 1.;
0729     state.pVector[55] = 0.;  // dCM/
0730 
0731     state.pVector[56] = 0.;
0732     state.pVector[57] = 0.;
0733     state.pVector[58] = 0.;
0734     state.pVector[59] = 0.;
0735 
0736     // special treatment for surface types
0737     // the disc needs polar coordinate adaptations
0738     if (surface.type() == Surface::Disc) {
0739       double lCf = std::cos(boundParams[eBoundLoc1]);
0740       double lSf = std::sin(boundParams[eBoundLoc1]);
0741       double Ax[3] = {transform(0, 0), transform(1, 0), transform(2, 0)};
0742       double Ay[3] = {transform(0, 1), transform(1, 1), transform(2, 1)};
0743       double d0 = lCf * Ax[0] + lSf * Ay[0];
0744       double d1 = lCf * Ax[1] + lSf * Ay[1];
0745       double d2 = lCf * Ax[2] + lSf * Ay[2];
0746       state.pVector[8] = d0;
0747       state.pVector[9] = d1;
0748       state.pVector[10] = d2;
0749       state.pVector[16] = boundParams[eBoundLoc0] * (lCf * Ay[0] - lSf * Ax[0]);
0750       state.pVector[17] = boundParams[eBoundLoc0] * (lCf * Ay[1] - lSf * Ax[1]);
0751       state.pVector[18] = boundParams[eBoundLoc0] * (lCf * Ay[2] - lSf * Ax[2]);
0752     }
0753     // the line needs components that relate direction change
0754     // with global frame change
0755     if (surface.type() == Surface::Perigee ||
0756         surface.type() == Surface::Straw) {
0757       // sticking to the nomenclature of the original RkPropagator
0758       // - axis pointing along the drift/transverse direction
0759       double B[3] = {transform(0, 0), transform(1, 0), transform(2, 0)};
0760       // - axis along the straw
0761       double A[3] = {transform(0, 1), transform(1, 1), transform(2, 1)};
0762       // - normal vector of the reference frame
0763       double C[3] = {transform(0, 2), transform(1, 2), transform(2, 2)};
0764 
0765       // projection of direction onto normal vector of reference frame
0766       double PC = state.pVector[4] * C[0] + state.pVector[5] * C[1] +
0767                   state.pVector[6] * C[2];
0768       double Bn = 1. / PC;
0769 
0770       double Bx2 = -A[2] * state.pVector[29];
0771       double Bx3 = A[1] * state.pVector[38] - A[2] * state.pVector[37];
0772 
0773       double By2 = A[2] * state.pVector[28];
0774       double By3 = A[2] * state.pVector[36] - A[0] * state.pVector[38];
0775 
0776       double Bz2 = A[0] * state.pVector[29] - A[1] * state.pVector[28];
0777       double Bz3 = A[0] * state.pVector[37] - A[1] * state.pVector[36];
0778 
0779       double B2 = B[0] * Bx2 + B[1] * By2 + B[2] * Bz2;
0780       double B3 = B[0] * Bx3 + B[1] * By3 + B[2] * Bz3;
0781 
0782       Bx2 = (Bx2 - B[0] * B2) * Bn;
0783       Bx3 = (Bx3 - B[0] * B3) * Bn;
0784       By2 = (By2 - B[1] * B2) * Bn;
0785       By3 = (By3 - B[1] * B3) * Bn;
0786       Bz2 = (Bz2 - B[2] * B2) * Bn;
0787       Bz3 = (Bz3 - B[2] * B3) * Bn;
0788 
0789       //  /dPhi      |     /dThe       |
0790       state.pVector[24] = Bx2 * boundParams[eBoundLoc0];
0791       state.pVector[32] = Bx3 * boundParams[eBoundLoc0];  // dX/
0792       state.pVector[25] = By2 * boundParams[eBoundLoc0];
0793       state.pVector[33] = By3 * boundParams[eBoundLoc0];  // dY/
0794       state.pVector[26] = Bz2 * boundParams[eBoundLoc0];
0795       state.pVector[34] = Bz3 * boundParams[eBoundLoc0];  // dZ/
0796     }
0797 
0798     state.covariance = new BoundSquareMatrix(covariance);
0799     state.covTransport = true;
0800     state.useJacobian = true;
0801 
0802     // declare the state as ready
0803     state.state_ready = true;
0804   }
0805 
0806   /// Method to update momentum, direction and p
0807   ///
0808   /// @param state The state object
0809   /// @param uposition the updated position
0810   /// @param udirection the updated direction
0811   /// @param qop the updated momentum value
0812   /// @param time the update time
0813   void update(State& state, const Vector3& uposition, const Vector3& udirection,
0814               double qop, double time) const {
0815     // update the vector
0816     state.pVector[0] = uposition[0];
0817     state.pVector[1] = uposition[1];
0818     state.pVector[2] = uposition[2];
0819     state.pVector[3] = time;
0820     state.pVector[4] = udirection[0];
0821     state.pVector[5] = udirection[1];
0822     state.pVector[6] = udirection[2];
0823     state.pVector[7] = qop;
0824   }
0825 
0826   /// Method for on-demand transport of the covariance
0827   /// to a new curvilinear frame at current  position,
0828   /// or direction of the state
0829   ///
0830   /// @param [in,out] state State of the stepper
0831   void transportCovarianceToCurvilinear(State& state) const {
0832     double P[60];
0833     for (unsigned int i = 0; i < 60; ++i) {
0834       P[i] = state.pVector[i];
0835     }
0836 
0837     double p = 1. / P[7];
0838     P[40] *= p;
0839     P[41] *= p;
0840     P[42] *= p;
0841     P[44] *= p;
0842     P[45] *= p;
0843     P[46] *= p;
0844 
0845     double An = std::sqrt(P[4] * P[4] + P[5] * P[5]);
0846     double Ax[3];
0847     if (An != 0.) {
0848       Ax[0] = -P[5] / An;
0849       Ax[1] = P[4] / An;
0850       Ax[2] = 0.;
0851     } else {
0852       Ax[0] = 1.;
0853       Ax[1] = 0.;
0854       Ax[2] = 0.;
0855     }
0856 
0857     double Ay[3] = {-Ax[1] * P[6], Ax[0] * P[6], An};
0858     double S[3] = {P[4], P[5], P[6]};
0859 
0860     double A = P[4] * S[0] + P[5] * S[1] + P[6] * S[2];
0861     if (A != 0.) {
0862       A = 1. / A;
0863     }
0864     S[0] *= A;
0865     S[1] *= A;
0866     S[2] *= A;
0867 
0868     double s0 = P[8] * S[0] + P[9] * S[1] + P[10] * S[2];
0869     double s1 = P[16] * S[0] + P[17] * S[1] + P[18] * S[2];
0870     double s2 = P[24] * S[0] + P[25] * S[1] + P[26] * S[2];
0871     double s3 = P[32] * S[0] + P[33] * S[1] + P[34] * S[2];
0872     double s4 = P[40] * S[0] + P[41] * S[1] + P[42] * S[2];
0873 
0874     P[8] -= (s0 * P[4]);
0875     P[9] -= (s0 * P[5]);
0876     P[10] -= (s0 * P[6]);
0877     P[11] -= (s0 * P[59]);
0878     P[12] -= (s0 * P[56]);
0879     P[13] -= (s0 * P[57]);
0880     P[14] -= (s0 * P[58]);
0881     P[16] -= (s1 * P[4]);
0882     P[17] -= (s1 * P[5]);
0883     P[18] -= (s1 * P[6]);
0884     P[19] -= (s1 * P[59]);
0885     P[20] -= (s1 * P[56]);
0886     P[21] -= (s1 * P[57]);
0887     P[22] -= (s1 * P[58]);
0888     P[24] -= (s2 * P[4]);
0889     P[25] -= (s2 * P[5]);
0890     P[26] -= (s2 * P[6]);
0891     P[27] -= (s2 * P[59]);
0892     P[28] -= (s2 * P[56]);
0893     P[29] -= (s2 * P[57]);
0894     P[30] -= (s2 * P[58]);
0895     P[32] -= (s3 * P[4]);
0896     P[33] -= (s3 * P[5]);
0897     P[34] -= (s3 * P[6]);
0898     P[35] -= (s3 * P[59]);
0899     P[36] -= (s3 * P[56]);
0900     P[37] -= (s3 * P[57]);
0901     P[38] -= (s3 * P[58]);
0902     P[40] -= (s4 * P[4]);
0903     P[41] -= (s4 * P[5]);
0904     P[42] -= (s4 * P[6]);
0905     P[43] -= (s4 * P[59]);
0906     P[44] -= (s4 * P[56]);
0907     P[45] -= (s4 * P[57]);
0908     P[46] -= (s4 * P[58]);
0909 
0910     double P3 = 0, P4 = 0, C = P[4] * P[4] + P[5] * P[5];
0911     if (C > 1.e-20) {
0912       C = 1. / C;
0913       P3 = P[4] * C;
0914       P4 = P[5] * C;
0915       C = -sqrt(C);
0916     } else {
0917       C = -1.e10;
0918       P3 = 1.;
0919       P4 = 0.;
0920     }
0921 
0922     // Jacobian production
0923     //
0924     state.jacobian[0] = Ax[0] * P[8] + Ax[1] * P[9];    // dL0/dL0
0925     state.jacobian[1] = Ax[0] * P[16] + Ax[1] * P[17];  // dL0/dL1
0926     state.jacobian[2] = Ax[0] * P[24] + Ax[1] * P[25];  // dL0/dPhi
0927     state.jacobian[3] = Ax[0] * P[32] + Ax[1] * P[33];  // dL0/dThe
0928     state.jacobian[4] = Ax[0] * P[40] + Ax[1] * P[41];  // dL0/dCM
0929     state.jacobian[5] = 0.;                             // dL0/dT
0930 
0931     state.jacobian[6] = Ay[0] * P[8] + Ay[1] * P[9] + Ay[2] * P[10];  // dL1/dL0
0932     state.jacobian[7] =
0933         Ay[0] * P[16] + Ay[1] * P[17] + Ay[2] * P[18];  // dL1/dL1
0934     state.jacobian[8] =
0935         Ay[0] * P[24] + Ay[1] * P[25] + Ay[2] * P[26];  // dL1/dPhi
0936     state.jacobian[9] =
0937         Ay[0] * P[32] + Ay[1] * P[33] + Ay[2] * P[34];  // dL1/dThe
0938     state.jacobian[10] =
0939         Ay[0] * P[40] + Ay[1] * P[41] + Ay[2] * P[42];  // dL1/dCM
0940     state.jacobian[11] = 0.;                            // dL1/dT
0941 
0942     state.jacobian[12] = P3 * P[13] - P4 * P[12];  // dPhi/dL0
0943     state.jacobian[13] = P3 * P[21] - P4 * P[20];  // dPhi/dL1
0944     state.jacobian[14] = P3 * P[29] - P4 * P[28];  // dPhi/dPhi
0945     state.jacobian[15] = P3 * P[37] - P4 * P[36];  // dPhi/dThe
0946     state.jacobian[16] = P3 * P[45] - P4 * P[44];  // dPhi/dCM
0947     state.jacobian[17] = 0.;                       // dPhi/dT
0948 
0949     state.jacobian[18] = C * P[14];  // dThe/dL0
0950     state.jacobian[19] = C * P[22];  // dThe/dL1
0951     state.jacobian[20] = C * P[30];  // dThe/dPhi
0952     state.jacobian[21] = C * P[38];  // dThe/dThe
0953     state.jacobian[22] = C * P[46];  // dThe/dCM
0954     state.jacobian[23] = 0.;         // dThe/dT
0955 
0956     state.jacobian[24] = 0.;     // dCM /dL0
0957     state.jacobian[25] = 0.;     // dCM /dL1
0958     state.jacobian[26] = 0.;     // dCM /dPhi
0959     state.jacobian[27] = 0.;     // dCM /dTheta
0960     state.jacobian[28] = P[47];  // dCM /dCM
0961     state.jacobian[29] = 0.;     // dCM/dT
0962 
0963     state.jacobian[30] = P[11];  // dT/dL0
0964     state.jacobian[31] = P[19];  // dT/dL1
0965     state.jacobian[32] = P[27];  // dT/dPhi
0966     state.jacobian[33] = P[35];  // dT/dThe
0967     state.jacobian[34] = P[43];  // dT/dCM
0968     state.jacobian[35] = P[51];  // dT/dT
0969 
0970     Eigen::Map<Eigen::Matrix<double, eBoundSize, eBoundSize, Eigen::RowMajor>>
0971         J(state.jacobian);
0972     state.cov = J * (*state.covariance) * J.transpose();
0973   }
0974 
0975   /// Method for on-demand transport of the covariance
0976   /// to a new curvilinear frame at current position,
0977   /// or direction of the state
0978   ///
0979   /// @param [in,out] state State of the stepper
0980   /// @param [in] surface is the surface to which the covariance is forwarded to
0981   void transportCovarianceToBound(
0982       State& state, const Surface& surface,
0983       const FreeToBoundCorrection& /*freeToBoundCorrection*/ =
0984           FreeToBoundCorrection(false)) const {
0985     Acts::Vector3 gp(state.pVector[0], state.pVector[1], state.pVector[2]);
0986     Acts::Vector3 mom(state.pVector[4], state.pVector[5], state.pVector[6]);
0987 
0988     double P[60];
0989     for (unsigned int i = 0; i < 60; ++i) {
0990       P[i] = state.pVector[i];
0991     }
0992 
0993     mom /= std::abs(state.pVector[7]);
0994 
0995     double p = 1. / state.pVector[7];
0996     P[40] *= p;
0997     P[41] *= p;
0998     P[42] *= p;
0999     P[44] *= p;
1000     P[45] *= p;
1001     P[46] *= p;
1002 
1003     const auto fFrame =
1004         surface.referenceFrame(state.options.geoContext, gp, mom);
1005 
1006     double Ax[3] = {fFrame(0, 0), fFrame(1, 0), fFrame(2, 0)};
1007     double Ay[3] = {fFrame(0, 1), fFrame(1, 1), fFrame(2, 1)};
1008     double S[3] = {fFrame(0, 2), fFrame(1, 2), fFrame(2, 2)};
1009 
1010     // this is the projection of direction onto the local normal vector
1011     double A = P[4] * S[0] + P[5] * S[1] + P[6] * S[2];
1012 
1013     if (A != 0.) {
1014       A = 1. / A;
1015     }
1016 
1017     S[0] *= A;
1018     S[1] *= A;
1019     S[2] *= A;
1020 
1021     double s0 = P[8] * S[0] + P[9] * S[1] + P[10] * S[2];
1022     double s1 = P[16] * S[0] + P[17] * S[1] + P[18] * S[2];
1023     double s2 = P[24] * S[0] + P[25] * S[1] + P[26] * S[2];
1024     double s3 = P[32] * S[0] + P[33] * S[1] + P[34] * S[2];
1025     double s4 = P[40] * S[0] + P[41] * S[1] + P[42] * S[2];
1026 
1027     // in case of line-type surfaces - we need to take into account that
1028     // the reference frame changes with variations of all local
1029     // parameters
1030     if (surface.type() == Surface::Straw ||
1031         surface.type() == Surface::Perigee) {
1032       // vector from position to center
1033       double x = P[0] - surface.center(state.options.geoContext).x();
1034       double y = P[1] - surface.center(state.options.geoContext).y();
1035       double z = P[2] - surface.center(state.options.geoContext).z();
1036 
1037       // this is the projection of the direction onto the local y axis
1038       double d = P[4] * Ay[0] + P[5] * Ay[1] + P[6] * Ay[2];
1039 
1040       // this is cos(beta)
1041       double a = (1. - d) * (1. + d);
1042       if (a != 0.) {
1043         a = 1. / a;  // i.e. 1./(1-d^2)
1044       }
1045 
1046       // that's the modified norm vector
1047       double X = d * Ay[0] - P[4];  //
1048       double Y = d * Ay[1] - P[5];  //
1049       double Z = d * Ay[2] - P[6];  //
1050 
1051       // d0 to d1
1052       double d0 = P[12] * Ay[0] + P[13] * Ay[1] + P[14] * Ay[2];
1053       double d1 = P[20] * Ay[0] + P[21] * Ay[1] + P[22] * Ay[2];
1054       double d2 = P[28] * Ay[0] + P[29] * Ay[1] + P[30] * Ay[2];
1055       double d3 = P[36] * Ay[0] + P[37] * Ay[1] + P[38] * Ay[2];
1056       double d4 = P[44] * Ay[0] + P[45] * Ay[1] + P[46] * Ay[2];
1057 
1058       s0 = (((P[8] * X + P[9] * Y + P[10] * Z) + x * (d0 * Ay[0] - P[12])) +
1059             (y * (d0 * Ay[1] - P[13]) + z * (d0 * Ay[2] - P[14]))) *
1060            (-a);
1061 
1062       s1 = (((P[16] * X + P[17] * Y + P[18] * Z) + x * (d1 * Ay[0] - P[20])) +
1063             (y * (d1 * Ay[1] - P[21]) + z * (d1 * Ay[2] - P[22]))) *
1064            (-a);
1065       s2 = (((P[24] * X + P[25] * Y + P[26] * Z) + x * (d2 * Ay[0] - P[28])) +
1066             (y * (d2 * Ay[1] - P[29]) + z * (d2 * Ay[2] - P[30]))) *
1067            (-a);
1068       s3 = (((P[32] * X + P[33] * Y + P[34] * Z) + x * (d3 * Ay[0] - P[36])) +
1069             (y * (d3 * Ay[1] - P[37]) + z * (d3 * Ay[2] - P[38]))) *
1070            (-a);
1071       s4 = (((P[40] * X + P[41] * Y + P[42] * Z) + x * (d4 * Ay[0] - P[44])) +
1072             (y * (d4 * Ay[1] - P[45]) + z * (d4 * Ay[2] - P[46]))) *
1073            (-a);
1074     }
1075 
1076     P[8] -= (s0 * P[4]);
1077     P[9] -= (s0 * P[5]);
1078     P[10] -= (s0 * P[6]);
1079     P[11] -= (s0 * P[59]);
1080     P[12] -= (s0 * P[56]);
1081     P[13] -= (s0 * P[57]);
1082     P[14] -= (s0 * P[58]);
1083 
1084     P[16] -= (s1 * P[4]);
1085     P[17] -= (s1 * P[5]);
1086     P[18] -= (s1 * P[6]);
1087     P[19] -= (s1 * P[59]);
1088     P[20] -= (s1 * P[56]);
1089     P[21] -= (s1 * P[57]);
1090     P[22] -= (s1 * P[58]);
1091 
1092     P[24] -= (s2 * P[4]);
1093     P[25] -= (s2 * P[5]);
1094     P[26] -= (s2 * P[6]);
1095     P[27] -= (s2 * P[59]);
1096     P[28] -= (s2 * P[56]);
1097     P[29] -= (s2 * P[57]);
1098     P[30] -= (s2 * P[58]);
1099 
1100     P[32] -= (s3 * P[4]);
1101     P[33] -= (s3 * P[5]);
1102     P[34] -= (s3 * P[6]);
1103     P[35] -= (s3 * P[59]);
1104     P[36] -= (s3 * P[56]);
1105     P[37] -= (s3 * P[57]);
1106     P[38] -= (s3 * P[58]);
1107 
1108     P[40] -= (s4 * P[4]);
1109     P[41] -= (s4 * P[5]);
1110     P[42] -= (s4 * P[6]);
1111     P[43] -= (s4 * P[59]);
1112     P[44] -= (s4 * P[56]);
1113     P[45] -= (s4 * P[57]);
1114     P[46] -= (s4 * P[58]);
1115 
1116     double P3 = 0, P4 = 0, C = P[4] * P[4] + P[5] * P[5];
1117     if (C > 1.e-20) {
1118       C = 1. / C;
1119       P3 = P[4] * C;
1120       P4 = P[5] * C;
1121       C = -sqrt(C);
1122     } else {
1123       C = -1.e10;
1124       P3 = 1.;
1125       P4 = 0.;
1126     }
1127 
1128     double MA[3] = {Ax[0], Ax[1], Ax[2]};
1129     double MB[3] = {Ay[0], Ay[1], Ay[2]};
1130     // Jacobian production of transport and to_local
1131     if (surface.type() == Surface::Disc) {
1132       // the vector from the disc surface to the p
1133       const auto& sfc = surface.center(state.options.geoContext);
1134       double d[3] = {P[0] - sfc(0), P[1] - sfc(1), P[2] - sfc(2)};
1135       // this needs the transformation to polar coordinates
1136       double RC = d[0] * Ax[0] + d[1] * Ax[1] + d[2] * Ax[2];
1137       double RS = d[0] * Ay[0] + d[1] * Ay[1] + d[2] * Ay[2];
1138       double R2 = RC * RC + RS * RS;
1139 
1140       // inverse radius
1141       double Ri = 1. / sqrt(R2);
1142       MA[0] = (RC * Ax[0] + RS * Ay[0]) * Ri;
1143       MA[1] = (RC * Ax[1] + RS * Ay[1]) * Ri;
1144       MA[2] = (RC * Ax[2] + RS * Ay[2]) * Ri;
1145       MB[0] = (RC * Ay[0] - RS * Ax[0]) * (Ri = 1. / R2);
1146       MB[1] = (RC * Ay[1] - RS * Ax[1]) * Ri;
1147       MB[2] = (RC * Ay[2] - RS * Ax[2]) * Ri;
1148     }
1149 
1150     state.jacobian[0] = MA[0] * P[8] + MA[1] * P[9] + MA[2] * P[10];  // dL0/dL0
1151     state.jacobian[1] =
1152         MA[0] * P[16] + MA[1] * P[17] + MA[2] * P[18];  // dL0/dL1
1153     state.jacobian[2] =
1154         MA[0] * P[24] + MA[1] * P[25] + MA[2] * P[26];  // dL0/dPhi
1155     state.jacobian[3] =
1156         MA[0] * P[32] + MA[1] * P[33] + MA[2] * P[34];  // dL0/dThe
1157     state.jacobian[4] =
1158         MA[0] * P[40] + MA[1] * P[41] + MA[2] * P[42];  // dL0/dCM
1159     state.jacobian[5] = 0.;                             // dL0/dT
1160 
1161     state.jacobian[6] = MB[0] * P[8] + MB[1] * P[9] + MB[2] * P[10];  // dL1/dL0
1162     state.jacobian[7] =
1163         MB[0] * P[16] + MB[1] * P[17] + MB[2] * P[18];  // dL1/dL1
1164     state.jacobian[8] =
1165         MB[0] * P[24] + MB[1] * P[25] + MB[2] * P[26];  // dL1/dPhi
1166     state.jacobian[9] =
1167         MB[0] * P[32] + MB[1] * P[33] + MB[2] * P[34];  // dL1/dThe
1168     state.jacobian[10] =
1169         MB[0] * P[40] + MB[1] * P[41] + MB[2] * P[42];  // dL1/dCM
1170     state.jacobian[11] = 0.;                            // dL1/dT
1171 
1172     state.jacobian[12] = P3 * P[13] - P4 * P[12];  // dPhi/dL0
1173     state.jacobian[13] = P3 * P[21] - P4 * P[20];  // dPhi/dL1
1174     state.jacobian[14] = P3 * P[29] - P4 * P[28];  // dPhi/dPhi
1175     state.jacobian[15] = P3 * P[37] - P4 * P[36];  // dPhi/dThe
1176     state.jacobian[16] = P3 * P[45] - P4 * P[44];  // dPhi/dCM
1177     state.jacobian[17] = 0.;                       // dPhi/dT
1178 
1179     state.jacobian[18] = C * P[14];  // dThe/dL0
1180     state.jacobian[19] = C * P[22];  // dThe/dL1
1181     state.jacobian[20] = C * P[30];  // dThe/dPhi
1182     state.jacobian[21] = C * P[38];  // dThe/dThe
1183     state.jacobian[22] = C * P[46];  // dThe/dCM
1184     state.jacobian[23] = 0.;         // dThe/dT
1185 
1186     state.jacobian[24] = 0.;     // dCM /dL0
1187     state.jacobian[25] = 0.;     // dCM /dL1
1188     state.jacobian[26] = 0.;     // dCM /dPhi
1189     state.jacobian[27] = 0.;     // dCM /dTheta
1190     state.jacobian[28] = P[47];  // dCM /dCM
1191     state.jacobian[29] = 0.;     // dCM/dT
1192 
1193     state.jacobian[30] = P[11];  // dT/dL0
1194     state.jacobian[31] = P[19];  // dT/dL1
1195     state.jacobian[32] = P[27];  // dT/dPhi
1196     state.jacobian[33] = P[35];  // dT/dThe
1197     state.jacobian[34] = P[43];  // dT/dCM
1198     state.jacobian[35] = P[51];  // dT/dT
1199 
1200     Eigen::Map<Eigen::Matrix<double, eBoundSize, eBoundSize, Eigen::RowMajor>>
1201         J(state.jacobian);
1202     state.cov = J * (*state.covariance) * J.transpose();
1203   }
1204 
1205   /// Perform the actual step on the state
1206   ///
1207   /// @param [in,out] state State of the stepper
1208   /// @param propDir is the direction of propagation
1209   /// @param material is the optional volume material we are stepping through.
1210   //         This is simply ignored if `nullptr`.
1211   /// @return the result of the step
1212   ///
1213   /// @note The state contains the desired step size. It can be negative during
1214   ///       backwards track propagation, and since we're using an adaptive
1215   ///       algorithm, it can be modified by the stepper class during
1216   ///       propagation.
1217   Result<double> step(State& state, Direction propDir,
1218                       const IVolumeMaterial* material) const {
1219     (void)material;
1220 
1221     // we use h for keeping the nominclature with the original atlas code
1222     auto h = state.stepSize.value() * propDir;
1223     bool Jac = state.useJacobian;
1224 
1225     double* R = &(state.pVector[0]);  // Coordinates
1226     double* A = &(state.pVector[4]);  // Directions
1227     double* sA = &(state.pVector[56]);
1228     // Invert mometum/2.
1229     double Pi = 0.5 * state.pVector[7];
1230     //    double dltm = 0.0002 * .03;
1231     Vector3 f0, f;
1232 
1233     // if new field is required get it
1234     if (state.newfield) {
1235       const Vector3 pos(R[0], R[1], R[2]);
1236       // This is sd.B_first in EigenStepper
1237       auto fRes = getField(state, pos);
1238       if (!fRes.ok()) {
1239         return fRes.error();
1240       }
1241       f0 = *fRes;
1242     } else {
1243       f0 = state.field;
1244     }
1245 
1246     bool Helix = false;
1247     // if (std::abs(S) < m_cfg.helixStep) Helix = true;
1248 
1249     const auto calcStepSizeScaling =
1250         [&](const double errorEstimate_) -> double {
1251       // For details about these values see ATL-SOFT-PUB-2009-001
1252       constexpr double lower = 0.25;
1253       constexpr double upper = 4.0;
1254       // This is given by the order of the Runge-Kutta method
1255       constexpr double exponent = 0.25;
1256 
1257       double x = state.options.stepTolerance / errorEstimate_;
1258 
1259       if constexpr (exponent == 0.25) {
1260         // This is 3x faster than std::pow
1261         x = std::sqrt(std::sqrt(x));
1262       } else {
1263         x = std::pow(x, exponent);
1264       }
1265 
1266       return std::clamp(x, lower, upper);
1267     };
1268 
1269     const auto isErrorTolerable = [&](const double errorEstimate_) {
1270       // For details about these values see ATL-SOFT-PUB-2009-001
1271       constexpr double marginFactor = 4.0;
1272 
1273       return errorEstimate_ <= marginFactor * state.options.stepTolerance;
1274     };
1275 
1276     double EST = 0;
1277     double initialH = h;
1278 
1279     std::size_t nStepTrials = 0;
1280     while (h != 0.) {
1281       nStepTrials++;
1282 
1283       // PS2 is h/(2*momentum) in EigenStepper
1284       double S3 = (1. / 3.) * h, S4 = .25 * h, PS2 = Pi * h;
1285 
1286       // First point
1287       //
1288       // H0 is (h/(2*momentum) * sd.B_first) in EigenStepper
1289       double H0[3] = {f0[0] * PS2, f0[1] * PS2, f0[2] * PS2};
1290       // { A0, B0, C0 } is (h/2 * sd.k1) in EigenStepper
1291       double A0 = A[1] * H0[2] - A[2] * H0[1];
1292       double B0 = A[2] * H0[0] - A[0] * H0[2];
1293       double C0 = A[0] * H0[1] - A[1] * H0[0];
1294       // { A2, B2, C2 } is (h/2 * sd.k1 + direction) in EigenStepper
1295       double A2 = A0 + A[0];
1296       double B2 = B0 + A[1];
1297       double C2 = C0 + A[2];
1298       // { A1, B1, C1 } is (h/2 * sd.k1 + 2*direction) in EigenStepper
1299       double A1 = A2 + A[0];
1300       double B1 = B2 + A[1];
1301       double C1 = C2 + A[2];
1302 
1303       // Second point
1304       //
1305       if (!Helix) {
1306         // This is pos1 in EigenStepper
1307         const Vector3 pos(R[0] + A1 * S4, R[1] + B1 * S4, R[2] + C1 * S4);
1308         // This is sd.B_middle in EigenStepper
1309         auto fRes = getField(state, pos);
1310         if (!fRes.ok()) {
1311           return fRes.error();
1312         }
1313         f = *fRes;
1314       } else {
1315         f = f0;
1316       }
1317 
1318       // H1 is (h/(2*momentum) * sd.B_middle) in EigenStepper
1319       double H1[3] = {f[0] * PS2, f[1] * PS2, f[2] * PS2};
1320       // { A3, B3, C3 } is (direction + h/2 * sd.k2) in EigenStepper
1321       double A3 = (A[0] + B2 * H1[2]) - C2 * H1[1];
1322       double B3 = (A[1] + C2 * H1[0]) - A2 * H1[2];
1323       double C3 = (A[2] + A2 * H1[1]) - B2 * H1[0];
1324       // { A4, B4, C4 } is (direction + h/2 * sd.k3) in EigenStepper
1325       double A4 = (A[0] + B3 * H1[2]) - C3 * H1[1];
1326       double B4 = (A[1] + C3 * H1[0]) - A3 * H1[2];
1327       double C4 = (A[2] + A3 * H1[1]) - B3 * H1[0];
1328       // { A5, B5, C5 } is (direction + h * sd.k3) in EigenStepper
1329       double A5 = 2. * A4 - A[0];
1330       double B5 = 2. * B4 - A[1];
1331       double C5 = 2. * C4 - A[2];
1332 
1333       // Last point
1334       //
1335       if (!Helix) {
1336         // This is pos2 in EigenStepper
1337         const Vector3 pos(R[0] + h * A4, R[1] + h * B4, R[2] + h * C4);
1338         // This is sd.B_last in Eigen stepper
1339         auto fRes = getField(state, pos);
1340         if (!fRes.ok()) {
1341           return fRes.error();
1342         }
1343         f = *fRes;
1344       } else {
1345         f = f0;
1346       }
1347 
1348       // H2 is (h/(2*momentum) * sd.B_last) in EigenStepper
1349       double H2[3] = {f[0] * PS2, f[1] * PS2, f[2] * PS2};
1350       // { A6, B6, C6 } is (h/2 * sd.k4) in EigenStepper
1351       double A6 = B5 * H2[2] - C5 * H2[1];
1352       double B6 = C5 * H2[0] - A5 * H2[2];
1353       double C6 = A5 * H2[1] - B5 * H2[0];
1354 
1355       // Test approximation quality on give step and possible step reduction
1356       //
1357       // This is (h2 * (sd.k1 - sd.k2 - sd.k3 + sd.k4).template lpNorm<1>())
1358       // in EigenStepper
1359       EST = 2. * std::abs(h) *
1360             (std::abs((A1 + A6) - (A3 + A4)) + std::abs((B1 + B6) - (B3 + B4)) +
1361              std::abs((C1 + C6) - (C3 + C4)));
1362       EST = std::max(1e-20, EST);
1363       if (!isErrorTolerable(EST)) {
1364         const double stepSizeScaling = calcStepSizeScaling(EST);
1365         h *= stepSizeScaling;
1366         // neutralize the sign of h again
1367         state.stepSize.setAccuracy(h * propDir);
1368         //        dltm = 0.;
1369         continue;
1370       }
1371 
1372       //      if (EST < dltm) h *= 2.;
1373 
1374       // Parameters calculation
1375       //
1376       double A00 = A[0], A11 = A[1], A22 = A[2];
1377 
1378       A[0] = 2. * A3 + (A0 + A5 + A6);
1379       A[1] = 2. * B3 + (B0 + B5 + B6);
1380       A[2] = 2. * C3 + (C0 + C5 + C6);
1381 
1382       double D = (A[0] * A[0] + A[1] * A[1]) + (A[2] * A[2] - 9.);
1383       double Sl = 2. / h;
1384       D = (1. / 3.) - ((1. / 648.) * D) * (12. - D);
1385 
1386       R[0] += (A2 + A3 + A4) * S3;
1387       R[1] += (B2 + B3 + B4) * S3;
1388       R[2] += (C2 + C3 + C4) * S3;
1389       A[0] *= D;
1390       A[1] *= D;
1391       A[2] *= D;
1392       sA[0] = A6 * Sl;
1393       sA[1] = B6 * Sl;
1394       sA[2] = C6 * Sl;
1395 
1396       double mass = particleHypothesis(state).mass();
1397       double momentum = absoluteMomentum(state);
1398 
1399       // Evaluate the time propagation
1400       double dtds = std::sqrt(1 + mass * mass / (momentum * momentum));
1401       state.pVector[3] += h * dtds;
1402       state.pVector[59] = dtds;
1403       state.field = f;
1404       state.newfield = false;
1405 
1406       if (Jac) {
1407         double dtdl = h * mass * mass * qOverP(state) / dtds;
1408         state.pVector[43] += dtdl;
1409 
1410         // Jacobian calculation
1411         //
1412         double* d2A = &state.pVector[28];
1413         double* d3A = &state.pVector[36];
1414         double* d4A = &state.pVector[44];
1415         double d2A0 = H0[2] * d2A[1] - H0[1] * d2A[2];
1416         double d2B0 = H0[0] * d2A[2] - H0[2] * d2A[0];
1417         double d2C0 = H0[1] * d2A[0] - H0[0] * d2A[1];
1418         double d3A0 = H0[2] * d3A[1] - H0[1] * d3A[2];
1419         double d3B0 = H0[0] * d3A[2] - H0[2] * d3A[0];
1420         double d3C0 = H0[1] * d3A[0] - H0[0] * d3A[1];
1421         double d4A0 = (A0 + H0[2] * d4A[1]) - H0[1] * d4A[2];
1422         double d4B0 = (B0 + H0[0] * d4A[2]) - H0[2] * d4A[0];
1423         double d4C0 = (C0 + H0[1] * d4A[0]) - H0[0] * d4A[1];
1424         double d2A2 = d2A0 + d2A[0];
1425         double d2B2 = d2B0 + d2A[1];
1426         double d2C2 = d2C0 + d2A[2];
1427         double d3A2 = d3A0 + d3A[0];
1428         double d3B2 = d3B0 + d3A[1];
1429         double d3C2 = d3C0 + d3A[2];
1430         double d4A2 = d4A0 + d4A[0];
1431         double d4B2 = d4B0 + d4A[1];
1432         double d4C2 = d4C0 + d4A[2];
1433         double d0 = d4A[0] - A00;
1434         double d1 = d4A[1] - A11;
1435         double d2 = d4A[2] - A22;
1436         double d2A3 = (d2A[0] + d2B2 * H1[2]) - d2C2 * H1[1];
1437         double d2B3 = (d2A[1] + d2C2 * H1[0]) - d2A2 * H1[2];
1438         double d2C3 = (d2A[2] + d2A2 * H1[1]) - d2B2 * H1[0];
1439         double d3A3 = (d3A[0] + d3B2 * H1[2]) - d3C2 * H1[1];
1440         double d3B3 = (d3A[1] + d3C2 * H1[0]) - d3A2 * H1[2];
1441         double d3C3 = (d3A[2] + d3A2 * H1[1]) - d3B2 * H1[0];
1442         double d4A3 = ((A3 + d0) + d4B2 * H1[2]) - d4C2 * H1[1];
1443         double d4B3 = ((B3 + d1) + d4C2 * H1[0]) - d4A2 * H1[2];
1444         double d4C3 = ((C3 + d2) + d4A2 * H1[1]) - d4B2 * H1[0];
1445         double d2A4 = (d2A[0] + d2B3 * H1[2]) - d2C3 * H1[1];
1446         double d2B4 = (d2A[1] + d2C3 * H1[0]) - d2A3 * H1[2];
1447         double d2C4 = (d2A[2] + d2A3 * H1[1]) - d2B3 * H1[0];
1448         double d3A4 = (d3A[0] + d3B3 * H1[2]) - d3C3 * H1[1];
1449         double d3B4 = (d3A[1] + d3C3 * H1[0]) - d3A3 * H1[2];
1450         double d3C4 = (d3A[2] + d3A3 * H1[1]) - d3B3 * H1[0];
1451         double d4A4 = ((A4 + d0) + d4B3 * H1[2]) - d4C3 * H1[1];
1452         double d4B4 = ((B4 + d1) + d4C3 * H1[0]) - d4A3 * H1[2];
1453         double d4C4 = ((C4 + d2) + d4A3 * H1[1]) - d4B3 * H1[0];
1454         double d2A5 = 2. * d2A4 - d2A[0];
1455         double d2B5 = 2. * d2B4 - d2A[1];
1456         double d2C5 = 2. * d2C4 - d2A[2];
1457         double d3A5 = 2. * d3A4 - d3A[0];
1458         double d3B5 = 2. * d3B4 - d3A[1];
1459         double d3C5 = 2. * d3C4 - d3A[2];
1460         double d4A5 = 2. * d4A4 - d4A[0];
1461         double d4B5 = 2. * d4B4 - d4A[1];
1462         double d4C5 = 2. * d4C4 - d4A[2];
1463         double d2A6 = d2B5 * H2[2] - d2C5 * H2[1];
1464         double d2B6 = d2C5 * H2[0] - d2A5 * H2[2];
1465         double d2C6 = d2A5 * H2[1] - d2B5 * H2[0];
1466         double d3A6 = d3B5 * H2[2] - d3C5 * H2[1];
1467         double d3B6 = d3C5 * H2[0] - d3A5 * H2[2];
1468         double d3C6 = d3A5 * H2[1] - d3B5 * H2[0];
1469         double d4A6 = d4B5 * H2[2] - d4C5 * H2[1];
1470         double d4B6 = d4C5 * H2[0] - d4A5 * H2[2];
1471         double d4C6 = d4A5 * H2[1] - d4B5 * H2[0];
1472 
1473         double* dR = &state.pVector[24];
1474         dR[0] += (d2A2 + d2A3 + d2A4) * S3;
1475         dR[1] += (d2B2 + d2B3 + d2B4) * S3;
1476         dR[2] += (d2C2 + d2C3 + d2C4) * S3;
1477         d2A[0] = ((d2A0 + 2. * d2A3) + (d2A5 + d2A6)) * (1. / 3.);
1478         d2A[1] = ((d2B0 + 2. * d2B3) + (d2B5 + d2B6)) * (1. / 3.);
1479         d2A[2] = ((d2C0 + 2. * d2C3) + (d2C5 + d2C6)) * (1. / 3.);
1480 
1481         dR = &state.pVector[32];
1482         dR[0] += (d3A2 + d3A3 + d3A4) * S3;
1483         dR[1] += (d3B2 + d3B3 + d3B4) * S3;
1484         dR[2] += (d3C2 + d3C3 + d3C4) * S3;
1485         d3A[0] = ((d3A0 + 2. * d3A3) + (d3A5 + d3A6)) * (1. / 3.);
1486         d3A[1] = ((d3B0 + 2. * d3B3) + (d3B5 + d3B6)) * (1. / 3.);
1487         d3A[2] = ((d3C0 + 2. * d3C3) + (d3C5 + d3C6)) * (1. / 3.);
1488 
1489         dR = &state.pVector[40];
1490         dR[0] += (d4A2 + d4A3 + d4A4) * S3;
1491         dR[1] += (d4B2 + d4B3 + d4B4) * S3;
1492         dR[2] += (d4C2 + d4C3 + d4C4) * S3;
1493         d4A[0] = ((d4A0 + 2. * d4A3) + (d4A5 + d4A6 + A6)) * (1. / 3.);
1494         d4A[1] = ((d4B0 + 2. * d4B3) + (d4B5 + d4B6 + B6)) * (1. / 3.);
1495         d4A[2] = ((d4C0 + 2. * d4C3) + (d4C5 + d4C6 + C6)) * (1. / 3.);
1496       }
1497 
1498       break;
1499     }
1500 
1501     state.pathAccumulated += h;
1502     ++state.nSteps;
1503     state.nStepTrials += nStepTrials;
1504 
1505     const double stepSizeScaling = calcStepSizeScaling(EST);
1506     const double nextAccuracy = std::abs(h * stepSizeScaling);
1507     const double previousAccuracy = std::abs(state.stepSize.accuracy());
1508     const double initialStepLength = std::abs(initialH);
1509     if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) {
1510       state.stepSize.setAccuracy(nextAccuracy);
1511     }
1512 
1513     return h;
1514   }
1515 
1516   /// Method that reset the Jacobian to the Identity for when no bound state are
1517   /// available
1518   ///
1519   /// @param [in,out] state State of the stepper
1520   void setIdentityJacobian(State& state) const {
1521     state.jacobian[0] = 1.;  // dL0/dL0
1522     state.jacobian[1] = 0.;  // dL0/dL1
1523     state.jacobian[2] = 0.;  // dL0/dPhi
1524     state.jacobian[3] = 0.;  // dL0/dThe
1525     state.jacobian[4] = 0.;  // dL0/dCM
1526     state.jacobian[5] = 0.;  // dL0/dT
1527 
1528     state.jacobian[6] = 0.;   // dL1/dL0
1529     state.jacobian[7] = 1.;   // dL1/dL1
1530     state.jacobian[8] = 0.;   // dL1/dPhi
1531     state.jacobian[9] = 0.;   // dL1/dThe
1532     state.jacobian[10] = 0.;  // dL1/dCM
1533     state.jacobian[11] = 0.;  // dL1/dT
1534 
1535     state.jacobian[12] = 0.;  // dPhi/dL0
1536     state.jacobian[13] = 0.;  // dPhi/dL1
1537     state.jacobian[14] = 1.;  // dPhi/dPhi
1538     state.jacobian[15] = 0.;  // dPhi/dThe
1539     state.jacobian[16] = 0.;  // dPhi/dCM
1540     state.jacobian[17] = 0.;  // dPhi/dT
1541 
1542     state.jacobian[18] = 0.;  // dThe/dL0
1543     state.jacobian[19] = 0.;  // dThe/dL1
1544     state.jacobian[20] = 0.;  // dThe/dPhi
1545     state.jacobian[21] = 1.;  // dThe/dThe
1546     state.jacobian[22] = 0.;  // dThe/dCM
1547     state.jacobian[23] = 0.;  // dThe/dT
1548 
1549     state.jacobian[24] = 0.;  // dCM /dL0
1550     state.jacobian[25] = 0.;  // dCM /dL1
1551     state.jacobian[26] = 0.;  // dCM /dPhi
1552     state.jacobian[27] = 0.;  // dCM /dTheta
1553     state.jacobian[28] = 1.;  // dCM /dCM
1554     state.jacobian[29] = 0.;  // dCM/dT
1555 
1556     state.jacobian[30] = 0.;  // dT/dL0
1557     state.jacobian[31] = 0.;  // dT/dL1
1558     state.jacobian[32] = 0.;  // dT/dPhi
1559     state.jacobian[33] = 0.;  // dT/dThe
1560     state.jacobian[34] = 0.;  // dT/dCM
1561     state.jacobian[35] = 1.;  // dT/dT
1562   }
1563 
1564  private:
1565   std::shared_ptr<const MagneticFieldProvider> m_bField;
1566 
1567   /// Overstep limit: could/should be dynamic
1568   double m_overstepLimit = 100 * UnitConstants::um;
1569 };
1570 
1571 }  // namespace Acts