123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- import io
- from os.path import dirname, join, abspath, normpath
- import sys
- from math import ceil, sqrt
- from subprocess import run
- import itertools as it
- import ROOT
- PRJ_PATH = normpath(join(dirname(abspath(__file__)), "../"))
- EXE_PATH = join(PRJ_PATH, "build/main")
- class OutputCapture:
- def __init__(self):
- self.my_stdout = io.StringIO()
- self.my_stderr = io.StringIO()
- def get_stdout(self):
- self.my_stdout.seek(0)
- return self.my_stdout.read()
- def get_stderr(self):
- self.my_stderr.seek(0)
- return self.my_stderr.read()
- def __enter__(self):
- self.stdout = sys.stdout
- self.stderr = sys.stderr
- sys.stdout = self.my_stdout
- sys.stderr = self.my_stderr
- def __exit__(self, *args):
- sys.stdout = self.stdout
- sys.stderr = self.stderr
- self.stdout = None
- self.stderr = None
- def bin_range(n, end=None):
- if end is None:
- return range(1, n+1)
- else:
- return range(n+1, end+1)
- def normalize_columns(hist2d):
- normHist = ROOT.TH2D(hist2d)
- cols, rows = hist2d.GetNbinsX(), hist2d.GetNbinsY()
- for col in bin_range(cols):
- sum_ = 0
- for row in bin_range(rows):
- sum_ += hist2d.GetBinContent(col, row)
- if sum_ == 0:
- continue
- for row in bin_range(rows):
- norm = hist2d.GetBinContent(col, row) / sum_
- normHist.SetBinContent(col, row, norm)
- return normHist
- class HistCollection:
- def __init__(self, sample_name, input_filename,
- rebuild_hists=False):
- self.sample_name = sample_name
- if rebuild_hists:
- run([EXE_PATH, "-s", "-f", input_filename])
- output_filename = input_filename.replace(".root", "_result.root")
- file = ROOT.TFile.Open(output_filename)
- l = file.GetListOfKeys()
- self.map = {}
- for i in range(l.GetSize()):
- name = l.At(i).GetName()
- new_name = ":".join((sample_name, name))
- obj = file.Get(name)
- try:
- obj.SetName(new_name)
- obj.SetDirectory(0) # disconnects Object from file
- except AttributeError:
- pass
- self.map[name] = obj
- setattr(self, name, obj)
- file.Close()
- # Now add these histograms into the current ROOT directory (in memory)
- # and remove old versions if needed
- for obj in self.map.values():
- try:
- old_obj = ROOT.gDirectory.Get(obj.GetName())
- ROOT.gDirectory.Remove(old_obj)
- ROOT.gDirectory.Add(obj)
- except AttributeError:
- pass
- HistCollection.add_collection(self)
- def draw(self, shape=None):
- if shape is None:
- n = int(ceil(sqrt(len(self.map))))
- shape = (n, n)
- self.canvas.Clear()
- self.canvas.Divide(*shape)
- i = 1
- for hist in self.map.values():
- self.canvas.cd(i)
- try:
- hist.SetStats(False)
- except AttributeError:
- pass
- draw_option = ""
- if type(hist) in (ROOT.TH1F, ROOT.TH1I, ROOT.TH1D):
- draw_option = ""
- elif type(hist) in (ROOT.TH2F, ROOT.TH2I, ROOT.TH2D):
- draw_option = "COLZ"
- elif type(hist) in (ROOT.TGraph,):
- draw_option = "A*"
- else:
- print("cannot draw object", hist)
- continue # Not a drawable type(probably)
- hist.Draw(draw_option)
- i += 1
- self.canvas.Draw()
- @classmethod
- def get_hist_set(cls, attrname):
- labels, hists = zip(*[(sample_name, getattr(h, attrname))
- for sample_name, h in cls.collections.items()])
- return labels, hists
- @classmethod
- def add_collection(cls, hc):
- if not hasattr(cls, "collections"):
- cls.collections = {}
- cls.collections[hc.sample_name] = hc
- print("collection added: " + hc.sample_name)
- print("collections present: " + ', '.join(list(hc.collections.keys())))
- @property
- def canvas(self):
- cls = self.__class__
- if not hasattr(cls, "_canvas"):
- cls._canvas = ROOT.TCanvas("c1", "", 1600, 1200)
- return cls._canvas
- @canvas.setter
- def canvas(self, canvas):
- cls = self.__class__
- cls._canvas = canvas
- @classmethod
- def stack_hist(cls,
- hist_name,
- title="", enable_fill=False,
- normalize_to=0, draw=False,
- draw_option="",
- make_legend=False,
- _stacks={}):
- labels, hists = cls.get_hist_set(hist_name)
- colors = it.cycle([ROOT.kRed, ROOT.kBlue, ROOT.kGreen])
- stack = ROOT.THStack(hist_name+"_stack", title)
- if labels is None:
- labels = [hist.GetName() for hist in hists]
- if type(normalize_to) in (int, float):
- normalize_to = [normalize_to]*len(hists)
- ens = enumerate(zip(hists, labels, colors, normalize_to))
- for i, (hist, label, color, norm) in ens:
- hist_copy = hist
- hist_copy = hist.Clone(hist.GetName()+"_clone")
- hist_copy.SetTitle(label)
- if enable_fill:
- hist_copy.SetFillColorAlpha(color, 0.75)
- hist_copy.SetLineColorAlpha(color, 0.75)
- if norm:
- integral = hist_copy.Integral()
- hist_copy.Scale(norm/integral, "nosw2")
- hist_copy.SetStats(False)
- stack.Add(hist_copy)
- if draw:
- stack.Draw(draw_option)
- if make_legend:
- cls._canvas.BuildLegend(0.75, 0.75, 0.95, 0.95, "")
- # cls._canvas.Draw()
- # prevent stack from getting garbage collected
- _stacks[stack.GetName()] = stack
- return stack
- @classmethod
- def stack_hist_array(cls,
- hist_names,
- titles,
- shape=None, **kwargs):
- n_hists = len(hist_names)
- if shape is None:
- if n_hists <= 4:
- shape = (1, n_hists)
- else:
- shape = (ceil(sqrt(n_hists)),)*2
- cls._canvas.Divide(*shape)
- for i, hist_name, title in zip(bin_range(n_hists), hist_names, titles):
- cls._canvas.cd(i)
- hists, labels = cls.get_hist_set(hist_name)
- cls.stack_hist(hist_name, title=title, draw=True, **kwargs)
- cls._canvas.cd(1).BuildLegend(0.75, 0.75, 0.95, 0.95, "")
|