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'].SetMarkerStyle(self.args['styles'][self.args['objects'].index(objName)])
611  obj['hist'].SetMarkerSize(1.5)
612 
613  #set general style for DMRs
614  tStyle = ROOT.TStyle("StyleCMS","Style CMS")
615 
616  #zero horizontal error bars
617  tStyle.SetErrorX(0)
618 
619  #canvas settings
620  tStyle.SetCanvasBorderMode(0)
621  tStyle.SetCanvasColor(ROOT.kWhite)
622  tStyle.SetCanvasDefH(800) #800
623  tStyle.SetCanvasDefW(800)
624  tStyle.SetCanvasDefX(0)
625  tStyle.SetCanvasDefY(0)
626 
627  #frame settings
628  tStyle.SetFrameBorderMode(0)
629  tStyle.SetFrameBorderSize(10)
630  tStyle.SetFrameFillColor(ROOT.kBlack)
631  tStyle.SetFrameFillStyle(0)
632  tStyle.SetFrameLineColor(ROOT.kBlack)
633  tStyle.SetFrameLineStyle(0)
634  tStyle.SetFrameLineWidth(1)
635  tStyle.SetLineWidth(2)
636 
637  #pad settings
638  tStyle.SetPadBorderMode(0)
639  tStyle.SetPadColor(ROOT.kWhite)
640  tStyle.SetPadGridX(False)
641  tStyle.SetPadGridY(False)
642  tStyle.SetGridColor(0)
643  tStyle.SetGridStyle(3)
644  tStyle.SetGridWidth(1)
645 
646  #margins
647  tStyle.SetPadTopMargin(0.08)
648  tStyle.SetPadBottomMargin(0.13)
649  tStyle.SetPadLeftMargin(0.16)
650  tStyle.SetPadRightMargin(0.05)
651 
652  #common histogram settings
653  tStyle.SetHistLineStyle(0)
654  tStyle.SetHistLineWidth(3)
655  tStyle.SetMarkerSize(0.8)
656  tStyle.SetEndErrorSize(4)
657  tStyle.SetHatchesLineWidth(1)
658 
659  #stat box
660  tStyle.SetOptFile(0)
661 
662  #axis settings
663  tStyle.SetAxisColor(1,"XYZ")
664  tStyle.SetTickLength(0.03,"XYZ")
665  tStyle.SetNdivisions(510,"XYZ")
666  tStyle.SetPadTickX(1)
667  tStyle.SetPadTickY(1)
668  tStyle.SetStripDecimals(ROOT.kFALSE)
669 
670  #axis labels and titles
671  tStyle.SetTitleColor(1,"XYZ")
672  tStyle.SetLabelColor(1,"XYZ")
673  tStyle.SetLabelFont(42,"XYZ")
674  tStyle.SetLabelOffset(0.007,"XYZ")
675  tStyle.SetLabelSize(0.04,"XYZ")
676  tStyle.SetTitleFont(42,"XYZ")
677  tStyle.SetTitleSize(0.047,"XYZ")
678  tStyle.SetTitleXOffset(1.2)
679  tStyle.SetTitleYOffset(1.7)
680 
681  #legend
682  tStyle.SetLegendBorderSize(0)
683  tStyle.SetLegendTextSize(self.legendTextSize)
684  tStyle.SetLegendFont(42)
685 
686  #assign changes to gROOT current style
687  tStyle.cd()
688 
689  return tStyle
690 
691  def __beautify__(self, canvas, CMSextraLabel, eraLabel):
692 
695 
696  leftMargin = canvas.GetLeftMargin()
697  rightMargin = canvas.GetRightMargin()
698  topMargin = canvas.GetTopMargin()
699  canvas.cd()
700 
701  #CMStext
702  CMSlabel = "CMS"
703  CMSextraOffset = 0.10
704  CMStext = ROOT.TLatex()
705  CMSextra = ROOT.TLatex()
706 
707  CMStext.SetNDC()
708  CMSextra.SetNDC()
709 
710  CMStext.SetTextAngle(0)
711  CMSextra.SetTextAngle(0)
712 
713  CMStext.SetTextColor(ROOT.kBlack)
714  CMSextra.SetTextColor(ROOT.kBlack)
715 
716  CMStext.SetTextFont(61)
717  CMSextra.SetTextFont(52)
718 
719  CMStext.SetTextAlign(11)
720  CMStext.SetTextSize(0.045)
721  CMSextra.SetTextSize(0.035)
722 
723  CMStext.DrawLatex(leftMargin,1.-topMargin+0.01,CMSlabel)
724  CMSextra.DrawLatex(leftMargin+CMSextraOffset,1-topMargin+0.01,CMSextraLabel)
725 
726  #Era text
727  eraText = ROOT.TLatex()
728  eraText.SetNDC()
729  eraText.SetTextAngle(0)
730  eraText.SetTextColor(ROOT.kBlack)
731  eraText.SetTextFont(42)
732  eraText.SetTextAlign(33)
733  eraText.SetTextSize(0.035)
734  eraText.DrawLatex(1.-rightMargin,1.-topMargin+0.035,eraLabel)
735 
736  #Redraw axis
737  canvas.RedrawAxis()
738 
739  def __cleanSingle__(self):
740 
744 
745  for dirpath,dirs,files in os.walk(self.cwd):
746  if dirpath != self.cwd: continue
747  for n_file in files:
748  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
749  self.__log__("i","File "+n_file+" was created.")
750  os.system("mv "+n_file+" "+self.outputDir)
751  self.__log__("i","Done.")
752 
753  def __clean__(self):
754 
758 
759  for datafile in self.dataFiles:
760  os.system("mv "+datafile+" "+self.outputDir)
761  for mcfile in self.MCobjects:
762  os.system("mv "+mcfile+" "+self.outputDir)
763  for dirpath,dirs,files in os.walk(self.cwd):
764  if dirpath != self.cwd: continue
765  for n_file in files:
766  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
767  self.__log__("i","File "+n_file+" was created.")
768  os.system("mv "+n_file+" "+self.outputDir)
769  self.__log__("i","Done.")
770 
771  def __finalize__(self):
772 
775 
776  for dirpath,dirs,files in os.walk(self.outputDir):
777  for n_file in files:
778  if ".png" in n_file or ".pdf" in n_file or ".eps" in n_file:
779  self.__log__("i","File "+n_file+" was created.")
780  self.__log__("i","Done.")
781 
782 
783  def addDATA(self,filename):
784 
787  if os.path.isfile(str(filename)):
788  self.__log__("i","DATA file: "+str(filename)+" was added for plotting.")
789  self.dataFiles.append(str(filename))
790  elif os.path.isfile(os.path.join(str(filename),self.fileBaseName)):
791  self.__log__("i","DATA file: "+os.path.join(str(filename),self.fileBaseName)+" was added for plotting.")
792  self.dataFiles.append(os.path.join(str(filename),self.fileBaseName))
793  else:
794  self.__log__("w","DATA file: "+os.path.join(str(filename),self.fileBaseName)+" NOT found.")
795 
796  def addDirDATA(self, dataDir):
797 
800  if os.path.isdir(dataDir):
801  self.__log__("i","DATA dir: "+dataDir+" was added for plotting.")
802  self.dataDirs.append(dataDir)
803  else:
804  self.__log__("w","DATA dir: "+dataDir+" NOT found.")
805 
806  #Create list of dataFiles #FIXME for multiple DATA inputs
807  if len(self.dataDirs) != 0:
808  if self.args['isDMR']:
809  for dataDir in self.dataDirs:
810  for root,dirs,files in os.walk(dataDir):
811  for dir in dirs:
812  if dir.startswith("offline"):
813  self.dataFiles.append(dataDir+"/"+dir+"/ExtendedOfflineValidation_Images/OfflineValidationSummary.root")
814 
815  def addDirMC(self, mcDir):
816 
819  if os.path.isdir(mcDir):
820  self.__log__("i","MC dir: "+mcDir+" was added for plotting.")
821  nFiles = 0
822  for dirpath,dirs,files in os.walk(mcDir):
823  for file in files:
824  if self.fileBaseName.replace(".root","") in file and file.endswith(".root"):
825  self.__log__("i","MC file: "+str(file)+" was added for plotting.")
826  self.MCobjects.append(os.path.join(dirpath,file))
827  nFiles += 1
828  if nFiles == 0:
829  self.__log__("w","No MC file found in "+str(mcDir)+".")
830  else:
831  self.__log__("w","MC dir: "+mcDir+" NOT found.")
832 
833  def addMC(self,filename):
834 
837  if os.path.isfile(str(filename)):
838  self.__log__("i","MC file: "+str(filename)+" was added for plotting.")
839  self.MCobjects.append(str(filename))
840  elif os.path.isfile(os.path.join(str(filename),self.fileBaseName)):
841  self.__log__("i","MC file: "+os.path.join(str(filename),self.fileBaseName)+" was added for plotting.")
842  self.MCobjects.append(os.path.join(str(filename),self.fileBaseName))
843  else:
844  self.__log__("w","MC file: "+str(os.path.join(str(filename),self.fileBaseName))+" NOT found.")
845 
846  def plotSingle(self):
847 
850 
851  #check for input file and create output dir
853 
854  #access histograms in rootfiles, select different validation objects and store them separately
855  objects = self.__defineSingleObjects__()
856  objectsData = objects['DATA']
857  objectsMC = objects['MC']
858 
859  #set histogram style
860  for objDict in objectsData:
861  self.__setTHStyle__(objDict)
862  for objDict in objectsMC:
863  self.__setTHStyle__(objDict)
864 
865  #really plot
866  ROOT.gROOT.SetBatch(True) #turn off printing canvas on screen
867  ROOT.gROOT.ProcessLine("gErrorIgnoreLevel = 1001;") #turn off printing messages on terminal
868 
869  for objDict in objectsData:
870  for segment in self.segments:
871  for var in self.varsX:
872  id = "0"
873  for key in objDict.keys():
874  for _obj in objDict[key]:
875  id = _obj['id']
876  canvas = ROOT.TCanvas(id+"_"+var+"_"+segment)
877  canvas.cd()
878 
879  #set labels positioning
880  segmentText = {'text' : segment, 'xmin' : 0.0, 'xmax' : 0.0}
881  statText = {'xmin' : 0.0, 'xmax' : 0.0}
882  if "median" in var:
883  segmentText['xmin'] = 2.5
884  segmentText['xmax'] = 3.5
885  statText['xmin'] = 0.20
886  statText['xmax'] = 0.85
887  else:
888  segmentText['xmin'] = 1.4
889  segmentText['xmax'] = 1.6
890  statText['xmin'] = 0.65
891  statText['xmax'] = 0.95
892 
893  #order plots & prepare y-axis scale factors
894  isEmpty = True
895  maxY = 0.0
896  objGroup = []
897  for objName in self.objNameList: #follow plotting order
898  for obj in objDict[objName]:
899  if obj['var'] == var and obj['segment'] == segment:
900  if obj['hist'].GetBinContent(obj['hist'].GetMaximumBin()) >= maxY:
901  maxY = obj['hist'].GetBinContent(obj['hist'].GetMaximumBin())
902  isEmpty = False
903  legendLabel = objName.replace("_"," ")
904  if len(self.args['labels']) != 0:
905  legendLabel = self.args['labels'][self.args['objects'].index(objName)]
906 
907  #Add MC for each data file
908  histsMC = []
909  labelsMC = []
910  statsMC = []
911  for objDictMC in objectsMC:
912  for objNameMC in self.objNameListMC:
913  for objMC in objDictMC[objNameMC]:
914  if objMC['var'] == var and objMC['segment'] == segment:
915  if objMC['hist'].GetBinContent(objMC['hist'].GetMaximumBin()) >= maxY:
916  maxY = objMC['hist'].GetBinContent(objMC['hist'].GetMaximumBin())
917  legendLabelMC = objNameMC.replace("_"," ")
918  if len(self.args['labels']) != 0:
919  legendLabelMC = self.args['labels'][self.args['objects'].index(objNameMC)]
920  objMC['hist'].SetDirectory(0)
921  histsMC.append(objMC['hist'])
922  labelsMC.append(legendLabelMC)
923  statsMC.append(self.__getStat__(objMC['hist'],var))
924  objGroup.append({'hist' : obj['hist'],
925  'histsMC' : histsMC,
926  'labelsMC': labelsMC,
927  'statsMC' : statsMC,
928  'label' : legendLabel,
929  'stat' : self.__getStat__(obj['hist'],var)
930  })
931  #draw & save
932  if not isEmpty:
933  datasetType = "singlemuon" #FIXME make it an option
934  legMinY = (1./self.legendOffset)+(1.-1./self.legendOffset)*(self.maxEntriesPerColumn-len(objGroup))/(self.maxEntriesPerColumn*3)
935  nColumns = 1
936  if len(objGroup) > self.maxEntriesPerColumn:
937  nColumns = 2
938  legMinY = 1./self.legendOffset
939  leg = ROOT.TLegend(0.08,legMinY,0.45,0.88)
940  leg.SetNColumns(nColumns)
941  seg = ROOT.TLatex()
942  maxX = objGroup[0]['hist'].GetXaxis().GetXmax()
943  stat = self._styledTPaveText(maxX*statText['xmin'],(legMinY+0.025)*self.legendOffset*maxY,maxX*statText['xmax'],0.95*self.legendOffset*maxY,var)
944  for igroup,group in enumerate(objGroup):
945  group['hist'].GetYaxis().SetRangeUser(0,self.legendOffset*maxY)
946  leg.AddEntry(group['hist'],group['label'],"l")
947  stat.AddText(group['stat'])
948  group['hist'].Draw("HISTSAME")
949  #for last data group add also MC
950  if igroup == len(objGroup)-1:
951  for ihist,histmc in enumerate(group['histsMC']):
952  leg.AddEntry(histmc,group['labelsMC'][ihist],"l")
953  stat.AddText(group['statsMC'][ihist])
954  histmc.Draw("HISTSAME")
955  leg.Draw("SAME")
956  seg.DrawLatex(segmentText['xmin'],self.segmentTextOffset['ymin']*maxY,segmentText['text'])
957  stat.Draw("SAME")
958  self.__beautify__(canvas,self.args['CMSlabel'],self.args['Rlabel'])
959  canvas.SaveAs(self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment+".png")
960  canvas.SaveAs(self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment+".pdf")
961  self.__log__("i","Saving "+self.outputDir+"/offline_"+datasetType+"_"+str(id)+"/"+var+"_"+segment)
962 
963  #finalize
964  #self.__cleanSingle__()
965  self.__log__("i","Done.")
966 
967  def plot(self):
968 
971 
972  #check for input file and create output dir if needed
974 
975  #access histograms in rootfiles, select different validation objects and store them separately
976  objects = self.__defineObjects__()
977 
978  #set histogram style
979  currentStyle = self.__setTHStyle__(objects) #NOTE: for CMSSW_11 and higher, currentStyle must be returned to plotting function
980 
981  #really plot
982  ROOT.gROOT.SetBatch(True) #turn off printing canvas on screen
983  ROOT.gROOT.ProcessLine("gErrorIgnoreLevel = 1001;") #turn off printing messages on terminal
984 
985  for segment in self.segments:
986  for var in self.varsX:
987  canvas = ROOT.TCanvas(var+"_"+segment)
988  canvas.cd()
989 
990  #set labels positioning
991  segmentText = {'text' : segment, 'xmin' : 0.0, 'xmax' : 0.0}
992  statText = {'xmin' : 0.0, 'xmax' : 0.0}
993  if "median" in var:
994  segmentText['xmin'] = 2.5
995  segmentText['xmax'] = 3.5
996  statText['xmin'] = 0.27
997  statText['xmax'] = 0.92
998  else:
999  segmentText['xmin'] = 1.4
1000  segmentText['xmax'] = 1.6
1001  statText['xmin'] = 0.75
1002  statText['xmax'] = 0.95
1003 
1004  #order plots & prepare y-axis scale factors
1005  isEmpty = True
1006  maxY = 0.0
1007  objGroup = []
1008  for objName in self.objNameList: #follow plotting order
1009  for obj in objects[objName]:
1010  if obj['var'] == var and obj['segment'] == segment:
1011  if obj['hist'].GetBinContent(obj['hist'].GetMaximumBin()) >= maxY:
1012  maxY = obj['hist'].GetBinContent(obj['hist'].GetMaximumBin())
1013  isEmpty = False
1014  legendLabel = objName.replace("_"," ")
1015  if len(self.args['labels']) != 0:
1016  legendLabel = self.args['labels'][self.args['objects'].index(objName)]
1017  drawStyle = ""
1018  legStyle = ""
1019  if obj['type'] == "MC":
1020  drawStyle = "HIST SAME"
1021  legStyle = "l"
1022  if obj['type'] == "DATA":
1023  drawStyle += "P HIST SAME"
1024  legStyle = "p"
1025  objGroup.append({'hist' : obj['hist'],
1026  'label' : legendLabel,
1027  'stat' : self.__getStat__(obj['hist'],var),
1028  'drawStyle' : drawStyle,
1029  'legStyle' : legStyle
1030  })
1031  #draw & save
1032  if not isEmpty:
1033  legMinY = (1./self.legendOffset)+(1.-1./self.legendOffset)*(self.maxEntriesPerColumn-len(objGroup))/(self.maxEntriesPerColumn*3)
1034  nColumns = 1
1035  if len(objGroup) > self.maxEntriesPerColumn:
1036  nColumns = 2
1037  legMinY = 1./self.legendOffset
1038  leg = ROOT.TLegend(0.20,legMinY,0.50,0.88)
1039  leg.SetNColumns(nColumns)
1040  seg = ROOT.TLatex()
1041  maxX = objGroup[0]['hist'].GetXaxis().GetXmax()
1042  stat = self._styledTPaveText(maxX*statText['xmin'],(legMinY+0.025)*self.legendOffset*maxY,maxX*statText['xmax'],0.95*self.legendOffset*maxY,var)
1043  for group in objGroup:
1044  group['hist'].GetYaxis().SetRangeUser(0,self.legendOffset*maxY)
1045  leg.AddEntry(group['hist'],group['label'],group['legStyle'])
1046  stat.AddText(group['stat'])
1047  group['hist'].Draw(group['drawStyle'])
1048  leg.Draw("SAME")
1049  seg.DrawLatex(segmentText['xmin'],self.segmentTextOffset['ymin']*maxY,segmentText['text'])
1050  stat.Draw("SAME")
1051  self.__beautify__(canvas,self.args['CMSlabel'],self.args['Rlabel'])
1052  canvas.Print(self.outputDir+"/"+var+"_"+segment+".png")
1053  canvas.Print(self.outputDir+"/"+var+"_"+segment+".pdf")
1054 
1055  #finalize
1056  self.__finalize__()
def addDirDATA(self, dataDir)
Definition: DMRplotter.py:796
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:691
varsX
Set histogram labels, axis titles, line color, stat bar, etc.
Definition: DMRplotter.py:21
def addDATA(self, filename)
Definition: DMRplotter.py:783
def addMC(self, filename)
Definition: DMRplotter.py:833
def addDirMC(self, mcDir)
Definition: DMRplotter.py:815
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:739
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:771
def __defineObjects__(self)
Definition: DMRplotter.py:304
def _styledTPaveText(self, x1, y1, x2, y2, var)
Definition: DMRplotter.py:76
#define str(s)