File indexing completed on 2026-07-21 07:50:40
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Geant4/Geant4Simulation.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/Utilities/Logger.hpp"
0014 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0015 #include "ActsExamples/Framework/IAlgorithm.hpp"
0016 #include "ActsExamples/Framework/RandomNumbers.hpp"
0017 #include "ActsExamples/Framework/WhiteBoard.hpp"
0018 #include "ActsExamples/Geant4/EventStore.hpp"
0019 #include "ActsExamples/Geant4/Geant4Manager.hpp"
0020 #include "ActsExamples/Geant4/MagneticFieldWrapper.hpp"
0021 #include "ActsExamples/Geant4/MaterialPhysicsList.hpp"
0022 #include "ActsExamples/Geant4/MaterialSteppingAction.hpp"
0023 #include "ActsExamples/Geant4/ParticleKillAction.hpp"
0024 #include "ActsExamples/Geant4/ParticleTrackingAction.hpp"
0025 #include "ActsExamples/Geant4/SensitiveSteppingAction.hpp"
0026 #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"
0027 #include "ActsExamples/Geant4/SimParticleTranslation.hpp"
0028 #include "ActsExamples/Geant4/SteppingActionList.hpp"
0029 #include "ActsPlugins/FpeMonitoring/FpeMonitor.hpp"
0030
0031 #include <stdexcept>
0032 #include <utility>
0033
0034 #include <G4FieldManager.hh>
0035 #include <G4PropagatorInField.hh>
0036 #include <G4RunManager.hh>
0037 #include <G4TransportationManager.hh>
0038 #include <G4UniformMagField.hh>
0039 #include <G4UserEventAction.hh>
0040 #include <G4UserLimits.hh>
0041 #include <G4UserRunAction.hh>
0042 #include <G4UserSteppingAction.hh>
0043 #include <G4UserTrackingAction.hh>
0044 #include <G4VUserDetectorConstruction.hh>
0045 #include <G4VUserPhysicsList.hh>
0046 #include <G4Version.hh>
0047 #include <Randomize.hh>
0048
0049 namespace ActsExamples {
0050
0051 Geant4SimulationBase::Geant4SimulationBase(
0052 const Config& cfg, const std::string& name,
0053 std::unique_ptr<const Acts::Logger> logger)
0054 : IAlgorithm(name, std::move(logger)) {
0055 if (cfg.inputParticles.empty()) {
0056 throw std::invalid_argument("Missing input particle collection");
0057 }
0058 if (cfg.detector == nullptr) {
0059 throw std::invalid_argument("Missing detector construction factory");
0060 }
0061 if (cfg.randomNumbers == nullptr) {
0062 throw std::invalid_argument("Missing random numbers");
0063 }
0064
0065 m_eventStore = std::make_shared<Geant4::EventStore>();
0066
0067
0068
0069
0070 m_geant4Level = this->logger().level() == Acts::Logging::VERBOSE ? 2 : 0;
0071 }
0072
0073 Geant4SimulationBase::~Geant4SimulationBase() = default;
0074
0075 void Geant4SimulationBase::commonInitialization() {
0076
0077 {
0078
0079 if (runManager().GetUserDetectorConstruction() != nullptr) {
0080 delete runManager().GetUserDetectorConstruction();
0081 }
0082
0083 m_detectorConstruction =
0084 config()
0085 .detector
0086 ->buildGeant4DetectorConstruction(config().constructionOptions)
0087 .release();
0088 runManager().SetUserInitialization(m_detectorConstruction);
0089 runManager().InitializeGeometry();
0090 }
0091
0092 m_geant4Instance->tweakLogging(m_geant4Level);
0093 }
0094
0095 G4RunManager& Geant4SimulationBase::runManager() const {
0096 return *m_geant4Instance->runManager;
0097 }
0098
0099 Geant4::EventStore& Geant4SimulationBase::eventStore() const {
0100 return *m_eventStore;
0101 }
0102
0103 ProcessCode Geant4SimulationBase::initialize() {
0104
0105 runManager().Initialize();
0106
0107 return ProcessCode::SUCCESS;
0108 }
0109
0110 ProcessCode Geant4SimulationBase::execute(const AlgorithmContext& ctx) const {
0111
0112 std::lock_guard<std::mutex> guard(m_geant4Instance->mutex);
0113
0114
0115
0116
0117 if (std::isfinite(config().propagatorLargestAcceptableStep)) {
0118 G4PropagatorInField* propagator =
0119 G4TransportationManager::GetTransportationManager()
0120 ->GetPropagatorInField();
0121 propagator->SetLargestAcceptableStep(
0122 config().propagatorLargestAcceptableStep / Acts::UnitConstants::mm *
0123 CLHEP::mm);
0124 }
0125
0126
0127 G4Random::setTheSeed(config().randomNumbers->generateSeed(ctx));
0128
0129
0130 eventStore() = Geant4::EventStore{};
0131
0132
0133
0134 eventStore().store = &(ctx.eventStore);
0135
0136
0137 eventStore().inputParticles = &m_inputParticles;
0138
0139 eventStore().geoContext = ctx.geoContext;
0140
0141 ACTS_DEBUG("Sending Geant RunManager the BeamOn() command.");
0142 {
0143 ActsPlugins::FpeMonitor mon{0};
0144
0145 runManager().BeamOn(1);
0146 }
0147
0148
0149 if (eventStore().particleIdCollisionsInitial > 0 ||
0150 eventStore().particleIdCollisionsFinal > 0 ||
0151 eventStore().parentIdNotFound > 0) {
0152 ACTS_WARNING(
0153 "Particle ID collisions detected, don't trust the particle "
0154 "identification!");
0155 ACTS_WARNING(
0156 "- initial states: " << eventStore().particleIdCollisionsInitial);
0157 ACTS_WARNING("- final states: " << eventStore().particleIdCollisionsFinal);
0158 ACTS_WARNING("- parent ID not found: " << eventStore().parentIdNotFound);
0159 }
0160
0161 if (eventStore().hits.empty()) {
0162 ACTS_DEBUG("Step merging: No steps recorded");
0163 } else {
0164 ACTS_DEBUG("Step merging: mean hits per hit: "
0165 << static_cast<double>(eventStore().numberGeantSteps) /
0166 eventStore().hits.size());
0167 ACTS_DEBUG(
0168 "Step merging: max hits per hit: " << eventStore().maxStepsForHit);
0169 }
0170
0171 return ProcessCode::SUCCESS;
0172 }
0173
0174 std::shared_ptr<Geant4Handle> Geant4SimulationBase::geant4Handle() const {
0175 return m_geant4Instance;
0176 }
0177
0178 Geant4Simulation::Geant4Simulation(const Config& cfg,
0179 std::unique_ptr<const Acts::Logger> logger)
0180 : Geant4SimulationBase(cfg, "Geant4Simulation", std::move(logger)),
0181 m_cfg(cfg) {
0182 m_geant4Instance =
0183 m_cfg.geant4Handle
0184 ? m_cfg.geant4Handle
0185 : Geant4Manager::instance().createHandle(m_cfg.physicsList);
0186 if (m_geant4Instance->physicsListName != m_cfg.physicsList) {
0187 throw std::runtime_error("inconsistent physics list");
0188 }
0189
0190 commonInitialization();
0191
0192
0193 {
0194
0195 if (runManager().GetUserPrimaryGeneratorAction() != nullptr) {
0196 delete runManager().GetUserPrimaryGeneratorAction();
0197 }
0198 Geant4::SimParticleTranslation::Config prCfg;
0199 prCfg.eventStore = m_eventStore;
0200
0201 auto primaryGeneratorAction = new Geant4::SimParticleTranslation(
0202 prCfg, this->logger().cloneWithSuffix("SimParticleTranslation"));
0203
0204 runManager().SetUserAction(primaryGeneratorAction);
0205 }
0206
0207
0208 {
0209
0210 if (runManager().GetUserTrackingAction() != nullptr) {
0211 delete runManager().GetUserTrackingAction();
0212 }
0213 Geant4::ParticleTrackingAction::Config trackingCfg;
0214 trackingCfg.eventStore = m_eventStore;
0215 trackingCfg.keepParticlesWithoutHits = cfg.keepParticlesWithoutHits;
0216
0217 auto trackingAction = new Geant4::ParticleTrackingAction(
0218 trackingCfg, this->logger().cloneWithSuffix("ParticleTracking"));
0219 runManager().SetUserAction(trackingAction);
0220 }
0221
0222
0223 Geant4::SensitiveSteppingAction* sensitiveSteppingActionAccess = nullptr;
0224 {
0225
0226 if (runManager().GetUserSteppingAction() != nullptr) {
0227 delete runManager().GetUserSteppingAction();
0228 }
0229
0230 Geant4::ParticleKillAction::Config particleKillCfg;
0231 particleKillCfg.eventStore = m_eventStore;
0232 particleKillCfg.volume = cfg.killVolume;
0233 particleKillCfg.maxTime = cfg.killAfterTime;
0234 particleKillCfg.secondaries = cfg.killSecondaries;
0235
0236 Geant4::SensitiveSteppingAction::Config stepCfg;
0237 stepCfg.eventStore = m_eventStore;
0238 stepCfg.charged = cfg.recordHitsOfCharged;
0239 stepCfg.neutral = cfg.recordHitsOfNeutrals;
0240 stepCfg.primary = cfg.recordHitsOfPrimaries;
0241 stepCfg.secondary = cfg.recordHitsOfSecondaries;
0242 stepCfg.stepLogging = cfg.recordPropagationSummaries;
0243
0244 Geant4::SteppingActionList::Config steppingCfg;
0245 steppingCfg.actions.push_back(std::make_unique<Geant4::ParticleKillAction>(
0246 particleKillCfg, this->logger().cloneWithSuffix("Killer")));
0247
0248 auto sensitiveSteppingAction =
0249 std::make_unique<Geant4::SensitiveSteppingAction>(
0250 stepCfg, this->logger().cloneWithSuffix("SensitiveStepping"));
0251 sensitiveSteppingActionAccess = sensitiveSteppingAction.get();
0252
0253 steppingCfg.actions.push_back(std::move(sensitiveSteppingAction));
0254
0255
0256 auto steppingAction = new Geant4::SteppingActionList(steppingCfg);
0257 runManager().SetUserAction(steppingAction);
0258 }
0259
0260
0261 G4VPhysicalVolume* g4World = m_detectorConstruction->Construct();
0262
0263
0264
0265
0266
0267
0268 if (cfg.magneticField) {
0269 ACTS_LOG_WITH_LOGGER(this->logger(), Acts::Logging::INFO,
0270 "Setting ACTS configured field to Geant4.");
0271
0272 Geant4::MagneticFieldWrapper::Config g4FieldCfg;
0273 g4FieldCfg.magneticField = cfg.magneticField;
0274 m_magneticField =
0275 std::make_unique<Geant4::MagneticFieldWrapper>(g4FieldCfg);
0276
0277
0278 m_fieldManager = std::make_unique<G4FieldManager>();
0279 m_fieldManager->SetDetectorField(m_magneticField.get());
0280 m_fieldManager->CreateChordFinder(m_magneticField.get());
0281
0282
0283 g4World->GetLogicalVolume()->SetFieldManager(m_fieldManager.get(), true);
0284 }
0285
0286
0287 if (cfg.sensitiveSurfaceMapper != nullptr) {
0288 Geant4::SensitiveSurfaceMapper::State sState;
0289 ACTS_LOG_WITH_LOGGER(this->logger(), Acts::Logging::INFO,
0290 "Remapping selected volumes from Geant4 to "
0291 "Acts::Surface::GeometryID");
0292 cfg.sensitiveSurfaceMapper->remapSensitiveNames(
0293 sState, Acts::GeometryContext::dangerouslyDefaultConstruct(), g4World,
0294 Acts::Transform3::Identity());
0295
0296 auto allSurfacesMapped = cfg.sensitiveSurfaceMapper->checkMapping(
0297 sState, Acts::GeometryContext::dangerouslyDefaultConstruct(), false,
0298 false);
0299 if (!allSurfacesMapped) {
0300 ACTS_LOG_WITH_LOGGER(this->logger(), Acts::Logging::WARNING,
0301 "Not all sensitive surfaces have been mapped to "
0302 "Geant4 volumes!");
0303 }
0304
0305 sensitiveSteppingActionAccess->assignSurfaceMapping(
0306 sState.g4VolumeToSurfaces);
0307 }
0308
0309 m_inputParticles.initialize(cfg.inputParticles);
0310 m_outputSimHits.initialize(cfg.outputSimHits);
0311 m_outputParticles.initialize(cfg.outputParticles);
0312
0313 if (cfg.recordPropagationSummaries) {
0314 m_outputPropagationSummaries.initialize(cfg.outputPropagationSummaries);
0315 }
0316 }
0317
0318 Geant4Simulation::~Geant4Simulation() = default;
0319
0320 ProcessCode Geant4Simulation::execute(const AlgorithmContext& ctx) const {
0321 auto ret = Geant4SimulationBase::execute(ctx);
0322 if (ret != ProcessCode::SUCCESS) {
0323 return ret;
0324 }
0325
0326
0327 m_outputParticles(
0328 ctx, SimParticleContainer(eventStore().particlesSimulated.begin(),
0329 eventStore().particlesSimulated.end()));
0330
0331 m_outputSimHits(
0332 ctx, SimHitContainer(eventStore().hits.begin(), eventStore().hits.end()));
0333
0334
0335 if (m_cfg.recordPropagationSummaries) {
0336 PropagationSummaries summaries;
0337 summaries.reserve(eventStore().propagationRecords.size());
0338 for (auto& [trackId, summary] : eventStore().propagationRecords) {
0339 summaries.push_back(std::move(summary));
0340 }
0341 m_outputPropagationSummaries(ctx, std::move(summaries));
0342 }
0343
0344 return ProcessCode::SUCCESS;
0345 }
0346
0347 Geant4MaterialRecording::Geant4MaterialRecording(
0348 const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
0349 : Geant4SimulationBase(cfg, "Geant4Simulation", std::move(logger)),
0350 m_cfg(cfg) {
0351 auto physicsListName = "MaterialPhysicsList";
0352 m_geant4Instance =
0353 m_cfg.geant4Handle
0354 ? m_cfg.geant4Handle
0355 : Geant4Manager::instance().createHandle(
0356 std::make_unique<Geant4::MaterialPhysicsList>(
0357 this->logger().cloneWithSuffix("MaterialPhysicsList")),
0358 physicsListName);
0359 if (m_geant4Instance->physicsListName != physicsListName) {
0360 throw std::runtime_error("inconsistent physics list");
0361 }
0362
0363 commonInitialization();
0364
0365
0366 {
0367
0368 if (runManager().GetUserPrimaryGeneratorAction() != nullptr) {
0369 delete runManager().GetUserPrimaryGeneratorAction();
0370 }
0371
0372 Geant4::SimParticleTranslation::Config prCfg;
0373 prCfg.eventStore = m_eventStore;
0374 prCfg.forcedPdgCode = 0;
0375 prCfg.forcedCharge = 0.;
0376 prCfg.forcedMass = 0.;
0377
0378
0379 auto primaryGeneratorAction = new Geant4::SimParticleTranslation(
0380 prCfg, this->logger().cloneWithSuffix("SimParticleTranslation"));
0381
0382 runManager().SetUserAction(primaryGeneratorAction);
0383 }
0384
0385
0386 {
0387
0388 if (runManager().GetUserTrackingAction() != nullptr) {
0389 delete runManager().GetUserTrackingAction();
0390 }
0391 Geant4::ParticleTrackingAction::Config trackingCfg;
0392 trackingCfg.eventStore = m_eventStore;
0393 trackingCfg.keepParticlesWithoutHits = true;
0394
0395 auto trackingAction = new Geant4::ParticleTrackingAction(
0396 trackingCfg, this->logger().cloneWithSuffix("ParticleTracking"));
0397 runManager().SetUserAction(trackingAction);
0398 }
0399
0400
0401 {
0402
0403 if (runManager().GetUserSteppingAction() != nullptr) {
0404 delete runManager().GetUserSteppingAction();
0405 }
0406 Geant4::MaterialSteppingAction::Config steppingCfg;
0407 steppingCfg.eventStore = m_eventStore;
0408 steppingCfg.excludeMaterials = m_cfg.excludeMaterials;
0409 steppingCfg.recordElementFractions = m_cfg.recordElementFractions;
0410
0411 auto steppingAction = new Geant4::MaterialSteppingAction(
0412 steppingCfg, this->logger().cloneWithSuffix("MaterialSteppingAction"));
0413 runManager().SetUserAction(steppingAction);
0414 }
0415
0416 runManager().Initialize();
0417
0418 m_inputParticles.initialize(cfg.inputParticles);
0419 m_outputMaterialTracks.initialize(cfg.outputMaterialTracks);
0420 }
0421
0422 Geant4MaterialRecording::~Geant4MaterialRecording() = default;
0423
0424 ProcessCode Geant4MaterialRecording::execute(
0425 const AlgorithmContext& ctx) const {
0426 const auto ret = Geant4SimulationBase::execute(ctx);
0427 if (ret != ProcessCode::SUCCESS) {
0428 return ret;
0429 }
0430
0431
0432 m_outputMaterialTracks(
0433 ctx, decltype(eventStore().materialTracks)(eventStore().materialTracks));
0434
0435 return ProcessCode::SUCCESS;
0436 }
0437
0438 }