ATLAS Offline Software
Loading...
Searching...
No Matches
HardTruthThinning.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6// HardTruthThinning.cxx, (c) ATLAS Detector software
7// Author: Frank Paige
8// Based on MenuTruthThinning and should perhaps be merged with it.
9// Intended for use with CompactHardTruth.
10// Preserves graph only for decays of selected particles.
11//
12// No treatment of Geant particles (yet).
13//
15
18#include "xAODBase/IParticle.h"
20#include "GaudiKernel/SystemOfUnits.h"
22
24#include <vector>
25#include <string>
26#include <format>
27
28using Gaudi::Units::GeV;
29
31// Constructor
33
34// Required parameters:
35// EventInfo, TruthParticles, TruthVertices, HardParticles
36// Parameters that turn on features:
37// JetName If not empty, save constituents with cuts
38// KeepIds If pdgId list not empty, save particles and decay chains
39// IsolRadius If positive, save stable particles in isolation cones
40
42 const std::string& t, const std::string& n, const IInterface* p ) :
43 base_class(t,n,p),
44 m_jetPtCut(0.),
45 m_jetEtaCut(5.0),
48 m_isolR(-1),
49 m_isolPtCut(0),
50 m_maxCount(0)
51{
52 declareProperty("JetPtCut", m_jetPtCut,
53 "truth jet minumum pt");
54
55 declareProperty("JetEtaCut", m_jetEtaCut,
56 "truth jet maximum abs(eta)");
57
58 declareProperty("JetConstPtCut", m_jetConstPtCut,
59 "jet constituent minimum pt");
60
61 declareProperty("JetPhotonPtCut", m_jetPhotonPtCut,
62 "jet constituent photon minimum pt");
63
64 declareProperty("KeepIds", m_keepIds,
65 "list of abs(pdgID) to keep");
66
67 declareProperty("IsolRadius", m_isolR,
68 "isolation radius for leptons and photons");
69
70 declareProperty("IsolPtCut", m_isolPtCut,
71 "isolation particle minimum pt");
72
73 declareProperty("MaxCount",
75 "maximum number of events to print");
76}
77
78
80// Destructor
82
85
86
88// Athena initialize and finalize
90
92{
93 ATH_CHECK( m_eventInfoKey.initialize() );
94 ATH_CHECK( m_hardParticleKey.initialize() );
98
99 m_evtCount = -1;
100 m_errCount = 0;
101
102 // Check for unset photon cut
104
105 return StatusCode::SUCCESS;
106}
107
108
110{
111 ATH_MSG_INFO("finalize() ...");
112 if( m_errCount > 0 ){
113 ATH_MSG_WARNING("TruthHard/TruthEvent pdgId mismatches " <<m_errCount);
114 } else {
115 ATH_MSG_INFO("No TruthHard/TruthEvent pdgId mismatches");
116 }
117
118 return StatusCode::SUCCESS;
119}
120
121
123// doThinning
125
126StatusCode DerivationFramework::HardTruthThinning::doThinning(const EventContext& ctx) const
127{
128
129 ++m_evtCount;
130 bool doPrint = m_evtCount < m_maxCount;
131
132 // Retrieve truth particles and vertices
134 (m_truthParticleName, ctx);
136 (m_truthVertexName, ctx);
137
139 if (!inHardParts.isValid()) {
140 ATH_MSG_ERROR("Cannot retrieve TruthParticleContainer " << m_hardParticleKey);
141 return StatusCode::FAILURE;
142 }
143
144 // Hard particles are different objects but have matching unique IDs.
145 // Find hard particles with status==1.
146 // Save 4vectors of leptons and photons for matching if requested.
147 // Do we need a photon pt cut for soft photons from quarks?
148
149 std::vector<const xAOD::TruthParticle*> hardPart;
150 std::vector<TLorentzVector> pLepGam;
151
152 for (const auto* pItr : *inHardParts) {
153 if( MC::isStable(pItr) ){
154 hardPart.push_back(pItr);
155 if( m_isolR > 0 ){
156 int ida = pItr->absPdgId();
157 if( MC::isElectron(ida) || MC::isMuon(ida) || MC::isTau(ida) || MC::isPhoton(ida) ){
158 pLepGam.push_back( pItr->p4() );
159 }
160 }
161 }
162 }
163
164 // Print full input event
165 long long int evtNum{};
166 if( doPrint ){
168 if(!evt.isValid()) {
169 ATH_MSG_ERROR("Failed to retrieve EventInfo");
170 return StatusCode::FAILURE;
171 }
172 evtNum = evt->eventNumber();
173
174 printxAODTruth(evtNum, inTruthParts.cptr());
175 }
176
177
178 // Set up masks for particles/vertices
179 // Default is false for all
180
181 std::vector<bool> partMask;
182 std::vector<bool> vertMask;
183 unsigned int inPartNum = inTruthParts->size();
184 unsigned int inVertNum = inTruthVerts->size();
185 partMask.assign(inPartNum, false);
186 vertMask.assign(inVertNum, false);
187
188 ATH_MSG_DEBUG("Initial particles/vertices = " <<inPartNum <<" " <<inVertNum);
189
190
191 // Particles from hard event are stable => keep no parent/child vertices.
192 // No descendants (except Geant).
193 // Particles from keep list need descendants and their parent vertices.
194 // There could be overlap, e.g. for taus.
195 // Expect no pdgId mismatches.
196
197 std::vector<const xAOD::TruthParticle*> kids;
198
199 for (const auto* pItr : *inTruthParts) {
200 // Stable hard particle matches
201 int uid = HepMC::uniqueID(pItr);
202 bool isHard = false;
203 for(unsigned int i=0; i<hardPart.size(); ++i){
204 if( uid == HepMC::uniqueID(hardPart[i]) ){
205 isHard = true;
206 if( pItr->pdgId() != (hardPart[i])->pdgId() ){
207 ATH_MSG_WARNING("pdgID mismatch, TruthParticle uid/pdgid "
208 <<HepMC::uniqueID(pItr) <<" " <<pItr->pdgId()
209 <<" Hard uid/pdgid " <<HepMC::uniqueID(hardPart[i])
210 <<" " <<(hardPart[i])->pdgId());
211 ++m_errCount;
212 break;
213 }
214 }
215 }
216 if( isHard ){
217 if( doPrint ) ATH_MSG_DEBUG("ParticleMask isHard " <<uid);
218 partMask[ pItr->index() ] = true;
219 }
220
221 // Keep particles
222 int ida = pItr->absPdgId();
223 bool isKeep = false;
224 for(unsigned int i=0; i<m_keepIds.size(); ++i){
225 if( ida == m_keepIds[i] ){
226 isKeep = true;
227 break;
228 }
229 }
230 if( isKeep ){
231 if( doPrint ) ATH_MSG_DEBUG("ParticleMask isKeep " <<uid);
232 partMask[pItr->index()] = true;
233 int nkids = getDescendants( pItr, kids );
234 for(int i=0; i<nkids; ++i){
235 if( doPrint ) ATH_MSG_DEBUG("ParticleMask isKeep kid "
236 <<uid <<" " <<HepMC::uniqueID(kids[i]));
237 partMask[ (kids[i])->index() ] = true;
238 const xAOD::TruthVertex* v = (kids[i])->prodVtx();
239 if( v ) vertMask[ v->index() ] = true;
240 }
241 }
242
243 // Delta(R) matches to hard truth leptons/photons
244 if( !pLepGam.empty() && pItr->pt() > m_isolPtCut ){
245 TLorentzVector pp4 = pItr->p4();
246 for(unsigned int lep=0; lep<pLepGam.size(); ++lep){
247 double r = pp4.DeltaR( pLepGam[lep] );
248 if( r < m_isolR ){
249 if( doPrint ) ATH_MSG_DEBUG("ParticleMask isol " <<uid);
250 partMask[ pItr->index() ] = true;
251 }
252 }
253 }
254 }
255
256
257 // Retrieve optional jets
258 // Add particles that are constituents of selected jets using unique IDs.
259 // Is index() for JetConstituentVector or TruthParticleContainer or??
260
261 if( !m_truthJetsKey.empty() ){
262
264 if (!inJets.isValid()) {
265 ATH_MSG_ERROR("Cannot retrieve JetContainer " << m_truthJetsKey);
266 return StatusCode::FAILURE;
267 }
268
269 std::vector<int> uidJetConst;
270
271 for(const auto* ajet : *inJets){
272 if( ajet->pt() < m_jetPtCut ) continue;
273 if( std::abs(ajet->eta()) > m_jetEtaCut ) continue;
274
275 xAOD::JetConstituentVector aconst = ajet->getConstituents();
278 for( ; aItr != aItrE; ++aItr){
279 const xAOD::JetConstituent* aip = (*aItr);
280 const xAOD::IParticle* aipraw = aip->rawConstituent();
281 const xAOD::TruthParticle* pp =
282 dynamic_cast<const xAOD::TruthParticle*>(aipraw);
283 if( pp ) {
284 if( pp->pt()>m_jetConstPtCut && !MC::isPhoton(pp) ){
285 uidJetConst.push_back( HepMC::uniqueID(pp) );
286 }
287 if( pp->pt()>m_jetPhotonPtCut && !MC::isPhoton(pp) ){
288 uidJetConst.push_back( HepMC::uniqueID(pp) );
289 }
290 } else {
291 ATH_MSG_WARNING("Bad cast for particle in jet " <<ajet->index());
292 }
293 }
294 }
295
296 for (const auto* pItr : *inTruthParts) {
297 int uid = HepMC::uniqueID(pItr);
298 bool isJet = false;
299 for(unsigned int i=0; i<uidJetConst.size(); ++i){
300 if( uid == uidJetConst[i] ){
301 isJet = true;
302 break;
303 }
304 }
305 if( isJet ){
306 if( doPrint ) ATH_MSG_DEBUG("ParticleMask isJet " <<uid);
307 partMask[ pItr->index() ] = true;
308 }
309 }
310
311 } //end optional jets
312
313
314 // Execute the thinning service based on the mask. Finish.
315 inTruthParts.keep (partMask);
316 inTruthVerts.keep (vertMask);
317
318 // Final statistics
319 int outPartNum = 0;
320 for(unsigned int i=0; i<partMask.size(); ++i){
321 if( partMask[i] ) ++outPartNum;
322 }
323 int outVertNum = 0;
324 for(unsigned int i=0; i<vertMask.size(); ++i){
325 if( vertMask[i] ) ++outVertNum;
326 }
327
328 ATH_MSG_DEBUG("Final particles/vertices = " <<outPartNum <<" " <<outVertNum);
329
330 if( doPrint ){
331 std::cout <<"======================================================================================" <<std::endl;
332 std::cout <<"HardTruthThinning complete for event " <<evtNum <<std::endl;
333 std::cout <<"Saved " <<outPartNum <<" particles" <<std::endl;
334 std::cout <<"Particle unique IDs = ";
335 for(unsigned int i=0; i<partMask.size(); ++i){
336 if( partMask[i] ) std::cout << HepMC::uniqueID((*inTruthParts)[i]) <<" ";
337 }
338 std::cout <<std::endl;
339
340 std::cout <<"Saved " <<outVertNum <<" vertices" <<std::endl;
341 std::cout <<"Vertex unique IDs = ";
342 for(unsigned int i=0; i<vertMask.size(); ++i){
343 if( vertMask[i] ) std::cout << HepMC::uniqueID((*inTruthVerts)[i]) <<" ";
344 }
345 std::cout <<std::endl;
346 std::cout <<"======================================================================================" <<std::endl;
347 }
348
349 return StatusCode::SUCCESS;
350
351}
352
353
355// Utility functions
357
358// Emulate HepMC descendant iterator
359// Multiple particles can give same descendant (string/cluster)
360// Remove Geant descendants
361// MUST check ElementLink validity with isValid() for thinned samples
362
364 const xAOD::TruthParticle* p,
365 std::vector<const xAOD::TruthParticle*>& descendants ) {
366 descendants.clear();
367 if( ! (p->hasDecayVtx()) ) return 0;
368 const xAOD::TruthVertex* dvtx = p->decayVtx();
369 if( !dvtx ) return 0;
370 if( dvtx->nOutgoingParticles() == 0 ) return 0;
371 if(HepMC::is_simulation_vertex(dvtx)) return 0;
372 const std::vector< ElementLink< xAOD::TruthParticleContainer > >& outPart =
373 dvtx->outgoingParticleLinks();
374 for(unsigned int k=0; k<outPart.size(); ++k){
375 if( ! (outPart[k]).isValid() ) continue;
376 const xAOD::TruthParticle* kid = *(outPart[k]);
377 descendants.push_back(kid);
378 }
379
380 int nstart = 0;
381 int nstop = descendants.size();
382
383 while( nstop > nstart ){
384 for(int i=nstart; i<nstop; ++i){
385 const xAOD::TruthParticle* pp = descendants[i];
386 if( ! (pp->hasDecayVtx()) ) continue;
387 const xAOD::TruthVertex* vpp = pp->decayVtx();
388 if( !vpp ) continue;
389 if( vpp->nOutgoingParticles() == 0 ) continue;
390 if( HepMC::is_simulation_vertex(vpp)) continue;
391 const std::vector< ElementLink< xAOD::TruthParticleContainer > >&
392 outPart2 = vpp->outgoingParticleLinks();
393 for(unsigned int k=0; k<outPart2.size(); ++k){
394 if( ! (outPart2[k]).isValid() ) continue;
395 const xAOD::TruthParticle* kpp = *(outPart2[k]);
396 if( HepMC::is_simulation_particle(kpp)) continue;
397 bool isIn = false;
398 for(unsigned int kk=0; kk<descendants.size(); ++kk){
399 if(kpp==descendants[kk]) isIn = true;
400 }
401 if( !isIn ) descendants.push_back(kpp);
402 }
403 }
404 nstart = nstop;
405 nstop = descendants.size();
406 }
407
408 return nstop;
409}
410
411// Print xAODTruth Event. The printout is particle oriented, unlike
412// the HepMC particle/vertex printout. Geant and pileup particles are
413// omitted.
414
416 const xAOD::TruthParticleContainer* truths) {
417
418 std::vector<int> uidPars;
419 std::vector<int> uidKids;
420
421 std::cout <<"======================================================================================\n" ;
422 std::cout <<"xAODTruth Event " <<evnum <<"\n";
423 std::cout <<" Unique ID PDG Id Status px(GeV) py(GeV) pz(GeV) E(GeV) Parent: Decay\n" ;
424 std::cout <<" -----------------------------------------------------------------------------------\n" ;
425
426 for (const auto* tpItr : *truths) {
427 if (HepMC::is_simulation_particle(tpItr)) continue;
428 int uid = HepMC::uniqueID(tpItr);
429 int id = tpItr->pdgId();
430 int stat = tpItr->status();
431 float px = tpItr->px()/GeV;
432 float py = tpItr->py()/GeV;
433 float pz = tpItr->pz()/GeV;
434 float e = tpItr->e()/GeV;
435 uidPars.clear();
436 uidKids.clear();
437
438 if( tpItr->hasProdVtx() ){
439 const xAOD::TruthVertex* pvtx = tpItr->prodVtx();
440 if( pvtx ){
441 const std::vector< ElementLink< xAOD::TruthParticleContainer > >& pars =
442 pvtx->incomingParticleLinks();
443 for(unsigned int k=0; k<pars.size(); ++k){
444 if( ! (pars[k]).isValid() ) continue;
445 const xAOD::TruthParticle* par = *(pars[k]);
446 uidPars.push_back(HepMC::uniqueID(par));
447 }
448 }
449 }
450 if( tpItr->hasDecayVtx() ){
451 const xAOD::TruthVertex* dvtx = tpItr->decayVtx();
452 if( dvtx ){
453 const std::vector< ElementLink< xAOD::TruthParticleContainer > >& kids =
454 dvtx->outgoingParticleLinks();
455 for(unsigned int k=0; k<kids.size(); ++k){
456 if( ! (kids[k]).isValid() ) continue;
457 const xAOD::TruthParticle* kid = *(kids[k]);
458 uidKids.push_back(HepMC::uniqueID(kid));
459 }
460 }
461 }
462
463 std::cout << std::format("{:>10}{:>12}{:>8}{:>10.2f}{:>10.2f}{:>10.2f}{:>10.2f} P: ",
464 uid, id, stat, px, py, pz, e);
465 for(unsigned int k=0; k<uidPars.size(); ++k){
466 std::cout <<uidPars[k] <<" ";
467 }
468 std::cout <<" D: ";
469 for(unsigned int k=0; k<uidKids.size(); ++k){
470 std::cout <<uidKids[k] <<" ";
471 }
472 std::cout <<"\n";
473 }
474 std::cout <<"======================================================================================" <<std::endl;
475}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Helpers for checking error return status codes and reporting errors.
ATLAS-specific HepMC functions.
Handle for requesting thinning for a data object.
SG::ThinningHandleKey< xAOD::TruthVertexContainer > m_truthVertexName
virtual StatusCode initialize() override
static void printxAODTruth(long long evnum, const xAOD::TruthParticleContainer *truths)
HardTruthThinning(const std::string &t, const std::string &n, const IInterface *p)
SG::ThinningHandleKey< xAOD::TruthParticleContainer > m_truthParticleName
virtual StatusCode finalize() override
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfoKey
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_hardParticleKey
virtual StatusCode doThinning(const EventContext &ctx) const override
SG::ReadHandleKey< xAOD::JetContainer > m_truthJetsKey
static int getDescendants(const xAOD::TruthParticle *p, std::vector< const xAOD::TruthParticle * > &d)
virtual bool isValid() override final
Can the handle be successfully dereferenced?
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.
Class providing the definition of the 4-vector interface.
A vector of jet constituents at the scale used during jet finding.
iterator begin() const
iterator on the first constituent
iterator end() const
iterator after the last constituent
4-vector of jet constituent at the scale used during jet finding.
const IParticle * rawConstituent() const
Access the real underlying IParticle.
const TruthVertex_v1 * decayVtx() const
The decay vertex of this particle.
bool hasDecayVtx() const
Check for a decay vertex on this particle.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
const TPLinks_t & outgoingParticleLinks() const
Get all the outgoing particles.
size_t nOutgoingParticles() const
Get the number of outgoing particles.
const TPLinks_t & incomingParticleLinks() const
Get all the incoming particles.
int r
Definition globals.cxx:22
bool is_simulation_vertex(const T &v)
Method to establish if the vertex was created during simulation (TODO migrate to be based on status).
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 isPhoton(const T &p)
bool isElectron(const T &p)
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
bool isMuon(const T &p)
bool isTau(const T &p)
Definition index.py:1
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
TruthParticle_v1 TruthParticle
Typedef to implementation.
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.