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