Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:18

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