From 9895db64ed906bd92112df4ff1e449100eb64a36 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Sat, 11 Apr 2026 00:37:27 +0200 Subject: [PATCH 1/8] add minimisation status histogram --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 20 ++++++++++++++++++-- PWGHF/D2H/Macros/cut_variation.py | 14 +++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 0099185472b..693fc1087c1 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -16,6 +16,7 @@ import ROOT # pylint: disable=import-error sys.path.insert(0, '..') from cut_variation import CutVarMinimiser +from cut_variation import MinimisationStatus from style_formatter import set_object_style # pylint: disable=no-member,too-many-locals,too-many-statements @@ -100,7 +101,8 @@ def main(config): hist_covariance_npnp = hist_rawy[0].Clone("hCovNonPromptNonPrompt") hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt") hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt") - for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt: + hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus") + for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt, hist_minimisation_status: histo.Reset() hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt") hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt") @@ -109,6 +111,12 @@ def main(config): hist_covariance_npnp.GetYaxis().SetTitle("#sigma(non-prompt, non-prompt)") hist_corrfrac_prompt.GetYaxis().SetTitle("corrected fraction prompt") hist_corrfrac_nonprompt.GetYaxis().SetTitle("corrected fraction non-prompt") + hist_minimisation_status.GetYaxis().SetTitle("minimisation status") + hist_minimisation_status_title = "" + for min_status in MinimisationStatus: + hist_minimisation_status_title += (str(min_status.value) + " = " + min_status.name + ", ") + hist_minimisation_status_title = hist_minimisation_status_title[:-2] + hist_minimisation_status.SetTitle(hist_minimisation_status_title) set_object_style( hist_corry_prompt, color=ROOT.kRed + 1, @@ -124,6 +132,7 @@ def main(config): set_object_style(hist_covariance_pnp) set_object_style(hist_covariance_pp) set_object_style(hist_covariance_npnp) + set_object_style(hist_minimisation_status) set_object_style( hist_corrfrac_prompt, color=ROOT.kRed + 1, @@ -168,6 +177,7 @@ def main(config): for ipt in range(hist_rawy[0].GetNbinsX()): if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: continue + all_vectors_monotonous = MinimisationStatus.Success pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1) pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1) print(f"\n\nINFO: processing pt range {ipt+1} from {pt_min} to {pt_max} {pt_axis_title}") @@ -183,16 +193,18 @@ def main(config): if cfg["minimisation"]["correlated"]: if not (np.all(rawy[1:] > rawy[:-1]) or np.all(rawy[1:] < rawy[:-1])): + all_vectors_monotonous = MinimisationStatus.MonotonyViolation print("\0\33[33mWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\0\33[0m") print(f"raw yield vector elements = {rawy}\n") if not (np.all(unc_rawy[1:] > unc_rawy[:-1]) or np.all(unc_rawy[1:] < unc_rawy[:-1])): + all_vectors_monotonous = MinimisationStatus.MonotonyViolation print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m") print(f"raw yield uncertainties vector elements = {unc_rawy}\n") minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp) status = minimiser.minimise_system(cfg["minimisation"]["correlated"]) - if status: + if status != MinimisationStatus.Fail: hist_corry_prompt.SetBinContent(ipt + 1, minimiser.get_prompt_yield_and_error()[0]) hist_corry_prompt.SetBinError(ipt + 1, minimiser.get_prompt_yield_and_error()[1]) hist_corry_nonprompt.SetBinContent(ipt + 1, minimiser.get_nonprompt_yield_and_error()[0]) @@ -209,6 +221,7 @@ def main(config): hist_corrfrac_prompt.SetBinError(ipt + 1, corr_frac_prompt[1]) hist_corrfrac_nonprompt.SetBinContent(ipt + 1, corr_frac_nonprompt[0]) hist_corrfrac_nonprompt.SetBinError(ipt + 1, corr_frac_nonprompt[1]) + hist_minimisation_status.SetBinContent(ipt + 1, max(status, all_vectors_monotonous)) if cfg["central_efficiency"]["computerawfrac"]: raw_frac_prompt = minimiser.get_raw_prompt_fraction( hist_central_effp.GetBinContent(ipt + 1), hist_central_effnp.GetBinContent(ipt + 1) @@ -268,10 +281,12 @@ def main(config): canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") + hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail) canv_rawy = ROOT.TCanvas("c_rawy_minimization_error", "Minimization error", 500, 500) canv_eff = ROOT.TCanvas("c_eff_minimization_error", "Minimization error", 500, 500) canv_frac = ROOT.TCanvas("c_frac_minimization_error", "Minimization error", 500, 500) canv_cov = ROOT.TCanvas("c_conv_minimization_error", "Minimization error", 500, 500) + canv_unc = ROOT.TCanvas("c_unc_minimization_error", "Minimization error", 500, 500) canv_combined = ROOT.TCanvas(f"canv_combined_{ipt}", "", 1000, 1000) canv_combined.Divide(2, 2) @@ -316,6 +331,7 @@ def main(config): hist_covariance_npnp.Write() hist_corrfrac_prompt.Write() hist_corrfrac_nonprompt.Write() + hist_minimisation_status.Write() if cfg["central_efficiency"]["computerawfrac"]: hist_frac_raw_prompt.Write() hist_frac_raw_nonprompt.Write() diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index f9b49f15710..74a5a91d09c 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -13,7 +13,13 @@ import ROOT # pylint: disable=import-error sys.path.insert(0, '..') from style_formatter import set_global_style, set_object_style +from enum import IntEnum +class MinimisationStatus(IntEnum): + Undefined = 0 + Success = 1 + MonotonyViolation = 2 + Fail = 3 # pylint: disable=too-many-instance-attributes class CutVarMinimiser: @@ -171,7 +177,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) try: self.m_weights = np.linalg.inv(np.linalg.cholesky(self.m_cov_sets)) except np.linalg.LinAlgError: - return False + return MinimisationStatus.Fail self.m_weights = self.m_weights.T * self.m_weights m_eff_tr = self.m_eff.T @@ -179,7 +185,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) try: self.m_covariance = np.linalg.inv(np.linalg.cholesky(self.m_covariance)) except np.linalg.LinAlgError: - return False + return MinimisationStatus.Fail self.m_covariance = self.m_covariance.T * self.m_covariance self.m_corr_yields = self.m_covariance * (m_eff_tr * self.m_weights) * self.m_rawy @@ -196,9 +202,11 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) m_corr_yields_old = np.copy(self.m_corr_yields) print(f"INFO: number of processed iterations = {iteration+1}\n") + minimisation_status = MinimisationStatus.Success if correlated: m_cov_sets_diag = np.diag(self.m_cov_sets) if not (np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1])): + minimisation_status = MinimisationStatus.MonotonyViolation print("\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m") print(f"residual vector uncertainties elements = {np.sqrt(m_cov_sets_diag)}\n") @@ -229,7 +237,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) self.unc_frac_prompt[i_set] = unc_fp self.unc_frac_nonprompt[i_set] = unc_fnp - return True + return minimisation_status def get_red_chi2(self): """ From f8f096da4d7bdbc981848d1a6fd37153de6d51c6 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Sun, 19 Apr 2026 16:03:28 +0200 Subject: [PATCH 2/8] exit with fatal error when eff vector is not monotonous --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 693fc1087c1..d3c5514a111 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -200,6 +200,10 @@ def main(config): all_vectors_monotonous = MinimisationStatus.MonotonyViolation print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m") print(f"raw yield uncertainties vector elements = {unc_rawy}\n") + if not (np.all(effp[1:] > effp[:-1]) or np.all(effp[1:] < effp[:-1])): + sys.exit(f"\33[31mFatal error: the prompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") + if not (np.all(effnp[1:] > effnp[:-1]) or np.all(effnp[1:] < effnp[:-1])): + sys.exit(f"\33[31mFatal error: the nonprompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp) status = minimiser.minimise_system(cfg["minimisation"]["correlated"]) From 0bd17fdcaee26c13586363442d9a9796ae8e7ea8 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Wed, 22 Apr 2026 10:34:46 +0200 Subject: [PATCH 3/8] make is_draw_title and is_save_canvas_as_macro vectors --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 50 ++++++++++++--------- PWGHF/D2H/Macros/cut_variation.py | 9 ++-- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index d3c5514a111..84bc3c8def5 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -14,6 +14,7 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error +from enum import IntEnum, auto sys.path.insert(0, '..') from cut_variation import CutVarMinimiser from cut_variation import MinimisationStatus @@ -21,6 +22,13 @@ # pylint: disable=no-member,too-many-locals,too-many-statements +class PlotType(IntEnum): + Rawy = 0 + Eff = auto() + Frac = auto() + Cov = auto() + Unc = auto() + N = auto() def main(config): """ @@ -62,17 +70,19 @@ def main(config): if (pt_bin_to_process != -1 and pt_bin_to_process < 1) or pt_bin_to_process > hist_rawy[0].GetNbinsX(): sys.exit("\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit.") - is_draw_title_rawy = cfg.get("is_draw_title", {}).get("rawy", True) - is_draw_title_eff = cfg.get("is_draw_title", {}).get("eff", False) - is_draw_title_frac = cfg.get("is_draw_title", {}).get("frac", False) - is_draw_title_cov = cfg.get("is_draw_title", {}).get("cov", False) - is_draw_title_unc = cfg.get("is_draw_title", {}).get("unc", True) + is_draw_title = [False] * PlotType.N + is_draw_title[PlotType.Rawy] = cfg.get("is_draw_title", {}).get("rawy", True) + is_draw_title[PlotType.Eff] = cfg.get("is_draw_title", {}).get("eff", False) + is_draw_title[PlotType.Frac] = cfg.get("is_draw_title", {}).get("frac", False) + is_draw_title[PlotType.Cov] = cfg.get("is_draw_title", {}).get("cov", False) + is_draw_title[PlotType.Unc] = cfg.get("is_draw_title", {}).get("unc", True) - is_save_canvas_as_macro_rawy = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False) - is_save_canvas_as_macro_eff = cfg.get("is_save_canvas_as_macro", {}).get("eff", False) - is_save_canvas_as_macro_frac = cfg.get("is_save_canvas_as_macro", {}).get("frac", False) - is_save_canvas_as_macro_cov = cfg.get("is_save_canvas_as_macro", {}).get("cov", False) - is_save_canvas_as_macro_unc = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) + is_save_canvas_as_macro = [False] * PlotType.N + is_save_canvas_as_macro[PlotType.Rawy] = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False) + is_save_canvas_as_macro[PlotType.Eff] = cfg.get("is_save_canvas_as_macro", {}).get("eff", False) + is_save_canvas_as_macro[PlotType.Frac] = cfg.get("is_save_canvas_as_macro", {}).get("frac", False) + is_save_canvas_as_macro[PlotType.Cov] = cfg.get("is_save_canvas_as_macro", {}).get("cov", False) + is_save_canvas_as_macro[PlotType.Unc] = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) if cfg["central_efficiency"]["computerawfrac"]: infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"]) @@ -240,48 +250,48 @@ def main(config): hist_bin_title = f"bin # {ipt+1}; {pt_axis_title}#in ({pt_min}; {pt_max})" - hist_bin_title_rawy = hist_bin_title if is_draw_title_rawy else "" + hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else "" canv_rawy, histos_rawy, leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) output.cd() canv_rawy.Write() for _, hist in histos_rawy.items(): hist.Write() - if (is_save_canvas_as_macro_rawy): + if (is_save_canvas_as_macro[PlotType.Rawy]): canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") - hist_bin_title_unc = hist_bin_title if is_draw_title_unc else "" + hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" canv_unc, histos_unc, leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) output.cd() canv_unc.Write() for _, hist in histos_unc.items(): hist.Write() - if (is_save_canvas_as_macro_unc): + if (is_save_canvas_as_macro[PlotType.Unc]): canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") - hist_bin_title_eff = hist_bin_title if is_draw_title_eff else "" + hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" canv_eff, histos_eff, leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) output.cd() canv_eff.Write() for _, hist in histos_eff.items(): hist.Write() - if (is_save_canvas_as_macro_eff): + if (is_save_canvas_as_macro[PlotType.Eff]): canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") - hist_bin_title_frac = hist_bin_title if is_draw_title_frac else "" + hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" canv_frac, histos_frac, leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) output.cd() canv_frac.Write() for _, hist in histos_frac.items(): hist.Write() - if (is_save_canvas_as_macro_frac): + if (is_save_canvas_as_macro[PlotType.Frac]): canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") - hist_bin_title_cov = hist_bin_title if is_draw_title_cov else "" + hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) output.cd() canv_cov.Write() histo_cov.Write() - if (is_save_canvas_as_macro_cov): + if (is_save_canvas_as_macro[PlotType.Cov]): canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index 74a5a91d09c..3389da692ec 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -11,15 +11,16 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error +from enum import IntEnum, auto sys.path.insert(0, '..') from style_formatter import set_global_style, set_object_style -from enum import IntEnum class MinimisationStatus(IntEnum): Undefined = 0 - Success = 1 - MonotonyViolation = 2 - Fail = 3 + Success = auto() + MonotonyViolation = auto() + Fail = auto() + N = auto() # pylint: disable=too-many-instance-attributes class CutVarMinimiser: From 33d92deb025538a12ae832e70160b66783586111 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Wed, 22 Apr 2026 11:05:41 +0200 Subject: [PATCH 4/8] save objects to .root file optionally on user's request --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 76 +++++++++++++++------ PWGHF/D2H/Macros/config_cutvar_example.json | 12 ++++ PWGHF/D2H/Macros/cut_variation.py | 1 - 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 84bc3c8def5..c8101f9c43f 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -30,6 +30,19 @@ class PlotType(IntEnum): Unc = auto() N = auto() +class ObjectToSave(IntEnum): + Canvas = 0 + RawYield = auto() + Uncertainty = auto() + Efficiency = auto() + Fraction = auto() + CorrectedYield = auto() + CorrelationMatrix = auto() + Covariance = auto() + CorrectedFraction = auto() + MinimisationStatus = auto() + N = auto() + def main(config): """ Main function @@ -84,6 +97,18 @@ def main(config): is_save_canvas_as_macro[PlotType.Cov] = cfg.get("is_save_canvas_as_macro", {}).get("cov", False) is_save_canvas_as_macro[PlotType.Unc] = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) + is_save_to_root_file = [False] * ObjectToSave.N + is_save_to_root_file[ObjectToSave.Canvas] = cfg.get("is_save_to_root_file", {}).get("canvas", False) + is_save_to_root_file[ObjectToSave.RawYield] = cfg.get("is_save_to_root_file", {}).get("raw_yield", False) + is_save_to_root_file[ObjectToSave.Uncertainty] = cfg.get("is_save_to_root_file", {}).get("uncertainty", False) + is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", False) + is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", False) + is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True) + is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", False) + is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", False) + is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", False) + is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get("minimisation_status", True) + if cfg["central_efficiency"]["computerawfrac"]: infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"]) infile_central_eff = ROOT.TFile.Open(infile_name) @@ -253,44 +278,48 @@ def main(config): hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else "" canv_rawy, histos_rawy, leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) output.cd() - canv_rawy.Write() - for _, hist in histos_rawy.items(): - hist.Write() + if is_save_to_root_file[ObjectToSave.Canvas]: canv_rawy.Write() + if is_save_to_root_file[ObjectToSave.RawYield]: + for _, hist in histos_rawy.items(): + hist.Write() if (is_save_canvas_as_macro[PlotType.Rawy]): canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" canv_unc, histos_unc, leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) output.cd() - canv_unc.Write() - for _, hist in histos_unc.items(): - hist.Write() + if is_save_to_root_file[ObjectToSave.Canvas]: canv_unc.Write() + if is_save_to_root_file[ObjectToSave.Uncertainty]: + for _, hist in histos_unc.items(): + hist.Write() if (is_save_canvas_as_macro[PlotType.Unc]): canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" canv_eff, histos_eff, leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) output.cd() - canv_eff.Write() - for _, hist in histos_eff.items(): - hist.Write() + if is_save_to_root_file[ObjectToSave.Canvas]: canv_eff.Write() + if is_save_to_root_file[ObjectToSave.Efficiency]: + for _, hist in histos_eff.items(): + hist.Write() if (is_save_canvas_as_macro[PlotType.Eff]): canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" canv_frac, histos_frac, leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) output.cd() - canv_frac.Write() - for _, hist in histos_frac.items(): - hist.Write() + if is_save_to_root_file[ObjectToSave.Canvas]: canv_frac.Write() + if is_save_to_root_file[ObjectToSave.Fraction]: + for _, hist in histos_frac.items(): + hist.Write() if (is_save_canvas_as_macro[PlotType.Frac]): canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) output.cd() - canv_cov.Write() - histo_cov.Write() + if is_save_to_root_file[ObjectToSave.Canvas]: canv_cov.Write() + if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: histo_cov.Write() if (is_save_canvas_as_macro[PlotType.Cov]): canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") else: @@ -338,14 +367,17 @@ def main(config): canv_unc.Print(f"{os.path.join(cfg['output']['directory'], output_name_unc_pdf)}{print_bracket}") output.cd() - hist_corry_prompt.Write() - hist_corry_nonprompt.Write() - hist_covariance_pnp.Write() - hist_covariance_pp.Write() - hist_covariance_npnp.Write() - hist_corrfrac_prompt.Write() - hist_corrfrac_nonprompt.Write() - hist_minimisation_status.Write() + if is_save_to_root_file[ObjectToSave.CorrectedYield]: + hist_corry_prompt.Write() + hist_corry_nonprompt.Write() + if is_save_to_root_file[ObjectToSave.Covariance]: + hist_covariance_pnp.Write() + hist_covariance_pp.Write() + hist_covariance_npnp.Write() + if is_save_to_root_file[ObjectToSave.CorrectedFraction]: + hist_corrfrac_prompt.Write() + hist_corrfrac_nonprompt.Write() + if is_save_to_root_file[ObjectToSave.MinimisationStatus]: hist_minimisation_status.Write() if cfg["central_efficiency"]["computerawfrac"]: hist_frac_raw_prompt.Write() hist_frac_raw_nonprompt.Write() diff --git a/PWGHF/D2H/Macros/config_cutvar_example.json b/PWGHF/D2H/Macros/config_cutvar_example.json index e1b768b807d..a2a1fcfa852 100644 --- a/PWGHF/D2H/Macros/config_cutvar_example.json +++ b/PWGHF/D2H/Macros/config_cutvar_example.json @@ -71,6 +71,18 @@ "cov": false, "unc": false }, + "is_save_to_root_file": { + "canvas": false, + "raw_yield": false, + "uncertainty": false, + "efficiency": false, + "fraction": false, + "corrected_yield": true, + "correlation_matrix": false, + "covariance": false, + "corrected_fraction": false, + "minimisation_status": true + }, "central_efficiency": { "computerawfrac": true, "inputdir": "path/to/central/efficiency", diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index 3389da692ec..6d45ac25607 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -20,7 +20,6 @@ class MinimisationStatus(IntEnum): Success = auto() MonotonyViolation = auto() Fail = auto() - N = auto() # pylint: disable=too-many-instance-attributes class CutVarMinimiser: From 32fabcc1acbbff24e7287c40336d626f7bed1bdc Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Wed, 22 Apr 2026 11:51:40 +0200 Subject: [PATCH 5/8] place if-statement and 1-line-expression into same line --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index c8101f9c43f..fd5b20e92b4 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -202,16 +202,14 @@ def main(config): ) pt_bin_to_process_name_suffix = "" - if pt_bin_to_process != -1: - pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process) + if pt_bin_to_process != -1: pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process) output_name_template = cfg['output']['file'].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" output = ROOT.TFile(os.path.join(cfg["output"]["directory"], output_name_template), "recreate") n_sets = len(hist_rawy) pt_axis_title = hist_rawy[0].GetXaxis().GetTitle() for ipt in range(hist_rawy[0].GetNbinsX()): - if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: - continue + if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: continue all_vectors_monotonous = MinimisationStatus.Success pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1) pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1) @@ -282,8 +280,7 @@ def main(config): if is_save_to_root_file[ObjectToSave.RawYield]: for _, hist in histos_rawy.items(): hist.Write() - if (is_save_canvas_as_macro[PlotType.Rawy]): - canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") + if is_save_canvas_as_macro[PlotType.Rawy]: canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" canv_unc, histos_unc, leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) @@ -292,8 +289,7 @@ def main(config): if is_save_to_root_file[ObjectToSave.Uncertainty]: for _, hist in histos_unc.items(): hist.Write() - if (is_save_canvas_as_macro[PlotType.Unc]): - canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") + if is_save_canvas_as_macro[PlotType.Unc]: canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" canv_eff, histos_eff, leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) @@ -302,8 +298,7 @@ def main(config): if is_save_to_root_file[ObjectToSave.Efficiency]: for _, hist in histos_eff.items(): hist.Write() - if (is_save_canvas_as_macro[PlotType.Eff]): - canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") + if is_save_canvas_as_macro[PlotType.Eff]: canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" canv_frac, histos_frac, leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) @@ -312,16 +307,14 @@ def main(config): if is_save_to_root_file[ObjectToSave.Fraction]: for _, hist in histos_frac.items(): hist.Write() - if (is_save_canvas_as_macro[PlotType.Frac]): - canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") + if is_save_canvas_as_macro[PlotType.Frac]: canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) output.cd() if is_save_to_root_file[ObjectToSave.Canvas]: canv_cov.Write() if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: histo_cov.Write() - if (is_save_canvas_as_macro[PlotType.Cov]): - canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") + if is_save_canvas_as_macro[PlotType.Cov]: canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail) From 442a21c35845ed491b70e568609289fd668f9bd3 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Wed, 22 Apr 2026 11:54:02 +0200 Subject: [PATCH 6/8] default is_save_to_root_file's to true --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index fd5b20e92b4..649f3114419 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -98,15 +98,15 @@ def main(config): is_save_canvas_as_macro[PlotType.Unc] = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) is_save_to_root_file = [False] * ObjectToSave.N - is_save_to_root_file[ObjectToSave.Canvas] = cfg.get("is_save_to_root_file", {}).get("canvas", False) - is_save_to_root_file[ObjectToSave.RawYield] = cfg.get("is_save_to_root_file", {}).get("raw_yield", False) - is_save_to_root_file[ObjectToSave.Uncertainty] = cfg.get("is_save_to_root_file", {}).get("uncertainty", False) - is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", False) - is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", False) + is_save_to_root_file[ObjectToSave.Canvas] = cfg.get("is_save_to_root_file", {}).get("canvas", True) + is_save_to_root_file[ObjectToSave.RawYield] = cfg.get("is_save_to_root_file", {}).get("raw_yield", True) + is_save_to_root_file[ObjectToSave.Uncertainty] = cfg.get("is_save_to_root_file", {}).get("uncertainty", True) + is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", True) + is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", True) is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True) - is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", False) - is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", False) - is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", False) + is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", True) + is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", True) + is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", True) is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get("minimisation_status", True) if cfg["central_efficiency"]["computerawfrac"]: From 647c1b65c4aa9d30b8e308b19fb11614bed5be08 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Wed, 22 Apr 2026 11:55:21 +0200 Subject: [PATCH 7/8] default is_save_to_root_file's to true (x2) --- PWGHF/D2H/Macros/config_cutvar_example.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PWGHF/D2H/Macros/config_cutvar_example.json b/PWGHF/D2H/Macros/config_cutvar_example.json index a2a1fcfa852..f13a2d53d7f 100644 --- a/PWGHF/D2H/Macros/config_cutvar_example.json +++ b/PWGHF/D2H/Macros/config_cutvar_example.json @@ -72,15 +72,15 @@ "unc": false }, "is_save_to_root_file": { - "canvas": false, - "raw_yield": false, - "uncertainty": false, - "efficiency": false, - "fraction": false, + "canvas": true, + "raw_yield": true, + "uncertainty": true, + "efficiency": true, + "fraction": true, "corrected_yield": true, - "correlation_matrix": false, - "covariance": false, - "corrected_fraction": false, + "correlation_matrix": true, + "covariance": true, + "corrected_fraction": true, "minimisation_status": true }, "central_efficiency": { From 6a96d3d428cd30da7ee2fd7443750128e2c3b7a7 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Fri, 24 Apr 2026 18:30:16 +0200 Subject: [PATCH 8/8] add chi2/ndf histogram --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 649f3114419..0e5ced6f8a5 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -137,7 +137,8 @@ def main(config): hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt") hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt") hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus") - for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt, hist_minimisation_status: + hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") + for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt, hist_minimisation_status, hist_red_chi2: histo.Reset() hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt") hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt") @@ -147,6 +148,7 @@ def main(config): hist_corrfrac_prompt.GetYaxis().SetTitle("corrected fraction prompt") hist_corrfrac_nonprompt.GetYaxis().SetTitle("corrected fraction non-prompt") hist_minimisation_status.GetYaxis().SetTitle("minimisation status") + hist_red_chi2.GetYaxis().SetTitle("#chi^{2}/ndf") hist_minimisation_status_title = "" for min_status in MinimisationStatus: hist_minimisation_status_title += (str(min_status.value) + " = " + min_status.name + ", ") @@ -168,6 +170,7 @@ def main(config): set_object_style(hist_covariance_pp) set_object_style(hist_covariance_npnp) set_object_style(hist_minimisation_status) + set_object_style(hist_red_chi2) set_object_style( hist_corrfrac_prompt, color=ROOT.kRed + 1, @@ -259,6 +262,7 @@ def main(config): hist_corrfrac_nonprompt.SetBinContent(ipt + 1, corr_frac_nonprompt[0]) hist_corrfrac_nonprompt.SetBinError(ipt + 1, corr_frac_nonprompt[1]) hist_minimisation_status.SetBinContent(ipt + 1, max(status, all_vectors_monotonous)) + hist_red_chi2.SetBinContent(ipt + 1, minimiser.get_red_chi2()) if cfg["central_efficiency"]["computerawfrac"]: raw_frac_prompt = minimiser.get_raw_prompt_fraction( hist_central_effp.GetBinContent(ipt + 1), hist_central_effnp.GetBinContent(ipt + 1) @@ -370,7 +374,9 @@ def main(config): if is_save_to_root_file[ObjectToSave.CorrectedFraction]: hist_corrfrac_prompt.Write() hist_corrfrac_nonprompt.Write() - if is_save_to_root_file[ObjectToSave.MinimisationStatus]: hist_minimisation_status.Write() + if is_save_to_root_file[ObjectToSave.MinimisationStatus]: + hist_minimisation_status.Write() + hist_red_chi2.Write() if cfg["central_efficiency"]["computerawfrac"]: hist_frac_raw_prompt.Write() hist_frac_raw_nonprompt.Write()