| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- import math
- import warnings
- import numpy as np
- from pypulseq.Sequence.sequence import Sequence
- from pypulseq.calc_rf_center import calc_rf_center
- from pypulseq.make_adc import make_adc
- from pypulseq.make_delay import make_delay
- from pypulseq.make_extended_trapezoid import make_extended_trapezoid
- from pypulseq.make_sinc_pulse import make_sinc_pulse
- from pypulseq.make_trapezoid import make_trapezoid
- from pypulseq.opts import Opts
- def main(plot: bool, write_seq: bool, seq_filename: str = "haste_pypulseq.seq"):
- # ======
- # SETUP
- # ======
- dG = 250e-6
- # Set system limits
- system = Opts(
- max_grad=30,
- grad_unit="mT/m",
- max_slew=170,
- slew_unit="T/m/s",
- rf_ringdown_time=100e-6,
- rf_dead_time=100e-6,
- adc_dead_time=10e-6,
- )
- seq = Sequence(system=system) # Create a new sequence object
- fov = 256e-3 # Define FOV and resolution
- Ny_pre = 8
- Nx, Ny = 128, 128
- n_echo = int(Ny / 2 + Ny_pre) # Number of echoes
- n_slices = 1
- rf_flip = 180 # Flip angle
- if isinstance(rf_flip, int):
- rf_flip = np.zeros(n_echo) + rf_flip
- slice_thickness = 5e-3 # Slice thickness
- TE = 12e-3 # Echo time
- TR = 2000e-3 # Repetition time
- sampling_time = 6.4e-3
- readout_time = sampling_time + 2 * system.adc_dead_time
- t_ex = 2.5e-3
- t_ex_wd = t_ex + system.rf_ringdown_time + system.rf_dead_time
- t_ref = 2e-3
- tf_ref_wd = t_ref + system.rf_ringdown_time + system.rf_dead_time
- t_sp = 0.5 * (TE - readout_time - tf_ref_wd)
- t_sp_ex = 0.5 * (TE - t_ex_wd - tf_ref_wd)
- fspR = 1.0
- fspS = 0.5
- rfex_phase = math.pi / 2
- rfref_phase = 0
- # ======
- # CREATE EVENTS
- # ======
- # Create 90 degree slice selection pulse and gradient
- flipex = 90 * math.pi / 180
- rfex, gz, _ = make_sinc_pulse(
- flip_angle=flipex,
- system=system,
- duration=t_ex,
- slice_thickness=slice_thickness,
- apodization=0.5,
- time_bw_product=4,
- phase_offset=rfex_phase,
- return_gz=True,
- )
- GS_ex = make_trapezoid(
- channel="z",
- system=system,
- amplitude=gz.amplitude,
- flat_time=t_ex_wd,
- rise_time=dG,
- )
- flipref = rf_flip[0] * math.pi / 180
- rfref, gz, _ = make_sinc_pulse(
- flip_angle=flipref,
- system=system,
- duration=t_ref,
- slice_thickness=slice_thickness,
- apodization=0.5,
- time_bw_product=4,
- phase_offset=rfref_phase,
- use="refocusing",
- return_gz=True,
- )
- GS_ref = make_trapezoid(
- channel="z",
- system=system,
- amplitude=GS_ex.amplitude,
- flat_time=tf_ref_wd,
- rise_time=dG,
- )
- AGS_ex = GS_ex.area / 2
- GS_spr = make_trapezoid(
- channel="z",
- system=system,
- area=AGS_ex * (1 + fspS),
- duration=t_sp,
- rise_time=dG,
- )
- GS_spex = make_trapezoid(
- channel="z", system=system, area=AGS_ex * fspS, duration=t_sp_ex, rise_time=dG
- )
- delta_k = 1 / fov
- k_width = Nx * delta_k
- GR_acq = make_trapezoid(
- channel="x",
- system=system,
- flat_area=k_width,
- flat_time=readout_time,
- rise_time=dG,
- )
- adc = make_adc(num_samples=Nx, duration=sampling_time, delay=system.adc_dead_time)
- GR_spr = make_trapezoid(
- channel="x", system=system, area=GR_acq.area * fspR, duration=t_sp, rise_time=dG
- )
- GR_spex = make_trapezoid(
- channel="x",
- system=system,
- area=GR_acq.area * (1 + fspR),
- duration=t_sp_ex,
- rise_time=dG,
- )
- AGR_spr = GR_spr.area
- AGR_preph = GR_acq.area / 2 + AGR_spr
- GR_preph = make_trapezoid(
- channel="x", system=system, area=AGR_preph, duration=t_sp_ex, rise_time=dG
- )
- n_ex = 1
- PE_order = np.arange(-Ny_pre, Ny + 1).T
- phase_areas = PE_order * delta_k
- # Split gradients and recombine into blocks
- GS1_times = np.array([0, GS_ex.rise_time])
- GS1_amp = np.array([0, GS_ex.amplitude])
- GS1 = make_extended_trapezoid(channel="z", times=GS1_times, amplitudes=GS1_amp)
- GS2_times = np.array([0, GS_ex.flat_time])
- GS2_amp = np.array([GS_ex.amplitude, GS_ex.amplitude])
- GS2 = make_extended_trapezoid(channel="z", times=GS2_times, amplitudes=GS2_amp)
- GS3_times = np.array(
- [
- 0,
- GS_spex.rise_time,
- GS_spex.rise_time + GS_spex.flat_time,
- GS_spex.rise_time + GS_spex.flat_time + GS_spex.fall_time,
- ]
- )
- GS3_amp = np.array(
- [GS_ex.amplitude, GS_spex.amplitude, GS_spex.amplitude, GS_ref.amplitude]
- )
- GS3 = make_extended_trapezoid(channel="z", times=GS3_times, amplitudes=GS3_amp)
- GS4_times = np.array([0, GS_ref.flat_time])
- GS4_amp = np.array([GS_ref.amplitude, GS_ref.amplitude])
- GS4 = make_extended_trapezoid(channel="z", times=GS4_times, amplitudes=GS4_amp)
- GS5_times = np.array(
- [
- 0,
- GS_spr.rise_time,
- GS_spr.rise_time + GS_spr.flat_time,
- GS_spr.rise_time + GS_spr.flat_time + GS_spr.fall_time,
- ]
- )
- GS5_amp = np.array([GS_ref.amplitude, GS_spr.amplitude, GS_spr.amplitude, 0])
- GS5 = make_extended_trapezoid(channel="z", times=GS5_times, amplitudes=GS5_amp)
- GS7_times = np.array(
- [
- 0,
- GS_spr.rise_time,
- GS_spr.rise_time + GS_spr.flat_time,
- GS_spr.rise_time + GS_spr.flat_time + GS_spr.fall_time,
- ]
- )
- GS7_amp = np.array([0, GS_spr.amplitude, GS_spr.amplitude, GS_ref.amplitude])
- GS7 = make_extended_trapezoid(channel="z", times=GS7_times, amplitudes=GS7_amp)
- # Readout gradient
- GR3 = GR_preph
- GR5_times = np.array(
- [
- 0,
- GR_spr.rise_time,
- GR_spr.rise_time + GR_spr.flat_time,
- GR_spr.rise_time + GR_spr.flat_time + GR_spr.fall_time,
- ]
- )
- GR5_amp = np.array([0, GR_spr.amplitude, GR_spr.amplitude, GR_acq.amplitude])
- GR5 = make_extended_trapezoid(channel="x", times=GR5_times, amplitudes=GR5_amp)
- GR6_times = np.array([0, readout_time])
- GR6_amp = np.array([GR_acq.amplitude, GR_acq.amplitude])
- GR6 = make_extended_trapezoid(channel="x", times=GR6_times, amplitudes=GR6_amp)
- GR7_times = np.array(
- [
- 0,
- GR_spr.rise_time,
- GR_spr.rise_time + GR_spr.flat_time,
- GR_spr.rise_time + GR_spr.flat_time + GR_spr.fall_time,
- ]
- )
- GR7_amp = np.array([GR_acq.amplitude, GR_spr.amplitude, GR_spr.amplitude, 0])
- GR7 = make_extended_trapezoid(channel="x", times=GR7_times, amplitudes=GR7_amp)
- # Fill-times
- tex = GS1.shape_dur + GS2.shape_dur + GS3.shape_dur
- tref = GS4.shape_dur + GS5.shape_dur + GS7.shape_dur + readout_time
- tend = GS4.shape_dur + GS5.shape_dur
- TE_train = tex + n_echo * tref + tend
- TR_fill = (TR - n_slices * TE_train) / n_slices # Round to gradient raster
- TR_fill = system.grad_raster_time * round(TR_fill / system.grad_raster_time)
- if TR_fill < 0:
- TR_fill = 1e-3
- warnings.warn(
- f"TR too short, adapted to include all slices to: {1000 * n_slices * (TE_train + TR_fill)} ms"
- )
- else:
- print(f"TR fill: {1000 * TR_fill} ms")
- delay_TR = make_delay(TR_fill)
- delay_end = make_delay(5)
- # ======
- # CONSTRUCT SEQUENCE
- # ======
- # Define sequence blocks
- for k_ex in range(n_ex):
- for s in range(n_slices):
- rfex.freq_offset = (
- GS_ex.amplitude * slice_thickness * (s - (n_slices - 1) / 2)
- )
- rfref.freq_offset = (
- GS_ref.amplitude * slice_thickness * (s - (n_slices - 1) / 2)
- )
- # Align the phase for off-center slices
- rfex.phase_offset = (
- rfex_phase - 2 * math.pi * rfex.freq_offset * calc_rf_center(rfex)[0]
- )
- rfref.phase_offset = (
- rfref_phase - 2 * math.pi * rfref.freq_offset * calc_rf_center(rfref)[0]
- )
- seq.add_block(GS1)
- seq.add_block(GS2, rfex)
- seq.add_block(GS3, GR3)
- for k_ech in range(n_echo):
- if k_ex >= 0:
- phase_area = phase_areas[k_ech]
- else:
- phase_area = 0
- GP_pre = make_trapezoid(
- channel="y",
- system=system,
- area=phase_area,
- duration=t_sp,
- rise_time=dG,
- )
- GP_rew = make_trapezoid(
- channel="y",
- system=system,
- area=-phase_area,
- duration=t_sp,
- rise_time=dG,
- )
- seq.add_block(GS4, rfref)
- seq.add_block(GS5, GR5, GP_pre)
- if k_ex >= 0:
- seq.add_block(GR6, adc)
- else:
- seq.add_block(GR6)
- seq.add_block(GS7, GR7, GP_rew)
- seq.add_block(GS4)
- seq.add_block(GS5)
- seq.add_block(delay_TR)
- seq.add_block(delay_end)
- # Check whether the timing of the sequence is correct
- ok, error_report = seq.check_timing()
- if ok:
- print("Timing check passed successfully")
- else:
- print("Timing check failed. Error listing follows:")
- [print(e) for e in error_report]
- # ======
- # VISUALIZATION
- # ======
- if plot:
- seq.plot()
- # =========
- # WRITE .SEQ
- # =========
- if write_seq:
- seq.write(seq_filename)
- # SETUPeq")
- if __name__ == "__main__":
- main(plot=True, write_seq=True)
|