File indexing completed on 2025-07-12 07:51:34
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp"
0013
0014 #include "Acts/Definitions/Algebra.hpp"
0015 #include "Acts/Material/IVolumeMaterial.hpp"
0016 #include "Acts/Material/Interactions.hpp"
0017 #include "Acts/Material/Material.hpp"
0018 #include "Acts/Material/MaterialSlab.hpp"
0019 #include "Acts/Propagator/EigenStepperDefaultExtension.hpp"
0020 #include "Acts/Utilities/MathHelpers.hpp"
0021
0022 namespace Acts {
0023
0024
0025
0026
0027
0028
0029 struct EigenStepperDenseExtension {
0030
0031 EigenStepperDefaultExtension defaultExtension;
0032
0033
0034 double currentMomentum = 0.;
0035
0036 double initialMomentum = 0.;
0037
0038
0039 Material material = Material::Vacuum();
0040
0041 std::array<double, 4> dLdl{};
0042
0043 std::array<double, 4> qop{};
0044
0045 std::array<double, 4> dPds{};
0046
0047 double dgdqopValue = 0.;
0048
0049 double g = 0.;
0050
0051 std::array<double, 4> tKi{};
0052
0053 std::array<double, 4> Lambdappi{};
0054
0055 std::array<double, 4> energy{};
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073 template <int i, typename stepper_t>
0074 bool k(const typename stepper_t::State& state, const stepper_t& stepper,
0075 const IVolumeMaterial* volumeMaterial, Vector3& knew,
0076 const Vector3& bField, std::array<double, 4>& kQoP,
0077 const double h = 0., const Vector3& kprev = Vector3::Zero())
0078 requires(i >= 0 && i <= 3)
0079 {
0080 if (volumeMaterial == nullptr) {
0081 return defaultExtension.template k<i>(state, stepper, volumeMaterial,
0082 knew, bField, kQoP, h, kprev);
0083 }
0084
0085 double q = stepper.charge(state);
0086 const auto& particleHypothesis = stepper.particleHypothesis(state);
0087 float mass = particleHypothesis.mass();
0088
0089
0090 if constexpr (i == 0) {
0091
0092 Vector3 position = stepper.position(state);
0093 material = volumeMaterial->material(position.template cast<double>());
0094 initialMomentum = stepper.absoluteMomentum(state);
0095 currentMomentum = initialMomentum;
0096 qop[0] = stepper.qOverP(state);
0097 initializeEnergyLoss(state, stepper);
0098
0099 knew = qop[0] * stepper.direction(state).cross(bField);
0100
0101 Lambdappi[0] = -qop[0] * qop[0] * qop[0] * g * energy[0] / (q * q);
0102
0103 tKi[0] = fastHypot(1, mass * qop[0]);
0104 kQoP[0] = Lambdappi[0];
0105 } else {
0106
0107 updateEnergyLoss(mass, h, state, stepper, i);
0108 if (currentMomentum < state.options.dense.momentumCutOff) {
0109 return false;
0110 }
0111
0112 knew = qop[i] * (stepper.direction(state) + h * kprev).cross(bField);
0113
0114 auto qopNew = qop[0] + h * Lambdappi[i - 1];
0115 Lambdappi[i] = -qopNew * qopNew * qopNew * g * energy[i] / (q * q);
0116 tKi[i] = fastHypot(1, mass * qopNew);
0117 kQoP[i] = Lambdappi[i];
0118 }
0119 return true;
0120 }
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 template <typename stepper_t>
0136 bool finalize(typename stepper_t::State& state, const stepper_t& stepper,
0137 const IVolumeMaterial* volumeMaterial, const double h) const {
0138 if (volumeMaterial == nullptr) {
0139 return defaultExtension.finalize(state, stepper, volumeMaterial, h);
0140 }
0141
0142 const auto& particleHypothesis = stepper.particleHypothesis(state);
0143 float mass = particleHypothesis.mass();
0144
0145
0146 auto newMomentum =
0147 stepper.absoluteMomentum(state) +
0148 (h / 6.) * (dPds[0] + 2. * (dPds[1] + dPds[2]) + dPds[3]);
0149
0150
0151 if (newMomentum < state.options.dense.momentumCutOff) {
0152 return false;
0153 }
0154
0155
0156 state.derivative(7) = -fastHypot(mass, newMomentum) * g /
0157 (newMomentum * newMomentum * newMomentum);
0158
0159
0160 state.pars[eFreeQOverP] = stepper.charge(state) / newMomentum;
0161
0162 state.derivative(3) = fastHypot(1, mass / newMomentum);
0163
0164 state.pars[eFreeTime] +=
0165 (h / 6.) * (tKi[0] + 2. * (tKi[1] + tKi[2]) + tKi[3]);
0166
0167 return true;
0168 }
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 template <typename stepper_t>
0187 bool finalize(typename stepper_t::State& state, const stepper_t& stepper,
0188 const IVolumeMaterial* volumeMaterial, const double h,
0189 FreeMatrix& D) const {
0190 if (volumeMaterial == nullptr) {
0191 return defaultExtension.finalize(state, stepper, volumeMaterial, h, D);
0192 }
0193
0194 return finalize(state, stepper, volumeMaterial, h) &&
0195 transportMatrix(state, stepper, h, D);
0196 }
0197
0198 private:
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 template <typename stepper_t>
0211 bool transportMatrix(typename stepper_t::State& state,
0212 const stepper_t& stepper, const double h,
0213 FreeMatrix& D) const {
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 auto& sd = state.stepData;
0235 auto dir = stepper.direction(state);
0236 const auto& particleHypothesis = stepper.particleHypothesis(state);
0237 float mass = particleHypothesis.mass();
0238
0239 D = FreeMatrix::Identity();
0240 const double half_h = h * 0.5;
0241
0242
0243
0244 auto dFdT = D.block<3, 3>(0, 4);
0245 auto dFdL = D.block<3, 1>(0, 7);
0246
0247 auto dGdT = D.block<3, 3>(4, 4);
0248 auto dGdL = D.block<3, 1>(4, 7);
0249
0250 SquareMatrix3 dk1dT = SquareMatrix3::Zero();
0251 SquareMatrix3 dk2dT = SquareMatrix3::Identity();
0252 SquareMatrix3 dk3dT = SquareMatrix3::Identity();
0253 SquareMatrix3 dk4dT = SquareMatrix3::Identity();
0254
0255 Vector3 dk1dL = Vector3::Zero();
0256 Vector3 dk2dL = Vector3::Zero();
0257 Vector3 dk3dL = Vector3::Zero();
0258 Vector3 dk4dL = Vector3::Zero();
0259
0260
0261 std::array<double, 4> jdL{};
0262
0263
0264 jdL[0] = dLdl[0];
0265 dk1dL = dir.cross(sd.B_first);
0266
0267 jdL[1] = dLdl[1] * (1. + half_h * jdL[0]);
0268 dk2dL = (1. + half_h * jdL[0]) * (dir + half_h * sd.k1).cross(sd.B_middle) +
0269 qop[1] * half_h * dk1dL.cross(sd.B_middle);
0270
0271 jdL[2] = dLdl[2] * (1. + half_h * jdL[1]);
0272 dk3dL = (1. + half_h * jdL[1]) * (dir + half_h * sd.k2).cross(sd.B_middle) +
0273 qop[2] * half_h * dk2dL.cross(sd.B_middle);
0274
0275 jdL[3] = dLdl[3] * (1. + h * jdL[2]);
0276 dk4dL = (1. + h * jdL[2]) * (dir + h * sd.k3).cross(sd.B_last) +
0277 qop[3] * h * dk3dL.cross(sd.B_last);
0278
0279 dk1dT(0, 1) = sd.B_first.z();
0280 dk1dT(0, 2) = -sd.B_first.y();
0281 dk1dT(1, 0) = -sd.B_first.z();
0282 dk1dT(1, 2) = sd.B_first.x();
0283 dk1dT(2, 0) = sd.B_first.y();
0284 dk1dT(2, 1) = -sd.B_first.x();
0285 dk1dT *= qop[0];
0286
0287 dk2dT += half_h * dk1dT;
0288 dk2dT = qop[1] * VectorHelpers::cross(dk2dT, sd.B_middle);
0289
0290 dk3dT += half_h * dk2dT;
0291 dk3dT = qop[2] * VectorHelpers::cross(dk3dT, sd.B_middle);
0292
0293 dk4dT += h * dk3dT;
0294 dk4dT = qop[3] * VectorHelpers::cross(dk4dT, sd.B_last);
0295
0296 dFdT.setIdentity();
0297 dFdT += h / 6. * (dk1dT + dk2dT + dk3dT);
0298 dFdT *= h;
0299
0300 dFdL = h * h / 6. * (dk1dL + dk2dL + dk3dL);
0301
0302 dGdT += h / 6. * (dk1dT + 2. * (dk2dT + dk3dT) + dk4dT);
0303
0304 dGdL = h / 6. * (dk1dL + 2. * (dk2dL + dk3dL) + dk4dL);
0305
0306
0307 D(7, 7) += (h / 6.) * (jdL[0] + 2. * (jdL[1] + jdL[2]) + jdL[3]);
0308
0309
0310
0311
0312
0313
0314
0315
0316 double dtp1dl = qop[0] * mass * mass / fastHypot(1, qop[0] * mass);
0317 double qopNew = qop[0] + half_h * Lambdappi[0];
0318
0319
0320
0321
0322
0323
0324 double dtp2dl = qopNew * mass * mass / fastHypot(1, qopNew * mass);
0325 qopNew = qop[0] + half_h * Lambdappi[1];
0326
0327
0328
0329
0330
0331
0332 double dtp3dl = qopNew * mass * mass / fastHypot(1, qopNew * mass);
0333 qopNew = qop[0] + half_h * Lambdappi[2];
0334 double dtp4dl = qopNew * mass * mass / fastHypot(1, qopNew * mass);
0335
0336
0337
0338
0339
0340 D(3, 7) = (h / 6.) * (dtp1dl + 2. * (dtp2dl + dtp3dl) + dtp4dl);
0341 return true;
0342 }
0343
0344
0345
0346
0347
0348
0349
0350
0351 template <typename stepper_t>
0352 void initializeEnergyLoss(const typename stepper_t::State& state,
0353 const stepper_t& stepper) {
0354 const auto& particleHypothesis = stepper.particleHypothesis(state);
0355 float mass = particleHypothesis.mass();
0356 PdgParticle absPdg = particleHypothesis.absolutePdg();
0357 float absQ = particleHypothesis.absoluteCharge();
0358
0359 energy[0] = fastHypot(initialMomentum, mass);
0360
0361 MaterialSlab slab(material, 1);
0362
0363 if (state.options.dense.meanEnergyLoss) {
0364 g = -computeEnergyLossMean(slab, absPdg, mass, static_cast<float>(qop[0]),
0365 absQ);
0366 } else {
0367
0368
0369 g = -computeEnergyLossMode(slab, absPdg, mass, static_cast<float>(qop[0]),
0370 absQ);
0371 }
0372
0373
0374 dPds[0] = g * energy[0] / initialMomentum;
0375 if (state.covTransport) {
0376
0377
0378 if (state.options.dense.includeGradient) {
0379 if (state.options.dense.meanEnergyLoss) {
0380 dgdqopValue = deriveEnergyLossMeanQOverP(
0381 slab, absPdg, mass, static_cast<float>(qop[0]), absQ);
0382 } else {
0383
0384 dgdqopValue = deriveEnergyLossModeQOverP(
0385 slab, absPdg, mass, static_cast<float>(qop[0]), absQ);
0386 }
0387 }
0388
0389 dLdl[0] = (-qop[0] * qop[0] * g * energy[0] *
0390 (3. - (initialMomentum * initialMomentum) /
0391 (energy[0] * energy[0])) -
0392 qop[0] * qop[0] * qop[0] * energy[0] * dgdqopValue);
0393 }
0394 }
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406 template <typename stepper_t>
0407 void updateEnergyLoss(const double mass, const double h,
0408 const typename stepper_t::State& state,
0409 const stepper_t& stepper, const int i) {
0410
0411 currentMomentum = initialMomentum + h * dPds[i - 1];
0412 energy[i] = fastHypot(currentMomentum, mass);
0413 dPds[i] = g * energy[i] / currentMomentum;
0414 qop[i] = stepper.charge(state) / currentMomentum;
0415
0416 if (state.covTransport) {
0417 dLdl[i] = (-qop[i] * qop[i] * g * energy[i] *
0418 (3. - (currentMomentum * currentMomentum) /
0419 (energy[i] * energy[i])) -
0420 qop[i] * qop[i] * qop[i] * energy[i] * dgdqopValue);
0421 }
0422 }
0423 };
0424
0425 }