ATLAS Offline Software
ThinGeantTruthAlg.cxx
Go to the documentation of this file.
1 
3 /*
4  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5 */
6 
7 // ThinGeantTruthAlg.cxx
8 // Author: James Catmore <James.Catmore@cern.ch>
9 // based on similar code by Karsten Koeneke <karsten.koeneke@cern.ch>
10 // Uses thinning service to remove unwanted xAOD truth particles that
11 // can't be dropped earlier in the simulation chain.
12 // Not intended for use in derivations!
13 // - Keep all truth generator particles
14 // - Keep all truth particles associated with reco photons, electrons
15 // and muons, and their ancestors.
16 // - Drop any vertices that, after the above thinning, have neither
17 // incoming nor outgoing particles
18 // Unlike other algs in this package, no tool is used to select the
19 // objects for thinning - everything is done in this one class.
20 // Expression evaluation is also not used.
22 
23 // EventUtils includes
24 #include "ThinGeantTruthAlg.h"
27 // STL includes
28 #include <algorithm>
29 
30 // FrameWork includes
31 #include "Gaudi/Property.h"
33 
34 // Standard includes
35 #include <cstdlib>
36 
39 
42 {
43  if (m_streamName.empty()) {
44  ATH_MSG_ERROR("StreamName property was not initialized.");
45  return StatusCode::FAILURE;
46  }
47 
52  ATH_CHECK(m_photonsKey.initialize(m_keepEGamma));
55  if (m_keepEGamma) {
58  if (!m_fwdElectronsKey.empty()){
60  }
61  }
62  if (!m_muonsKey.empty()){
64  }
65 
66  ATH_CHECK(m_readDecorKeys.initialize());
67 
68 
69  return StatusCode::SUCCESS;
70 }
71 
74 {
75  ATH_MSG_INFO("Processed " << m_nEventsProcessed << " events containing "
76  << m_nParticlesProcessed << " truth particles and "
77  << m_nVerticesProcessed << " truth vertices ");
78  ATH_MSG_INFO("Removed " << m_nParticlesThinned
79  << " Geant truth particles and " << m_nVerticesThinned
80  << " corresponding truth vertices ");
81  return StatusCode::SUCCESS;
82 }
83 
85 ThinGeantTruthAlg::execute(const EventContext& ctx) const
86 {
87  // Increase the event counter
89 
90  // Retrieve truth and vertex containers
91  SG::ThinningHandle truthParticles{m_truthParticlesKey, ctx};
92  SG::ThinningHandle truthVertices{m_truthVerticesKey, ctx};
93  if (!truthParticles.isValid()) {
94  ATH_MSG_FATAL("No TruthParticleContainer with key " << m_truthParticlesKey.key() << " found.");
95  return StatusCode::FAILURE;
96  }
97  if (!truthVertices.isValid()) {
98  ATH_MSG_FATAL("No TruthVertexContainer with key " << m_truthVerticesKey.key() << " found.");
99  return StatusCode::FAILURE;
100  }
101 
102  // Loop over photons, electrons and muons and get the associated truth
103  // particles Retain the associated index number
104  std::vector<int> recoParticleTruthIndices;
105  std::vector<int> egammaTruthIndices{};
106 
107  // Muons
108  if (m_keepMuons) {
109  const xAOD::MuonContainer* muons{nullptr};
110  ATH_CHECK(SG::get(muons, m_muonsKey, ctx));
111  for (const xAOD::Muon* muon : *muons) {
113  if (truthMuon) {
115  if (truthMuon) {
116  recoParticleTruthIndices.push_back(truthMuon->index());
117  }
118  }
119  }
120  }
121 
122  // Electrons and photons
123  if (m_keepEGamma) {
124 
125  // Electrons
126  const xAOD::ElectronContainer* electrons{nullptr};
128 
129  for (const xAOD::Electron* electron : *electrons) {
130  const xAOD::TruthParticle* truthElectron =
132  if (truthElectron) {
133  recoParticleTruthIndices.push_back(truthElectron->index());
134  }
135  }
136 
137  // Forward Electrons
138  const xAOD::ElectronContainer* fwdElectrons{nullptr};
139  ATH_CHECK(SG::get(fwdElectrons, m_fwdElectronsKey, ctx));
140  if (fwdElectrons) {
141 
142  for (const xAOD::Electron* electron : *fwdElectrons) {
143  const xAOD::TruthParticle* truthElectron =
145  if (truthElectron) {
146  recoParticleTruthIndices.push_back(truthElectron->index());
147  }
148  }
149  }
150 
151  // Photons
152  const xAOD::PhotonContainer* photons{nullptr};
153  ATH_CHECK(SG::get(photons, m_photonsKey, ctx));
154  for (const xAOD::Photon* photon : *photons) {
155  const xAOD::TruthParticle* truthPhoton =
157  if (truthPhoton) {
158  recoParticleTruthIndices.push_back(truthPhoton->index());
159  }
160  }
161 
162  // egamma Truth Particles
163  const xAOD::TruthParticleContainer* egammaTruthParticles{nullptr};
164  ATH_CHECK(SG::get(egammaTruthParticles, m_egammaTruthKey, ctx));
165 
166  for (const xAOD::TruthParticle* egTruthParticle : *egammaTruthParticles) {
167 
168  static const SG::AuxElement::ConstAccessor<int> accType("truthType");
169 
170  if (!accType.isAvailable(*egTruthParticle) ||
171  accType(*egTruthParticle) != MCTruthPartClassifier::IsoElectron ||
172  std::abs(egTruthParticle->eta()) > m_etaMaxEgTruth) {
173  continue;
174  }
175  // Only isolated true electrons
177  static const SG::AuxElement::ConstAccessor<TruthLink_t> linkToTruth(
178  "truthParticleLink");
179  if (!linkToTruth.isAvailable(*egTruthParticle)) {
180  continue;
181  }
182 
183  const TruthLink_t& truthegamma = linkToTruth(*egTruthParticle);
184  if (!truthegamma.isValid()) {
185  continue;
186  }
187  egammaTruthIndices.push_back((*truthegamma)->index());
188  }
189  }
190 
191  // Set up masks
192  std::vector<bool> particleMask, vertexMask;
193  int nTruthParticles = truthParticles->size();
194  int nTruthVertices = truthVertices->size();
195  m_nParticlesProcessed.fetch_add(nTruthParticles, std::memory_order_relaxed);
196  m_nVerticesProcessed.fetch_add(nTruthVertices, std::memory_order_relaxed);
197  particleMask.assign(nTruthParticles, false);
198  vertexMask.assign(nTruthVertices, false);
199 
200  // Vector of pairs keeping track of how many incoming/outgoing particles each
201  // vertex has
202  std::vector<std::pair<int, int>> vertexLinksCounts;
203  for (const auto *vertex : *truthVertices) {
204  std::pair<int, int> tmpPair;
205  tmpPair.first = vertex->nIncomingParticles();
206  tmpPair.second = vertex->nOutgoingParticles();
207  vertexLinksCounts.push_back(tmpPair);
208  }
209 
210  // Loop over truth particles and update mask
211  std::unordered_set<int> encounteredUniqueIDs; // for loop protection
212  for (int i = 0; i < nTruthParticles; ++i) {
213  encounteredUniqueIDs.clear();
214  const xAOD::TruthParticle* particle = (*truthParticles)[i];
215  // Retain status 1 BSM particles and descendants
217  descendants(particle, particleMask, encounteredUniqueIDs);
218  encounteredUniqueIDs.clear();
219  }
220  // Retain children of longer-lived generator particles
221  if (MC::isStable(particle)) {
222  int pdgId = abs(particle->pdgId());
223  if (std::find(m_longlived.begin(), m_longlived.end(), pdgId) !=
224  m_longlived.end()) {
225  const xAOD::TruthVertex* decayVtx(nullptr);
226  if (particle->hasDecayVtx()) {
227  decayVtx = particle->decayVtx();
228  }
229  int nChildren = 0;
230  if (decayVtx)
231  nChildren = decayVtx->nOutgoingParticles();
232  for (int i = 0; i < nChildren; ++i) {
233  particleMask[decayVtx->outgoingParticle(i)->index()] = true;
234  }
235  }
236  }
237 
238  // Retain particles and their descendants/ancestors associated with the
239  // reconstructed objects
240  if (std::find(recoParticleTruthIndices.begin(),
241  recoParticleTruthIndices.end(),
242  i) != recoParticleTruthIndices.end()) {
243  if (HepMC::is_simulation_particle(particle)) { // only need to do this for Geant particles since
244  // non-Geant are kept anyway
245  ancestors(particle, particleMask, encounteredUniqueIDs);
246  encounteredUniqueIDs.clear();
247  descendants(particle, particleMask, encounteredUniqueIDs);
248  encounteredUniqueIDs.clear();
249  }
250  }
251 
252  // Retain particles and their descendants associated with the egamma Truth
253  // Particles
254  if (std::find(egammaTruthIndices.begin(), egammaTruthIndices.end(), i) !=
255  egammaTruthIndices.end()) {
256  descendants(particle, particleMask, encounteredUniqueIDs);
257  encounteredUniqueIDs.clear();
258  }
259 
261  particleMask[i] = true;
262  }
263  else {
264  // deal with the products of interactions of quasi-stable
265  // particles
266  if (particle->hasProdVtx()) {
267  const xAOD::TruthVertex* prodVtx = particle->prodVtx();
268  const int nParents = prodVtx->nIncomingParticles();
269  for (int parent = 0; parent < nParents; ++parent) {
270  if (MC::isDecayed(prodVtx->incomingParticle(parent))) {
271  // "simulation particle" with a parent with status==2 was
272  // produced via the interaction of a quasi-stable particle
273  // with the detector material. Such particles should be kept.
274  particleMask[i] = true;
275  break;
276  }
277  }
278  }
279  }
280  }
281 
282  // Loop over the mask and update vertex association counters
283  for (int i = 0; i < nTruthParticles; ++i) {
284  if (!particleMask[i]) {
286  const xAOD::TruthParticle* particle = (*truthParticles)[i];
287  if (particle->hasProdVtx()) {
288  const auto *prodVertex = particle->prodVtx();
289  --vertexLinksCounts[prodVertex->index()].second;
290  }
291  if (particle->hasDecayVtx()) {
292  const auto *decayVertex = particle->decayVtx();
293  --vertexLinksCounts[decayVertex->index()].first;
294  }
295  }
296  }
297 
298  // Loop over truth vertices and update mask
299  // Those for which all incoming and outgoing particles are to be thinned, will
300  // be thinned as well
301  unsigned int nVerticesThinned = 0;
302  for (int i = 0; i < nTruthVertices; ++i) {
303  if (vertexLinksCounts[i].first != 0 || vertexLinksCounts[i].second != 0) {
304  vertexMask[i] = true;
305  } else {
306  ++nVerticesThinned;
307  }
308  }
309  m_nVerticesThinned.fetch_add(nVerticesThinned, std::memory_order_relaxed);
310  // Apply masks to thinning
311  truthParticles.keep(particleMask);
312  truthVertices.keep(vertexMask);
313 
314  return StatusCode::SUCCESS;
315 }
316 
317 // Inline methods
318 //
319 // ==============================
320 // ancestors
321 // ==============================
322 // Updates particle mask such that particle and all ancestors are retained
323 void
325  std::vector<bool>& particleMask,
326  std::unordered_set<int>& encounteredUniqueIDs) const
327 {
328 
329  // Check that this uniqueID hasn't been seen before (e.g. we are in a loop)
330  std::unordered_set<int>::const_iterator found =
331  encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
332  if (found != encounteredUniqueIDs.end())
333  return;
334  encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
335 
336  // Save particle position in the mask
337  int headIndex = pHead->index();
338  particleMask[headIndex] = true;
339 
340  // Get the production vertex
341  const xAOD::TruthVertex* prodVtx(nullptr);
342  if (pHead->hasProdVtx()) {
343  prodVtx = pHead->prodVtx();
344  } else {
345  return;
346  }
347 
348  // Get children particles and self-call
349  int nParents = prodVtx->nIncomingParticles();
350  for (int i = 0; i < nParents; ++i)
351  ancestors(prodVtx->incomingParticle(i), particleMask, encounteredUniqueIDs);
352 }
353 
354 // ==============================
355 // descendants
356 // ==============================
357 // Updates particle mask such that particle and all descendants are retained
358 void
360  const xAOD::TruthParticle* pHead,
361  std::vector<bool>& particleMask,
362  std::unordered_set<int>& encounteredUniqueIDs) const
363 {
364  // Check that this unique ID hasn't been seen before (e.g. we are in a loop)
365  std::unordered_set<int>::const_iterator found =
366  encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
367  if (found != encounteredUniqueIDs.end())
368  return;
369  encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
370 
371  // Save the particle position in the mask
372  int headIndex = pHead->index();
373  particleMask[headIndex] = true;
374 
375  // Get the decay vertex
376  const xAOD::TruthVertex* decayVtx(nullptr);
377  if (pHead->hasDecayVtx()) {
378  decayVtx = pHead->decayVtx();
379  } else {
380  return;
381  }
382 
383  // Get children particles and self-call
384  int nChildren = decayVtx->nOutgoingParticles();
385  for (int i = 0; i < nChildren; ++i) {
386  descendants(
387  decayVtx->outgoingParticle(i), particleMask, encounteredUniqueIDs);
388  }
389 
390  }
391 
392 
xAOD::TruthVertex_v1::nOutgoingParticles
size_t nOutgoingParticles() const
Get the number of outgoing particles.
xAOD::muon
@ muon
Definition: TrackingPrimitives.h:196
ThinGeantTruthAlg::m_etaMaxEgTruth
Gaudi::Property< float > m_etaMaxEgTruth
Definition: ThinGeantTruthAlg.h:52
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
Trk::ParticleSwitcher::particle
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
Definition: ParticleHypothesis.h:79
ThinGeantTruthAlg::execute
virtual StatusCode execute(const EventContext &ctx) const override final
Definition: ThinGeantTruthAlg.cxx:85
ThinGeantTruthAlg::descendants
void descendants(const xAOD::TruthParticle *, std::vector< bool > &, std::unordered_set< int > &) const
Definition: ThinGeantTruthAlg.cxx:359
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
find
std::string find(const std::string &s)
return a remapped string
Definition: hcg.cxx:135
isBSM
bool isBSM(const T &p)
APID: graviton and all Higgs extensions are BSM.
Definition: AtlasPID.h:836
ThinningHandle.h
Handle for requesting thinning for a data object.
ThinGeantTruthAlg.h
python.SystemOfUnits.second
float second
Definition: SystemOfUnits.py:135
ThinGeantTruthAlg::initialize
virtual StatusCode initialize() override
Definition: ThinGeantTruthAlg.cxx:41
ThinGeantTruthAlg::m_truthParticlesKey
SG::ThinningHandleKey< xAOD::TruthParticleContainer > m_truthParticlesKey
Definition: ThinGeantTruthAlg.h:73
SG::ConstAccessor
Helper class to provide constant type-safe access to aux data.
Definition: ConstAccessor.h:55
SG::VarHandleKey::empty
bool empty() const
Test if the key is blank.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleKey.cxx:150
ThinGeantTruthAlg::m_electronsKey
SG::ReadHandleKey< xAOD::ElectronContainer > m_electronsKey
Definition: ThinGeantTruthAlg.h:87
ThinGeantTruthAlg::m_truthLinkDecor
Gaudi::Property< std::string > m_truthLinkDecor
Truth particle link decorations.
Definition: ThinGeantTruthAlg.h:61
ThinGeantTruthAlg::m_muonsKey
SG::ReadHandleKey< xAOD::MuonContainer > m_muonsKey
Definition: ThinGeantTruthAlg.h:109
xAODTruthHelpers.h
ThinGeantTruthAlg::m_nParticlesThinned
std::atomic< unsigned long > m_nParticlesThinned
Definition: ThinGeantTruthAlg.h:122
xAOD::Muon_v1
Class describing a Muon.
Definition: Muon_v1.h:38
SG::ThinningHandle
Handle for requesting thinning for a data object.
Definition: ThinningHandle.h:84
ThinGeantTruthAlg::m_photonsKey
SG::ReadHandleKey< xAOD::PhotonContainer > m_photonsKey
Definition: ThinGeantTruthAlg.h:101
MCTruthClassifierDefs.h
xAOD::TruthParticle_v1::hasDecayVtx
bool hasDecayVtx() const
Check for a decay vertex on this particle.
ThinGeantTruthAlg::ancestors
void ancestors(const xAOD::TruthParticle *, std::vector< bool > &, std::unordered_set< int > &) const
Inline method.
Definition: ThinGeantTruthAlg.cxx:324
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
HepMC::is_simulation_particle
bool is_simulation_particle(const T &p)
Method to establish if a particle (or barcode) was created during the simulation (TODO update to be s...
Definition: MagicNumbers.h:354
lumiFormat.i
int i
Definition: lumiFormat.py:85
SG::get
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
Definition: ReadCondHandle.h:287
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
xAOD::TruthParticle_v1
Class describing a truth particle in the MC record.
Definition: TruthParticle_v1.h:37
xAOD::TruthParticle_v1::hasProdVtx
bool hasProdVtx() const
Check for a production vertex on this particle.
Definition: TruthParticle_v1.cxx:69
HepMC::uniqueID
int uniqueID(const T &p)
Definition: MagicNumbers.h:116
test_pyathena.parent
parent
Definition: test_pyathena.py:15
ThinGeantTruthAlg::m_longlived
Gaudi::Property< std::vector< int > > m_longlived
Definition: ThinGeantTruthAlg.h:65
ThinGeantTruthAlg::m_truthVerticesKey
SG::ThinningHandleKey< xAOD::TruthVertexContainer > m_truthVerticesKey
Definition: ThinGeantTruthAlg.h:80
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
ThinGeantTruthAlg::m_keepEGamma
Gaudi::Property< bool > m_keepEGamma
Definition: ThinGeantTruthAlg.h:58
ThinGeantTruthAlg::m_fwdElectronsKey
SG::ReadHandleKey< xAOD::ElectronContainer > m_fwdElectronsKey
Definition: ThinGeantTruthAlg.h:94
xAOD::TruthVertex_v1::incomingParticle
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
Definition: TruthVertex_v1.cxx:70
SG::VarHandleKey::initialize
StatusCode initialize(bool used=true)
If this object is used as a property, then this should be called during the initialize phase.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleKey.cxx:103
SG::AuxElement::index
size_t index() const
Return the index of this element within its container.
DataVector
Derived DataVector<T>.
Definition: DataVector.h:794
xAOD::TruthParticle_v1::decayVtx
const TruthVertex_v1 * decayVtx() const
The decay vertex of this particle.
xAOD::TruthParticle_v1::prodVtx
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
Definition: TruthParticle_v1.cxx:75
xAOD::TruthVertex_v1
Class describing a truth vertex in the MC record.
Definition: TruthVertex_v1.h:37
MagicNumbers.h
ThinGeantTruthAlg::m_nVerticesProcessed
std::atomic< unsigned long > m_nVerticesProcessed
Definition: ThinGeantTruthAlg.h:121
ThinGeantTruthAlg::finalize
virtual StatusCode finalize() override
Definition: ThinGeantTruthAlg.cxx:73
xAOD::TruthHelpers::getTruthParticle
const xAOD::TruthParticle * getTruthParticle(const xAOD::IParticle &p)
Return the truthParticle associated to the given IParticle (if any)
Definition: xAODTruthHelpers.cxx:25
runIDPVM.pdgId
pdgId
Definition: runIDPVM.py:91
checkTriggerxAOD.found
found
Definition: checkTriggerxAOD.py:328
xAOD::Electron_v1
Definition: Electron_v1.h:34
ThinGeantTruthAlg::m_nEventsProcessed
std::atomic< unsigned long > m_nEventsProcessed
Counters.
Definition: ThinGeantTruthAlg.h:119
python.TruthMuonD3PDObject.truthMuon
truthMuon
Definition: TruthMuonD3PDObject.py:51
ThinGeantTruthAlg::m_nParticlesProcessed
std::atomic< unsigned long > m_nParticlesProcessed
Definition: ThinGeantTruthAlg.h:120
Trk::vertex
@ vertex
Definition: MeasurementType.h:21
MC::isStable
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
Definition: HepMCHelpers.h:45
xAOD::photon
@ photon
Definition: TrackingPrimitives.h:200
xAOD::Photon_v1
Definition: Photon_v1.h:37
xAOD::TruthVertex_v1::nIncomingParticles
size_t nIncomingParticles() const
Get the number of incoming particles.
MC::isDecayed
bool isDecayed(const T &p)
Identify if the particle decayed.
Definition: HepMCHelpers.h:42
DeMoScan.first
bool first
Definition: DeMoScan.py:534
ThinGeantTruthAlg::m_egammaTruthKey
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_egammaTruthKey
Definition: ThinGeantTruthAlg.h:111
ThinGeantTruthAlg::m_readDecorKeys
SG::ReadDecorHandleKeyArray< xAOD::IParticleContainer > m_readDecorKeys
Schedule the algorithm's dependency on the truth particle link.
Definition: ThinGeantTruthAlg.h:63
xAOD::EgammaParameters::electron
@ electron
Definition: EgammaEnums.h:18
SG::ConstAccessor::isAvailable
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
ThinGeantTruthAlg::m_keepMuons
Gaudi::Property< bool > m_keepMuons
Definition: ThinGeantTruthAlg.h:57
xAOD::TruthVertex_v1::outgoingParticle
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
Definition: TruthVertex_v1.cxx:120
ThinGeantTruthAlg::m_nVerticesThinned
std::atomic< unsigned long > m_nVerticesThinned
Definition: ThinGeantTruthAlg.h:123
InDetDD::electrons
@ electrons
Definition: InDetDD_Defs.h:17
HepMCHelpers.h
ThinGeantTruthAlg::m_streamName
StringProperty m_streamName
Definition: ThinGeantTruthAlg.h:47