def S_G(self, i, density, qbx_forced_limit, op_map=None): # noqa: N802 if op_map is None: op_map = lambda x: x # noqa: E731 from sumpy.kernel import HelmholtzKernel hhk = HelmholtzKernel(2, allow_evanescent=True) hhk_scaling = 1j / 4 if i == 0: lam1, lam2 = self.lambdas return ( # FIXME: Verify scaling -1 / (2 * np.pi * (lam1**2 - lam2**2)) / hhk_scaling * (op_map( sym.S(hhk, density, k=1j * lam1, qbx_forced_limit=qbx_forced_limit)) - op_map( sym.S(hhk, density, k=1j * lam2, qbx_forced_limit=qbx_forced_limit)))) else: return ( # FIXME: Verify scaling -1 / (2 * np.pi) / hhk_scaling * op_map( sym.S(hhk, density, k=1j * self.lambdas[i - 1], qbx_forced_limit=qbx_forced_limit)))
def inner_fmm_level_to_nterms(tree, level): if helmholtz_k == 0: return fmm_level_to_order(LaplaceKernel(tree.dimensions), frozenset(), tree, level) else: return fmm_level_to_order(HelmholtzKernel(tree.dimensions), frozenset([("k", helmholtz_k)]), tree, level)
def __init__(self, omega, mus, epss): from sumpy.kernel import HelmholtzKernel self.kernel = HelmholtzKernel(3) self.omega = omega self.mus = mus self.epss = epss self.ks = [ sym.cse(omega*(eps*mu)**0.5, "k%d" % i) for i, (eps, mu) in enumerate(zip(epss, mus))]
def __init__(self, ambient_dim: int, *, dim: Optional[int] = None, precond: str = "left", helmholtz_k_name: str = "k") -> None: from sumpy.kernel import HelmholtzKernel super().__init__( HelmholtzKernel(ambient_dim, helmholtz_k_name=helmholtz_k_name), dim=dim, precond=precond, kernel_arguments={helmholtz_k_name: sym.var(helmholtz_k_name)})
def __init__(self, domain_n_exprs, ne, interfaces, use_l2_weighting=None): """ :attr interfaces: a tuple of tuples ``(outer_domain, inner_domain, interface_id)``, where *outer_domain* and *inner_domain* are indices into *domain_k_names*, and *interface_id* is a symbolic name for the discretization of the interface. 'outer' designates the side of the interface to which the normal points. :attr domain_n_exprs: a tuple of variable names of the Helmholtz parameter *k*, to be used inside each part of the source geometry. May also be a tuple of strings, which will be transformed into variable references of the corresponding names. :attr beta: A symbolic expression for the wave number in the :math:`z` direction. May be a string, which will be interpreted as a variable name. """ self.interfaces = interfaces ne = sym.var(ne) self.ne = sym.cse(ne, "ne") self.domain_n_exprs = [ sym.var(n_expr) for idom, n_expr in enumerate(domain_n_exprs)] del domain_n_exprs import pymbolic.primitives as p def upper_half_square_root(x): return p.If( p.Comparison( (x**0.5).a.imag, "<", 0), 1j*(-x)**0.5, x**0.5) self.domain_K_exprs = [ sym.cse( upper_half_square_root(n_expr**2-ne**2), "K%d" % i) for i, n_expr in enumerate(self.domain_n_exprs)] from sumpy.kernel import HelmholtzKernel self.kernel = HelmholtzKernel(2, allow_evanescent=True)
def test_cost_model_metadata_gathering(ctx_factory): """Test that the cost model correctly gathers metadata.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder fmm_level_to_order = SimpleExpansionOrderFinder(tol=1e-5) lpot_source = get_lpot_source(actx, 2).copy( fmm_level_to_order=fmm_level_to_order) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) sigma = get_density(actx, density_discr) sigma_sym = sym.var("sigma") k_sym = HelmholtzKernel(2, "k") k = 2 sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1, k=sym.var("k")) op_S = bind(places, sym_op_S) _, metadata = op_S.cost_per_stage( "constant_one", sigma=sigma, k=k, return_metadata=True ) metadata = one(metadata.values()) geo_data = lpot_source.qbx_fmm_geometry_data( places, places.auto_source, target_discrs_and_qbx_sides=((density_discr, 1),)) tree = geo_data.tree() assert metadata["p_qbx"] == QBX_ORDER assert metadata["nlevels"] == tree.nlevels assert metadata["nsources"] == tree.nsources assert metadata["ntargets"] == tree.ntargets assert metadata["ncenters"] == geo_data.ncenters for level in range(tree.nlevels): assert ( metadata["p_fmm_lev%d" % level] == fmm_level_to_order(k_sym, {"k": 2}, tree, level))
def get_lpot_cost(which, helmholtz_k, geometry_getter, lpot_kwargs, kind): """ Parameters: which: "D" or "S" kind: "actual" or "model" """ context = cl.create_some_context(interactive=False) queue = cl.CommandQueue(context) lpot_source = geometry_getter(queue, lpot_kwargs) from sumpy.kernel import LaplaceKernel, HelmholtzKernel sigma_sym = sym.var("sigma") if helmholtz_k == 0: k_sym = LaplaceKernel(lpot_source.ambient_dim) kernel_kwargs = {} else: k_sym = HelmholtzKernel(lpot_source.ambient_dim, "k") kernel_kwargs = {"k": helmholtz_k} if which == "S": op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1, **kernel_kwargs) elif which == "D": op = sym.D(k_sym, sigma_sym, qbx_forced_limit="avg", **kernel_kwargs) else: raise ValueError("unknown lpot symbol: '%s'" % which) bound_op = bind(lpot_source, op) density_discr = lpot_source.density_discr nodes = density_discr.nodes().with_queue(queue) sigma = cl.clmath.sin(10 * nodes[0]) if kind == "actual": timing_data = {} result = bound_op.eval(queue, {"sigma": sigma}, timing_data=timing_data) assert not np.isnan(result.get(queue)).any() result = one(timing_data.values()) elif kind == "model": perf_results = bound_op.get_modeled_performance(queue, sigma=sigma) result = one(perf_results.values()) return result
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 get_true_sol(fspace, kappa, cl_ctx, queue): """ Get the ufl expression for the true solution (3D) or a function with the evaluated solution (2D) """ mesh_dim = fspace.mesh().geometric_dimension() if mesh_dim == 3: spatial_coord = SpatialCoordinate(fspace.mesh()) x, y, z = spatial_coord # pylint: disable=C0103 norm = sqrt(x**2 + y**2 + z**2) return Constant(1j / (4 * pi)) / norm * exp(1j * kappa * norm) if mesh_dim == 2: # Evaluate true-sol using sumpy from sumpy.p2p import P2P from sumpy.kernel import HelmholtzKernel # https://github.com/inducer/sumpy/blob/900745184d2618bc27a64c847f247e01c2b90b02/examples/curve-pot.py#L87-L88 p2p = P2P(cl_ctx, [HelmholtzKernel(dim=2)], exclude_self=False, value_dtypes=np.complex128) # source is just (0, 0) sources = np.array([[0.0], [0.0]]) strengths = np.array([[1.0], [1.0]]) # targets are everywhere targets = np.array([ Function(fspace).interpolate(x_i).dat.data for x_i in SpatialCoordinate(fspace.mesh()) ]) evt, (true_sol_arr, ) = p2p(queue, targets, sources, strengths, k=kappa) true_sol = Function(fspace) true_sol.dat.data[:] = true_sol_arr[:] return true_sol raise ValueError("Only meshes of dimension 2, 3 supported")
def test_cost_model_metadata_gathering(ctx_getter): """Test that the cost model correctly gathers metadata.""" cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder fmm_level_to_order = SimpleExpansionOrderFinder(tol=1e-5) lpot_source = get_lpot_source( queue, 2).copy(fmm_level_to_order=fmm_level_to_order) sigma = get_density(queue, lpot_source) sigma_sym = sym.var("sigma") k_sym = HelmholtzKernel(2, "k") k = 2 sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1, k=sym.var("k")) op_S = bind(lpot_source, sym_op_S) cost_S = one(op_S.get_modeled_cost(queue, sigma=sigma, k=k).values()) geo_data = lpot_source.qbx_fmm_geometry_data( target_discrs_and_qbx_sides=((lpot_source.density_discr, 1), )) tree = geo_data.tree() assert cost_S.params["p_qbx"] == QBX_ORDER assert cost_S.params["nlevels"] == tree.nlevels assert cost_S.params["nsources"] == tree.nsources assert cost_S.params["ntargets"] == tree.ntargets assert cost_S.params["ncenters"] == geo_data.ncenters for level in range(tree.nlevels): assert (cost_S.params["p_fmm_lev%d" % level] == fmm_level_to_order( k_sym, {"k": 2}, tree, level))
def find_flops(): ctx = cl.create_some_context() if 0: knl = LaplaceKernel(2) m_expn_cls = LaplaceConformingVolumeTaylorMultipoleExpansion l_expn_cls = LaplaceConformingVolumeTaylorLocalExpansion flop_type = np.float64 else: knl = HelmholtzKernel(2) m_expn_cls = HelmholtzConformingVolumeTaylorMultipoleExpansion l_expn_cls = HelmholtzConformingVolumeTaylorLocalExpansion flop_type = np.complex128 orders = list(range(1, 11, 1)) flop_counts = [] for order in orders: print(order) m_expn = m_expn_cls(knl, order) l_expn = l_expn_cls(knl, order) m2l = E2EFromCSR(ctx, m_expn, l_expn) loopy_knl = m2l.get_kernel() loopy_knl = lp.add_and_infer_dtypes( loopy_knl, { "target_boxes,src_box_lists,src_box_starts": np.int32, "centers,src_expansions": np.float64, }) flops = lp.get_op_map(loopy_knl).filter_by(dtype=[flop_type]).sum() flop_counts.append( flops.eval_with_dict(dict(isrc_start=0, isrc_stop=1, ntgt_boxes=1))) print(orders) print(flop_counts)
("bdry_normals", bdry_normals), ]) else: raise ValueError("invalid mesh dim") # }}} # {{{ set up operator from pytential.symbolic.pde.scalar import (DirichletOperator, NeumannOperator) from sumpy.kernel import LaplaceKernel, HelmholtzKernel if case.k: knl = HelmholtzKernel(mesh.ambient_dim) knl_kwargs = {"k": sym.var("k")} concrete_knl_kwargs = {"k": case.k} else: knl = LaplaceKernel(mesh.ambient_dim) knl_kwargs = {} concrete_knl_kwargs = {} if knl.is_complex_valued: dtype = np.complex128 else: dtype = np.float64 loc_sign = +1 if case.prob_side in [+1, "scat"] else -1 if case.bc_type == "dirichlet":
def test_target_specific_qbx(actx_factory, op, helmholtz_k, qbx_order): logging.basicConfig(level=logging.INFO) actx = actx_factory() target_order = 4 fmm_tol = 1e-3 from meshmode.mesh.generation import generate_sphere mesh = generate_sphere(1, target_order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order=qbx_order, fmm_level_to_order=SimpleExpansionOrderFinder(fmm_tol), fmm_backend="fmmlib", _expansions_in_tree_have_extent=True, _expansion_stick_out_factor=0.9, _use_target_specific_qbx=False, ) kernel_length_scale = 5 / abs(helmholtz_k) if helmholtz_k else None places = { "qbx": qbx, "qbx_target_specific": qbx.copy(_use_target_specific_qbx=True) } from pytential.qbx.refinement import refine_geometry_collection places = GeometryCollection(places, auto_where="qbx") places = refine_geometry_collection( places, kernel_length_scale=kernel_length_scale) density_discr = places.get_discretization("qbx") nodes = thaw(density_discr.nodes(), actx) u_dev = actx.np.sin(nodes[0]) if helmholtz_k == 0: kernel = LaplaceKernel(3) kernel_kwargs = {} else: kernel = HelmholtzKernel(3, allow_evanescent=True) kernel_kwargs = {"k": sym.var("k")} u_sym = sym.var("u") if op == "S": op = sym.S elif op == "D": op = sym.D elif op == "Sp": op = sym.Sp else: raise ValueError("unknown operator: '%s'" % op) expr = op(kernel, u_sym, qbx_forced_limit=-1, **kernel_kwargs) bound_op = bind(places, expr) pot_ref = actx.to_numpy( flatten(bound_op(actx, u=u_dev, k=helmholtz_k), actx)) bound_op = bind(places, expr, auto_where="qbx_target_specific") pot_tsqbx = actx.to_numpy( flatten(bound_op(actx, u=u_dev, k=helmholtz_k), actx)) assert np.allclose(pot_tsqbx, pot_ref, atol=1e-13, rtol=1e-13)
def run_dielectric_test(cl_ctx, queue, nelements, qbx_order, op_class, mode, k0=3, k1=2.9, mesh_order=10, bdry_quad_order=None, bdry_ovsmp_quad_order=None, use_l2_weighting=False, fmm_order=None, visualize=False): if fmm_order is None: fmm_order = qbx_order * 2 if bdry_quad_order is None: bdry_quad_order = mesh_order if bdry_ovsmp_quad_order is None: bdry_ovsmp_quad_order = 4 * bdry_quad_order from meshmode.mesh.generation import ellipse, make_curve_mesh from functools import partial mesh = make_curve_mesh(partial(ellipse, 3), np.linspace(0, 1, nelements + 1), mesh_order) density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) logger.info("%d elements" % mesh.nelements) # from meshmode.discretization.visualization import make_visualizer # bdry_vis = make_visualizer(queue, density_discr, 20) # {{{ solve bvp from sumpy.kernel import HelmholtzKernel, AxisTargetDerivative kernel = HelmholtzKernel(2) beta = 2.5 K0 = np.sqrt(k0**2 - beta**2) # noqa K1 = np.sqrt(k1**2 - beta**2) # noqa pde_op = op_class(mode, k_vacuum=1, interfaces=((0, 1, sym.DEFAULT_SOURCE), ), domain_k_exprs=(k0, k1), beta=beta, use_l2_weighting=use_l2_weighting) op_unknown_sym = pde_op.make_unknown("unknown") representation0_sym = pde_op.representation(op_unknown_sym, 0) representation1_sym = pde_op.representation(op_unknown_sym, 1) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=fmm_order).with_refinement() #print(sym.pretty(pde_op.operator(op_unknown_sym))) #1/0 bound_pde_op = bind(qbx, pde_op.operator(op_unknown_sym)) e_factor = float(pde_op.ez_enabled) h_factor = float(pde_op.hz_enabled) e_sources_0 = make_obj_array(list(np.array([[0.1, 0.2]]).T.copy())) e_strengths_0 = np.array([1 * e_factor]) e_sources_1 = make_obj_array(list(np.array([[4, 4]]).T.copy())) e_strengths_1 = np.array([1 * e_factor]) h_sources_0 = make_obj_array(list(np.array([[0.2, 0.1]]).T.copy())) h_strengths_0 = np.array([1 * h_factor]) h_sources_1 = make_obj_array(list(np.array([[4, 5]]).T.copy())) h_strengths_1 = np.array([1 * h_factor]) kernel_grad = [ AxisTargetDerivative(i, kernel) for i in range(density_discr.ambient_dim) ] from sumpy.p2p import P2P pot_p2p = P2P(cl_ctx, [kernel], exclude_self=False) pot_p2p_grad = P2P(cl_ctx, kernel_grad, exclude_self=False) normal = bind(density_discr, sym.normal())(queue).as_vector(np.object) tangent = bind(density_discr, sym.pseudoscalar() / sym.area_element())(queue).as_vector( np.object) _, (E0, ) = pot_p2p(queue, density_discr.nodes(), e_sources_0, [e_strengths_0], out_host=False, k=K0) _, (E1, ) = pot_p2p(queue, density_discr.nodes(), e_sources_1, [e_strengths_1], out_host=False, k=K1) _, (grad0_E0, grad1_E0) = pot_p2p_grad(queue, density_discr.nodes(), e_sources_0, [e_strengths_0], out_host=False, k=K0) _, (grad0_E1, grad1_E1) = pot_p2p_grad(queue, density_discr.nodes(), e_sources_1, [e_strengths_1], out_host=False, k=K1) _, (H0, ) = pot_p2p(queue, density_discr.nodes(), h_sources_0, [h_strengths_0], out_host=False, k=K0) _, (H1, ) = pot_p2p(queue, density_discr.nodes(), h_sources_1, [h_strengths_1], out_host=False, k=K1) _, (grad0_H0, grad1_H0) = pot_p2p_grad(queue, density_discr.nodes(), h_sources_0, [h_strengths_0], out_host=False, k=K0) _, (grad0_H1, grad1_H1) = pot_p2p_grad(queue, density_discr.nodes(), h_sources_1, [h_strengths_1], out_host=False, k=K1) E0_dntarget = (grad0_E0 * normal[0] + grad1_E0 * normal[1]) # noqa E1_dntarget = (grad0_E1 * normal[0] + grad1_E1 * normal[1]) # noqa H0_dntarget = (grad0_H0 * normal[0] + grad1_H0 * normal[1]) # noqa H1_dntarget = (grad0_H1 * normal[0] + grad1_H1 * normal[1]) # noqa E0_dttarget = (grad0_E0 * tangent[0] + grad1_E0 * tangent[1]) # noqa E1_dttarget = (grad0_E1 * tangent[0] + grad1_E1 * tangent[1]) # noqa H0_dttarget = (grad0_H0 * tangent[0] + grad1_H0 * tangent[1]) # noqa H1_dttarget = (grad0_H1 * tangent[0] + grad1_H1 * tangent[1]) # noqa sqrt_w = bind(density_discr, sym.sqrt_jac_q_weight())(queue) bvp_rhs = np.zeros(len(pde_op.bcs), dtype=np.object) for i_bc, terms in enumerate(pde_op.bcs): for term in terms: assert term.i_interface == 0 if term.field_kind == pde_op.field_kind_e: if term.direction == pde_op.dir_none: bvp_rhs[i_bc] += (term.coeff_outer * E0 + term.coeff_inner * E1) elif term.direction == pde_op.dir_normal: bvp_rhs[i_bc] += (term.coeff_outer * E0_dntarget + term.coeff_inner * E1_dntarget) elif term.direction == pde_op.dir_tangential: bvp_rhs[i_bc] += (term.coeff_outer * E0_dttarget + term.coeff_inner * E1_dttarget) else: raise NotImplementedError("direction spec in RHS") elif term.field_kind == pde_op.field_kind_h: if term.direction == pde_op.dir_none: bvp_rhs[i_bc] += (term.coeff_outer * H0 + term.coeff_inner * H1) elif term.direction == pde_op.dir_normal: bvp_rhs[i_bc] += (term.coeff_outer * H0_dntarget + term.coeff_inner * H1_dntarget) elif term.direction == pde_op.dir_tangential: bvp_rhs[i_bc] += (term.coeff_outer * H0_dttarget + term.coeff_inner * H1_dttarget) else: raise NotImplementedError("direction spec in RHS") if use_l2_weighting: bvp_rhs[i_bc] *= sqrt_w scipy_op = bound_pde_op.scipy_op(queue, "unknown", domains=[sym.DEFAULT_TARGET] * len(pde_op.bcs), K0=K0, K1=K1, dtype=np.complex128) if mode == "tem" or op_class is SRep: from sumpy.tools import vector_from_device, vector_to_device from pytential.solve import lu unknown = lu(scipy_op, vector_from_device(queue, bvp_rhs)) unknown = vector_to_device(queue, unknown) else: from pytential.solve import gmres gmres_result = gmres(scipy_op, bvp_rhs, tol=1e-14, progress=True, hard_failure=True, stall_iterations=0) unknown = gmres_result.solution # }}} targets_0 = make_obj_array( list(np.array([[3.2 + t, -4] for t in [0, 0.5, 1]]).T.copy())) targets_1 = make_obj_array( list(np.array([[t * -0.3, t * -0.2] for t in [0, 0.5, 1]]).T.copy())) from pytential.target import PointsTarget from sumpy.tools import vector_from_device F0_tgt = vector_from_device( queue, bind( # noqa (qbx, PointsTarget(targets_0)), representation0_sym)(queue, unknown=unknown, K0=K0, K1=K1)) F1_tgt = vector_from_device( queue, bind( # noqa (qbx, PointsTarget(targets_1)), representation1_sym)(queue, unknown=unknown, K0=K0, K1=K1)) _, (E0_tgt_true, ) = pot_p2p(queue, targets_0, e_sources_0, [e_strengths_0], out_host=True, k=K0) _, (E1_tgt_true, ) = pot_p2p(queue, targets_1, e_sources_1, [e_strengths_1], out_host=True, k=K1) _, (H0_tgt_true, ) = pot_p2p(queue, targets_0, h_sources_0, [h_strengths_0], out_host=True, k=K0) _, (H1_tgt_true, ) = pot_p2p(queue, targets_1, h_sources_1, [h_strengths_1], out_host=True, k=K1) err_F0_total = 0 # noqa err_F1_total = 0 # noqa i_field = 0 def vec_norm(ary): return la.norm(ary.reshape(-1)) def field_kind_to_string(field_kind): return {pde_op.field_kind_e: "E", pde_op.field_kind_h: "H"}[field_kind] for field_kind in pde_op.field_kinds: if not pde_op.is_field_present(field_kind): continue if field_kind == pde_op.field_kind_e: F0_tgt_true = E0_tgt_true # noqa F1_tgt_true = E1_tgt_true # noqa elif field_kind == pde_op.field_kind_h: F0_tgt_true = H0_tgt_true # noqa F1_tgt_true = H1_tgt_true # noqa else: assert False abs_err_F0 = vec_norm(F0_tgt[i_field] - F0_tgt_true) # noqa abs_err_F1 = vec_norm(F1_tgt[i_field] - F1_tgt_true) # noqa rel_err_F0 = abs_err_F0 / vec_norm(F0_tgt_true) # noqa rel_err_F1 = abs_err_F1 / vec_norm(F1_tgt_true) # noqa err_F0_total = max(rel_err_F0, err_F0_total) # noqa err_F1_total = max(rel_err_F1, err_F1_total) # noqa print("Abs Err %s0" % field_kind_to_string(field_kind), abs_err_F0) print("Abs Err %s1" % field_kind_to_string(field_kind), abs_err_F1) print("Rel Err %s0" % field_kind_to_string(field_kind), rel_err_F0) print("Rel Err %s1" % field_kind_to_string(field_kind), rel_err_F1) i_field += 1 if visualize: from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=300) from pytential.target import PointsTarget fld0 = bind((qbx, PointsTarget(fplot.points)), representation0_sym)(queue, unknown=unknown, K0=K0) fld1 = bind((qbx, PointsTarget(fplot.points)), representation1_sym)(queue, unknown=unknown, K1=K1) comp_fields = [] i_field = 0 for field_kind in pde_op.field_kinds: if not pde_op.is_field_present(field_kind): continue fld_str = field_kind_to_string(field_kind) comp_fields.extend([ ("%s_fld0" % fld_str, fld0[i_field].get()), ("%s_fld1" % fld_str, fld1[i_field].get()), ]) i_field += 0 low_order_qbx = QBXLayerPotentialSource( density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=2, fmm_order=3).with_refinement() from sumpy.kernel import LaplaceKernel from pytential.target import PointsTarget ones = (cl.array.empty(queue, (density_discr.nnodes, ), dtype=np.float64).fill(1)) ind_func = -bind( (low_order_qbx, PointsTarget(fplot.points)), sym.D(LaplaceKernel(2), sym.var("u")))(queue, u=ones).get() _, (e_fld0_true, ) = pot_p2p(queue, fplot.points, e_sources_0, [e_strengths_0], out_host=True, k=K0) _, (e_fld1_true, ) = pot_p2p(queue, fplot.points, e_sources_1, [e_strengths_1], out_host=True, k=K1) _, (h_fld0_true, ) = pot_p2p(queue, fplot.points, h_sources_0, [h_strengths_0], out_host=True, k=K0) _, (h_fld1_true, ) = pot_p2p(queue, fplot.points, h_sources_1, [h_strengths_1], out_host=True, k=K1) #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) fplot.write_vtk_file("potential-n%d.vts" % nelements, [ ("e_fld0_true", e_fld0_true), ("e_fld1_true", e_fld1_true), ("h_fld0_true", h_fld0_true), ("h_fld1_true", h_fld1_true), ("ind", ind_func), ] + comp_fields) return err_F0_total, err_F1_total
def main(mesh_name="ellipse", visualize=False): import logging logging.basicConfig(level=logging.INFO) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import ellipse, make_curve_mesh from functools import partial if mesh_name == "ellipse": mesh = make_curve_mesh(partial(ellipse, 1), np.linspace(0, 1, nelements + 1), mesh_order) elif mesh_name == "ellipse_array": base_mesh = make_curve_mesh(partial(ellipse, 1), np.linspace(0, 1, nelements + 1), mesh_order) from meshmode.mesh.processing import affine_map, merge_disjoint_meshes nx = 2 ny = 2 dx = 2 / nx meshes = [ affine_map(base_mesh, A=np.diag([dx * 0.25, dx * 0.25]), b=np.array([dx * (ix - nx / 2), dx * (iy - ny / 2)])) for ix in range(nx) for iy in range(ny) ] mesh = merge_disjoint_meshes(meshes, single_group=True) if visualize: from meshmode.mesh.visualization import draw_curve draw_curve(mesh) import matplotlib.pyplot as plt plt.show() else: raise ValueError(f"unknown mesh name: {mesh_name}") pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) from pytential.qbx import (QBXLayerPotentialSource, QBXTargetAssociationFailedException) qbx = QBXLayerPotentialSource(pre_density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=fmm_order) from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=500) targets = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection( { "qbx": qbx, "qbx_high_target_assoc_tol": qbx.copy(target_association_tolerance=0.05), "targets": PointsTarget(targets) }, auto_where="qbx") density_discr = places.get_discretization("qbx") # {{{ describe bvp from sumpy.kernel import LaplaceKernel, HelmholtzKernel kernel = HelmholtzKernel(2) sigma_sym = sym.var("sigma") sqrt_w = sym.sqrt_jac_q_weight(2) inv_sqrt_w_sigma = sym.cse(sigma_sym / sqrt_w) # Brakhage-Werner parameter alpha = 1j # -1 for interior Dirichlet # +1 for exterior Dirichlet loc_sign = +1 k_sym = sym.var("k") bdry_op_sym = ( -loc_sign * 0.5 * sigma_sym + sqrt_w * (alpha * sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit=+1) - sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit="avg"))) # }}} bound_op = bind(places, bdry_op_sym) # {{{ fix rhs and solve from meshmode.dof_array import thaw nodes = thaw(actx, density_discr.nodes()) k_vec = np.array([2, 1]) k_vec = k * k_vec / la.norm(k_vec, 2) def u_incoming_func(x): return actx.np.exp(1j * (x[0] * k_vec[0] + x[1] * k_vec[1])) bc = -u_incoming_func(nodes) bvp_rhs = bind(places, sqrt_w * sym.var("bc"))(actx, bc=bc) from pytential.solve import gmres gmres_result = gmres(bound_op.scipy_op(actx, sigma_sym.name, dtype=np.complex128, k=k), bvp_rhs, tol=1e-8, progress=True, stall_iterations=0, hard_failure=True) # }}} # {{{ postprocess/visualize repr_kwargs = dict(source="qbx_high_target_assoc_tol", target="targets", qbx_forced_limit=None) representation_sym = ( alpha * sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, **repr_kwargs) - sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, **repr_kwargs)) u_incoming = u_incoming_func(targets) ones_density = density_discr.zeros(actx) for elem in ones_density: elem.fill(1) indicator = actx.to_numpy( bind(places, sym.D(LaplaceKernel(2), sigma_sym, **repr_kwargs))(actx, sigma=ones_density)) try: fld_in_vol = actx.to_numpy( bind(places, representation_sym)(actx, sigma=gmres_result.solution, k=k)) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file("helmholtz-dirichlet-failed-targets.vts", [("failed", e.failed_target_flags.get(queue))]) raise #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) fplot.write_vtk_file("helmholtz-dirichlet-potential.vts", [ ("potential", fld_in_vol), ("indicator", indicator), ("u_incoming", actx.to_numpy(u_incoming)), ])
def __init__(self, k=sym.var("k")): from sumpy.kernel import HelmholtzKernel self.kernel = HelmholtzKernel(3) self.k = k
#flags="print_hl_cl", out_host=True, **extra_source_kwargs) expected_result += mpoles norm = la.norm(actual_result - expected_result) / la.norm(expected_result) assert norm < 1e-12 @pytest.mark.parametrize("order", [4]) @pytest.mark.parametrize(("base_knl", "expn_class"), [ (LaplaceKernel(2), VolumeTaylorLocalExpansion), (LaplaceKernel(2), VolumeTaylorMultipoleExpansion), (LaplaceKernel(2), LaplaceConformingVolumeTaylorLocalExpansion), (LaplaceKernel(2), LaplaceConformingVolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), VolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), VolumeTaylorLocalExpansion), (HelmholtzKernel(2), HelmholtzConformingVolumeTaylorLocalExpansion), (HelmholtzKernel(2), HelmholtzConformingVolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), H2DLocalExpansion), (HelmholtzKernel(2), H2DMultipoleExpansion), (DirectionalSourceDerivative(BiharmonicKernel(2), "dir_vec"), VolumeTaylorMultipoleExpansion), (DirectionalSourceDerivative(BiharmonicKernel(2), "dir_vec"), VolumeTaylorLocalExpansion), (HelmholtzKernel(2, allow_evanescent=True), VolumeTaylorMultipoleExpansion), (HelmholtzKernel(2, allow_evanescent=True), VolumeTaylorLocalExpansion), (HelmholtzKernel(2, allow_evanescent=True), HelmholtzConformingVolumeTaylorLocalExpansion), (HelmholtzKernel(2, allow_evanescent=True),
def test_pyfmmlib_fmm(ctx_getter, dims, use_dipoles, helmholtz_k): logging.basicConfig(level=logging.INFO) from pytest import importorskip importorskip("pyfmmlib") ctx = ctx_getter() queue = cl.CommandQueue(ctx) nsources = 3000 ntargets = 1000 dtype = np.float64 sources = p_normal(queue, nsources, dims, dtype, seed=15) targets = (p_normal(queue, ntargets, dims, dtype, seed=18) + np.array([2, 0, 0])[:dims]) sources_host = particle_array_to_host(sources) targets_host = particle_array_to_host(targets) from boxtree import TreeBuilder tb = TreeBuilder(ctx) tree, _ = tb(queue, sources, targets=targets, max_particles_in_box=30, debug=True) from boxtree.traversal import FMMTraversalBuilder tbuild = FMMTraversalBuilder(ctx) trav, _ = tbuild(queue, tree, debug=True) trav = trav.get(queue=queue) from pyopencl.clrandom import PhiloxGenerator rng = PhiloxGenerator(queue.context, seed=20) weights = rng.uniform(queue, nsources, dtype=np.float64).get() #weights = np.ones(nsources) if use_dipoles: np.random.seed(13) dipole_vec = np.random.randn(dims, nsources) else: dipole_vec = None if dims == 2 and helmholtz_k == 0: base_nterms = 20 else: base_nterms = 10 def fmm_level_to_nterms(tree, lev): result = base_nterms if lev < 3 and helmholtz_k: # exercise order-varies-by-level capability result += 5 if use_dipoles: result += 1 return result from boxtree.pyfmmlib_integration import FMMLibExpansionWrangler wrangler = FMMLibExpansionWrangler(trav.tree, helmholtz_k, fmm_level_to_nterms=fmm_level_to_nterms, dipole_vec=dipole_vec) from boxtree.fmm import drive_fmm timing_data = {} pot = drive_fmm(trav, wrangler, weights, timing_data=timing_data) print(timing_data) assert timing_data # {{{ ref fmmlib computation logger.info("computing direct (reference) result") import pyfmmlib fmmlib_routine = getattr( pyfmmlib, "%spot%s%ddall%s_vec" % (wrangler.eqn_letter, "fld" if dims == 3 else "grad", dims, "_dp" if use_dipoles else "")) kwargs = {} if dims == 3: kwargs["iffld"] = False else: kwargs["ifgrad"] = False kwargs["ifhess"] = False if use_dipoles: if helmholtz_k == 0 and dims == 2: kwargs["dipstr"] = -weights * (dipole_vec[0] + 1j * dipole_vec[1]) else: kwargs["dipstr"] = weights kwargs["dipvec"] = dipole_vec else: kwargs["charge"] = weights if helmholtz_k: kwargs["zk"] = helmholtz_k ref_pot = wrangler.finalize_potentials( fmmlib_routine(sources=sources_host.T, targets=targets_host.T, **kwargs)[0]) rel_err = la.norm(pot - ref_pot, np.inf) / la.norm(ref_pot, np.inf) logger.info("relative l2 error vs fmmlib direct: %g" % rel_err) assert rel_err < 1e-5, rel_err # }}} # {{{ check against sumpy try: import sumpy # noqa except ImportError: have_sumpy = False from warnings import warn warn("sumpy unavailable: cannot compute independent reference " "values for pyfmmlib") else: have_sumpy = True if have_sumpy: from sumpy.kernel import (LaplaceKernel, HelmholtzKernel, DirectionalSourceDerivative) from sumpy.p2p import P2P sumpy_extra_kwargs = {} if helmholtz_k: knl = HelmholtzKernel(dims) sumpy_extra_kwargs["k"] = helmholtz_k else: knl = LaplaceKernel(dims) if use_dipoles: knl = DirectionalSourceDerivative(knl) sumpy_extra_kwargs["src_derivative_dir"] = dipole_vec p2p = P2P(ctx, [knl], exclude_self=False) evt, (sumpy_ref_pot, ) = p2p(queue, targets, sources, [weights], out_host=True, **sumpy_extra_kwargs) sumpy_rel_err = (la.norm(pot - sumpy_ref_pot, np.inf) / la.norm(sumpy_ref_pot, np.inf)) logger.info("relative l2 error vs sumpy direct: %g" % sumpy_rel_err) assert sumpy_rel_err < 1e-5, sumpy_rel_err
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
def draw_pot_figure(aspect_ratio, nsrc=100, novsmp=None, helmholtz_k=0, what_operator="S", what_operator_lpot=None, order=4, ovsmp_center_exp=0.66, force_center_side=None): import logging logging.basicConfig(level=logging.INFO) if novsmp is None: novsmp = 4 * nsrc if what_operator_lpot is None: what_operator_lpot = what_operator ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) # {{{ make plot targets center = np.asarray([0, 0], dtype=np.float64) from sumpy.visualization import FieldPlotter fp = FieldPlotter(center, npoints=1000, extent=6) # }}} # {{{ make p2p kernel calculator from sumpy.p2p import P2P from sumpy.kernel import LaplaceKernel, HelmholtzKernel from sumpy.expansion.local import H2DLocalExpansion, LineTaylorLocalExpansion if helmholtz_k: if isinstance(helmholtz_k, complex): knl = HelmholtzKernel(2, allow_evanescent=True) expn_class = H2DLocalExpansion knl_kwargs = {"k": helmholtz_k} else: knl = HelmholtzKernel(2) expn_class = H2DLocalExpansion knl_kwargs = {"k": helmholtz_k} else: knl = LaplaceKernel(2) expn_class = LineTaylorLocalExpansion knl_kwargs = {} vol_knl = process_kernel(knl, what_operator) p2p = P2P(ctx, [vol_knl], exclude_self=False, value_dtypes=np.complex128) lpot_knl = process_kernel(knl, what_operator_lpot) from sumpy.qbx import LayerPotential lpot = LayerPotential(ctx, [expn_class(lpot_knl, order=order)], value_dtypes=np.complex128) # }}} # {{{ set up geometry # r,a,b match the corresponding letters from G. J. Rodin and O. Steinbach, # Boundary Element Preconditioners for problems defined on slender domains. # http://dx.doi.org/10.1137/S1064827500372067 a = 1 b = 1 / aspect_ratio def map_to_curve(t): t = t * (2 * np.pi) x = a * np.cos(t) y = b * np.sin(t) w = (np.zeros_like(t) + 1) / len(t) return x, y, w from curve import CurveGrid native_t = np.linspace(0, 1, nsrc, endpoint=False) native_x, native_y, native_weights = map_to_curve(native_t) native_curve = CurveGrid(native_x, native_y) ovsmp_t = np.linspace(0, 1, novsmp, endpoint=False) ovsmp_x, ovsmp_y, ovsmp_weights = map_to_curve(ovsmp_t) ovsmp_curve = CurveGrid(ovsmp_x, ovsmp_y) curve_len = np.sum(ovsmp_weights * ovsmp_curve.speed) hovsmp = curve_len / novsmp center_dist = 5 * hovsmp if force_center_side is not None: center_side = force_center_side * np.ones(len(native_curve)) else: center_side = -np.sign(native_curve.mean_curvature) centers = (native_curve.pos + center_side[:, np.newaxis] * center_dist * native_curve.normal) #native_curve.plot() #pt.show() volpot_kwargs = knl_kwargs.copy() lpot_kwargs = knl_kwargs.copy() if what_operator == "D": volpot_kwargs["src_derivative_dir"] = native_curve.normal if what_operator_lpot == "D": lpot_kwargs["src_derivative_dir"] = ovsmp_curve.normal if what_operator_lpot == "S'": lpot_kwargs["tgt_derivative_dir"] = native_curve.normal # }}} if 0: # {{{ build matrix from fourier import make_fourier_interp_matrix fim = make_fourier_interp_matrix(novsmp, nsrc) from sumpy.tools import build_matrix from scipy.sparse.linalg import LinearOperator def apply_lpot(x): xovsmp = np.dot(fim, x) evt, (y, ) = lpot(queue, native_curve.pos, ovsmp_curve.pos, centers, [xovsmp * ovsmp_curve.speed * ovsmp_weights], expansion_radii=np.ones(centers.shape[1]), **lpot_kwargs) return y op = LinearOperator((nsrc, nsrc), apply_lpot) mat = build_matrix(op, dtype=np.complex128) w, v = la.eig(mat) pt.plot(w.real, "o-") #import sys; sys.exit(0) return # }}} # {{{ compute potentials mode_nr = 0 density = np.cos(mode_nr * 2 * np.pi * native_t).astype(np.complex128) ovsmp_density = np.cos(mode_nr * 2 * np.pi * ovsmp_t).astype(np.complex128) evt, (vol_pot, ) = p2p(queue, fp.points, native_curve.pos, [native_curve.speed * native_weights * density], **volpot_kwargs) evt, (curve_pot, ) = lpot( queue, native_curve.pos, ovsmp_curve.pos, centers, [ovsmp_density * ovsmp_curve.speed * ovsmp_weights], expansion_radii=np.ones(centers.shape[1]), **lpot_kwargs) # }}} if 0: # {{{ plot on-surface potential in 2D pt.plot(curve_pot, label="pot") pt.plot(density, label="dens") pt.legend() pt.show() # }}} fp.write_vtk_file("potential.vts", [("potential", vol_pot.real)]) if 0: # {{{ 2D false-color plot pt.clf() plotval = np.log10(1e-20 + np.abs(vol_pot)) im = fp.show_scalar_in_matplotlib(plotval.real) from matplotlib.colors import Normalize im.set_norm(Normalize(vmin=-2, vmax=1)) src = native_curve.pos pt.plot(src[:, 0], src[:, 1], "o-k") # close the curve pt.plot(src[-1::-len(src) + 1, 0], src[-1::-len(src) + 1, 1], "o-k") #pt.gca().set_aspect("equal", "datalim") cb = pt.colorbar(shrink=0.9) cb.set_label(r"$\log_{10}(\mathdefault{Error})$") #from matplotlib.ticker import NullFormatter #pt.gca().xaxis.set_major_formatter(NullFormatter()) #pt.gca().yaxis.set_major_formatter(NullFormatter()) fp.set_matplotlib_limits() # }}} else: # {{{ 3D plots plotval_vol = vol_pot.real plotval_c = curve_pot.real scale = 1 if 0: # crop singularities--doesn't work very well neighbors = [ np.roll(plotval_vol, 3, 0), np.roll(plotval_vol, -3, 0), np.roll(plotval_vol, 6, 0), np.roll(plotval_vol, -6, 0), ] avg = np.average(np.abs(plotval_vol)) outlier_flag = sum(np.abs(plotval_vol - nb) for nb in neighbors) > avg plotval_vol[outlier_flag] = sum( nb[outlier_flag] for nb in neighbors) / len(neighbors) fp.show_scalar_in_mayavi(scale * plotval_vol, max_val=1) from mayavi import mlab mlab.colorbar() if 1: mlab.points3d(native_curve.pos[0], native_curve.pos[1], scale * plotval_c, scale_factor=0.02) mlab.show()
def __init__(self, mode, k_vacuum, domain_k_exprs, beta, interfaces, use_l2_weighting=None): """ :attr mode: one of 'te', 'tm', 'tem' :attr k_vacuum: A symbolic expression for the wave number in vacuum. May be a string, which will be interpreted as a variable name. :attr interfaces: a tuple of tuples ``(outer_domain, inner_domain, interface_id)``, where *outer_domain* and *inner_domain* are indices into *domain_k_names*, and *interface_id* is a symbolic name for the discretization of the interface. 'outer' designates the side of the interface to which the normal points. :attr domain_k_exprs: a tuple of variable names of the Helmholtz parameter *k*, to be used inside each part of the source geometry. May also be a tuple of strings, which will be transformed into variable references of the corresponding names. :attr beta: A symbolic expression for the wave number in the :math:`z` direction. May be a string, which will be interpreted as a variable name. """ if use_l2_weighting is None: use_l2_weighting = False super(Dielectric2DBoundaryOperatorBase, self).__init__(use_l2_weighting=use_l2_weighting) if mode == "te": self.ez_enabled = False self.hz_enabled = True elif mode == "tm": self.ez_enabled = True self.hz_enabled = False elif mode == "tem": self.ez_enabled = True self.hz_enabled = True else: raise ValueError("invalid mode '%s'" % mode) self.interfaces = interfaces fk_e = self.field_kind_e fk_h = self.field_kind_h dir_none = self.dir_none dir_normal = self.dir_normal dir_tangential = self.dir_tangential if isinstance(beta, str): beta = sym.var(beta) beta = sym.cse(beta, "beta") if isinstance(k_vacuum, str): k_vacuum = sym.var(k_vacuum) k_vacuum = sym.cse(k_vacuum, "k_vac") self.domain_k_exprs = [ sym.var(k_expr) if isinstance(k_expr, str) else sym.cse( k_expr, "k%d" % idom) for idom, k_expr in enumerate(domain_k_exprs) ] del domain_k_exprs # Note the case of k/K! # "K" is the 2D Helmholtz parameter. # "k" is the 3D Helmholtz parameter. self.domain_K_exprs = [ sym.cse((k_expr**2 - beta**2)**0.5, "K%d" % i) for i, k_expr in enumerate(self.domain_k_exprs) ] from sumpy.kernel import HelmholtzKernel self.kernel = HelmholtzKernel(2, allow_evanescent=True) # {{{ build bc list # list of tuples, where each tuple consists of BCTermDescriptor instances all_bcs = [] for i_interface, (outer_domain, inner_domain, _) in (enumerate(self.interfaces)): k_outer = self.domain_k_exprs[outer_domain] k_inner = self.domain_k_exprs[inner_domain] all_bcs += [ ( # [E] = 0 self.BCTermDescriptor(i_interface=i_interface, direction=dir_none, field_kind=fk_e, coeff_outer=1, coeff_inner=-1), ), ( # [H] = 0 self.BCTermDescriptor(i_interface=i_interface, direction=dir_none, field_kind=fk_h, coeff_outer=1, coeff_inner=-1), ), ( self.BCTermDescriptor( i_interface=i_interface, direction=dir_tangential, field_kind=fk_e, coeff_outer=beta / (k_outer**2 - beta**2), coeff_inner=-beta / (k_inner**2 - beta**2)), self.BCTermDescriptor( i_interface=i_interface, direction=dir_normal, field_kind=fk_h, coeff_outer=sym.cse(-k_vacuum / (k_outer**2 - beta**2)), coeff_inner=sym.cse(k_vacuum / (k_inner**2 - beta**2))), ), (self.BCTermDescriptor( i_interface=i_interface, direction=dir_tangential, field_kind=fk_h, coeff_outer=beta / (k_outer**2 - beta**2), coeff_inner=-beta / (k_inner**2 - beta**2)), self.BCTermDescriptor( i_interface=i_interface, direction=dir_normal, field_kind=fk_e, coeff_outer=sym.cse( (k_outer**2 / k_vacuum) / (k_outer**2 - beta**2)), coeff_inner=sym.cse(-(k_inner**2 / k_vacuum) / (k_inner**2 - beta**2)))), ] del k_outer del k_inner self.bcs = [] for bc in all_bcs: any_significant_e = any( term.field_kind == fk_e and term.direction in [dir_normal, dir_none] for term in bc) any_significant_h = any( term.field_kind == fk_h and term.direction in [dir_normal, dir_none] for term in bc) is_necessary = ((self.ez_enabled and any_significant_e) or (self.hz_enabled and any_significant_h)) # Only keep tangential modes for TEM. Otherwise, # no jump in H already implies jump condition on # tangential derivative. is_tem = self.ez_enabled and self.hz_enabled terms = tuple(term for term in bc if term.direction != dir_tangential or is_tem) if is_necessary: self.bcs.append(terms) assert (len(all_bcs) * (int(self.ez_enabled) + int(self.hz_enabled)) // 2 == len(self.bcs))
def __init__(self, k=None): if k is None: k = sym.var("k") from sumpy.kernel import HelmholtzKernel self.kernel = HelmholtzKernel(3) self.k = k
eoc_rec.add_data_point(h, err) print(eoc_rec) assert eoc_rec.order_estimate() > order - 2 - 0.1 class FakeTree: def __init__(self, dimensions, root_extent, stick_out_factor): self.dimensions = dimensions self.root_extent = root_extent self.stick_out_factor = stick_out_factor @pytest.mark.parametrize("knl", [ LaplaceKernel(2), HelmholtzKernel(2), LaplaceKernel(3), HelmholtzKernel(3) ]) def test_order_finder(knl): from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder ofind = SimpleExpansionOrderFinder(1e-5) tree = FakeTree(knl.dim, 200, 0.5) orders = [ ofind(knl, frozenset([("k", 5)]), tree, level) for level in range(30) ] print(orders) # Order should not increase with level
for resolution in (getattr(case, "resolutions", None) or case.geometry.resolutions): mesh = case.geometry.get_mesh(resolution, target_order) if mesh is None: break d = mesh.ambient_dim k = case.k 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")} from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource pre_density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) refiner_extra_kwargs = {} if case.k != 0: refiner_extra_kwargs["kernel_length_scale"] = 5 / case.k
def main(curve_fn=starfish, visualize=True): import logging logging.basicConfig(level=logging.WARNING) # INFO for more progress info import pyopencl as cl cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, force_device_scalars=True) from meshmode.mesh.generation import make_curve_mesh mesh = make_curve_mesh( curve_fn, np.linspace(0, 1, nelements+1), target_order) from pytential.qbx import QBXLayerPotentialSource from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, qbx_order, fmm_order=qbx_order+3, target_association_tolerance=0.005, #fmm_backend="fmmlib", ) from pytential.target import PointsTarget fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1000) targets_dev = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ "qbx": qbx, "targets": PointsTarget(targets_dev), }, auto_where="qbx") density_discr = places.get_discretization("qbx") nodes = thaw(density_discr.nodes(), actx) angle = actx.np.arctan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(2) kernel_kwargs = {"k": sym.var("k")} else: kernel = LaplaceKernel(2) kernel_kwargs = {} def op(**kwargs): kwargs.update(kernel_kwargs) #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), **kwargs)) return sym.D(kernel, sym.var("sigma"), **kwargs) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) if 0: from random import randrange sigma = actx.zeros(density_discr.ndofs, angle.entry_dtype) for _ in range(5): sigma[randrange(len(sigma))] = 1 from arraycontext import unflatten sigma = unflatten(angle, sigma, actx) else: sigma = actx.np.cos(mode_nr*angle) if isinstance(kernel, HelmholtzKernel): for i, elem in np.ndenumerate(sigma): sigma[i] = elem.astype(np.complex128) bound_bdry_op = bind(places, op()) if visualize: fld_in_vol = actx.to_numpy( bind(places, op( source="qbx", target="targets", qbx_forced_limit=None))(actx, sigma=sigma, k=k)) if enable_mayavi: fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) else: fplot.write_vtk_file("layerpot-potential.vts", [ ("potential", fld_in_vol) ]) if 0: apply_op = bound_bdry_op.scipy_op(actx, "sigma", np.float64, k=k) from sumpy.tools import build_matrix mat = build_matrix(apply_op) import matplotlib.pyplot as pt pt.imshow(mat) pt.colorbar() pt.show() if enable_mayavi: # {{{ plot boundary field from arraycontext import flatten fld_on_bdry = actx.to_numpy( flatten(bound_bdry_op(actx, sigma=sigma, k=k), actx)) nodes_host = actx.to_numpy( flatten(density_discr.nodes(), actx) ).reshape(density_discr.ambient_dim, -1) mlab.points3d(nodes_host[0], nodes_host[1], fld_on_bdry.real, scale_factor=0.03) mlab.colorbar() mlab.show()
def main(mesh_name="ellipsoid"): import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) if mesh_name == "ellipsoid": cad_file_name = "geometries/ellipsoid.step" h = 0.6 elif mesh_name == "two-cylinders": cad_file_name = "geometries/two-cylinders-smooth.step" h = 0.4 else: raise ValueError("unknown mesh name: %s" % mesh_name) from meshmode.mesh.io import generate_gmsh, FileSource mesh = generate_gmsh( FileSource(cad_file_name), 2, order=2, other_options=["-string", "Mesh.CharacteristicLengthMax = %g;" % h], target_unit="MM") from meshmode.mesh.processing import perform_flips # Flip elements--gmsh generates inside-out geometry. mesh = perform_flips(mesh, np.ones(mesh.nelements)) from meshmode.mesh.processing import find_bounding_box bbox_min, bbox_max = find_bounding_box(mesh) bbox_center = 0.5 * (bbox_min + bbox_max) bbox_size = max(bbox_max - bbox_min) / 2 logger.info("%d elements" % mesh.nelements) from pytential.qbx import QBXLayerPotentialSource from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource(density_discr, 4 * target_order, qbx_order, fmm_order=qbx_order + 3, target_association_tolerance=0.15) from pytential.target import PointsTarget fplot = FieldPlotter(bbox_center, extent=3.5 * bbox_size, npoints=150) from pytential import GeometryCollection places = GeometryCollection( { "qbx": qbx, "targets": PointsTarget(fplot.points) }, auto_where="qbx") density_discr = places.get_discretization("qbx") nodes = thaw(actx, density_discr.nodes()) angle = actx.np.arctan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(3) else: kernel = LaplaceKernel(3) #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None)) op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None) sigma = actx.np.cos(mode_nr * angle) if 0: from meshmode.dof_array import flatten, unflatten sigma = flatten(0 * angle) from random import randrange for i in range(5): sigma[randrange(len(sigma))] = 1 sigma = unflatten(actx, density_discr, sigma) if isinstance(kernel, HelmholtzKernel): for i, elem in np.ndenumerate(sigma): sigma[i] = elem.astype(np.complex128) fld_in_vol = actx.to_numpy( bind(places, op, auto_where=("qbx", "targets"))(actx, sigma=sigma, k=k)) #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) fplot.write_vtk_file("layerpot-3d-potential.vts", [("potential", fld_in_vol)]) bdry_normals = bind(places, sym.normal( density_discr.ambient_dim))(actx).as_vector(dtype=object) from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, density_discr, target_order) bdry_vis.write_vtk_file("layerpot-3d-density.vtu", [ ("sigma", sigma), ("bdry_normals", bdry_normals), ])
queue = cl.CommandQueue(cl_ctx) target_order = 5 qbx_order = 3 mode_nr = 4 if 1: cad_file_name = "geometries/ellipsoid.step" h = 0.6 else: cad_file_name = "geometries/two-cylinders-smooth.step" h = 0.4 k = 0 if k: kernel = HelmholtzKernel(3) else: kernel = LaplaceKernel(3) #kernel = OneKernel() def main(): import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.WARNING) # INFO for more progress info from meshmode.mesh.io import generate_gmsh, FileSource mesh = generate_gmsh( FileSource(cad_file_name), 2, order=2, other_options=["-string", "Mesh.CharacteristicLengthMax = %g;" % h])
except ImportError: pass else: faulthandler.enable() @pytest.mark.parametrize("knl, local_expn_class, mpole_expn_class", [ (LaplaceKernel(2), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), (LaplaceKernel(2), LaplaceConformingVolumeTaylorLocalExpansion, LaplaceConformingVolumeTaylorMultipoleExpansion), (LaplaceKernel(3), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), (LaplaceKernel(3), LaplaceConformingVolumeTaylorLocalExpansion, LaplaceConformingVolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), HelmholtzConformingVolumeTaylorLocalExpansion, HelmholtzConformingVolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), H2DLocalExpansion, H2DMultipoleExpansion), (HelmholtzKernel(3), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), (HelmholtzKernel(3), HelmholtzConformingVolumeTaylorLocalExpansion, HelmholtzConformingVolumeTaylorMultipoleExpansion), (YukawaKernel(2), Y2DLocalExpansion, Y2DMultipoleExpansion), ]) def test_sumpy_fmm(ctx_getter, knl, local_expn_class, mpole_expn_class): logging.basicConfig(level=logging.INFO) ctx = ctx_getter() queue = cl.CommandQueue(ctx)
def main(): logging.basicConfig(level=logging.INFO) nelements = 60 qbx_order = 3 k_fac = 4 k0 = 3 * k_fac k1 = 2.9 * k_fac mesh_order = 10 bdry_quad_order = mesh_order bdry_ovsmp_quad_order = bdry_quad_order * 4 fmm_order = qbx_order * 2 cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) from meshmode.mesh.generation import ellipse, make_curve_mesh from functools import partial mesh = make_curve_mesh(partial(ellipse, 3), np.linspace(0, 1, nelements + 1), mesh_order) density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) logger.info("%d elements" % mesh.nelements) # from meshmode.discretization.visualization import make_visualizer # bdry_vis = make_visualizer(queue, density_discr, 20) # {{{ solve bvp from sumpy.kernel import HelmholtzKernel kernel = HelmholtzKernel(2) beta = 2.5 * k_fac K0 = np.sqrt(k0**2 - beta**2) K1 = np.sqrt(k1**2 - beta**2) from pytential.symbolic.pde.scalar import DielectricSDRep2DBoundaryOperator pde_op = DielectricSDRep2DBoundaryOperator( mode='tm', k_vacuum=1, interfaces=((0, 1, sym.DEFAULT_SOURCE), ), domain_k_exprs=(k0, k1), beta=beta) op_unknown_sym = pde_op.make_unknown("unknown") representation0_sym = pde_op.representation(op_unknown_sym, 0) representation1_sym = pde_op.representation(op_unknown_sym, 1) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=fmm_order) bound_pde_op = bind(qbx, pde_op.operator(op_unknown_sym)) # in inner domain sources_1 = make_obj_array(list(np.array([[-1.5, 0.5]]).T.copy())) strengths_1 = np.array([1]) from sumpy.p2p import P2P pot_p2p = P2P(cl_ctx, [kernel], exclude_self=False) _, (Einc, ) = pot_p2p(queue, density_discr.nodes(), sources_1, [strengths_1], out_host=False, k=K0) sqrt_w = bind(density_discr, sym.sqrt_jac_q_weight())(queue) bvp_rhs = np.zeros(len(pde_op.bcs), dtype=object) for i_bc, terms in enumerate(pde_op.bcs): for term in terms: assert term.i_interface == 0 assert term.field_kind == pde_op.field_kind_e if term.direction == pde_op.dir_none: bvp_rhs[i_bc] += (term.coeff_outer * (-Einc)) elif term.direction == pde_op.dir_normal: # no jump in normal derivative bvp_rhs[i_bc] += 0 * Einc else: raise NotImplementedError("direction spec in RHS") bvp_rhs[i_bc] *= sqrt_w from pytential.solve import gmres gmres_result = gmres(bound_pde_op.scipy_op(queue, "unknown", dtype=np.complex128, domains=[sym.DEFAULT_TARGET] * 2, K0=K0, K1=K1), bvp_rhs, tol=1e-6, progress=True, hard_failure=True, stall_iterations=0) # }}} unknown = gmres_result.solution # {{{ visualize from pytential.qbx import QBXLayerPotentialSource lap_qbx = QBXLayerPotentialSource(density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=qbx_order) from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=300) from pytential.target import PointsTarget fld0 = bind((qbx, PointsTarget(fplot.points)), representation0_sym)(queue, unknown=unknown, K0=K0).get() fld1 = bind((qbx, PointsTarget(fplot.points)), representation1_sym)(queue, unknown=unknown, K1=K1).get() ones = cl.array.empty(queue, density_discr.nnodes, np.float64) dom1_indicator = -bind( (lap_qbx, PointsTarget(fplot.points)), sym.D(0, sym.var("sigma")))( queue, sigma=ones.fill(1)).get() _, (fld_inc_vol, ) = pot_p2p(queue, fplot.points, sources_1, [strengths_1], out_host=True, k=K0) #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) fplot.write_vtk_file("potential.vts", [ ("fld0", fld0), ("fld1", fld1), ("fld_inc_vol", fld_inc_vol), ("fld_total", ((fld_inc_vol + fld0) * (1 - dom1_indicator) + fld1 * dom1_indicator)), ("dom1_indicator", dom1_indicator), ])
def test_target_specific_qbx(ctx_getter, op, helmholtz_k, qbx_order): logging.basicConfig(level=logging.INFO) cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) target_order = 4 fmm_tol = 1e-3 from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(1, target_order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource pre_density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder refiner_extra_kwargs = {} if helmholtz_k != 0: refiner_extra_kwargs["kernel_length_scale"] = 5 / abs(helmholtz_k) qbx, _ = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order=qbx_order, fmm_level_to_order=SimpleExpansionOrderFinder(fmm_tol), fmm_backend="fmmlib", _expansions_in_tree_have_extent=True, _expansion_stick_out_factor=0.9, _use_target_specific_qbx=False, ).with_refinement(**refiner_extra_kwargs) density_discr = qbx.density_discr nodes = density_discr.nodes().with_queue(queue) u_dev = clmath.sin(nodes[0]) if helmholtz_k == 0: kernel = LaplaceKernel(3) kernel_kwargs = {} else: kernel = HelmholtzKernel(3, allow_evanescent=True) kernel_kwargs = {"k": sym.var("k")} u_sym = sym.var("u") if op == "S": op = sym.S elif op == "D": op = sym.D elif op == "Sp": op = sym.Sp else: raise ValueError("unknown operator: '%s'" % op) expr = op(kernel, u_sym, qbx_forced_limit=-1, **kernel_kwargs) bound_op = bind(qbx, expr) pot_ref = bound_op(queue, u=u_dev, k=helmholtz_k).get() qbx = qbx.copy(_use_target_specific_qbx=True) bound_op = bind(qbx, expr) pot_tsqbx = bound_op(queue, u=u_dev, k=helmholtz_k).get() assert np.allclose(pot_tsqbx, pot_ref, atol=1e-13, rtol=1e-13)