# Import modules here from __future__ import print_function import numpy as np import math from fenics import * from numpy.core.fromnumeric import shape from numpy.lib.npyio import save import scipy.sparse as sp from scipy.sparse import csc_matrix from scipy.sparse.linalg import spsolve import decimal import time class Unwrapper: def __init__(self, options): #self.method = options['method'] self.in_file = options['io']['in_file'] self.savepath = options['io']['savepath'] self.save_checkpoints = options['io']['write_checkpoints'] self.save_xdmf = options['io']['write_xdmf'] self.venc = options['VENC'] self.dt = options['dt'] self.mesh_options = options['mesh'] #get method-specific parameters from the options if 'Temporal' in options['unwrapping']: self.t_ref = options['unwrapping']['Temporal']['t_ref'] #if self.method == 'Probability': if 'Probability' in options['unwrapping']: self.n_low = options['unwrapping']['Probability']['nlow'] self.n_high = options['unwrapping']['Probability']['nhigh'] self.t_low = options['unwrapping']['Probability']['t_low'] self.t_high = options['unwrapping']['Probability']['t_high'] self.w_s = options['unwrapping']['Probability']['w_s'] self.w_t = options['unwrapping']['Probability']['w_t'] self.path_addons = {} for method in options['unwrapping']: self.path_addons[method] = options['unwrapping'][method]['path_addon'] #get mesh from the input file, with boundaries self.mesh = Mesh() h5 = HDF5File(self.mesh.mpi_comm(), options['mesh_file'], 'r') h5.read(self.mesh, 'mesh', False) if h5.has_dataset('boundaries'): self.bnds = MeshFunction('size_t', self.mesh, self.mesh.topology().dim()-1) h5.read(self.bnds, 'boundaries') #assuming we only ever work in one function space self.mesh_type = self.mesh_options['mesh_type'] if self.mesh_type == 'slice': self.V = FunctionSpace(self.mesh, 'DG', 0) else: self.V = FunctionSpace(self.mesh, 'P', 1) def set_method(self, new_method): self.method = new_method def save(self, u_list, filelist, save_checkpoints = False, savepath = False): comm = self.mesh.mpi_comm() if not savepath: savepath = str(self.savepath) xdmf_u = XDMFFile(savepath + 'u.xdmf') u = Function(self.V) u.rename('velocity', 'u') for l in range(len(u_list)): # writing xdmf u.assign(u_list[l]) xdmf_u.write(u, l*self.dt) # writing checkpoint if save_checkpoints: if filelist: print('saving checkpoint', l) ckpath = filelist[l].replace(self.in_file, savepath) hdf = HDF5File(comm, ckpath, 'w') hdf.write(u, '/u', float(l*self.dt)) hdf.close() else: print('saving checkpoint', l) ckpath = savepath name = '/u{i}'.format(i = math.ceil(l*self.dt*1000)) + '.h5' hdf = HDF5File(comm, ckpath + name, 'w') hdf.write(u, '/u', float(l*self.dt)) hdf.close() def make_numpy_box(self, velocity, save_numpy = True, save_box = False): ''' makes numpy array from fenics functions on aorta mesh Args (list): velocity input Return (list): numpy array can also save intermittent fenics function on box mesh ''' #this only works for scalar data for now! #find mesh dimensions and stuff self.Lt = len(velocity) coords = self.mesh.coordinates() [x0, x1] = [np.min(coords[:, 0]), np.max(coords[:, 0])] [y0, y1] = [np.min(coords[:, 1]), np.max(coords[:, 1])] [z0, z1] = [np.min(coords[:, 2]), np.max(coords[:, 2])] [dx, dy, dz] = self.mesh_options['dxs'] [Lx, Ly, Lz] = [math.ceil((x1-x0)/dx), math.ceil((y1-y0)/dy), math.ceil((z1 - z0)/dz)] # new spatial coordinates for the box-mesh deltax = (dx*(Lx) - (x1 - x0))/2 deltay = (dy*(Ly) - (y1 - y0))/2 deltaz = (dz*(Lz) - (z1 - z0))/2 x0_new = x0 - deltax x1_new = x1 + deltax y0_new = y0 - deltay y1_new = y1 + deltay z0_new = z0 - deltaz z1_new = z1 + deltaz # Defining the box-mesh if self.mesh_type == 'aorta': boxmesh = BoxMesh(Point(x0_new,y0_new,z0_new),Point(x1_new,y1_new,z1_new),Lx,Ly,Lz) Vbox = FunctionSpace(boxmesh, 'P', 1) Xt = Vbox.tabulate_dof_coordinates() else: Xt = self.V.tabulate_dof_coordinates() # This array contains indexes instead of coordinates SqXt = np.zeros(Xt.shape) decimal.getcontext().rounding = decimal.ROUND_HALF_UP for k in range(Xt.shape[0]): xkprime = round((Xt[k][0] - x0_new)/dx, 2) xk = int(decimal.Decimal(xkprime).to_integral_value()) ykprime = round((Xt[k][1] - y0_new)/dy, 2) yk = int(decimal.Decimal(ykprime).to_integral_value()) zkprime = round((Xt[k][2] - z0_new)/dx, 2) zk = int(decimal.Decimal(zkprime).to_integral_value()) SqXt[k, 0] = int(xk) SqXt[k, 1] = int(yk) SqXt[k, 2] = int(zk) SqXt = SqXt.astype(int) u = Function(self.V) S = np.zeros((Lx+1, Ly+1, Lz+1, self.Lt)) t = 0 velocity_box = {} print('interpolating...') for t in range(self.Lt): u = velocity[t] if self.mesh_type == 'aorta': ubox = Function(Vbox) LagrangeInterpolator.interpolate(ubox, u) u2 = Function(Vbox) u2.assign(ubox) velocity_box[t] = u2 values = ubox.vector()[:] else: values = u.vector()[:] for k in range(Xt.shape[0]): S[SqXt[k, 0], SqXt[k,1], SqXt[k,2], t] = values[k] t+=1 if save_box == True: print('saving new meshes') xdmf = XDMFFile(self.in_file + 'ubox.xdmf') for t in range(self.Lt): velocity_box[t].rename('velocity', 'u') xdmf.write(velocity_box[t], t*self.dt) if save_numpy == True: np.save(self.in_file + 'u', S) return S def make_aorta_mesh(self, S, save_aorta = True, save_box = False): ''' interpolate numpy array into aorta mesh Args(numpy array): S input data Return (list of fenics functions): same data, in aorta mesh can also save an intermittent fenics function on a box mesh ''' #this only works for scalar data for now! coords = self.mesh.coordinates() [x0, x1] = [np.min(coords[:, 0]), np.max(coords[:, 0])] [y0, y1] = [np.min(coords[:, 1]), np.max(coords[:, 1])] [z0, z1] = [np.min(coords[:, 2]), np.max(coords[:, 2])] [dx, dy, dz] = self.mesh_options['dxs'] [Lx, Ly, Lz] = [math.ceil((x1-x0)/dx), math.ceil((y1-y0)/dy), math.ceil((z1 - z0)/dz)] # new spatial coordinates for the box-mesh deltax = (dx*(Lx) - (x1 - x0))/2 deltay = (dy*(Ly) - (y1 - y0))/2 deltaz = (dz*(Lz) - (z1 - z0))/2 x0_new = x0 - deltax x1_new = x1 + deltax y0_new = y0 - deltay y1_new = y1 + deltay z0_new = z0 - deltaz z1_new = z1 + deltaz # Defining the box-mesh mesh0 = BoxMesh(Point(x0_new,y0_new,z0_new),Point(x1_new,y1_new,z1_new),Lx,Ly,Lz) P1 = FunctionSpace(mesh0, 'P', 1) Xt = P1.tabulate_dof_coordinates() # This array contains indexes instead of coordinates SqXt = np.zeros(Xt.shape) decimal.getcontext().rounding = decimal.ROUND_HALF_UP for k in range(Xt.shape[0]): xkprime = round((Xt[k][0] - x0_new)/dx, 2) xk = int(decimal.Decimal(xkprime).to_integral_value()) ykprime = round((Xt[k][1] - y0_new)/dy, 2) yk = int(decimal.Decimal(ykprime).to_integral_value()) zkprime = round((Xt[k][2] - z0_new)/dx, 2) zk = int(decimal.Decimal(zkprime).to_integral_value()) SqXt[k,0] = int(xk) SqXt[k,1] = int(yk) SqXt[k,2] = int(zk) SqXt = SqXt.astype(int) # To ensure int values! v = Function(self.V) velocity = [Function(self.V) for i in range(self.Lt)] velocity_box = [Function(P1) for i in range(self.Lt)] for t in range(self.Lt): v1 = Function(P1) values = np.zeros(v1.vector()[:].shape) for k in range(Xt.shape[0]): values[k] = S[SqXt[k, 0], SqXt[k, 1], SqXt[k, 2], t] v1.vector()[:] = values velocity_box[t] = v1 v = Function(self.V) LagrangeInterpolator.interpolate(v, v1) if self.mesh_type == 'slice': #due to problems with interpolating into hexahedral meshes (it interpolates with fake zeros somehow?) #this multiplication is necessary for the slices v.vector()[:] *= 2 velocity[t] = v if save_aorta == True: self.save(velocity, False, False, savepath=self.savepath + 'aorta/') if save_box == True: self.save(velocity_box, False, False, savepath = self.savepath + 'box/') return velocity def lap_unwrap(self, phiwin, nd = 3): ''' Args (numpy array): wrapped phase Return (numpy array): unwrapped phase ''' si = [phiwin.shape[0], phiwin.shape[1], phiwin.shape[2], phiwin.shape[3]] phiw = phiwin if self.mesh_type == 'slice': if nd == 3: [X, Y] = np.mgrid[-si[0]/2:si[0]/2, -si[1]/2:si[1]/2] ss = 4 mod = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) - 4 modi = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) - 4 modi[math.floor(si[0]/2), math.floor(si[1]/2)] = 1 else: [X, Y, T] = np.mgrid[-si[0]/2:si[0]/2, -si[1]/2:si[1]/2, -si[3]/2:si[3]/2] ss = 4 ts = 2 mod = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) + ts*np.cos(pi*T/si[3]) - ss - ts modi = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) + ts*np.cos(pi*T/si[3]) - ss - ts modi[math.floor(si[0]/2), math.floor(si[1]/2), math.floor(si[3]/2)] = 1 phiw = phiwin[:, :, 1, :] si = [si[0], si[1], si[3]] def lap(phi): '''computes laplacian with priorly defined stencil''' K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = np.multiply(K, mod) return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) def ilap(phi): K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = K / modi return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) lap_phiw = np.zeros(si) lap_phi_vec = np.zeros(si) phi_vec = np.zeros(si) nr = np.zeros(si) if nd == 3: for t in range(self.Lt): #compute Laplacian of the correct phase lap_phi_vec[:,:,t] = np.cos(phiw[:,:,t])*lap(np.sin(phiw[:,:,t])) - np.sin(phiw[:,:,t])*lap(np.cos(phiw[:,:,t])) lap_phiw[:, :, t] = lap(phiw[:, :, t]) phi_vec[:, :, t] = ilap(lap_phi_vec[:, :, t] - lap_phiw[:, :, t]) if nd == 4: lap_phiw = lap(phiw) lap_phi_vec = np.cos(phiw)*lap(np.sin(phiw)) - np.sin(phiw)*lap(np.cos(phiw)) phi_vec = ilap(lap_phi_vec - lap_phiw) nr = np.round(phi_vec/(2*pi)) nrout = np.zeros(phiwin.shape) nrout[:, :, 1, :] = nr print(np.count_nonzero(nr)) u = self.make_aorta_mesh((phiwin + 2*pi*nrout)*self.venc/pi, False, False) return u if nd <=3: [X, Y, Z] = np.mgrid[-si[0]/2:si[0]/2, -si[1]/2:si[1]/2, -si[2]/2:si[2]/2] ss = 6 mod = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) - ss + 2*np.cos(pi*Z/si[2]) modi = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) - ss + 2*np.cos(pi*Z/si[2]) modi[math.floor(si[0]/2), math.floor(si[1]/2), math.floor(si[2]/2)] = 1 def lap(phi): '''computes laplacian with priorly defined stencil''' K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = np.multiply(K, mod) return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) def ilap(phi): K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = K / modi return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) lap_phiw = np.zeros(si) lap_phi_vec = np.zeros(si) phi_vec = np.zeros(si) nr = np.zeros(si) for t in range(self.Lt): #compute Laplacian of the correct phase lap_phi_vec[:,:,:,t] = np.cos(phiw[:,:,:,t])*lap(np.sin(phiw[:,:,:,t])) - np.sin(phiw[:,:,:,t])*lap(np.cos(phiw[:,:,:,t])) lap_phiw[:, :, :, t] = lap(phiw[:, :, :, t]) phi_vec[:, :, :, t] = ilap(lap_phi_vec[:, :, :, t] - lap_phiw[:, :, :, t]) nr = np.round(phi_vec/(2*pi)) nrout = np.zeros(phiwin.shape) nrout = nr print(np.count_nonzero(nr)) if nd == 3: u = self.make_aorta_mesh((phiwin + 2*pi*nrout)*self.venc/pi, False, False) return u else: #define boundary conditions if self.mesh_type == 'slice': lap_phi_out = np.zeros(phiwin.shape) lap_phi_out[:,:,1,:] = lap_phi_vec[:,:,0,:] lap_phiw_out = np.zeros(phiwin.shape) lap_phiw_out[:,:,1,:] = lap_phiw[:,:,0,:] else: lap_phi_out = lap_phi_vec lap_phiw_out = lap_phiw lap_phi = self.make_aorta_mesh(lap_phi_out, False, False) phi_w= self.make_aorta_mesh(phiwin, False, False) lap_phi_w = self.make_aorta_mesh(lap_phiw_out, False, False) u = [Function(self.V) for i in range(self.Lt)] def boundary_D(x, on_boundary): return on_boundary for t in range(self.Lt): if self.mesh_type == 'aorta': bc_phi = DirichletBC(self.V, phi_w[t], self.bnds, 1) else: bc_phi = DirichletBC(self.V, phi_w[t], boundary_D) #bc_phi = DirichletBC(self.V, Constant(0.0), self.bnds, 1) ds = Measure("ds", subdomain_data=self.bnds) g_phi = Constant(0.0) phi = TrialFunction(self.V) v = TestFunction(self.V) a = dot(grad(phi), grad(v))*dx #L = (lap_phi[t] - lap_phi_w[t])*v*dx + g_phi*v*ds L = lap_phi[t]*v*dx phi = Function(self.V) solve(a == L, phi, bc_phi) nr = Function(self.V) nr.vector()[:] = np.round((phi.vector()[:])/(2*pi)) #u[t].assign(phi_w[t]*self.venc/pi + nr*2*self.venc) u[t].assign(phi*self.venc/pi) return u if nd == 4: [X, Y, Z, T] = np.mgrid[-si[0]/2:si[0]/2, -si[1]/2:si[1]/2, -si[2]/2:si[2]/2, -si[3]/2:si[3]/2] ts = 2 ss = 6 if self.mesh_type == 'slice': ss = 4 mod = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) + 2*np.cos(pi*Z/si[2]) + ts*np.cos(pi*T/si[3]) - ss - ts modi = 2*np.cos(pi*X/si[0]) + 2*np.cos(pi*Y/si[1]) + 2*np.cos(pi*Z/si[2]) + ts*np.cos(pi*T/si[3]) - ss - ts modi[math.floor(si[0]/2), math.floor(si[1]/2), math.floor(si[2]/2), math.floor(si[3]/2)] = 1 def lap(phi): '''computes laplacian with priorly defined stencil''' K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = np.multiply(K, mod) return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) def ilap(phi): K = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(phi))) K = K / modi return np.real(np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(K)))) lap_phiw = lap(phiw) lap_phi_vec = np.cos(phiw)*lap(np.sin(phiw)) - np.sin(phiw)*lap(np.cos(phiw)) phi_vec = ilap(lap_phi_vec - lap_phiw) nr = np.round(phi_vec/(2*pi)) nrout = nr print(np.count_nonzero(nr)) u = self.make_aorta_mesh((phiwin + 2*pi*nrout)*self.venc/pi, False, False) return u def temporal_unwrap(self, phi_w): ''' unwrapping with the temporal method described in Xiang, 1995 Args (list): wrapped phase Return (list): unwrapped phase ''' def wrap(u): ''' wraps all values of u into the interval (-pi, pi)''' u_array = u.vector()[:] for i, x in enumerate(u_array): while x<-pi or x>pi: if x>pi: u_array[i] = x - 2*pi x = x - 2*pi if x<-pi: u_array[i] = x + 2*pi x = x + 2*pi u.vector()[:] = u_array return u phi_diff = [Function(self.V) for i in range(len(phi_w))] phi = [Function(self.V) for i in range(len(phi_w))] phi[self.t_ref] = phi_w[self.t_ref] for t in range(self.t_ref, len(phi_w)-1): phi_diff[t].vector()[:] = phi_w[t+1].vector()[:] - phi_w[t].vector()[:] phi_diff[t] = wrap(phi_diff[t]) phi[t+1] = phi[t] + phi_diff[t] for t in reversed(range(0, self.t_ref)): phi_diff[t].vector()[:] = phi_w[t+1].vector()[:] - phi_w[t].vector()[:] phi_diff[t] = wrap(phi_diff[t]) phi[t] = phi[t+1] - phi_diff[t] return phi def probability_unwrap(self, phi_w): ''' unwraps with probability-based method described in Loecher et al., 2011 Args (list): wrapped phase Return (list): unwrapped phase ''' timesteps = len(phi_w) if self.mesh_type == 'aorta': def prob_pass(phi_w, threshold, v_to_d, mesh, neighborhoods, w_s, w_t): ''' performs one single pass over all vertices, estimating probability that they're wrapped and unwrapping if necessary ''' coords = mesh.coordinates() prob = [[0]*mesh.num_vertices() for i in range(timesteps+1)] phi = phi_w for v in vertices(mesh): for t in range(0, timesteps): #sum the gradients of the neighbours grad_sum = 0 dofs = v_to_d[v.index()] value = phi_w[t].vector()[dofs] for idx in neighborhoods[v.index()]: grad_sum = grad_sum + w_s * (phi_w[t](coords[idx]) - value) if t>0: grad_sum = grad_sum + w_t*(phi_w[t-1].vector()[dofs] - value) if t < timesteps-1: grad_sum = grad_sum + w_t*(phi_w[t+1].vector()[dofs] - value) #calculate probability tot = max(0.000001, w_s*len(neighborhoods[v.index()]) + w_t*int(t>0) + w_t*int(t threshold: phi[t].vector()[dofs] = phi_w[t].vector()[dofs] + 2*pi if prob[t][v.index()] < -threshold: phi[t].vector()[dofs] = phi_w[t].vector()[dofs] - 2*pi return phi #prepare vertex-dof maps v_to_d=vertex_to_dof_map(self.V) #find all neighborhood vertices neighborhoods = [0]*self.mesh.num_vertices() for v in vertices(self.mesh): neighborhood = [Edge(self.mesh, i).entities(0) for i in v.entities(1)] neighborhood = np.array(neighborhood).flatten() neighborhood = neighborhood[np.where(neighborhood != v.index())[0]] neighborhoods[v.index()] = neighborhood #low threshold passes phi = prob_pass(phi_w, self.t_low, v_to_d, self.mesh, neighborhoods, self.w_s, self.w_t) for i in range(self.n_low-1): phi = prob_pass(phi, self.t_low, v_to_d, self.mesh, neighborhoods, self.w_s, self.w_t) #high threshold passes for i in range(self.n_high): phi = prob_pass(phi, self.t_high, v_to_d, self.mesh, neighborhoods, self.w_s, self.w_t) return phi else: phiw = self.make_numpy_box(phi_w, False, False) si = phiw.shape def prob_pass(phiw, threshold, w_s, w_t): phi = np.copy(phiw) pw = 0 for x in range(si[0]): for y in range(si[1]): for z in range(si[2]): for t in range(si[3]): grad_sum = 0 val = phiw[x, y, z, t] tot = 0 if 0 threshold: phi[x, y, z, t] = val + 2*pi pw +=1 if prob < -threshold: phi[x, y, z, t] = val - 2*pi pw +=1 print('wrapped pixels: ', pw) return phi phi = prob_pass(phiw, self.t_low, self.w_s, self.w_t) for i in range(self.n_low - 1): phi = prob_pass(phi, self.t_low, self.w_s, self.w_t) for i in range(self.n_high): phi = prob_pass(phi, self.t_high, self.w_s, self.w_t) r_phi = self.make_aorta_mesh(phi, False, False) return r_phi def dual_venc(self, high_venc_files, low_venc_files, addon = False): print('unwrapping with dual venc') if not addon: addon = self.path_addons['lee'] #read files with low venc t = 0 u = Function(self.V) low_venc = [Function(self.V) for i in range(len(low_venc_files))] for file in low_venc_files: h5u = HDF5File(self.mesh.mpi_comm(), file, 'r') h5u.read(u, 'u') low_venc[t].assign(u) t +=1 #read files with high venc t = 0 u = Function(self.V) high_venc = [Function(self.V) for i in range(len(high_venc_files))] for file in high_venc_files: if isinstance(file, str): h5u = HDF5File(self.mesh.mpi_comm(), file, 'r') h5u.read(u, 'u') high_venc[t].assign(u) else: high_venc[t].assign(file) t +=1 self.Lt = len(low_venc_files) u = low_venc for t in range(self.Lt): diff = high_venc[t].vector()[:] - low_venc[t].vector()[:] n = np.round(diff/self.venc) u[t].vector()[:] += n*self.venc if self.save_xdmf: self.save(u, False, self.save_checkpoints, self.savepath + addon) return u def omme(self, velocities, vencs): uw = Function(self.V) uw_vec = np.zeros(velocities[0].shape) #venc_uw = np.lcm.reduce(vencs) venc_uw = np.max(vencs) min_ind = np.argmin(vencs) min_venc = np.min(vencs) nb_samples = math.ceil(venc_uw/min_venc)+2 range_u = np.arange(-nb_samples, nb_samples+1)*min_venc u = np.outer(velocities[min_ind][:],np.ones((1, len(range_u)))) + np.outer(np.ones((len(velocities[0][:]), 1)),range_u) for k in range(len(velocities[0])): J = 0 u_k = u[k, abs(u[k,:]) <= venc_uw] for v_ind in range(len(vencs)): J = J - np.cos(np.pi * (velocities[v_ind][k] - u_k)/vencs[v_ind]) ind_k = np.argmin(J) uw_vec[k] = u_k[ind_k] uw.vector()[:] = uw_vec return uw def multi_venc(self, venc_files, vencs, addon = False): if not addon: addon = self.path_addons['omme'] self.Lt = len(venc_files[0]) print('unwrapping with %i vencs'%len(vencs)) uw = [] for t in range(self.Lt): velocities = [] for filelist in venc_files: if isinstance(filelist[t], str): u = Function(self.V) h5u = HDF5File(self.mesh.mpi_comm(), filelist[t], 'r') h5u.read(u, 'u') velocities.append(u.vector()[:]) else: velocities.append(filelist[t].vector()[:]) uw.append(self.omme(velocities, vencs)) if self.save_xdmf: self.save(uw, False, self.save_checkpoints, self.savepath + addon) print('done!') return uw def unwrap(self, filelist, method = 'Temporal'): ''' unwraps the velocity data :) Args (list/numpy array): filelist, list of files to unwrap OR one numpy array to unwrap Return (list): unwrapped velocity ''' #get files from file list if isinstance(filelist, str) and (filelist.endswith('npy') or filelist.endswith('npz')): #using numpy array saves with default folder structure, ie all h5 files in one folder if filelist.endswith('npy'): u_in = np.load(filelist) if filelist.endswith('npz'): S = np.load(filelist) u_in = S['u'] filelist = False self.Lt = u_in.shape[3] else: t = 0 u = Function(self.V) u_in = [Function(self.V) for i in range(len(filelist))] for file in filelist: h5u = HDF5File(self.mesh.mpi_comm(), file, 'r') h5u.read(u, 'u') u_in[t].assign(u) t +=1 self.Lt = len(filelist) if self.Lt == 0: raise Exception('No input files found!') if method.startswith('lap'): if type(u_in) == np.ndarray: vx = u_in else: print('transforming to numpy array') vx = self.make_numpy_box(u_in, save_numpy = False, save_box = False) #transform to phase print('unwrapping with laplacian method...') start_l = time.time() phase = np.asarray(vx * pi / self.venc) if method.endswith('3d'): u_list = self.lap_unwrap(phase, nd=3) if method.endswith('4d'): u_list = self.lap_unwrap(phase, nd=4) if method.endswith('fenics'): u_list = self.lap_unwrap(phase, nd=0) end_l = time.time() print('done!') print('total time for Laplacian: ' + str(end_l - start_l)) if method == 'Temporal': if type(u_in) == np.ndarray: print('transforming to aorta mesh') u_wrapped = self.make_aorta_mesh(u_in, save_aorta=False, save_box=False) else: u_wrapped = u_in print('unwrapping with temporal method...') phi_w = [Function(self.V) for i in range(len(u_wrapped))] u_list = [Function(self.V) for i in range(len(u_wrapped))] for t, u in enumerate(u_wrapped): phi_w[t].assign(u * pi / self.venc) phi = self.temporal_unwrap(phi_w) for t, phit in enumerate(phi): u_list[t].assign(phit * self.venc / pi) print('done!') if method == 'Probability': if type(u_in) == np.ndarray: print('transforming to aorta mesh') u_wrapped = self.make_aorta_mesh(u_in, save_aorta=False, save_box=False) else: u_wrapped = u_in print('unwrapping with probability method...') phi_w = [Function(self.V) for i in range(len(u_wrapped))] u_list = [Function(self.V) for i in range(len(u_wrapped))] for t, u in enumerate(u_wrapped): phi_w[t].assign(u_wrapped[t] * pi/ self.venc) phi = self.probability_unwrap(phi_w) for t, phit in enumerate(phi): u_list[t].assign(phit * self.venc / pi) print('done!') if self.save_xdmf: self.save(u_in, filelist, self.save_checkpoints, self.savepath + 'wrapped/') self.save(u_list, filelist, self.save_checkpoints, self.savepath + self.path_addons[method]) return u_list def get_lumen(self, ground_truth): #separate only lumen voxels here lumen_indxs = [] for k in range(len(ground_truth[0].vector()[:])): for t in range(self.Lt): #not-lumen voxels should always be zero in ground truth if ground_truth[t].vector()[k] != 0: lumen_indxs.append(k) break self.lumen_indxs = lumen_indxs return lumen_indxs def eval(self, wrapped, ground_truth, method = 'error'): lumen_indxs = getattr(self, 'lumen_indxs', range(len(ground_truth[0].vector()[:]))) if method == 'number': n = 0 for t in range(self.Lt): for k in lumen_indxs: if abs(wrapped[t].vector()[k] - ground_truth[t].vector()[k]) > 0.1: n += 1 print('number of wraps: ', n) print('percentage of wraps:', n / (self.Lt * len(lumen_indxs)) * 100) return n if method == 'error': #concatenate all timesteps together length = len(ground_truth[0].vector()[lumen_indxs]) ground = np.zeros((self.Lt*length)) u = np.zeros((self.Lt*length)) for t in range(self.Lt): ground[t*length:(t+1)*length] = ground_truth[t].vector()[lumen_indxs] u[t*length:(t+1)*length] = wrapped[t].vector()[lumen_indxs] #compute norms error = np.linalg.norm(u-ground)/np.linalg.norm(ground) print('error: ', error) return error return 0