def operator(self, unk): u = self.split_unknown(unk) Jxyz = cse(tangential_to_xyz(u.jt), "Jxyz") Mxyz = cse(tangential_to_xyz(u.mt), "Mxyz") omega = self.omega mu0, mu1 = self.mus eps0, eps1 = self.epss k0, k1 = self.ks S = partial(sym.S, self.kernel, qbx_forced_limit="avg") def curl_S(dens, k): return sym.curl(sym.S(self.kernel, dens, qbx_forced_limit="avg", k=k)) grad = partial(sym.grad, 3) E0 = sym.cse(1j*omega*mu0*eps0*S(Jxyz, k=k0) + mu0*curl_S(Mxyz, k=k0) - grad(S(u.rho_e, k=k0)), "E0") H0 = sym.cse(-1j*omega*mu0*eps0*S(Mxyz, k=k0) + eps0*curl_S(Jxyz, k=k0) + grad(S(u.rho_m, k=k0)), "H0") E1 = sym.cse(1j*omega*mu1*eps1*S(Jxyz, k=k1) + mu1*curl_S(Mxyz, k=k1) - grad(S(u.rho_e, k=k1)), "E1") H1 = sym.cse(-1j*omega*mu1*eps1*S(Mxyz, k=k1) + eps1*curl_S(Jxyz, k=k1) + grad(S(u.rho_m, k=k1)), "H1") F1 = (xyz_to_tangential(sym.n_cross(H1-H0) + 0.5*(eps0+eps1)*Jxyz)) F2 = (sym.n_dot(eps1*E1-eps0*E0) + 0.5*(eps1+eps0)*u.rho_e) F3 = (xyz_to_tangential(sym.n_cross(E1-E0) + 0.5*(mu0+mu1)*Mxyz)) # sign flip included F4 = -sym.n_dot(mu1*H1-mu0*H0) + 0.5*(mu1+mu0)*u.rho_m # noqa pylint:disable=invalid-unary-operand-type return sym.join_fields(F1, F2, F3, F4)
def operator(self, unk): u = self.split_unknown(unk) Jxyz = cse(tangential_to_xyz(u.jt), "Jxyz") Mxyz = cse(tangential_to_xyz(u.mt), "Mxyz") omega = self.omega mu0, mu1 = self.mus eps0, eps1 = self.epss k0, k1 = self.ks S = partial(sym.S, self.kernel, qbx_forced_limit="avg") def curl_S(dens, k): return sym.curl(sym.S(self.kernel, dens, qbx_forced_limit="avg", k=k)) grad = partial(sym.grad, 3) E0 = sym.cse(1j*omega*mu0*eps0*S(Jxyz, k=k0) + mu0*curl_S(Mxyz, k=k0) - grad(S(u.rho_e, k=k0)), "E0") H0 = sym.cse(-1j*omega*mu0*eps0*S(Mxyz, k=k0) + eps0*curl_S(Jxyz, k=k0) + grad(S(u.rho_m, k=k0)), "H0") E1 = sym.cse(1j*omega*mu1*eps1*S(Jxyz, k=k1) + mu1*curl_S(Mxyz, k=k1) - grad(S(u.rho_e, k=k1)), "E1") H1 = sym.cse(-1j*omega*mu1*eps1*S(Mxyz, k=k1) + eps1*curl_S(Jxyz, k=k1) + grad(S(u.rho_m, k=k1)), "H1") F1 = (xyz_to_tangential(sym.n_cross(H1-H0) + 0.5*(eps0+eps1)*Jxyz)) F2 = (sym.n_dot(eps1*E1-eps0*E0) + 0.5*(eps1+eps0)*u.rho_e) F3 = (xyz_to_tangential(sym.n_cross(E1-E0) + 0.5*(mu0+mu1)*Mxyz)) # sign flip included F4 = -sym.n_dot(mu1*H1-mu0*H0) + 0.5*(mu1+mu0)*u.rho_m # noqa pylint:disable=invalid-unary-operand-type return sym.flat_obj_array(F1, F2, F3, F4)
def rho_rhs(self, Jt, Einc_xyz): Jxyz = cse(tangential_to_xyz(Jt), "Jxyz") return (sym.n_dot(Einc_xyz) + 1j*self.k*sym.n_dot(sym.S( self.kernel, Jxyz, k=self.k, # continuous--qbx_forced_limit doesn't really matter qbx_forced_limit="avg")))
def rhs(self, Einc_xyz, Hinc_xyz): mu1 = self.mus[1] eps1 = self.epss[1] return sym.flat_obj_array(xyz_to_tangential(sym.n_cross(Hinc_xyz)), sym.n_dot(eps1 * Einc_xyz), xyz_to_tangential(sym.n_cross(Einc_xyz)), sym.n_dot(-mu1 * Hinc_xyz))
def rho_rhs(self, Jt, Einc_xyz): Jxyz = cse(tangential_to_xyz(Jt), "Jxyz") return (sym.n_dot(Einc_xyz) + 1j*self.k*sym.n_dot(sym.S( self.kernel, Jxyz, k=self.k, # continuous--qbx_forced_limit doesn't really matter qbx_forced_limit="avg")))
def rhs(self, Einc_xyz, Hinc_xyz): mu1 = self.mus[1] eps1 = self.epss[1] return sym.join_fields(xyz_to_tangential(sym.n_cross(Hinc_xyz)), sym.n_dot(eps1 * Einc_xyz), xyz_to_tangential(sym.n_cross(Einc_xyz)), sym.n_dot(-mu1 * Hinc_xyz))
def rhs(self, Einc_xyz, Hinc_xyz): mu1 = self.mus[1] eps1 = self.epss[1] return sym.join_fields( xyz_to_tangential(sym.n_cross(Hinc_xyz)), sym.n_dot(eps1*Einc_xyz), xyz_to_tangential(sym.n_cross(Einc_xyz)), sym.n_dot(-mu1*Hinc_xyz))
def nonlocal_integral_eq( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, fspace=None, vfspace=None, true_sol_grad_expr=None, actx=None, dgfspace=None, dgvfspace=None, meshmode_src_connection=None, qbx_kwargs=None, ): r""" see run_method for descriptions of unlisted args args: gamma and beta are used to precondition with the following equation: \Delta u - \kappa^2 \gamma u = 0 (\partial_n - i\kappa\beta) u |_\Sigma = 0 """ # make sure we get outer bdy id as tuple in case it consists of multiple ids if isinstance(outer_bdy_id, int): outer_bdy_id = [outer_bdy_id] outer_bdy_id = tuple(outer_bdy_id) # away from the excluded region, but firedrake and meshmode point # into pyt_inner_normal_sign = -1 ambient_dim = mesh.geometric_dimension() # {{{ Build src and tgt # build connection meshmode near src boundary -> src boundary inside meshmode from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from meshmode.discretization.connection import make_face_restriction factory = InterpolatoryQuadratureSimplexGroupFactory( dgfspace.finat_element.degree) src_bdy_connection = make_face_restriction(actx, meshmode_src_connection.discr, factory, scatterer_bdy_id) # source is a qbx layer potential from pytential.qbx import QBXLayerPotentialSource disable_refinement = (fspace.mesh().geometric_dimension() == 3) qbx = QBXLayerPotentialSource(src_bdy_connection.to_discr, **qbx_kwargs, _disable_refinement=disable_refinement) # get target indices and point-set target_indices, target = get_target_points_and_indices( fspace, outer_bdy_id) # }}} # build the operations from pytential import bind, sym r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * sym.grad( ambient_dim, sym.D(HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) """ op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D( HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) # bind the operations pyt_grad_op = bind((qbx, target), grad_op) pyt_op = bind((qbx, target), op) # }}} class MatrixFreeB(object): def __init__(self, A, pyt_grad_op, pyt_op, actx, kappa): """ :arg kappa: The wave number """ self.actx = actx self.k = kappa self.pyt_op = pyt_op self.pyt_grad_op = pyt_grad_op self.A = A self.meshmode_src_connection = meshmode_src_connection # {{{ Create some functions needed for multing self.x_fntn = Function(fspace) # CG self.potential_int = Function(fspace) self.potential_int.dat.data[:] = 0.0 self.grad_potential_int = Function(vfspace) self.grad_potential_int.dat.data[:] = 0.0 self.pyt_result = Function(fspace) self.n = FacetNormal(mesh) self.v = TestFunction(fspace) # some meshmode ones self.x_mm_fntn = self.meshmode_src_connection.discr.empty( self.actx, dtype='c') # }}} def mult(self, mat, x, y): # Copy function data into the fivredrake function self.x_fntn.dat.data[:] = x[:] # Transfer the function to meshmode self.meshmode_src_connection.from_firedrake(project( self.x_fntn, dgfspace), out=self.x_mm_fntn) # Restrict to boundary x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn) # Apply the operation potential_int_mm = self.pyt_op(self.actx, u=x_mm_fntn_on_bdy, k=self.k) grad_potential_int_mm = self.pyt_grad_op(self.actx, u=x_mm_fntn_on_bdy, k=self.k) # Store in firedrake self.potential_int.dat.data[target_indices] = potential_int_mm.get( ) for dim in range(grad_potential_int_mm.shape[0]): self.grad_potential_int.dat.data[ target_indices, dim] = grad_potential_int_mm[dim].get() # Integrate the potential r""" Compute the inner products using firedrake. Note this will be subtracted later, hence appears off by a sign. .. math:: \langle n(x) \cdot \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma - \langle i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma """ self.pyt_result = assemble( inner(inner(self.grad_potential_int, self.n), self.v) * ds(outer_bdy_id) - inner(self.potential_int, self.v) * ds(outer_bdy_id)) # y <- Ax - evaluated potential self.A.mult(x, y) with self.pyt_result.dat.vec_ro as ep: y.axpy(-1, ep) # {{{ Compute normal helmholtz operator u = TrialFunction(fspace) v = TestFunction(fspace) r""" .. math:: \langle \nabla u, \nabla v \rangle - \kappa^2 \cdot \langle u, v \rangle - i \kappa \langle u, v \rangle_\Sigma """ a = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2) * inner(u, v) * dx \ - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id) # get the concrete matrix from a general bilinear form A = assemble(a).M.handle # }}} # {{{ Setup Python matrix B = PETSc.Mat().create() # build matrix context Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, actx, wave_number) # set up B as same size as A B.setSizes(*A.getSizes()) B.setType(B.Type.PYTHON) B.setPythonContext(Bctx) B.setUp() # }}} # {{{ Create rhs # Remember f is \partial_n(true_sol)|_\Gamma # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y) sigma = sym.make_sym_vector("sigma", ambient_dim) r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * \ sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ op = 1j * sym.var("k") * pyt_inner_normal_sign * \ sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None) rhs_grad_op = bind((qbx, target), grad_op) rhs_op = bind((qbx, target), op) # Transfer to meshmode metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()} dg_true_sol_grad = project(true_sol_grad_expr, dgvfspace, form_compiler_parameters=metadata) true_sol_grad_mm = meshmode_src_connection.from_firedrake(dg_true_sol_grad, actx=actx) true_sol_grad_mm = src_bdy_connection(true_sol_grad_mm) # Apply the operations f_grad_convoluted_mm = rhs_grad_op(actx, sigma=true_sol_grad_mm, k=wave_number) f_convoluted_mm = rhs_op(actx, sigma=true_sol_grad_mm, k=wave_number) # Transfer function back to firedrake f_grad_convoluted = Function(vfspace) f_convoluted = Function(fspace) f_grad_convoluted.dat.data[:] = 0.0 f_convoluted.dat.data[:] = 0.0 for dim in range(f_grad_convoluted_mm.shape[0]): f_grad_convoluted.dat.data[target_indices, dim] = f_grad_convoluted_mm[dim].get() f_convoluted.dat.data[target_indices] = f_convoluted_mm.get() r""" \langle f, v \rangle_\Gamma + \langle i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma - \langle n(x) \cdot \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma """ rhs_form = inner(inner(true_sol_grad_expr, FacetNormal(mesh)), v) * ds(scatterer_bdy_id, metadata=metadata) \ + inner(f_convoluted, v) * ds(outer_bdy_id) \ - inner(inner(f_grad_convoluted, FacetNormal(mesh)), v) * ds(outer_bdy_id) rhs = assemble(rhs_form) # {{{ set up a solver: solution = Function(fspace, name="Computed Solution") # {{{ Used for preconditioning if 'gamma' in solver_parameters or 'beta' in solver_parameters: gamma = complex(solver_parameters.pop('gamma', 1.0)) import cmath beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma))) p = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2 * gamma) * inner(u, v) * dx \ - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id) P = assemble(p).M.handle else: P = A # }}} # Set up options to contain solver parameters: ksp = PETSc.KSP().create() if solver_parameters['pc_type'] == 'pyamg': del solver_parameters['pc_type'] # We are using the AMG preconditioner pyamg_tol = solver_parameters.get('pyamg_tol', None) if pyamg_tol is not None: pyamg_tol = float(pyamg_tol) pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None) if pyamg_maxiter is not None: pyamg_maxiter = int(pyamg_maxiter) ksp.setOperators(B) ksp.setUp() pc = ksp.pc pc.setType(pc.Type.PYTHON) pc.setPythonContext( AMGTransmissionPreconditioner(wave_number, fspace, A, tol=pyamg_tol, maxiter=pyamg_maxiter, use_plane_waves=True)) # Otherwise use regular preconditioner else: ksp.setOperators(B, P) options_manager = OptionsManager(solver_parameters, options_prefix) options_manager.set_from_options(ksp) import petsc4py.PETSc petsc4py.PETSc.Sys.popErrorHandler() with rhs.dat.vec_ro as b: with solution.dat.vec as x: ksp.solve(b, x) # }}} return ksp, solution
i.e. a shift of the fundamental solution """ expr = fd.ln(fd.sqrt((xx[0] + 2)**2 + (xx[1] + 2)**2)) f = fd.Function(V).interpolate(expr) gradf = fd.Function(Vdim).interpolate(fd.grad(expr)) # Let's create an operator which plugs in f, \partial_n f # to Green's formula sigma = sym.make_sym_vector("sigma", 2) op = -(sym.D(LaplaceKernel(2), sym.var("u"), qbx_forced_limit=None) - sym.S(LaplaceKernel(2), sym.n_dot(sigma), qbx_forced_limit=None)) from meshmode.mesh import BTAG_ALL outer_bdy_id = BTAG_ALL # Think of this like :mod:`pytential`'s :function:`bind` pyt_op = fd2mm.fd_bind(cl_ctx, fspace_analog, op, source=(V, outer_bdy_id), target=V, with_refinement=with_refinement, qbx_kwargs=qbx_kwargs ) # Compute the operation and store in g g = fd.Function(V) pyt_op(queue, u=f, sigma=gradf, result_function=g)
def nonlocal_integral_eq( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, fspace=None, vfspace=None, true_sol_grad=None, queue=None, fspace_analog=None, qbx_kwargs=None, ): r""" see run_method for descriptions of unlisted args args: :arg queue: A command queue for the computing context gamma and beta are used to precondition with the following equation: \Delta u - \kappa^2 \gamma u = 0 (\partial_n - i\kappa\beta) u |_\Sigma = 0 """ with_refinement = True # away from the excluded region, but firedrake and meshmode point # into pyt_inner_normal_sign = -1 ambient_dim = mesh.geometric_dimension() # {{{ Create operator from pytential import sym r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * sym.grad( ambient_dim, sym.D(HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) """ op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D( HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) pyt_grad_op = fd2mm.fd_bind( queue.context, fspace_analog, grad_op, source=(fspace, scatterer_bdy_id), target=(vfspace, outer_bdy_id), with_refinement=with_refinement, qbx_kwargs=qbx_kwargs, ) pyt_op = fd2mm.fd_bind( queue.context, fspace_analog, op, source=(fspace, scatterer_bdy_id), target=(fspace, outer_bdy_id), with_refinement=with_refinement, qbx_kwargs=qbx_kwargs, ) # }}} class MatrixFreeB(object): def __init__(self, A, pyt_grad_op, pyt_op, queue, kappa): """ :arg kappa: The wave number """ self.queue = queue self.k = kappa self.pyt_op = pyt_op self.pyt_grad_op = pyt_grad_op self.A = A # {{{ Create some functions needed for multing self.x_fntn = Function(fspace) self.potential_int = Function(fspace) self.potential_int.dat.data[:] = 0.0 self.grad_potential_int = Function(vfspace) self.grad_potential_int.dat.data[:] = 0.0 self.pyt_result = Function(fspace) self.n = FacetNormal(mesh) self.v = TestFunction(fspace) # }}} def mult(self, mat, x, y): # Perform pytential operation self.x_fntn.dat.data[:] = x[:] self.pyt_op(self.queue, self.potential_int, u=self.x_fntn, k=self.k) self.pyt_grad_op(self.queue, self.grad_potential_int, u=self.x_fntn, k=self.k) # Integrate the potential r""" Compute the inner products using firedrake. Note this will be subtracted later, hence appears off by a sign. .. math:: \langle n(x) \cdot \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma - \langle i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma """ self.pyt_result = assemble( inner(inner(self.grad_potential_int, self.n), self.v) * ds(outer_bdy_id) - inner(self.potential_int, self.v) * ds(outer_bdy_id)) # y <- Ax - evaluated potential self.A.mult(x, y) with self.pyt_result.dat.vec_ro as ep: y.axpy(-1, ep) # {{{ Compute normal helmholtz operator u = TrialFunction(fspace) v = TestFunction(fspace) r""" .. math:: \langle \nabla u, \nabla v \rangle - \kappa^2 \cdot \langle u, v \rangle - i \kappa \langle u, v \rangle_\Sigma """ a = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2) * inner(u, v) * dx \ - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id) # get the concrete matrix from a general bilinear form A = assemble(a).M.handle # }}} # {{{ Setup Python matrix B = PETSc.Mat().create() # build matrix context Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, queue, wave_number) # set up B as same size as A B.setSizes(*A.getSizes()) B.setType(B.Type.PYTHON) B.setPythonContext(Bctx) B.setUp() # }}} # {{{ Create rhs # Remember f is \partial_n(true_sol)|_\Gamma # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y) from pytential import sym sigma = sym.make_sym_vector("sigma", ambient_dim) r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * \ sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ op = 1j * sym.var("k") * pyt_inner_normal_sign * \ sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None) rhs_grad_op = fd2mm.fd_bind( queue.context, fspace_analog, grad_op, source=(vfspace, scatterer_bdy_id), target=(vfspace, outer_bdy_id), with_refinement=with_refinement, qbx_kwargs=qbx_kwargs, ) rhs_op = fd2mm.fd_bind( queue.context, fspace_analog, op, source=(vfspace, scatterer_bdy_id), target=(fspace, outer_bdy_id), with_refinement=with_refinement, qbx_kwargs=qbx_kwargs, ) f_grad_convoluted = Function(vfspace) f_convoluted = Function(fspace) rhs_grad_op(queue, f_grad_convoluted, sigma=true_sol_grad, k=wave_number) rhs_op(queue, f_convoluted, sigma=true_sol_grad, k=wave_number) r""" \langle f, v \rangle_\Gamma + \langle i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma - \langle n(x) \cdot \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma """ rhs_form = inner(inner(true_sol_grad, FacetNormal(mesh)), v) * ds(scatterer_bdy_id) \ + inner(f_convoluted, v) * ds(outer_bdy_id) \ - inner(inner(f_grad_convoluted, FacetNormal(mesh)), v) * ds(outer_bdy_id) rhs = assemble(rhs_form) # {{{ set up a solver: solution = Function(fspace, name="Computed Solution") # {{{ Used for preconditioning if 'gamma' in solver_parameters or 'beta' in solver_parameters: gamma = complex(solver_parameters.pop('gamma', 1.0)) import cmath beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma))) p = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2 * gamma) * inner(u, v) * dx \ - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id) P = assemble(p).M.handle else: P = A # }}} # Set up options to contain solver parameters: ksp = PETSc.KSP().create() if solver_parameters['pc_type'] == 'pyamg': del solver_parameters['pc_type'] # We are using the AMG preconditioner pyamg_tol = solver_parameters.get('pyamg_tol', None) if pyamg_tol is not None: pyamg_tol = float(pyamg_tol) pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None) if pyamg_maxiter is not None: pyamg_maxiter = int(pyamg_maxiter) ksp.setOperators(B) ksp.setUp() pc = ksp.pc pc.setType(pc.Type.PYTHON) pc.setPythonContext( AMGTransmissionPreconditioner(wave_number, fspace, A, tol=pyamg_tol, maxiter=pyamg_maxiter, use_plane_waves=True)) # Otherwise use regular preconditioner else: ksp.setOperators(B, P) options_manager = OptionsManager(solver_parameters, options_prefix) options_manager.set_from_options(ksp) with rhs.dat.vec_ro as b: with solution.dat.vec as x: ksp.solve(b, x) # }}} return ksp, solution
def test_greens_formula(degree, family, ambient_dim): fine_order = 4 * degree # Parameter to tune accuracy of pytential fmm_order = 5 # This should be (order of convergence = qbx_order + 1) qbx_order = degree with_refinement = True qbx_kwargs = { 'fine_order': fine_order, 'fmm_order': fmm_order, 'qbx_order': qbx_order } if ambient_dim == 2: mesh = mesh2d r""" ..math: \ln(\sqrt{(x+1)^2 + (y+1)^2}) i.e. a shift of the fundamental solution """ x, y = fd.SpatialCoordinate(mesh) expr = fd.ln(fd.sqrt((x + 2)**2 + (y + 2)**2)) elif ambient_dim == 3: mesh = mesh3d x, y, z = fd.SpatialCoordinate(mesh) r""" ..math: \f{1}{4\pi \sqrt{(x-2)^2 + (y-2)^2 + (z-2)^2)}} i.e. a shift of the fundamental solution """ expr = fd.Constant(1 / 4 / fd.pi) * 1 / fd.sqrt((x - 2)**2 + (y - 2)**2 + (z - 2)**2) else: raise ValueError("Ambient dimension must be 2 or 3, not %s" % ambient_dim) # Let's compute some layer potentials! V = fd.FunctionSpace(mesh, family, degree) Vdim = fd.VectorFunctionSpace(mesh, family, degree) mesh_analog = fd2mm.MeshAnalog(mesh) fspace_analog = fd2mm.FunctionSpaceAnalog(cl_ctx, mesh_analog, V) true_sol = fd.Function(V).interpolate(expr) grad_true_sol = fd.Function(Vdim).interpolate(fd.grad(expr)) # Let's create an operator which plugs in f, \partial_n f # to Green's formula sigma = sym.make_sym_vector("sigma", ambient_dim) op = -(sym.D( LaplaceKernel(ambient_dim), sym.var("u"), qbx_forced_limit=None) - sym.S(LaplaceKernel(ambient_dim), sym.n_dot(sigma), qbx_forced_limit=None)) from meshmode.mesh import BTAG_ALL outer_bdy_id = BTAG_ALL # Think of this like :mod:`pytential`'s :function:`bind` pyt_op = fd2mm.fd_bind(cl_ctx, fspace_analog, op, source=(V, outer_bdy_id), target=V, qbx_kwargs=qbx_kwargs, with_refinement=with_refinement) # Compute the operation and store in result result = fd.Function(V) pyt_op(queue, u=true_sol, sigma=grad_true_sol, result_function=result) # Compare with f fnorm = fd.sqrt(fd.assemble(fd.inner(true_sol, true_sol) * fd.dx)) l2_err = fd.sqrt( fd.assemble(fd.inner(true_sol - result, true_sol - result) * fd.dx)) rel_l2_err = l2_err / fnorm # TODO: Make this more strict assert rel_l2_err < 0.09