2 from PhysicsTools.HeppyCore.framework.analyzer
import Analyzer
3 from PhysicsTools.HeppyCore.statistics.counter
import Counter, Counters
4 from PhysicsTools.Heppy.analyzers.AutoHandle
import AutoHandle
5 from PhysicsTools.Heppy.physicsobjects.DiObject
import DiObject
6 from PhysicsTools.Heppy.physicsobjects.PhysicsObjects
import Lepton
7 from PhysicsTools.HeppyCore.utils.TriggerMatching
import triggerMatched
8 from PhysicsTools.HeppyCore.utils.DeltaR
import deltaR
12 """Generic analyzer for Di-Leptons.
13 See ZMuMuAnalyzer for a concrete case.
15 Example configuration, and list of parameters:
20 scaleShift1 = eScaleShift, #O shift factor for leg 1 energy scale
21 scaleShift2 = tauScaleShift,#O same for leg 2
22 pt1 = 20, # pt, eta, iso cuts for leg 1
25 pt2 = 20, # same for leg 2
28 m_min = 10, # mass range
30 dR_min = 0.5, #O min delta R between the two legs
31 triggerMap = pathsAndFilters, #O, necessary for trigger matching
32 verbose = False #from base Analyzer class
35 COLIN: need to specify what is needed in the event.
36 COLIN: need to make delta R non optional.
37 COLIN: make the dR_min parameter non optional
45 DiObjectClass = DiObject
47 OtherLeptonClass = Lepton
50 super(DiLeptonAnalyzer,self).
beginLoop(setup)
51 self.counters.addCounter(
'DiLepton')
52 count = self.counters.counter(
'DiLepton')
53 count.register(
'all events')
54 count.register(
'> 0 di-lepton')
56 count.register(
'lepton accept')
57 count.register(
'third lepton veto')
58 count.register(
'leg1 offline cuts passed')
59 count.register(
'leg1 trig matched')
60 count.register(
'leg2 offline cuts passed')
61 count.register(
'leg2 trig matched')
62 count.register(
'{min:3.1f} < m < {max:3.1f}'.
format( min = self.cfg_ana.m_min,
63 max = self.cfg_ana.m_max ))
64 if hasattr(self.cfg_ana,
'dR_min'):
65 count.register(
'dR > {min:3.1f}'.
format( min = self.cfg_ana.dR_min))
67 count.register(
'exactly 1 di-lepton')
71 '''Creates python DiLeptons from the di-leptons read from the disk.
72 to be overloaded if needed.'''
73 return map( self.__class__.DiObjectClass, cmgDiLeptons )
77 '''Creates python Leptons from the leptons read from the disk.
78 to be overloaded if needed.'''
79 return map( self.__class__.LeptonClass, cmgLeptons )
83 '''Creates python Leptons from the leptons read from the disk.
84 to be overloaded if needed.'''
85 return map( self.__class__.LeptonClass, cmgLeptons )
90 self.readCollections( iEvent )
91 event.diLeptons = self.
buildDiLeptons( self.handles[
'diLeptons'].product(), event )
92 event.leptons = self.
buildLeptons( self.handles[
'leptons'].product(), event )
93 event.otherLeptons = self.
buildOtherLeptons( self.handles[
'otherLeptons'].product(), event )
96 leg1IsoCut=self.cfg_ana.iso1,
97 leg2IsoCut=self.cfg_ana.iso2)
103 if hasattr( self.cfg_ana,
'scaleShift1'):
104 scaleShift1 = self.cfg_ana.scaleShift1
105 if hasattr( self.cfg_ana,
'scaleShift2'):
106 scaleShift2 = self.cfg_ana.scaleShift2
109 map(
lambda x: x.leg1().scaleEnergy(scaleShift1), event.diLeptons )
111 map(
lambda x: x.leg2().scaleEnergy(scaleShift2), event.diLeptons )
112 map(
lambda x: x.scaleEnergy(scaleShift2), event.leptons )
117 if fillCounter: self.counters.counter(
'DiLepton').inc(
'all events')
126 if len(event.diLeptons) == 0:
128 if fillCounter: self.counters.counter(
'DiLepton').inc(
'> 0 di-lepton')
132 selDiLeptons = event.diLeptons
135 event.leptonAccept =
False
137 if fillCounter: self.counters.counter(
'DiLepton').inc(
'lepton accept')
138 event.leptonAccept =
True
140 event.thirdLeptonVeto =
False
142 if fillCounter: self.counters.counter(
'DiLepton').inc(
'third lepton veto')
143 event.thirdLeptonVeto =
True
146 selDiLeptons = [ diL
for diL
in selDiLeptons
if \
147 self.
testLeg1( diL.leg1(), leg1IsoCut ) ]
148 if len(selDiLeptons) == 0:
151 if fillCounter: self.counters.counter(
'DiLepton').inc(
'leg1 offline cuts passed')
153 if len(self.cfg_comp.triggers)>0:
155 selDiLeptons = [diL
for diL
in selDiLeptons
if \
157 if len(selDiLeptons) == 0:
160 if fillCounter: self.counters.counter(
'DiLepton').inc(
'leg1 trig matched')
163 selDiLeptons = [ diL
for diL
in selDiLeptons
if \
164 self.
testLeg2( diL.leg2(), leg2IsoCut ) ]
165 if len(selDiLeptons) == 0:
168 if fillCounter: self.counters.counter(
'DiLepton').inc(
'leg2 offline cuts passed')
170 if len(self.cfg_comp.triggers)>0:
172 selDiLeptons = [diL
for diL
in selDiLeptons
if \
174 if len(selDiLeptons) == 0:
177 if fillCounter: self.counters.counter(
'DiLepton').inc(
'leg2 trig matched')
180 selDiLeptons = [ diL
for diL
in selDiLeptons
if \
182 if len(selDiLeptons)==0:
185 if fillCounter: self.counters.counter(
'DiLepton').inc(
186 '{min:3.1f} < m < {max:3.1f}'.
format( min = self.cfg_ana.m_min,
187 max = self.cfg_ana.m_max )
191 if hasattr(self.cfg_ana,
'dR_min'):
192 selDiLeptons = [ diL
for diL
in selDiLeptons
if \
194 if len(selDiLeptons)==0:
197 if fillCounter: self.counters.counter(
'DiLepton').inc(
198 'dR > {min:3.1f}'.
format( min = self.cfg_ana.dR_min )
202 if len(selDiLeptons)==0:
204 elif len(selDiLeptons)==1:
205 if fillCounter: self.counters.counter(
'DiLepton').inc(
'exactly 1 di-lepton')
208 event.leg1 = event.diLepton.leg1()
209 event.leg2 = event.diLepton.leg2()
210 event.selectedLeptons = [event.leg1, event.leg2]
223 '''Should implement a default version running on event.leptons.'''
228 '''Should implement a default version running on event.leptons.'''
233 '''returns testLeg1ID && testLeg1Iso && testLegKine for leg1'''
236 self.
testLegKine(leg, self.cfg_ana.pt1, self.cfg_ana.eta1)
240 '''returns testLeg2ID && testLeg2Iso && testLegKine for leg2'''
243 self.
testLegKine(leg, self.cfg_ana.pt2, self.cfg_ana.eta2)
247 '''Tests pt and eta.'''
248 return leg.pt() > ptcut
and \
249 abs(leg.eta()) < etacut
253 '''Always return true by default, overload in your subclass'''
258 '''If isocut is None, the iso value is taken from the iso1 parameter.
259 Checks the standard dbeta corrected isolation.
262 isocut = self.cfg_ana.iso1
263 return leg.relIso(0.5) < isocut
267 '''Always return true by default, overload in your subclass'''
272 '''If isocut is None, the iso value is taken from the iso2 parameter.
273 Checks the standard dbeta corrected isolation.
276 isocut = self.cfg_ana.iso2
277 return leg.relIso(0.5) < isocut
281 '''returns True if the mass of the dilepton is between the m_min and m_max parameters'''
282 mass = diLepton.mass()
283 return self.cfg_ana.m_min < mass
and mass < self.cfg_ana.m_max
287 '''returns True if the two diLepton.leg1() and .leg2() have a delta R larger than the dR_min parameter.'''
288 dR =
deltaR( diLepton.leg1().
eta(), diLepton.leg1().phi(),
289 diLepton.leg2().
eta(), diLepton.leg2().phi())
290 return dR > self.cfg_ana.dR_min
294 '''Returns the best diLepton (the one with highest pt1 + pt2).'''
295 return max( diLeptons, key=operator.methodcaller(
'sumPt' ) )
299 '''Returns true if the leg is matched to a trigger object as defined in the
300 triggerMap parameter'''
301 if not hasattr( self.cfg_ana,
'triggerMap'):
304 triggerObjects = event.triggerObjects
305 filters = self.cfg_ana.triggerMap[ path ]
307 if legName ==
'leg1':
309 elif legName ==
'leg2':
312 raise ValueError(
'legName should be leg1 or leg2, not {leg}'.
format(
322 filter, pdgIds = filter[0], filter[1]
323 return triggerMatched(leg, triggerObjects, path, filter,
Abs< T >::type abs(const T &t)
double deltaR(double eta1, double eta2, double phi1, double phi2)