Exemple #1
0
    def test_modify_gradient(self):
        g1 = make_trapezoid(channel='x',
                            amplitude=5000,
                            duration=1e-3,
                            rise_time=1e-4)
        g1_area = g1.area
        g1_flat = g1.flat_area
        g2 = make_extended_trapezoid(channel='z',
                                     amplitudes=[4000, 5000, 5000, 6000],
                                     times=[0, 1e-3, 6e-3, 7e-3])

        # Use a random scale
        sc = np.random.rand()

        # Modify a trapezoidal gradient
        modify_gradient(g1, scale=sc, channel='z')
        np.testing.assert_equal(g1.amplitude, 5000 * sc)
        np.testing.assert_equal(g1.channel, 'z')
        np.testing.assert_equal(g1.area, g1_area * sc)
        np.testing.assert_equal(g1.flat_area, g1_flat * sc)

        # Modify an arbitrary gradient
        modify_gradient(g2, scale=0, channel='y')
        np.testing.assert_equal(g2.channel, 'y')
        np.testing.assert_equal(np.sum(g2.waveform), 0)
        np.testing.assert_equal(g2.first, 0)
        np.testing.assert_equal(g2.last, 0)
Exemple #2
0
    def test_make_scaled_extended_trapezoid(self):
        sc = np.random.rand() + 0.5
        g0 = make_extended_trapezoid(channel='x',
                                     system=Opts(),
                                     times=[0, 1e-3, 6e-3, 7e-3],
                                     amplitudes=[4e3, 5e3, 5e3, 6e3])
        g1 = make_scaled_extended_trapezoid(
            channel='x',
            system=Opts(),
            scale=1,
            times=np.array([0, 1e-3, 6e-3, 7e-3]),
            amplitudes=np.array([4e3, 5e3, 5e3, 6e3]))
        g2 = make_scaled_extended_trapezoid(
            channel='x',
            system=Opts(),
            scale=sc,
            times=np.array([0, 1e-3, 6e-3, 7e-3]),
            amplitudes=np.array([4e3, 5e3, 5e3, 6e3]))
        g3 = make_scaled_extended_trapezoid(
            channel='x',
            system=Opts(),
            scale=0,
            times=np.array([0, 1e-3, 6e-3, 7e-3]),
            amplitudes=np.array([4e3, 5e3, 5e3, 6e3]))

        np.testing.assert_equal(g0.channel, g1.channel)
        np.testing.assert_array_equal(g0.waveform, g1.waveform)
        np.testing.assert_equal(g0.type, g1.type)
        np.testing.assert_array_equal(g0.t, g1.t)

        np.testing.assert_array_almost_equal(g0.waveform * sc, g2.waveform)

        np.testing.assert_equal(g3.type, 'trap')
        np.testing.assert_equal(g3.amplitude, 0)
        np.testing.assert_equal(g3.area, 0)
Exemple #3
0
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=GR_acq.flat_time - 40e-6, delay=20e-6)
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

GS1_times = [0, GS_ex.rise_time]
GS1_amp = [0, GS_ex.amplitude]
GS1 = make_extended_trapezoid(channel='z', times=GS1_times, amplitudes=GS1_amp)

GS2_times = [0, GS_ex.flat_time]
GS2_amp = [GS_ex.amplitude, GS_ex.amplitude]
GS2 = make_extended_trapezoid(channel='z', times=GS2_times, amplitudes=GS2_amp)

