reboost 0.7.0__py3-none-any.whl → 0.8.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.
- reboost/_version.py +2 -2
- reboost/core.py +0 -2
- reboost/optmap/cli.py +40 -101
- reboost/optmap/convolve.py +12 -285
- reboost/optmap/create.py +39 -123
- reboost/optmap/evt.py +5 -2
- reboost/optmap/mapview.py +9 -7
- reboost/optmap/optmap.py +11 -12
- reboost/spms/pe.py +80 -1
- reboost/utils.py +1 -1
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/METADATA +4 -2
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/RECORD +16 -16
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/WHEEL +0 -0
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/entry_points.txt +0 -0
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {reboost-0.7.0.dist-info → reboost-0.8.0.dist-info}/top_level.txt +0 -0
reboost/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.8.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 8, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
reboost/core.py
CHANGED
|
@@ -325,8 +325,6 @@ def get_one_detector_mapping(
|
|
|
325
325
|
out_list = list(output_detector_expression)
|
|
326
326
|
|
|
327
327
|
for expression_tmp in out_list:
|
|
328
|
-
func, globs = utils.get_function_string(expression_tmp)
|
|
329
|
-
|
|
330
328
|
# if no package was imported its just a name
|
|
331
329
|
try:
|
|
332
330
|
objs = evaluate_object(expression_tmp, local_dict={"ARGS": args, "OBJECTS": objects})
|
reboost/optmap/cli.py
CHANGED
|
@@ -35,21 +35,7 @@ def optical_cli() -> None:
|
|
|
35
35
|
|
|
36
36
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
37
37
|
|
|
38
|
-
# STEP
|
|
39
|
-
evt_parser = subparsers.add_parser("evt", help="build optmap-evt file from remage stp file")
|
|
40
|
-
evt_parser_det_group = evt_parser.add_mutually_exclusive_group(required=True)
|
|
41
|
-
evt_parser_det_group.add_argument(
|
|
42
|
-
"--geom",
|
|
43
|
-
help="GDML geometry file",
|
|
44
|
-
)
|
|
45
|
-
evt_parser_det_group.add_argument(
|
|
46
|
-
"--detectors",
|
|
47
|
-
help="file with detector ids of all optical channels.",
|
|
48
|
-
)
|
|
49
|
-
evt_parser.add_argument("input", help="input stp LH5 file", metavar="INPUT_STP")
|
|
50
|
-
evt_parser.add_argument("output", help="output evt LH5 file", metavar="OUTPUT_EVT")
|
|
51
|
-
|
|
52
|
-
# STEP 2a: build map file from evt tier
|
|
38
|
+
# STEP 1a: build map file from evt tier
|
|
53
39
|
map_parser = subparsers.add_parser("createmap", help="build optical map from evt file(s)")
|
|
54
40
|
map_parser.add_argument(
|
|
55
41
|
"--settings",
|
|
@@ -91,42 +77,67 @@ def optical_cli() -> None:
|
|
|
91
77
|
)
|
|
92
78
|
map_parser.add_argument("output", help="output map LH5 file", metavar="OUTPUT_MAP")
|
|
93
79
|
|
|
94
|
-
# STEP
|
|
95
|
-
mapview_parser = subparsers.add_parser(
|
|
80
|
+
# STEP 1b: view maps
|
|
81
|
+
mapview_parser = subparsers.add_parser(
|
|
82
|
+
"viewmap",
|
|
83
|
+
help="view optical map (arrows: navigate slices/axes, 'c': channel selector)",
|
|
84
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
85
|
+
description=(
|
|
86
|
+
"Interactively view optical maps stored in LH5 files.\n\n"
|
|
87
|
+
"Keyboard controls:\n"
|
|
88
|
+
" left/right - previous/next slice along the current axis\n"
|
|
89
|
+
" up/down - switch slicing axis (x, y, z)\n"
|
|
90
|
+
" c - open channel selector overlay to switch detector map\n\n"
|
|
91
|
+
"Display notes:\n"
|
|
92
|
+
" - Cells where no primary photons were simulated are shown in white.\n"
|
|
93
|
+
" - Cells where no photons were detected are shown in grey.\n"
|
|
94
|
+
" - Cells with values above the colormap maximum are shown in red.\n"
|
|
95
|
+
" - Use --hist to choose which histogram to display. 'prob_unc_rel' shows the\n"
|
|
96
|
+
" relative uncertainty prob_unc / prob where defined.\n"
|
|
97
|
+
" - Use --divide to show the ratio of two map files (this/other)."
|
|
98
|
+
),
|
|
99
|
+
epilog=(
|
|
100
|
+
"Examples:\n"
|
|
101
|
+
" reboost-optical viewmap mymap.lh5\n"
|
|
102
|
+
" reboost-optical viewmap mymap.lh5 --channel _1067205\n"
|
|
103
|
+
" reboost-optical viewmap mymap.lh5 --hist prob_unc_rel --min 0 --max 1\n"
|
|
104
|
+
" reboost-optical viewmap mymap.lh5 --divide other.lh5 --title 'Comparison'"
|
|
105
|
+
),
|
|
106
|
+
)
|
|
96
107
|
mapview_parser.add_argument("input", help="input map LH5 file", metavar="INPUT_MAP")
|
|
97
108
|
mapview_parser.add_argument(
|
|
98
109
|
"--channel",
|
|
99
110
|
action="store",
|
|
100
111
|
default="all",
|
|
101
|
-
help="default: %(default)s",
|
|
112
|
+
help="channel to display ('all' or '_<detid>'). Press 'c' in the viewer to switch. default: %(default)s",
|
|
102
113
|
)
|
|
103
114
|
mapview_parser.add_argument(
|
|
104
115
|
"--hist",
|
|
105
|
-
choices=("
|
|
116
|
+
choices=("_nr_gen", "_nr_det", "prob", "prob_unc", "prob_unc_rel"),
|
|
106
117
|
action="store",
|
|
107
|
-
default="
|
|
118
|
+
default="prob",
|
|
108
119
|
help="select optical map histogram to show. default: %(default)s",
|
|
109
120
|
)
|
|
110
121
|
mapview_parser.add_argument(
|
|
111
122
|
"--divide",
|
|
112
123
|
action="store",
|
|
113
|
-
help="default: none",
|
|
124
|
+
help="divide by another map file before display (ratio). default: none",
|
|
114
125
|
)
|
|
115
126
|
mapview_parser.add_argument(
|
|
116
127
|
"--min",
|
|
117
128
|
default=1e-4,
|
|
118
129
|
type=(lambda s: s if s == "auto" else float(s)),
|
|
119
|
-
help="colormap min value. default: %(default)e",
|
|
130
|
+
help="colormap min value; use 'auto' for automatic scaling. default: %(default)e",
|
|
120
131
|
)
|
|
121
132
|
mapview_parser.add_argument(
|
|
122
133
|
"--max",
|
|
123
134
|
default=1e-2,
|
|
124
135
|
type=(lambda s: s if s == "auto" else float(s)),
|
|
125
|
-
help="colormap max value. default: %(default)e",
|
|
136
|
+
help="colormap max value; use 'auto' for automatic scaling. default: %(default)e",
|
|
126
137
|
)
|
|
127
138
|
mapview_parser.add_argument("--title", help="title of figure. default: stem of filename")
|
|
128
139
|
|
|
129
|
-
# STEP
|
|
140
|
+
# STEP 1c: merge maps
|
|
130
141
|
mapmerge_parser = subparsers.add_parser("mergemap", help="merge optical maps")
|
|
131
142
|
mapmerge_parser.add_argument(
|
|
132
143
|
"input", help="input map LH5 files", metavar="INPUT_MAP", nargs="+"
|
|
@@ -151,49 +162,10 @@ def optical_cli() -> None:
|
|
|
151
162
|
help="""Check map statistics after creation. default: %(default)s""",
|
|
152
163
|
)
|
|
153
164
|
|
|
154
|
-
# STEP
|
|
165
|
+
# STEP 1d: check map
|
|
155
166
|
checkmap_parser = subparsers.add_parser("checkmap", help="check optical maps")
|
|
156
167
|
checkmap_parser.add_argument("input", help="input map LH5 file", metavar="INPUT_MAP")
|
|
157
168
|
|
|
158
|
-
# STEP 3: convolve with hits from non-optical simulations
|
|
159
|
-
convolve_parser = subparsers.add_parser(
|
|
160
|
-
"convolve", help="convolve non-optical hits with optical map"
|
|
161
|
-
)
|
|
162
|
-
convolve_parser.add_argument(
|
|
163
|
-
"--material",
|
|
164
|
-
action="store",
|
|
165
|
-
choices=("lar", "pen", "fiber"),
|
|
166
|
-
default="lar",
|
|
167
|
-
help="default: %(default)s",
|
|
168
|
-
)
|
|
169
|
-
convolve_parser.add_argument(
|
|
170
|
-
"--map",
|
|
171
|
-
action="store",
|
|
172
|
-
required=True,
|
|
173
|
-
metavar="INPUT_MAP",
|
|
174
|
-
help="input map LH5 file",
|
|
175
|
-
)
|
|
176
|
-
convolve_parser.add_argument(
|
|
177
|
-
"--edep",
|
|
178
|
-
action="store",
|
|
179
|
-
required=True,
|
|
180
|
-
metavar="INPUT_EDEP",
|
|
181
|
-
help="input non-optical LH5 hit file",
|
|
182
|
-
)
|
|
183
|
-
convolve_parser.add_argument(
|
|
184
|
-
"--edep-lgdo",
|
|
185
|
-
action="store",
|
|
186
|
-
required=True,
|
|
187
|
-
metavar="LGDO_PATH",
|
|
188
|
-
help="path to LGDO inside non-optical LH5 hit file (e.g. /stp/detXX)",
|
|
189
|
-
)
|
|
190
|
-
convolve_parser.add_argument(
|
|
191
|
-
"--dist-mode",
|
|
192
|
-
action="store",
|
|
193
|
-
default="poisson+no-fano",
|
|
194
|
-
)
|
|
195
|
-
convolve_parser.add_argument("--output", help="output hit LH5 file", metavar="OUTPUT_HIT")
|
|
196
|
-
|
|
197
169
|
# STEP X: rebin maps
|
|
198
170
|
rebin_parser = subparsers.add_parser("rebin", help="rebin optical maps")
|
|
199
171
|
rebin_parser.add_argument("input", help="input map LH5 files", metavar="INPUT_MAP")
|
|
@@ -205,24 +177,7 @@ def optical_cli() -> None:
|
|
|
205
177
|
log_level = (None, logging.INFO, logging.DEBUG)[min(args.verbose, 2)]
|
|
206
178
|
setup_log(log_level)
|
|
207
179
|
|
|
208
|
-
# STEP
|
|
209
|
-
if args.command == "evt":
|
|
210
|
-
from .evt import build_optmap_evt, get_optical_detectors_from_geom
|
|
211
|
-
|
|
212
|
-
_check_input_file(parser, args.input)
|
|
213
|
-
_check_output_file(parser, args.output)
|
|
214
|
-
|
|
215
|
-
# load detector ids from the geometry.
|
|
216
|
-
if args.geom is not None:
|
|
217
|
-
_check_input_file(parser, args.geom, "geometry")
|
|
218
|
-
detectors = get_optical_detectors_from_geom(args.geom)
|
|
219
|
-
else:
|
|
220
|
-
_check_input_file(parser, args.detectors, "detectors")
|
|
221
|
-
detectors = dbetto.utils.load_dict(args.detectors)
|
|
222
|
-
|
|
223
|
-
build_optmap_evt(args.input, args.output, detectors, args.bufsize)
|
|
224
|
-
|
|
225
|
-
# STEP 2a: build map file from evt tier
|
|
180
|
+
# STEP 1a: build map file from evt tier
|
|
226
181
|
if args.command == "createmap":
|
|
227
182
|
from .create import create_optical_maps
|
|
228
183
|
|
|
@@ -250,7 +205,7 @@ def optical_cli() -> None:
|
|
|
250
205
|
geom_fn=args.geom,
|
|
251
206
|
)
|
|
252
207
|
|
|
253
|
-
# STEP
|
|
208
|
+
# STEP 1b: view maps
|
|
254
209
|
if args.command == "viewmap":
|
|
255
210
|
from .mapview import view_optmap
|
|
256
211
|
|
|
@@ -267,7 +222,7 @@ def optical_cli() -> None:
|
|
|
267
222
|
histogram_choice=args.hist,
|
|
268
223
|
)
|
|
269
224
|
|
|
270
|
-
# STEP
|
|
225
|
+
# STEP 1c: merge maps
|
|
271
226
|
if args.command == "mergemap":
|
|
272
227
|
from .create import merge_optical_maps
|
|
273
228
|
|
|
@@ -281,29 +236,13 @@ def optical_cli() -> None:
|
|
|
281
236
|
args.input, args.output, settings, check_after_create=args.check, n_procs=args.n_procs
|
|
282
237
|
)
|
|
283
238
|
|
|
284
|
-
# STEP
|
|
239
|
+
# STEP 1d: check maps
|
|
285
240
|
if args.command == "checkmap":
|
|
286
241
|
from .create import check_optical_map
|
|
287
242
|
|
|
288
243
|
_check_input_file(parser, args.input)
|
|
289
244
|
check_optical_map(args.input)
|
|
290
245
|
|
|
291
|
-
# STEP 3: convolve with hits from non-optical simulations
|
|
292
|
-
if args.command == "convolve":
|
|
293
|
-
from .convolve import convolve
|
|
294
|
-
|
|
295
|
-
_check_input_file(parser, [args.map, args.edep])
|
|
296
|
-
_check_output_file(parser, args.output, optional=True)
|
|
297
|
-
convolve(
|
|
298
|
-
args.map,
|
|
299
|
-
args.edep,
|
|
300
|
-
args.edep_lgdo,
|
|
301
|
-
args.material,
|
|
302
|
-
args.output,
|
|
303
|
-
args.bufsize,
|
|
304
|
-
dist_mode=args.dist_mode,
|
|
305
|
-
)
|
|
306
|
-
|
|
307
246
|
# STEP X: rebin maps
|
|
308
247
|
if args.command == "rebin":
|
|
309
248
|
from .create import rebin_optical_maps
|
reboost/optmap/convolve.py
CHANGED
|
@@ -10,12 +10,9 @@ import numba
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
from legendoptics import fibers, lar, pen
|
|
12
12
|
from lgdo import lh5
|
|
13
|
-
from lgdo.lh5 import LH5Iterator
|
|
14
13
|
from lgdo.types import Array, Histogram, Table
|
|
15
|
-
from numba import njit
|
|
16
|
-
from numpy.lib.recfunctions import structured_to_unstructured
|
|
14
|
+
from numba import njit
|
|
17
15
|
from numpy.typing import NDArray
|
|
18
|
-
from pint import Quantity
|
|
19
16
|
|
|
20
17
|
from .numba_pdg import numba_pdgid_funcs
|
|
21
18
|
|
|
@@ -42,7 +39,7 @@ def open_optmap(optmap_fn: str) -> OptmapForConvolve:
|
|
|
42
39
|
detids = np.array([int(m.lstrip("_")) for m in det_ntuples])
|
|
43
40
|
detidx = np.arange(0, detids.shape[0])
|
|
44
41
|
|
|
45
|
-
optmap_all = lh5.read("/all/
|
|
42
|
+
optmap_all = lh5.read("/all/prob", optmap_fn)
|
|
46
43
|
assert isinstance(optmap_all, Histogram)
|
|
47
44
|
optmap_edges = tuple([b.edges for b in optmap_all.binning])
|
|
48
45
|
|
|
@@ -50,7 +47,7 @@ def open_optmap(optmap_fn: str) -> OptmapForConvolve:
|
|
|
50
47
|
# 0, ..., len(detidx)-1 AND OPTMAP_ANY_CH might be negative.
|
|
51
48
|
ow[OPTMAP_ANY_CH] = optmap_all.weights.nda
|
|
52
49
|
for i, nt in zip(detidx, det_ntuples, strict=True):
|
|
53
|
-
optmap = lh5.read(f"/{nt}/
|
|
50
|
+
optmap = lh5.read(f"/{nt}/prob", optmap_fn)
|
|
54
51
|
assert isinstance(optmap, Histogram)
|
|
55
52
|
ow[i] = optmap.weights.nda
|
|
56
53
|
|
|
@@ -98,7 +95,7 @@ def open_optmap_single(optmap_fn: str, spm_det_uid: int) -> OptmapForConvolve:
|
|
|
98
95
|
except lh5.exceptions.LH5DecodeError: # the _hitcounts_exp might not be always present.
|
|
99
96
|
pass
|
|
100
97
|
|
|
101
|
-
optmap = lh5.read(f"/_{spm_det_uid}/
|
|
98
|
+
optmap = lh5.read(f"/_{spm_det_uid}/prob", optmap_fn)
|
|
102
99
|
assert isinstance(optmap, Histogram)
|
|
103
100
|
ow = np.empty((1, *optmap.weights.nda.shape), dtype=np.float64)
|
|
104
101
|
ow[0] = optmap.weights.nda
|
|
@@ -107,67 +104,13 @@ def open_optmap_single(optmap_fn: str, spm_det_uid: int) -> OptmapForConvolve:
|
|
|
107
104
|
return OptmapForConvolve(np.array([spm_det_uid]), np.array([0]), optmap_edges, ow)
|
|
108
105
|
|
|
109
106
|
|
|
110
|
-
def iterate_stepwise_depositions(
|
|
111
|
-
edep_df: np.rec.recarray,
|
|
112
|
-
optmap: OptmapForConvolve,
|
|
113
|
-
scint_mat_params: sc.ComputedScintParams,
|
|
114
|
-
rng: np.random.Generator = None,
|
|
115
|
-
dist: str = "poisson",
|
|
116
|
-
mode: str = "no-fano",
|
|
117
|
-
):
|
|
118
|
-
# those np functions are not supported by numba, but needed for efficient array access below.
|
|
119
|
-
if "xloc_pre" in edep_df.dtype.names:
|
|
120
|
-
x0 = structured_to_unstructured(edep_df[["xloc_pre", "yloc_pre", "zloc_pre"]], np.float64)
|
|
121
|
-
x1 = structured_to_unstructured(
|
|
122
|
-
edep_df[["xloc_post", "yloc_post", "zloc_post"]], np.float64
|
|
123
|
-
)
|
|
124
|
-
else:
|
|
125
|
-
x0 = structured_to_unstructured(edep_df[["xloc", "yloc", "zloc"]], np.float64)
|
|
126
|
-
x1 = None
|
|
127
|
-
|
|
128
|
-
rng = np.random.default_rng() if rng is None else rng
|
|
129
|
-
output_map, res = _iterate_stepwise_depositions(
|
|
130
|
-
edep_df,
|
|
131
|
-
x0,
|
|
132
|
-
x1,
|
|
133
|
-
rng,
|
|
134
|
-
optmap.detids,
|
|
135
|
-
optmap.detidx,
|
|
136
|
-
optmap.edges,
|
|
137
|
-
optmap.weights,
|
|
138
|
-
scint_mat_params,
|
|
139
|
-
dist,
|
|
140
|
-
mode,
|
|
141
|
-
)
|
|
142
|
-
if res["any_no_stats"] > 0 or res["det_no_stats"] > 0:
|
|
143
|
-
log.warning(
|
|
144
|
-
"had edep out in voxels without stats: %d (%.2f%%)",
|
|
145
|
-
res["any_no_stats"],
|
|
146
|
-
res["det_no_stats"],
|
|
147
|
-
)
|
|
148
|
-
if res["oob"] > 0:
|
|
149
|
-
log.warning(
|
|
150
|
-
"had edep out of map bounds: %d (%.2f%%)",
|
|
151
|
-
res["oob"],
|
|
152
|
-
(res["oob"] / (res["ib"] + res["oob"])) * 100,
|
|
153
|
-
)
|
|
154
|
-
log.debug(
|
|
155
|
-
"VUV_primary %d ->hits_any %d ->hits %d (%.2f %% primaries detected)",
|
|
156
|
-
res["vuv_primary"],
|
|
157
|
-
res["hits_any"],
|
|
158
|
-
res["hits"],
|
|
159
|
-
(res["hits_any"] / res["vuv_primary"]) * 100,
|
|
160
|
-
)
|
|
161
|
-
log.debug("hits/hits_any %.2f", res["hits"] / res["hits_any"])
|
|
162
|
-
return output_map
|
|
163
|
-
|
|
164
|
-
|
|
165
107
|
def iterate_stepwise_depositions_pois(
|
|
166
108
|
edep_hits: ak.Array,
|
|
167
109
|
optmap: OptmapForConvolve,
|
|
168
110
|
scint_mat_params: sc.ComputedScintParams,
|
|
169
111
|
det_uid: int,
|
|
170
112
|
map_scaling: float = 1,
|
|
113
|
+
map_scaling_sigma: float = 0,
|
|
171
114
|
rng: np.random.Generator | None = None,
|
|
172
115
|
):
|
|
173
116
|
if edep_hits.particle.ndim == 1:
|
|
@@ -180,6 +123,7 @@ def iterate_stepwise_depositions_pois(
|
|
|
180
123
|
rng,
|
|
181
124
|
np.where(optmap.detids == det_uid)[0][0],
|
|
182
125
|
map_scaling,
|
|
126
|
+
map_scaling_sigma,
|
|
183
127
|
optmap.edges,
|
|
184
128
|
optmap.weights,
|
|
185
129
|
scint_mat_params,
|
|
@@ -254,184 +198,6 @@ def _pdgid_to_particle(pdgid: int) -> sc.ParticleIndex:
|
|
|
254
198
|
__counts_per_bin_key_type = numba.types.UniTuple(numba.types.int64, 3)
|
|
255
199
|
|
|
256
200
|
|
|
257
|
-
# - run with NUMBA_FULL_TRACEBACKS=1 NUMBA_BOUNDSCHECK=1 for testing/checking
|
|
258
|
-
# - cache=True does not work with outer prange, i.e. loading the cached file fails (numba bug?)
|
|
259
|
-
# - the output dictionary is not threadsafe, so parallel=True is not working with it.
|
|
260
|
-
@njit(parallel=False, nogil=True, cache=True)
|
|
261
|
-
def _iterate_stepwise_depositions(
|
|
262
|
-
edep_df,
|
|
263
|
-
x0,
|
|
264
|
-
x1,
|
|
265
|
-
rng,
|
|
266
|
-
detids,
|
|
267
|
-
detidx,
|
|
268
|
-
optmap_edges,
|
|
269
|
-
optmap_weights,
|
|
270
|
-
scint_mat_params: sc.ComputedScintParams,
|
|
271
|
-
dist: str,
|
|
272
|
-
mode: str,
|
|
273
|
-
):
|
|
274
|
-
pdgid_map = {}
|
|
275
|
-
output_map = {}
|
|
276
|
-
oob = ib = ph_cnt = ph_det = ph_det2 = any_no_stats = det_no_stats = 0 # for statistics
|
|
277
|
-
for rowid in prange(edep_df.shape[0]):
|
|
278
|
-
# if rowid % 100000 == 0:
|
|
279
|
-
# print(rowid)
|
|
280
|
-
t = edep_df[rowid]
|
|
281
|
-
|
|
282
|
-
# get the particle information.
|
|
283
|
-
if t.particle not in pdgid_map:
|
|
284
|
-
pdgid_map[t.particle] = (_pdgid_to_particle(t.particle), _pdg_func.charge(t.particle))
|
|
285
|
-
|
|
286
|
-
# do the scintillation.
|
|
287
|
-
part, charge = pdgid_map[t.particle]
|
|
288
|
-
|
|
289
|
-
# if we have both pre and post step points use them
|
|
290
|
-
# else pass as None
|
|
291
|
-
|
|
292
|
-
scint_times = sc.scintillate(
|
|
293
|
-
scint_mat_params,
|
|
294
|
-
x0[rowid],
|
|
295
|
-
x1[rowid] if x1 is not None else None,
|
|
296
|
-
t.v_pre if x1 is not None else None,
|
|
297
|
-
t.v_post if x1 is not None else None,
|
|
298
|
-
t.time,
|
|
299
|
-
part,
|
|
300
|
-
charge,
|
|
301
|
-
t.edep,
|
|
302
|
-
rng,
|
|
303
|
-
emission_term_model=("poisson" if mode == "no-fano" else "normal_fano"),
|
|
304
|
-
)
|
|
305
|
-
if scint_times.shape[0] == 0: # short-circuit if we have no photons at all.
|
|
306
|
-
continue
|
|
307
|
-
ph_cnt += scint_times.shape[0]
|
|
308
|
-
|
|
309
|
-
# coordinates -> bins of the optical map.
|
|
310
|
-
bins = np.empty((scint_times.shape[0], 3), dtype=np.int64)
|
|
311
|
-
for j in range(3):
|
|
312
|
-
bins[:, j] = np.digitize(scint_times[:, j + 1], optmap_edges[j])
|
|
313
|
-
# normalize all out-of-bounds bins just to one end.
|
|
314
|
-
bins[:, j][bins[:, j] == optmap_edges[j].shape[0]] = 0
|
|
315
|
-
|
|
316
|
-
# there are _much_ less unique bins, unfortunately np.unique(..., axis=n) does not work
|
|
317
|
-
# with numba; also np.sort(..., axis=n) also does not work.
|
|
318
|
-
|
|
319
|
-
counts_per_bin = numba.typed.Dict.empty(
|
|
320
|
-
key_type=__counts_per_bin_key_type,
|
|
321
|
-
value_type=np.int64,
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
# get probabilities from map.
|
|
325
|
-
hitcount = np.zeros((detidx.shape[0], bins.shape[0]), dtype=np.int64)
|
|
326
|
-
for j in prange(bins.shape[0]):
|
|
327
|
-
# note: subtract 1 from bins, to account for np.digitize output.
|
|
328
|
-
cur_bins = (bins[j, 0] - 1, bins[j, 1] - 1, bins[j, 2] - 1)
|
|
329
|
-
if cur_bins[0] == -1 or cur_bins[1] == -1 or cur_bins[2] == -1:
|
|
330
|
-
oob += 1
|
|
331
|
-
continue # out-of-bounds of optmap
|
|
332
|
-
ib += 1
|
|
333
|
-
|
|
334
|
-
px_any = optmap_weights[OPTMAP_ANY_CH, cur_bins[0], cur_bins[1], cur_bins[2]]
|
|
335
|
-
if px_any < 0.0:
|
|
336
|
-
any_no_stats += 1
|
|
337
|
-
continue
|
|
338
|
-
if px_any == 0.0:
|
|
339
|
-
continue
|
|
340
|
-
|
|
341
|
-
if dist == "multinomial":
|
|
342
|
-
if rng.uniform() >= px_any:
|
|
343
|
-
continue
|
|
344
|
-
ph_det += 1
|
|
345
|
-
# we detect this energy deposition; we should at least get one photon out here!
|
|
346
|
-
|
|
347
|
-
detsel_size = 1
|
|
348
|
-
|
|
349
|
-
px_sum = optmap_weights[OPTMAP_SUM_CH, cur_bins[0], cur_bins[1], cur_bins[2]]
|
|
350
|
-
assert px_sum >= 0.0 # should not be negative.
|
|
351
|
-
detp = np.empty(detidx.shape, dtype=np.float64)
|
|
352
|
-
had_det_no_stats = 0
|
|
353
|
-
for d in detidx:
|
|
354
|
-
# normalize so that sum(detp) = 1
|
|
355
|
-
detp[d] = optmap_weights[d, cur_bins[0], cur_bins[1], cur_bins[2]] / px_sum
|
|
356
|
-
if detp[d] < 0.0:
|
|
357
|
-
had_det_no_stats = 1
|
|
358
|
-
detp[d] = 0.0
|
|
359
|
-
det_no_stats += had_det_no_stats
|
|
360
|
-
|
|
361
|
-
# should be equivalent to rng.choice(detidx, size=detsel_size, p=detp)
|
|
362
|
-
detsel = detidx[
|
|
363
|
-
np.searchsorted(np.cumsum(detp), rng.random(size=(detsel_size,)), side="right")
|
|
364
|
-
]
|
|
365
|
-
for d in detsel:
|
|
366
|
-
hitcount[d, j] += 1
|
|
367
|
-
ph_det2 += detsel.shape[0]
|
|
368
|
-
|
|
369
|
-
elif dist == "poisson":
|
|
370
|
-
# store the photon count in each bin, to sample them all at once below.
|
|
371
|
-
if cur_bins not in counts_per_bin:
|
|
372
|
-
counts_per_bin[cur_bins] = 1
|
|
373
|
-
else:
|
|
374
|
-
counts_per_bin[cur_bins] += 1
|
|
375
|
-
|
|
376
|
-
else:
|
|
377
|
-
msg = "unknown distribution"
|
|
378
|
-
raise RuntimeError(msg)
|
|
379
|
-
|
|
380
|
-
if dist == "poisson":
|
|
381
|
-
for j, (cur_bins, ph_counts_to_poisson) in enumerate(counts_per_bin.items()):
|
|
382
|
-
had_det_no_stats = 0
|
|
383
|
-
had_any = 0
|
|
384
|
-
for d in detidx:
|
|
385
|
-
detp = optmap_weights[d, cur_bins[0], cur_bins[1], cur_bins[2]]
|
|
386
|
-
if detp < 0.0:
|
|
387
|
-
had_det_no_stats = 1
|
|
388
|
-
continue
|
|
389
|
-
pois_cnt = rng.poisson(lam=ph_counts_to_poisson * detp)
|
|
390
|
-
hitcount[d, j] += pois_cnt
|
|
391
|
-
ph_det2 += pois_cnt
|
|
392
|
-
had_any = 1
|
|
393
|
-
ph_det += had_any
|
|
394
|
-
det_no_stats += had_det_no_stats
|
|
395
|
-
|
|
396
|
-
assert scint_times.shape[0] >= hitcount.shape[1] # TODO: use the right assertion here.
|
|
397
|
-
out_hits_len = np.sum(hitcount)
|
|
398
|
-
if out_hits_len > 0:
|
|
399
|
-
out_times = np.empty(out_hits_len, dtype=np.float64)
|
|
400
|
-
out_det = np.empty(out_hits_len, dtype=np.int64)
|
|
401
|
-
out_idx = 0
|
|
402
|
-
for d in detidx:
|
|
403
|
-
hc_d_plane_max = np.max(hitcount[d, :])
|
|
404
|
-
# untangle the hitcount array in "planes" that only contain the given number of hits per
|
|
405
|
-
# channel. example: assume a "histogram" of hits per channel:
|
|
406
|
-
# x | | <-- this is plane 2 with 1 hit ("max plane")
|
|
407
|
-
# x | | x <-- this is plane 1 with 2 hits
|
|
408
|
-
# ch: 1 | 2 | 3
|
|
409
|
-
for hc_d_plane_cnt in range(1, hc_d_plane_max + 1):
|
|
410
|
-
hc_d_plane = hitcount[d, :] >= hc_d_plane_cnt
|
|
411
|
-
hc_d_plane_len = np.sum(hc_d_plane)
|
|
412
|
-
if hc_d_plane_len == 0:
|
|
413
|
-
continue
|
|
414
|
-
|
|
415
|
-
# note: we assume "immediate" propagation after scintillation. Here, a single timestamp
|
|
416
|
-
# might be coipied to output/"detected" twice.
|
|
417
|
-
out_times[out_idx : out_idx + hc_d_plane_len] = scint_times[hc_d_plane, 0]
|
|
418
|
-
out_det[out_idx : out_idx + hc_d_plane_len] = detids[d]
|
|
419
|
-
out_idx += hc_d_plane_len
|
|
420
|
-
assert out_idx == out_hits_len # ensure that all of out_{det,times} is filled.
|
|
421
|
-
output_map[np.int64(rowid)] = (t.evtid, out_det, out_times)
|
|
422
|
-
|
|
423
|
-
stats = {
|
|
424
|
-
"oob": oob,
|
|
425
|
-
"ib": ib,
|
|
426
|
-
"vuv_primary": ph_cnt,
|
|
427
|
-
"hits_any": ph_det,
|
|
428
|
-
"hits": ph_det2,
|
|
429
|
-
"any_no_stats": any_no_stats,
|
|
430
|
-
"det_no_stats": det_no_stats,
|
|
431
|
-
}
|
|
432
|
-
return output_map, stats
|
|
433
|
-
|
|
434
|
-
|
|
435
201
|
# - run with NUMBA_FULL_TRACEBACKS=1 NUMBA_BOUNDSCHECK=1 for testing/checking
|
|
436
202
|
# - cache=True does not work with outer prange, i.e. loading the cached file fails (numba bug?)
|
|
437
203
|
# - the output dictionary is not threadsafe, so parallel=True is not working with it.
|
|
@@ -441,6 +207,7 @@ def _iterate_stepwise_depositions_pois(
|
|
|
441
207
|
rng,
|
|
442
208
|
detidx: int,
|
|
443
209
|
map_scaling: float,
|
|
210
|
+
map_scaling_sigma: float,
|
|
444
211
|
optmap_edges,
|
|
445
212
|
optmap_weights,
|
|
446
213
|
scint_mat_params: sc.ComputedScintParams,
|
|
@@ -453,6 +220,10 @@ def _iterate_stepwise_depositions_pois(
|
|
|
453
220
|
hit = edep_hits[rowid]
|
|
454
221
|
hit_output = []
|
|
455
222
|
|
|
223
|
+
map_scaling_evt = map_scaling
|
|
224
|
+
if map_scaling_sigma > 0:
|
|
225
|
+
map_scaling_evt = rng.normal(loc=map_scaling, scale=map_scaling_sigma)
|
|
226
|
+
|
|
456
227
|
assert len(hit.particle) == len(hit.num_scint_ph)
|
|
457
228
|
# iterate steps inside the hit
|
|
458
229
|
for si in range(len(hit.particle)):
|
|
@@ -473,7 +244,7 @@ def _iterate_stepwise_depositions_pois(
|
|
|
473
244
|
ib += 1
|
|
474
245
|
|
|
475
246
|
# get probabilities from map.
|
|
476
|
-
detp = optmap_weights[detidx, cur_bins[0], cur_bins[1], cur_bins[2]] *
|
|
247
|
+
detp = optmap_weights[detidx, cur_bins[0], cur_bins[1], cur_bins[2]] * map_scaling_evt
|
|
477
248
|
if detp < 0.0:
|
|
478
249
|
det_no_stats += 1
|
|
479
250
|
continue
|
|
@@ -563,50 +334,6 @@ def get_output_table(output_map):
|
|
|
563
334
|
return ph_count_o, tbl
|
|
564
335
|
|
|
565
336
|
|
|
566
|
-
def convolve(
|
|
567
|
-
map_file: str,
|
|
568
|
-
edep_file: str,
|
|
569
|
-
edep_path: str,
|
|
570
|
-
material: str | tuple[sc.ScintConfig, tuple[Quantity, ...]],
|
|
571
|
-
output_file: str | None = None,
|
|
572
|
-
buffer_len: int = int(1e6),
|
|
573
|
-
dist_mode: str = "poisson+no-fano",
|
|
574
|
-
):
|
|
575
|
-
scint_mat_params = _get_scint_params(material)
|
|
576
|
-
|
|
577
|
-
# special handling of distributions and flags.
|
|
578
|
-
dist, mode = dist_mode.split("+")
|
|
579
|
-
if (
|
|
580
|
-
dist not in ("multinomial", "poisson")
|
|
581
|
-
or mode not in ("", "no-fano")
|
|
582
|
-
or (dist == "poisson" and mode != "no-fano")
|
|
583
|
-
):
|
|
584
|
-
msg = f"unsupported statistical distribution {dist_mode} for scintillation emission"
|
|
585
|
-
raise ValueError(msg)
|
|
586
|
-
|
|
587
|
-
log.info("opening map %s", map_file)
|
|
588
|
-
optmap_for_convolve = open_optmap(map_file)
|
|
589
|
-
|
|
590
|
-
log.info("opening energy deposition hit output %s", edep_file)
|
|
591
|
-
it = LH5Iterator(edep_file, edep_path, buffer_len=buffer_len)
|
|
592
|
-
|
|
593
|
-
for it_count, edep_lgdo in enumerate(it):
|
|
594
|
-
edep_df = _reflatten_scint_vov(edep_lgdo.view_as("ak")).to_numpy()
|
|
595
|
-
|
|
596
|
-
log.info("start event processing (%d)", it_count)
|
|
597
|
-
output_map = iterate_stepwise_depositions(
|
|
598
|
-
edep_df, optmap_for_convolve, scint_mat_params, dist=dist, mode=mode
|
|
599
|
-
)
|
|
600
|
-
|
|
601
|
-
log.info("store output photon hits (%d)", it_count)
|
|
602
|
-
ph_count_o, tbl = get_output_table(output_map)
|
|
603
|
-
log.debug(
|
|
604
|
-
"output photons: %d energy depositions -> %d photons", len(output_map), ph_count_o
|
|
605
|
-
)
|
|
606
|
-
if output_file is not None:
|
|
607
|
-
lh5.write(tbl, "optical", lh5_file=output_file, group="stp", wo_mode="append")
|
|
608
|
-
|
|
609
|
-
|
|
610
337
|
def _reflatten_scint_vov(arr: ak.Array) -> ak.Array:
|
|
611
338
|
if all(arr[f].ndim == 1 for f in ak.fields(arr)):
|
|
612
339
|
return arr
|
reboost/optmap/create.py
CHANGED
|
@@ -9,17 +9,14 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Literal
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
|
-
import
|
|
13
|
-
from lgdo import Array, Histogram, Scalar, lh5
|
|
12
|
+
from lgdo import Histogram, lh5
|
|
14
13
|
from numba import njit
|
|
15
14
|
from numpy.typing import NDArray
|
|
16
15
|
|
|
17
16
|
from ..log_utils import setup_log
|
|
18
17
|
from .evt import (
|
|
19
|
-
EVT_TABLE_NAME,
|
|
20
18
|
generate_optmap_evt,
|
|
21
19
|
get_optical_detectors_from_geom,
|
|
22
|
-
read_optmap_evt,
|
|
23
20
|
)
|
|
24
21
|
from .optmap import OpticalMap
|
|
25
22
|
|
|
@@ -27,23 +24,21 @@ log = logging.getLogger(__name__)
|
|
|
27
24
|
|
|
28
25
|
|
|
29
26
|
def _optmaps_for_channels(
|
|
30
|
-
|
|
27
|
+
all_det_ids: dict[int, str],
|
|
31
28
|
settings,
|
|
32
29
|
chfilter: tuple[str | int] | Literal["*"] = (),
|
|
33
30
|
use_shmem: bool = False,
|
|
34
31
|
):
|
|
35
|
-
all_det_ids = [ch_id for ch_id in optmap_evt_columns if ch_id.isnumeric()]
|
|
36
|
-
|
|
37
32
|
if chfilter != "*":
|
|
38
33
|
chfilter = [str(ch) for ch in chfilter] # normalize types
|
|
39
|
-
optmap_det_ids =
|
|
34
|
+
optmap_det_ids = {det: name for det, name in all_det_ids.items() if str(det) in chfilter}
|
|
40
35
|
else:
|
|
41
36
|
optmap_det_ids = all_det_ids
|
|
42
37
|
|
|
43
38
|
log.info("creating empty optmaps")
|
|
44
39
|
optmap_count = len(optmap_det_ids) + 1
|
|
45
40
|
optmaps = [
|
|
46
|
-
OpticalMap("all" if i == 0 else optmap_det_ids[i - 1], settings, use_shmem)
|
|
41
|
+
OpticalMap("all" if i == 0 else list(optmap_det_ids.values())[i - 1], settings, use_shmem)
|
|
47
42
|
for i in range(optmap_count)
|
|
48
43
|
]
|
|
49
44
|
|
|
@@ -76,34 +71,6 @@ def _fill_hit_maps(optmaps: list[OpticalMap], loc, hitcounts: NDArray, ch_idx_to
|
|
|
76
71
|
optmaps[i].fill_hits(locm)
|
|
77
72
|
|
|
78
73
|
|
|
79
|
-
def _count_multi_ph_detection(hitcounts) -> NDArray:
|
|
80
|
-
hits_per_primary = hitcounts.sum(axis=1)
|
|
81
|
-
bins = np.arange(0, hits_per_primary.max() + 1.5) - 0.5
|
|
82
|
-
return np.histogram(hits_per_primary, bins)[0]
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _fit_multi_ph_detection(hits_per_primary) -> float:
|
|
86
|
-
if len(hits_per_primary) <= 2: # have only 0 and 1 hits, can't fit (and also don't need to).
|
|
87
|
-
return np.inf
|
|
88
|
-
|
|
89
|
-
x = np.arange(0, len(hits_per_primary))
|
|
90
|
-
popt, pcov = scipy.optimize.curve_fit(
|
|
91
|
-
lambda x, p0, k: p0 * np.exp(-k * x), x[1:], hits_per_primary[1:]
|
|
92
|
-
)
|
|
93
|
-
best_fit_exponent = popt[1]
|
|
94
|
-
|
|
95
|
-
log.info(
|
|
96
|
-
"p(> 1 detected photon)/p(1 detected photon) = %f",
|
|
97
|
-
sum(hits_per_primary[2:]) / hits_per_primary[1],
|
|
98
|
-
)
|
|
99
|
-
log.info(
|
|
100
|
-
"p(> 1 detected photon)/p(<=1 detected photon) = %f",
|
|
101
|
-
sum(hits_per_primary[2:]) / sum(hits_per_primary[0:2]),
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
return best_fit_exponent
|
|
105
|
-
|
|
106
|
-
|
|
107
74
|
def _create_optical_maps_process_init(optmaps, log_level) -> None:
|
|
108
75
|
# need to use shared global state. passing the shared memory arrays via "normal" arguments to
|
|
109
76
|
# the worker function is not supported...
|
|
@@ -115,34 +82,29 @@ def _create_optical_maps_process_init(optmaps, log_level) -> None:
|
|
|
115
82
|
|
|
116
83
|
|
|
117
84
|
def _create_optical_maps_process(
|
|
118
|
-
optmap_events_fn, buffer_len,
|
|
119
|
-
) ->
|
|
85
|
+
optmap_events_fn, buffer_len, all_det_ids, ch_idx_to_map_idx
|
|
86
|
+
) -> bool:
|
|
120
87
|
log.info("started worker task for %s", optmap_events_fn)
|
|
121
88
|
x = _create_optical_maps_chunk(
|
|
122
89
|
optmap_events_fn,
|
|
123
90
|
buffer_len,
|
|
124
|
-
is_stp_file,
|
|
125
91
|
all_det_ids,
|
|
126
92
|
_shared_optmaps,
|
|
127
93
|
ch_idx_to_map_idx,
|
|
128
94
|
)
|
|
129
95
|
log.info("finished worker task for %s", optmap_events_fn)
|
|
130
|
-
return
|
|
96
|
+
return x
|
|
131
97
|
|
|
132
98
|
|
|
133
99
|
def _create_optical_maps_chunk(
|
|
134
|
-
optmap_events_fn, buffer_len,
|
|
135
|
-
) ->
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
else:
|
|
139
|
-
optmap_events_it = generate_optmap_evt(optmap_events_fn, all_det_ids, buffer_len)
|
|
100
|
+
optmap_events_fn, buffer_len, all_det_ids, optmaps, ch_idx_to_map_idx
|
|
101
|
+
) -> bool:
|
|
102
|
+
cols = [str(c) for c in all_det_ids]
|
|
103
|
+
optmap_events_it = generate_optmap_evt(optmap_events_fn, cols, buffer_len)
|
|
140
104
|
|
|
141
|
-
hits_per_primary = np.zeros(10, dtype=np.int64)
|
|
142
|
-
hits_per_primary_len = 0
|
|
143
105
|
for it_count, events_lgdo in enumerate(optmap_events_it):
|
|
144
106
|
optmap_events = events_lgdo.view_as("pd")
|
|
145
|
-
hitcounts = optmap_events[
|
|
107
|
+
hitcounts = optmap_events[cols].to_numpy()
|
|
146
108
|
loc = optmap_events[["xloc", "yloc", "zloc"]].to_numpy()
|
|
147
109
|
|
|
148
110
|
log.debug("filling vertex histogram (%d)", it_count)
|
|
@@ -150,23 +112,19 @@ def _create_optical_maps_chunk(
|
|
|
150
112
|
|
|
151
113
|
log.debug("filling hits histogram (%d)", it_count)
|
|
152
114
|
_fill_hit_maps(optmaps, loc, hitcounts, ch_idx_to_map_idx)
|
|
153
|
-
hpp = _count_multi_ph_detection(hitcounts)
|
|
154
|
-
hits_per_primary_len = max(hits_per_primary_len, len(hpp))
|
|
155
|
-
hits_per_primary[0 : len(hpp)] += hpp
|
|
156
115
|
|
|
157
116
|
# commit the final part of the hits to the maps.
|
|
158
117
|
for i in range(len(optmaps)):
|
|
159
118
|
optmaps[i].fill_hits_flush()
|
|
160
119
|
gc.collect()
|
|
161
120
|
|
|
162
|
-
return
|
|
121
|
+
return True
|
|
163
122
|
|
|
164
123
|
|
|
165
124
|
def create_optical_maps(
|
|
166
125
|
optmap_events_fn: list[str],
|
|
167
126
|
settings,
|
|
168
127
|
buffer_len: int = int(5e6),
|
|
169
|
-
is_stp_file: bool = True,
|
|
170
128
|
chfilter: tuple[str | int] | Literal["*"] = (),
|
|
171
129
|
output_lh5_fn: str | None = None,
|
|
172
130
|
after_save: Callable[[int, str, OpticalMap]] | None = None,
|
|
@@ -182,8 +140,6 @@ def create_optical_maps(
|
|
|
182
140
|
list of filenames to lh5 files, that can either be stp files from remage or "optmap-evt"
|
|
183
141
|
files with a table ``/optmap_evt`` with columns ``{x,y,z}loc`` and one column (with numeric
|
|
184
142
|
header) for each SiPM channel.
|
|
185
|
-
is_stp_file
|
|
186
|
-
if true, do convert a remage output file (stp file) on-the-fly to an optmap-evt file.
|
|
187
143
|
chfilter
|
|
188
144
|
tuple of detector ids that will be included in the resulting optmap. Those have to match
|
|
189
145
|
the column names in ``optmap_events_fn``.
|
|
@@ -196,12 +152,7 @@ def create_optical_maps(
|
|
|
196
152
|
|
|
197
153
|
use_shmem = n_procs is None or n_procs > 1
|
|
198
154
|
|
|
199
|
-
|
|
200
|
-
optmap_evt_columns = list(
|
|
201
|
-
lh5.read(EVT_TABLE_NAME, optmap_events_fn[0], start_row=0, n_rows=1).keys()
|
|
202
|
-
) # peek into the (first) file to find column names.
|
|
203
|
-
else:
|
|
204
|
-
optmap_evt_columns = [str(i) for i in get_optical_detectors_from_geom(geom_fn)]
|
|
155
|
+
optmap_evt_columns = get_optical_detectors_from_geom(geom_fn)
|
|
205
156
|
|
|
206
157
|
all_det_ids, optmaps, optmap_det_ids = _optmaps_for_channels(
|
|
207
158
|
optmap_evt_columns, settings, chfilter=chfilter, use_shmem=use_shmem
|
|
@@ -209,11 +160,17 @@ def create_optical_maps(
|
|
|
209
160
|
|
|
210
161
|
# indices for later use in _compute_hit_maps.
|
|
211
162
|
ch_idx_to_map_idx = np.array(
|
|
212
|
-
[
|
|
163
|
+
[
|
|
164
|
+
list(optmap_det_ids.keys()).index(d) + 1 if d in optmap_det_ids else -1
|
|
165
|
+
for d in all_det_ids
|
|
166
|
+
]
|
|
213
167
|
)
|
|
214
168
|
assert np.sum(ch_idx_to_map_idx > 0) == len(optmaps) - 1
|
|
215
169
|
|
|
216
|
-
log.info(
|
|
170
|
+
log.info(
|
|
171
|
+
"creating optical map groups: %s",
|
|
172
|
+
", ".join(["all", *[str(t) for t in optmap_det_ids.items()]]),
|
|
173
|
+
)
|
|
217
174
|
|
|
218
175
|
q = []
|
|
219
176
|
|
|
@@ -221,9 +178,7 @@ def create_optical_maps(
|
|
|
221
178
|
if not use_shmem:
|
|
222
179
|
for fn in optmap_events_fn:
|
|
223
180
|
q.append(
|
|
224
|
-
_create_optical_maps_chunk(
|
|
225
|
-
fn, buffer_len, is_stp_file, all_det_ids, optmaps, ch_idx_to_map_idx
|
|
226
|
-
)
|
|
181
|
+
_create_optical_maps_chunk(fn, buffer_len, all_det_ids, optmaps, ch_idx_to_map_idx)
|
|
227
182
|
)
|
|
228
183
|
else:
|
|
229
184
|
ctx = mp.get_context("forkserver")
|
|
@@ -243,14 +198,14 @@ def create_optical_maps(
|
|
|
243
198
|
for fn in optmap_events_fn:
|
|
244
199
|
r = pool.apply_async(
|
|
245
200
|
_create_optical_maps_process,
|
|
246
|
-
args=(fn, buffer_len,
|
|
201
|
+
args=(fn, buffer_len, all_det_ids, ch_idx_to_map_idx),
|
|
247
202
|
)
|
|
248
203
|
pool_results.append((r, fn))
|
|
249
204
|
|
|
250
205
|
pool.close()
|
|
251
206
|
for r, fn in pool_results:
|
|
252
207
|
try:
|
|
253
|
-
q.append(
|
|
208
|
+
q.append(r.get())
|
|
254
209
|
except BaseException as e:
|
|
255
210
|
msg = f"error while processing file {fn}"
|
|
256
211
|
raise RuntimeError(msg) from e # re-throw errors of workers.
|
|
@@ -258,17 +213,8 @@ def create_optical_maps(
|
|
|
258
213
|
pool.join()
|
|
259
214
|
log.info("joined worker process pool")
|
|
260
215
|
|
|
261
|
-
# merge hitcounts.
|
|
262
216
|
if len(q) != len(optmap_events_fn):
|
|
263
217
|
log.error("got %d results for %d files", len(q), len(optmap_events_fn))
|
|
264
|
-
hits_per_primary = np.zeros(10, dtype=np.int64)
|
|
265
|
-
hits_per_primary_len = 0
|
|
266
|
-
for hitcounts in q:
|
|
267
|
-
hits_per_primary[0 : len(hitcounts)] += hitcounts
|
|
268
|
-
hits_per_primary_len = max(hits_per_primary_len, len(hitcounts))
|
|
269
|
-
|
|
270
|
-
hits_per_primary = hits_per_primary[0:hits_per_primary_len]
|
|
271
|
-
hits_per_primary_exponent = _fit_multi_ph_detection(hits_per_primary)
|
|
272
218
|
|
|
273
219
|
# all maps share the same vertex histogram.
|
|
274
220
|
for i in range(1, len(optmaps)):
|
|
@@ -279,7 +225,7 @@ def create_optical_maps(
|
|
|
279
225
|
optmaps[i].create_probability()
|
|
280
226
|
if check_after_create:
|
|
281
227
|
optmaps[i].check_histograms()
|
|
282
|
-
group = "all" if i == 0 else "
|
|
228
|
+
group = "all" if i == 0 else "channels/" + list(optmap_det_ids.values())[i - 1]
|
|
283
229
|
if output_lh5_fn is not None:
|
|
284
230
|
optmaps[i].write_lh5(lh5_file=output_lh5_fn, group=group)
|
|
285
231
|
|
|
@@ -288,14 +234,12 @@ def create_optical_maps(
|
|
|
288
234
|
|
|
289
235
|
optmaps[i] = None # clear some memory.
|
|
290
236
|
|
|
291
|
-
if output_lh5_fn is not None:
|
|
292
|
-
lh5.write(Array(hits_per_primary), "_hitcounts", lh5_file=output_lh5_fn)
|
|
293
|
-
lh5.write(Scalar(hits_per_primary_exponent), "_hitcounts_exp", lh5_file=output_lh5_fn)
|
|
294
|
-
|
|
295
237
|
|
|
296
238
|
def list_optical_maps(lh5_file: str) -> list[str]:
|
|
297
|
-
maps = lh5.ls(lh5_file)
|
|
298
|
-
|
|
239
|
+
maps = list(lh5.ls(lh5_file, "/channels/"))
|
|
240
|
+
if "all" in lh5.ls(lh5_file):
|
|
241
|
+
maps.append("all")
|
|
242
|
+
return maps
|
|
299
243
|
|
|
300
244
|
|
|
301
245
|
def _merge_optical_maps_process(
|
|
@@ -313,9 +257,9 @@ def _merge_optical_maps_process(
|
|
|
313
257
|
|
|
314
258
|
all_edges = None
|
|
315
259
|
for optmap_fn in map_l5_files:
|
|
316
|
-
nr_det = lh5.read(f"/{d}/
|
|
260
|
+
nr_det = lh5.read(f"/{d}/_nr_det", optmap_fn)
|
|
317
261
|
assert isinstance(nr_det, Histogram)
|
|
318
|
-
nr_gen = lh5.read(f"/{d}/
|
|
262
|
+
nr_gen = lh5.read(f"/{d}/_nr_gen", optmap_fn)
|
|
319
263
|
assert isinstance(nr_gen, Histogram)
|
|
320
264
|
|
|
321
265
|
assert OpticalMap._edges_eq(nr_det.binning, nr_gen.binning)
|
|
@@ -333,7 +277,8 @@ def _merge_optical_maps_process(
|
|
|
333
277
|
merged_map.check_histograms(include_prefix=True)
|
|
334
278
|
|
|
335
279
|
if write_part_file:
|
|
336
|
-
|
|
280
|
+
d_for_tmp = d.replace("/", "_")
|
|
281
|
+
output_lh5_fn = f"{output_lh5_fn}_{d_for_tmp}.mappart.lh5"
|
|
337
282
|
wo_mode = "overwrite_file" if write_part_file else "write_safe"
|
|
338
283
|
merged_map.write_lh5(lh5_file=output_lh5_fn, group=d, wo_mode=wo_mode)
|
|
339
284
|
|
|
@@ -410,7 +355,7 @@ def merge_optical_maps(
|
|
|
410
355
|
# transfer to actual output file.
|
|
411
356
|
for d, part_fn in q:
|
|
412
357
|
assert isinstance(part_fn, str)
|
|
413
|
-
for h_name in ("
|
|
358
|
+
for h_name in ("_nr_det", "_nr_gen", "prob", "prob_unc"):
|
|
414
359
|
obj = f"/{d}/{h_name}"
|
|
415
360
|
log.info("transfer %s from %s", obj, part_fn)
|
|
416
361
|
h = lh5.read(obj, part_fn)
|
|
@@ -418,43 +363,19 @@ def merge_optical_maps(
|
|
|
418
363
|
lh5.write(h, obj, output_lh5_fn, wo_mode="write_safe")
|
|
419
364
|
Path(part_fn).unlink()
|
|
420
365
|
|
|
421
|
-
# merge hitcounts.
|
|
422
|
-
hits_per_primary = np.zeros(10, dtype=np.int64)
|
|
423
|
-
hits_per_primary_len = 0
|
|
424
|
-
for optmap_fn in map_l5_files:
|
|
425
|
-
if "_hitcounts" not in lh5.ls(optmap_fn):
|
|
426
|
-
log.warning("skipping _hitcounts calculations, missing in file %s", optmap_fn)
|
|
427
|
-
return
|
|
428
|
-
hitcounts = lh5.read("/_hitcounts", optmap_fn)
|
|
429
|
-
assert isinstance(hitcounts, Array)
|
|
430
|
-
hits_per_primary[0 : len(hitcounts)] += hitcounts
|
|
431
|
-
hits_per_primary_len = max(hits_per_primary_len, len(hitcounts))
|
|
432
|
-
|
|
433
|
-
hits_per_primary = hits_per_primary[0:hits_per_primary_len]
|
|
434
|
-
lh5.write(Array(hits_per_primary), "_hitcounts", lh5_file=output_lh5_fn)
|
|
435
|
-
|
|
436
|
-
# re-calculate hitcounts exponent.
|
|
437
|
-
hits_per_primary_exponent = _fit_multi_ph_detection(hits_per_primary)
|
|
438
|
-
lh5.write(Scalar(hits_per_primary_exponent), "_hitcounts_exp", lh5_file=output_lh5_fn)
|
|
439
|
-
|
|
440
366
|
|
|
441
367
|
def check_optical_map(map_l5_file: str):
|
|
442
368
|
"""Run a health check on the map file.
|
|
443
369
|
|
|
444
370
|
This checks for consistency, and outputs details on map statistics.
|
|
445
371
|
"""
|
|
446
|
-
if
|
|
447
|
-
|
|
448
|
-
|
|
372
|
+
if (
|
|
373
|
+
"_hitcounts_exp" in lh5.ls(map_l5_file)
|
|
374
|
+
and lh5.read("_hitcounts_exp", lh5_file=map_l5_file).value != np.inf
|
|
375
|
+
):
|
|
449
376
|
log.error("unexpected _hitcounts_exp not equal to positive infinity")
|
|
450
377
|
return
|
|
451
378
|
|
|
452
|
-
if "_hitcounts" not in lh5.ls(map_l5_file):
|
|
453
|
-
log.info("no _hitcounts found")
|
|
454
|
-
elif lh5.read("_hitcounts", lh5_file=map_l5_file).nda.shape != (2,):
|
|
455
|
-
log.error("unexpected _hitcounts shape")
|
|
456
|
-
return
|
|
457
|
-
|
|
458
379
|
all_binning = None
|
|
459
380
|
for submap in list_optical_maps(map_l5_file):
|
|
460
381
|
try:
|
|
@@ -503,8 +424,3 @@ def rebin_optical_maps(map_l5_file: str, output_lh5_file: str, factor: int):
|
|
|
503
424
|
om_new.h_hits = _rebin_map(om.h_hits, factor)
|
|
504
425
|
om_new.create_probability()
|
|
505
426
|
om_new.write_lh5(lh5_file=output_lh5_file, group=submap, wo_mode="write_safe")
|
|
506
|
-
|
|
507
|
-
# just copy hitcounts exponent.
|
|
508
|
-
for dset in ("_hitcounts_exp", "_hitcounts"):
|
|
509
|
-
if dset in lh5.ls(map_l5_file):
|
|
510
|
-
lh5.write(lh5.read(dset, lh5_file=map_l5_file), dset, lh5_file=output_lh5_file)
|
reboost/optmap/evt.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections import OrderedDict
|
|
4
5
|
from collections.abc import Generator, Iterable
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
@@ -125,13 +126,15 @@ def build_optmap_evt(
|
|
|
125
126
|
lh5_out_file_tmp.rename(lh5_out_file)
|
|
126
127
|
|
|
127
128
|
|
|
128
|
-
def get_optical_detectors_from_geom(geom_fn) ->
|
|
129
|
+
def get_optical_detectors_from_geom(geom_fn) -> dict[int, str]:
|
|
129
130
|
import pyg4ometry
|
|
130
131
|
import pygeomtools
|
|
131
132
|
|
|
132
133
|
geom_registry = pyg4ometry.gdml.Reader(geom_fn).getRegistry()
|
|
133
134
|
detectors = pygeomtools.get_all_sensvols(geom_registry)
|
|
134
|
-
return
|
|
135
|
+
return OrderedDict(
|
|
136
|
+
[(d.uid, name) for name, d in detectors.items() if d.detector_type == "optical"]
|
|
137
|
+
)
|
|
135
138
|
|
|
136
139
|
|
|
137
140
|
def read_optmap_evt(lh5_file: str, buffer_len: int = int(5e6)) -> LH5Iterator:
|
reboost/optmap/mapview.py
CHANGED
|
@@ -92,14 +92,16 @@ def _channel_selector(fig) -> None:
|
|
|
92
92
|
def _read_data(
|
|
93
93
|
optmap_fn: str,
|
|
94
94
|
detid: str = "all",
|
|
95
|
-
histogram_choice: str = "
|
|
95
|
+
histogram_choice: str = "prob",
|
|
96
96
|
) -> tuple[tuple[NDArray], NDArray]:
|
|
97
|
-
histogram = histogram_choice if histogram_choice != "
|
|
97
|
+
histogram = histogram_choice if histogram_choice != "prob_unc_rel" else "prob"
|
|
98
|
+
detid = f"channels/{detid}" if detid != all and not detid.startswith("channels/") else detid
|
|
99
|
+
|
|
98
100
|
optmap_all = lh5.read(f"/{detid}/{histogram}", optmap_fn)
|
|
99
101
|
optmap_edges = tuple([b.edges for b in optmap_all.binning])
|
|
100
102
|
optmap_weights = optmap_all.weights.nda.copy()
|
|
101
|
-
if histogram_choice == "
|
|
102
|
-
optmap_err = lh5.read(f"/{detid}/
|
|
103
|
+
if histogram_choice == "prob_unc_rel":
|
|
104
|
+
optmap_err = lh5.read(f"/{detid}/prob_unc", optmap_fn)
|
|
103
105
|
divmask = optmap_weights > 0
|
|
104
106
|
optmap_weights[divmask] = optmap_err.weights.nda[divmask] / optmap_weights[divmask]
|
|
105
107
|
optmap_weights[~divmask] = -1
|
|
@@ -112,13 +114,13 @@ def _prepare_data(
|
|
|
112
114
|
divide_fn: str | None = None,
|
|
113
115
|
cmap_min: float | Literal["auto"] = 1e-4,
|
|
114
116
|
cmap_max: float | Literal["auto"] = 1e-2,
|
|
115
|
-
histogram_choice: str = "
|
|
117
|
+
histogram_choice: str = "prob",
|
|
116
118
|
detid: str = "all",
|
|
117
119
|
) -> tuple[tuple[NDArray], NDArray]:
|
|
118
120
|
optmap_edges, optmap_weights = _read_data(optmap_fn, detid, histogram_choice)
|
|
119
121
|
|
|
120
122
|
if divide_fn is not None:
|
|
121
|
-
|
|
123
|
+
_, divide_map = _read_data(divide_fn, detid, histogram_choice)
|
|
122
124
|
divmask = divide_map > 0
|
|
123
125
|
optmap_weights[divmask] = optmap_weights[divmask] / divide_map[divmask]
|
|
124
126
|
optmap_weights[~divmask] = -1
|
|
@@ -158,7 +160,7 @@ def view_optmap(
|
|
|
158
160
|
start_axis: int = 2,
|
|
159
161
|
cmap_min: float | Literal["auto"] = 1e-4,
|
|
160
162
|
cmap_max: float | Literal["auto"] = 1e-2,
|
|
161
|
-
histogram_choice: str = "
|
|
163
|
+
histogram_choice: str = "prob",
|
|
162
164
|
title: str | None = None,
|
|
163
165
|
) -> None:
|
|
164
166
|
available_dets = list_optical_maps(optmap_fn)
|
reboost/optmap/optmap.py
CHANGED
|
@@ -8,7 +8,7 @@ import multiprocessing as mp
|
|
|
8
8
|
from collections.abc import Mapping
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
|
-
from lgdo import Histogram, lh5
|
|
11
|
+
from lgdo import Histogram, Struct, lh5
|
|
12
12
|
from numpy.typing import NDArray
|
|
13
13
|
|
|
14
14
|
log = logging.getLogger(__name__)
|
|
@@ -66,10 +66,10 @@ class OpticalMap:
|
|
|
66
66
|
raise RuntimeError(msg)
|
|
67
67
|
return h.weights.nda, h.binning
|
|
68
68
|
|
|
69
|
-
om.h_vertex, bin_nr_gen = read_hist("
|
|
70
|
-
om.h_hits, bin_nr_det = read_hist("
|
|
71
|
-
om.h_prob, bin_p_det = read_hist("
|
|
72
|
-
om.h_prob_uncert, bin_p_det_err = read_hist("
|
|
69
|
+
om.h_vertex, bin_nr_gen = read_hist("_nr_gen", lh5_file, group=group)
|
|
70
|
+
om.h_hits, bin_nr_det = read_hist("_nr_det", lh5_file, group=group)
|
|
71
|
+
om.h_prob, bin_p_det = read_hist("prob", lh5_file, group=group)
|
|
72
|
+
om.h_prob_uncert, bin_p_det_err = read_hist("prob_unc", lh5_file, group=group)
|
|
73
73
|
|
|
74
74
|
for bins in (bin_nr_det, bin_p_det, bin_p_det_err):
|
|
75
75
|
if not OpticalMap._edges_eq(bin_nr_gen, bins):
|
|
@@ -227,18 +227,17 @@ class OpticalMap:
|
|
|
227
227
|
|
|
228
228
|
def write_hist(h: NDArray, name: str, fn: str, group: str, wo_mode: str):
|
|
229
229
|
lh5.write(
|
|
230
|
-
Histogram(self._nda(h), self.binning),
|
|
231
|
-
|
|
230
|
+
Struct({name: Histogram(self._nda(h), self.binning)}),
|
|
231
|
+
group,
|
|
232
232
|
fn,
|
|
233
|
-
group=group,
|
|
234
233
|
wo_mode=wo_mode,
|
|
235
234
|
)
|
|
236
235
|
|
|
237
236
|
# only use the passed wo_mode for the first file.
|
|
238
|
-
write_hist(self.h_vertex, "
|
|
239
|
-
write_hist(self.h_hits, "
|
|
240
|
-
write_hist(self.h_prob, "
|
|
241
|
-
write_hist(self.h_prob_uncert, "
|
|
237
|
+
write_hist(self.h_vertex, "_nr_gen", lh5_file, group, wo_mode)
|
|
238
|
+
write_hist(self.h_hits, "_nr_det", lh5_file, group, "append_column")
|
|
239
|
+
write_hist(self.h_prob, "prob", lh5_file, group, "append_column")
|
|
240
|
+
write_hist(self.h_prob_uncert, "prob_unc", lh5_file, group, "append_column")
|
|
242
241
|
|
|
243
242
|
def get_settings(self) -> dict:
|
|
244
243
|
"""Get the binning settings that were used to create this optical map instance."""
|
reboost/spms/pe.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
import awkward as ak
|
|
6
|
+
import numpy as np
|
|
6
7
|
from lgdo import VectorOfVectors
|
|
7
8
|
|
|
8
9
|
from ..optmap import convolve
|
|
@@ -21,6 +22,80 @@ def load_optmap(map_file: str, spm_det_uid: int) -> convolve.OptmapForConvolve:
|
|
|
21
22
|
return convolve.open_optmap_single(map_file, spm_det_uid)
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def _nested_unflatten(data: ak.Array, lengths: ak.Array):
|
|
26
|
+
return ak.unflatten(ak.unflatten(ak.flatten(data), ak.flatten(lengths)), ak.num(lengths))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def corrected_photoelectrons(
|
|
30
|
+
simulated_pe: ak.Array,
|
|
31
|
+
simulated_uids: ak.Array,
|
|
32
|
+
data_pe: ak.Array,
|
|
33
|
+
data_uids: ak.Array,
|
|
34
|
+
*,
|
|
35
|
+
seed: int | None = None,
|
|
36
|
+
) -> tuple[ak.Array, ak.Array]:
|
|
37
|
+
r"""Add a correction to the observed number of photoelectrons (p.e.) using forced trigger data.
|
|
38
|
+
|
|
39
|
+
For every simulated event a corresponding forced trigger event in data is chosen
|
|
40
|
+
and the resulting number of p.e. for each channel (i) is:
|
|
41
|
+
|
|
42
|
+
.. math::
|
|
43
|
+
|
|
44
|
+
n_i = n_{\text{sim},i} + n_{\text{data},i}
|
|
45
|
+
|
|
46
|
+
.. warning::
|
|
47
|
+
The number of supplied forced trigger events in data should ideally be
|
|
48
|
+
more than that in the simulations. If this is not the case and "allow_data_reuse"
|
|
49
|
+
is True then some data events will be used multiple times. This introduces
|
|
50
|
+
a small amount of correlation between the simulated events, but is probably acceptable
|
|
51
|
+
in most circumstances.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
simulated_pe
|
|
56
|
+
The number of number of detected pe per sipm channel.
|
|
57
|
+
simulated_uids
|
|
58
|
+
The unique identifier (uid) for each sipm hit.
|
|
59
|
+
data_pe
|
|
60
|
+
The collection of forced trigger pe.
|
|
61
|
+
data_uids
|
|
62
|
+
The uids for each forced trigger event.
|
|
63
|
+
seed
|
|
64
|
+
Seed for random number generator
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
a tuple of the corrected pe and sipm uids.
|
|
69
|
+
"""
|
|
70
|
+
rand = np.random.default_rng(seed=seed)
|
|
71
|
+
rand_ints = rand.integers(0, len(data_pe), size=len(simulated_pe))
|
|
72
|
+
|
|
73
|
+
selected_data_pe = data_pe[rand_ints]
|
|
74
|
+
selected_data_uids = data_uids[rand_ints]
|
|
75
|
+
|
|
76
|
+
# combine sims with data
|
|
77
|
+
pe_tot = ak.concatenate([simulated_pe, selected_data_pe], axis=1)
|
|
78
|
+
uid_tot = ak.concatenate([simulated_uids, selected_data_uids], axis=1)
|
|
79
|
+
|
|
80
|
+
# sort by uid
|
|
81
|
+
order = ak.argsort(uid_tot)
|
|
82
|
+
pe_tot = pe_tot[order]
|
|
83
|
+
uid_tot = uid_tot[order]
|
|
84
|
+
|
|
85
|
+
# add an extra axis
|
|
86
|
+
n = ak.run_lengths(uid_tot)
|
|
87
|
+
|
|
88
|
+
# add another dimension
|
|
89
|
+
pe_tot = _nested_unflatten(pe_tot, n)
|
|
90
|
+
uid_tot = _nested_unflatten(uid_tot, n)
|
|
91
|
+
|
|
92
|
+
# sum pe and take the first uid (should all be the same)
|
|
93
|
+
corrected_pe = ak.sum(pe_tot, axis=-1)
|
|
94
|
+
uid_tot = ak.fill_none(ak.firsts(uid_tot, axis=-1), np.nan)
|
|
95
|
+
|
|
96
|
+
return corrected_pe, uid_tot
|
|
97
|
+
|
|
98
|
+
|
|
24
99
|
def detected_photoelectrons(
|
|
25
100
|
num_scint_ph: ak.Array,
|
|
26
101
|
particle: ak.Array,
|
|
@@ -32,6 +107,7 @@ def detected_photoelectrons(
|
|
|
32
107
|
material: str,
|
|
33
108
|
spm_detector_uid: int,
|
|
34
109
|
map_scaling: float = 1,
|
|
110
|
+
map_scaling_sigma: float = 0,
|
|
35
111
|
) -> VectorOfVectors:
|
|
36
112
|
"""Derive the number of detected photoelectrons (p.e.) from scintillator hits using an optical map.
|
|
37
113
|
|
|
@@ -58,6 +134,9 @@ def detected_photoelectrons(
|
|
|
58
134
|
SiPM detector uid as used in the optical map.
|
|
59
135
|
map_scaling
|
|
60
136
|
scale the detection probability in the map for this detector by this factor.
|
|
137
|
+
map_scaling_sigma
|
|
138
|
+
if larger than zero, sample the used scaling factor for each (reshaped) event
|
|
139
|
+
from a normal distribution with this standard deviation.
|
|
61
140
|
"""
|
|
62
141
|
hits = ak.Array(
|
|
63
142
|
{
|
|
@@ -72,7 +151,7 @@ def detected_photoelectrons(
|
|
|
72
151
|
|
|
73
152
|
scint_mat_params = convolve._get_scint_params(material)
|
|
74
153
|
pe = convolve.iterate_stepwise_depositions_pois(
|
|
75
|
-
hits, optmap, scint_mat_params, spm_detector_uid, map_scaling
|
|
154
|
+
hits, optmap, scint_mat_params, spm_detector_uid, map_scaling, map_scaling_sigma
|
|
76
155
|
)
|
|
77
156
|
|
|
78
157
|
return VectorOfVectors(pe, attrs={"units": "ns"})
|
reboost/utils.py
CHANGED
|
@@ -284,7 +284,7 @@ def get_function_string(expr: str, aliases: dict | None = None) -> tuple[str, di
|
|
|
284
284
|
if "." not in func_call:
|
|
285
285
|
continue
|
|
286
286
|
|
|
287
|
-
subpackage,
|
|
287
|
+
subpackage, _func = func_call.rsplit(".", 1)
|
|
288
288
|
package = subpackage.split(".")[0]
|
|
289
289
|
|
|
290
290
|
# import the subpackage
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reboost
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: New LEGEND Monte-Carlo simulation post-processing
|
|
5
5
|
Author-email: Manuel Huber <info@manuelhu.de>, Toby Dixon <toby.dixon.23@ucl.ac.uk>, Luigi Pertoldi <gipert@pm.me>
|
|
6
6
|
Maintainer: The LEGEND Collaboration
|
|
@@ -700,7 +700,7 @@ Requires-Dist: hdf5plugin
|
|
|
700
700
|
Requires-Dist: colorlog
|
|
701
701
|
Requires-Dist: numpy
|
|
702
702
|
Requires-Dist: scipy
|
|
703
|
-
Requires-Dist: numba
|
|
703
|
+
Requires-Dist: numba>=0.60
|
|
704
704
|
Requires-Dist: legend-pydataobj>=1.15.1
|
|
705
705
|
Requires-Dist: legend-pygeom-optics>=0.12.0
|
|
706
706
|
Requires-Dist: legend-pygeom-tools>=0.0.11
|
|
@@ -728,6 +728,8 @@ Dynamic: license-file
|
|
|
728
728
|
|
|
729
729
|
# reboost
|
|
730
730
|
|
|
731
|
+
[](https://pypi.org/project/reboost/)
|
|
732
|
+
[](https://anaconda.org/conda-forge/reboost)
|
|
731
733
|

|
|
732
734
|
[](https://github.com/legend-exp/reboost/actions)
|
|
733
735
|
[](https://github.com/pre-commit/pre-commit)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
reboost/__init__.py,sha256=VZz9uo7i2jgAx8Zi15SptLZnE_qcnGuNWwqkD3rYHFA,278
|
|
2
|
-
reboost/_version.py,sha256=
|
|
2
|
+
reboost/_version.py,sha256=Rttl-BDadtcW1QzGnNffCWA_Wc9mUKDMOBPZp--Mnsc,704
|
|
3
3
|
reboost/build_evt.py,sha256=VXIfK_pfe_Cgym6gI8dESwONZi-v_4fll0Pn09vePQY,3767
|
|
4
4
|
reboost/build_glm.py,sha256=IerSLQfe51ZO7CQP2kmfPnOIVaDtcfw3byOM02Vaz6o,9472
|
|
5
5
|
reboost/build_hit.py,sha256=pjEaiPW63Q3MfpjI29uJXx9gtwfiOIgOcADRDrDpRrA,17409
|
|
6
6
|
reboost/cli.py,sha256=68EzKiWTHJ2u1RILUv7IX9HaVq6nTTM80_W_MUnWRe4,6382
|
|
7
|
-
reboost/core.py,sha256=
|
|
7
|
+
reboost/core.py,sha256=TPxvZgUaHZdxfQSDdX2zIerQXt3Gq-zQaA6AeXZKNvA,15232
|
|
8
8
|
reboost/iterator.py,sha256=qlEqRv5qOh8eIs-dyVOLYTvH-ZpQDx9fLckpcAdtWjs,6975
|
|
9
9
|
reboost/log_utils.py,sha256=VqS_9OC5NeNU3jcowVOBB0NJ6ssYvNWnirEY-JVduEA,766
|
|
10
10
|
reboost/profile.py,sha256=EOTmjmS8Rm_nYgBWNh6Rntl2XDsxdyed7yEdWtsZEeg,2598
|
|
11
11
|
reboost/units.py,sha256=LUwl6swLQoG09Rt9wcDdu6DTrwDsy-C751BNGzX4sz8,3651
|
|
12
|
-
reboost/utils.py,sha256
|
|
12
|
+
reboost/utils.py,sha256=-4315U6m1M-rwoaI_inI1wIo_l20kvoGmC84D_QOhkE,14563
|
|
13
13
|
reboost/daq/__init__.py,sha256=rNPhxx1Yawt3tENYhmOYSum9_TdV57ZU5kjxlWFAGuo,107
|
|
14
14
|
reboost/daq/core.py,sha256=Rs6Q-17fzEod2iX_2WqEmnqKnNRFoWTYURl3wYhFihU,9915
|
|
15
15
|
reboost/daq/utils.py,sha256=KcH6zvlInmD2YiF6V--DSYBTYudJw3G-hp2JGOcES2o,1042
|
|
@@ -21,22 +21,22 @@ reboost/math/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
21
21
|
reboost/math/functions.py,sha256=OymiYTcA0NXxxm-MBDw5kqyNwHoLCmuv4J48AwnSrbU,5633
|
|
22
22
|
reboost/math/stats.py,sha256=Rq4Wdzv-3aoSK7EsPZCuOEHfnOz3w0moIzCEHbC07xw,3173
|
|
23
23
|
reboost/optmap/__init__.py,sha256=imvuyld-GLw8qdwqW-lXCg2feptcTyQo3wIzPvDHwmY,93
|
|
24
|
-
reboost/optmap/cli.py,sha256=
|
|
25
|
-
reboost/optmap/convolve.py,sha256=
|
|
26
|
-
reboost/optmap/create.py,sha256=
|
|
27
|
-
reboost/optmap/evt.py,sha256=
|
|
28
|
-
reboost/optmap/mapview.py,sha256=
|
|
24
|
+
reboost/optmap/cli.py,sha256=KJFyO4Sd2oYlRC65Kvrdq0BN_Qygp6o7LgO36D5O6_s,8887
|
|
25
|
+
reboost/optmap/convolve.py,sha256=UVc-KjiYvi8LoAlykL__5qDd5T8teT89aDjiE7WYjE4,12464
|
|
26
|
+
reboost/optmap/create.py,sha256=R9W8Wyl8cgXIYESenVpCRuN_MoHhLfaEv4a44cf3AxU,14479
|
|
27
|
+
reboost/optmap/evt.py,sha256=p5ngsCuvOxIZDDQNL9efcLn8Q4CfGL7G726BRrCpE6Y,5637
|
|
28
|
+
reboost/optmap/mapview.py,sha256=_zGiONqCWc4kCp1yHvNFRkh7svvud4ZLqaSHkNieo_M,6878
|
|
29
29
|
reboost/optmap/numba_pdg.py,sha256=y8cXR5PWE2Liprp4ou7vl9do76dl84vXU52ZJD9_I7A,731
|
|
30
|
-
reboost/optmap/optmap.py,sha256=
|
|
30
|
+
reboost/optmap/optmap.py,sha256=3clc1RA8jA4YJte83w085MY8zLpG-G7DBkpZ2UeKPpM,12825
|
|
31
31
|
reboost/shape/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
reboost/shape/cluster.py,sha256=nwR1Dnf00SDICGPqpXeM1Q7_DwTtO9uP3wmuML45c3g,8195
|
|
33
33
|
reboost/shape/group.py,sha256=gOCYgir2gZqmW1JXtbNRPlQqP0gmUcbe7RVb9CbY1pU,5540
|
|
34
34
|
reboost/shape/reduction.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
reboost/spms/__init__.py,sha256=ffi0rH-ZFGmpURbFt-HY1ZAHCdady0mLXNsblRNh-JY,207
|
|
36
|
-
reboost/spms/pe.py,sha256=
|
|
37
|
-
reboost-0.
|
|
38
|
-
reboost-0.
|
|
39
|
-
reboost-0.
|
|
40
|
-
reboost-0.
|
|
41
|
-
reboost-0.
|
|
42
|
-
reboost-0.
|
|
36
|
+
reboost/spms/pe.py,sha256=Q_GzN5HEp2BmgB4fni5mfc5ZOXh4mlJepDwZd1EzFdc,5696
|
|
37
|
+
reboost-0.8.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
38
|
+
reboost-0.8.0.dist-info/METADATA,sha256=t8uYwYlKBZPoO5Rbrv4RVr5AQj4D_rglFCQ-HFrYe1I,44442
|
|
39
|
+
reboost-0.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
reboost-0.8.0.dist-info/entry_points.txt,sha256=DxhD6BidSWNot9BrejHJjQ7RRLmrMaBIl52T75oWTwM,93
|
|
41
|
+
reboost-0.8.0.dist-info/top_level.txt,sha256=q-IBsDepaY_AbzbRmQoW8EZrITXRVawVnNrB-_zyXZs,8
|
|
42
|
+
reboost-0.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|