ATLAS Offline Software
Loading...
Searching...
No Matches
ThinGeantTruthAlg.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2026 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
28// FrameWork includes
29#include "Gaudi/Property.h"
31
34
35#include <algorithm>
36#include <cstdlib>
37
40{
41 if (m_streamName.empty()) {
42 ATH_MSG_ERROR("StreamName property was not initialized.");
43 return StatusCode::FAILURE;
44 }
45
52 ATH_CHECK(m_muonsKey.initialize(m_keepMuons));
53 ATH_CHECK(m_lrtMuonsKey.initialize(m_keepMuons && !m_lrtMuonsKey.empty()));
55 if (m_keepEGamma) {
58 if (!m_fwdElectronsKey.empty()){
60 }
61 if (!m_lrtElectronsKey.empty()){
63 }
64 }
65 if (!m_muonsKey.empty()){
67 }
68 if (!m_lrtMuonsKey.empty()){
70 }
71
72 ATH_CHECK(m_readDecorKeys.initialize());
73
74
75 return StatusCode::SUCCESS;
76}
77
78StatusCode
80{
81 ATH_MSG_INFO("Processed " << m_nEventsProcessed << " events containing "
82 << m_nParticlesProcessed << " truth particles and "
83 << m_nVerticesProcessed << " truth vertices ");
85 << " Geant truth particles and " << m_nVerticesThinned
86 << " corresponding truth vertices ");
87 return StatusCode::SUCCESS;
88}
89
90StatusCode
91ThinGeantTruthAlg::execute(const EventContext& ctx) const
92{
93 // Increase the event counter
95
96 // Retrieve truth and vertex containers
97 SG::ThinningHandle truthParticles{m_truthParticlesKey, ctx};
98 SG::ThinningHandle truthVertices{m_truthVerticesKey, ctx};
99 if (!truthParticles.isValid()) {
100 ATH_MSG_FATAL("No TruthParticleContainer with key " << m_truthParticlesKey.key() << " found.");
101 return StatusCode::FAILURE;
102 }
103 if (!truthVertices.isValid()) {
104 ATH_MSG_FATAL("No TruthVertexContainer with key " << m_truthVerticesKey.key() << " found.");
105 return StatusCode::FAILURE;
106 }
107
108 // Loop over photons, electrons and muons and get the associated truth
109 // particles Retain the associated index number
110 std::vector<int> recoParticleTruthIndices;
111 std::vector<int> egammaTruthIndices{};
112
113 // Muons
114 if (m_keepMuons) {
115 const xAOD::MuonContainer* muons{nullptr};
116 ATH_CHECK(SG::get(muons, m_muonsKey, ctx));
117 for (const xAOD::Muon* muon : *muons) {
119 if (truthMuon) {
120 truthMuon = xAOD::TruthHelpers::getTruthParticle(*truthMuon);
121 if (truthMuon) {
122 recoParticleTruthIndices.push_back(truthMuon->index());
123 }
124 }
125 }
126
127 // LRT muons
128 const xAOD::MuonContainer* lrtMuons{nullptr};
129 ATH_CHECK(SG::get(lrtMuons, m_lrtMuonsKey, ctx));
130 if (lrtMuons) {
131 for (const xAOD::Muon* muon : *lrtMuons) {
133 if (truthMuon) {
134 truthMuon = xAOD::TruthHelpers::getTruthParticle(*truthMuon);
135 if (truthMuon) {
136 recoParticleTruthIndices.push_back(truthMuon->index());
137 }
138 }
139 }
140 }
141 }
142
143 // Electrons and photons
144 if (m_keepEGamma) {
145
146 // Electrons
147 const xAOD::ElectronContainer* electrons{nullptr};
148 ATH_CHECK(SG::get(electrons, m_electronsKey, ctx));
149
150 for (const xAOD::Electron* electron : *electrons) {
151 const xAOD::TruthParticle* truthElectron =
153 if (truthElectron) {
154 recoParticleTruthIndices.push_back(truthElectron->index());
155 }
156 }
157
158 // Forward Electrons
159 const xAOD::ElectronContainer* fwdElectrons{nullptr};
160 ATH_CHECK(SG::get(fwdElectrons, m_fwdElectronsKey, ctx));
161 if (fwdElectrons) {
162
163 for (const xAOD::Electron* electron : *fwdElectrons) {
164 const xAOD::TruthParticle* truthElectron =
166 if (truthElectron) {
167 recoParticleTruthIndices.push_back(truthElectron->index());
168 }
169 }
170 }
171
172 // LRT electrons
173 const xAOD::ElectronContainer* lrtElectrons{nullptr};
174 ATH_CHECK(SG::get(lrtElectrons, m_lrtElectronsKey, ctx));
175 if (lrtElectrons) {
176 for (const xAOD::Electron* electron : *lrtElectrons) {
177 const xAOD::TruthParticle* truthElectron =
179 if (truthElectron) {
180 recoParticleTruthIndices.push_back(truthElectron->index());
181 }
182 }
183 }
184
185 // Photons
186 const xAOD::PhotonContainer* photons{nullptr};
187 ATH_CHECK(SG::get(photons, m_photonsKey, ctx));
188 for (const xAOD::Photon* photon : *photons) {
189 const xAOD::TruthParticle* truthPhoton =
191 if (truthPhoton) {
192 recoParticleTruthIndices.push_back(truthPhoton->index());
193 }
194 }
195
196 // egamma Truth Particles
197 const xAOD::TruthParticleContainer* egammaTruthParticles{nullptr};
198 ATH_CHECK(SG::get(egammaTruthParticles, m_egammaTruthKey, ctx));
199
200 for (const xAOD::TruthParticle* egTruthParticle : *egammaTruthParticles) {
201 //coverity[UNNECESSARY_STRING_COPY:FALSE]
202 static const SG::ConstAccessor<int> accType("truthType");
203
204 if (!accType.isAvailable(*egTruthParticle) ||
205 accType(*egTruthParticle) != MCTruthPartClassifier::IsoElectron ||
206 std::abs(egTruthParticle->eta()) > m_etaMaxEgTruth) {
207 continue;
208 }
209 // Only isolated true electrons
211 static const SG::ConstAccessor<TruthLink_t> linkToTruth(
212 "truthParticleLink");
213 if (!linkToTruth.isAvailable(*egTruthParticle)) {
214 continue;
215 }
216
217 const TruthLink_t& truthegamma = linkToTruth(*egTruthParticle);
218 if (!truthegamma.isValid()) {
219 continue;
220 }
221 egammaTruthIndices.push_back((*truthegamma)->index());
222 }
223 }
224
225 // Set up masks
226 std::vector<bool> particleMask, vertexMask;
227 int nTruthParticles = truthParticles->size();
228 int nTruthVertices = truthVertices->size();
229 m_nParticlesProcessed.fetch_add(nTruthParticles, std::memory_order_relaxed);
230 m_nVerticesProcessed.fetch_add(nTruthVertices, std::memory_order_relaxed);
231 particleMask.assign(nTruthParticles, false);
232 vertexMask.assign(nTruthVertices, false);
233
234 // Vector of pairs keeping track of how many incoming/outgoing particles each
235 // vertex has
236 std::vector<std::pair<int, int>> vertexLinksCounts;
237 for (const auto *vertex : *truthVertices) {
238 std::pair<int, int> tmpPair;
239 tmpPair.first = vertex->nIncomingParticles();
240 tmpPair.second = vertex->nOutgoingParticles();
241 vertexLinksCounts.push_back(tmpPair);
242 }
243
244 // Loop over truth particles and update mask
245 std::unordered_set<int> encounteredUniqueIDs; // for loop protection
246 for (int i = 0; i < nTruthParticles; ++i) {
247 encounteredUniqueIDs.clear();
248 const xAOD::TruthParticle* particle = (*truthParticles)[i];
249 // Retain status 1 BSM particles and descendants
250 if (MC::isBSM(particle) && MC::isStable(particle)) {
251 descendants(particle, particleMask, encounteredUniqueIDs);
252 encounteredUniqueIDs.clear();
253 }
254
255 // Retain stable Tau particles produced by Geant4 and their descendants
256 if (MC::isTau(particle) && MC::isStable(particle)) {
257 descendants(particle, particleMask, encounteredUniqueIDs);
258 encounteredUniqueIDs.clear();
259 }
260
261 // Retain children of longer-lived generator particles
262 if (MC::isStable(particle)) {
263 int pdgId = abs(particle->pdgId());
264 if (std::find(m_longlived.begin(), m_longlived.end(), pdgId) !=
265 m_longlived.end()) {
266 const xAOD::TruthVertex* decayVtx(nullptr);
267 if (particle->hasDecayVtx()) {
268 decayVtx = particle->decayVtx();
269 }
270 int nChildren = 0;
271 if (decayVtx)
272 nChildren = decayVtx->nOutgoingParticles();
273 for (int i = 0; i < nChildren; ++i) {
274 particleMask[decayVtx->outgoingParticle(i)->index()] = true;
275 }
276 }
277 }
278
279 // Retain particles and their descendants/ancestors associated with the
280 // reconstructed objects
281 if (std::find(recoParticleTruthIndices.begin(),
282 recoParticleTruthIndices.end(),
283 i) != recoParticleTruthIndices.end()) {
284 if (HepMC::is_simulation_particle(particle)) { // only need to do this for Geant particles since
285 // non-Geant are kept anyway
286 ancestors(particle, particleMask, encounteredUniqueIDs);
287 encounteredUniqueIDs.clear();
288 descendants(particle, particleMask, encounteredUniqueIDs);
289 encounteredUniqueIDs.clear();
290 }
291 }
292
293 // Retain particles and their descendants associated with the egamma Truth
294 // Particles
295 if (std::find(egammaTruthIndices.begin(), egammaTruthIndices.end(), i) !=
296 egammaTruthIndices.end()) {
297 descendants(particle, particleMask, encounteredUniqueIDs);
298 encounteredUniqueIDs.clear();
299 }
300
301 if (!HepMC::is_simulation_particle(particle)) {
302 particleMask[i] = true;
303 }
304 else {
305 // deal with the products of interactions of quasi-stable
306 // particles
307 if (particle->hasProdVtx()) {
308 const xAOD::TruthVertex* prodVtx = particle->prodVtx();
309 const int nParents = prodVtx->nIncomingParticles();
310 for (int parent = 0; parent < nParents; ++parent) {
311 if (MC::isDecayed(prodVtx->incomingParticle(parent))) {
312 // "simulation particle" with a parent with status==2 was
313 // produced via the interaction of a quasi-stable particle
314 // with the detector material. Such particles should be kept.
315 particleMask[i] = true;
316 break;
317 }
318 }
319 }
320 }
321 }
322
323 // Loop over the mask and update vertex association counters
324 for (int i = 0; i < nTruthParticles; ++i) {
325 if (!particleMask[i]) {
327 const xAOD::TruthParticle* particle = (*truthParticles)[i];
328 if (particle->hasProdVtx()) {
329 const auto *prodVertex = particle->prodVtx();
330 --vertexLinksCounts[prodVertex->index()].second;
331 }
332 if (particle->hasDecayVtx()) {
333 const auto *decayVertex = particle->decayVtx();
334 --vertexLinksCounts[decayVertex->index()].first;
335 }
336 }
337 }
338
339 // Loop over truth vertices and update mask
340 // Those for which all incoming and outgoing particles are to be thinned, will
341 // be thinned as well
342 unsigned int nVerticesThinned = 0;
343 for (int i = 0; i < nTruthVertices; ++i) {
344 if (vertexLinksCounts[i].first != 0 || vertexLinksCounts[i].second != 0) {
345 vertexMask[i] = true;
346 } else {
347 ++nVerticesThinned;
348 }
349 }
350 m_nVerticesThinned.fetch_add(nVerticesThinned, std::memory_order_relaxed);
351 // Apply masks to thinning
352 truthParticles.keep(particleMask);
353 truthVertices.keep(vertexMask);
354
355 return StatusCode::SUCCESS;
356}
357
358// Inline methods
359//
360// ==============================
361// ancestors
362// ==============================
363// Updates particle mask such that particle and all ancestors are retained
364void
366 std::vector<bool>& particleMask,
367 std::unordered_set<int>& encounteredUniqueIDs) const
368{
369
370 // Check that this uniqueID hasn't been seen before (e.g. we are in a loop)
371 std::unordered_set<int>::const_iterator found =
372 encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
373 if (found != encounteredUniqueIDs.end())
374 return;
375 encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
376
377 // Save particle position in the mask
378 int headIndex = pHead->index();
379 particleMask[headIndex] = true;
380
381 // Get the production vertex
382 const xAOD::TruthVertex* prodVtx(nullptr);
383 if (pHead->hasProdVtx()) {
384 prodVtx = pHead->prodVtx();
385 } else {
386 return;
387 }
388
389 // Get children particles and self-call
390 int nParents = prodVtx->nIncomingParticles();
391 for (int i = 0; i < nParents; ++i)
392 ancestors(prodVtx->incomingParticle(i), particleMask, encounteredUniqueIDs);
393}
394
395// ==============================
396// descendants
397// ==============================
398// Updates particle mask such that particle and all descendants are retained
399void
401 const xAOD::TruthParticle* pHead,
402 std::vector<bool>& particleMask,
403 std::unordered_set<int>& encounteredUniqueIDs) const
404{
405 // Check that this unique ID hasn't been seen before (e.g. we are in a loop)
406 std::unordered_set<int>::const_iterator found =
407 encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
408 if (found != encounteredUniqueIDs.end())
409 return;
410 encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
411
412 // Save the particle position in the mask
413 int headIndex = pHead->index();
414 particleMask[headIndex] = true;
415
416 // Get the decay vertex
417 const xAOD::TruthVertex* decayVtx(nullptr);
418 if (pHead->hasDecayVtx()) {
419 decayVtx = pHead->decayVtx();
420 } else {
421 return;
422 }
423
424 // Get children particles and self-call
425 int nChildren = decayVtx->nOutgoingParticles();
426 for (int i = 0; i < nChildren; ++i) {
428 decayVtx->outgoingParticle(i), particleMask, encounteredUniqueIDs);
429 }
430
431 }
432
433
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
ATLAS-specific HepMC functions.
ElementLink< xAOD::TruthParticleContainer > TruthLink_t
Handle for requesting thinning for a data object.
Helper class to provide constant type-safe access to aux data.
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
SG::ReadHandleKey< xAOD::MuonContainer > m_lrtMuonsKey
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.
SG::ReadHandleKey< xAOD::ElectronContainer > m_lrtElectronsKey
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.
::StatusCode StatusCode
StatusCode definition for legacy code.
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 isTau(const T &p)
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".