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)
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)
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
def test_rasterTime_val(self): with self.assertRaises(ValueError): trapwave2(area=1, mxg=system['max_grad'], mxs=system['max_slew'], rasterTime=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'])
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)