legend-daq2lh5 1.5.0__py3-none-any.whl → 1.6.1__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.
daq2lh5/_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 = '1.5.0'
21
- __version_tuple__ = version_tuple = (1, 5, 0)
20
+ __version__ = version = '1.6.1'
21
+ __version_tuple__ = version_tuple = (1, 6, 1)
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
18
18
  log = logging.getLogger(__name__)
19
19
 
20
20
 
21
- def buffer_processor(rb: RawBuffer) -> Table:
21
+ def buffer_processor(rb: RawBuffer, db_dict: dict = None) -> Table:
22
22
  r"""Process raw data buffers.
23
23
 
24
24
  Takes in a :class:`.RawBuffer`, performs any processes specified in the
@@ -142,7 +142,7 @@ def buffer_processor(rb: RawBuffer) -> Table:
142
142
 
143
143
  # Read in and perform the DSP routine
144
144
  if "dsp_config" in rb.proc_spec.keys():
145
- process_dsp(rb, tmp_table)
145
+ process_dsp(rb, tmp_table, db_dict)
146
146
 
147
147
  # Cast as requested dtype before writing to the table
148
148
  if "dtype_conv" in rb.proc_spec.keys():
@@ -294,7 +294,7 @@ def process_windowed_t0(t0s: Array, dts: Array, start_index: int) -> Array:
294
294
  return copy_t0s
295
295
 
296
296
 
297
- def process_dsp(rb: RawBuffer, tmp_table: Table) -> None:
297
+ def process_dsp(rb: RawBuffer, tmp_table: Table, db_dict: dict = None) -> None:
298
298
  r"""Run a DSP processing chain with :mod:`dspeed`.
299
299
 
300
300
  Run a provided DSP config from `rb.proc_spec` using
@@ -309,6 +309,8 @@ def process_dsp(rb: RawBuffer, tmp_table: Table) -> None:
309
309
  tmp_table
310
310
  a :class:`lgdo.Table` that is temporarily created to be written
311
311
  to the raw file.
312
+ db_dict
313
+ a database dictionary storing parameters for each channel
312
314
 
313
315
  Notes
314
316
  -----
@@ -322,10 +324,11 @@ def process_dsp(rb: RawBuffer, tmp_table: Table) -> None:
322
324
  try:
323
325
  # execute the processing chain
324
326
  # This checks that the rb.lgdo is a table and that the field_name is present in the table
325
- proc_chain, mask, dsp_out = bpc(rb.lgdo, dsp_dict)
327
+ proc_chain, mask, dsp_out = bpc(rb.lgdo, dsp_dict, db_dict=db_dict)
326
328
  # Allow for exceptions, in the case of "*" key expansion in the build_raw out_spec
327
- except ProcessingChainError:
329
+ except ProcessingChainError as e:
328
330
  log.info("DSP could not be performed")
331
+ log.info(f"Error: {e}")
329
332
  return None
330
333
 
331
334
  proc_chain.execute()
@@ -19,6 +19,7 @@ def lh5_buffer_processor(
19
19
  overwrite: bool = False,
20
20
  out_spec: dict = None,
21
21
  proc_file_name: str = None,
22
+ db_dict: dict = None,
22
23
  ) -> None:
