File indexing completed on 2025-07-02 08:05:54
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/EventData/Charge.hpp"
0012 #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp"
0013 #include "Acts/EventData/TrackParameters.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0016 #include "Acts/Propagator/AbortList.hpp"
0017 #include "Acts/Propagator/ActionList.hpp"
0018 #include "Acts/Propagator/Propagator.hpp"
0019 #include "Acts/Propagator/StandardAborters.hpp"
0020 #include "Acts/Utilities/Logger.hpp"
0021 #include "Acts/Utilities/Result.hpp"
0022 #include "ActsFatras/EventData/Hit.hpp"
0023 #include "ActsFatras/EventData/Particle.hpp"
0024 #include "ActsFatras/Kernel/SimulationResult.hpp"
0025 #include "ActsFatras/Kernel/detail/SimulationActor.hpp"
0026 #include "ActsFatras/Kernel/detail/SimulationError.hpp"
0027
0028 #include <algorithm>
0029 #include <cassert>
0030 #include <iterator>
0031 #include <memory>
0032 #include <vector>
0033
0034 namespace ActsFatras {
0035
0036
0037
0038
0039
0040
0041
0042 template <typename propagator_t, typename interactions_t,
0043 typename hit_surface_selector_t, typename decay_t>
0044 struct SingleParticleSimulation {
0045
0046 propagator_t propagator;
0047
0048 double maxStepSize = std::numeric_limits<double>::max();
0049
0050 double pathLimit = std::numeric_limits<double>::max();
0051
0052 decay_t decay;
0053
0054 interactions_t interactions;
0055
0056 hit_surface_selector_t selectHitSurface;
0057
0058 std::unique_ptr<const Acts::Logger> logger;
0059
0060
0061 SingleParticleSimulation(propagator_t &&propagator_,
0062 std::unique_ptr<const Acts::Logger> _logger)
0063 : propagator(propagator_), logger(std::move(_logger)) {}
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074 template <typename generator_t>
0075 Acts::Result<SimulationResult> simulate(
0076 const Acts::GeometryContext &geoCtx,
0077 const Acts::MagneticFieldContext &magCtx, generator_t &generator,
0078 const Particle &particle) const {
0079
0080 using Actor = detail::SimulationActor<generator_t, decay_t, interactions_t,
0081 hit_surface_selector_t>;
0082 using Aborter = typename Actor::ParticleNotAlive;
0083 using Result = typename Actor::result_type;
0084 using Actions = Acts::ActionList<Actor>;
0085 using Abort = Acts::AbortList<Aborter, Acts::EndOfWorldReached>;
0086 using PropagatorOptions =
0087 typename propagator_t::template Options<Actions, Abort>;
0088
0089
0090 PropagatorOptions options(geoCtx, magCtx);
0091 options.stepping.maxStepSize = maxStepSize;
0092 options.pathLimit = pathLimit;
0093
0094 auto &actor = options.actionList.template get<Actor>();
0095 actor.generator = &generator;
0096 actor.decay = decay;
0097 actor.interactions = interactions;
0098 actor.selectHitSurface = selectHitSurface;
0099 actor.initialParticle = particle;
0100
0101 if (particle.hasReferenceSurface()) {
0102 auto result = propagator.propagate(
0103 particle.boundParameters(geoCtx).value(), options);
0104 if (!result.ok()) {
0105 return result.error();
0106 }
0107 auto &value = result.value().template get<Result>();
0108 return std::move(value);
0109 }
0110
0111 auto result =
0112 propagator.propagate(particle.curvilinearParameters(), options);
0113 if (!result.ok()) {
0114 return result.error();
0115 }
0116 auto &value = result.value().template get<Result>();
0117 return std::move(value);
0118 }
0119 };
0120
0121
0122 struct FailedParticle {
0123
0124
0125
0126
0127
0128 Particle particle;
0129
0130 std::error_code error;
0131 };
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143 template <typename charged_selector_t, typename charged_simulator_t,
0144 typename neutral_selector_t, typename neutral_simulator_t>
0145 struct Simulation {
0146 charged_selector_t selectCharged;
0147 neutral_selector_t selectNeutral;
0148 charged_simulator_t charged;
0149 neutral_simulator_t neutral;
0150
0151
0152 Simulation(charged_simulator_t &&charged_, neutral_simulator_t &&neutral_)
0153 : charged(std::move(charged_)), neutral(std::move(neutral_)) {}
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191 template <typename generator_t, typename input_particles_t,
0192 typename output_particles_t, typename hits_t>
0193 Acts::Result<std::vector<FailedParticle>> simulate(
0194 const Acts::GeometryContext &geoCtx,
0195 const Acts::MagneticFieldContext &magCtx, generator_t &generator,
0196 const input_particles_t &inputParticles,
0197 output_particles_t &simulatedParticlesInitial,
0198 output_particles_t &simulatedParticlesFinal, hits_t &hits) const {
0199 assert(
0200 (simulatedParticlesInitial.size() == simulatedParticlesFinal.size()) &&
0201 "Inconsistent initial sizes of the simulated particle containers");
0202
0203 using SingleParticleSimulationResult = Acts::Result<SimulationResult>;
0204
0205 std::vector<FailedParticle> failedParticles;
0206
0207 for (const Particle &inputParticle : inputParticles) {
0208
0209 if (!selectParticle(inputParticle)) {
0210 continue;
0211 }
0212
0213 if ((inputParticle.particleId().generation() != 0u) ||
0214 (inputParticle.particleId().subParticle() != 0u)) {
0215 return detail::SimulationError::eInvalidInputParticleId;
0216 }
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227 auto iinitial = simulatedParticlesInitial.size();
0228 simulatedParticlesInitial.push_back(inputParticle);
0229 for (; iinitial < simulatedParticlesInitial.size(); ++iinitial) {
0230 const auto &initialParticle = simulatedParticlesInitial[iinitial];
0231
0232
0233
0234 SingleParticleSimulationResult result =
0235 SingleParticleSimulationResult::success({});
0236 if (initialParticle.charge() != Particle::Scalar{0}) {
0237 result = charged.simulate(geoCtx, magCtx, generator, initialParticle);
0238 } else {
0239 result = neutral.simulate(geoCtx, magCtx, generator, initialParticle);
0240 }
0241
0242 if (!result.ok()) {
0243
0244 simulatedParticlesInitial.erase(
0245 std::next(simulatedParticlesInitial.begin(), iinitial));
0246
0247 failedParticles.push_back({initialParticle, result.error()});
0248 continue;
0249 }
0250
0251 copyOutputs(result.value(), simulatedParticlesInitial,
0252 simulatedParticlesFinal, hits);
0253
0254
0255
0256
0257
0258 renumberTailParticleIds(simulatedParticlesInitial, iinitial);
0259 }
0260 }
0261
0262
0263
0264
0265
0266 return failedParticles;
0267 }
0268
0269 private:
0270
0271 bool selectParticle(const Particle &particle) const {
0272 if (particle.charge() != Particle::Scalar{0}) {
0273 return selectCharged(particle);
0274 } else {
0275 return selectNeutral(particle);
0276 }
0277 }
0278
0279
0280
0281
0282
0283 template <typename particles_t, typename hits_t>
0284 void copyOutputs(const SimulationResult &result,
0285 particles_t &particlesInitial, particles_t &particlesFinal,
0286 hits_t &hits) const {
0287
0288
0289 particlesFinal.push_back(result.particle);
0290
0291 std::copy_if(
0292 result.generatedParticles.begin(), result.generatedParticles.end(),
0293 std::back_inserter(particlesInitial),
0294 [this](const Particle &particle) { return selectParticle(particle); });
0295 std::copy(result.hits.begin(), result.hits.end(), std::back_inserter(hits));
0296 }
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312 template <typename particles_t>
0313 static void renumberTailParticleIds(particles_t &particles,
0314 std::size_t lastValid) {
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334 for (auto j = lastValid; (j + 1u) < particles.size(); ++j) {
0335 const auto prevId = particles[j].particleId();
0336 auto currId = particles[j + 1u].particleId();
0337
0338
0339 if (prevId.generation() != currId.generation()) {
0340 continue;
0341 }
0342
0343 if (prevId.subParticle() < currId.subParticle()) {
0344 continue;
0345 }
0346
0347 currId.setSubParticle(prevId.subParticle() + 1u);
0348 particles[j + 1u] = particles[j + 1u].withParticleId(currId);
0349 }
0350 }
0351 };
0352
0353 }