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.
- legend_dataflow_scripts-0.1.0.dist-info/METADATA +57 -0
- legend_dataflow_scripts-0.1.0.dist-info/RECORD +36 -0
- legend_dataflow_scripts-0.1.0.dist-info/WHEEL +5 -0
- legend_dataflow_scripts-0.1.0.dist-info/entry_points.txt +18 -0
- legend_dataflow_scripts-0.1.0.dist-info/top_level.txt +1 -0
- legenddataflowscripts/__init__.py +17 -0
- legenddataflowscripts/_version.py +21 -0
- legenddataflowscripts/par/__init__.py +0 -0
- legenddataflowscripts/par/geds/__init__.py +0 -0
- legenddataflowscripts/par/geds/dsp/__init__.py +0 -0
- legenddataflowscripts/par/geds/dsp/dplms.py +145 -0
- legenddataflowscripts/par/geds/dsp/eopt.py +398 -0
- legenddataflowscripts/par/geds/dsp/evtsel.py +400 -0
- legenddataflowscripts/par/geds/dsp/nopt.py +120 -0
- legenddataflowscripts/par/geds/dsp/pz.py +217 -0
- legenddataflowscripts/par/geds/dsp/svm.py +28 -0
- legenddataflowscripts/par/geds/dsp/svm_build.py +69 -0
- legenddataflowscripts/par/geds/hit/__init__.py +0 -0
- legenddataflowscripts/par/geds/hit/aoe.py +245 -0
- legenddataflowscripts/par/geds/hit/ecal.py +778 -0
- legenddataflowscripts/par/geds/hit/lq.py +213 -0
- legenddataflowscripts/par/geds/hit/qc.py +326 -0
- legenddataflowscripts/tier/__init__.py +0 -0
- legenddataflowscripts/tier/dsp.py +263 -0
- legenddataflowscripts/tier/hit.py +148 -0
- legenddataflowscripts/utils/__init__.py +15 -0
- legenddataflowscripts/utils/alias_table.py +28 -0
- legenddataflowscripts/utils/cfgtools.py +14 -0
- legenddataflowscripts/utils/convert_np.py +31 -0
- legenddataflowscripts/utils/log.py +77 -0
- legenddataflowscripts/utils/pulser_removal.py +16 -0
- legenddataflowscripts/workflow/__init__.py +20 -0
- legenddataflowscripts/workflow/execenv.py +327 -0
- legenddataflowscripts/workflow/filedb.py +107 -0
- legenddataflowscripts/workflow/pre_compile_catalog.py +24 -0
- legenddataflowscripts/workflow/utils.py +113 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import pickle as pkl
|
|
5
|
+
import warnings
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from dbetto import TextDB
|
|
10
|
+
from dbetto.catalog import Props
|
|
11
|
+
from pygama.math.distributions import gaussian
|
|
12
|
+
from pygama.pargen.AoE_cal import * # noqa: F403
|
|
13
|
+
from pygama.pargen.lq_cal import * # noqa: F403
|
|
14
|
+
from pygama.pargen.lq_cal import LQCal
|
|
15
|
+
from pygama.pargen.utils import load_data
|
|
16
|
+
|
|
17
|
+
from ....utils import (
|
|
18
|
+
build_log,
|
|
19
|
+
convert_dict_np_to_float,
|
|
20
|
+
get_pulser_mask,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
warnings.filterwarnings(action="ignore", category=RuntimeWarning)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_results_dict(lq_class):
|
|
27
|
+
return {
|
|
28
|
+
"cal_energy_param": lq_class.cal_energy_param,
|
|
29
|
+
"DEP_means": lq_class.timecorr_df.to_dict("index"),
|
|
30
|
+
"rt_correction": lq_class.dt_fit_pars,
|
|
31
|
+
"cut_fit_pars": lq_class.cut_fit_pars.to_dict(),
|
|
32
|
+
"cut_value": lq_class.cut_val,
|
|
33
|
+
"sfs": lq_class.low_side_sf.to_dict("index"),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def fill_plot_dict(lq_class, data, plot_options, plot_dict=None):
|
|
38
|
+
if plot_dict is not None:
|
|
39
|
+
for key, item in plot_options.items():
|
|
40
|
+
if item["options"] is not None:
|
|
41
|
+
plot_dict[key] = item["function"](lq_class, data, **item["options"])
|
|
42
|
+
else:
|
|
43
|
+
plot_dict[key] = item["function"](lq_class, data)
|
|
44
|
+
else:
|
|
45
|
+
plot_dict = {}
|
|
46
|
+
return plot_dict
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def par_geds_hit_lq() -> None:
|
|
50
|
+
argparser = argparse.ArgumentParser()
|
|
51
|
+
argparser.add_argument("files", help="files", nargs="*", type=str)
|
|
52
|
+
argparser.add_argument(
|
|
53
|
+
"--pulser-file", help="pulser_file", type=str, required=False
|
|
54
|
+
)
|
|
55
|
+
argparser.add_argument(
|
|
56
|
+
"--tcm-filelist", help="tcm_filelist", type=str, required=False
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
argparser.add_argument("--ecal-file", help="ecal_file", type=str, required=True)
|
|
60
|
+
argparser.add_argument("--eres-file", help="eres_file", type=str, required=True)
|
|
61
|
+
argparser.add_argument("--inplots", help="in_plot_path", type=str, required=False)
|
|
62
|
+
|
|
63
|
+
argparser.add_argument("--configs", help="configs", type=str, required=True)
|
|
64
|
+
argparser.add_argument("--log", help="log_file", type=str)
|
|
65
|
+
|
|
66
|
+
argparser.add_argument("--datatype", help="Datatype", type=str, required=True)
|
|
67
|
+
argparser.add_argument("--timestamp", help="Timestamp", type=str, required=True)
|
|
68
|
+
argparser.add_argument("--channel", help="Channel", type=str, required=True)
|
|
69
|
+
argparser.add_argument("--table-name", help="table name", type=str, required=True)
|
|
70
|
+
|
|
71
|
+
argparser.add_argument("--plot-file", help="plot_file", type=str, required=False)
|
|
72
|
+
argparser.add_argument("--hit-pars", help="hit_pars", type=str)
|
|
73
|
+
argparser.add_argument("--lq-results", help="lq_results", type=str)
|
|
74
|
+
|
|
75
|
+
argparser.add_argument("-d", "--debug", help="debug_mode", action="store_true")
|
|
76
|
+
args = argparser.parse_args()
|
|
77
|
+
|
|
78
|
+
configs = TextDB(args.configs, lazy=True).on(args.timestamp, system=args.datatype)
|
|
79
|
+
config_dict = configs["snakemake_rules"]["pars_hit_lqcal"]
|
|
80
|
+
|
|
81
|
+
log = build_log(config_dict, args.log)
|
|
82
|
+
|
|
83
|
+
channel_dict = config_dict["inputs"]["lqcal_config"][args.channel]
|
|
84
|
+
kwarg_dict = Props.read_from(channel_dict)
|
|
85
|
+
|
|
86
|
+
ecal_dict = Props.read_from(args.ecal_file)
|
|
87
|
+
cal_dict = ecal_dict["pars"]["operations"]
|
|
88
|
+
eres_dict = ecal_dict["results"]["ecal"]
|
|
89
|
+
|
|
90
|
+
with Path(args.eres_file).open("rb") as o:
|
|
91
|
+
object_dict = pkl.load(o)
|
|
92
|
+
|
|
93
|
+
if kwarg_dict["run_lq"] is True:
|
|
94
|
+
kwarg_dict.pop("run_lq")
|
|
95
|
+
|
|
96
|
+
cdf = eval(kwarg_dict.pop("cdf")) if "cdf" in kwarg_dict else gaussian
|
|
97
|
+
|
|
98
|
+
if "plot_options" in kwarg_dict:
|
|
99
|
+
for field, item in kwarg_dict["plot_options"].items():
|
|
100
|
+
kwarg_dict["plot_options"][field]["function"] = eval(item["function"])
|
|
101
|
+
|
|
102
|
+
with Path(args.files[0]).open() as f:
|
|
103
|
+
files = f.read().splitlines()
|
|
104
|
+
files = sorted(files)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
eres = eres_dict[kwarg_dict["cal_energy_param"]]["eres_linear"].copy()
|
|
108
|
+
|
|
109
|
+
def eres_func(x):
|
|
110
|
+
return eval(eres["expression"], dict(x=x, **eres["parameters"]))
|
|
111
|
+
|
|
112
|
+
except KeyError:
|
|
113
|
+
|
|
114
|
+
def eres_func(x):
|
|
115
|
+
return x * np.nan
|
|
116
|
+
|
|
117
|
+
params = [
|
|
118
|
+
"lq80",
|
|
119
|
+
"dt_eff",
|
|
120
|
+
kwarg_dict["energy_param"],
|
|
121
|
+
kwarg_dict["cal_energy_param"],
|
|
122
|
+
kwarg_dict["cut_field"],
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
# load data in
|
|
126
|
+
data, threshold_mask = load_data(
|
|
127
|
+
files,
|
|
128
|
+
args.table_name,
|
|
129
|
+
cal_dict,
|
|
130
|
+
params=params,
|
|
131
|
+
threshold=kwarg_dict.pop("threshold"),
|
|
132
|
+
return_selection_mask=True,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
mask = get_pulser_mask(
|
|
136
|
+
pulser_file=args.pulser_file,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
data["is_pulser"] = mask[threshold_mask]
|
|
140
|
+
|
|
141
|
+
lq = LQCal(
|
|
142
|
+
cal_dict,
|
|
143
|
+
kwarg_dict["cal_energy_param"],
|
|
144
|
+
kwarg_dict["dt_param"],
|
|
145
|
+
eres_func,
|
|
146
|
+
cdf,
|
|
147
|
+
selection_string=f"{kwarg_dict.pop('cut_field')}&(~is_pulser)",
|
|
148
|
+
debug_mode=args.debug | kwarg_dict.get("debug_mode", False),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
data["LQ_Ecorr"] = np.divide(data["lq80"], data[kwarg_dict["energy_param"]])
|
|
152
|
+
|
|
153
|
+
lq.update_cal_dicts(
|
|
154
|
+
{
|
|
155
|
+
"LQ_Ecorr": {
|
|
156
|
+
"expression": f"lq80/{kwarg_dict['energy_param']}",
|
|
157
|
+
"parameters": {},
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
lq.calibrate(data, "LQ_Ecorr")
|
|
163
|
+
log.info("Calibrated LQ")
|
|
164
|
+
|
|
165
|
+
out_dict = get_results_dict(lq)
|
|
166
|
+
plot_dict = fill_plot_dict(lq, data, kwarg_dict.get("plot_options", None))
|
|
167
|
+
|
|
168
|
+
# need to change eres func as can't pickle lambdas
|
|
169
|
+
try:
|
|
170
|
+
lq.eres_func = eres_dict[kwarg_dict["cal_energy_param"]][
|
|
171
|
+
"eres_linear"
|
|
172
|
+
].copy()
|
|
173
|
+
except KeyError:
|
|
174
|
+
lq.eres_func = {}
|
|
175
|
+
else:
|
|
176
|
+
out_dict = {}
|
|
177
|
+
plot_dict = {}
|
|
178
|
+
lq = None
|
|
179
|
+
|
|
180
|
+
if args.plot_file:
|
|
181
|
+
common_dict = plot_dict.pop("common") if "common" in list(plot_dict) else None
|
|
182
|
+
if args.inplots:
|
|
183
|
+
with Path(args.inplots).open("rb") as r:
|
|
184
|
+
out_plot_dict = pkl.load(r)
|
|
185
|
+
out_plot_dict.update({"lq": plot_dict})
|
|
186
|
+
else:
|
|
187
|
+
out_plot_dict = {"lq": plot_dict}
|
|
188
|
+
|
|
189
|
+
if "common" in list(out_plot_dict) and common_dict is not None:
|
|
190
|
+
out_plot_dict["common"].update(common_dict)
|
|
191
|
+
elif common_dict is not None:
|
|
192
|
+
out_plot_dict["common"] = common_dict
|
|
193
|
+
|
|
194
|
+
Path(args.plot_file).parent.mkdir(parents=True, exist_ok=True)
|
|
195
|
+
with Path(args.plot_file).open("wb") as w:
|
|
196
|
+
pkl.dump(out_plot_dict, w, protocol=pkl.HIGHEST_PROTOCOL)
|
|
197
|
+
|
|
198
|
+
final_hit_dict = convert_dict_np_to_float(
|
|
199
|
+
{
|
|
200
|
+
"pars": {"operations": cal_dict},
|
|
201
|
+
"results": dict(**ecal_dict["results"], lq=out_dict),
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
Path(args.hit_pars).parent.mkdir(parents=True, exist_ok=True)
|
|
205
|
+
Props.write_to(args.hit_pars, final_hit_dict)
|
|
206
|
+
|
|
207
|
+
final_object_dict = dict(
|
|
208
|
+
**object_dict,
|
|
209
|
+
lq=lq,
|
|
210
|
+
)
|
|
211
|
+
Path(args.lq_results).parent.mkdir(parents=True, exist_ok=True)
|
|
212
|
+
with Path(args.lq_results).open("wb") as w:
|
|
213
|
+
pkl.dump(final_object_dict, w, protocol=pkl.HIGHEST_PROTOCOL)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import pickle as pkl
|
|
6
|
+
import re
|
|
7
|
+
import warnings
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from dbetto import TextDB
|
|
12
|
+
from dbetto.catalog import Props
|
|
13
|
+
from lgdo.lh5 import ls
|
|
14
|
+
from pygama.pargen.data_cleaning import (
|
|
15
|
+
generate_cut_classifiers,
|
|
16
|
+
get_keys,
|
|
17
|
+
)
|
|
18
|
+
from pygama.pargen.utils import load_data
|
|
19
|
+
|
|
20
|
+
from ....utils import (
|
|
21
|
+
build_log,
|
|
22
|
+
convert_dict_np_to_float,
|
|
23
|
+
get_pulser_mask,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
warnings.filterwarnings(action="ignore", category=RuntimeWarning)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def par_geds_hit_qc() -> None:
|
|
30
|
+
argparser = argparse.ArgumentParser()
|
|
31
|
+
argparser.add_argument("--cal-files", help="cal_files", nargs="*", type=str)
|
|
32
|
+
argparser.add_argument("--fft-files", help="fft_files", nargs="*", type=str)
|
|
33
|
+
|
|
34
|
+
argparser.add_argument(
|
|
35
|
+
"--tcm-filelist", help="tcm_filelist", type=str, required=False
|
|
36
|
+
)
|
|
37
|
+
argparser.add_argument(
|
|
38
|
+
"--pulser-file", help="pulser_file", type=str, required=False
|
|
39
|
+
)
|
|
40
|
+
argparser.add_argument(
|
|
41
|
+
"--overwrite-files",
|
|
42
|
+
help="overwrite_files",
|
|
43
|
+
type=str,
|
|
44
|
+
required=False,
|
|
45
|
+
nargs="*",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
argparser.add_argument("--configs", help="config", type=str, required=True)
|
|
49
|
+
argparser.add_argument("--log", help="log_file", type=str)
|
|
50
|
+
|
|
51
|
+
argparser.add_argument("--datatype", help="Datatype", type=str, required=True)
|
|
52
|
+
argparser.add_argument("--timestamp", help="Timestamp", type=str, required=True)
|
|
53
|
+
argparser.add_argument("--channel", help="Channel", type=str, required=True)
|
|
54
|
+
argparser.add_argument("--table-name", help="table name", type=str, required=True)
|
|
55
|
+
argparser.add_argument("--tier", help="tier", type=str, default="hit")
|
|
56
|
+
|
|
57
|
+
argparser.add_argument("--plot-path", help="plot_path", type=str, required=False)
|
|
58
|
+
argparser.add_argument("--save-path", help="save_path", type=str)
|
|
59
|
+
args = argparser.parse_args()
|
|
60
|
+
|
|
61
|
+
configs = TextDB(args.configs, lazy=True).on(args.timestamp, system=args.datatype)
|
|
62
|
+
if args.tier == "hit":
|
|
63
|
+
config_dict = configs["snakemake_rules"]["pars_hit_qc"]
|
|
64
|
+
elif args.tier == "pht":
|
|
65
|
+
config_dict = configs["snakemake_rules"]["pars_pht_qc"]
|
|
66
|
+
else:
|
|
67
|
+
msg = f"tier {args.tier} not recognized"
|
|
68
|
+
raise ValueError(msg)
|
|
69
|
+
|
|
70
|
+
log = build_log(config_dict, args.log)
|
|
71
|
+
|
|
72
|
+
# get metadata dictionary
|
|
73
|
+
channel_dict = config_dict["inputs"]["qc_config"][args.channel]
|
|
74
|
+
kwarg_dict = Props.read_from(channel_dict)
|
|
75
|
+
|
|
76
|
+
if args.overwrite_files:
|
|
77
|
+
overwrite = Props.read_from(args.overwrite_files)
|
|
78
|
+
if args.channel in overwrite:
|
|
79
|
+
overwrite = overwrite[args.channel]["pars"]["operations"]
|
|
80
|
+
else:
|
|
81
|
+
overwrite = None
|
|
82
|
+
else:
|
|
83
|
+
overwrite = None
|
|
84
|
+
|
|
85
|
+
if len(args.fft_files) == 1 and Path(args.fft_files[0]).suffix == ".filelist":
|
|
86
|
+
with Path(args.fft_files[0]).open() as f:
|
|
87
|
+
fft_files = f.read().splitlines()
|
|
88
|
+
else:
|
|
89
|
+
fft_files = args.fft_files
|
|
90
|
+
|
|
91
|
+
if len(args.cal_files) == 1 and Path(args.cal_files[0]).suffix == ".filelist":
|
|
92
|
+
with Path(args.cal_files[0]).open() as f:
|
|
93
|
+
cal_files = f.read().splitlines()
|
|
94
|
+
else:
|
|
95
|
+
cal_files = args.fft_files
|
|
96
|
+
|
|
97
|
+
search_name = (
|
|
98
|
+
args.table_name if args.table_name[-1] == "/" else args.table_name + "/"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
kwarg_dict_fft = kwarg_dict["fft_fields"]
|
|
102
|
+
kwarg_dict_cal = kwarg_dict["cal_fields"]
|
|
103
|
+
|
|
104
|
+
cut_fields = get_keys(
|
|
105
|
+
[key.replace(search_name, "") for key in ls(cal_files[0], search_name)],
|
|
106
|
+
kwarg_dict_cal["cut_parameters"],
|
|
107
|
+
)
|
|
108
|
+
cut_fields += get_keys(
|
|
109
|
+
[key.replace(search_name, "") for key in ls(cal_files[0], search_name)],
|
|
110
|
+
kwarg_dict_fft["cut_parameters"],
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if "initial_cal_cuts" in kwarg_dict:
|
|
114
|
+
init_cal = kwarg_dict["initial_cal_cuts"]
|
|
115
|
+
cut_fields += get_keys(
|
|
116
|
+
[key.replace(search_name, "") for key in ls(cal_files[0], search_name)],
|
|
117
|
+
init_cal["cut_parameters"],
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if len(fft_files) > 0:
|
|
121
|
+
fft_data = load_data(
|
|
122
|
+
fft_files,
|
|
123
|
+
args.table_name,
|
|
124
|
+
{},
|
|
125
|
+
[*cut_fields, "t_sat_lo", "timestamp", "trapTmax"],
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
discharges = fft_data["t_sat_lo"] > 0
|
|
129
|
+
discharge_timestamps = np.where(fft_data["timestamp"][discharges])[0]
|
|
130
|
+
is_recovering = np.full(len(fft_data), False, dtype=bool)
|
|
131
|
+
for tstamp in discharge_timestamps:
|
|
132
|
+
is_recovering = is_recovering | np.where(
|
|
133
|
+
(
|
|
134
|
+
((fft_data["timestamp"] - tstamp) < 0.01)
|
|
135
|
+
& ((fft_data["timestamp"] - tstamp) > 0)
|
|
136
|
+
),
|
|
137
|
+
True,
|
|
138
|
+
False,
|
|
139
|
+
)
|
|
140
|
+
fft_data["is_recovering"] = is_recovering
|
|
141
|
+
|
|
142
|
+
hit_dict_fft = {}
|
|
143
|
+
plot_dict_fft = {}
|
|
144
|
+
cut_data = fft_data.query("is_recovering==0")
|
|
145
|
+
msg = f"cut_data shape: {len(cut_data)}"
|
|
146
|
+
log.debug(msg)
|
|
147
|
+
for name, cut in kwarg_dict_fft["cut_parameters"].items():
|
|
148
|
+
cut_dict, cut_plots = generate_cut_classifiers(
|
|
149
|
+
cut_data,
|
|
150
|
+
{name: cut},
|
|
151
|
+
kwarg_dict.get("rounding", 4),
|
|
152
|
+
display=1 if args.plot_path else 0,
|
|
153
|
+
)
|
|
154
|
+
hit_dict_fft.update(cut_dict)
|
|
155
|
+
plot_dict_fft.update(cut_plots)
|
|
156
|
+
|
|
157
|
+
msg = f"{name} calculated cut_dict is: {json.dumps(convert_dict_np_to_float(cut_dict), indent=2)}"
|
|
158
|
+
log.debug(msg)
|
|
159
|
+
|
|
160
|
+
ct_mask = np.full(len(fft_data), True, dtype=bool)
|
|
161
|
+
for outname, info in cut_dict.items():
|
|
162
|
+
# convert to pandas eval
|
|
163
|
+
exp = info["expression"]
|
|
164
|
+
for key in info.get("parameters", None):
|
|
165
|
+
exp = re.sub(
|
|
166
|
+
f"(?<![a-zA-Z0-9]){key}(?![a-zA-Z0-9])", f"@{key}", exp
|
|
167
|
+
)
|
|
168
|
+
fft_data[outname] = fft_data.eval(
|
|
169
|
+
exp, local_dict=info.get("parameters", None)
|
|
170
|
+
)
|
|
171
|
+
if "_classifier" not in outname:
|
|
172
|
+
ct_mask = ct_mask & fft_data[outname]
|
|
173
|
+
cut_data = fft_data[ct_mask]
|
|
174
|
+
|
|
175
|
+
log.debug("fft cuts applied")
|
|
176
|
+
msg = f"cut_dict is: {json.dumps(convert_dict_np_to_float(hit_dict_fft), indent=2)}"
|
|
177
|
+
log.debug(msg)
|
|
178
|
+
|
|
179
|
+
else:
|
|
180
|
+
hit_dict_fft = {}
|
|
181
|
+
plot_dict_fft = {}
|
|
182
|
+
|
|
183
|
+
if overwrite is not None:
|
|
184
|
+
for name in kwarg_dict_fft["cut_parameters"]:
|
|
185
|
+
for cut_name, cut_dict in overwrite.items():
|
|
186
|
+
if name in cut_name:
|
|
187
|
+
hit_dict_fft.update({cut_name: cut_dict})
|
|
188
|
+
|
|
189
|
+
# load data in
|
|
190
|
+
data, threshold_mask = load_data(
|
|
191
|
+
cal_files,
|
|
192
|
+
args.table_name,
|
|
193
|
+
{},
|
|
194
|
+
[*cut_fields, "timestamp", "trapTmax", "t_sat_lo"],
|
|
195
|
+
threshold=kwarg_dict_cal.get("threshold", 0),
|
|
196
|
+
return_selection_mask=True,
|
|
197
|
+
cal_energy_param="trapTmax",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
mask = get_pulser_mask(
|
|
201
|
+
pulser_file=args.pulser_file,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
data["is_pulser"] = mask[threshold_mask]
|
|
205
|
+
|
|
206
|
+
discharges = data["t_sat_lo"] > 0
|
|
207
|
+
discharge_timestamps = np.where(data["timestamp"][discharges])[0]
|
|
208
|
+
is_recovering = np.full(len(data), False, dtype=bool)
|
|
209
|
+
for tstamp in discharge_timestamps:
|
|
210
|
+
is_recovering = is_recovering | np.where(
|
|
211
|
+
(
|
|
212
|
+
((data["timestamp"] - tstamp) < 0.01)
|
|
213
|
+
& ((data["timestamp"] - tstamp) > 0)
|
|
214
|
+
),
|
|
215
|
+
True,
|
|
216
|
+
False,
|
|
217
|
+
)
|
|
218
|
+
data["is_recovering"] = is_recovering
|
|
219
|
+
|
|
220
|
+
rng = np.random.default_rng()
|
|
221
|
+
mask = np.full(len(data.query("~is_pulser & ~is_recovering")), False, dtype=bool)
|
|
222
|
+
mask[
|
|
223
|
+
rng.choice(len(data.query("~is_pulser & ~is_recovering")), 4000, replace=False)
|
|
224
|
+
] = True
|
|
225
|
+
|
|
226
|
+
if "initial_cal_cuts" in kwarg_dict:
|
|
227
|
+
init_cal = kwarg_dict["initial_cal_cuts"]
|
|
228
|
+
hit_dict_init_cal, plot_dict_init_cal = generate_cut_classifiers(
|
|
229
|
+
data.query("~is_pulser & ~is_recovering")[mask],
|
|
230
|
+
init_cal["cut_parameters"],
|
|
231
|
+
init_cal.get("rounding", 4),
|
|
232
|
+
display=1 if args.plot_path else 0,
|
|
233
|
+
)
|
|
234
|
+
ct_mask = np.full(len(data), True, dtype=bool)
|
|
235
|
+
for outname, info in hit_dict_init_cal.items():
|
|
236
|
+
# convert to pandas eval
|
|
237
|
+
exp = info["expression"]
|
|
238
|
+
for key in info.get("parameters", None):
|
|
239
|
+
exp = re.sub(f"(?<![a-zA-Z0-9]){key}(?![a-zA-Z0-9])", f"@{key}", exp)
|
|
240
|
+
data[outname] = data.eval(exp, local_dict=info.get("parameters", None))
|
|
241
|
+
if "classifier" not in outname:
|
|
242
|
+
ct_mask = ct_mask & data[outname]
|
|
243
|
+
|
|
244
|
+
mask = mask[ct_mask[(~data["is_pulser"] & ~data["is_recovering"]).to_numpy()]]
|
|
245
|
+
data = data[ct_mask]
|
|
246
|
+
log.debug("initial cal cuts applied")
|
|
247
|
+
msg = f"cut_dict is: {json.dumps(convert_dict_np_to_float(hit_dict_init_cal), indent=2)}"
|
|
248
|
+
log.debug(msg)
|
|
249
|
+
|
|
250
|
+
else:
|
|
251
|
+
hit_dict_init_cal = {}
|
|
252
|
+
plot_dict_init_cal = {}
|
|
253
|
+
|
|
254
|
+
if len(data.query("~is_pulser & ~is_recovering")) < 500:
|
|
255
|
+
log.info("Less than 500 pulser events")
|
|
256
|
+
cal_data = data.query("~is_pulser & ~is_recovering")
|
|
257
|
+
else:
|
|
258
|
+
cal_data = data.query("~is_pulser & ~is_recovering")[mask]
|
|
259
|
+
|
|
260
|
+
hit_dict_cal, plot_dict_cal = generate_cut_classifiers(
|
|
261
|
+
cal_data,
|
|
262
|
+
kwarg_dict_cal["cut_parameters"],
|
|
263
|
+
kwarg_dict.get("rounding", 4),
|
|
264
|
+
display=1 if args.plot_path else 0,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
if overwrite is not None:
|
|
268
|
+
for name in kwarg_dict_cal["cut_parameters"]:
|
|
269
|
+
for cut_name, cut_dict in overwrite.items():
|
|
270
|
+
if name in cut_name:
|
|
271
|
+
hit_dict_cal.update({cut_name: cut_dict})
|
|
272
|
+
|
|
273
|
+
hit_dict = {**hit_dict_fft, **hit_dict_init_cal, **hit_dict_cal}
|
|
274
|
+
plot_dict = {**plot_dict_fft, **plot_dict_init_cal, **plot_dict_cal}
|
|
275
|
+
|
|
276
|
+
hit_dict = convert_dict_np_to_float(hit_dict)
|
|
277
|
+
|
|
278
|
+
for outname, info in hit_dict.items():
|
|
279
|
+
# convert to pandas eval
|
|
280
|
+
exp = info["expression"]
|
|
281
|
+
for key in info.get("parameters", None):
|
|
282
|
+
exp = re.sub(f"(?<![a-zA-Z0-9]){key}(?![a-zA-Z0-9])", f"@{key}", exp)
|
|
283
|
+
if outname not in fft_data:
|
|
284
|
+
fft_data[outname] = fft_data.eval(
|
|
285
|
+
exp, local_dict=info.get("parameters", None)
|
|
286
|
+
)
|
|
287
|
+
if outname not in data:
|
|
288
|
+
data[outname] = data.eval(exp, local_dict=info.get("parameters", None))
|
|
289
|
+
|
|
290
|
+
qc_results = {}
|
|
291
|
+
for entry in hit_dict:
|
|
292
|
+
if "classifier" not in entry:
|
|
293
|
+
sf_cal = len(data.query(f"{entry}& ~is_pulser & ~is_recovering")) / len(
|
|
294
|
+
data.query("~is_pulser & ~is_recovering")
|
|
295
|
+
)
|
|
296
|
+
sf_cal_err = 100 * np.sqrt(
|
|
297
|
+
((sf_cal) * (1 - sf_cal))
|
|
298
|
+
/ len(data.query("~is_pulser & ~is_recovering"))
|
|
299
|
+
)
|
|
300
|
+
sf_fft = len(fft_data.query(f"{entry} & ~is_recovering")) / len(
|
|
301
|
+
fft_data.query("~is_recovering")
|
|
302
|
+
)
|
|
303
|
+
sf_fft_err = 100 * np.sqrt(
|
|
304
|
+
((sf_fft) * (1 - sf_fft)) / len(fft_data.query("~is_recovering"))
|
|
305
|
+
)
|
|
306
|
+
sf_cal *= 100
|
|
307
|
+
sf_fft *= 100
|
|
308
|
+
msg = f"{entry} cut applied: {sf_cal:.2f}% of events passed the cut for cal data, {sf_fft:.2f}% for fft data"
|
|
309
|
+
log.info(msg)
|
|
310
|
+
qc_results[entry] = {
|
|
311
|
+
"sf_cal": sf_cal,
|
|
312
|
+
"sf_cal_err": sf_cal_err,
|
|
313
|
+
"sf_fft": sf_fft,
|
|
314
|
+
"sf_fft_err": sf_fft_err,
|
|
315
|
+
}
|
|
316
|
+
qc_results = convert_dict_np_to_float(qc_results)
|
|
317
|
+
|
|
318
|
+
Path(args.save_path).parent.mkdir(parents=True, exist_ok=True)
|
|
319
|
+
Props.write_to(
|
|
320
|
+
args.save_path, {"operations": hit_dict, "results": {"qc": qc_results}}
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if args.plot_path:
|
|
324
|
+
Path(args.plot_path).parent.mkdir(parents=True, exist_ok=True)
|
|
325
|
+
with Path(args.plot_path).open("wb") as f:
|
|
326
|
+
pkl.dump({"qc": plot_dict}, f, protocol=pkl.HIGHEST_PROTOCOL)
|
|
File without changes
|