Example #1
0
 def test_g_shape_out(self):
     g1 = makeSystemlength(g=np.ones((1, 50)), raster=4e-6)
     self.assertEqual(g1.shape[1], 1)
     g2 = makeSystemlength(g=np.ones((50, 1)), raster=4e-6)
     self.assertEqual(g2.shape[1], 1)
     g4 = makeSystemlength(g=np.ones((50, )), raster=4e-6)
     self.assertEqual(g4.shape[1], 1)
def rf_interpolate(rf_signal, rf_raster_time=1e-6):
    '''
    RF waveform interpolation to meet raster time requirements

    Parameters
    ----------
    rf_signal : numpy.ndarray
        RF signal with raster time 4 microseconds
    rf_raster_time : float
        Raster time for interpolation. Defalult is 1 microsecond

    Returns
    -------
    rf_out :  numpy.ndarray
        Interpolated RF waveform with new raster time
    '''
    if rf_raster_time <= 0:
        raise ValueError('Raster time has to be larger than 0')

    GE_rf_raster_time = 4e-6
    T = len(rf_signal) * GE_rf_raster_time  # pulse duration
    t_GE = np.linspace(0, T - GE_rf_raster_time, T / GE_rf_raster_time)
    t_out = np.linspace(0, T - rf_raster_time, T / rf_raster_time)
    rf_out = np.interp(t_out, t_GE, rf_signal.flatten())
    rf_out = makeSystemlength(rf_out, rf_raster_time)

    return rf_out
def grad_interpolate(grad_signal, grad_raster_time=10e-6):
    '''
    Gradient waveform interpolation to meet raster time requirements

    Parameters
    ----------
    grad_signal : numpy.ndarray
        Gradient signal with raster time 4 microseconds
    grad_raster_time : float
        Raster time for interpolation. Default is 10 microseconds

    Returns
    -------
    grad_out : numpy.ndarray
        Interpolated gradient waveform with new raster time
    '''
    if grad_raster_time <= 0:
        raise ValueError('Raster time has to be larger than 0')

    GE_grad_raster_time = 4e-6
    T = len(grad_signal) * GE_grad_raster_time  # pulse duration
    t_GE = np.linspace(0, T - GE_grad_raster_time, T / GE_grad_raster_time)
    t_out = np.linspace(0, T - grad_raster_time, T / grad_raster_time)
    grad_out = np.interp(t_out, t_GE, grad_signal.flatten())
    grad_out = makeSystemlength(grad_out, grad_raster_time)

    return grad_out
def make_crusher(ncycles, opslthick, gzarea, max_grad, max_slew, sys_lims):
    '''
    Creates a crusher gradient within the system limits given the number of phase cycles across a thickness

    Parameters
    ----------
    ncycles : int
        Number of cycles of phase across slice/slab
    opslthick : float
        Slice/slab thickness in meters
    gzarea : int
        Half-area of slice-select gradient in mT/m*sec. Default: 0.
    max_grad : int
        Max gradient in mT/m
    max_slew : float
        Max slew rate  in T/m/s
    sys_lims : dict
        System limits

    Returns
    -------
    gcrush : numpy.ndarray
        Crusher gradient waveform in Hz/m
    '''
    if ncycles <= 0:
        raise ValueError(
            'Number of phase cycles cannot be equal or smaller than 0.')
    if opslthick == 0:
        raise ValueError('Thickness cannot be 0')
    if 'gamma' not in sys_lims:
        raise ValueError('Please specify gamma in the system limits')

    gamma = sys_lims['gamma']  # Hz/T

    area = ncycles / (gamma * opslthick) * 1e3  # mT/m*s

    dt = sys_lims['grad_raster_time']  # seconds
    gcrush = trapwave2(area - gzarea, max_grad, max_slew,
                       dt) * 1e-3 * gamma  # Hz/m
    gcrush = makeSystemlength(gcrush.reshape(-1, 1),
                              sys_lims['grad_raster_time'])
    gcrush[np.where(gcrush == -0.)] = 0
    return gcrush
