File indexing completed on 2026-09-15 08:20:05
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Geant4/ParticleTrackingAction.hpp"
0010
0011 #include "Acts/Definitions/PdgParticle.hpp"
0012 #include "ActsExamples/EventData/SimParticle.hpp"
0013 #include "ActsExamples/Geant4/EventStore.hpp"
0014 #include "ActsExamples/Geant4/UnitConversion.hpp"
0015 #include "ActsFatras/EventData/Barcode.hpp"
0016 #include "ActsFatras/EventData/GenerationProcess.hpp"
0017 #include "ActsFatras/EventData/SimulationOutcome.hpp"
0018
0019 #include <cassert>
0020 #include <ostream>
0021 #include <unordered_map>
0022 #include <utility>
0023
0024 #include <G4EmProcessSubType.hh>
0025 #include <G4ParticleDefinition.hh>
0026 #include <G4ProcessType.hh>
0027 #include <G4RunManager.hh>
0028 #include <G4Track.hh>
0029 #include <G4UnitsTable.hh>
0030 #include <G4VProcess.hh>
0031
0032 namespace ActsExamples::Geant4 {
0033
0034 namespace {
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 ActsFatras::GenerationProcess g4CreatorToGenerationProcess(
0052 const G4VProcess* proc) {
0053 using ActsFatras::GenerationProcess;
0054 if (proc == nullptr) {
0055 return GenerationProcess::eUndefined;
0056 }
0057 const G4ProcessType type = proc->GetProcessType();
0058 if (type == fDecay) {
0059 return GenerationProcess::eDecay;
0060 }
0061 if (type == fHadronic || type == fPhotolepton_hadron) {
0062 return GenerationProcess::eNuclearInteraction;
0063 }
0064 switch (proc->GetProcessSubType()) {
0065 case fBremsstrahlung:
0066 return GenerationProcess::eBremsstrahlung;
0067 case fGammaConversion:
0068 return GenerationProcess::ePhotonConversion;
0069 case fIonisation:
0070 return GenerationProcess::eIonisation;
0071 default:
0072 return GenerationProcess::eOther;
0073
0074 }
0075 }
0076
0077 }
0078
0079 ParticleTrackingAction::ParticleTrackingAction(
0080 const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
0081 : G4UserTrackingAction(), m_cfg(cfg), m_logger(std::move(logger)) {}
0082
0083 void ParticleTrackingAction::PreUserTrackingAction(const G4Track* trackPtr) {
0084 assert(trackPtr != nullptr);
0085 const G4Track& track = *trackPtr;
0086
0087
0088
0089
0090
0091
0092
0093 if (!eventStore().hitBuffer.empty()) {
0094 eventStore().hitBuffer.clear();
0095 ACTS_WARNING("Hit buffer not empty after track");
0096 }
0097
0098 const std::optional<SimBarcode> barcode =
0099 makeParticleId(track.GetTrackID(), track.GetParentID());
0100
0101
0102
0103 if (!barcode.has_value()) {
0104 return;
0105 }
0106
0107 const SimParticleState fatrasParticle = convert(track, *barcode);
0108 SimParticle particle(fatrasParticle, fatrasParticle);
0109
0110
0111
0112
0113 if (const auto pit = eventStore().trackIdMapping.find(track.GetParentID());
0114 pit != eventStore().trackIdMapping.end()) {
0115 particle.setParentParticleId(pit->second);
0116 }
0117 const auto [it, success] = eventStore().particlesInitial.insert(particle);
0118
0119
0120
0121 if (success) {
0122 eventStore().trackIdMapping[track.GetTrackID()] = particle.particleId();
0123 } else {
0124 eventStore().particleIdCollisionsInitial++;
0125 ACTS_WARNING("Particle ID collision with "
0126 << particle.particleId()
0127 << " detected for initial particles. Skip particle");
0128 }
0129 }
0130
0131 void ParticleTrackingAction::PostUserTrackingAction(const G4Track* trackPtr) {
0132 assert(trackPtr != nullptr);
0133 const G4Track& track = *trackPtr;
0134
0135
0136
0137 if (!eventStore().trackIdMapping.contains(track.GetTrackID())) {
0138 ACTS_WARNING("Particle ID for track ID " << track.GetTrackID()
0139 << " not registered. Skip");
0140 return;
0141 }
0142
0143 const SimBarcode barcode = eventStore().trackIdMapping.at(track.GetTrackID());
0144
0145 const bool hasHits = eventStore().particleHitCount.contains(barcode) &&
0146 eventStore().particleHitCount.at(barcode) > 0;
0147 if (!m_cfg.keepParticlesWithoutHits && !hasHits) {
0148 [[maybe_unused]] const std::size_t n =
0149 eventStore().particlesSimulated.erase(
0150 SimParticle(barcode, Acts::PdgParticle::eInvalid));
0151 assert(n == 1);
0152 return;
0153 }
0154
0155 const auto particleIt = eventStore().particlesInitial.find(barcode);
0156 if (particleIt == eventStore().particlesInitial.end()) {
0157 ACTS_WARNING("Particle ID " << barcode
0158 << " not found in initial particles");
0159 return;
0160 }
0161 SimParticle particle = *particleIt;
0162 particle.finalState() = convert(track, barcode);
0163
0164 particle.setParentParticleId(particle.parentParticleId());
0165
0166 const auto [it, success] = eventStore().particlesSimulated.insert(particle);
0167
0168 if (!success) {
0169 eventStore().particleIdCollisionsFinal++;
0170 ACTS_WARNING("Particle ID collision with "
0171 << particle.particleId()
0172 << " detected for final particles. Skip particle");
0173 }
0174 }
0175
0176 SimParticleState ParticleTrackingAction::convert(const G4Track& track,
0177 SimBarcode particleId) const {
0178
0179 const G4ParticleDefinition* particleDef = track.GetParticleDefinition();
0180 const G4int pdg = particleDef->GetPDGEncoding();
0181 const G4double charge = particleDef->GetPDGCharge();
0182 const G4double mass = convertEnergyToActs * particleDef->GetPDGMass();
0183 const G4ThreeVector pPosition = convertLengthToActs * track.GetPosition();
0184 const G4double pTime = convertTimeToActs * track.GetGlobalTime();
0185 const G4ThreeVector pDirection = track.GetMomentumDirection();
0186 const G4double p = convertEnergyToActs * track.GetMomentum().mag();
0187
0188 std::uint32_t numberOfHits = 0;
0189 if (const auto it = eventStore().particleHitCount.find(particleId);
0190 it != eventStore().particleHitCount.end()) {
0191 numberOfHits = it->second;
0192 }
0193
0194 ActsFatras::SimulationOutcome particleOutcome =
0195 ActsFatras::SimulationOutcome::Alive;
0196 if (const auto it = eventStore().particleOutcome.find(particleId);
0197 it != eventStore().particleOutcome.end()) {
0198 particleOutcome = it->second;
0199 }
0200
0201
0202 SimParticleState aParticle(particleId, Acts::PdgParticle{pdg}, charge, mass);
0203 aParticle.setPosition4(pPosition[0], pPosition[1], pPosition[2], pTime);
0204 aParticle.setDirection(pDirection[0], pDirection[1], pDirection[2]);
0205 aParticle.setAbsoluteMomentum(p);
0206 aParticle.setNumberOfHits(numberOfHits);
0207 aParticle.setOutcome(particleOutcome);
0208
0209
0210 aParticle.setProcess(g4CreatorToGenerationProcess(track.GetCreatorProcess()));
0211 return aParticle;
0212 }
0213
0214 std::optional<SimBarcode> ParticleTrackingAction::makeParticleId(
0215 G4int trackId, G4int parentId) const {
0216
0217
0218 if (eventStore().trackIdMapping.contains(trackId)) {
0219 return std::nullopt;
0220 }
0221
0222 if (!eventStore().trackIdMapping.contains(parentId)) {
0223 ACTS_DEBUG("Parent particle " << parentId
0224 << " not registered, cannot build barcode");
0225 eventStore().parentIdNotFound++;
0226 return std::nullopt;
0227 }
0228
0229 SimBarcode pid = eventStore().trackIdMapping.at(parentId).makeDescendant();
0230 const SimBarcode key = pid.withoutSubparticle();
0231 ++eventStore().subparticleMap[key];
0232 pid = pid.withSubParticle(eventStore().subparticleMap[key]);
0233
0234 return pid;
0235 }
0236
0237 }