Skip to content

Commit 78b7854

Browse files
authored
[PWGHF] Cut variation: add QA histograms, save output upon user's request (#16297)
1 parent c9f305c commit 78b7854

3 files changed

Lines changed: 137 additions & 56 deletions

File tree

PWGHF/D2H/Macros/compute_fraction_cutvar.py

Lines changed: 114 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,34 @@
1414

1515
import numpy as np # pylint: disable=import-error
1616
import ROOT # pylint: disable=import-error
17+
from enum import IntEnum, auto
1718
sys.path.insert(0, '..')
1819
from cut_variation import CutVarMinimiser
20+
from cut_variation import MinimisationStatus
1921
from style_formatter import set_object_style
2022

2123
# pylint: disable=no-member,too-many-locals,too-many-statements
2224

25+
class PlotType(IntEnum):
26+
Rawy = 0
27+
Eff = auto()
28+
Frac = auto()
29+
Cov = auto()
30+
Unc = auto()
31+
N = auto()
32+
33+
class ObjectToSave(IntEnum):
34+
Canvas = 0
35+
RawYield = auto()
36+
Uncertainty = auto()
37+
Efficiency = auto()
38+
Fraction = auto()
39+
CorrectedYield = auto()
40+
CorrelationMatrix = auto()
41+
Covariance = auto()
42+
CorrectedFraction = auto()
43+
MinimisationStatus = auto()
44+
N = auto()
2345

