ATLAS Offline Software
Loading...
Searching...
No Matches
TruthClassificationTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5
10
13
14#include <set>
15
16namespace
17{
18 bool isInSet(int origin, const std::set<int> &s)
19 {
20 return s.find(origin) != s.end();
21 }
22}
23
24
26 : asg::AsgTool(type)
27{
28 declareProperty ("separateChargeFlipElectrons", m_separateChargeFlipElectrons, "separate prompt charge-flipped electrons");
29 declareProperty ("separateChargeFlipMuons", m_separateChargeFlipMuons, "separate prompt charge-flipped muons");
30 declareProperty ("useTruthParticleDecorations", m_useTruthParticleDecorations, "use truth particle decorations");
31}
32
33
35 unsigned int &classification) const
36{
37 Truth::Type type = Truth::Type::Unknown;
38 ANA_CHECK(classify(particle, type));
39 classification = static_cast<int>(type);
40 return StatusCode::SUCCESS;
41}
42
43
45 Truth::Type &classification) const
46{
47 const xAOD::TruthParticle *truthParticle = dynamic_cast<const xAOD::TruthParticle *> (&particle);
48 if (dynamic_cast<const xAOD::Electron *> (&particle) || (truthParticle != nullptr && MC::isElectron(truthParticle)))
49 {
50 ANA_CHECK(classifyElectron(particle, classification));
51 }
52 else if (dynamic_cast<const xAOD::Muon *> (&particle) || (truthParticle != nullptr && MC::isMuon(truthParticle)))
53 {
54 ANA_CHECK(classifyMuon(particle, classification));
55 }
56 else
57 {
58 ANA_MSG_ERROR("Only electrons and muons are supported.");
59 return StatusCode::FAILURE;
60 }
61
62 return StatusCode::SUCCESS;
63}
64
65
67 Truth::Type &classification) const
68{
69
70
71 // Check if xAOD::TruthParticle or if not if it has the TruthParticleLink
72 const xAOD::TruthParticle *truthParticle = dynamic_cast<const xAOD::TruthParticle *> (&electron);
73 bool isTruthParticle{};
74 if (truthParticle == nullptr)
75 {
76 // need to find the truth particle
77 truthParticle = xAOD::TruthHelpers::getTruthParticle(electron);
78 }
79 else
80 {
81 isTruthParticle = true;
82 }
83
84 if (!m_truthPdgId.isAvailable(electron) && !isTruthParticle)
85 {
86 ANA_MSG_ERROR("Electron does not have the 'truthPdgId' decoration.");
87 return StatusCode::FAILURE;
88 }
89
90 if (!m_firstMotherTruthType.isAvailable(electron)
91 || !m_firstMotherTruthOrigin.isAvailable(electron)
92 || !m_firstMotherPdgId.isAvailable(electron))
93 {
94 ANA_MSG_ERROR("Electron does not have one or more 'firstEgMother' decorations.");
95 return StatusCode::FAILURE;
96 }
97
98 int type = isTruthParticle ? m_classifierParticleType(electron) : m_truthType(electron);
99 int origin = isTruthParticle ? m_classifierParticleOrigin(electron) : m_truthOrigin(electron);
100 int pdgId = isTruthParticle ? truthParticle->pdgId() : m_truthPdgId(electron);
101 if (m_useTruthParticleDecorations && !isTruthParticle)
102 {
103 type = m_classifierParticleType(*truthParticle);
104 origin = m_classifierParticleOrigin(*truthParticle);
105 }
106
107 int firstMotherType = m_firstMotherTruthType(electron);
108 int firstMotherOrigin = m_firstMotherTruthOrigin(electron);
109 int firstMotherPdgId = m_firstMotherPdgId(electron);
110 // not in the smart slimming list, thus only in few derivations
111 int lastMotherType = m_lastMotherTruthType.isAvailable(electron) ? m_lastMotherTruthType(electron) : -1;
112 int lastMotherOrigin = m_lastMotherTruthOrigin.isAvailable(electron) ? m_lastMotherTruthOrigin(electron) : -1;
113 int lastMotherPdgId = m_lastMotherPdgId.isAvailable(electron) ? m_lastMotherPdgId(electron) : -1;
114 // fallback recorations
115 int fallbackType{-1};
116 if (m_fallbackTruthType.isAvailable(electron) && m_fallbackDR.isAvailable(electron))
117 {
118 fallbackType = m_fallbackDR(electron) < 0.05 ? m_fallbackTruthType(electron) : -1;
119 }
120
121 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
122 // Prompt Photon Conversions
123 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
124
125 // gamma -> e+ e-
128 && firstMotherType == MCTruthPartClassifier::IsoPhoton && firstMotherOrigin == MCTruthPartClassifier::PromptPhot)
129 {
130 classification = Truth::Type::PromptPhotonConversion;
131 return StatusCode::SUCCESS;
132 }
133
134 // H -> gamma gamma, gamma -> e+ e-
136 && firstMotherType == MCTruthPartClassifier::IsoPhoton && firstMotherOrigin == MCTruthPartClassifier::Higgs)
137 {
138 classification = Truth::Type::PromptPhotonConversion;
139 return StatusCode::SUCCESS;
140 }
141
142 // bkg electrons from bkg photons
144 && firstMotherType == MCTruthPartClassifier::UnknownPhoton && firstMotherOrigin == MCTruthPartClassifier::NonDefined)
145 {
146 classification = Truth::Type::PromptPhotonConversion;
147 return StatusCode::SUCCESS;
148 }
149
150 // bkg photon from UndrPhot; (Here there is a generator level photon (not gen electron ) that later converts)
153 && firstMotherType == MCTruthPartClassifier::BkgPhoton && firstMotherOrigin == MCTruthPartClassifier::UndrPhot)
154 {
155 classification = Truth::Type::PromptPhotonConversion;
156 return StatusCode::SUCCESS;
157 }
158
159 // type = 16 and origin = 38 (again, this is a photon)
161 {
162 classification = Truth::Type::PromptPhotonConversion;
163 return StatusCode::SUCCESS;
164 }
165
166 // Is an isolated photon
167 if (type == MCTruthPartClassifier::IsoPhoton && pdgId == 22)
168 {
169 classification = Truth::Type::PromptPhotonConversion;
170 return StatusCode::SUCCESS;
171 }
172
173 // electrons from ElMagProc
174 // when FSR, a better classification can be made with the fall back vars
176 && firstMotherType == MCTruthPartClassifier::UnknownPhoton && firstMotherOrigin == MCTruthPartClassifier::NonDefined)
177 {
178 classification = Truth::Type::PromptPhotonConversion;
179 return StatusCode::SUCCESS;
180 }
182 && firstMotherType == MCTruthPartClassifier::NonIsoPhoton && firstMotherOrigin == MCTruthPartClassifier::FSRPhot)
183 {
184 classification = Truth::Type::PromptPhotonConversion;
185 return StatusCode::SUCCESS;
186 }
187
188 // TODO: Message from Otilia: """
189 // but it's not clear if these electrons are really
190 // "fakes" or they should go in the real category (we don't know from where
191 // this photon is coming...). I would say more truth studies should be done.
192 // """
193 // Hence the warning message...
195 && firstMotherType == MCTruthPartClassifier::Unknown && firstMotherOrigin == MCTruthPartClassifier::ZBoson)
196 {
197 ANA_MSG_WARNING("Electron identified as from a PromptPhotonConversion, "
198 "but this type of electron needs further study!");
199 classification = Truth::Type::PromptPhotonConversion;
200 return StatusCode::SUCCESS;
201 }
202
203 // when always a photon (last mum is a photon, even if the truth PDG is 11 and first mum PDG is 11 ):
204 // very likely these are internal conversions; last_mum_pdgId == 22 important as the cases with last_mum_pdgId == 11 were found to be quite often close to a true electron
207 && std::abs(firstMotherPdgId) == 11 && std::abs(pdgId) == 11)
208 { // electron
209 if(lastMotherType == -1 || (lastMotherType == MCTruthPartClassifier::GenParticle && (lastMotherPdgId == 22 || std::abs(lastMotherPdgId) == 11)))
210 {
211 // lastMotherType == -1 ==> when the last mother info is not stored in the derivations
212 classification = Truth::Type::PromptPhotonConversion;
213 return StatusCode::SUCCESS;
214 }
215 }
216
217
218 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
219 // Is muon reco as electron or ele radiated by muons
220 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
221
223 && (origin == MCTruthPartClassifier::Mu || firstMotherOrigin == MCTruthPartClassifier::Mu))
224 {
225 classification = Truth::Type::ElectronFromMuon;
226 return StatusCode::SUCCESS;
227 }
228
230 {
231 classification = Truth::Type::ElectronFromMuon;
232 return StatusCode::SUCCESS;
233 }
234
236 classification = Truth::Type::ElectronFromMuon;
237 return StatusCode::SUCCESS;
238 }
239
240
241 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
242 // Tau decays
243 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
244
245 // Non-isolated electron/photon from tau decay
248 {
249 classification = Truth::Type::TauDecay;
250 return StatusCode::SUCCESS;
251 }
252
253 // tau -> tau gamma, gamma -> e+ e-, etc
254 if ((firstMotherType == MCTruthPartClassifier::NonIsoElectron || firstMotherType == MCTruthPartClassifier::NonIsoPhoton)
255 && firstMotherOrigin == MCTruthPartClassifier::TauLep)
256 {
257 classification = Truth::Type::TauDecay;
258 return StatusCode::SUCCESS;
259 }
260
261
262 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
263 // Light hadron sources
264 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
265
267 {
268 classification = Truth::Type::LightFlavorDecay;
269 return StatusCode::SUCCESS;
270 }
271
272 if (firstMotherType == MCTruthPartClassifier::BkgElectron)
273 {
275 && (hasLightHadronOrigin(origin) || hasLightHadronOrigin(firstMotherOrigin)))
276 {
277 classification = Truth::Type::LightFlavorDecay;
278 return StatusCode::SUCCESS;
279 }
280 }
281
283 {
284 if (origin == MCTruthPartClassifier::DalitzDec || firstMotherOrigin == MCTruthPartClassifier::DalitzDec) {
285 classification = Truth::Type::LightFlavorDecay;
286 return StatusCode::SUCCESS;
287 }
288 if (hasLightHadronOrigin(origin) || hasLightHadronOrigin(firstMotherOrigin)) {
289 classification = Truth::Type::LightFlavorDecay;
290 return StatusCode::SUCCESS;
291 }
292 }
293
295 && (hasLightHadronOrigin(origin) || hasLightHadronOrigin(firstMotherOrigin)))
296 {
297 classification = Truth::Type::LightFlavorDecay;
298 return StatusCode::SUCCESS;
299 }
300
301
302 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
303 // From B hadron
304 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
305
306 if (hasBHadronOrigin(origin) || hasBHadronOrigin(firstMotherOrigin))
307 {
308 classification = Truth::Type::BHadronDecay;
309 return StatusCode::SUCCESS;
310 }
311
312 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
313 // From C hadron
314 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
315
316 if (type != MCTruthPartClassifier::IsoElectron && (hasCHadronOrigin(origin) || hasCHadronOrigin(firstMotherOrigin)))
317 {
318 classification = Truth::Type::CHadronDecay;
319 return StatusCode::SUCCESS;
320 }
321
322
323 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
324 // Prompt / Isolated electrons
325 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
326
327 if (isPromptElectron(electron, isTruthParticle, truthParticle))
328 {
329 if (m_separateChargeFlipElectrons && isChargeFlipElectron(electron, isTruthParticle, truthParticle))
330 {
331 classification = Truth::Type::ChargeFlipIsoElectron;
332 return StatusCode::SUCCESS;
333 }
334
335 classification = Truth::Type::IsoElectron;
336 return StatusCode::SUCCESS;
337 }
338
339
340 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
341 // Unknown & known Unknown
342 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
343
344 // TODO: See if we want this or not. Now we check if this is something we are
345 // able to classify or not. Note that this might be a bit dangerous because
346 // the reasons for not having origin and status codes might be complex. The
347 // main idea is to weed out things we don't have a hope of classifying due to
348 // missing or unknown information.
349 static const SG::ConstAccessor<int> statusAcc("status");
350 bool stable = (truthParticle != nullptr && statusAcc.isAvailable(*truthParticle)) ? MC::isStable(truthParticle) : false;
351
352
353 if (origin == MCTruthPartClassifier::NonDefined && firstMotherOrigin == MCTruthPartClassifier::NonDefined)
354 {
355 if (!stable)
356 {
358 {
359 classification = Truth::Type::KnownUnknown;
360 return StatusCode::SUCCESS;
361 }
362 } else {
365 {
366 classification = Truth::Type::KnownUnknown;
367 return StatusCode::SUCCESS;
368 }
369
371 {
372 classification = Truth::Type::KnownUnknown;
373 return StatusCode::SUCCESS;
374 }
375 }
376 }
377
378 // non-iso photons with no info available to classify
380 && firstMotherType == 0 && firstMotherOrigin == 0 && firstMotherPdgId == 0)
381 {
382 if (lastMotherType == -1 || (lastMotherType == 0 && lastMotherOrigin == 0 && lastMotherPdgId == 0))
383 { // last_firstMotherType == -1 ==> when the last_mum info is not stored in the derivations
384 classification = Truth::Type::KnownUnknown;
385 return StatusCode::SUCCESS;
386 }
387 }
388
389 ANA_MSG_WARNING("Electron type unknown: type = " << type << ", origin = " << origin);
390
391 // debug printout
392 if (truthParticle != nullptr)
393 {
394 const xAOD::TruthParticle *parent = truthParticle;
395 ATH_MSG_DEBUG("Unknown particle decay chain (approximate):");
396 std::string out = "\t";
397 while (parent != nullptr)
398 {
399 out.append(std::to_string(parent->pdgId()));
400 parent = parent->parent(0); //AV: Note the ambiguity: only the first particle is considered.
401 if (parent) out.append(" -> ");
402 }
403 ATH_MSG_DEBUG(out);
404 }
405
406 classification = Truth::Type::Unknown;
407 return StatusCode::SUCCESS;
408}
409
410
412 Truth::Type &classification) const
413{
414
415
416 // Check if xAOD::TruthParticle or if not if it has the TruthParticleLink
417 const xAOD::TruthParticle *truthParticle
418 = dynamic_cast<const xAOD::TruthParticle *> (&muon);
419 bool isTruthParticle{};
420 if (truthParticle == nullptr)
421 {
422 // need to find the truth particle
423 truthParticle = xAOD::TruthHelpers::getTruthParticle(muon);
424 }
425 else
426 {
427 isTruthParticle = true;
428 }
429
430 int type = isTruthParticle ? m_classifierParticleType(muon) : m_truthType(muon);
431 int origin = isTruthParticle ? m_classifierParticleOrigin(muon) : m_truthOrigin(muon);
432 if (m_useTruthParticleDecorations && !isTruthParticle && truthParticle != nullptr)
433 {
434 type = m_classifierParticleType(*truthParticle);
435 origin = m_classifierParticleOrigin(*truthParticle);
436 }
437
438 // fallback recorations
439 int fallbackType{-1};
440 if (m_fallbackTruthType.isAvailable(muon) && m_fallbackDR.isAvailable(muon))
441 {
442 fallbackType = m_fallbackDR(muon) < 0.05 ? m_fallbackTruthType(muon) : -1;
443 }
444
445 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
446 // muons from taus
447 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
448
450 {
451 classification = Truth::Type::TauDecay;
452 return StatusCode::SUCCESS;
453 }
454
455
456 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
457 // Light hadron sources
458 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
459
461 {
462 classification = Truth::Type::LightFlavorDecay;
463 return StatusCode::SUCCESS;
464 }
465
467 {
468 classification = Truth::Type::LightFlavorDecay;
469 return StatusCode::SUCCESS;
470 }
471
472
473 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
474 // From B hadron
475 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
476
477 if (hasBHadronOrigin(origin))
478 {
479 classification = Truth::Type::BHadronDecay;
480 return StatusCode::SUCCESS;
481 }
482
483 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
484 // From C hadron
485 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
486
488 {
489 classification = Truth::Type::CHadronDecay;
490 return StatusCode::SUCCESS;
491 }
492 // TODO:: There is a comment in the example code about J/psi but there is a
493 // separate origin code for that: `MCTruthPartClassifier::JPsi == 28.` --> this might not be in all samples/generators?
494
495
496 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
497 // prompt muons
498 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
499 // Check if the type of muon is IsoMuon(6) and whether the origin
500 // of the muon is from a prompt source
501 static const std::set<int> promptOrigin({
502 MCTruthPartClassifier::SingleMuon, // Single muon (origin = 2) from muon twiki
510 MCTruthPartClassifier::CCbarMeson, // PromptQuarkoniumDecay
514 });
515 if (type == MCTruthPartClassifier::IsoMuon && isInSet(origin, promptOrigin))
516 {
517 //separate charge-flip muons
518 if (m_separateChargeFlipMuons && isChargeFlipMuon(muon, isTruthParticle, truthParticle))
519 {
520 classification = Truth::Type::ChargeFlipMuon;
521 return StatusCode::SUCCESS;
522 }
523 classification = Truth::Type::PromptMuon;
524 return StatusCode::SUCCESS;
525 }
526
527
528 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
529 // Known Unknown
530 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
531
533 {
534 classification = Truth::Type::KnownUnknown;
535 return StatusCode::SUCCESS;
536 }
537
538 static const SG::ConstAccessor<int> statusAcc("status");
539 bool stable = (truthParticle != nullptr && statusAcc.isAvailable(*truthParticle)) ? MC::isStable(truthParticle) : false;
540
541 if (!stable) {
543 { // Data
544 classification = Truth::Type::KnownUnknown;
545 return StatusCode::SUCCESS;
546 }
547 if (type == -99999 && origin == -99999)
548 { // MC - no status = 1 truth particle associated with the primary track
549 classification = Truth::Type::KnownUnknown;
550 return StatusCode::SUCCESS;
551 }
552 }
553
554 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
555 // NonMuonlike
556 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
557 // Check if the type of muon is related to electrons, ie IsoElectron (2) or NonIsoElectron (3) or BkgElectron (4)
559 {
560 classification = Truth::Type::NonMuonlike;
561 return StatusCode::SUCCESS;
562 }
563
564 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
565 // TauLike, BHadLike, CHadLike
566 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
567 // Check if the matched parent of truth muon fall into these 3 xxxLike
568 // Only truth muon with one matched parent is considered
569 // If parent is tau, the muons is TauLike
570 // If parent is bhadron/b quark, the muons is BHadLike
571 // If parent is chadron/c quark, the muons is CHadLike
573 if( truthParticle != nullptr && truthParticle->nParents() == 1 && truthParticle->parent(0) != nullptr){
574 const xAOD::TruthParticle *parent = truthParticle->parent(0);
575 int parent_pdgid = parent->pdgId();
576 if(parent->isTau()){
577 classification = Truth::Type::TauDecayLike;
578 ATH_MSG_WARNING("Muon type taudecaylike: type = " << type << ", origin = " << origin << ", parent = " << parent_pdgid) ;
579 return StatusCode::SUCCESS;
580 }
581 if(parent->isBottomHadron() || parent->hasBottom()){
582 classification = Truth::Type::BHadronDecayLike;
583 ATH_MSG_WARNING("Muon type bhadrondecaylike: type = " << type << ", origin = " << origin << ", parent = " << parent_pdgid) ;
584 return StatusCode::SUCCESS;
585 }
586 if(parent->isCharmHadron() || parent->hasCharm()){
587 if( (parent_pdgid / 1000) % 10 != 0 || (parent_pdgid / 100) % 10 != 4 || (parent_pdgid / 10) % 10 != 4){ // to exclude ccbarmeson
588 classification = Truth::Type::CHadronDecayLike;
589 ATH_MSG_WARNING("Muon type chadrondecaylike: type = " << type << ", origin = " << origin << ", parent = " << parent_pdgid) ;
590 return StatusCode::SUCCESS;
591 }
592 }
593 }
594 }
595
596 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
597 // Unknown
598 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
599 classification = Truth::Type::Unknown;
600 ATH_MSG_WARNING("Muon type unknown: type = " << type << ", origin = " << origin) ;
601
602 return StatusCode::SUCCESS;
604}
605
607 bool isTruthParticle,
608 const xAOD::TruthParticle *truthParticle) const
609{
610
611
612 int type = isTruthParticle ? m_classifierParticleType(electron) : m_truthType(electron);
613 int origin = isTruthParticle ? m_classifierParticleOrigin(electron) : m_truthOrigin(electron);
614 if (m_useTruthParticleDecorations && !isTruthParticle && truthParticle != nullptr)
615 {
616 type = m_classifierParticleType(*truthParticle);
617 origin = m_classifierParticleOrigin(*truthParticle);
618 }
619
620 // Electron is IsoElectron - return true
622 {
623 return true;
624 }
625
626 int pdgId = isTruthParticle ? truthParticle->pdgId() : m_truthPdgId(electron);
627 int firstMotherType = m_firstMotherTruthType(electron);
628 int firstMotherOrigin = m_firstMotherTruthOrigin(electron);
629
630 // Adding these cases from ElectronEfficiencyHelpers
631 if (firstMotherType == MCTruthPartClassifier::IsoElectron && std::abs(m_firstMotherPdgId(electron)) == 11)
632 {
633 return true;
634 }
635
636 // FSR photons from electrons
637 if (origin == MCTruthPartClassifier::FSRPhot && type == MCTruthPartClassifier::NonIsoPhoton && std::abs(pdgId) == 11)
638 {
639 return true;
640 }
641
643 && firstMotherType == MCTruthPartClassifier::NonIsoPhoton && firstMotherOrigin == MCTruthPartClassifier::FSRPhot
644 && std::abs(pdgId) == 11)
645 {
646 return true;
647 }
648
649 // If we reach here then it is not a prompt electron
650 return false;
651}
652
653
655 bool isTruthParticle,
656 const xAOD::TruthParticle *truthParticle) const
657{
658
659
660 int type = isTruthParticle ? m_classifierParticleType(electron) : m_truthType(electron);
661 int origin = isTruthParticle ? m_classifierParticleOrigin(electron) : m_truthOrigin(electron);
662 int pdgId = isTruthParticle && truthParticle ? truthParticle->pdgId() : m_truthPdgId(electron);
663 if (m_useTruthParticleDecorations && !isTruthParticle && truthParticle != nullptr)
664 {
665 type = m_classifierParticleType(*truthParticle);
666 origin = m_classifierParticleOrigin(*truthParticle);
667 }
668
669 int firstMotherType = m_firstMotherTruthType(electron);
670 int firstMotherOrigin = m_firstMotherTruthOrigin(electron);
671 int firstMotherPdgId = m_firstMotherPdgId(electron);
672
673 // not consider FSR photons from electrons (the photon has no charge)
674 if (origin == MCTruthPartClassifier::FSRPhot && type == MCTruthPartClassifier::NonIsoPhoton && std::abs(pdgId) == 11)
675 {
676 return false;
677 }
679 && firstMotherType == MCTruthPartClassifier::NonIsoPhoton && firstMotherOrigin == MCTruthPartClassifier::FSRPhot
680 && std::abs(pdgId) == 11)
681 {
682 return false;
683 }
684
685 // bkg electrons with no additional info to help us classify them FSR -- not in the charge flip category
687 && firstMotherType == MCTruthPartClassifier::BkgElectron && firstMotherOrigin == MCTruthPartClassifier::PhotonConv
688 && std::abs(pdgId) == 11)
689 {
690 return false;
691 }
692
693 if (isTruthParticle)
694 {
695 if (truthParticle && truthParticle->charge() != 0)
696 {
697 return (firstMotherPdgId * truthParticle->charge()) > 0;
698 }
699 }
700 else
701 {
702 const xAOD::Electron &xAODElectron = *static_cast<const xAOD::Electron *> (&electron);
703 if (xAODElectron.charge() != 0)
704 {
705 return (firstMotherPdgId * xAODElectron.charge()) > 0;
706 }
707 }
708
709 return (firstMotherPdgId * (-pdgId)) > 0;
710}
711
712
714 bool isTruthParticle,
715 const xAOD::TruthParticle *truthParticle) const
716{
717 if (isTruthParticle)
718 {
719 return false;
720 }
721
722 if (truthParticle != nullptr && xAOD::P4Helpers::isInDeltaR(*truthParticle, muon, 0.025))
723 {
724 const xAOD::Muon &xAODMuon = *static_cast<const xAOD::Muon *> (&muon);
725 return (truthParticle->charge() * xAODMuon.charge()) < 0;
726 }
727
728 ANA_MSG_DEBUG("Cannot find associated truth-particle... assuming muon has correct charge");
729 return false;
730}
731
732
734{
735 static const std::set<int> b_hadrons({
739 });
740 return b_hadrons.contains(origin);
741}
742
743
745 static const std::set<int> c_hadrons({
749 });
750 return c_hadrons.contains(origin);
751}
752
753
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ANA_MSG_ERROR(xmsg,...)
Macro printing error messages.
#define ANA_MSG_DEBUG(xmsg,...)
Macro printing debug messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
#define ANA_MSG_WARNING(xmsg,...)
Macro printing warning messages.
ATLAS-specific HepMC functions.
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
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.
const SG::ConstAccessor< int > m_truthPdgId
bool m_separateChargeFlipElectrons
separately store charge-flip electrons/muons
const SG::ConstAccessor< int > m_lastMotherTruthOrigin
const SG::ConstAccessor< int > m_truthType
StatusCode classifyElectron(const xAOD::IParticle &electron, Truth::Type &classification) const
electron classification helper
const SG::ConstAccessor< float > m_fallbackDR
const SG::ConstAccessor< int > m_firstMotherTruthType
bool hasCHadronOrigin(int origin) const
a helper to check if the origin is a c-hadron
const SG::ConstAccessor< int > m_lastMotherPdgId
const SG::ConstAccessor< unsigned int > m_classifierParticleType
StatusCode classifyMuon(const xAOD::IParticle &muon, Truth::Type &classification) const
muon classification helper
const SG::ConstAccessor< int > m_fallbackTruthType
const SG::ConstAccessor< int > m_firstMotherPdgId
virtual StatusCode classify(const xAOD::IParticle &particle, unsigned int &classification) const override
classify and return unsigned int
bool isChargeFlipElectron(const xAOD::IParticle &electron, bool isTruthParticle, const xAOD::TruthParticle *truthParticle) const
a helper to check if an electron has an incorrectly reconstructed charge
bool hasBHadronOrigin(int origin) const
a helper to check if the origin is a b-hadron
TruthClassificationTool(const std::string &type)
bool isPromptElectron(const xAOD::IParticle &electron, bool isTruthParticle, const xAOD::TruthParticle *truthParticle) const
a helper to check if an electron is prompt
const SG::ConstAccessor< int > m_firstMotherTruthOrigin
bool isChargeFlipMuon(const xAOD::IParticle &muon, bool isTruthParticle, const xAOD::TruthParticle *truthParticle) const
a helper to check if a muon has an incorrectly reconstructed charge
const SG::ConstAccessor< int > m_truthOrigin
bool hasLightHadronOrigin(int origin) const
a helper to check if the origin is a light hadron
bool m_useTruthParticleDecorations
use truth particle decorations
const SG::ConstAccessor< unsigned int > m_classifierParticleOrigin
const SG::ConstAccessor< int > m_lastMotherTruthType
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
float charge() const
Obtain the charge of the object.
Class providing the definition of the 4-vector interface.
float charge() const
int pdgId() const
PDG ID code.
const TruthParticle_v1 * parent(size_t i) const
Retrieve the i-th mother (TruthParticle) of this TruthParticle.
double charge() const
Physical charge.
size_t nParents() const
Number of parents of this particle.
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)
Type
truth classification type enum
bool isInDeltaR(const xAOD::IParticle &p1, const xAOD::IParticle &p2, double dR, bool useRapidity=true)
Check if 2 xAOD::IParticle are in a cone.
const xAOD::TruthParticle * getTruthParticle(const xAOD::IParticle &p)
Return the truthParticle associated to the given IParticle (if any).
TruthParticle_v1 TruthParticle
Typedef to implementation.
Muon_v1 Muon
Reference the current persistent version:
Definition Muon.h:13
Electron_v1 Electron
Definition of the current "egamma version".
Definition Electron.h:17