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