legend-dataflow-scripts 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. legend_dataflow_scripts-0.1.0.dist-info/METADATA +57 -0
  2. legend_dataflow_scripts-0.1.0.dist-info/RECORD +36 -0
  3. legend_dataflow_scripts-0.1.0.dist-info/WHEEL +5 -0
  4. legend_dataflow_scripts-0.1.0.dist-info/entry_points.txt +18 -0
  5. legend_dataflow_scripts-0.1.0.dist-info/top_level.txt +1 -0
  6. legenddataflowscripts/__init__.py +17 -0
  7. legenddataflowscripts/_version.py +21 -0
  8. legenddataflowscripts/par/__init__.py +0 -0
  9. legenddataflowscripts/par/geds/__init__.py +0 -0
  10. legenddataflowscripts/par/geds/dsp/__init__.py +0 -0
  11. legenddataflowscripts/par/geds/dsp/dplms.py +145 -0
  12. legenddataflowscripts/par/geds/dsp/eopt.py +398 -0
  13. legenddataflowscripts/par/geds/dsp/evtsel.py +400 -0
  14. legenddataflowscripts/par/geds/dsp/nopt.py +120 -0
  15. legenddataflowscripts/par/geds/dsp/pz.py +217 -0
  16. legenddataflowscripts/par/geds/dsp/svm.py +28 -0
  17. legenddataflowscripts/par/geds/dsp/svm_build.py +69 -0
  18. legenddataflowscripts/par/geds/hit/__init__.py +0 -0
  19. legenddataflowscripts/par/geds/hit/aoe.py +245 -0
  20. legenddataflowscripts/par/geds/hit/ecal.py +778 -0
  21. legenddataflowscripts/par/geds/hit/lq.py +213 -0
  22. legenddataflowscripts/par/geds/hit/qc.py +326 -0
  23. legenddataflowscripts/tier/__init__.py +0 -0
  24. legenddataflowscripts/tier/dsp.py +263 -0
  25. legenddataflowscripts/tier/hit.py +148 -0
  26. legenddataflowscripts/utils/__init__.py +15 -0
  27. legenddataflowscripts/utils/alias_table.py +28 -0
  28. legenddataflowscripts/utils/cfgtools.py +14 -0
  29. legenddataflowscripts/utils/convert_np.py +31 -0
  30. legenddataflowscripts/utils/log.py +77 -0
  31. legenddataflowscripts/utils/pulser_removal.py +16 -0
  32. legenddataflowscripts/workflow/__init__.py +20 -0
  33. legenddataflowscripts/workflow/execenv.py +327 -0
  34. legenddataflowscripts/workflow/filedb.py +107 -0
  35. legenddataflowscripts/workflow/pre_compile_catalog.py +24 -0
  36. legenddataflowscripts/workflow/utils.py +113 -0