2446
def main(config):
2547
"""
@@ -61,17 +83,31 @@ def main(config):
6183
if (pt_bin_to_process != -1 and pt_bin_to_process < 1) or pt_bin_to_process > hist_rawy[0].GetNbinsX():
6284
sys.exit("\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit.")
6385

64-
is_draw_title_rawy = cfg.get("is_draw_title", {}).get("rawy", True)
65-
is_draw_title_eff = cfg.get("is_draw_title", {}).get("eff", False)
66-
is_draw_title_frac = cfg.get("is_draw_title", {}).get("frac", False)
67-
is_draw_title_cov = cfg.get("is_draw_title", {}).get("cov", False)
68-
is_draw_title_unc = cfg.get("is_draw_title", {}).get("unc", True)
69-
70-
is_save_canvas_as_macro_rawy = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False)
71-
is_save_canvas_as_macro_eff = cfg.get("is_save_canvas_as_macro", {}).get("eff", False)
72-
is_save_canvas_as_macro_frac = cfg.get("is_save_canvas_as_macro", {}).get("frac", False)
73-
is_save_canvas_as_macro_cov = cfg.get("is_save_canvas_as_macro", {}).get("cov", False)
74-
is_save_canvas_as_macro_unc = cfg.get("is_save_canvas_as_macro", {}).get("unc", False)
86+
is_draw_title = [False] * PlotType.N
87+
is_draw_title[PlotType.Rawy] = cfg.get("is_draw_title", {}).get("rawy", True)
88+
is_draw_title[PlotType.Eff] = cfg.get("is_draw_title", {}).get("eff", False)
89+
is_draw_title[PlotType.Frac] = cfg.get("is_draw_title", {}).get("frac", False)
90+
is_draw_title[PlotType.Cov] = cfg.get("is_draw_title", {}).get("cov", False)
91+
is_draw_title[PlotType.Unc] = cfg.get("is_draw_title", {}).get("unc", True)
92+
93+
is_save_canvas_as_macro = [False] * PlotType.N
94+
is_save_canvas_as_macro[PlotType.Rawy] = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False)
95+
is_save_canvas_as_macro[PlotType.Eff] = cfg.get("is_save_canvas_as_macro", {}).get("eff", False)
96+
is_save_canvas_as_macro[PlotType.Frac] = cfg.get("is_save_canvas_as_macro", {}).get("frac", False)
97+
is_save_canvas_as_macro[PlotType.Cov] = cfg.get("is_save_canvas_as_macro", {}).get("cov", False)
98+
is_save_canvas_as_macro[PlotType.Unc] = cfg.get("is_save_canvas_as_macro", {}).get("unc", False)
99+
100+
is_save_to_root_file = [False] * ObjectToSave.N
101+
is_save_to_root_file[ObjectToSave.Canvas] = cfg.get("is_save_to_root_file", {}).get("canvas", True)
102+
is_save_to_root_file[ObjectToSave.RawYield] = cfg.get("is_save_to_root_file", {}).get("raw_yield", True)
103+
is_save_to_root_file[ObjectToSave.Uncertainty] = cfg.get("is_save_to_root_file", {}).get("uncertainty", True)
104+
is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", True)
105+
is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", True)
106+
is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True)
107+
is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", True)
108+
is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", True)
109+
is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", True)
110+
is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get("minimisation_status", True)
75111

76112
if cfg["central_efficiency"]["computerawfrac"]:
77113
infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"])
@@ -100,7 +136,9 @@ def main(config):
100136
hist_covariance_npnp = hist_rawy[0].Clone("hCovNonPromptNonPrompt")
101137
hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt")
102138
hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt")
103-
for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt:
139+
hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus")
140+
hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf")
141+
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:
104142
histo.Reset()
105143
hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt")
106144
hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt")
@@ -109,6 +147,13 @@ def main(config):
109147
hist_covariance_npnp.GetYaxis().SetTitle("#sigma(non-prompt, non-prompt)")
110148
hist_corrfrac_prompt.GetYaxis().SetTitle("corrected fraction prompt")
111149
hist_corrfrac_nonprompt.GetYaxis().SetTitle("corrected fraction non-prompt")
150+
hist_minimisation_status.GetYaxis().SetTitle("minimisation status")
151+
hist_red_chi2.GetYaxis().SetTitle("#chi^{2}/ndf")
152+
hist_minimisation_status_title = ""
153+
for min_status in MinimisationStatus:
154+
hist_minimisation_status_title += (str(min_status.value) + " = " + min_status.name + ", ")
155+
hist_minimisation_status_title = hist_minimisation_status_title[:-2]
156+
hist_minimisation_status.SetTitle(hist_minimisation_status_title)
112157
set_object_style(
113158
hist_corry_prompt,
114159
color=ROOT.kRed + 1,
@@ -124,6 +169,8 @@ def main(config):
124169
set_object_style(hist_covariance_pnp)
125170
set_object_style(hist_covariance_pp)
126171
set_object_style(hist_covariance_npnp)
172+
set_object_style(hist_minimisation_status)
173+
set_object_style(hist_red_chi2)
127174
set_object_style(
128175
hist_corrfrac_prompt,
129176
color=ROOT.kRed + 1,
@@ -158,16 +205,15 @@ def main(config):
158205
)
159206

160207
pt_bin_to_process_name_suffix = ""
161-
if pt_bin_to_process != -1:
162-
pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process)
208+
if pt_bin_to_process != -1: pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process)
163209

164210
output_name_template = cfg['output']['file'].replace(".root", "") + pt_bin_to_process_name_suffix + ".root"
165211
output = ROOT.TFile(os.path.join(cfg["output"]["directory"], output_name_template), "recreate")
166212
n_sets = len(hist_rawy)
167213
pt_axis_title = hist_rawy[0].GetXaxis().GetTitle()
168214
for ipt in range(hist_rawy[0].GetNbinsX()):
169-
if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process:
170-
continue
215+
if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: continue
216+
all_vectors_monotonous = MinimisationStatus.Success
171217
pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1)
172218
pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1)
173219
print(f"\n\nINFO: processing pt range {ipt+1} from {pt_min} to {pt_max} {pt_axis_title}")
@@ -183,16 +229,22 @@ def main(config):
183229

184230
if cfg["minimisation"]["correlated"]:
185231
if not (np.all(rawy[1:] > rawy[:-1]) or np.all(rawy[1:] < rawy[:-1])):
232+
all_vectors_monotonous = MinimisationStatus.MonotonyViolation
186233
print("\0\33[33mWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\0\33[0m")
187234
print(f"raw yield vector elements = {rawy}\n")
188235
if not (np.all(unc_rawy[1:] > unc_rawy[:-1]) or np.all(unc_rawy[1:] < unc_rawy[:-1])):
236+
all_vectors_monotonous = MinimisationStatus.MonotonyViolation
189237
print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m")
190238
print(f"raw yield uncertainties vector elements = {unc_rawy}\n")
239+
if not (np.all(effp[1:] > effp[:-1]) or np.all(effp[1:] < effp[:-1])):
240+
sys.exit(f"\33[31mFatal error: the prompt efficiency vector is not monotonous. Check the input. Exit.\33[0m")
241+
if not (np.all(effnp[1:] > effnp[:-1]) or np.all(effnp[1:] < effnp[:-1])):
242+
sys.exit(f"\33[31mFatal error: the nonprompt efficiency vector is not monotonous. Check the input. Exit.\33[0m")
191243

192244
minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp)
193245
status = minimiser.minimise_system(cfg["minimisation"]["correlated"])
194246

195-
if status:
247+
if status != MinimisationStatus.Fail:
196248
hist_corry_prompt.SetBinContent(ipt + 1, minimiser.get_prompt_yield_and_error()[0])
197249
hist_corry_prompt.SetBinError(ipt + 1, minimiser.get_prompt_yield_and_error()[1])
198250
hist_corry_nonprompt.SetBinContent(ipt + 1, minimiser.get_nonprompt_yield_and_error()[0])
@@ -209,6 +261,8 @@ def main(config):
209261
hist_corrfrac_prompt.SetBinError(ipt + 1, corr_frac_prompt[1])
210262
hist_corrfrac_nonprompt.SetBinContent(ipt + 1, corr_frac_nonprompt[0])
211263
hist_corrfrac_nonprompt.SetBinError(ipt + 1, corr_frac_nonprompt[1])
264+
hist_minimisation_status.SetBinContent(ipt + 1, max(status, all_vectors_monotonous))
265+
hist_red_chi2.SetBinContent(ipt + 1, minimiser.get_red_chi2())
212266
if cfg["central_efficiency"]["computerawfrac"]:
213267
raw_frac_prompt = minimiser.get_raw_prompt_fraction(
214268
hist_central_effp.GetBinContent(ipt + 1), hist_central_effnp.GetBinContent(ipt + 1)
@@ -223,55 +277,56 @@ def main(config):
223277

224278
hist_bin_title = f"bin # {ipt+1}; {pt_axis_title}#in ({pt_min}; {pt_max})"
225279

226-
hist_bin_title_rawy = hist_bin_title if is_draw_title_rawy else ""
280+
hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else ""
227281
canv_rawy, histos_rawy, leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy)
228282
output.cd()
229-
canv_rawy.Write()
230-
for _, hist in histos_rawy.items():
231-
hist.Write()
232-
if (is_save_canvas_as_macro_rawy):
233-
canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C")
283+
if is_save_to_root_file[ObjectToSave.Canvas]: canv_rawy.Write()
284+
if is_save_to_root_file[ObjectToSave.RawYield]:
285+
for _, hist in histos_rawy.items():
286+
hist.Write()
287+
if is_save_canvas_as_macro[PlotType.Rawy]: canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C")
234288

235-
hist_bin_title_unc = hist_bin_title if is_draw_title_unc else ""
289+
hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else ""
236290
canv_unc, histos_unc, leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc)
237291
output.cd()
238-
canv_unc.Write()
239-
for _, hist in histos_unc.items():
240-
hist.Write()
241-
if (is_save_canvas_as_macro_unc):
242-
canv_unc.SaveAs(f"canv_unc_{ipt+1}.C")
292+
if is_save_to_root_file[ObjectToSave.Canvas]: canv_unc.Write()
293+
if is_save_to_root_file[ObjectToSave.Uncertainty]:
294+
for _, hist in histos_unc.items():
295+
hist.Write()
296+
if is_save_canvas_as_macro[PlotType.Unc]: canv_unc.SaveAs(f"canv_unc_{ipt+1}.C")
243297

244-
hist_bin_title_eff = hist_bin_title if is_draw_title_eff else ""
298+
hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else ""
245299
canv_eff, histos_eff, leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff)
246300
output.cd()
247-
canv_eff.Write()
248-
for _, hist in histos_eff.items():
249-
hist.Write()
250-
if (is_save_canvas_as_macro_eff):
251-
canv_eff.SaveAs(f"canv_eff_{ipt+1}.C")
301+
if is_save_to_root_file[ObjectToSave.Canvas]: canv_eff.Write()
302+
if is_save_to_root_file[ObjectToSave.Efficiency]:
303+
for _, hist in histos_eff.items():
304+
hist.Write()
305+
if is_save_canvas_as_macro[PlotType.Eff]: canv_eff.SaveAs(f"canv_eff_{ipt+1}.C")
252306

253-
hist_bin_title_frac = hist_bin_title if is_draw_title_frac else ""
307+
hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else ""
254308
canv_frac, histos_frac, leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac)
255309
output.cd()
256-
canv_frac.Write()
257-
for _, hist in histos_frac.items():
258-
hist.Write()
259-
if (is_save_canvas_as_macro_frac):
260-
canv_frac.SaveAs(f"canv_frac_{ipt+1}.C")
310+
if is_save_to_root_file[ObjectToSave.Canvas]: canv_frac.Write()
311+
if is_save_to_root_file[ObjectToSave.Fraction]:
312+
for _, hist in histos_frac.items():
313+
hist.Write()
314+
if is_save_canvas_as_macro[PlotType.Frac]: canv_frac.SaveAs(f"canv_frac_{ipt+1}.C")
261315

262-
hist_bin_title_cov = hist_bin_title if is_draw_title_cov else ""
316+
hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else ""
263317
canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov)
264318
output.cd()
265-
canv_cov.Write()
266-
histo_cov.Write()
267-
if (is_save_canvas_as_macro_cov):
268-
canv_cov.SaveAs(f"canv_cov_{ipt+1}.C")
319+
if is_save_to_root_file[ObjectToSave.Canvas]: canv_cov.Write()
320+
if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: histo_cov.Write()
321+
if is_save_canvas_as_macro[PlotType.Cov]: canv_cov.SaveAs(f"canv_cov_{ipt+1}.C")
269322
else:
270323
print(f"Minimization for pT {pt_min}, {pt_max} not successful")
324+
hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail)
271325
canv_rawy = ROOT.TCanvas("c_rawy_minimization_error", "Minimization error", 500, 500)
272326
canv_eff = ROOT.TCanvas("c_eff_minimization_error", "Minimization error", 500, 500)
273327
canv_frac = ROOT.TCanvas("c_frac_minimization_error", "Minimization error", 500, 500)
274328
canv_cov = ROOT.TCanvas("c_conv_minimization_error", "Minimization error", 500, 500)
329+
canv_unc = ROOT.TCanvas("c_unc_minimization_error", "Minimization error", 500, 500)
275330

276331
canv_combined = ROOT.TCanvas(f"canv_combined_{ipt}", "", 1000, 1000)
277332
canv_combined.Divide(2, 2)
@@ -309,13 +364,19 @@ def main(config):
309364
canv_unc.Print(f"{os.path.join(cfg['output']['directory'], output_name_unc_pdf)}{print_bracket}")
310365

311366
output.cd()
312-
hist_corry_prompt.Write()
313-
hist_corry_nonprompt.Write()
314-
hist_covariance_pnp.Write()
315-
hist_covariance_pp.Write()
316-
hist_covariance_npnp.Write()
317-
hist_corrfrac_prompt.Write()
318-
hist_corrfrac_nonprompt.Write()
367+
if is_save_to_root_file[ObjectToSave.CorrectedYield]:
368+
hist_corry_prompt.Write()
369+
hist_corry_nonprompt.Write()
370+
if is_save_to_root_file[ObjectToSave.Covariance]:
371+
hist_covariance_pnp.Write()
372+
hist_covariance_pp.Write()
373+
hist_covariance_npnp.Write()
374+
if is_save_to_root_file[ObjectToSave.CorrectedFraction]:
375+
hist_corrfrac_prompt.Write()
376+
hist_corrfrac_nonprompt.Write()
377+
if is_save_to_root_file[ObjectToSave.MinimisationStatus]:
378+
hist_minimisation_status.Write()
379+
hist_red_chi2.Write()
319380
if cfg["central_efficiency"]["computerawfrac"]:
320381
hist_frac_raw_prompt.Write()
321382
hist_frac_raw_nonprompt.Write()

PWGHF/D2H/Macros/config_cutvar_example.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@
7171
"cov": false,
7272
"unc": false
7373
},
74+
"is_save_to_root_file": {
75+
"canvas": true,
76+
"raw_yield": true,
77+
"uncertainty": true,
78+
"efficiency": true,
79+
"fraction": true,
80+
"corrected_yield": true,
81+
"correlation_matrix": true,
82+
"covariance": true,
83+
"corrected_fraction": true,
84+
"minimisation_status": true
85+
},
7486
"central_efficiency": {
7587
"computerawfrac": true,
7688
"inputdir": "path/to/central/efficiency",

PWGHF/D2H/Macros/cut_variation.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@
1111

1212
import numpy as np # pylint: disable=import-error
1313
import ROOT # pylint: disable=import-error
14+
from enum import IntEnum, auto
1415
sys.path.insert(0, '..')
1516
from style_formatter import set_global_style, set_object_style
1617

18+
class MinimisationStatus(IntEnum):
19+
Undefined = 0
20+
Success = auto()
21+
MonotonyViolation = auto()
22+
Fail = auto()
1723

1824
# pylint: disable=too-many-instance-attributes
1925
class CutVarMinimiser:
@@ -171,15 +177,15 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100)
171177
try:
172178
self.m_weights = np.linalg.inv(np.linalg.cholesky(self.m_cov_sets))
173179
except np.linalg.LinAlgError:
174-
return False
180+
return MinimisationStatus.Fail
175181
self.m_weights = self.m_weights.T * self.m_weights
176182
m_eff_tr = self.m_eff.T
177183

178184
self.m_covariance = (m_eff_tr * self.m_weights) * self.m_eff
179185
try:
180186
self.m_covariance = np.linalg.inv(np.linalg.cholesky(self.m_covariance))
181187
except np.linalg.LinAlgError:
182-
return False
188+
return MinimisationStatus.Fail
183189
self.m_covariance = self.m_covariance.T * self.m_covariance
184190

185191
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)
196202
m_corr_yields_old = np.copy(self.m_corr_yields)
197203

198204
print(f"INFO: number of processed iterations = {iteration+1}\n")
205+
minimisation_status = MinimisationStatus.Success
199206
if correlated:
200207
m_cov_sets_diag = np.diag(self.m_cov_sets)
201208
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])):
209+
minimisation_status = MinimisationStatus.MonotonyViolation
202210
print("\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m")
203211
print(f"residual vector uncertainties elements = {np.sqrt(m_cov_sets_diag)}\n")
204212

@@ -229,7 +237,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100)
229237
self.unc_frac_prompt[i_set] = unc_fp
230238
self.unc_frac_nonprompt[i_set] = unc_fnp
231239

232-
return True
240+
return minimisation_status
233241

234242
def get_red_chi2(self):
235243
"""

0 commit comments

Comments
 (0)