def _set_derivative_method(self): if self.solver_type == 'spectral': self.dx = lambda x: fourier(x, self.ikx) self.dy = lambda x: fourier(x, self.iky) else: self.dx = lambda x: fd_x_4(x, self.ebdyc.grid.xh) self.dy = lambda x: fd_y_4(x, self.ebdyc.grid.yh)
def laplacian(self, ff, derivative_type='spectral'): """ Compute the laplacian of a function defined on the embedded boundary collection Inputs: ff, EmbeddedFunction: function to take the gradient of derivative_type: 'spectral' or 'fourth' in both cases, spectral differentation is used in the radial regions. On the grid, if 'spectral', then the function is cutoff and fourier based estimation of the derivative is used; if 'fourth', then the function is NOT cutoff and fourth-order centered differences are used. This may provide better accuracy when the cutoff windows are thin. However, if the cutoff windows are not at least 2h, then this can give catastrophically bad values Outputs: lapf: EmbeddedFunction giving the laplacian of f """ f, _, fr_list = ff.get_components() if derivative_type == 'spectral': fc = f * self.grid_step fc[self.ext] = 0.0 fch = np.fft.fft2(fc) lapfh = fch * self.lap lapf = np.fft.ifft2(lapfh).real else: fx = fd_x_4(f, self.grid.xh) fy = fd_y_4(f, self.grid.yh) fxx = fd_x_4(fx, self.grid.xh) fyy = fd_y_4(fy, self.grid.yh) # compute on the radial grid lapfrs = [] for i in range(self.N): lapfr = self.ebdys[i].laplacian(lapf, fr_list[i]) lapfrs.append(lapfr) # set to 0 on regular grid in the exterior region lapf *= self.phys # generate EmbeddedFunctions lapff = EmbeddedFunction(self) lapff.load_data(lapf, lapfrs) return lapff
def gradient(self, ff, derivative_type='spectral'): """ Compute the gradient of a function defined on the embedded boundary collection Inputs: ff, EmbeddedFunction: function to take the gradient of derivative_type: 'spectral' or 'fourth' in both cases, spectral differentation is used in the radial regions. On the grid, if 'spectral', then the function is cutoff and fourier based estimation of the derivative is used; if 'fourth', then the function is NOT cutoff and fourth-order centered differences are used. This may provide better accuracy when the cutoff windows are thin. However, if the cutoff windows are not at least 2h, then this can give catastrophically bad values Outputs: fx, fy: EmbeddedFunctions giving each required derivative """ f, _, fr_list = ff.get_components() if derivative_type == 'spectral': fc = f * self.grid_step fc[self.ext] = 0.0 fch = np.fft.fft2(fc) fxh = fch * self.ikx fyh = fch * self.iky fx = np.fft.ifft2(fxh).real fy = np.fft.ifft2(fyh).real else: fx = fd_x_4(f, self.grid.xh) fy = fd_y_4(f, self.grid.yh) # compute on the radial grid fxrs, fyrs = [], [] for i in range(self.N): fxr, fyr = self[i].gradient(fx, fy, fr_list[i]) fxrs.append(fxr) fyrs.append(fyr) # set to 0 on regular grid in the exterior region fx *= self.phys fy *= self.phys # generate EmbeddedFunctions ffx = EmbeddedFunction(self) ffy = EmbeddedFunction(self) ffx.load_data(fx, fxrs) ffy.load_data(fy, fyrs) return ffx, ffy
by = ebdy.bdy.y + dt * vb bx, by, new_t = arc_length_parameterize(bx, by, return_t=True) bu_interp = interp1d(0, 2 * np.pi, bdy.dt, ub, p=True) bv_interp = interp1d(0, 2 * np.pi, bdy.dt, vb, p=True) # old boundary velocity values have to be in the correct place ubo_new_parm = bu_interp(new_t) vbo_new_parm = bv_interp(new_t) ubo = ub.copy() vbo = vb.copy() x_tracers.append(bx) y_tracers.append(by) # take gradients of the velocity fields dx = lambda f: fd_x_4(f, grid.xh, periodic_fix=True) dy = lambda f: fd_y_4(f, grid.yh, periodic_fix=True) if u_differentiation_type == 'spectral': ux, uy = ebdyc.gradient2(u, xder, yder, cutoff=True) vx, vy = ebdyc.gradient2(v, xder, yder, cutoff=True) elif u_differentiation_type == 'fourth': ux, uy = ebdyc.gradient2(u, dx, dy, cutoff=False) vx, vy = ebdyc.gradient2(v, dx, dy, cutoff=False) else: ux = EmbeddedFunction(ebdyc) uy = EmbeddedFunction(ebdyc) vx = EmbeddedFunction(ebdyc) vy = EmbeddedFunction(ebdyc) ux.define_via_function(lambda x, y: ux_function(x, y, t)) uy.define_via_function(lambda x, y: uy_function(x, y, t)) vx.define_via_function(lambda x, y: vx_function(x, y, t))
err = max(errs) print('Error in grid --> interface interpolation: {:0.2e}'.format(err)) # Interpolation of a function from radial to grid frs = [test_func(ebdy.radial_x, ebdy.radial_y) for ebdy in ebdys] fts = ebdyc.interpolate_radial_to_grid(frs) fes = [test_func(ebdy.grid_ia_x, ebdy.grid_ia_y) for ebdy in ebdys] errs = [np.abs(fe - ft).max() for fe, ft in zip(fes, fts)] err = max(errs) print('Error in radial --> grid interpolation: {:0.2e}'.format(err)) ################################################################################ # Test derivatives # fourth order accurate gradient on whole domain dx = lambda x: fd_x_4(x, grid.xh, periodic_fix=True) dy = lambda x: fd_y_4(x, grid.yh, periodic_fix=True) fxe, fye, fxres, fyres = ebdyc.gradient(f, frs, dx, dy, cutoff=False) fxt = test_func_x(grid.xg, grid.yg) fyt = test_func_y(grid.xg, grid.yg) err_x = np.abs(fxt - fxe)[ebdyc.phys].max() err_y = np.abs(fyt - fye)[ebdyc.phys].max() err = max(err_x, err_y) print('Error in gradient, 4th order FD: {:0.2e}'.format(err)) # spectrally accurate gradient on whole domain kxv = np.fft.fftfreq(grid.Nx, grid.xh / (2 * np.pi)) kyv = np.fft.fftfreq(grid.Ny, grid.yh / (2 * np.pi)) kx, ky = np.meshgrid(kxv, kyv, indexing='ij') ikx, iky = 1j * kx, 1j * ky dx = lambda x: fourier(x, ikx)
print('Error in radial --> grid interpolation: {:0.2e}'.format(err)) ################################################################################ # Test derivatives # radial gradient frxe, frye = ebdy.radial_grid_derivatives(fr) frxt = test_func_x(ebdy.radial_x, ebdy.radial_y) fryt = test_func_y(ebdy.radial_x, ebdy.radial_y) err_x = np.abs(frxt-frxe).max() err_y = np.abs(fryt-frye).max() err = max(err_x, err_y) print('Error in radial grid differentiation: {:0.2e}'.format(err)) # fourth order accurate gradient on whole domain dx = lambda x: fd_x_4(x, grid.xh, periodic_fix=not interior) dy = lambda x: fd_y_4(x, grid.yh, periodic_fix=not interior) fxe, fye, fxre, fyre = ebdy.gradient(f, fr, dx, dy) fxt = test_func_x(grid.xg, grid.yg) fyt = test_func_y(grid.xg, grid.yg) err_x = np.abs(fxt-fxe)[ebdy.phys].max() err_y = np.abs(fyt-fye)[ebdy.phys].max() err = max(err_x, err_y) print('Error in gradient, 4th order FD: {:0.2e}'.format(err)) # spectrally accurate gradient on whole domain kxv = np.fft.fftfreq(grid.Nx, grid.xh/(2*np.pi)) kyv = np.fft.fftfreq(grid.Ny, grid.yh/(2*np.pi)) kx, ky = np.meshgrid(kxv, kyv, indexing='ij') ikx, iky = 1j*kx, 1j*ky dx = lambda x: fourier(x, ikx)
ebdyc.register_grid(grid) # initial c field c0 = EmbeddedFunction(ebdyc) c0.define_via_function(c0_function) def xder(f): return np.fft.ifft2(np.fft.fft2(f) * ikx).real def yder(f): return np.fft.ifft2(np.fft.fft2(f) * iky).real xder = lambda f: fd_x_4(f, grid.xh) yder = lambda f: fd_y_4(f, grid.yh) # now timestep c = c0.copy() t = 0.0 x_tracers = [] y_tracers = [] ts = [] new_ebdyc = ebdyc qfs0 = QFS_Evaluator(new_ebdyc.ebdys[0].bdy_qfs, True, [ Singular_SLP0,