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
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
221 // Retain stable Tau particles produced by Geant4 and their descendants
222 if (MC::isTau(particle) && MC::isStable(particle)) {
223 descendants(particle, particleMask, encounteredUniqueIDs);
224 encounteredUniqueIDs.clear();
225 }
226
227 // Retain children of longer-lived generator particles
228 if (MC::isStable(particle)) {
229 int pdgId = abs(particle->pdgId());
230 if (std::find(m_longlived.begin(), m_longlived.end(), pdgId) !=
231 m_longlived.end()) {
232 const xAOD::TruthVertex* decayVtx(nullptr);
233 if (particle->hasDecayVtx()) {
234 decayVtx = particle->decayVtx();
235 }
236 int nChildren = 0;
237 if (decayVtx)
238 nChildren = decayVtx->nOutgoingParticles();
239 for (int i = 0; i < nChildren; ++i) {
240 particleMask[decayVtx->outgoingParticle(i)->index()] = true;
241 }
242 }
243 }
244
245 // Retain particles and their descendants/ancestors associated with the
246 // reconstructed objects
247 if (std::find(recoParticleTruthIndices.begin(),
248 recoParticleTruthIndices.end(),
249 i) != recoParticleTruthIndices.end()) {
250 if (HepMC::is_simulation_particle(particle)) { // only need to do this for Geant particles since
251 // non-Geant are kept anyway
252 ancestors(particle, particleMask, encounteredUniqueIDs);
253 encounteredUniqueIDs.clear();
254 descendants(particle, particleMask, encounteredUniqueIDs);
255 encounteredUniqueIDs.clear();
256 }
257 }
258
259 // Retain particles and their descendants associated with the egamma Truth
260 // Particles
261 if (std::find(egammaTruthIndices.begin(), egammaTruthIndices.end(), i) !=
262 egammaTruthIndices.end()) {
263 descendants(particle, particleMask, encounteredUniqueIDs);
264 encounteredUniqueIDs.clear();
265 }
266
267 if (!HepMC::is_simulation_particle(particle)) {
268 particleMask[i] = true;
269 }
270 else {
271 // deal with the products of interactions of quasi-stable
272 // particles
273 if (particle->hasProdVtx()) {
274 const xAOD::TruthVertex* prodVtx = particle->prodVtx();
275 const int nParents = prodVtx->nIncomingParticles();
276 for (int parent = 0; parent < nParents; ++parent) {
277 if (MC::isDecayed(prodVtx->incomingParticle(parent))) {
278 // "simulation particle" with a parent with status==2 was
279 // produced via the interaction of a quasi-stable particle
280 // with the detector material. Such particles should be kept.
281 particleMask[i] = true;
282 break;
283 }
284 }
285 }
286 }
287 }
288
289 // Loop over the mask and update vertex association counters
290 for (int i = 0; i < nTruthParticles; ++i) {
291 if (!particleMask[i]) {
293 const xAOD::TruthParticle* particle = (*truthParticles)[i];
294 if (particle->hasProdVtx()) {
295 const auto *prodVertex = particle->prodVtx();
296 --vertexLinksCounts[prodVertex->index()].second;
297 }
298 if (particle->hasDecayVtx()) {
299 const auto *decayVertex = particle->decayVtx();
300 --vertexLinksCounts[decayVertex->index()].first;
301 }
302 }
303 }
304
305 // Loop over truth vertices and update mask
306 // Those for which all incoming and outgoing particles are to be thinned, will
307 // be thinned as well
308 unsigned int nVerticesThinned = 0;
309 for (int i = 0; i < nTruthVertices; ++i) {
310 if (vertexLinksCounts[i].first != 0 || vertexLinksCounts[i].second != 0) {
311 vertexMask[i] = true;
312 } else {
313 ++nVerticesThinned;
314 }
315 }
316 m_nVerticesThinned.fetch_add(nVerticesThinned, std::memory_order_relaxed);
317 // Apply masks to thinning
318 truthParticles.keep(particleMask);
319 truthVertices.keep(vertexMask);
320
321 return StatusCode::SUCCESS;
322}
323
324// Inline methods
325//
326// ==============================
327// ancestors
328// ==============================
329// Updates particle mask such that particle and all ancestors are retained
330void
332 std::vector<bool>& particleMask,
333 std::unordered_set<int>& encounteredUniqueIDs) const
334{
335
336 // Check that this uniqueID hasn't been seen before (e.g. we are in a loop)
337 std::unordered_set<int>::const_iterator found =
338 encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
339 if (found != encounteredUniqueIDs.end())
340 return;
341 encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
342
343 // Save particle position in the mask
344 int headIndex = pHead->index();
345 particleMask[headIndex] = true;
346
347 // Get the production vertex
348 const xAOD::TruthVertex* prodVtx(nullptr);
349 if (pHead->hasProdVtx()) {
350 prodVtx = pHead->prodVtx();
351 } else {
352 return;
353 }
354
355 // Get children particles and self-call
356 int nParents = prodVtx->nIncomingParticles();
357 for (int i = 0; i < nParents; ++i)
358 ancestors(prodVtx->incomingParticle(i), particleMask, encounteredUniqueIDs);
359}
360
361// ==============================
362// descendants
363// ==============================
364// Updates particle mask such that particle and all descendants are retained
365void
367 const xAOD::TruthParticle* pHead,
368 std::vector<bool>& particleMask,
369 std::unordered_set<int>& encounteredUniqueIDs) const
370{
371 // Check that this unique ID hasn't been seen before (e.g. we are in a loop)
372 std::unordered_set<int>::const_iterator found =
373 encounteredUniqueIDs.find(HepMC::uniqueID(pHead));
374 if (found != encounteredUniqueIDs.end())
375 return;
376 encounteredUniqueIDs.insert(HepMC::uniqueID(pHead));
377
378 // Save the particle position in the mask
379 int headIndex = pHead->index();
380 particleMask[headIndex] = true;
381
382 // Get the decay vertex
383 const xAOD::TruthVertex* decayVtx(nullptr);
384 if (pHead->hasDecayVtx()) {
385 decayVtx = pHead->decayVtx();
386 } else {
387 return;
388 }
389
390 // Get children particles and self-call
391 int nChildren = decayVtx->nOutgoingParticles();
392 for (int i = 0; i < nChildren; ++i) {
394 decayVtx->outgoingParticle(i), particleMask, encounteredUniqueIDs);
395 }
396
397 }
398
399
#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.
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.
::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".