Skip to content

Commit

Permalink
bug fix; adc readout now depends on matrix size and dwell time
Browse files Browse the repository at this point in the history
  • Loading branch information
mcencini committed Oct 23, 2024
1 parent fa05e59 commit 7e7fd16
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 16 deletions.
25 changes: 18 additions & 7 deletions src/pulserver/_core/_ceq.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,34 +297,38 @@ def __init__(

# build CEQ structure
self.n_parent_blocks = len(parent_blocks)
self.n_segments = len(segments)
self.parent_blocks = parent_blocks
self.n_segments = len(segments)
self.segments = segments
self.n_max = loop.shape[0]
self.n_columns_in_loop_array = loop.shape[1] - 2 # discard "hasrot", "hasadc"
self.n_readouts = int(np.sum(loop[:, -2]))
self.loop = loop[:, :-2]

# Safety, RF scaling and scan duration info
self.max_power = 0.0
self.max_rf_power = 0.0
self.max_b1 = _find_b1_max(parent_blocks)
self.max_grad = _find_grad_max(parent_blocks)
self.max_slew = _find_slew_max(parent_blocks)
self.duration = _calc_duration(self.loop[:, 0], self.loop[:, 9])
self.n_readouts = int(np.sum(loop[:, -2]))

def to_bytes(self, endian=">") -> bytes: # noqa
bytes_data = struct.pack(endian + "h", self.n_parent_blocks) + struct.pack(
endian + "h", self.n_segments
)
bytes_data = struct.pack(endian + "h", self.n_parent_blocks)
for block in self.parent_blocks:
bytes_data += block.to_bytes(endian)
bytes_data += struct.pack(endian + "h", self.n_segments)
for segment in self.segments:
bytes_data += segment.to_bytes(endian)
bytes_data += struct.pack(endian + "i", self.n_max)
bytes_data += struct.pack(endian + "h", self.n_columns_in_loop_array)
bytes_data += struct.pack(endian + "i", self.n_readouts)
bytes_data += self.loop.astype(endian + "f4").tobytes()

bytes_data += struct.pack(endian + "f", self.max_rf_power)
bytes_data += struct.pack(endian + "f", self.max_b1)
bytes_data += struct.pack(endian + "f", self.max_grad)
bytes_data += struct.pack(endian + "f", self.max_slew)
bytes_data += struct.pack(endian + "f", self.duration)
bytes_data += struct.pack(endian + "i", self.n_readouts)

return bytes_data

Expand Down Expand Up @@ -385,6 +389,13 @@ def _find_b1_max(parent_blocks):
[block.rf.wav.amplitude for block in parent_blocks if block.rf is not None]
)

# TODO: implement this
def _find_grad_max(parent_blocks):
return 0.0

# TODO: implement this
def _find_slew_max(parent_blocks):
return 0.0

def _calc_duration(segment_id, block_duration):
block_duration = block_duration.sum()
Expand Down
4 changes: 2 additions & 2 deletions src/pulserver/_core/_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,8 @@ def build(self):
self._loop,
self._sections_edges,
)
P = compute_max_energy(self._sequence, self._system)
self._sequence.max_power = P
P = compute_max_energy(deepcopy(self._sequence), self._system)
self._sequence.max_rf_power = P

if self._header is not None:
return self._sequence, self._header
Expand Down
15 changes: 11 additions & 4 deletions src/pulserver/blocks/_readout/_make_line_readout.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

__all__ = ["make_line_readout"]


import numpy as np
import pypulseq as pp


Expand All @@ -11,8 +13,8 @@ def make_line_readout(
npix: int,
osf: float = 1.0,
has_adc: bool = True,
adc_duration: float = 3.0e-3,
flyback: bool = False,
dwell: float | None = None,
ndim: int = 1,
) -> tuple[dict, dict] | tuple[dict, dict, dict]:
"""
Expand All @@ -32,8 +34,6 @@ def make_line_readout(
If ``True``, ``read_block`` contains and ADC event.
Otherwise, it does not (e.g., dummy shots for steady-state preparation).
The default is ``True``.
adc_duration : float, optional
ADC window duration in ``[s]``. The default is '`3.0e-3``.
flyback : bool, optional
If ``True``, design a flyback gradient. The default is ``False``.
ndim : int, optional
Expand Down Expand Up @@ -92,10 +92,17 @@ def make_line_readout(
fov *= 1e-3

# apply oversampling
npix = osf * npix
npix = int(np.ceil(osf * npix))

# k space density
dk = 1 / fov

# get dwell
if dwell is None:
dwell = system.grad_raster_time

# calculate duration
adc_duration = npix * dwell

# frequency encoding gradients
gread = pp.make_trapezoid(
Expand Down
6 changes: 3 additions & 3 deletions src/pulserver/sequences/_design_2D_spgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ def design_2D_spgr(
15-degree flip angle and hardware limits 40 mT/m, 150 T/m/s, 4e-6 s raster time as:
>>> from pulserver.sequences import design_2D_spgr
>>> opts_dict = {"max_grad": 40, "max_slew": 150, "grad_raster_time": 4e-6, , "rf_raster_time": 4e-6}
>>> opts_dict = {"max_grad": 40, "max_slew": 150, "grad_raster_time": 4e-6, "rf_raster_time": 4e-6}
Actual design:
>>> design_2D_spgr(240.0, 5.0, 256, 1, 15.0, opts_dict)
>>> design_2D_spgr(240.0, 5.0, 256, 1, 15.0, opts_dict=opts_dict=opts_dict)
Generate the same sequence and export it in GEHC format:
>>> design_2D_spgr(240.0, 5.0, 256, 1, 15.0, opts_dict, platform='gehc')
>>> design_2D_spgr(240.0, 5.0, 256, 1, 15.0, opts_dict=opts_dict=opts_dict, platform='gehc')
"""
# Sequence Parameters
Expand Down

0 comments on commit 7e7fd16

Please sign in to comment.