ATLAS Offline Software
Loading...
Searching...
No Matches
ThinGeantTruthAlg.cxx
Go to the documentation of this file.
1
2
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
40StatusCode
42{
43 if (m_streamName.empty()) {
44 ATH_MSG_ERROR("StreamName property was not initialized.");
45 return StatusCode::FAILURE;
46 }
47
53 ATH_CHECK(m_muonsKey.initialize(m_keepMuons));
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
72StatusCode
74{
75 ATH_MSG_INFO("Processed " << m_nEventsProcessed << " events containing "
76 << m_nParticlesProcessed << " truth particles and "
77 << m_nVerticesProcessed << " truth vertices ");
79 << " Geant truth particles and " << m_nVerticesThinned
80 << " corresponding truth vertices ");
81 return StatusCode::SUCCESS;
82}
83
84StatusCode
85ThinGeantTruthAlg::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) {
114 truthMuon = xAOD::TruthHelpers::getTruthParticle(*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};
127 ATH_CHECK(SG::get(electrons, m_electronsKey, ctx));
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
216 if (MC::isBSM(particle) && MC::isStable(particle)) {
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
260 if (!HepMC::is_simulation_particle(particle)) {
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
323void
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
358void
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) {
387 decayVtx->outgoingParticle(i), particleMask, encounteredUniqueIDs);
388 }
389
390 }
391
392
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
ATLAS-specific HepMC functions.
ElementLink< xAOD::TruthParticleContainer > TruthLink_t
Handle for requesting thinning for a data object.
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
size_t index() const
Return the index of this element within its container.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
void keep(size_t ndx)
Mark that index ndx in the container should be kept (not thinned away).
Handle for requesting thinning for a data object.
SG::ReadHandleKey< xAOD::MuonContainer > m_muonsKey
std::atomic< unsigned long > m_nParticlesThinned
void ancestors(const xAOD::TruthParticle *, std::vector< bool > &, std::unordered_set< int > &) const
Inline method.
std::atomic< unsigned long > m_nVerticesProcessed
std::atomic< unsigned long > m_nVerticesThinned
Gaudi::Property< std::string > m_truthLinkDecor
Truth particle link decorations.
SG::ReadHandleKey< xAOD::PhotonContainer > m_photonsKey
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_egammaTruthKey
virtual StatusCode execute(const EventContext &ctx) const override final
SG::ThinningHandleKey< xAOD::TruthVertexContainer > m_truthVerticesKey
StringProperty m_streamName
virtual StatusCode initialize() override
void descendants(const xAOD::TruthParticle *, std::vector< bool > &, std::unordered_set< int > &) const
Gaudi::Property< std::vector< int > > m_longlived
SG::ReadHandleKey< xAOD::ElectronContainer > m_electronsKey
std::atomic< unsigned long > m_nEventsProcessed
Counters.
SG::ReadDecorHandleKeyArray< xAOD::IParticleContainer > m_readDecorKeys
Schedule the algorithm's dependency on the truth particle link.
virtual StatusCode finalize() override
Gaudi::Property< bool > m_keepMuons
Gaudi::Property< bool > m_keepEGamma
std::atomic< unsigned long > m_nParticlesProcessed
SG::ThinningHandleKey< xAOD::TruthParticleContainer > m_truthParticlesKey
Gaudi::Property< float > m_etaMaxEgTruth
SG::ReadHandleKey< xAOD::ElectronContainer > m_fwdElectronsKey
const TruthVertex_v1 * decayVtx() const
The decay vertex of this particle.
bool hasProdVtx() const
Check for a production vertex on this particle.
bool hasDecayVtx() const
Check for a decay vertex on this particle.
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
size_t nOutgoingParticles() const
Get the number of outgoing particles.
size_t nIncomingParticles() const
Get the number of incoming particles.
int uniqueID(const T &p)
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...
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
bool isDecayed(const T &p)
Identify if the particle decayed.
bool isBSM(const T &p)
APID: graviton and all Higgs extensions are BSM.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
const xAOD::TruthParticle * getTruthParticle(const xAOD::IParticle &p)
Return the truthParticle associated to the given IParticle (if any)
PhotonContainer_v1 PhotonContainer
Definition of the current "photon container version".
ElectronContainer_v1 ElectronContainer
Definition of the current "electron container version".
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
TruthParticle_v1 TruthParticle
Typedef to implementation.
Muon_v1 Muon
Reference the current persistent version:
Photon_v1 Photon
Definition of the current "egamma version".
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.
Electron_v1 Electron
Definition of the current "egamma version".