Example #1
0
 def test_maxGrad(self):
     maxGrad = 50
     gtrap = trapwave2(area=1,
                       mxg=maxGrad,
                       mxs=system['max_slew'],
                       rasterTime=system['grad_raster_time'])
     self.assertLess(gtrap.max(), maxGrad)
Example #2
0
 def test_maxSlew(self):
     maxSlew = 150
     gtrap = trapwave2(area=1,
                       mxg=system['max_grad'],
                       mxs=maxSlew,
                       rasterTime=system['grad_raster_time'])
     slew = np.diff(gtrap) / system['grad_raster_time'] * 1e-3
     self.assertLess(slew.max(), maxSlew)
Example #3
0
    def test_area(self):
        area_in = 10
        gtrap = trapwave2(area=area_in,
                          mxg=system['max_grad'],
                          mxs=system['max_slew'],
                          rasterTime=system['grad_raster_time'])

        base1 = gtrap.shape[1]
        base2 = len(np.where(gtrap == gtrap.max())[1])
        height = gtrap.max()
        area_out = (base1 + base2
                    ) * system['grad_raster_time'] / 2 * height  # Hz/m*sec
        self.assertEqual(area_in, round(area_out))
def make_balanced(g, max_grad, max_slew, system):
    '''
    Adds a trapezoid at end of the gradient waveform to make the total area zero.

    Parameters
    ----------
    g : numpy.ndarray
        Gradient waveform in Hz/m
    system : dict
        System limits and specifications

    Returns
    -------
    gbal : numpy.ndarray
        Balanced gradient in Hz/m
    '''
    if len(g.shape) == 1:
        g = g.reshape(g.shape[0], 1)
    elif g.shape[1] > 1 and g.shape[0] == 1:
        g = g.reshape(-1, 1)
    elif g.shape[0] > 1 and g.shape[1] > 1:
        raise ValueError('Only one dimensional waveforms are allowed')

    if all([v == 0 for v in g]):  #g.all() == 0:
        raise ValueError('The gradient has to have nonzero elements')

    if 'gamma' not in system or 'grad_raster_time' not in system:
        raise ValueError('Gamma and gradient raster time have to be specified')

    max_slew_Hzms = max_slew * system['gamma']  # Hz/m/s

    # dt  seconds
    dt = system['grad_raster_time']
    # ramp to zero
    dg = -np.sign(g[-1]) * max_slew_Hzms * 0.995 * dt  # Hz/sample
    ramp = np.arange(g[-1], 0 + dg, dg)
    g = np.concatenate((g, ramp[:-1].reshape(len(ramp) - 1, 1)))

    # change of units
    g = g * 1e3 / system['gamma']  # mT/m

    # add balancing trapezoid
    area = np.sum(g) * dt  # mT * s / m
    gblip = trapwave2(np.abs(area), max_grad * 0.995, max_slew * 0.995,
                      dt) * 1e-3 * system['gamma']  # Hz/m
    g = g * 1e-3 * system['gamma']  # Hz/m
    gbal = np.concatenate((g, -np.sign(area) * gblip.T))

    return gbal
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 #6
0
 def test_rasterTime_val(self):
     with self.assertRaises(ValueError):
         trapwave2(area=1,
                   mxg=system['max_grad'],
                   mxs=system['max_slew'],
                   rasterTime=0)
Example #7
0
 def test_area_val(self):
     with self.assertRaises(ValueError):
         trapwave2(area=0,
                   mxg=system['max_grad'],
                   mxs=system['max_slew'],
                   rasterTime=system['grad_raster_time'])
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
                          sys_acq['max_slew'] / sqrt(2), sys_gen)
gy_ro_wav = make_balanced(g_ro_wav.imag, sys_acq['max_grad'],
                          sys_acq['max_slew'] / sqrt(2), sys_gen)
n = max(len(gx_ro_wav), len(gy_ro_wav))
gx_ro_wav = np.concatenate((gx_ro_wav, np.zeros((n - len(gx_ro_wav), 1))))
gy_ro_wav = np.concatenate((gy_ro_wav, np.zeros((n - len(gy_ro_wav), 1))))

# make it spiral in
gx_ro_wav = np.flipud(gx_ro_wav)
gy_ro_wav = np.flipud(gy_ro_wav)

# add partition (kz) enconding trapezoids
gzamp = (1 / sys_gen['grad_raster_time']) / (system.gamma *
                                             acq_params['fovz']) * 1e3  # mT/m
zarea = gzamp * acq_params['nz'] * sys_gen['grad_raster_time']  # mT/m*s
gpe = -trapwave2(zarea / 2, sys_acq['max_grad'], sys_acq['max_slew'],
                 sys_gen['grad_raster_time']) * 1e-3 * system.gamma  # Hz/m
gx_ro_wav_orig = makeSystemlength(
    np.vstack((0 * gpe.reshape(-1, 1), np.zeros(
        (2, 1)), gx_ro_wav, 0 * gpe.reshape(-1, 1))),
    sys_gen['grad_raster_time'])  #gx_ro_wav.T
gy_ro_wav_orig = makeSystemlength(
    np.vstack((0 * gpe.reshape(-1, 1), np.zeros(
        (2, 1)), gy_ro_wav, 0 * gpe.reshape(-1, 1))),
    sys_gen['grad_raster_time'])  #gy_ro_wav.T
gz_ro_wav_orig = makeSystemlength(
    np.vstack((gpe.reshape(-1, 1), np.zeros(
        (2, 1)), 0 * gx_ro_wav, -gpe.reshape(-1, 1))),
    sys_gen['grad_raster_time'])

gx_ro_wav_orig = grad_interpolate(gx_ro_wav_orig)
gy_ro_wav_orig = grad_interpolate(gy_ro_wav_orig)