Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 13:32:27

0001 import operator 
0002 from PhysicsTools.HeppyCore.framework.analyzer import Analyzer
0003 from PhysicsTools.HeppyCore.statistics.counter import Counter, Counters
0004 from PhysicsTools.Heppy.analyzers.AutoHandle import AutoHandle
0005 from PhysicsTools.Heppy.physicsobjects.DiObject import DiObject
0006 from PhysicsTools.Heppy.physicsobjects.PhysicsObjects import Lepton
0007 from PhysicsTools.HeppyCore.utils.TriggerMatching import triggerMatched
0008 from PhysicsTools.HeppyCore.utils.DeltaR import deltaR
0009 
0010 class DiLeptonAnalyzer( Analyzer ):
0011 
0012     """Generic analyzer for Di-Leptons.
0013     See ZMuMuAnalyzer for a concrete case.
0014 
0015     Example configuration, and list of parameters:
0016     #O means optional
0017     
0018     ana = cfg.Analyzer(
0019       'DiLeptonAnalyzer',
0020       scaleShift1 = eScaleShift,  #O shift factor for leg 1 energy scale
0021       scaleShift2 = tauScaleShift,#O same for leg 2
0022       pt1 = 20,   # pt, eta, iso cuts for leg 1
0023       eta1 = 2.3,
0024       iso1 = None,
0025       pt2 = 20,   # same for leg 2
0026       eta2 = 2.1,
0027       iso2 = 0.1,
0028       m_min = 10,    # mass range
0029       m_max = 99999,
0030       dR_min = 0.5,  #O min delta R between the two legs
0031       triggerMap = pathsAndFilters, #O, necessary for trigger matching
0032       verbose = False               #from base Analyzer class
0033       )
0034 
0035     COLIN: need to specify what is needed in the event.
0036     COLIN: need to make delta R non optional.
0037     COLIN: make the dR_min parameter non optional
0038     """
0039 
0040     # The DiObject class will be used as the di-object class
0041     # and the Lepton class as the lepton class
0042     # Child classes override this choice, and can e.g. decide to use
0043     # the TauMuon class as a di-object class
0044     # ... not sure other people can understand this comment ;-)
0045     DiObjectClass = DiObject
0046     LeptonClass = Lepton 
0047     OtherLeptonClass = Lepton 
0048 
0049     def beginLoop(self, setup):
0050         super(DiLeptonAnalyzer,self).beginLoop(setup)
0051         self.counters.addCounter('DiLepton')
0052         count = self.counters.counter('DiLepton')
0053         count.register('all events')
0054         count.register('> 0 di-lepton')
0055         # count.register('di-lepton cut string ok')
0056         count.register('lepton accept')
0057         count.register('third lepton veto')
0058         count.register('leg1 offline cuts passed')
0059         count.register('leg1 trig matched')
0060         count.register('leg2 offline cuts passed')
0061         count.register('leg2 trig matched')
0062         count.register('{min:3.1f} < m < {max:3.1f}'.format( min = self.cfg_ana.m_min,
0063                                                              max = self.cfg_ana.m_max ))
0064         if hasattr(self.cfg_ana, 'dR_min'):
0065             count.register('dR > {min:3.1f}'.format( min = self.cfg_ana.dR_min))
0066                            
0067         count.register('exactly 1 di-lepton')
0068 
0069 
0070     def buildDiLeptons(self, cmgDiLeptons, event):
0071         '''Creates python DiLeptons from the di-leptons read from the disk.
0072         to be overloaded if needed.'''
0073         return map( self.__class__.DiObjectClass, cmgDiLeptons )
0074 
0075 
0076     def buildLeptons(self, cmgLeptons, event):
0077         '''Creates python Leptons from the leptons read from the disk.
0078         to be overloaded if needed.'''
0079         return map( self.__class__.LeptonClass, cmgLeptons )
0080 
0081 
0082     def buildOtherLeptons(self, cmgLeptons, event):
0083         '''Creates python Leptons from the leptons read from the disk.
0084         to be overloaded if needed.'''
0085         return map( self.__class__.LeptonClass, cmgLeptons )
0086 
0087 
0088         
0089     def process(self, iEvent, event):
0090         self.readCollections( iEvent )
0091         event.diLeptons = self.buildDiLeptons( self.handles['diLeptons'].product(), event )
0092         event.leptons = self.buildLeptons( self.handles['leptons'].product(), event )
0093         event.otherLeptons = self.buildOtherLeptons( self.handles['otherLeptons'].product(), event )
0094         self.shiftEnergyScale(event)
0095         return self.selectionSequence(event, fillCounter=True,
0096                                       leg1IsoCut=self.cfg_ana.iso1,
0097                                       leg2IsoCut=self.cfg_ana.iso2)
0098 
0099 
0100     def shiftEnergyScale(self, event):
0101         scaleShift1 = None
0102         scaleShift2 = None
0103         if hasattr( self.cfg_ana, 'scaleShift1'):
0104             scaleShift1 = self.cfg_ana.scaleShift1
0105         if hasattr( self.cfg_ana, 'scaleShift2'):
0106             scaleShift2 = self.cfg_ana.scaleShift2
0107         if scaleShift1:
0108             # import pdb; pdb.set_trace()
0109             map( lambda x: x.leg1().scaleEnergy(scaleShift1), event.diLeptons )
0110         if scaleShift2:
0111             map( lambda x: x.leg2().scaleEnergy(scaleShift2), event.diLeptons )
0112             map( lambda x: x.scaleEnergy(scaleShift2), event.leptons )
0113         
0114 
0115     def selectionSequence(self, event, fillCounter, leg1IsoCut=None, leg2IsoCut=None):
0116 
0117         if fillCounter: self.counters.counter('DiLepton').inc('all events')
0118         # if not self.triggerList.triggerPassed(event.triggerObject):
0119         #    return False
0120         # self.counters.counter('DiLepton').inc('trigger passed ')
0121 
0122         # if event.eventId == 155035:
0123         #    import pdb; pdb.set_trace()
0124             
0125         # import pdb; pdb.set_trace()
0126         if len(event.diLeptons) == 0:
0127             return False
0128         if fillCounter: self.counters.counter('DiLepton').inc('> 0 di-lepton')
0129 
0130         # import pdb; pdb.set_trace()
0131         # testing di-lepton itself
0132         selDiLeptons = event.diLeptons
0133         # selDiLeptons = self.selectDiLeptons( selDiLeptons ) 
0134 
0135         event.leptonAccept = False
0136         if self.leptonAccept( event.leptons ):
0137             if fillCounter: self.counters.counter('DiLepton').inc('lepton accept')
0138             event.leptonAccept = True
0139 
0140         event.thirdLeptonVeto = False
0141         if self.thirdLeptonVeto(event.leptons, event.otherLeptons):
0142             if fillCounter: self.counters.counter('DiLepton').inc('third lepton veto')
0143             event.thirdLeptonVeto = True
0144 
0145         # testing leg1
0146         selDiLeptons = [ diL for diL in selDiLeptons if \
0147                          self.testLeg1( diL.leg1(), leg1IsoCut ) ]
0148         if len(selDiLeptons) == 0:
0149             return False
0150         else:
0151             if fillCounter: self.counters.counter('DiLepton').inc('leg1 offline cuts passed')
0152 
0153         if len(self.cfg_comp.triggers)>0:
0154             # trigger matching leg1
0155             selDiLeptons = [diL for diL in selDiLeptons if \
0156                             self.trigMatched(event, diL.leg1(), 'leg1')]
0157             if len(selDiLeptons) == 0:
0158                 return False
0159             else:
0160                 if fillCounter: self.counters.counter('DiLepton').inc('leg1 trig matched')
0161 
0162         # testing leg2 
0163         selDiLeptons = [ diL for diL in selDiLeptons if \
0164                          self.testLeg2( diL.leg2(), leg2IsoCut ) ]
0165         if len(selDiLeptons) == 0:
0166             return False
0167         else:
0168             if fillCounter: self.counters.counter('DiLepton').inc('leg2 offline cuts passed')
0169 
0170         if len(self.cfg_comp.triggers)>0:
0171             # trigger matching leg2
0172             selDiLeptons = [diL for diL in selDiLeptons if \
0173                             self.trigMatched(event, diL.leg2(), 'leg2')]
0174             if len(selDiLeptons) == 0:
0175                 return False
0176             else:
0177                 if fillCounter: self.counters.counter('DiLepton').inc('leg2 trig matched')
0178 
0179         # mass cut 
0180         selDiLeptons = [ diL for diL in selDiLeptons if \
0181                          self.testMass(diL) ]
0182         if len(selDiLeptons)==0:
0183             return False
0184         else:
0185             if fillCounter: self.counters.counter('DiLepton').inc(
0186                 '{min:3.1f} < m < {max:3.1f}'.format( min = self.cfg_ana.m_min,
0187                                                       max = self.cfg_ana.m_max )
0188                 )
0189 
0190         # delta R cut
0191         if hasattr(self.cfg_ana, 'dR_min'):
0192             selDiLeptons = [ diL for diL in selDiLeptons if \
0193                              self.testDeltaR(diL) ]
0194             if len(selDiLeptons)==0:
0195                 return False
0196             else:
0197                 if fillCounter: self.counters.counter('DiLepton').inc(
0198                     'dR > {min:3.1f}'.format( min = self.cfg_ana.dR_min )
0199                 )
0200 
0201         # exactly one? 
0202         if len(selDiLeptons)==0:
0203             return False
0204         elif len(selDiLeptons)==1:
0205             if fillCounter: self.counters.counter('DiLepton').inc('exactly 1 di-lepton')
0206         
0207         event.diLepton = self.bestDiLepton( selDiLeptons )
0208         event.leg1 = event.diLepton.leg1()
0209         event.leg2 = event.diLepton.leg2()
0210         event.selectedLeptons = [event.leg1, event.leg2]
0211 
0212         return True
0213     
0214 
0215     def declareHandles(self):        
0216         super(DiLeptonAnalyzer, self).declareHandles()
0217         # self.handles['cmgTriggerObjectSel'] =  AutoHandle(
0218         #     'cmgTriggerObjectSel',
0219         #     'std::vector<cmg::TriggerObject>'
0220         #     )
0221     
0222     def leptonAccept(self, leptons):
0223         '''Should implement a default version running on event.leptons.'''
0224         return True
0225     
0226 
0227     def thirdLeptonVeto(self, leptons, otherLeptons, isoCut = 0.3) :
0228         '''Should implement a default version running on event.leptons.'''
0229         return True
0230 
0231 
0232     def testLeg1(self, leg, isocut=None):
0233         '''returns testLeg1ID && testLeg1Iso && testLegKine for leg1'''
0234         return self.testLeg1ID(leg) and \
0235                self.testLeg1Iso(leg, isocut) and \
0236                self.testLegKine(leg, self.cfg_ana.pt1, self.cfg_ana.eta1)
0237 
0238 
0239     def testLeg2(self, leg, isocut=None):
0240         '''returns testLeg2ID && testLeg2Iso && testLegKine for leg2'''
0241         return self.testLeg2ID(leg) and \
0242                self.testLeg2Iso(leg, isocut) and \
0243                self.testLegKine(leg, self.cfg_ana.pt2, self.cfg_ana.eta2)
0244 
0245 
0246     def testLegKine(self, leg, ptcut, etacut ):
0247         '''Tests pt and eta.'''
0248         return leg.pt() > ptcut and \
0249                abs(leg.eta()) < etacut 
0250 
0251     
0252     def testLeg1ID(self, leg):
0253         '''Always return true by default, overload in your subclass'''
0254         return True
0255 
0256     
0257     def testLeg1Iso(self, leg, isocut):
0258         '''If isocut is None, the iso value is taken from the iso1 parameter.
0259         Checks the standard dbeta corrected isolation.
0260         '''
0261         if isocut is None:
0262             isocut = self.cfg_ana.iso1
0263         return leg.relIso(0.5) < isocut
0264 
0265     
0266     def testLeg2ID(self, leg):
0267         '''Always return true by default, overload in your subclass'''
0268         return True
0269 
0270     
0271     def testLeg2Iso(self, leg, isocut):
0272         '''If isocut is None, the iso value is taken from the iso2 parameter.
0273         Checks the standard dbeta corrected isolation.
0274         '''
0275         if isocut is None:
0276             isocut = self.cfg_ana.iso2
0277         return leg.relIso(0.5) < isocut
0278 
0279 
0280     def testMass(self, diLepton):
0281         '''returns True if the mass of the dilepton is between the m_min and m_max parameters'''
0282         mass = diLepton.mass()
0283         return self.cfg_ana.m_min < mass and mass < self.cfg_ana.m_max
0284 
0285 
0286     def testDeltaR(self, diLepton):
0287         '''returns True if the two diLepton.leg1() and .leg2() have a delta R larger than the dR_min parameter.'''
0288         dR = deltaR( diLepton.leg1().eta(), diLepton.leg1().phi(),
0289                      diLepton.leg2().eta(), diLepton.leg2().phi())
0290         return dR > self.cfg_ana.dR_min
0291 
0292     
0293     def bestDiLepton(self, diLeptons):
0294         '''Returns the best diLepton (the one with highest pt1 + pt2).'''
0295         return max( diLeptons, key=operator.methodcaller( 'sumPt' ) )
0296     
0297 
0298     def trigMatched(self, event, leg, legName):
0299         '''Returns true if the leg is matched to a trigger object as defined in the
0300         triggerMap parameter'''
0301         if not hasattr( self.cfg_ana, 'triggerMap'):
0302             return True
0303         path = event.hltPath
0304         triggerObjects = event.triggerObjects
0305         filters = self.cfg_ana.triggerMap[ path ]
0306         filter = None
0307         if legName == 'leg1':
0308             filter = filters[0]
0309         elif legName == 'leg2':
0310             filter = filters[1]
0311         else:
0312             raise ValueError( 'legName should be leg1 or leg2, not {leg}'.format(
0313                 leg=legName )  )
0314 
0315         # JAN: Need a hack for the embedded samples: No trigger matching in that case
0316         if filter == '':
0317             return True
0318 
0319         # the dR2Max value is 0.3^2
0320         pdgIds = None
0321         if len(filter) == 2:
0322             filter, pdgIds = filter[0], filter[1]
0323         return triggerMatched(leg, triggerObjects, path, filter,
0324                               # dR2Max=0.089999,
0325                               dR2Max=0.25,
0326                               pdgIds=pdgIds )