Skip to content
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

Fixed read_seq for reading v1.3.1 sequence files #162

Merged
merged 2 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions pypulseq/Sequence/read_seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,11 @@ def read(self, path: str, detect_rf_use: bool = False) -> None:
if version_combined < 1004000:
# Scan through RF objects
for i in self.rf_library.data:
self.rf_library.data[i] = [
*self.rf_library.data[i][:3],
0,
*self.rf_library.data[i][3:],
]
self.rf_library.lengths[i] += 1
self.rf_library.update(i, None, (
*self.rf_library.data[i][:3],
0,
*self.rf_library.data[i][3:]
))

# Scan through the gradient objects and update 't'-s (trapezoids) und 'g'-s (free-shape gradients)
for i in self.grad_library.data:
Expand All @@ -231,36 +230,37 @@ def read(self, path: str, detect_rf_use: bool = False) -> None:
abs(self.grad_library.data[i][0]) == 0
and self.grad_library.data[i][2] > 0
):
self.grad_library.data[i][2] -= self.grad_raster_time
self.grad_library.data[i][1] = self.grad_raster_time
d = self.grad_library.data[i]
self.grad_library.update(i, None, (d[0], self.grad_raster_time, d[2] - self.grad_raster_time) + d[3:], self.grad_library.type[i])

if self.grad_library.data[i][3] == 0:
if (
abs(self.grad_library.data[i][0]) == 0
and self.grad_library.data[i][2] > 0
):
self.grad_library.data[i][2] -= self.grad_raster_time
self.grad_library.data[i][3] = self.grad_raster_time
d = self.grad_library.data[i]
self.grad_library.update(i, None, d[:2] + (d[2] - self.grad_raster_time, self.grad_raster_time) + d[4:], self.grad_library.type[i])

if self.grad_library.type[i] == "g":
self.grad_library.data[i] = [
self.grad_library.update(i, None, (
self.grad_library.data[i][:2],
0,
self.grad_library.data[i][2:],
]
self.grad_library.lengths[i] += 1
), self.grad_library.type[i])

# For versions prior to 1.4.0 block_durations have not been initialized
self.block_durations = dict()
# Scan through blocks and calculate durations
for block_counter in self.block_events:
block = self.get_block(block_counter)
# Insert delay as temporary block_duration
self.block_durations[block_counter] = 0
if delay_ind_temp[block_counter] > 0:
block.delay = SimpleNamespace()
block.delay.type = "delay"
block.delay.delay = temp_delay_library.data[
delay_ind_temp[block_counter]
]
self.block_durations[block_counter] = temp_delay_library.data[
delay_ind_temp[block_counter]
][0]
Comment on lines 257 to +260
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is needed. In the old format, delay events were explicitly defined in the event table. In the new format, only the block duration is stored in the event table. However, to calculate the duration, we need the maximum of the stored delay and the actual duration of the block events (determined with calc_duration). get_block gives us the stored block duration (where you previously got an error), i.e. the delay, which is used by calc_duration (but the output of calc_duration might be larger depending on the events).

I do feel the whole handling of the delay event is a bit messy in the new format. We define a delay event with make_delay, and can use it in calc_duration. But as soon as we add it to the sequence, the delay event disappears. Instead, get_block will just give us a block_duration field, which is a special case in calc_duration (isinstance(event, (float, int))). As a consequence, the only reason calc_duration can determine the duration in this case, is because the duration was already stored.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for the nice explanation. Wasn't aware that the delay event is gone after adding it to a block.


block = self.get_block(block_counter)
# Calculate actual block duration
self.block_durations[block_counter] = calc_duration(block)

# TODO: Is it possible to avoid expensive get_block calls here?
Expand Down Expand Up @@ -473,7 +473,7 @@ def __read_events(
input_file,
scale: tuple = (1,),
event_type: str = str(),
event_library: EventLibrary = EventLibrary(),
event_library: EventLibrary = None,
append=None
) -> EventLibrary:
"""
Expand All @@ -495,6 +495,9 @@ def __read_events(
event_library : EventLibrary
Event library containing Pulseq events.
"""

if event_library is None:
event_library = EventLibrary()
line = __strip_line(input_file)

while line != "" and line != "#":
Expand Down
1 change: 0 additions & 1 deletion pypulseq/event_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ class EventLibrary:

Sequence Properties:
- data - A struct array with field 'array' to store data of varying lengths, remaining compatible with codegen.
- lengths - Corresponding lengths of the data arrays
- type - Type to distinguish events in the same class (e.g. trapezoids and arbitrary gradients)

Sequence Methods:
Expand Down