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