CMS 3D CMS Logo

LeptonAnalyzer.py
Go to the documentation of this file.
1 from __future__ import print_function
2 from PhysicsTools.Heppy.analyzers.core.Analyzer import Analyzer
3 from PhysicsTools.Heppy.analyzers.core.AutoHandle import AutoHandle
4 from PhysicsTools.Heppy.physicsobjects.Electron import Electron
5 from PhysicsTools.Heppy.physicsobjects.Muon import Muon
6 #from CMGTools.TTHAnalysis.tools.EfficiencyCorrector import EfficiencyCorrector
7 
8 from PhysicsTools.HeppyCore.utils.deltar import bestMatch
9 from PhysicsTools.Heppy.physicsutils.RochesterCorrections import rochcor
10 from PhysicsTools.Heppy.physicsutils.MuScleFitCorrector import MuScleFitCorr
11 from PhysicsTools.Heppy.physicsutils.KalmanMuonCorrector import KalmanMuonCorrector
12 from PhysicsTools.Heppy.physicsutils.ElectronCalibrator import Run2ElectronCalibrator
13 #from CMGTools.TTHAnalysis.electronCalibrator import ElectronCalibrator
14 import PhysicsTools.HeppyCore.framework.config as cfg
17 
18 
19 from ROOT import heppy
20 cmgMuonCleanerBySegments = heppy.CMGMuonCleanerBySegmentsAlgo()
21 
22 class LeptonAnalyzer( Analyzer ):
23 
24 
25  def __init__(self, cfg_ana, cfg_comp, looperName ):
26  super(LeptonAnalyzer,self).__init__(cfg_ana,cfg_comp,looperName)
27  if hasattr(self.cfg_ana, 'doMuScleFitCorrections'):
28  raise RuntimeError("doMuScleFitCorrections is not supported. Please set instead doMuonScaleCorrections = ( 'MuScleFit', <name> )")
29  if hasattr(self.cfg_ana, 'doRochesterCorrections'):
30  raise RuntimeError("doRochesterCorrections is not supported. Please set instead doMuonScaleCorrections = ( 'Rochester', <name> )")
31  if self.cfg_ana.doMuonScaleCorrections:
32  algo, options = self.cfg_ana.doMuonScaleCorrections
33  if algo == "Kalman":
34  corr = options['MC' if self.cfg_comp.isMC else 'Data']
36  self.cfg_comp.isMC,
37  options['isSync'] if 'isSync' in options else False,
38  options['smearMode'] if 'smearMode' in options else "ebe")
39  elif algo == "Rochester":
40  print("WARNING: the Rochester correction in heppy is still from Run 1")
42  elif algo == "MuScleFit":
43  print("WARNING: the MuScleFit correction in heppy is still from Run 1 (and probably no longer functional)")
44  if options not in [ "prompt", "prompt-sync", "rereco", "rereco-sync" ]:
45  raise RuntimeError('MuScleFit correction name must be one of [ "prompt", "prompt-sync", "rereco", "rereco-sync" ] ')
46  rereco = ("prompt" not in self.cfg_ana.doMuScleFitCorrections)
47  sync = ("sync" in self.cfg_ana.doMuScleFitCorrections)
48  self.muonScaleCorrector = MuScleFitCorr(cfg_comp.isMC, rereco, sync)
49  else: raise RuntimeError("Unknown muon scale correction algorithm")
50  else:
51  self.muonScaleCorrector = None
52  #FIXME: only Embedded works
53  if self.cfg_ana.doElectronScaleCorrections:
54  conf = cfg_ana.doElectronScaleCorrections
55  self.electronEnergyCalibrator = Run2ElectronCalibrator(
56  conf['data'],
57  conf['GBRForest'],
58  cfg_comp.isMC,
59  conf['isSync'] if 'isSync' in conf else False,
60  )
61 # if hasattr(cfg_comp,'efficiency'):
62 # self.efficiency= EfficiencyCorrector(cfg_comp.efficiency)
63  # Isolation cut
64  if hasattr(cfg_ana, 'loose_electron_isoCut'):
65  self.eleIsoCut = cfg_ana.loose_electron_isoCut
66  else:
67  self.eleIsoCut = lambda ele : (
68  ele.relIso03 <= self.cfg_ana.loose_electron_relIso and
69  ele.absIso03 < getattr(self.cfg_ana,'loose_electron_absIso',9e99))
70  if hasattr(cfg_ana, 'loose_muon_isoCut'):
71  self.muIsoCut = cfg_ana.loose_muon_isoCut
72  else:
73  self.muIsoCut = lambda mu : (
74  mu.relIso03 <= self.cfg_ana.loose_muon_relIso and
75  mu.absIso03 < getattr(self.cfg_ana,'loose_muon_absIso',9e99))
76 
77 
78 
79  self.eleEffectiveArea = getattr(cfg_ana, 'ele_effectiveAreas', "Spring15_25ns_v1")
80  self.muEffectiveArea = getattr(cfg_ana, 'mu_effectiveAreas', "Spring15_25ns_v1")
81  # MiniIsolation
82  self.doMiniIsolation = getattr(cfg_ana, 'doMiniIsolation', False)
83  if self.doMiniIsolation:
84  self.miniIsolationPUCorr = self.cfg_ana.miniIsolationPUCorr
85  self.miniIsolationVetoLeptons = self.cfg_ana.miniIsolationVetoLeptons
86  if self.miniIsolationVetoLeptons not in [ None, 'any', 'inclusive' ]:
87  raise RuntimeError("miniIsolationVetoLeptons should be None, or 'any' (all reco leptons), or 'inclusive' (all inclusive leptons)")
88  if self.miniIsolationPUCorr == "weights":
90  else:
92 
93  self.doIsoAnnulus = getattr(cfg_ana, 'doIsoAnnulus', False)
94  if self.doIsoAnnulus:
95  if not self.doMiniIsolation:
97 
98  self.doIsolationScan = getattr(cfg_ana, 'doIsolationScan', False)
99  if self.doIsolationScan:
100  if self.doMiniIsolation:
101  assert (self.miniIsolationPUCorr!="weights")
102  assert (self.miniIsolationVetoLeptons==None)
103  else:
105 
106 
107  self.doMatchToPhotons = getattr(cfg_ana, 'do_mc_match_photons', False)
108 
109  #----------------------------------------
110  # DECLARATION OF HANDLES OF LEPTONS STUFF
111  #----------------------------------------
112 
113 
114  def declareHandles(self):
115  super(LeptonAnalyzer, self).declareHandles()
116 
117  #leptons
118  self.handles['muons'] = AutoHandle(self.cfg_ana.muons,"std::vector<pat::Muon>")
119  self.handles['electrons'] = AutoHandle(self.cfg_ana.electrons,"std::vector<pat::Electron>")
120 
121  #rho for muons
122  self.handles['rhoMu'] = AutoHandle( self.cfg_ana.rhoMuon, 'double')
123  #rho for electrons
124  self.handles['rhoEle'] = AutoHandle( self.cfg_ana.rhoElectron, 'double')
125 
126  if self.doMiniIsolation or self.doIsolationScan:
127  self.handles['packedCandidates'] = AutoHandle( self.cfg_ana.packedCandidates, 'std::vector<pat::PackedCandidate>')
128 
129  if self.doMatchToPhotons:
130  if self.doMatchToPhotons == "any":
131  self.mchandles['genPhotons'] = AutoHandle( 'packedGenParticles', 'std::vector<pat::PackedGenParticle>' )
132  else:
133  self.mchandles['genPhotons'] = AutoHandle( 'prunedGenParticles', 'std::vector<reco::GenParticle>' )
134 
135  def beginLoop(self, setup):
136  super(LeptonAnalyzer,self).beginLoop(setup)
137  self.counters.addCounter('events')
138  count = self.counters.counter('events')
139  count.register('all events')
140 
141  #------------------
142  # MAKE LEPTON LISTS
143  #------------------
144 
145 
146  def makeLeptons(self, event):
147  ### inclusive leptons = all leptons that could be considered somewhere in the analysis, with minimal requirements (used e.g. to match to MC)
148  event.inclusiveLeptons = []
149  ### selected leptons = subset of inclusive leptons passing some basic id definition and pt requirement
150  ### other leptons = subset of inclusive leptons failing some basic id definition and pt requirement
151  event.selectedLeptons = []
152  event.selectedMuons = []
153  event.selectedElectrons = []
154  event.otherLeptons = []
155 
156  if self.doMiniIsolation or self.doIsolationScan:
157  self.IsolationComputer.setPackedCandidates(self.handles['packedCandidates'].product())
158  if self.doMiniIsolation:
159  if self.miniIsolationVetoLeptons == "any":
160  for lep in self.handles['muons'].product():
161  self.IsolationComputer.addVetos(lep)
162  for lep in self.handles['electrons'].product():
163  self.IsolationComputer.addVetos(lep)
164 
165  #muons
166  allmuons = self.makeAllMuons(event)
167 
168  #electrons
169  allelectrons = self.makeAllElectrons(event)
170 
171  #make inclusive leptons
172  inclusiveMuons = []
173  inclusiveElectrons = []
174  for mu in allmuons:
175  if (mu.track().isNonnull() and mu.muonID(self.cfg_ana.inclusive_muon_id) and
176  mu.pt()>self.cfg_ana.inclusive_muon_pt and abs(mu.eta())<self.cfg_ana.inclusive_muon_eta and
177  abs(mu.dxy())<self.cfg_ana.inclusive_muon_dxy and abs(mu.dz())<self.cfg_ana.inclusive_muon_dz):
178  inclusiveMuons.append(mu)
179  for ele in allelectrons:
180  if ( ele.electronID(self.cfg_ana.inclusive_electron_id) and
181  ele.pt()>self.cfg_ana.inclusive_electron_pt and abs(ele.eta())<self.cfg_ana.inclusive_electron_eta and
182  abs(ele.dxy())<self.cfg_ana.inclusive_electron_dxy and abs(ele.dz())<self.cfg_ana.inclusive_electron_dz and
183  ele.lostInner()<=self.cfg_ana.inclusive_electron_lostHits ):
184  inclusiveElectrons.append(ele)
185  event.inclusiveLeptons = inclusiveMuons + inclusiveElectrons
186 
187  if self.doMiniIsolation:
188  if self.miniIsolationVetoLeptons == "inclusive":
189  for lep in event.inclusiveLeptons:
190  self.IsolationComputer.addVetos(lep.physObj)
191  for lep in event.inclusiveLeptons:
192  self.attachMiniIsolation(lep)
193 
194  if self.doIsoAnnulus:
195  for lep in event.inclusiveLeptons:
196  self.attachIsoAnnulus04(lep)
197 
198  if self.doIsolationScan:
199  for lep in event.inclusiveLeptons:
200  self.attachIsolationScan(lep)
201 
202  # make loose leptons (basic selection)
203  for mu in inclusiveMuons:
204  if (mu.muonID(self.cfg_ana.loose_muon_id) and
205  mu.pt() > self.cfg_ana.loose_muon_pt and abs(mu.eta()) < self.cfg_ana.loose_muon_eta and
206  abs(mu.dxy()) < self.cfg_ana.loose_muon_dxy and abs(mu.dz()) < self.cfg_ana.loose_muon_dz and
207  self.muIsoCut(mu)):
208  mu.looseIdSusy = True
209  event.selectedLeptons.append(mu)
210  event.selectedMuons.append(mu)
211  else:
212  mu.looseIdSusy = False
213  event.otherLeptons.append(mu)
214  looseMuons = event.selectedLeptons[:]
215  for ele in inclusiveElectrons:
216  if (ele.electronID(self.cfg_ana.loose_electron_id) and
217  ele.pt()>self.cfg_ana.loose_electron_pt and abs(ele.eta())<self.cfg_ana.loose_electron_eta and
218  abs(ele.dxy()) < self.cfg_ana.loose_electron_dxy and abs(ele.dz())<self.cfg_ana.loose_electron_dz and
219  self.eleIsoCut(ele) and
220  ele.lostInner() <= self.cfg_ana.loose_electron_lostHits and
221  ( True if getattr(self.cfg_ana,'notCleaningElectrons',False) else (bestMatch(ele, looseMuons)[1] > (self.cfg_ana.min_dr_electron_muon**2)) )):
222  event.selectedLeptons.append(ele)
223  event.selectedElectrons.append(ele)
224  ele.looseIdSusy = True
225  else:
226  event.otherLeptons.append(ele)
227  ele.looseIdSusy = False
228 
229  event.otherLeptons.sort(key = lambda l : l.pt(), reverse = True)
230  event.selectedLeptons.sort(key = lambda l : l.pt(), reverse = True)
231  event.selectedMuons.sort(key = lambda l : l.pt(), reverse = True)
232  event.selectedElectrons.sort(key = lambda l : l.pt(), reverse = True)
233  event.inclusiveLeptons.sort(key = lambda l : l.pt(), reverse = True)
234 
235  for lepton in event.selectedLeptons:
236  if hasattr(self,'efficiency'):
237  self.efficiency.attachToObject(lepton)
238 
239  def makeAllMuons(self, event):
240  """
241  make a list of all muons, and apply basic corrections to them
242  """
243  # Start from all muons
244  allmuons = map( Muon, self.handles['muons'].product() )
245 
246  # Muon scale and resolution corrections (if enabled)
247  if self.muonScaleCorrector:
248  self.muonScaleCorrector.correct_all(allmuons, event.run)
249 
250  # Clean up dulicate muons (note: has no effect unless the muon id is removed)
251  if self.cfg_ana.doSegmentBasedMuonCleaning:
252  isgood = cmgMuonCleanerBySegments.clean( self.handles['muons'].product() )
253  newmu = []
254  for i,mu in enumerate(allmuons):
255  if isgood[i]: newmu.append(mu)
256  allmuons = newmu
257 
258  # Attach EAs for isolation:
259  for mu in allmuons:
260  mu.rho = float(self.handles['rhoMu'].product()[0])
261  if self.muEffectiveArea == "Data2012":
262  if aeta < 1.0 : mu.EffectiveArea03 = 0.382;
263  elif aeta < 1.47 : mu.EffectiveArea03 = 0.317;
264  elif aeta < 2.0 : mu.EffectiveArea03 = 0.242;
265  elif aeta < 2.2 : mu.EffectiveArea03 = 0.326;
266  elif aeta < 2.3 : mu.EffectiveArea03 = 0.462;
267  else : mu.EffectiveArea03 = 0.372;
268  if aeta < 1.0 : mu.EffectiveArea04 = 0.674;
269  elif aeta < 1.47 : mu.EffectiveArea04 = 0.565;
270  elif aeta < 2.0 : mu.EffectiveArea04 = 0.442;
271  elif aeta < 2.2 : mu.EffectiveArea04 = 0.515;
272  elif aeta < 2.3 : mu.EffectiveArea04 = 0.821;
273  else : mu.EffectiveArea04 = 0.660;
274  elif self.muEffectiveArea == "Phys14_25ns_v1":
275  aeta = abs(mu.eta())
276  if aeta < 0.800: mu.EffectiveArea03 = 0.0913
277  elif aeta < 1.300: mu.EffectiveArea03 = 0.0765
278  elif aeta < 2.000: mu.EffectiveArea03 = 0.0546
279  elif aeta < 2.200: mu.EffectiveArea03 = 0.0728
280  else: mu.EffectiveArea03 = 0.1177
281  if aeta < 0.800: mu.EffectiveArea04 = 0.1564
282  elif aeta < 1.300: mu.EffectiveArea04 = 0.1325
283  elif aeta < 2.000: mu.EffectiveArea04 = 0.0913
284  elif aeta < 2.200: mu.EffectiveArea04 = 0.1212
285  else: mu.EffectiveArea04 = 0.2085
286  elif self.muEffectiveArea == "Spring15_25ns_v1":
287  aeta = abs(mu.eta())
288  if aeta < 0.800: mu.EffectiveArea03 = 0.0735
289  elif aeta < 1.300: mu.EffectiveArea03 = 0.0619
290  elif aeta < 2.000: mu.EffectiveArea03 = 0.0465
291  elif aeta < 2.200: mu.EffectiveArea03 = 0.0433
292  else: mu.EffectiveArea03 = 0.0577
293  mu.EffectiveArea04 = 0 # not computed
294  else: raise RuntimeError("Unsupported value for mu_effectiveAreas: can only use Data2012 (rho: ?) and Phys14_25ns_v1 or Spring15_25ns_v1 (rho: fixedGridRhoFastjetAll)")
295  # Attach the vertex to them, for dxy/dz calculation
296  for mu in allmuons:
297  mu.associatedVertex = event.goodVertices[0] if len(event.goodVertices)>0 else event.vertices[0]
298  mu.setTrackForDxyDz(self.cfg_ana.muon_dxydz_track)
299 
300  # Set tight id if specified
301  if hasattr(self.cfg_ana, "mu_tightId"):
302  for mu in allmuons:
303  mu.tightIdResult = mu.muonID(self.cfg_ana.mu_tightId)
304 
305  # Compute relIso in 0.3 and 0.4 cones
306  for mu in allmuons:
307  if self.cfg_ana.mu_isoCorr=="rhoArea" :
308  mu.absIso03 = (mu.pfIsolationR03().sumChargedHadronPt + max( mu.pfIsolationR03().sumNeutralHadronEt + mu.pfIsolationR03().sumPhotonEt - mu.rho * mu.EffectiveArea03,0.0))
309  mu.absIso04 = (mu.pfIsolationR04().sumChargedHadronPt + max( mu.pfIsolationR04().sumNeutralHadronEt + mu.pfIsolationR04().sumPhotonEt - mu.rho * mu.EffectiveArea04,0.0))
310  elif self.cfg_ana.mu_isoCorr=="deltaBeta" :
311  mu.absIso03 = (mu.pfIsolationR03().sumChargedHadronPt + max( mu.pfIsolationR03().sumNeutralHadronEt + mu.pfIsolationR03().sumPhotonEt - mu.pfIsolationR03().sumPUPt/2,0.0))
312  mu.absIso04 = (mu.pfIsolationR04().sumChargedHadronPt + max( mu.pfIsolationR04().sumNeutralHadronEt + mu.pfIsolationR04().sumPhotonEt - mu.pfIsolationR04().sumPUPt/2,0.0))
313  else :
314  raise RuntimeError("Unsupported mu_isoCorr name '" + str(self.cfg_ana.mu_isoCorr) + "'! For now only 'rhoArea' and 'deltaBeta' are supported.")
315  mu.relIso03 = mu.absIso03/mu.pt()
316  mu.relIso04 = mu.absIso04/mu.pt()
317  return allmuons
318 
319  def makeAllElectrons(self, event):
320  """
321  make a list of all electrons, and apply basic corrections to them
322  """
323  allelectrons = map( Electron, self.handles['electrons'].product() )
324 
325  ## Duplicate removal for fast sim (to be checked if still necessary in latest greatest 5.3.X releases)
326  allelenodup = []
327  for e in allelectrons:
328  dup = False
329  for e2 in allelenodup:
330  if abs(e.pt()-e2.pt()) < 1e-6 and abs(e.eta()-e2.eta()) < 1e-6 and abs(e.phi()-e2.phi()) < 1e-6 and e.charge() == e2.charge():
331  dup = True
332  break
333  if not dup: allelenodup.append(e)
334  allelectrons = allelenodup
335 
336  # fill EA for rho-corrected isolation
337  for ele in allelectrons:
338  ele.rho = float(self.handles['rhoEle'].product()[0])
339  if self.eleEffectiveArea == "Data2012":
340  # https://twiki.cern.ch/twiki/bin/viewauth/CMS/EgammaEARhoCorrection?rev=14
341  SCEta = abs(ele.superCluster().eta())
342  if SCEta < 1.0 : ele.EffectiveArea03 = 0.13 # 0.130;
343  elif SCEta < 1.479: ele.EffectiveArea03 = 0.14 # 0.137;
344  elif SCEta < 2.0 : ele.EffectiveArea03 = 0.07 # 0.067;
345  elif SCEta < 2.2 : ele.EffectiveArea03 = 0.09 # 0.089;
346  elif SCEta < 2.3 : ele.EffectiveArea03 = 0.11 # 0.107;
347  elif SCEta < 2.4 : ele.EffectiveArea03 = 0.11 # 0.110;
348  else : ele.EffectiveArea03 = 0.14 # 0.138;
349  if SCEta < 1.0 : ele.EffectiveArea04 = 0.208;
350  elif SCEta < 1.479: ele.EffectiveArea04 = 0.209;
351  elif SCEta < 2.0 : ele.EffectiveArea04 = 0.115;
352  elif SCEta < 2.2 : ele.EffectiveArea04 = 0.143;
353  elif SCEta < 2.3 : ele.EffectiveArea04 = 0.183;
354  elif SCEta < 2.4 : ele.EffectiveArea04 = 0.194;
355  else : ele.EffectiveArea04 = 0.261;
356  elif self.eleEffectiveArea == "Phys14_25ns_v1":
357  aeta = abs(ele.eta())
358  if aeta < 0.800: ele.EffectiveArea03 = 0.1013
359  elif aeta < 1.300: ele.EffectiveArea03 = 0.0988
360  elif aeta < 2.000: ele.EffectiveArea03 = 0.0572
361  elif aeta < 2.200: ele.EffectiveArea03 = 0.0842
362  else: ele.EffectiveArea03 = 0.1530
363  if aeta < 0.800: ele.EffectiveArea04 = 0.1830
364  elif aeta < 1.300: ele.EffectiveArea04 = 0.1734
365  elif aeta < 2.000: ele.EffectiveArea04 = 0.1077
366  elif aeta < 2.200: ele.EffectiveArea04 = 0.1565
367  else: ele.EffectiveArea04 = 0.2680
368  elif self.eleEffectiveArea == "Spring15_50ns_v1":
369  SCEta = abs(ele.superCluster().eta())
370  ## ----- https://github.com/ikrav/cmssw/blob/egm_id_747_v2/RecoEgamma/ElectronIdentification/data/Spring15/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_50ns.txt
371  if SCEta < 0.800: ele.EffectiveArea03 = 0.0973
372  elif SCEta < 1.300: ele.EffectiveArea03 = 0.0954
373  elif SCEta < 2.000: ele.EffectiveArea03 = 0.0632
374  elif SCEta < 2.200: ele.EffectiveArea03 = 0.0727
375  else: ele.EffectiveArea03 = 0.1337
376  # warning: EAs not computed for cone DR=0.4 yet. Do not correct
377  ele.EffectiveArea04 = 0.0
378  elif self.eleEffectiveArea == "Spring15_25ns_v1":
379  SCEta = abs(ele.superCluster().eta())
380  ## ----- https://github.com/ikrav/cmssw/blob/egm_id_747_v2/RecoEgamma/ElectronIdentification/data/Spring15/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_25ns.txt
381  if SCEta < 1.000: ele.EffectiveArea03 = 0.1752
382  elif SCEta < 1.479: ele.EffectiveArea03 = 0.1862
383  elif SCEta < 2.000: ele.EffectiveArea03 = 0.1411
384  elif SCEta < 2.200: ele.EffectiveArea03 = 0.1534
385  elif SCEta < 2.300: ele.EffectiveArea03 = 0.1903
386  elif SCEta < 2.400: ele.EffectiveArea03 = 0.2243
387  else: ele.EffectiveArea03 = 0.2687
388  # warning: EAs not computed for cone DR=0.4 yet. Do not correct
389  ele.EffectiveArea04 = 0.0
390  else: raise RuntimeError("Unsupported value for ele_effectiveAreas: can only use Data2012 (rho: ?), Phys14_v1 and Spring15_v1 (rho: fixedGridRhoFastjetAll)")
391 
392  # Electron scale calibrations
393  if self.cfg_ana.doElectronScaleCorrections:
394  for ele in allelectrons:
395  self.electronEnergyCalibrator.correct(ele, event.run)
396 
397  # Attach the vertex
398  for ele in allelectrons:
399  ele.associatedVertex = event.goodVertices[0] if len(event.goodVertices)>0 else event.vertices[0]
400 
401  # Compute relIso with R=0.3 and R=0.4 cones
402  for ele in allelectrons:
403  if self.cfg_ana.ele_isoCorr=="rhoArea" :
404  ele.absIso03 = (ele.chargedHadronIsoR(0.3) + max(ele.neutralHadronIsoR(0.3)+ele.photonIsoR(0.3)-ele.rho*ele.EffectiveArea03,0))
405  ele.absIso04 = (ele.chargedHadronIsoR(0.4) + max(ele.neutralHadronIsoR(0.4)+ele.photonIsoR(0.4)-ele.rho*ele.EffectiveArea04,0))
406  elif self.cfg_ana.ele_isoCorr=="deltaBeta" :
407  ele.absIso03 = (ele.chargedHadronIsoR(0.3) + max( ele.neutralHadronIsoR(0.3)+ele.photonIsoR(0.3) - ele.puChargedHadronIsoR(0.3)/2, 0.0))
408  ele.absIso04 = (ele.chargedHadronIsoR(0.4) + max( ele.neutralHadronIsoR(0.4)+ele.photonIsoR(0.4) - ele.puChargedHadronIsoR(0.4)/2, 0.0))
409  else :
410  raise RuntimeError("Unsupported ele_isoCorr name '" + str(self.cfg_ana.ele_isoCorr) + "'! For now only 'rhoArea' and 'deltaBeta' are supported.")
411  ele.relIso03 = ele.absIso03/ele.pt()
412  ele.relIso04 = ele.absIso04/ele.pt()
413 
414  # Set tight MVA id
415  for ele in allelectrons:
416  if self.cfg_ana.ele_tightId=="MVA" :
417  ele.tightIdResult = ele.electronID("POG_MVA_ID_Trig_full5x5")
418  elif self.cfg_ana.ele_tightId=="Cuts_2012" :
419  ele.tightIdResult = -1 + 1*ele.electronID("POG_Cuts_ID_2012_Veto_full5x5") + 1*ele.electronID("POG_Cuts_ID_2012_Loose_full5x5") + 1*ele.electronID("POG_Cuts_ID_2012_Medium_full5x5") + 1*ele.electronID("POG_Cuts_ID_2012_Tight_full5x5")
420  elif self.cfg_ana.ele_tightId=="Cuts_PHYS14_25ns_v1_ConvVetoDxyDz" :
421  ele.tightIdResult = -1 + 1*ele.electronID("POG_Cuts_ID_PHYS14_25ns_v1_ConvVetoDxyDz_Veto_full5x5") + 1*ele.electronID("POG_Cuts_ID_PHYS14_25ns_v1_ConvVetoDxyDz_Loose_full5x5") + 1*ele.electronID("POG_Cuts_ID_PHYS14_25ns_v1_ConvVetoDxyDz_Medium_full5x5") + 1*ele.electronID("POG_Cuts_ID_PHYS14_25ns_v1_ConvVetoDxyDz_Tight_full5x5")
422  elif self.cfg_ana.ele_tightId=="Cuts_SPRING15_25ns_v1_ConvVetoDxyDz" :
423  ele.tightIdResult = -1 + 1*ele.electronID("POG_Cuts_ID_SPRING15_25ns_v1_ConvVetoDxyDz_Veto_full5x5") + 1*ele.electronID("POG_Cuts_ID_SPRING15_25ns_v1_ConvVetoDxyDz_Loose_full5x5") + 1*ele.electronID("POG_Cuts_ID_SPRING15_25ns_v1_ConvVetoDxyDz_Medium_full5x5") + 1*ele.electronID("POG_Cuts_ID_SPRING15_25ns_v1_ConvVetoDxyDz_Tight_full5x5")
424 
425  else :
426  try:
427  ele.tightIdResult = ele.electronID(self.cfg_ana.ele_tightId)
428  except RuntimeError:
429  raise RuntimeError("Unsupported ele_tightId name '" + str(self.cfg_ana.ele_tightId) + "'! For now only 'MVA' and 'Cuts_2012' are supported, in addition to what provided in Electron.py.")
430 
431 
432  return allelectrons
433 
434  def attachMiniIsolation(self, mu):
435  mu.miniIsoR = 10.0/min(max(mu.pt(), 50),200)
436  # -- version with increasing cone at low pT, gives slightly better performance for tight cuts and low pt leptons
437  # mu.miniIsoR = 10.0/min(max(mu.pt(), 50),200) if mu.pt() > 20 else 4.0/min(max(mu.pt(),10),20)
438  what = "mu" if (abs(mu.pdgId()) == 13) else ("eleB" if mu.isEB() else "eleE")
439  if what == "mu":
440  mu.miniAbsIsoCharged = self.IsolationComputer.chargedAbsIso(mu.physObj, mu.miniIsoR, {"mu":0.0001,"eleB":0,"eleE":0.015}[what], 0.0);
441  else:
442  mu.miniAbsIsoCharged = self.IsolationComputer.chargedAbsIso(mu.physObj, mu.miniIsoR, {"mu":0.0001,"eleB":0,"eleE":0.015}[what], 0.0,self.IsolationComputer.selfVetoNone);
443 
444  if self.miniIsolationPUCorr == None: puCorr = self.cfg_ana.mu_isoCorr if what=="mu" else self.cfg_ana.ele_isoCorr
445  else: puCorr = self.miniIsolationPUCorr
446 
447  if puCorr == "weights":
448  if what == "mu":
449  mu.miniAbsIsoNeutral = self.IsolationComputer.neutralAbsIsoWeighted(mu.physObj, mu.miniIsoR, 0.01, 0.5);
450  else:
451  mu.miniAbsIsoNeutral = ( self.IsolationComputer.photonAbsIsoWeighted( mu.physObj, mu.miniIsoR, 0.08 if what == "eleE" else 0.0, 0.0, self.IsolationComputer.selfVetoNone) +
452  self.IsolationComputer.neutralHadAbsIsoWeighted(mu.physObj, mu.miniIsoR, 0.0, 0.0, self.IsolationComputer.selfVetoNone) )
453  else:
454  if what == "mu":
455  mu.miniAbsIsoNeutral = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, mu.miniIsoR, 0.01, 0.5);
456  else:
457  mu.miniAbsIsoPho = self.IsolationComputer.photonAbsIsoRaw( mu.physObj, mu.miniIsoR, 0.08 if what == "eleE" else 0.0, 0.0, self.IsolationComputer.selfVetoNone)
458  mu.miniAbsIsoNHad = self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, mu.miniIsoR, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
459  mu.miniAbsIsoNeutral = mu.miniAbsIsoPho + mu.miniAbsIsoNHad
460  # -- version relying on PF candidate vetos; apparently less performant, and the isolation computed at RECO level doesn't have them
461  #mu.miniAbsIsoPhoSV = self.IsolationComputer.photonAbsIsoRaw( mu.physObj, mu.miniIsoR, 0.0, 0.0)
462  #mu.miniAbsIsoNHadSV = self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, mu.miniIsoR, 0.0, 0.0)
463  #mu.miniAbsIsoNeutral = mu.miniAbsIsoPhoSV + mu.miniAbsIsoNHadSV
464  if puCorr == "rhoArea":
465  mu.miniAbsIsoNeutral = max(0.0, mu.miniAbsIsoNeutral - mu.rho * mu.EffectiveArea03 * (mu.miniIsoR/0.3)**2)
466  elif puCorr == "deltaBeta":
467  if what == "mu":
468  mu.miniAbsIsoPU = self.IsolationComputer.puAbsIso(mu.physObj, mu.miniIsoR, 0.01, 0.5);
469  else:
470  mu.miniAbsIsoPU = self.IsolationComputer.puAbsIso(mu.physObj, mu.miniIsoR, 0.015 if what == "eleE" else 0.0, 0.0,self.IsolationComputer.selfVetoNone);
471  mu.miniAbsIsoNeutral = max(0.0, mu.miniAbsIsoNeutral - 0.5*mu.miniAbsIsoPU)
472  elif puCorr != 'raw':
473  raise RuntimeError("Unsupported miniIsolationCorr name '" + puCorr + "'! For now only 'rhoArea', 'deltaBeta', 'raw', 'weights' are supported (and 'weights' is not tested).")
474 
475  mu.miniAbsIso = mu.miniAbsIsoCharged + mu.miniAbsIsoNeutral
476  mu.miniRelIso = mu.miniAbsIso/mu.pt()
477 
478 
479  def attachIsoAnnulus04(self, mu): # annulus isolation with outer cone of 0.4 and delta beta PU correction
480  mu.miniIsoR = 10.0/min(max(mu.pt(), 50),200)
481  mu.absIsoAnCharged = self.IsolationComputer.chargedAbsIso (mu.physObj, 0.4, mu.miniIsoR, 0.0, self.IsolationComputer.selfVetoNone)
482  mu.absIsoAnPho = self.IsolationComputer.photonAbsIsoRaw (mu.physObj, 0.4, mu.miniIsoR, 0.0, self.IsolationComputer.selfVetoNone)
483  mu.absIsoAnNHad = self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.4, mu.miniIsoR, 0.0, self.IsolationComputer.selfVetoNone)
484  mu.absIsoAnPU = self.IsolationComputer.puAbsIso (mu.physObj, 0.4, mu.miniIsoR, 0.0, self.IsolationComputer.selfVetoNone)
485  mu.absIsoAnNeutral = max(0.0, mu.absIsoAnPho + mu.absIsoAnNHad - 0.5*mu.absIsoAnPU)
486 
487  mu.absIsoAn04 = mu.absIsoAnCharged + mu.absIsoAnNeutral
488  mu.relIsoAn04 = mu.absIsoAn04/mu.pt()
489 
490 
491  def attachIsolationScan(self, mu):
492 
493  what = "mu" if (abs(mu.pdgId()) == 13) else ("eleB" if mu.isEB() else "eleE")
494  vetoreg = {"mu":0.0001,"eleB":0,"eleE":0.015}[what]
495 
496  if what=="mu":
497  mu.ScanAbsIsoCharged005 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.05, vetoreg, 0.0)
498  mu.ScanAbsIsoCharged01 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.1, vetoreg, 0.0)
499  mu.ScanAbsIsoCharged02 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.2, vetoreg, 0.0)
500  mu.ScanAbsIsoCharged03 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.3, vetoreg, 0.0)
501  mu.ScanAbsIsoCharged04 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.4, vetoreg, 0.0)
502  else:
503  mu.ScanAbsIsoCharged005 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.05, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)
504  mu.ScanAbsIsoCharged01 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.1, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)
505  mu.ScanAbsIsoCharged02 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.2, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)
506  mu.ScanAbsIsoCharged03 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.3, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)
507  mu.ScanAbsIsoCharged04 = self.IsolationComputer.chargedAbsIso(mu.physObj, 0.4, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)
508 
509  if what=="mu":
510  mu.ScanAbsIsoNeutral005 = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, 0.05, 0.01, 0.5)
511  mu.ScanAbsIsoNeutral01 = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, 0.1, 0.01, 0.5)
512  mu.ScanAbsIsoNeutral02 = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, 0.2, 0.01, 0.5)
513  mu.ScanAbsIsoNeutral03 = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, 0.3, 0.01, 0.5)
514  mu.ScanAbsIsoNeutral04 = self.IsolationComputer.neutralAbsIsoRaw(mu.physObj, 0.4, 0.01, 0.5)
515  else:
516  vetoreg = {"eleB":0.0,"eleE":0.08}[what]
517  mu.ScanAbsIsoNeutral005 = self.IsolationComputer.photonAbsIsoRaw(mu.physObj, 0.05, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)+self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.05, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
518  mu.ScanAbsIsoNeutral01 = self.IsolationComputer.photonAbsIsoRaw(mu.physObj, 0.1, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)+self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.1, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
519  mu.ScanAbsIsoNeutral02 = self.IsolationComputer.photonAbsIsoRaw(mu.physObj, 0.2, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)+self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.2, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
520  mu.ScanAbsIsoNeutral03 = self.IsolationComputer.photonAbsIsoRaw(mu.physObj, 0.3, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)+self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.3, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
521  mu.ScanAbsIsoNeutral04 = self.IsolationComputer.photonAbsIsoRaw(mu.physObj, 0.4, vetoreg, 0.0, self.IsolationComputer.selfVetoNone)+self.IsolationComputer.neutralHadAbsIsoRaw(mu.physObj, 0.4, 0.0, 0.0, self.IsolationComputer.selfVetoNone)
522 
523 
524  def matchLeptons(self, event):
525  def plausible(rec,gen):
526  if abs(rec.pdgId()) == 11 and abs(gen.pdgId()) != 11: return False
527  if abs(rec.pdgId()) == 13 and abs(gen.pdgId()) != 13: return False
528  dr = deltaR(rec.eta(),rec.phi(),gen.eta(),gen.phi())
529  if dr < 0.3: return True
530  if rec.pt() < 10 and abs(rec.pdgId()) == 13 and gen.pdgId() != rec.pdgId(): return False
531  if dr < 0.7: return True
532  if min(rec.pt(),gen.pt())/max(rec.pt(),gen.pt()) < 0.3: return False
533  return True
534 
535  leps = event.inclusiveLeptons if self.cfg_ana.match_inclusiveLeptons else event.selectedLeptons
536  match = matchObjectCollection3(leps,
537  event.genleps + event.gentauleps,
538  deltaRMax = 1.2, filter = plausible)
539  for lep in leps:
540  gen = match[lep]
541  lep.mcMatchId = (gen.sourceId if gen != None else 0)
542  lep.mcMatchTau = (gen in event.gentauleps if gen else -99)
543  lep.mcLep=gen
544 
545  def isFromB(self,particle,bid=5, done={}):
546  for i in xrange( particle.numberOfMothers() ):
547  mom = particle.mother(i)
548  momid = abs(mom.pdgId())
549  if momid / 1000 == bid or momid / 100 == bid or momid == bid:
550  return True
551  elif mom.status() == 2 and self.isFromB(mom, done=done, bid=bid):
552  return True
553  return False
554 
555  def matchAnyLeptons(self, event):
556  event.anyLeptons = [ x for x in event.genParticles if x.status() == 1 and abs(x.pdgId()) in [11,13] ]
557  leps = event.inclusiveLeptons if hasattr(event, 'inclusiveLeptons') else event.selectedLeptons
558  match = matchObjectCollection3(leps, event.anyLeptons, deltaRMax = 0.3, filter = lambda x,y : abs(x.pdgId()) == abs(y.pdgId()))
559  for lep in leps:
560  gen = match[lep]
561  lep.mcMatchAny_gp = gen
562  if gen:
563  if self.isFromB(gen): lep.mcMatchAny = 5 # B (inclusive of B->D)
564  elif self.isFromB(gen,bid=4): lep.mcMatchAny = 4 # Charm
565  else: lep.mcMatchAny = 1
566  if not getattr(lep, 'mcLep', None): lep.mcLep = gen
567  else:
568  if not getattr(lep, 'mcLep', None): lep.mcLep = None
569  lep.mcMatchAny = 0
570  # fix case where the matching with the only prompt leptons failed, but we still ended up with a prompt match
571  if gen != None and hasattr(lep,'mcMatchId') and lep.mcMatchId == 0:
572  if isPromptLepton(gen, False) or (gen.isPromptFinalState() or gen.isDirectPromptTauDecayProductFinalState()):
573  lep.mcMatchId = 100
574  lep.mcLep = gen
575  elif not hasattr(lep,'mcMatchId'):
576  lep.mcMatchId = 0
577  if not hasattr(lep,'mcMatchTau'): lep.mcMatchTau = 0
578 
579  def matchToPhotons(self, event):
580  event.anyPho = [ x for x in self.mchandles['genPhotons'].product() if x.status() == 1 and x.pdgId() == 22 and x.pt() > 1.0 ]
581  leps = event.inclusiveLeptons if hasattr(event, 'inclusiveLeptons') else event.selectedLeptons
582  leps = [ l for l in leps if abs(l.pdgId()) == 11 ]
583  plausible = lambda rec, gen : 0.3*gen.pt() < rec.pt() and rec.pt() < 1.5*gen.pt()
584  match = matchObjectCollection3(leps, event.anyPho, deltaRMax = 0.3, filter = plausible)
585  for lep in leps:
586  gen = match[lep]
587  lep.mcPho = gen
588  if lep.mcPho and lep.mcLep:
589  # I have both, I should keep the best one
590  def distance(rec,gen):
591  dr = deltaR(rec.eta(),rec.phi(),gen.eta(),gen.phi())
592  dptRel = abs(rec.pt()-gen.pt())/gen.pt()
593  return dr + 0.2*dptRel
594  dpho = distance(lep,lep.mcPho)
595  dlep = distance(lep,lep.mcLep)
596  if getattr(lep,'mcMatchAny_gp',None) and lep.mcMatchAny_gp != lep.mcLep:
597  dlep = min(dlep, distance(lep,lep.mcMatchAny_gp))
598  if dlep <= dpho: lep.mcPho = None
599 
600  def process(self, event):
601  self.readCollections( event.input )
602  self.counters.counter('events').inc('all events')
603 
604  #call the leptons functions
605  self.makeLeptons(event)
606 
607  if self.cfg_comp.isMC and self.cfg_ana.do_mc_match:
608  self.matchLeptons(event)
609  self.matchAnyLeptons(event)
610  if self.doMatchToPhotons:
611  self.matchToPhotons(event)
612 
613  return True
614 
615 #A default config
616 setattr(LeptonAnalyzer,"defaultConfig",cfg.Analyzer(
617  verbose=False,
618  class_object=LeptonAnalyzer,
619  # input collections
620  muons='slimmedMuons',
621  electrons='slimmedElectrons',
622  rhoMuon= 'fixedGridRhoFastjetAll',
623  rhoElectron = 'fixedGridRhoFastjetAll',
624 ## photons='slimmedPhotons',
625  # energy scale corrections and ghost muon suppression (off by default)
626  doMuonScaleCorrections=False,
627  doElectronScaleCorrections=False,
628  doSegmentBasedMuonCleaning=False,
629  # inclusive very loose muon selection
630  inclusive_muon_id = "POG_ID_Loose",
631  inclusive_muon_pt = 3,
632  inclusive_muon_eta = 2.4,
633  inclusive_muon_dxy = 0.5,
634  inclusive_muon_dz = 1.0,
635  muon_dxydz_track = "muonBestTrack",
636  # loose muon selection
637  loose_muon_id = "POG_ID_Loose",
638  loose_muon_pt = 5,
639  loose_muon_eta = 2.4,
640  loose_muon_dxy = 0.05,
641  loose_muon_dz = 0.2,
642  loose_muon_relIso = 0.4,
643  # loose_muon_isoCut = lambda muon :muon.miniRelIso < 0.2
644  # inclusive very loose electron selection
645  inclusive_electron_id = "",
646  inclusive_electron_pt = 5,
647  inclusive_electron_eta = 2.5,
648  inclusive_electron_dxy = 0.5,
649  inclusive_electron_dz = 1.0,
650  inclusive_electron_lostHits = 1.0,
651  # loose electron selection
652  loose_electron_id = "", #POG_MVA_ID_NonTrig_full5x5",
653  loose_electron_pt = 7,
654  loose_electron_eta = 2.4,
655  loose_electron_dxy = 0.05,
656  loose_electron_dz = 0.2,
657  loose_electron_relIso = 0.4,
658  # loose_electron_isoCut = lambda electron : electron.miniRelIso < 0.1
659  loose_electron_lostHits = 1.0,
660  # muon isolation correction method (can be "rhoArea" or "deltaBeta")
661  mu_isoCorr = "rhoArea" ,
662  mu_effectiveAreas = "Spring15_25ns_v1", #(can be 'Data2012' or 'Phys14_25ns_v1' or 'Spring15_25ns_v1')
663  mu_tightId = "POG_ID_Tight" ,
664  # electron isolation correction method (can be "rhoArea" or "deltaBeta")
665  ele_isoCorr = "rhoArea" ,
666  ele_effectiveAreas = "Spring15_25ns_v1" , #(can be 'Data2012' or 'Phys14_25ns_v1', or 'Spring15_50ns_v1' or 'Spring15_25ns_v1')
667  ele_tightId = "Cuts_2012" ,
668  # minimum deltaR between a loose electron and a loose muon (on overlaps, discard the electron)
669  min_dr_electron_muon = 0.02,
670  # Mini-isolation, with pT dependent cone: will fill in the miniRelIso, miniRelIsoCharged, miniRelIsoNeutral variables of the leptons (see https://indico.cern.ch/event/368826/ )
671  doMiniIsolation = False, # off by default since it requires access to all PFCandidates
672  packedCandidates = 'packedPFCandidates',
673  miniIsolationPUCorr = 'rhoArea', # Allowed options: 'rhoArea' (EAs for 03 cone scaled by R^2), 'deltaBeta', 'raw' (uncorrected), 'weights' (delta beta weights; not validated)
674  # Choose None to just use the individual object's PU correction
675  miniIsolationVetoLeptons = None, # use 'inclusive' to veto inclusive leptons and their footprint in all isolation cones
676  # Activity Annulus
677  doIsoAnnulus = False, # off by default since it requires access to all PFCandidates
678  # do MC matching
679  do_mc_match = True, # note: it will in any case try it only on MC, not on data
680  do_mc_match_photons = False, # mc match electrons to photons
681  match_inclusiveLeptons = False, # match to all inclusive leptons
682  )
683 )
def isFromB(self, particle, bid=5, done={})
def isPromptLepton(lepton, beforeFSR, includeMotherless=True, includeTauDecays=False)
Definition: genutils.py:49
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:65
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
T min(T a, T b)
Definition: MathUtil.h:58
double deltaR(double eta1, double eta2, double phi1, double phi2)
Definition: TreeUtility.cc:17
def bestMatch(object, matchCollection)
Definition: deltar.py:138
eleEffectiveArea
Duplicate removal for fast sim (to be checked if still necessary in latest greatest 5...
def __init__(self, cfg_ana, cfg_comp, looperName)
miniIsolationVetoLeptons
inclusive leptons = all leptons that could be considered somewhere in the analysis, with minimal requirements (used e.g.
#define str(s)
def matchObjectCollection3
Definition: deltar.py:41