File indexing completed on 2023-10-25 09:57:50
0001 from PhysicsTools.Heppy.physicsobjects.Lepton import Lepton
0002 from PhysicsTools.HeppyCore.utils.deltar import deltaR
0003
0004 class Muon( Lepton ):
0005
0006 def __init__(self, *args, **kwargs):
0007 super(Muon, self).__init__(*args, **kwargs)
0008 self._trackForDxyDz = "muonBestTrack"
0009
0010 def setTrackForDxyDz(self,what):
0011 if not hasattr(self,what):
0012 raise RuntimeError("I don't have a track called "+what)
0013 self._trackForDxyDz = what
0014
0015 def looseId( self ):
0016 '''Loose ID as recommended by mu POG.'''
0017 return self.physObj.isLooseMuon()
0018
0019 def tightId( self ):
0020 '''Tight ID as recommended by mu POG
0021 (unless redefined in the lepton analyzer).
0022
0023 If not using the LeptonAnalyzer, make sure to set self.associatedVertex,
0024 that is necessary for tight muon identification.
0025 '''
0026 return getattr(self,"tightIdResult",self.muonID("POG_ID_Tight"))
0027
0028 def muonID(self, name, vertex=None):
0029 if name == "" or name is None:
0030 return True
0031 if name.startswith("POG_"):
0032 if name == "POG_ID_Loose": return self.physObj.isLooseMuon()
0033 if vertex is None:
0034 vertex = getattr(self, 'associatedVertex', None)
0035 if name == "POG_ID_Tight": return self.physObj.isTightMuon(vertex)
0036 if name == "POG_ID_HighPt": return self.physObj.isHighPtMuon(vertex)
0037 if name == "POG_ID_Soft": return self.physObj.isSoftMuon(vertex)
0038 if name == "POG_ID_TightNoVtx": return self.looseId() and \
0039 self.isGlobalMuon() and \
0040 self.globalTrack().normalizedChi2() < 10 and \
0041 self.globalTrack().hitPattern().numberOfValidMuonHits() > 0 and \
0042 self.numberOfMatchedStations()>1 and \
0043 self.innerTrack().hitPattern().numberOfValidPixelHits()>0 and \
0044 self.innerTrack().hitPattern().trackerLayersWithMeasurement() > 5
0045 if name == "POG_ID_Medium":
0046 if not self.looseId(): return False
0047 goodGlb = self.physObj.isGlobalMuon() and self.physObj.globalTrack().normalizedChi2() < 3 and self.physObj.combinedQuality().chi2LocalPosition < 12 and self.physObj.combinedQuality().trkKink < 20;
0048 return self.physObj.innerTrack().validFraction() > 0.8 and self.physObj.segmentCompatibility() >= (0.303 if goodGlb else 0.451)
0049 if name == "POG_Global_OR_TMArbitrated":
0050 return self.physObj.isGlobalMuon() or (self.physObj.isTrackerMuon() and self.physObj.numberOfMatchedStations() > 0)
0051 return self.physObj.muonID(name)
0052
0053 def mvaId(self):
0054 '''For a transparent treatment of electrons and muons. Returns -99'''
0055 return -99
0056
0057
0058 def dxy(self, vertex=None):
0059 '''either pass the vertex, or set associatedVertex before calling the function.
0060 note: the function does not work with standalone muons as innerTrack
0061 is not available.
0062 '''
0063 if vertex is None:
0064 vertex = self.associatedVertex
0065 return getattr(self,self._trackForDxyDz)().dxy( vertex.position() )
0066
0067 def edxy(self):
0068 '''returns the uncertainty on dxy (from gsf track)'''
0069 return getattr(self,self._trackForDxyDz)().dxyError()
0070
0071
0072 def dz(self, vertex=None):
0073 '''either pass the vertex, or set associatedVertex before calling the function.
0074 note: the function does not work with standalone muons as innerTrack
0075 is not available.
0076 '''
0077 if vertex is None:
0078 vertex = self.associatedVertex
0079 return getattr(self,self._trackForDxyDz)().dz( vertex.position() )
0080
0081 def edz(self):
0082 '''returns the uncertainty on dxz (from gsf track)'''
0083 return getattr(self,self._trackForDxyDz)().dzError()
0084
0085 def chargedHadronIsoR(self,R=0.4):
0086 if R == 0.3: return self.physObj.pfIsolationR03().sumChargedHadronPt
0087 elif R == 0.4: return self.physObj.pfIsolationR04().sumChargedHadronPt
0088 raise RuntimeError("Muon chargedHadronIso missing for R=%s" % R)
0089
0090 def neutralHadronIsoR(self,R=0.4):
0091 if R == 0.3: return self.physObj.pfIsolationR03().sumNeutralHadronEt
0092 elif R == 0.4: return self.physObj.pfIsolationR04().sumNeutralHadronEt
0093 raise RuntimeError("Muon neutralHadronIso missing for R=%s" % R)
0094
0095 def photonIsoR(self,R=0.4):
0096 if R == 0.3: return self.physObj.pfIsolationR03().sumPhotonEt
0097 elif R == 0.4: return self.physObj.pfIsolationR04().sumPhotonEt
0098 raise RuntimeError("Muon photonIso missing for R=%s" % R)
0099
0100 def chargedAllIsoR(self,R=0.4):
0101 if R == 0.3: return self.physObj.pfIsolationR03().sumChargedParticlePt
0102 elif R == 0.4: return self.physObj.pfIsolationR04().sumChargedParticlePt
0103 raise RuntimeError("Muon chargedAllIso missing for R=%s" % R)
0104
0105 def chargedAllIso(self):
0106 return self.chargedAllIsoR()
0107
0108 def puChargedHadronIsoR(self,R=0.4):
0109 if R == 0.3: return self.physObj.pfIsolationR03().sumPUPt
0110 elif R == 0.4: return self.physObj.pfIsolationR04().sumPUPt
0111 raise RuntimeError("Muon chargedHadronIso missing for R=%s" % R)
0112
0113
0114 def absIsoWithFSR(self, R=0.4, puCorr="deltaBeta", dBetaFactor=0.5):
0115 '''
0116 Calculate Isolation, subtract FSR, apply specific PU corrections"
0117 '''
0118 photonIso = self.photonIsoR(R)
0119 if hasattr(self,'fsrPhotons'):
0120 for gamma in self.fsrPhotons:
0121 dr = deltaR(gamma.eta(), gamma.phi(), self.physObj.eta(), self.physObj.phi())
0122 if dr > 0.01 and dr < R:
0123 photonIso = max(photonIso-gamma.pt(),0.0)
0124 if puCorr == "deltaBeta":
0125 offset = dBetaFactor * self.puChargedHadronIsoR(R)
0126 elif puCorr == "rhoArea":
0127 offset = self.rho*getattr(self,"EffectiveArea"+(str(R).replace(".","")))
0128 elif puCorr in ["none","None",None]:
0129 offset = 0
0130 else:
0131 raise RuntimeError("Unsupported PU correction scheme %s" % puCorr)
0132 return self.chargedHadronIsoR(R)+max(0.,photonIso+self.neutralHadronIsoR(R)-offset)
0133
0134 def ptErr(self):
0135 if "_ptErr" in self.__dict__: return self.__dict__['_ptErr']
0136 return self.bestTrack().ptError()