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