write_gre_label.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import math
  2. import numpy as np
  3. import pypulseq as pp
  4. def main(plot: bool, write_seq: bool, seq_filename: str = "gre_label_pypulseq.seq"):
  5. # ======
  6. # SETUP
  7. # ======
  8. seq = pp.Sequence() # Create a new sequence object
  9. fov = 224e-3 # Define FOV and resolution
  10. Nx = 256
  11. Ny = Nx
  12. alpha = 7 # Flip angle
  13. slice_thickness = 3e-3 # Slice thickness
  14. n_slices = 1
  15. TE = 4.3e-3 # Echo time
  16. TR = 10e-3 # Repetition time
  17. rf_spoiling_inc = 117 # RF spoiling increment
  18. ro_duration = 3.2e-3 # ADC duration
  19. # Set system limits
  20. system = pp.Opts(
  21. max_grad=28,
  22. grad_unit="mT/m",
  23. max_slew=150,
  24. slew_unit="T/m/s",
  25. rf_ringdown_time=20e-6,
  26. rf_dead_time=100e-6,
  27. adc_dead_time=10e-6,
  28. )
  29. # ======
  30. # CREATE EVENTS
  31. # ======
  32. # Create alpha-degree slice selection pulse and gradient
  33. rf, gz, _ = pp.make_sinc_pulse(
  34. flip_angle=alpha * np.pi / 180,
  35. duration=3e-3,
  36. slice_thickness=slice_thickness,
  37. apodization=0.5,
  38. time_bw_product=4,
  39. system=system,
  40. return_gz=True,
  41. )
  42. # Define other gradients and ADC events
  43. delta_k = 1 / fov
  44. gx = pp.make_trapezoid(
  45. channel="x", flat_area=Nx * delta_k, flat_time=ro_duration, system=system
  46. )
  47. adc = pp.make_adc(
  48. num_samples=Nx, duration=gx.flat_time, delay=gx.rise_time, system=system
  49. )
  50. gx_pre = pp.make_trapezoid(
  51. channel="x", area=-gx.area / 2, duration=1e-3, system=system
  52. )
  53. gz_reph = pp.make_trapezoid(
  54. channel="z", area=-gz.area / 2, duration=1e-3, system=system
  55. )
  56. phase_areas = -(np.arange(Ny) - Ny / 2) * delta_k
  57. # Gradient spoiling
  58. gx_spoil = pp.make_trapezoid(channel="x", area=2 * Nx * delta_k, system=system)
  59. gz_spoil = pp.make_trapezoid(channel="z", area=4 / slice_thickness, system=system)
  60. # Calculate timing
  61. delay_TE = (
  62. math.ceil(
  63. (
  64. TE
  65. - pp.calc_duration(gx_pre)
  66. - gz.fall_time
  67. - gz.flat_time / 2
  68. - pp.calc_duration(gx) / 2
  69. )
  70. / seq.grad_raster_time
  71. )
  72. * seq.grad_raster_time
  73. )
  74. delay_TR = (
  75. math.ceil(
  76. (
  77. TR
  78. - pp.calc_duration(gz)
  79. - pp.calc_duration(gx_pre)
  80. - pp.calc_duration(gx)
  81. - delay_TE
  82. )
  83. / seq.grad_raster_time
  84. )
  85. * seq.grad_raster_time
  86. )
  87. assert np.all(delay_TE >= 0)
  88. assert np.all(delay_TR >= pp.calc_duration(gx_spoil, gz_spoil))
  89. rf_phase = 0
  90. rf_inc = 0
  91. seq.add_block(pp.make_label(label="REV", type="SET", value=1))
  92. # ======
  93. # CONSTRUCT SEQUENCE
  94. # ======
  95. # Loop over slices
  96. for s in range(n_slices):
  97. rf.freq_offset = gz.amplitude * slice_thickness * (s - (n_slices - 1) / 2)
  98. # Loop over phase encodes and define sequence blocks
  99. for i in range(Ny):
  100. rf.phase_offset = rf_phase / 180 * np.pi
  101. adc.phase_offset = rf_phase / 180 * np.pi
  102. rf_inc = divmod(rf_inc + rf_spoiling_inc, 360.0)[1]
  103. rf_phase = divmod(rf_phase + rf_inc, 360.0)[1]
  104. seq.add_block(rf, gz)
  105. gy_pre = pp.make_trapezoid(
  106. channel="y",
  107. area=phase_areas[i],
  108. duration=pp.calc_duration(gx_pre),
  109. system=system,
  110. )
  111. seq.add_block(gx_pre, gy_pre, gz_reph)
  112. seq.add_block(pp.make_delay(delay_TE))
  113. seq.add_block(gx, adc)
  114. gy_pre.amplitude = -gy_pre.amplitude
  115. spoil_block_contents = [pp.make_delay(delay_TR), gx_spoil, gy_pre, gz_spoil]
  116. if i != Ny - 1:
  117. spoil_block_contents.append(
  118. pp.make_label(type="INC", label="LIN", value=1)
  119. )
  120. else:
  121. spoil_block_contents.extend(
  122. [
  123. pp.make_label(type="SET", label="LIN", value=0),
  124. pp.make_label(type="INC", label="SLC", value=1),
  125. ]
  126. )
  127. seq.add_block(*spoil_block_contents)
  128. ok, error_report = seq.check_timing()
  129. if ok:
  130. print("Timing check passed successfully")
  131. else:
  132. print("Timing check failed. Error listing follows:")
  133. [print(e) for e in error_report]
  134. # ======
  135. # VISUALIZATION
  136. # ======
  137. if plot:
  138. seq.plot(label="lin", time_range=np.array([0, 32]) * TR, time_disp="ms")
  139. # =========
  140. # WRITE .SEQ
  141. # =========
  142. if write_seq:
  143. # Prepare the sequence output for the scanner
  144. seq.set_definition(key="FOV", value=[fov, fov, slice_thickness * n_slices])
  145. seq.set_definition(key="Name", value="gre_label")
  146. seq.write(seq_filename)
  147. if __name__ == "__main__":
  148. main(plot=True, write_seq=True)