CMS 3D CMS Logo

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