-
Notifications
You must be signed in to change notification settings - Fork 8
Make WD2 processing lazy #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
14e623d
808625e
af59f09
c279542
fdd1711
6e53e1f
bfcc2e9
58b20fb
1718bba
cd3e9fb
f0bef21
bd63930
5262429
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,4 +8,4 @@ save_path = '/path/to/file.h5' | |
| [optional] | ||
|
|
||
| overwrite = True | ||
| counts = -1 | ||
| print_mod = -1 | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,8 +13,11 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import BinaryIO | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Generic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import List | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Generator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # imports start from MULE/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from packs.core.core_utils import flatten | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -196,9 +199,9 @@ def process_header(file_path : str, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # open file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not os.path.exists(file_path): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise FileNotFoundError(2, 'Path or file not found', file_path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise FileNotFoundError(2, 'Path or file not found', file_path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(file_path, 'rb') as file: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(file_path, 'rb') as file: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| event_number, timestamp, samples, sampling_period = read_defaults_WD2(file, byte_order) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # attempt to read channels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -231,6 +234,41 @@ def process_header(file_path : str, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return wdtype, samples, sampling_period, channels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def read_binary_lazy(file : BinaryIO, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wdtype : np.dtype) -> Generator: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ''' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Reads the binary in with the expected format/offset, lazily, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| depending on counts to break the data up. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NOTE: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The counts are hardset to 1, making this function relatively inefficient. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| In the future, the logic should be revised to allow `np.fromfile`'s count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value to be set based on optimal read-in speed. The logic of the WD2 function | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| will have to accomodate this when indexing the files. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Parameters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ---------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| file (BufferedReader) : Opened file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wdtype (ndtype) : Custom data type for extracting information from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binary files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data (ndarray) : Unformatted data from binary file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+240
to
+259
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Reads the binary in with the expected format/offset, lazily, | |
| depending on counts to break the data up. | |
| NOTE: | |
| The counts are hardset to 1, making this function relatively inefficient. | |
| In the future, the logic should be revised to allow `np.fromfile`'s count | |
| value to be set based on optimal read-in speed. The logic of the WD2 function | |
| will have to accomodate this when indexing the files. | |
| Parameters | |
| ---------- | |
| file (BufferedReader) : Opened file | |
| wdtype (ndtype) : Custom data type for extracting information from | |
| binary files | |
| counts (int) : How many events you want to read in. -1 sets it to take all events. | |
| offset (int) : Offset at which to start reading the data. Used for chunking purposes | |
| and so should by default be set to zero if not chunking. | |
| Returns | |
| ------- | |
| data (ndarray) : Unformatted data from binary file | |
| Lazily read binary data using the provided numpy dtype. | |
| This function repeatedly calls ``np.fromfile`` with ``count=1`` to read | |
| one record at a time from the open binary file and yields each record | |
| as it is read. This makes the function simple to use in streaming | |
| contexts but relatively inefficient due to the small fixed read size. | |
| NOTE: | |
| The read size is currently hard-set to 1 element per call to | |
| ``np.fromfile``. In the future, the logic may be revised to allow the | |
| ``count`` value to be tuned for optimal read-in speed; any calling code | |
| that indexes into the resulting data will need to accommodate such | |
| changes. | |
| Parameters | |
| ---------- | |
| file (BufferedReader) : Opened binary file object. | |
| wdtype (numpy.dtype) : Custom data type for extracting information | |
| from binary files. | |
| Returns | |
| ------- | |
| Generator[Tuple[bool, numpy.ndarray], None, None] | |
| A generator yielding tuples of the form ``(has_data, data)``: | |
| * ``has_data`` is ``True`` while records are being read, and | |
| ``False`` once processing has finished. | |
| * ``data`` is a numpy array containing the record(s) read by the | |
| corresponding call to ``np.fromfile``. |
Copilot
AI
Apr 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read_binary_lazy prints "Processing Finished!" unconditionally and yields a final (False, zeros) sentinel. This introduces side effects even when callers set print_mod=-1 and makes the generator harder to consume (callers must remember to ignore the last value). Prefer ending the generator with return/StopIteration and let the caller decide whether/when to print completion.
| # yield 1 when finished | |
| print('Processing Finished!') | |
| yield (False, np.zeros(shape = (1,))) | |
| # when no more data is available, the generator stops naturally | |
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need to keep both process_bin_WD2_lazy and process_bin_WD2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theres no reason to remove the eager methods yet, as it will be 'faster' than the lazy method. Once implementation of 'chunks' in the lazy method is done, we can remove process_bin_WD2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of opening the file twice, can process_header be changed to fit after:
# open file for reading
with open(file_path, 'rb') as file:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to try this soon.
Copilot
AI
Apr 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The print_mod logic uses i % print_mod before validating print_mod. If a user sets print_mod=0, this will raise ZeroDivisionError. Consider validating print_mod (e.g., require -1 or a positive int) before entering the loop, or treat 0 as disabled printing.
| if (i % print_mod == 0) and (print_mod != -1): | |
| if print_mod not in (-1, 0) and (i % print_mod == 0): |
Copilot
AI
Apr 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
evt_info returned by format_wfs() is a length-1 ndarray when reading lazily (count=1). In fixed-size mode, writer.write() assigns a single row at index, so passing an ndarray here can raise a shape/type error. Write the scalar record instead (e.g., evt_info[0]) to match how process_bin_WD1 passes a single structured row.
| write('event_info', evt_info, (True, num_of_events, i)) | |
| # In lazy mode, format_wfs can return a length-1 ndarray; unwrap to a single record | |
| if isinstance(evt_info, np.ndarray) and getattr(evt_info, "shape", ()) and evt_info.shape[0] == 1: | |
| evt_row = evt_info[0] | |
| else: | |
| evt_row = evt_info | |
| write('event_info', evt_row, (True, num_of_events, i)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would perhaps explain here, or in another page how lazy reading and lazy writing works in mule
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It uses a generator (read_binary_lazy() for WD2) that yields raw arrays from the binary file, and then reformats them and writes them to a file. I can add more comments if thats what you wish :)
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for completeness, parameters could be added to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its done as |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,9 +19,11 @@ | |
| from packs.proc.processing_utils import read_defaults_WD2 | ||
| from packs.proc.processing_utils import process_header | ||
| from packs.proc.processing_utils import read_binary | ||
| from packs.proc.processing_utils import read_binary_lazy | ||
| from packs.proc.processing_utils import format_wfs | ||
| from packs.proc.processing_utils import check_save_path | ||
| from packs.proc.processing_utils import save_data | ||
| from packs.proc.processing_utils import number_of_events_WD2 | ||
|
|
||
| from packs.types.types import generate_wfdtype | ||
| from packs.types.types import rwf_type | ||
|
|
@@ -64,7 +66,7 @@ def test_header_components_read_as_expected(wd2_3ch_bin): | |
|
|
||
|
|
||
| def test_nonexistent_file_raises_error(): | ||
|
|
||
| fake_path = '/this/path/does/not/exist.bin' | ||
|
|
||
| with raises(FileNotFoundError): | ||
|
|
@@ -152,7 +154,7 @@ def test_formatting_works(data_dir, wd2_3ch_bin): | |
| def test_save_path_exists(): | ||
|
|
||
| data_path = 'some/fake/path/three_channels_WD2.h5' | ||
|
|
||
| with raises(FileNotFoundError): | ||
| check_save_path(data_path, overwrite = False) | ||
|
|
||
|
|
@@ -276,4 +278,43 @@ def test_lazy_loading_short_header_WD1(MULE_dir): | |
| a = process_event_lazy_WD1(file) | ||
| next(a) | ||
|
|
||
| @mark.parametrize("file, samples, channels, header_size, output", [('100bytes.bin', 1, 1, 0, 25), ('100bytes.bin', 1, 1, 46, 2), ('100bytes.bin', 2, 10, 20, 1), ('10000bytes.bin', 4, 8, 72, 50)]) | ||
| def test_number_of_events_correct(data_dir, file, samples, channels, header_size, output): | ||
| ''' | ||
| Simple test to ensure the logic returns the number of events we expect. | ||
| ''' | ||
| file_path = data_dir + file | ||
|
|
||
| assert output == number_of_events_WD2(file_path, samples, channels, header_size) | ||
|
|
||
|
|
||
| @mark.parametrize("inpt", [("one_channel_WD2.bin"),("three_channels_WD2.bin")]) | ||
| def test_lazy_eager_WD2_match(data_dir, inpt): | ||
| ''' | ||
| test to ensure that lazy and eager WD2 | ||
| provide the same result | ||
| ''' | ||
|
|
||
| # how many events are we looking at? | ||
| counts = 30 | ||
|
|
||
| # extract directory | ||
| file_path = data_dir + inpt | ||
|
|
||
| # collect header info | ||
| wdtype, samples, sampling_period, channels = process_header(file_path) | ||
|
|
||
| # collect lazy data | ||
| lazy_data = [] | ||
| with open(file_path) as f: | ||
| binary_lazy_readout = read_binary_lazy(f, wdtype) | ||
| for i in range(0,counts): | ||
| _, lazy_wf = next(binary_lazy_readout) | ||
| lazy_data.append(lazy_wf) | ||
|
|
||
| # open eager data | ||
| with open(file_path) as f: | ||
| data = read_binary (f, wdtype, counts) | ||
|
|
||
| for i in range(0,counts): | ||
| assert data[i] == lazy_data[i] | ||
|
Comment on lines
+308
to
+320
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has an issue already been opened for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but one should be opened after this review. I believe WD1 has the same issue.