legend-dataflow-scripts 0.3.2__tar.gz → 0.4.0a1__tar.gz

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 (53) hide show
  1. {legend_dataflow_scripts-0.3.2/src/legend_dataflow_scripts.egg-info → legend_dataflow_scripts-0.4.0a1}/PKG-INFO +3 -3
  2. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/pyproject.toml +3 -3
  3. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1/src/legend_dataflow_scripts.egg-info}/PKG-INFO +3 -3
  4. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/SOURCES.txt +5 -0
  5. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/requires.txt +2 -2
  6. legend_dataflow_scripts-0.4.0a1/src/legend_dataflow_scripts.egg-info/scm_file_list.json +66 -0
  7. legend_dataflow_scripts-0.4.0a1/src/legend_dataflow_scripts.egg-info/scm_version.json +8 -0
  8. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/_version.py +3 -3
  9. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/dplms.py +37 -18
  10. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/eopt.py +59 -24
  11. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/evtsel.py +98 -44
  12. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/nopt.py +24 -11
  13. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/pz.py +45 -42
  14. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/svm.py +5 -0
  15. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/svm_build.py +12 -3
  16. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/hit/aoe.py +168 -92
  17. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/hit/ecal.py +64 -11
  18. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/hit/lq.py +532 -453
  19. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/hit/qc.py +43 -50
  20. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/tier/dsp.py +43 -21
  21. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/tier/hit.py +66 -32
  22. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/__init__.py +42 -0
  23. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/utils/alias_table.py +20 -13
  24. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/cfgtools.py +150 -0
  25. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/convert_np.py +32 -0
  26. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/discharge.py +67 -0
  27. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/files.py +101 -0
  28. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/lgdo_utils.py +62 -0
  29. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/utils/log.py +11 -19
  30. legend_dataflow_scripts-0.4.0a1/src/legenddataflowscripts/utils/pulser_removal.py +60 -0
  31. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/workflow/execenv.py +18 -3
  32. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/workflow/filedb.py +15 -11
  33. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/workflow/utils.py +1 -1
  34. legend_dataflow_scripts-0.3.2/src/legenddataflowscripts/utils/__init__.py +0 -17
  35. legend_dataflow_scripts-0.3.2/src/legenddataflowscripts/utils/cfgtools.py +0 -38
  36. legend_dataflow_scripts-0.3.2/src/legenddataflowscripts/utils/convert_np.py +0 -31
  37. legend_dataflow_scripts-0.3.2/src/legenddataflowscripts/utils/pulser_removal.py +0 -32
  38. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/LICENSE +0 -0
  39. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/README.md +0 -0
  40. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/setup.cfg +0 -0
  41. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/dependency_links.txt +0 -0
  42. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/entry_points.txt +0 -0
  43. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/not-zip-safe +0 -0
  44. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legend_dataflow_scripts.egg-info/top_level.txt +0 -0
  45. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/__init__.py +0 -0
  46. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/__init__.py +0 -0
  47. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/__init__.py +0 -0
  48. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/dsp/__init__.py +0 -0
  49. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/par/geds/hit/__init__.py +0 -0
  50. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/tier/__init__.py +0 -0
  51. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/utils/plot_dict.py +0 -0
  52. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/workflow/__init__.py +0 -0
  53. {legend_dataflow_scripts-0.3.2 → legend_dataflow_scripts-0.4.0a1}/src/legenddataflowscripts/workflow/pre_compile_catalog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: legend-dataflow-scripts
3
- Version: 0.3.2
3
+ Version: 0.4.0a1
4
4
  Summary: Python package for the processing scripts for LEGEND-200 data
5
5
  Author-email: George Marshall <ggmarsh@uw.edu>, Luigi Pertoldi <gipert@pm.me>
6
6
  Maintainer: The LEGEND Collaboration
@@ -18,8 +18,8 @@ Classifier: Topic :: Scientific/Engineering
18
18
  Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  Requires-Dist: colorlog
