reboost 0.2.2__py3-none-any.whl → 0.2.3__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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.2'
21
- __version_tuple__ = version_tuple = (0, 2, 2)
20
+ __version__ = version = '0.2.3'
21
+ __version_tuple__ = version_tuple = (0, 2, 3)
reboost/build_evt.py CHANGED
@@ -71,7 +71,6 @@ def build_evt(
71
71
  path to the evt tier (output) file, if `None` the :class:`Table` is returned in memory
72
72
  config
73
73
  dictionary of the configuration.
74
-
75
74
  buffer
76
75
  number of events to process simultaneously
77
76
 
reboost/build_glm.py CHANGED
@@ -224,7 +224,7 @@ def build_glm(
224
224
  lh5_table_list = list(lh5.ls(stp_file, "stp/"))
225
225
 
226
226
  # get rows in the table
227
- if files.glm is None:
227
+ if files.glm[file_idx] is None:
228
228
  for lh5_table in lh5_table_list:
229
229
  if lh5_table.replace("stp/", "") not in glm_sum:
230
230
  glm_sum[lh5_table.replace("stp/", "")] = None
@@ -274,7 +274,7 @@ def build_glm(
274
274
 
275
275
  lh5_subgroup = lh5_table.replace("stp/", "")
276
276
 
277
- if files.glm is not None:
277
+ if files.glm[file_idx] is not None:
278
278
  store.write(
279
279
  out_tab,
280
280
  f"{out_table_name}/{lh5_subgroup}",
reboost/build_hit.py CHANGED
@@ -179,7 +179,7 @@ def build_hit(
179
179
  config: Mapping | str,
180
180
  args: Mapping | AttrsDict,
181
181
  stp_files: str | list[str],
182
- glm_files: str | list[str],
182
+ glm_files: str | list[str] | None,
183
183
  hit_files: str | list[str] | None,
184
184
  *,
185
185
  start_evtid: int = 0,
@@ -199,7 +199,7 @@ def build_hit(
199
199
  stp_files
200
200
  list of strings or string of the stp file path.
201
201
  glm_files
202
- list of strings or string of the glm file path.
202
+ list of strings or string of the glm file path, if `None` will be build in memory.
203
203
  hit_files
204
204
  list of strings or string of the hit file path. The `hit` file can also be `None` in which
205
205
  case the hits are returned as an `ak.Array` in memory.
@@ -294,19 +294,25 @@ def build_hit(
294
294
  if stps is None:
295
295
  continue
296
296
 
297
- # produce the hit table
298
297
  ak_obj = stps.view_as("ak")
299
298
 
299
+ # produce the hit table
300
300
  for out_det_idx, out_detector in enumerate(out_detectors):
301
301
  # loop over the rows
302
302
  if out_detector not in output_tables and files.hit is None:
303
303
  output_tables[out_detector] = None
304
304
 
305
- hit_table = core.evaluate_hit_table_layout(
306
- copy.deepcopy(ak_obj),
307
- expression=proc_group["hit_table_layout"],
308
- time_dict=time_dict[proc_name],
309
- )
305
+ # get the attributes
306
+ attrs = utils.copy_units(stps)
307
+
308
+ if "hit_table_layout" in proc_group:
309
+ hit_table = core.evaluate_hit_table_layout(
310
+ copy.deepcopy(ak_obj),
311
+ expression=proc_group["hit_table_layout"],
312
+ time_dict=time_dict[proc_name],
313
+ )
314
+ else:
315
+ hit_table = copy.deepcopy(stps)
310
316
 
311
317
  local_dict = {
312
318
  "DETECTOR_OBJECTS": det_objects[out_detector],
@@ -314,7 +320,7 @@ def build_hit(
314
320
  "DETECTOR": out_detector,
315
321
  }
316
322
  # add fields
317
- for field, expression in proc_group["operations"].items():
323
+ for field, expression in proc_group.get("operations", {}).items():
318
324
  # evaluate the expression
319
325
  col = core.evaluate_output_column(
320
326
  hit_table,
@@ -327,10 +333,20 @@ def build_hit(
327
333
  hit_table.add_field(field, col)
328
334
 
329
335
  # remove unwanted fields
330
- hit_table = core.remove_columns(hit_table, outputs=proc_group["outputs"])
336
+ if "outputs" in proc_group:
337
+ hit_table = core.remove_columns(
338
+ hit_table, outputs=proc_group["outputs"]
339
+ )
340
+
341
+ # assign units in the output table
342
+ hit_table = utils.assign_units(hit_table, attrs)
331
343
 
332
344
  # get the IO mode
333
345
 
346
+ new_hit_file = (file_idx == 0) or (
347
+ files.hit[file_idx] != files.hit[file_idx - 1]
348
+ )
349
+
334
350
  wo_mode = (
335
351
  "of"
336
352
  if (
@@ -338,6 +354,7 @@ def build_hit(
338
354
  and out_det_idx == 0
339
355
  and in_det_idx == 0
340
356
  and chunk_idx == 0
357
+ and new_hit_file
341
358
  )
342
359
  else "append"
343
360
  )
reboost/build_tcm.py CHANGED
@@ -6,7 +6,7 @@ import re
6
6
  import awkward as ak
7
7
  from lgdo import Table, lh5
8
8
 
9
- from reboost import shape
9
+ from reboost.shape import group
10
10
 
11
11
  log = logging.getLogger(__name__)
12
12
 
@@ -102,7 +102,7 @@ def get_tcm_from_ak(
102
102
 
103
103
  obj_tot = ak.concatenate(sort_objs)
104
104
 
105
- return shape.group.group_by_time(
105
+ return group.group_by_time(
106
106
  obj_tot,
107
107
  time_name=time_name,
108
108
  evtid_name=idx_name,
reboost/iterator.py CHANGED
@@ -5,7 +5,9 @@ import time
5
5
  import typing
6
6
 
7
7
  from lgdo.lh5 import LH5Store
8
- from lgdo.types import LGDO
8
+ from lgdo.types import LGDO, Table
9
+
10
+ from reboost import build_glm
9
11
 
10
12
  log = logging.getLogger(__name__)
11
13
 
@@ -15,7 +17,7 @@ class GLMIterator:
15
17
 
16
18
  def __init__(
17
19
  self,
18
- glm_file: str,
20
+ glm_file: str | None,
19
21
  stp_file: str,
20
22
  lh5_group: str,
21
23
  start_row: int,
@@ -31,7 +33,8 @@ class GLMIterator:
31
33
  Parameters
32
34
  ----------
33
35
  glm_file
34
- the file containing the event lookup map.
36
+ the file containing the event lookup map, if `None` the glm will
37
+ be created in memory.
35
38
  stp_file
36
39
  the file containing the steps to read.
37
40
  lh5_group
@@ -65,6 +68,11 @@ class GLMIterator:
65
68
  self.sto = LH5Store()
66
69
  self.n_rows_read = 0
67
70
  self.time_dict = time_dict
71
+ self.glm = None
72
+
73
+ # build the glm in memory
74
+ if self.glm_file is None:
75
+ self.glm = build_glm.build_glm(stp_file, None, out_table_name="glm", id_name="evtid")
68
76
 
69
77
  def __iter__(self) -> typing.Iterator:
70
78
  self.current_i_entry = 0
@@ -83,10 +91,21 @@ class GLMIterator:
83
91
  if self.time_dict is not None:
84
92
  time_start = time.time()
85
93
 
86
- # read the glm rows
87
- glm_rows, n_rows_read = self.sto.read(
88
- f"glm/{self.lh5_group}", self.glm_file, start_row=self.start_row_tmp, n_rows=n_rows
89
- )
94
+ # read the glm rows]
95
+ if self.glm_file is not None:
96
+ glm_rows, n_rows_read = self.sto.read(
97
+ f"glm/{self.lh5_group}", self.glm_file, start_row=self.start_row_tmp, n_rows=n_rows
98
+ )
99
+ else:
100
+ # get the maximum row to read
101
+ max_row = self.start_row_tmp + n_rows
102
+ max_row = min(len(self.glm[self.lh5_group]), max_row)
103
+
104
+ if max_row != self.start_row_tmp:
105
+ glm_rows = Table(self.glm[self.lh5_group][self.start_row_tmp : max_row])
106
+
107
+ n_rows_read = max_row - self.start_row_tmp
108
+
90
109
  if self.time_dict is not None:
91
110
  self.time_dict.update_field("read/glm", time_start)
92
111
 
@@ -106,7 +125,6 @@ class GLMIterator:
106
125
  # extract range of stp rows to read
107
126
  start = glm_ak.start_row[0]
108
127
  n = sum(glm_ak.n_rows)
109
-
110
128
  if self.time_dict is not None:
111
129
  time_start = time.time()
112
130
 
reboost/shape/group.py CHANGED
@@ -25,7 +25,9 @@ def _sort_data(obj: ak.Array, *, time_name: str = "time", evtid_name: str = "evt
25
25
  -------
26
26
  sorted awkward array
27
27
  """
28
+ obj = obj[ak.argsort(obj[evtid_name])]
28
29
  obj_unflat = ak.unflatten(obj, ak.run_lengths(obj[evtid_name]))
30
+
29
31
  indices = ak.argsort(obj_unflat[time_name], axis=-1)
30
32
  sorted_obj = obj_unflat[indices]
31
33
 
@@ -120,9 +122,9 @@ def group_by_time(
120
122
 
121
123
  # get difference
122
124
  time_diffs = np.diff(obj[time_name])
123
- index_diffs = np.diff(obj[evtid_name])
125
+ index_diffs = np.array(np.diff(obj[evtid_name]), dtype=np.int32)
124
126
 
125
- # index of thhe last element in each run
127
+ # index of the last element in each run
126
128
  time_change = (time_diffs > window * 1000) & (index_diffs == 0)
127
129
  index_change = index_diffs > 0
128
130
 
reboost/utils.py CHANGED
@@ -1,25 +1,62 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import itertools
4
5
  import logging
5
6
  import re
6
- from collections.abc import Iterable
7
+ from collections.abc import Iterable, Mapping
7
8
  from contextlib import contextmanager
8
9
  from pathlib import Path
9
10
 
10
11
  from dbetto import AttrsDict
12
+ from lgdo.types import Table
11
13
 
12
14
  log = logging.getLogger(__name__)
13
15
 
14
16
 
15
17
  def get_file_dict(
16
18
  stp_files: list[str] | str,
17
- glm_files: list[str] | str,
19
+ glm_files: list[str] | str | None,
18
20
  hit_files: list[str] | str | None = None,
19
21
  ) -> AttrsDict:
20
- """Get the file info as a AttrsDict."""
22
+ """Get the file info as a AttrsDict.
23
+
24
+ Creates an :class:`dbetto.AttrsDict` with keys `stp_files`,
25
+ `glm_files` and `hit_files`. Each key contains a list of
26
+ file-paths (or `None`).
27
+
28
+ Parameters
29
+ ----------
30
+ stp_files
31
+ string or list of strings of the stp files.
32
+ glm_files
33
+ string or list of strings of the glm files, or None in which
34
+ case the glm will be created in memory.
35
+ hit_files
36
+ string or list of strings of the hit files, if None the output
37
+ files will be created in memory.
38
+ """
39
+ # make a list of the right length
40
+ glm_files_list = [None] * len(stp_files) if glm_files is None else glm_files
41
+
42
+ # make a list of files in case
43
+ # 1) hit_files is a str and stp_files is a list
44
+ # 2) hit_files and stp_files are both lists of different length
45
+
46
+ hit_is_list = isinstance(hit_files, list)
47
+ stp_is_list = isinstance(stp_files, list)
48
+
49
+ make_files_list = (not hit_is_list and stp_is_list) or (
50
+ hit_is_list and stp_is_list and len(hit_files) == 1 and len(stp_files) > 1
51
+ )
52
+
53
+ hit_files_list = [hit_files] * len(stp_files) if (make_files_list) else hit_files
54
+
21
55
  files = {}
22
- for file_type, file_list in zip(["stp", "glm", "hit"], [stp_files, glm_files, hit_files]):
56
+
57
+ for file_type, file_list in zip(
58
+ ["stp", "glm", "hit"], [stp_files, glm_files_list, hit_files_list]
59
+ ):
23
60
  if isinstance(file_list, str):
24
61
  files[file_type] = [file_list]
25
62
  else:
@@ -35,6 +72,48 @@ def get_file_list(path: str | None, threads: int | None = None) -> list[str]:
35
72
  return [f"{(Path(path).with_suffix(''))}_t{idx}.lh5" for idx in range(threads)]
36
73
 
37
74
 
75
+ def copy_units(tab: Table) -> dict:
76
+ """Extract a dictionary of attributes (i.e. units).
77
+
78
+ Parameters
79
+ ----------
80
+ tab
81
+ Table to get the units from.
82
+
83
+ Returns
84
+ -------
85
+ a dictionary with the units for each field
86
+ in the table.
87
+ """
88
+ units = {}
89
+
90
+ for field in tab:
91
+ if "units" in tab[field].attrs:
92
+ units[field] = tab[field].attrs["units"]
93
+
94
+ return units
95
+
96
+
97
+ def assign_units(tab: Table, units: Mapping) -> Table:
98
+ """Copy the attributes from the map of attributes to the table.
99
+
100
+ Parameters
101
+ ----------
102
+ tab
103
+ Table to add attributes to.
104
+ units
105
+ mapping (dictionary like) of units of each field
106
+
107
+ Returns
108
+ -------
109
+ an updated table with LGDO attributes.
110
+ """
111
+ for field in tab:
112
+ if field in units:
113
+ tab[field].attrs["units"] = units[field]
114
+ return tab
115
+
116
+
38
117
  def _search_string(string: str):
39
118
  """Capture the characters matching the pattern for a function call."""
40
119
  pattern = r"\b([a-zA-Z_][a-zA-Z0-9_\.]*)\s*\("
@@ -119,6 +198,33 @@ def get_function_string(expr: str, aliases: dict | None = None) -> tuple[str, di
119
198
  return expr, globs
120
199
 
121
200
 
201
+ def get_channels_from_groups(names: list | str | None, groupings: dict | None = None) -> list:
202
+ """Get a list of channels from a list of groups.
203
+
204
+ Parameters
205
+ ----------
206
+ names
207
+ list of channel names
208
+ groupings
209
+ dictionary of the groupings of channels
210
+
211
+ Returns
212
+ -------
213
+ list of channels
214
+ """
215
+ if names is None:
216
+ channels_e = []
217
+ elif isinstance(names, str):
218
+ channels_e = groupings[names]
219
+ elif isinstance(names, list):
220
+ channels_e = list(itertools.chain.from_iterable([groupings[e] for e in names]))
221
+ else:
222
+ msg = f"names {names} must be list or str or `None`"
223
+ raise ValueError(msg)
224
+
225
+ return channels_e
226
+
227
+
122
228
  def merge_dicts(dict_list: list) -> dict:
123
229
  """Merge a list of dictionaries, concatenating the items where they exist.
124
230
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reboost
3
- Version: 0.2.2
3
+ Version: 0.2.3
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,15 +1,15 @@
1
1
  reboost/__init__.py,sha256=RVNl3Qgx_hTUeBGXaWYmiTcmXUDhTfvlAGGC8bo_jP8,316
2
- reboost/_version.py,sha256=OjGGK5TcHVG44Y62aAqeJH4CskkZoY9ydbHOtCDew50,511
3
- reboost/build_evt.py,sha256=5Q3T0LCl8xMtyRRhcs6layC1xh4vp2f26PgB1yab2zs,4798
4
- reboost/build_glm.py,sha256=5cuHnMogRKGaVCi4eqTrgDroLFy4s8mGp2nKcKfKQGI,9243
5
- reboost/build_hit.py,sha256=yAjYmBJUZvnyTuU_kpuJle_TKeSU027OIJr7No9z2Ms,13476
6
- reboost/build_tcm.py,sha256=N1rZwht88ZaKWmURch1VrVUbQROXfP56D0aj_JLsRhU,2951
2
+ reboost/_version.py,sha256=wD8hnA5gV5UmPkQnpT3xR6V2csgj9K5NEADogbLK79M,511
3
+ reboost/build_evt.py,sha256=zj3wG_kaV3EoRMQ33AkCNa_2Fv8cLtRuhyRyRmSrOYQ,4797
4
+ reboost/build_glm.py,sha256=LQkM6x6mMOE92-c78uoclOvP9zp3vdMuLQCSP2f2Zk4,9263
5
+ reboost/build_hit.py,sha256=KKfTJgoR5JnAMQVru58B76zPcqZxiexIUlWPUhb1zmU,14260
6
+ reboost/build_tcm.py,sha256=-PawBHoHj0zsm4XsZu5bco9d9a09STicZchduefSNfI,2951
7
7
  reboost/cli.py,sha256=HTZ05DRnDodcf_D6BJCCavx5HqhKDadJCgf-oh8HTJk,6365
8
8
  reboost/core.py,sha256=7Nclc6RUCOSJ1CWVAX0rFNJGM1LEgqvc4tD04CxEAtg,10766
9
- reboost/iterator.py,sha256=cqfh3c0uLP67S0YGaw05-McZQzdMb8BISULIm3PEbKA,3990
9
+ reboost/iterator.py,sha256=72AyoRTgMpWghZt2UOqRj0RGiNzaiBAwgNIUZdduK2s,4698
10
10
  reboost/log_utils.py,sha256=VqS_9OC5NeNU3jcowVOBB0NJ6ssYvNWnirEY-JVduEA,766
11
11
  reboost/profile.py,sha256=EOTmjmS8Rm_nYgBWNh6Rntl2XDsxdyed7yEdWtsZEeg,2598
12
- reboost/utils.py,sha256=PMnHvSD5MpIzJyA3IQD_fLAK-O1RMY68DPGbQJp7Yww,4967
12
+ reboost/utils.py,sha256=T9GIknSKWsKAalbQT9Ny3u9UTYEvy8gghFhmoCs41Io,7751
13
13
  reboost/hpge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  reboost/hpge/psd.py,sha256=vFs8Y5XVW261pB6aOvWmIDzqOaBg-gEOLhL9PbjlEKI,2113
15
15
  reboost/hpge/surface.py,sha256=SZyTmOCTipf27jYaJhtdInzGF1RZ2wKpbtf6HlOQYwM,3662
@@ -26,11 +26,11 @@ reboost/optmap/numba_pdg.py,sha256=y8cXR5PWE2Liprp4ou7vl9do76dl84vXU52ZJD9_I7A,7
26
26
  reboost/optmap/optmap.py,sha256=j4rfbQ84PYSpE-BvP4Rdt96ZjPdwy8P4e4eZz1mATys,12817
27
27
  reboost/shape/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  reboost/shape/cluster.py,sha256=RIvBlhHzp88aaUZGofp5SD9bimnoiqIOddhQ84jiwoM,8135
29
- reboost/shape/group.py,sha256=bSmFCl_yi1hGaKudjiicDEJsiBNyAHiKYdr8ZuH4pSM,4406
29
+ reboost/shape/group.py,sha256=Q3DhEPxbhw3p4bwvpswSd0A-p224l5vRZnfQIEkOVJE,4475
30
30
  reboost/shape/reduction.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- reboost-0.2.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
- reboost-0.2.2.dist-info/METADATA,sha256=F4NsVNDavleMAL-xEYSpR5ElKHtjQXLevD_-XAaT3y4,44219
33
- reboost-0.2.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
34
- reboost-0.2.2.dist-info/entry_points.txt,sha256=DxhD6BidSWNot9BrejHJjQ7RRLmrMaBIl52T75oWTwM,93
35
- reboost-0.2.2.dist-info/top_level.txt,sha256=q-IBsDepaY_AbzbRmQoW8EZrITXRVawVnNrB-_zyXZs,8
36
- reboost-0.2.2.dist-info/RECORD,,
31
+ reboost-0.2.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
+ reboost-0.2.3.dist-info/METADATA,sha256=9cPQ0Bz2cGao4exSC1XwIqp61rfSHNIDLrIwD8SdbB8,44219
33
+ reboost-0.2.3.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
34
+ reboost-0.2.3.dist-info/entry_points.txt,sha256=DxhD6BidSWNot9BrejHJjQ7RRLmrMaBIl52T75oWTwM,93
35
+ reboost-0.2.3.dist-info/top_level.txt,sha256=q-IBsDepaY_AbzbRmQoW8EZrITXRVawVnNrB-_zyXZs,8
36
+ reboost-0.2.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5