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/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 = Acts::PropagatorOptions<Actions, Abort>;
0087
0088
0089 PropagatorOptions options(geoCtx, magCtx);
0090 options.maxStepSize = maxStepSize;
0091 options.pathLimit = pathLimit;
0092
0093 auto &actor = options.actionList.template get<Actor>();
0094 actor.generator = &generator;
0095 actor.decay = decay;
0096 actor.interactions = interactions;
0097 actor.selectHitSurface = selectHitSurface;
0098 actor.initialParticle = particle;
0099
0100 if (particle.hasReferenceSurface()) {
0101 auto result = propagator.propagate(
0102 particle.boundParameters(geoCtx).value(), options);
0103 if (!result.ok()) {
0104 return result.error();
0105 }
0106 auto &value = result.value().template get<Result>();
0107 return std::move(value);
0108 }
0109
0110 auto result =
0111 propagator.propagate(particle.curvilinearParameters(), options);
0112 if (!result.ok()) {
0113 return result.error();
0114 }
0115 auto &value = result.value().template get<Result>();
0116 return std::move(value);
0117 }
0118 };
0119
0120
0121 struct FailedParticle {
0122
0123
0124
0125
0126
0127 Particle particle;
0128
0129 std::error_code error;
0130 };
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142 template <typename charged_selector_t, typename charged_simulator_t,
0143 typename neutral_selector_t, typename neutral_simulator_t>
0144 struct Simulation {
0145 charged_selector_t selectCharged;
0146 neutral_selector_t selectNeutral;
0147 charged_simulator_t charged;
0148 neutral_simulator_t neutral;
0149
0150
0151 Simulation(charged_simulator_t &&charged_, neutral_simulator_t &&neutral_)
0152 : charged(std::move(charged_)), neutral(std::move(neutral_)) {}
0153
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 template <typename generator_t, typename input_particles_t,
0191 typename output_particles_t, typename hits_t>
0192 Acts::Result<std::vector<FailedParticle>> simulate(
0193 const Acts::GeometryContext &geoCtx,
0194 const Acts::MagneticFieldContext &magCtx, generator_t &generator,
0195 const input_particles_t &inputParticles,
0196 output_particles_t &simulatedParticlesInitial,
0197 output_particles_t &simulatedParticlesFinal, hits_t &hits) const {
0198 assert(
0199 (simulatedParticlesInitial.size() == simulatedParticlesFinal.size()) &&
0200 "Inconsistent initial sizes of the simulated particle containers");
0201
0202 using SingleParticleSimulationResult = Acts::Result<SimulationResult>;
0203
0204 std::vector<FailedParticle> failedParticles;
0205
0206 for (const Particle &inputParticle : inputParticles) {
0207
0208 if (!selectParticle(inputParticle)) {
0209 continue;
0210 }
0211
0212 if ((inputParticle.particleId().generation() != 0u) ||
0213 (inputParticle.particleId().subParticle() != 0u)) {
0214 return detail::SimulationError::eInvalidInputParticleId;
0215 }
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 auto iinitial = simulatedParticlesInitial.size();
0227 simulatedParticlesInitial.push_back(inputParticle);
0228 for (; iinitial < simulatedParticlesInitial.size(); ++iinitial) {
0229 const auto &initialParticle = simulatedParticlesInitial[iinitial];
0230
0231
0232
0233 SingleParticleSimulationResult result =
0234 SingleParticleSimulationResult::success({});
0235 if (initialParticle.charge() != Particle::Scalar{0}) {
0236 result = charged.simulate(geoCtx, magCtx, generator, initialParticle);
0237 } else {
0238 result = neutral.simulate(geoCtx, magCtx, generator, initialParticle);
0239 }
0240
0241 if (!result.ok()) {
0242
0243 simulatedParticlesInitial.erase(
0244 std::next(simulatedParticlesInitial.begin(), iinitial));
0245
0246 failedParticles.push_back({initialParticle, result.error()});
0247 continue;
0248 }
0249
0250 copyOutputs(result.value(), simulatedParticlesInitial,
0251 simulatedParticlesFinal, hits);
0252
0253
0254
0255
0256
0257 renumberTailParticleIds(simulatedParticlesInitial, iinitial);
0258 }
0259 }
0260
0261
0262
0263
0264
0265 return failedParticles;
0266 }
0267
0268 private:
0269
0270 bool selectParticle(const Particle &particle) const {
0271 if (particle.charge() != Particle::Scalar{0}) {
0272 return selectCharged(particle);
0273 } else {
0274 return selectNeutral(particle);
0275 }
0276 }
0277
0278
0279
0280
0281
0282 template <typename particles_t, typename hits_t>
0283 void copyOutputs(const SimulationResult &result,
0284 particles_t &particlesInitial, particles_t &particlesFinal,
0285 hits_t &hits) const {
0286
0287
0288 particlesFinal.push_back(result.particle);
0289
0290 std::copy_if(
0291 result.generatedParticles.begin(), result.generatedParticles.end(),
0292 std::back_inserter(particlesInitial),
0293 [this](const Particle &particle) { return selectParticle(particle); });
0294 std::copy(result.hits.begin(), result.hits.end(), std::back_inserter(hits));
0295 }
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311 template <typename particles_t>
0312 static void renumberTailParticleIds(particles_t &particles,
0313 std::size_t lastValid) {
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333 for (auto j = lastValid; (j + 1u) < particles.size(); ++j) {
0334 const auto prevId = particles[j].particleId();
0335 auto currId = particles[j + 1u].particleId();
0336
0337
0338 if (prevId.generation() != currId.generation()) {
0339 continue;
0340 }
0341
0342 if (prevId.subParticle() < currId.subParticle()) {
0343 continue;
0344 }
0345
0346 currId.setSubParticle(prevId.subParticle() + 1u);
0347 particles[j + 1u] = particles[j + 1u].withParticleId(currId);
0348 }
0349 }
0350 };
0351
0352 }