21
- Requires-Dist: dbetto>=1.3.6
22
- Requires-Dist: pygama>=2.3.10
21
+ Requires-Dist: dbetto>=1.4
22
+ Requires-Dist: pygama>=2.7.0a1
23
23
  Requires-Dist: dspeed>=2.1.4
24
24
  Requires-Dist: pylegendmeta>=1.4.4
25
25
  Requires-Dist: legend-pydataobj>=2.0.2
@@ -51,8 +51,8 @@ dynamic = ["version"]
51
51
 
52
52
  dependencies = [
53
53
  "colorlog",
54
- "dbetto>=1.3.6",
55
- "pygama>=2.3.10",
54
+ "dbetto>=1.4",
55
+ "pygama>=2.7.0a1",
56
56
  "dspeed>=2.1.4",
57
57
  "pylegendmeta>=1.4.4",
58
58
  "legend-pydataobj>=2.0.2",
@@ -162,7 +162,7 @@ isort.required-imports = ["from __future__ import annotations"]
162
162
 
163
163
 
164
164
  [tool.pylint]
165
- py-version = "3.9"
165
+ py-version = "3.11"
166
166
  ignore-paths = [".*/_version.py"]
167
167
  reports.output-format = "colorized"
168
168
  similarities.ignore-imports = "yes"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: legend-dataflow-scripts
3
- Version: 0.3.2
3
+ Version: 0.4.0a1
4
4
  Summary: Python package for the processing scripts for LEGEND-200 data
5
5
  Author-email: George Marshall <ggmarsh@uw.edu>, Luigi Pertoldi <gipert@pm.me>
6
6
  Maintainer: The LEGEND Collaboration
@@ -18,8 +18,8 @@ Classifier: Topic :: Scientific/Engineering
18
18
  Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  Requires-Dist: colorlog
21
- Requires-Dist: dbetto>=1.3.6
22
- Requires-Dist: pygama>=2.3.10
21
+ Requires-Dist: dbetto>=1.4
22
+ Requires-Dist: pygama>=2.7.0a1
23
23
  Requires-Dist: dspeed>=2.1.4
24
24
  Requires-Dist: pylegendmeta>=1.4.4
25
25
  Requires-Dist: legend-pydataobj>=2.0.2
@@ -7,6 +7,8 @@ src/legend_dataflow_scripts.egg-info/dependency_links.txt
7
7
  src/legend_dataflow_scripts.egg-info/entry_points.txt
8
8
  src/legend_dataflow_scripts.egg-info/not-zip-safe
9
9
  src/legend_dataflow_scripts.egg-info/requires.txt
10
+ src/legend_dataflow_scripts.egg-info/scm_file_list.json
11
+ src/legend_dataflow_scripts.egg-info/scm_version.json
10
12
  src/legend_dataflow_scripts.egg-info/top_level.txt
11
13
  src/legenddataflowscripts/__init__.py
12
14
  src/legenddataflowscripts/_version.py
@@ -32,6 +34,9 @@ src/legenddataflowscripts/utils/__init__.py
32
34
  src/legenddataflowscripts/utils/alias_table.py
33
35
  src/legenddataflowscripts/utils/cfgtools.py
34
36
  src/legenddataflowscripts/utils/convert_np.py
37
+ src/legenddataflowscripts/utils/discharge.py
38
+ src/legenddataflowscripts/utils/files.py
39
+ src/legenddataflowscripts/utils/lgdo_utils.py
35
40
  src/legenddataflowscripts/utils/log.py
36
41
  src/legenddataflowscripts/utils/plot_dict.py
37
42
  src/legenddataflowscripts/utils/pulser_removal.py
@@ -1,6 +1,6 @@
1
1
  colorlog
2
- dbetto>=1.3.6
3
- pygama>=2.3.10
2
+ dbetto>=1.4
3
+ pygama>=2.7.0a1
4
4
  dspeed>=2.1.4
5
5
  pylegendmeta>=1.4.4
6
6
  legend-pydataobj>=2.0.2
@@ -0,0 +1,66 @@
1
+ {
2
+ "files": [
3
+ ".github/dependabot.yml",
4
+ ".github/release.yml",
5
+ ".github/workflows/cd.yml",
6
+ ".github/workflows/main.yml",
7
+ ".gitignore",
8
+ ".pre-commit-config.yaml",
9
+ ".readthedocs.yaml",
10
+ "AI_POLICY.md",
11
+ "LICENSE",
12
+ "MANIFEST.in",
13
+ "README.md",
14
+ "docs/Makefile",
15
+ "docs/source/callables.rst",
16
+ "docs/source/conf.py",
17
+ "docs/source/developer.rst",
18
+ "docs/source/index.rst",
19
+ "docs/source/overview.rst",
20
+ "pyproject.toml",
21
+ "src/legenddataflowscripts/__init__.py",
22
+ "src/legenddataflowscripts/par/__init__.py",
23
+ "src/legenddataflowscripts/par/geds/__init__.py",
24
+ "src/legenddataflowscripts/par/geds/dsp/__init__.py",
25
+ "src/legenddataflowscripts/par/geds/dsp/dplms.py",
26
+ "src/legenddataflowscripts/par/geds/dsp/eopt.py",
27
+ "src/legenddataflowscripts/par/geds/dsp/evtsel.py",
28
+ "src/legenddataflowscripts/par/geds/dsp/nopt.py",
29
+ "src/legenddataflowscripts/par/geds/dsp/pz.py",
30
+ "src/legenddataflowscripts/par/geds/dsp/svm.py",
31
+ "src/legenddataflowscripts/par/geds/dsp/svm_build.py",
32
+ "src/legenddataflowscripts/par/geds/hit/__init__.py",
33
+ "src/legenddataflowscripts/par/geds/hit/aoe.py",
34
+ "src/legenddataflowscripts/par/geds/hit/ecal.py",
35
+ "src/legenddataflowscripts/par/geds/hit/lq.py",
36
+ "src/legenddataflowscripts/par/geds/hit/qc.py",
37
+ "src/legenddataflowscripts/tier/__init__.py",
38
+ "src/legenddataflowscripts/tier/dsp.py",
39
+ "src/legenddataflowscripts/tier/hit.py",
40
+ "src/legenddataflowscripts/utils/__init__.py",
41
+ "src/legenddataflowscripts/utils/alias_table.py",
42
+ "src/legenddataflowscripts/utils/cfgtools.py",
43
+ "src/legenddataflowscripts/utils/convert_np.py",
44
+ "src/legenddataflowscripts/utils/discharge.py",
45
+ "src/legenddataflowscripts/utils/files.py",
46
+ "src/legenddataflowscripts/utils/lgdo_utils.py",
47
+ "src/legenddataflowscripts/utils/log.py",
48
+ "src/legenddataflowscripts/utils/plot_dict.py",
49
+ "src/legenddataflowscripts/utils/pulser_removal.py",
50
+ "src/legenddataflowscripts/workflow/__init__.py",
51
+ "src/legenddataflowscripts/workflow/execenv.py",
52
+ "src/legenddataflowscripts/workflow/filedb.py",
53
+ "src/legenddataflowscripts/workflow/pre_compile_catalog.py",
54
+ "src/legenddataflowscripts/workflow/utils.py",
55
+ "tests/dataflow-config.yaml",
56
+ "tests/test_build_tier_hit.py",
57
+ "tests/test_console_scripts.py",
58
+ "tests/test_convert_py.py",
59
+ "tests/test_discharge.py",
60
+ "tests/test_execenv.py",
61
+ "tests/test_lgdo_utils.py",
62
+ "tests/test_peak_selection.py",
63
+ "tests/test_util.py",
64
+ "tests/test_utils_helpers.py"
65
+ ]
66
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.4.0a1",
3
+ "distance": 0,
4
+ "node": "g30429c60446a5d3e19023e462981054d0cda7d7d",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-16"
8
+ }
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.2'
22
- __version_tuple__ = version_tuple = (0, 3, 2)
21
+ __version__ = version = '0.4.0a1'
22
+ __version_tuple__ = version_tuple = (0, 4, 0, 'a1')
23
23
 
24
- __commit_id__ = commit_id = 'g30a72af68'
24
+ __commit_id__ = commit_id = 'g30429c604'
@@ -14,7 +14,16 @@ from pygama.pargen.data_cleaning import generate_cuts
14
14
  from pygama.pargen.dplms_ge_dict import dplms_ge_dict
15
15
  from pygama.pargen.dsp_optimize import run_one_dsp
16
16
 
17
- from ....utils import build_log, convert_dict_np_to_float
17
+ from ....utils import (
18
+ build_log,
19
+ check_input_files,
20
+ convert_dict_np_to_float,
21
+ expand_filelist,
22
+ prepare_output_paths,
23
+ require_config_keys,
24
+ require_peaks_present,
25
+ take_table_rows,
26
+ )
18
27
 
19
28
 
20
29
  def par_geds_dsp_dplms() -> None:
@@ -55,7 +64,13 @@ def par_geds_dsp_dplms() -> None:
55
64
  Processing chain configuration file(s).
56
65
  ``--config-file`` : list of str
57
66
  DPLMS configuration file(s). Must contain ``run_dplms`` (bool),
58
- ``n_baselines`` (int), and ``peaks_kev`` (list).
67
+ ``n_baselines`` (int), and ``peaks_kev`` (list). The optional key
68
+ ``use_log_pdf`` (bool, default false) is forwarded to
69
+ :func:`pygama.pargen.dplms_ge_dict.dplms_ge_dict` and builds the
70
+ grid-search FOM fits from the model's log-density (iminuit
71
+ ``log=True``) — substantially faster, results differ at
72
+ machine-precision level; requires a pygama version with
73
+ ``use_log_pdf`` support.
59
74
  ``--channel`` : str
