def operator(self, u): from sumpy.kernel import HelmholtzKernel, LaplaceKernel sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) knl = self.kernel lknl = self.laplace_kernel knl_kwargs = {} knl_kwargs["kernel_arguments"] = self.kernel_arguments DpS0u = sym.Dp( knl, # noqa cse(sym.S(lknl, inv_sqrt_w_u)), **knl_kwargs) if self.use_improved_operator: Dp0S0u = -0.25 * u + sym.Sp( # noqa lknl, # noqa sym.Sp(lknl, inv_sqrt_w_u, qbx_forced_limit="avg"), qbx_forced_limit="avg") if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( # noqa sym.Dp( knl - lknl, # noqa cse(sym.S(lknl, inv_sqrt_w_u, qbx_forced_limit=+1)), qbx_forced_limit=+1, **knl_kwargs) + Dp0S0u) elif isinstance(knl, LaplaceKernel): DpS0u = Dp0S0u # noqa else: raise ValueError( f"no improved operator for '{self.kernel}' known") if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. amb_dim = self.kernel.dim ones_contribution = (sym.Ones() * sym.mean(amb_dim, amb_dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 return ( -self.loc_sign * 0.5 * u + sqrt_w * (sym.Sp(knl, inv_sqrt_w_u, qbx_forced_limit="avg", **knl_kwargs) - self.alpha * DpS0u + ones_contribution))
def get_operator(self, ambient_dim, qbx_forced_limit="avg"): knl = self.knl_class(ambient_dim) kwargs = self.knl_sym_kwargs.copy() kwargs["qbx_forced_limit"] = qbx_forced_limit if self.op_type == "scalar": sym_u = sym.var("u") sym_op = sym.S(knl, sym_u, **kwargs) elif self.op_type == "scalar_mixed": sym_u = sym.var("u") sym_op = sym.S(knl, 0.3 * sym_u, **kwargs) \ + sym.D(knl, 0.5 * sym_u, **kwargs) elif self.op_type == "vector": sym_u = sym.make_sym_vector("u", ambient_dim) sym_op = make_obj_array([ sym.Sp(knl, sym_u[0], **kwargs) + sym.D(knl, sym_u[1], **kwargs), sym.S(knl, 0.4 * sym_u[0], **kwargs) + 0.3 * sym.D(knl, sym_u[0], **kwargs) ]) else: raise ValueError(f"unknown operator type: '{self.op_type}'") sym_op = 0.5 * sym_u + sym_op return sym_u, sym_op
def _build_op(lpot_id, k=0, ndim=2, source=sym.DEFAULT_SOURCE, target=sym.DEFAULT_TARGET, qbx_forced_limit="avg"): from sumpy.kernel import LaplaceKernel, HelmholtzKernel if k: knl = HelmholtzKernel(ndim) knl_kwargs = {"k": k} else: knl = LaplaceKernel(ndim) knl_kwargs = {} lpot_kwargs = { "qbx_forced_limit": qbx_forced_limit, "source": source, "target": target} lpot_kwargs.update(knl_kwargs) if lpot_id == 1: # scalar single-layer potential u_sym = sym.var("u") op = sym.S(knl, u_sym, **lpot_kwargs) elif lpot_id == 2: # scalar combination of layer potentials u_sym = sym.var("u") op = sym.S(knl, 0.3 * u_sym, **lpot_kwargs) \ + sym.D(knl, 0.5 * u_sym, **lpot_kwargs) elif lpot_id == 3: # vector potential u_sym = sym.make_sym_vector("u", 2) u0_sym, u1_sym = u_sym op = make_obj_array([ sym.Sp(knl, u0_sym, **lpot_kwargs) + sym.D(knl, u1_sym, **lpot_kwargs), sym.S(knl, 0.4 * u0_sym, **lpot_kwargs) + 0.3 * sym.D(knl, u0_sym, **lpot_kwargs) ]) else: raise ValueError("Unknown lpot_id: {}".format(lpot_id)) op = 0.5 * u_sym + op return op, u_sym, knl_kwargs
def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, force_direct, visualize=False): logging.basicConfig(level=logging.INFO) print("ellipse_aspect: %s, mode_nr: %d, qbx_order: %d" % (ellipse_aspect, mode_nr, qbx_order)) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() if ellipse_aspect != 1: nelements_values = [60, 100, 150, 200] else: nelements_values = [30, 70] # See # # [1] G. J. Rodin and O. Steinbach, "Boundary Element Preconditioners # for Problems Defined on Slender Domains", SIAM Journal on Scientific # Computing, Vol. 24, No. 4, pg. 1450, 2003. # https://dx.doi.org/10.1137/S1064827500372067 for nelements in nelements_values: mesh = make_curve_mesh(partial(ellipse, ellipse_aspect), np.linspace(0, 1, nelements + 1), target_order) fmm_order = 12 if force_direct: fmm_order = False pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=fmm_order, _expansions_in_tree_have_extent=True, ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from meshmode.dof_array import thaw, flatten nodes = thaw(actx, density_discr.nodes()) if visualize: # plot geometry, centers, normals centers = bind(places, sym.expansion_centers(qbx.ambient_dim, +1))(actx) normals = bind(places, sym.normal(qbx.ambient_dim))(actx).as_vector(object) nodes_h = np.array( [actx.to_numpy(axis) for axis in flatten(nodes)]) centers_h = np.array( [actx.to_numpy(axis) for axis in flatten(centers)]) normals_h = np.array( [actx.to_numpy(axis) for axis in flatten(normals)]) pt.plot(nodes_h[0], nodes_h[1], "x-") pt.plot(centers_h[0], centers_h[1], "o") pt.quiver(nodes_h[0], nodes_h[1], normals_h[0], normals_h[1]) pt.gca().set_aspect("equal") pt.show() angle = actx.np.arctan2(nodes[1] * ellipse_aspect, nodes[0]) ellipse_fraction = ((1 - ellipse_aspect) / (1 + ellipse_aspect))**mode_nr # (2.6) in [1] J = actx.np.sqrt( # noqa actx.np.sin(angle)**2 + (1 / ellipse_aspect)**2 * actx.np.cos(angle)**2) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(2) # {{{ single layer sigma_sym = sym.var("sigma") s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1) sigma = actx.np.cos(mode_nr * angle) / J s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) s_eigval = 1 / (2 * mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction) # (2.12) in [1] s_sigma_ref = s_eigval * J * sigma if 0: #pt.plot(s_sigma.get(), label="result") #pt.plot(s_sigma_ref.get(), label="ref") pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err") pt.legend() pt.show() h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_err = (norm(density_discr, s_sigma - s_sigma_ref) / norm(density_discr, s_sigma_ref)) s_eoc_rec.add_data_point(h_max, s_err) # }}} # {{{ double layer d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) d_eigval = -(-1)**mode_nr * 1 / 2 * ellipse_fraction d_sigma_ref = d_eigval * sigma if 0: pt.plot(actx.to_numpy(flatten(d_sigma)), label="result") pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref") pt.legend() pt.show() if ellipse_aspect == 1: d_ref_norm = norm(density_discr, sigma) else: d_ref_norm = norm(density_discr, d_sigma_ref) d_err = (norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm) d_eoc_rec.add_data_point(h_max, d_err) # }}} if ellipse_aspect == 1: # {{{ S' sp_sigma_op = sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma) sp_eigval = 0 sp_sigma_ref = sp_eigval * sigma sp_err = (norm(density_discr, sp_sigma - sp_sigma_ref) / norm(density_discr, sigma)) sp_eoc_rec.add_data_point(h_max, sp_err) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 1.5 if ellipse_aspect == 1: print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5
def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, fmm_backend): logging.basicConfig(level=logging.INFO) special = pytest.importorskip("scipy.special") cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() dp_eoc_rec = EOCRecorder() def rel_err(comp, ref): return (norm(density_discr, comp - ref) / norm(density_discr, ref)) for nrefinements in [0, 1]: from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(1, target_order) from meshmode.mesh.refinement import Refiner refiner = Refiner(mesh) for i in range(nrefinements): flags = np.ones(mesh.nelements, dtype=bool) refiner.refine(flags) mesh = refiner.get_current_mesh() pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=6, fmm_backend=fmm_backend, ) places = GeometryCollection(qbx) from meshmode.dof_array import flatten, unflatten, thaw density_discr = places.get_discretization(places.auto_source.geometry) nodes = thaw(actx, density_discr.nodes()) r = actx.np.sqrt(nodes[0] * nodes[0] + nodes[1] * nodes[1] + nodes[2] * nodes[2]) phi = actx.np.arccos(nodes[2] / r) theta = actx.np.arctan2(nodes[0], nodes[1]) ymn = unflatten( actx, density_discr, actx.from_numpy( special.sph_harm(mode_m, mode_n, actx.to_numpy(flatten(theta)), actx.to_numpy(flatten(phi))))) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(3) # {{{ single layer s_sigma_op = bind( places, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1)) s_sigma = s_sigma_op(actx, sigma=ymn) s_eigval = 1 / (2 * mode_n + 1) h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval * ymn)) # }}} # {{{ double layer d_sigma_op = bind( places, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) d_sigma = d_sigma_op(actx, sigma=ymn) d_eigval = -1 / (2 * (2 * mode_n + 1)) d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval * ymn)) # }}} # {{{ S' sp_sigma_op = bind( places, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) sp_sigma = sp_sigma_op(actx, sigma=ymn) sp_eigval = -1 / (2 * (2 * mode_n + 1)) sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval * ymn)) # }}} # {{{ D' dp_sigma_op = bind( places, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) dp_sigma = dp_sigma_op(actx, sigma=ymn) dp_eigval = -(mode_n * (mode_n + 1)) / (2 * mode_n + 1) dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval * ymn)) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 0.5 print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D':") print(dp_eoc_rec) required_order = qbx_order assert dp_eoc_rec.order_estimate() > required_order - 1.5
def test_3d_jump_relations(ctx_factory, relation, visualize=False): # logging.basicConfig(level=logging.INFO) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) if relation == "div_s": target_order = 3 else: target_order = 4 qbx_order = target_order from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_factor in [6, 10, 14]: from meshmode.mesh.generation import generate_torus mesh = generate_torus( 5, 2, order=target_order, n_major=2*nel_factor, n_minor=nel_factor) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(3)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource( pre_discr, fine_order=4*target_order, qbx_order=qbx_order, fmm_order=qbx_order + 5, fmm_backend="fmmlib" ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from sumpy.kernel import LaplaceKernel knl = LaplaceKernel(3) def nxcurlS(qbx_forced_limit): return sym.n_cross(sym.curl(sym.S( knl, sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"), qbx_forced_limit=qbx_forced_limit))) from meshmode.dof_array import thaw x, y, z = thaw(actx, density_discr.nodes()) m = actx.np if relation == "nxcurls": density_sym = sym.make_sym_vector("density", 2) jump_identity_sym = ( nxcurlS(+1) - (nxcurlS("avg") + 0.5*sym.tangential_to_xyz(density_sym))) # The tangential coordinate system is element-local, so we can't just # conjure up some globally smooth functions, interpret their values # in the tangential coordinate system, and be done. Instead, generate # an XYZ function and project it. density = bind(places, sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))( actx, jxyz=sym.make_obj_array([ m.cos(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z), m.sin(0.5*x) * m.cos(0.5*y) * m.sin(0.5*z), m.sin(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z), ])) elif relation == "sp": density = m.cos(2*x) * m.cos(2*y) * m.cos(z) density_sym = sym.var("density") jump_identity_sym = ( sym.Sp(knl, density_sym, qbx_forced_limit=+1) - (sym.Sp(knl, density_sym, qbx_forced_limit="avg") - 0.5*density_sym)) elif relation == "div_s": density = m.cos(2*x) * m.cos(2*y) * m.cos(z) density_sym = sym.var("density") jump_identity_sym = ( sym.div(sym.S(knl, sym.normal(3).as_vector()*density_sym, qbx_forced_limit="avg")) + sym.D(knl, density_sym, qbx_forced_limit="avg")) else: raise ValueError("unexpected value of 'relation': %s" % relation) bound_jump_identity = bind(places, jump_identity_sym) jump_identity = bound_jump_identity(actx, density=density) h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) err = ( norm(density_discr, jump_identity, np.inf) / norm(density_discr, density, np.inf)) print("ERROR", h_max, err) eoc_rec.add_data_point(h_max, err) # {{{ visualization if visualize and relation == "nxcurls": nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density) nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density) jtxyz = bind(places, sym.tangential_to_xyz(density_sym))( actx, density=density) from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) bdry_normals = bind(places, sym.normal(3))(actx)\ .as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ ("jt", jtxyz), ("nxcurlS_ext", nxcurlS_ext), ("nxcurlS_avg", nxcurlS_avg), ("bdry_normals", bdry_normals), ]) if visualize and relation == "sp": op = sym.Sp(knl, density_sym, qbx_forced_limit=+1) sp_ext = bind(places, op)(actx, density=density) op = sym.Sp(knl, density_sym, qbx_forced_limit="avg") sp_avg = bind(places, op)(actx, density=density) from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) bdry_normals = bind(places, sym.normal(3))(actx).as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ ("density", density), ("sp_ext", sp_ext), ("sp_avg", sp_avg), ("bdry_normals", bdry_normals), ]) # }}} print(eoc_rec) assert eoc_rec.order_estimate() >= qbx_order - 1.5
def Sp(density): return sym.Sp(self.laplace_kernel, density, qbx_forced_limit="avg")
def operator(self, u, **kwargs): """ :param u: symbolic variable for the density. :param kwargs: additional keyword arguments passed on to the layer potential constructor. """ sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = sym.cse(u/sqrt_w) laplace_s_inv_sqrt_w_u = sym.cse( sym.S(self.laplace_kernel, inv_sqrt_w_u, qbx_forced_limit=+1) ) kwargs["kernel_arguments"] = self.kernel_arguments # NOTE: the improved operator here is based on right-precondioning # by a single layer and then using some Calderon identities to simplify # the result. The integral equation we start with for Neumann is # I + S' + alpha D' = g # where D' is hypersingular if self.use_improved_operator: def Sp(density): return sym.Sp(self.laplace_kernel, density, qbx_forced_limit="avg") # NOTE: using the Calderon identity # D' S = -u/4 + S'^2 Dp0S0u = -0.25 * u + Sp(Sp(inv_sqrt_w_u)) from sumpy.kernel import HelmholtzKernel, LaplaceKernel if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( sym.Dp( self.kernel - self.laplace_kernel, laplace_s_inv_sqrt_w_u, qbx_forced_limit=+1, **kwargs) + Dp0S0u) elif isinstance(self.kernel, LaplaceKernel): DpS0u = Dp0S0u else: raise ValueError(f"no improved operator for '{self.kernel}' known") else: DpS0u = sym.Dp(self.kernel, laplace_s_inv_sqrt_w_u, **kwargs) if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. ones_contribution = ( sym.Ones() * sym.mean(self.dim, self.dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 kwargs["qbx_forced_limit"] = "avg" return ( -0.5 * self.loc_sign * u + sqrt_w * ( sym.Sp(self.kernel, inv_sqrt_w_u, **kwargs) - self.alpha * DpS0u + ones_contribution ) )
def rho_operator(self, loc, rho): return loc*0.5*rho+sym.Sp( self.kernel, rho, k=self.k, qbx_forced_limit="avg")
def test_3d_jump_relations(actx_factory, relation, visualize=False): # logging.basicConfig(level=logging.INFO) actx = actx_factory() if relation == "div_s": target_order = 3 else: target_order = 4 qbx_order = target_order if relation == "sp": resolutions = [10, 14, 18] else: resolutions = [6, 10, 14] from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_factor in resolutions: from meshmode.mesh.generation import generate_torus mesh = generate_torus( 5, 2, n_major=2 * nel_factor, n_minor=nel_factor, order=target_order, ) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(pre_density_discr, fine_order=5 * target_order, qbx_order=qbx_order, fmm_order=qbx_order + 5, fmm_backend="fmmlib") places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from sumpy.kernel import LaplaceKernel knl = LaplaceKernel(places.ambient_dim) def nxcurlS(qbx_forced_limit): sigma_sym = sym.cse(sym.tangential_to_xyz(density_sym), "jxyz") return sym.n_cross( sym.curl( sym.S(knl, sigma_sym, qbx_forced_limit=qbx_forced_limit))) x, y, z = thaw(density_discr.nodes(), actx) if relation == "nxcurls": density_sym = sym.make_sym_vector("density", 2) jump_identity_sym = (nxcurlS(+1) - nxcurlS("avg") - 0.5 * sym.tangential_to_xyz(density_sym)) # The tangential coordinate system is element-local, so we can't just # conjure up some globally smooth functions, interpret their values # in the tangential coordinate system, and be done. Instead, generate # an XYZ function and project it. jxyz = sym.make_obj_array([ actx.np.cos(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.cos(0.5 * z), actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.sin(0.5 * z), actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.cos(0.5 * z), ]) density = bind( places, sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))(actx, jxyz=jxyz) elif relation == "sp": density_sym = sym.var("density") jump_identity_sym = ( 0.5 * density_sym + sym.Sp(knl, density_sym, qbx_forced_limit=+1) - sym.Sp(knl, density_sym, qbx_forced_limit="avg")) density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z) elif relation == "div_s": density_sym = sym.var("density") sigma_sym = sym.normal( places.ambient_dim).as_vector() * density_sym jump_identity_sym = ( sym.div(sym.S(knl, sigma_sym, qbx_forced_limit="avg")) + sym.D(knl, density_sym, qbx_forced_limit="avg")) density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z) else: raise ValueError(f"unexpected value of 'relation': '{relation}'") bound_jump_identity = bind(places, jump_identity_sym) jump_identity = bound_jump_identity(actx, density=density) h_max = actx.to_numpy( bind(places, sym.h_max(places.ambient_dim))(actx)) err = actx.to_numpy( norm(density_discr, jump_identity, np.inf) / norm(density_discr, density, np.inf)) eoc_rec.add_data_point(h_max, err) logging.info("error: nel %d h_max %.5e %.5e", nel_factor, h_max, err) # {{{ visualization if not visualize: continue from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, density_discr, target_order) normals = bind(places, sym.normal(places.ambient_dim).as_vector())(actx) error = actx.np.log10(actx.np.abs(jump_identity) + 1.0e-15) if relation == "nxcurls": nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density) nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density) jtxyz = bind(places, sym.tangential_to_xyz(density_sym))(actx, density=density) vis.write_vtk_file(f"source-nxcurls-{nel_factor:03d}.vtu", [ ("jt", jtxyz), ("nxcurlS_ext", nxcurlS_ext), ("nxcurlS_avg", nxcurlS_avg), ("bdry_normals", normals), ("error", error), ]) elif relation == "sp": op = sym.Sp(knl, density_sym, qbx_forced_limit=+1) sp_ext = bind(places, op)(actx, density=density) op = sym.Sp(knl, density_sym, qbx_forced_limit="avg") sp_avg = bind(places, op)(actx, density=density) vis.write_vtk_file(f"source-sp-{nel_factor:03d}.vtu", [ ("density", density), ("sp_ext", sp_ext), ("sp_avg", sp_avg), ("bdry_normals", normals), ("error", error), ]) elif relation == "div_s": vis.write_vtk_file(f"source-div-{nel_factor:03d}.vtu", [ ("density", density), ("bdry_normals", normals), ("error", error), ]) # }}} logger.info("\n%s", str(eoc_rec)) assert eoc_rec.order_estimate() >= qbx_order - 1.5
def get_bvp_error(lpot_source, fmm_order, qbx_order, k=0): # This returns a tuple (err_l2, err_linf, nit). queue = cl.CommandQueue(lpot_source.cl_context) lpot_source = lpot_source.copy( qbx_order=qbx_order, fmm_level_to_order=(False if fmm_order is False else lambda *args: fmm_order)) d = lpot_source.ambient_dim assert k == 0 # Helmholtz would require a different representation from sumpy.kernel import LaplaceKernel, HelmholtzKernel lap_k_sym = LaplaceKernel(d) if k == 0: k_sym = lap_k_sym knl_kwargs = {} else: k_sym = HelmholtzKernel(d) knl_kwargs = {"k": sym.var("k")} density_discr = lpot_source.density_discr # {{{ find source and target points source_angles = (np.pi / 2 + np.linspace(0, 2 * np.pi * BVP_EXPERIMENT_N_ARMS, BVP_EXPERIMENT_N_ARMS, endpoint=False)) / BVP_EXPERIMENT_N_ARMS source_points = 0.75 * np.array([ np.cos(source_angles), np.sin(source_angles), ]) target_angles = (np.pi + np.pi / 2 + np.linspace(0, 2 * np.pi * BVP_EXPERIMENT_N_ARMS, BVP_EXPERIMENT_N_ARMS, endpoint=False)) / BVP_EXPERIMENT_N_ARMS target_points = 1.5 * np.array([ np.cos(target_angles), np.sin(target_angles), ]) np.random.seed(17) source_charges = np.random.randn(BVP_EXPERIMENT_N_ARMS) source_points_dev = cl.array.to_device(queue, source_points) target_points_dev = cl.array.to_device(queue, target_points) source_charges_dev = cl.array.to_device(queue, source_charges) from pytential.source import PointPotentialSource from pytential.target import PointsTarget point_source = PointPotentialSource(lpot_source.cl_context, source_points_dev) pot_src = sym.IntG( # FIXME: qbx_forced_limit--really? k_sym, sym.var("charges"), qbx_forced_limit=None, **knl_kwargs) ref_direct = bind((point_source, PointsTarget(target_points_dev)), pot_src)(queue, charges=source_charges_dev, **knl_kwargs).get() sym_sqrt_j = sym.sqrt_jac_q_weight(density_discr.ambient_dim) bc = bind((point_source, density_discr), sym.normal_derivative(density_discr.ambient_dim, pot_src, where=sym.DEFAULT_TARGET))( queue, charges=source_charges_dev, **knl_kwargs) rhs = bind(density_discr, sym.var("bc") * sym_sqrt_j)(queue, bc=bc) # }}} # {{{ solve bound_op = bind( lpot_source, -0.5 * sym.var("u") + sym_sqrt_j * sym.Sp(k_sym, sym.var("u") / sym_sqrt_j, qbx_forced_limit="avg", **knl_kwargs)) from pytential.solve import gmres gmres_result = gmres(bound_op.scipy_op(queue, "u", np.float64, **knl_kwargs), rhs, tol=1e-10, stall_iterations=100, progress=True, hard_failure=True) u = gmres_result.solution # }}} points_target = PointsTarget(target_points_dev) bound_tgt_op = bind((lpot_source, points_target), sym.S(k_sym, sym.var("u") / sym_sqrt_j, qbx_forced_limit=None)) test_via_bdry = bound_tgt_op(queue, u=u).get() err = ref_direct - test_via_bdry err_l2 = la.norm(err, 2) / la.norm(ref_direct, 2) err_linf = la.norm(err, np.inf) / la.norm(ref_direct, np.inf) return err_l2, err_linf, gmres_result.iteration_count