Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-13 07:50:14

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