make_block_pulse.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. from types import SimpleNamespace
  2. from typing import Tuple, Union
  3. import numpy as np
  4. from seqgen.pypulseq.calc_duration import calc_duration
  5. from seqgen.pypulseq.make_delay import make_delay
  6. from seqgen.pypulseq.opts import Opts
  7. from seqgen.pypulseq.supported_labels_rf_use import get_supported_rf_uses
  8. def make_block_pulse(
  9. flip_angle: float,
  10. bandwidth: float = 0,
  11. delay: float = 0,
  12. duration: float = 4e-3,
  13. freq_offset: float = 0,
  14. phase_offset: float = 0,
  15. return_delay: bool = False,
  16. system: Opts = Opts(),
  17. time_bw_product: float = 0,
  18. use: str = str(),
  19. ) -> Union[SimpleNamespace, Tuple[SimpleNamespace, SimpleNamespace]]:
  20. """
  21. Create a block pulse with optional slice selectiveness.
  22. Parameters
  23. ----------
  24. flip_angle : float
  25. Flip angle in radians.
  26. bandwidth : float, default=0
  27. Bandwidth in Hertz (hz).
  28. delay : float, default=0
  29. Delay in seconds (s) of accompanying slice select trapezoidal event.
  30. duration : float, default=4e-3
  31. Duration in seconds (s).
  32. freq_offset : float, default=0
  33. Frequency offset in Hertz (Hz).
  34. phase_offset : float, default=0
  35. Phase offset Hertz (Hz).
  36. return_delay : bool, default=False
  37. Boolean flag to indicate if the delay event has to be returned.
  38. system : Opts, default=Opts()
  39. System limits.
  40. time_bw_product : float, default=0
  41. Time-bandwidth product.
  42. use : str, default=str()
  43. Use of radio-frequency block pulse event. Must be one of 'excitation', 'refocusing' or 'inversion'.
  44. Returns
  45. -------
  46. rf : SimpleNamespace
  47. Radio-frequency block pulse event.
  48. delay : SimpleNamespace, optional
  49. Slice select trapezoidal gradient event accompanying the radio-frequency block pulse event.
  50. Raises
  51. ------
  52. ValueError
  53. If invalid `use` parameter is passed. Must be one of 'excitation', 'refocusing' or 'inversion'.
  54. If neither `bandwidth` nor `duration` are passed.
  55. If `return_gz=True`, and `slice_thickness` is not passed.
  56. """
  57. valid_use_pulses = get_supported_rf_uses()
  58. if use != "" and use not in valid_use_pulses:
  59. raise ValueError(
  60. f"Invalid use parameter. Must be one of 'excitation', 'refocusing' or 'inversion'. Passed: {use}"
  61. )
  62. if duration == 0:
  63. if time_bw_product > 0:
  64. duration = time_bw_product / bandwidth
  65. elif bandwidth > 0:
  66. duration = 1 / (4 * bandwidth)
  67. else:
  68. raise ValueError("Either bandwidth or duration must be defined")
  69. BW = 1 / (4 * duration)
  70. N = np.round(duration / system.rf_raster_time)
  71. t = np.array([0, N]) * system.rf_raster_time
  72. signal = flip_angle / (2 * np.pi) / duration * np.ones_like(t)
  73. rf = SimpleNamespace()
  74. rf.type = "rf"
  75. rf.signal = signal
  76. rf.t = t
  77. rf.shape_dur = t[-1]
  78. rf.freq_offset = freq_offset
  79. rf.phase_offset = phase_offset
  80. rf.dead_time = system.rf_dead_time
  81. rf.ringdown_time = system.rf_ringdown_time
  82. rf.delay = delay
  83. if use != "":
  84. rf.use = use
  85. if rf.dead_time > rf.delay:
  86. rf.delay = rf.dead_time
  87. if rf.ringdown_time > 0 and return_delay:
  88. delay = make_delay(calc_duration(rf) + rf.ringdown_time)
  89. if return_delay:
  90. return rf, delay
  91. else:
  92. return rf