@@ -0,0 +1,778 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import copy
5
+ import pickle as pkl
6
+ import warnings
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ import matplotlib as mpl
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+ import pygama.math.distributions as pgf
14
+ import pygama.math.histogram as pgh
15
+ from dbetto import TextDB
16
+ from dbetto.catalog import Props
17
+ from legendmeta import LegendMetadata
18
+ from lgdo import lh5
19
+ from matplotlib.colors import LogNorm
20
+ from pygama.math.distributions import nb_poly
21
+ from pygama.pargen.data_cleaning import get_mode_stdev
22
+ from pygama.pargen.energy_cal import FWHMLinear, FWHMQuadratic, HPGeCalibration
23
+ from pygama.pargen.utils import load_data
24
+ from scipy.stats import binned_statistic
25
+
26
+ from ....utils import (
27
+ build_log,
28
+ convert_dict_np_to_float,
29
+ get_pulser_mask,
30
+ )
31
+
32
+ mpl.use("agg")
33
+ sto = lh5.LH5Store()
34
+
35
+ warnings.filterwarnings(action="ignore", category=RuntimeWarning)
36
+ warnings.filterwarnings(action="ignore", category=np.exceptions.RankWarning)
37
+
38
+
39
+ def plot_2614_timemap(
40
+ data,
41
+ cal_energy_param,
42
+ selection_string,
43
+ figsize=(8, 6),
44
+ fontsize=12,
45
+ erange=(2580, 2630),
46
+ dx=1,
47
+ time_dx=180,
48
+ ):
49
+ plt.rcParams["figure.figsize"] = figsize
50
+ plt.rcParams["font.size"] = fontsize
51
+
52
+ selection = data.query(
53
+ f"{cal_energy_param}>2560&{cal_energy_param}<2660&{selection_string}"
54
+ )
55
+
56
+ fig = plt.figure()
57
+ if len(selection) == 0:
58
+ pass
59
+ else:
60
+ time_bins = np.arange(
61
+ (np.amin(data["timestamp"]) // time_dx) * time_dx,
62
+ ((np.amax(data["timestamp"]) // time_dx) + 2) * time_dx,
63
+ time_dx,
64
+ )
65
+
66
+ plt.hist2d(
67
+ selection["timestamp"],
68
+ selection[cal_energy_param],
69
+ bins=[time_bins, np.arange(erange[0], erange[1] + dx, dx)],
70
+ norm=LogNorm(),
71
+ )
72
+
73
+ ticks, labels = plt.xticks()
74
+ plt.xlabel(
75
+ f"Time starting : {datetime.utcfromtimestamp(ticks[0]).strftime('%d/%m/%y %H:%M')}"
76
+ )
77
+ plt.ylabel("Energy(keV)")
78
+ plt.ylim([erange[0], erange[1]])
79
+
80
+ plt.xticks(
81
+ ticks,
82
+ [datetime.utcfromtimestamp(tick).strftime("%H:%M") for tick in ticks],
83
+ )
84
+ plt.close()
85
+ return fig
86
+
87
+
88
+ def plot_pulser_timemap(
89
+ data,
90
+ cal_energy_param,
91
+ selection_string, # noqa: ARG001
92
+ pulser_field="is_pulser",
93
+ figsize=(8, 6),
94
+ fontsize=12,
95
+ dx=0.2,
96
+ time_dx=180,
97
+ n_spread=3,
98
+ ):
99
+ plt.rcParams["figure.figsize"] = figsize
100
+ plt.rcParams["font.size"] = fontsize
101
+
102
+ time_bins = np.arange(
103
+ (np.amin(data["timestamp"]) // time_dx) * time_dx,
104
+ ((np.amax(data["timestamp"]) // time_dx) + 2) * time_dx,
105
+ time_dx,
106
+ )
107
+
108
+ selection = data.query(pulser_field)
109
+ fig = plt.figure()
110
+ if len(selection) == 0:
111
+ pass
112
+
113
+ else:
114
+ mean = np.nanpercentile(selection[cal_energy_param], 50)
115
+ spread = mean - np.nanpercentile(selection[cal_energy_param], 10)
116
+
117
+ plt.hist2d(
118
+ selection["timestamp"],
119
+ selection[cal_energy_param],
120
+ bins=[
121
+ time_bins,
122
+ np.arange(mean - n_spread * spread, mean + n_spread * spread + dx, dx),
123
+ ],
124
+ norm=LogNorm(),
125
+ )
126
+ plt.ylim([mean - n_spread * spread, mean + n_spread * spread])
127
+ ticks, labels = plt.xticks()
128
+ plt.xlabel(
129
+ f"Time starting : {datetime.utcfromtimestamp(ticks[0]).strftime('%d/%m/%y %H:%M')}"
130
+ )
131
+ plt.ylabel("Energy(keV)")
132
+
133
+ plt.xticks(
134
+ ticks,
135
+ [datetime.utcfromtimestamp(tick).strftime("%H:%M") for tick in ticks],
136
+ )
137
+ plt.close()
138
+ return fig
139
+
140
+
141
+ def get_median(x):
142
+ if len(x[~np.isnan(x)]) >= 10:
143
+ return np.nan
144
+ return np.nanpercentile(x, 50)
145
+
146
+
147
+ def get_err(x):
148
+ if len(x[~np.isnan(x)]) >= 10:
149
+ return np.nan
150
+ return np.nanvar(x) / np.sqrt(len(x))
151
+
152
+
153
+ def bin_pulser_stability(
154
+ data,
155
+ cal_energy_param,
156
+ selection_string, # noqa: ARG001
157
+ pulser_field="is_pulser",
158
+ time_slice=180,
159
+ ):
160
+ selection = data.query(pulser_field)
161
+
162
+ utime_array = data["timestamp"]
163
+ select_energies = selection[cal_energy_param].to_numpy()
164
+
165
+ time_bins = np.arange(
166
+ (np.amin(utime_array) // time_slice) * time_slice,
167
+ ((np.amax(utime_array) // time_slice) + 2) * time_slice,
168
+ time_slice,
169
+ )
170
+ # bin time values
171
+ times_average = (time_bins[:-1] + time_bins[1:]) / 2
172
+
173
+ if len(selection) == 0:
174
+ return {
175
+ "time": times_average,
176
+ "energy": np.full_like(times_average, np.nan),
177
+ "spread": np.full_like(times_average, np.nan),
178
+ }
179
+
180
+ par_average, _, _ = binned_statistic(
181
+ selection["timestamp"], select_energies, statistic=get_median, bins=time_bins
182
+ )
183
+ par_error, _, _ = binned_statistic(
184
+ selection["timestamp"], select_energies, statistic=get_err, bins=time_bins
185
+ )
186
+
187
+ return {"time": times_average, "energy": par_average, "spread": par_error}
188
+
189
+
190
+ def bin_stability(
191
+ data,
192
+ cal_energy_param,
193
+ selection_string,
194
+ time_slice=180,
195
+ energy_range=(2585, 2660),
196
+ ):
197
+ selection = data.query(
198
+ f"{cal_energy_param}>{energy_range[0]}&{cal_energy_param}<{energy_range[1]}&{selection_string}"
199
+ )
200
+
201
+ utime_array = data["timestamp"]
202
+ select_energies = selection[cal_energy_param].to_numpy()
203
+
204
+ time_bins = np.arange(
205
+ (np.amin(utime_array) // time_slice) * time_slice,
206
+ ((np.amax(utime_array) // time_slice) + 2) * time_slice,
207
+ time_slice,
208
+ )
209
+ # bin time values
210
+ times_average = (time_bins[:-1] + time_bins[1:]) / 2
211
+
212
+ if len(selection) == 0:
213
+ return {
214
+ "time": times_average,
215
+ "energy": np.full_like(times_average, np.nan),
216
+ "spread": np.full_like(times_average, np.nan),
217
+ }
218
+
219
+ par_average, _, _ = binned_statistic(
220
+ selection["timestamp"], select_energies, statistic=get_median, bins=time_bins
221
+ )
222
+ par_error, _, _ = binned_statistic(
223
+ selection["timestamp"], select_energies, statistic=get_err, bins=time_bins
224
+ )
225
+
226
+ return {"time": times_average, "energy": par_average, "spread": par_error}
227
+
228
+
229
+ def bin_spectrum(
230
+ data,
231
+ cal_energy_param,
232
+ selection_string,
233
+ cut_field="is_valid_cal",
234
+ pulser_field="is_pulser",
235
+ erange=(0, 3000),
236
+ dx=0.5,
237
+ ):
238
+ bins = np.arange(erange[0], erange[1] + dx, dx)
239
+ return {
240
+ "bins": pgh.get_bin_centers(bins),
241
+ "counts": np.histogram(data.query(selection_string)[cal_energy_param], bins)[0],
242
+ "cut_counts": np.histogram(
243
+ data.query(f"(~{cut_field})&(~{pulser_field})")[cal_energy_param],
244
+ bins,
245
+ )[0],
246
+ "pulser_counts": np.histogram(
247
+ data.query(pulser_field)[cal_energy_param],
248
+ bins,
249
+ )[0],
250
+ }
251
+
252
+
253
+ def bin_survival_fraction(
254
+ data,
255
+ cal_energy_param,
256
+ selection_string,
257
+ cut_field="is_valid_cal",
258
+ pulser_field="is_pulser",
259
+ erange=(0, 3000),
260
+ dx=6,
261
+ ):
262
+ counts_pass, bins_pass, _ = pgh.get_hist(
263
+ data.query(selection_string)[cal_energy_param],
264
+ bins=np.arange(erange[0], erange[1] + dx, dx),
265
+ )
266
+ counts_fail, bins_fail, _ = pgh.get_hist(
267
+ data.query(f"(~{cut_field})&(~{pulser_field})")[cal_energy_param],
268
+ bins=np.arange(erange[0], erange[1] + dx, dx),
269
+ )
270
+ sf = 100 * (counts_pass + 10 ** (-6)) / (counts_pass + counts_fail + 10 ** (-6))
271
+ return {"bins": pgh.get_bin_centers(bins_pass), "sf": sf}
272
+
273
+
274
+ def plot_baseline_timemap(
275
+ data,
276
+ figsize=(8, 6),
277
+ fontsize=12,
278
+ parameter="bl_mean",
279
+ dx=1,
280
+ n_spread=5,
281
+ time_dx=180,
282
+ ):
283
+ plt.rcParams["figure.figsize"] = figsize
284
+ plt.rcParams["font.size"] = fontsize
285
+
286
+ time_bins = np.arange(
287
+ (np.amin(data["timestamp"]) // time_dx) * time_dx,
288
+ ((np.amax(data["timestamp"]) // time_dx) + 2) * time_dx,
289
+ time_dx,
290
+ )
291
+
292
+ mean = np.nanpercentile(data[parameter], 50)
293
+ spread = mean - np.nanpercentile(data[parameter], 10)
294
+ fig = plt.figure()
295
+ plt.hist2d(
296
+ data["timestamp"],
297
+ data[parameter],
298
+ bins=[
299
+ time_bins,
300
+ np.arange(mean - n_spread * spread, mean + n_spread * spread + dx, dx),
301
+ ],
302
+ norm=LogNorm(),
303
+ )
304
+
305
+ ticks, labels = plt.xticks()
306
+ plt.xlabel(
307
+ f"Time starting : {datetime.utcfromtimestamp(ticks[0]).strftime('%d/%m/%y %H:%M')}"
308
+ )
309
+ plt.ylabel("Baseline Value")
310
+ plt.ylim([mean - n_spread * spread, mean + n_spread * spread])
311
+
312
+ plt.xticks(
313
+ ticks,
314
+ [datetime.utcfromtimestamp(tick).strftime("%H:%M") for tick in ticks],
315
+ )
316
+ plt.close()
317
+ return fig
318
+
319
+
320
+ def bin_bl_stability(data, time_slice=180, parameter="bl_mean"):
321
+ utime_array = data["timestamp"]
322
+ select_bls = data[parameter].to_numpy()
323
+
324
+ time_bins = np.arange(
325
+ (np.amin(utime_array) // time_slice) * time_slice,
326
+ ((np.amax(utime_array) // time_slice) + 2) * time_slice,
327
+ time_slice,
328
+ )
329
+ # bin time values
330
+ times_average = (time_bins[:-1] + time_bins[1:]) / 2
331
+
332
+ def nanmedian(x):
333
+ return np.nanpercentile(x, 50) if len(x) >= 10 else np.nan
334
+
335
+ def error(x):
336
+ return np.nanvar(x) / np.sqrt(len(x)) if len(x) >= 10 else np.nan
337
+
338
+ par_average, _, _ = binned_statistic(
339
+ data["timestamp"], select_bls, statistic=nanmedian, bins=time_bins
340
+ )
341
+ par_error, _, _ = binned_statistic(
342
+ data["timestamp"], select_bls, statistic=error, bins=time_bins
343
+ )
344
+
345
+ return {"time": times_average, "baseline": par_average, "spread": par_error}
346
+
347
+
348
+ def bin_baseline(data, parameter="bl_mean-baseline", dx=1, bl_range=None):
349
+ if bl_range is None:
350
+ bl_range = [-500, 500]
351
+ par_array = data.eval(parameter)
352
+ bins = np.arange(bl_range[0], bl_range[1], dx)
353
+ bl_array, bins, _ = pgh.get_hist(par_array, bins=bins)
354
+ return {"bl_array": bl_array, "bins": (bins[1:] + bins[:-1]) / 2}
355
+
356
+
357
+ def baseline_tracking_plots(files, lh5_path, plot_options=None):
358
+ if plot_options is None:
359
+ plot_options = {}
360
+ plot_dict = {}
361
+ data = lh5.read_as(
362
+ lh5_path, files, "pd", field_mask=["bl_mean", "baseline", "timestamp"]
363
+ )
364
+ for key, item in plot_options.items():
365
+ if item["options"] is not None:
366
+ plot_dict[key] = item["function"](data, **item["options"])
367
+ else:
368
+ plot_dict[key] = item["function"](data)
369
+ return plot_dict
370
+
371
+
372
+ def monitor_parameters(files, lh5_path, parameters):
373
+ data = lh5.read_as(lh5_path, files, "pd", field_mask=parameters)
374
+ out_dict = {}
375
+ for param in parameters:
376
+ mode, stdev = get_mode_stdev(data[param].to_numpy())
377
+ out_dict[param] = {"mode": mode, "stdev": stdev}
378
+ return out_dict
379
+
380
+
381
+ def get_results_dict(ecal_class, data, cal_energy_param, selection_string):
382
+ if np.isnan(ecal_class.pars).all():
383
+ return {}
384
+ results_dict = copy.deepcopy(ecal_class.results["hpge_fit_energy_peaks_1"])
385
+
386
+ if "FWHMLinear" in results_dict:
387
+ fwhm_linear = results_dict["FWHMLinear"]
388
+ fwhm_linear["function"] = fwhm_linear["function"].__name__
389
+ fwhm_linear["parameters"] = fwhm_linear["parameters"].to_dict()
390
+ fwhm_linear["uncertainties"] = fwhm_linear["uncertainties"].to_dict()
391
+ fwhm_linear["cov"] = fwhm_linear["cov"].tolist()
392
+ else:
393
+ fwhm_linear = None
394
+
395
+ if "FWHMQuadratic" in results_dict:
396
+ fwhm_quad = results_dict["FWHMQuadratic"]
397
+ fwhm_quad["function"] = fwhm_quad["function"].__name__
398
+ fwhm_quad["parameters"] = fwhm_quad["parameters"].to_dict()
399
+ fwhm_quad["uncertainties"] = fwhm_quad["uncertainties"].to_dict()
400
+ fwhm_quad["cov"] = fwhm_quad["cov"].tolist()
401
+ else:
402
+ fwhm_quad = None
403
+
404
+ pk_dict = results_dict["peak_parameters"]
405
+
406
+ for _, dic in pk_dict.items():
407
+ dic["function"] = dic["function"].name
408
+ dic["parameters"] = dic["parameters"].to_dict()
409
+ dic["uncertainties"] = dic["uncertainties"].to_dict()
410
+ dic.pop("covariance")
411
+
412
+ return {
413
+ "total_fep": len(
414
+ data.query(f"{cal_energy_param}>2604&{cal_energy_param}<2624")
415
+ ),
416
+ "total_sep": len(
417
+ data.query(f"{cal_energy_param}>2095&{cal_energy_param}<2115")
418
+ ),
419
+ "total_dep": len(
420
+ data.query(f"{cal_energy_param}>1587&{cal_energy_param}<1597")
421
+ ),
422
+ "pass_fep": len(
423
+ data.query(
424
+ f"{cal_energy_param}>2604&{cal_energy_param}<2624&{selection_string}"
425
+ )
426
+ ),
427
+ "pass_sep": len(
428
+ data.query(
429
+ f"{cal_energy_param}>2095&{cal_energy_param}<2115&{selection_string}"
430
+ )
431
+ ),
432
+ "pass_dep": len(
433
+ data.query(
434
+ f"{cal_energy_param}>1587&{cal_energy_param}<1597&{selection_string}"
435
+ )
436
+ ),
437
+ "eres_linear": fwhm_linear,
438
+ "eres_quadratic": fwhm_quad,
439
+ # "calibration_parameters":results_dict["calibration_parameters"].to_dict(),
440
+ # "calibration_uncertainty":results_dict["calibration_uncertainties"].to_dict(),
441
+ "fitted_peaks": ecal_class.peaks_kev.tolist(),
442
+ "pk_fits": pk_dict,
443
+ }
444
+
445
+
446
+ def par_geds_hit_ecal() -> None:
447
+ argparser = argparse.ArgumentParser()
448
+ argparser.add_argument("--files", help="filelist", nargs="*", type=str)
449
+ argparser.add_argument(
450
+ "--tcm-filelist", help="tcm_filelist", type=str, required=False
451
+ )
452
+ argparser.add_argument(
453
+ "--pulser-file", help="pulser_file", type=str, required=False
454
+ )
455
+
456
+ argparser.add_argument("--ctc-dict", help="ctc_dict", nargs="*")
457
+ argparser.add_argument("--in-hit-dict", help="in_hit_dict", required=False)
458
+ argparser.add_argument("--inplot-dict", help="inplot_dict", required=False)
459
+
460
+ argparser.add_argument("--datatype", help="Datatype", type=str, required=True)
461
+ argparser.add_argument("--timestamp", help="Timestamp", type=str, required=True)
462
+ argparser.add_argument("--channel", help="Channel", type=str, required=True)
463
+ argparser.add_argument("--table-name", help="table name", type=str, required=True)
464
+
465
+ argparser.add_argument("--tier", help="tier", type=str, default="hit")
466
+ argparser.add_argument("--configs", help="config", type=str, required=True)
467
+ argparser.add_argument("--metadata", help="metadata path", type=str, required=True)
468
+
469
+ argparser.add_argument("--log", help="log_file", type=str)
470
+
471
+ argparser.add_argument("--plot-path", help="plot_path", type=str, required=False)
472
+ argparser.add_argument("--save-path", help="save_path", type=str)
473
+ argparser.add_argument("--results-path", help="results_path", type=str)
474
+
475
+ argparser.add_argument("-d", "--debug", help="debug_mode", action="store_true")
476
+ args = argparser.parse_args()
477
+
478
+ configs = TextDB(args.configs, lazy=True).on(args.timestamp, system=args.datatype)
479
+ config_dict = configs["snakemake_rules"]
480
+ if args.tier == "hit":
481
+ config_dict = config_dict["pars_hit_ecal"]
482
+ elif args.tier == "pht":
483
+ config_dict = config_dict["pars_pht_ecal"]
484
+ else:
485
+ msg = "invalid tier"
486
+ raise ValueError(msg)
487
+
488
+ build_log(config_dict, args.log)
489
+
490
+ chmap = LegendMetadata(args.metadata).channelmap(
491
+ args.timestamp, system=args.datatype
492
+ )
493
+ det_status = chmap[args.channel]["analysis"]["usability"]
494
+
495
+ if args.in_hit_dict:
496
+ hit_dict = Props.read_from(args.in_hit_dict)
497
+ in_results_dict = hit_dict.get("results", {})
498
+ hit_dict = hit_dict.get("operations", hit_dict)
499
+
500
+ db_files = [
501
+ par_file
502
+ for par_file in args.ctc_dict
503
+ if Path(par_file).suffix in (".json", ".yml", ".yaml")
504
+ ]
505
+
506
+ database_dic = Props.read_from(db_files)
507
+
508
+ hit_dict.update(database_dic[args.channel]["ctc_params"])
509
+
510
+ channel_dict = config_dict["inputs"]["ecal_config"][args.channel]
511
+ kwarg_dict = Props.read_from(channel_dict)
512
+
513
+ # convert plot functions from strings to functions and split off baseline and common plots
514
+ for field, item in kwarg_dict["plot_options"].items():
515
+ kwarg_dict["plot_options"][field]["function"] = eval(item["function"])
516
+
517
+ bl_plots = kwarg_dict.pop("bl_plot_options")
518
+ for field, item in bl_plots.items():
519
+ bl_plots[field]["function"] = eval(item["function"])
520
+ common_plots = kwarg_dict.pop("common_plots")
521
+
522
+ with Path(args.files[0]).open() as f:
523
+ files = f.read().splitlines()
524
+ files = sorted(files)
525
+
526
+ # load data in
527
+ data, threshold_mask = load_data(
528
+ files,
529
+ args.table_name,
530
+ hit_dict,
531
+ params=[
532
+ *kwarg_dict["energy_params"],
533
+ kwarg_dict["cut_param"],
534
+ "timestamp",
535
+ "trapTmax",
536
+ ],
537
+ threshold=kwarg_dict["threshold"],
538
+ return_selection_mask=True,
539
+ cal_energy_param="trapTmax",
540
+ )
541
+
542
+ mask = get_pulser_mask(
543
+ pulser_file=args.pulser_file,
544
+ )
545
+
546
+ data["is_pulser"] = mask[threshold_mask]
547
+
548
+ pk_pars = [
549
+ (583.191, (20, 20), pgf.hpge_peak),
550
+ (727.330, (30, 30), pgf.hpge_peak),
551
+ (860.564, (30, 25), pgf.hpge_peak),
552
+ (1592.511, (40, 20), pgf.gauss_on_step),
553
+ (1620.50, (20, 40), pgf.gauss_on_step),
554
+ (2103.511, (40, 40), pgf.gauss_on_step),
555
+ (2614.511, (40, 40), pgf.hpge_peak),
556
+ ]
557
+
558
+ glines = [pk_par[0] for pk_par in pk_pars]
559
+
560
+ if "cal_energy_params" not in kwarg_dict:
561
+ cal_energy_params = [
562
+ energy_param + "_cal" for energy_param in kwarg_dict["energy_params"]
563
+ ]
564
+ else:
565
+ cal_energy_params = kwarg_dict["cal_energy_params"]
566
+
567
+ selection_string = f"~is_pulser&{kwarg_dict['cut_param']}"
568
+
569
+ results_dict = {}
570
+ plot_dict = {}
571
+ full_object_dict = {}
572
+
573
+ for energy_param, cal_energy_param in zip(
574
+ kwarg_dict["energy_params"], cal_energy_params, strict=False
575
+ ):
576
+ e_uncal = data.query(selection_string)[energy_param].to_numpy()
577
+
578
+ hist, bins, bar = pgh.get_hist(
579
+ e_uncal[
580
+ (e_uncal > np.nanpercentile(e_uncal, 95))
581
+ & (e_uncal < np.nanpercentile(e_uncal, 99.9))
582
+ ],
583
+ dx=1,
584
+ range=[np.nanpercentile(e_uncal, 95), np.nanpercentile(e_uncal, 99.9)],
585
+ )
586
+
587
+ guess = 2614.511 / bins[np.nanargmax(hist)]
588
+ full_object_dict[cal_energy_param] = HPGeCalibration(
589
+ energy_param,
590
+ glines,
591
+ guess,
592
+ kwarg_dict.get("deg", 0),
593
+ debug_mode=kwarg_dict.get("debug_mode", False) | args.debug,
594
+ )
595
+ full_object_dict[cal_energy_param].hpge_get_energy_peaks(
596
+ e_uncal, etol_kev=5 if det_status == "on" else 20
597
+ )
598
+ if 2614.511 not in full_object_dict[cal_energy_param].peaks_kev:
599
+ full_object_dict[cal_energy_param] = HPGeCalibration(
600
+ energy_param,
601
+ glines,
602
+ guess,
603
+ kwarg_dict.get("deg", 0),
604
+ debug_mode=kwarg_dict.get("debug_mode", False),
605
+ )
606
+ full_object_dict[cal_energy_param].hpge_get_energy_peaks(
607
+ e_uncal, etol_kev=5 if det_status == "on" else 30, n_sigma=2
608
+ )
609
+ got_peaks_kev = full_object_dict[cal_energy_param].peaks_kev.copy()
610
+ if det_status != "on":
611
+ full_object_dict[cal_energy_param].hpge_cal_energy_peak_tops(
612
+ e_uncal,
613
+ peaks_kev=got_peaks_kev,
614
+ update_cal_pars=True,
615
+ allowed_p_val=0,
616
+ )
617
+ full_object_dict[cal_energy_param].hpge_fit_energy_peaks(
618
+ e_uncal,
619
+ peaks_kev=[2614.511],
620
+ peak_pars=pk_pars,
621
+ tail_weight=kwarg_dict.get("tail_weight", 0),
622
+ n_events=kwarg_dict.get("n_events", None),
623
+ allowed_p_val=kwarg_dict.get("p_val", 0),
624
+ update_cal_pars=bool(det_status == "on"),
625
+ bin_width_kev=0.5,
626
+ )
627
+ full_object_dict[cal_energy_param].hpge_fit_energy_peaks(
628
+ e_uncal,
629
+ peaks_kev=got_peaks_kev,
630
+ peak_pars=pk_pars,
631
+ tail_weight=kwarg_dict.get("tail_weight", 0),
632
+ n_events=kwarg_dict.get("n_events", None),
633
+ allowed_p_val=kwarg_dict.get("p_val", 0),
634
+ update_cal_pars=False,
635
+ bin_width_kev=0.5,
636
+ )
637
+
638
+ full_object_dict[cal_energy_param].get_energy_res_curve(
639
+ FWHMLinear,
640
+ interp_energy_kev={"Qbb": 2039.0},
641
+ )
642
+ full_object_dict[cal_energy_param].get_energy_res_curve(
643
+ FWHMQuadratic,
644
+ interp_energy_kev={"Qbb": 2039.0},
645
+ )
646
+
647
+ data[cal_energy_param] = nb_poly(
648
+ data[energy_param].to_numpy(), full_object_dict[cal_energy_param].pars
649
+ )
650
+
651
+ results_dict[cal_energy_param] = get_results_dict(
652
+ full_object_dict[cal_energy_param], data, cal_energy_param, selection_string
653
+ )
654
+
655
+ hit_dict.update(
656
+ {cal_energy_param: full_object_dict[cal_energy_param].gen_pars_dict()}
657
+ )
658
+ if "ctc" in cal_energy_param:
659
+ no_ctc_dict = full_object_dict[cal_energy_param].gen_pars_dict()
660
+ no_ctc_dict["expression"] = no_ctc_dict["expression"].replace("_ctc", "")
661
+ hit_dict.update({cal_energy_param.replace("ctc", "noctc"): no_ctc_dict})
662
+ hit_dict.update(
663
+ {
664
+ cal_energy_param.replace("_ctc", ""): {
665
+ "expression": f"where({cal_energy_param.replace('ctc', 'noctc')}>{kwarg_dict.get('dt_theshold_kev', 100)}, {cal_energy_param}, {cal_energy_param.replace('ctc', 'noctc')})",
666
+ "parameters": {},
667
+ }
668
+ }
669
+ )
670
+ if args.plot_path:
671
+ param_plot_dict = {}
672
+ if ~np.isnan(full_object_dict[cal_energy_param].pars).all():
673
+ param_plot_dict["fwhm_fit"] = full_object_dict[
674
+ cal_energy_param
675
+ ].plot_eres_fit(e_uncal)
676
+ param_plot_dict["cal_fit"] = full_object_dict[
677
+ cal_energy_param
678
+ ].plot_cal_fit(e_uncal)
679
+ param_plot_dict["peak_fits"] = full_object_dict[
680
+ cal_energy_param
681
+ ].plot_fits(e_uncal)
682
+
683
+ if "plot_options" in kwarg_dict:
684
+ for key, item in kwarg_dict["plot_options"].items():
685
+ if item["options"] is not None:
686
+ param_plot_dict[key] = item["function"](
687
+ data,
688
+ cal_energy_param,
689
+ selection_string,
690
+ **item["options"],
691
+ )
692
+ else:
693
+ param_plot_dict[key] = item["function"](
694
+ data,
695
+ cal_energy_param,
696
+ selection_string,
697
+ )
698
+ plot_dict[cal_energy_param] = param_plot_dict
699
+
700
+ for peak_dict in (
701
+ full_object_dict[cal_energy_param]
702
+ .results["hpge_fit_energy_peaks_1"]["peak_parameters"]
703
+ .values()
704
+ ):
705
+ peak_dict["function"] = peak_dict["function"].name
706
+ peak_dict["parameters"] = peak_dict["parameters"].to_dict()
707
+ peak_dict["uncertainties"] = peak_dict["uncertainties"].to_dict()
708
+ for peak_dict in (
709
+ full_object_dict[cal_energy_param]
710
+ .results["hpge_fit_energy_peaks"]["peak_parameters"]
711
+ .values()
712
+ ):
713
+ peak_dict["function"] = peak_dict["function"].name
714
+ peak_dict["parameters"] = peak_dict["parameters"].to_dict()
715
+ peak_dict["uncertainties"] = peak_dict["uncertainties"].to_dict()
716
+
717
+ if det_status != "on":
718
+ for peak_dict in (
719
+ full_object_dict[cal_energy_param]
720
+ .results["hpge_cal_energy_peak_tops"]["peak_parameters"]
721
+ .values()
722
+ ):
723
+ peak_dict["function"] = peak_dict["function"].name
724
+ peak_dict["parameters"] = peak_dict["parameters"].to_dict()
725
+ peak_dict["uncertainties"] = peak_dict["uncertainties"].to_dict()
726
+
727
+ if "monitoring_parameters" in kwarg_dict:
728
+ monitor_dict = monitor_parameters(
729
+ files, args.table_name, kwarg_dict["monitoring_parameters"]
730
+ )
731
+ results_dict.update({"monitoring_parameters": monitor_dict})
732
+
733
+ # get baseline plots and save all plots to file
734
+ if args.plot_path:
735
+ common_dict = baseline_tracking_plots(
736
+ sorted(files), args.table_name, plot_options=bl_plots
737
+ )
738
+
739
+ for plot in list(common_dict):
740
+ if plot not in common_plots:
741
+ plot_item = common_dict.pop(plot)
742
+ plot_dict.update({plot: plot_item})
743
+
744
+ for key, item in plot_dict.items():
745
+ if isinstance(item, dict) and len(item) > 0:
746
+ param_dict = {}
747
+ for plot in common_plots:
748
+ if plot in item:
749
+ param_dict.update({plot: item[plot]})
750
+ common_dict.update({key: param_dict})
751
+
752
+ if args.inplot_dict:
753
+ with Path(args.inplot_dict).open("rb") as f:
754
+ total_plot_dict = pkl.load(f)
755
+ else:
756
+ total_plot_dict = {}
757
+
758
+ if "common" in total_plot_dict:
759
+ total_plot_dict["common"].update(common_dict)
760
+ else:
761
+ total_plot_dict["common"] = common_dict
762
+
763
+ total_plot_dict.update({"ecal": plot_dict})
764
+
765
+ Path(args.plot_path).parent.mkdir(parents=True, exist_ok=True)
766
+ with Path(args.plot_path).open("wb") as f:
767
+ pkl.dump(total_plot_dict, f, protocol=pkl.HIGHEST_PROTOCOL)
768
+
769
+ # save output dictionary
770
+ output_dict = convert_dict_np_to_float(
771
+ {"pars": hit_dict, "results": dict(**in_results_dict, ecal=results_dict)}
772
+ )
773
+ Props.write_to(args.save_path, output_dict)
774
+
775
+ # save calibration objects
776
+ with Path(args.results_path).open("wb") as fp:
777
+ Path(args.results_path).parent.mkdir(parents=True, exist_ok=True)
778
+ pkl.dump({"ecal": full_object_dict}, fp, protocol=pkl.HIGHEST_PROTOCOL)