23
24
  r"""Process raw buffers from an LH5 file.
24
25
 
@@ -74,14 +75,14 @@ def lh5_buffer_processor(
74
75
  # Look one layer deeper for a :meth:`lgdo.Table` if necessary
75
76
  elif lh5.ls(lh5_file, f"{tb}"):
76
77
  # Check to make sure that this isn't a table itself
77
- maybe_table = raw_store.read(f"{tb}", lh5_file)
78
+ maybe_table = lh5.read(f"{tb}", lh5_file)
78
79
  if isinstance(maybe_table, lgdo.Table):
79
80
  lh5_tables.append(f"{tb}")
80
81
  del maybe_table
81
82
  # otherwise, go deeper
82
83
  else:
83
84
  for sub_table in lh5.ls(lh5_file, f"{tb}"):
84
- maybe_table, _ = raw_store.read(f"{tb}/{sub_table}", lh5_file)
85
+ maybe_table = lh5.read(f"{tb}/{sub_table}", lh5_file)
85
86
  if isinstance(maybe_table, lgdo.Table):
86
87
  lh5_tables.append(f"{tb}/{sub_table}")
87
88
  del maybe_table
@@ -114,7 +115,7 @@ def lh5_buffer_processor(
114
115
 
115
116
  # Write everything in the raw file to the new file, check for proc_spec under either the group name, out_name, or the name
116
117
  for tb in lh5_tables:
117
- lgdo_obj = raw_store.read(f"{tb}", lh5_file)
118
+ lgdo_obj = lh5.read(f"{tb}", lh5_file)
118
119
 
119
120
  # Find the out_name.
120
121
  # If the top level group has an lgdo table in it, then the out_name is group
@@ -138,7 +139,9 @@ def lh5_buffer_processor(
138
139
  out_name=out_name,
139
140
  proc_spec=out_spec[decoder_name][group_name]["proc_spec"],
140
141
  )
141
- tmp_table = buffer_processor(rb)
142
+ tmp_table = buffer_processor(
143
+ rb, db_dict=db_dict[tb] if db_dict is not None else None
144
+ )
142
145
  # Update the lgdo_obj to be written to the processed file
143
146
  lgdo_obj = tmp_table
144
147
  else:
@@ -198,4 +201,4 @@ def lh5_buffer_processor(
198
201
  pass
199
202
 
200
203
  # Write the (possibly processed) lgdo_obj to a file
201
- raw_store.write(lgdo_obj, out_name, lh5_file=proc_file_name, group=group_name)
204
+ lh5.write(lgdo_obj, out_name, lh5_file=proc_file_name, group=group_name)
daq2lh5/build_raw.py CHANGED
@@ -28,6 +28,7 @@ def build_raw(
28
28
  overwrite: bool = False,
29
29
  compass_config_file: str = None,
30
30
  hdf5_settings: dict[str, ...] = None,
31
+ db_dict: dict = None,
31
32
  **kwargs,
32
33
  ) -> None:
33
34
  """Convert data into LEGEND HDF5 raw-tier format.
@@ -200,7 +201,7 @@ def build_raw(
200
201
  in_stream,
201
202
  rb_lib=rb_lib,
202
203
  buffer_size=buffer_size,
203
- chunk_mode="full_only",
204
+ chunk_mode="only_full",
204
205
  out_stream=out_stream,
205
206
  )
206
207
  rb_lib = streamer.rb_lib
