Source code for pypulseq.make_block_pulse

from types import SimpleNamespace
from typing import Union
from warnings import warn

import numpy as np

from pypulseq.opts import Opts
from pypulseq.supported_labels_rf_use import get_supported_rf_uses
from pypulseq.utils.tracing import trace, trace_enabled


[docs] def make_block_pulse( flip_angle: float, delay: float = 0.0, duration: Union[float, None] = None, bandwidth: Union[float, None] = None, time_bw_product: Union[float, None] = None, freq_offset: float = 0.0, phase_offset: float = 0.0, system: Union[Opts, None] = None, use: str = 'undefined', freq_ppm: float = 0.0, phase_ppm: float = 0.0, ) -> SimpleNamespace: """ Create a block (RECT or hard) pulse. Define duration, or bandwidth, or bandwidth and time_bw_product. If none are provided a default 4 ms pulse will be generated. Parameters ---------- flip_angle : float Flip angle in radians. delay : float, default=0 Delay in seconds (s). duration : float, default=None Duration in seconds (s). bandwidth : float, default=None Bandwidth in Hertz (Hz). If supplied without time_bw_product duration = 1 / (4 * bandwidth) time_bw_product : float, default=None Time-bandwidth product. If supplied with bandwidth, duration = time_bw_product / bandwidth freq_offset : float, default=0 Frequency offset in Hertz (Hz). phase_offset : float, default=0 Phase offset Hertz (Hz). system : Opts, default=Opts() System limits. use : str, default='undefined' Use of radio-frequency block pulse event. Must be one of 'excitation', 'refocusing', 'inversion', 'saturation', 'preparation', 'other', 'undefined'. freq_ppm : float, default=0 PPM frequency offset. phase_ppm : float, default=0 PPM phase offset. Returns ------- rf : SimpleNamespace Radio-frequency block pulse event. Raises ------ ValueError If invalid `use` parameter is passed. One of bandwidth or duration must be defined, but not both. One of bandwidth or duration must be defined and be > 0. """ if system is None: system = Opts.default valid_pulse_uses = get_supported_rf_uses() if use != '' and use not in valid_pulse_uses: raise ValueError(f'Invalid use parameter. Must be one of {valid_pulse_uses}. Passed: {use}') if duration is None and bandwidth is None: warn('Using default 4 ms duration for block pulse.') duration = 4e-3 elif duration is not None and bandwidth is not None and duration > 0: # Multiple arguments raise ValueError('One of bandwidth or duration must be defined, but not both.') elif duration is not None and duration > 0: # Explicitly handle this most expected case. # There is probably a better way of writing this if block pass elif duration is None and bandwidth is not None and bandwidth > 0: if time_bw_product is not None and time_bw_product > 0: duration = time_bw_product / bandwidth else: duration = 1 / (4 * bandwidth) else: # Invalid arguments raise ValueError( 'One of bandwidth or duration must be defined and be > 0. ' f'duration = {duration} s, bandwidth = {bandwidth} Hz.' ) n_samples = round(duration / system.rf_raster_time) t = np.array([0, n_samples]) * system.rf_raster_time signal = flip_angle / (2 * np.pi) / duration * np.ones_like(t) rf = SimpleNamespace() rf.type = 'rf' rf.signal = signal rf.t = t rf.shape_dur = t[-1] rf.freq_offset = freq_offset rf.phase_offset = phase_offset rf.freq_ppm = freq_ppm rf.phase_ppm = phase_ppm rf.dead_time = system.rf_dead_time rf.ringdown_time = system.rf_ringdown_time rf.delay = delay rf.center = rf.shape_dur / 2 rf.use = use if rf.dead_time > rf.delay: warn( f'Specified RF delay {rf.delay * 1e6:.2f} us is less than the dead time {rf.dead_time * 1e6:.0f} us. Delay was increased to the dead time.', stacklevel=2, ) rf.delay = rf.dead_time if trace_enabled(): rf.trace = trace() return rf