CFL, r, filename = 'file-out.npy') #, init = 'cont.npy') #, init = 'macro_restart.txt') # restart from macroparameters array t2 = time.time() log = open('log.txt', 'a') log.write('Time = ' + str(t2 - t1) + '\n') log.close() fig, ax = plt.subplots(figsize = (20,10)) line, = ax.semilogy(S.frob_norm_iter/S.frob_norm_iter[0]) ax.set(title='$Steps =$' + str(nt)) plt.savefig('norm_iter.png') plt.close() data = np.zeros((mesh.nc, 7)) data[:, 0] = S.n[:] data[:, 1] = S.ux[:] data[:, 2] = S.uy[:] data[:, 3] = S.uz[:] data[:, 4] = S.p[:] data[:, 5] = S.T[:] data[:, 6] = S.rank[:] np.savetxt('macroparameters_data.txt', data) # save macroparameters write_tecplot(mesh, data, 'tec_tt.dat', ('n', 'ux', 'uy', 'uz', 'p', 'T', 'rank')) log = open('log.txt', 'a') log.write('Residual = ' + str('{0:5.2e}'.format(S.frob_norm_iter[-1]/S.frob_norm_iter[0])) + '\n') log.close()
def solver_tt(gas_params, problem, mesh, nt, nv, vx_, vx, vy, vz, \ CFL, tol, filename, init = '0'): """Solve Boltzmann equation with model collision integral gas_params -- object of class GasParams, contains gas parameters and viscosity law problem -- object of class Problem, contains list of boundary conditions, data for b.c., and function for initial condition mesh - object of class Mesh nt -- number of time steps vmax -- maximum velocity in each direction in velocity mesh nv -- number of nodes in velocity mesh CFL -- courant number filename -- name of output file for f init - name of restart file """ # Function for LU-SGS mydivide = lambda x: x[:, 0] / x[:, 1] zero_tt = 0. * tt.ones((nv, nv, nv)) ones_tt = tt.ones((nv, nv, nv)) # # Initialize main arrays and lists # vn = [None ] * mesh.nf # list of tensors of normal velocities at each mesh face vn_tmp = np.zeros((nv, nv, nv)) vnm = [None] * mesh.nf # negative part of vn: 0.5 * (vn - |vn|) vnp = [None] * mesh.nf # positive part of vn: 0.5 * (vn + |vn|) vn_abs = [None] * mesh.nf # approximations of |vn| vx_tt = tt.tensor(vx).round(tol) vy_tt = tt.tensor(vy).round(tol) vz_tt = tt.tensor(vz).round(tol) vn_error = 0. for jf in range(mesh.nf): vn_tmp = mesh.face_normals[jf, 0] * vx + mesh.face_normals[ jf, 1] * vy + mesh.face_normals[jf, 2] * vz vn[jf] = mesh.face_normals[jf, 0] * vx_tt + mesh.face_normals[ jf, 1] * vy_tt + mesh.face_normals[jf, 2] * vz_tt vnp[jf] = tt.tensor(np.where(vn_tmp > 0, vn_tmp, 0.), eps=tol) vnm[jf] = tt.tensor(np.where(vn_tmp < 0, vn_tmp, 0.), eps=tol) vn_abs[jf] = tt.tensor(np.abs(vn_tmp), rmax=4) vn_error = max( vn_error, np.linalg.norm(vn_abs[jf].full() - np.abs(vn_tmp)) / np.linalg.norm(np.abs(vn_tmp))) print('max||vn_abs_tt - vn_abs||_F/max||vn_abs||_F = ', vn_error) h = np.min(mesh.cell_diam) tau = h * CFL / (np.max(vx_) * (3.**0.5)) v2 = (vx_tt * vx_tt + vy_tt * vy_tt + vz_tt * vz_tt).round(tol) diag = [None] * mesh.nc # part of diagonal coefficient in implicit scheme # precompute diag diag_full = np.zeros( (mesh.nc, nv, nv, nv)) # part of diagonal coefficient in implicit scheme # precompute diag for ic in range(mesh.nc): for j in range(6): jf = mesh.cell_face_list[ic, j] vn_tmp = mesh.face_normals[jf, 0] * vx + mesh.face_normals[ jf, 1] * vy + mesh.face_normals[jf, 2] * vz vnp_full = np.where( mesh.cell_face_normal_direction[ic, j] * vn_tmp[:, :, :] > 0, mesh.cell_face_normal_direction[ic, j] * vn_tmp[:, :, :], 0.) diag_full[ic, :, :, :] += (mesh.face_areas[jf] / mesh.cell_volumes[ic]) * vnp_full for ic in range(mesh.nc): diag_temp = np.zeros((nv, nv, nv)) for j in range(6): jf = mesh.cell_face_list[ic, j] vn_full = (mesh.face_normals[jf, 0] * vx + mesh.face_normals[jf, 1] * vy \ + mesh.face_normals[jf, 2] * vz) * mesh.cell_face_normal_direction[ic, j] vnp_full = np.where(vn_full > 0, vn_full, 0.) vn_abs_full = np.abs(vn_full) diag_temp += (mesh.face_areas[jf] / mesh.cell_volumes[ic]) * vnp_full # diag_temp += 0.5 * (mesh.face_areas[jf] / mesh.cell_volumes[ic]) * vn_abs_full diag[ic] = tt.tensor(diag_temp) # Compute mean rank of diag diag_rank = 0. for ic in range(mesh.nc): diag_rank += 0.5 * (diag[ic].r[1] + diag[ic].r[2]) diag_rank = diag_rank / ic print('diag_rank = ', diag_rank) # diag_scal = np.zeros(mesh.nc) for ic in range(mesh.nc): for j in range(6): jf = mesh.cell_face_list[ic, j] diag_scal[ic] += 0.5 * (mesh.face_areas[jf] / mesh.cell_volumes[ic]) diag_scal *= np.max(np.abs(vx_)) * 3**0.5 # set initial condition f = [None] * mesh.nc if (init == '0'): for i in range(mesh.nc): x = mesh.cell_center_coo[i, 0] y = mesh.cell_center_coo[i, 1] z = mesh.cell_center_coo[i, 2] f[i] = problem.f_init(x, y, z, vx, vy, vz) else: # restart from distribution function # f = load_tt(init, mesh.nc, nv) # restart form macroparameters array init_data = np.loadtxt(init) for ic in range(mesh.nc): f[ic] = tt.tensor(f_maxwell(vx, vy, vz, init_data[ic, 5], \ init_data[ic, 0], init_data[ic, 1], init_data[ic, 2], init_data[ic, 3], gas_params.Rg), tol) # TODO: may be join f_plus and f_minus in one array f_plus = [None] * mesh.nf # Reconstructed values on the right f_minus = [None] * mesh.nf # reconstructed values on the left flux = [None] * mesh.nf # Flux values rhs = [None] * mesh.nc df = [None] * mesh.nc # Arrays for macroparameters n = np.zeros(mesh.nc) rho = np.zeros(mesh.nc) ux = np.zeros(mesh.nc) uy = np.zeros(mesh.nc) uz = np.zeros(mesh.nc) p = np.zeros(mesh.nc) T = np.zeros(mesh.nc) nu = np.zeros(mesh.nc) rank = np.zeros(mesh.nc) data = np.zeros((mesh.nc, 7)) # Dummy tensor with [1, 1, 1, 1] ranks F = tt.rand([nv, nv, nv], 3, [1, 1, 1, 1]) frob_norm_iter = np.array([]) it = 0 while (it < nt): it += 1 # reconstruction for inner faces # 1st order for ic in range(mesh.nc): for j in range(6): jf = mesh.cell_face_list[ic, j] if (mesh.cell_face_normal_direction[ic, j] == 1): f_minus[jf] = f[ic].copy() else: f_plus[jf] = f[ic].copy() # boundary condition # loop over all boundary faces for j in range(mesh.nbf): jf = mesh.bound_face_info[j, 0] # global face index bc_num = mesh.bound_face_info[j, 1] bc_type = problem.bc_type_list[bc_num] bc_data = problem.bc_data[bc_num] if (mesh.bound_face_info[j, 2] == 1): f_plus[jf] = set_bc_tt(gas_params, bc_type, bc_data, f_minus[jf], vx, vy, vz, vn[jf], vnp[jf], vnm[jf], tol) else: f_minus[jf] = set_bc_tt(gas_params, bc_type, bc_data, f_plus[jf], vx, vy, vz, -vn[jf], -vnm[jf], -vnp[jf], tol) # riemann solver - compute fluxes for jf in range(mesh.nf): flux[jf] = 0.5 * mesh.face_areas[jf] *\ ((f_plus[jf] + f_minus[jf]) * vn[jf] - (f_plus[jf] - f_minus[jf]) * vn_abs[jf]) flux[jf] = flux[jf].round(tol) # computation of the right-hand side for ic in range(mesh.nc): rhs[ic] = zero_tt.copy() # sum up fluxes from all faces of this cell for j in range(6): jf = mesh.cell_face_list[ic, j] rhs[ic] += -(mesh.cell_face_normal_direction[ic, j]) * ( 1. / mesh.cell_volumes[ic]) * flux[jf] rhs[ic] = rhs[ic].round(tol) # Compute macroparameters and collision integral J, n[ic], ux[ic], uy[ic], uz[ic], T[ic], nu[ic], rho[ic], p[ ic] = comp_macro_param_and_j_tt(f[ic], vx_, vx, vy, vz, vx_tt, vy_tt, vz_tt, v2, gas_params, tol, F, ones_tt) rhs[ic] += J rhs[ic] = rhs[ic].round(tol) frob_norm_iter = np.append( frob_norm_iter, np.sqrt(sum([(rhs[ic].norm())**2 for ic in range(mesh.nc)]))) # # update values, expclicit scheme # for ic in range(mesh.nc): f[ic] = (f[ic] + tau * rhs[ic]).round(tol) # save rhs norm and tec tile if ((it % 100) == 0): fig, ax = plt.subplots(figsize=(20, 10)) line, = ax.semilogy(frob_norm_iter / frob_norm_iter[0]) ax.set(title='$Steps =$' + str(it)) plt.savefig('norm_iter.png') plt.close() data[:, 0] = n[:] data[:, 1] = ux[:] data[:, 2] = uy[:] data[:, 3] = uz[:] data[:, 4] = p[:] data[:, 5] = T[:] data[:, 6] = rank[:] write_tecplot(mesh, data, 'tec_tt.dat', ('n', 'ux', 'uy', 'uz', 'p', 'T', 'rank')) save_tt(filename, f, mesh.nc, nv) Return = namedtuple( 'Return', ['f', 'n', 'ux', 'uy', 'uz', 'T', 'p', 'rank', 'frob_norm_iter']) S = Return(f, n, ux, uy, uz, T, p, rank, frob_norm_iter) return S
def solver(gas_params, problem, mesh, nt, vmax, nv, CFL, filename, init = '0'): """Solve Boltzmann equation with model collision integral gas_params -- object of class GasParams, contains gas parameters and viscosity law problem -- object of class Problem, contains list of boundary conditions, data for b.c., and function for initial condition mesh - object of class Mesh nt -- number of time steps vmax -- maximum velocity in each direction in velocity mesh nv -- number of nodes in velocity mesh CFL -- courant number filename -- name of output file for f init - name of restart file """ h = np.min(mesh.cell_diam) tau = h * CFL / (vmax * (3.**0.5)) hv = 2. * vmax / nv vx_ = np.linspace(-vmax+hv/2, vmax-hv/2, nv) # coordinates of velocity nodes vx, vy, vz = np.meshgrid(vx_, vx_, vx_, indexing='ij') # set initial condition f = np.zeros((mesh.nc, nv, nv, nv)) if (init == '0'): for i in range(mesh.nc): x = mesh.cell_center_coo[i, 0] y = mesh.cell_center_coo[i, 1] z = mesh.cell_center_coo[i, 2] f[i, :, :, :] = problem.f_init(x, y, z, vx, vy, vz) else: # restart from distribution function f = np.reshape(np.load(init), (mesh.nc, nv, nv, nv)) # restart form macroparameters array # init_data = np.loadtxt(init) # for ic in range(mesh.nc): # f[ic, :, :, :] = f_maxwell(vx, vy, vz, init_data[ic, 5], init_data[ic, 0], init_data[ic, 1], init_data[ic, 2], init_data[ic, 3], gas_params.Rg) # TODO: may be join f_plus and f_minus in one array f_plus = np.zeros((mesh.nf, nv, nv, nv)) # Reconstructed values on the right f_minus = np.zeros((mesh.nf, nv, nv, nv)) # reconstructed values on the left flux = np.zeros((mesh.nf, nv, nv, nv)) # Flux values rhs = np.zeros((mesh.nc, nv, nv, nv)) df = np.zeros((mesh.nc, nv, nv, nv)) # Array for increments \Delta f vn = np.zeros((mesh.nf, nv, nv, nv)) for jf in range(mesh.nf): vn[jf, :, :, :] = mesh.face_normals[jf, 0] * vx + mesh.face_normals[jf, 1] * vy + mesh.face_normals[jf, 2] * vz diag = np.zeros((mesh.nc, nv, nv, nv)) # part of diagonal coefficient in implicit scheme # precompute diag for ic in range(mesh.nc): for j in range(6): jf = mesh.cell_face_list[ic, j] vnp = np.where(mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :] > 0, mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :], 0.) diag[ic, :, :, :] += (mesh.face_areas[jf] / mesh.cell_volumes[ic]) * vnp # Arrays for macroparameters n = np.zeros(mesh.nc) rho = np.zeros(mesh.nc) ux = np.zeros(mesh.nc) uy = np.zeros(mesh.nc) uz = np.zeros(mesh.nc) p = np.zeros(mesh.nc) T = np.zeros(mesh.nc) nu = np.zeros(mesh.nc) data = np.zeros((mesh.nc, 7)) frob_norm_iter = np.array([]) it = 0 while(it < nt): it += 1 # reconstruction for inner faces # 1st order for ic in range(mesh.nc): for j in range(6): jf = mesh.cell_face_list[ic, j] # TODO: think how do this without 'if' if (mesh.cell_face_normal_direction[ic, j] == 1): f_minus[jf, :, :, :] = f[ic, :, :, :] else: f_plus[jf, :, :, :] = f[ic, :, :, :] # boundary condition # loop over all boundary faces for j in range(mesh.nbf): jf = mesh.bound_face_info[j, 0] # global face index bc_num = mesh.bound_face_info[j, 1] bc_type = problem.bc_type_list[bc_num] bc_data = problem.bc_data[bc_num] if (mesh.bound_face_info[j, 2] == 1): # TODO: normal velocities vn can be pre-computed one time # then we can pass to function p.bc only vn f_plus[jf, :, :, :] = set_bc(gas_params, bc_type, bc_data, f_minus[jf, :, :, :], vx, vy, vz, vn[jf, :, :, :]) else: f_minus[jf, :, :, :] = set_bc(gas_params, bc_type, bc_data, f_plus[jf, :, :, :], vx, vy, vz, -vn[jf, :, :, :]) # riemann solver - compute fluxes for jf in range(mesh.nf): # TODO: Pre-compute array of vn[:] ??? flux[jf, :, :, :] = mesh.face_areas[jf] * vn[jf, :, :, :] * \ np.where((vn[jf, :, :, :] < 0), f_plus[jf, :, :, :], f_minus[jf, :, :, :]) # flux[jf] = (1. / 2.) * mesh.face_areas[jf] * ((vn * (f_plus[jf, :, :, :] + f_minus[jf, :, :, :])) - (vn_abs * (f_plus[jf, :, :, :] - f_minus[jf, :, :, :]))) # computation of the right-hand side rhs[:] = 0. for ic in range(mesh.nc): # sum up fluxes from all faces of this cell for j in range(6): jf = mesh.cell_face_list[ic, j] rhs[ic, :, :, :] += - (mesh.cell_face_normal_direction[ic, j]) * (1. / mesh.cell_volumes[ic]) * flux[jf, :, :, :] # Compute macroparameters and collision integral J, n[ic], ux[ic], uy[ic], uz[ic], T[ic], nu[ic], rho[ic], p[ic] = comp_macro_param_and_j(f[ic, :, :, :], vx, vy, vz, gas_params) rhs[ic, :, :, :] += J frob_norm_iter = np.append(frob_norm_iter, np.linalg.norm(rhs)) # # update values - explicit scheme # # f += tau * rhs ''' LU-SGS iteration ''' # # Backward sweep # for ic in range(mesh.nc - 1, -1, -1): df[ic, :, :, :] = rhs[ic, :, :, :] # loop over neighbors of cell ic for j in range(6): jf = mesh.cell_face_list[ic, j] icn = mesh.cell_neighbors_list[ic, j] # index of neighbor vnm = np.where(mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :] < 0, mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :], 0.) if (icn >= 0 ) and (icn > ic): df[ic, :, :, :] += -(mesh.face_areas[jf] / mesh.cell_volumes[ic]) \ * vnm * df[icn, : , :, :] # divide by diagonal coefficient df[ic, :, :, :] = df[ic, :, :, :] / (np.ones((nv, nv, nv)) * (1/tau + nu[ic]) + diag[ic]) # # Forward sweep # for ic in range(mesh.nc): # loop over neighbors of cell ic incr = np.zeros((nv, nv, nv)) for j in range(6): jf = mesh.cell_face_list[ic, j] icn = mesh.cell_neighbors_list[ic, j] # index of neighbor vnm = np.where(mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :] < 0, mesh.cell_face_normal_direction[ic, j] * vn[jf, :, :, :], 0.) if (icn >= 0 ) and (icn < ic): incr+= -(mesh.face_areas[jf] / mesh.cell_volumes[ic]) \ * vnm * df[icn, : , :, :] # divide by diagonal coefficient df[ic, :, :, :] += incr / (np.ones((nv, nv, nv)) * (1./tau + nu[ic]) + diag[ic]) f += df ''' end of LU-SGS iteration ''' if ((it % 50) == 0): fig, ax = plt.subplots(figsize = (20,10)) line, = ax.semilogy(frob_norm_iter/frob_norm_iter[0]) ax.set(title='$Steps =$' + str(it)) plt.grid(True) plt.savefig('norm_iter.png') plt.close() data[:, 0] = n[:] data[:, 1] = ux[:] data[:, 2] = uy[:] data[:, 3] = uz[:] data[:, 4] = p[:] data[:, 5] = T[:] data[:, 6] = np.zeros(mesh.nc) write_tecplot(mesh, data, 'tec.dat', ('n', 'ux', 'uy', 'uz', 'p', 'T', 'rank')) np.save(filename, np.ravel(f)) Return = namedtuple('Return', ['f', 'n', 'ux', 'uy', 'uz', 'T', 'p', 'frob_norm_iter']) S = Return(f, n, ux, uy, uz, T, p, frob_norm_iter) return S