Skip to content
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
11 changes: 3 additions & 8 deletions src/pypulseq/Sequence/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import numpy as np

from pypulseq import eps
from pypulseq.block_to_events import block_to_events
from pypulseq.compress_shape import compress_shape
from pypulseq.decompress_shape import decompress_shape
Expand Down Expand Up @@ -148,10 +147,7 @@ def set_block(self, block_index: int, *args: SimpleNamespace) -> None:
else:
event_id = register_rotation_event(self, event)

ext = {
'type': self.get_extension_type_ID('ROTATIONS'),
'ref': event_id
}
ext = {'type': self.get_extension_type_ID('ROTATIONS'), 'ref': event_id}
extensions.append(ext)
elif event.type in ['labelset', 'labelinc']:
if hasattr(event, 'id'):
Expand Down Expand Up @@ -205,7 +201,6 @@ def set_block(self, block_index: int, *args: SimpleNamespace) -> None:
# PERFORM GRADIENT CHECKS
# =========
grad_check(self, block_index, check_g, duration, rot_event)


self.block_events[block_index] = new_block
self.block_durations[block_index] = float(duration)
Expand Down Expand Up @@ -353,10 +348,10 @@ def get_block(self, block_index: int) -> SimpleNamespace:
block.trigger[len(block.trigger)] = trigger
else:
block.trigger = {0: trigger}
elif ext_type == "ROTATIONS":
elif ext_type == 'ROTATIONS':
data = self.rotation_library.data[ext_data[1]]
rotation = SimpleNamespace()
rotation.type = "rotation"
rotation.type = 'rotation'
rotation.rot_matrix = np.asarray(data).reshape(3, 3)
block.rotation = rotation
elif ext_type in ['LABELSET', 'LABELINC']:
Expand Down
129 changes: 43 additions & 86 deletions src/pypulseq/Sequence/grad_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,10 @@ def grad_check_norot(self, block_index, check_g, duration):
"""
for grad_to_check in check_g.values():
if (
abs(grad_to_check.start[1])
> self.system.max_slew * self.system.grad_raster_time
): # noqa: SIM102
if grad_to_check.start[0] > eps:
raise RuntimeError(
"No delay allowed for gradients which start with a non-zero amplitude"
)
abs(grad_to_check.start[1]) > self.system.max_slew * self.system.grad_raster_time
and grad_to_check.start[0] > eps
):
raise RuntimeError('No delay allowed for gradients which start with a non-zero amplitude')

# Check whether any blocks exist in the sequence
if self.next_free_block_ID > 1:
Expand All @@ -86,9 +83,7 @@ def grad_check_norot(self, block_index, check_g, duration):
# Existing block overwritten
idx = blocks.index(block_index)
prev_block_index = blocks[idx - 1] if idx > 0 else None
next_block_index = (
blocks[idx + 1] if idx < len(blocks) - 1 else None
)
next_block_index = blocks[idx + 1] if idx < len(blocks) - 1 else None
except ValueError:
# Inserting a new block with non-contiguous numbering
prev_block_index = next(reversed(self.block_events))
Expand All @@ -100,66 +95,50 @@ def grad_check_norot(self, block_index, check_g, duration):
prev_id = self.block_events[prev_block_index][grad_to_check.idx]
if prev_id != 0:
prev_lib = self.grad_library.get(prev_id)
prev_type = prev_lib["type"]
prev_type = prev_lib['type']

if prev_type == "t":
if prev_type == 't':
last = 0
elif prev_type == "g":
last = prev_lib["data"][5]
elif prev_type == 'g':
last = prev_lib['data'][5]

# Check whether the difference between the last gradient value and
# the first value of the new gradient is achievable with the
# specified slew rate.
if (
abs(last - grad_to_check.start[1])
> self.system.max_slew * self.system.grad_raster_time
):
raise RuntimeError(
"Two consecutive gradients need to have the same amplitude at the connection point"
)
if abs(last - grad_to_check.start[1]) > self.system.max_slew * self.system.grad_raster_time:
raise RuntimeError('Two consecutive gradients need to have the same amplitude at the connection point')

# Look up the first gradient value in the next block
# (this only happens when using set_block to patch a block)
if next_block_index is not None:
next_id = self.block_events[next_block_index][grad_to_check.idx]
if next_id != 0:
next_lib = self.grad_library.get(next_id)
next_type = next_lib["type"]
next_type = next_lib['type']

if next_type == "t":
if next_type == 't':
first = 0
elif next_type == "g":
first = next_lib["data"][4]
elif next_type == 'g':
first = next_lib['data'][4]
else:
first = 0

# Check whether the difference between the first gradient value
# in the next block and the last value of the new gradient is
# achievable with the specified slew rate.
if (
abs(first - grad_to_check.stop[1])
> self.system.max_slew * self.system.grad_raster_time
):
if abs(first - grad_to_check.stop[1]) > self.system.max_slew * self.system.grad_raster_time:
raise RuntimeError(
"Two consecutive gradients need to have the same amplitude at the connection point"
'Two consecutive gradients need to have the same amplitude at the connection point'
)
elif (
abs(grad_to_check.start[1])
> self.system.max_slew * self.system.grad_raster_time
):
raise RuntimeError(
"First gradient in the the first block has to start at 0."
)
elif abs(grad_to_check.start[1]) > self.system.max_slew * self.system.grad_raster_time:
raise RuntimeError('First gradient in the the first block has to start at 0.')

# Check if gradients, which do not end at 0, are as long as the block itself.
if (
abs(grad_to_check.stop[1])
> self.system.max_slew * self.system.grad_raster_time
abs(grad_to_check.stop[1]) > self.system.max_slew * self.system.grad_raster_time
and abs(grad_to_check.stop[0] - duration) > 1e-7
):
raise RuntimeError(
"A gradient that doesn't end at zero needs to be aligned to the block boundary."
)
raise RuntimeError("A gradient that doesn't end at zero needs to be aligned to the block boundary.")


def grad_check_rot(self, block_index, check_g, duration, rot_event):
Expand Down Expand Up @@ -193,13 +172,10 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
for grad_to_check in check_g.values():
# Check beginning of gradient event
if (
abs(grad_to_check.start[1])
> self.system.max_slew * self.system.grad_raster_time
): # noqa: SIM102
if grad_to_check.start[0] > eps:
raise RuntimeError(
"No delay allowed for gradients which start with a non-zero amplitude"
)
abs(grad_to_check.start[1]) > self.system.max_slew * self.system.grad_raster_time
and grad_to_check.start[0] > eps
):
raise RuntimeError('No delay allowed for gradients which start with a non-zero amplitude')

# Check whether any blocks exist in the sequence
if self.next_free_block_ID > 1:
Expand All @@ -208,7 +184,7 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
next_has_rot = False

# Rotation extension ID
rot_type_id = self.get_extension_type_ID("ROTATIONS")
rot_type_id = self.get_extension_type_ID('ROTATIONS')

# Get indexes of previous and next blocks
if block_index == self.next_free_block_ID:
Expand All @@ -235,13 +211,13 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
prev_id = self.block_events[prev_block_index][grad_to_check.idx]
if prev_id != 0:
prev_lib = self.grad_library.get(prev_id)
prev_type = prev_lib["type"]
prev_type = prev_lib['type']

# Trapezoids end with zeros,
# so we cannot have the same amplitude
# as the initial value of current block
if prev_type == "g":
prev_grad_last[grad_to_check.idx - 2] = prev_lib["data"][5]
if prev_type == 'g':
prev_grad_last[grad_to_check.idx - 2] = prev_lib['data'][5]

# Get previous block rotation matrix
ext_id = self.block_events[prev_block_index][-1]
Expand All @@ -250,9 +226,7 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
ext = self.extensions_library.data.get(ext_id)
if ext[0] == rot_type_id:
previous_has_rot = True
previous_rotmat = np.asarray(
self.rotation_library.data.get(ext[1])
).reshape((3, 3))
previous_rotmat = np.asarray(self.rotation_library.data.get(ext[1])).reshape((3, 3))
else:
ext_id = ext[-1]
except KeyError:
Expand All @@ -272,30 +246,26 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
curr_grad_first = rot_event.rot_matrix @ curr_grad_first

# Compare current block with previous
if any(
abs(curr_grad_first - prev_grad_last)
> self.system.max_slew * self.system.grad_raster_time
):
if any(abs(curr_grad_first - prev_grad_last) > self.system.max_slew * self.system.grad_raster_time):
raise RuntimeError(
f"Error in block {block_index}: Two consecutive gradients need to have the same amplitude at the connection point."
f'Error in block {block_index}: Two consecutive gradients need to have the same amplitude at the connection point.'
)

# 2) Comparison with next block
if next_block_index is not None:

# Look up the last gradient value in the previous block
next_grad_first = np.zeros(3, dtype=np.float32)
for grad_to_check in check_g.values():
next_id = self.block_events[next_block_index][grad_to_check.idx]
if next_id != 0:
next_lib = self.grad_library.get(next_id)
next_type = next_lib["type"]
next_type = next_lib['type']

# Trapezoids start with zeros,
# so we cannot have the same amplitude
# as the final value of current block
if next_type == "g":
next_grad_first[grad_to_check.idx - 2] = next_lib["data"][4]
if next_type == 'g':
next_grad_first[grad_to_check.idx - 2] = next_lib['data'][4]

# Get next block rotation matrix
ext_id = self.block_events[next_block_index][-1]
Expand All @@ -304,9 +274,7 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
ext = self.extensions_library.data.get(ext_id)
if ext[0] == rot_type_id:
next_has_rot = True
next_rotmat = np.asarray(
self.rotation_library.data.get(ext[1])
).reshape((3, 3))
next_rotmat = np.asarray(self.rotation_library.data.get(ext[1])).reshape((3, 3))
else:
ext_id = ext[-1]
except KeyError:
Expand All @@ -326,31 +294,20 @@ def grad_check_rot(self, block_index, check_g, duration, rot_event):
curr_grad_last = rot_event.rot_matrix @ curr_grad_last

# Compare current block with next
if any(
abs(curr_grad_last - next_grad_first)
> self.system.max_slew * self.system.grad_raster_time
):
if any(abs(curr_grad_last - next_grad_first) > self.system.max_slew * self.system.grad_raster_time):
raise RuntimeError(
f"Error in block {block_index}: Two consecutive gradients need to have the same amplitude at the connection point."
f'Error in block {block_index}: Two consecutive gradients need to have the same amplitude at the connection point.'
)
else:
for grad_to_check in check_g.values():
# Check beginning of gradient event
if (
abs(grad_to_check.start[1])
> self.system.max_slew * self.system.grad_raster_time
):
raise RuntimeError(
"First gradient in the the first block has to start at 0."
)
if abs(grad_to_check.start[1]) > self.system.max_slew * self.system.grad_raster_time:
raise RuntimeError('First gradient in the the first block has to start at 0.')

# Check if gradients, which do not end at 0, are as long as the block itself.
for grad_to_check in check_g.values():
if (
abs(grad_to_check.stop[1])
> self.system.max_slew * self.system.grad_raster_time
abs(grad_to_check.stop[1]) > self.system.max_slew * self.system.grad_raster_time
and abs(grad_to_check.stop[0] - duration) > 1e-7
):
if abs(grad_to_check.stop[0] - duration) > 1e-7:
raise RuntimeError(
"A gradient that doesn't end at zero needs to be aligned to the block boundary."
)
raise RuntimeError("A gradient that doesn't end at zero needs to be aligned to the block boundary.")
4 changes: 2 additions & 2 deletions src/pypulseq/Sequence/read_seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ def read(self, path: str, detect_rf_use: bool = False, remove_duplicates: bool =
extension_id = int(section[18:])
self.set_extension_string_ID('TRIGGERS', extension_id)
self.trigger_library = __read_events(input_file, (1, 1, 1e-6, 1e-6), event_library=self.trigger_library)
elif section[:19] == "extension ROTATIONS":
elif section[:19] == 'extension ROTATIONS':
extension_id = int(section[19:])
self.set_extension_string_ID("ROTATIONS", extension_id)
self.set_extension_string_ID('ROTATIONS', extension_id)
self.rotation_library = __read_events(
input_file, (1, 1, 1, 1, 1, 1, 1, 1, 1), event_library=self.rotation_library
)
Expand Down
Loading
Loading