File indexing completed on 2025-02-22 09:55:27
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Material/ISurfaceMaterial.hpp"
0012 #include "Acts/Propagator/ConstrainedStep.hpp"
0013 #include "Acts/Propagator/Propagator.hpp"
0014 #include "Acts/Propagator/StandardAborters.hpp"
0015 #include "Acts/Surfaces/Surface.hpp"
0016 #include "ActsFatras/EventData/Hit.hpp"
0017 #include "ActsFatras/EventData/Particle.hpp"
0018 #include "ActsFatras/Kernel/SimulationResult.hpp"
0019
0020 #include <algorithm>
0021 #include <cassert>
0022 #include <cmath>
0023 #include <limits>
0024
0025 namespace ActsFatras::detail {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039 template <typename generator_t, typename decay_t, typename interactions_t,
0040 typename hit_surface_selector_t>
0041 struct SimulationActor {
0042 using result_type = SimulationResult;
0043
0044
0045 struct ParticleNotAlive {
0046
0047
0048 using action_type = SimulationActor;
0049
0050 template <typename propagator_state_t, typename stepper_t,
0051 typename navigator_t>
0052 constexpr bool operator()(propagator_state_t & ,
0053 const stepper_t & ,
0054 const navigator_t & ,
0055 const result_type &result,
0056 const Acts::Logger & ) const {
0057
0058 return !result.isAlive;
0059 }
0060 };
0061
0062
0063 generator_t *generator = nullptr;
0064
0065 decay_t decay;
0066
0067 interactions_t interactions;
0068
0069 hit_surface_selector_t selectHitSurface;
0070
0071 Particle initialParticle;
0072
0073
0074 Particle::Scalar properTimeRelativeTolerance = 1e-3;
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085 template <typename propagator_state_t, typename stepper_t,
0086 typename navigator_t>
0087 void operator()(propagator_state_t &state, stepper_t &stepper,
0088 navigator_t &navigator, result_type &result,
0089 const Acts::Logger &logger) const {
0090 assert(generator and "The generator pointer must be valid");
0091
0092
0093 if (!result.isAlive) {
0094 return;
0095 }
0096
0097 if (Acts::EndOfWorldReached{}(state, stepper, navigator, logger)) {
0098 result.isAlive = false;
0099 return;
0100 }
0101
0102
0103 if ((navigator.startSurface(state.navigation) != nullptr) &&
0104 (navigator.startSurface(state.navigation) ==
0105 navigator.currentSurface(state.navigation))) {
0106 return;
0107 }
0108
0109
0110
0111
0112
0113 if (std::isnan(result.properTimeLimit)) {
0114
0115
0116 result.particle =
0117 makeParticle(initialParticle, state, stepper, navigator);
0118 result.properTimeLimit =
0119 decay.generateProperTimeLimit(*generator, initialParticle);
0120 } else {
0121 result.particle =
0122 makeParticle(result.particle, state, stepper, navigator);
0123 }
0124
0125
0126 if (std::isfinite(result.properTimeLimit) &&
0127 (result.properTimeLimit - result.particle.properTime() <
0128 result.properTimeLimit * properTimeRelativeTolerance)) {
0129 auto descendants = decay.run(generator, result.particle);
0130 for (auto &&descendant : descendants) {
0131 result.generatedParticles.emplace_back(std::move(descendant));
0132 }
0133 result.isAlive = false;
0134 return;
0135 }
0136
0137
0138 if (std::isfinite(result.properTimeLimit)) {
0139 assert(result.particle.mass() > 0.0 && "Particle must have mass");
0140
0141
0142
0143
0144 const auto properTimeDiff =
0145 result.properTimeLimit - result.particle.properTime();
0146
0147
0148 const auto stepSize = properTimeDiff *
0149 result.particle.absoluteMomentum() /
0150 result.particle.mass();
0151 stepper.updateStepSize(state.stepping, stepSize,
0152 Acts::ConstrainedStep::user);
0153 }
0154
0155
0156 if (std::isnan(result.x0Limit) || std::isnan(result.l0Limit)) {
0157 armPointLikeInteractions(initialParticle, result);
0158 }
0159
0160
0161 if (state.stage == Acts::PropagatorStage::postPropagation) {
0162 return;
0163 }
0164
0165 if (!navigator.currentSurface(state.navigation)) {
0166 return;
0167 }
0168 const Acts::Surface &surface = *navigator.currentSurface(state.navigation);
0169
0170
0171
0172 const Particle before = result.particle;
0173
0174
0175 if (surface.surfaceMaterial()) {
0176
0177
0178
0179 auto lpResult = surface.globalToLocal(state.geoContext, before.position(),
0180 before.direction());
0181 if (lpResult.ok()) {
0182 Acts::Vector2 local = lpResult.value();
0183 Acts::MaterialSlab slab =
0184 surface.surfaceMaterial()->materialSlab(local);
0185
0186 if (slab) {
0187
0188 auto normal = surface.normal(state.geoContext, before.position(),
0189 before.direction());
0190
0191
0192 auto cosIncidenceInv = normal.norm() / normal.dot(before.direction());
0193
0194 slab.scaleThickness(std::abs(cosIncidenceInv));
0195
0196 interact(slab, result);
0197 }
0198 }
0199 }
0200 Particle &after = result.particle;
0201
0202
0203 if (selectHitSurface(surface)) {
0204 result.hits.emplace_back(
0205 surface.geometryId(), before.particleId(),
0206
0207 Hit::Scalar{0.5} * (before.fourPosition() + after.fourPosition()),
0208 before.fourMomentum(), after.fourMomentum(), result.hits.size());
0209
0210 after.setNumberOfHits(result.hits.size());
0211 }
0212
0213 if (after.absoluteMomentum() == 0.0) {
0214 result.isAlive = false;
0215 return;
0216 }
0217
0218
0219 stepper.update(state.stepping, after.position(), after.direction(),
0220 after.qOverP(), after.time());
0221 }
0222
0223
0224 template <typename propagator_state_t, typename stepper_t,
0225 typename navigator_t>
0226 Particle makeParticle(const Particle &previous, propagator_state_t &state,
0227 stepper_t &stepper, navigator_t &navigator) const {
0228
0229
0230
0231
0232 const auto deltaLabTime = stepper.time(state.stepping) - previous.time();
0233
0234
0235
0236
0237 const auto gammaInv = previous.mass() / previous.energy();
0238 const auto properTime = previous.properTime() + gammaInv * deltaLabTime;
0239 const Acts::Surface *currentSurface = nullptr;
0240 if (navigator.currentSurface(state.navigation) != nullptr) {
0241 currentSurface = navigator.currentSurface(state.navigation);
0242 }
0243
0244 return Particle(previous)
0245 .setPosition4(stepper.position(state.stepping),
0246 stepper.time(state.stepping))
0247 .setDirection(stepper.direction(state.stepping))
0248 .setAbsoluteMomentum(stepper.absoluteMomentum(state.stepping))
0249 .setProperTime(properTime)
0250 .setReferenceSurface(currentSurface);
0251 }
0252
0253
0254 void armPointLikeInteractions(const Particle &particle,
0255 result_type &result) const {
0256 auto selection = interactions.armPointLike(*generator, particle);
0257 result.x0Limit = selection.x0Limit;
0258 result.l0Limit = selection.l0Limit;
0259 result.x0Process = selection.x0Process;
0260 result.l0Process = selection.l0Process;
0261 }
0262
0263
0264
0265
0266
0267 void interact(const Acts::MaterialSlab &slab, result_type &result) const {
0268
0269
0270 auto runContinuousPartial = [&, this](float fraction) {
0271 Acts::MaterialSlab partialSlab = slab;
0272 partialSlab.scaleThickness(fraction);
0273
0274 const auto x0 = result.particle.pathInX0() + partialSlab.thicknessInX0();
0275 const auto l0 = result.particle.pathInX0() + partialSlab.thicknessInL0();
0276 bool retval = false;
0277 if (interactions.runContinuous(*(this->generator), partialSlab,
0278 result.particle,
0279 result.generatedParticles)) {
0280 result.isAlive = false;
0281 retval = true;
0282 }
0283
0284
0285
0286
0287
0288 result.particle.setMaterialPassed(x0, l0);
0289 return retval;
0290 };
0291
0292
0293 const auto slabX0 = slab.thicknessInX0();
0294 const auto slabL0 = slab.thicknessInL0();
0295
0296
0297 const auto x0Dist = result.x0Limit - result.particle.pathInX0();
0298 const auto l0Dist = result.l0Limit - result.particle.pathInL0();
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316 const float fracX0 =
0317 std::clamp(static_cast<float>(x0Dist / slabX0), 0.0f, 1.0f);
0318 const float fracL0 =
0319 std::clamp(static_cast<float>(l0Dist / slabL0), 0.0f, 1.0f);
0320
0321 const float frac = std::min(fracX0, fracL0);
0322
0323
0324 if (0.0f < frac) {
0325
0326 if (runContinuousPartial(frac)) {
0327 return;
0328 }
0329 }
0330
0331 if (frac < 1.0f) {
0332
0333 const std::size_t process =
0334 (fracX0 < fracL0) ? result.x0Process : result.l0Process;
0335
0336 if (interactions.runPointLike(*generator, process, result.particle,
0337 result.generatedParticles)) {
0338 result.isAlive = false;
0339 return;
0340 }
0341
0342 if (runContinuousPartial(1.0 - frac)) {
0343 return;
0344 }
0345
0346
0347
0348
0349
0350
0351
0352 armPointLikeInteractions(result.particle, result);
0353 }
0354 }
0355 };
0356
0357 }