Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:10:56

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