ATLAS Offline Software
TruthTools.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
3  */
4 
5 // Filename: TruthTools.cxx
6 // Description:
7 // Author: Fabian Wilk
8 // Created: Wed Feb 4 11:05:43 2015
9 
11 #include "TopEvent/EventTools.h"
12 
13 #include "xAODTruth/TruthVertex.h"
17 
19 
20 #include <list>
21 #include <string>
22 #include <sstream>
23 #include <functional>
24 #include <iostream>
25 #include <stdexcept>
26 
28 
29 using namespace TopParticleLevel;
30 
31 namespace top {
32  namespace truth {
33 
34 
35  void initCommonMuonHistoryInfo(const xAOD::IParticle* muon, bool doPartonHistory)
36  {
38  muon->auxdecor<const xAOD::TruthParticle*>("truthMotherLink") = 0;
39  muon->auxdecor<const xAOD::TruthParticle*>("truthFirstNonLeptonMotherLink") = 0;
40  muon->auxdecor<const xAOD::TruthParticle*>("truthBMotherLink") = 0;
41  muon->auxdecor<const xAOD::TruthParticle*>("truthCMotherLink") = 0;
42 
43  if(doPartonHistory)
44  {
46  muon->auxdecor<const xAOD::TruthParticle*>("truthPartonMotherLink") = 0;
47  muon->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = 0;
48  muon->auxdecor<const xAOD::TruthParticle*>("truthWMotherLink") = 0;
49  muon->auxdecor<const xAOD::TruthParticle*>("truthZMotherLink") = 0;
50  muon->auxdecor<const xAOD::TruthParticle*>("truthPhotonMotherLink") = 0;
51  muon->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = 0;
52  muon->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = 0;
53  }
54  }
55 
56  void initRecoMuonHistoryInfo(const xAOD::Muon* muon, bool doPartonHistory)
57  {
58  muon->auxdecor<bool>("hasRecoMuonHistoryInfo")=false;
59  muon->auxdecor<const xAOD::TruthParticle*>("truthMuonLink") = 0;
60  initCommonMuonHistoryInfo(muon,doPartonHistory);
61  }
62  void initTruthMuonHistoryInfo(const xAOD::TruthParticle* truthmu, bool doPartonHistory)
63  {
64  truthmu->auxdecor<bool>("hasTruthMuonHistoryInfo")=false;
65  if(doPartonHistory) truthmu->auxdecor<bool>("hasTruthMuonPartonHistoryInfo")=false;
66  initCommonMuonHistoryInfo(truthmu,doPartonHistory);
67 
68  }
69 
70  void copyRecoMuonHistoryInfo(const xAOD::Muon* m_origin, const xAOD::Muon* m_target)
71  {
72  copyCommonMuonHistoryInfo(m_origin,m_target);
73  m_target->auxdecor<bool>("hasRecoMuonHistoryInfo") = m_origin->auxdecor<bool>("hasRecoMuonHistoryInfo");
74  m_target->auxdecor<const xAOD::TruthParticle*>("truthMuonLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthMuonLink");
75  }
76 
77  void copyTruthMuonHistoryInfo(const xAOD::TruthParticle* tm_origin, const xAOD::TruthParticle* tm_target)
78  {
79  copyCommonMuonHistoryInfo(tm_origin,tm_target);
80  tm_target->auxdecor<bool>("hasTruthMuonHistoryInfo")= tm_origin->auxdecor<bool>("hasTruthMuonHistoryInfo");
81  tm_target->auxdecor<bool>("hasTruthMuonPartonHistoryInfo")= tm_origin->auxdecor<bool>("hasTruthMuonPartonHistoryInfo");
82  }
83 
84  void copyCommonMuonHistoryInfo(const xAOD::IParticle* m_origin, const xAOD::IParticle* m_target)
85  {
86  if(!m_origin->isAvailable<top::LepParticleOriginFlag>("LepParticleOriginFlag"))
87  {
88  ATH_MSG_ERROR(" top::truth::copyCommonMuonHistoryInfo called with an origin muon which has no truth info!");
89  throw std::runtime_error("Muon has no truth info in copyCommonMuonHistoryInfo");
90  }
91  m_target->auxdecor<top::LepParticleOriginFlag>("LepParticleOriginFlag") = m_origin->auxdecor<top::LepParticleOriginFlag>("LepParticleOriginFlag");
92  m_target->auxdecor<const xAOD::TruthParticle*>("truthMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthMotherLink");
93  m_target->auxdecor<const xAOD::TruthParticle*>("truthFirstNonLeptonMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthFirstNonLeptonMotherLink");
94  m_target->auxdecor<const xAOD::TruthParticle*>("truthBMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthBMotherLink");
95  m_target->auxdecor<const xAOD::TruthParticle*>("truthCMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthCMotherLink");
96 
97  if(m_origin->isAvailable<top::LepPartonOriginFlag>("LepPartonOriginFlag"))
98  {
99  m_target->auxdecor<top::LepPartonOriginFlag>("LepPartonOriginFlag") = m_origin->auxdecor<top::LepPartonOriginFlag>("LepPartonOriginFlag");
100  m_target->auxdecor<const xAOD::TruthParticle*>("truthPartonMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthPartonMotherLink");
101  m_target->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink");
102  m_target->auxdecor<const xAOD::TruthParticle*>("truthWMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthWMotherLink");
103  m_target->auxdecor<const xAOD::TruthParticle*>("truthZMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthZMotherLink");
104  m_target->auxdecor<const xAOD::TruthParticle*>("truthPhotonMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthPhotonMotherLink");
105  m_target->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink");
106  m_target->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = m_origin->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink");
107  }
108  }
109 
111  {
112  if(!muon) return 0;
113 
115  if(!truthmu)
116  {
117  const xAOD::TrackParticle* track = muon->trackParticle( xAOD::Muon::InnerDetectorTrackParticle );
118  if(!track) track=muon->primaryTrackParticle();
120  }
121 
122  if(!truthmu) return 0;
123  if(!truthmu->isMuon()) return 0; //note that the truth particle associated with a muon can be e.g. a pion/kaon in some cases (since pion/kaon decays are not done at generator level, but at simulation level), we ignore these cases
124 
125  return truthmu;
126  }
127 
128  void getRecoMuonHistory(const xAOD::Muon* muon, bool doPartonHistory, SampleXsection::showering shAlgo, bool verbose)
129  {
130 
131  if(verbose) ATH_MSG_INFO("getRecoMuonHistory:: ---------------entering in function-----------------");
132  if(!muon) return;
133 
134  if(verbose)
135  {
136  //we retrieve the origin from the MCClassifier, it's useful for crosschecks; see https://gitlab.cern.ch/atlas/athena/blob/21.2/PhysicsAnalysis/MCTruthClassifier/MCTruthClassifier/MCTruthClassifierDefs.h
137  int origin=0;
138  const static SG::AuxElement::Accessor<int> origmu("truthOrigin");
139  if (origmu.isAvailable(*muon)) origin = origmu(*muon);
140 
141  ATH_MSG_INFO("getRecoMuonHistory::called on muon with pt="<<muon->pt()<<" origin="<<origin);
142  }
143 
144  if(doPartonHistory && shAlgo!=SampleXsection::pythia8 && shAlgo!=SampleXsection::herwigpp)
145  {
146  static int nWarnings=0;
147  if(nWarnings<10 || verbose)
148  {
149  ATH_MSG_WARNING("top::truth::getRecoMuonHistory : partonHistory can be checked only for pythia8 and herwigpp showering algorithms, SoftMuonAdditionalTruthInfoCheckPartonOrigin will be ignored for this sample");
150  nWarnings++;
151  }
152  doPartonHistory=false;
153  }
154 
155  if(!muon->isAvailable<bool>("hasRecoMuonHistoryInfo")) //add default values
156  {
157  initRecoMuonHistoryInfo(muon,doPartonHistory);
158  }
159  else if(muon->auxdecor<bool>("hasRecoMuonHistoryInfo"))
160  {
161  if(verbose) ATH_MSG_INFO("getRecoMuonHistory::we already ran on this muon, exiting");
162  return; //this means we already ran on this muon in some way
163  }
164  muon->auxdecor<bool>("hasRecoMuonHistoryInfo")=true;
165 
166  //first we retrieve the truth muon associated with the reco one
167  const xAOD::TruthParticle* truthmu = muon->auxdecor<const xAOD::TruthParticle*>("truthMuonLink"); //pointer to the truth muon associated with the reco muon
168 
170 
171  if(verbose) ATH_MSG_INFO("getRecoMuonHistory:: retrieved truth muon pointer="<<truthmu);
172  if(!truthmu) return;
173 
174  muon->auxdecor<top::LepParticleOriginFlag>("LepParticleOriginFlag") = top::LepParticleOriginFlag::Unknown;
175  if(doPartonHistory) muon->auxdecor<top::LepPartonOriginFlag>("LepPartonOriginFlag") = top::LepPartonOriginFlag::Unknown;
176 
177  if(verbose)
178  {
179  ATH_MSG_INFO("getRecoMuonHistory::---truth decay chain---");
181  }
182 
183  getTruthMuonHistory(truthmu, doPartonHistory, shAlgo, verbose);
184  //we attach the truth muon to the reco muon
186 
187  muon->auxdecor<const xAOD::TruthParticle*>("truthMuonLink") = truthmu;
188 
189  if(verbose)
190  {
191  ATH_MSG_INFO("getRecoMuonHistory:: written LepParticleOriginFlag decoration = "<<static_cast<int>(muon->auxdecor<top::LepParticleOriginFlag>("LepParticleOriginFlag")));
192  if(doPartonHistory) ATH_MSG_INFO("getRecoMuonHistory:: written LepPartonOriginFlag decoration = "<<static_cast<int>(muon->auxdecor<top::LepPartonOriginFlag>("LepPartonOriginFlag")));
193  ATH_MSG_INFO("getRecoMuonHistory:: exiting function");
194  }
195  return;
196 
197  }
198 
200  void getTruthMuonHistory(const xAOD::TruthParticle* truthmu, bool doPartonHistory, SampleXsection::showering shAlgo, bool verbose)
201  {
202  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: entering in function");
203 
204  if(!truthmu)
205  {
206  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: -> called on empty truth muon");
207  return;
208  }
209  if(!(truthmu->isMuon()))
210  {
211  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: -> called on non-muon particle");
212  return;
213  }
214 
215  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: -> called on truth particle"<<truthmu);
216 
217  if(!truthmu->isAvailable<bool>("hasTruthMuonHistoryInfo")) //add default values
218  {
219  initTruthMuonHistoryInfo(truthmu,doPartonHistory);
220  }
221  else if(truthmu->auxdecor<bool>("hasTruthMuonHistoryInfo"))
222  {
223  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: -> we already ran on this truth muon, exiting");
224  return;
225  }
226  truthmu->auxdecor<bool>("hasTruthMuonHistoryInfo")=true;
227 
228  top::LepParticleOriginFlag lepParticleOriginFlag=top::LepParticleOriginFlag::Unknown; //flag defined in TopEvent/EventTools.h
229  const xAOD::TruthParticle* truthmu_mother=0; //direct mother of the muon
230  const xAOD::TruthParticle* truthmu_firstNonLeptonMother=0; //in case of X->muon or X->tau->muon, this will always contain X
231  const xAOD::TruthParticle* truthmu_Bmother=0; //first bottom hadron encountered when going through ancestors
232  const xAOD::TruthParticle* truthmu_Cmother=0; //first charmed hadron encountered when going through ancestors
233 
234  const xAOD::TruthParticle* initial_mu = getInitialStateParticle(truthmu);
235  if(initial_mu) truthmu_mother= initial_mu->parent(0);
236 
237  if(truthmu_mother && !MC::isMuon(truthmu_mother)) //let's then look at the history of this muon
238  {
239 
240  if(!truthmu_mother->isLepton()) truthmu_firstNonLeptonMother=truthmu_mother; //basically all cases apart from tau->muon
241 
242  if(verbose) ATH_MSG_INFO("getTruthMuonHistory::mother pdgId="<<truthmu_mother->pdgId()<<" isB="<<truthmu_mother->isBottomHadron()<<" isC="<<truthmu_mother->isCharmHadron()<<" isLight="<<truthmu_mother->isLightHadron());
243 
244  if(truthmu_mother->isLightHadron()) lepParticleOriginFlag=top::LepParticleOriginFlag::FromLightHadron; //in this case we set the muon as coming from a light hadron and we don't investigate further
245  else if(truthmu_mother->isW()) //W->munu
246  {
247  lepParticleOriginFlag=top::LepParticleOriginFlag::FromLeptonicW;
248  }
249  else if(truthmu_mother->isZ()) //Z->mumu
250  {
251  lepParticleOriginFlag=top::LepParticleOriginFlag::FromLeptonicZ;
252  }
253  else if(truthmu_mother->isPhoton()) //gamma*->mumu decay
254  {
255  lepParticleOriginFlag=top::LepParticleOriginFlag::FromPhoton;
256  }
257  else if(truthmu_mother->isHiggs())
258  {
259  lepParticleOriginFlag=top::LepParticleOriginFlag::FromHiggs;
260  }
261  else if(truthmu_mother->isBSM())
262  {
263  lepParticleOriginFlag=top::LepParticleOriginFlag::FromBSM;
264  }
265  else if(truthmu_mother->isTau()) //muon from tau
266  {
267  lepParticleOriginFlag=top::truth::getTruthMuonFromTauHistory(truthmu_mother,truthmu_Bmother,truthmu_Cmother,truthmu_firstNonLeptonMother,verbose);
268  }
269  else if(truthmu_mother->isBottomHadron()) //muon from B->mu+X decay
270  {
271  lepParticleOriginFlag=top::LepParticleOriginFlag::FromB;
272  truthmu_Bmother=truthmu_mother;
273  }
274  else if(truthmu_mother->isCharmHadron()) //muon from D->mu+X decay, now we have to understand where the D is coming from
275  {
276  truthmu_Cmother=truthmu_mother;
277  lepParticleOriginFlag=top::truth::getTruthMuonFromCharmHistory(truthmu_Bmother,truthmu_Cmother,verbose);
278  }//end of if
279  }//end of if(truthmu_mother)
280 
281  if(verbose)
282  {
283  ATH_MSG_INFO("getTruthMuonHistory::LepParticleOriginFlag="<<static_cast<int>(lepParticleOriginFlag));
284  if(truthmu) ATH_MSG_INFO("getTruthMuonHistory::-->truth muon "<<truthmu);
285  else ATH_MSG_INFO("getTruthMuonHistory::-->truth muon not found");
286  if(truthmu_mother) ATH_MSG_INFO("getTruthMuonHistory::-->truth muon mother "<<truthmu_mother);
287  else ATH_MSG_INFO("getTruthMuonHistory::-->truth muon mother not found");
288  if(truthmu_firstNonLeptonMother) ATH_MSG_INFO("getTruthMuonHistory::-->truthmu_firstNonLeptonMother "<<truthmu_firstNonLeptonMother);
289  else ATH_MSG_INFO("getTruthMuonHistory::-->truthmu_firstNonLeptonMother not found");
290  if(truthmu_Bmother) ATH_MSG_INFO("getTruthMuonHistory::-->truth muon Bmother "<<truthmu_Bmother);
291  else ATH_MSG_INFO("getTruthMuonHistory::-->truth muon Bmother not found");
292  if(truthmu_Cmother) ATH_MSG_INFO("getTruthMuonHistory::-->truth muon Cmother "<<truthmu_Cmother);
293  else ATH_MSG_INFO("getTruthMuonHistory::-->truth muon Cmother not found");
294  }
295 
296  //we decorate also on the truth muon associated with the reco one, because in this way if the truth muon was already decorated in another copy of the muon (for systematics) we avoid losing time.
297  truthmu->auxdecor<top::LepParticleOriginFlag>("LepParticleOriginFlag") = lepParticleOriginFlag;
298  truthmu->auxdecor<const xAOD::TruthParticle*>("truthFirstNonLeptonMotherLink") = truthmu_firstNonLeptonMother;
299  truthmu->auxdecor<const xAOD::TruthParticle*>("truthMotherLink") = truthmu_mother;
300  truthmu->auxdecor<const xAOD::TruthParticle*>("truthBMotherLink") = truthmu_Bmother;
301  truthmu->auxdecor<const xAOD::TruthParticle*>("truthCMotherLink") = truthmu_Cmother;
302 
303  if(doPartonHistory) top::truth::getTruthMuonPartonHistory(truthmu, lepParticleOriginFlag, truthmu_Bmother, truthmu_Cmother, truthmu_firstNonLeptonMother, shAlgo, verbose);
304 
305  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: exiting from function");
306 
307  return;
308  }
309 
311  void getTruthMuonPartonHistory(const xAOD::TruthParticle* truthmu, top::LepParticleOriginFlag lepParticleOriginFlag, const xAOD::TruthParticle* truthmu_Bmother, const xAOD::TruthParticle* truthmu_Cmother, const xAOD::TruthParticle* truthmu_firstNonLeptonMother, SampleXsection::showering shAlgo, bool verbose)
312  {
313  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: entering in function");
314 
315  if(!truthmu && verbose)
316  {
317  ATH_MSG_INFO("getTruthMuonPartonHistory:: -> called on empty truth muon");
318  return;
319  }
320 
321  if(!truthmu->isAvailable<bool>("hasTruthMuonPartonHistoryInfo"))//default values
322  {
323  initTruthMuonHistoryInfo(truthmu,true);
324  }
325  else if(truthmu->auxdecor<bool>("hasTruthMuonPartonHistoryInfo"))
326  {
327  if(verbose) ATH_MSG_INFO("getTruthMuonHistory:: -> we already ran on this truth muon, exiting");
328  return;
329  }
330  truthmu->auxdecor<bool>("hasTruthMuonPartonHistoryInfo")=true;
331 
332  const xAOD::TruthParticle* truthmu_mother=truthmu_firstNonLeptonMother;
333 
334  if(!truthmu_mother)
335  {
336  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> no muon mother available, returning");
337  return;
338  }
339  if(lepParticleOriginFlag==top::LepParticleOriginFlag::MissingTruthInfo || lepParticleOriginFlag==top::LepParticleOriginFlag::FromLightHadron)
340  {
341 
342  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from light hadron, will not fill info");
343  return;
344  }
345 
347 
348  //this is the priority we use to look for parents in order: top, higgs, BSM, W/Z, gamma*
349  auto get_priority = [](const xAOD::TruthParticle* p) -> int {
350  if(!p) return 999;
351  if(p->isTop()) return 0;
352  if(p->isHiggs()) return 10;
353  if(p->isBSM()) return 15;
354  if(p->isW() || p->isZ()) return 20;
355  if(p->isPhoton()) return 25;
356  return 100;
357  };
358 
359  if(truthmu_mother->isTop()) // shouldn't happen, but maybe some generators don't store intermediate bosons
360  {
361  truthmu->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = truthmu_mother;
362  truthmu->auxdecor<const xAOD::TruthParticle*>("truthPartonMotherLink") = truthmu_mother;
363  if(truthmu_mother->pdgId()>0) lepPartonOriginFlag=top::LepPartonOriginFlag::FromTopViaLeptonicBoson;
365  }
366  else if(truthmu_mother->isHiggs())
367  {
368  truthmu->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = truthmu_mother;
369  lepPartonOriginFlag=top::LepPartonOriginFlag::FromHiggs;
370  }
371  else if(truthmu_mother->isBSM())
372  {
373  truthmu->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = truthmu_mother;
374  lepPartonOriginFlag=top::LepPartonOriginFlag::FromBSM;
375  }
376  else if(truthmu_mother->isW() || truthmu_mother->isZ() || truthmu_mother->isPhoton() ) //the muon is coming from a leptonically decaying boson (eventually via a boson->tau->mu)
377  {
378  lepPartonOriginFlag=top::LepPartonOriginFlag::FromLeptonicBoson; //this is the basic info if we don't find further infromation
379 
380  if(truthmu_mother->isW()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthWMotherLink") = truthmu_mother;
381  else if(truthmu_mother->isZ()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthZMotherLink") = truthmu_mother;
382  else if(truthmu_mother->isPhoton()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthPhotonMotherLink") = truthmu_mother;
383 
384  //then we check in case we can find the mother of the boson
385  const xAOD::TruthParticle* initialStateBoson=top::truth::getInitialStateParticle(truthmu_mother,verbose);
386  const xAOD::TruthParticle* bosonOrigin=0;
387  unsigned int bosonOrigin_priority=999;
388 
389  if(initialStateBoson)
390  {
391  for(unsigned int ip=0; ip<initialStateBoson->nParents(); ip++)
392  {
393  const xAOD::TruthParticle* parent=initialStateBoson->parent(ip);
394  if(!parent) continue;
395 
396  unsigned int parent_priority=get_priority(parent);
397  if(parent_priority<bosonOrigin_priority)
398  {
399  bosonOrigin_priority=parent_priority;
400  bosonOrigin=parent;
401  }
402  }
403  }
404  if(bosonOrigin && bosonOrigin->isTop())
405  {
406  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from boson from top");
407 
408  truthmu->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = bosonOrigin;
409  if(bosonOrigin->pdgId()>0) lepPartonOriginFlag=top::LepPartonOriginFlag::FromTopViaLeptonicBoson;
411  }
412  else if(bosonOrigin && bosonOrigin->isHiggs())
413  {
414  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from boson from higgs");
415  truthmu->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = bosonOrigin;
417  }
418  else if(bosonOrigin && bosonOrigin->isBSM())
419  {
420  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from boson from BSM");
421  truthmu->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = bosonOrigin;
423  }
424  else
425  {
426  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from boson not from top or higgs");
427  lepPartonOriginFlag=top::LepPartonOriginFlag::FromLeptonicBoson;
428  }
429  }
430  else if(truthmu_Bmother || truthmu_Cmother) //in this case the muon is not coming from a boson direct decay, we treat only cases from HF hadrons
431  {
432  lepPartonOriginFlag=top::LepPartonOriginFlag::FromHFHadronOfUnkownOrigin; //default case
433 
434  bool isBHadronMother=truthmu_Bmother;
435  const xAOD::TruthParticle* hadronMother= isBHadronMother ? truthmu_Bmother : truthmu_Cmother;
436  const xAOD::TruthParticle* firstHadronMother=getFirstHFHadronOfSameFlavour(hadronMother,verbose);
437 
438  if(verbose)
439  {
440  ATH_MSG_INFO("getTruthMuonPartonHistory:: isBHadronmother="<<isBHadronMother<<" Bmom="<<truthmu_Bmother<<" Cmom="<<truthmu_Cmother<<" hadMom="<<hadronMother<<" firstHadMom="<<firstHadronMother);
441  ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from hadron pdgId="<<(hadronMother ? hadronMother->pdgId() : 0)<<", first hadron in chain pdgId="<<(firstHadronMother ? firstHadronMother->pdgId() : 0));
442  }
443  if(firstHadronMother)
444  {
445 
446  const xAOD::TruthParticle* hf_parton_parent=0;
447 
448  if(shAlgo == SampleXsection::herwigpp) //in herwig we have pdgId==81 for clusters as hadron parents
449  {
450  if(firstHadronMother->parent(0) && firstHadronMother->parent(0)->pdgId()==81)
451  {
452  firstHadronMother=getInitialStateParticle(firstHadronMother->parent(0));
453  }
454  }
455 
456  //first we find the HF parton that originated the HF hadron
457  for(unsigned int ip=0; ip<firstHadronMother->nParents(); ip++)
458  {
459  const xAOD::TruthParticle* parent=firstHadronMother->parent(ip);
460  if(!parent) continue;
461  if(isBHadronMother && MC::isBottom(parent))
462  {
463  hf_parton_parent=parent;
464  break;
465  }
466  else if(!isBHadronMother && MC::isCharm(parent))
467  {
468  hf_parton_parent=parent;
469  break;
470  }
471 
472  }
473 
474  truthmu->auxdecor<const xAOD::TruthParticle*>("truthPartonMotherLink") =hf_parton_parent;
475 
476  //the we get the first HF parton of the evolution chain
477  const xAOD::TruthParticle* initial_hf_parton_parent=0;
478  if(hf_parton_parent) initial_hf_parton_parent = top::truth::getInitialStateParticle(hf_parton_parent,verbose);
479 
480  //parton ancestor will be the particle/resonance originating the first HF quark
481  const xAOD::TruthParticle* parton_ancestor=0;
482  unsigned int parton_ancestor_priority=999;
483 
484  if(initial_hf_parton_parent)
485  {
486  for(unsigned int ip=0; ip<initial_hf_parton_parent->nParents(); ip++)
487  {
488  const xAOD::TruthParticle* parent=initial_hf_parton_parent->parent(ip);
489  if(!parent) continue;
490  if(!parton_ancestor) parton_ancestor=parent;
491  else
492  {
493  unsigned int priority =get_priority(parent);
494  if(priority < parton_ancestor_priority)
495  {
496  parton_ancestor_priority=priority;
497  parton_ancestor=parent;
498  }
499  }
500  }
501  }
502 
503  //finally we (hopefully) know where the HF hadron is originally coming from
504  if(parton_ancestor && parton_ancestor->isTop())
505  {
506  truthmu->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = parton_ancestor;
507  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from HF from top");
508 
509  if(parton_ancestor->pdgId()>0) lepPartonOriginFlag=top::LepPartonOriginFlag::FromTopViaQuarkToHF;
510  else lepPartonOriginFlag=top::LepPartonOriginFlag::FromAntiTopViaQuarkToHF;
511  }
512  else if(parton_ancestor && parton_ancestor->isHiggs())
513  {
514  truthmu->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = parton_ancestor;
515  lepPartonOriginFlag=top::LepPartonOriginFlag::FromHiggsToHF;
516  }
517  else if(parton_ancestor && parton_ancestor->isBSM())
518  {
519  truthmu->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = parton_ancestor;
520  lepPartonOriginFlag=top::LepPartonOriginFlag::FromBSMToHF;
521  }
522  else if(parton_ancestor && (parton_ancestor->isW() || parton_ancestor->isZ() || parton_ancestor->isPhoton()))
523  {
524  const xAOD::TruthParticle* first_bosonMotherOfHfQuark=top::truth::getInitialStateParticle(parton_ancestor);
525 
526  if(parton_ancestor->isW()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthWMotherLink") = parton_ancestor;
527  else if(parton_ancestor->isZ()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthZMotherLink") = parton_ancestor;
528  else if(parton_ancestor->isPhoton()) truthmu->auxdecor<const xAOD::TruthParticle*>("truthPhotonMotherLink") = parton_ancestor;
529 
530  const xAOD::TruthParticle* bosonOrigin=0;
531  unsigned int bosonOrigin_priority=999;
532 
533  if(first_bosonMotherOfHfQuark)
534  {
535  for(unsigned int ip=0; ip<first_bosonMotherOfHfQuark->nParents(); ip++)
536  {
537  const xAOD::TruthParticle* parent=first_bosonMotherOfHfQuark->parent(ip);
538  if(!parent) continue;
539 
540  unsigned int priority =get_priority(parent);
541  if(priority<bosonOrigin_priority)
542  {
543  bosonOrigin=parent;
544  bosonOrigin_priority=priority;
545  }
546  }
547  if(bosonOrigin && bosonOrigin->isTop())
548  {
549  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from HF from boson from top");
550  truthmu->auxdecor<const xAOD::TruthParticle*>("truthTopMotherLink") = bosonOrigin;
551 
552  if(bosonOrigin->pdgId()>0) lepPartonOriginFlag=top::LepPartonOriginFlag::FromTopViaHadronicBosonToHF;
554  }
555  else if(bosonOrigin && bosonOrigin->isHiggs())
556  {
557  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from HF from boson from higgs");
558  truthmu->auxdecor<const xAOD::TruthParticle*>("truthHiggsMotherLink") = bosonOrigin;
560  }
561  else if(bosonOrigin && bosonOrigin->isBSM())
562  {
563  if(verbose) ATH_MSG_INFO("getTruthMuonPartonHistory:: -> muon from HF from boson from BSM");
564  truthmu->auxdecor<const xAOD::TruthParticle*>("truthBSMMotherLink") = bosonOrigin;
566  }
567  else
568  {
569  lepPartonOriginFlag=top::LepPartonOriginFlag::FromHadronicBosonToHF; //not sure where this is coming from
570  }
571  }
572  }//end of V->HF->muon case
573 
574  }//end of HF->muon case
575 
576  }//end of non-bosonic case
577 
578  truthmu->auxdecor<top::LepPartonOriginFlag>("LepPartonOriginFlag") = lepPartonOriginFlag;
579 
580  if(verbose) ATH_MSG_INFO("getTruthMuonHistory::------> LepPartonOriginFlag="<<static_cast<int>(lepPartonOriginFlag));
581 
582  return;
583 
584  }//end of getTruthMuonPartonHistory
585 
588  {
589  if(verbose) ATH_MSG_INFO("called getTruthMuonFromCharmHistory");
590  const xAOD::TruthParticle* initial_C_hadron=getFirstHFHadronOfSameFlavour(truthmu_Cmother); //we find the first non-Chadron ancestor of the C-hadron
591 
592  if(initial_C_hadron)
593  {
594  bool hasBHadronParent=false;
595  for(unsigned int ip=0; ip<initial_C_hadron->nParents(); ip++)
596  {
597  const xAOD::TruthParticle* parent=initial_C_hadron->parent(ip);
598  if(!parent) continue;
599  if(parent->isBottomHadron())
600  {
601  hasBHadronParent=true;
602  truthmu_Bmother=parent;
603  break;
604  }
605  }
606  if(hasBHadronParent) return top::LepParticleOriginFlag::FromBtoC;
607  }
608 
609  return top::LepParticleOriginFlag::FromC; //we don't know where the C-hadron is coming from
610  } //end of getTruthMuonFromCharmHistory method
611 
613  top::LepParticleOriginFlag getTruthMuonFromTauHistory(const xAOD::TruthParticle* tau, const xAOD::TruthParticle* &truthmu_Bmother, const xAOD::TruthParticle* &truthmu_Cmother, const xAOD::TruthParticle* &truthmu_firstNonLeptonMother, bool verbose)
614  {
615  if(!tau && verbose)
616  {
617  ATH_MSG_WARNING("getTruthMuonFromTauHistory called on empty tau");
619  }
620  if(!tau->isTau())
621  {
622  ATH_MSG_ERROR("getTruthMuonFromTauHistory called on non-tau, pdgId"<<(tau->pdgId()));
623  throw std::runtime_error("getTruthMuonFromTauHistory called with a non-tau particle ID");
624  }
626  if(!initialTau)
627  {
628  if(verbose)ATH_MSG_INFO("getTruthMuonFromTauHistory:: could not find initial state tau");
630  }
631 
632  const xAOD::TruthParticle* tauMother=initialTau->parent(0);
633 
634  if(!tauMother) return top::LepParticleOriginFlag::FromTau; //muon from tau, we don't know where the tau is coming from
635 
636  if(!tauMother->isLepton()) truthmu_firstNonLeptonMother=tauMother;
637 
638  if(tauMother->isW()) //W->taunu with tau->mununu decay
639  {
641  }
642  if(tauMother->isZ()) //Z->tautau with tau->mununu
643  {
645  }
646  if(tauMother->isPhoton())
647  {
649  }
650  if(tauMother->isHiggs())
651  {
653  }
654  if(tauMother->isBSM())
655  {
657  }
658  if(tauMother->isBottomHadron()) //the muon is from a B->tau+X with tau->mununu decay
659  {
660  truthmu_Bmother=tauMother;
662  }
663  if(tauMother->isCharmHadron()) //now this is getting complicated, the muon is from e.g. D->tau+X with tau->mununu decay, now we have to understand where the D is coming from...
664  {
665  truthmu_Cmother=tauMother;
666  const xAOD::TruthParticle* initial_C_hadron=getFirstHFHadronOfSameFlavour(truthmu_Cmother); //we find the first non-Chadron ancestor of the C-hadron
667 
668  bool hasBHadronParent=false;
669 
670  for(unsigned int ip=0; ip<initial_C_hadron->nParents(); ip++)
671  {
672  const xAOD::TruthParticle* parent=initial_C_hadron->parent(ip);
673  if(!parent) continue;
674  if(parent->isBottomHadron())
675  {
676  hasBHadronParent=true;
677  truthmu_Bmother=parent;
678  break;
679  }
680  }
681  if(hasBHadronParent) return top::LepParticleOriginFlag::FromBtoCtoTau;
682  else //in this case this is coming from D->tau->mu+X or something with no B-hadron ancestor
683  {
685  }
686  }//end of if(tauMother->isCharmHadron())
687 
688  return top::LepParticleOriginFlag::FromTau; //in all other cases we don't know anything apart from the fact that the muon is coming from a tau
689  }//end of getTruthMuonFromTauHistory method
690 
691  //getFirstHadronOfSameFlavour//
693  {
694  if(!truthParticle) return 0;
695  bool isBottomHadron=truthParticle->isBottomHadron();
696  bool isCharmHadron=truthParticle->isCharmHadron();
697 
698  if(!(isBottomHadron) && !(isCharmHadron))
699  {
700  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: this can be called only on HF hadrons...");
701  return 0;
702  }
703  const xAOD::TruthParticle* parent=truthParticle;
704  int niterations=0;
705  while(parent)
706  {
707  if((niterations++)>100)
708  {
709  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: too many iterations on particle "<<(truthParticle->pdgId()));
710  return 0;
711  }
712 
714  if(!parent)
715  {
716  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: initial state particle not found");
717  break;
718  }
719 
720  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: initial state particle found="<<(parent->pdgId()));
721 
722  bool hasSameFlavourParent=false;
723  for(unsigned int ip=0; ip<parent->nParents(); ip++)
724  {
725  if(parent->parent(ip))
726  {
727  if((isBottomHadron && parent->parent(ip)->isBottomHadron())||(isCharmHadron && parent->parent(ip)->isCharmHadron()))
728  {
729  parent=parent->parent(ip);
730  hasSameFlavourParent=true;
731  break;
732  }
733  }
734  }
735 
736  if(!hasSameFlavourParent)
737  {
738  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: parent="<<(parent->pdgId())<<" has no same flavour ancestor");
739  break;
740  }
741  else
742  {
743  if(verbose) ATH_MSG_INFO("getFirstHFHadronOfSameFlavour:: found same flavour parent="<<(parent->pdgId()));
744  }
745 
746  }
747  return parent;
748  }
749  //end of getFirstHadronOfSameFlavour method//
750 
751  //getInitialStateParticle//
753  {
754  if(!truthParticle) return 0;
755  int pdgId=truthParticle->pdgId();
756  int niterations=0;
757  const xAOD::TruthParticle* parent=truthParticle;
758 
759  while(parent)
760  {
761  if((niterations++)>100)
762  {
763  if(verbose) ATH_MSG_INFO("getInitialStateParticle:: too many iterations for particle "<<(truthParticle->pdgId()));
764  return 0;
765  }
766 
767  bool hasParentWithSamePdgId=false;
768  for(unsigned int ip=0; ip<parent->nParents(); ip++)
769  {
770  if(parent->parent(ip) && (parent->parent(ip)->pdgId()==pdgId))
771  {
772  parent=parent->parent(ip);
773  hasParentWithSamePdgId=true;
774  break;
775  }
776  }
777  if(!hasParentWithSamePdgId) break;
778  }
779  return parent;
780  }
781  //end of getInitialStateParticle method//
782 
783  void printDecayChain(const xAOD::TruthParticle* truthPart,
784  std::ostream& os /* = std::cout */,
785  const std::string& prefix /* = "" */) {
786  // Protect against a faulty production vertex reference.
787  if (!truthPart || !truthPart->prodVtx()) {
788  os << prefix << "[" << (truthPart ? truthPart->pdgId() :0x0) << " <- NULL]" << '\n';
789  return;
790  }
791 
792  os << prefix << "[" << truthPart->pdgId();
793  int niterations=0;
794 
795  while (truthPart) {
796  if((niterations++)>30)
797  {
798  os<<" STOP! too many iterations ";
799  break;
800  }
801  if(truthPart->nParents()<1) break;
802 
803  os << " <- ";
804  const xAOD::TruthParticle* firstWithSamePdgId=0;
805  const xAOD::TruthParticle* firstNonParton=0;
806  const xAOD::TruthParticle* firstCorrectFlavorQuark=0;
807 
808  for(unsigned int ip=0; ip<truthPart->nParents(); ip++)
809  {
810  if(ip>0) os<<", ";
811  os<<" "<<(truthPart->parent(ip) ? truthPart->parent(ip)->pdgId() : 0);
812  if(!truthPart->parent(ip)) continue;
813 
814  if(!firstWithSamePdgId && truthPart->parent(ip)->pdgId()==truthPart->pdgId())
815  {
816  firstWithSamePdgId = truthPart->parent(ip);
817  }
818  if(!firstNonParton && !truthPart->parent(ip)->isParton())
819  {
820  firstNonParton = truthPart->parent(ip);
821  }
822  if(truthPart->isBottomHadron() && MC::isBottom(truthPart->parent(ip)))
823  {
824  firstCorrectFlavorQuark = truthPart->parent(ip);
825  }
826  if(truthPart->isCharmHadron() && MC::isCharm(truthPart->parent(ip)))
827  {
828  firstCorrectFlavorQuark = truthPart->parent(ip);
829  }
830 
831  }
832  if(firstWithSamePdgId) truthPart=firstWithSamePdgId;
833  else if(firstNonParton) truthPart=firstNonParton;
834  else if(firstCorrectFlavorQuark) truthPart=firstCorrectFlavorQuark;
835  else truthPart=truthPart->parent(0);
836 
837  }
838  os << "]" << '\n';
839  }
840 
841  bool isFrom(const xAOD::TruthParticle* truthParticle,
842  const std::vector<int>& parentPDGIds,
843  bool bOnlyDirect /* = false */) {
844  // If the input does not have aproper production vertex reference or
845  // there are no incoming particle to the production vertex, directly
846  // return false
847  if (!truthParticle->hasProdVtx() ||
848  !truthParticle->prodVtx() ||
849  truthParticle->prodVtx()->nIncomingParticles() == 0) {
850  return false;
851  }
852 
853  // The current production vertex
854  const xAOD::TruthVertex* prodVtx = truthParticle->prodVtx();
855  // The previous production vertex (initialised to the current
856  // production vertex)
857  const xAOD::TruthVertex* prevProdVtx = prodVtx;
858 
859  auto numIncoming = prodVtx->nIncomingParticles();
860  for (std::size_t motherIndex = 0;
861  motherIndex < numIncoming;
862  ++motherIndex) {
863  const xAOD::TruthParticle* mother = nullptr;
864  int motherPdgId = truthParticle->pdgId();
865 
866  // Ascend through the decay chain until we find the "actual"
867  // decay, i.e. a change in the PDG ID. This skips all the
868  // non-decay truth table entries which come from "particle
869  // evolution" rather than physical decay
870  while (truthParticle->pdgId() == motherPdgId) {
871  mother = prodVtx->incomingParticle(motherIndex);
872  if (mother) {
873  motherPdgId = mother->pdgId();
874  } else {
875  break;
876  }
877 
878  if (truthParticle->pdgId() != motherPdgId) {
879  break;
880  }
881 
882  // Include protection against cyclic or broken reference
883  // chains which can occur in SHERPA samples
884  if (!mother->hasProdVtx()) {
885  break;
886  }
887  if (prevProdVtx == mother->prodVtx()) {
888  break;
889  }
890 
891  // Update the previous / current production vertex references
892  prevProdVtx = prodVtx;
893  prodVtx = mother->prodVtx();
894 
895  // safeguard
896  if (!prodVtx || prodVtx->nIncomingParticles() == 0) {
897  break;
898  }
899  }
900 
901  // Check that the mother pointer is valid. If it is not valid, then
902  // the particle could not possibly come from any of the requested
903  // PDG Ids, hence return false
904  if (!mother) {
905  return false;
906  }
907 
908  // If the mother PDG ID is in the parentPDGIds collection, then return true
909  if (std::find(parentPDGIds.begin(), parentPDGIds.end(), motherPdgId) != parentPDGIds.end()) {
910  return true;
911  }
912 
913  // If we allow chained matching (I.e. not only _direct_ decays) and
914  // the mother particle does come from the request PDG ID(s), return true
915  if (!bOnlyDirect && isFrom(mother, parentPDGIds, bOnlyDirect)) {
916  return true;
917  }
918  }
919 
920  // If we did't find an ancestor with the requested pdg id, return false
921  return false;
922  }
923 
924  bool isFromWZDecay(const xAOD::TruthParticle* truthParticle,
925  bool bOnlyDirect /* = false */) {
926  // If the input does not have aproper production vertex reference or
927  // there are no incoming particle to the production vertex, directly
928  // return false
929  if (!truthParticle->hasProdVtx() ||
930  !truthParticle->prodVtx() ||
931  truthParticle->prodVtx()->nIncomingParticles() == 0) {
932  return false;
933  }
934 
935  // The current production vertex
936  const xAOD::TruthVertex* prodVtx = truthParticle->prodVtx();
937  // The previous production vertex (initialised to the current
938  // production vertex)
939  const xAOD::TruthVertex* prevProdVtx = prodVtx;
940 
941 
942  auto numIncoming = prodVtx->nIncomingParticles();
943  for (std::size_t motherIndex = 0;
944  motherIndex < numIncoming;
945  ++motherIndex) {
946  const xAOD::TruthParticle* mother = nullptr;
947  int motherAbsPdgId = truthParticle->absPdgId();
948 
949  // Ascend through the decay chain until we find the "actual"
950  // decay, i.e. a change in the PDG ID. This skips all the
951  // non-decay truth table entries which come from "particle
952  // evolution" rather than physical decay
953  while (truthParticle->absPdgId() == motherAbsPdgId) {
954  mother = prodVtx->incomingParticle(motherIndex);
955  if (mother) {
956  motherAbsPdgId = mother->absPdgId();
957  } else {
958  break;
959  }
960 
961  if (truthParticle->absPdgId() != motherAbsPdgId) {
962  break;
963  }
964 
965  // Include protection against cyclic or broken reference
966  // chains which can occur in SHERPA samples
967  if (!mother->hasProdVtx()) {
968  break;
969  }
970  if (prevProdVtx == mother->prodVtx()) {
971  break;
972  }
973 
974  // Update the previous / current production vertex references
975  prevProdVtx = prodVtx;
976  prodVtx = mother->prodVtx();
977 
978  // safeguard
979  if (!prodVtx || prodVtx->nIncomingParticles() == 0) {
980  break;
981  }
982  }
983 
984  // Check that the mother pointer is valid. If it is not valid, then
985  // the particle could not possibly come from any of the requested
986  // PDG Ids, hence return false
987  if (!mother) {
988  return false;
989  }
990 
991  // If the direct physical decay in the MC record was from a Z or W
992  // boson to the truth particle then return true
993  // Otherwise perform a vertex-based identification of W/Z's
994  if (motherAbsPdgId == 23 || motherAbsPdgId == 24) {
995  return true;
996  }
997 
998  // The following vertex-based identification of W/Z's is needed
999  // for SHERPA samples where the W/Z particle is not explicitly
1000  // in the particle record. At this point if we have a valid
1001  // vertex, it should be a true decay vertex.
1002  //
1003  // If it is a W or Z then two of those decay products should be
1004  // lepton / neutrino pair corresponding to the transitions
1005  // W+ -> l+ nu
1006  // W- -> l- nu~
1007  // Z -> l+ l-
1008  // Z -> nu nu~
1009  // Hence loop through the outgoing particles of the truth vertex
1010  // and check that the vertex matches the requirement. We
1011  // simplify this check by just counting the number of leptonic
1012  // outgoing particles which should be equal to 2. It is assumed
1013  // that the MC generator does not produce unphysical decays.
1014  //
1015  // Furthermore, prompt W/Z's should come from a vertex with more
1016  // than one incoming particle. Consequently, test this
1017  // requirement prior to checking the outgoing states.
1018  int nDecay = 0;
1019  if (prodVtx && prodVtx->nIncomingParticles() > 1) {
1020  for (const auto& child : prodVtx->outgoingParticleLinks()) {
1021  if (MC::isSMLepton(*child)) {
1022  nDecay++;
1023  }
1024  }
1025  }
1026 
1027  // There must be exactly two leptonic outgoing particles in that vertex
1028  if (nDecay == 2) {
1029  return true;
1030  }
1031 
1032  // If we allow chained matching (I.e. not only _direct_ decays) and
1033  // the mother particle does come from a W / Z, return true
1034  if (!bOnlyDirect && isFromWZDecay(mother, bOnlyDirect)) {
1035  return true;
1036  }
1037  }
1038 
1039  // We did not find any W / Z in the ancestry of the input particle ->
1040  // return false
1041  return false;
1042  }
1043 
1044  bool isLeptonFromTau(const xAOD::TruthParticle* truthParticle) {
1045  // If the input particle is not a lepton directly terminate the
1046  // algorithm retunring false
1047  if (!MC::isSMLepton(truthParticle)) {
1048  return false;
1049  }
1050  return isFrom(truthParticle, {15, -15}, true);
1051  }
1052 
1053  bool isNotFromHadron(const xAOD::TruthParticle* truthParticle,
1054  bool bOnlyDirect /* = false */,
1055  bool bTauIsHadron /* = false */) {
1056  // If there is no production vertex, either something is wrong, or
1057  // we are at the beginning of the record. However, either way, the
1058  // particle does not come from a hadron, hence return true.
1059  if (!truthParticle->hasProdVtx() ||
1060  !truthParticle->prodVtx() ||
1061  truthParticle->prodVtx()->nIncomingParticles() == 0) {
1062  return true;
1063  }
1064 
1065  // The current production vertex
1066  const xAOD::TruthVertex* prodVtx = truthParticle->prodVtx();
1067  // The previous production vertex (initialised to the current
1068  // production vertex)
1069  const xAOD::TruthVertex* prevProdVtx = prodVtx;
1070 
1071  // Loop all the particles going into the production vertex of the
1072  // current truth particle.
1073  // * If the mother of any of the is a physical hadron return false
1074  // * If the mother of any of them descends from a physical return false
1075  // * Otherwise return true
1076  auto numIncoming = prodVtx->nIncomingParticles();
1077  for (std::size_t motherIndex = 0;
1078  motherIndex < numIncoming;
1079  ++motherIndex) {
1080  int motherPdgId = truthParticle->pdgId();
1081  const xAOD::TruthParticle* mother = nullptr;
1082 
1083  // Ascend through the decay chain until we find the "actual"
1084  // decay, i.e. a change in the PDG ID. This skips all the
1085  // non-decay truth table entries which come from "particle
1086  // evolution" rather than physical decay
1087  while (truthParticle->pdgId() == motherPdgId) {
1088  mother = prodVtx->incomingParticle(motherIndex);
1089  if (mother) {
1090  motherPdgId = mother->pdgId();
1091  } else {
1092  break;
1093  }
1094 
1095  // Include protection against cyclic or broken reference
1096  // chains which can occur in SHERPA samples
1097  if (!mother->hasProdVtx()) {
1098  break;
1099  }
1100  if (prevProdVtx == mother->prodVtx()) {
1101  break;
1102  }
1103 
1104  // Update the previous / current production vertex references
1105  prevProdVtx = prodVtx;
1106  prodVtx = mother->prodVtx();
1107 
1108  // safeguard
1109  if (!prodVtx || prodVtx->nIncomingParticles() == 0) {
1110  break;
1111  }
1112  }
1113 
1114  // Check that the mother pointer is valid. If it is not valid, then
1115  // the particle could not possibly come from a hadron, hence return
1116  // true
1117  if (!mother) {
1118  return true;
1119  }
1120 
1121  // If the mother particle is a "physical decayed" particle (HepMC
1122  // status code) and a hadron, then return false.
1123  if (MC::isDecayed(mother) &&
1124  (MC::isHadron(motherPdgId) ||
1125  (bTauIsHadron && MC::isTau(motherPdgId)))) {
1126  return false;
1127  }
1128 
1129  // If we allow chained matching (I.e. not only _direct_ decays) and
1130  // the mother particle does come from a hadron, return false
1131  if (!bOnlyDirect && !isNotFromHadron(mother, bOnlyDirect, bTauIsHadron)) {
1132  return false;
1133  }
1134  }
1135  // No hadronic && physical parents
1136  return true;
1137  }
1138  }
1139 }
top::LepPartonOriginFlag::FromAntiTopViaLeptonicBoson
@ FromAntiTopViaLeptonicBoson
tipically means that the muon is coming from tbar->W->...->muon in some way (or any tbar->W/Z/gamma*‍...
top::LepParticleOriginFlag::FromB
@ FromB
Higgs->tau->mu.
top::truth::isNotFromHadron
bool isNotFromHadron(const xAOD::TruthParticle *truthParticle, bool bOnlyDirect, bool bTauIsHadron)
Check whether a given truth particle is not produced in the decay of physical hadrons or their descen...
Definition: TruthTools.cxx:1053
xAOD::TruthParticle_v1::parent
const TruthParticle_v1 * parent(size_t i=0) const
Retrieve the i-th mother (TruthParticle) of this TruthParticle.
Definition: TruthParticle_v1.cxx:131
xAOD::muon
@ muon
Definition: TrackingPrimitives.h:195
top::LepParticleOriginFlag::Unknown
@ Unknown
BSMparticle->tau->muon.
top::LepPartonOriginFlag::FromAntiTopViaQuarkToHF
@ FromAntiTopViaQuarkToHF
tipically means that the muon is coming from tbar->bbar->...->muon in some way (or any tbar->qbar->....
xAOD::TruthParticle_v1::absPdgId
int absPdgId() const
Absolute PDG ID code (often useful)
top::truth::getFirstHFHadronOfSameFlavour
const xAOD::TruthParticle * getFirstHFHadronOfSameFlavour(const xAOD::TruthParticle *truthParticle, bool verbose)
Definition: TruthTools.cxx:692
top::LepPartonOriginFlag::FromAntiTopViaHadronicBosonToHF
@ FromAntiTopViaHadronicBosonToHF
tipically means that the muon is coming from tbar->W->...->muon in some way (or any tbar->W/Z/gamma*‍...
top
TopConfig A simple configuration that is NOT a singleton.
Definition: AnalysisTrackingHelper.cxx:58
top::truth::isFrom
bool isFrom(const xAOD::TruthParticle *truthParticle, const std::vector< int > &parentPDGIds, bool bOnlyDirect)
Function to determine whether the input particle was produced in the physical decay of a particle of ...
Definition: TruthTools.cxx:841
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
top::LepParticleOriginFlag::FromBtoTau
@ FromBtoTau
from B-hadron to tau to mu decay
top::LepParticleOriginFlag::FromLeptonicW
@ FromLeptonicW
from W with leptonic decay (note: this will not work for Sherpa)
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
find
std::string find(const std::string &s)
return a remapped string
Definition: hcg.cxx:135
top::truth::copyCommonMuonHistoryInfo
void copyCommonMuonHistoryInfo(const xAOD::IParticle *m_origin, const xAOD::IParticle *m_target)
Definition: TruthTools.cxx:84
top::LepPartonOriginFlag::FromHiggs
@ FromHiggs
H->HF->muon, not sure if this can happen in some generators.
top::LepParticleOriginFlag
LepParticleOriginFlag
this enum defines a flag used to understand the particle origin of a lepton (tipically a soft muon),...
Definition: EventTools.h:57
top::truth::getTruthMuonAssociatedToRecoMuon
const xAOD::TruthParticle * getTruthMuonAssociatedToRecoMuon(const xAOD::Muon *muon)
Definition: TruthTools.cxx:110
xAOD::TruthParticle_v1::isCharmHadron
bool isCharmHadron() const
Determine if the PID is that of a c-hadron.
SG::Accessor
Helper class to provide type-safe access to aux data.
Definition: Control/AthContainers/AthContainers/Accessor.h:66
top::LepParticleOriginFlag::FromLeptonicZToTau
@ FromLeptonicZToTau
from Z->tau->lep (note: this will not work for Sherpa)
top::LepPartonOriginFlag::MissingTruthInfo
@ MissingTruthInfo
e.g. this can mean the muon is coming from light hadrons and there is no truth history,...
TruthParticleContainer.h
top::LepPartonOriginFlag::FromHiggsViaHadronicBosonToHF
@ FromHiggsViaHadronicBosonToHF
H->VV->muon.
top::LepPartonOriginFlag::FromTopViaLeptonicBoson
@ FromTopViaLeptonicBoson
tipically means that the muon is coming from t->W->...->muon in some way (or any t->W/Z/gamma*‍/H->....
top::LepParticleOriginFlag::FromHiggs
@ FromHiggs
SampleXsection::showering
showering
Definition: SampleXsection.h:21
xAOD::TruthParticle_v1::isW
bool isW() const
Check if this particle is a W boson.
top::truth::getTruthMuonFromCharmHistory
top::LepParticleOriginFlag getTruthMuonFromCharmHistory(const xAOD::TruthParticle *&truthmu_Bmother, const xAOD::TruthParticle *truthmu_Cmother, bool verbose)
Definition: TruthTools.cxx:587
python.selector.AtlRunQuerySelectorLhcOlc.priority
priority
Definition: AtlRunQuerySelectorLhcOlc.py:611
top::LepParticleOriginFlag::FromLightHadron
@ FromLightHadron
often these muons are Unknown, but in some cases we have the truth record and we can verify they are ...
xAOD::IParticle
Class providing the definition of the 4-vector interface.
Definition: Event/xAOD/xAODBase/xAODBase/IParticle.h:40
xAODTruthHelpers.h
xAOD::TruthVertex_v1::outgoingParticleLinks
const TPLinks_t & outgoingParticleLinks() const
Get all the outgoing particles.
PowhegPy8EG_H2a.pdgId
dictionary pdgId
Definition: PowhegPy8EG_H2a.py:128
xAOD::TruthParticle_v1::isBSM
bool isBSM() const
Check if this is a BSM particle.
xAOD::Muon_v1
Class describing a Muon.
Definition: Muon_v1.h:38
SG::AuxElement::auxdecor
Decorator< T, ALLOC >::reference_type auxdecor(const std::string &name) const
Fetch an aux decoration, as a non-const reference.
isBottomHadron
bool isBottomHadron(const T &p)
Definition: AtlasPID.h:466
EventTools.h
A few functions for doing operations on particles / events. Currently holds code for dR,...
top::LepPartonOriginFlag::FromTopViaHadronicBosonToHF
@ FromTopViaHadronicBosonToHF
tipically means that the muon is coming from t->W->...->muon in some way (or any t->W/Z/gamma*‍/H->....
isSMLepton
bool isSMLepton(const T &p)
Definition: AtlasPID.h:134
xAOD::TruthParticle_v1::isParton
bool isParton() const
Check if this particle is a parton.
xAOD::TruthParticle_v1::isZ
bool isZ() const
Check if this particle is a Z boson.
top::truth::getTruthMuonFromTauHistory
top::LepParticleOriginFlag getTruthMuonFromTauHistory(const xAOD::TruthParticle *tau, const xAOD::TruthParticle *&truthmu_Bmother, const xAOD::TruthParticle *&truthmu_Cmother, const xAOD::TruthParticle *&truthmu_firstNonLeptonMother, bool verbose)
Definition: TruthTools.cxx:613
top::LepParticleOriginFlag::FromTau
@ FromTau
from Tau leptonic (not coming from W or HF-hadron, so not sure this can really happen)
top::LepPartonOriginFlag::FromBSM
@ FromBSM
direct H->muon
xAOD::TruthParticle_v1::isLepton
bool isLepton() const
Whether the particle is a lepton.
SampleXsection::herwigpp
@ herwigpp
Definition: SampleXsection.h:26
top::LepPartonOriginFlag::FromHiggsViaLeptonicBosonToHF
@ FromHiggsViaLeptonicBosonToHF
top::LepPartonOriginFlag::FromTopViaQuarkToHF
@ FromTopViaQuarkToHF
tipically means that the muon is coming from t->b->...->muon in some way (or any t->q->....
xAOD::TruthParticle_v1::nParents
size_t nParents() const
Number of parents of this particle.
Definition: TruthParticle_v1.cxx:122
top::truth::initTruthMuonHistoryInfo
void initTruthMuonHistoryInfo(const xAOD::TruthParticle *truthmu, bool doPartonHistory)
Definition: TruthTools.cxx:62
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
isBottom
bool isBottom(const T &p)
Definition: AtlasPID.h:123
top::LepParticleOriginFlag::FromBSM
@ FromBSM
xAOD::TruthParticle_v1
Class describing a truth particle in the MC record.
Definition: TruthParticle_v1.h:41
top::truth::initRecoMuonHistoryInfo
void initRecoMuonHistoryInfo(const xAOD::Muon *muon, bool doPartonHistory)
Definition: TruthTools.cxx:56
top::LepPartonOriginFlag::FromLeptonicBoson
@ FromLeptonicBoson
general case of W/Z/gamma*‍/H->muon or W/Z/gamma*‍/H->tau->muon
top::LepParticleOriginFlag::MissingTruthInfo
@ MissingTruthInfo
no associated truth muon tipically this happens for muon from light-hadrons
xAOD::TruthParticle_v1::hasProdVtx
bool hasProdVtx() const
Check for a production vertex on this particle.
Definition: TruthParticle_v1.cxx:74
top::LepPartonOriginFlag::Unknown
@ Unknown
BSMparticle->HF->muon.
checkCorrelInHIST.prefix
dictionary prefix
Definition: checkCorrelInHIST.py:391
test_pyathena.parent
parent
Definition: test_pyathena.py:15
find_tgc_unfilled_channelids.ip
ip
Definition: find_tgc_unfilled_channelids.py:3
top::LepParticleOriginFlag::FromCtoTau
@ FromCtoTau
from C-hadron (with no B-hadron parent) to tau to mu decay
top::truth::getTruthMuonHistory
void getTruthMuonHistory(const xAOD::TruthParticle *truthmu, bool doPartonHistory, SampleXsection::showering shAlgo, bool verbose)
Definition: TruthTools.cxx:200
top::LepParticleOriginFlag::FromBtoC
@ FromBtoC
from B-hadron to C-hadron to muon decay
top::LepParticleOriginFlag::FromBtoCtoTau
@ FromBtoCtoTau
from B-hadron to C-hadron to tau to muon decay
xAOD::TruthParticle_v1::isTau
bool isTau() const
Whether the particle is a tau (or antitau)
xAOD::TruthVertex_v1::incomingParticle
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
Definition: TruthVertex_v1.cxx:71
top::truth::getRecoMuonHistory
void getRecoMuonHistory(const xAOD::Muon *muon, bool doPartonHistory, SampleXsection::showering shAlgo, bool verbose)
Definition: TruthTools.cxx:128
ReadFromCoolCompare.os
os
Definition: ReadFromCoolCompare.py:231
isTau
bool isTau(const T &p)
Definition: AtlasPID.h:148
top::truth::getInitialStateParticle
const xAOD::TruthParticle * getInitialStateParticle(const xAOD::TruthParticle *truthParticle, bool verbose)
Definition: TruthTools.cxx:752
TruthVertex.h
xAOD::TruthParticle_v1::isBottomHadron
bool isBottomHadron() const
Determine if the PID is that of a b-hadron.
xAOD::TruthParticle_v1::prodVtx
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
Definition: TruthParticle_v1.cxx:80
xAOD::TruthParticle_v1::isLightHadron
bool isLightHadron() const
Determine if the PID is that of a light flavour (not b or c) hadron.
xAOD::TruthVertex_v1
Class describing a truth vertex in the MC record.
Definition: TruthVertex_v1.h:41
top::LepPartonOriginFlag::FromBSMToHF
@ FromBSMToHF
BSMparticle->V->HF->muon.
top::truth::initCommonMuonHistoryInfo
void initCommonMuonHistoryInfo(const xAOD::IParticle *muon, bool doPartonHistory)
Definition: TruthTools.cxx:35
TruthTools.h
xAOD::TruthHelpers::getTruthParticle
const xAOD::TruthParticle * getTruthParticle(const xAOD::IParticle &p)
Return the truthParticle associated to the given IParticle (if any)
Definition: xAODTruthHelpers.cxx:25
isHadron
bool isHadron(const T &p)
Definition: AtlasPID.h:207
top::LepPartonOriginFlag::FromHiggsToHF
@ FromHiggsToHF
H->VV->HF->muon.
SampleXsection::pythia8
@ pythia8
Definition: SampleXsection.h:25
xAOD::IParticle::isAvailable
bool isAvailable(const std::string &name, const std::string &clsname="") const
Check if a user property is available for reading or not.
Definition: Event/xAOD/xAODBase/xAODBase/IParticle.h:131
top::LepParticleOriginFlag::FromLeptonicWToTau
@ FromLeptonicWToTau
from W->tau->lep (note: this will not work for Sherpa)
top::truth::isLeptonFromTau
bool isLeptonFromTau(const xAOD::TruthParticle *truthParticle)
Function the check if a particle is a lepton which was produced in the decay of a tau lepton.
Definition: TruthTools.cxx:1044
xAOD::TruthParticle_v1::isPhoton
bool isPhoton() const
Whether the particle is a photon.
top::LepParticleOriginFlag::FromPhotonToTau
@ FromPhotonToTau
gamma*->muonmuon
top::LepParticleOriginFlag::FromPhoton
@ FromPhoton
xAOD::TruthParticle_v1::isHiggs
bool isHiggs() const
Check if this particle is a Higgs boson.
top::LepPartonOriginFlag::FromBSMViaHadronicBosonToHF
@ FromBSMViaHadronicBosonToHF
BSMparticle->V->muon.
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
python.Constants.INFO
int INFO
Definition: Control/AthenaCommon/python/Constants.py:16
xAOD::TruthVertex_v1::nIncomingParticles
size_t nIncomingParticles() const
Get the number of incoming particles.
Definition: TruthVertex_v1.cxx:49
MC::isDecayed
bool isDecayed(const T &p)
Definition: HepMCHelpers.h:29
top::LepParticleOriginFlag::FromLeptonicZ
@ FromLeptonicZ
gamma*->tautau and tau->muon
python.TriggerHandler.verbose
verbose
Definition: TriggerHandler.py:297
xAOD::TruthParticle_v1::isMuon
bool isMuon() const
Whether the particle is a muon (or antimuon)
top::LepParticleOriginFlag::FromBSMToTau
@ FromBSMToTau
BSMparticle->muon.
isCharm
bool isCharm(const T &p)
Definition: AtlasPID.h:120
isCharmHadron
bool isCharmHadron(const T &p)
Definition: AtlasPID.h:465
SG::ConstAccessor< T, AuxAllocator_t< T > >::isAvailable
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
top::LepPartonOriginFlag::FromHFHadronOfUnkownOrigin
@ FromHFHadronOfUnkownOrigin
HF hadron->muon or HF hadron->tau->muon, we're not sure where the HF hadron is coming from (maybe a g...
top::truth::getTruthMuonPartonHistory
void getTruthMuonPartonHistory(const xAOD::TruthParticle *truthmu, top::LepParticleOriginFlag lepParticleOriginFlag, const xAOD::TruthParticle *truthmu_Bmother, const xAOD::TruthParticle *truthmu_Cmother, const xAOD::TruthParticle *truthmu_firstNonLeptonMother, SampleXsection::showering shAlgo, bool verbose)
Definition: TruthTools.cxx:311
top::truth::isFromWZDecay
bool isFromWZDecay(const xAOD::TruthParticle *truthParticle, bool bOnlyDirect)
Function to determine whether a given truth particle is a result of a decay chain that started in the...
Definition: TruthTools.cxx:924
xAOD::track
@ track
Definition: TrackingPrimitives.h:512
xAOD::TrackParticle_v1
Class describing a TrackParticle.
Definition: TrackParticle_v1.h:43
top::LepPartonOriginFlag::FromHadronicBosonToHF
@ FromHadronicBosonToHF
general case of W/Z/gamma*‍/H->HF hadrons->muon
MsgCategory.h
top::LepParticleOriginFlag::FromC
@ FromC
from direct C-hadron (with no B-hadron parent) decay
top::LepPartonOriginFlag::FromBSMViaLeptonicBosonToHF
@ FromBSMViaLeptonicBosonToHF
BSMparticle->muon.
xAOD::TruthParticle_v1::pdgId
int pdgId() const
PDG ID code.
top::truth::printDecayChain
void printDecayChain(const xAOD::TruthParticle *truthPart, std::ostream &os, const std::string &prefix)
Prints the decay chain leading up to a certain particle to a std::ostream.
Definition: TruthTools.cxx:783
top::LepPartonOriginFlag
LepPartonOriginFlag
this enum defines a flag used to understand the partonic origin of a lepton (tipically a soft muon),...
Definition: EventTools.h:33
python.AutoConfigFlags.msg
msg
Definition: AutoConfigFlags.py:7
top::truth::copyTruthMuonHistoryInfo
void copyTruthMuonHistoryInfo(const xAOD::TruthParticle *tm_origin, const xAOD::TruthParticle *tm_target)
Definition: TruthTools.cxx:77
HepMCHelpers.h
top::LepParticleOriginFlag::FromHiggsToTau
@ FromHiggsToTau
Higgs->muon.
AuxElement.h
Base class for elements of a container that can have aux data.
xAOD::TruthParticle_v1::isTop
bool isTop() const
Check if this particle is a top quark.
isMuon
bool isMuon(const T &p)
Definition: AtlasPID.h:145
top::truth::copyRecoMuonHistoryInfo
void copyRecoMuonHistoryInfo(const xAOD::Muon *m_origin, const xAOD::Muon *m_target)
Definition: TruthTools.cxx:70