60
75
  Channel identifier; used as the HDF5 group name in *lh5-path*.
61
76
  ``--raw-table-name`` : str
@@ -100,17 +115,22 @@ def par_geds_dsp_dplms() -> None:
100
115
 
101
116
  args = argparser.parse_args()
102
117
 
103
- dsp_config = Props.read_from(args.processing_chain)
104
118
  log = build_log(args.log_config, args.log)
105
119
 
120
+ check_input_files(args.peak_file, "--peak-file")
121
+ check_input_files(args.inplots, "--inplots")
122
+ prepare_output_paths(args.lh5_path, args.dsp_pars, args.plot_path)
123
+
124
+ dsp_config = Props.read_from(args.processing_chain)
125
+
106
126
  t0 = time.time()
107
127
 
108
128
  dplms_dict = Props.read_from(args.config_file)
109
129
  db_dict = Props.read_from(args.database)
130
+ require_config_keys(dplms_dict, ["run_dplms"], f"dplms config ({args.config_file})")
110
131
 
111
132
  if dplms_dict["run_dplms"] is True:
112
- with Path(args.fft_raw_filelist).open() as f:
113
- fft_files = sorted(f.read().splitlines())
133
+ fft_files = expand_filelist([args.fft_raw_filelist], "--fft-raw-filelist")
114
134
 
