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_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(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 was created during the simulation (based on the status value).
bool is_same_vertex(const T1 &p1, const T2 &p2)
Method to establish if two vertices in the GenEvent actually represent the same vertex.
ParticleOutCome defOutComeOfElectron(T thePart)
Determine the outcome of a simulated electron from its end vertex (NonInteract, ElectrMagInter,...
ParticleOutCome defOutComeOfPhoton(T thePart)
Determine the outcome of a simulated photon from its end vertex (UnConverted, Converted,...
ParticleType defTypeOfPhoton(ParticleOrigin PhotOrig)
Classify a photon (Unknown/Iso/NonIso/Bkg) from its origin.
ParticleOrigin convHadronTypeToOrig(ParticleType pType, int motherPDG)
Convert a hadron ParticleType to a ParticleOrigin (JPsi if a c-cbar meson has a J/psi mother).
ParticleOutCome defOutComeOfTau(T thePart)
Determine the outcome of a simulated tau from its decay products (NonInteract, NuclInteraction,...
ParticleType defTypeOfTau(ParticleOrigin TauOrig)
Classify a tau (Unknown/Iso/NonIso/Bkg) from its origin.
ParticleType defTypeOfMuon(ParticleOrigin MuOrig, bool isPrompt)
Classify a muon (Unknown/Iso/NonIso/Bkg) from its origin; prompt muons are always Iso.
ParticleType defTypeOfHadron(int pdg)
Classify a hadron by PDG ID into a ParticleType (Unknown if none applies).
int isPrompt(const unsigned int classify, bool allow_prompt_tau_decays=true)
Tell whether a particle is prompt, from the word returned by defOrigOfParticle().
ParticleOutCome defOutComeOfMuon(T thePart)
Determine the outcome of a simulated muon from its end vertex (NonInteract, DecaytoElectron,...
ParticleType defTypeOfElectron(ParticleOrigin EleOrig, bool isPrompt)
Classify an electron (Unknown/Iso/NonIso/Bkg) from its origin; prompt electrons are always Iso.
bool isConditionA(const T &p)
Match selected Pythia status codes for hardest-subprocess and later-stage particles: 21: incoming par...
static const int PI0
static const int UQUARK
static const int KPLUS
T findMatching(C TruthContainer, T p)
Function to find in the container the particle that p descends from in the simulation (see HepMC::is_...
bool isNeutrinoRH(const T &p)
PDG rule 12: Occasionally program authors add their own states.
bool isZ(const T &p)
static const int SQUARK
static const int NEUTRON
static const int DQUARK
PDG Rule 1: Particles are given positive numbers, antiparticles negative numbers.
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: Occasionally program authors add their own states.
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.