CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Electron.py
Go to the documentation of this file.
1 from PhysicsTools.Heppy.physicsobjects.Lepton import Lepton
3 import ROOT
4 
5 class Electron( Lepton ):
6 
7  def __init__(self, *args, **kwargs):
8  '''Initializing tightIdResult to None. The user is responsible
9  for setting this attribute externally if he wants to use the tightId
10  function.'''
11  super(Electron, self).__init__(*args, **kwargs)
12  self._physObjInit()
13 
14  def _physObjInit(self):
15  self.tightIdResult = None
16  self.associatedVertex = None
17  self.rho = None
18  self._mvaNonTrigV0 = {True:None, False:None}
19  self._mvaTrigV0 = {True:None, False:None}
20  self._mvaTrigNoIPV0 = {True:None, False:None}
21  self._mvaRun2 = {}
22 
23  def electronID( self, id, vertex=None, rho=None ):
24  if id is None or id == "": return True
25  if vertex == None and hasattr(self,'associatedVertex') and self.associatedVertex != None: vertex = self.associatedVertex
26  if rho == None and hasattr(self,'rho') and self.rho != None: rho = self.rho
27  if id == "POG_MVA_ID_NonTrig": return self.mvaIDLoose()
28  elif id == "POG_MVA_ID_Trig": return self.mvaIDTight()
29  elif id == "POG_MVA_ID_NonTrig_full5x5": return self.mvaIDLoose(full5x5=True)
30  elif id == "POG_MVA_ID_Trig_full5x5": return self.mvaIDTight(full5x5=True)
31  elif id == "POG_MVA_ID_Run2_NonTrig_VLoose": return self.mvaIDRun2("NonTrigPhys14","VLoose")
32  elif id == "POG_MVA_ID_Run2_NonTrig_Loose": return self.mvaIDRun2("NonTrigPhys14","Loose")
33  elif id == "POG_MVA_ID_Run2_NonTrig_Tight": return self.mvaIDRun2("NonTrigPhys14","Tight")
34  elif id.startswith("POG_Cuts_ID_"):
35  return self.cutBasedId(id.replace("POG_Cuts_ID_","POG_"))
36  for ID in self.electronIDs():
37  if ID.first == id:
38  return ID.second
39  raise RuntimeError, "Electron id '%s' not yet implemented in Electron.py" % id
40 
41  def cutBasedId(self, wp, showerShapes="auto"):
42  if "_full5x5" in wp:
43  showerShapes = "full5x5"
44  wp = wp.replace("_full5x5","")
45  elif showerShapes == "auto":
46  if "POG_CSA14_25ns_v1" in wp or "POG_CSA14_50ns_v1" in wp or "POG_PHYS14_25ns_v1" in wp or "POG_PHYS14_25ns_v1_ConvVeto" in wp or "POG_PHYS14_25ns_v1_ConvVetoDxyDz" in wp:
47  showerShapes = "full5x5"
48  vars = {
49  'dEtaIn' : abs(self.physObj.deltaEtaSuperClusterTrackAtVtx()),
50  'dPhiIn' : abs(self.physObj.deltaPhiSuperClusterTrackAtVtx()),
51  'sigmaIEtaIEta' : self.physObj.full5x5_sigmaIetaIeta() if showerShapes == "full5x5" else self.physObj.sigmaIetaIeta(),
52  'H/E' : self.physObj.hadronicOverEm(),
53  #'1/E-1/p' : abs(1.0/self.physObj.ecalEnergy() - self.physObj.eSuperClusterOverP()/self.physObj.ecalEnergy()),
54  '1/E-1/p' : abs(1.0/self.physObj.ecalEnergy() - self.physObj.eSuperClusterOverP()/self.physObj.ecalEnergy()) if self.physObj.ecalEnergy()>0. else 9e9,
55  'conversionVeto' : self.physObj.passConversionVeto(),
56  'missingHits' : self.physObj.gsfTrack().hitPattern().numberOfHits(ROOT.reco.HitPattern.MISSING_INNER_HITS), # http://cmslxr.fnal.gov/source/DataFormats/TrackReco/interface/HitPattern.h?v=CMSSW_7_2_3#0153
57  'dxy' : abs(self.dxy()),
58  'dz' : abs(self.dz()),
59  }
60  WP = {
61  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/EgammaCutBasedIdentification?rev=31
62  'POG_2012_Veto' : [('dEtaIn', [0.007, 0.01]), ('dPhiIn', [0.8, 0.7 ]), ('sigmaIEtaIEta', [0.01, 0.03]), ('H/E', [0.15, 9e9]), ('1/E-1/p', [9e9, 9e9])],
63  'POG_2012_Loose' : [('dEtaIn', [0.007, 0.009]), ('dPhiIn', [0.15, 0.1 ]), ('sigmaIEtaIEta', [0.01, 0.03]), ('H/E', [0.12, 0.1]), ('1/E-1/p', [0.05, 0.05])],
64  'POG_2012_Medium' : [('dEtaIn', [0.004, 0.007]), ('dPhiIn', [0.06, 0.03]), ('sigmaIEtaIEta', [0.01, 0.03]), ('H/E', [0.12, 0.1]), ('1/E-1/p', [0.05, 0.05])],
65  'POG_2012_Tight' : [('dEtaIn', [0.004, 0.005]), ('dPhiIn', [0.03, 0.02]), ('sigmaIEtaIEta', [0.01, 0.03]), ('H/E', [0.12, 0.1]), ('1/E-1/p', [0.05, 0.05])],
66  # RIC: in the EG POG WPs, isolation is included too. Here only the pure ID part.
67  # dz and d0 cuts are excluded here as well.
68  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/CutBasedElectronIdentificationRun2#Working_points_for_CSA14_samples?rev=13
69  'POG_CSA14_25ns_v1_Veto' : [('dEtaIn', [0.017938, 0.014569]), ('dPhiIn', [0.182958, 0.230914]), ('sigmaIEtaIEta', [0.012708, 0.036384]), ('H/E', [0.335015, 0.200792]), ('1/E-1/p', [0.198287, 0.146856])],
70  'POG_CSA14_25ns_v1_Loose' : [('dEtaIn', [0.014928, 0.013045]), ('dPhiIn', [0.141050, 0.149017]), ('sigmaIEtaIEta', [0.011304, 0.035536]), ('H/E', [0.127690, 0.107898]), ('1/E-1/p', [0.097806, 0.102261])],
71  'POG_CSA14_25ns_v1_Medium' : [('dEtaIn', [0.013071, 0.010006]), ('dPhiIn', [0.132113, 0.052321]), ('sigmaIEtaIEta', [0.010726, 0.032882]), ('H/E', [0.109761, 0.101755]), ('1/E-1/p', [0.032639, 0.041427])],
72  'POG_CSA14_25ns_v1_Tight' : [('dEtaIn', [0.012671, 0.008823]), ('dPhiIn', [0.025218, 0.027286]), ('sigmaIEtaIEta', [0.010061, 0.030222]), ('H/E', [0.065085, 0.090710]), ('1/E-1/p', [0.027873, 0.019404])],
73  'POG_CSA14_50ns_v1_Veto' : [('dEtaIn', [0.021, 0.028]), ('dPhiIn', [0.25 , 0.23 ]), ('sigmaIEtaIEta', [0.012, 0.035]), ('H/E', [0.24 , 0.19 ]), ('1/E-1/p', [0.32 , 0.13 ])],
74  'POG_CSA14_50ns_v1_Loose' : [('dEtaIn', [0.016, 0.025]), ('dPhiIn', [0.080, 0.097]), ('sigmaIEtaIEta', [0.012, 0.032]), ('H/E', [0.15 , 0.12 ]), ('1/E-1/p', [0.11 , 0.11 ])],
75  'POG_CSA14_50ns_v1_Medium' : [('dEtaIn', [0.015, 0.023]), ('dPhiIn', [0.051, 0.056]), ('sigmaIEtaIEta', [0.010, 0.030]), ('H/E', [0.10 , 0.099]), ('1/E-1/p', [0.053, 0.11 ])],
76  'POG_CSA14_50ns_v1_Tight' : [('dEtaIn', [0.012, 0.019]), ('dPhiIn', [0.024, 0.043]), ('sigmaIEtaIEta', [0.010, 0.029]), ('H/E', [0.074, 0.080]), ('1/E-1/p', [0.026, 0.076])],
77  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/CutBasedElectronIdentificationRun2#Working_points_for_PHYS14_sample?rev=13
78  'POG_PHYS14_25ns_v1_Veto' : [('dEtaIn', [0.016315, 0.010671]), ('dPhiIn', [0.252044, 0.245263]), ('sigmaIEtaIEta', [0.011100 , 0.033987]), ('H/E', [0.345843, 0.134691]), ('1/E-1/p', [0.248070, 0.157160])],
79  'POG_PHYS14_25ns_v1_Loose' : [('dEtaIn', [0.012442, 0.010654]), ('dPhiIn', [0.072624, 0.145129]), ('sigmaIEtaIEta', [0.010557 , 0.032602]), ('H/E', [0.121476, 0.131862]), ('1/E-1/p', [0.221803, 0.142283])],
80  'POG_PHYS14_25ns_v1_Medium' : [('dEtaIn', [0.007641, 0.009285]), ('dPhiIn', [0.032643, 0.042447]), ('sigmaIEtaIEta', [0.010399 , 0.029524]), ('H/E', [0.060662, 0.104263]), ('1/E-1/p', [0.153897, 0.137468])],
81  'POG_PHYS14_25ns_v1_Tight' : [('dEtaIn', [0.006574, 0.005681]), ('dPhiIn', [0.022868, 0.032046]), ('sigmaIEtaIEta', [0.010181 , 0.028766]), ('H/E', [0.037553, 0.081902]), ('1/E-1/p', [0.131191, 0.106055])],
82  }
83  WP_conversion_veto = {
84  # missing Hits incremented by 1 because we return False if >=, note the '='
85  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/CutBasedElectronIdentificationRun2#Working_points_for_CSA14_samples?rev=13
86  'POG_CSA14_25ns_v1_ConvVeto_Veto' : WP['POG_CSA14_25ns_v1_Veto' ]+[('conversionVeto', [True, True]), ('missingHits', [3, 4])],
87  'POG_CSA14_25ns_v1_ConvVeto_Loose' : WP['POG_CSA14_25ns_v1_Loose' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
88  'POG_CSA14_25ns_v1_ConvVeto_Medium' : WP['POG_CSA14_25ns_v1_Medium']+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
89  'POG_CSA14_25ns_v1_ConvVeto_Tight' : WP['POG_CSA14_25ns_v1_Tight' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
90  'POG_CSA14_50ns_v1_ConvVeto_Veto' : WP['POG_CSA14_50ns_v1_Veto' ]+[('conversionVeto', [True, True]), ('missingHits', [3, 4])],
91  'POG_CSA14_50ns_v1_ConvVeto_Loose' : WP['POG_CSA14_50ns_v1_Loose' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
92  'POG_CSA14_50ns_v1_ConvVeto_Medium' : WP['POG_CSA14_50ns_v1_Medium']+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
93  'POG_CSA14_50ns_v1_ConvVeto_Tight' : WP['POG_CSA14_50ns_v1_Tight' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
94  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/CutBasedElectronIdentificationRun2#Working_points_for_PHYS14_sample?rev=13
95  'POG_PHYS14_25ns_v1_ConvVeto_Veto' : WP['POG_PHYS14_25ns_v1_Veto' ]+[('conversionVeto', [True, True]), ('missingHits', [3, 4])],
96  'POG_PHYS14_25ns_v1_ConvVeto_Loose' : WP['POG_PHYS14_25ns_v1_Loose' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
97  'POG_PHYS14_25ns_v1_ConvVeto_Medium' : WP['POG_PHYS14_25ns_v1_Medium']+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
98  'POG_PHYS14_25ns_v1_ConvVeto_Tight' : WP['POG_PHYS14_25ns_v1_Tight' ]+[('conversionVeto', [True, True]), ('missingHits', [2, 2])],
99  }
100 
101  WP.update(WP_conversion_veto)
102 
103  WP_conversion_veto_DxyDz = {
104  # missing Hits incremented by 1 because we return False if >=, note the '='
105  ## ------- https://twiki.cern.ch/twiki/bin/viewauth/CMS/CutBasedElectronIdentificationRun2#Working_points_for_PHYS14_sample
106  'POG_PHYS14_25ns_v1_ConvVetoDxyDz_Veto' : WP['POG_PHYS14_25ns_v1_ConvVeto_Veto' ]+[('dxy',[0.060279, 0.273097]), ('dz',[0.800538, 0.885860])],
107  'POG_PHYS14_25ns_v1_ConvVetoDxyDz_Loose' : WP['POG_PHYS14_25ns_v1_ConvVeto_Loose' ]+[('dxy',[0.022664, 0.097358]), ('dz',[0.173670, 0.198444])],
108  'POG_PHYS14_25ns_v1_ConvVetoDxyDz_Medium' : WP['POG_PHYS14_25ns_v1_ConvVeto_Medium']+[('dxy',[0.011811, 0.051682]), ('dz',[0.070775, 0.180720])],
109  'POG_PHYS14_25ns_v1_ConvVetoDxyDz_Tight' : WP['POG_PHYS14_25ns_v1_ConvVeto_Tight' ]+[('dxy',[0.009924, 0.027261]), ('dz',[0.015310, 0.147154])],
110  }
111 
112  WP.update(WP_conversion_veto_DxyDz)
113 
114 
115  if wp not in WP:
116  raise RuntimeError, "Working point '%s' not yet implemented in Electron.py" % wp
117  for (cut_name,(cut_eb,cut_ee)) in WP[wp]:
118  if cut_name == 'conversionVeto':
119  if (cut_eb if self.physObj.isEB() else cut_ee) and not vars[cut_name]:
120  return False
121  elif vars[cut_name] >= (cut_eb if self.physObj.isEB() else cut_ee):
122  return False
123  return True
124 
125  def absEffAreaIso(self,rho,effectiveAreas):
126  '''MIKE, missing doc.
127  Should have the same name as the function in the mother class.
128  Can call the mother class function with super.
129  '''
130  return self.absIsoFromEA(rho,self.superCluster().eta(),effectiveAreas.eGamma)
131 
132  def mvaId( self ):
133  return self.mvaNonTrigV0()
134 
135  def tightId( self ):
136  return self.tightIdResult
137 
138  def mvaNonTrigV0( self, full5x5=False, debug = False ):
139  if self._mvaNonTrigV0[full5x5] == None:
140  if self.associatedVertex == None: raise RuntimeError, "You need to set electron.associatedVertex before calling any MVA"
141  if self.rho == None: raise RuntimeError, "You need to set electron.rho before calling any MVA"
142  self._mvaNonTrigV0[full5x5] = ElectronMVAID_NonTrig(self.physObj, self.associatedVertex, self.rho, full5x5, debug)
143  return self._mvaNonTrigV0[full5x5]
144 
145  def mvaTrigV0( self, full5x5=False, debug = False ):
146  if self._mvaTrigV0[full5x5] == None:
147  if self.associatedVertex == None: raise RuntimeError, "You need to set electron.associatedVertex before calling any MVA"
148  if self.rho == None: raise RuntimeError, "You need to set electron.rho before calling any MVA"
149  self._mvaTrigV0[full5x5] = ElectronMVAID_Trig(self.physObj, self.associatedVertex, self.rho, full5x5, debug)
150  return self._mvaTrigV0[full5x5]
151 
152  def mvaTrigNoIPV0( self, full5x5=False, debug = False ):
153  if self._mvaTrigNoIPV0[full5x5] == None:
154  if self.associatedVertex == None: raise RuntimeError, "You need to set electron.associatedVertex before calling any MVA"
155  if self.rho == None: raise RuntimeError, "You need to set electron.rho before calling any MVA"
156  self._mvaTrigNoIPV0[full5x5] = ElectronMVAID_TrigNoIP(self.physObj, self.associatedVertex, self.rho, full5x5, debug)
157  return self._mvaTrigNoIPV0[full5x5]
158 
159  def mvaRun2( self, name, debug = False ):
160  if name not in self._mvaRun2:
161  if name not in ElectronMVAID_ByName: raise RuntimeError, "Unknown electron run2 mva id %s (known ones are: %s)\n" % (name, ElectronMVAID_ByName.keys())
162  if self.associatedVertex == None: raise RuntimeError, "You need to set electron.associatedVertex before calling any MVA"
163  if self.rho == None: raise RuntimeError, "You need to set electron.rho before calling any MVA"
164  self._mvaRun2[name] = ElectronMVAID_ByName[name](self.physObj, self.associatedVertex, self.rho, True, debug)
165  return self._mvaRun2[name]
166 
167  def mvaIDTight(self, full5x5=False):
168  eta = abs(self.superCluster().eta())
169  if self.pt() < 20:
170  if (eta < 0.8) : return self.mvaTrigV0(full5x5) > +0.00;
171  elif (eta < 1.479): return self.mvaTrigV0(full5x5) > +0.10;
172  else : return self.mvaTrigV0(full5x5) > +0.62;
173  else:
174  if (eta < 0.8) : return self.mvaTrigV0(full5x5) > +0.94;
175  elif (eta < 1.479): return self.mvaTrigV0(full5x5) > +0.85;
176  else : return self.mvaTrigV0(full5x5) > +0.92;
177 
178  def mvaIDLoose(self, full5x5=False):
179  eta = abs(self.superCluster().eta())
180  if self.pt() < 10:
181  if (eta < 0.8) : return self.mvaNonTrigV0(full5x5) > +0.47;
182  elif (eta < 1.479): return self.mvaNonTrigV0(full5x5) > +0.004;
183  else : return self.mvaNonTrigV0(full5x5) > +0.295;
184  else:
185  if (eta < 0.8) : return self.mvaNonTrigV0(full5x5) > -0.34;
186  elif (eta < 1.479): return self.mvaNonTrigV0(full5x5) > -0.65;
187  else : return self.mvaNonTrigV0(full5x5) > +0.60;
188 
189  def mvaIDRun2(self, name, wp):
190  eta = abs(self.superCluster().eta())
191  if name == "NonTrigPhys14":
192  if wp=="Loose":
193  if (eta < 0.8) : return self.mvaRun2(name) > +0.35;
194  elif (eta < 1.479): return self.mvaRun2(name) > +0.20;
195  else : return self.mvaRun2(name) > -0.52;
196  elif wp=="VLoose":
197  if (eta < 0.8) : return self.mvaRun2(name) > -0.11;
198  elif (eta < 1.479): return self.mvaRun2(name) > -0.35;
199  else : return self.mvaRun2(name) > -0.55;
200  elif wp=="Tight":
201  if (eta < 0.8) : return self.mvaRun2(name) > 0.73;
202  elif (eta < 1.479): return self.mvaRun2(name) > 0.57;
203  else : return self.mvaRun2(name) > 0.05;
204  else: raise RuntimeError, "Ele MVA ID Working point not found"
205  else: raise RuntimeError, "Ele MVA ID type not found"
206 
207 
208  def mvaIDZZ(self):
209  return self.mvaIDLoose() and (self.gsfTrack().trackerExpectedHitsInner().numberOfLostHits()<=1)
210 
211  def chargedHadronIsoR(self,R=0.4):
212  if R == 0.3: return self.physObj.pfIsolationVariables().sumChargedHadronPt
213  elif R == 0.4: return self.physObj.chargedHadronIso()
214  raise RuntimeError, "Electron chargedHadronIso missing for R=%s" % R
215 
216  def neutralHadronIsoR(self,R=0.4):
217  if R == 0.3: return self.physObj.pfIsolationVariables().sumNeutralHadronEt
218  elif R == 0.4: return self.physObj.neutralHadronIso()
219  raise RuntimeError, "Electron neutralHadronIso missing for R=%s" % R
220 
221  def photonIsoR(self,R=0.4):
222  if R == 0.3: return self.physObj.pfIsolationVariables().sumPhotonEt
223  elif R == 0.4: return self.physObj.photonIso()
224  raise RuntimeError, "Electron photonIso missing for R=%s" % R
225 
226  def chargedAllIsoR(self,R=0.4):
227  if R == 0.3: return self.physObj.pfIsolationVariables().sumChargedParticlePt
228  raise RuntimeError, "Electron chargedAllIso missing for R=%s" % R
229 
230  def chargedAllIso(self):
231  raise RuntimeError, "Electron chargedAllIso missing"
232 
233  def puChargedHadronIsoR(self,R=0.4):
234  if R == 0.3: return self.physObj.pfIsolationVariables().sumPUPt
235  elif R == 0.4: return self.physObj.puChargedHadronIso()
236  raise RuntimeError, "Electron chargedHadronIso missing for R=%s" % R
237 
238  def dxy(self, vertex=None):
239  '''Returns dxy.
240  Computed using vertex (or self.associatedVertex if vertex not specified),
241  and the gsf track.
242  '''
243  if vertex is None:
244  vertex = self.associatedVertex
245  return self.gsfTrack().dxy( vertex.position() )
246 
247  def edxy(self):
248  '''returns the uncertainty on dxy (from gsf track)'''
249  return self.gsfTrack().dxyError()
250 
251  def p4(self):
252  return ROOT.reco.Candidate.p4(self.physObj)
253 
254 # def p4(self):
255 # return self.physObj.p4(self.physObj.candidateP4Kind()) # if kind == None else kind)
256 
257  def dz(self, vertex=None):
258  '''Returns dz.
259  Computed using vertex (or self.associatedVertex if vertex not specified),
260  and the gsf track.
261  '''
262  if vertex is None:
263  vertex = self.associatedVertex
264  return self.gsfTrack().dz( vertex.position() )
265 
266  def edz(self):
267  '''returns the uncertainty on dxz (from gsf track)'''
268  return self.gsfTrack().dzError()
269 
270 
271  def lostInner(self) :
272  if hasattr(self.gsfTrack(),"trackerExpectedHitsInner") :
273  return self.gsfTrack().trackerExpectedHitsInner().numberOfLostHits()
274  else :
275  return self.gsfTrack().hitPattern().numberOfHits(ROOT.reco.HitPattern.MISSING_INNER_HITS)
276 
tuple ElectronMVAID_NonTrig
T eta() const
tuple ElectronMVAID_TrigNoIP
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def puChargedHadronIsoR
Definition: Electron.py:233
Definition: Lepton.py:1
tuple ElectronMVAID_Trig