115
135
  t0 = time.time()
116
136
  log.info("\nLoad fft data")
@@ -130,17 +150,20 @@ def par_geds_dsp_dplms() -> None:
130
150
 
131
151
  log.info("\nRunning event selection")
132
152
  peaks_kev = np.array(dplms_dict["peaks_kev"])
133
- # kev_widths = [tuple(kev_width) for kev_width in dplms_dict["kev_widths"]]
134
153
 
135
154
  peaks_rounded = [int(peak) for peak in peaks_kev]
136
155
  peaks = lh5.read_as(f"{args.raw_table_name}/peak", args.peak_file, library="np")
156
+ require_peaks_present(
157
+ np.unique(peaks),
158
+ peaks_rounded,
159
+ f"peak file {args.peak_file} requested by dplms config ({args.config_file})",
160
+ )
137
161
  ids = np.isin(peaks, peaks_rounded)
138
- peaks = peaks[ids]
139
- # idx_list = [np.where(peaks == peak)[0] for peak in peaks_rounded]
140
162
 
141
163
  raw_cal = lh5.read(args.raw_table_name, args.peak_file, idx=ids)
142
164
  msg = f"Time to run event selection {(time.time() - t1):.2f} s, total events {len(raw_cal)}"
143
165
  log.info(msg)
