File indexing completed on 2025-12-16 09:23:28
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Fatras/FatrasSimulation.hpp"
0010
0011 #include "Acts/Geometry/GeometryContext.hpp"
0012 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0013 #include "Acts/Propagator/Navigator.hpp"
0014 #include "Acts/Propagator/Propagator.hpp"
0015 #include "Acts/Propagator/StraightLineStepper.hpp"
0016 #include "Acts/Propagator/SympyStepper.hpp"
0017 #include "Acts/Surfaces/Surface.hpp"
0018 #include "Acts/Utilities/Logger.hpp"
0019 #include "Acts/Utilities/Result.hpp"
0020 #include "ActsExamples/EventData/SimHit.hpp"
0021 #include "ActsExamples/EventData/SimParticle.hpp"
0022 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0023 #include "ActsExamples/Framework/IAlgorithm.hpp"
0024 #include "ActsExamples/Framework/RandomNumbers.hpp"
0025 #include "ActsFatras/EventData/Hit.hpp"
0026 #include "ActsFatras/EventData/Particle.hpp"
0027 #include "ActsFatras/Kernel/InteractionList.hpp"
0028 #include "ActsFatras/Kernel/Simulation.hpp"
0029 #include "ActsFatras/Physics/Decay/NoDecay.hpp"
0030 #include "ActsFatras/Physics/ElectroMagnetic/PhotonConversion.hpp"
0031 #include "ActsFatras/Physics/StandardInteractions.hpp"
0032 #include "ActsFatras/Selectors/SelectorHelpers.hpp"
0033 #include "ActsFatras/Selectors/SurfaceSelectors.hpp"
0034
0035 #include <algorithm>
0036 #include <ostream>
0037 #include <stdexcept>
0038 #include <system_error>
0039 #include <utility>
0040 #include <vector>
0041
0042 namespace {
0043
0044
0045 struct HitSurfaceSelector {
0046 bool sensitive = false;
0047 bool material = false;
0048 bool passive = false;
0049
0050
0051 bool operator()(const Acts::Surface &surface) const {
0052
0053 bool isSensitive = surface.associatedDetectorElement() != nullptr;
0054 bool isMaterial = surface.surfaceMaterial() != nullptr;
0055
0056 bool isPassive = !(isSensitive || isMaterial);
0057 return (isSensitive && sensitive) || (isMaterial && material) ||
0058 (isPassive && passive);
0059 }
0060 };
0061
0062 }
0063
0064
0065 struct ActsExamples::detail::FatrasSimulation {
0066 virtual ~FatrasSimulation() = default;
0067 virtual Acts::Result<std::vector<ActsFatras::FailedParticle>> simulate(
0068 const Acts::GeometryContext &, const Acts::MagneticFieldContext &,
0069 ActsExamples::RandomEngine &, const std::vector<ActsFatras::Particle> &,
0070 std::vector<ActsFatras::Particle> &, std::vector<ActsFatras::Particle> &,
0071 std::vector<ActsFatras::Hit> &) const = 0;
0072 };
0073
0074 namespace {
0075
0076
0077
0078
0079
0080 struct FatrasSimulationT final : ActsExamples::detail::FatrasSimulation {
0081 using CutPMin = ActsFatras::Min<ActsFatras::Casts::P>;
0082
0083
0084
0085 using ChargedStepper = Acts::SympyStepper;
0086 using ChargedPropagator = Acts::Propagator<ChargedStepper, Acts::Navigator>;
0087
0088 using ChargedSelector = CutPMin;
0089 using ChargedSimulation = ActsFatras::SingleParticleSimulation<
0090 ChargedPropagator, ActsFatras::StandardChargedElectroMagneticInteractions,
0091 HitSurfaceSelector, ActsFatras::NoDecay>;
0092
0093
0094
0095 using NeutralStepper = Acts::StraightLineStepper;
0096 using NeutralPropagator = Acts::Propagator<NeutralStepper, Acts::Navigator>;
0097
0098 using NeutralSelector = CutPMin;
0099 using NeutralInteractions =
0100 ActsFatras::InteractionList<ActsFatras::PhotonConversion>;
0101 using NeutralSimulation = ActsFatras::SingleParticleSimulation<
0102 NeutralPropagator, NeutralInteractions, ActsFatras::NoSurface,
0103 ActsFatras::NoDecay>;
0104
0105
0106 using Simulation = ActsFatras::Simulation<ChargedSelector, ChargedSimulation,
0107 NeutralSelector, NeutralSimulation>;
0108
0109 Simulation simulation;
0110
0111 FatrasSimulationT(const ActsExamples::FatrasSimulation::Config &cfg,
0112 Acts::Logging::Level lvl)
0113 : simulation(
0114 ChargedSimulation(
0115 ChargedPropagator(
0116 ChargedStepper(cfg.magneticField),
0117 Acts::Navigator({cfg.trackingGeometry},
0118 Acts::getDefaultLogger("SimNav", lvl)),
0119 Acts::getDefaultLogger("SimProp", lvl)),
0120 Acts::getDefaultLogger("Simulation", lvl)),
0121 NeutralSimulation(
0122 NeutralPropagator(
0123 NeutralStepper(),
0124 Acts::Navigator({cfg.trackingGeometry},
0125 Acts::getDefaultLogger("SimNav", lvl)),
0126 Acts::getDefaultLogger("SimProp", lvl)),
0127 Acts::getDefaultLogger("Simulation", lvl))) {
0128 using namespace ActsFatras;
0129 using namespace ActsFatras::detail;
0130
0131
0132
0133 simulation.selectCharged.valMin = cfg.pMin;
0134 simulation.selectNeutral.valMin = cfg.pMin;
0135 simulation.charged.interactions =
0136 makeStandardChargedElectroMagneticInteractions(cfg.pMin);
0137
0138
0139 if (!cfg.emScattering) {
0140 simulation.charged.interactions.template disable<StandardScattering>();
0141 }
0142 if (!cfg.emEnergyLossIonisation) {
0143 simulation.charged.interactions.template disable<StandardBetheBloch>();
0144 }
0145 if (!cfg.emEnergyLossRadiation) {
0146 simulation.charged.interactions.template disable<StandardBetheHeitler>();
0147 }
0148 if (!cfg.emPhotonConversion) {
0149 simulation.neutral.interactions.template disable<PhotonConversion>();
0150 }
0151
0152
0153 simulation.charged.selectHitSurface.sensitive = cfg.generateHitsOnSensitive;
0154 simulation.charged.selectHitSurface.material = cfg.generateHitsOnMaterial;
0155 simulation.charged.selectHitSurface.passive = cfg.generateHitsOnPassive;
0156
0157 simulation.charged.maxStepSize = cfg.maxStepSize;
0158 simulation.charged.pathLimit = cfg.pathLimit;
0159 simulation.neutral.maxStepSize = cfg.maxStepSize;
0160 simulation.neutral.pathLimit = cfg.pathLimit;
0161 }
0162 ~FatrasSimulationT() final = default;
0163
0164 Acts::Result<std::vector<ActsFatras::FailedParticle>> simulate(
0165 const Acts::GeometryContext &geoCtx,
0166 const Acts::MagneticFieldContext &magCtx, ActsExamples::RandomEngine &rng,
0167 const std::vector<ActsFatras::Particle> &inputParticles,
0168 std::vector<ActsFatras::Particle> &simulatedParticlesInitial,
0169 std::vector<ActsFatras::Particle> &simulatedParticlesFinal,
0170 std::vector<ActsFatras::Hit> &simHits) const final {
0171 return simulation.simulate(geoCtx, magCtx, rng, inputParticles,
0172 simulatedParticlesInitial,
0173 simulatedParticlesFinal, simHits);
0174 }
0175 };
0176
0177 }
0178
0179 ActsExamples::FatrasSimulation::FatrasSimulation(Config cfg,
0180 Acts::Logging::Level lvl)
0181 : ActsExamples::IAlgorithm("FatrasSimulation", lvl), m_cfg(std::move(cfg)) {
0182 ACTS_DEBUG("hits on sensitive surfaces: " << m_cfg.generateHitsOnSensitive);
0183 ACTS_DEBUG("hits on material surfaces: " << m_cfg.generateHitsOnMaterial);
0184 ACTS_DEBUG("hits on passive surfaces: " << m_cfg.generateHitsOnPassive);
0185
0186 if (!m_cfg.generateHitsOnSensitive && !m_cfg.generateHitsOnMaterial &&
0187 !m_cfg.generateHitsOnPassive) {
0188 ACTS_WARNING("FatrasSimulation not configured to generate any hits!");
0189 }
0190
0191 if (!m_cfg.trackingGeometry) {
0192 throw std::invalid_argument{"Missing tracking geometry"};
0193 }
0194 if (!m_cfg.magneticField) {
0195 throw std::invalid_argument{"Missing magnetic field"};
0196 }
0197 if (!m_cfg.randomNumbers) {
0198 throw std::invalid_argument("Missing random numbers tool");
0199 }
0200
0201
0202 m_sim = std::make_unique<FatrasSimulationT>(m_cfg, lvl);
0203
0204 m_inputParticles.initialize(m_cfg.inputParticles);
0205 m_outputParticles.initialize(m_cfg.outputParticles);
0206 m_outputSimHits.initialize(m_cfg.outputSimHits);
0207 }
0208
0209
0210 ActsExamples::FatrasSimulation::~FatrasSimulation() = default;
0211
0212 ActsExamples::ProcessCode ActsExamples::FatrasSimulation::execute(
0213 const AlgorithmContext &ctx) const {
0214
0215 const auto &inputParticles = m_inputParticles(ctx);
0216
0217 ACTS_DEBUG(inputParticles.size() << " input particles");
0218
0219
0220 std::vector<ActsFatras::Particle> particlesInput;
0221 particlesInput.reserve(inputParticles.size());
0222 for (const auto &p : inputParticles) {
0223 particlesInput.push_back(p.initialState());
0224 }
0225
0226
0227 std::vector<ActsFatras::Particle> particlesInitialUnordered;
0228 std::vector<ActsFatras::Particle> particlesFinalUnordered;
0229 std::vector<ActsFatras::Hit> simHitsUnordered;
0230
0231 particlesInitialUnordered.reserve(inputParticles.size());
0232 particlesFinalUnordered.reserve(inputParticles.size());
0233 simHitsUnordered.reserve(inputParticles.size() *
0234 m_cfg.averageHitsPerParticle);
0235
0236
0237 auto rng = m_cfg.randomNumbers->spawnGenerator(ctx);
0238 auto ret = m_sim->simulate(ctx.geoContext, ctx.magFieldContext, rng,
0239 particlesInput, particlesInitialUnordered,
0240 particlesFinalUnordered, simHitsUnordered);
0241
0242 if (!ret.ok()) {
0243 ACTS_FATAL("event " << ctx.eventNumber << " simulation failed with error "
0244 << ret.error().message());
0245 return ProcessCode::ABORT;
0246 }
0247
0248
0249
0250 for (const auto &failed : ret.value()) {
0251 ACTS_ERROR("event " << ctx.eventNumber << " particle " << failed.particle
0252 << " failed to simulate with error " << failed.error
0253 << ": " << failed.error.message());
0254 }
0255
0256 ACTS_DEBUG(particlesInitialUnordered.size()
0257 << " simulated particles (initial state)");
0258 ACTS_DEBUG(particlesFinalUnordered.size()
0259 << " simulated particles (final state)");
0260 ACTS_DEBUG(simHitsUnordered.size() << " simulated hits");
0261
0262 if (particlesInitialUnordered.size() != particlesFinalUnordered.size()) {
0263 ACTS_ERROR("number of initial and final state particles differ");
0264 }
0265
0266
0267 SimParticleStateContainer particlesInitial(particlesInitialUnordered.begin(),
0268 particlesInitialUnordered.end());
0269 SimParticleStateContainer particlesFinal(particlesFinalUnordered.begin(),
0270 particlesFinalUnordered.end());
0271 SimHitContainer simHits(simHitsUnordered.begin(), simHitsUnordered.end());
0272
0273 SimParticleContainer particlesSimulated;
0274 particlesSimulated.reserve(particlesInitial.size());
0275 for (const auto &particleInitial : particlesInitial) {
0276 SimParticle particleSimulated(particleInitial, particleInitial);
0277
0278 if (auto it = particlesFinal.find(particleInitial.particleId());
0279 it != particlesFinal.end()) {
0280 particleSimulated.finalState() = *it;
0281 } else {
0282 ACTS_ERROR("particle " << particleInitial.particleId()
0283 << " has no final state");
0284 }
0285
0286 particlesSimulated.insert(particleSimulated);
0287 }
0288
0289
0290 m_outputParticles(ctx, std::move(particlesSimulated));
0291 m_outputSimHits(ctx, std::move(simHits));
0292
0293 return ActsExamples::ProcessCode::SUCCESS;
0294 }