GS3_times = [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 = [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 = [0, GS_ref.flat_time]
GS4_amp = [GS_ref.amplitude, GS_ref.amplitude]
GS4 = make_extended_trapezoid(channel='z', times=GS4_times, amplitudes=GS4_amp)

GS5_times = [0, GS_spr.rise_time, GS_spr.rise_time + GS_spr.flat_time,
def split_gradient_at(grad: np.ndarray, time_point: float, system: Opts = Opts()):
    """
    Split gradient waveform `grad` into two at time point `time_point`.

    Parameters
    ----------
    grad : numpy.ndarray
        Gradient waveform to be split into two gradient waveforms.
    time_point : float, optional
        Time point at which `grad` will be split into two gradient waveforms.
    system : Opts, optional
        System limits. Default is a system limits object initialised to default values.
    Returns
    -------
    grad1, grad2 : numpy.ndarray
        Gradient waveforms after splitting.
    """
    grad_raster_time = system.grad_raster_time

    time_index = round(time_point / grad_raster_time)
    time_point = time_index * grad_raster_time
    time_index += 1

    if grad.type == 'trap':
        ch = grad.channel
        grad.delay = round(grad.delay / grad_raster_time) * grad_raster_time
        grad.rise_time = round(grad.rise_time / grad_raster_time) * grad_raster_time
        grad.flat_time = round(grad.flat_time / grad_raster_time) * grad_raster_time
        grad.fall_time = round(grad.fall_time / grad_raster_time) * grad_raster_time

        if grad.flat_time == 0:
            times = [0, grad.rise_time, grad.rise_time + grad.fall_time]
            amplitudes = [0, grad.amplitude, 0]
        else:
            times = [0, grad.rise_time, grad.rise_time + grad.flat_time,
                     grad.rise_time + grad.flat_time + grad.fall_time]
            amplitudes = [0, grad.amplitude, grad.amplitude, 0]

        if time_point < grad.delay:
            times = np.insert(grad.delay + times, 0, 0)
            amplitudes = [0, amplitudes]
            grad.delay = 0

        amplitudes = np.array(amplitudes)
        times = np.array(times)

        amp_tp = np.interp(x=time_point, xp=times, fp=amplitudes)
        times1 = np.append(times[np.where(times < time_point)], time_point)
        amplitudes1 = np.append(amplitudes[np.where(times < time_point)], amp_tp)
        times2 = np.insert(times[times > time_point], 0, time_point) - time_point
        amplitudes2 = np.insert(amplitudes[times > time_point], 0, amp_tp)

        grad1 = make_extended_trapezoid(channel=ch, system=system, times=times1, amplitudes=amplitudes1,
                                        skip_check=True)
        grad1.delay = grad.delay
        grad2 = make_extended_trapezoid(channel=ch, system=system, times=times2, amplitudes=amplitudes2,
                                        skip_check=True)
        grad2.delay = time_point
        return grad1, grad2
    elif grad.type == 'grad':
        if time_index == 1 or time_index >= len(grad.t):
            return grad
        else:
            grad1 = grad
            grad2 = grad
            grad1.last = grad.waveform[time_index]
            grad2.first = grad.waveform[time_index]
            grad2.delay = grad.delay + grad.t[time_index]
            grad1.t = grad.t[:time_index]
            grad1.waveform = grad.waveform[:time_index]
            grad2.t = grad.t[time_index:]
            grad2.waveform = grad.waveform[time_index:]
            return grad1, grad2
    else:
        raise ValueError('Splitting of unsupported event.')
Exemple #5
0
                          system=system,
                          area=agr_preph,
                          duration=t_spex,
                          rise_time=dG)

n_ex = math.floor(Ny / n_echo)
pe_steps = np.arange(1, n_echo * n_ex + 1) - 0.5 * n_echo * n_ex - 1
if divmod(n_echo, 2)[1] == 0:
    pe_steps = np.roll(pe_steps, -round(n_ex / 2))
pe_order = pe_steps.reshape((n_ex, n_echo), order='F').T
phase_areas = pe_order * delta_k

# Split gradients and recombine into blocks
gs1_times = [0, gs_ex.rise_time]
gs1_amp = [0, gs_ex.amplitude]
gs1 = make_extended_trapezoid(channel='z', times=gs1_times, amplitudes=gs1_amp)

gs2_times = [0, gs_ex.flat_time]
gs2_amp = [gs_ex.amplitude, gs_ex.amplitude]
gs2 = make_extended_trapezoid(channel='z', times=gs2_times, amplitudes=gs2_amp)

gs3_times = [
    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 = [
    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 = [0, gs_ref.flat_time]
def make_extended_trapezoid_area(channel: str, Gs: float, Ge: float, A: float,
                                 system: Opts) -> Tuple[SimpleNamespace, np.array, np.array]:
    """
    Makes shortest possible extended trapezoid with a given area.

    Parameters
    ----------
    channel : str
        Orientation of extended trapezoidal gradient event. Must be one of 'x', 'y' or 'z'.
    Gs : float
        Starting non-zero gradient value.
    Ge : float
        Ending non-zero gradient value.
    A : float
        Area of extended trapezoid.
    system: Opts
        System limits.

    Returns
    -------
    grad : SimpleNamespace
        Extended trapezoid event.
    times : numpy.ndarray
    amplitude : numpy.ndarray

    Raises
    ------
    ValueError

    """
    SR = system.max_slew * 0.99

    Tp = 0
    obj1 = lambda x: (A - __testGA(x, 0, SR, system.grad_raster_time, Gs, Ge)) ** 2
    res = minimize(fun=obj1, x0=0, method='Nelder-Mead')
    Gp, obj1val = *res.x, res.fun

    if obj1val > 1e-3 or abs(Gp) > system.max_grad:  # Search did not converge
        Gp = system.max_grad * np.sign(Gp)
        obj2 = lambda x: (A - __testGA(Gp, x, SR, system.grad_raster_time, Gs, Ge)) ** 2
        res2 = minimize(fun=obj2, x0=0, method='Nelder-Mead')
        T, obj2val = *res2.x, res2.fun
        assert obj2val < 1e-2

        Tp = math.ceil(T / system.grad_raster_time) * system.grad_raster_time

        # Fix the ramps
        Tru = math.ceil(abs(Gp - Gs) / SR / system.grad_raster_time) * system.grad_raster_time
        Trd = math.ceil(abs(Gp - Ge) / SR / system.grad_raster_time) * system.grad_raster_time
        obj3 = lambda x: (A - __testGA1(x, Tru, Tp, Trd, Gs, Ge)) ** 2

        res = minimize(fun=obj3, x0=Gp, method='Nelder-Mead')
        Gp, obj3val = *res.x, res.fun
        assert obj3val < 1e-3

    if Tp > 0:
        times = np.cumsum([0, Tru, Tp, Trd])
        amplitudes = [Gs, Gp, Gp, Ge]
    else:
        Tru = math.ceil(abs(Gp - Gs) / SR / system.grad_raster_time) * system.grad_raster_time
        Trd = math.ceil(abs(Gp - Ge) / SR / system.grad_raster_time) * system.grad_raster_time

        if Trd > 0:
            if Tru > 0:
                times = np.cumsum([0, Tru, Trd])
                amplitudes = np.array([Gs, Gp, Ge])
            else:
                times = np.cumsum([0, Trd])
                amplitudes = np.array([Gs, Ge])
        else:
            times = np.cumsum([0, Tru])
            amplitudes = np.array([Gs, Ge])

    grad = make_extended_trapezoid(channel=channel, system=system, times=times, amplitudes=amplitudes)

    return grad, times, amplitudes
Exemple #7
0
def split_gradient(grad: SimpleNamespace,
                   system: Opts = Opts()) -> Tuple[SimpleNamespace, SimpleNamespace, SimpleNamespace]:
    """
    Split gradient waveform `grad` into two gradient waveforms at the center.

    Parameters
    ----------
    grad : array_like
        Gradient waveform to be split into two gradient waveforms.
    system : Opts, optional, default=Opts()
        System limits.

    Returns
    -------
    grad1, grad2 : numpy.ndarray
        Split gradient waveforms.

    Raises
    ------
    ValueError
         If arbitrary gradients are passed.
         If non-gradient event is passed.
    """
    grad_raster_time = system.grad_raster_time
    total_length = calc_duration(grad)

    if grad.type == 'trap':
        ch = grad.channel
        grad.delay = round(grad.delay / grad_raster_time) * grad_raster_time
        grad.rise_time = round(grad.rise_time / grad_raster_time) * grad_raster_time
        grad.flat_time = round(grad.flat_time / grad_raster_time) * grad_raster_time
        grad.fall_time = round(grad.fall_time / grad_raster_time) * grad_raster_time

        times = [0, grad.rise_time]
        amplitudes = [0, grad.amplitude]
        ramp_up = make_extended_trapezoid(channel=ch, system=system, times=times, amplitudes=amplitudes,
                                          skip_check=True)
        ramp_up.delay = grad.delay

        times = [0, grad.fall_time]
        amplitudes = [grad.amplitude, 0]
        ramp_down = make_extended_trapezoid(channel=ch, system=system, times=times, amplitudes=amplitudes,
                                            skip_check=True)
        ramp_down.delay = total_length - grad.fall_time
        ramp_down.t = ramp_down.t * grad_raster_time

        flat_top = SimpleNamespace()
        flat_top.type = 'grad'
        flat_top.channel = ch
        flat_top.delay = grad.delay + grad.rise_time
        flat_top.t = np.arange(step=grad_raster_time,
                               stop=ramp_down.delay - grad_raster_time - grad.delay - grad.rise_time)
        flat_top.waveform = grad.amplitude * np.ones(len(flat_top.t))
        flat_top.first = grad.amplitude
        flat_top.last = grad.amplitude

        return ramp_up, flat_top, ramp_down
    elif grad.type == 'grad':
        raise ValueError('Splitting of arbitrary gradients is not implemented yet.')
    else:
        raise ValueError('Splitting of unsupported event.')
Exemple #8
0
def bssfp_readout(seq, system, fov=200e-3, Nstartup=11, Ny=128):
    """
    Creates a Balanced steady-state free precession (bSSFP) sequence and adds
    to seq object

    Parameters
    ----------
    seq: object
        Sequence object
    system : Opts
        System limits.
    fov : float, optional
        Field-of-view [m]. Default is 0.2 m.
    Nstartup : float, optional
        Number of start-up RF pulses. Default is 11
    Ny : float, optional
        Number of phase encoding lines. Default is 128.
   Returns
    -------
    seq : SimpleNamespace
        Seq object with bSSFP readout
    TR : float
        Repetition time
    Ny : float
        Final number of phase encoding lines.
    """
    # Sequence Parameters
    enc = 'xyz'
    Nx = 128
    thk = 6e-3
    fa = 35  # [deg]

    Nramp = Nstartup

    # ADC duration (controls TR/TE)
    adc_dur = 2560 / 2  # [us]

    rf_dur = 490  # [us]
    rf_apo = 0.5
    rf_bwt = 1.5

    #############################################################################
    #                 Create slice selection pulse and gradient
    rf, g_ss, __ = make_sinc_pulse(
        flip_angle=fa * pi / 180,
        system=system,
        duration=rf_dur * 1e-6,
        slice_thickness=thk,
        apodization=rf_apo,
        time_bw_product=rf_bwt
    )  # for next pyPulseq version add:, return_gz=True)
    g_ss.channel = enc[2]

    # Slice refocusing
    g_ss_reph = make_trapezoid(channel=enc[2],
                               system=system,
                               area=-g_ss.area / 2,
                               duration=0.00017 * 2)
    #############################################################################

    rf.delay = calc_duration(g_ss) - calc_duration(rf) + rf.delay

    #############################################################################
    #                        Readout gradient and ADC
    delta_k = 1 / fov
    kWidth = Nx * delta_k

    # Readout and ADC
    g_ro = make_trapezoid(channel=enc[0],
                          system=system,
                          flat_area=kWidth,
                          flat_time=(adc_dur) * 1e-6)
    adc = make_adc(num_samples=Nx,
                   duration=g_ro.flat_time,
                   delay=g_ro.rise_time)

    # Readout rewinder
    g_ro_pre = make_trapezoid(channel=enc[0],
                              system=system,
                              area=-g_ro.area / 2)

    phaseAreas_tmp = np.arange(0, Ny, 1).tolist()
    phaseAreas = np.dot(np.subtract(phaseAreas_tmp, Ny / 2), delta_k)

    gs8_times = [
        0, g_ro.fall_time, g_ro.fall_time + g_ro_pre.rise_time,
        g_ro.fall_time + g_ro_pre.rise_time + g_ro_pre.flat_time,
        g_ro.fall_time + g_ro_pre.rise_time + g_ro_pre.flat_time +
        g_ro_pre.fall_time
    ]
    gs8_amp = [g_ro.amplitude, 0, g_ro_pre.amplitude, g_ro_pre.amplitude, 0]
    gx_2 = make_extended_trapezoid(channel=enc[0],
                                   times=gs8_times,
                                   amplitudes=gs8_amp)

    # Calculate phase encoding gradient duration
    pe_dur = calc_duration(gx_2)

    gx_allExt_times = [
        0, g_ro_pre.rise_time, g_ro_pre.rise_time + g_ro_pre.flat_time,
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time,
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time +
        g_ro.rise_time, g_ro_pre.rise_time + g_ro_pre.flat_time +
        g_ro_pre.fall_time + g_ro.rise_time + g_ro.flat_time + 1e-5,
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time +
        g_ro.rise_time + g_ro.flat_time + 1e-5 + g_ro.fall_time,
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time +
        g_ro.rise_time + g_ro.flat_time + 1e-5 + g_ro.fall_time +
        g_ro_pre.rise_time, g_ro_pre.rise_time + g_ro_pre.flat_time +
        g_ro_pre.fall_time + g_ro.rise_time + g_ro.flat_time + 1e-5 +
        g_ro.fall_time + g_ro_pre.rise_time + g_ro_pre.flat_time,
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time +
        g_ro.rise_time + g_ro.flat_time + 1e-5 + g_ro.fall_time +
        g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time
    ]
    gx_allExt_amp = [
        0, g_ro_pre.amplitude, g_ro_pre.amplitude, 0, g_ro.amplitude,
        g_ro.amplitude, 0, g_ro_pre.amplitude, g_ro_pre.amplitude, 0
    ]
    gx_all = make_extended_trapezoid(channel=enc[0],
                                     times=gx_allExt_times,
                                     amplitudes=gx_allExt_amp)
    #############################################################################

    gzrep_times = [
        0, g_ss_reph.rise_time, g_ss_reph.rise_time + g_ss_reph.flat_time,
        g_ss_reph.rise_time + g_ss_reph.flat_time + g_ss_reph.fall_time,
        g_ss_reph.rise_time + g_ss_reph.flat_time + g_ss_reph.fall_time +
        calc_duration(g_ro) + 2 * calc_duration(g_ro_pre) -
        2 * calc_duration(g_ss_reph) + 1e-5, g_ss_reph.rise_time +
        g_ss_reph.flat_time + g_ss_reph.fall_time + calc_duration(g_ro) +
        2 * calc_duration(g_ro_pre) - 2 * calc_duration(g_ss_reph) + 1e-5 +
        g_ss_reph.rise_time, g_ss_reph.rise_time + g_ss_reph.flat_time +
        g_ss_reph.fall_time + calc_duration(g_ro) +
        2 * calc_duration(g_ro_pre) - 2 * calc_duration(g_ss_reph) + 1e-5 +
        g_ss_reph.rise_time + g_ss_reph.flat_time, g_ss_reph.rise_time +
        g_ss_reph.flat_time + g_ss_reph.fall_time + calc_duration(g_ro) +
        2 * calc_duration(g_ro_pre) - 2 * calc_duration(g_ss_reph) + 1e-5 +
        g_ss_reph.rise_time + g_ss_reph.flat_time + g_ss_reph.fall_time
    ]
    gzrep_amp = [
        0, g_ss_reph.amplitude, g_ss_reph.amplitude, 0, 0, g_ss_reph.amplitude,
        g_ss_reph.amplitude, 0
    ]
    gzrep_all = make_extended_trapezoid(channel=enc[2],
                                        times=gzrep_times,
                                        amplitudes=gzrep_amp)

    adc.delay = g_ro_pre.rise_time + g_ro_pre.flat_time + g_ro_pre.fall_time + g_ro.rise_time + 0.5e-5

    # finish timing calculation
    TR = calc_duration(g_ss) + calc_duration(gx_all)
    TE = TR / 2

    ni_acqu_pattern = np.arange(1, Ny + 1, 1)
    Ny_aq = len(ni_acqu_pattern)
    print('Acquisition window is: %3.2f ms' % (TR * Ny_aq * 1e3))

    rf05 = rf
    rf_waveform = rf.signal
    ############################################################################
    #                          Start-up RF pulses
    #                          (ramp-up of Nramp)
    for nRamp in np.arange(1, Nramp + 1, 1):
        if np.mod(nRamp, 2):
            rf.phase_offset = 0
            adc.phase_offset = 0
        else:
            rf.phase_offset = -pi
            adc.phase_offset = -pi

        rf05.signal = np.divide(nRamp, Nramp) * rf_waveform

        gyPre_2 = make_trapezoid('y',
                                 area=phaseAreas[0],
                                 duration=pe_dur,
                                 system=system)
        gyPre_1 = make_trapezoid('y',
                                 area=-phaseAreas[0],
                                 duration=pe_dur,
                                 system=system)

        gyPre_times = [
            0, gyPre_2.rise_time, gyPre_2.rise_time + gyPre_2.flat_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5, gyPre_2.rise_time + gyPre_2.flat_time +
            gyPre_2.fall_time + g_ro.flat_time + 1e-5 + gyPre_1.rise_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5 + gyPre_1.rise_time + gyPre_1.flat_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5 + gyPre_1.rise_time + gyPre_1.flat_time +
            gyPre_1.fall_time
        ]
        gyPre_amp = [
            0, gyPre_2.amplitude, gyPre_2.amplitude, 0, 0, gyPre_1.amplitude,
            gyPre_1.amplitude, 0
        ]
        gyPre_all = make_extended_trapezoid(channel=enc[1],
                                            times=gyPre_times,
                                            amplitudes=gyPre_amp)

        if nRamp == 1:
            seq.add_block(rf05, g_ss)
            seq.add_block(gx_all, gyPre_all, gzrep_all)
        else:
            seq.add_block(rf05, g_ss)
            seq.add_block(gx_all, gyPre_all, gzrep_all)
    ############################################################################

    ############################################################################
    #           Actual Readout iterate number of phase encoding steps Ny
    for i in np.arange(1, Ny + 1, 1):
        # *******************************
        # Phase cycling
        if np.mod(i + Nramp, 2):
            rf.phase_offset = 0
            adc.phase_offset = 0
        else:
            rf.phase_offset = -pi
            adc.phase_offset = -pi
        # *******************************

        # ***************************************************************************************
        #                     Create phase encoding gradients
        if np.mod(Nramp, 2):
            gyPre_2 = make_trapezoid('y',
                                     area=phaseAreas[i - 1],
                                     duration=pe_dur,
                                     system=system)
            if i > 1:
                gyPre_1 = make_trapezoid(
                    'y',
                    area=-phaseAreas[np.mod(i + Ny - 2, Ny)],
                    duration=pe_dur,
                    system=system)
            else:
                gyPre_1 = make_trapezoid(
                    'y',
                    area=-phaseAreas[np.mod(i + Ny - 1, Ny)],
                    duration=pe_dur,
                    system=system)
        else:
            gyPre_2 = make_trapezoid('y',
                                     area=phaseAreas[i],
                                     duration=pe_dur,
                                     system=system)
            if i > 1:
                gyPre_1 = make_trapezoid(
                    'y',
                    area=-phaseAreas[np.mod(i + Ny - 2, Ny)],
                    duration=pe_dur,
                    system=system)
            else:
                gyPre_1 = make_trapezoid('y',
                                         area=phaseAreas[np.mod(
                                             i + Ny - 2, Ny)],
                                         duration=pe_dur,
                                         system=system)

        gyPre_times = [
            0, gyPre_2.rise_time, gyPre_2.rise_time + gyPre_2.flat_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5, gyPre_2.rise_time + gyPre_2.flat_time +
            gyPre_2.fall_time + g_ro.flat_time + 1e-5 + gyPre_2.rise_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5 + gyPre_2.rise_time + gyPre_2.flat_time,
            gyPre_2.rise_time + gyPre_2.flat_time + gyPre_2.fall_time +
            g_ro.flat_time + 1e-5 + gyPre_2.rise_time + gyPre_2.flat_time +
            gyPre_2.fall_time
        ]
        gyPre_amp = [
            0, gyPre_2.amplitude, gyPre_2.amplitude, 0, 0, -gyPre_2.amplitude,
            -gyPre_2.amplitude, 0
        ]

        # Verify if phase encoding gradient amplitude is not zero
        try:
            gyPre_all = make_extended_trapezoid(channel=enc[1],
                                                times=gyPre_times,
                                                amplitudes=gyPre_amp)
            flag_zerogy = 0
        except:
            gyPre_times = [
                0, pe_dur, pe_dur + g_ro.flat_time + 1e-5,
                pe_dur + g_ro.flat_time + 1e-5 + pe_dur
            ]
            gyPre_amp = [0, gyPre_2.amplitude, 0, 0]
            flag_zerogy = 1
        # ***************************************************************************************

        # add RF and Slice selecitve gradient
        seq.add_block(rf, g_ss)

        # add Phase and frequency encoding gradients and ADC
        if flag_zerogy == 1:
            seq.add_block(gx_all, gzrep_all, adc)
        else:
            seq.add_block(gx_all, gyPre_all, gzrep_all, adc)

    return seq, TR, Ny
Exemple #9
0
def write_tse(n=256,
              fov=250e-3,
              thk=5e-3,
              fa_exc=90,
              fa_ref=180,
              te=50e-3,
              tr=2000e-3,
              slice_locations=[0],
              turbo_factor=4,
              enc='xyz'):
    """
    2D TSE sequence with interleaved slices and user-defined turbo factor

    Inputs
    ------
    n : integer
        Matrix size (isotropic)
    fov : float
        Field-of-View in [meters]
    thk : float
        Slice thickness in [meters]
    fa_exc : float
        Initial excitation flip angle in [degrees]
    fa_ref : float
        All following flip angles for spin echo refocusing in [degrees]
    te : float
        Echo Time in [seconds]
    tr : float
        Repetition Time in [seconds]
    slice_locations : array_like
        Array of slice locations from isocenter in [meters]
    turbo_factor : integer
        Number of echoes per TR
    enc : str
        Spatial encoding directions; 1st - readout; 2nd - phase encoding; 3rd - slice select
        Use str with any permutation of x, y, and z to obtain orthogonal slices
        e.g. The default 'xyz' means axial(z) slice with readout in x and phase encoding in y

    Returns
    -------
    seq : pypulseq.Sequence.sequence Sequence object
        Output sequence object. Can be saved with seq.write('file_name.seq')
    pe_order : numpy.ndarray
        (turbo_factor) x (number_of_excitations) matrix of phase encoding order
        This is required for phase sorting before ifft2 reconstruction.

    """
    # Set system limits
    ramp_time = 250e-6  # Ramp up/down time for all gradients where this is specified
    system = Opts(
        max_grad=32,
        grad_unit='mT/m',
        max_slew=130,
        slew_unit='T/m/s',
        rf_ringdown_time=100e-6,  # changed from 30e-6
        rf_dead_time=100e-6,
        adc_dead_time=20e-6)
    # Initialize sequence
    seq = Sequence(system)

    # Spatial encoding directions
    ch_ro = enc[0]
    ch_pe = enc[1]
    ch_ss = enc[2]

    # Derived parameters
    Nf, Np = (n, n)
    delta_k = 1 / fov
    k_width = Nf * delta_k

    # Number of echoes per excitation (i.e. turbo factor)
    n_echo = turbo_factor

    # Readout duration
    readout_time = 6.4e-3 + 2 * system.adc_dead_time

    # Excitation pulse duration
    t_ex = 2.5e-3
    t_exwd = t_ex + system.rf_ringdown_time + system.rf_dead_time

    # Refocusing pulse duration
    t_ref = 2e-3
    t_refwd = t_ref + system.rf_ringdown_time + system.rf_dead_time

    # Time gaps for spoilers
    t_sp = 0.5 * (te - readout_time - t_refwd
                  )  # time gap between pi-pulse and readout
    t_spex = 0.5 * (te - t_exwd - t_refwd
                    )  # time gap between pi/2-pulse and pi-pulse

    # Spoiling factors
    fsp_r = 1  # in readout direction per refocusing
    fsp_s = 0.5  # in slice direction per refocusing

    # 3. Calculate sequence components

    ## Slice-selective RF pulses & gradient
    #* RF pulses with zero frequency shift are created. The frequency is then changed before adding the pulses to sequence blocks for each slice.

    # 90 deg pulse (+y')
    rf_ex_phase = np.pi / 2
    flip_ex = fa_exc * np.pi / 180
    rf_ex, g_ss, _ = make_sinc_pulse(flip_angle=flip_ex,
                                     system=system,
                                     duration=t_ex,
                                     slice_thickness=thk,
                                     apodization=0.5,
                                     time_bw_product=4,
                                     phase_offset=rf_ex_phase,
                                     return_gz=True)
    gs_ex = make_trapezoid(channel=ch_ss,
                           system=system,
                           amplitude=g_ss.amplitude,
                           flat_time=t_exwd,
                           rise_time=ramp_time)

    # 180 deg pulse (+x')
    rf_ref_phase = 0
    flip_ref = fa_ref * np.pi / 180
    rf_ref, gz, _ = make_sinc_pulse(flip_angle=flip_ref,
                                    system=system,
                                    duration=t_ref,
                                    slice_thickness=thk,
                                    apodization=0.5,
                                    time_bw_product=4,
                                    phase_offset=rf_ref_phase,
                                    use='refocusing',
                                    return_gz=True)
    gs_ref = make_trapezoid(channel=ch_ss,
                            system=system,
                            amplitude=gs_ex.amplitude,
                            flat_time=t_refwd,
                            rise_time=ramp_time)

    rf_ex, g_ss, _ = make_sinc_pulse(flip_angle=flip_ex,
                                     system=system,
                                     duration=t_ex,
                                     slice_thickness=thk,
                                     apodization=0.5,
                                     time_bw_product=4,
                                     phase_offset=rf_ex_phase,
                                     return_gz=True)

    ## Make gradients and ADC
    # gs_spex : slice direction spoiler between initial excitation and 1st 180 pulse
    # gs_spr : slice direction spoiler between 180 pulses
    # gr_spr : readout direction spoiler; area is (fsp_r) x (full readout area)

    # SS spoiling
    ags_ex = gs_ex.area / 2
    gs_spr = make_trapezoid(channel=ch_ss,
                            system=system,
                            area=ags_ex * (1 + fsp_s),
                            duration=t_sp,
                            rise_time=ramp_time)
    gs_spex = make_trapezoid(channel=ch_ss,
                             system=system,
                             area=ags_ex * fsp_s,
                             duration=t_spex,
                             rise_time=ramp_time)

    # Readout gradient and ADC
    gr_acq = make_trapezoid(channel=ch_ro,
                            system=system,
                            flat_area=k_width,
                            flat_time=readout_time,
                            rise_time=ramp_time)

    # No need for risetime delay since it is set at beginning of flattime; delay is ADC deadtime
    adc = make_adc(num_samples=Nf,
                   duration=gr_acq.flat_time - 40e-6,
                   delay=20e-6)

    # RO spoiling
    gr_spr = make_trapezoid(channel=ch_ro,
                            system=system,
                            area=gr_acq.area * fsp_r,
                            duration=t_sp,
                            rise_time=ramp_time)

    # Following is not used anywhere
    # gr_spex = make_trapezoid(channel=ch_ro, system=system, area=gr_acq.area * (1 + fsp_r), duration=t_spex, rise_time=ramp_time)

    # Prephasing gradient in RO direction
    agr_preph = gr_acq.area / 2 + gr_spr.area
    gr_preph = make_trapezoid(channel=ch_ro,
                              system=system,
                              area=agr_preph,
                              duration=t_spex,
                              rise_time=ramp_time)

    # Phase encoding areas
    # Need to export the pe_order for reconsturuction

    # Number of readouts/echoes to be produced per TR
    n_ex = math.floor(Np / n_echo)
    pe_steps = np.arange(1, n_echo * n_ex + 1) - 0.5 * n_echo * n_ex - 1
    if divmod(n_echo, 2)[1] == 0:  # If there is an even number of echoes
        pe_steps = np.roll(pe_steps, -round(n_ex / 2))

    pe_order = pe_steps.reshape((n_ex, n_echo), order='F').T

    savemat('pe_info.mat', {'order': pe_order, 'dims': ['n_echo', 'n_ex']})
    phase_areas = pe_order * delta_k

    # Split gradients and recombine into blocks

    # gs1 : ramp up of gs_ex
    gs1_times = [0, gs_ex.rise_time]
    gs1_amp = [0, gs_ex.amplitude]
    gs1 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs1_times,
                                  amplitudes=gs1_amp)

    # gs2 : flat part of gs_ex
    gs2_times = [0, gs_ex.flat_time]
    gs2_amp = [gs_ex.amplitude, gs_ex.amplitude]
    gs2 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs2_times,
                                  amplitudes=gs2_amp)

    # gs3 : Bridged slice pre-spoiler
    gs3_times = [
        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 = [
        gs_ex.amplitude, gs_spex.amplitude, gs_spex.amplitude, gs_ref.amplitude
    ]
    gs3 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs3_times,
                                  amplitudes=gs3_amp)

    # gs4 : Flat slice selector for pi-pulse
    gs4_times = [0, gs_ref.flat_time]
    gs4_amp = [gs_ref.amplitude, gs_ref.amplitude]
    gs4 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs4_times,
                                  amplitudes=gs4_amp)

    # gs5 : Bridged slice post-spoiler
    gs5_times = [
        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 = [gs_ref.amplitude, gs_spr.amplitude, gs_spr.amplitude, 0]
    gs5 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs5_times,
                                  amplitudes=gs5_amp)

    # gs7 : The gs3 for next pi-pulse
    gs7_times = [
        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 = [0, gs_spr.amplitude, gs_spr.amplitude, gs_ref.amplitude]
    gs7 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs7_times,
                                  amplitudes=gs7_amp)

    # gr3 : pre-readout gradient
    gr3 = gr_preph

    # gr5 : Readout post-spoiler
    gr5_times = [
        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 = [0, gr_spr.amplitude, gr_spr.amplitude, gr_acq.amplitude]
    gr5 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr5_times,
                                  amplitudes=gr5_amp)

    # gr6 : Flat readout gradient
    gr6_times = [0, readout_time]
    gr6_amp = [gr_acq.amplitude, gr_acq.amplitude]
    gr6 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr6_times,
                                  amplitudes=gr6_amp)

    # gr7 : the gr3 for next repeat
    gr7_times = [
        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 = [gr_acq.amplitude, gr_spr.amplitude, gr_spr.amplitude, 0]
    gr7 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr7_times,
                                  amplitudes=gr7_amp)

    # Timing (delay) calculations

    # delay_TR : delay at the end of each TSE pulse train (i.e. each TR)
    t_ex = gs1.t[-1] + gs2.t[-1] + gs3.t[-1]
    t_ref = gs4.t[-1] + gs5.t[-1] + gs7.t[-1] + readout_time
    t_end = gs4.t[-1] + gs5.t[-1]
    TE_train = t_ex + n_echo * t_ref + t_end
    TR_fill = (tr - n_slices * TE_train) / n_slices
    TR_fill = system.grad_raster_time * round(
        TR_fill / system.grad_raster_time)
    if TR_fill < 0:
        TR_fill = 1e-3
        print(
            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)

    # Add building blocks to sequence

    for k_ex in range(n_ex + 1):  # For each TR
        for s in range(n_slices):  # For each slice (multislice)

            if slice_locations is None:
                rf_ex.freq_offset = gs_ex.amplitude * thk * (
                    s - (n_slices - 1) / 2)
                rf_ref.freq_offset = gs_ref.amplitude * thk * (
                    s - (n_slices - 1) / 2)
                rf_ex.phase_offset = rf_ex_phase - 2 * np.pi * rf_ex.freq_offset * calc_rf_center(
                    rf_ex)[0]
                rf_ref.phase_offset = rf_ref_phase - 2 * np.pi * rf_ref.freq_offset * calc_rf_center(
                    rf_ref)[0]
            else:
                rf_ex.freq_offset = gs_ex.amplitude * slice_locations[s]
                rf_ref.freq_offset = gs_ref.amplitude * slice_locations[s]
                rf_ex.phase_offset = rf_ex_phase - 2 * np.pi * rf_ex.freq_offset * calc_rf_center(
                    rf_ex)[0]
                rf_ref.phase_offset = rf_ref_phase - 2 * np.pi * rf_ref.freq_offset * calc_rf_center(
                    rf_ref)[0]

            seq.add_block(gs1)
            seq.add_block(gs2, rf_ex)  # make sure gs2 has channel ch_ss
            seq.add_block(gs3, gr3)

            for k_echo in range(n_echo):  # For each echo
                if k_ex > 0:
                    phase_area = phase_areas[k_echo, k_ex - 1]
                else:
                    # First TR is skipped so zero phase encoding is needed
                    phase_area = 0.0  # 0.0 and not 0 because -phase_area should successfully result in negative zero

                gp_pre = make_trapezoid(channel=ch_pe,
                                        system=system,
                                        area=phase_area,
                                        duration=t_sp,
                                        rise_time=ramp_time)
                # print('gp_pre info: ', gp_pre)

                gp_rew = make_trapezoid(channel=ch_pe,
                                        system=system,
                                        area=-phase_area,
                                        duration=t_sp,
                                        rise_time=ramp_time)

                seq.add_block(gs4, rf_ref)
                seq.add_block(gs5, gr5, gp_pre)

                # Skipping first TR
                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)

    # Check timing to make sure sequence runs on scanner
    seq.check_timing()

    return seq, pe_order
