def drawHist(self, output_dir = None, normalize_signal = False, draw_option = 'EHist', bkgr_draw_option = 'Stack', draw_cuts = None, custom_labels = None, draw_lines = None, message = None, min_cutoff = None): # # Some default settings # setDefault() # # Create Canvas and pads # self.canvas = ROOT.TCanvas("Canv"+self.name, "Canv"+self.name, 1000, 1000) self.setPads() self.plotpad.Draw() self.plotpad.cd() # # Throw some errors in case of faulty input # if isinstance(self.x_name, list): raise RuntimeError('invalid x_names') if custom_labels is not None and len(custom_labels) != self.s[0].GetNbinsX(): raise RuntimeError("Inconsistent number of bins ("+str(self.s[0].GetNbinsX())+") compared to number of labels given ("+len(custom_labels)+")") if draw_option == 'Stack' and len(self.b) > 0: raise RuntimeError('The "Stack" option for signal is meant to be used when there is no background. In case of background, input the hist to stack as background.') # # Set Signal Histogram Styles # for h, n in zip(self.s, self.tex_names): # Prepare for stacked signal if draw_option == "Stack": # Order signals from lowest to highest self.s, self.s_tex_names = pt.orderHist(self.s, self.s_tex_names, lowest_first=True) self.hs = ROOT.THStack("hs", "hs") for i, (h, n) in enumerate(zip(self.s, self.s_tex_names)): color_index = ps.getPaletteIndex(self.color_palette, self.s.index(h), n) h.SetFillColor(ps.getColor(self.color_palette, color_index)) h.SetLineColor(ps.getColor(self.color_palette, color_index)) self.hs.Add(h) # Prepare for very basic signal elif draw_option == "HNL": # Order signals so those with 0 sum of weights come last and arent skipped (which causes the axis to not be updated) self.s, self.s_tex_names = pt.orderHist(self.s, self.s_tex_names) h.SetLineColor(ps.getHNLColor(n)) h.SetLineWidth(4) # Otherwise generic settings else: # Order signals so those with 0 sum of weights come last and arent skipped (which causes the axis to not be updated) self.s, self.s_tex_names = pt.orderHist(self.s, self.s_tex_names) color_index = ps.getPaletteIndex(self.color_palette, self.s.index(h), n) h.SetLineColor(ps.getColor(self.color_palette, color_index)) h.SetLineWidth(4) h.SetMarkerStyle(0) h.SetMarkerColor(ps.getColor(self.color_palette, color_index)) # # Set Bkgr Histogram Styles # if len(self.b) > 0: self.total_b = self.b[0].Clone('total_bkgr') # Generic settings for i, (h, n) in enumerate(zip(self.b, self.b_tex_names)): color_index = ps.getPaletteIndex(self.color_palette_bkgr, i, n) h.SetLineColor(ps.getColor(self.color_palette_bkgr, color_index)) h.SetLineWidth(3) if i != 0: self.total_b.Add(h) # Prepare for stacking (Can not conflict with signal stacks due to runtimeError raise at the beginning) if bkgr_draw_option == 'Stack': self.b, self.b_tex_names = pt.orderHist(self.b, self.b_tex_names, lowest_first = True) self.hs = ROOT.THStack("hs", "hs") for i, (h, n) in enumerate(zip(self.b, self.b_tex_names)): color_index = ps.getPaletteIndex(self.color_palette_bkgr, i, n) h.SetFillColor(ps.getColor(self.color_palette_bkgr, color_index)) self.hs.Add(h) self.tex_names = self.s_tex_names + self.b_tex_names # # Set title (background is drawn first so it gets dibs) # if self.draw_ratio is not None or self.draw_significance: title = " ; ; "+self.y_name else: title = " ;" +self.x_name+ " ; "+self.y_name if len(self.b) > 0: if bkgr_draw_option == 'Stack': self.hs.SetTitle(title) else: self.b[0].SetTitle(title) else: if draw_option == "Stack": self.hs.SetTitle(title) else: self.s[0].SetTitle(title) # # Draw background first # tmp_bkgr_draw_option = bkgr_draw_option.split('E')[-1] if len(self.b) > 0: if bkgr_draw_option == 'Stack': self.hs.Draw("Hist") #Draw before using GetHistogram, see https://root-forum.cern.ch/t/thstack-gethistogram-null-pointer-error/12892/4 if self.draw_ratio is not None or self.draw_significance: self.hs.GetHistogram().GetXaxis().SetLabelOffset(9999999) else: for i, b in enumerate(self.b): if i == 0: b.Draw(tmp_bkgr_draw_option) if self.draw_ratio is not None or self.draw_significance: b.GetXaxis().SetLabelOffset(9999999) else: b.Draw(tmp_bkgr_draw_option + 'Same') # # Draw signal # tmp_draw_option = draw_option.split('E')[-1] if draw_option == "Stack": self.hs.Draw("Hist") #Draw before using GetHistogram, see https://root-forum.cern.ch/t/thstack-gethistogram-null-pointer-error/12892/4 if self.draw_ratio is not None or self.draw_significance: self.hs.GetHistogram().GetXaxis().SetLabelOffset(9999999) else: for ih, h in enumerate(self.s): if h.GetSumOfWeights() == 0: continue if normalize_signal: if len(self.b) == 0: # raise RuntimeError("Trying to normalize a signal to a nonexisting background in drawHist") #Normalize everything to the first histogram if ih > 0: h.Scale(self.s[0].GetSumOfWeights()/h.GetSumOfWeights()) else: h.Scale(self.total_b.GetSumOfWeights()/h.GetSumOfWeights()) if(self.s.index(h) == 0 and self.b is None): h.Draw(tmp_draw_option) else: h.Draw(tmp_draw_option+'Same') self.drawErrors(draw_option, bkgr_draw_option) #Draw observed if self.observed is not None: self.observed.SetLineColor(ROOT.kBlack) self.observed.SetMarkerColor(ROOT.kBlack) self.observed.SetMarkerStyle(8) self.observed.SetMarkerSize(1) self.observed.Draw("EPSame") tdr.setTDRStyle() #TODO: Find out why we need a setTDRStyle after drawing the stack # # Calculate ranges of axis and set to log if requested # self.setAxisLog(stacked = (len(self.b) > 0 and 'Stack' in bkgr_draw_option) or 'Stack' in draw_option, min_cutoff = min_cutoff) # # Set custom labels if needed # if custom_labels is not None and not self.draw_ratio and not self.draw_significance: if len(self.b) > 0: if bkgr_draw_option == 'Stack': for i, n in enumerate(custom_labels): self.hs.GetHistogram().GetXaxis().SetBinLabel(i+1, n) else: for i, n in enumerate(custom_labels): self.b[0].GetXaxis().SetBinLabel(i+1, n) else: if draw_option == "Stack": for i, n in enumerate(custom_labels): self.hs.GetHistogram().GetXaxis().SetBinLabel(i+1, n) else: for i, n in enumerate(custom_labels): self.s[0].GetXaxis().SetBinLabel(i+1, n) # # Option only used in plotVariables.py # if draw_cuts is not None: if self.extra_text is None: self.extra_text = [] lines = [] i = 0 #Cant use enumerate because of if statement, first filled value might be at i=2 but lines[2] doesnt exist then for j, (h, l) in enumerate(zip(self.s, draw_cuts[0])): if l is not None: lines.append(ROOT.TLine(l, 0, l, self.s[0].GetMaximum())) lines[i].SetLineColor(self.s[j].GetLineColor()) lines[i].SetLineStyle(10) lines[i].SetLineWidth(2) lines[i].Draw('Same') self.extra_text.append(pt.extraTextFormat('p_{T}(l'+str(j+1)+') < '+str(l) +' GeV')) i += 1 self.extra_text.append(pt.extraTextFormat('Eff: %.4g' % draw_cuts[1]+'%')) if draw_lines is not None: line_collection = [] #Multiple lines if isinstance(draw_lines[0], list): for il, l in enumerate(draw_lines): x0 = l[0] if l[0] is not None else self.overall_min*0.001 x1 = l[1] if l[1] is not None else self.overall_max*3000 y0 = l[2] if l[2] is not None else self.min_to_set*0.01 y1 = l[3] if l[3] is not None else self.max_to_set*300 color = l[4] if l[4] is not None else ROOT.kBlack line_collection.append(ROOT.TLine(x0, y0, x1, y1)) line_collection[il].SetLineColor(color) line_collection[il].SetLineWidth(l[5]) line_collection[il].SetLineStyle(l[6]) for l in line_collection: l.Draw('same') #Write extra text if self.extra_text is not None: self.drawExtraText() self.canvas.cd() #Create Legend legend = ROOT.TLegend(0.4, .7, .9, .9) legend.SetNColumns(1) legend.SetTextSize(.03) loop_obj = [item for item in self.s] if len(self.b) > 0: loop_obj.extend(self.b) for h, n in zip(loop_obj, self.tex_names): legend.AddEntry(h, n) if self.observed is not None: legend.AddEntry(self.observed, 'data') legend.SetFillStyle(0) legend.SetBorderSize(0) legend.Draw() if self.draw_ratio is not None: just_errors = self.draw_ratio == 'errorsOnly' if self.b is None: raise RuntimeError("Cannot ask ratio or significance if no background is given") ratios = self.calculateRatio() self.drawRatio(ratios, custom_labels, just_errors) self.ratio_pad.Update() if self.draw_significance: if self.b is None: raise RuntimeError("Cannot ask ratio or significance if no background is given") significance_lines = [] for s in self.s: significance_lines.append(ROOT.TLine(s.GetBinLowEdge(1), 0, s.GetBinLowEdge(self.s[0].GetNbinsX()+1), 0)) significances = self.calculateSignificance(cumulative=True) self.drawSignificance(significances, custom_labels, significance_lines) self.sig_pad.Update() ROOT.gPad.Update() self.canvas.Update() #CMS lumi cl.CMS_lumi(self.canvas, 4, 11, 'Preliminary', self.year) #Save everything self.savePlot(output_dir +'/'+ self.name, message) ROOT.SetOwnership(self.canvas, False) return
def setDefault(paintformat="4.2f"): gROOT.SetBatch(True) gStyle.SetOptStat(0) tdr.setTDRStyle() gStyle.SetPaintTextFormat(paintformat) gROOT.ProcessLine("gErrorIgnoreLevel = 1001;")
def generalSettings(paintformat = "4.2f"): ROOT.gROOT.SetBatch(True) ROOT.gStyle.SetOptStat(0) tdr.setTDRStyle() ROOT.gStyle.SetPaintTextFormat(paintformat) ROOT.gROOT.ProcessLine( "gErrorIgnoreLevel = 1001;")