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