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.gennusFromTop = [] # Neutrinos from t->W decay
36  event.genleps = [] # leptons from direct decays
37  event.gentauleps = [] # leptons from prompt taus
38  event.gentaus = [] # hadronically-decaying taus (if allGenTaus is False) or all taus (if allGenTaus is True)
39  event.gentopquarks = []
40  event.genbquarks = [] # b quarks from hard event (e.g. from top decays)
41  event.genwzquarks = [] # quarks from W,Z decays
42  event.genbquarksFromTop = []
43  event.genbquarksFromH = []
44  event.genlepsFromTop = [] #mu/ele that have a t->W chain as ancestor, also contained in event.genleps
45  event.genwzquarks and event.genbquarks, might have overlaps
46  event.genbquarksFromTop and event.genbquarksFromH are all contained in event.genbquarks
47 
48  """
49 
50  def __init__(self, cfg_ana, cfg_comp, looperName ):
51  super(GeneratorAnalyzer,self).__init__(cfg_ana,cfg_comp,looperName)
52  self.stableBSMParticleIds = set(cfg_ana.stableBSMParticleIds) # neutralinos and such
53  self.savePreFSRParticleIds = set(cfg_ana.savePreFSRParticleIds)
54  self.makeAllGenParticles = cfg_ana.makeAllGenParticles
55  self.makeSplittedGenLists = cfg_ana.makeSplittedGenLists
56  self.allGenTaus = cfg_ana.allGenTaus if self.makeSplittedGenLists else False
57 
58  def declareHandles(self):
59  super(GeneratorAnalyzer, self).declareHandles()
60  self.mchandles['genParticles'] = AutoHandle( 'prunedGenParticles', 'std::vector<reco::GenParticle>' )
61 
62  def beginLoop(self,setup):
63  super(GeneratorAnalyzer,self).beginLoop(setup)
64 
65  def makeMCInfo(self, event):
66  verbose = getattr(self.cfg_ana, 'verbose', False)
67  rawGenParticles = self.mchandles['genParticles'].product()
68  good = []; keymap = {};
69  allGenParticles = []
70  for rawIndex,p in enumerate(rawGenParticles):
71  if self.makeAllGenParticles: allGenParticles.append(p)
72  id = abs(p.pdgId())
73  status = p.status()
74  # particles must be status > 2, except for prompt leptons, photons, neutralinos
75  if status <= 2:
76  if ((id not in self.stableBSMParticleIds) and
77  (id not in [11,12,13,14,15,16,22] or not isNotFromHadronicShower(p))):
78  continue
79  # a particle must not be decaying into itself
80  #print " test %6d : %+8d %3d : %8.2f %+5.2f %+5.2f : %d %d : %+8d {%6d}: %s" % ( rawIndex,
81  # p.pdgId(), p.status(), p.pt(), p.eta(), p.phi(), p.numberOfMothers(), p.numberOfDaughters(),
82  # p.motherRef(0).pdgId() if p.numberOfMothers() > 0 else -999, p.motherRef(0).key() if p.numberOfMothers() > 0 else -999,
83  # " ".join("%d[%d]" % (p.daughter(i).pdgId(), p.daughter(i).status()) for i in xrange(p.numberOfDaughters())))
84  if id in self.savePreFSRParticleIds:
85  # for light objects, we want them pre-radiation
86  if any((p.mother(j).pdgId() == p.pdgId()) for j in xrange(p.numberOfMothers())):
87  #print " fail auto-decay"
88  continue
89  else:
90  # everything else, we want it after radiation, i.e. just before decay
91  if any((p.daughter(j).pdgId() == p.pdgId() and p.daughter(j).status() > 2) for j in xrange(p.numberOfDaughters())):
92  #print " fail auto-decay"
93  continue
94  # FIXME find a better criterion to discard there
95  if status == 71:
96  #drop QCD radiation with unclear parentage
97  continue
98  # is it an interesting particle?
99  ok = False
100  if interestingPdgId(id):
101  #print " pass pdgId"
102  ok = True
103  ### no: we don't select by decay, so that we keep the particle summary free of incoming partons and such
104  # if not ok and any(interestingPdgId(d.pdgId()) for d in realGenDaughters(p)):
105  # #print " pass dau"
106  # ok = True
107  if not ok:
108  for mom in realGenMothers(p):
109  if interestingPdgId(mom.pdgId()) or (getattr(mom,'rawIndex',-1) in keymap):
110  #print " interesting mom"
111  # exclude extra x from p -> p + x
112  if not any(mom.daughter(j2).pdgId() == mom.pdgId() for j2 in xrange(mom.numberOfDaughters())):
113  #print " pass no-self-decay"
114  ok = True
115  # Account for generator feature with Higgs decaying to itself with same four-vector but no daughters
116  elif mom.pdgId() == 25 and any(mom.daughter(j2).pdgId() == 25 and mom.daughter(j2).numberOfDaughters()==0 for j2 in range(mom.numberOfDaughters())):
117  ok = True
118  if abs(mom.pdgId()) == 15:
119  # if we're a tau daughter we're status 2
120  # if we passed all the previous steps, then we're a prompt lepton
121  # so we'd like to be included
122  ok = True
123  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)):
124  # interesting for being a parton brother of an interesting particle (to get the extra jets in ME+PS)
125  ok = True
126  if ok:
127  gp = p
128  gp.rawIndex = rawIndex # remember its index, so that we can set the mother index later
129  keymap[rawIndex] = len(good)
130  good.append(gp)
131  # connect mother links
132  for igp,gp in enumerate(good):
133  gp.motherIndex = -1
134  gp.sourceId = 99
135  gp.genSummaryIndex = igp
136  ancestor = None if gp.numberOfMothers() == 0 else gp.motherRef(0)
137  while ancestor != None and ancestor.isNonnull():
138  if ancestor.key() in keymap:
139  gp.motherIndex = keymap[ancestor.key()]
140  if ancestor.pdgId() != good[gp.motherIndex].pdgId():
141  print "Error keying %d: motherIndex %d, ancestor.pdgId %d, good[gp.motherIndex].pdgId() %d " % (igp, gp.motherIndex, ancestor.pdgId(), good[gp.motherIndex].pdgId())
142  break
143  ancestor = None if ancestor.numberOfMothers() == 0 else ancestor.motherRef(0)
144  if abs(gp.pdgId()) not in [1,2,3,4,5,11,12,13,14,15,16,21]:
145  gp.sourceId = gp.pdgId()
146  if gp.motherIndex != -1:
147  ancestor = good[gp.motherIndex]
148  if ancestor.sourceId != 99 and (ancestor.mass() > gp.mass() or gp.sourceId == 99):
149  gp.sourceId = ancestor.sourceId
150  event.generatorSummary = good
151  # add the ID of the mother to be able to recreate last decay chains
152  for ip,p in enumerate(good):
153  moms = realGenMothers(p)
154  if len(moms)==0:
155  p.motherId = 0
156  p.grandmotherId = 0
157  elif len(moms)==1:
158  p.motherId = moms[0].pdgId()
159  gmoms = realGenMothers(moms[0])
160  p.grandmotherId = (gmoms[0].pdgId() if len(gmoms)==1 else (0 if len(gmoms)==0 else -9999))
161  else:
162  #print " unclear what mothers to give to this particle, among "," ".join("%d[%d]" % (m.pdgId(),m.status()) for m in moms)
163  p.motherId = -9999
164  p.grandmotherId = -9999
165  if verbose:
166  print "%3d {%6d}: %+8d %3d : %8.2f %+5.2f %+5.2f : %d %2d : %+8d {%3d}: %s" % ( ip,p.rawIndex,
167  p.pdgId(), p.status(), p.pt(), p.eta(), p.phi(), len(moms), p.numberOfDaughters(),
168  p.motherId, p.motherIndex,
169  " ".join("%d[%d]" % (p.daughter(i).pdgId(), p.daughter(i).status()) for i in xrange(p.numberOfDaughters())))
170  if verbose:
171  print "\n\n"
172 
173  if self.makeAllGenParticles:
174  event.genParticles = allGenParticles
175 
176  if self.makeSplittedGenLists:
177  event.genHiggsBosons = []
178  event.genVBosons = []
179  event.gennus = []
180  event.gennusFromTop = []
181  event.genleps = []
182  event.gentauleps = []
183  event.gentaus = []
184  event.gentopquarks = []
185  event.genbquarks = []
186  event.genwzquarks = []
187  event.genbquarksFromTop = []
188  event.genbquarksFromH = []
189  event.genlepsFromTop = []
190  for p in event.generatorSummary:
191  id = abs(p.pdgId())
192  if id == 25:
193  event.genHiggsBosons.append(p)
194  elif id in {23,24}:
195  event.genVBosons.append(p)
196  elif id in {12,14,16}:
197  event.gennus.append(p)
198 
199  momids = [(m, abs(m.pdgId())) for m in realGenMothers(p)]
200 
201  #have a look at the lepton mothers
202  for mom, momid in momids:
203  #lepton from W
204  if momid == 24:
205  wmomids = [abs(m.pdgId()) for m in realGenMothers(mom)]
206  #W from t
207  if 6 in wmomids:
208  #save mu,e from t->W->mu/e
209  event.gennusFromTop.append(p)
210 
211  elif id in {11,13}:
212  #taus to separate vector
213  if abs(p.motherId) == 15:
214  event.gentauleps.append(p)
215  #all muons and electrons
216  else:
217  event.genleps.append(p)
218  momids = [(m, abs(m.pdgId())) for m in realGenMothers(p)]
219 
220  #have a look at the lepton mothers
221  for mom, momid in momids:
222  #lepton from W
223  if momid == 24:
224  wmomids = [abs(m.pdgId()) for m in realGenMothers(mom)]
225  #W from t
226  if 6 in wmomids:
227  #save mu,e from t->W->mu/e
228  event.genlepsFromTop.append(p)
229  elif id == 15:
230  if self.allGenTaus or not any([abs(d.pdgId()) in {11,13} for d in realGenDaughters(p)]):
231  event.gentaus.append(p)
232  elif id == 6:
233  event.gentopquarks.append(p)
234  elif id == 5:
235  event.genbquarks.append(p)
236  momids = [abs(m.pdgId()) for m in realGenMothers(p)]
237  if 6 in momids: event.genbquarksFromTop.append(p)
238  if 25 in momids: event.genbquarksFromH.append(p)
239  if id <= 5 and any([abs(m.pdgId()) in {23,24} for m in realGenMothers(p)]):
240  event.genwzquarks.append(p)
241 
242  def process(self, event):
243  self.readCollections( event.input )
244 
245  # if not MC, nothing to do
246  if not self.cfg_comp.isMC:
247  return True
248  # do MC level analysis
249  self.makeMCInfo(event)
250  return True
251 
253 setattr(GeneratorAnalyzer,"defaultConfig",
254  cfg.Analyzer(GeneratorAnalyzer,
255  # BSM particles that can appear with status <= 2 and should be kept
256  stableBSMParticleIds = [ 1000022 ],
257  # Particles of which we want to save the pre-FSR momentum (a la status 3).
258  # Note that for quarks and gluons the post-FSR doesn't make sense,
259  # so those should always be in the list
260  savePreFSRParticleIds = [ 1,2,3,4,5, 11,12,13,14,15,16, 21 ],
261  # Make also the list of all genParticles, for other analyzers to handle
262  makeAllGenParticles = True,
263  # Make also the splitted lists
264  makeSplittedGenLists = True,
265  allGenTaus = False,
266  # Print out debug information
267  verbose = False,
268  )
269 )
unsigned int numberOfDaughters(const reco::GenParticle &p)
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