CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
PhotonAnalyzer.py
Go to the documentation of this file.
1 import operator
2 import itertools
3 import copy
4 import types
5 import re
6 
7 from ROOT import TLorentzVector
8 from ROOT import heppy
9 
10 from PhysicsTools.Heppy.analyzers.core.Analyzer import Analyzer
11 from PhysicsTools.HeppyCore.framework.event import Event
12 from PhysicsTools.HeppyCore.statistics.counter import Counter, Counters
13 from PhysicsTools.Heppy.analyzers.core.AutoHandle import AutoHandle
14 from PhysicsTools.Heppy.physicsobjects.Photon import Photon
15 
16 from PhysicsTools.HeppyCore.utils.deltar import deltaR, deltaPhi, bestMatch, matchObjectCollection3
17 
18 import PhysicsTools.HeppyCore.framework.config as cfg
19 
20 
21 class PhotonAnalyzer( Analyzer ):
22 
23 
24  def __init__(self, cfg_ana, cfg_comp, looperName ):
25  super(PhotonAnalyzer,self).__init__(cfg_ana,cfg_comp,looperName)
26  self.etaCentral = self.cfg_ana.etaCentral if hasattr(self.cfg_ana, 'etaCentral') else 9999
27  # footprint removed isolation
28  self.doFootprintRemovedIsolation = getattr(cfg_ana, 'doFootprintRemovedIsolation', False)
30  self.footprintRemovedIsolationPUCorr = self.cfg_ana.footprintRemovedIsolationPUCorr
32 
33  def declareHandles(self):
34  super(PhotonAnalyzer, self).declareHandles()
35  self.handles['rhoPhoton'] = AutoHandle( self.cfg_ana.rhoPhoton, 'double')
36 
37  #----------------------------------------
38  # DECLARATION OF HANDLES OF PHOTONS STUFF
39  #----------------------------------------
40 
41  self.handles['photons'] = AutoHandle( self.cfg_ana.photons,'std::vector<pat::Photon>')
42  self.mchandles['packedGen'] = AutoHandle( 'packedGenParticles', 'std::vector<pat::PackedGenParticle>' )
43  self.mchandles['prunedGen'] = AutoHandle( 'prunedGenParticles', 'std::vector<reco::GenParticle>' )
44 
45  self.handles['packedCandidates'] = AutoHandle( 'packedPFCandidates', 'std::vector<pat::PackedCandidate>')
46  self.handles['jets'] = AutoHandle( "slimmedJets", 'std::vector<pat::Jet>' )
47 
48 
49  def beginLoop(self, setup):
50  super(PhotonAnalyzer,self).beginLoop(setup)
51  self.counters.addCounter('events')
52  count = self.counters.counter('events')
53  count.register('all events')
54  count.register('has >=1 gamma at preselection')
55  count.register('has >=1 selected gamma')
56 
57  def makePhotons(self, event):
58  event.allphotons = map( Photon, self.handles['photons'].product() )
59  event.allphotons.sort(key = lambda l : l.pt(), reverse = True)
60 
61  event.selectedPhotons = []
62  event.selectedPhotonsCentral = []
63 
65  # values are taken from EGamma implementation: https://github.com/cms-sw/cmssw/blob/CMSSW_7_6_X/RecoEgamma/PhotonIdentification/plugins/PhotonIDValueMapProducer.cc#L198-L199
66  self.IsolationComputer.setPackedCandidates(self.handles['packedCandidates'].product(), -1, 0.1, 0.2)
67 
68  foundPhoton = False
69  for gamma in event.allphotons:
70  if gamma.pt() < self.cfg_ana.ptMin: continue
71  if abs(gamma.eta()) > self.cfg_ana.etaMax: continue
72  foundPhoton = True
73 
74  gamma.rho = float(self.handles['rhoPhoton'].product()[0])
75  # https://twiki.cern.ch/twiki/bin/view/CMS/CutBasedPhotonIdentificationRun2#Selection_implementation_details
76  if abs(gamma.eta()) < 1.0: gamma.EffectiveArea03 = [ 0.0234, 0.0053, 0.078 ]
77  elif abs(gamma.eta()) < 1.479: gamma.EffectiveArea03 = [ 0.0189, 0.0103, 0.0629 ]
78  elif abs(gamma.eta()) < 2.0: gamma.EffectiveArea03 = [ 0.0171, 0.0057, 0.0264 ]
79  elif abs(gamma.eta()) < 2.2: gamma.EffectiveArea03 = [ 0.0129, 0.0070, 0.0462 ]
80  elif abs(gamma.eta()) < 2.3: gamma.EffectiveArea03 = [ 0.0110, 0.0152, 0.0740 ]
81  elif abs(gamma.eta()) < 2.4: gamma.EffectiveArea03 = [ 0.0074, 0.0232, 0.0924 ]
82  else: gamma.EffectiveArea03 = [ 0.0035, 0.1709, 0.1484 ]
83 
86 
87  gamma.relIso = (max(gamma.chargedHadronIso()-gamma.rho*gamma.EffectiveArea03[0],0) + max(gamma.neutralHadronIso()-gamma.rho*gamma.EffectiveArea03[1],0) + max(gamma.photonIso() - gamma.rho*gamma.EffectiveArea03[2],0))/gamma.pt()
88 
89  def idWP(gamma,X):
90  """Create an integer equal to 1-2-3 for (loose,medium,tight)"""
91 
92  id=0
93  if gamma.photonID(X%"Loose"):
94  id=1
95  #if gamma.photonID(X%"Medium"):
96  # id=2
97  if gamma.photonID(X%"Tight"):
98  id=3
99  return id
100 
101  gamma.idCutBased = idWP(gamma, "PhotonCutBasedID%s")
102 
103 
104  keepThisPhoton = True
105 
106  if self.cfg_ana.gammaID=="PhotonCutBasedIDLoose_CSA14" or self.cfg_ana.gammaID=="PhotonCutBasedIDLoose_PHYS14" :
107  gamma.idCutBased = gamma.photonIDCSA14(self.cfg_ana.gammaID)
108  # we're keeing sigmaietaieta sidebands:
109  keepThisPhoton = gamma.photonIDCSA14(self.cfg_ana.gammaID, True)
110 
111  if gamma.hasPixelSeed():
112  keepThisPhoton = False
113  gamma.idCutBased = 0
114  elif "NoIso" in self.cfg_ana.gammaID:
115  idName = re.split('_NoIso',self.cfg_ana.gammaID)
116  keepThisPhoton = gamma.passPhotonID(idName[0],self.cfg_ana.conversionSafe_eleVeto)
117  basenameID = re.split('_looseSieie',idName[0])
118  gamma.idCutBased = gamma.passPhotonID(basenameID[0],self.cfg_ana.conversionSafe_eleVeto)
119  else:
120  # Reading from miniAOD directly
121  #keepThisPhoton = gamma.photonID(self.cfg_ana.gammaID)
122 
123  # implement cut based ID with CMGTools
124  keepThisPhoton = gamma.passPhotonID(self.cfg_ana.gammaID,self.cfg_ana.conversionSafe_eleVeto) and gamma.passPhotonIso(self.cfg_ana.gammaID,self.cfg_ana.gamma_isoCorr)
125 
126  if keepThisPhoton:
127  event.selectedPhotons.append(gamma)
128 
129  if keepThisPhoton and abs(gamma.eta()) < self.etaCentral:
130  event.selectedPhotonsCentral.append(gamma)
131 
132  event.selectedPhotons.sort(key = lambda l : l.pt(), reverse = True)
133  event.selectedPhotonsCentral.sort(key = lambda l : l.pt(), reverse = True)
134 
135  self.counters.counter('events').inc('all events')
136  if foundPhoton: self.counters.counter('events').inc('has >=1 gamma at preselection')
137  if len(event.selectedPhotons): self.counters.counter('events').inc('has >=1 selected gamma')
138 
139  def matchPhotons(self, event):
140  event.genPhotons = [ x for x in event.genParticles if x.status() == 1 and abs(x.pdgId()) == 22 ]
141  event.genPhotonsWithMom = [ x for x in event.genPhotons if x.numberOfMothers()>0 ]
142  event.genPhotonsWithoutMom = [ x for x in event.genPhotons if x.numberOfMothers()==0 ]
143  event.genPhotonsMatched = [ x for x in event.genPhotonsWithMom if abs(x.mother(0).pdgId())<23 or x.mother(0).pdgId()==2212 ]
144  match = matchObjectCollection3(event.allphotons, event.genPhotonsMatched, deltaRMax = 0.1)
145  matchNoMom = matchObjectCollection3(event.allphotons, event.genPhotonsWithoutMom, deltaRMax = 0.1)
146  packedGenParts = [ p for p in self.mchandles['packedGen'].product() if abs(p.eta()) < 3.1 ]
147  partons = [ p for p in self.mchandles['prunedGen'].product() if (p.status()==23 or p.status()==22) and abs(p.pdgId())<22 ]
148  for gamma in event.allphotons:
149  gen = match[gamma]
150  gamma.mcGamma = gen
151  if gen and gen.pt()>=0.5*gamma.pt() and gen.pt()<=2.*gamma.pt():
152  gamma.mcMatchId = 22
153  sumPt03 = 0.;
154  sumPt04 = 0.;
155  for part in packedGenParts:
156  if abs(part.pdgId())==12: continue # exclude neutrinos
157  if abs(part.pdgId())==14: continue
158  if abs(part.pdgId())==16: continue
159  if abs(part.pdgId())==18: continue
160  deltar = deltaR(gen.eta(), gen.phi(), part.eta(), part.phi())
161  if deltar <= 0.3:
162  sumPt03 += part.pt()
163  if deltar <= 0.4:
164  sumPt04 += part.pt()
165  sumPt03 -= gen.pt()
166  sumPt04 -= gen.pt()
167  if sumPt03<0. : sumPt03=0.
168  if sumPt04<0. : sumPt04=0.
169  gamma.genIso03 = sumPt03
170  gamma.genIso04 = sumPt04
171  # match to parton
172  deltaRmin = 999.
173  for p in partons:
174  deltar = deltaR(gen.eta(), gen.phi(), p.eta(), p.phi())
175  if deltar < deltaRmin:
176  deltaRmin = deltar
177  gamma.drMinParton = deltaRmin
178  else:
179  genNoMom = matchNoMom[gamma]
180  if genNoMom:
181  gamma.mcMatchId = 7
182  sumPt03 = 0.;
183  sumPt04 = 0.;
184  for part in packedGenParts:
185  if abs(part.pdgId())==12: continue # exclude neutrinos
186  if abs(part.pdgId())==14: continue
187  if abs(part.pdgId())==16: continue
188  if abs(part.pdgId())==18: continue
189  deltar = deltaR(genNoMom.eta(), genNoMom.phi(), part.eta(), part.phi())
190  if deltar <= 0.3:
191  sumPt03 += part.pt()
192  if deltar <= 0.4:
193  sumPt04 += part.pt()
194  sumPt03 -= genNoMom.pt()
195  sumPt04 -= genNoMom.pt()
196  if sumPt03<0. : sumPt03=0.
197  if sumPt04<0. : sumPt04=0.
198  gamma.genIso03 = sumPt03
199  gamma.genIso04 = sumPt04
200  # match to parton
201  deltaRmin = 999.
202  for p in partons:
203  deltar = deltaR(genNoMom.eta(), genNoMom.phi(), p.eta(), p.phi())
204  if deltar < deltaRmin:
205  deltaRmin = deltar
206  gamma.drMinParton = deltaRmin
207  else:
208  gamma.mcMatchId = 0
209  gamma.genIso03 = -1.
210  gamma.genIso04 = -1.
211  gamma.drMinParton = -1.
212 
213 
214 
215 
216 
217  def checkMatch( self, eta, phi, particles, deltar ):
218 
219  for part in particles:
220  if deltaR(eta, phi, part.eta(), part.phi()) < deltar:
221  return True
222 
223  return False
224 
225 
226 
227 
228 
229  def computeRandomCone( self, event, eta, phi, deltarmax, charged, jets, photons ):
230 
231  if self.checkMatch( eta, phi, jets, 2.*deltarmax ):
232  return -1.
233 
234  if self.checkMatch( eta, phi, photons, 2.*deltarmax ):
235  return -1.
236 
237  if self.checkMatch( eta, phi, event.selectedLeptons, deltarmax ):
238  return -1.
239 
240  iso = 0.
241 
242  for part in charged:
243  if deltaR(eta, phi, part.eta(), part.phi()) > deltarmax : continue
244  #if deltaR(eta, phi, part.eta(), part.phi()) < 0.02: continue
245  iso += part.pt()
246 
247  return iso
248 
249 
250 
251 
252 
253 
254  def randomCone( self, event ):
255 
256  patcands = self.handles['packedCandidates'].product()
257  jets = self.handles['jets'].product()
258 
259  charged = [ p for p in patcands if ( p.charge() != 0 and abs(p.pdgId())>20 and abs(p.dz())<=0.1 and p.fromPV()>1 and p.trackHighPurity() ) ]
260  photons10 = [ p for p in patcands if ( p.pdgId() == 22 and p.pt()>10. ) ]
261  jets20 = [ j for j in jets if j.pt() > 20 and abs(j.eta())<2.5 ]
262 
263  for gamma in event.allphotons:
264 
265  etaPhot = gamma.eta()
266  phiPhot = gamma.eta()
267  pi = 3.14159
268  phiRC = phiPhot + 0.5*pi
269  while phiRC>pi:
270  phiRC -= 2.*pi
271 
272 
273  gamma.chHadIsoRC03 = self.computeRandomCone( event, etaPhot, phiRC, 0.3, charged, jets20, photons10 )
274  gamma.chHadIsoRC04 = self.computeRandomCone( event, etaPhot, phiRC, 0.4, charged, jets20, photons10 )
275 
276 
277  #try other side
278  phiRC = phiPhot - 0.5*pi
279  while phiRC<-pi:
280  phiRC += 2.*pi
281 
282  if gamma.chHadIsoRC03<0. : gamma.chHadIsoRC03 = self.computeRandomCone( event, etaPhot, phiRC, 0.3, charged, jets20, photons10 )
283  if gamma.chHadIsoRC04<0. : gamma.chHadIsoRC04 = self.computeRandomCone( event, etaPhot, phiRC, 0.4, charged, jets20, photons10 )
284 
285 
287  # cone deltar=0.3
288  gamma.ftprAbsIsoCharged03 = self.IsolationComputer.chargedAbsIso(gamma.physObj, 0.3, 0, 0.0);
289  gamma.ftprAbsIsoPho03 = self.IsolationComputer.photonAbsIsoRaw( gamma.physObj, 0.3, 0, 0.0);
290  gamma.ftprAbsIsoNHad03 = self.IsolationComputer.neutralHadAbsIsoRaw( gamma.physObj, 0.3, 0, 0.0);
291  gamma.ftprAbsIso03 = gamma.ftprAbsIsoCharged03 + gamma.ftprAbsIsoPho03 + gamma.ftprAbsIsoNHad03
292  if self.cfg_ana.gamma_isoCorr == "rhoArea":
293  gamma.ftprAbsIso03 = (max(gamma.ftprAbsIsoCharged03-gamma.rho*gamma.EffectiveArea03[0],0) + max(gamma.ftprAbsIsoPho03-gamma.rho*gamma.EffectiveArea03[1],0) + max(gamma.ftprAbsIsoNHad03 - gamma.rho*gamma.EffectiveArea03[2],0))
294  elif self.cfg_ana.gamma_isoCorr != 'raw':
295  raise RuntimeError, "Unsupported gamma_isoCorr name '" + str(self.cfg_ana.gamma_isoCorr) + "'! For now only 'rhoArea', 'raw' are supported."
296  gamma.ftprRelIso03 = gamma.ftprAbsIso03/gamma.pt()
297 
298  def printInfo(self, event):
299  print '----------------'
300  if len(event.selectedPhotons)>0:
301  print 'lenght: ',len(event.selectedPhotons)
302  print 'gamma candidate pt: ',event.selectedPhotons[0].pt()
303  print 'gamma candidate eta: ',event.selectedPhotons[0].eta()
304  print 'gamma candidate phi: ',event.selectedPhotons[0].phi()
305  print 'gamma candidate mass: ',event.selectedPhotons[0].mass()
306  print 'gamma candidate HoE: ',event.selectedPhotons[0].hOVERe()
307  print 'gamma candidate r9: ',event.selectedPhotons[0].full5x5_r9()
308  print 'gamma candidate sigmaIetaIeta: ',event.selectedPhotons[0].full5x5_sigmaIetaIeta()
309  print 'gamma candidate had iso: ',event.selectedPhotons[0].chargedHadronIso()
310  print 'gamma candidate neu iso: ',event.selectedPhotons[0].neutralHadronIso()
311  print 'gamma candidate gamma iso: ',event.selectedPhotons[0].photonIso()
312  print 'gamma idCutBased',event.selectedPhotons[0].idCutBased
313 
314 
315  def process(self, event):
316  self.readCollections( event.input )
317  self.makePhotons(event)
318 # self.printInfo(event)
319 
320  if self.cfg_ana.do_randomCone:
321  self.randomCone(event)
322 
323  if not self.cfg_comp.isMC:
324  return True
325 
326  if self.cfg_ana.do_mc_match and hasattr(event, 'genParticles'):
327  self.matchPhotons(event)
328 
329 
330  return True
331 
332 
333 setattr(PhotonAnalyzer,"defaultConfig",cfg.Analyzer(
334  class_object=PhotonAnalyzer,
335  photons='slimmedPhotons',
336  ptMin = 20,
337  etaMax = 2.5,
338  gammaID = "PhotonCutBasedIDLoose_CSA14",
339  rhoPhoton = 'fixedGridRhoFastjetAll',
340  gamma_isoCorr = 'rhoArea',
341  # Footprint-removed isolation, removing all the footprint of the photon
342  doFootprintRemovedIsolation = False, # off by default since it requires access to all PFCandidates
343  packedCandidates = 'packedPFCandidates',
344  footprintRemovedIsolationPUCorr = 'rhoArea', # Allowed options: 'rhoArea', 'raw' (uncorrected)
345  conversionSafe_eleVeto = False,
346  do_mc_match = True,
347  do_randomCone = False,
348  )
349 )
350 
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
double deltaR(double eta1, double eta2, double phi1, double phi2)
Definition: TreeUtility.cc:17
def matchObjectCollection3
Definition: deltar.py:41