def p2p(kernels): if any(knl.is_complex_valued for knl in kernels): value_dtype = self.complex_dtype else: value_dtype = self.real_dtype from sumpy.p2p import P2P return P2P(actx.context, kernels, exclude_self=False, value_dtypes=value_dtype)
def get_p2p(self, kernels): # needs to be separate method for caching from pytools import any if any(knl.is_complex_valued for knl in kernels): value_dtype = self.density_discr.complex_dtype else: value_dtype = self.density_discr.real_dtype from sumpy.p2p import P2P p2p = P2P(self.cl_context, kernels, exclude_self=False, value_dtypes=value_dtype) return p2p
def test_p2p(ctx_factory, exclude_self): ctx = ctx_factory() queue = cl.CommandQueue(ctx) dimensions = 3 n = 5000 from sumpy.p2p import P2P lknl = LaplaceKernel(dimensions) knl = P2P(ctx, [lknl, AxisTargetDerivative(0, lknl)], exclude_self=exclude_self) targets = np.random.rand(dimensions, n) sources = targets if exclude_self else np.random.rand(dimensions, n) strengths = np.ones(n, dtype=np.float64) extra_kwargs = {} if exclude_self: extra_kwargs["target_to_source"] = np.arange(n, dtype=np.int32) evt, (potential, x_derivative) = knl(queue, targets, sources, [strengths], out_host=True, **extra_kwargs) potential_ref = np.empty_like(potential) targets = targets.T sources = sources.T for itarg in range(n): with np.errstate(divide="ignore"): invdists = np.sum((targets[itarg] - sources)**2, axis=-1)**-0.5 if exclude_self: assert np.isinf(invdists[itarg]) invdists[itarg] = 0 potential_ref[itarg] = np.sum(strengths * invdists) potential_ref *= 1 / (4 * np.pi) rel_err = la.norm(potential - potential_ref) / la.norm(potential_ref) print(rel_err) assert rel_err < 1e-3
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 get_p2p(self): from sumpy.p2p import P2P return P2P(self.cl_context, [self.kernel], exclude_self=False)
def test_translations(ctx_factory, knl, local_expn_class, mpole_expn_class): logging.basicConfig(level=logging.INFO) from sympy.core.cache import clear_cache clear_cache() ctx = ctx_factory() queue = cl.CommandQueue(ctx) np.random.seed(17) res = 20 nsources = 15 out_kernels = [knl] extra_kwargs = {} if isinstance(knl, HelmholtzKernel): extra_kwargs["k"] = 0.05 if isinstance(knl, StokesletKernel): extra_kwargs["mu"] = 0.05 # Just to make sure things also work away from the origin origin = np.array([2, 1, 0][:knl.dim], np.float64) sources = (0.7 * (-0.5 + np.random.rand(knl.dim, nsources).astype(np.float64)) + origin[:, np.newaxis]) strengths = np.ones(nsources, dtype=np.float64) * (1 / nsources) pconv_verifier_p2m2p = PConvergenceVerifier() pconv_verifier_p2m2m2p = PConvergenceVerifier() pconv_verifier_p2m2m2l2p = PConvergenceVerifier() pconv_verifier_full = PConvergenceVerifier() from sumpy.visualization import FieldPlotter eval_offset = np.array([5.5, 0.0, 0][:knl.dim]) centers = ( np.array( [ # box 0: particles, first mpole here [0, 0, 0][:knl.dim], # box 1: second mpole here np.array([-0.2, 0.1, 0][:knl.dim], np.float64), # box 2: first local here eval_offset + np.array([0.3, -0.2, 0][:knl.dim], np.float64), # box 3: second local and eval here eval_offset ], dtype=np.float64) + origin).T.copy() del eval_offset from sumpy.expansion import VolumeTaylorExpansionBase if isinstance(knl, HelmholtzKernel) and \ issubclass(local_expn_class, VolumeTaylorExpansionBase): # FIXME: Embarrassing--but we run out of memory for higher orders. orders = [2, 3] else: orders = [2, 3, 4] nboxes = centers.shape[-1] def eval_at(e2p, source_box_nr, rscale): e2p_target_boxes = np.array([source_box_nr], dtype=np.int32) # These are indexed by global box numbers. e2p_box_target_starts = np.array([0, 0, 0, 0], dtype=np.int32) e2p_box_target_counts_nonchild = np.array([0, 0, 0, 0], dtype=np.int32) e2p_box_target_counts_nonchild[source_box_nr] = ntargets evt, (pot, ) = e2p( queue, src_expansions=mpoles, src_base_ibox=0, target_boxes=e2p_target_boxes, box_target_starts=e2p_box_target_starts, box_target_counts_nonchild=e2p_box_target_counts_nonchild, centers=centers, targets=targets, rscale=rscale, out_host=True, **extra_kwargs) return pot for order in orders: m_expn = mpole_expn_class(knl, order=order) l_expn = local_expn_class(knl, order=order) from sumpy import P2EFromSingleBox, E2PFromSingleBox, P2P, E2EFromCSR p2m = P2EFromSingleBox(ctx, m_expn) m2m = E2EFromCSR(ctx, m_expn, m_expn) m2p = E2PFromSingleBox(ctx, m_expn, out_kernels) m2l = E2EFromCSR(ctx, m_expn, l_expn) l2l = E2EFromCSR(ctx, l_expn, l_expn) l2p = E2PFromSingleBox(ctx, l_expn, out_kernels) p2p = P2P(ctx, out_kernels, exclude_self=False) fp = FieldPlotter(centers[:, -1], extent=0.3, npoints=res) targets = fp.points # {{{ compute (direct) reference solution evt, (pot_direct, ) = p2p(queue, targets, sources, (strengths, ), out_host=True, **extra_kwargs) # }}} m1_rscale = 0.5 m2_rscale = 0.25 l1_rscale = 0.5 l2_rscale = 0.25 # {{{ apply P2M p2m_source_boxes = np.array([0], dtype=np.int32) # These are indexed by global box numbers. p2m_box_source_starts = np.array([0, 0, 0, 0], dtype=np.int32) p2m_box_source_counts_nonchild = np.array([nsources, 0, 0, 0], dtype=np.int32) evt, (mpoles, ) = p2m( queue, source_boxes=p2m_source_boxes, box_source_starts=p2m_box_source_starts, box_source_counts_nonchild=p2m_box_source_counts_nonchild, centers=centers, sources=sources, strengths=(strengths, ), nboxes=nboxes, rscale=m1_rscale, tgt_base_ibox=0, #flags="print_hl_wrapper", out_host=True, **extra_kwargs) # }}} ntargets = targets.shape[-1] pot = eval_at(m2p, 0, m1_rscale) err = la.norm((pot - pot_direct) / res**2) err = err / (la.norm(pot_direct) / res**2) pconv_verifier_p2m2p.add_data_point(order, err) # {{{ apply M2M m2m_target_boxes = np.array([1], dtype=np.int32) m2m_src_box_starts = np.array([0, 1], dtype=np.int32) m2m_src_box_lists = np.array([0], dtype=np.int32) evt, (mpoles, ) = m2m( queue, src_expansions=mpoles, src_base_ibox=0, tgt_base_ibox=0, ntgt_level_boxes=mpoles.shape[0], target_boxes=m2m_target_boxes, src_box_starts=m2m_src_box_starts, src_box_lists=m2m_src_box_lists, centers=centers, src_rscale=m1_rscale, tgt_rscale=m2_rscale, #flags="print_hl_cl", out_host=True, **extra_kwargs) # }}} pot = eval_at(m2p, 1, m2_rscale) err = la.norm((pot - pot_direct) / res**2) err = err / (la.norm(pot_direct) / res**2) pconv_verifier_p2m2m2p.add_data_point(order, err) # {{{ apply M2L m2l_target_boxes = np.array([2], dtype=np.int32) m2l_src_box_starts = np.array([0, 1], dtype=np.int32) m2l_src_box_lists = np.array([1], dtype=np.int32) evt, (mpoles, ) = m2l( queue, src_expansions=mpoles, src_base_ibox=0, tgt_base_ibox=0, ntgt_level_boxes=mpoles.shape[0], target_boxes=m2l_target_boxes, src_box_starts=m2l_src_box_starts, src_box_lists=m2l_src_box_lists, centers=centers, src_rscale=m2_rscale, tgt_rscale=l1_rscale, #flags="print_hl_cl", out_host=True, **extra_kwargs) # }}} pot = eval_at(l2p, 2, l1_rscale) err = la.norm((pot - pot_direct) / res**2) err = err / (la.norm(pot_direct) / res**2) pconv_verifier_p2m2m2l2p.add_data_point(order, err) # {{{ apply L2L l2l_target_boxes = np.array([3], dtype=np.int32) l2l_src_box_starts = np.array([0, 1], dtype=np.int32) l2l_src_box_lists = np.array([2], dtype=np.int32) evt, (mpoles, ) = l2l( queue, src_expansions=mpoles, src_base_ibox=0, tgt_base_ibox=0, ntgt_level_boxes=mpoles.shape[0], target_boxes=l2l_target_boxes, src_box_starts=l2l_src_box_starts, src_box_lists=l2l_src_box_lists, centers=centers, src_rscale=l1_rscale, tgt_rscale=l2_rscale, #flags="print_hl_wrapper", out_host=True, **extra_kwargs) # }}} pot = eval_at(l2p, 3, l2_rscale) err = la.norm((pot - pot_direct) / res**2) err = err / (la.norm(pot_direct) / res**2) pconv_verifier_full.add_data_point(order, err) for name, verifier in [ ("p2m2p", pconv_verifier_p2m2p), ("p2m2m2p", pconv_verifier_p2m2m2p), ("p2m2m2l2p", pconv_verifier_p2m2m2l2p), ("full", pconv_verifier_full), ]: print(30 * "-") print(name) print(30 * "-") print(verifier) print(30 * "-") verifier()
def test_p2e2p(ctx_factory, base_knl, expn_class, order, with_source_derivative): #logging.basicConfig(level=logging.INFO) from sympy.core.cache import clear_cache clear_cache() ctx = ctx_factory() queue = cl.CommandQueue(ctx) np.random.seed(17) res = 100 nsources = 100 extra_kwargs = {} if isinstance(base_knl, HelmholtzKernel): if base_knl.allow_evanescent: extra_kwargs["k"] = 0.2 * (0.707 + 0.707j) else: extra_kwargs["k"] = 0.2 if isinstance(base_knl, StokesletKernel): extra_kwargs["mu"] = 0.2 if with_source_derivative: knl = DirectionalSourceDerivative(base_knl, "dir_vec") else: knl = base_knl out_kernels = [ knl, AxisTargetDerivative(0, knl), ] expn = expn_class(knl, order=order) from sumpy import P2EFromSingleBox, E2PFromSingleBox, P2P p2e = P2EFromSingleBox(ctx, expn, kernels=[knl]) e2p = E2PFromSingleBox(ctx, expn, kernels=out_kernels) p2p = P2P(ctx, out_kernels, exclude_self=False) from pytools.convergence import EOCRecorder eoc_rec_pot = EOCRecorder() eoc_rec_grad_x = EOCRecorder() from sumpy.expansion.local import LocalExpansionBase if issubclass(expn_class, LocalExpansionBase): h_values = [1 / 5, 1 / 7, 1 / 20] else: h_values = [1 / 2, 1 / 3, 1 / 5] center = np.array([2, 1, 0][:knl.dim], np.float64) sources = (0.7 * (-0.5 + np.random.rand(knl.dim, nsources).astype(np.float64)) + center[:, np.newaxis]) strengths = np.ones(nsources, dtype=np.float64) * (1 / nsources) source_boxes = np.array([0], dtype=np.int32) box_source_starts = np.array([0], dtype=np.int32) box_source_counts_nonchild = np.array([nsources], dtype=np.int32) extra_source_kwargs = extra_kwargs.copy() if isinstance(knl, DirectionalSourceDerivative): alpha = np.linspace(0, 2 * np.pi, nsources, np.float64) dir_vec = np.vstack([np.cos(alpha), np.sin(alpha)]) extra_source_kwargs["dir_vec"] = dir_vec from sumpy.visualization import FieldPlotter for h in h_values: if issubclass(expn_class, LocalExpansionBase): loc_center = np.array([5.5, 0.0, 0.0][:knl.dim]) + center centers = np.array(loc_center, dtype=np.float64).reshape(knl.dim, 1) fp = FieldPlotter(loc_center, extent=h, npoints=res) else: eval_center = np.array([1 / h, 0.0, 0.0][:knl.dim]) + center fp = FieldPlotter(eval_center, extent=0.1, npoints=res) centers = (np.array([0.0, 0.0, 0.0][:knl.dim], dtype=np.float64).reshape(knl.dim, 1) + center[:, np.newaxis]) targets = fp.points rscale = 0.5 # pick something non-1 # {{{ apply p2e evt, (mpoles, ) = p2e( queue, source_boxes=source_boxes, box_source_starts=box_source_starts, box_source_counts_nonchild=box_source_counts_nonchild, centers=centers, sources=sources, strengths=(strengths, ), nboxes=1, tgt_base_ibox=0, rscale=rscale, #flags="print_hl_cl", out_host=True, **extra_source_kwargs) # }}} # {{{ apply e2p ntargets = targets.shape[-1] box_target_starts = np.array([0], dtype=np.int32) box_target_counts_nonchild = np.array([ntargets], dtype=np.int32) evt, ( pot, grad_x, ) = e2p( queue, src_expansions=mpoles, src_base_ibox=0, target_boxes=source_boxes, box_target_starts=box_target_starts, box_target_counts_nonchild=box_target_counts_nonchild, centers=centers, targets=targets, rscale=rscale, #flags="print_hl_cl", out_host=True, **extra_kwargs) # }}} # {{{ compute (direct) reference solution evt, ( pot_direct, grad_x_direct, ) = p2p(queue, targets, sources, (strengths, ), out_host=True, **extra_source_kwargs) err_pot = la.norm((pot - pot_direct) / res**2) err_grad_x = la.norm((grad_x - grad_x_direct) / res**2) if 1: err_pot = err_pot / la.norm((pot_direct) / res**2) err_grad_x = err_grad_x / la.norm((grad_x_direct) / res**2) if 0: import matplotlib.pyplot as pt from matplotlib.colors import Normalize pt.subplot(131) im = fp.show_scalar_in_matplotlib(pot.real) im.set_norm(Normalize(vmin=-0.1, vmax=0.1)) pt.subplot(132) im = fp.show_scalar_in_matplotlib(pot_direct.real) im.set_norm(Normalize(vmin=-0.1, vmax=0.1)) pt.colorbar() pt.subplot(133) im = fp.show_scalar_in_matplotlib( np.log10(1e-15 + np.abs(pot - pot_direct))) im.set_norm(Normalize(vmin=-6, vmax=1)) pt.colorbar() pt.show() # }}} eoc_rec_pot.add_data_point(h, err_pot) eoc_rec_grad_x.add_data_point(h, err_grad_x) print(expn_class, knl, order) print("POTENTIAL:") print(eoc_rec_pot) print("X TARGET DERIVATIVE:") print(eoc_rec_grad_x) tgt_order = order + 1 if issubclass(expn_class, LocalExpansionBase): tgt_order_grad = tgt_order - 1 slack = 0.7 grad_slack = 0.5 else: tgt_order_grad = tgt_order + 1 slack = 0.5 grad_slack = 1 if order <= 2: slack += 1 grad_slack += 1 if isinstance(knl, DirectionalSourceDerivative): slack += 1 grad_slack += 2 if isinstance(base_knl, DirectionalSourceDerivative): slack += 1 grad_slack += 2 if isinstance(base_knl, HelmholtzKernel): if base_knl.allow_evanescent: slack += 0.5 grad_slack += 0.5 if issubclass(expn_class, VolumeTaylorMultipoleExpansionBase): slack += 0.3 grad_slack += 0.3 assert eoc_rec_pot.order_estimate() > tgt_order - slack assert eoc_rec_grad_x.order_estimate() > tgt_order_grad - grad_slack
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 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_p2p_direct(ctx_factory, exclude_self, factor, lpot_id): logging.basicConfig(level=logging.INFO) ctx = ctx_factory() queue = cl.CommandQueue(ctx) ndim = 2 nblks = 10 mode_nr = 25 from sumpy.kernel import LaplaceKernel, DirectionalSourceDerivative if lpot_id == 1: lknl = LaplaceKernel(ndim) elif lpot_id == 2: lknl = LaplaceKernel(ndim) lknl = DirectionalSourceDerivative(lknl, dir_vec_name="dsource_vec") else: raise ValueError("unknow lpot_id") from sumpy.p2p import P2P lpot = P2P(ctx, [lknl], exclude_self=exclude_self) from sumpy.p2p import P2PMatrixGenerator mat_gen = P2PMatrixGenerator(ctx, [lknl], exclude_self=exclude_self) from sumpy.p2p import P2PMatrixBlockGenerator blk_gen = P2PMatrixBlockGenerator(ctx, [lknl], exclude_self=exclude_self) for n in [200, 300, 400]: targets, sources, _, _, sigma = \ _build_geometry(queue, n, mode_nr, target_radius=1.2) h = 2 * np.pi / n strengths = (sigma * h, ) tgtindices = _build_block_index(queue, n, nblks, factor) srcindices = _build_block_index(queue, n, nblks, factor) index_set = MatrixBlockIndexRanges(ctx, tgtindices, srcindices) extra_kwargs = {} if exclude_self: extra_kwargs["target_to_source"] = \ cl.array.arange(queue, 0, n, dtype=np.int) if lpot_id == 2: from pytools.obj_array import make_obj_array extra_kwargs["dsource_vec"] = \ vector_to_device(queue, make_obj_array(np.ones((ndim, n)))) _, (result_lpot, ) = lpot(queue, targets=targets, sources=sources, strength=strengths, **extra_kwargs) result_lpot = result_lpot.get() _, (mat, ) = mat_gen(queue, targets=targets, sources=sources, **extra_kwargs) mat = mat.get() result_mat = mat.dot(strengths[0].get()) _, (blk, ) = blk_gen(queue, targets=targets, sources=sources, index_set=index_set, **extra_kwargs) blk = blk.get() eps = 1.0e-10 * la.norm(result_lpot) assert la.norm(result_mat - result_lpot) < eps index_set = index_set.get(queue) for i in range(index_set.nblocks): assert la.norm( index_set.block_take(blk, i) - index_set.take(mat, i)) < eps
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 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