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
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  self.qglcalc = QGLikelihoodCalculator("%s/src/PhysicsTools/Heppy/data/pdfQG_AK4chs_antib_13TeV_v1.root" % os.environ['CMSSW_BASE'])
74  if not hasattr(self.cfg_ana ,"collectionPostFix"):self.cfg_ana.collectionPostFix=""
75 
76  def declareHandles(self):
77  super(JetAnalyzer, self).declareHandles()
78  self.handles['jets'] = AutoHandle( self.cfg_ana.jetCol, 'std::vector<pat::Jet>' )
79  self.handles['genJet'] = AutoHandle( self.cfg_ana.genJetCol, 'vector<reco::GenJet>' )
80  self.shiftJER = self.cfg_ana.shiftJER if hasattr(self.cfg_ana, 'shiftJER') else 0
81  self.handles['rho'] = AutoHandle( self.cfg_ana.rho, 'double' )
82 
83  def beginLoop(self, setup):
84  super(JetAnalyzer,self).beginLoop(setup)
85 
86  def process(self, event):
87  self.readCollections( event.input )
88  rho = float(self.handles['rho'].product()[0])
89  self.rho = rho
90 
91  ## Read jets, if necessary recalibrate and shift MET
92  if self.cfg_ana.copyJetsByValue:
93  import ROOT
94  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
95  else:
96  allJets = map(Jet, self.handles['jets'].product())
97 
98  self.deltaMetFromJEC = [0.,0.]
99 # print "before. rho",self.rho,self.cfg_ana.collectionPostFix,'allJets len ',len(allJets),'pt', [j.pt() for j in allJets]
100  if self.doJEC:
101 # print "\nCalibrating jets %s for lumi %d, event %d" % (self.cfg_ana.jetCol, event.lumi, event.eventId)
102  self.jetReCalibrator.correctAll(allJets, rho, delta=self.shiftJEC, metShift=self.deltaMetFromJEC)
103  self.allJetsUsedForMET = allJets
104 # print "after. rho",self.rho,self.cfg_ana.collectionPostFix,'allJets len ',len(allJets),'pt', [j.pt() for j in allJets]
105 
106  if self.cfg_comp.isMC:
107  self.genJets = [ x for x in self.handles['genJet'].product() ]
108  self.matchJets(event, allJets)
109  if getattr(self.cfg_ana, 'smearJets', False):
110  self.smearJets(event, allJets)
111 
112  ##Sort Jets by pT
113  allJets.sort(key = lambda j : j.pt(), reverse = True)
114  ## Apply jet selection
115  self.jets = []
116  self.jetsFailId = []
117  self.jetsAllNoID = []
118  self.jetsIdOnly = []
119  for jet in allJets:
120  if self.testJetNoID( jet ):
121  self.jetsAllNoID.append(jet)
122  if self.testJetID (jet ):
123 
124  if(self.cfg_ana.doQG):
125  self.computeQGvars(jet)
126  jet.qgl = self.qglcalc.computeQGLikelihood(jet, rho)
127 
128 
129  self.jets.append(jet)
130  self.jetsIdOnly.append(jet)
131  else:
132  self.jetsFailId.append(jet)
133  elif self.testJetID (jet ):
134  self.jetsIdOnly.append(jet)
135 
136  ## Clean Jets from leptons
137  leptons = []
138  if hasattr(event, 'selectedLeptons'):
139  leptons = [ l for l in event.selectedLeptons if l.pt() > self.lepPtMin and self.lepSelCut(l) ]
140  if self.cfg_ana.cleanJetsFromTaus and hasattr(event, 'selectedTaus'):
141  leptons = leptons[:] + event.selectedTaus
142  if self.cfg_ana.cleanJetsFromIsoTracks and hasattr(event, 'selectedIsoCleanTrack'):
143  leptons = leptons[:] + event.selectedIsoCleanTrack
144  self.cleanJetsAll, cleanLeptons = cleanJetsAndLeptons(self.jets, leptons, self.jetLepDR, self.jetLepArbitration)
145  self.cleanJets = [j for j in self.cleanJetsAll if abs(j.eta()) < self.cfg_ana.jetEtaCentral ]
146  self.cleanJetsFwd = [j for j in self.cleanJetsAll if abs(j.eta()) >= self.cfg_ana.jetEtaCentral ]
147  self.discardedJets = [j for j in self.jets if j not in self.cleanJetsAll]
148  if hasattr(event, 'selectedLeptons') and self.cfg_ana.cleanSelectedLeptons:
149  event.discardedLeptons = [ l for l in leptons if l not in cleanLeptons ]
150  event.selectedLeptons = [ l for l in event.selectedLeptons if l not in event.discardedLeptons ]
151 
152  ## Clean Jets from photons
153  photons = []
154  if hasattr(event, 'selectedPhotons'):
155  if self.cfg_ana.cleanJetsFromFirstPhoton:
156  photons = event.selectedPhotons[:1]
157  else:
158  photons = [ g for g in event.selectedPhotons ]
159 
160  self.gamma_cleanJetsAll = cleanNearestJetOnly(self.cleanJetsAll, photons, self.jetGammaDR)
161  self.gamma_cleanJets = [j for j in self.gamma_cleanJetsAll if abs(j.eta()) < self.cfg_ana.jetEtaCentral ]
162  self.gamma_cleanJetsFwd = [j for j in self.gamma_cleanJetsAll if abs(j.eta()) >= self.cfg_ana.jetEtaCentral ]
163  ###
164 
165  if self.cfg_ana.alwaysCleanPhotons:
166  self.cleanJets = self.gamma_cleanJets
167  self.cleanJetsAll = self.gamma_cleanJetsAll
168  self.cleanJetsFwd = self.gamma_cleanJetsFwd
169 
170  ## Associate jets to leptons
171  leptons = event.inclusiveLeptons if hasattr(event, 'inclusiveLeptons') else event.selectedLeptons
172  jlpairs = matchObjectCollection( leptons, allJets, self.jetLepDR**2)
173 
174  for jet in allJets:
175  jet.leptons = [l for l in jlpairs if jlpairs[l] == jet ]
176 
177  for lep in leptons:
178  jet = jlpairs[lep]
179  if jet is None:
180  lep.jet = lep
181  else:
182  lep.jet = jet
183  ## Associate jets to taus
184  taus = getattr(event,'selectedTaus',[])
185  jtaupairs = matchObjectCollection( taus, allJets, self.jetLepDR**2)
186 
187  for jet in allJets:
188  jet.taus = [l for l in jtaupairs if jtaupairs[l] == jet ]
189  for tau in taus:
190  tau.jet = jtaupairs[tau]
191 
192  #MC stuff
193  if self.cfg_comp.isMC:
194  self.deltaMetFromJetSmearing = [0, 0]
195  for j in self.cleanJetsAll:
196  if hasattr(j, 'deltaMetFromJetSmearing'):
197  self.deltaMetFromJetSmearing[0] += j.deltaMetFromJetSmearing[0]
198  self.deltaMetFromJetSmearing[1] += j.deltaMetFromJetSmearing[1]
199  self.cleanGenJets = cleanNearestJetOnly(self.genJets, leptons, self.jetLepDR)
200 
201  if self.cfg_ana.cleanGenJetsFromPhoton:
202  self.cleanGenJets = cleanNearestJetOnly(self.cleanGenJets, photons, self.jetLepDR)
203 
204 
205  #event.nGenJets25 = 0
206  #event.nGenJets25Cen = 0
207  #event.nGenJets25Fwd = 0
208  #for j in event.cleanGenJets:
209  # event.nGenJets25 += 1
210  # if abs(j.eta()) <= 2.4: event.nGenJets25Cen += 1
211  # else: event.nGenJets25Fwd += 1
212 
213  self.jetFlavour(event)
214 
215  setattr(event,"rho" +self.cfg_ana.collectionPostFix, self.rho )
216  setattr(event,"deltaMetFromJEC" +self.cfg_ana.collectionPostFix, self.deltaMetFromJEC )
217  setattr(event,"allJetsUsedForMET" +self.cfg_ana.collectionPostFix, self.allJetsUsedForMET )
218  setattr(event,"jets" +self.cfg_ana.collectionPostFix, self.jets )
219  setattr(event,"jetsFailId" +self.cfg_ana.collectionPostFix, self.jetsFailId )
220  setattr(event,"jetsAllNoID" +self.cfg_ana.collectionPostFix, self.jetsAllNoID )
221  setattr(event,"jetsIdOnly" +self.cfg_ana.collectionPostFix, self.jetsIdOnly )
222  setattr(event,"cleanJetsAll" +self.cfg_ana.collectionPostFix, self.cleanJetsAll )
223  setattr(event,"cleanJets" +self.cfg_ana.collectionPostFix, self.cleanJets )
224  setattr(event,"cleanJetsFwd" +self.cfg_ana.collectionPostFix, self.cleanJetsFwd )
225  setattr(event,"discardedJets" +self.cfg_ana.collectionPostFix, self.discardedJets )
226  setattr(event,"gamma_cleanJetsAll" +self.cfg_ana.collectionPostFix, self.gamma_cleanJetsAll )
227  setattr(event,"gamma_cleanJets" +self.cfg_ana.collectionPostFix, self.gamma_cleanJets )
228  setattr(event,"gamma_cleanJetsFwd" +self.cfg_ana.collectionPostFix, self.gamma_cleanJetsFwd )
229 
230 
231  if self.cfg_comp.isMC:
232  setattr(event,"cleanGenJets" +self.cfg_ana.collectionPostFix, self.cleanGenJets )
233  setattr(event,"bqObjects" +self.cfg_ana.collectionPostFix, self.bqObjects )
234  setattr(event,"cqObjects" +self.cfg_ana.collectionPostFix, self.cqObjects )
235  setattr(event,"partons" +self.cfg_ana.collectionPostFix, self.partons )
236  setattr(event,"heaviestQCDFlavour" +self.cfg_ana.collectionPostFix, self.heaviestQCDFlavour )
237  setattr(event,"deltaMetFromJetSmearing"+self.cfg_ana.collectionPostFix, self.deltaMetFromJetSmearing)
238  setattr(event,"genJets" +self.cfg_ana.collectionPostFix, self.genJets )
239 
240  return True
241 
242 
243 
244  def testJetID(self, jet):
245  jet.puJetIdPassed = jet.puJetId()
246  jet.pfJetIdPassed = jet.jetID('POG_PFID_Loose')
247  if self.cfg_ana.relaxJetId:
248  return True
249  else:
250  return jet.pfJetIdPassed and (jet.puJetIdPassed or not(self.doPuId))
251 
252  def testJetNoID( self, jet ):
253  # 2 is loose pile-up jet id
254  return jet.pt() > self.cfg_ana.jetPt and \
255  abs( jet.eta() ) < self.cfg_ana.jetEta;
256 
257  def computeQGvars(self, jet):
258 
259  jet.mult = 0
260  sum_weight = 0.
261  sum_pt = 0.
262  sum_deta = 0.
263  sum_dphi = 0.
264  sum_deta2 = 0.
265  sum_detadphi = 0.
266  sum_dphi2 = 0.
267 
268 
269 
270  for ii in range(0, jet.numberOfDaughters()) :
271 
272  part = jet.daughter(ii)
273 
274  if part.charge() == 0 : # neutral particles
275 
276  if part.pt() < 1.: continue
277 
278  else : # charged particles
279 
280  if part.trackHighPurity()==False: continue
281  if part.fromPV()<=1: continue
282 
283 
284  jet.mult += 1
285 
286  deta = part.eta() - jet.eta()
287  dphi = deltaPhi(part.phi(), jet.phi())
288  partPt = part.pt()
289  weight = partPt*partPt
290  sum_weight += weight
291  sum_pt += partPt
292  sum_deta += deta*weight
293  sum_dphi += dphi*weight
294  sum_deta2 += deta*deta*weight
295  sum_detadphi += deta*dphi*weight
296  sum_dphi2 += dphi*dphi*weight
297 
298 
299 
300 
301  a = 0.
302  b = 0.
303  c = 0.
304 
305  if sum_weight > 0 :
306  jet.ptd = math.sqrt(sum_weight)/sum_pt
307  ave_deta = sum_deta/sum_weight
308  ave_dphi = sum_dphi/sum_weight
309  ave_deta2 = sum_deta2/sum_weight
310  ave_dphi2 = sum_dphi2/sum_weight
311  a = ave_deta2 - ave_deta*ave_deta
312  b = ave_dphi2 - ave_dphi*ave_dphi
313  c = -(sum_detadphi/sum_weight - ave_deta*ave_dphi)
314  else: jet.ptd = 0.
315 
316  delta = math.sqrt(math.fabs((a-b)*(a-b)+4.*c*c))
317 
318  if a+b-delta > 0: jet.axis2 = -math.log(math.sqrt(0.5*(a+b-delta)))
319  else: jet.axis2 = -1.
320 
321 
322  def jetFlavour(self,event):
323  def isFlavour(x,f):
324  id = abs(x.pdgId())
325  if id > 999: return (id/1000)%10 == f
326  if id > 99: return (id/100)%10 == f
327  return id % 100 == f
328 
329 
330 
331  self.bqObjects = [ p for p in event.genParticles if (p.status() == 2 and isFlavour(p,5)) ]
332  self.cqObjects = [ p for p in event.genParticles if (p.status() == 2 and isFlavour(p,4)) ]
333 
334  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]) ) ]
335  match = matchObjectCollection2(self.cleanJetsAll,
336  self.partons,
337  deltaRMax = 0.3)
338 
339  for jet in self.cleanJetsAll:
340  parton = match[jet]
341  jet.partonId = (parton.pdgId() if parton != None else 0)
342  jet.partonMotherId = (parton.mother(0).pdgId() if parton != None and parton.numberOfMothers()>0 else 0)
343 
344  for jet in self.jets:
345  (bmatch, dr) = bestMatch(jet, self.bqObjects)
346  if dr < 0.4:
347  jet.mcFlavour = 5
348  else:
349  (cmatch, dr) = bestMatch(jet, self.cqObjects)
350  if dr < 0.4:
351  jet.mcFlavour = 4
352  else:
353  jet.mcFlavour = 0
354 
355  self.heaviestQCDFlavour = 5 if len(self.bqObjects) else (4 if len(self.cqObjects) else 1);
356 
357 
358  def matchJets(self, event, jets):
359  match = matchObjectCollection2(jets,
360  event.genbquarks + event.genwzquarks,
361  deltaRMax = 0.3)
362  for jet in jets:
363  gen = match[jet]
364  jet.mcParton = gen
365  jet.mcMatchId = (gen.sourceId if gen != None else 0)
366  jet.mcMatchFlav = (abs(gen.pdgId()) if gen != None else 0)
367 
368  match = matchObjectCollection2(jets,
369  self.genJets,
370  deltaRMax = 0.3)
371  for jet in jets:
372  jet.mcJet = match[jet]
373 
374 
375 
376  def smearJets(self, event, jets):
377  # https://twiki.cern.ch/twiki/bin/viewauth/CMS/TWikiTopRefSyst#Jet_energy_resolution
378  for jet in jets:
379  gen = jet.mcJet
380  if gen != None:
381  genpt, jetpt, aeta = gen.pt(), jet.pt(), abs(jet.eta())
382  # from https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution
383  factor = 1.052 + self.shiftJER*math.hypot(0.012,0.062);
384  if aeta > 2.3: factor = 1.288 + self.shiftJER*math.hypot(0.127,0.154)
385  elif aeta > 1.7: factor = 1.134 + self.shiftJER*math.hypot(0.035,0.066)
386  elif aeta > 1.1: factor = 1.096 + self.shiftJER*math.hypot(0.017,0.063)
387  elif aeta > 0.5: factor = 1.057 + self.shiftJER*math.hypot(0.012,0.056)
388  ptscale = max(0.0, (jetpt + (factor-1)*(jetpt-genpt))/jetpt)
389  #print "get with pt %.1f (gen pt %.1f, ptscale = %.3f)" % (jetpt,genpt,ptscale)
390  jet.deltaMetFromJetSmearing = [ -(ptscale-1)*jet.rawFactor()*jet.px(), -(ptscale-1)*jet.rawFactor()*jet.py() ]
391  if ptscale != 0:
392  jet.setP4(jet.p4()*ptscale)
393  # leave the uncorrected unchanged for sync
394  jet._rawFactorMultiplier *= (1.0/ptscale) if ptscale != 0 else 1
395  #else: print "jet with pt %.1d, eta %.2f is unmatched" % (jet.pt(), jet.eta())
396 
397 
398 
399 setattr(JetAnalyzer,"defaultConfig", cfg.Analyzer(
400  class_object = JetAnalyzer,
401  jetCol = 'slimmedJets',
402  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)
403  genJetCol = 'slimmedGenJets',
404  rho = ('fixedGridRhoFastjetAll','',''),
405  jetPt = 25.,
406  jetEta = 4.7,
407  jetEtaCentral = 2.4,
408  jetLepDR = 0.4,
409  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
410  cleanSelectedLeptons = True, #Whether to clean 'selectedLeptons' after disambiguation. Treat with care (= 'False') if running Jetanalyzer more than once
411  minLepPt = 10,
412  lepSelCut = lambda lep : True,
413  relaxJetId = False,
414  doPuId = False, # Not commissioned in 7.0.X
415  doQG = False,
416  recalibrateJets = False,
417  recalibrationType = "AK4PFchs",
418  shiftJEC = 0, # set to +1 or -1 to get +/-1 sigma shifts
419  smearJets = True,
420  shiftJER = 0, # set to +1 or -1 to get +/-1 sigma shifts
421  cleanJetsFromFirstPhoton = False,
422  cleanJetsFromTaus = False,
423  cleanJetsFromIsoTracks = False,
424  alwaysCleanPhotons = False,
425  jecPath = "",
426  cleanGenJetsFromPhoton = False,
427  collectionPostFix = ""
428  )
429 )
def bestMatch
Definition: deltar.py:139
deltaMetFromJEC
Read jets, if necessary recalibrate and shift MET.
Definition: JetAnalyzer.py:98
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"))