10Validation plots for CDC dedx calibration.
16import matplotlib.pyplot
as plt
19from matplotlib.backends.backend_pdf
import PdfPages
22import process_wiregain
as pw
23import process_cosgain
as pc
24import process_onedcell
as oned
25import process_rungain
as rg
28from prompt
import ValidationSettings
30settings = ValidationSettings(name=
"CDC dedx",
34 "GT":
"data_prompt_rel09",
38def get_latest_calibration_dir(base_dir, cal_name):
43 for idir
in os.listdir(base_dir):
45 match = re.fullmatch(rf
"{re.escape(cal_name)}(\d+)", idir)
50 idx = int(match.group(1))
56 if latest_dir
is None:
57 basf2.B2FATAL(f
"No calibration directory found for {cal_name}")
59 basf2.B2INFO(f
"Using latest calibration directory: {latest_dir}")
61 return os.path.join(base_dir, latest_dir)
64def save_to_pdf(pdf, fig):
70def read_txt(filepath, columns, sep=r"\s+"):
71 if not os.path.exists(filepath):
72 basf2.B2ERROR(f
"File not found: {filepath}")
74 return pd.read_csv(filepath, sep=sep, header=
None, names=columns)
77def make_pdf_path(prefix, suffix):
78 pdf_path = os.path.join(
"plots",
"validation", f
"{prefix}_{suffix}.pdf")
79 os.makedirs(os.path.dirname(pdf_path), exist_ok=
True)
83def get_positive_minmax(series):
84 positive = series[series > 0]
85 ymin = positive.min()
if not positive.empty
else series.min()
90def rungain_validation(path, suffix):
91 val_path = os.path.join(path,
"plots",
"run", f
"dedx_vs_run_{suffix}.txt")
92 df = read_txt(val_path, [
"run",
"mean",
"mean_err",
"reso",
"reso_err"])
96 df[
'run'] = df[
'run'].astype(str)
98 pdf_path = make_pdf_path(
"dedx_vs_run", suffix)
100 with PdfPages(pdf_path)
as pdf:
101 fig, ax = plt.subplots(1, 2, figsize=(20, 6))
103 space = max(10, min(50, int(200 / max(n, 1))))
106 ymin, ymax = get_positive_minmax(df[
'mean'])
107 pc.hist(y_min=ymin-0.02, y_max=ymax+0.02, xlabel=
"Run range", ylabel=
"dE/dx mean", space=space, ax=ax[0])
108 ax[0].errorbar(df[
'run'], df[
'mean'], yerr=df[
'mean_err'], fmt=
'*', markersize=8, rasterized=
True, label=
'Bhabha mean')
109 ax[0].legend(fontsize=12)
110 ax[0].set_title(
'dE/dx Mean vs Run', fontsize=14)
113 ymin, ymax = get_positive_minmax(df[
'reso'])
114 pc.hist(y_min=ymin-0.01, y_max=ymax+0.01, xlabel=
"Run range", ylabel=
"dE/dx reso", space=space, ax=ax[1])
115 ax[1].errorbar(df[
'run'], df[
'reso'], yerr=df[
'reso_err'], fmt=
'*', markersize=8, rasterized=
True, label=
'Bhabha reso')
116 ax[1].legend(fontsize=12)
117 ax[1].set_title(
'dE/dx Resolution vs Run', fontsize=14)
119 fig.suptitle(
"dE/dx vs Run", fontsize=20)
120 save_to_pdf(pdf, fig)
123def wiregain_validation(path, suffix):
125 val_path_gwire = os.path.join(path,
"plots",
"wire", f
"dedx_mean_gwire_{suffix}.txt")
126 val_path_bwire = os.path.join(path,
"plots",
"wire", f
"dedx_mean_badwire_{suffix}.txt")
127 val_path_layer = os.path.join(path,
"plots",
"wire", f
"dedx_mean_layer_{suffix}.txt")
129 df_gwire = read_txt(val_path_gwire, [
"wire",
"mean"])
130 df_bwire = read_txt(val_path_bwire, [
"wire",
"mean"])
131 df_layer = read_txt(val_path_layer, [
"layer",
"mean",
"gmean"])
133 if df_gwire
is None or df_bwire
is None or df_layer
is None:
136 pdf_path = make_pdf_path(
"dedx_vs_wire_layer", suffix)
138 with PdfPages(pdf_path)
as pdf:
139 fig, ax = plt.subplots(2, 2, figsize=(20, 12))
141 ymin, ymax = get_positive_minmax(df_gwire[
'mean'])
143 pc.hist(y_min=ymin-0.05, y_max=ymax+0.05, xlabel=
"Wire", ylabel=
"dE/dx mean", space=1000, ax=ax[0, 0])
144 ax[0, 0].
plot(df_gwire[
'wire'], df_gwire[
'mean'],
'*', markersize=5, rasterized=
True)
145 ax[0, 0].set_title(
'dE/dx Mean vs good Wire', fontsize=14)
147 ymin, ymax = get_positive_minmax(df_bwire[
'mean'])
149 pc.hist(y_min=ymin-0.05, y_max=ymax+0.05, xlabel=
"Wire", ylabel=
"dE/dx mean", space=1000, ax=ax[1, 0])
150 ax[1, 0].
plot(df_bwire[
'wire'], df_bwire[
'mean'],
'*', markersize=5, rasterized=
True)
151 ax[1, 0].set_title(
'dE/dx Mean vs bad Wire', fontsize=14)
153 ymin, ymax = get_positive_minmax(df_layer[
'mean'])
155 pc.hist(x_min=0, x_max=56, y_min=ymin-0.05, y_max=ymax+0.05, xlabel=
"Layer", ylabel=
"dE/dx mean", space=3, ax=ax[0, 1])
156 ax[0, 1].
plot(df_layer[
'layer'], df_layer[
'mean'],
'*', markersize=10, rasterized=
True)
157 ax[0, 1].set_title(
'dE/dx Mean vs Layer', fontsize=14)
159 ymin, ymax = get_positive_minmax(df_layer[
'gmean'])
160 pc.hist(x_min=0, x_max=56, y_min=ymin-0.02, y_max=ymax+0.02, xlabel=
"Layer", ylabel=
"dE/dx mean", space=3, ax=ax[1, 1])
161 ax[1, 1].
plot(df_layer[
'layer'], df_layer[
'gmean'],
'*', markersize=10, rasterized=
True)
162 ax[1, 1].set_title(
'dE/dx Mean vs Layer (good wires)', fontsize=14)
164 fig.suptitle(f
"dE/dx vs #wire {suffix}", fontsize=20)
165 save_to_pdf(pdf, fig)
168def cosgain_validation(path, suffix):
169 val_path_el = os.path.join(path,
"plots",
"costh", f
"dedx_vs_cos_electrons_{suffix}.txt")
170 val_path_po = os.path.join(path,
"plots",
"costh", f
"dedx_vs_cos_positrons_{suffix}.txt")
172 df_el = read_txt(val_path_el, [
"cos",
"mean",
"mean_err",
"reso",
"reso_err"])
173 df_po = read_txt(val_path_po, [
"cos",
"mean",
"mean_err",
"reso",
"reso_err"])
175 if df_el
is None or df_po
is None:
179 df_el = df_el.sort_values(by=
'cos').reset_index(drop=
True)
180 df_po = df_po.sort_values(by=
'cos').reset_index(drop=
True)
183 mean_avg = (df_el[
'mean'] + df_po[
'mean']) / 2
184 err_avg = 0.5 * np.sqrt(df_el[
'mean_err']**2 + df_po[
'mean_err']**2)
185 df_sum = pd.DataFrame({
'cos': df_el[
'cos'],
'mean_sum': mean_avg,
'err_avg': err_avg})
187 pdf_path = make_pdf_path(
"dedx_vs_cosine", suffix)
189 with PdfPages(pdf_path)
as pdf:
190 fig, ax = plt.subplots(1, 2, figsize=(20, 6))
192 pc.hist(x_min=-1.0, x_max=1.0, y_min=0.96, y_max=1.03, xlabel=
r"cos#theta", ylabel=
"dE/dx mean", space=0.1, ax=ax[0])
196 yerr=df_el[
'mean_err'],
204 yerr=df_po[
'mean_err'],
209 ax[0].errorbar(df_sum[
'cos'], df_sum[
'mean_sum'], yerr=df_sum[
'err_avg'], fmt=
'*',
210 markersize=10, rasterized=
True, label=
r'average of e^{+} and e^{-}')
211 ax[0].legend(fontsize=17)
212 ax[0].set_title(
'dE/dx Mean vs cosine', fontsize=14)
215 pc.hist(x_min=-1.0, x_max=1.0, y_min=0.04, y_max=0.13, xlabel=
r"cos#theta", ylabel=
"dE/dx reso", space=0.1, ax=ax[1])
219 yerr=df_el[
'reso_err'],
227 yerr=df_po[
'reso_err'],
232 ax[1].legend(fontsize=17)
233 ax[1].set_title(
'dE/dx Resolution vs cosine', fontsize=14)
235 fig.suptitle(fr
"dE/dx vs cos$\theta$ {suffix}", fontsize=20)
236 save_to_pdf(pdf, fig)
239def injection_validation(path, suffix):
241 cols = [
"var",
"bin",
"mean",
"mean_err",
"reso",
"reso_err"]
243 val_path_ler = os.path.join(path,
"plots",
"injection", f
"dedx_vs_inj_ler_{suffix}.txt")
244 val_path_her = os.path.join(path,
"plots",
"injection", f
"dedx_vs_inj_her_{suffix}.txt")
246 df_ler = read_txt(val_path_ler, cols)
247 df_her = read_txt(val_path_her, cols)
250 val_path_ler_nocor = os.path.join(path,
"plots",
"injection", f
"dedx_vs_inj_nocor_ler_{suffix}.txt")
251 val_path_her_nocor = os.path.join(path,
"plots",
"injection", f
"dedx_vs_inj_nocor_her_{suffix}.txt")
253 df_ler_nocor = read_txt(val_path_ler_nocor, cols)
254 df_her_nocor = read_txt(val_path_her_nocor, cols)
256 if df_ler
is None or df_her
is None or df_ler_nocor
is None or df_her_nocor
is None:
259 for df
in [df_ler, df_her, df_ler_nocor, df_her_nocor]:
260 df[
"bin"] = df[
"bin"].astype(str)
262 pdf_path = make_pdf_path(
"dedx_mean_inj", suffix)
264 with PdfPages(pdf_path)
as pdf:
266 fig, ax = plt.subplots(2, 1, figsize=(18, 10), sharex=
True)
268 ymin, ymax = get_positive_minmax(
269 pd.concat([df_ler[
"mean"], df_her[
"mean"]])
272 pc.hist(y_min=ymin - 0.01, y_max=ymax + 0.01,
273 xlabel=
"", ylabel=
"dE/dx mean",
276 ax[0].errorbar(df_ler[
'bin'], df_ler[
'mean'], yerr=df_ler[
'mean_err'],
277 fmt=
'*', markersize=10, rasterized=
True, label=
'LER')
278 ax[0].errorbar(df_her[
'bin'], df_her[
'mean'], yerr=df_her[
'mean_err'],
279 fmt=
'*', markersize=10, rasterized=
True, label=
'HER')
281 ax[0].legend(fontsize=14)
282 ax[0].set_title(
"Corrected", fontsize=16)
284 all_means = pd.concat([
285 df_ler[
"mean"], df_her[
"mean"],
286 df_ler_nocor[
"mean"], df_her_nocor[
"mean"]
289 positive_means = all_means[all_means > 0]
290 ymin2 = positive_means.min()
if not positive_means.empty
else all_means.min()
291 ymax2 = all_means.max()
293 pc.hist(y_min=ymin2 - 0.01, y_max=ymax2 + 0.01,
294 xlabel=
"injection time", ylabel=
"dE/dx mean",
298 (
"LER corr", df_ler,
"o"),
299 (
"HER corr", df_her,
"s"),
300 (
"LER no corr", df_ler_nocor,
"^"),
301 (
"HER no corr", df_her_nocor,
"D"),
304 for label, df, marker
in datasets:
305 ax[1].errorbar(df[
'bin'], df[
'mean'], yerr=df[
'mean_err'],
306 fmt=marker, markersize=6, rasterized=
True, label=label)
308 ax[1].legend(fontsize=12)
309 ax[1].set_title(
"Corrected vs No correction", fontsize=16)
311 fig.suptitle(f
"dE/dx vs Injection time {suffix}", fontsize=20)
313 plt.tight_layout(rect=[0, 0, 1, 0.96])
314 save_to_pdf(pdf, fig)
317def mom_validation(path, suffix):
321 "cos$\\theta > 0.0$",
322 "cos$\\theta < 0.0$",
323 "cos$\\theta \\leq -0.8$",
324 "cos$\\theta > -0.8$ and $\\cos\\theta \\leq -0.6$",
325 "cos$\\theta > -0.6$ and $\\cos\\theta \\leq -0.4$",
326 "cos$\\theta > -0.4$ and $\\cos\\theta \\leq -0.2$",
327 "cos$\\theta > -0.2$ and $\\cos\\theta \\leq 0$",
328 "cos$\\theta > 0$ and $\\cos\\theta \\leq 0.2$",
329 "cos$\\theta > 0.2$ and $\\cos\\theta \\leq 0.4$",
330 "cos$\\theta > 0.4$ and $\\cos\\theta \\leq 0.6$",
331 "cos$\\theta > 0.6$ and $\\cos\\theta \\leq 0.8$",
337 "low": make_pdf_path(
"dedx_vs_mom", suffix),
338 "high": make_pdf_path(
"dedx_vs_mom", f
"{suffix}_cosbins"),
341 with PdfPages(pdf_paths[
"low"])
as pdf_low, PdfPages(pdf_paths[
"high"])
as pdf_high:
343 cols = [
"mom",
"mean",
"mean_err",
"reso",
"reso_err"]
344 val_path_el = os.path.join(path,
"plots",
"mom", f
"dedx_vs_mom_{i}_elec_{suffix}.txt")
345 val_path_po = os.path.join(path,
"plots",
"mom", f
"dedx_vs_mom_{i}_posi_{suffix}.txt")
347 df_el = read_txt(val_path_el, cols)
348 df_po = read_txt(val_path_po, cols)
350 if df_el
is None or df_po
is None:
355 fig, ax = plt.subplots(2, 2, figsize=(20, 12))
357 ymin, ymax = get_positive_minmax(df_el[
'mean'])
360 {
"xlim": (-7, 7),
"ylim": (ymin-0.01, ymax+0.01),
361 "ylabel":
"dE/dx mean",
"df_col":
"mean",
"err_col":
"mean_err",
362 "title":
"dE/dx Mean vs momentum"},
363 {
"xlim": (-7, 7),
"ylim": (0.04, 0.1),
364 "ylabel":
"dE/dx reso",
"df_col":
"reso",
"err_col":
"reso_err",
365 "title":
"dE/dx resolution vs momentum"},
366 {
"xlim": (-3, 3),
"ylim": (ymin-0.01, ymax+0.01),
367 "ylabel":
"dE/dx mean",
"df_col":
"mean",
"err_col":
"mean_err",
368 "title":
"dE/dx Mean vs momentum (zoomed)"},
369 {
"xlim": (-3, 3),
"ylim": (0.04, 0.1),
370 "ylabel":
"dE/dx reso",
"df_col":
"reso",
"err_col":
"reso_err",
371 "title":
"dE/dx resolution vs momentum (zoomed)"},
374 for ax_i, panel
in zip(ax.flat, panels):
375 pc.hist(x_min=panel[
"xlim"][0], x_max=panel[
"xlim"][1],
376 y_min=panel[
"ylim"][0], y_max=panel[
"ylim"][1],
377 xlabel=
"Momentum", ylabel=panel[
"ylabel"],
380 ax_i.errorbar(df_el[
'mom'], df_el[panel[
"df_col"]],
381 yerr=df_el[panel[
"err_col"]],
382 fmt=
'*', markersize=10, rasterized=
True, label=
'electron')
383 ax_i.errorbar(df_po[
'mom'], df_po[panel[
"df_col"]],
384 yerr=df_po[panel[
"err_col"]],
385 fmt=
'*', markersize=10, rasterized=
True, label=
'positron')
386 ax_i.legend(fontsize=17)
387 ax_i.set_title(panel[
"title"], fontsize=14)
388 if i == 3
and panel[
"df_col"] ==
"reso":
389 ymin, ymax = ax_i.get_ylim()
390 ax_i.set_ylim(ymin, ymax * 1.5)
392 fig.suptitle(f
"dE/dx vs Momentum ({cos_labels[i]}) {suffix}", fontsize=20)
397 save_to_pdf(pdf_low, fig)
399 save_to_pdf(pdf_high, fig)
402def oneDcell_validation(path, suffix):
404 val_path_il = os.path.join(path,
"plots",
"oneD", f
"dedx_vs_1D_IL_{suffix}.txt")
405 val_path_ol = os.path.join(path,
"plots",
"oneD", f
"dedx_vs_1D_OL_{suffix}.txt")
407 df_il = read_txt(val_path_il, [
"enta",
"mean"])
408 df_ol = read_txt(val_path_ol, [
"enta",
"mean"])
410 if df_il
is None or df_ol
is None:
413 pdf_path = make_pdf_path(
"dedx_vs_enta", suffix)
415 with PdfPages(pdf_path)
as pdf:
416 fig, ax = plt.subplots(2, 2, figsize=(20, 12))
418 pc.hist(x_min=-1.5, x_max=1.5, y_min=0.9, y_max=1.07, xlabel=
r"entaRS", ylabel=
"dE/dx mean", space=0.3, ax=ax[0, 0])
419 ax[0, 0].
plot(df_il[
'enta'], df_il[
'mean'],
'-', markersize=10, rasterized=
True, label=
'IL')
420 ax[0, 0].legend(fontsize=17)
421 ax[0, 0].set_title(
'dE/dx Mean vs entaRS (IL)', fontsize=14)
423 pc.hist(x_min=-1.5, x_max=1.5, y_min=0.9, y_max=1.05, xlabel=
r"entaRS", ylabel=
"dE/dx mean", space=0.3, ax=ax[0, 1])
424 ax[0, 1].
plot(df_ol[
'enta'], df_ol[
'mean'],
'-', markersize=10, rasterized=
True, label=
'OL')
425 ax[0, 1].legend(fontsize=17)
426 ax[0, 1].set_title(
'dE/dx Mean vs entaRS (OL)', fontsize=14)
428 pc.hist(x_min=-0.2, x_max=0.2, y_min=0.9, y_max=1.07, xlabel=
r"entaRS", ylabel=
"dE/dx mean", space=0.02, ax=ax[1, 0])
429 ax[1, 0].
plot(df_il[
'enta'], df_il[
'mean'],
'-', markersize=10, rasterized=
True, label=
'IL')
430 ax[1, 0].legend(fontsize=17)
431 ax[1, 0].set_title(
'dE/dx Mean vs entaRS (IL) zoom', fontsize=14)
433 pc.hist(x_min=-0.2, x_max=0.2, y_min=0.9, y_max=1.05, xlabel=
r"entaRS", ylabel=
"dE/dx mean", space=0.02, ax=ax[1, 1])
434 ax[1, 1].
plot(df_ol[
'enta'], df_ol[
'mean'],
'-', markersize=10, rasterized=
True, label=
'OL')
435 ax[1, 1].legend(fontsize=17)
436 ax[1, 1].set_title(
'dE/dx Mean vs entaRS (OL) zoom', fontsize=14)
438 fig.suptitle(f
"dE/dx vs entaRS {suffix}", fontsize=20)
439 save_to_pdf(pdf, fig)
442def run_validation(job_path, input_data_path, requested_iov, expert_config, **kwargs):
444 Makes validation plots
445 :job_path: path to cdcdedx calibration output
446 :input_data_path: path to the input files
447 :requested_iov: required argument but not used
448 :expert_config: required argument
450 os.makedirs(
'plots/validation', exist_ok=
True)
451 os.makedirs(
'plots/constant', exist_ok=
True)
453 expert_config = json.loads(expert_config)
454 GT = expert_config[
"GT"]
456 basf2.B2INFO(
"Starting validation...")
459 (
"rungain",
"run gain", rg.getRunGain),
460 (
"coscorr",
"coscorr", pc.process_cosgain),
461 (
"wiregain",
"wire gain", pw.process_wiregain),
462 (
"onedcell",
"1D gain", oned.process_onedgain),
467 for dirname, label, function
in payloads:
469 basf2.B2INFO(f
"Processing {label} payloads...")
471 caldir = get_latest_calibration_dir(job_path, dirname)
473 dbpath = os.path.join(caldir,
"outputdb")
475 result = function(dbpath, GT)
477 if dirname ==
"wiregain":
478 exp_run_dict = result
480 basf2.B2INFO(
"Generating validation plots...")
481 val_path = os.path.join(job_path,
'validation0',
'0',
'algorithm_output')
484 (
"rungain validation plots", rungain_validation),
485 (
"wire gain validation plots", wiregain_validation),
486 (
"cosine correction validation plots", cosgain_validation),
487 (
"injection time validation plots", injection_validation),
488 (
"momentum validation plots", mom_validation),
489 (
"1D validation plots", oneDcell_validation),
492 for exp, run_list
in exp_run_dict.items():
494 suffix = f
"e{exp}_r{run}"
495 for msg, func
in validators:
496 basf2.B2INFO(f
"Processing {msg} for {suffix}...")
497 func(val_path, suffix)
499 source_path = os.path.join(job_path,
'validation0',
'0',
'algorithm_output',
'plots')
500 shutil.copy(source_path+f
"/costh/dedxpeaks_vs_cos_{suffix}.pdf",
'plots/validation/')
502 shutil.copy(source_path+f
"/mom/dedxpeaks_vs_mom_{suffix}.pdf",
'plots/validation/')
505if __name__ ==
"__main__":