CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
GeneratorAnalyzer.py
Go to the documentation of this file.
1 from PhysicsTools.Heppy.analyzers.core.Analyzer import Analyzer
2 from PhysicsTools.Heppy.analyzers.core.AutoHandle import AutoHandle
3 from PhysicsTools.Heppy.physicsutils.genutils import isNotFromHadronicShower, realGenMothers, realGenDaughters
4 
5 def interestingPdgId(id,includeLeptons=False):
6  id = abs(id)
7  return id in [6,7,8,17,18] or (includeLeptons and 11 <= id and id < 16) or (22 <= id and id < 40) or id > 1000000
8 
9 class GeneratorAnalyzer( Analyzer ):
10  """Save the hard-scattering final state of the event: top quarks, gauge & higgs bosons and BSM
11  particles, plus their immediate decay products, and their siblings (in order to get the jets
12  from matched X+jets generation.
13  Incoming partons are not included, by design.
14 
15  In the default configuration, leptons, light quarks and gluons are saved before FSR (a la status 3).
16  Everything else is saved after any radiation, i.e. immediately before the decay.
17 
18  Particles are saved in a list event.generatorSummary, with the index of their mothers ('motherIndex')
19  if the mother is also in the list, and with the pdgId of the mother ('motherId') and grand-mother
20  ('grandmotherId'). Particles also carry their index in the miniAOD genparticles collection ('rawIndex')
21  In addition, a 'sourceId' is set to the pdgId of the heaviest ancestor (or of the particle itself)
22  i.e. in top -> W -> lepton: the lepton sourceId will be 6
23  in tt+W with W -> lepton, the sourceId of the lepton will be 24.
24  sourceId will be 99 for paricles from hard scattering whose mother is light
25 
26  If requested, the full list of genParticles is also produced in event.genParticles (with indices
27  aligned to the miniAOD one). For particles that are in the generatorSummary, the same object is used.
28  An extra index 'genSummaryIndex' will be added to all particles, with the index in the generatorSummary
29  or -1 if the particle is not in the generatorSummary.
30 
31  Also, if requested it creates the splitted collections:
32  event.genHiggsBosons = []
33  event.genVBosons = []
34  event.gennus = [] # prompt neutrinos
35  event.genleps = [] # leptons from direct decays
36  event.gentauleps = [] # leptons from prompt taus
37  event.gentaus = [] # hadronically-decaying taus (if allGenTaus is False) or all taus (if allGenTaus is True)
38  event.gentopquarks = []
39  event.genbquarks = [] # b quarks from hard event (e.g. from top decays)
40  event.genwzquarks = [] # quarks from W,Z decays
41  event.genbquarksFromTop = []
42  event.genbquarksFromH = []
43  event.genwzquarks and event.genbquarks, might have overlaps
44  event.genbquarksFromTop and event.genbquarksFromH are all contained in event.genbquarks
45 
46  """
47 
48  def __init__(self, cfg_ana, cfg_comp, looperName ):
49  super(GeneratorAnalyzer,self).__init__(cfg_ana,cfg_comp,looperName)
50  self.stableBSMParticleIds = set(cfg_ana.stableBSMParticleIds) # neutralinos and such
51  self.savePreFSRParticleIds = set(cfg_ana.savePreFSRParticleIds)
52  self.makeAllGenParticles = cfg_ana.makeAllGenParticles
53  self.makeSplittedGenLists = cfg_ana.makeSplittedGenLists
54  self.allGenTaus = cfg_ana.allGenTaus if self.makeSplittedGenLists else False
55 
56  def declareHandles(self):
57  super(GeneratorAnalyzer, self).declareHandles()
58  self.mchandles['genParticles'] = AutoHandle( 'prunedGenParticles', 'std::vector<reco::GenParticle>' )
59 
60  def beginLoop(self,setup):
61  super(GeneratorAnalyzer,self).beginLoop(setup)
62 
63  def makeMCInfo(self, event):
64  verbose = getattr(self.cfg_ana, 'verbose', False)
65  rawGenParticles = self.mchandles['genParticles'].product()
66  good = []; keymap = {};
67  for rawIndex,p in enumerate(rawGenParticles):
68  id = abs(p.pdgId())
69  status = p.status()
70  # particles must be status > 2, except for prompt leptons, photons, neutralinos
71  if status <= 2:
72  if ((id not in self.stableBSMParticleIds) and
73  (id not in [11,12,13,14,15,16,22] or not isNotFromHadronicShower(p))):
74  continue
75  # a particle must not be decaying into itself
76  #print " test %6d : %+8d %3d : %8.2f %+5.2f %+5.2f : %d %d : %+8d {%6d}: %s" % ( rawIndex,
77  # p.pdgId(), p.status(), p.pt(), p.eta(), p.phi(), p.numberOfMothers(), p.numberOfDaughters(),
78  # p.motherRef(0).pdgId() if p.numberOfMothers() > 0 else -999, p.motherRef(0).key() if p.numberOfMothers() > 0 else -999,
79  # " ".join("%d[%d]" % (p.daughter(i).pdgId(), p.daughter(i).status()) for i in xrange(p.numberOfDaughters())))
80  if id in self.savePreFSRParticleIds:
81  # for light objects, we want them pre-radiation
82  if any((p.mother(j).pdgId() == p.pdgId()) for j in xrange(p.numberOfMothers())):
83  #print " fail auto-decay"
84  continue
85  else:
86  # everything else, we want it after radiation, i.e. just before decay
87  if any((p.daughter(j).pdgId() == p.pdgId() and p.daughter(j).status > 2) for j in xrange(p.numberOfDaughters())):
88  #print " fail auto-decay"
89  continue
90  # FIXME find a better criterion to discard there
91  if status == 71:
92  #drop QCD radiation with unclear parentage
93  continue
94  # is it an interesting particle?
95  ok = False
96  if interestingPdgId(id):
97  #print " pass pdgId"
98  ok = True
99  ### no: we don't select by decay, so that we keep the particle summary free of incoming partons and such
100  # if not ok and any(interestingPdgId(d.pdgId()) for d in realGenDaughters(p)):
101  # #print " pass dau"
102  # ok = True
103  if not ok:
104  for mom in realGenMothers(p):
105  if interestingPdgId(mom.pdgId()) or (getattr(mom,'rawIndex',-1) in keymap):
106  #print " interesting mom"
107  # exclude extra x from p -> p + x
108  if not any(mom.daughter(j2).pdgId() == mom.pdgId() for j2 in xrange(mom.numberOfDaughters())):
109  #print " pass no-self-decay"
110  ok = True
111  if abs(mom.pdgId()) == 15:
112  # if we're a tau daughter we're status 2
113  # if we passed all the previous steps, then we're a prompt lepton
114  # so we'd like to be included
115  ok = True
116  if not ok and p.pt() > 10 and id in [1,2,3,4,5,21,22] and any(interestingPdgId(d.pdgId()) for d in realGenDaughters(mom)):
117  # interesting for being a parton brother of an interesting particle (to get the extra jets in ME+PS)
118  ok = True
119  if ok:
120  gp = p
121  gp.rawIndex = rawIndex # remember its index, so that we can set the mother index later
122  keymap[rawIndex] = len(good)
123  good.append(gp)
124  # connect mother links
125  for igp,gp in enumerate(good):
126  gp.motherIndex = -1
127  gp.sourceId = 99
128  gp.genSummaryIndex = igp
129  ancestor = None if gp.numberOfMothers() == 0 else gp.motherRef(0)
130  while ancestor != None and ancestor.isNonnull():
131  if ancestor.key() in keymap:
132  gp.motherIndex = keymap[ancestor.key()]
133  if ancestor.pdgId() != good[gp.motherIndex].pdgId():
134  print "Error keying %d: motherIndex %d, ancestor.pdgId %d, good[gp.motherIndex].pdgId() %d " % (igp, gp.motherIndex, ancestor.pdgId(), good[gp.motherIndex].pdgId())
135  break
136  ancestor = None if ancestor.numberOfMothers() == 0 else ancestor.motherRef(0)
137  if abs(gp.pdgId()) not in {1,2,3,4,5,11,12,13,14,15,16,21}:
138  gp.sourceId = gp.pdgId()
139  if gp.motherIndex != -1:
140  ancestor = good[gp.motherIndex]
141  if ancestor.sourceId != 99 and (ancestor.mass() > gp.mass() or gp.sourceId == 99):
142  gp.sourceId = ancestor.sourceId
143  event.generatorSummary = good
144  # add the ID of the mother to be able to recreate last decay chains
145  for ip,p in enumerate(event.generatorSummary):
146  moms = realGenMothers(p)
147  if len(moms)==0:
148  p.motherId = 0
149  p.grandmotherId = 0
150  elif len(moms)==1:
151  p.motherId = moms[0].pdgId()
152  gmoms = realGenMothers(moms[0])
153  p.grandmotherId = (gmoms[0].pdgId() if len(gmoms)==1 else (0 if len(gmoms)==0 else -9999))
154  else:
155  #print " unclear what mothers to give to this particle, among "," ".join("%d[%d]" % (m.pdgId(),m.status()) for m in moms)
156  p.motherId = -9999
157  p.grandmotherId = -9999
158  if verbose:
159  print "%3d {%6d}: %+8d %3d : %8.2f %+5.2f %+5.2f : %d %2d : %+8d {%3d}: %s" % ( ip,p.rawIndex,
160  p.pdgId(), p.status(), p.pt(), p.eta(), p.phi(), len(moms), p.numberOfDaughters(),
161  p.motherId, p.motherIndex,
162  " ".join("%d[%d]" % (p.daughter(i).pdgId(), p.daughter(i).status()) for i in xrange(p.numberOfDaughters())))
163  if verbose:
164  print "\n\n"
165 
166  if self.makeAllGenParticles:
167  event.genParticles = []
168  for rawIndex,p in enumerate(rawGenParticles):
169  if rawIndex in keymap:
170  gp = event.generatorSummary[keymap[rawIndex]]
171  else:
172  gp = p
173  gp.rawIndex = rawIndex
174  gp.genSummaryIndex = -1
175  event.genParticles.append(gp)
176  if self.makeSplittedGenLists:
177  event.genHiggsBosons = []
178  event.genVBosons = []
179  event.gennus = []
180  event.genleps = []
181  event.gentauleps = []
182  event.gentaus = []
183  event.gentopquarks = []
184  event.genbquarks = []
185  event.genwzquarks = []
186  event.genbquarksFromTop = []
187  event.genbquarksFromH = []
188  for p in event.generatorSummary:
189  id = abs(p.pdgId())
190  if id == 25:
191  event.genHiggsBosons.append(p)
192  elif id in {23,24}:
193  event.genVBosons.append(p)
194  elif id in {12,14,16}:
195  event.gennus.append(p)
196  elif id in {11,13}:
197  if abs(p.motherId) == 15:
198  event.gentauleps.append(p)
199  else:
200  event.genleps.append(p)
201  elif id == 15:
202  if self.allGenTaus or not any([abs(d.pdgId()) in {11,13} for d in realGenDaughters(p)]):
203  event.gentaus.append(p)
204  elif id == 6:
205  event.gentopquarks.append(p)
206  elif id == 5:
207  event.genbquarks.append(p)
208  momids = [abs(m.pdgId()) for m in realGenMothers(p)]
209  if 6 in momids: event.genbquarksFromTop.append(p)
210  if 25 in momids: event.genbquarksFromH.append(p)
211  if id <= 5 and any([abs(m.pdgId()) in {23,24} for m in realGenMothers(p)]):
212  event.genwzquarks.append(p)
213 
214  def process(self, event):
215  self.readCollections( event.input )
216 
217  # if not MC, nothing to do
218  if not self.cfg_comp.isMC:
219  return True
220  # do MC level analysis
221  self.makeMCInfo(event)
222  return True
223 
225 setattr(GeneratorAnalyzer,"defaultConfig",
226  cfg.Analyzer(GeneratorAnalyzer,
227  # BSM particles that can appear with status <= 2 and should be kept
228  stableBSMParticleIds = { 1000022 },
229  # Particles of which we want to save the pre-FSR momentum (a la status 3).
230  # Note that for quarks and gluons the post-FSR doesn't make sense,
231  # so those should always be in the list
232  savePreFSRParticleIds = { 1,2,3,4,5, 11,12,13,14,15,16, 21 },
233  # Make also the list of all genParticles, for other analyzers to handle
234  makeAllGenParticles = True,
235  # Make also the splitted lists
236  makeSplittedGenLists = True,
237  allGenTaus = False,
238  # Print out debug information
239  verbose = False,
240  )
241 )
def realGenMothers
Definition: genutils.py:96
def isNotFromHadronicShower
Definition: genutils.py:65
bool any(const std::vector< T > &v, const T &what)
Definition: ECalSD.cc:34
def realGenDaughters
Definition: genutils.py:77
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
tuple status
Definition: ntuplemaker.py:245