Belle II Software development
TrackingAction.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <simulation/kernel/TrackingAction.h>
10#include <simulation/kernel/UserInfo.h>
11#include <simulation/kernel/SensitiveDetectorBase.h>
12#include <simulation/dataobjects/MCParticleTrajectory.h>
13#include <framework/logging/Logger.h>
14#include <framework/gearbox/Unit.h>
15
16#include <G4TrackingManager.hh>
17#include <G4Track.hh>
18#include <G4ParticleDefinition.hh>
19#include <G4ParticleTypes.hh>
20#include <G4EmProcessSubType.hh>
21#include <G4DecayProcessType.hh>
22#include <G4Event.hh>
23#include <G4ParticleTable.hh>
24
25using namespace Belle2;
26using namespace Belle2::Simulation;
27
41
45
46void TrackingAction::setStoreTrajectories(int store, double distanceTolerance)
47{
48 m_storeTrajectories = store;
49 m_distanceTolerance = distanceTolerance;
50 if (store) {
51 // registration of store arrays and relations
52 m_storeMCTrajectories.registerInDataStore();
53 StoreArray<MCParticle> mcParticles;
55
56 // additional registration of MCParticle relation
57 // (note: m_relMCTrajectories is already defined by TrackingAction::TrackingAction)
59 }
60}
61
62void TrackingAction::PreUserTrackingAction(const G4Track* track)
63{
64 // We only want to do the following for new tracks, not for suspended and reactivated ones"
65 if (track->GetCurrentStepNumber() > 0)
66 return;
67
68 const G4DynamicParticle* dynamicParticle = track->GetDynamicParticle();
69
70 try {
71 // Check if the dynamic particle has a primary particle attached.
72 // If the answer is yes, the UserInfo of the primary particle is recycled as the UserInfo of the track.
73 const bool isPrimary = dynamicParticle->GetPrimaryParticle() != nullptr;
74 bool neutral_llp = false;
75 if (isPrimary) {
76 const G4PrimaryParticle* primaryParticle = dynamicParticle->GetPrimaryParticle();
77 if (primaryParticle->GetUserInformation() != nullptr) {
78 const_cast<G4Track*>(track)->SetUserInformation(new TrackInfo(ParticleInfo::getInfo(*primaryParticle)));
79 // check for neutral long-lived primary particle
80 if (primaryParticle->GetParticleDefinition() == G4ParticleTable::GetParticleTable()->FindParticle("LongLivedNeutralParticle")) {
81 neutral_llp = true;
82 }
83 } else {
84 B2WARNING(track->GetDefinition()->GetPDGEncoding() << " has no MCParticle user information !");
85 }
86 }
87
88 // Get particle of current track
90
91 G4ThreeVector dpMom = dynamicParticle->GetMomentum() / CLHEP::MeV * Unit::MeV;
92 currParticle.setTrackID(track->GetTrackID());
93 // If the particle is a primary particle with simulated parents update the
94 // production vertex and time to the real values from Geant4. As convention
95 // we set NaN as production time in the MCParticleGenerator if the particle
96 // wasn't directly placed with a primary vertex in Geant4.
97 if (!isPrimary or std::isnan(currParticle.getProductionTime())) {
98 G4ThreeVector trVtxPos = track->GetVertexPosition() / CLHEP::mm * Unit::mm;
99 currParticle.setProductionTime(track->GetGlobalTime()); // Time does not need a conversion factor
100 currParticle.setProductionVertex(trVtxPos.x(), trVtxPos.y(), trVtxPos.z());
101 }
102
103 // Set the Values of the particle which are already known
104 if (!neutral_llp) {
105 currParticle.setPDG(dynamicParticle->GetPDGcode());
106 currParticle.setMass(dynamicParticle->GetMass() / CLHEP::MeV * Unit::MeV);
107 currParticle.setEnergy(dynamicParticle->GetTotalEnergy() / CLHEP::MeV * Unit::MeV);
108 currParticle.setMomentum(dpMom.x(), dpMom.y(), dpMom.z());
109 }
110
111 // Primary or secondary particle?
112 if (isPrimary) {
113 // Primary particle
114 currParticle.setSecondaryPhysicsProcess(0);
115 currParticle.setIgnore(false); // Store the generator info in the MCParticles block.
116 } else if (track->GetCreatorProcess() != nullptr) {
117 // Secondary particle
118 const int& processSubType = track->GetCreatorProcess()->GetProcessSubType();
119 currParticle.setSecondaryPhysicsProcess(processSubType); // Store the physics process type.
120
121 // Decay-in-flight
122 if (processSubType >= static_cast<int>(DECAY) && processSubType <= static_cast<int>(DECAY_External))
123 currParticle.setIgnore(false); // Store the generator info in the MCParticles block.
124 } else {
125 // Unknown origin. This could be a bug originated from Geant4.
126 currParticle.setSecondaryPhysicsProcess(-1);
127 }
128
129 // Either we store all the trajectories
130 if (m_storeTrajectories > 2 ||
131 // Or only the primary ones
132 (m_storeTrajectories == 1 && isPrimary) ||
133 // Or all except optical photons
134 (m_storeTrajectories == 2 && track->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition())) {
135 TrackInfo& info = dynamic_cast<TrackInfo&>(*track->GetUserInformation());
136 m_relMCTrajectories.add(track->GetTrackID(), m_storeMCTrajectories.getEntries());
137 info.setTrajectory(m_storeMCTrajectories.appendNew());
138 }
139 } catch (CouldNotFindUserInfo& exc) {
140 B2FATAL(exc.what());
141 }
142}
143
145{
146 G4StepPoint* postStep = track->GetStep()->GetPostStepPoint();
147
148 // Get particle of the current track
149 try {
151
153
154 // A custom check to flip the ignore flag for seco primaries
155 const bool producedInBarrel = (currParticle.getProductionVertex().Rho() > m_regionRho);
156 const bool producedInEndCap = (currParticle.getProductionVertex().Z() > m_regionZForward)
157 || (currParticle.getProductionVertex().Z() < m_regionZBackward);
158 const bool producedInRegion = producedInBarrel || producedInEndCap;
159 const bool isAboveKinematicThreshold = std::sqrt(currParticle.getMomentum().mag2()) > m_kineticEnergyThreshold;
160 G4ThreeVector decVtx = postStep->GetPosition() / CLHEP::mm * Unit::mm;
161 currParticle.setDecayVertex(decVtx.x(), decVtx.y(), decVtx.z());
162 const float distance = std::sqrt((currParticle.getProductionVertex() - currParticle.getDecayVertex()).mag2());
163 const bool hasTraveledDistance = distance >
165 const bool isEM = ((currParticle.getPDG() == 11) || (currParticle.getPDG() == -11) || (currParticle.getPDG() == 22));
166 const bool isNuclei = (currParticle.getPDG() > 100'000'000'0);
167 const bool seenInECL = currParticle.hasSeenInDetector(Const::ECL);
168 B2DEBUG(10, "Particle: " << currParticle.getIndex() << "Particle pdg: " << currParticle.getPDG() << "current ignore flag: " <<
169 currParticle.getIgnore() << " produced in Region: " <<
170 producedInRegion << " above threshold: " <<
171 isAboveKinematicThreshold
172 << " traveled distance: " << hasTraveledDistance << " is EM: " << isEM << " is Nuclei: " << isNuclei << " seen in ECL: " <<
173 seenInECL);
174 B2DEBUG(10, "Extra Info: " << "Region Z Backward: " << m_regionZBackward << " Region Z Forward: " << m_regionZForward <<
175 " Region Rho: " <<
177 << " Kinetic Energy Threshold: " << m_kineticEnergyThreshold
178 << "Distance: " << distance << " Distance Threshold: " << m_distanceThreshold << " Do Not Store EM: " << m_doNotStoreEMParticles <<
179 " Do Not Store Nuclei: "
180 <<
181 m_doNotStoreNuclei << " Use Seen in ECL: " << m_useSeenInECL);
182
183 // first, check if the particle is above the kinetic energy threshold
184 if (isAboveKinematicThreshold) {
185 // next check, if the particle was produced before the Region
186 if (!producedInRegion) {
187 // check, if we want to care about the seenInECL flag
188 if (m_useSeenInECL) {
189 // check, if the particle was seen in the ECL
190 if (seenInECL) {
191 // now, set the ignore flag to false
192 currParticle.setIgnore(false);
193 }
194 } else {
195 // if we don't care about the seenInECL flag, set the ignore flag to false
196 currParticle.setIgnore(false);
197 }
198 } else {
199 // if the particle was produced in the Region, first check the distance threshold
200 if (hasTraveledDistance) {
201 // check, if we want to care about the isEM flag, but not the isNuclei flag
203 // check, if the particle is not an EM particle
204 if (!isEM) {
205 // now, set the ignore flag to false
206 currParticle.setIgnore(false);
207 }
208 // next check, if we care about the isNuclei flag, but not the isEM flag
210 // check, if the particle is not a nucleus
211 if (!isNuclei) {
212 // now, set the ignore flag to false
213 currParticle.setIgnore(false);
214 }
215 // next check, if we care about both the isEM and isNuclei flags
217 // check, if the particle is not an EM particle or a nucleus
218 if (!isEM || !isNuclei) {
219 // now, set the ignore flag to false
220 currParticle.setIgnore(false);
221 }
222 // if we don't care about either flag, set the ignore flag to false
223 } else {
224 currParticle.setIgnore(false);
225 }
226 }
227 }
228 }
229 }
230 B2DEBUG(10, "Ignore Flag: " << currParticle.getIgnore());
231 // Add particle and decay Information to all the secondaries
232 for (G4Track* daughterTrack : *fpTrackingManager->GimmeSecondaries()) {
233
234 // Add the particle to the particle graph and as UserInfo to the track
235 // if it is a secondary particle created by Geant4.
236 if (daughterTrack->GetDynamicParticle()->GetPrimaryParticle() == NULL &&
237 daughterTrack->GetUserInformation() == NULL) {
238 MCParticleGraph::GraphParticle& daughterParticle = m_mcParticleGraph.addParticle();
239 const_cast<G4Track*>(daughterTrack)->SetUserInformation(new TrackInfo(daughterParticle));
240
241 currParticle.decaysInto(daughterParticle); // Add the decay history
242
243 // Optical photons and secondaries: steering of output to MCParticles
244 if (daughterTrack->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) {
245
246 // Optical photons
248 daughterParticle.setIgnore();
249 // to apply quantum efficiency only once, if optical photon is a daugher of optical photon
250 if (track->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) {
251 TrackInfo* currInfo = dynamic_cast<TrackInfo*>(track->GetUserInformation());
252 TrackInfo* daughterInfo = dynamic_cast<TrackInfo*>(daughterTrack->GetUserInformation());
253 daughterInfo->setStatus(currInfo->getStatus());
254 daughterInfo->setFraction(currInfo->getFraction());
255 }
256 } else if (daughterTrack->GetCreatorProcess()->GetProcessSubType() == fBremsstrahlung) {
257
258 // Bremsstrahlung photons
259 // Do not store the generator info in the final MCParticles block
260 // if the ignore flag is set or its energy is too low in [MeV].
261 if (m_ignoreBremsstrahlungPhotons || daughterTrack->GetKineticEnergy() < m_bremsstrahlungPhotonsEnergyCut)
262 daughterParticle.setIgnore();
263 } else if (daughterTrack->GetCreatorProcess()->GetProcessSubType() == fGammaConversion) {
264
265 // e+ or e- created by gamma conversion to pairs
266 // Do not store the generator info in the final MCParticles block
267 // if the ignore flag is set or kinetic energy is too low in [MeV].
268 if (m_ignorePairConversions || daughterTrack->GetKineticEnergy() < m_pairConversionsEnergyCut)
269 daughterParticle.setIgnore();
270 } else {
271
272 // Do not store the generator info in the final MCParticles block
273 // if the ignore flag is set or its kinetic energy is too low in [MeV].
274 if (m_ignoreSecondaries || daughterTrack->GetKineticEnergy() < m_secondariesEnergyCut)
275 daughterParticle.setIgnore();
276
277 // B2INFO("Secondary Physics Process: " << daughterTrack->GetCreatorProcess()->GetProcessSubType());
278 }
279 }
280 }
281 // If the track is just suspended we can return here: the rest should be filled once the track is done
282 if (track->GetTrackStatus() == fSuspend)
283 return;
284
285 // Check if particle left the detector/simulation volume.
286 if (postStep->GetStepStatus() == fWorldBoundary) {
288 }
289
290 // Check if particle was stopped in the detector/simulation volume
291 if (track->GetKineticEnergy() <= 0.0) {
293 }
294
295 // Set the values for the particle
296 G4ThreeVector decVtx = postStep->GetPosition() / CLHEP::mm * Unit::mm;
297 currParticle.setDecayVertex(decVtx.x(), decVtx.y(), decVtx.z());
298 currParticle.setDecayTime(postStep->GetGlobalTime()); // Time does not need a conversion factor
299 currParticle.setValidVertex(true);
300
301 // Check if we can remove some points from the trajectory
303 MCParticleTrajectory* tr = dynamic_cast<TrackInfo*>(track->GetUserInformation())->getTrajectory();
304 if (tr) {
306 }
307 }
308 } catch (CouldNotFindUserInfo& exc) {
309 B2FATAL(exc.what());
310 }
311}
Class to represent Particle data in graph.
void decaysInto(GraphParticle &daughter)
Tells the graph that this particle decays into daughter.
void setTrackID(int trackID)
Set the track ID for the particle.
void setIgnore(bool ignore=true)
Set or remove the ignore flag.
bool getIgnore() const
Get the ignore flag.
Class to build, validate and sort a particle decay chain.
Class to save the full simulated trajectory of a particle.
void simplify(float distanceTolerance)
Simplify the trajectory using the Ramer-Douglas-Peuker algorithm.
A Class to store the Monte Carlo particle information.
Definition MCParticle.h:32
@ c_LeftDetector
bit 2: Particle left the detector (the simulation volume).
Definition MCParticle.h:51
@ c_StoppedInDetector
bit 3: Particle was stopped in the detector (the simulation volume).
Definition MCParticle.h:53
void setDecayTime(float time)
Set decay time.
Definition MCParticle.h:379
void setMass(float mass)
Set particle mass.
Definition MCParticle.h:355
void setDecayVertex(const ROOT::Math::XYZVector &vertex)
Set decay vertex.
Definition MCParticle.h:436
int getIndex() const
Get 1-based index of the particle in the corresponding MCParticle list.
Definition MCParticle.h:219
ROOT::Math::XYZVector getDecayVertex() const
Return decay vertex.
Definition MCParticle.h:208
void addStatus(unsigned short int bitmask)
Add bitmask to current status.
Definition MCParticle.h:342
void setEnergy(float energy)
Set energy.
Definition MCParticle.h:361
bool hasSeenInDetector(Const::DetectorSet set) const
Return if the seen-in flag for a specific subdetector is set or not.
Definition MCParticle.h:299
ROOT::Math::XYZVector getProductionVertex() const
Return production vertex position.
Definition MCParticle.h:178
void setValidVertex(bool valid)
Set indication whether vertex and time information is valid or just default.
Definition MCParticle.h:367
void setProductionVertex(const ROOT::Math::XYZVector &vertex)
Set production vertex position.
Definition MCParticle.h:385
void setSecondaryPhysicsProcess(int physicsProcess)
Sets the physics process type of a secondary particle.
Definition MCParticle.h:457
void setPDG(int pdg)
Set PDG code of the particle.
Definition MCParticle.h:324
int getPDG() const
Return PDG code of particle.
Definition MCParticle.h:101
float getProductionTime() const
Return production time in ns.
Definition MCParticle.h:148
ROOT::Math::XYZVector getMomentum() const
Return momentum.
Definition MCParticle.h:187
void setMomentum(const ROOT::Math::XYZVector &momentum)
Set particle momentum.
Definition MCParticle.h:406
void setProductionTime(float time)
Set production time.
Definition MCParticle.h:373
@ c_negativeWeight
Flip the sign of the weight to become negative if the original element got re-attributed.
static void registerMCParticleRelation(const std::string &name, RelationArray::EConsolidationAction ignoreAction=RelationArray::c_negativeWeight)
Register an relation involving MCParticles.
double m_regionZBackward
Region backward z limit.
bool m_ignoreSecondaries
do not store secondaries in MCParticles
bool m_doNotStoreNuclei
use is Nuclei check
MCParticleGraph & m_mcParticleGraph
Reference to the MCParticle graph which is updated by the tracking action.
bool m_doNotStoreEMParticles
use is EM check
TrackingAction(MCParticleGraph &mcParticleGraph)
Constructor.
double m_distanceThreshold
distance threshold
double m_pairConversionsEnergyCut
kinetic energy cut for stored e+ or e- from pair conversions [MeV]
void setStoreTrajectories(int store, double distanceTolerance)
Sets the trajectory option to enable storing of the simulated particle trajectories.
RelationArray m_relMCTrajectories
RelationArry for the relation between MCParticles and Trajectories.
bool m_useDetailedParticleMatching
use detailed particle matching logic to filter secondaries
bool m_useSeenInECL
use seen in ECL check
StoreArray< MCParticleTrajectory > m_storeMCTrajectories
Store array for the Trajectories.
double m_distanceTolerance
distance tolerance to merge trajectory points
void PreUserTrackingAction(const G4Track *track)
Checks if the particle associated to the track is already in the MCParticle list.
double m_secondariesEnergyCut
kinetic energy cut for stored secondaries [MeV]
double m_kineticEnergyThreshold
kinetic energy threshold
double m_regionRho
Region rho limit.
void PostUserTrackingAction(const G4Track *track)
Updates the data of the MCParticle associated with the Geant4 track.
bool m_ignorePairConversions
do not store e+ or e- from pair conversions in MCparticles
bool m_ignoreBremsstrahlungPhotons
do not store bremsstrahlung photons in MCParticles
bool m_ignoreOpticalPhotons
do not store optical photons in MCParticles
double m_bremsstrahlungPhotonsEnergyCut
kinetic energy cut for stored bremsstrahlung photons [MeV]
double m_regionZForward
Region forward z limit.
int m_storeTrajectories
Store trajectories for 0=none, 1=primary or 2=all particles.
void setFraction(double fraction)
Store optical photon propagation fraction (used for performance speed-ups)
Definition UserInfo.h:82
void setStatus(int status)
Set status of optical photon (used for performance speed-ups)
Definition UserInfo.h:76
double getFraction()
Get optical photon propagation fraction (used for performance speed-ups) status=0: fraction=1 status=...
Definition UserInfo.h:67
int getStatus()
Get status of optical photon (used for performance speed-ups) 0 initial 1 prescaled in StackingAction...
Definition UserInfo.h:58
Accessor to arrays stored in the data store.
Definition StoreArray.h:113
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition StoreArray.h:140
static const double mm
[millimeters]
Definition Unit.h:70
static const double MeV
[megaelectronvolt]
Definition Unit.h:114
Abstract base class for different kinds of events.