File indexing completed on 2026-09-20 08:19:46
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Definitions/Direction.hpp"
0014 #include "Acts/Definitions/TrackParametrization.hpp"
0015 #include "Acts/Definitions/Units.hpp"
0016 #include "Acts/EventData/ParticleHypothesis.hpp"
0017 #include "Acts/Geometry/GeometryContext.hpp"
0018 #include "Acts/Material/MaterialSlab.hpp"
0019 #include "Acts/Surfaces/Surface.hpp"
0020 #include "Acts/Utilities/MathHelpers.hpp"
0021
0022 #include <algorithm>
0023 #include <cmath>
0024
0025 namespace Acts::detail {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 MaterialUpdateMode determineMaterialUpdateMode(
0037 const Surface& surface, const Surface* startSurface,
0038 const Surface* targetSurface, MaterialUpdateMode requestedMode);
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 template <typename propagator_state_t, typename navigator_t>
0052 MaterialUpdateMode determineMaterialUpdateMode(
0053 const propagator_state_t& state, const navigator_t& navigator,
0054 MaterialUpdateMode requestedMode) {
0055 const Surface* surface = navigator.currentSurface(state.navigation);
0056 const Surface* startSurface = navigator.startSurface(state.navigation);
0057 const Surface* targetSurface = navigator.targetSurface(state.navigation);
0058
0059 if (surface == nullptr) {
0060 return MaterialUpdateMode::NoUpdate;
0061 }
0062
0063 return determineMaterialUpdateMode(*surface, startSurface, targetSurface,
0064 requestedMode);
0065 }
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075 Result<MaterialSlab> evaluateMaterialSlab(const GeometryContext& geoContext,
0076 const Surface& surface,
0077 Direction propagationDirection,
0078 const Vector3& position,
0079 const Vector3& direction,
0080 MaterialUpdateMode updateMode);
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090 template <typename propagator_state_t, typename stepper_t>
0091 Result<MaterialSlab> evaluateMaterialSlab(const propagator_state_t& state,
0092 const stepper_t& stepper,
0093 const Surface& surface,
0094 MaterialUpdateMode updateMode) {
0095 const GeometryContext& geoContext = state.options.geoContext;
0096 const Direction propagationDirection = state.options.direction;
0097 const Vector3 position = stepper.position(state.stepping);
0098 const Vector3 direction = stepper.direction(state.stepping);
0099
0100 return evaluateMaterialSlab(geoContext, surface, propagationDirection,
0101 position, direction, updateMode);
0102 }
0103
0104
0105 struct PointwiseMaterialEffects {
0106 double eLoss = 0;
0107 double variancePhi = 0;
0108 double varianceTheta = 0;
0109 double varianceQoverP = 0;
0110 };
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121 PointwiseMaterialEffects computeMaterialEffects(
0122 const MaterialSlab& slab, const ParticleHypothesis& particleHypothesis,
0123 const Vector3& direction, float qOverP, bool multipleScattering,
0124 bool energyLoss, bool covTransport);
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 template <typename propagator_state_t, typename stepper_t>
0136 PointwiseMaterialEffects computeMaterialEffects(const propagator_state_t& state,
0137 const stepper_t& stepper,
0138 const MaterialSlab& slab,
0139 bool multipleScattering,
0140 bool energyLoss) {
0141 const bool covTransport = state.stepping.covTransport;
0142 const Vector3 direction = stepper.direction(state.stepping);
0143 const float qOverP = stepper.qOverP(state.stepping);
0144 const ParticleHypothesis& particleHypothesis =
0145 stepper.particleHypothesis(state.stepping);
0146
0147 return computeMaterialEffects(slab, particleHypothesis, direction, qOverP,
0148 multipleScattering, energyLoss, covTransport);
0149 }
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162 template <typename propagator_state_t, typename stepper_t>
0163 PointwiseMaterialEffects performMaterialInteraction(
0164 propagator_state_t& state, const stepper_t& stepper,
0165 const MaterialSlab& slab, NoiseUpdateMode noiseUpdateMode,
0166 bool multipleScattering, bool energyLoss) {
0167 if (slab.isVacuum()) {
0168 return {};
0169 }
0170
0171 const PointwiseMaterialEffects effects = computeMaterialEffects(
0172 state, stepper, slab, multipleScattering, energyLoss);
0173
0174 const Direction propDir = state.options.direction;
0175 const ParticleHypothesis& particleHypothesis =
0176 stepper.particleHypothesis(state.stepping);
0177 const double mass = particleHypothesis.mass();
0178 const double absQ = particleHypothesis.absoluteCharge();
0179 const Vector3 position = stepper.position(state.stepping);
0180 const double time = stepper.time(state.stepping);
0181 const Vector3 direction = stepper.direction(state.stepping);
0182 const float qOverP = stepper.qOverP(state.stepping);
0183 const double momentum = stepper.absoluteMomentum(state.stepping);
0184
0185
0186
0187
0188 const double nextE = fastHypot(mass, momentum) - effects.eLoss * propDir;
0189
0190 double nextP = (mass < nextE) ? fastCathetus(nextE, mass) : 0;
0191
0192
0193
0194
0195 static constexpr double minP = 10 * Acts::UnitConstants::MeV;
0196 nextP = std::max(minP, nextP);
0197 const double nextQOverP =
0198 particleHypothesis.qOverP(nextP, std::copysign(absQ, qOverP));
0199
0200
0201 stepper.update(state.stepping, position, direction, nextQOverP, time);
0202
0203
0204
0205
0206 const auto updateVariance = [](double variance, double change,
0207 NoiseUpdateMode updateMode) {
0208
0209
0210 return std::max(0.,
0211 variance + std::copysign(change, toUnderlying(updateMode)));
0212 };
0213
0214
0215
0216 state.stepping.cov(eBoundPhi, eBoundPhi) =
0217 updateVariance(state.stepping.cov(eBoundPhi, eBoundPhi),
0218 effects.variancePhi, noiseUpdateMode);
0219 state.stepping.cov(eBoundTheta, eBoundTheta) =
0220 updateVariance(state.stepping.cov(eBoundTheta, eBoundTheta),
0221 effects.varianceTheta, noiseUpdateMode);
0222 state.stepping.cov(eBoundQOverP, eBoundQOverP) =
0223 updateVariance(state.stepping.cov(eBoundQOverP, eBoundQOverP),
0224 effects.varianceQoverP, noiseUpdateMode);
0225
0226
0227 return effects;
0228 }
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243 template <typename propagator_state_t, typename stepper_t>
0244 Result<PointwiseMaterialEffects> performMaterialInteraction(
0245 propagator_state_t& state, const stepper_t& stepper, const Surface& surface,
0246 MaterialUpdateMode updateMode, NoiseUpdateMode noiseUpdateMode,
0247 bool multipleScattering, bool energyLoss, const Logger& logger) {
0248 const Result<MaterialSlab> slabResult =
0249 evaluateMaterialSlab(state, stepper, surface, updateMode);
0250 if (!slabResult.ok()) {
0251 return slabResult.error();
0252 }
0253 const MaterialSlab& slab = slabResult.value();
0254 if (slab.isVacuum()) {
0255 ACTS_VERBOSE("No material effects on surface: " << surface.geometryId()
0256 << " with update mode: "
0257 << updateMode);
0258 }
0259
0260 const PointwiseMaterialEffects effects = performMaterialInteraction(
0261 state, stepper, slab, noiseUpdateMode, multipleScattering, energyLoss);
0262
0263 const Direction propDir = state.options.direction;
0264
0265 ACTS_VERBOSE("Material effects on surface: "
0266 << surface.geometryId() << " with update mode: " << updateMode
0267 << " are :");
0268 ACTS_VERBOSE("eLoss = " << effects.eLoss * propDir << ", "
0269 << "variancePhi = " << effects.variancePhi << ", "
0270 << "varianceTheta = " << effects.varianceTheta << ", "
0271 << "varianceQoverP = " << effects.varianceQoverP);
0272
0273 return effects;
0274 }
0275
0276 }