PyPulseq compatibility#
A PyPulseq script runs once you change its import:
import pypulseqpp as pp # in place of: import pypulseq as pp
The event factories (make_sinc_pulse, make_trapezoid, make_adc,
make_delay, make_label), calc_duration, Opts and the Sequence methods
(add_block, check_timing, test_report, calculate_kspace, plot,
write, read) keep PyPulseq’s signatures and units. The
API reference lists what is implemented.
Sequence functions#
To reuse the script, you turn it into a sequence function: the script with the
system and the parameters as arguments. The first parameter is system, the
keyword parameters after it are the protocol, and the function returns the
sequence, or a list of sequences with the prescans first and the main sequence
last:
PyPulseq script |
Sequence function |
|---|---|
|
the |
parameters at the top |
keyword parameters after |
events built before the loop, |
the body of the function |
|
|
|
unchanged, before the function returns |
|
|
write_gre.py, at a 64 matrix:
>>> import numpy as np
>>> import pypulseqpp as pp
>>> from pypulseqpp import cli, sequences
>>> def gre(system, *, fov: float = 256e-3, n: int = 64, te: float = 4.3e-3,
... tr: float = 10e-3) -> pp.Sequence:
... """Gradient echo with RF spoiling.
...
... Parameters
... ----------
... fov : float, default=0.256
... Field of view (m).
... n : int, default=64
... Matrix size.
... te : float, default=0.0043
... Echo time (s).
... tr : float, default=0.01
... Repetition time (s).
... """
... system = pp.cap_system(system, max_grad=28.0, max_slew=150.0)
... dk = 1 / fov
... rf, gz, _ = pp.make_sinc_pulse(
... flip_angle=np.deg2rad(10), duration=3e-3, slice_thickness=3e-3,
... apodization=0.5, time_bw_product=4, system=system, return_gz=True)
... gx = pp.make_trapezoid("x", flat_area=n * dk, flat_time=3.2e-3, system=system)
... adc = pp.make_adc(n, duration=gx.flat_time, delay=gx.rise_time, system=system)
... gx_pre = pp.make_trapezoid("x", area=-gx.area / 2, duration=1e-3, system=system)
... gz_reph = pp.make_trapezoid("z", area=-gz.area / 2, duration=1e-3, system=system)
... gx_spoil = pp.make_trapezoid("x", area=2 * n * dk, system=system)
... gz_spoil = pp.make_trapezoid("z", area=4 / 3e-3, system=system)
... delay_te = pp.round_to_raster(
... te - pp.calc_duration(gx_pre) - gz.fall_time
... - gz.flat_time / 2 - pp.calc_duration(gx) / 2, system.grad_raster_time)
... delay_tr = pp.round_to_raster(
... tr - pp.calc_duration(gz) - pp.calc_duration(gx_pre)
... - pp.calc_duration(gx) - delay_te, system.grad_raster_time)
... seq, labels, phase = pp.Sequence(system), sequences.Labels(), 0.0
... for line in range(n):
... phase += np.deg2rad(117) * line
... rf.phase_offset = adc.phase_offset = phase % (2 * np.pi)
... gy_pre = pp.make_trapezoid("y", area=(line - n / 2) * dk, duration=1e-3,
... system=system)
... seq.add_block(rf, gz)
... seq.add_block(gx_pre, gy_pre, gz_reph)
... seq.add_block(pp.make_delay(delay_te))
... seq.add_block(gx, adc, *labels(LIN=line))
... seq.add_block(pp.make_delay(delay_tr), gx_spoil,
... pp.scale_grad(gy_pre, -1), gz_spoil)
... return seq
>>> seq = gre(pp.Opts(), te=5e-3)
>>> seq.check_timing()[0]
True
labels(LIN=line) returns the event that sets the line counter a
reconstruction places the readout by, and nothing for a value that has not
changed; pp.make_label does the same as in PyPulseq.
write() writes the sequence, or a list of sequences
as linked files. pypulseqpp.cli.run() derives one command-line option from
each keyword parameter, with the help text from the Parameters section,
builds system from --max-grad-mtm and --max-slew-tm-s, and writes the
result in the same way:
>>> sequences.write("gre.seq", seq)
['gre.seq']
>>> cli.run(gre, ["-o", "gre_32.seq", "--n", "32", "--te", "0.005"])
Wrote sequence: gre_32.seq
0
As the last lines of the script, the same call takes the options from the command line:
if __name__ == "__main__":
raise SystemExit(cli.run(gre, sys.argv[1:], default_output="gre.seq"))