Example #5
0
 def test_remove_negzero(self):
     g = makeSystemlength(g=-np.zeros((4, 1)), raster=4e-6)
     self.assertNotEqual(g.all(), (-np.zeros((4, 1))).all)
Example #6
0
 def test_g_len_out(self):
     gGE = makeSystemlength(g=np.ones((43, 1)), raster=4e-6)
     self.assertEqual(len(gGE) % 4, 0)
     gSiemens = makeSystemlength(g=np.ones((43, 1)), raster=1e-6)
     self.assertEqual(len(gSiemens) % 2, 0)
Example #7
0
 def test_rater_val(self):
     with self.assertRaises(ValueError):
         makeSystemlength(g=np.ones((50, 1)), raster=0)
     with self.assertRaises(ValueError):
         makeSystemlength(g=np.ones((50, 1)), raster=-1)
Example #8
0
def make_slr_rf(flip_angle: float, slice_thickness: float,
                time_bw_product: float, duration: float, ncycles: int,
                system: dict):
    '''
    Creates a slice-selective SLR pulse with gradient crusher (or balancing blip) before it.

    Parameters
    ----------
    flip_angle : float
        Flip angle in degrees.
    slice_thickness : float
        Slab thickness in millimeters (mm) of accompanying slice select trapezoidal event. The slice thickness determines the area of the
        slice select event.
    time_bw_product : float
        Time-bandwidth product of SLR pulse
    duration : float
        Duration in seconds (s).
    ncycles : int
        number of cycles of phase (spoiler) (0 = balanced)
    system : dict
        System limits. Default is a system limits object initialised to default values.

    Returns
    -------
    rf : SimpleNamespace
        Radio-frequency slr pulse event.
    gz : SimpleNamespace, optional
        Accompanying slice select trapezoidal gradient event. Returned only if `slice_thickness` is not zero.
    '''
    if flip_angle < 0 or flip_angle > 360:
        raise ValueError('Flip angle in degrees has to be within [0, 360]')
    if slice_thickness <= 0:
        raise ValueError('Slice thickness has to be larger than 0')
    if time_bw_product < 1.5:
        raise ValueError('Time bandwidth product has to be at least 1.5')
    if duration <= 0:
        raise ValueError('Pulse duration cannot be 0 or negative')

    # TODO: Remove commented lines
    # TODO: Remove frequency offset from input
    pulse_type = 'ex'  # Only supported type by now
    # make RF waveform
    dt = system['rf_raster_time']  # s
    res = round(duration / dt)  # Number of (10us) samples in RF waveform
    duration = res * dt

    nrf = 200  # number of samples for SLR design
    rf = JP_dzrf(nrf, time_bw_product, pulse_type)
    rf = np.real(rf)
    rf = resample_poly(rf, res, nrf)

    flip = pi / 2

    rf = flip * rf / np.sum(rf, axis=0)
    rf = rf / dt / 2 / pi  # Hz

    rf = np.concatenate((rf.reshape(len(rf), 1), np.zeros((len(rf) % 2, 1))),
                        axis=0)  # Make duration even
    npix = len(rf)

    iref, _ = np.where(rf == np.max(rf))  # Center of RF pulse

    # make slice-select gradient waveform
    bw = time_bw_product / duration  # Hz
    gplateau = bw / (system['gamma'] * slice_thickness) * 1e3  # mT / m

    if gplateau > system['max_grad']:
        raise ValueError('Gradient exceeds the maximum gradient')

    # slice-select trapezoid
    gss = gplateau * np.ones((1, npix))
    s = system['max_slew'] * 1e3 * dt * 0.999  # mT/m
    gss_ramp = np.arange(s, gplateau, s)
    if np.all(gss_ramp == 0):
        gss_ramp = np.asarray([[0]])

    if gplateau - gss_ramp[-1] > 0:
        gss_ramp = np.concatenate(
            (gss_ramp.reshape(1, len(gss_ramp)),
             ((gplateau + gss_ramp[-1]) / 2).reshape(1, 1)),
            axis=1)

    gss_trap = np.concatenate((gss_ramp, gss, np.fliplr(gss_ramp)), axis=1)
    iref = iref[0] + gss_ramp.shape[1]

    # slice-select rephaser trapezoid

    arearep = np.sum(gss_trap[:, iref:]) * dt  # mT / m * s
    gzrep = -trapwave2(arearep, system['max_grad'], system['max_slew'], dt)

    # slice-select prephaser trapezoid

    areaprep = np.sum(gss_trap[:, :iref]) * dt  # mT / m * s
    gzprep = -trapwave2(areaprep, system['max_grad'], system['max_slew'], dt)

    #irep = (np.concatenate((gzprep, gss_trap), axis=1)).shape[1]
    #iref = iref + gzprep.shape[1]
    gex = np.concatenate(
        (gzprep, gss_trap, gzrep), axis=1) * 1e-3 * system['gamma']  # Hz/m
    #idep = gzprep.shape[1]

    # make gss and rf the same length
    rf = np.vstack((0 * gzprep.reshape(-1, 1), np.zeros(
        (gss_ramp.shape[1], 1)), rf,
                    np.zeros((gss_ramp.shape[1] + gzrep.shape[1], 1))))
    # rf = np.concatenate((0 * gzprep, np.zeros((1, gss_ramp.shape[1])), rf.T, np.zeros((1, gss_ramp.shape[1] + gzrep.shape[1]))), axis=1)

    # ensure that duration is on a 40 us (4 sample) boundary
    rf = makeSystemlength(rf, system['grad_raster_time'])
    gex = makeSystemlength(gex, system['grad_raster_time'])

    # make sure waveforms start and end at zero
    rf = np.vstack((0, rf, 0))
    # rf = np.concatenate((np.asarray([[0]]), rf, np.asarray([[0]])), axis=1)
    gex = np.vstack((0, gex.reshape(-1, 1), 0))
    # gex = np.concatenate((np.asarray([[0]]), gex, np.asarray([[0]])), axis=1)

    rf = rf / 90 * flip_angle

    # slice offset frequency
    # gfreq = system['gamma']*gplateau*freq_offset

    #remove -0 values
    # rf[np.where(rf == -0.)] = 0
    # gex[np.where(gex == -0.)] = 0
    return rf, gex
              max_slew=sys['max_slew'],
              slew_unit=sys['slew_unit'],
              grad_raster_time=sys['grad_raster_time'],
              rf_raster_time=sys['rf_raster_time'])
seq = Sequence(system)

###
# 2. Generate the RF and gradient waveforms
###
'''
Fat Saturation Module
'''
slThick = 10  # dummy value [m]
rf_fs_wav, _ = make_slr_rf(rf_fatsat['flip'], slThick, rf_fatsat['tbw'],
                           rf_fatsat['dur'], 0, sys_gen)
rf_fs_wav = makeSystemlength(rf_fs_wav, sys_gen['rf_raster_time'])
rf_fs_wav = rf_interpolate(rf_fs_wav, sys_acq['rf_raster_time'])
'''
Slab selective excitation module and PRESTO gradients (Tip down module)
'''
# RF pulse
nCyclesSpoil = 0
rf_td_wav, g_td_wav = make_slr_rf(rf_tipdown['flip'],
                                  rf_tipdown['slab_thickness'],
                                  rf_tipdown['tbw'], rf_tipdown['dur'],
                                  nCyclesSpoil, sys_gen)

# Spoiler gradients
gspoil1 = make_crusher(fmri['nCyclesSpoil'], acq_params['dz'], 0,
                       sys_gen['max_grad'],
                       0.9 * sys_gen['max_slew'] / sqrt(2), sys_gen)