ATLAS Offline Software
MCTruthClassifierGen.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 /*
6  * Implementation file that mainly contains the code logic
7  * dealing with Truth - record classification
8  * Contributors: Pierre-Antoine Delsart
9  * Andrii Verbytskyi <andrii.verbytskyi@mpp.mpg.de>
10  */
11 
15 #ifndef XAOD_ANALYSIS
16 #include "AtlasHepMC/GenEvent.h"
17 #include "AtlasHepMC/GenVertex.h"
18 #include "AtlasHepMC/GenParticle.h"
19 #endif
21 using namespace MCTruthPartClassifier;
22 using std::abs;
23 
24 #ifndef XAOD_ANALYSIS
25 std::pair<ParticleType, ParticleOrigin>
27  // Retrieve the links between HepMC and xAOD::TruthParticle
28  const EventContext& ctx = info ? info->eventContext : Gaudi::Hive::currentContext();
29  SG::ReadHandle<xAODTruthParticleLinkVector> truthParticleLinkVecReadHandle(m_truthLinkVecReadHandleKey, ctx);
30  if (!truthParticleLinkVecReadHandle.isValid()) {
31  ATH_MSG_WARNING(" Invalid ReadHandle for xAODTruthParticleLinkVector with key: " << truthParticleLinkVecReadHandle.key());
32  return std::make_pair(Unknown, NonDefined);
33  }
34  ElementLink<xAOD::TruthParticleContainer> tplink = truthParticleLinkVecReadHandle->find (theLink);
35  if (tplink.isValid()) {
36  return particleTruthClassifier (*tplink, info);
37  }
38  return std::make_pair(Unknown, NonDefined);
39 }
40 
41 std::pair<ParticleType, ParticleOrigin>
43  ParticleType partType = Unknown;
44  ParticleOrigin partOrig = NonDefined;
45 
46  if (!theGenPart) return std::make_pair(partType, partOrig);
47 
48  // Retrieve the links between HepMC and xAOD::TruthParticle
49  const EventContext& ctx = info ? info->eventContext : Gaudi::Hive::currentContext();
50 
51  SG::ReadHandle<xAODTruthParticleLinkVector> truthParticleLinkVecReadHandle(m_truthLinkVecReadHandleKey, ctx);
52  if (!truthParticleLinkVecReadHandle.isValid()) {
53  ATH_MSG_WARNING( " Invalid ReadHandle for xAODTruthParticleLinkVector with key: " << truthParticleLinkVecReadHandle.key());
54  return std::make_pair(partType, partOrig);
55  }
56  for (const auto *const entry : *truthParticleLinkVecReadHandle) {
57  if (entry->first.isValid() && entry->second.isValid() && HepMC::is_same_particle(entry->first,theGenPart)) {
58  const xAOD::TruthParticle* truthParticle = *entry->second;
59  if (!theGenPart || !truthParticle ||
60  theGenPart->pdg_id() != truthParticle->pdgId() ||
61  theGenPart->status() != truthParticle->status() ||
62  HepMC::is_same_particle(theGenPart,truthParticle)) {
64  "HepMC::GenParticle and xAOD::TruthParticle do not match");
65  return std::make_pair(partType, partOrig);
66  }
67  return particleTruthClassifier(truthParticle, info);
68  }
69  }
70  return std::make_pair(partType, partOrig);
71 }
72 #endif
73 
74 std::pair<ParticleType, ParticleOrigin>
77  MCTruthPartClassifier::Info& info = (infoin) ? *infoin : tmpinfo;
78 
79  ATH_MSG_DEBUG("Executing particleTruthClassifier");
80 
81  ParticleType partType = Unknown;
82  ParticleOrigin partOrig = NonDefined;
83  if (!thePart) {
84  return std::make_pair(partType, partOrig);
85  }
86  info.genPart = thePart;
87 
88  // retrieve collection and get a pointer
89  SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainerReadHandle(m_truthParticleContainerKey,info.eventContext);
90  if (!truthParticleContainerReadHandle.isValid()) {
91  ATH_MSG_WARNING( " Invalid ReadHandle for xAOD::TruthParticleContainer with key: " << truthParticleContainerReadHandle.key());
92  return std::make_pair(partType, partOrig);
93  }
94 
95  ATH_MSG_DEBUG("xAODTruthParticleContainer with key " << truthParticleContainerReadHandle.key() << " has valid ReadHandle ");
96 
97  if (!MC::isStable(thePart) && !MC::isDecayed(thePart)) {
98  return std::make_pair(GenParticle, partOrig);
99  }
100  const bool isPartHadr = MC::isHadron(thePart) && !MC::isBeam(thePart);
101  if (MC::isDecayed(thePart) && (!MC::isTau(thePart) && !isPartHadr)) return std::make_pair(GenParticle, partOrig);
102 
103  // SUSY datasets: tau(status==2)->tau(status==2)
104  if (MC::isDecayed(thePart) && MC::isTau(thePart)) {
105  const xAOD::TruthVertex* endVert = thePart->decayVtx();
106  if (endVert) {
107  if (endVert->nOutgoingParticles() == 1 && MC::isTau(endVert->outgoingParticle(0))) {
108  return std::make_pair(GenParticle, partOrig);
109  }
110  }
111  }
112 
113  if (MC::isStable(thePart) && MC::isSUSY(thePart)) return std::make_pair(SUSYParticle, partOrig);
114 
115  if (MC::isStable(thePart) && MC::isBSM(thePart)) return std::make_pair(OtherBSMParticle, partOrig);
116 
117  if (MC::isDecayed(thePart) &&
118  (!MC::isElectron(thePart) && !MC::isMuon(thePart) &&
119  !MC::isTau(thePart) && !MC::isPhoton(thePart)) &&
120  !isPartHadr)
121  return std::make_pair(GenParticle, partOrig);
122 
123  // FIXME vetoing protons here to preserve previous behaviour
124  if (MC::isNucleus(thePart) && std::abs(thePart->pdgId()) != MC::PROTON) return std::make_pair(NuclFrag, partOrig);
125 
126  if ( !MC::isSMLepton(thePart) && !MC::isPhoton(thePart) && !isPartHadr) return std::make_pair(partType, partOrig);
127  // don't consider generator particles
128 
129  const xAOD::TruthVertex* partProdVtx = thePart->hasProdVtx() ? thePart->prodVtx() : nullptr;
130 
131  const xAOD::TruthParticle* parent{};
132  if (partProdVtx) {
133  for (const auto& temp: partProdVtx->particles_in()) {if (temp) parent = temp;}
134  }
135  const int parentPDG = parent?parent->pdg_id():0;
136  info.setMotherProperties(parent);
137 
138  if (!partProdVtx && HepMC::is_simulation_particle(thePart)) {
139  return std::make_pair(NonPrimary, partOrig);
140  }
141  if (!partProdVtx && MC::isElectron(thePart)) {
142  // to define electron outcome status
143  bool isPrompt = false; // updated by defOrigOfElectron
144  partOrig = defOrigOfElectron(*truthParticleContainerReadHandle, thePart, isPrompt, info);
145  return std::make_pair(UnknownElectron, partOrig);
146  }
147  if (!partProdVtx && MC::isMuon(thePart)) {
148  // to define electron outcome status
149  bool isPrompt = false; // updated by defOrigOfMuon
150  partOrig = defOrigOfMuon(*truthParticleContainerReadHandle, thePart, isPrompt, info);
151  return std::make_pair(UnknownMuon, partOrig);
152  }
153  if (!partProdVtx && MC::isTau(thePart)) {
154  // to define electron outcome status
155  partOrig = defOrigOfTau(*truthParticleContainerReadHandle, thePart, parentPDG, info);
156  return std::make_pair(UnknownTau, partOrig);
157  }
158  if (!partProdVtx && MC::isPhoton(thePart)) {
159  // to define photon outcome
160  bool isPrompt = false; // updated by defOrigOfPhoton
161  partOrig = defOrigOfPhoton(*truthParticleContainerReadHandle, thePart, isPrompt, info);
162  return std::make_pair(UnknownPhoton, partOrig);
163  }
164  if (!partProdVtx && MC::isNeutrino(thePart)) {
165  // to define neutrino outcome
166  info.particleOutCome = NonInteract;
167  return std::make_pair(Neutrino, partOrig);
168  }
169 
170  if (thePart && info.Mother() && HepMC::is_same_generator_particle(thePart,info.Mother()))
171  return std::make_pair(NonPrimary, partOrig);
172 
173  if (isPartHadr) return std::make_pair(Hadron, partOrig);
174 
175  if (partProdVtx && parentPDG == 0 && partProdVtx->nOutgoingParticles() == 1 &&
176  partProdVtx->nIncomingParticles() == 0) {
177  if (MC::isElectron(thePart)) {
178  info.particleOutCome = defOutComeOfElectron(thePart);
179  return std::make_pair(IsoElectron, SingleElec);
180  }
181  if (MC::isMuon(thePart)) {
182  info.particleOutCome = defOutComeOfMuon(thePart);
183  return std::make_pair(IsoMuon, SingleMuon);
184  }
185  if (MC::isTau(thePart)) {
186  info.particleOutCome = defOutComeOfTau(thePart);
187  return std::make_pair(IsoTau, SingleTau);
188  }
189  if (MC::isPhoton(thePart)) {
190  info.particleOutCome = defOutComeOfPhoton(thePart);
191  return std::make_pair(IsoPhoton, SinglePhot);
192  }
193  }
194 
195  if (parentPDG == thePart->pdg_id() && parent && parent->status() == 3 && MC::isDecayed(thePart)) return std::make_pair(GenParticle, partOrig);
196 
197  if (MC::isElectron(thePart)) {
198  bool isPrompt = false; // updated by defOrigOfElectron
199  partOrig = defOrigOfElectron(*truthParticleContainerReadHandle, thePart, isPrompt, info);
200  partType = defTypeOfElectron(partOrig, isPrompt);
201  } else if (MC::isMuon(thePart)) {
202  bool isPrompt = false; // updated by defOrigOfMuon
203  partOrig = defOrigOfMuon(*truthParticleContainerReadHandle, thePart, isPrompt, info);
204  partType = defTypeOfMuon(partOrig, isPrompt);
205  } else if (MC::isTau(thePart)) {
206  partOrig = defOrigOfTau(*truthParticleContainerReadHandle, thePart, parentPDG, info);
207  partType = defTypeOfTau(partOrig);
208  } else if (MC::isPhoton(thePart)) {
209  bool isPrompt = false; // updated by defOrigOfPhoton
210  partOrig = defOrigOfPhoton(*truthParticleContainerReadHandle, thePart, isPrompt, info);
211  partType = defTypeOfPhoton(partOrig);
212  } else if (MC::isNeutrino(thePart)) {
213  bool isPrompt = false; // updated by defOrigOfNeutrino
214  partOrig = defOrigOfNeutrino(*truthParticleContainerReadHandle, thePart, isPrompt, info);
215  partType = Neutrino;
216  }
217 
218  ATH_MSG_DEBUG("particleTruthClassifier succeeded ");
219  return std::make_pair(partType, partOrig);
220 }
221 
222 
224 {
225  // Start of method 1 of protecting against loops
226  const int parentPDG = parent->pdgId();
227  for (const auto& aChild: childOrigVtx->particles_out()) {
228  if (!aChild) continue;
229  if (parentPDG == aChild->pdgId() && HepMC::is_same_generator_particle(aChild, parent)) {
230  // One of the children produced in the decay of parent is
231  // actually the same particle. NB In the case of multiple
232  // children this child may not necessarily be
233  // thePriPart. Does this matter?
234  return true;
235  }
236  }
237 
238  // to resolve Sherpa loop
239  return TruthLoopDetectionMethod3(childOrigVtx, parent);
240  // End of method 1 of protecting against loops
241 }
242 
243 
245 {
246  // Start of method 2 of protecting against loops
247  // to prevent Sherpa loop
248  const xAOD::TruthVertex* child_prdVtx{};
249  const xAOD::TruthVertex* child_endVtx{};
250  if (child) {
251  child_prdVtx = child->hasProdVtx() ? child->prodVtx() : nullptr;
252  child_endVtx = child->decayVtx();
253  }
254  const xAOD::TruthVertex* parent_prdVtx{};
255  const xAOD::TruthVertex* parent_endVtx{};
256  if (parent) {
257  parent_prdVtx = parent->hasProdVtx() ? parent->prodVtx() : nullptr;
258  parent_endVtx = parent->decayVtx();
259  }
260  // V0->parent->V1-> ...->V2->child->V3
261  // V3 == V0 && V1 == V2
262  return (child_endVtx == parent_prdVtx && child_prdVtx == parent_endVtx);
263 }
264 
265 
267 {
268  // Start of method 3 of protecting against loops
269  // to resolve Sherpa loop
270  const xAOD::TruthVertex* parentOrigVtx = parent->hasProdVtx() ? parent->prodVtx() : nullptr;
271  if (parentOrigVtx && HepMC::is_same_vertex(parentOrigVtx,childOrigVtx)) {
272  // The "parent" and the "child" have the same production vertex.
273  return true;
274  }
275  return false;
276  // End of method 3 of protecting against loops
277 }
278 
279 
281  const xAOD::TruthParticle* thePart,
282  bool& isPrompt,
284 {
285  ATH_MSG_DEBUG("Executing DefOrigOfElectron ");
286 
287  // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
288  const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
289  if (!thePriPart) return NonDefined;
290  if (!MC::isElectron(thePriPart)) return NonDefined;
291 
292  //-- to define electron outcome status
293  info.particleOutCome = defOutComeOfElectron(thePriPart);
294 
295  const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
296  if (!partProdVtx) return NonDefined;
297 
298  if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfElectron:: electron has more than one parent.");
299 
300  const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
301  info.setMotherProperties(ancestor);
302  if (!ancestor) { return NonDefined; } // After this point "ancestor" cannot be nullptr
303 
304  // Start of method 1 of protecting against loops
305  bool samePart = TruthLoopDetectionMethod1(partProdVtx, ancestor);
306  // to resolve Sherpa loop
307  // End of method 1 of protecting against loops
308 
309  if ((MC::isMuon(ancestor) || MC::isTau(ancestor) || MC::isW(ancestor)) && ancestor->hasProdVtx() && !samePart) {
310  int pPDG(0);
311  const xAOD::TruthParticle* ancestorParent{};
312  do {
313  pPDG = 0; // reset pPDG
314  ancestorParent = MC::findMother(ancestor);
315  // Start of method 2 of protecting against loops
316  // to prevent Sherpa loop
317  if (ancestor == ancestorParent) { break; }
318  if (TruthLoopDetectionMethod2(ancestor,ancestorParent)) {
319  ancestorParent = ancestor;
320  break;
321  }
322  // End of method 2 of protecting against loops
323  // FIXME why are slightly different criteria used in method 1 and method 2???
324  if (ancestorParent) {
325  pPDG = ancestorParent->pdgId(); // Only set pPDG in the case that we aren't in a loop.
326  if (MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG)) { // There will be another iteration so set ancestor to ancestorParent
327  ancestor = ancestorParent; // ancestorParent is not nullptr here
328  }
329  }
330  } while ((MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG)));
331 
332  if (MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG) || MC::isZ(pPDG) || MC::isHiggs(pPDG) ||
333  MC::isMSSMHiggs(pPDG) || MC::isHeavyBoson(pPDG) || MC::isTop(pPDG) || // MSSM Higgs bosons, Heavy bosons( Z', Z'', W'+)
334  std::abs(pPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(pPDG) || // Left-right symmetric model WBoson || Right-handed neutrino (Pythia-specific)
335  MC::isSUSY(pPDG)) {
336  ancestor = ancestorParent; // ancestorParent is not nullptr here
337  }
338  }
339 
340  info.setMotherProperties(ancestor);
341  const int ancestorPDG = ancestor->pdgId();
342  const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
343  partProdVtx = ancestor->decayVtx();
344  const int numOfParents = partProdVtx->nIncomingParticles();
345  const int numberOfChildren = partProdVtx->nOutgoingParticles();
346 
347  // Determine decay products
348  auto DP = DecayProducts(partProdVtx);
349  const int NumOfPhot = DP.pd(MC::PHOTON);
350  const int NumOfEl = DP.pd(MC::ELECTRON);
351  const int NumOfPos = DP.pd(MC::POSITRON);
352  const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
353  const int NumOfgluon = DP.apd(MC::GLUON);
354  const int NumOfElNeut = DP.apd(MC::NU_E);
355  const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
356  const int NumOfMuPl = DP.pd(-MC::MUON);
357  const int NumOfMuMin = DP.pd(MC::MUON);
358  const int NumOfMuNeut = DP.apd(MC::NU_MU);
359  const int NumOfTau = DP.apd(MC::TAU);
360  const int NumOfTauNeut = DP.apd(MC::NU_TAU);
361 
362  samePart = false;
363  int NumOfNucFr(0);
364  const bool possibleNuclearFragment = (numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG) || MC::isMuon(ancestorPDG) || std::abs(ancestorPDG) == MC::PIPLUS));
365  for (const auto& aChild: partProdVtx->particles_out()) {
366  if (!aChild) continue;
367  const int childPDG = aChild->pdgId();
368  if (std::abs(childPDG) == std::abs(ancestorPDG) && HepMC::is_same_generator_particle(aChild, ancestor )) samePart = true;
369  if (possibleNuclearFragment &&
370  (MC::isNucleus(childPDG) || childPDG == 0 || childPDG == MC::PROTON || childPDG == MC::NEUTRON || // FIXME Do we really expect particles with PDG_ID = 0 in the truth record?
371  std::abs(childPDG) == MC::PIPLUS || std::abs(childPDG) == MC::PI0))
372  NumOfNucFr++;
373  }
374  // End of section determining decay products
375 
376  if (MC::isPhoton(ancestorPDG) && ancestorProdVtx) {
377  if (ancestorProdVtx->nIncomingParticles() > 1) { ATH_MSG_DEBUG("DefOrigOfElectron:: photon has more than one parent."); }
378  for (const auto& photonParent: ancestorProdVtx->particles_in()) {
379  if (!photonParent) continue;
380  info.photonMother = photonParent; // FIXME Just taking the first valid particle...
381  }
382  }
383 
384  if ((MC::isPhoton(ancestorPDG) && numberOfChildren == 2 && NumOfEl == 1 && NumOfPos == 1) || (MC::isPhoton(ancestorPDG) && numberOfChildren == 1 && (NumOfEl == 1 || NumOfPos == 1))) return PhotonConv;
385 
386  // e,gamma,pi+Nuclear->NuclearFragments+nuclons+e
387  if ((numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG) || MC::isTau(ancestorPDG))) && numberOfChildren > 1 && NumOfNucFr != 0) return ElMagProc;
388 
389  if (numOfParents == 1 && std::abs(ancestorPDG) == MC::PIPLUS && numberOfChildren > 2 && NumOfNucFr != 0) return ElMagProc;
390 
391  // nuclear photo fission
392  if (MC::isPhoton(ancestorPDG) && numberOfChildren > 4 && NumOfNucFr != 0) return ElMagProc;
393 
394  // unknown process el(pos)->el+pos??
395  if (MC::isElectron(ancestorPDG) && numberOfChildren == 2 && NumOfEl == 1 && NumOfPos == 1) return ElMagProc;
396 
397  // unknown process el->el+el??
398  if (ancestorPDG == MC::ELECTRON && numberOfChildren == 2 && NumOfEl == 2 && NumOfPos == 0) return ElMagProc;
399 
400  // unknown process pos->pos+pos??
401  if (ancestorPDG == MC::POSITRON && numberOfChildren == 2 && NumOfEl == 0 && NumOfPos == 2) return ElMagProc;
402 
403  // unknown process pos/el->pos/el??
404  if (MC::isElectron(ancestorPDG) && !MC::isDecayed(ancestor) && ancestorPDG == thePriPart->pdgId() && numberOfChildren == 1 && !samePart) return ElMagProc;
405 
406  // pi->pi+e+/e-; mu->mu+e+/e- ;
407  // gamma+ atom->gamma(the same) + e (compton scattering)
408  if (numberOfChildren == 2 && (NumOfEl == 1 || NumOfPos == 1) && !MC::isElectron(ancestorPDG) && samePart) return ElMagProc;
409 
410  if ((ancestorPDG == MC::PI0 && numberOfChildren == 3 && NumOfPhot == 1 && NumOfEl == 1 && NumOfPos == 1) ||
411  (ancestorPDG == MC::PI0 && numberOfChildren == 4 && NumOfPhot == 0 && NumOfEl == 2 && NumOfPos == 2))
412  return DalitzDec;
413 
414  // Quark weak decay
415  if (MC::isSMQuark(ancestorPDG) && numOfParents == 1 && numberOfChildren == 3 && NumOfquark == 1 && NumOfElNeut == 1) return QuarkWeakDec;
416 
417  if (MC::isMuon(ancestorPDG) && NumOfNucFr != 0) return ElMagProc;
418 
419  if (MC::isTop(ancestorPDG)) return top;
420 
421  if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
422 
423  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
424  const xAOD::TruthParticle* ptrPart{};
425  do {
426  ptrPart = prodVert->incomingParticle(0); // FIXME just taking the first one
427  prodVert = ptrPart->hasProdVtx() ? ptrPart->prodVtx() : nullptr;
428  } while (MC::isW(ptrPart) && prodVert);
429 
430  if (prodVert && prodVert->nIncomingParticles() == 1) {
431  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
432  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
433  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
434  }
435  return WBoson;
436  }
437  if (MC::isW(ancestorPDG)) return WBoson;
438  if (MC::isZ(ancestorPDG)) return ZBoson;
439 
440  // MadGraphPythia ZWW*->lllnulnu
441  if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
442 
443  const xAOD::TruthParticle* thePartToCheck = thePriPart;
444  const xAOD::TruthParticle* theParent = thePriPart->hasProdVtx() ? thePriPart->prodVtx()->incomingParticle(0) : nullptr; // FIXME just taking the first one
445  if (theParent && MC::isElectron(theParent) && MC::isDecayed(theParent)) { thePartToCheck = theParent; }
446 
447  bool isZboson = false;
448  bool isWboson = false;
449  bool skipnext = false;
450 
451  for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ++ipOut) {
452  const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
453  if (!aChild) continue;
454  const xAOD::TruthParticle* theNextChild = nullptr;
455  for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
456  theNextChild = partProdVtx->outgoingParticle(ipOut1);
457  if (theNextChild) break;
458  }
459  if (!theNextChild) continue;
460  if (skipnext) {
461  skipnext = false;
462  continue;
463  }
464 
465  if (MC::isElectron(aChild) && MC::isElectron(theNextChild)) {
466  // Zboson
467  if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
468  isZboson = true;
469  break;
470  }
471  skipnext = true;
472  } else if (MC::isElectron(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_E) {
473  // WBoson
474  if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
475  isWboson = true;
476  break;
477  }
478  skipnext = true;
479  }
480  }
481  if (isWboson) return WBoson;
482  if (isZboson) return ZBoson;
483  }
484  if (numOfParents == 2) {
485  //--Sherpa Z->ee
486  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfEl == 1 && NumOfPos == 1) return ZBoson;
487 
488  //--Sherpa W->enu ??
489  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && (NumOfEl == 1 || NumOfPos == 1) && NumOfElNeut == 1) return WBoson;
490 
491  const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
492  const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
493  //--Sherpa ZZ,ZW
494  if ((numberOfChildren - NumOfquark - NumOfgluon) == 4 &&
495  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
496  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
497 
498  //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
499  if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 &&
500  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
501  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
502  }
503 
504  // New Sherpa Z->ee
505  if (partProdVtx == ancestorProdVtx) {
506  int NumOfEleLoop = 0;
507  int NumOfLepLoop = 0;
508  int NumOfEleNeuLoop = 0;
509  for (const auto *const pout: partProdVtx->particles_out()) {
510  if (!pout) continue;
511  for (const auto *const pin: partProdVtx->particles_in()) {
512  if (!pin) continue;
513  if (!HepMC::is_same_particle(pout,pin)) continue;
514  if (MC::isElectron(pout)) NumOfEleLoop++;
515  if (std::abs(pout->pdgId()) == MC::NU_E) NumOfEleNeuLoop++;
516  if (MC::isSMLepton(pout)) NumOfLepLoop++;
517  break; // break out of inner loop after having found two matching particles
518  }
519  }
520  if (NumOfEleLoop == 2 && NumOfEleNeuLoop == 0) return ZBoson;
521  if (NumOfEleLoop == 1 && NumOfEleNeuLoop == 1) return WBoson;
522  if ((NumOfEleLoop == 4 && NumOfEleNeuLoop == 0) || (NumOfEleLoop == 3 && NumOfEleNeuLoop == 1) ||
523  (NumOfEleLoop == 2 && NumOfEleNeuLoop == 2)) return DiBoson;
524  if (NumOfLepLoop == 4) return DiBoson;
525  }
526 
527  //-- McAtNLo
528 
529  if (MC::isHiggs(ancestorPDG)) return Higgs;
530 
531  if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
532 
533  if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
534 
535  if (MC::isMuon(ancestorPDG)) return Mu;
536  if (MC::isTau(ancestorPDG)) {
537  const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
538  const ParticleType tautype = defTypeOfTau(tauOrig);
539  return (tautype == IsoTau)?tauOrig:TauLep;
540  }
541 
542  if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
543  if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
544  if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
545  if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
546  if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
547  if (MC::isSUSY(ancestorPDG)) return SUSY;
548  if (MC::isBSM(ancestorPDG)) return OtherBSM;
549 
550  const ParticleType pType = defTypeOfHadron(ancestorPDG);
551  if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
552  return convHadronTypeToOrig(pType, ancestorPDG);
553 }
554 
555 
557  const xAOD::TruthParticle* thePart,
558  bool& isPrompt,
560 {
561  ATH_MSG_DEBUG("Executing DefOrigOfMuon ");
562 
563  // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
564  const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
565  if (!thePriPart) return NonDefined;
566  if (!MC::isMuon(thePriPart)) return NonDefined;
567 
568  //-- to define muon outcome status
569  info.particleOutCome = defOutComeOfMuon(thePriPart);
570 
571  const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
572  if (!partProdVtx) return NonDefined;
573 
574  if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfMuon:: muon has more than one parent.");
575 
576  const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
577  info.setMotherProperties(ancestor);
578  if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
579 
580  // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
581 
582  if ((MC::isTau(ancestor)|| MC::isW(ancestor)) && ancestor->hasProdVtx()) {
583  int pPDG(0);
584  const xAOD::TruthParticle* ancestorParent{};
585  do {
586  pPDG = 0;
587  ancestorParent = MC::findMother(ancestor);
588  // Start of method 2 of protecting against loops
589  // to prevent Sherpa loop
590  if (ancestor == ancestorParent) { break; }
591  if (TruthLoopDetectionMethod2(ancestor,ancestorParent)) {
592  ancestorParent = ancestor;
593  break;
594  }
595  // End of method 2 of protecting against loops
596 
597  if (ancestorParent) {
598  pPDG = ancestorParent->pdgId();// Only set pPDG in the case that we aren't in a loop.
599  if (MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG)) { // FIXME should this be (MC::isTau(pPDG) || MC::isW(pPDG)) ???
600  // There will be another iteration so set ancestor to ancestorParent
601  ancestor = ancestorParent; // ancestorParent is not nullptr here
602  }
603  }
604  } while ((MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG))); // FIXME should this be (MC::isTau(pPDG) || MC::isW(pPDG)) ???
605 
606  if (MC::isTau(pPDG) || MC::isW(pPDG) || MC::isZ(pPDG) || MC::isHiggs(pPDG) ||
607  MC::isMSSMHiggs(pPDG) || MC::isHeavyBoson(pPDG) || MC::isTop(pPDG) || // MSSM Higgs bosons, Heavy bosons( Z', Z'', W'+)
608  std::abs(pPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(pPDG) || // Left-right symmetric model WBoson || Right-handed neutrino (Pythia-specific)
609  MC::isSUSY(pPDG)) {
610  ancestor = ancestorParent; // ancestorParent is not nullptr here
611  }
612  }
613 
614  info.setMotherProperties(ancestor);
615  const int ancestorPDG = ancestor->pdgId();
616  const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
617  partProdVtx = ancestor->decayVtx();
618  const int numOfParents = partProdVtx->nIncomingParticles();
619  const int numberOfChildren = partProdVtx->nOutgoingParticles();
620 
621  // Determine decay products
622  auto DP = DecayProducts(partProdVtx);
623  const int NumOfPhot = DP.pd(MC::PHOTON);
624  const int NumOfEl = DP.pd(MC::ELECTRON);
625  const int NumOfPos = DP.pd(MC::POSITRON);
626  const int NumOfElNeut = DP.apd(MC::NU_E);
627  const int NumOfMuNeut = DP.apd(MC::NU_MU);
628  const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
629  const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
630  const int NumOfgluon = DP.apd(MC::GLUON);
631  const int NumOfMuPl = DP.pd(-MC::MUON);
632  const int NumOfMuMin = DP.pd(MC::MUON);
633  const int NumOfTau = DP.apd(MC::TAU);
634  const int NumOfTauNeut = DP.apd(MC::NU_TAU);
635  // End of section determining decay products
636 
637  if (std::abs(ancestorPDG) == MC::PIPLUS && numberOfChildren == 2 && NumOfMuNeut == 1) return PionDecay;
638  if (std::abs(ancestorPDG) == MC::KPLUS && numberOfChildren == 2 && NumOfMuNeut == 1) return KaonDecay;
639  if (MC::isTau(ancestorPDG)) {
640  const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
641  const ParticleType tautype = defTypeOfTau(tauOrig);
642  return (tautype == IsoTau)?tauOrig:TauLep;
643  }
644 
645  if (MC::isTop(ancestorPDG)) return top;
646  // Quark weak decay
647  if (MC::isSMQuark(ancestorPDG) && numOfParents == 1 && numberOfChildren == 3 && NumOfquark == 1 && NumOfMuNeut == 1) return QuarkWeakDec;
648 
649  if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
650  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
651  const xAOD::TruthParticle* itrP;
652  do {
653  itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
654  prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
655  } while (MC::isW(itrP) && prodVert);
656 
657  if (prodVert && prodVert->nIncomingParticles() == 1) {
658  if (std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
659  if (std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
660  if (std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
661  }
662  return WBoson;
663  }
664  if (MC::isW(ancestorPDG)) return WBoson;
665  if (MC::isZ(ancestorPDG)) return ZBoson;
666  if (MC::isPhoton(ancestorPDG) && numberOfChildren == 2 && NumOfMuMin == 1 && NumOfMuPl == 1) return PhotonConv;
667  //-- Exotics
668 
669  // MadGraphPythia ZWW*->lllnulnu
670  if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
671  bool isZboson = false;
672  bool isWboson = false;
673  bool skipnext = false;
674  for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
675  if (skipnext) {
676  skipnext = false;
677  continue;
678  }
679  const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
680  if (!aChild) continue;
681  const xAOD::TruthParticle* theNextChild{};
682  for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
683  theNextChild = partProdVtx->outgoingParticle(ipOut1);
684  if (theNextChild) break;
685  }
686  if (!theNextChild) continue;
687  if (MC::isMuon(aChild) && MC::isMuon(theNextChild)) {
688  // Zboson
689  if (thePriPart == aChild || thePriPart == theNextChild) {
690  isZboson = true;
691  break;
692  }
693  skipnext = true;
694  } else if (MC::isMuon(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_MU) {
695  // WBoson
696  if (thePriPart == aChild || thePriPart == theNextChild) {
697  isWboson = true;
698  break;
699  }
700  skipnext = true;
701  }
702  }
703  if (isWboson) return WBoson;
704  if (isZboson) return ZBoson;
705  }
706  if (numOfParents == 2 ) {
707  //--Sherpa Z->mumu
708  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfMuPl == 1 && NumOfMuMin == 1) return ZBoson;
709 
710  //--Sherpa W->munu ??
711  // if(numOfParents==2&&(numberOfChildren-NumOfquark-NumOfgluon)==2&&(NumOfEl==1||NumOfPos==1)&&NumOfElNeut==1) return WBoson;
712  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && (NumOfMuPl == 1 || NumOfMuMin == 1) && NumOfMuNeut == 1) return WBoson;
713 
714  const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
715  const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
716  //--Sherpa ZZ,ZW
717  if ((numberOfChildren - NumOfquark - NumOfgluon) == 4 &&
718  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
719  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
720 
721  //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
722  if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 &&
723  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
724  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
725  }
726 
727  //--New Sherpa Z->mumu
728  if (partProdVtx == ancestorProdVtx) {
729  int NumOfMuLoop = 0;
730  int NumOfMuNeuLoop = 0;
731  int NumOfLepLoop = 0;
732  for (const auto & pout: partProdVtx->particles_out()) {
733  if (!pout) continue;
734  for (const auto & pin: partProdVtx->particles_in()) {
735  if (!pin) continue;
736  if (HepMC::is_same_particle(pout,pin)) {
737  if (MC::isMuon(pout)) NumOfMuLoop++;
738  if (std::abs(pout->pdg_id()) == MC::NU_MU) NumOfMuNeuLoop++;
739  if (MC::isSMLepton(pout)) NumOfLepLoop++;
740  break; // break out of inner loop after having found two matching particles
741  }
742  }
743  }
744  if (NumOfMuLoop == 2 && NumOfMuNeuLoop == 0) return ZBoson;
745  if (NumOfMuLoop == 1 && NumOfMuNeuLoop == 1) return WBoson;
746  if ((NumOfMuLoop == 4 && NumOfMuNeuLoop == 0) || (NumOfMuLoop == 3 && NumOfMuNeuLoop == 1) ||
747  (NumOfMuLoop == 2 && NumOfMuNeuLoop == 2)) return DiBoson;
748  if (NumOfLepLoop == 4) return DiBoson;
749  }
750 
751  //-- McAtNLo
752 
753  if (MC::isHiggs(ancestorPDG)) return Higgs;
754 
755  if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
756 
757  if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
758 
759  if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
760  if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
761  if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
762  if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
763  if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
764  if (MC::isSUSY(ancestorPDG)) return SUSY;
765  if (MC::isBSM(ancestorPDG)) return OtherBSM;
766 
767  const ParticleType pType = defTypeOfHadron(ancestorPDG);
768  if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
769 
770  return convHadronTypeToOrig(pType, ancestorPDG);
771 }
772 
773 
775  const xAOD::TruthParticle* thePart,
776  int ancestorPDGin,
778 {
779  ATH_MSG_DEBUG("Executing DefOrigOfTau ");
780 
781  // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
782  const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
783  if (!thePriPart) return NonDefined;
784  if (!MC::isTau(thePriPart)) return NonDefined;
785 
786  //-- to define tau outcome status
787  if (MC::isPhysical(thePriPart)) info.particleOutCome = defOutComeOfTau(thePriPart); // FIXME why do we need the additional check on MC::isPhysical here c.f. defOrigOfElectron and defOrigOfMuon?
788 
789  const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
790  if (!partProdVtx) return NonDefined;
791 
792  if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfTau:: tau has more than one parent.");
793 
794  const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
795  info.setMotherProperties(ancestor);
796  if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
797 
798  // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
799 
800  // Difference from defOrigOfElectron and defOrigOfMuon - no loop through ancestor particles
801 
802  if (MC::isW(ancestorPDGin) && ancestor->hasProdVtx()) { // FIXME ancestorPDGin here could in principle be inconsistent with ancestorProdVtx
803  const xAOD::TruthParticle* ancestorParent = MC::findMother(ancestor);
804  if (ancestorParent && MC::isTop(ancestorParent->pdgId())) {
805  ancestor = ancestorParent; //...so ancestor cannot be nullptr
806  }
807  }
808 
809  const int ancestorPDG = ancestor->pdgId();
810  info.setMotherProperties(ancestor);
811  const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
812  partProdVtx = ancestor->decayVtx();
813  if (!partProdVtx) return NonDefined; // FIXME not sure this could ever be true?
814  const int numOfParents = partProdVtx->nIncomingParticles();
815 
816  // Determine decay products
817  auto DP = DecayProducts(partProdVtx);
818  const int numberOfChildren = DP.size();
819  const int NumOfPhot = DP.pd(MC::PHOTON);
820  const int NumOfEl = DP.pd(MC::ELECTRON);
821  const int NumOfPos = DP.pd(MC::POSITRON);
822  const int NumOfElNeut = DP.apd(MC::NU_E);
823  const int NumOfMuNeut = DP.apd(MC::NU_MU);
824  /* const int NumOfLQ = DP.apd(MC::LEPTOQUARK); */ // FIXME Leptoquarks not an option?
825  const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
826  const int NumOfgluon = DP.apd(MC::GLUON);
827  const int NumOfMuPl = DP.pd(-MC::MUON);
828  const int NumOfMuMin = DP.pd(MC::MUON);
829  const int NumOfTau = DP.apd(MC::TAU);
830  const int NumOfTauNeut = DP.apd(MC::NU_TAU);
831  // End of section determining decay products
832 
833  if (MC::isTop(ancestorPDG)) return top;
834  if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
835  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
836  const xAOD::TruthParticle* itrP;
837  do {
838  itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
839  prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
840  } while (MC::isW(itrP) && prodVert);
841 
842  if (prodVert && prodVert->nIncomingParticles() == 1 ) {
843  if (std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
844  if (std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
845  if (std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
846  }
847  return WBoson;
848  }
849  if (MC::isW(ancestorPDG)) { return WBoson;}
850  if (MC::isZ(ancestorPDG)) { return ZBoson;}
851  if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
852  bool isZboson = false;
853  bool isWboson = false;
854  bool skipnext = false;
855  for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
856  if (skipnext) {
857  skipnext = false;
858  continue;
859  }
860  const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
861  if (!aChild) continue;
862  const xAOD::TruthParticle* theNextChild{};
863  for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
864  theNextChild = partProdVtx->outgoingParticle(ipOut1);
865  if (theNextChild) break;
866  }
867  if (!theNextChild) {
868  continue;
869  }
870  if (MC::isTau(aChild) && MC::isTau(theNextChild)) {
871  // Zboson
872  if (thePriPart == aChild || thePriPart == theNextChild) {
873  isZboson = true;
874  break;
875  }
876  skipnext = true;
877  } else if (MC::isTau(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_TAU) {
878  // WBoson
879  if (thePriPart == aChild || thePriPart == theNextChild) {
880  isWboson = true;
881  break;
882  }
883  skipnext = true;
884  }
885  }
886  if (isWboson) return WBoson;
887  if (isZboson) return ZBoson;
888  }
889  if (numOfParents == 2 ) {
890  const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
891  const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
892  //--Sherpa Z->tautau
893  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfTau == 2 && (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return ZBoson; // FIXME Why the extra checks on incoming particles compared to Z->ee, Z->mumu and Z->nunu?
894 
895  //--Sherpa W->taunu new
896  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfTau == 1 && NumOfTauNeut == 1) return WBoson;
897 
898  //--Sherpa ZZ,ZW
899  if ((numberOfChildren - NumOfquark - NumOfgluon) == 4 &&
900  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
901  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
902 
903  //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
904  if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 &&
905  (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
906  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
907  }
908 
909  // New Sherpa Z->tautau
910  if (partProdVtx == ancestorProdVtx) {
911  int NumOfTauLoop = 0;
912  int NumOfTauNeuLoop = 0;
913  int NumOfLepLoop = 0;
914  for ( const auto *const pout: partProdVtx->particles_out()) {
915  if (!pout) continue;
916  for (const auto *const pin: partProdVtx->particles_in()) {
917  if (!pin) continue;
918  if (!HepMC::is_same_particle(pout,pin)) continue;
919  if (MC::isTau(pout)) NumOfTauLoop++;
920  if (std::abs(pout->pdgId()) == MC::NU_TAU) NumOfTauNeuLoop++;
921  if (MC::isSMLepton(pout)) NumOfLepLoop++;
922  break; // break out of inner loop after having found two matching particles
923  }
924  }
925  if (NumOfTauLoop == 2 && NumOfTauNeuLoop == 0) return ZBoson;
926  if (NumOfTauLoop == 1 && NumOfTauNeuLoop == 1) return WBoson;
927  if ((NumOfTauLoop == 4 && NumOfTauNeuLoop == 0) || (NumOfTauLoop == 3 && NumOfTauNeuLoop == 1) || (NumOfTauLoop == 2 && NumOfTauNeuLoop == 2)) return DiBoson;
928  if (NumOfLepLoop == 4) return DiBoson;
929  }
930 
931  //-- McAtNLo
932 
933  if (MC::isHiggs(ancestorPDG)) return Higgs;
934  if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
935  if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
936  if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
937  if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
938  if (MC::isSUSY(ancestorPDG)) return SUSY;
939  if (MC::isBSM(ancestorPDG)) return OtherBSM;
940  if (std::abs(ancestorPDG) == MC::JPSI) return JPsi;
941 
942  const ParticleType pType = defTypeOfHadron(ancestorPDG);
943  return convHadronTypeToOrig(pType, ancestorPDG);
944 }
945 
946 
948  const xAOD::TruthParticle* thePart,
949  bool& isPrompt,
951 {
952  if (!thePart) return NonDefined; // FIXME Why is this extra protection needed for this function and not the others?
953  ATH_MSG_DEBUG("Executing DefOrigOfPhoton ");
954 
955  info.resetMotherProperties();
956  info.photonMother = nullptr;
957 
958  // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
959  const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
960  if (!thePriPart) return NonDefined;
961  if (!MC::isPhoton(thePriPart)) return NonDefined;
962 
963  const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
964 
965  //-- to define photon outcome status
966  info.particleOutCome = defOutComeOfPhoton(thePriPart);
967 
968  if (!partProdVtx) return NonDefined;
969 
970  int numOfParents = partProdVtx->nIncomingParticles();
971  if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfPhoton:: photon has more than one parent.");
972 
973  const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
974  info.setMotherProperties(ancestor);
975  if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
976 
977  int ancestorPDG = ancestor->pdgId();
978  // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
979  const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
980 
981  partProdVtx = ancestor->decayVtx(); // FIXME how often does this line actually change the pointer???
982  numOfParents = partProdVtx->nIncomingParticles();
983 
984  const int numberOfChildren = partProdVtx->nOutgoingParticles();
985 
986  // Determine decay products
987  auto DP = DecayProducts(partProdVtx);
988  const int NumOfEl = DP.pd(MC::ELECTRON);
989  const int NumOfPos = DP.pd(MC::POSITRON);
990  const int NumOfMu = DP.apd(MC::MUON);
991  const int NumOfTau = DP.apd(MC::TAU);
992  const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
993  const int NumOfLep = NumOfEl + NumOfPos + NumOfMu + NumOfTau;
994  const int NumOfNeut = DP.apd({MC::NU_E,MC::NU_MU,MC::NU_TAU});
995  const int NumOfPht = DP.pd(MC::PHOTON);
996 
997  int childPDG(0);
998  int NumOfPartons(0);
999  int NumOfNucFr(0);
1000  const bool possibleNuclearFragment = (numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG) || std::abs(ancestorPDG) == MC::PIPLUS));
1001  const xAOD::TruthParticle* child{};
1002  for (const auto& pout: partProdVtx->particles_out()) {
1003  if (!pout) continue;
1004  childPDG = pout->pdg_id();
1005  if (possibleNuclearFragment &&
1006  (MC::isNucleus(childPDG) || childPDG == 0 || childPDG == MC::PROTON || childPDG == MC::NEUTRON)) { // FIXME Do we really expect particles with PDG_ID = 0 in the truth record?
1007  NumOfNucFr++;
1008  }
1009  if (std::abs(childPDG) < MC::ELECTRON ||
1010  (std::abs(childPDG) > MC::NU_TAU && std::abs(childPDG) < 43 && !MC::isPhoton(childPDG))) {
1011  // FIXME Too loose? This definition picks up 4th generation quarks and leptons as well as all gauge bosons and leptoquarks.
1012  // Suggest MC::isSMQuark(childPDG) || (MC::isBoson(childPDG) && !MC::isPhoton(childPDG))
1013  // or maybe even MC::isSMQuark(childPDG) || MC::isGluon(childPDG)
1014  // AKA const int NumOfPartons = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK,MC::GLUON});
1015  NumOfPartons++;
1016  }
1017  if (childPDG == ancestorPDG) {
1018  child = pout;
1019  }
1020  }
1021  // End of section determining decay products
1022 
1023  bool foundISR = false;
1024  bool foundFSR = false;
1025  if (numOfParents == 1 && numberOfChildren == 2 && child && HepMC::is_same_generator_particle(child, ancestor)) return BremPhot;
1026  if (numOfParents == 1 && numberOfChildren == 2 && MC::isElectron(ancestorPDG) && NumOfPht == 2) return ElMagProc;
1027 
1028  // decay of W,Z and Higgs to lepton with FSR generated by Pythia
1029  if (numOfParents == 1 && numberOfChildren == 2 && (MC::isElectron(ancestorPDG) || MC::isMuon(ancestorPDG) || MC::isTau(ancestorPDG)) &&
1030  !(child && HepMC::is_same_generator_particle(child, ancestor)) && ancestorProdVtx &&
1031  ancestorProdVtx->nIncomingParticles() == 1) {
1032  int itr = 0;
1033  int PartPDG = 0;
1034  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1035  const xAOD::TruthVertex* Vert{};
1036  do {
1037  Vert = prodVert;
1038  for (const auto & pin: Vert->particles_in()) {
1039  if (!pin) continue;
1040  PartPDG = std::abs(pin->pdgId());
1041  prodVert = pin->prodVtx();
1042  if (MC::isZ(PartPDG) || MC::isW(PartPDG) || MC::isHiggs(PartPDG)) foundFSR = true;
1043  }
1044  itr++;
1045  if (itr > 100) { // FIXME Improve loop detection here?
1046  ATH_MSG_WARNING("DefOrigOfPhoton:: infinite while");
1047  break;
1048  }
1049  } while (prodVert && std::abs(ancestorPDG) == PartPDG);
1050 
1051  if (foundFSR) return FSRPhot;
1052  }
1053 
1054  // Nucl reaction
1055  // gamma+Nuclear=>gamma+Nucl.Fr+Nuclons+pions
1056  // e+Nuclear=>e+gamma+Nucl.Fr+Nuclons+pions
1057  // pi+Nuclear=>gamma+Nucl.Fr+Nuclons+pions
1058 
1059  if ((numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG)) && numberOfChildren > 2 && NumOfNucFr != 0) ||
1060  (numOfParents == 1 && std::abs(ancestorPDG) == MC::PIPLUS && numberOfChildren > 10 && NumOfNucFr != 0) ||
1061  (numOfParents == 1 && MC::isPhoton(ancestorPDG) && numberOfChildren > 10 && MC::isStable(ancestor)) ||
1062  (numOfParents == 1 && MC::isNucleus(ancestorPDG) && std::abs(ancestorPDG) != MC::PROTON)) // FIXME vetoing protons here to preserve previous behaviour
1063  return NucReact;
1064 
1065  if (MC::isMuon(ancestorPDG) && NumOfMu == 0) return Mu;
1066  if (MC::isTau(ancestorPDG) && NumOfTau == 0) return TauLep;
1067 
1068  if (numOfParents == 1 && ancestor->status() == 3) return (foundISR)? ISRPhot:UndrPhot; // FIXME foundISR is always false at this point
1069 
1070  //-- to find initial and final state raiation and underline photons
1071  //-- SUSY
1072  if (numOfParents == 1 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG)) &&
1073  (numberOfChildren != NumOfPht + NumOfPartons || !MC::Pythia8::isConditionA(ancestor))) {
1074  for (const auto& pout: partProdVtx->particles_out()) {
1075  if (!pout) continue;
1076  if (ancestorPDG != pout->pdgId()) continue;
1077  const xAOD::TruthVertex* Vrtx = pout->decayVtx();
1078  if (!Vrtx) continue;
1079  if (Vrtx->nOutgoingParticles() != 1 && Vrtx->nIncomingParticles() == 1) continue;
1080  if (!Vrtx->outgoingParticle(0)) continue;
1081  if (Vrtx->outgoingParticle(0)->pdgId() == 91) foundISR = true; // Herwig "cluster"
1082  }
1083  return (foundISR)?ISRPhot:UndrPhot;
1084  }
1085 
1086  //-- to find final state radiation
1087  //-- Exotics
1088 
1089  // FSR from Photos
1090  //-- Exotics- CompHep
1091  if (numOfParents == 2 && ((MC::isElectron(ancestorPDG) && NumOfEl == 1 && NumOfPos == 1) || (MC::isMuon(ancestorPDG) && NumOfMu == 2) || (MC::isTau(ancestorPDG) && NumOfTau == 2))) {
1092  if (std::abs(partProdVtx->incomingParticle(0)->pdgId()) == std::abs(partProdVtx->incomingParticle(1)->pdgId())) return FSRPhot;
1093  }
1094 
1095  if (numOfParents == 2 && NumOfLep == 1 && NumOfNeut == 1 && (MC::isElectron(ancestorPDG) || std::abs(ancestorPDG) == MC::NU_E)) return FSRPhot;
1096 
1097  //-- Exotics - CompHep
1098  if (MC::isElectron(ancestorPDG) && numOfParents == 1 && numberOfChildren == 2 && (NumOfEl == 1 || NumOfPos == 1) && NumOfPht == 1 &&
1099  !( child && HepMC::is_same_generator_particle(child, ancestor)) && !HepMC::is_simulation_particle(child) && !HepMC::is_simulation_particle(ancestor))
1100  return FSRPhot;
1101 
1102  // FSR from Photos
1103  if (MC::isZ(ancestorPDG) && ((NumOfEl + NumOfPos == 2 || NumOfEl + NumOfPos == 4) || (NumOfMu == 2 || NumOfMu == 4) || (NumOfTau == 2 || NumOfTau == 4)) && NumOfPht > 0) return FSRPhot;
1104 
1105  if (NumOfPht > 0 && (std::abs(ancestorPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(ancestorPDG))) return FSRPhot; // Left-right symmetric model WBoson || Right-handed neutrinos (Pythia-specific)
1106 
1107  if (numOfParents == 2 && NumOfLQ == 1) return FSRPhot;
1108 
1109  //--- other process
1110 
1111  if (MC::isZ(ancestorPDG)) return ZBoson;
1112  if (MC::isW(ancestorPDG)) {
1113 
1114  if (NumOfLep == 1 && NumOfNeut == 1 && numberOfChildren == NumOfLep + NumOfNeut + NumOfPht) return FSRPhot;
1115 
1116  if (ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
1117  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1118  const xAOD::TruthParticle* itrP;
1119  do {
1120  itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
1121  prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
1122  } while (MC::isW(itrP) && prodVert);
1123 
1124  if (prodVert && prodVert->nIncomingParticles() == 1 ) {
1125  if ( MC::isTau(itrP)) return TauLep;
1126  if ( MC::isMuon(itrP)) return Mu;
1127  if ( std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1128  if ( std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1129  if ( std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1130  }
1131  } else
1132  return WBoson;
1133  }
1134 
1135  // MadGraphPythia ZWW*->lllnulnu
1136  if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
1137  bool isZboson = false;
1138  bool isWboson = false;
1139  bool skipnext = false;
1140  for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
1141  if (skipnext) {
1142  skipnext = false;
1143  continue;
1144  }
1145  const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
1146  if (!aChild) continue;
1147  const xAOD::TruthParticle* theNextChild{};
1148  for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
1149  theNextChild = partProdVtx->outgoingParticle(ipOut1);
1150  if (theNextChild) break;
1151  }
1152  if (!theNextChild) continue;
1153  if (MC::isTau(aChild) && MC::isTau(theNextChild)) {
1154  // Zboson
1155  if (thePriPart == aChild || thePriPart == theNextChild) {
1156  isZboson = true;
1157  break;
1158  }
1159  skipnext = true;
1160  } else if (MC::isTau(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_TAU) {
1161  // WBoson
1162  if (thePriPart == aChild || thePriPart == theNextChild) {
1163  isWboson = true;
1164  break;
1165  }
1166  skipnext = true;
1167  }
1168  }
1169  if (isWboson) return WBoson;
1170  if (isZboson) return ZBoson;
1171  }
1172 
1173  //--Sherpa ZZ,ZW+FSR
1174  if (numOfParents == 4 && (numberOfChildren - NumOfPht) == 4 && (NumOfLep + NumOfNeut == 4)) {
1175  if (MC::isSMLepton(partProdVtx->incomingParticle(0))&&MC::isSMLepton(partProdVtx->incomingParticle(1))
1176  && MC::isSMLepton(partProdVtx->incomingParticle(2))&&MC::isSMLepton(partProdVtx->incomingParticle(3)))
1177  return FSRPhot;
1178  }
1179 
1180  //--New Sherpa single photon
1181  if (partProdVtx == ancestorProdVtx) {
1182  for (const auto *const pout: partProdVtx->particles_out()) {
1183  if (!pout) continue;
1184  for (const auto *const pin: partProdVtx->particles_in()) {
1185  if (!pin) continue;
1186  if (!HepMC::is_same_particle(pout,pin)) continue;
1187  if (MC::isPhoton(pout)) return SinglePhot;
1188  break; // break out of inner loop after having found two matching particles
1189  }
1190  }
1191  }
1192 
1193  if (MC::isHiggs(ancestorPDG)) return Higgs;
1194  if (std::abs(ancestorPDG) == MC::PI0) return PiZero;
1195  if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
1196  if (MC::isHeavyBoson(ancestorPDG) || std::abs(ancestorPDG) == 5100039 ) return HeavyBoson; // Heavy Bosons (Z' Z'' W'+) + KK excited graviton
1197 
1198  if (MC::isSUSY(ancestorPDG)) return SUSY;
1199  if (MC::isBSM(ancestorPDG)) return OtherBSM;
1200 
1201  // Pythia8 gamma+jet samples
1202  if (MC::Pythia8::isConditionA(ancestor) && MC::isStable(thePriPart) && NumOfPht == 1 && numberOfChildren == (NumOfPht + NumOfPartons)) return PromptPhot;
1203 
1204  const ParticleType pType = defTypeOfHadron(ancestorPDG);
1205  if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
1206  return convHadronTypeToOrig(pType, ancestorPDG);
1207 }
1208 
1211  const xAOD::TruthParticle* thePart,
1212  bool& isPrompt,
1214 {
1215  ATH_MSG_DEBUG("Executing DefOrigOfNeutrino ");
1216 
1217  const int nuFlav = std::abs(thePart->pdgId());
1218  // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
1219  const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
1220  if (!thePriPart) return NonDefined;
1221  if (std::abs(thePriPart->pdgId()) != nuFlav) return NonDefined; // FIXME should this be if (!MC::isSMNeutrino(thePriPart) || abs(thePriPart->pdgId()) != nuFlav) return NonDefined; // (Use MC::isNeutrino if 4th generation neutrinos OK)
1222 
1223  //-- to define neutrino outcome status
1224  info.particleOutCome = NonInteract;
1225 
1226  const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
1227  if (!partProdVtx) return NonDefined;
1228 
1229  if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfNeutrino:: neutrino has more than one parent.");
1230 
1231  const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
1232  info.setMotherProperties(ancestor);
1233  if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
1234 
1235  // Start of method 3 of protecting against loops
1236  // to resolve Sherpa loop
1237  bool samePart = TruthLoopDetectionMethod1(partProdVtx, ancestor);
1238  // End of method 3 of protecting against loops
1239 
1240  if ((std::abs(ancestor->pdgId()) == nuFlav || MC::isTau(ancestor) || MC::isW(ancestor)) && ancestor->hasProdVtx() && !samePart) {
1241  int pPDG(0);
1242  const xAOD::TruthParticle* ancestorParent{};
1243  do {
1244  pPDG = 0;
1245  ancestorParent = MC::findMother(ancestor);
1246  // Start of method 2 of protecting against loops
1247  // to prevent Sherpa loop
1248  if (TruthLoopDetectionMethod2(ancestor,ancestorParent)) {
1249  ancestorParent = ancestor;
1250  break;
1251  }
1252  //
1253  if (ancestorParent) {
1254  pPDG = ancestorParent->pdgId(); // FIXME difference in behaviour compared to defOrigOfElectron/Muon pPDG set even if we are in a loop
1255  }
1256  // to prevent Sherpa loop
1257  if (ancestor == ancestorParent) { break; }
1258  // End of method 2 of protecting against Sherpa loops
1259  if (std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG) ) {
1260  // There will be another iteration so set ancestor to ancestorParent
1261  ancestor = ancestorParent; // ancestorParent is not a nullptr
1262  info.setMotherProperties(ancestor); // FIXME difference in behaviour compared to MCTruthClassifier::defOrigOfElectron/Muon
1263  }
1264 
1265  } while ((std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG)));
1266 
1267  if (std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG) || MC::isZ(pPDG) || MC::isHiggs(pPDG) ||
1268  MC::isMSSMHiggs(pPDG) || MC::isHeavyBoson(pPDG) || MC::isTop(pPDG) || // MSSM Higgs bosons, Heavy bosons( Z', Z'', W'+)
1269  std::abs(pPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(pPDG) || // Left-right symmetric model WBoson || Right-handed neutrino (Pythia-specific)
1270  MC::isSUSY(pPDG)) {
1271  ancestor = ancestorParent; // ancestorParent is not nullptr here
1272  info.setMotherProperties(ancestor);
1273  }
1274  }
1275  //if ancestor is still nullptr, we have a problem
1276  if (!ancestor) return NonDefined; // FIXME it should not be possible for ancestor to be nullptr at this point???
1277 
1278  info.setMotherProperties(ancestor);
1279  const int ancestorPDG = ancestor->pdgId();
1280  partProdVtx = ancestor->decayVtx();
1281  const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
1282  const int numOfParents = partProdVtx->nIncomingParticles();
1283  const int numberOfChildren = partProdVtx->nOutgoingParticles();
1284 
1285  // Determine decay products
1286  auto DP = DecayProducts(partProdVtx);
1287  const int NumOfPhot = DP.pd(MC::PHOTON);
1288  const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
1289  const int NumOfgluon = DP.apd(MC::GLUON);
1290  const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
1291  const int NumOfElNeut = DP.apd(MC::NU_E);
1292  const int NumOfMuNeut = DP.apd(MC::NU_MU);
1293  const int NumOfTauNeut = DP.apd(MC::NU_TAU);
1294  const int NumOfEl = DP.apd(MC::ELECTRON);
1295  const int NumOfMu = DP.apd(MC::MUON);
1296  const int NumOfTau = DP.apd(MC::TAU);
1297 
1298  samePart = false;
1299  for (const auto& aChild: partProdVtx->particles_out()) {
1300  if (!aChild) continue;
1301  if (aChild->pdgId() == ancestorPDG && HepMC::is_same_generator_particle(aChild,ancestor)) {
1302  samePart = true;
1303  break;
1304  }
1305  }
1306  // End of section determining decay products
1307 
1308  // Quark weak decay
1309  if (MC::isQuark(ancestorPDG) && numOfParents == 1 && numberOfChildren == 3 && NumOfquark == 1 && (NumOfEl == 1 || NumOfMu == 1 || NumOfTau == 1)) return QuarkWeakDec;
1310  if (MC::isTop(ancestorPDG)) return top;
1311 
1312  if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
1313  const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1314  const xAOD::TruthParticle* ptrPart;
1315  do {
1316  ptrPart = prodVert->incomingParticle(0); // FIXME just taking the first one
1317  prodVert = ptrPart->hasProdVtx() ? ptrPart->prodVtx() : nullptr;
1318  } while (MC::isW(ptrPart) && prodVert);
1319 
1320  if (prodVert && prodVert->nIncomingParticles() == 1) {
1321  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1322  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1323  if (std::abs(ptrPart->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1324  }
1325  return WBoson;
1326  }
1327  if (MC::isW(ancestorPDG)) return WBoson;
1328  if (MC::isZ(ancestorPDG)) return ZBoson;
1329 
1330  //-- Exotics
1331 
1332  // MadGraphPythia ZWW*->lllnulnu or ZWW*->nunulnulnu (don't even know if the latter is generated)
1333  if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
1334 
1335  const xAOD::TruthParticle* thePartToCheck = thePriPart;
1336  const xAOD::TruthParticle* theParent = thePriPart->hasProdVtx() ? thePriPart->prodVtx()->incomingParticle(0) : nullptr; // FIXME just taking the first one
1337 
1338  if (MC::isElectron(theParent) && MC::isDecayed(theParent)) { thePartToCheck = theParent; }
1339  bool isZboson = false;
1340  bool isWboson = false;
1341  bool skipnext = false;
1342 
1343  for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ++ipOut) {
1344  const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
1345  if (!aChild) continue;
1346  const xAOD::TruthParticle* theNextChild{};
1347  for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
1348  theNextChild = partProdVtx->outgoingParticle(ipOut1);
1349  if (theNextChild) break;
1350  }
1351  if (!theNextChild) continue;
1352 
1353  if (skipnext) {
1354  skipnext = false;
1355  continue;
1356  }
1357 
1358  const int apdgID1 = std::abs(aChild->pdgId());
1359  const int apdgID2 = std::abs(theNextChild->pdgId());
1360  if (apdgID1 == apdgID2 && MC::isSMNeutrino(apdgID1)) {
1361  // Zboson
1362  if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
1363  isZboson = true;
1364  break;
1365  }
1366  skipnext = true;
1367  } else if ((apdgID1 == MC::ELECTRON && apdgID2 == MC::NU_E) ||
1368  (apdgID1 == MC::NU_E && apdgID2 == MC::ELECTRON) ||
1369  (apdgID1 == MC::MUON && apdgID2 == MC::NU_MU) ||
1370  (apdgID1 == MC::NU_MU && apdgID2 == MC::MUON) ||
1371  (apdgID1 == MC::TAU && apdgID2 == MC::NU_TAU) ||
1372  (apdgID1 == MC::NU_TAU && apdgID2 == MC::TAU)
1373  ) {
1374  // WBoson
1375  if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
1376  isWboson = true;
1377  break;
1378  }
1379  skipnext = true;
1380  }
1381  }
1382  if (isWboson) return WBoson;
1383  if (isZboson) return ZBoson;
1384  }
1385 
1386  if (numOfParents == 2) {
1387  //--Sherpa Z->nunu
1388  if ( (numberOfChildren - NumOfquark - NumOfgluon) == 2 && (NumOfElNeut == 2 || NumOfMuNeut == 2 || NumOfTauNeut == 2)) return ZBoson;
1389 
1390  //--Sherpa W->enu ??
1391  if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && ((NumOfEl == 1 && NumOfElNeut == 1) || (NumOfMu == 1 && NumOfMuNeut == 1) || (NumOfTau == 1 && NumOfTauNeut == 1))) return WBoson;
1392 
1393  const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
1394  const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
1395  //--Sherpa ZZ,ZW
1396  if ( (numberOfChildren - NumOfquark - NumOfgluon) == 4 && (NumOfEl + NumOfMu + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
1397  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
1398 
1399  //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
1400  if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 && (NumOfEl + NumOfMu + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
1401  (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
1402  }
1403 
1404  // New Sherpa Z->nunu
1405  if (partProdVtx == ancestorProdVtx) {
1406  int NumOfLepLoop = 0;
1407  int NumOfNeuLoop = 0;
1408  for (const auto *const pout: partProdVtx->particles_out()) {
1409  if (!pout) continue;
1410  for (const auto *const pin: partProdVtx->particles_in()) {
1411  if (!pin) continue;
1412  if (HepMC::is_same_particle(pin,pout)) continue;
1413  const int apdgid = std::abs(pout->pdgId());
1414  if (MC::isSMLepton(apdgid)) {
1415  if (MC::isSMNeutrino(apdgid)) { NumOfNeuLoop++; }
1416  else { NumOfLepLoop++; }
1417  }
1418  break; // break out of inner loop after having found two matching particles
1419  }
1420  }
1421  if (NumOfNeuLoop == 2 && NumOfLepLoop == 0) return ZBoson;
1422  if (NumOfNeuLoop == 1 && NumOfLepLoop == 1) return WBoson;
1423  if (NumOfNeuLoop + NumOfLepLoop == 4) return DiBoson;
1424  }
1425 
1426  //-- McAtNLo
1427 
1428  if (MC::isHiggs(ancestorPDG)) return Higgs;
1429  if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
1430  if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
1431 
1432  if (MC::isTau(ancestorPDG)) {
1433  const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
1434  const ParticleType tautype = defTypeOfTau(tauOrig);
1435  return (tautype == IsoTau)?tauOrig:TauLep;
1436  }
1437 
1438  if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
1439  if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1440  if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1441  if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1442  if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
1443  if (MC::isSUSY(ancestorPDG)) return SUSY;
1444  if (MC::isBSM(ancestorPDG)) return OtherBSM;
1445 
1446  const ParticleType pType = defTypeOfHadron(ancestorPDG);
1447  if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
1448 
1449  return convHadronTypeToOrig(pType, ancestorPDG);
1450 }
xAOD::TruthVertex_v1::nOutgoingParticles
size_t nOutgoingParticles() const
Get the number of outgoing particles.
NuRMu
@ NuRMu
Definition: TruthClasses.h:73
temp
Definition: JetEventDict.h:21
IsoPhoton
@ IsoPhoton
Definition: TruthClasses.h:23
MCTruthPartClassifier::defOutComeOfMuon
ParticleOutCome defOutComeOfMuon(T thePart)
Definition: TruthClassifiers.h:213
MCTruthPartClassifier::defTypeOfPhoton
ParticleType defTypeOfPhoton(ParticleOrigin PhotOrig)
Definition: TruthClassifiers.h:115
calibdata.pout
def pout(output, newline=True)
Definition: calibdata.py:129
GenEvent.h
TauLep
@ TauLep
Definition: TruthClasses.h:63
isNucleus
bool isNucleus(const T &p)
PDG rule 16 Nuclear codes are given as 10-digit numbers ±10LZZZAAAI.
Definition: AtlasPID.h:697
HepMC::is_same_vertex
bool is_same_vertex(const T1 &p1, const T2 &p2)
Method to establish if two particles in the GenEvent actually represent the same vertex.
Definition: MagicNumbers.h:369
OtherBSMParticle
@ OtherBSMParticle
Definition: TruthClasses.h:32
isHeavyBoson
bool isHeavyBoson(const T &p)
APID: Additional "Heavy"/"prime" versions of W and Z bosons (Used in MCTruthClassifier)
Definition: AtlasPID.h:383
NuREle
@ NuREle
Definition: TruthClasses.h:72
Mu
@ Mu
Definition: TruthClasses.h:62
xAOD::TruthVertex_v1::particles_in
std::vector< const TruthParticle * > particles_in() const
Get the incoming particles.
Definition: TruthVertex_v1.cxx:60
WBosonLRSM
@ WBosonLRSM
Definition: TruthClasses.h:71
SingleTau
@ SingleTau
Definition: TruthClasses.h:57
isBSM
bool isBSM(const T &p)
APID: graviton and all Higgs extensions are BSM.
Definition: AtlasPID.h:836
PionDecay
@ PionDecay
Definition: TruthClasses.h:90
SG::ReadHandle
Definition: StoreGate/StoreGate/ReadHandle.h:67
MCTruthPartClassifier::defOutComeOfTau
ParticleOutCome defOutComeOfTau(T thePart)
Definition: TruthClassifiers.h:240
MCTruthClassifier::defOrigOfElectron
MCTruthPartClassifier::ParticleOrigin defOrigOfElectron(const xAOD::TruthParticleContainer &xTruthParticleContainer, const xAOD::TruthParticle *, bool &isPrompt, MCTruthPartClassifier::Info &info) const
Definition: MCTruthClassifierGen.cxx:280
GenVertex.h
MCTruthClassifier::particleHepMCTruthClassifier
virtual std::pair< MCTruthPartClassifier::ParticleType, MCTruthPartClassifier::ParticleOrigin > particleHepMCTruthClassifier(const HepMcParticleLink &theLink, MCTruthPartClassifier::Info *info=nullptr) const override final
Definition: MCTruthClassifierGen.cxx:26
ElMagProc
@ ElMagProc
Definition: TruthClasses.h:61
xAODTruthParticleLinkVector::find
ElementLink< xAOD::TruthParticleContainer > find(const HepMcParticleLink &hepMCLink) const
Definition: xAODTruthParticleLink.h:28
isNeutrino
bool isNeutrino(const T &p)
APID: the fourth generation neutrinos are neutrinos.
Definition: AtlasPID.h:209
MCTruthClassifier::TruthLoopDetectionMethod1
bool TruthLoopDetectionMethod1(const xAOD::TruthVertex *childOrigVtx, const xAOD::TruthParticle *parent) const
Definition: MCTruthClassifierGen.cxx:223
IsoElectron
@ IsoElectron
Definition: TruthClasses.h:11
SingleElec
@ SingleElec
Definition: TruthClasses.h:54
PhotonConv
@ PhotonConv
Definition: TruthClasses.h:59
ISRPhot
@ ISRPhot
Definition: TruthClasses.h:95
PromptPhot
@ PromptPhot
Definition: TruthClasses.h:93
MCTruthPartClassifier::defTypeOfElectron
ParticleType defTypeOfElectron(ParticleOrigin EleOrig, bool isPrompt)
Definition: TruthClassifiers.h:61
MCTruthClassifier.h
NuRTau
@ NuRTau
Definition: TruthClasses.h:74
UnknownMuon
@ UnknownMuon
Definition: TruthClasses.h:14
ZBoson
@ ZBoson
Definition: TruthClasses.h:67
UnknownTau
@ UnknownTau
Definition: TruthClasses.h:18
Hadron
@ Hadron
Definition: TruthClasses.h:26
xAOD::TruthParticle_v1::pdg_id
int pdg_id() const
PDG ID code.
Definition: TruthParticle_v1.h:52
HepMC::is_same_particle
bool is_same_particle(const T1 &p1, const T2 &p2)
Method to establish if two particles in the GenEvent actually represent the same particle.
Definition: MagicNumbers.h:366
GenParticle.h
WBoson
@ WBoson
Definition: TruthClasses.h:66
isSMQuark
bool isSMQuark(const T &p)
Definition: AtlasPID.h:169
isSMLepton
bool isSMLepton(const T &p)
APID: the fourth generation leptons are not standard model leptons.
Definition: AtlasPID.h:191
isGluon
bool isGluon(const T &p)
Definition: AtlasPID.h:370
FSRPhot
@ FSRPhot
Definition: TruthClasses.h:96
isHiggs
bool isHiggs(const T &p)
APID: HIGGS boson is only one particle.
Definition: AtlasPID.h:387
NuclFrag
@ NuclFrag
Definition: TruthClasses.h:28
BBbarMesonPart
@ BBbarMesonPart
Definition: TruthClasses.h:33
MCTruthPartClassifier::defTypeOfMuon
ParticleType defTypeOfMuon(ParticleOrigin MuOrig, bool isPrompt)
Definition: TruthClassifiers.h:80
NonDefined
@ NonDefined
Definition: TruthClasses.h:52
HiggsMSSM
@ HiggsMSSM
Definition: TruthClasses.h:69
MC::isPhysical
bool isPhysical(const T &p)
Identify if the particle is physical, i.e. is stable or decayed.
Definition: HepMCHelpers.h:51
CCbarMesonPart
@ CCbarMesonPart
Definition: TruthClasses.h:35
isQuark
bool isQuark(const T &p)
PDG rule 2: Quarks and leptons are numbered consecutively starting from 1 and 11 respectively; to do ...
Definition: AtlasPID.h:164
Neutrino
@ Neutrino
Definition: TruthClasses.h:27
HepMC::is_same_generator_particle
bool is_same_generator_particle(const T1 &p1, const T2 &p2)
Method to establish if two particles in the GenEvent actually represent the same generated particle.
Definition: MagicNumbers.h:363
HepMC::is_simulation_particle
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...
Definition: MagicNumbers.h:354
MC::findMatching
T findMatching(C TruthContainer, T p)
Function to find a particle in container.
Definition: HepMCHelpers.h:118
UnknownElectron
@ UnknownElectron
Definition: TruthClasses.h:10
MCTruthClassifier::defOrigOfMuon
MCTruthPartClassifier::ParticleOrigin defOrigOfMuon(const xAOD::TruthParticleContainer &xTruthParticleContainer, const xAOD::TruthParticle *, bool &isPrompt, MCTruthPartClassifier::Info &info) const
Definition: MCTruthClassifierGen.cxx:556
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
ParticleOrigin
ParticleOrigin
Definition: TruthClasses.h:51
PiZero
@ PiZero
Definition: TruthClasses.h:98
xAOD::EgammaHelpers::isElectron
bool isElectron(const xAOD::Egamma *eg)
is the object an electron (not Fwd)
Definition: EgammaxAODHelpers.cxx:12
MultiBoson
@ MultiBoson
Definition: TruthClasses.h:101
isZ
bool isZ(const T &p)
Definition: AtlasPID.h:376
xAOD::TruthParticle_v1
Class describing a truth particle in the MC record.
Definition: TruthParticle_v1.h:37
xAOD::TruthParticle_v1::hasProdVtx
bool hasProdVtx() const
Check for a production vertex on this particle.
Definition: TruthParticle_v1.cxx:69
MCTruthClassifier::defOrigOfNeutrino
MCTruthPartClassifier::ParticleOrigin defOrigOfNeutrino(const xAOD::TruthParticleContainer &xTruthParticleContainer, const xAOD::TruthParticle *, bool &isPrompt, MCTruthPartClassifier::Info &info) const
Definition: MCTruthClassifierGen.cxx:1210
test_pyathena.parent
parent
Definition: test_pyathena.py:15
DiBoson
@ DiBoson
Definition: TruthClasses.h:99
IsoTau
@ IsoTau
Definition: TruthClasses.h:19
LQ
@ LQ
Definition: TruthClasses.h:75
JPsi
@ JPsi
Definition: TruthClasses.h:84
MC::findMother
T findMother(T thePart)
Function to get a mother of particle. MCTruthClassifier legacy.
Definition: HepMCHelpers.h:85
MCTruthClassifier::TruthLoopDetectionMethod2
bool TruthLoopDetectionMethod2(const xAOD::TruthParticle *child, const xAOD::TruthParticle *parent) const
Definition: MCTruthClassifierGen.cxx:244
xAOD::TruthVertex_v1::incomingParticle
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
Definition: TruthVertex_v1.cxx:70
IsoMuon
@ IsoMuon
Definition: TruthClasses.h:15
DataVector
Derived DataVector<T>.
Definition: DataVector.h:794
MCTruthPartClassifier::defTypeOfHadron
ParticleType defTypeOfHadron(int pdg)
Definition: TruthClassifiers.h:46
SG::ReadHandle::isValid
virtual bool isValid() override final
Can the handle be successfully dereferenced?
isSUSY
bool isSUSY(const T &p)
Definition: AtlasPID.h:620
MCTruthClassifier::defOrigOfPhoton
MCTruthPartClassifier::ParticleOrigin defOrigOfPhoton(const xAOD::TruthParticleContainer &xTruthParticleContainer, const xAOD::TruthParticle *, bool &isPrompt, MCTruthPartClassifier::Info &info) const
Definition: MCTruthClassifierGen.cxx:947
isTau
bool isTau(const T &p)
Definition: AtlasPID.h:205
MCTruthClassifier::TruthLoopDetectionMethod3
bool TruthLoopDetectionMethod3(const xAOD::TruthVertex *childOrigVtx, const xAOD::TruthParticle *parent) const
Definition: MCTruthClassifierGen.cxx:266
MC::Pythia8::isConditionA
bool isConditionA(const T &p)
To be understood.
Definition: HepMCHelpers.h:23
xAOD::TruthParticle_v1::decayVtx
const TruthVertex_v1 * decayVtx() const
The decay vertex of this particle.
xAOD::TruthParticle_v1::prodVtx
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
Definition: TruthParticle_v1.cxx:75
GetAllXsec.entry
list entry
Definition: GetAllXsec.py:132
HepMC::ConstGenParticlePtr
const GenParticle * ConstGenParticlePtr
Definition: GenParticle.h:38
xAOD::TruthVertex_v1
Class describing a truth vertex in the MC record.
Definition: TruthVertex_v1.h:37
SinglePhot
@ SinglePhot
Definition: TruthClasses.h:56
MCTruthPartClassifier::defOutComeOfElectron
ParticleOutCome defOutComeOfElectron(T thePart)
Definition: TruthClassifiers.h:189
MagicNumbers.h
ReadHandle.h
Handle class for reading from StoreGate.
SUSY
@ SUSY
Definition: TruthClasses.h:77
HeavyBoson
@ HeavyBoson
Definition: TruthClasses.h:70
isHadron
bool isHadron(const T &p)
Definition: AtlasPID.h:348
isNeutrinoRH
bool isNeutrinoRH(const T &p)
PDG Rule 12: APID: Helper function for right-handed neutrino states These are generator defined PDG I...
Definition: AtlasPID.h:417
xAOD::TruthVertex_v1::particles_out
std::vector< const TruthParticle * > particles_out() const
Get the outgoing particles.
Definition: TruthVertex_v1.cxx:65
UndrPhot
@ UndrPhot
Definition: TruthClasses.h:94
SG::VarHandleBase::key
virtual const std::string & key() const override final
Return the StoreGate ID for the referenced object.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleBase.cxx:64
NucReact
@ NucReact
Definition: TruthClasses.h:97
NonInteract
@ NonInteract
Definition: TruthClasses.h:110
QuarkWeakDec
@ QuarkWeakDec
Definition: TruthClasses.h:65
isW
bool isW(const T &p)
Definition: AtlasPID.h:379
xAOD::TruthParticle_v1::status
int status() const
Status code.
MC::isStable
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
Definition: HepMCHelpers.h:45
SUSYParticle
@ SUSYParticle
Definition: TruthClasses.h:31
isTop
bool isTop(const T &p)
Definition: AtlasPID.h:182
MCTruthPartClassifier::isPrompt
int isPrompt(const unsigned int classify, bool allow_prompt_tau_decays=true)
Definition: TruthClassifiers.h:180
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
MCTruthPartClassifier
Definition: TruthClassifiers.h:12
xAOD::TruthVertex_v1::nIncomingParticles
size_t nIncomingParticles() const
Get the number of incoming particles.
MC::isDecayed
bool isDecayed(const T &p)
Identify if the particle decayed.
Definition: HepMCHelpers.h:42
xAOD::EgammaHelpers::isPhoton
bool isPhoton(const xAOD::Egamma *eg)
is the object a photon
Definition: EgammaxAODHelpers.cxx:21
MC::isBeam
bool isBeam(const T &p)
Identify if the particle is beam particle.
Definition: HepMCHelpers.h:39
MCTruthPartClassifier::convHadronTypeToOrig
ParticleOrigin convHadronTypeToOrig(ParticleType pType, int motherPDG)
Definition: TruthClassifiers.h:15
NonPrimary
@ NonPrimary
Definition: TruthClasses.h:29
Higgs
@ Higgs
Definition: TruthClasses.h:68
isSMNeutrino
bool isSMNeutrino(const T &p)
Definition: AtlasPID.h:212
DecayProducts.h
SingleMuon
@ SingleMuon
Definition: TruthClasses.h:55
MCTruthPartClassifier::defTypeOfTau
ParticleType defTypeOfTau(ParticleOrigin TauOrig)
Definition: TruthClassifiers.h:98
OtherBSM
@ OtherBSM
Definition: TruthClasses.h:78
DalitzDec
@ DalitzDec
Definition: TruthClasses.h:60
top
@ top
Definition: TruthClasses.h:64
MCTruthPartClassifier::defOutComeOfPhoton
ParticleOutCome defOutComeOfPhoton(T thePart)
Definition: TruthClassifiers.h:268
KaonDecay
@ KaonDecay
Definition: TruthClasses.h:91
xAOD::TruthParticle_v1::pdgId
int pdgId() const
PDG ID code.
MCTruthClassifier::defOrigOfTau
MCTruthPartClassifier::ParticleOrigin defOrigOfTau(const xAOD::TruthParticleContainer &xTruthParticleContainer, const xAOD::TruthParticle *, int motherPDG, MCTruthPartClassifier::Info &info) const
Definition: MCTruthClassifierGen.cxx:774
MCTruthClassifier::particleTruthClassifier
virtual std::pair< MCTruthPartClassifier::ParticleType, MCTruthPartClassifier::ParticleOrigin > particleTruthClassifier(const xAOD::TruthParticle *, MCTruthPartClassifier::Info *info=nullptr) const override final
Definition: MCTruthClassifierGen.cxx:75
ParticleType
ParticleType
Definition: TruthClasses.h:8
MCTruthPartClassifier::Info
Definition: IMCTruthClassifier.h:49
Neutrino
Definition: Neutrino.h:33
python.ParticleTypeUtil.info
def info
Definition: ParticleTypeUtil.py:87
xAOD::TruthVertex_v1::outgoingParticle
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
Definition: TruthVertex_v1.cxx:120
UnknownPhoton
@ UnknownPhoton
Definition: TruthClasses.h:22
isMSSMHiggs
bool isMSSMHiggs(const T &p)
APID: Additional Higgs bosons for MSSM (Used in MCTruthClassifier)
Definition: AtlasPID.h:391
BremPhot
@ BremPhot
Definition: TruthClasses.h:92
GenParticle
@ GenParticle
Definition: TruthClasses.h:30
MC::isHardScatteringVertex
bool isHardScatteringVertex(T pVert)
Function to classify the vertex as hard scattering vertex.
Definition: HepMCHelpers.h:169
isMuon
bool isMuon(const T &p)
Definition: AtlasPID.h:202
isLeptoQuark
bool isLeptoQuark(const T &p)
PDG rule 11c: “One-of-a-kind” exotic particles are assigned numbers in the range 41–80.
Definition: AtlasPID.h:405