Exemple #10
0
def write_irse_interleaved_split_gradient(n=256,
                                          fov=250e-3,
                                          thk=5e-3,
                                          fa=90,
                                          te=12e-3,
                                          tr=2000e-3,
                                          ti=150e-3,
                                          slice_locations=[0],
                                          enc='xyz'):
    """
    2D IRSE sequence with overlapping gradient ramps and interleaved slices

    Inputs
    ------
    n : integer
        Matrix size (isotropic)
    fov : float
        Field-of-View in [meters]
    thk : float
        Slice thickness in [meters]
    fa : float
        Flip angle in [degrees]
    te : float
        Echo Time in [seconds]
    tr : float
        Repetition Time in [seconds]
    ti : float
        Inversion Time in [seconds]
    slice_locations : array_like
        Array of slice locations from isocenter in [meters]
    enc : str
        Spatial encoding directions; 1st - readout; 2nd - phase encoding; 3rd - slice select
        Use str with any permutation of x, y, and z to obtain orthogonal slices
        e.g. The default 'xyz' means axial(z) slice with readout in x and phase encoding in y


    Returns
    -------
    seq : pypulseq.Sequence.sequence Sequence object
        Output sequence object. Can be saved with seq.write('file_name.seq')
    sl_order : numpy.ndarray
        Randomly generated slice order. Useful for reconstruction.

    """
    # =========
    # SYSTEM LIMITS
    # =========
    # Set the hardware limits and initialize sequence object
    dG = 250e-6  # Fixed ramp time for all gradients
    system = Opts(max_grad=32,
                  grad_unit='mT/m',
                  max_slew=130,
                  slew_unit='T/m/s',
                  rf_ringdown_time=100e-6,
                  rf_dead_time=100e-6,
                  adc_dead_time=10e-6)
    seq = Sequence(system)

    # =========
    # TIME CALCULATIONS
    # =========
    readout_time = 6.4e-3 + 2 * system.adc_dead_time
    t_ex = 2.5e-3
    t_exwd = t_ex + system.rf_ringdown_time + system.rf_dead_time
    t_ref = 2e-3
    t_refwd = t_ref + system.rf_ringdown_time + system.rf_dead_time
    t_sp = 0.5 * (te - readout_time - t_refwd)
    t_spex = 0.5 * (te - t_exwd - t_refwd)
    fsp_r = 1
    fsp_s = 0.5

    # =========
    # ENCODING DIRECTIONS
    # ==========
    ch_ro = enc[0]
    ch_pe = enc[1]
    ch_ss = enc[2]

    # =========
    # RF AND GRADIENT SHAPES - BASED ON RESOLUTION REQUIREMENTS : kmax and Npe
    # =========

    # RF Phases
    rf_ex_phase = np.pi / 2
    rf_ref_phase = 0

    # Excitation phase (90 deg)
    flip_ex = 90 * np.pi / 180
    rf_ex, gz, _ = make_sinc_pulse(flip_angle=flip_ex,
                                   system=system,
                                   duration=t_ex,
                                   slice_thickness=thk,
                                   apodization=0.5,
                                   time_bw_product=4,
                                   phase_offset=rf_ex_phase,
                                   return_gz=True)
    gs_ex = make_trapezoid(channel=ch_ss,
                           system=system,
                           amplitude=gz.amplitude,
                           flat_time=t_exwd,
                           rise_time=dG)

    # Refocusing (same gradient & RF is used for initial inversion)
    flip_ref = fa * np.pi / 180
    rf_ref, gz, _ = make_sinc_pulse(flip_angle=flip_ref,
                                    system=system,
                                    duration=t_ref,
                                    slice_thickness=thk,
                                    apodization=0.5,
                                    time_bw_product=4,
                                    phase_offset=rf_ref_phase,
                                    use='refocusing',
                                    return_gz=True)
    gs_ref = make_trapezoid(channel=ch_ss,
                            system=system,
                            amplitude=gs_ex.amplitude,
                            flat_time=t_refwd,
                            rise_time=dG)

    ags_ex = gs_ex.area / 2
    gs_spr = make_trapezoid(channel=ch_ss,
                            system=system,
                            area=ags_ex * (1 + fsp_s),
                            duration=t_sp,
                            rise_time=dG)
    gs_spex = make_trapezoid(channel=ch_ss,
                             system=system,
                             area=ags_ex * fsp_s,
                             duration=t_spex,
                             rise_time=dG)

    delta_k = 1 / fov
    k_width = n * delta_k

    gr_acq = make_trapezoid(channel=ch_ro,
                            system=system,
                            flat_area=k_width,
                            flat_time=readout_time,
                            rise_time=dG)
    adc = make_adc(num_samples=n,
                   duration=gr_acq.flat_time - 40e-6,
                   delay=20e-6)
    gr_spr = make_trapezoid(channel=ch_ro,
                            system=system,
                            area=gr_acq.area * fsp_r,
                            duration=t_sp,
                            rise_time=dG)
    gr_spex = make_trapezoid(channel=ch_ro,
                             system=system,
                             area=gr_acq.area * (1 + fsp_r),
                             duration=t_spex,
                             rise_time=dG)

    agr_spr = gr_spr.area
    agr_preph = gr_acq.area / 2 + agr_spr
    gr_preph = make_trapezoid(channel=ch_ro,
                              system=system,
                              area=agr_preph,
                              duration=t_spex,
                              rise_time=dG)

    phase_areas = (np.arange(n) - n / 2) * delta_k

    # Split gradients and recombine into blocks
    gs1_times = [0, gs_ex.rise_time]
    gs1_amp = [0, gs_ex.amplitude]
    gs1 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs1_times,
                                  amplitudes=gs1_amp)

    gs2_times = [0, gs_ex.flat_time]
    gs2_amp = [gs_ex.amplitude, gs_ex.amplitude]
    gs2 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs2_times,
                                  amplitudes=gs2_amp)

    gs3_times = [
        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 = [
        gs_ex.amplitude, gs_spex.amplitude, gs_spex.amplitude, gs_ref.amplitude
    ]
    gs3 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs3_times,
                                  amplitudes=gs3_amp)

    gs4_times = [0, gs_ref.flat_time]
    gs4_amp = [gs_ref.amplitude, gs_ref.amplitude]
    gs4 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs4_times,
                                  amplitudes=gs4_amp)

    gs5_times = [
        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 = [gs_ref.amplitude, gs_spr.amplitude, gs_spr.amplitude, 0]
    gs5 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs5_times,
                                  amplitudes=gs5_amp)

    gs7_times = [
        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 = [0, gs_spr.amplitude, gs_spr.amplitude, gs_ref.amplitude]
    gs7 = make_extended_trapezoid(channel=ch_ss,
                                  times=gs7_times,
                                  amplitudes=gs7_amp)

    gr3 = gr_preph

    gr5_times = [
        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 = [0, gr_spr.amplitude, gr_spr.amplitude, gr_acq.amplitude]
    gr5 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr5_times,
                                  amplitudes=gr5_amp)

    gr6_times = [0, readout_time]
    gr6_amp = [gr_acq.amplitude, gr_acq.amplitude]
    gr6 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr6_times,
                                  amplitudes=gr6_amp)

    gr7_times = [
        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 = [gr_acq.amplitude, gr_spr.amplitude, gr_spr.amplitude, 0]
    gr7 = make_extended_trapezoid(channel=ch_ro,
                                  times=gr7_times,
                                  amplitudes=gr7_amp)

    t_ex = gs1.t[-1] + gs2.t[-1] + gs3.t[-1]
    t_ref = gs4.t[-1] + gs5.t[-1] + gs7.t[-1] + readout_time
    t_end = gs4.t[-1] + gs5.t[-1]

    # Calculate maximum number of slices that can fit in one TR
    # Without spoilers on each side
    TE_prime = 0.5 * calc_duration(gs_ref) + ti + te + 0.5 * readout_time + np.max(
        [calc_duration(gs7), calc_duration(gr7)]) + \
               calc_duration(gs4) + calc_duration(gs5)

    ns_per_TR = np.floor(tr / TE_prime)
    print('Number of slices that can be accommodated = ' + str(ns_per_TR))

    # Lengthen TR to accommodate slices if needed, and display message
    n_slices = len(slice_locations)
    if (ns_per_TR < n_slices):
        print(
            f'TR too short, adapted to include all slices to: {n_slices * TE_prime + 50e-6} s'
        )
        TR = round(n_slices * TE_prime + 50e-6, ndigits=5)
        print('New TR = ' + str(TR))
        ns_per_TR = np.floor(TR / TE_prime)
    if (n_slices < ns_per_TR):
        ns_per_TR = n_slices
    # randperm so that adjacent slices do not get excited one after the other
    sl_order = np.random.permutation(n_slices)

    print('Number of slices acquired per TR = ' + str(ns_per_TR))

    # Delays
    TI_fill = ti - (0.5 * calc_duration(gs_ref) + calc_duration(gs1) +
                    0.5 * calc_duration(gs2))
    delay_TI = make_delay(TI_fill)
    TR_fill = tr - ns_per_TR * TE_prime
    delay_TR = make_delay(TR_fill)

    for k_ex in range(n):
        phase_area = phase_areas[k_ex]
        gp_pre = make_trapezoid(channel=ch_pe,
                                system=system,
                                area=phase_area,
                                duration=t_sp,
                                rise_time=dG)
        gp_rew = make_trapezoid(channel=ch_pe,
                                system=system,
                                area=-phase_area,
                                duration=t_sp,
                                rise_time=dG)
        s_in_TR = 0

        for s in range(len(sl_order)):

            # rf_ex.freq_offset = gs_ex.amplitude * slice_thickness * (sl_order[s] - (n_slices - 1) / 2)
            # rf_ref.freq_offset = gs_ref.amplitude * slice_thickness * (sl_order[s] - (n_slices - 1) / 2)

            rf_ex.freq_offset = gs_ex.amplitude * slice_locations[sl_order[s]]
            rf_ref.freq_offset = gs_ref.amplitude * slice_locations[
                sl_order[s]]

            rf_ex.phase_offset = rf_ex_phase - 2 * np.pi * rf_ex.freq_offset * calc_rf_center(
                rf_ex)[0]
            rf_ref.phase_offset = rf_ref_phase - 2 * np.pi * rf_ref.freq_offset * calc_rf_center(
                rf_ref)[0]

            # Inversion using refocusing pulse
            seq.add_block(gs_ref, rf_ref)
            seq.add_block(delay_TI)

            # SE portion
            seq.add_block(gs1)
            seq.add_block(gs2, rf_ex)
            seq.add_block(gs3, gr3)

            seq.add_block(gs4, rf_ref)

            seq.add_block(gs5, gr5, gp_pre)
            seq.add_block(gr6, adc)

            seq.add_block(gs7, gr7, gp_rew)

            seq.add_block(gs4)
            seq.add_block(gs5)
            s_in_TR += 1
            if (s_in_TR == ns_per_TR):
                seq.add_block(delay_TR)
                s_in_TR = 0

    # Check timing to make sure sequence runs on scanner
    seq.check_timing()

    return seq, sl_order
Exemple #11
0
def split_gradient_at(
    grad: SimpleNamespace, time_point: float, system: Opts = Opts()
) -> Union[SimpleNamespace, Tuple[SimpleNamespace, SimpleNamespace]]:
    """
    Split gradient waveform `grad` into two at time point `time_point`.

    Parameters
    ----------
    grad : SimpleNamespace
        Gradient event to be split into two gradient events.
    time_point : float
        Time point at which `grad` will be split into two gradient waveforms.
    system : Opts, optional, default=Opts()
        System limits.

    Returns
    -------
    grad1, grad2 : SimpleNamespace
        Gradient waveforms after splitting.

    Raises
    ------
    ValueError
        If non-gradient event is passed.
    """
    grad_raster_time = system.grad_raster_time

    time_index = round(time_point / grad_raster_time)
    time_point = round(time_index * grad_raster_time,
                       6)  # Work around floating-point arithmetic limitation
    time_index += 1

    if grad.type == 'trap':
        ch = grad.channel
        grad.delay = round(grad.delay / grad_raster_time) * grad_raster_time
        grad.rise_time = round(
            grad.rise_time / grad_raster_time) * grad_raster_time
        grad.flat_time = round(
            grad.flat_time / grad_raster_time) * grad_raster_time
        grad.fall_time = round(
            grad.fall_time / grad_raster_time) * grad_raster_time

        if grad.flat_time == 0:
            times = [0, grad.rise_time, grad.rise_time + grad.fall_time]
            amplitudes = [0, grad.amplitude, 0]
        else:
            times = [
                0, grad.rise_time, grad.rise_time + grad.flat_time,
                grad.rise_time + grad.flat_time + grad.fall_time
            ]
            amplitudes = [0, grad.amplitude, grad.amplitude, 0]

        if time_point < grad.delay:
            times = np.insert(grad.delay + times, 0, 0)
            amplitudes = [0, amplitudes]
            grad.delay = 0

        amplitudes = np.array(amplitudes)
        times = np.array(times).round(
            6)  # Work around floating-point arithmetic limitation

        amp_tp = np.interp(x=time_point, xp=times, fp=amplitudes)
        times1 = np.append(times[np.where(times < time_point)], time_point)
        amplitudes1 = np.append(amplitudes[np.where(times < time_point)],
                                amp_tp)
        times2 = np.insert(times[times > time_point], 0,
                           time_point) - time_point
        amplitudes2 = np.insert(amplitudes[times > time_point], 0, amp_tp)

        grad1 = make_extended_trapezoid(channel=ch,
                                        system=system,
                                        times=times1,
                                        amplitudes=amplitudes1,
                                        skip_check=True)
        grad1.delay = grad.delay
        grad2 = make_extended_trapezoid(channel=ch,
                                        system=system,
                                        times=times2,
                                        amplitudes=amplitudes2,
                                        skip_check=True)
        grad2.delay = time_point
        return grad1, grad2
    elif grad.type == 'grad':
        if time_index == 1 or time_index >= len(grad.t):
            return grad
        else:
            grad1 = grad
            grad2 = grad
            grad1.last = 0.5 * (grad.waveform[time_index - 1] +
                                grad.waveform[time_index])
            grad2.first = grad1.last
            grad2.delay = grad.delay + grad.t[time_index]
            grad1.t = grad.t[:time_index]
            grad1.waveform = grad.waveform[:time_index]
            grad2.t = grad.t[time_index:] - time_point
            grad2.waveform = grad.waveform[time_index:]
            return grad1, grad2
    else:
        raise ValueError('Splitting of unsupported event.')