166
+ del peaks, ids
144
167
 
145
168
  if isinstance(dsp_config, str | list):
146
169
  dsp_config = Props.read_from(dsp_config)
@@ -156,13 +179,12 @@ def par_geds_dsp_dplms() -> None:
156
179
  for cut in cut_dict:
157
180
  idxs = dsp_fft[cut].nda & idxs
158
181
 
159
- selected_eidxs = eidxs[: len(dsp_fft)]
160
- raw_fft = lh5.read(
161
- args.raw_table_name,
162
- fft_files,
163
- n_rows=dplms_dict["n_baselines"],
164
- idx=selected_eidxs[idxs],
165
- )
182
+ # idxs is a boolean mask over the rows already loaded in raw_fft, so
183
+ # subset in memory instead of re-reading the FFT files from disk
184
+ # (row-identical: the first read was idx=eidxs capped at n_baselines,
185
+ # so the re-applied n_rows cap was a no-op)
186
+ raw_fft = take_table_rows(raw_fft, idxs)
187
+ del dsp_fft, energies, eidxs, cut_dict, idxs
166
188
 
167
189
  log.debug("Applied Cuts")
168
190
 
@@ -201,17 +223,14 @@ def par_geds_dsp_dplms() -> None:
201
223
 
202
224
  db_dict.update(out_dict)
203
225
 
204
- Path(args.lh5_path).parent.mkdir(parents=True, exist_ok=True)
205
226
  lh5.write(
206
227
  Table(col_dict={"dplms": dplms_pars}),
207
228
  name=args.channel,
208
229
  lh5_file=args.lh5_path,
209
230
  wo_mode="overwrite",
210
231
  )
211
- Path(args.dsp_pars).parent.mkdir(parents=True, exist_ok=True)
212
232
  Props.write_to(args.dsp_pars, convert_dict_np_to_float(db_dict))
213
233
 
214
234
  if args.plot_path:
215
- Path(args.plot_path).parent.mkdir(parents=True, exist_ok=True)
216
235
  with Path(args.plot_path).open("wb") as f:
217
236
  pkl.dump(inplot_dict, f, protocol=pkl.HIGHEST_PROTOCOL)
@@ -19,7 +19,13 @@ from pygama.pargen.dsp_optimize import (
19
19
  run_bayesian_optimisation,
20
20
  )
21
21
 
22
- from ....utils import build_log
22
+ from ....utils import (
23
+ build_log,
24
+ check_input_files,
25
+ prepare_output_paths,
26
+ require_config_keys,
27
+ require_peaks_present,
28
+ )
23
29
 
24
30
  warnings.filterwarnings(action="ignore", category=RuntimeWarning)
25
31
  try:
@@ -64,7 +70,11 @@ def par_geds_dsp_eopt() -> None:
64
70
  Energy optimisation configuration file(s). Must contain ``run_eopt``
65
71
  (bool), ``peaks`` (list of keV values), ``kev_widths``, ``fom``,
66
72
  ``fom_field``, ``fom_err_field``, ``initial_samples``, ``acq_func``,
67
- ``batch_size``, and ``n_iter``.
73
+ ``batch_size``, and ``n_iter``. Optional: ``use_log_pdf`` (bool,
74
+ default false) builds the FOM's unbinned fits from the model's
75
+ log-density (iminuit ``log=True``) — substantially faster, results
76
+ differ at machine-precision level; requires a pygama version with
77
+ ``use_log_pdf`` support.
68
78
  ``--log-config`` : str, optional
69
79
  Logging configuration file.
70
80
  ``--raw-table-name`` : str
