11 obj = root_file.Get(path)
14 raise RuntimeError(f
"Object not found at path: {path}")
16 logging.warning(f
"Object not found at path: {path}")
20 path_parts = path.strip(
"/").
split(
"/")
21 *dirs, name = path_parts
22 current_dir = root_file
24 current_dir = current_dir.Get(d)
27 raise RuntimeError(f
"Directory '{d}' not found in path '{'/'.join(dirs)}' for deletion.")
29 logging.warning(f
"Directory '{d}' not found in path '{'/'.join(dirs)}' for deletion.")
32 current_dir.Delete(f
"{name};*")
39 for part
in path.strip(
"/").
split(
"/"):
40 next_dir = current.Get(part)
42 logging.info(f
"Saving directory not exist. Creating directory '{part}' in '{current.GetName()}'")
45 current = ROOT.gDirectory
52 with open(config_file,
"r")
as f:
53 config_data = yaml.safe_load(f)
54 self.
configs[name] = config_data
55 logging.info(f
"Configuration for '{name}' loaded from {config_file}")
58 """Returns the config for a specific domain (e.g., 'Electron')"""
62 """Returns all configs as (name, config) pairs"""
66 def __init__(self, input, output, rebin_factor, delete_original=False):
76 raise RuntimeError(f
"Histogram '{self.input}' not found for rebinning.")
78 logging.error(f
"Histogram '{self.input}' not found for rebinning.")
82 *dir_parts, output_name = path_parts
83 directory_path =
"/".
join(dir_parts)
85 root_file.cd(directory_path)
87 if hist.GetDimension() == 1:
89 rebinned.Write(output_name, ROOT.TObject.kOverwrite)
90 logging.info(f
"Rebinned 1D histogram and saved as '{output_name}' in '{directory_path}'.")
91 elif hist.GetDimension() == 2:
94 hist.SetName(output_name)
95 hist.Write(output_name, ROOT.TObject.kOverwrite)
96 logging.info(f
"Rebinned 2D histogram and saved as '{output_name}' in '{directory_path}'.")
103 input=fragment[
"input"],
104 output=fragment[
"output"],
105 rebin_factor=fragment[
"rebin"],
106 delete_original=fragment.get(
"delete_original",
False)
110 def __init__(self, numerator, denominator, output):
118 if not num
or not den:
120 raise RuntimeError(f
"Missing histogram: {self.numerator} or {self.denominator}")
122 logging.error(f
"Missing histogram: {self.numerator} or {self.denominator}")
125 if not ROOT.TEfficiency.CheckConsistency(num, den):
126 logging.warning(
"Inconsistency detected between numerator and denominator histograms.")
127 logging.info(
"Attempting to fix...")
128 for i
in range(1, num.GetNbinsX() + 1):
129 num_val = num.GetBinContent(i)
130 den_val = den.GetBinContent(i)
132 den.SetBinContent(i, 1e-6)
134 logging.info(f
"Setting denominator to a smaller value for bin {i} as it is zero. Any other options?")
135 if num_val > den_val:
136 logging.info(f
"For Bin {i}: Num ({num_val}) > Den ({den_val})! Adjusting by setting Den to Num. Any other options?")
137 den.SetBinContent(i, num_val)
138 if not ROOT.TEfficiency.CheckConsistency(num, den):
139 logging.error(
"Unable to fix histogram inconsistencies. Aborting efficiency calculation.")
142 eff = ROOT.TEfficiency(num, den)
144 *dir_path, obj_name = path_parts
145 directory_path =
"/".
join(dir_path)
146 eff.SetName(obj_name)
147 eff.SetTitle(f
"{self.numerator}/{self.denominator};{num.GetXaxis().GetTitle()};Efficiency")
149 root_file.cd(directory_path)
151 logging.info(f
"Saved efficiency '{obj_name}' in '{directory_path}'.")
156 numerator=fragment[
"numerator"],
157 denominator=fragment[
"denominator"],
158 output=fragment[
"output"]
170 raise RuntimeError(f
"Histogram '{self.histogram_name}' not found.")
172 logging.error(f
"Histogram '{self.histogram_name}' not found.")
176 cum = [hist.GetBinContent(i)
for i
in range(0, hist.GetNbinsX() + 2)]
177 for i
in range(1, len(cum)):
179 total = cum[-1]
or 1.0
180 cum = [x / total
for x
in cum]
186 logging.info(f
"Resolution widths for {self.histogram_name} -> s68: {s68:.4f}, s90: {s90:.4f}")
187 logging.info(
"Todo: Take a 2D histo, take the quantity say xaxis, make its small intervals %s",
"and then compute the Resolution widths and then save as a 1D histo with quantity on xaxis and Resolution widths on yaxis")
191 min_width =
float(
"inf")
193 for i
in range(len(cum)):
194 target = cum[i] + prob
197 for j
in range(i + 1, len(cum)):
199 width = hist.GetBinCenter(j) - hist.GetBinCenter(i)
200 if width < min_width:
204 return 0.5 * (hist.GetBinCenter(best[1]) - hist.GetBinCenter(best[0]))
209 histogram_name=fragment[
"histogram"]
213 def __init__(self, signal_path, background_path, output):
222 if not sig
or not bkg:
224 raise RuntimeError(f
"Missing histogram: {self.signal_path} or {self.background_path}")
226 logging.error(f
"Missing histogram: {self.signal_path} or {self.background_path}")
231 logging.error(
"ROC graph is empty or could not be constructed.")
235 *dir_path, obj_name = path_parts
236 directory_path =
"/".
join(dir_path)
238 graph.SetName(obj_name)
239 graph.SetTitle(f
"{obj_name};Signal Efficiency;Background Rejection")
242 root_file.cd(directory_path)
244 logging.info(f
"Saved ROC curve '{obj_name}' in '{directory_path}'.")
247 n_bins = sig.GetNbinsX()
248 total_sig = sig.Integral()
249 total_bkg = bkg.Integral()
251 x_vals, y_vals = [], []
252 if total_sig == 0
or total_bkg == 0:
253 logging.error(
"Zero total signal or background counts; cannot compute ROC.")
256 for cut_bin
in range(1, n_bins + 1):
257 sig_pass = sig.Integral(cut_bin, n_bins)
258 bkg_pass = bkg.Integral(cut_bin, n_bins)
260 eff = sig_pass / total_sig
266 rej = total_bkg / bkg_pass
274 graph = ROOT.TGraphErrors(len(x_vals))
275 for i, (x, y)
in enumerate(zip(x_vals, y_vals)):
276 graph.SetPoint(i, x, y)
283 signal_path=fragment[
"signal"],
284 background_path=fragment[
"background"],
285 output=fragment[
"output"]
289 def __init__(self, input_hist, bins_x, bins_y, projection_axis, output=None):
301 raise RuntimeError(f
"Histogram '{self.input_hist}' not found.")
303 logging.error(f
"Histogram '{self.input_hist}' not found.")
310 *dir_path, output_name = path_parts
311 directory_path =
"/".
join(dir_path)
313 root_file.cd(directory_path)
316 proj_x = hist.ProjectionX(output_name, b1, b2).Clone(output_name)
317 self.
project(proj_x, hist.GetXaxis().GetTitle(),
"X")
319 proj_y = hist.ProjectionY(output_name, b3, b4).Clone(output_name)
320 self.
project(proj_y, hist.GetYaxis().GetTitle(),
"Y")
322 def project(self, projection, axis_title, axis):
323 projection.SetTitle(f
";{axis_title};Entries")
324 projection.SetStats(
False)
326 logging.info(f
"Saved projection by integrating over {axis} axis as '{projection.GetName()}'.")
331 input_hist=fragment[
"hist"],
332 bins_x=fragment.get(
"bins_projection_x", [10, 200]),
333 bins_y=fragment.get(
"bins_projection_y", [-2.47, 2.47]),
334 projection_axis=fragment.get(
"projection_axis",
"x"),
335 output=fragment.get(
"output")
339 def __init__(self, histo1, histo2, output, delete_inputs=False):
348 if not histo1
or not histo2:
350 raise RuntimeError(f
"Missing histograms: {self.histo1} or {self.histo2}")
352 logging.error(f
"Missing histograms: {self.histo1} or {self.histo2}")
356 *dir_path, output_name = path_parts
357 directory_path =
"/".
join(dir_path)
358 summed = histo1.Clone(output_name)
361 root_file.cd(directory_path)
362 summed.Write(output_name, ROOT.TObject.kOverwrite)
363 logging.info(f
"Saved added histogram '{output_name}' in '{directory_path}'.")
372 histo1=fragment[
"histo1_name"],
373 histo2=fragment[
"histo2_name"],
374 output=fragment[
"output"],
375 delete_inputs=fragment.get(
"delete_inputs",
False)
379 def __init__(self, input_hist, new_path, x_label=None, y_label=None, xlow=None, xhigh=None):
388 ROOT.TH1.AddDirectory(
False)
392 raise RuntimeError(f
"Histogram '{self.input_hist}' not found.")
394 logging.error(f
"Histogram '{self.input_hist}' not found.")
398 *dir_path, new_name = path_parts
399 directory_path =
"/".
join(dir_path)
401 root_file.cd(directory_path)
403 if self.
xlow is None or self.
xhigh is None:
404 h_out = hist.Clone(new_name)
407 bin_low =
max(1,
min(ax.FindBin(self.
xlow), ax.GetNbins()))
408 bin_high =
max(1,
min(ax.FindBin(self.
xhigh), ax.GetNbins()))
409 nbins = bin_high - bin_low
410 h_out = ROOT.TH1D(new_name, hist.GetTitle(), nbins + 1,
float(self.
xlow),
float(self.
xhigh))
411 for i
in range(nbins + 1):
412 h_out.SetBinContent(i + 1, hist.GetBinContent(i + bin_low))
415 h_out.GetXaxis().SetTitle(self.
x_label)
417 h_out.GetYaxis().SetTitle(self.
y_label)
418 h_out.Write(new_name, ROOT.TObject.kOverwrite)
419 logging.info(f
"Renamed and updated histogram '{self.input_hist}' → '{self.new_path}' in '{directory_path}' | X-axis: '{self.x_label}' | Y-axis: '{self.y_label}' | Range: ({self.xlow}, {self.xhigh})")
424 input_hist=fragment[
"hist"],
425 new_path=fragment.get(
"final_histo_name", fragment[
"hist"]),
426 x_label=fragment.get(
"x_axis_label"),
427 y_label=fragment.get(
"y_axis_label"),
428 xlow=fragment.get(
"xlow"),
429 xhigh=fragment.get(
"xhigh")
442 toDelete=fragment[
"toDelete"]