ATLAS Offline Software
ThinGeantTruthAlg.cxx
Go to the documentation of this file.
1 
3 /*
4  Copyright (C) 2002-2024 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));
53  ATH_CHECK(m_muonsKey.initialize(m_keepMuons));
55 
56  return StatusCode::SUCCESS;
57 }
58 
61 {
62  ATH_MSG_INFO("Processed " << m_nEventsProcessed << " events containing "
63  << m_nParticlesProcessed << " truth particles and "
64  << m_nVerticesProcessed << " truth vertices ");
65  ATH_MSG_INFO("Removed " << m_nParticlesThinned
66  << " Geant truth particles and " << m_nVerticesThinned
67  << " corresponding truth vertices ");
68  return StatusCode::SUCCESS;
69 }
70 
72 ThinGeantTruthAlg::execute(const EventContext& ctx) const
73 {
74  // Increase the event counter
76 
77  // Retrieve truth and vertex containers
79  m_truthParticlesKey, ctx);
81  m_truthVerticesKey, ctx);
82  if (!truthParticles.isValid()) {
83  ATH_MSG_FATAL("No TruthParticleContainer with key " << m_truthParticlesKey.key() << " found.");
84  return StatusCode::FAILURE;
85  }
86  if (!truthVertices.isValid()) {
87  ATH_MSG_FATAL("No TruthVertexContainer with key " << m_truthVerticesKey.key() << " found.");
88  return StatusCode::FAILURE;
89  }
90 
91  // Loop over photons, electrons and muons and get the associated truth
92  // particles Retain the associated index number
93  std::vector<int> recoParticleTruthIndices;
94  std::vector<int> egammaTruthIndices{};
95 
96  // Muons
97  if (m_keepMuons) {
99  // Retrieve muons, electrons and photons
100  if (!muons.isValid()) {
101  ATH_MSG_WARNING("No muon container with key " << m_muonsKey.key() << " found.");
102  }
103  for (const xAOD::Muon* muon : *muons) {
106  if (truthMuon) {
107  recoParticleTruthIndices.push_back(truthMuon->index());
108  }
109  }
110  }
111 
112  // Electrons and photons
113  if (m_keepEGamma) {
114 
115  // Electrons
117  if (!electrons.isValid()) {
118  ATH_MSG_WARNING("No electron container with key " << m_electronsKey.key() << " found.");
119  }
120  for (const xAOD::Electron* electron : *electrons) {
121  const xAOD::TruthParticle* truthElectron =
123  if (truthElectron) {
124  recoParticleTruthIndices.push_back(truthElectron->index());
125  }
126  }
127 
128  // Forward Electrons
129  if (!m_fwdElectronsKey.empty()) {
131  ctx);
132  if (!fwdElectrons.isValid()) {
133  ATH_MSG_WARNING("No forward electron container with key "
134  << m_fwdElectronsKey.key() << " found.");
135  }
136  for (const xAOD::Electron* electron : *fwdElectrons) {
137  const xAOD::TruthParticle* truthElectron =
139  if (truthElectron) {
140  recoParticleTruthIndices.push_back(truthElectron->index());
141  }
142  }
143  }
144 
145  // Photons
147  if (!photons.isValid()) {
148  ATH_MSG_WARNING("No photon container with key " << m_photonsKey.key() << " found.");
149  }
150 
151  for (const xAOD::Photon* photon : *photons) {
152  const xAOD::TruthParticle* truthPhoton =
154  if (truthPhoton) {
155  recoParticleTruthIndices.push_back(truthPhoton->index());
156  }
157  }
158 
159  // egamma Truth Particles
161  m_egammaTruthKey, ctx);
162  if (!egammaTruthParticles.isValid()) {
163  ATH_MSG_WARNING("No e-gamma truth container with key " << m_egammaTruthKey.key() << " found.");
164  }
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  }
264 
265  // Loop over the mask and update vertex association counters
266  for (int i = 0; i < nTruthParticles; ++i) {
267  if (!particleMask[i]) {
269  const xAOD::TruthParticle* particle = (*truthParticles)[i];
270  if (particle->hasProdVtx()) {
271  const auto *prodVertex = particle->prodVtx();
272  --vertexLinksCounts[prodVertex->index()].second;
273  }
274  if (particle->hasDecayVtx()) {
275  const auto *decayVertex = particle->decayVtx();
276  --vertexLinksCounts[decayVertex->index()].first;
277  }
278  }
279  }
280 
281  // Loop over truth vertices and update mask
282  // Those for which all incoming and outgoing particles are to be thinned, will
283  // be thinned as well
284  unsigned int nVerticesThinned = 0;
285  for (int i = 0; i < nTruthVertices; ++i) {
286  if (vertexLinksCounts[i].first != 0 || vertexLinksCounts[i].second != 0) {
287  vertexMask[i] = true;
288  } else {
289  ++nVerticesThinned;
290  }
291  }
292  m_nVerticesThinned.fetch_add(nVerticesThinned, std::memory_order_relaxed);
293  // Apply masks to thinning
294  truthParticles.keep(particleMask);
295  truthVertices.keep(vertexMask);
296 
297  return StatusCode::SUCCESS;
298 }
299 
300 // Inline methods
301 //
302 // ==============================
303 // ancestors
304 // ==============================
305 // Updates particle mask such that particle and all ancestors are retained
306 void
308  std::vector<bool>& particleMask,
309  std::unordered_set<int>& encounteredUniqueIDs) const
310 {
311 
312  // Check that this uniqueID hasn't been seen before (e.g. we are in a loop)
313  std::unordered_set<int>::const_iterator found =
314  encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
315  if (found != encounteredUniqueIDs.end())
316  return;
317  encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
318 
319  // Save particle position in the mask
320  int headIndex = pHead->index();
321  particleMask[headIndex] = true;
322 
323  // Get the production vertex
324  const xAOD::TruthVertex* prodVtx(nullptr);
325  if (pHead->hasProdVtx()) {
326  prodVtx = pHead->prodVtx();
327  } else {
328  return;
329  }
330 
331  // Get children particles and self-call
332  int nParents = prodVtx->nIncomingParticles();
333  for (int i = 0; i < nParents; ++i)
334  ancestors(prodVtx->incomingParticle(i), particleMask, encounteredUniqueIDs);
335 }
336 
337 // ==============================
338 // descendants
339 // ==============================
340 // Updates particle mask such that particle and all descendants are retained
341 void
343  const xAOD::TruthParticle* pHead,
344  std::vector<bool>& particleMask,
345  std::unordered_set<int>& encounteredUniqueIDs) const
346 {
347  // Check that this unique ID hasn't been seen before (e.g. we are in a loop)
348  std::unordered_set<int>::const_iterator found =
349  encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
350  if (found != encounteredUniqueIDs.end())
351  return;
352  encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
353 
354  // Save the particle position in the mask
355  int headIndex = pHead->index();
356  particleMask[headIndex] = true;
357 
358  // Get the decay vertex
359  const xAOD::TruthVertex* decayVtx(nullptr);
360  if (pHead->hasDecayVtx()) {
361  decayVtx = pHead->decayVtx();
362  } else {
363  return;
364  }
365 
366  // Get children particles and self-call
367  int nChildren = decayVtx->nOutgoingParticles();
368  for (int i = 0; i < nChildren; ++i) {
369  descendants(
370  decayVtx->outgoingParticle(i), particleMask, encounteredUniqueIDs);
371  }
372 
373  }
374 
375 
xAOD::TruthVertex_v1::nOutgoingParticles
size_t nOutgoingParticles() const
Get the number of outgoing particles.
python.SystemOfUnits.second
int second
Definition: SystemOfUnits.py:120
xAOD::muon
@ muon
Definition: TrackingPrimitives.h:195
ThinGeantTruthAlg::m_etaMaxEgTruth
Gaudi::Property< float > m_etaMaxEgTruth
Definition: ThinGeantTruthAlg.h:51
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:76
ThinGeantTruthAlg::execute
virtual StatusCode execute(const EventContext &ctx) const override final
Definition: ThinGeantTruthAlg.cxx:72
ThinGeantTruthAlg::descendants
void descendants(const xAOD::TruthParticle *, std::vector< bool > &, std::unordered_set< int > &) const
Definition: ThinGeantTruthAlg.cxx:342
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)
Definition: AtlasPID.h:213
ThinningHandle.h
Handle for requesting thinning for a data object.
SG::ReadHandle
Definition: StoreGate/StoreGate/ReadHandle.h:70
ThinGeantTruthAlg.h
ThinGeantTruthAlg::initialize
virtual StatusCode initialize() override
Definition: ThinGeantTruthAlg.cxx:41
ThinGeantTruthAlg::m_truthParticlesKey
SG::ThinningHandleKey< xAOD::TruthParticleContainer > m_truthParticlesKey
Definition: ThinGeantTruthAlg.h:67
SG::ConstAccessor
Helper class to provide constant type-safe access to aux data.
Definition: ConstAccessor.h:54
SG::VarHandleKey::key
const std::string & key() const
Return the StoreGate ID for the referenced object.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleKey.cxx:141
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:81
ThinGeantTruthAlg::m_muonsKey
SG::ReadHandleKey< xAOD::MuonContainer > m_muonsKey
Definition: ThinGeantTruthAlg.h:103
xAODTruthHelpers.h
PowhegPy8EG_H2a.pdgId
dictionary pdgId
Definition: PowhegPy8EG_H2a.py:128
ThinGeantTruthAlg::m_nParticlesThinned
std::atomic< unsigned long > m_nParticlesThinned
Definition: ThinGeantTruthAlg.h:116
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:95
SG::ThinningHandleBase::keep
void keep(size_t ndx)
Mark that index ndx in the container should be kept (not thinned away).
Definition: ThinningHandleBase.cxx:68
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:307
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:299
lumiFormat.i
int i
Definition: lumiFormat.py:92
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:41
xAOD::TruthParticle_v1::hasProdVtx
bool hasProdVtx() const
Check for a production vertex on this particle.
Definition: TruthParticle_v1.cxx:74
HepMC::uniqueID
int uniqueID(const T &p)
Definition: MagicNumbers.h:113
ThinGeantTruthAlg::m_longlived
Gaudi::Property< std::vector< int > > m_longlived
Definition: ThinGeantTruthAlg.h:59
ThinGeantTruthAlg::m_truthVerticesKey
SG::ThinningHandleKey< xAOD::TruthVertexContainer > m_truthVerticesKey
Definition: ThinGeantTruthAlg.h:74
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
ThinGeantTruthAlg::m_keepEGamma
Gaudi::Property< bool > m_keepEGamma
Definition: ThinGeantTruthAlg.h:57
ThinGeantTruthAlg::m_fwdElectronsKey
SG::ReadHandleKey< xAOD::ElectronContainer > m_fwdElectronsKey
Definition: ThinGeantTruthAlg.h:88
xAOD::TruthVertex_v1::incomingParticle
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
Definition: TruthVertex_v1.cxx:71
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.
SG::ReadHandle::isValid
virtual bool isValid() override final
Can the handle be successfully dereferenced?
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:80
xAOD::TruthVertex_v1
Class describing a truth vertex in the MC record.
Definition: TruthVertex_v1.h:41
MagicNumbers.h
ThinGeantTruthAlg::m_nVerticesProcessed
std::atomic< unsigned long > m_nVerticesProcessed
Definition: ThinGeantTruthAlg.h:115
ThinGeantTruthAlg::finalize
virtual StatusCode finalize() override
Definition: ThinGeantTruthAlg.cxx:60
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
xAOD::Electron_v1
Definition: Electron_v1.h:34
ThinGeantTruthAlg::m_nEventsProcessed
std::atomic< unsigned long > m_nEventsProcessed
Counters.
Definition: ThinGeantTruthAlg.h:113
python.TruthMuonD3PDObject.truthMuon
truthMuon
Definition: TruthMuonD3PDObject.py:51
ThinGeantTruthAlg::m_nParticlesProcessed
std::atomic< unsigned long > m_nParticlesProcessed
Definition: ThinGeantTruthAlg.h:114
Trk::vertex
@ vertex
Definition: MeasurementType.h:21
MC::isStable
bool isStable(const T &p)
Definition: HepMCHelpers.h:30
xAOD::photon
@ photon
Definition: TrackingPrimitives.h:199
CondAlgsOpts.found
int found
Definition: CondAlgsOpts.py:101
xAOD::Photon_v1
Definition: Photon_v1.h:37
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
xAOD::TruthVertex_v1::nIncomingParticles
size_t nIncomingParticles() const
Get the number of incoming particles.
Definition: TruthVertex_v1.cxx:49
DeMoScan.first
bool first
Definition: DeMoScan.py:534
ThinGeantTruthAlg::m_egammaTruthKey
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_egammaTruthKey
Definition: ThinGeantTruthAlg.h:105
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:56
xAOD::TruthVertex_v1::outgoingParticle
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
Definition: TruthVertex_v1.cxx:121
ThinGeantTruthAlg::m_nVerticesThinned
std::atomic< unsigned long > m_nVerticesThinned
Definition: ThinGeantTruthAlg.h:117
InDetDD::electrons
@ electrons
Definition: InDetDD_Defs.h:17
HepMCHelpers.h
ThinGeantTruthAlg::m_streamName
StringProperty m_streamName
Definition: ThinGeantTruthAlg.h:46