@@ -107,7 +117,9 @@ def par_geds_dsp_eopt() -> None:
107
117
  argparser.add_argument(
108
118
  "--final-dsp-pars", help="final_dsp_pars", type=str, required=True
109
119
  )
110
- argparser.add_argument("--qbb-grid-path", help="qbb_grid_path", type=str)
120
+ argparser.add_argument(
121
+ "--qbb-grid-path", help="qbb_grid_path", type=str, required=True
122
+ )
111
123
  argparser.add_argument("--plot-path", help="plot_path", type=str)
112
124
 
113
125
  argparser.add_argument(
@@ -115,25 +127,31 @@ def par_geds_dsp_eopt() -> None:
115
127
  )
116
128
  args = argparser.parse_args()
117
129
 
118
- dsp_config = Props.read_from(args.processing_chain)
119
130
  log = build_log(args.log_config, args.log)
120
131
 
132
+ check_input_files(args.peak_file, "--peak-file")
133
+ check_input_files(args.inplots, "--inplots")
134
+ prepare_output_paths(args.qbb_grid_path, args.final_dsp_pars, args.plot_path)
135
+
136
+ dsp_config = Props.read_from(args.processing_chain)
137
+
121
138
  t0 = time.time()
122
139
 
123
140
  opt_dict = Props.read_from(args.config_file)
124
141
  db_dict = Props.read_from(args.decay_const)
142
+ require_config_keys(opt_dict, ["run_eopt"], f"eopt config ({args.config_file})")
125
143
 
126
- if opt_dict.pop("run_eopt") is True:
144
+ run_eopt = opt_dict.pop("run_eopt") is True
145
+ if run_eopt:
127
146
  peaks_kev = np.array(opt_dict["peaks"])
128
147
  kev_widths = [tuple(kev_width) for kev_width in opt_dict["kev_widths"]]
129
148
 
130
149
  kwarg_dicts_cusp = []
131
150
  kwarg_dicts_trap = []
132
151
  kwarg_dicts_zac = []
