def test_against_gre(self): '''Test implementation against GRE simulation.''' TR = 15e-3 TE = 6e-3 h = 1e-4 num_TRs = 100 Nt = num_TRs * TR / h spins0 = bloch.gre(self.T1, self.T2, self.M0, Nt, h, *self.RF, TR, TE) spins0 = spins0[0, ...] + 1j * spins0[1, ...] spins1 = gre_sim(self.T1, self.T2, TR, TE, alpha=self.RF[1], field_map=None, phi=0, dphi=0, M0=self.M0, maxiter=num_TRs, spoil=True) # from mr_utils import view # view(np.stack((spins0, spins1))) self.assertTrue(np.allclose(spins0, spins1))
def test_gre_sim_mat_against_gre_sim_loop(self): '''Verify against loop implementation.''' T1s, T2s, PD = cylinder_2d(dims=(16, 16)) dphi = np.pi im1 = gre_sim_loop(T1s, T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, field_map=None, dphi=dphi, M0=PD, maxiter=50) im2 = gre_sim(T1s, T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, field_map=None, dphi=dphi, M0=PD, maxiter=50) self.assertTrue(np.allclose(np.abs(im1), np.abs(im2)))
def test_gre_unspoiled_and_bssfp(self): '''Very balanced steady state solution.''' M0, T1, T2 = 5.0, 1.0, .5 im1 = gre_sim(T1, T2, TR=self.TR, TE=self.TR/2, alpha=self.alpha, field_map=np.zeros(1), M0=M0, spoil=False, maxiter=200) im2 = ssfp(T1, T2, TR=self.TR, alpha=self.alpha, field_map=np.zeros(1), phase_cyc=0, M0=M0) # Currently failing... self.assertEqual(im1, im2)
def test_against_gre(self): TR = 15e-3 TE = 6e-3 h = 1e-4 num_TRs = 100 Nt = num_TRs*TR/h spins0 = bloch.gre(self.T1,self.T2,self.M0,Nt,h,*self.RF,TR,TE) spins0 = spins0[0,...] + 1j*spins0[1,...] spins1 = gre_sim(self.T1,self.T2,TR,TE,alpha=self.RF[1],field_map=None,phi=0,dphi=0,M0=self.M0,iter=num_TRs,spoil=True) view(np.stack((spins0,spins1)))
def test_gre_sim_using_tol_against_closed_form_sol(self): '''Verify iterative solution against closed form solution. Used tolerance instead of fixed number of iterations. ''' im1 = spoiled_gre(self.T1s, self.T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, M0=self.PD) im2 = gre_sim(self.T1s, self.T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, field_map=None, M0=self.PD, tol=1) # same within a scale factor... val = np.abs(im1) - np.abs(im2) self.assertTrue(np.all(np.diff(val[np.nonzero(val)]) == 0))
def gre_acq(T1s, T2s, PD, field_map, TR, TE, alpha=np.pi / 2): return (gre_sim(T1s, T2s, TR=TR, TE=TE, alpha=alpha, field_map=field_map, phi=0, dphi=0, M0=PD, tol=1e-5, iter=None, spoil=True))
def gre_acq(T1s, T2s, PD, field_map, TR, TE, alpha=np.pi / 2): '''Wrapper to simulate spoled GRE acquisition.''' return gre_sim(T1s, T2s, TR=TR, TE=TE, alpha=alpha, field_map=field_map, phi=0, dphi=0, M0=PD, tol=1e-5, maxiter=None, spoil=True)
def spgr_2d_cylinder(TR=0.3, TE=0.003, alpha=np.pi / 3, dims=(64, 64), FOV=((-1, 1), (-1, 1)), radius=.5, field_map=None, kspace=False): '''Simulates axial spoiled GRE scan of cylindrical phantom. Parameters ---------- TR : float, optional Repetition time. TE : float, optional Echo time. alpha : float, optional Flip angle (in rad). dims : tuple of ints, optional Matrix size, (dim_x,dim_y) FOV : tuple of tuples, optional Field of view in arbitrary units, ( (x_min,x_max), (y_min,y_max) ) radius : float, optional Radius of cylinder in arbitrary units. field_map : array_like, optional (dim_x,dim_y) field map. If None, linear gradient in x used. kspace : bool, optional Whether or not to return data in kspace or imspace. Returns ------- im : array_like Complex simulated image with spoiled GRE contrast. ''' # Get the base cylinder maps PD, T1s, T2s = cylinder_2d(dims=dims, FOV=FOV, radius=radius) # Do the sim # im = spoiled_gre(T1s,T2s,TR,TE,alpha,field_map=field_map,M0=PD) im = gre_sim(T1s, T2s, TR, TE, alpha, field_map, M0=PD) # Hand back what we asked for if kspace: return np.fft.fftshift(np.fft.fft2(np.fft.fftshift(im), axes=(0, 1)), axes=(0, 1)) #else... return im
def test_gre_sim_using_tol_against_closed_form_sol(self): im1 = spoiled_gre(self.T1s, self.T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, M0=self.PD) im2 = gre_sim(self.T1s, self.T2s, TR=self.TR, TE=self.TE, alpha=self.alpha, field_map=None, M0=self.PD, tol=1) # same within a scale factor... val = np.abs(im1) - np.abs(im2) self.assertTrue(np.all(np.diff(val[np.nonzero(val)]) == 0))
def test_simulated_phantom_2d(self): # Create the target field map dim = 64 min_df, max_df = -500, 500 fx = np.linspace(min_df, max_df, dim) fy = np.zeros(dim) field_map, _ = np.meshgrid(fx, fy) # field_map = 100*np.random.normal(0,1,(dim,dim)) # view(field_map) # Simulate phase-cycled bSSFP acquisitons # Seems to be better with higher flip angle! # For some reason worse with higher TR args = { 'TR': 5e-3, 'alpha': np.pi / 3, 'dims': (dim, dim), 'FOV': ((-1, 1), (-1, 1)), 'radius': .75, 'field_map': field_map } pc_vals = [0, np.pi / 2, np.pi, 3 * np.pi / 2] pcs = np.zeros((len(pc_vals), dim, dim), dtype='complex') for ii, pc in enumerate(pc_vals): pcs[ii, ...] = bssfp_2d_cylinder(**args, phase_cyc=pc) # view(pcs) # Estimate field map using GS to ESM gsfm = gs_field_map( *[x.squeeze() for x in np.split(pcs, len(pc_vals))], TR=args['TR'], gs_recon_opts={'second_pass': False}) # TODO: Interestingly, the second pass solution fails... Why is this? # Now do sims for GRE for a sanity check TE1 = args['TR'] / 2 TE2 = TE1 - args['TR'] / 2 PD, T1s, T2s = cylinder_2d(dims=args['dims'], FOV=args['FOV'], radius=args['radius']) m1 = gre_sim(T1s, T2s, TR=args['TR'], TE=TE1, alpha=args['alpha'], field_map=args['field_map'], dphi=0, M0=PD, tol=1e-4) m2 = gre_sim(T1s, T2s, TR=args['TR'], TE=TE2, alpha=args['alpha'], field_map=args['field_map'], dphi=0, M0=PD, tol=1e-4) grefm = dual_echo_gre(m1, m2, TE1, TE2) # Phase wrap the real field map so we prove equivalence up to phase # unwrapping... dTE = np.abs(TE1 - TE2) field_map_pw = np.mod(field_map - 1 / (2 * dTE), 1 / dTE) - 1 / (2 * dTE) # Make sure we're getting the same thing when wrapped idx = np.where(np.abs(grefm) > 0) self.assertTrue(np.allclose(grefm[idx], field_map_pw[idx])) idx = np.where(np.abs(gsfm) > 0) self.assertTrue(np.allclose(gsfm[idx], field_map_pw[idx])) # Just for fun, try phase unwrapping... mask = np.zeros(gsfm.shape).astype(bool) mask[idx] = True # scale between [-pi,pi], do unwrap, scale back up scale_fac = np.pi / np.max(np.abs(gsfm)) * np.sign(np.max(gsfm)) val = unwrap_phase(mask * gsfm * scale_fac) / scale_fac # For some reason the edges are off by about pi, so let's clip it... val = val[:, 19:-19] field_map_clipped = field_map[:, 19:-19] idx = np.where(np.abs(val) > 0) self.assertTrue(np.allclose(field_map_clipped[idx], val[idx]))
pcs, len(pc_vals))], \ TR=args['TR'], gs_recon_opts={'second_pass': False}) # TODO: Interestingly, the second pass solution fails... Why is this? view(gsfm) # Now do sims for GRE for a sanity check TE1 = args['TR'] / 2 TE2 = TE1 - args['TR'] / 2 PD, T1s, T2s = cylinder_2d(dims=args['dims'], FOV=args['FOV'], radius=args['radius']) m1 = gre_sim(T1s, T2s, TR=args['TR'], TE=TE1, alpha=args['alpha'], field_map=args['field_map'], dphi=0, M0=PD, tol=1e-4) m2 = gre_sim(T1s, T2s, TR=args['TR'], TE=TE2, alpha=args['alpha'], field_map=args['field_map'], dphi=0, M0=PD, tol=1e-4) grefm = dual_echo_gre(m1, m2, TE1, TE2) view(grefm)