write_gre.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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_pypulseq.seq"):
  5. # ======
  6. # SETUP
  7. # ======
  8. # Create a new sequence object
  9. seq = pp.Sequence()
  10. fov = 256e-3 # Define FOV and resolution
  11. Nx = 256
  12. Ny = 256
  13. alpha = 10 # flip angle
  14. slice_thickness = 3e-3 # slice
  15. TR = 12e-3 # Repetition time
  16. TE = 5e-3 # Echo time
  17. rf_spoiling_inc = 117 # RF spoiling increment
  18. system = pp.Opts(
  19. max_grad=28,
  20. grad_unit="mT/m",
  21. max_slew=150,
  22. slew_unit="T/m/s",
  23. rf_ringdown_time=20e-6,
  24. rf_dead_time=100e-6,
  25. adc_dead_time=10e-6,
  26. )
  27. # ======
  28. # CREATE EVENTS
  29. # ======
  30. rf, gz, _ = pp.make_sinc_pulse(
  31. flip_angle=alpha * math.pi / 180,
  32. duration=3e-3,
  33. slice_thickness=slice_thickness,
  34. apodization=0.42,
  35. time_bw_product=4,
  36. system=system,
  37. return_gz=True,
  38. )
  39. # Define other gradients and ADC events
  40. delta_k = 1 / fov
  41. gx = pp.make_trapezoid(
  42. channel="x", flat_area=Nx * delta_k, flat_time=3.2e-3, system=system
  43. )
  44. adc = pp.make_adc(
  45. num_samples=Nx, duration=gx.flat_time, delay=gx.rise_time, system=system
  46. )
  47. gx_pre = pp.make_trapezoid(
  48. channel="x", area=-gx.area / 2, duration=1e-3, system=system
  49. )
  50. gz_reph = pp.make_trapezoid(
  51. channel="z", area=-gz.area / 2, duration=1e-3, system=system
  52. )
  53. phase_areas = (np.arange(Ny) - Ny / 2) * delta_k
  54. # gradient spoiling
  55. gx_spoil = pp.make_trapezoid(channel="x", area=2 * Nx * delta_k, system=system)
  56. gz_spoil = pp.make_trapezoid(channel="z", area=4 / slice_thickness, system=system)
  57. # Calculate timing
  58. delay_TE = (
  59. np.ceil(
  60. (
  61. TE
  62. - pp.calc_duration(gx_pre)
  63. - gz.fall_time
  64. - gz.flat_time / 2
  65. - pp.calc_duration(gx) / 2
  66. )
  67. / seq.grad_raster_time
  68. )
  69. * seq.grad_raster_time
  70. )
  71. delay_TR = (
  72. np.ceil(
  73. (
  74. TR
  75. - pp.calc_duration(gz)
  76. - pp.calc_duration(gx_pre)
  77. - pp.calc_duration(gx)
  78. - delay_TE
  79. )
  80. / seq.grad_raster_time
  81. )
  82. * seq.grad_raster_time
  83. )
  84. assert np.all(delay_TE >= 0)
  85. assert np.all(delay_TR >= pp.calc_duration(gx_spoil, gz_spoil))
  86. rf_phase = 0
  87. rf_inc = 0
  88. # ======
  89. # CONSTRUCT SEQUENCE
  90. # ======
  91. # Loop over phase encodes and define sequence blocks
  92. for i in range(Ny):
  93. rf.phase_offset = rf_phase / 180 * np.pi
  94. adc.phase_offset = rf_phase / 180 * np.pi
  95. rf_inc = divmod(rf_inc + rf_spoiling_inc, 360.0)[1]
  96. rf_phase = divmod(rf_phase + rf_inc, 360.0)[1]
  97. seq.add_block(rf, gz)
  98. gy_pre = pp.make_trapezoid(
  99. channel="y",
  100. area=phase_areas[i],
  101. duration=pp.calc_duration(gx_pre),
  102. system=system,
  103. )
  104. seq.add_block(gx_pre, gy_pre, gz_reph)
  105. seq.add_block(pp.make_delay(delay_TE))
  106. seq.add_block(gx, adc)
  107. gy_pre.amplitude = -gy_pre.amplitude
  108. seq.add_block(pp.make_delay(delay_TR), gx_spoil, gy_pre, gz_spoil)
  109. # Check whether the timing of the sequence is correct
  110. ok, error_report = seq.check_timing()
  111. if ok:
  112. print("Timing check passed successfully")
  113. else:
  114. print("Timing check failed. Error listing follows:")
  115. [print(e) for e in error_report]
  116. # ======
  117. # VISUALIZATION
  118. # ======
  119. if plot:
  120. seq.plot()
  121. seq.calculate_kspacePP()
  122. # Very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within
  123. # slew-rate limits
  124. rep = seq.test_report()
  125. print(rep)
  126. # =========
  127. # WRITE .SEQ
  128. # =========
  129. if write_seq:
  130. # Prepare the sequence output for the scanner
  131. seq.set_definition(key="FOV", value=[fov, fov, slice_thickness])
  132. seq.set_definition(key="Name", value="gre")
  133. seq.write(seq_filename)
  134. if __name__ == "__main__":
  135. main(plot=False, write_seq=True)