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 //--Sherpa tttt with all t->Wb; W->lnu
471 if ((numberOfChildren - NumOfquark - NumOfgluon) == 8 &&
472 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 8) &&
473 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) {
474 ATH_MSG_VERBOSE("MultiBoson for 4topLep");
475 return MultiBoson;
476 }
477 }
478
479 // New Sherpa Z->ee
480 if (partProdVtx == ancestorProdVtx) {
481 int NumOfEleLoop = 0;
482 int NumOfLepLoop = 0;
483 int NumOfEleNeuLoop = 0;
484 for (const auto *const pout: partProdVtx->particles_out()) {
485 if (!pout) continue;
486 for (const auto *const pin: partProdVtx->particles_in()) {
487 if (!pin) continue;
488 if (!HepMC::is_same_particle(pout,pin)) continue;
489 if (MC::isElectron(pout)) NumOfEleLoop++;
490 if (std::abs(pout->pdgId()) == MC::NU_E) NumOfEleNeuLoop++;
491 if (MC::isSMLepton(pout)) NumOfLepLoop++;
492 break; // break out of inner loop after having found two matching particles
493 }
494 }
495 if (NumOfEleLoop == 2 && NumOfEleNeuLoop == 0) return ZBoson;
496 if (NumOfEleLoop == 1 && NumOfEleNeuLoop == 1) return WBoson;
497 if ((NumOfEleLoop == 4 && NumOfEleNeuLoop == 0) || (NumOfEleLoop == 3 && NumOfEleNeuLoop == 1) ||
498 (NumOfEleLoop == 2 && NumOfEleNeuLoop == 2)) return DiBoson;
499 if (NumOfLepLoop == 4) return DiBoson;
500 }
501
502 //-- McAtNLo
503
504 if (MC::isHiggs(ancestorPDG)) return Higgs;
505
506 if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
507
508 if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
509
510 if (MC::isMuon(ancestorPDG)) return Mu;
511 if (MC::isTau(ancestorPDG)) {
512 const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
513 const ParticleType tautype = defTypeOfTau(tauOrig);
514 return (tautype == IsoTau)?tauOrig:TauLep;
515 }
516
517 if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
518 if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
519 if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
520 if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
521 if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
522 if (MC::isSUSY(ancestorPDG)) return SUSY;
523 if (MC::isBSM(ancestorPDG)) return OtherBSM;
524
525 const ParticleType pType = defTypeOfHadron(ancestorPDG);
526 if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
527 return convHadronTypeToOrig(pType, ancestorPDG);
528}
529
530
532 const xAOD::TruthParticle* thePart,
533 bool& isPrompt,
534 MCTruthPartClassifier::Info& info) const
535{
536 ATH_MSG_DEBUG("Executing DefOrigOfMuon ");
537
538 // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
539 const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
540 if (!thePriPart) return NonDefined;
541 if (!MC::isMuon(thePriPart)) return NonDefined;
542
543 //-- to define muon outcome status
544 info.particleOutCome = defOutComeOfMuon(thePriPart);
545
546 const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
547 if (!partProdVtx) return NonDefined;
548
549 if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfMuon:: muon has more than one parent.");
550
551 const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
552 info.setMotherProperties(ancestor);
553 if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
554
555 // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
556
557 if ((MC::isTau(ancestor)|| MC::isW(ancestor)) && ancestor->hasProdVtx()) {
558 int pPDG(0);
559 const xAOD::TruthParticle* ancestorParent{};
560 do {
561 pPDG = 0;
562 ancestorParent = MC::findMother(ancestor);
563 // Start of method 2 of protecting against loops
564 // to prevent Sherpa loop
565 if (ancestor == ancestorParent) { break; }
566 if (TruthLoopDetectionMethod2(ancestor,ancestorParent)) {
567 ancestorParent = ancestor;
568 break;
569 }
570 // End of method 2 of protecting against loops
571
572 if (ancestorParent) {
573 pPDG = ancestorParent->pdgId();// Only set pPDG in the case that we aren't in a loop.
574 if (MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG)) { // FIXME should this be (MC::isTau(pPDG) || MC::isW(pPDG)) ???
575 // There will be another iteration so set ancestor to ancestorParent
576 ancestor = ancestorParent; // ancestorParent is not nullptr here
577 }
578 }
579 } while ((MC::isMuon(pPDG) || MC::isTau(pPDG) || MC::isW(pPDG))); // FIXME should this be (MC::isTau(pPDG) || MC::isW(pPDG)) ???
580
581 if (MC::isTau(pPDG) || MC::isW(pPDG) || MC::isZ(pPDG) || MC::isHiggs(pPDG) ||
582 MC::isMSSMHiggs(pPDG) || MC::isHeavyBoson(pPDG) || MC::isTop(pPDG) || // MSSM Higgs bosons, Heavy bosons( Z', Z'', W'+)
583 std::abs(pPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(pPDG) || // Left-right symmetric model WBoson || Right-handed neutrino (Pythia-specific)
584 MC::isSUSY(pPDG)) {
585 ancestor = ancestorParent; // ancestorParent is not nullptr here
586 }
587 }
588
589 info.setMotherProperties(ancestor);
590 const int ancestorPDG = ancestor->pdgId();
591 const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
592 partProdVtx = ancestor->decayVtx();
593 const int numOfParents = partProdVtx->nIncomingParticles();
594 const int numberOfChildren = partProdVtx->nOutgoingParticles();
595
596 // Determine decay products
597 auto DP = DecayProducts(partProdVtx);
598 const int NumOfPhot = DP.pd(MC::PHOTON);
599 const int NumOfEl = DP.pd(MC::ELECTRON);
600 const int NumOfPos = DP.pd(MC::POSITRON);
601 const int NumOfElNeut = DP.apd(MC::NU_E);
602 const int NumOfMuNeut = DP.apd(MC::NU_MU);
603 const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
604 const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
605 const int NumOfgluon = DP.apd(MC::GLUON);
606 const int NumOfMuPl = DP.pd(-MC::MUON);
607 const int NumOfMuMin = DP.pd(MC::MUON);
608 const int NumOfTau = DP.apd(MC::TAU);
609 const int NumOfTauNeut = DP.apd(MC::NU_TAU);
610 // End of section determining decay products
611
612 if (std::abs(ancestorPDG) == MC::PIPLUS && numberOfChildren == 2 && NumOfMuNeut == 1) return PionDecay;
613 if (std::abs(ancestorPDG) == MC::KPLUS && numberOfChildren == 2 && NumOfMuNeut == 1) return KaonDecay;
614 if (MC::isTau(ancestorPDG)) {
615 const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
616 const ParticleType tautype = defTypeOfTau(tauOrig);
617 return (tautype == IsoTau)?tauOrig:TauLep;
618 }
619
620 if (MC::isTop(ancestorPDG)) return top;
621 // Quark weak decay
622 if (MC::isSMQuark(ancestorPDG) && numOfParents == 1 && numberOfChildren == 3 && NumOfquark == 1 && NumOfMuNeut == 1) return QuarkWeakDec;
623
624 if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
625 const xAOD::TruthVertex* prodVert = ancestorProdVtx;
626 const xAOD::TruthParticle* itrP;
627 do {
628 itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
629 prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
630 } while (MC::isW(itrP) && prodVert);
631
632 if (prodVert && prodVert->nIncomingParticles() == 1) {
633 if (std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
634 if (std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
635 if (std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
636 }
637 return WBoson;
638 }
639 if (MC::isW(ancestorPDG)) return WBoson;
640 if (MC::isZ(ancestorPDG)) return ZBoson;
641 if (MC::isPhoton(ancestorPDG) && numberOfChildren == 2 && NumOfMuMin == 1 && NumOfMuPl == 1) return PhotonConv;
642 //-- Exotics
643
644 // MadGraphPythia ZWW*->lllnulnu
645 if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
646 bool isZboson = false;
647 bool isWboson = false;
648 bool skipnext = false;
649 for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
650 if (skipnext) {
651 skipnext = false;
652 continue;
653 }
654 const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
655 if (!aChild) continue;
656 const xAOD::TruthParticle* theNextChild{};
657 for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
658 theNextChild = partProdVtx->outgoingParticle(ipOut1);
659 if (theNextChild) break;
660 }
661 if (!theNextChild) continue;
662 if (MC::isMuon(aChild) && MC::isMuon(theNextChild)) {
663 // Zboson
664 if (thePriPart == aChild || thePriPart == theNextChild) {
665 isZboson = true;
666 break;
667 }
668 skipnext = true;
669 } else if (MC::isMuon(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_MU) {
670 // WBoson
671 if (thePriPart == aChild || thePriPart == theNextChild) {
672 isWboson = true;
673 break;
674 }
675 skipnext = true;
676 }
677 }
678 if (isWboson) return WBoson;
679 if (isZboson) return ZBoson;
680 }
681 if (numOfParents == 2 ) {
682 //--Sherpa Z->mumu
683 if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfMuPl == 1 && NumOfMuMin == 1) return ZBoson;
684
685 //--Sherpa W->munu ??
686 // if(numOfParents==2&&(numberOfChildren-NumOfquark-NumOfgluon)==2&&(NumOfEl==1||NumOfPos==1)&&NumOfElNeut==1) return WBoson;
687 if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && (NumOfMuPl == 1 || NumOfMuMin == 1) && NumOfMuNeut == 1) return WBoson;
688
689 const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
690 const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
691 //--Sherpa ZZ,ZW
692 if ((numberOfChildren - NumOfquark - NumOfgluon) == 4 &&
693 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
694 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
695
696 //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
697 if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 &&
698 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
699 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
700
701 //--Sherpa tttt with all t->Wb; W->lnu
702 if ((numberOfChildren - NumOfquark - NumOfgluon) == 8 &&
703 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 8) &&
704 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) {
705 ATH_MSG_VERBOSE("MultiBoson for 4topLep");
706 return MultiBoson;
707 }
708
709
710 }
711
712 //--New Sherpa Z->mumu
713 if (partProdVtx == ancestorProdVtx) {
714 int NumOfMuLoop = 0;
715 int NumOfMuNeuLoop = 0;
716 int NumOfLepLoop = 0;
717 for (const auto & pout: partProdVtx->particles_out()) {
718 if (!pout) continue;
719 for (const auto & pin: partProdVtx->particles_in()) {
720 if (!pin) continue;
721 if (HepMC::is_same_particle(pout,pin)) {
722 if (MC::isMuon(pout)) NumOfMuLoop++;
723 if (std::abs(pout->pdg_id()) == MC::NU_MU) NumOfMuNeuLoop++;
724 if (MC::isSMLepton(pout)) NumOfLepLoop++;
725 break; // break out of inner loop after having found two matching particles
726 }
727 }
728 }
729 if (NumOfMuLoop == 2 && NumOfMuNeuLoop == 0) return ZBoson;
730 if (NumOfMuLoop == 1 && NumOfMuNeuLoop == 1) return WBoson;
731 if ((NumOfMuLoop == 4 && NumOfMuNeuLoop == 0) || (NumOfMuLoop == 3 && NumOfMuNeuLoop == 1) ||
732 (NumOfMuLoop == 2 && NumOfMuNeuLoop == 2)) return DiBoson;
733 if (NumOfLepLoop == 4) return DiBoson;
734 }
735
736 //-- McAtNLo
737
738 if (MC::isHiggs(ancestorPDG)) return Higgs;
739
740 if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
741
742 if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
743
744 if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
745 if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
746 if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
747 if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
748 if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
749 if (MC::isSUSY(ancestorPDG)) return SUSY;
750 if (MC::isBSM(ancestorPDG)) return OtherBSM;
751
752 const ParticleType pType = defTypeOfHadron(ancestorPDG);
753 if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
754
755 return convHadronTypeToOrig(pType, ancestorPDG);
756}
757
758
760 const xAOD::TruthParticle* thePart,
761 int ancestorPDGin,
762 MCTruthPartClassifier::Info& info) const
763{
764 ATH_MSG_DEBUG("Executing DefOrigOfTau ");
765
766 // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
767 const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
768 if (!thePriPart) return NonDefined;
769 if (!MC::isTau(thePriPart)) return NonDefined;
770
771 //-- to define tau outcome status
772 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?
773
774 const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
775 if (!partProdVtx) return NonDefined;
776
777 if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfTau:: tau has more than one parent.");
778
779 const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
780 info.setMotherProperties(ancestor);
781 if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
782
783 // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
784
785 // Difference from defOrigOfElectron and defOrigOfMuon - no loop through ancestor particles
786
787 if (MC::isW(ancestorPDGin) && ancestor->hasProdVtx()) { // FIXME ancestorPDGin here could in principle be inconsistent with ancestorProdVtx
788 const xAOD::TruthParticle* ancestorParent = MC::findMother(ancestor);
789 if (ancestorParent && MC::isTop(ancestorParent->pdgId())) {
790 ancestor = ancestorParent; //...so ancestor cannot be nullptr
791 }
792 }
793
794 const int ancestorPDG = ancestor->pdgId();
795 info.setMotherProperties(ancestor);
796 const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
797 partProdVtx = ancestor->decayVtx();
798 if (!partProdVtx) return NonDefined; // FIXME not sure this could ever be true?
799 const int numOfParents = partProdVtx->nIncomingParticles();
800
801 // Determine decay products
802 auto DP = DecayProducts(partProdVtx);
803 const int numberOfChildren = DP.size();
804 const int NumOfPhot = DP.pd(MC::PHOTON);
805 const int NumOfEl = DP.pd(MC::ELECTRON);
806 const int NumOfPos = DP.pd(MC::POSITRON);
807 const int NumOfElNeut = DP.apd(MC::NU_E);
808 const int NumOfMuNeut = DP.apd(MC::NU_MU);
809 /* const int NumOfLQ = DP.apd(MC::LEPTOQUARK); */ // FIXME Leptoquarks not an option?
810 const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
811 const int NumOfgluon = DP.apd(MC::GLUON);
812 const int NumOfMuPl = DP.pd(-MC::MUON);
813 const int NumOfMuMin = DP.pd(MC::MUON);
814 const int NumOfTau = DP.apd(MC::TAU);
815 const int NumOfTauNeut = DP.apd(MC::NU_TAU);
816 // End of section determining decay products
817
818 if (MC::isTop(ancestorPDG)) return top;
819 if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
820 const xAOD::TruthVertex* prodVert = ancestorProdVtx;
821 const xAOD::TruthParticle* itrP;
822 do {
823 itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
824 prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
825 } while (MC::isW(itrP) && prodVert);
826
827 if (prodVert && prodVert->nIncomingParticles() == 1 ) {
828 if (std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
829 if (std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
830 if (std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
831 }
832 return WBoson;
833 }
834 if (MC::isW(ancestorPDG)) { return WBoson;}
835 if (MC::isZ(ancestorPDG)) { return ZBoson;}
836 if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
837 bool isZboson = false;
838 bool isWboson = false;
839 bool skipnext = false;
840 for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
841 if (skipnext) {
842 skipnext = false;
843 continue;
844 }
845 const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
846 if (!aChild) continue;
847 const xAOD::TruthParticle* theNextChild{};
848 for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
849 theNextChild = partProdVtx->outgoingParticle(ipOut1);
850 if (theNextChild) break;
851 }
852 if (!theNextChild) {
853 continue;
854 }
855 if (MC::isTau(aChild) && MC::isTau(theNextChild)) {
856 // Zboson
857 if (thePriPart == aChild || thePriPart == theNextChild) {
858 isZboson = true;
859 break;
860 }
861 skipnext = true;
862 } else if (MC::isTau(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_TAU) {
863 // WBoson
864 if (thePriPart == aChild || thePriPart == theNextChild) {
865 isWboson = true;
866 break;
867 }
868 skipnext = true;
869 }
870 }
871 if (isWboson) return WBoson;
872 if (isZboson) return ZBoson;
873 }
874 if (numOfParents == 2 ) {
875 const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
876 const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
877 //--Sherpa Z->tautau
878 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?
879
880 //--Sherpa W->taunu new
881 if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && NumOfTau == 1 && NumOfTauNeut == 1) return WBoson;
882
883 //--Sherpa ZZ,ZW
884 if ((numberOfChildren - NumOfquark - NumOfgluon) == 4 &&
885 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
886 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
887
888 //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
889 if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 &&
890 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
891 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
892
893 //--Sherpa tttt with all t->Wb; W->lnu
894 if ((numberOfChildren - NumOfquark - NumOfgluon) == 8 &&
895 (NumOfEl + NumOfPos + NumOfMuPl + NumOfMuMin + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 8) &&
896 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) {
897 ATH_MSG_VERBOSE("MultiBoson for 4topLep");
898 return MultiBoson;
899 }
900 }
901
902 // New Sherpa Z->tautau
903 if (partProdVtx == ancestorProdVtx) {
904 int NumOfTauLoop = 0;
905 int NumOfTauNeuLoop = 0;
906 int NumOfLepLoop = 0;
907 for ( const auto *const pout: partProdVtx->particles_out()) {
908 if (!pout) continue;
909 for (const auto *const pin: partProdVtx->particles_in()) {
910 if (!pin) continue;
911 if (!HepMC::is_same_particle(pout,pin)) continue;
912 if (MC::isTau(pout)) NumOfTauLoop++;
913 if (std::abs(pout->pdgId()) == MC::NU_TAU) NumOfTauNeuLoop++;
914 if (MC::isSMLepton(pout)) NumOfLepLoop++;
915 break; // break out of inner loop after having found two matching particles
916 }
917 }
918 if (NumOfTauLoop == 2 && NumOfTauNeuLoop == 0) return ZBoson;
919 if (NumOfTauLoop == 1 && NumOfTauNeuLoop == 1) return WBoson;
920 if ((NumOfTauLoop == 4 && NumOfTauNeuLoop == 0) || (NumOfTauLoop == 3 && NumOfTauNeuLoop == 1) || (NumOfTauLoop == 2 && NumOfTauNeuLoop == 2)) return DiBoson;
921 if (NumOfLepLoop == 4) return DiBoson;
922 }
923
924 //-- McAtNLo
925
926 if (MC::isHiggs(ancestorPDG)) return Higgs;
927 if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
928 if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
929 if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
930 if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
931 if (MC::isSUSY(ancestorPDG)) return SUSY;
932 if (MC::isBSM(ancestorPDG)) return OtherBSM;
933 if (std::abs(ancestorPDG) == MC::JPSI) return JPsi;
934
935 const ParticleType pType = defTypeOfHadron(ancestorPDG);
936 return convHadronTypeToOrig(pType, ancestorPDG);
937}
938
939
941 const xAOD::TruthParticle* thePart,
942 bool& isPrompt,
943 MCTruthPartClassifier::Info& info) const
944{
945 if (!thePart) return NonDefined; // FIXME Why is this extra protection needed for this function and not the others?
946 ATH_MSG_DEBUG("Executing DefOrigOfPhoton ");
947
948 info.resetMotherProperties();
949 info.photonMother = nullptr;
950
951 // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
952 const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
953 if (!thePriPart) return NonDefined;
954 if (!MC::isPhoton(thePriPart)) return NonDefined;
955
956 const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
957
958 //-- to define photon outcome status
959 info.particleOutCome = defOutComeOfPhoton(thePriPart);
960
961 if (!partProdVtx) return NonDefined;
962
963 int numOfParents = partProdVtx->nIncomingParticles();
964 if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfPhoton:: photon has more than one parent.");
965
966 const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
967 info.setMotherProperties(ancestor);
968 if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
969
970 int ancestorPDG = ancestor->pdgId();
971 // "method 1" for finding Sherpa loops from defOrigOfElectron not used here. Why?
972 const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
973
974 partProdVtx = ancestor->decayVtx(); // FIXME how often does this line actually change the pointer???
975 numOfParents = partProdVtx->nIncomingParticles();
976
977 const int numberOfChildren = partProdVtx->nOutgoingParticles();
978
979 // Determine decay products
980 auto DP = DecayProducts(partProdVtx);
981 const int NumOfEl = DP.pd(MC::ELECTRON);
982 const int NumOfPos = DP.pd(MC::POSITRON);
983 const int NumOfMu = DP.apd(MC::MUON);
984 const int NumOfTau = DP.apd(MC::TAU);
985 const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
986 const int NumOfLep = NumOfEl + NumOfPos + NumOfMu + NumOfTau;
987 const int NumOfNeut = DP.apd({MC::NU_E,MC::NU_MU,MC::NU_TAU});
988 const int NumOfPht = DP.pd(MC::PHOTON);
989
990 int childPDG(0);
991 int NumOfPartons(0);
992 int NumOfNucFr(0);
993 const bool possibleNuclearFragment = (numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG) || std::abs(ancestorPDG) == MC::PIPLUS));
994 const xAOD::TruthParticle* child{};
995 for (const auto& pout: partProdVtx->particles_out()) {
996 if (!pout) continue;
997 childPDG = pout->pdg_id();
998 if (possibleNuclearFragment &&
999 (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?
1000 NumOfNucFr++;
1001 }
1002 if (std::abs(childPDG) < MC::ELECTRON ||
1003 (std::abs(childPDG) > MC::NU_TAU && std::abs(childPDG) < 43 && !MC::isPhoton(childPDG))) {
1004 // FIXME Too loose? This definition picks up 4th generation quarks and leptons as well as all gauge bosons and leptoquarks.
1005 // Suggest MC::isSMQuark(childPDG) || (MC::isBoson(childPDG) && !MC::isPhoton(childPDG))
1006 // or maybe even MC::isSMQuark(childPDG) || MC::isGluon(childPDG)
1007 // AKA const int NumOfPartons = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK,MC::GLUON});
1008 NumOfPartons++;
1009 }
1010 if (childPDG == ancestorPDG) {
1011 child = pout;
1012 }
1013 }
1014 // End of section determining decay products
1015
1016 bool foundISR = false;
1017 bool foundFSR = false;
1018 if (numOfParents == 1 && numberOfChildren == 2 && child && HepMC::is_same_generator_particle(child, ancestor)) return BremPhot;
1019 if (numOfParents == 1 && numberOfChildren == 2 && MC::isElectron(ancestorPDG) && NumOfPht == 2) return ElMagProc;
1020
1021 // decay of W,Z and Higgs to lepton with FSR generated by Pythia
1022 if (numOfParents == 1 && numberOfChildren == 2 && (MC::isElectron(ancestorPDG) || MC::isMuon(ancestorPDG) || MC::isTau(ancestorPDG)) &&
1023 !(child && HepMC::is_same_generator_particle(child, ancestor)) && ancestorProdVtx &&
1024 ancestorProdVtx->nIncomingParticles() == 1) {
1025 int itr = 0;
1026 int PartPDG = 0;
1027 const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1028 const xAOD::TruthVertex* Vert{};
1029 do {
1030 Vert = prodVert;
1031 for (const auto & pin: Vert->particles_in()) {
1032 if (!pin) continue;
1033 PartPDG = std::abs(pin->pdgId());
1034 prodVert = pin->prodVtx();
1035 if (MC::isZ(PartPDG) || MC::isW(PartPDG) || MC::isHiggs(PartPDG)) foundFSR = true;
1036 }
1037 itr++;
1038 if (itr > 100) { // FIXME Improve loop detection here?
1039 ATH_MSG_WARNING("DefOrigOfPhoton:: infinite while");
1040 break;
1041 }
1042 } while (prodVert && std::abs(ancestorPDG) == PartPDG);
1043
1044 if (foundFSR) return FSRPhot;
1045 }
1046
1047 // Nucl reaction
1048 // gamma+Nuclear=>gamma+Nucl.Fr+Nuclons+pions
1049 // e+Nuclear=>e+gamma+Nucl.Fr+Nuclons+pions
1050 // pi+Nuclear=>gamma+Nucl.Fr+Nuclons+pions
1051
1052 if ((numOfParents == 1 && (MC::isPhoton(ancestorPDG) || MC::isElectron(ancestorPDG)) && numberOfChildren > 2 && NumOfNucFr != 0) ||
1053 (numOfParents == 1 && std::abs(ancestorPDG) == MC::PIPLUS && numberOfChildren > 10 && NumOfNucFr != 0) ||
1054 (numOfParents == 1 && MC::isPhoton(ancestorPDG) && numberOfChildren > 10 && MC::isStable(ancestor)) ||
1055 (numOfParents == 1 && MC::isNucleus(ancestorPDG) && std::abs(ancestorPDG) != MC::PROTON)) // FIXME vetoing protons here to preserve previous behaviour
1056 return NucReact;
1057
1058 if (MC::isMuon(ancestorPDG) && NumOfMu == 0) return Mu;
1059 if (MC::isTau(ancestorPDG) && NumOfTau == 0) return TauLep;
1060
1061 if (numOfParents == 1 && ancestor->status() == 3) return (foundISR)? ISRPhot:UndrPhot; // FIXME foundISR is always false at this point
1062
1063 //-- to find initial and final state raiation and underline photons
1064 //-- SUSY
1065 if (numOfParents == 1 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG)) &&
1066 (numberOfChildren != NumOfPht + NumOfPartons || !MC::Pythia8::isConditionA(ancestor))) {
1067 for (const auto& pout: partProdVtx->particles_out()) {
1068 if (!pout) continue;
1069 if (ancestorPDG != pout->pdgId()) continue;
1070 const xAOD::TruthVertex* Vrtx = pout->decayVtx();
1071 if (!Vrtx) continue;
1072 if (Vrtx->nOutgoingParticles() != 1 && Vrtx->nIncomingParticles() == 1) continue;
1073 if (!Vrtx->outgoingParticle(0)) continue;
1074 if (Vrtx->outgoingParticle(0)->pdgId() == 91) foundISR = true; // Herwig "cluster"
1075 }
1076 return (foundISR)?ISRPhot:UndrPhot;
1077 }
1078
1079 //-- to find final state radiation
1080 //-- Exotics
1081
1082 // FSR from Photos
1083 //-- Exotics- CompHep
1084 if (numOfParents == 2 && ((MC::isElectron(ancestorPDG) && NumOfEl == 1 && NumOfPos == 1) || (MC::isMuon(ancestorPDG) && NumOfMu == 2) || (MC::isTau(ancestorPDG) && NumOfTau == 2))) {
1085 if (std::abs(partProdVtx->incomingParticle(0)->pdgId()) == std::abs(partProdVtx->incomingParticle(1)->pdgId())) return FSRPhot;
1086 }
1087
1088 if (numOfParents == 2 && NumOfLep == 1 && NumOfNeut == 1 && (MC::isElectron(ancestorPDG) || std::abs(ancestorPDG) == MC::NU_E)) return FSRPhot;
1089
1090 //-- Exotics - CompHep
1091 if (MC::isElectron(ancestorPDG) && numOfParents == 1 && numberOfChildren == 2 && (NumOfEl == 1 || NumOfPos == 1) && NumOfPht == 1 &&
1092 !( child && HepMC::is_same_generator_particle(child, ancestor)) && !HepMC::is_simulation_particle(child) && !HepMC::is_simulation_particle(ancestor))
1093 return FSRPhot;
1094
1095 // FSR from Photos
1096 if (MC::isZ(ancestorPDG) && ((NumOfEl + NumOfPos == 2 || NumOfEl + NumOfPos == 4) || (NumOfMu == 2 || NumOfMu == 4) || (NumOfTau == 2 || NumOfTau == 4)) && NumOfPht > 0) return FSRPhot;
1097
1098 if (NumOfPht > 0 && (std::abs(ancestorPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(ancestorPDG))) return FSRPhot; // Left-right symmetric model WBoson || Right-handed neutrinos (Pythia-specific)
1099
1100 if (numOfParents == 2 && NumOfLQ == 1) return FSRPhot;
1101
1102 //--- other process
1103
1104 if (MC::isZ(ancestorPDG)) return ZBoson;
1105 if (MC::isW(ancestorPDG)) {
1106
1107 if (NumOfLep == 1 && NumOfNeut == 1 && numberOfChildren == NumOfLep + NumOfNeut + NumOfPht) return FSRPhot;
1108
1109 if (ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
1110 const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1111 const xAOD::TruthParticle* itrP;
1112 do {
1113 itrP = prodVert->incomingParticle(0); // FIXME just taking the first one
1114 prodVert = itrP->hasProdVtx() ? itrP->prodVtx() : nullptr;
1115 } while (MC::isW(itrP) && prodVert);
1116
1117 if (prodVert && prodVert->nIncomingParticles() == 1 ) {
1118 if ( MC::isTau(itrP)) return TauLep;
1119 if ( MC::isMuon(itrP)) return Mu;
1120 if ( std::abs(itrP->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1121 if ( std::abs(itrP->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1122 if ( std::abs(itrP->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1123 }
1124 } else
1125 return WBoson;
1126 }
1127
1128 // MadGraphPythia ZWW*->lllnulnu
1129 if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
1130 bool isZboson = false;
1131 bool isWboson = false;
1132 bool skipnext = false;
1133 for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ipOut++) {
1134 if (skipnext) {
1135 skipnext = false;
1136 continue;
1137 }
1138 const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
1139 if (!aChild) continue;
1140 const xAOD::TruthParticle* theNextChild{};
1141 for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
1142 theNextChild = partProdVtx->outgoingParticle(ipOut1);
1143 if (theNextChild) break;
1144 }
1145 if (!theNextChild) continue;
1146 if (MC::isTau(aChild) && MC::isTau(theNextChild)) {
1147 // Zboson
1148 if (thePriPart == aChild || thePriPart == theNextChild) {
1149 isZboson = true;
1150 break;
1151 }
1152 skipnext = true;
1153 } else if (MC::isTau(aChild) && std::abs(theNextChild->pdgId()) == MC::NU_TAU) {
1154 // WBoson
1155 if (thePriPart == aChild || thePriPart == theNextChild) {
1156 isWboson = true;
1157 break;
1158 }
1159 skipnext = true;
1160 }
1161 }
1162 if (isWboson) return WBoson;
1163 if (isZboson) return ZBoson;
1164 }
1165
1166 //--Sherpa ZZ,ZW+FSR
1167 if (numOfParents == 4 && (numberOfChildren - NumOfPht) == 4 && (NumOfLep + NumOfNeut == 4)) {
1168 if (MC::isSMLepton(partProdVtx->incomingParticle(0))&&MC::isSMLepton(partProdVtx->incomingParticle(1))
1169 && MC::isSMLepton(partProdVtx->incomingParticle(2))&&MC::isSMLepton(partProdVtx->incomingParticle(3)))
1170 return FSRPhot;
1171 }
1172
1173 //--New Sherpa single photon
1174 if (partProdVtx == ancestorProdVtx) {
1175 for (const auto *const pout: partProdVtx->particles_out()) {
1176 if (!pout) continue;
1177 for (const auto *const pin: partProdVtx->particles_in()) {
1178 if (!pin) continue;
1179 if (!HepMC::is_same_particle(pout,pin)) continue;
1180 if (MC::isPhoton(pout)) return SinglePhot;
1181 break; // break out of inner loop after having found two matching particles
1182 }
1183 }
1184 }
1185
1186 if (MC::isHiggs(ancestorPDG)) return Higgs;
1187 if (std::abs(ancestorPDG) == MC::PI0) return PiZero;
1188 if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
1189 if (MC::isHeavyBoson(ancestorPDG) || std::abs(ancestorPDG) == 5100039 ) return HeavyBoson; // Heavy Bosons (Z' Z'' W'+) + KK excited graviton
1190
1191 if (MC::isSUSY(ancestorPDG)) return SUSY;
1192 if (MC::isBSM(ancestorPDG)) return OtherBSM;
1193
1194 // Pythia8 gamma+jet samples
1195 if (MC::Pythia8::isConditionA(ancestor) && MC::isStable(thePriPart) && NumOfPht == 1 && numberOfChildren == (NumOfPht + NumOfPartons)) return PromptPhot;
1196
1197 const ParticleType pType = defTypeOfHadron(ancestorPDG);
1198 if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
1199 return convHadronTypeToOrig(pType, ancestorPDG);
1200}
1201
1204 const xAOD::TruthParticle* thePart,
1205 bool& isPrompt,
1206 MCTruthPartClassifier::Info& info) const
1207{
1208 ATH_MSG_DEBUG("Executing DefOrigOfNeutrino ");
1209
1210 const int nuFlav = std::abs(thePart->pdgId());
1211 // Find the first copy of this particle stored in the xAOD::TruthParticleContainer (i.e. the particle prior to any interactions)
1212 const xAOD::TruthParticle* thePriPart = MC::findMatching(xTruthParticleContainer, thePart);
1213 if (!thePriPart) return NonDefined;
1214 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)
1215
1216 //-- to define neutrino outcome status
1217 info.particleOutCome = NonInteract;
1218
1219 const xAOD::TruthVertex* partProdVtx = thePriPart->hasProdVtx() ? thePriPart->prodVtx() : nullptr;
1220 if (!partProdVtx) return NonDefined;
1221
1222 if (partProdVtx->nIncomingParticles() > 1) ATH_MSG_DEBUG("DefOrigOfNeutrino:: neutrino has more than one parent.");
1223
1224 const xAOD::TruthParticle* ancestor = MC::findMother(thePriPart);
1225 info.setMotherProperties(ancestor);
1226 if (!ancestor) { return NonDefined; } // ancestor is not a nullptr beyond this point
1227
1228 // Start of method 3 of protecting against loops
1229 // to resolve Sherpa loop
1230 bool samePart = TruthLoopDetectionMethod1(partProdVtx, ancestor);
1231 // End of method 3 of protecting against loops
1232
1233 if ((std::abs(ancestor->pdgId()) == nuFlav || MC::isTau(ancestor) || MC::isW(ancestor)) && ancestor->hasProdVtx() && !samePart) {
1234 int pPDG(0);
1235 const xAOD::TruthParticle* ancestorParent{};
1236 do {
1237 pPDG = 0;
1238 ancestorParent = MC::findMother(ancestor);
1239 // Start of method 2 of protecting against loops
1240 // to prevent Sherpa loop
1241 if (TruthLoopDetectionMethod2(ancestor,ancestorParent)) {
1242 ancestorParent = ancestor;
1243 break;
1244 }
1245 //
1246 if (ancestorParent) {
1247 pPDG = ancestorParent->pdgId(); // FIXME difference in behaviour compared to defOrigOfElectron/Muon pPDG set even if we are in a loop
1248 }
1249 // to prevent Sherpa loop
1250 if (ancestor == ancestorParent) { break; }
1251 // End of method 2 of protecting against Sherpa loops
1252 if (std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG) ) {
1253 // There will be another iteration so set ancestor to ancestorParent
1254 ancestor = ancestorParent; // ancestorParent is not a nullptr
1255 info.setMotherProperties(ancestor); // FIXME difference in behaviour compared to MCTruthClassifier::defOrigOfElectron/Muon
1256 }
1257
1258 } while ((std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG)));
1259
1260 if (std::abs(pPDG) == nuFlav || MC::isTau(pPDG) || MC::isW(pPDG) || MC::isZ(pPDG) || MC::isHiggs(pPDG) ||
1261 MC::isMSSMHiggs(pPDG) || MC::isHeavyBoson(pPDG) || MC::isTop(pPDG) || // MSSM Higgs bosons, Heavy bosons( Z', Z'', W'+)
1262 std::abs(pPDG) == MC::WBOSON_LRSM || MC::isNeutrinoRH(pPDG) || // Left-right symmetric model WBoson || Right-handed neutrino (Pythia-specific)
1263 MC::isSUSY(pPDG)) {
1264 ancestor = ancestorParent; // ancestorParent is not nullptr here
1265 info.setMotherProperties(ancestor);
1266 }
1267 }
1268 //if ancestor is still nullptr, we have a problem
1269 if (!ancestor) return NonDefined; // FIXME it should not be possible for ancestor to be nullptr at this point???
1270
1271 info.setMotherProperties(ancestor);
1272 const int ancestorPDG = ancestor->pdgId();
1273 partProdVtx = ancestor->decayVtx();
1274 const xAOD::TruthVertex* ancestorProdVtx = ancestor->hasProdVtx() ? ancestor->prodVtx() : nullptr;
1275 const int numOfParents = partProdVtx->nIncomingParticles();
1276 const int numberOfChildren = partProdVtx->nOutgoingParticles();
1277
1278 // Determine decay products
1279 auto DP = DecayProducts(partProdVtx);
1280 const int NumOfPhot = DP.pd(MC::PHOTON);
1281 const int NumOfquark = DP.apd({MC::DQUARK,MC::UQUARK,MC::SQUARK,MC::CQUARK,MC::BQUARK,MC::TQUARK});
1282 const int NumOfgluon = DP.apd(MC::GLUON);
1283 const int NumOfLQ = DP.apd(MC::LEPTOQUARK);
1284 const int NumOfElNeut = DP.apd(MC::NU_E);
1285 const int NumOfMuNeut = DP.apd(MC::NU_MU);
1286 const int NumOfTauNeut = DP.apd(MC::NU_TAU);
1287 const int NumOfEl = DP.apd(MC::ELECTRON);
1288 const int NumOfMu = DP.apd(MC::MUON);
1289 const int NumOfTau = DP.apd(MC::TAU);
1290
1291 samePart = false;
1292 for (const auto& aChild: partProdVtx->particles_out()) {
1293 if (!aChild) continue;
1294 if (aChild->pdgId() == ancestorPDG && HepMC::is_same_generator_particle(aChild,ancestor)) {
1295 samePart = true;
1296 break;
1297 }
1298 }
1299 // End of section determining decay products
1300
1301 // Quark weak decay
1302 if (MC::isQuark(ancestorPDG) && numOfParents == 1 && numberOfChildren == 3 && NumOfquark == 1 && (NumOfEl == 1 || NumOfMu == 1 || NumOfTau == 1)) return QuarkWeakDec;
1303 if (MC::isTop(ancestorPDG)) return top;
1304
1305 if (MC::isW(ancestorPDG) && ancestorProdVtx && ancestorProdVtx->nIncomingParticles() != 0) {
1306 const xAOD::TruthVertex* prodVert = ancestorProdVtx;
1307 const xAOD::TruthParticle* ptrPart;
1308 do {
1309 ptrPart = prodVert->incomingParticle(0); // FIXME just taking the first one
1310 prodVert = ptrPart->hasProdVtx() ? ptrPart->prodVtx() : nullptr;
1311 } while (MC::isW(ptrPart) && prodVert);
1312
1313 if (prodVert && prodVert->nIncomingParticles() == 1) {
1314 if (std::abs(ptrPart->pdgId()) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1315 if (std::abs(ptrPart->pdgId()) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1316 if (std::abs(ptrPart->pdgId()) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1317 }
1318 return WBoson;
1319 }
1320 if (MC::isW(ancestorPDG)) return WBoson;
1321 if (MC::isZ(ancestorPDG)) return ZBoson;
1322
1323 //-- Exotics
1324
1325 // MadGraphPythia ZWW*->lllnulnu or ZWW*->nunulnulnu (don't even know if the latter is generated)
1326 if (numOfParents == 1 && numberOfChildren > 4 && (MC::isSMQuark(ancestorPDG) || MC::isGluon(ancestorPDG))) {
1327
1328 const xAOD::TruthParticle* thePartToCheck = thePriPart;
1329 const xAOD::TruthParticle* theParent = thePriPart->hasProdVtx() ? thePriPart->prodVtx()->incomingParticle(0) : nullptr; // FIXME just taking the first one
1330
1331 if (MC::isElectron(theParent) && MC::isDecayed(theParent)) { thePartToCheck = theParent; }
1332 bool isZboson = false;
1333 bool isWboson = false;
1334 bool skipnext = false;
1335
1336 for (unsigned int ipOut = 0; ipOut + 1 < partProdVtx->nOutgoingParticles(); ++ipOut) {
1337 const xAOD::TruthParticle* aChild = partProdVtx->outgoingParticle(ipOut);
1338 if (!aChild) continue;
1339 const xAOD::TruthParticle* theNextChild{};
1340 for (unsigned int ipOut1 = ipOut + 1; ipOut1 < partProdVtx->nOutgoingParticles(); ipOut1++) {
1341 theNextChild = partProdVtx->outgoingParticle(ipOut1);
1342 if (theNextChild) break;
1343 }
1344 if (!theNextChild) continue;
1345
1346 if (skipnext) {
1347 skipnext = false;
1348 continue;
1349 }
1350
1351 const int apdgID1 = std::abs(aChild->pdgId());
1352 const int apdgID2 = std::abs(theNextChild->pdgId());
1353 if (apdgID1 == apdgID2 && MC::isSMNeutrino(apdgID1)) {
1354 // Zboson
1355 if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
1356 isZboson = true;
1357 break;
1358 }
1359 skipnext = true;
1360 } else if ((apdgID1 == MC::ELECTRON && apdgID2 == MC::NU_E) ||
1361 (apdgID1 == MC::NU_E && apdgID2 == MC::ELECTRON) ||
1362 (apdgID1 == MC::MUON && apdgID2 == MC::NU_MU) ||
1363 (apdgID1 == MC::NU_MU && apdgID2 == MC::MUON) ||
1364 (apdgID1 == MC::TAU && apdgID2 == MC::NU_TAU) ||
1365 (apdgID1 == MC::NU_TAU && apdgID2 == MC::TAU)
1366 ) {
1367 // WBoson
1368 if (thePartToCheck == aChild || thePartToCheck == theNextChild) {
1369 isWboson = true;
1370 break;
1371 }
1372 skipnext = true;
1373 }
1374 }
1375 if (isWboson) return WBoson;
1376 if (isZboson) return ZBoson;
1377 }
1378
1379 if (numOfParents == 2) {
1380 //--Sherpa Z->nunu
1381 if ( (numberOfChildren - NumOfquark - NumOfgluon) == 2 && (NumOfElNeut == 2 || NumOfMuNeut == 2 || NumOfTauNeut == 2)) return ZBoson;
1382
1383 //--Sherpa W->enu ??
1384 if ((numberOfChildren - NumOfquark - NumOfgluon) == 2 && ((NumOfEl == 1 && NumOfElNeut == 1) || (NumOfMu == 1 && NumOfMuNeut == 1) || (NumOfTau == 1 && NumOfTauNeut == 1))) return WBoson;
1385
1386 const int pdg1 = partProdVtx->incomingParticle(0)->pdgId();
1387 const int pdg2 = partProdVtx->incomingParticle(1)->pdgId();
1388 //--Sherpa ZZ,ZW
1389 if ( (numberOfChildren - NumOfquark - NumOfgluon) == 4 && (NumOfEl + NumOfMu + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 4) &&
1390 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return DiBoson;
1391
1392 //--Sherpa VVV -- Note, have to allow for prompt photon radiation or these get lost
1393 if ((numberOfChildren - NumOfquark - NumOfgluon - NumOfPhot) == 6 && (NumOfEl + NumOfMu + NumOfTau + NumOfElNeut + NumOfMuNeut + NumOfTauNeut == 6) &&
1394 (MC::isQuark(pdg1)||MC::isGluon(pdg1)) && (MC::isQuark(pdg2)||MC::isGluon(pdg2))) return MultiBoson;
1395 }
1396
1397 // New Sherpa Z->nunu
1398 if (partProdVtx == ancestorProdVtx) {
1399 int NumOfLepLoop = 0;
1400 int NumOfNeuLoop = 0;
1401 for (const auto *const pout: partProdVtx->particles_out()) {
1402 if (!pout) continue;
1403 for (const auto *const pin: partProdVtx->particles_in()) {
1404 if (!pin) continue;
1405 if (HepMC::is_same_particle(pin,pout)) continue;
1406 const int apdgid = std::abs(pout->pdgId());
1407 if (MC::isSMLepton(apdgid)) {
1408 if (MC::isSMNeutrino(apdgid)) { NumOfNeuLoop++; }
1409 else { NumOfLepLoop++; }
1410 }
1411 break; // break out of inner loop after having found two matching particles
1412 }
1413 }
1414 if (NumOfNeuLoop == 2 && NumOfLepLoop == 0) return ZBoson;
1415 if (NumOfNeuLoop == 1 && NumOfLepLoop == 1) return WBoson;
1416 if (NumOfNeuLoop + NumOfLepLoop == 4) return DiBoson;
1417 }
1418
1419 //-- McAtNLo
1420
1421 if (MC::isHiggs(ancestorPDG)) return Higgs;
1422 if (MC::isMSSMHiggs(ancestorPDG)) return HiggsMSSM; // MSSM Higgs bosons
1423 if (MC::isHeavyBoson(ancestorPDG)) return HeavyBoson; // Heavy bosons( Z', Z'', W'+)
1424
1425 if (MC::isTau(ancestorPDG)) {
1426 const ParticleOrigin tauOrig = defOrigOfTau(xTruthParticleContainer, ancestor, ancestorPDG, info);
1427 const ParticleType tautype = defTypeOfTau(tauOrig);
1428 return (tautype == IsoTau)?tauOrig:TauLep;
1429 }
1430
1431 if (std::abs(ancestorPDG) == MC::WBOSON_LRSM) return WBosonLRSM; // Left-right symmetric model WBoson (Pythia-specific)
1432 if (std::abs(ancestorPDG) == MC::RH_NU_E) return NuREle; // Right-handed NU_E (Pythia-specific)
1433 if (std::abs(ancestorPDG) == MC::RH_NU_MU) return NuRMu; // Right-handed NU_MU (Pythia-specific)
1434 if (std::abs(ancestorPDG) == MC::RH_NU_TAU) return NuRTau; // Right-handed NU_TAU (Pythia-specific)
1435 if (MC::isLeptoQuark(ancestorPDG) || NumOfLQ != 0) return LQ;
1436 if (MC::isSUSY(ancestorPDG)) return SUSY;
1437 if (MC::isBSM(ancestorPDG)) return OtherBSM;
1438
1439 const ParticleType pType = defTypeOfHadron(ancestorPDG);
1440 if ((pType == BBbarMesonPart || pType == CCbarMesonPart) && ancestorProdVtx && MC::isHardScatteringVertex(ancestorProdVtx)) isPrompt = true;
1441
1442 return convHadronTypeToOrig(pType, ancestorPDG);
1443}
#define ATH_MSG_VERBOSE(x)
#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.