133
- for peak in peaks_kev:
134
- peak_idx = np.where(peaks_kev == peak)[0][0]
135
- kev_width = kev_widths[peak_idx]
136
-
152
+ # strict: a length mismatch between the peaks and kev_widths config
153
+ # lists is a config error and must not silently drop peaks
154
+ for peak, kev_width in zip(peaks_kev, kev_widths, strict=True):
137
155
  kwarg_dicts_cusp.append(
138
156
  {
139
157
  "parameter": "cuspEmax",
@@ -164,6 +182,11 @@ def par_geds_dsp_eopt() -> None:
164
182
 
165
183
  peaks_rounded = [int(peak) for peak in peaks_kev]
166
184
  peaks = lh5.read_as(f"{args.raw_table_name}/peak", args.peak_file, library="np")
185
+ require_peaks_present(
186
+ np.unique(peaks),
187
+ peaks_rounded,
188
+ f"peak file {args.peak_file} requested by eopt config ({args.config_file})",
189
+ )
167
190
  ids = np.isin(peaks, peaks_rounded)
168
191
  peaks = peaks[ids]
169
192
  idx_list = [np.where(peaks == peak)[0] for peak in peaks_rounded]
@@ -191,29 +214,42 @@ def par_geds_dsp_eopt() -> None:
191
214
  db_dict["etrap"] = {"flat": flat_val}
192
215
 
193
216
  tb_data.add_column("dt_eff", init_data["dt_eff"])
217
+ # add_column stores the Array object itself, so dt_eff survives freeing
218
+ # the rest of the init DSP output (otherwise held for the whole
219
+ # optimisation)
220
+ del init_data, full_dt
194
221
 
195
222
  dsp_config["processors"].pop("dt_eff")
196
223
 
197
224
  dsp_config["outputs"] = ["zacEmax", "cuspEmax", "trapEmax", "dt_eff"]
198
225
 
226
+ # opt-in: build the unbinned NLL fits from the model's log-density
227
+ # (iminuit log=True mode) — much faster on large samples, results
228
+ # differ at machine-precision level. Requires pygama with
229
+ # use_log_pdf support; older versions silently ignore the key.
230
+ use_log_pdf = opt_dict.get("use_log_pdf", False)
231
+
199
232
  kwarg_dict = [
200
233
  {
201
234
  "peak_dicts": kwarg_dicts_cusp,
202
235
  "ctc_param": "dt_eff",
203
236
  "idx_list": idx_list,
204
237
  "peaks_kev": peaks_kev,
238
+ "use_log_pdf": use_log_pdf,
205
239
  },
206
240
  {
207
241
  "peak_dicts": kwarg_dicts_zac,
208
242
  "ctc_param": "dt_eff",
209
243
  "idx_list": idx_list,
210
244
  "peaks_kev": peaks_kev,
245
+ "use_log_pdf": use_log_pdf,
211
246
  },
212
247
  {
213
248
  "peak_dicts": kwarg_dicts_trap,
214
249
  "ctc_param": "dt_eff",
215
250
  "idx_list": idx_list,
216
251
  "peaks_kev": peaks_kev,
252
+ "use_log_pdf": use_log_pdf,
217
253
  },
218
254
  ]
219
255
 
@@ -414,14 +450,12 @@ def par_geds_dsp_eopt() -> None:
414
450
  else:
415
451
  db_dict.update({"ctc_params": out_alpha_dict})
416
452
 
417
- Path(args.qbb_grid_path).parent.mkdir(parents=True, exist_ok=True)
418
453
  with Path(args.qbb_grid_path).open("wb") as f:
419
454
  pkl.dump({"eopt": optimisers}, f, protocol=pkl.HIGHEST_PROTOCOL)
420
455
 
421
456
  else:
422
457
  Path(args.qbb_grid_path).touch()
423
458
 
424
- Path(args.final_dsp_pars).parent.mkdir(parents=True, exist_ok=True)
425
459
  Props.write_to(args.final_dsp_pars, db_dict)
426
460
 
427
461
  if args.plot_path:
@@ -431,21 +465,22 @@ def par_geds_dsp_eopt() -> None:
431
465
  else:
432
466
  plot_dict = {}
433
467
 
434
- plot_dict["trap_optimisation"] = {
435
- "kernel_space": bopt_trap.plot(init_samples=sample_x),
436
- "acq_space": bopt_trap.plot_acq(init_samples=sample_x),
437
- }
468
+ # the optimiser plots only exist when the optimisation actually ran
469
+ if run_eopt:
470
+ plot_dict["trap_optimisation"] = {
471
+ "kernel_space": bopt_trap.plot(init_samples=sample_x),
472
+ "acq_space": bopt_trap.plot_acq(init_samples=sample_x),
473
+ }
438
474
 
439
- plot_dict["cusp_optimisation"] = {
440
- "kernel_space": bopt_cusp.plot(init_samples=sample_x),
441
- "acq_space": bopt_cusp.plot_acq(init_samples=sample_x),
442
- }
475
+ plot_dict["cusp_optimisation"] = {
476
+ "kernel_space": bopt_cusp.plot(init_samples=sample_x),
477
+ "acq_space": bopt_cusp.plot_acq(init_samples=sample_x),
478
+ }
443
479
 
444
- plot_dict["zac_optimisation"] = {
445
- "kernel_space": bopt_zac.plot(init_samples=sample_x),
446
- "acq_space": bopt_zac.plot_acq(init_samples=sample_x),
447
- }
480
+ plot_dict["zac_optimisation"] = {
481
+ "kernel_space": bopt_zac.plot(init_samples=sample_x),
482
+ "acq_space": bopt_zac.plot_acq(init_samples=sample_x),
483
+ }
448
484
 
449
- Path(args.plot_path).parent.mkdir(parents=True, exist_ok=True)
450
485
  with Path(args.plot_path).open("wb") as w:
451
486
  pkl.dump(plot_dict, w, protocol=pkl.HIGHEST_PROTOCOL)