@@ -230,7 +231,7 @@ def build_raw(
230
231
 
231
232
  # Write header data
232
233
  lh5_store = lh5.LH5Store(keep_open=True)
233
- write_to_lh5_and_clear(header_data, lh5_store, **hdf5_settings)
234
+ write_to_lh5_and_clear(header_data, lh5_store, db_dict, **hdf5_settings)
234
235
 
235
236
  # Now loop through the data
236
237
  n_bytes_last = streamer.n_bytes_read
@@ -255,7 +256,7 @@ def build_raw(
255
256
  if log.getEffectiveLevel() <= logging.INFO and n_max < np.inf:
256
257
  progress_bar.update(n_read)
257
258
 
258
- write_to_lh5_and_clear(chunk_list, lh5_store, **hdf5_settings)
259
+ write_to_lh5_and_clear(chunk_list, lh5_store, db_dict, **hdf5_settings)
259
260
 
260
261
  if n_max <= 0:
261
262
  log.info(f"Wrote {n_max} rows, exiting...")
daq2lh5/data_decoder.py CHANGED
@@ -253,12 +253,10 @@ class DataDecoder:
253
253
  def write_out_garbage(
254
254
  self, filename: str, group: str = "/", lh5_store: LH5Store = None
255
255
  ) -> None:
256
- if lh5_store is None:
257
- lh5_store = lgdo.LH5Store()
258
256
  n_rows = self.garbage_table.loc
259
257
  if n_rows == 0:
260
258
  return
261
- lh5_store.write(
259
+ lgdo.lh5.write(
262
260
  self.garbage_table, "garbage", filename, group, n_rows=n_rows, append=True
263
261
  )
264
262
  self.garbage_table.clear()
daq2lh5/data_streamer.py CHANGED
@@ -97,7 +97,6 @@ class DataStreamer(ABC):
97
97
  dec_names = []
98
98
  for decoder in decoders:
99
99
  dec_name = type(decoder).__name__
100
-
101
100
  # set up wildcard decoder buffers
102
101
  if dec_name not in rb_lib:
103
102
  if "*" not in rb_lib:
@@ -128,33 +127,78 @@ class DataStreamer(ABC):
128
127
  # dec_name is in rb_lib: store the name, and initialize its buffer lgdos
129
128
  dec_names.append(dec_name)
130
129
 
131
- # set up wildcard key buffers
130
+ # Parse wildcard keys in RawBuffers and replace with known keys of the decoder.
131
+ dec_key_list = set(sum(decoder.get_key_lists(), []))
132
+ log.debug(f"{dec_name} offers keys: {dec_key_list}")
133
+
134
+ # track keys which are already used
135
+ matched_keys = set()
136
+ only_wildcard_rb = None
137
+ wildcard_rbs = []
138
+ # find wildcard key buffers
132
139
  for rb in rb_lib[dec_name]:
133
- if (
134
- len(rb.key_list) == 1
135
- and isinstance(rb.key_list[0], str)
136
- and "*" in rb.key_list[0]
137
- ):
138
- matched_key_lists = []
139
- for key_list in decoder.get_key_lists():
140
- # special case: decoders without keys
141
- if rb.key_list[0] == "*" and key_list == [None]:
142
- matched_key_lists.append(key_list)
143
- continue
144
- key_type = type(key_list[0])
145
- for ik in range(len(key_list)):
146
- key_list[ik] = str(key_list[ik])
147
- matched_keys = fnmatch.filter(key_list, rb.key_list[0])
148
- if len(matched_keys) > 1:
149
- for ik in range(len(matched_keys)):
150
- matched_keys[ik] = key_type(key_list[ik])
151
- matched_key_lists.append(matched_keys)
152
- if len(matched_key_lists) == 0:
153
- log.warning(
154
- f"no matched keys for key_list {rb.key_list[0]} in {dec_name}.{rb.out_name}"
155
- )
140
+ log.debug(f"rb {rb.out_name} seeks keys: {rb.key_list}")
141
+ for key in rb.key_list:
142
+ # only string can contain wildcard *
143
+ if not isinstance(key, str):
144
+ matched_keys.add(key)
156
145
  continue
157
- rb.key_list = sum(matched_key_lists, [])
146
+ if key == "*":
147
+ if only_wildcard_rb is None:
148
+ only_wildcard_rb = rb
149
+ else:
150
+ raise KeyError(
151
+ f"Only one '*' wildcard key allowed for decoder {dec_name}"
152
+ )
153
+
154
+ elif "*" in key:
155
+ wildcard_rbs.append(rb)
156
+ else:
157
+ matched_keys.add(key)
158
+
159
+ # append pure wildcard, so it matches last
160
+ if only_wildcard_rb is not None:
161
+ wildcard_rbs.append(only_wildcard_rb)
162
+
163
+ # remove already matched keys with original key type
164
+ dec_key_list = dec_key_list.difference(matched_keys)
165
+ dec_key_list = set(map(str, dec_key_list))
166
+ # remove already matched keys with str key type
167
+ dec_key_list = dec_key_list.difference(matched_keys)
168
+
169
+ log.debug(f"{dec_name} remaining keys: {dec_key_list}")
170
+
171
+ for rb in wildcard_rbs:
172
+ matched_keys = set()
173
+ for key in rb.key_list:
174
+ # find matching keys in the decoder list
175
+ matches = set(fnmatch.filter(dec_key_list, key))
176
+ dec_key_list = dec_key_list.difference(matches)
177
+
178
+ log.debug(f"{dec_name} {key} matched keys: {matches}")
179
+ log.debug(f"{dec_name} remaining keys: {dec_key_list}")
180
+ matched_keys |= matches
181
+
182
+ # Construct the new key_list for the RawBuffer
183
+ # Expect anything that can be cast to int wants to be cast
184
+ rb.key_list = []
185
+ for key in matched_keys:
186
+ if key == "None":
187
+ rb.key_list.append(None)
188
+ try:
189
+ new_key = int(key)
190
+ rb.key_list.append(new_key)
191
+ except ValueError:
192
+ rb.key_list.append(key)
193
+
194
+ if len(rb.key_list) == 0:
195
+ log.warning(
196
+ f"no matched keys for key_list {rb.key_list} in {dec_name}.{rb.out_name}"
197
+ )
198
+ log.debug(
199
+ f"{dec_name}:{rb.out_stream}/{rb.out_name} matched wildcards to {rb.key_list}"
200
+ )
201
+
158
202
  keyed_name_rbs = []
159
203
  ii = 0
160
204
  while ii < len(rb_lib[dec_name]):
@@ -175,7 +219,6 @@ class DataStreamer(ABC):
175
219
  else:
176
220
  key = str(key)
177
221
  expanded_name = rb.out_name.format(key=key)
178
-
179
222
  new_rb = RawBuffer(
180
223
  key_list=[key],
181
224
  out_stream=rb.out_stream,
@@ -191,7 +234,7 @@ class DataStreamer(ABC):
191
234
  rb.fill_safety = decoder.get_max_rows_in_packet()
192
235
  if buffer_size < rb.fill_safety:
193
236
  raise ValueError(
194
- f"{dec_name} requires a buffer of at least length"
237
+ f"{dec_name} requires a buffer of at least length "
195
238
  f"{rb.fill_safety} but buffer size is only {buffer_size}"
196
239
  )
197
240
 
@@ -333,6 +376,7 @@ class DataStreamer(ABC):
333
376
  """
334
377
  rb_lib = RawBufferLibrary()
335
378
  decoders = self.get_decoder_list()
379
+ log.debug(f"Default rb_lib knows about: {decoders}")
336
380
  if len(decoders) == 0:
337
381
  log.warning(
338
382
  f"no decoders returned by get_decoder_list() for {type(self).__name__}"
@@ -344,6 +388,7 @@ class DataStreamer(ABC):
344
388
  if dec_key.endswith("Decoder"):
345
389
  dec_key = dec_key.removesuffix("Decoder")
346
390
  key_lists = decoder.get_key_lists()
391
+ log.debug(f"{dec_key} supports keys {key_lists}")
347
392
  for ii, key_list in enumerate(key_lists):
348
393
  this_name = dec_key
349
394
  if len(key_lists) > 1:
@@ -1,15 +1,47 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import copy
3
4
  import logging
5
+ from typing import Any
4
6
 
5
- import fcutils
6
7
  import lgdo
7
8
  import numpy as np
9
+ from fcio import FCIO, Limits
8
10
 
9
11
  from ..data_decoder import DataDecoder
10
12
 
11
13
  log = logging.getLogger(__name__)
12
14
 
15
+ fc_config_decoded_values = {
16
+ "packet_id": {
17
+ "dtype": "uint32",
18
+ "description": "The index of this decoded packet in the file.",
19
+ },
20
+ "nsamples": {"dtype": "int32", "description": "samples per channel"},
21
+ "nadcs": {"dtype": "int32", "description": "number of adc channels"},
22
+ "ntriggers": {"dtype": "int32", "description": "number of triggertraces"},
23
+ "streamid": {"dtype": "int32", "description": "id of stream"},
24
+ "adcbits": {"dtype": "int32", "description": "bit range of the adc channels"},
25
+ "sumlength": {"dtype": "int32", "description": "length of the fpga integrator"},
26
+ "blprecision": {"dtype": "int32", "description": "precision of the fpga baseline"},
27
+ "mastercards": {"dtype": "int32", "description": "number of attached mastercards"},
28
+ "triggercards": {
29
+ "dtype": "int32",
30
+ "description": "number of attached triggercards",
31
+ },
32
+ "adccards": {"dtype": "int32", "description": "number of attached fadccards"},
33
+ "gps": {
34
+ "dtype": "int32",
35
+ "description": "gps mode (0: not used, >0: external pps and 10MHz)",
36
+ },
37
+ "tracemap": {
38
+ "dtype": "uint32",
39
+ "datatype": "array<1>{array<1>{real}}",
40
+ "length": Limits.MaxChannels,
41
+ "description": "",
42
+ },
43
+ }
44
+
13
45
 
14
46
  class FCConfigDecoder(DataDecoder):
15
47
  """Decode FlashCam config data.
@@ -22,9 +54,9 @@ class FCConfigDecoder(DataDecoder):
22
54
 
23
55
  Example
24
56
  -------
25
- >>> import fcutils
26
- >>> from daq2lh5.fc.fc_config_decoder import FCConfigDecoder
27
- >>> fc = fcutils.fcio('file.fcio')
57
+ >>> from fcio import fcio_open
58
+ >>> from daq2lh5.fc.config_decoder import FCConfigDecoder
59
+ >>> fc = fcio_open('file.fcio')
28
60
  >>> decoder = FCConfigDecoder()
29
61
  >>> config = decoder.decode_config(fc)
30
62
  >>> type(config)
@@ -33,29 +65,68 @@ class FCConfigDecoder(DataDecoder):
33
65
 
34
66
  def __init__(self, *args, **kwargs) -> None:
35
67
  super().__init__(*args, **kwargs)
36
- self.config = lgdo.Struct()
37
-
38
- def decode_config(self, fcio: fcutils.fcio) -> lgdo.Struct:
39
- config_names = [
40
- "nsamples", # samples per channel
41
- "nadcs", # number of adc channels
42
- "ntriggers", # number of triggertraces
43
- "telid", # id of telescope
44
- "adcbits", # bit range of the adc channels
45
- "sumlength", # length of the fpga integrator
46
- "blprecision", # precision of the fpga baseline
47
- "mastercards", # number of attached mastercards
48
- "triggercards", # number of attached triggercards
49
- "adccards", # number of attached fadccards
50
- "gps", # gps mode (0: not used, 1: external pps and 10MHz)
51
- ]
52
- for name in config_names:
53
- if name in self.config:
54
- log.warning(f"{name} already in self.config. skipping...")
68
+ self.decoded_values = copy.deepcopy(fc_config_decoded_values)
69
+
70
+ def decode_packet(
71
+ self,
72
+ fcio: FCIO,
73
+ config_rb: lgdo.Table,
74
+ packet_id: int,
75
+ ) -> bool:
76
+
77
+ tbl = config_rb.lgdo
78
+ loc = config_rb.loc
79
+
80
+ tbl["packet_id"].nda[loc] = packet_id
81
+
82
+ tbl["nsamples"].nda[loc] = fcio.config.eventsamples
83
+ tbl["nadcs"].nda[loc] = fcio.config.adcs
84
+ tbl["ntriggers"].nda[loc] = fcio.config.triggers
85
+ tbl["streamid"].nda[loc] = fcio.config.streamid
86
+ tbl["adcbits"].nda[loc] = fcio.config.adcbits
87
+ tbl["sumlength"].nda[loc] = fcio.config.sumlength
88
+ tbl["blprecision"].nda[loc] = fcio.config.blprecision
89
+ tbl["mastercards"].nda[loc] = fcio.config.mastercards
90
+ tbl["triggercards"].nda[loc] = fcio.config.triggercards
91
+ tbl["adccards"].nda[loc] = fcio.config.adccards
92
+ tbl["gps"].nda[loc] = fcio.config.gps
93
+ ntraces = fcio.config.adcs + fcio.config.triggers
94
+ tbl["tracemap"]._set_vector_unsafe(loc, fcio.config.tracemap[:ntraces])
95
+
96
+ config_rb.loc += 1
97
+
98
+ return config_rb.is_full()
99
+
100
+ def decode_config(self, fcio: FCIO) -> lgdo.Struct:
101
+
102
+ tbl = lgdo.Struct()
103
+
104
+ fcio_attr_names_map = {
105
+ "nsamples": "eventsamples",
106
+ "nadcs": "adcs",
107
+ "ntriggers": "triggers",
108
+ "streamid": "streamid",
109
+ "adcbits": "adcbits",
110
+ "sumlength": "sumlength",
111
+ "blprecision": "blprecision",
112
+ "mastercards": "mastercards",
113
+ "triggercards": "triggercards",
114
+ "adccards": "adccards",
115
+ "gps": "gps",
116
+ }
117
+
118
+ for name, fcio_attr_name in fcio_attr_names_map.items():
119
+ if name in tbl:
120
+ log.warning(f"{name} already in tbl. skipping...")
55
121
  continue
56
- value = np.int32(getattr(fcio, name)) # all config fields are int32
57
- self.config.add_field(name, lgdo.Scalar(value))
58
- return self.config
122
+ value = np.int32(
123
+ getattr(fcio.config, fcio_attr_name)
124
+ ) # all config fields are int32
125
+ tbl.add_field(name, lgdo.Scalar(value))
126
+ ntraces = fcio.config.adcs + fcio.config.triggers
127
+ tbl.add_field("tracemap", lgdo.Array(fcio.config.tracemap[:ntraces]))
128
+
129
+ return tbl
59
130
 
60
- def make_lgdo(self, key: int = None, size: int = None) -> lgdo.Struct:
61
- return self.config
131
+ def get_decoded_values(self, key: int | str = None) -> dict[str, dict[str, Any]]:
132
+ return self.decoded_values