reboost 0.8.4__py3-none-any.whl → 0.8.5__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 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.8.4'
32
- __version_tuple__ = version_tuple = (0, 8, 4)
31
+ __version__ = version = '0.8.5'
32
+ __version_tuple__ = version_tuple = (0, 8, 5)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -156,15 +156,70 @@ def iterate_stepwise_depositions_scintillate(
156
156
  raise ValueError(msg)
157
157
 
158
158
  rng = np.random.default_rng() if rng is None else rng
159
- output_list = _iterate_stepwise_depositions_scintillate(edep_hits, rng, scint_mat_params, mode)
159
+ counts = ak.num(edep_hits.edep)
160
+ output_array = _iterate_stepwise_depositions_scintillate(
161
+ edep_hits, rng, scint_mat_params, mode, ak.sum(counts)
162
+ )
160
163
 
161
- # convert the numba result back into an awkward array.
162
- builder = ak.ArrayBuilder()
163
- for r in output_list:
164
- with builder.list():
165
- builder.extend(r)
164
+ return ak.unflatten(output_array, counts)
166
165
 
167
- return builder.snapshot()
166
+
167
+ def iterate_stepwise_depositions_numdet(
168
+ edep_hits: ak.Array,
169
+ optmap: OptmapForConvolve,
170
+ det: str,
171
+ map_scaling: float = 1,
172
+ map_scaling_sigma: float = 0,
173
+ rng: np.random.Generator | None = None,
174
+ ):
175
+ if edep_hits.xloc.ndim == 1:
176
+ msg = "the pe processors only support already reshaped output"
177
+ raise ValueError(msg)
178
+
179
+ rng = np.random.default_rng() if rng is None else rng
180
+ counts = ak.num(edep_hits.num_scint_ph)
181
+ output_array, res = _iterate_stepwise_depositions_numdet(
182
+ edep_hits,
183
+ rng,
184
+ np.where(optmap.dets == det)[0][0],
185
+ map_scaling,
186
+ map_scaling_sigma,
187
+ optmap.edges,
188
+ optmap.weights,
189
+ ak.sum(counts),
190
+ )
191
+
192
+ if res["det_no_stats"] > 0:
193
+ log.warning(
194
+ "had edep out in voxels without stats: %d",
195
+ res["det_no_stats"],
196
+ )
197
+ if res["oob"] > 0:
198
+ log.warning(
199
+ "had edep out of map bounds: %d (%.2f%%)",
200
+ res["oob"],
201
+ (res["oob"] / (res["ib"] + res["oob"])) * 100,
202
+ )
203
+
204
+ return ak.unflatten(output_array, counts)
205
+
206
+
207
+ def iterate_stepwise_depositions_times(
208
+ edep_hits: ak.Array,
209
+ scint_mat_params: sc.ComputedScintParams,
210
+ rng: np.random.Generator | None = None,
211
+ ):
212
+ if edep_hits.particle.ndim == 1:
213
+ msg = "the pe processors only support already reshaped output"
214
+ raise ValueError(msg)
215
+
216
+ rng = np.random.default_rng() if rng is None else rng
217
+ counts = ak.sum(edep_hits.num_det_ph, axis=1)
218
+ output_array = _iterate_stepwise_depositions_times(
219
+ edep_hits, rng, scint_mat_params, ak.sum(counts)
220
+ )
221
+
222
+ return ak.unflatten(output_array, counts)
168
223
 
169
224
 
170
225
  _pdg_func = numba_pdgid_funcs()
@@ -270,17 +325,15 @@ def _iterate_stepwise_depositions_pois(
270
325
  # - cache=True does not work with outer prange, i.e. loading the cached file fails (numba bug?)
271
326
  @njit(parallel=False, nogil=True, cache=True)
272
327
  def _iterate_stepwise_depositions_scintillate(
273
- edep_hits, rng, scint_mat_params: sc.ComputedScintParams, mode: str
328
+ edep_hits, rng, scint_mat_params: sc.ComputedScintParams, mode: str, output_length: int
274
329
  ):
275
330
  pdgid_map = {}
276
- output_list = []
331
+ output = np.empty(shape=output_length, dtype=np.int64)
277
332
 
333
+ output_index = 0
278
334
  for rowid in range(len(edep_hits)): # iterate hits
279
335
  hit = edep_hits[rowid]
280
- hit_output = []
281
-
282
- # iterate steps inside the hit
283
- for si in range(len(hit.particle)):
336
+ for si in range(len(hit.particle)): # iterate steps inside the hit
284
337
  # get the particle information.
285
338
  particle = hit.particle[si]
286
339
  if particle not in pdgid_map:
@@ -295,12 +348,106 @@ def _iterate_stepwise_depositions_scintillate(
295
348
  rng,
296
349
  emission_term_model=("poisson" if mode == "no-fano" else "normal_fano"),
297
350
  )
298
- hit_output.append(num_phot)
351
+ output[output_index] = num_phot
352
+ output_index += 1
299
353
 
300
- assert len(hit_output) == len(hit.particle)
301
- output_list.append(hit_output)
354
+ assert output_index == output_length
355
+ return output
356
+
357
+
358
+ # - run with NUMBA_FULL_TRACEBACKS=1 NUMBA_BOUNDSCHECK=1 for testing/checking
359
+ # - cache=True does not work with outer prange, i.e. loading the cached file fails (numba bug?)
360
+ @njit(parallel=False, nogil=True, cache=True)
361
+ def _iterate_stepwise_depositions_numdet(
362
+ edep_hits,
363
+ rng,
364
+ detidx: int,
365
+ map_scaling: float,
366
+ map_scaling_sigma: float,
367
+ optmap_edges,
368
+ optmap_weights,
369
+ output_length: int,
370
+ ):
371
+ oob = ib = det_no_stats = 0
372
+ output = np.empty(shape=output_length, dtype=np.int64)
373
+
374
+ output_index = 0
375
+ for rowid in range(len(edep_hits)): # iterate hits
376
+ hit = edep_hits[rowid]
377
+
378
+ map_scaling_evt = map_scaling
379
+ if map_scaling_sigma > 0:
380
+ map_scaling_evt = rng.normal(loc=map_scaling, scale=map_scaling_sigma)
381
+
382
+ # iterate steps inside the hit
383
+ for si in range(len(hit.xloc)):
384
+ loc = np.array([hit.xloc[si], hit.yloc[si], hit.zloc[si]], dtype=np.float64)
385
+ # coordinates -> bins of the optical map.
386
+ bins = np.empty(3, dtype=np.int64)
387
+ for j in range(3):
388
+ edges = optmap_edges[j].astype(np.float64)
389
+ start = edges[0]
390
+ width = edges[1] - edges[0]
391
+ nbins = edges.shape[0] - 1
392
+ bins[j] = int((loc[j] - start) / width)
393
+
394
+ if bins[j] < 0 or bins[j] >= nbins:
395
+ bins[j] = -1 # normalize all out-of-bounds bins just to one end.
396
+
397
+ if bins[0] == -1 or bins[1] == -1 or bins[2] == -1:
398
+ detp = 0.0 # out-of-bounds of optmap
399
+ oob += 1
400
+ else:
401
+ # get probabilities from map.
402
+ detp = optmap_weights[detidx, bins[0], bins[1], bins[2]] * map_scaling_evt
403
+ if detp < 0:
404
+ det_no_stats += 1
405
+ ib += 1
406
+
407
+ pois_cnt = 0 if detp <= 0.0 else rng.poisson(lam=hit.num_scint_ph[si] * detp)
408
+ output[output_index] = pois_cnt
409
+ output_index += 1
410
+
411
+ assert output_index == output_length
412
+ return output, {"oob": oob, "ib": ib, "det_no_stats": det_no_stats}
413
+
414
+
415
+ # - run with NUMBA_FULL_TRACEBACKS=1 NUMBA_BOUNDSCHECK=1 for testing/checking
416
+ # - cache=True does not work with outer prange, i.e. loading the cached file fails (numba bug?)
417
+ # - the output dictionary is not threadsafe, so parallel=True is not working with it.
418
+ @njit(parallel=False, nogil=True, cache=True)
419
+ def _iterate_stepwise_depositions_times(
420
+ edep_hits, rng, scint_mat_params: sc.ComputedScintParams, output_length: int
421
+ ):
422
+ pdgid_map = {}
423
+ output = np.empty(shape=output_length, dtype=np.float64)
424
+
425
+ output_index = 0
426
+ for rowid in range(len(edep_hits)): # iterate hits
427
+ hit = edep_hits[rowid]
428
+
429
+ assert len(hit.particle) == len(hit.num_det_ph)
430
+ # iterate steps inside the hit
431
+ for si in range(len(hit.particle)):
432
+ pois_cnt = hit.num_det_ph[si]
433
+ if pois_cnt <= 0:
434
+ continue
435
+
436
+ # get the particle information.
437
+ particle = hit.particle[si]
438
+ if particle not in pdgid_map:
439
+ pdgid_map[particle] = (_pdgid_to_particle(particle), _pdg_func.charge(particle))
440
+ part, _charge = pdgid_map[particle]
441
+
442
+ # get time spectrum.
443
+ # note: we assume "immediate" propagation after scintillation.
444
+ scint_times = sc.scintillate_times(scint_mat_params, part, pois_cnt, rng) + hit.time[si]
445
+ assert len(scint_times) == pois_cnt
446
+ output[output_index : output_index + len(scint_times)] = scint_times
447
+ output_index += len(scint_times)
302
448
 
303
- return output_list
449
+ assert output_index == output_length
450
+ return output
304
451
 
305
452
 
306
453
  def _get_scint_params(material: str):
reboost/spms/__init__.py CHANGED
@@ -1,10 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from .pe import detected_photoelectrons, emitted_scintillation_photons, load_optmap, load_optmap_all
3
+ from .pe import (
4
+ detected_photoelectrons,
5
+ emitted_scintillation_photons,
6
+ load_optmap,
7
+ load_optmap_all,
8
+ number_of_detected_photoelectrons,
9
+ photoelectron_times,
10
+ )
4
11
 
5
12
  __all__ = [
6
13
  "detected_photoelectrons",
7
14
  "emitted_scintillation_photons",
8
15
  "load_optmap",
9
16
  "load_optmap_all",
17
+ "number_of_detected_photoelectrons",
18
+ "photoelectron_times",
10
19
  ]
reboost/spms/pe.py CHANGED
@@ -109,7 +109,12 @@ def detected_photoelectrons(
109
109
  map_scaling: float = 1,
110
110
  map_scaling_sigma: float = 0,
111
111
  ) -> VectorOfVectors:
112
- """Derive the number of detected photoelectrons (p.e.) from scintillator hits using an optical map.
112
+ """Derive the number and arrival times of detected photoelectrons (p.e.) from scintillator hits using an optical map.
113
+
114
+ .. deprecated :: 0.8.5
115
+ Use the other, more fine-granular and split processors
116
+ :func:`number_of_detected_photoelectrons` and :func:`photoelectron_times` to
117
+ replace this legacy processor.
113
118
 
114
119
  Parameters
115
120
  ----------
@@ -181,3 +186,77 @@ def emitted_scintillation_photons(
181
186
  scint_mat_params = convolve._get_scint_params(material)
182
187
  ph = convolve.iterate_stepwise_depositions_scintillate(hits, scint_mat_params)
183
188
  return VectorOfVectors(ph)
189
+
190
+
191
+ def number_of_detected_photoelectrons(
192
+ xloc: ak.Array,
193
+ yloc: ak.Array,
194
+ zloc: ak.Array,
195
+ num_scint_ph: ak.Array,
196
+ optmap: convolve.OptmapForConvolve,
197
+ spm_detector: str,
198
+ map_scaling: float = 1,
199
+ map_scaling_sigma: float = 0,
200
+ ) -> VectorOfVectors:
201
+ """Derive the number of detected photoelectrons (p.e.) from scintillator hits using an optical map.
202
+
203
+ Parameters
204
+ ----------
205
+ xloc
206
+ array of x coordinate position of scintillation events.
207
+ yloc
208
+ array of y coordinate position of scintillation events.
209
+ zloc
210
+ array of z coordinate position of scintillation events.
211
+ num_scint_ph
212
+ array of emitted scintillation photons, as generated by
213
+ :func:`emitted_scintillation_photons`.
214
+ detp
215
+ """
216
+ hits = ak.Array(
217
+ {
218
+ "xloc": units_conv_ak(xloc, "m"),
219
+ "yloc": units_conv_ak(yloc, "m"),
220
+ "zloc": units_conv_ak(zloc, "m"),
221
+ "num_scint_ph": units_conv_ak(num_scint_ph, "dimensionless"),
222
+ }
223
+ )
224
+
225
+ ph = convolve.iterate_stepwise_depositions_numdet(
226
+ hits, optmap, spm_detector, map_scaling, map_scaling_sigma
227
+ )
228
+ return VectorOfVectors(ph)
229
+
230
+
231
+ def photoelectron_times(
232
+ num_det_ph: ak.Array,
233
+ particle: ak.Array,
234
+ time: ak.Array,
235
+ material: str,
236
+ ) -> VectorOfVectors:
237
+ """Derive the arrival times of scintillation photons.
238
+
239
+ Parameters
240
+ ----------
241
+ num_det_ph
242
+ array of detected scintillation photons, as generated by
243
+ :func:`emitted_scintillation_photons`.
244
+ particle
245
+ array of particle PDG IDs of scintillation events.
246
+ time
247
+ array of timestamps of scintillation events.
248
+ material
249
+ scintillating material name.
250
+ """
251
+ hits = ak.Array(
252
+ {
253
+ "num_det_ph": units_conv_ak(num_det_ph, "dimensionless"),
254
+ "particle": units_conv_ak(particle, "dimensionless"),
255
+ "time": units_conv_ak(time, "ns"),
256
+ }
257
+ )
258
+
259
+ scint_mat_params = convolve._get_scint_params(material)
260
+ pe = convolve.iterate_stepwise_depositions_times(hits, scint_mat_params)
261
+
262
+ return VectorOfVectors(pe, attrs={"units": "ns"})
reboost/utils.py CHANGED
@@ -445,7 +445,7 @@ def write_lh5(
445
445
  time_dict.update_field("write", start_time)
446
446
 
447
447
 
448
- def get_remage_detector_uids(h5file: str | Path) -> dict:
448
+ def get_remage_detector_uids(h5file: str | Path, *, lh5_table: str = "stp") -> dict:
449
449
  """Get mapping of detector names to UIDs from a remage output file.
450
450
 
451
451
  The remage LH5 output files contain a link structure that lets the user
@@ -483,7 +483,7 @@ def get_remage_detector_uids(h5file: str | Path) -> dict:
483
483
 
484
484
  out = {}
485
485
  with h5py.File(h5file, "r") as f:
486
- g = f["/stp/__by_uid__"]
486
+ g = f[f"/{lh5_table}/__by_uid__"]
487
487
  # loop over links
488
488
  for key in g:
489
489
  # is this a link?
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reboost
3
- Version: 0.8.4
3
+ Version: 0.8.5
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
@@ -1,6 +1,6 @@
1
1
  reboost/__init__.py,sha256=VZz9uo7i2jgAx8Zi15SptLZnE_qcnGuNWwqkD3rYHFA,278
2
2
  reboost/__main__.py,sha256=42koSxY2st4mMIRSAnKz06nP5HppMPxBVFf2jaHljGs,95
3
- reboost/_version.py,sha256=6jY56wQpNbuVX1aDDCpFgsBq74u7lloIJcrJSefeVec,704
3
+ reboost/_version.py,sha256=ulkPqAGMUJNjTNjZ56AYF9MmX06386otNWNjTA8opI8,704
4
4
  reboost/build_evt.py,sha256=VXIfK_pfe_Cgym6gI8dESwONZi-v_4fll0Pn09vePQY,3767
5
5
  reboost/build_glm.py,sha256=IerSLQfe51ZO7CQP2kmfPnOIVaDtcfw3byOM02Vaz6o,9472
6
6
  reboost/build_hit.py,sha256=N_nxvH69SvILVNmyvVfhQwQdD_PDW8tlsqj2ciO5nKE,17409
@@ -10,7 +10,7 @@ reboost/iterator.py,sha256=qlEqRv5qOh8eIs-dyVOLYTvH-ZpQDx9fLckpcAdtWjs,6975
10
10
  reboost/log_utils.py,sha256=VqS_9OC5NeNU3jcowVOBB0NJ6ssYvNWnirEY-JVduEA,766
11
11
  reboost/profile.py,sha256=EOTmjmS8Rm_nYgBWNh6Rntl2XDsxdyed7yEdWtsZEeg,2598
12
12
  reboost/units.py,sha256=LUwl6swLQoG09Rt9wcDdu6DTrwDsy-C751BNGzX4sz8,3651
13
- reboost/utils.py,sha256=-4315U6m1M-rwoaI_inI1wIo_l20kvoGmC84D_QOhkE,14563
13
+ reboost/utils.py,sha256=vl-_BUOeXcazNs4zN-9k-OVEptdf3FtCeej2QZhClKc,14599
14
14
  reboost/daq/__init__.py,sha256=rNPhxx1Yawt3tENYhmOYSum9_TdV57ZU5kjxlWFAGuo,107
15
15
  reboost/daq/core.py,sha256=Rs6Q-17fzEod2iX_2WqEmnqKnNRFoWTYURl3wYhFihU,9915
16
16
  reboost/daq/utils.py,sha256=KcH6zvlInmD2YiF6V--DSYBTYudJw3G-hp2JGOcES2o,1042
@@ -24,7 +24,7 @@ reboost/math/stats.py,sha256=Rq4Wdzv-3aoSK7EsPZCuOEHfnOz3w0moIzCEHbC07xw,3173
24
24
  reboost/optmap/__init__.py,sha256=imvuyld-GLw8qdwqW-lXCg2feptcTyQo3wIzPvDHwmY,93
25
25
  reboost/optmap/__main__.py,sha256=DfzkXQ7labOe53hd7jH5pAbTW491jjQYSMLyl72L4Rk,111
26
26
  reboost/optmap/cli.py,sha256=_7WBlx55eRyW_wWB-ELbFaWXin2d3xsh6Q5bFoNJaHE,8694
27
- reboost/optmap/convolve.py,sha256=0UCOVy6BIu5OYoSWeAb6fuROqvGu9rVswly6XvnxYoE,10763
27
+ reboost/optmap/convolve.py,sha256=e3vTBurYSM4UmicIdDif6_cI04pV8khHEG5n7M_DWNg,16079
28
28
  reboost/optmap/create.py,sha256=GmGd0-F0eWmw7ywH8pT1lKiMb60QXCq9al8Ka_ySD1Q,14382
29
29
  reboost/optmap/evt.py,sha256=nZcB3aOPtMhySu00J23KlOTijeI5Oazde-vfM5A71e8,5664
30
30
  reboost/optmap/mapview.py,sha256=fswwXolA6au8u8gljBKy8PSXC2W7Cy_GwOV86-duYG8,6880
@@ -34,11 +34,11 @@ reboost/shape/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  reboost/shape/cluster.py,sha256=nwR1Dnf00SDICGPqpXeM1Q7_DwTtO9uP3wmuML45c3g,8195
35
35
  reboost/shape/group.py,sha256=gOCYgir2gZqmW1JXtbNRPlQqP0gmUcbe7RVb9CbY1pU,5540
36
36
  reboost/shape/reduction.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- reboost/spms/__init__.py,sha256=24b9GelP4ngXc7JibpWqLOtUuIaQQa0DzDQYP8lVKUc,262
38
- reboost/spms/pe.py,sha256=cFj-D1-pj5djRHmrfYMjNscNnQfveACMZMJq4u4th3w,5830
39
- reboost-0.8.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
- reboost-0.8.4.dist-info/METADATA,sha256=YdOpd1dR4rVjYwz6dkjtxvQ6qoJQmAMR6DXgvAvH1zM,3877
41
- reboost-0.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
- reboost-0.8.4.dist-info/entry_points.txt,sha256=DxhD6BidSWNot9BrejHJjQ7RRLmrMaBIl52T75oWTwM,93
43
- reboost-0.8.4.dist-info/top_level.txt,sha256=q-IBsDepaY_AbzbRmQoW8EZrITXRVawVnNrB-_zyXZs,8
44
- reboost-0.8.4.dist-info/RECORD,,
37
+ reboost/spms/__init__.py,sha256=8I6WT8i_kUPqEDnSD0aCf6A26cjKjQQZSNrvwZ3o-Ac,415
38
+ reboost/spms/pe.py,sha256=LwqrK1HOZWzGcNZnntaqI6r4rnDww4KW9Mao4xLFbDE,8226
39
+ reboost-0.8.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
+ reboost-0.8.5.dist-info/METADATA,sha256=4MY_lm3rUT_DfLU3hc-_U2LJIHuV_j-EPgi0SLRJYiU,3877
41
+ reboost-0.8.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
+ reboost-0.8.5.dist-info/entry_points.txt,sha256=DxhD6BidSWNot9BrejHJjQ7RRLmrMaBIl52T75oWTwM,93
43
+ reboost-0.8.5.dist-info/top_level.txt,sha256=q-IBsDepaY_AbzbRmQoW8EZrITXRVawVnNrB-_zyXZs,8
44
+ reboost-0.8.5.dist-info/RECORD,,