CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
JetAnalyzer.py
Go to the documentation of this file.
1 import math, os
2 from PhysicsTools.Heppy.analyzers.core.Analyzer import Analyzer
3 from PhysicsTools.Heppy.analyzers.core.AutoHandle import AutoHandle
4 from PhysicsTools.Heppy.physicsobjects.PhysicsObjects import Jet
5 from PhysicsTools.HeppyCore.utils.deltar import deltaR2, deltaPhi, matchObjectCollection, matchObjectCollection2, bestMatch
6 from PhysicsTools.Heppy.physicsutils.JetReCalibrator import JetReCalibrator
7 import PhysicsTools.HeppyCore.framework.config as cfg
8 
9 from PhysicsTools.Heppy.physicsutils.QGLikelihoodCalculator import QGLikelihoodCalculator
10 
11 def cleanNearestJetOnly(jets,leptons,deltaR):
12  dr2 = deltaR**2
13  good = [ True for j in jets ]
14  for l in leptons:
15  ibest, d2m = -1, dr2
16  for i,j in enumerate(jets):
17  d2i = deltaR2(l.eta(),l.phi(), j.eta(),j.phi())
18  if d2i < d2m:
19  ibest, d2m = i, d2i
20  if ibest != -1: good[ibest] = False
21  return [ j for (i,j) in enumerate(jets) if good[i] == True ]
22 
23 def cleanJetsAndLeptons(jets,leptons,deltaR,arbitration):
24  dr2 = deltaR**2
25  goodjet = [ True for j in jets ]
26  goodlep = [ True for l in leptons ]
27  for il, l in enumerate(leptons):
28  ibest, d2m = -1, dr2
29  for i,j in enumerate(jets):
30  d2i = deltaR2(l.eta(),l.phi(), j.eta(),j.phi())
31  if d2i < dr2:
32  choice = arbitration(j,l)
33  if choice == j:
34  # if the two match, and we prefer the jet, then drop the lepton and be done
35  goodlep[il] = False
36  break
37  elif choice == (j,l) or choice == (l,j):
38  # asked to keep both, so we don't consider this match
39  continue
40  if d2i < d2m:
41  ibest, d2m = i, d2i
42  # this lepton has been killed by a jet, then we clean the jet that best matches it
43  if not goodlep[il]: continue
44  if ibest != -1: goodjet[ibest] = False
45  return ( [ j for (i ,j) in enumerate(jets) if goodjet[i ] == True ],
46  [ l for (il,l) in enumerate(leptons) if goodlep[il] == True ] )
47 
48 
49 class JetAnalyzer( Analyzer ):
50  """Taken from RootTools.JetAnalyzer, simplified, modified, added corrections """
51  def __init__(self, cfg_ana, cfg_comp, looperName):
52  super(JetAnalyzer,self).__init__(cfg_ana, cfg_comp, looperName)
53  mcGT = cfg_ana.mcGT if hasattr(cfg_ana,'mcGT') else "PHYS14_25_V2"
54  dataGT = cfg_ana.dataGT if hasattr(cfg_ana,'dataGT') else "GR_70_V2_AN1"
55  self.shiftJEC = self.cfg_ana.shiftJEC if hasattr(self.cfg_ana, 'shiftJEC') else 0
56  self.recalibrateJets = self.cfg_ana.recalibrateJets
57  if self.recalibrateJets == "MC" : self.recalibrateJets = self.cfg_comp.isMC
58  elif self.recalibrateJets == "Data": self.recalibrateJets = not self.cfg_comp.isMC
59  elif self.recalibrateJets not in [True,False]: raise RuntimeError, "recalibrateJets must be any of { True, False, 'MC', 'Data' }, while it is %r " % self.recalibrateJets
60  self.doJEC = self.recalibrateJets or (self.shiftJEC != 0)
61  if self.doJEC:
62  if self.cfg_comp.isMC:
63  self.jetReCalibrator = JetReCalibrator(mcGT,self.cfg_ana.recalibrationType, False,cfg_ana.jecPath)
64  else:
65  self.jetReCalibrator = JetReCalibrator(dataGT,self.cfg_ana.recalibrationType, True,cfg_ana.jecPath)
66  self.doPuId = getattr(self.cfg_ana, 'doPuId', True)
67  self.jetLepDR = getattr(self.cfg_ana, 'jetLepDR', 0.4)
68  self.jetLepArbitration = getattr(self.cfg_ana, 'jetLepArbitration', lambda jet,lepton: lepton)
69  self.lepPtMin = getattr(self.cfg_ana, 'minLepPt', -1)
70  self.lepSelCut = getattr(self.cfg_ana, 'lepSelCut', lambda lep : True)
71  self.jetGammaDR = getattr(self.cfg_ana, 'jetGammaDR', 0.4)
72  if(self.cfg_ana.doQG):
73  qgdefname="{CMSSW_BASE}/src/PhysicsTools/Heppy/data/pdfQG_AK4chs_antib_13TeV_v1.root"
74  self.qglcalc = QGLikelihoodCalculator(getattr(self.cfg_ana,"QGpath",qgdefname).format(CMSSW_BASE= os.environ['CMSSW_BASE']))
75  if not hasattr(self.cfg_ana ,"collectionPostFix"):self.cfg_ana.collectionPostFix=""
76 
77  def declareHandles(self):
78  super(JetAnalyzer, self).declareHandles()
79  self.handles['jets'] = AutoHandle( self.cfg_ana.jetCol, 'std::vector<pat::Jet>' )
80  self.handles['genJet'] = AutoHandle( self.cfg_ana.genJetCol, 'vector<reco::GenJet>' )
81  self.shiftJER = self.cfg_ana.shiftJER if hasattr(self.cfg_ana, 'shiftJER') else 0
82  self.handles['rho'] = AutoHandle( self.cfg_ana.rho, 'double' )
83 
84  def beginLoop(self, setup):
85  super(JetAnalyzer,self).beginLoop(setup)
86 
87  def process(self, event):
88  self.readCollections( event.input )
89  rho = float(self.handles['rho'].product()[0])
90  self.rho = rho
91 
92  ## Read jets, if necessary recalibrate and shift MET
93  if self.cfg_ana.copyJetsByValue:
94  import ROOT
95  allJets = map(lambda j:Jet(ROOT.pat.Jet(j)), self.handles['jets'].product()) #copy-by-value is safe if JetAnalyzer is ran more than once
96  else:
97  allJets = map(Jet, self.handles['jets'].product())
98 
99  self.deltaMetFromJEC = [0.,0.]
100 # print "before. rho",self.rho,self.cfg_ana.collectionPostFix,'allJets len ',len(allJets),'pt', [j.pt() for j in allJets]
101  if self.doJEC:
102 # print "\nCalibrating jets %s for lumi %d, event %d" % (self.cfg_ana.jetCol, event.lumi, event.eventId)
103  self.jetReCalibrator.correctAll(allJets, rho, delta=self.shiftJEC, metShift=self.deltaMetFromJEC)
104  self.allJetsUsedForMET = allJets
105 # print "after. rho",self.rho,self.cfg_ana.collectionPostFix,'allJets len ',len(allJets),'pt', [j.pt() for j in allJets]
106 
107  if self.cfg_comp.isMC:
108  self.genJets = [ x for x in self.handles['genJet'].product() ]
109  self.matchJets(event, allJets)
110  if getattr(self.cfg_ana, 'smearJets', False):
111  self.smearJets(event, allJets)
112 
113  ##Sort Jets by pT
114  allJets.sort(key = lambda j : j.pt(), reverse = True)
115  ## Apply jet selection
116  self.jets = []
117  self.jetsFailId = []
118  self.jetsAllNoID = []
119  self.jetsIdOnly = []
120  for jet in allJets:
121  if self.testJetNoID( jet ):
122  self.jetsAllNoID.append(jet)
123  if self.testJetID (jet ):
124 
125  if(self.cfg_ana.doQG):
126  self.computeQGvars(jet)
127  jet.qgl = self.qglcalc.computeQGLikelihood(jet, rho)
128 
129 
130  self.jets.append(jet)
131  self.jetsIdOnly.append(jet)
132  else:
133  self.jetsFailId.append(jet)
134  elif self.testJetID (jet ):
135  self.jetsIdOnly.append(jet)
136 
137  ## Clean Jets from leptons
138  leptons = []
139  if hasattr(event, 'selectedLeptons'):
140  leptons = [ l for l in event.selectedLeptons if l.pt() > self.lepPtMin and self.lepSelCut(l) ]
141  if self.cfg_ana.cleanJetsFromTaus and hasattr(event, 'selectedTaus'):
142  leptons = leptons[:] + event.selectedTaus
143  if self.cfg_ana.cleanJetsFromIsoTracks and hasattr(event, 'selectedIsoCleanTrack'):
144  leptons = leptons[:] + event.selectedIsoCleanTrack
145  self.cleanJetsAll, cleanLeptons = cleanJetsAndLeptons(self.jets, leptons, self.jetLepDR, self.jetLepArbitration)
146  self.cleanJets = [j for j in self.cleanJetsAll if abs(j.eta()) < self.cfg_ana.jetEtaCentral ]
147  self.cleanJetsFwd = [j for j in self.cleanJetsAll if abs(j.eta()) >= self.cfg_ana.jetEtaCentral ]
148  self.discardedJets = [j for j in self.jets if j not in self.cleanJetsAll]
149  if hasattr(event, 'selectedLeptons') and self.cfg_ana.cleanSelectedLeptons:
150  event.discardedLeptons = [ l for l in leptons if l not in cleanLeptons ]
151  event.selectedLeptons = [ l for l in event.selectedLeptons if l not in event.discardedLeptons ]
152 
153  ## Clean Jets from photons
154  photons = []
155  if hasattr(event, 'selectedPhotons'):
156  if self.cfg_ana.cleanJetsFromFirstPhoton:
157  photons = event.selectedPhotons[:1]
158  else:
159  photons = [ g for g in event.selectedPhotons ]
160 
161  self.gamma_cleanJetsAll = cleanNearestJetOnly(self.cleanJetsAll, photons, self.jetGammaDR)
162  self.gamma_cleanJets = [j for j in self.gamma_cleanJetsAll if abs(j.eta()) < self.cfg_ana.jetEtaCentral ]
163  self.gamma_cleanJetsFwd = [j for j in self.gamma_cleanJetsAll if abs(j.eta()) >= self.cfg_ana.jetEtaCentral ]
164  ###
165 
166  if self.cfg_ana.alwaysCleanPhotons:
167  self.cleanJets = self.gamma_cleanJets
168  self.cleanJetsAll = self.gamma_cleanJetsAll
169  self.cleanJetsFwd = self.gamma_cleanJetsFwd
170 
171  ## Associate jets to leptons
172  leptons = event.inclusiveLeptons if hasattr(event, 'inclusiveLeptons') else event.selectedLeptons
173  jlpairs = matchObjectCollection( leptons, allJets, self.jetLepDR**2)
174 
175  for jet in allJets:
176  jet.leptons = [l for l in jlpairs if jlpairs[l] == jet ]
177 
178  for lep in leptons:
179  jet = jlpairs[lep]
180  if jet is None:
181  lep.jet = lep
182  else:
183  lep.jet = jet
184  ## Associate jets to taus
185  taus = getattr(event,'selectedTaus',[])
186  jtaupairs = matchObjectCollection( taus, allJets, self.jetLepDR**2)
187 
188  for jet in allJets:
189  jet.taus = [l for l in jtaupairs if jtaupairs[l] == jet ]
190  for tau in taus:
191  tau.jet = jtaupairs[tau]
192 
193  #MC stuff
194  if self.cfg_comp.isMC:
195  self.deltaMetFromJetSmearing = [0, 0]
196  for j in self.cleanJetsAll:
197  if hasattr(j, 'deltaMetFromJetSmearing'):
198  self.deltaMetFromJetSmearing[0] += j.deltaMetFromJetSmearing[0]
199  self.deltaMetFromJetSmearing[1] += j.deltaMetFromJetSmearing[1]
200  self.cleanGenJets = cleanNearestJetOnly(self.genJets, leptons, self.jetLepDR)
201 
202  if self.cfg_ana.cleanGenJetsFromPhoton:
203  self.cleanGenJets = cleanNearestJetOnly(self.cleanGenJets, photons, self.jetLepDR)
204 
205 
206  #event.nGenJets25 = 0
207  #event.nGenJets25Cen = 0
208  #event.nGenJets25Fwd = 0
209  #for j in event.cleanGenJets:
210  # event.nGenJets25 += 1
211  # if abs(j.eta()) <= 2.4: event.nGenJets25Cen += 1
212  # else: event.nGenJets25Fwd += 1
213 
214  self.jetFlavour(event)
215 
216  setattr(event,"rho" +self.cfg_ana.collectionPostFix, self.rho )
217  setattr(event,"deltaMetFromJEC" +self.cfg_ana.collectionPostFix, self.deltaMetFromJEC )
218  setattr(event,"allJetsUsedForMET" +self.cfg_ana.collectionPostFix, self.allJetsUsedForMET )
219  setattr(event,"jets" +self.cfg_ana.collectionPostFix, self.jets )
220  setattr(event,"jetsFailId" +self.cfg_ana.collectionPostFix, self.jetsFailId )
221  setattr(event,"jetsAllNoID" +self.cfg_ana.collectionPostFix, self.jetsAllNoID )
222  setattr(event,"jetsIdOnly" +self.cfg_ana.collectionPostFix, self.jetsIdOnly )
223  setattr(event,"cleanJetsAll" +self.cfg_ana.collectionPostFix, self.cleanJetsAll )
224  setattr(event,"cleanJets" +self.cfg_ana.collectionPostFix, self.cleanJets )
225  setattr(event,"cleanJetsFwd" +self.cfg_ana.collectionPostFix, self.cleanJetsFwd )
226  setattr(event,"discardedJets" +self.cfg_ana.collectionPostFix, self.discardedJets )
227  setattr(event,"gamma_cleanJetsAll" +self.cfg_ana.collectionPostFix, self.gamma_cleanJetsAll )
228  setattr(event,"gamma_cleanJets" +self.cfg_ana.collectionPostFix, self.gamma_cleanJets )
229  setattr(event,"gamma_cleanJetsFwd" +self.cfg_ana.collectionPostFix, self.gamma_cleanJetsFwd )
230 
231 
232  if self.cfg_comp.isMC:
233  setattr(event,"cleanGenJets" +self.cfg_ana.collectionPostFix, self.cleanGenJets )
234  setattr(event,"bqObjects" +self.cfg_ana.collectionPostFix, self.bqObjects )
235  setattr(event,"cqObjects" +self.cfg_ana.collectionPostFix, self.cqObjects )
236  setattr(event,"partons" +self.cfg_ana.collectionPostFix, self.partons )
237  setattr(event,"heaviestQCDFlavour" +self.cfg_ana.collectionPostFix, self.heaviestQCDFlavour )
238  setattr(event,"deltaMetFromJetSmearing"+self.cfg_ana.collectionPostFix, self.deltaMetFromJetSmearing)
239  setattr(event,"genJets" +self.cfg_ana.collectionPostFix, self.genJets )
240 
241  return True
242 
243 
244 
245  def testJetID(self, jet):
246  jet.puJetIdPassed = jet.puJetId()
247  jet.pfJetIdPassed = jet.jetID('POG_PFID_Loose')
248  if self.cfg_ana.relaxJetId:
249  return True
250  else:
251  return jet.pfJetIdPassed and (jet.puJetIdPassed or not(self.doPuId))
252 
253  def testJetNoID( self, jet ):
254  # 2 is loose pile-up jet id
255  return jet.pt() > self.cfg_ana.jetPt and \
256  abs( jet.eta() ) < self.cfg_ana.jetEta;
257 
258  def computeQGvars(self, jet):
259 
260  jet.mult = 0
261  sum_weight = 0.
262  sum_pt = 0.
263  sum_deta = 0.
264  sum_dphi = 0.
265  sum_deta2 = 0.
266  sum_detadphi = 0.
267  sum_dphi2 = 0.
268 
269 
270 
271  for ii in range(0, jet.numberOfDaughters()) :
272 
273  part = jet.daughter(ii)
274 
275  if part.charge() == 0 : # neutral particles
276 
277  if part.pt() < 1.: continue
278 
279  else : # charged particles
280 
281  if part.trackHighPurity()==False: continue
282  if part.fromPV()<=1: continue
283 
284 
285  jet.mult += 1
286 
287  deta = part.eta() - jet.eta()
288  dphi = deltaPhi(part.phi(), jet.phi())
289  partPt = part.pt()
290  weight = partPt*partPt
291  sum_weight += weight
292  sum_pt += partPt
293  sum_deta += deta*weight
294  sum_dphi += dphi*weight
295  sum_deta2 += deta*deta*weight
296  sum_detadphi += deta*dphi*weight
297  sum_dphi2 += dphi*dphi*weight
298 
299 
300 
301 
302  a = 0.
303  b = 0.
304  c = 0.
305 
306  if sum_weight > 0 :
307  jet.ptd = math.sqrt(sum_weight)/sum_pt
308  ave_deta = sum_deta/sum_weight
309  ave_dphi = sum_dphi/sum_weight
310  ave_deta2 = sum_deta2/sum_weight
311  ave_dphi2 = sum_dphi2/sum_weight
312  a = ave_deta2 - ave_deta*ave_deta
313  b = ave_dphi2 - ave_dphi*ave_dphi
314  c = -(sum_detadphi/sum_weight - ave_deta*ave_dphi)
315  else: jet.ptd = 0.
316 
317  delta = math.sqrt(math.fabs((a-b)*(a-b)+4.*c*c))
318 
319  if a+b-delta > 0: jet.axis2 = -math.log(math.sqrt(0.5*(a+b-delta)))
320  else: jet.axis2 = -1.
321 
322 
323  def jetFlavour(self,event):
324  def isFlavour(x,f):
325  id = abs(x.pdgId())
326  if id > 999: return (id/1000)%10 == f
327  if id > 99: return (id/100)%10 == f
328  return id % 100 == f
329 
330 
331 
332  self.bqObjects = [ p for p in event.genParticles if (p.status() == 2 and isFlavour(p,5)) ]
333  self.cqObjects = [ p for p in event.genParticles if (p.status() == 2 and isFlavour(p,4)) ]
334 
335  self.partons = [ p for p in event.genParticles if ((p.status() == 23 or p.status() == 3) and abs(p.pdgId())>0 and (abs(p.pdgId()) in [1,2,3,4,5,21]) ) ]
336  match = matchObjectCollection2(self.cleanJetsAll,
337  self.partons,
338  deltaRMax = 0.3)
339 
340  for jet in self.cleanJetsAll:
341  parton = match[jet]
342  jet.partonId = (parton.pdgId() if parton != None else 0)
343  jet.partonMotherId = (parton.mother(0).pdgId() if parton != None and parton.numberOfMothers()>0 else 0)
344 
345  for jet in self.jets:
346  (bmatch, dr) = bestMatch(jet, self.bqObjects)
347  if dr < 0.4:
348  jet.mcFlavour = 5
349  else:
350  (cmatch, dr) = bestMatch(jet, self.cqObjects)
351  if dr < 0.4:
352  jet.mcFlavour = 4
353  else:
354  jet.mcFlavour = 0
355 
356  self.heaviestQCDFlavour = 5 if len(self.bqObjects) else (4 if len(self.cqObjects) else 1);
357 
358 
359  def matchJets(self, event, jets):
360  match = matchObjectCollection2(jets,
361  event.genbquarks + event.genwzquarks,
362  deltaRMax = 0.3)
363  for jet in jets:
364  gen = match[jet]
365  jet.mcParton = gen
366  jet.mcMatchId = (gen.sourceId if gen != None else 0)
367  jet.mcMatchFlav = (abs(gen.pdgId()) if gen != None else 0)
368 
369  match = matchObjectCollection2(jets,
370  self.genJets,
371  deltaRMax = 0.3)
372  for jet in jets:
373  jet.mcJet = match[jet]
374 
375 
376 
377  def smearJets(self, event, jets):
378  # https://twiki.cern.ch/twiki/bin/viewauth/CMS/TWikiTopRefSyst#Jet_energy_resolution
379  for jet in jets:
380  gen = jet.mcJet
381  if gen != None:
382  genpt, jetpt, aeta = gen.pt(), jet.pt(), abs(jet.eta())
383  # from https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution
384  factor = 1.052 + self.shiftJER*math.hypot(0.012,0.062);
385  if aeta > 2.3: factor = 1.288 + self.shiftJER*math.hypot(0.127,0.154)
386  elif aeta > 1.7: factor = 1.134 + self.shiftJER*math.hypot(0.035,0.066)
387  elif aeta > 1.1: factor = 1.096 + self.shiftJER*math.hypot(0.017,0.063)
388  elif aeta > 0.5: factor = 1.057 + self.shiftJER*math.hypot(0.012,0.056)
389  ptscale = max(0.0, (jetpt + (factor-1)*(jetpt-genpt))/jetpt)
390  #print "get with pt %.1f (gen pt %.1f, ptscale = %.3f)" % (jetpt,genpt,ptscale)
391  jet.deltaMetFromJetSmearing = [ -(ptscale-1)*jet.rawFactor()*jet.px(), -(ptscale-1)*jet.rawFactor()*jet.py() ]
392  if ptscale != 0:
393  jet.setP4(jet.p4()*ptscale)
394  # leave the uncorrected unchanged for sync
395  jet._rawFactorMultiplier *= (1.0/ptscale) if ptscale != 0 else 1
396  #else: print "jet with pt %.1d, eta %.2f is unmatched" % (jet.pt(), jet.eta())
397 
398 
399 
400 setattr(JetAnalyzer,"defaultConfig", cfg.Analyzer(
401  class_object = JetAnalyzer,
402  jetCol = 'slimmedJets',
403  copyJetsByValue = False, #Whether or not to copy the input jets or to work with references (should be 'True' if JetAnalyzer is run more than once)
404  genJetCol = 'slimmedGenJets',
405  rho = ('fixedGridRhoFastjetAll','',''),
406  jetPt = 25.,
407  jetEta = 4.7,
408  jetEtaCentral = 2.4,
409  jetLepDR = 0.4,
410  jetLepArbitration = (lambda jet,lepton : lepton), # you can decide which to keep in case of overlaps; e.g. if the jet is b-tagged you might want to keep the jet
411  cleanSelectedLeptons = True, #Whether to clean 'selectedLeptons' after disambiguation. Treat with care (= 'False') if running Jetanalyzer more than once
412  minLepPt = 10,
413  lepSelCut = lambda lep : True,
414  relaxJetId = False,
415  doPuId = False, # Not commissioned in 7.0.X
416  doQG = False,
417  recalibrateJets = False,
418  recalibrationType = "AK4PFchs",
419  shiftJEC = 0, # set to +1 or -1 to get +/-1 sigma shifts
420  smearJets = True,
421  shiftJER = 0, # set to +1 or -1 to get +/-1 sigma shifts
422  cleanJetsFromFirstPhoton = False,
423  cleanJetsFromTaus = False,
424  cleanJetsFromIsoTracks = False,
425  alwaysCleanPhotons = False,
426  jecPath = "",
427  cleanGenJetsFromPhoton = False,
428  collectionPostFix = ""
429  )
430 )
def bestMatch
Definition: deltar.py:139
deltaMetFromJEC
Read jets, if necessary recalibrate and shift MET.
Definition: JetAnalyzer.py:99
def matchObjectCollection
Definition: deltar.py:152
Definition: Jet.py:1
def matchObjectCollection2
Definition: deltar.py:167
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
double deltaR2(const T1 &t1, const T2 &t2)
Definition: deltaR.h:36
if(conf.exists("allCellsPositionCalc"))