CMS 3D CMS Logo

DMRplotter.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import ROOT
4 import os
5 import sys
6 from decimal import Decimal
7 
8 class DMRplotter:
9  def __init__(self, args):
10  self.args = args
11  self.dataFiles = []
12  self.dataDirs = []
13  self.mcFiles = []
14  self.fileBaseName = "OfflineValidationSummary.root"
15  self.outputDir = self.args['outputDir']
16  self.cwd = os.getcwd()
17  self.objNameList = []
18  self.MCobjects = []
19  self.objNameListMC = []
20  self.segments = ["BPIX","FPIX","TEC","TID","TIB","TOB"]
21  self.varsX = {}
22  self.legendOffset = 1.5
23  self.legendTextSize = 0.032
24  self.statBoxTextSize = 0.0365
25  self.segmentTextOffset = {'ymin' : 0.9, 'ymax' : 1.2 }
27 
28  def __log__(self,log_type="",text=""):
29 
36 
37  v = int(sys.version_info[0])
38  source = "DMRplotter: "
39  text = str(text)
40  if v == 3:
41  if "i" in log_type:
42  print(source,"[INFO] ",text)
43  elif "n" in log_type:
44  print(" ",text)
45  elif "w" in log_type:
46  print(source,"[WARNING] ",text)
47  elif "e" in log_type:
48  print(source,"[ERROR] ",text)
49  elif "f" in log_type:
50  print(source,"[FATAL] ",text)
51  else:
52  print(text)
53 
54  def _replaceMulti(self, mainString, toBeReplaced, newString):
55 
59 
60  for elem in toBeReplaced:
61  if elem in mainString:
62  mainString = mainString.replace(elem, newString)
63  return mainString
64 
65  def _styledTPaveText(self,x1,y1,x2,y2,var):
66 
70 
71  textBox = ROOT.TPaveText(x1,y1,x2,y2)
72  textBox.SetFillColor(ROOT.kWhite)
73  if "median" not in var or not self.args['useFit']:
74  if self.args['showMeanError'] and self.args['showRMSError']:
75  textBox.SetTextSize(self.statBoxTextSize-0.008)
76  elif self.args['showMean'] and self.args['showRMS'] and (self.args['showMeanError'] or self.args['showRMSError']):
77  textBox.SetTextSize(self.statBoxTextSize-0.005)
78  else:
79  textBox.SetTextSize(self.statBoxTextSize)
80  else:
81  if self.args['useFitError']:
82  textBox.SetTextSize(self.statBoxTextSize-0.008)
83  else:
84  textBox.SetTextSize(self.statBoxTextSize-0.005)
85  textBox.SetTextFont(42)
86 
87  return textBox
88 
90 
94 
95  duplicity_check = False
96  if len(self.dataDirs) != 0:
97  if self.args['isDMR']:
98  #subdirectory
99  if not os.path.isdir(self.outputDir):
100  self.__log__("i","Creating subdirectory for single DMRs: "+self.outputDir)
101  os.system("mkdir "+self.outputDir)
102  else:
103  self.__log__("i","Results directory "+self.outputDir+" exists.")
104 
105  #per IoV directories/MC part directories
106  dirsToMake = []
107  for dataDir in self.dataDirs:
108  for root,dirs,files in os.walk(dataDir):
109  for dir in dirs:
110  if dir.startswith("offline"): dirsToMake.append(self.outputDir+"/"+dir)
111  for dir in dirsToMake:
112  if not os.path.isdir(dir):
113  os.system("mkdir "+dir)
114  else:
115  duplicity_check = True
116  else:
117  self.__log__("e","No input directory found! No DATA or MC present.")
118  sys.exit(0)
119 
120  if duplicity_check:
121  self.__log__("w","Duplicated file names found. Plots will be overwritten.")
122 
124 
128 
129  dataControl = True
130  for datafile in self.dataFiles:
131  if not os.path.isfile(datafile):
132  dataControl = False
133  for mcfile in self.MCobjects:
134  if not os.path.isfile(mcfile):
135  dataControl = False
136 
137  if dataControl and not (len(self.dataFiles) == 0 and len(self.MCobjects) == 0):
138  if not os.path.isdir(self.outputDir):
139  self.__log__("i","Final plots will be stored in: "+self.outputDir)
140  os.system("mkdir "+self.outputDir)
141  else:
142  self.__log__("i","Results directory "+self.outputDir+" exists.")
143  else:
144  self.__log__("f","Results file NOT found! No DATA or MC present.")
145  sys.exit(0)
146 
148 
151 
152  objDicts = {'DATA' : [], 'MC' : []}
153 
154  #DATA
155  for datafile in self.dataFiles:
156  if not os.path.isfile(datafile): continue
157  fInput = ROOT.TFile.Open(datafile,'READ')
158  keyList = ROOT.gDirectory.GetListOfKeys()
159  _id = [ id for id in datafile.split("/") if "offline_" in id ]
160  id = "0"
161  if len(_id) > 0:
162  id = str(_id[0].split("_")[-1])
163  objDict = {}
164  objList = []
165  objAreIgnored = []
166  _objNameList = []
167  for key in keyList:
168  obj = key.ReadObj()
169  if "TH1" in obj.ClassName():
170  objList.append(obj.Clone())
171  objName = obj.GetName()
172  #FIXME
173  skipHist = False
174  for tag in ["layer","disc","plus","minus"]:
175  if tag in objName: skipHist = True
176  if skipHist: continue
177  #END FIXME
178  if objName[-1] != "y":
179  generalObjName = self._replaceMulti(objName, [objName.split("_")[0]+"_","_"+objName.split("_")[-1]], "")
180  if len(self.args['objects']) == 0: #get different object names manually
181  if generalObjName not in _objNameList:
182  _objNameList.append(generalObjName)
183  else: #get different object names from user command input
184  if generalObjName not in self.args['objects']:
185  self.__log__("w","Object \""+generalObjName+"\" found but ignored for plotting!")
186  objAreIgnored.append(generalObjName)
187  else:
188  if generalObjName not in _objNameList:
189  _objNameList.append(generalObjName)
190  self.objNameList = [ genObjName for genObjName in _objNameList ]
191 
192  #now fill objects to the structured dictionary
193  for objName in self.objNameList:
194  objDict[objName] = []
195  for obj in objList:
196  if objName in obj.GetName():
197  segment = ""
198  var = ""
199  if obj.GetName()[-1] == "y":
200  segment = obj.GetName().split("_")[-2]
201  var = obj.GetName().split("_")[0]+"Y"
202  else:
203  segment = obj.GetName().split("_")[-1]
204  var = obj.GetName().split("_")[0]+"X"
205  obj.SetDirectory(0) #important to detach memory allocation
206  objDict[objName].append({ 'hist' : obj,
207  'segment' : segment,
208  'var' : var,
209  'id' : id,
210  'type' : "DATA"
211  })
212  fInput.Close()
213  objDicts['DATA'].append(objDict)
214 
215  #ensure plotting order
216  if len(self.args['objects']) != 0:
217  order = []
218  for genObjName in self.objNameList:
219  order.append(self.args['objects'].index(genObjName))
220  orderedList = [self.objNameList[i] for i in order]
221  self.objNameList = orderedList
222 
223  if len(self.objNameList) == 0 and len(self.dataFiles) !=0:
224  self.__log__("e","Data object names (if specified) must correspond to names in given input file!")
225  sys.exit(0)
226  else:
227  for genObjName in self.objNameList:
228  self.__log__("i","Object \""+genObjName+"\" found for plotting.")
229 
230  #MC
231  for mcFile in self.mcFiles:
232  fInputMC = ROOT.TFile.Open(mcFile,'READ')
233  keyListMC = ROOT.gDirectory.GetListOfKeys()
234  objListMC = []
235  objDictMC = {}
236  generalObjName = ""
237  objIsIgnored = False
238  for key in keyListMC:
239  obj = key.ReadObj()
240  if "TH1" in obj.ClassName():
241  objName = obj.GetName()
242  objListMC.append(obj.Clone(objName))
243  #FIXME
244  skipHist = False
245  for tag in ["layer","disc","plus","minus"]:
246  if tag in objName: skipHist = True
247  if skipHist: continue
248  #END FIXME
249  if objName[-1] != "y":
250  generalObjName = self._replaceMulti(objName, [objName.split("_")[0]+"_","_"+objName.split("_")[-1]], "")
251  if len(self.args['objects']) == 0: #get different object names manually
252  if generalObjName not in self.objNameListMC:
253  self.objNameListMC.append(generalObjName)
254  else: #get different object names from user command input
255  if generalObjName not in self.args['objects']:
256  self.__log__("w","Object \""+generalObjName+"\" found but ignored for plotting!")
257  objIsIgnored = True
258  else:
259  if generalObjName not in self.objNameListMC:
260  self.objNameListMC.append(generalObjName)
261 
262  #now fill MC objects to the structured dictionary
263  if not objIsIgnored:
264  objDictMC[generalObjName] = []
265  for obj in objListMC:
266  if generalObjName in obj.GetName():
267  segment = ""
268  var = ""
269  if obj.GetName()[-1] == "y":
270  segment = obj.GetName().split("_")[-2]
271  var = obj.GetName().split("_")[0]+"Y"
272  else:
273  segment = obj.GetName().split("_")[-1]
274  var = obj.GetName().split("_")[0]+"X"
275  obj.SetDirectory(0) #important to detach memory allocation
276  objDictMC[generalObjName].append({ 'hist' : obj,
277  'segment' : segment,
278  'var' : var,
279  'type' : "MC"
280  })
281  fInputMC.Close()
282  objDicts['MC'].append(objDictMC)
283 
284  if len(self.objNameListMC) == 0 and len(self.mcFiles) != 0:
285  self.__log__("e","MC object names (if specified) must correspond to names in given input file!")
286  sys.exit(0)
287  else:
288  for genObjName in self.objNameListMC:
289  self.__log__("i","Object \""+genObjName+"\" found for plotting.")
290 
291  return objDicts
292 
293  def __defineObjects__(self):
294 
300 
301  objDict = {}
302  for datafile in self.dataFiles:
303  fInput = ROOT.TFile.Open(datafile,'READ')
304  keyList = ROOT.gDirectory.GetListOfKeys()
305  objList = []
306  objAreIgnored = []
307  _objNameList = []
308  for key in keyList:
309  obj = key.ReadObj()
310  if "TH1" in obj.ClassName():
311  objList.append(obj.Clone())
312  objName = obj.GetName()
313  #FIXME if you want to average also subsegment histos
314  skipHist = False
315  for tag in ["layer","disc","plus","minus"]:
316  if tag in objName: skipHist = True
317  if skipHist: continue
318  #END FIXME
319  if objName[-1] != "y":
320  generalObjName = self._replaceMulti(objName, [objName.split("_")[0]+"_","_"+objName.split("_")[-1]], "")
321  if len(self.args['objects']) == 0: #get different object names manually
322  if generalObjName not in _objNameList:
323  _objNameList.append(generalObjName)
324  else: #get different object names from user command input
325  if generalObjName not in self.args['objects']:
326  self.__log__("w","Object \""+generalObjName+"\" found but ignored for plotting!")
327  objAreIgnored.append(generalObjName)
328  else:
329  if generalObjName not in _objNameList:
330  _objNameList.append(generalObjName)
331  duplicates = [ genObjName for genObjName in _objNameList if genObjName in self.objNameList ]
332  for dup in duplicates:
333  self.__log__("e","Duplicated object "+str(dup)+" was found! Please rename this object in your input file!")
334  sys.exit(0)
335  self.objNameList += [ genObjName for genObjName in _objNameList if genObjName not in self.objNameList ]
336 
337  #now fill objects to the structured dictionary
338  for objName in _objNameList:
339  if objName in objAreIgnored: continue
340  objDict[objName] = []
341  for obj in objList:
342  if objName in obj.GetName():
343  segment = ""
344  var = ""
345  if obj.GetName()[-1] == "y":
346  segment = obj.GetName().split("_")[-2]
347  var = obj.GetName().split("_")[0]+"Y"
348  else:
349  segment = obj.GetName().split("_")[-1]
350  var = obj.GetName().split("_")[0]+"X"
351  obj.SetDirectory(0) #important to detach memory allocation
352  objDict[objName].append({ 'hist' : obj,
353  'segment' : segment,
354  'var' : var,
355  'type' : "DATA"
356  })
357  fInput.Close()
358 
359  #ensure plotting order
360  '''
361  if len(self.args['objects']) != 0:
362  order = []
363  for genObjName in self.objNameList:
364  order.append(self.args['objects'].index(genObjName))
365  orderedList = [self.objNameList[i] for i in order]
366  self.objNameList = orderedList
367  '''
368 
369  if len(self.objNameList) == 0 and len(self.dataFiles) !=0:
370  self.__log__("e","Data object names (if specified) must correspond to names in given input file!")
371  sys.exit(0)
372  else:
373  for genObjName in self.objNameList:
374  self.__log__("i","Object \""+genObjName+"\" found for plotting.")
375 
376  #add MC objects
377  for MCobject in self.MCobjects:
378  fInputMC = ROOT.TFile.Open(MCobject,'READ')
379  keyListMC = ROOT.gDirectory.GetListOfKeys()
380  objListMC = []
381  #generalObjName = ""
382  #objIsIgnored = False
383  objAreIgnored = []
384  _objNameList = []
385  for key in keyListMC:
386  obj = key.ReadObj()
387  if "TH1" in obj.ClassName():
388  objName = obj.GetName()
389  objListMC.append(obj.Clone(objName))
390  #FIXME
391  skipHist = False
392  for tag in ["layer","disc","plus","minus"]:
393  if tag in objName: skipHist = True
394  if skipHist: continue
395  #END FIXME
396  if objName[-1] != "y":
397  generalObjName = self._replaceMulti(objName, [objName.split("_")[0]+"_","_"+objName.split("_")[-1]], "")
398  if len(self.args['objects']) == 0: #get different object names manually
399  if generalObjName not in _objNameList:
400  _objNameList.append(generalObjName)
401  else: #get different object names from user command input
402  if generalObjName not in self.args['objects']:
403  self.__log__("w","Object \""+generalObjName+"\" found but ignored for plotting!")
404  objAreIgnored.append(generalObjName)
405  else:
406  if generalObjName not in _objNameList:
407  _objNameList.append(generalObjName)
408  duplicates = [ genObjName for genObjName in _objNameList if genObjName in self.objNameListMC ]
409  for dup in duplicates:
410  self.__log__("e","Duplicated object "+str(dup)+" was found! Please rename this object in your input file!")
411  sys.exit(0)
412  self.objNameListMC += [ genObjName for genObjName in _objNameList if genObjName not in self.objNameListMC ]
413 
414  #now fill MC objects to the structured dictionary
415  for objName in _objNameList:
416  if objName in objAreIgnored: continue
417  objDict[objName] = []
418  for obj in objListMC:
419  if objName in obj.GetName():
420  segment = ""
421  var = ""
422  if obj.GetName()[-1] == "y":
423  segment = obj.GetName().split("_")[-2]
424  var = obj.GetName().split("_")[0]+"Y"
425  else:
426  segment = obj.GetName().split("_")[-1]
427  var = obj.GetName().split("_")[0]+"X"
428  obj.SetDirectory(0) #important to detach memory allocation
429  objDict[objName].append({ 'hist' : obj,
430  'segment' : segment,
431  'var' : var,
432  'type' : "MC"
433  })
434  fInputMC.Close()
435 
436  if len(self.objNameListMC) == 0 and len(self.MCobjects) != 0:
437  self.__log__("e","MC object names (if specified) must correspond to names in given input file!")
438  sys.exit(0)
439  else:
440  for genObjName in self.objNameListMC:
441  self.__log__("i","Object \""+genObjName+"\" found for plotting.")
442 
443  #ensure plotting order
444  self.objNameList += self.objNameListMC
445  if len(self.args['objects']) != 0:
446  order = []
447  for genObjName in self.objNameList:
448  order.append(self.args['objects'].index(genObjName))
449  orderedList = [self.objNameList[i] for i in order]
450  self.objNameList = orderedList
451  return objDict
452 
453  def __fitGauss__(self,hist):
454 
459 
460  if not hist or hist.GetEntries() < 20: return 0
461  self.__log__("i","Fitting histogram: "+hist.GetName())
462 
463  xScale = 10000.
464  mean = hist.GetMean(1)*xScale
465  sigma = hist.GetRMS(1)*xScale
466  funcName = "gaussian_"+hist.GetName()
467  func = ROOT.TF1(funcName,"gaus",mean - 2.*sigma,mean + 2.*sigma)
468  func.SetLineColor(ROOT.kMagenta)
469  func.SetLineStyle(2)
470 
471  #first fit
472  if int(hist.Fit(func,"QNR")) == 0:
473  mean = func.GetParameter(1)
474  sigma = func.GetParameter(2)
475  func.SetRange(mean - 3.*sigma, mean + 3.*sigma)
476  # I: Integral gives more correct results if binning is too wide
477  # L: Likelihood can treat empty bins correctly (if hist not weighted...)
478  #second fit
479  if int(hist.Fit(func,"Q0ILR")) == 0:
480  return func
481  else:
482  return 0
483  else:
484  return 0
485 
486  def __getStat__(self,hist,var):
487 
493 
494  statLabel = ""
495  delimeter = ""
496  muScale = 1.
497  muUnit = ""
498  form = "{:.2g}"
499  formScie = "{:.1e}"
500  if "median" in var:
501  muScale = 10000.
502  muUnit = " #mum"
503  if not self.args['useFit'] or "median" not in var:
504  if self.args['showMean'] and self.args['showRMS']:
505  delimeter = ", "
506  if self.args['showMean']:
507  statLabel += "#mu="
508  if hist.GetMean(1) >= 0.:
509  statLabel += (" "+form).format(Decimal(str(hist.GetMean(1)*muScale)))
510  else:
511  statLabel += form.format(Decimal(str(hist.GetMean(1)*muScale)))
512  if self.args['showMeanError']:
513  statLabel += " #pm "
514  statLabel += formScie.format(Decimal(str(hist.GetMeanError(1)*muScale)))
515  statLabel += delimeter
516  if self.args['showRMS']:
517  statLabel += "rms="
518  statLabel += (" "+form).format(Decimal(str(hist.GetRMS(1)*muScale)))
519  if self.args['showRMSError']:
520  statLabel += " #pm "
521  statLabel += form.format(Decimal(str(hist.GetRMSError(1)*muScale)))
522  statLabel += muUnit
523  else:
524  fitResults = self.__fitGauss__(hist)
525  if not isinstance(fitResults, int):
526  delimeter = ", "
527  meanFit = fitResults.GetParameter(1)
528  meanFitError = fitResults.GetParError(1)
529  sigmaFit = fitResults.GetParameter(2)
530  sigmaFitError = fitResults.GetParError(2)
531  statLabel += "#mu="
532  if meanFit >= 0.:
533  statLabel += (" "+formScie).format(Decimal(str(meanFit)))
534  if self.args['useFitError']:
535  statLabel += " #pm "
536  statLabel += form.format(Decimal(str(meanFitError)))
537  else:
538  statLabel += formScie.format(Decimal(str(meanFit)))
539  if self.args['useFitError']:
540  statLabel += " #pm "
541  statLabel += form.format(Decimal(str(meanFitError)))
542  statLabel += delimeter
543  statLabel += "#sigma="
544  statLabel += (" "+form).format(Decimal(str(sigmaFit)))
545  if self.args['useFitError']:
546  statLabel += " #pm "
547  statLabel += form.format(Decimal(str(sigmaFitError)))
548  statLabel += muUnit
549 
550  return statLabel
551 
552  def __setTHStyle__(self,objects):
553 
556 
557  #define DMR-specific properties
558  varsX = {'medianX' : "median(x\'_{pred}-x\'_{hit})[#mum]",
559  'medianY' : "median(y\'_{pred}-y\'_{hit})[#mum]",
560  'DrmsNRX' : "RMS((x\'_{pred}-x\'_{hit})/#sigma)",
561  'DrmsNRY' : "RMS((y\'_{pred}-y\'_{hit})/#sigma)"
562  }
563  self.varsX = varsX
564  varsY = {'medianX' : "luminosity-weighted number of modules",
565  'medianY' : "luminosity-weighted number of modules",
566  'DrmsNRX' : "luminosity-weighted number of modules",
567  'DrmsNRY' : "luminosity-weighted number of modules"
568  }
569  limitX = {'min' : 10000, 'max' : 10000}
570 
571  #set specific style for DMRs
572  for objName,objList in objects.items():
573  for obj in objList:
574  #axis
575  scaleFactor =""
576  obj['hist'].GetXaxis().SetTitle(varsX[obj['var']])
577  obj['hist'].GetXaxis().SetTitleFont(obj['hist'].GetYaxis().GetTitleFont())
578  obj['hist'].GetYaxis().SetTitleSize(0.038)
579  obj['hist'].GetYaxis().SetTitleOffset(1.7)
580  if "median" in obj['var']:
581  scaleFactor ="/"+'{:.2f}'.format((obj['hist'].GetXaxis().GetXmax()*limitX['max']-obj['hist'].GetXaxis().GetXmin()*limitX['min'])/obj['hist'].GetXaxis().GetNbins())+" #mum"
582  minX = obj['hist'].GetXaxis().GetXmin()
583  maxX = obj['hist'].GetXaxis().GetXmax()
584  obj['hist'].GetXaxis().SetLimits(minX*limitX['min'],maxX*limitX['max'])
585  obj['hist'].GetYaxis().SetTitle(varsY[obj['var']]+scaleFactor)
586 
587  #main title
588  obj['hist'].SetTitle("")
589 
590  #line color & style
591  if len(self.args['objects']) != 0:
592  if obj['type'] == "MC":
593  obj['hist'].SetLineColor(self.args['colors'][self.args['objects'].index(objName)])
594  obj['hist'].SetLineStyle(self.args['styles'][self.args['objects'].index(objName)])
595  obj['hist'].SetLineWidth(3) #2
596  elif obj['type'] == "DATA":
597  obj['hist'].SetMarkerColor(self.args['colors'][self.args['objects'].index(objName)])
598  obj['hist'].SetLineColor(self.args['colors'][self.args['objects'].index(objName)])
599  obj['hist'].SetMarkerStyle(self.args['styles'][self.args['objects'].index(objName)])
600  obj['hist'].SetMarkerSize(1.5)
601 
602  #set general style for DMRs
603  tStyle = ROOT.TStyle("StyleCMS","Style CMS")
604 
605  #zero horizontal error bars
606  tStyle.SetErrorX(0)
607 
608  #canvas settings
609  tStyle.SetCanvasBorderMode(0)
610  tStyle.SetCanvasColor(ROOT.kWhite)
611  tStyle.SetCanvasDefH(800) #800
612  tStyle.SetCanvasDefW(800)
613  tStyle.SetCanvasDefX(0)
614  tStyle.SetCanvasDefY(0)
615 
616  #frame settings
617  tStyle.SetFrameBorderMode(0)
618  tStyle.SetFrameBorderSize(10)
619  tStyle.SetFrameFillColor(ROOT.kBlack)
620  tStyle.SetFrameFillStyle(0)
621  tStyle.SetFrameLineColor(ROOT.kBlack)
622  tStyle.SetFrameLineStyle(0)
623  tStyle.SetFrameLineWidth(1)
624  tStyle.SetLineWidth(2)
625 
626  #pad settings
627  tStyle.SetPadBorderMode(0)
628  tStyle.SetPadColor(ROOT.kWhite)
629  tStyle.SetPadGridX(False)
630  tStyle.SetPadGridY(False)
631  tStyle.SetGridColor(0)
632  tStyle.SetGridStyle(3)
633  tStyle.SetGridWidth(1)
634 
635  #margins
636  tStyle.SetPadTopMargin(0.08)
637  tStyle.SetPadBottomMargin(0.13)
638  tStyle.SetPadLeftMargin(0.16)
639  tStyle.SetPadRightMargin(0.05)
640 
641  #common histogram settings
642  tStyle.SetHistLineStyle(0)
643  tStyle.SetHistLineWidth(3)
644  tStyle.SetMarkerSize(0.8)
645  tStyle.SetEndErrorSize(4)
646  tStyle.SetHatchesLineWidth(1)
647 
648  #stat box
649  tStyle.SetOptFile(0)
650 
651  #axis settings
652  tStyle.SetAxisColor(1,"XYZ")
653  tStyle.SetTickLength(0.03,"XYZ")
654  tStyle.SetNdivisions(510,"XYZ")
655  tStyle.SetPadTickX(1)
656  tStyle.SetPadTickY(1)
657  tStyle.SetStripDecimals(ROOT.kFALSE)
658 
659  #axis labels and titles
660  tStyle.SetTitleColor(1,"XYZ")
661  tStyle.SetLabelColor(1,"XYZ")
662  tStyle.SetLabelFont(42,"XYZ")
663  tStyle.SetLabelOffset(0.007,"XYZ")
664  tStyle.SetLabelSize(0.04,"XYZ")
665  tStyle.SetTitleFont(42,"XYZ")
666  tStyle.SetTitleSize(0.047,"XYZ")
667  tStyle.SetTitleXOffset(1.2)
668  tStyle.SetTitleYOffset(1.7)
669 
670  #legend
671  tStyle.SetLegendBorderSize(0)
672  tStyle.SetLegendTextSize(self.legendTextSize)
673  tStyle.SetLegendFont(42)
674 
675  #assign changes to gROOT current style
676  tStyle.cd()
677 
678  return tStyle
679 
680  def __beautify__(self, canvas, CMSextraLabel, eraLabel):
681 
684 
685  leftMargin = canvas.GetLeftMargin()
686  rightMargin = canvas.GetRightMargin()
687  topMargin = canvas.GetTopMargin()
688  canvas.cd()
689 
690  #CMStext
691  CMSlabel = "CMS"
692  CMSextraOffset = 0.10
693  CMStext = ROOT.TLatex()
694  CMSextra = ROOT.TLatex()
695 
696  CMStext.SetNDC()
697  CMSextra.SetNDC()
698 
699  CMStext.SetTextAngle(0)
700  CMSextra.SetTextAngle(0)
701 
702  CMStext.SetTextColor(ROOT.kBlack)
703  CMSextra.SetTextColor(ROOT.kBlack)
704 
705  CMStext.SetTextFont(61)
706  CMSextra.SetTextFont(52)
707 
708  CMStext.SetTextAlign(11)
709  CMStext.SetTextSize(0.045)
710  CMSextra.SetTextSize(0.035)
711 
712  CMStext.DrawLatex(leftMargin,1.-topMargin+0.01,CMSlabel)
713  CMSextra.DrawLatex(leftMargin+CMSextraOffset,1-topMargin+0.01,CMSextraLabel)
714 
715  #Era text
716  eraText = ROOT.TLatex()
717  eraText.SetNDC()
718  eraText.SetTextAngle(0)
719  eraText.SetTextColor(ROOT.kBlack)
720  eraText.SetTextFont(42)
721  eraText.SetTextAlign(33)
722  eraText.SetTextSize(0.035)
723  eraText.DrawLatex(1.-rightMargin,1.-topMargin+0.035,eraLabel)
724 
725  #Redraw axis
726  canvas.RedrawAxis()
727 
728  def __cleanSingle__(self):
729 
733 
734  for dirpath,dirs,files in os.walk(self.cwd):
735  if dirpath != self.cwd: continue
736  for n_file in files:
737  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
738  self.__log__("i","File "+n_file+" was created.")
739  os.system("mv "+n_file+" "+self.outputDir)
740  self.__log__("i","Done.")
741 
742  def __clean__(self):
743 
747 
748  for datafile in self.dataFiles:
749  os.system("mv "+datafile+" "+self.outputDir)
750  for mcfile in self.MCobjects:
751  os.system("mv "+mcfile+" "+self.outputDir)
752  for dirpath,dirs,files in os.walk(self.cwd):
753  if dirpath != self.cwd: continue
754  for n_file in files:
755  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
756  self.__log__("i","File "+n_file+" was created.")
757  os.system("mv "+n_file+" "+self.outputDir)
758  self.__log__("i","Done.")
759 
760  def __finalize__(self):
761 
764 
765  for dirpath,dirs,files in os.walk(self.outputDir):
766  for n_file in files:
767  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
768  self.__log__("i","File "+n_file+" was created.")
769  self.__log__("i","Done.")
770 
771 
772  def addDATA(self,filename):
773 
776  if os.path.isfile(str(filename)):
777  self.__log__("i","DATA file: "+str(filename)+" was added for plotting.")
778  self.dataFiles.append(str(filename))
779  elif os.path.isfile(os.path.join(str(filename),self.fileBaseName)):
780  self.__log__("i","DATA file: "+os.path.join(str(filename),self.fileBaseName)+" was added for plotting.")
781  self.dataFiles.append(os.path.join(str(filename),self.fileBaseName))
782  else:
783  self.__log__("w","DATA file: "+os.path.join(str(filename),self.fileBaseName)+" NOT found.")
784 
785  def addDirDATA(self, dataDir):
786 
789  if os.path.isdir(dataDir):
790  self.__log__("i","DATA dir: "+dataDir+" was added for plotting.")
791  self.dataDirs.append(dataDir)
792  else:
793  self.__log__("w","DATA dir: "+dataDir+" NOT found.")
794 
795  #Create list of dataFiles #FIXME for multiple DATA inputs
796  if len(self.dataDirs) != 0:
797  if self.args['isDMR']:
798  for dataDir in self.dataDirs:
799  for root,dirs,files in os.walk(dataDir):
800  for dir in dirs:
801  if dir.startswith("offline"):
802  self.dataFiles.append(dataDir+"/"+dir+"/ExtendedOfflineValidation_Images/OfflineValidationSummary.root")
803 
804  def addDirMC(self, mcDir):
805 
808  if os.path.isdir(mcDir):
809  self.__log__("i","MC dir: "+mcDir+" was added for plotting.")
810  nFiles = 0
811  for dirpath,dirs,files in os.walk(mcDir):
812  for file in files:
813  if self.fileBaseName.replace(".root","") in file and file.endswith(".root"):
814  self.__log__("i","MC file: "+str(file)+" was added for plotting.")
815  self.MCobjects.append(os.path.join(dirpath,file))
816  nFiles += 1
817  if nFiles == 0:
818  self.__log__("w","No MC file found in "+str(mcDir)+".")
819  else:
820  self.__log__("w","MC dir: "+mcDir+" NOT found.")
821 
822  def addMC(self,filename):
823 
826  if os.path.isfile(str(filename)):
827  self.__log__("i","MC file: "+str(filename)+" was added for plotting.")
828  self.MCobjects.append(str(filename))
829  elif os.path.isfile(os.path.join(str(filename),self.fileBaseName)):
830  self.__log__("i","MC file: "+os.path.join(str(filename),self.fileBaseName)+" was added for plotting.")
831  self.MCobjects.append(os.path.join(str(filename),self.fileBaseName))
832  else:
833  self.__log__("w","MC file: "+str(os.path.join(str(filename),self.fileBaseName))+" NOT found.")
834 
835  def plotSingle(self):
836 
839 
840  #check for input file and create output dir
842 
843  #access histograms in rootfiles, select different validation objects and store them separately
844  objects = self.__defineSingleObjects__()
845  objectsData = objects['DATA']
846  objectsMC = objects['MC']
847 
848  #set histogram style
849  for objDict in objectsData:
850  self.__setTHStyle__(objDict)
851  for objDict in objectsMC:
852  self.__setTHStyle__(objDict)
853 
854  #really plot
855  ROOT.gROOT.SetBatch(True) #turn off printing canvas on screen
856  ROOT.gROOT.ProcessLine("gErrorIgnoreLevel = 1001;") #turn off printing messages on terminal
857 
858  for objDict in objectsData:
859  for segment in self.segments:
860  for var in self.varsX:
861  id = "0"
862  for key in objDict.keys():
863  for _obj in objDict[key]:
864  id = _obj['id']
865  canvas = ROOT.TCanvas(id+"_"+var+"_"+segment)
866  canvas.cd()
867 
868  #set labels positioning
869  segmentText = {'text' : segment, 'xmin' : 0.0, 'xmax' : 0.0}
870  statText = {'xmin' : 0.0, 'xmax' : 0.0}
871  if "median" in var:
872  segmentText['xmin'] = 2.5
873  segmentText['xmax'] = 3.5
874  statText['xmin'] = 0.20
875  statText['xmax'] = 0.85
876  else:
877  segmentText['xmin'] = 1.4
878  segmentText['xmax'] = 1.6
879  statText['xmin'] = 0.65
880  statText['xmax'] = 0.95
881 
882  #order plots & prepare y-axis scale factors
883  isEmpty = True
884  maxY = 0.0
885  objGroup = []
886  for objName in self.objNameList: #follow plotting order
887  for obj in objDict[objName]:
888  if obj['var'] == var and obj['segment'] == segment:
889  if obj['hist'].GetBinContent(obj['hist'].GetMaximumBin()) >= maxY:
890  maxY = obj['hist'].GetBinContent(obj['hist'].GetMaximumBin())
891  isEmpty = False
892  legendLabel = objName.replace("_"," ")
893  if len(self.args['labels']) != 0:
894  legendLabel = self.args['labels'][self.args['objects'].index(objName)]
895 
896  #Add MC for each data file
897  histsMC = []
898  labelsMC = []
899  statsMC = []
900  for objDictMC in objectsMC:
901  for objNameMC in self.objNameListMC:
902  for objMC in objDictMC[objNameMC]:
903  if objMC['var'] == var and objMC['segment'] == segment:
904  if objMC['hist'].GetBinContent(objMC['hist'].GetMaximumBin()) >= maxY:
905  maxY = objMC['hist'].GetBinContent(objMC['hist'].GetMaximumBin())
906  legendLabelMC = objNameMC.replace("_"," ")
907  if len(self.args['labels']) != 0:
908  legendLabelMC = self.args['labels'][self.args['objects'].index(objNameMC)]
909  objMC['hist'].SetDirectory(0)
910  histsMC.append(objMC['hist'])
911  labelsMC.append(legendLabelMC)
912  statsMC.append(self.__getStat__(objMC['hist'],var))
913  objGroup.append({'hist' : obj['hist'],
914  'histsMC' : histsMC,
915  'labelsMC': labelsMC,
916  'statsMC' : statsMC,
917  'label' : legendLabel,
918  'stat' : self.__getStat__(obj['hist'],var)
919  })
920  #draw & save
921  if not isEmpty:
922  datasetType = "singlemuon" #FIXME make it an option
923  legMinY = (1./self.legendOffset)+(1.-1./self.legendOffset)*(self.maxEntriesPerColumn-len(objGroup))/(self.maxEntriesPerColumn*3)
924  nColumns = 1
925  if len(objGroup) > self.maxEntriesPerColumn:
926  nColumns = 2
927  legMinY = 1./self.legendOffset
928  leg = ROOT.TLegend(0.08,legMinY,0.45,0.88)
929  leg.SetNColumns(nColumns)
930  seg = ROOT.TLatex()
931  maxX = objGroup[0]['hist'].GetXaxis().GetXmax()
932  stat = self._styledTPaveText(maxX*statText['xmin'],(legMinY+0.025)*self.legendOffset*maxY,maxX*statText['xmax'],0.95*self.legendOffset*maxY,var)
933  for igroup,group in enumerate(objGroup):
934  group['hist'].GetYaxis().SetRangeUser(0,self.legendOffset*maxY)
935  leg.AddEntry(group['hist'],group['label'],"l")
936  stat.AddText(group['stat'])
937  group['hist'].Draw("HISTSAME")
938  #for last data group add also MC
939  if igroup == len(objGroup)-1:
940  for ihist,histmc in enumerate(group['histsMC']):
941  leg.AddEntry(histmc,group['labelsMC'][ihist],"l")
942  stat.AddText(group['statsMC'][ihist])
943  histmc.Draw("HISTSAME")
944  leg.Draw("SAME")
945  seg.DrawLatex(segmentText['xmin'],self.segmentTextOffset['ymin']*maxY,segmentText['text'])
946  stat.Draw("SAME")
947  self.__beautify__(canvas,self.args['CMSlabel'],self.args['Rlabel'])
948  canvas.SaveAs(self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment+".png")
949  canvas.SaveAs(self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment+".pdf")
950  self.__log__("i","Saving "+self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment)
951 
952  #finalize
953  #self.__cleanSingle__()
954  self.__log__("i","Done.")
955 
956  def plot(self):
957 
960 
961  #check for input file and create output dir if needed
963 
964  #access histograms in rootfiles, select different validation objects and store them separately
965  objects = self.__defineObjects__()
966 
967  #set histogram style
968  currentStyle = self.__setTHStyle__(objects) #NOTE: for CMSSW_11 and higher, currentStyle must be returned to plotting function
969 
970  #really plot
971  ROOT.gROOT.SetBatch(True) #turn off printing canvas on screen
972  ROOT.gROOT.ProcessLine("gErrorIgnoreLevel = 1001;") #turn off printing messages on terminal
973 
974  for segment in self.segments:
975  for var in self.varsX:
976  canvas = ROOT.TCanvas(var+"_"+segment)
977  canvas.cd()
978 
979  #set labels positioning
980  segmentText = {'text' : segment, 'xmin' : 0.0, 'xmax' : 0.0}
981  statText = {'xmin' : 0.0, 'xmax' : 0.0}
982  if "median" in var:
983  segmentText['xmin'] = 2.5
984  segmentText['xmax'] = 3.5
985  statText['xmin'] = 0.27
986  statText['xmax'] = 0.92
987  else:
988  segmentText['xmin'] = 1.4
989  segmentText['xmax'] = 1.6
990  statText['xmin'] = 0.75
991  statText['xmax'] = 0.95
992 
993  #order plots & prepare y-axis scale factors
994  isEmpty = True
995  maxY = 0.0
996  objGroup = []
997  for objName in self.objNameList: #follow plotting order
998  for obj in objects[objName]:
999  if obj['var'] == var and obj['segment'] == segment:
1000  if obj['hist'].GetBinContent(obj['hist'].GetMaximumBin()) >= maxY:
1001  maxY = obj['hist'].GetBinContent(obj['hist'].GetMaximumBin())
1002  isEmpty = False
1003  legendLabel = objName.replace("_"," ")
1004  if len(self.args['labels']) != 0:
1005  legendLabel = self.args['labels'][self.args['objects'].index(objName)]
1006  drawStyle = ""
1007  legStyle = ""
1008  if obj['type'] == "MC":
1009  drawStyle = "HIST SAME"
1010  legStyle = "l"
1011  if obj['type'] == "DATA":
1012  drawStyle += "P HIST SAME"
1013  legStyle = "p"
1014  objGroup.append({'hist' : obj['hist'],
1015  'label' : legendLabel,
1016  'stat' : self.__getStat__(obj['hist'],var),
1017  'drawStyle' : drawStyle,
1018  'legStyle' : legStyle
1019  })
1020 
1021  #draw & save
1022  if not isEmpty:
1023  legMinY = (1./self.legendOffset)+(1.-1./self.legendOffset)*(self.maxEntriesPerColumn-len(objGroup))/(self.maxEntriesPerColumn*3)
1024  nColumns = 1
1025  if len(objGroup) > self.maxEntriesPerColumn:
1026  nColumns = 2
1027  legMinY = 1./self.legendOffset
1028  leg = ROOT.TLegend(0.20,legMinY,0.50,0.88)
1029  leg.SetNColumns(nColumns)
1030  seg = ROOT.TLatex()
1031  maxX = objGroup[0]['hist'].GetXaxis().GetXmax()
1032  stat = self._styledTPaveText(maxX*statText['xmin'],(legMinY+0.025)*self.legendOffset*maxY,maxX*statText['xmax'],0.95*self.legendOffset*maxY,var)
1033  for group in objGroup:
1034  group['hist'].GetYaxis().SetRangeUser(0,self.legendOffset*maxY)
1035  leg.AddEntry(group['hist'],group['label'],group['legStyle'])
1036  stat.AddText(group['stat'])
1037  group['hist'].Draw(group['drawStyle'])
1038  leg.Draw("SAME")
1039  seg.DrawLatex(segmentText['xmin'],self.segmentTextOffset['ymin']*maxY,segmentText['text'])
1040  stat.Draw("SAME")
1041  self.__beautify__(canvas,self.args['CMSlabel'],self.args['Rlabel'])
1042  canvas.Print(self.outputDir+"/"+var+"_"+segment+".png")
1043  canvas.Print(self.outputDir+"/"+var+"_"+segment+".pdf")
1044 
1045  #finalize
1046  self.__finalize__()
def addDirDATA(self, dataDir)
Definition: DMRplotter.py:785
def __createSingleArchitecture__(self)
Definition: DMRplotter.py:89
def __log__(self, log_type="", text="")
Definition: DMRplotter.py:28
objNameList
Open each file separately and get object groups.
Definition: DMRplotter.py:17
def __fitGauss__(self, hist)
Definition: DMRplotter.py:453
def replace(string, replacements)
def __beautify__(self, canvas, CMSextraLabel, eraLabel)
Definition: DMRplotter.py:680
varsX
Set histogram labels, axis titles, line color, stat bar, etc.
Definition: DMRplotter.py:21
def addDATA(self, filename)
Definition: DMRplotter.py:772
def addMC(self, filename)
Definition: DMRplotter.py:822
def addDirMC(self, mcDir)
Definition: DMRplotter.py:804
def __defineSingleObjects__(self)
Definition: DMRplotter.py:147
def __createArchitecture__(self)
Definition: DMRplotter.py:123
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def _replaceMulti(self, mainString, toBeReplaced, newString)
Definition: DMRplotter.py:54
def __init__(self, args)
Definition: DMRplotter.py:9
def __setTHStyle__(self, objects)
Definition: DMRplotter.py:552
def __cleanSingle__(self)
Definition: DMRplotter.py:728
def __getStat__(self, hist, var)
Definition: DMRplotter.py:486
def __finalize__(self)
Definition: DMRplotter.py:760
def __defineObjects__(self)
Definition: DMRplotter.py:293
def _styledTPaveText(self, x1, y1, x2, y2, var)
Definition: DMRplotter.py:65
#define str(s)