def get_operator(self, ambient_dim, qbx_forced_limit="avg"): knl = self.knl_class(ambient_dim) kwargs = self.knl_sym_kwargs.copy() kwargs["qbx_forced_limit"] = qbx_forced_limit if self.op_type == "scalar": sym_u = sym.var("u") sym_op = sym.S(knl, sym_u, **kwargs) elif self.op_type == "scalar_mixed": sym_u = sym.var("u") sym_op = sym.S(knl, 0.3 * sym_u, **kwargs) \ + sym.D(knl, 0.5 * sym_u, **kwargs) elif self.op_type == "vector": sym_u = sym.make_sym_vector("u", ambient_dim) sym_op = make_obj_array([ sym.Sp(knl, sym_u[0], **kwargs) + sym.D(knl, sym_u[1], **kwargs), sym.S(knl, 0.4 * sym_u[0], **kwargs) + 0.3 * sym.D(knl, sym_u[0], **kwargs) ]) else: raise ValueError(f"unknown operator type: '{self.op_type}'") sym_op = 0.5 * sym_u + sym_op return sym_u, sym_op
def 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 representation(self, u, map_potentials=None, qbx_forced_limit=None, **kwargs): """ :param u: symbolic variable for the density. :param map_potentials: a callable that can be used to apply additional transformations on all the layer potentials in the representation, e.g. to take a target derivative. """ sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = sym.cse(u/sqrt_w) laplace_s_inv_sqrt_w_u = sym.cse(sym.S(self.laplace_kernel, inv_sqrt_w_u)) if map_potentials is None: def map_potentials(x): # pylint:disable=function-redefined return x kwargs["qbx_forced_limit"] = qbx_forced_limit kwargs["kernel_arguments"] = self.kernel_arguments return ( map_potentials( sym.S(self.kernel, inv_sqrt_w_u, **kwargs) ) - self.alpha * map_potentials( sym.D(self.kernel, laplace_s_inv_sqrt_w_u, **kwargs) ) )
def test_cost_model(ctx_factory, dim, use_target_specific_qbx): """Test that cost model gathering can execute successfully.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) lpot_source = get_lpot_source(actx, dim).copy( _use_target_specific_qbx=use_target_specific_qbx, cost_model=CostModel()) 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 = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) op_S = bind(places, sym_op_S) cost_S = op_S.get_modeled_cost(actx, sigma=sigma) assert len(cost_S) == 1 sym_op_S_plus_D = ( sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) + sym.D(k_sym, sigma_sym, qbx_forced_limit="avg")) op_S_plus_D = bind(places, sym_op_S_plus_D) cost_S_plus_D = op_S_plus_D.get_modeled_cost(actx, sigma=sigma) assert len(cost_S_plus_D) == 2
def apply_pressure(self, density_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit): """ Symbolic expression for pressure field associated with the stresslet""" import itertools from pytential.symbolic.mappers import DerivativeTaker kernel = LaplaceKernel(dim=self.dim) factor = (2. * mu_sym) for i, j in itertools.product(range(self.dim), range(self.dim)): if i + j < 1: sym_expr = factor * DerivativeTaker(i).map_int_g( DerivativeTaker(j).map_int_g( sym.S(kernel, density_vec_sym[i] * dir_vec_sym[j], qbx_forced_limit=qbx_forced_limit))) else: sym_expr = sym_expr + ( factor * DerivativeTaker(i).map_int_g( DerivativeTaker(j).map_int_g( sym.S(kernel, density_vec_sym[i] * dir_vec_sym[j], qbx_forced_limit=qbx_forced_limit)))) return sym_expr
def representation(self, sigma, map_potentials=None, qbx_forced_limit=None): if map_potentials is None: def map_potentials(x): # pylint:disable=function-redefined return x def dv(knl): return DirectionalSourceDerivative(knl, "normal_dir") def dt(knl): return DirectionalSourceDerivative(knl, "tangent_dir") normal_dir = sym.normal(2).as_vector() tangent_dir = np.array([-normal_dir[1], normal_dir[0]]) k1 = map_potentials(sym.S(dv(dv(dv(self.knl))), sigma[0], kernel_arguments={"normal_dir": normal_dir}, qbx_forced_limit=qbx_forced_limit)) + \ 3*map_potentials(sym.S(dv(dt(dt(self.knl))), sigma[0], kernel_arguments={"normal_dir": normal_dir, "tangent_dir": tangent_dir}, qbx_forced_limit=qbx_forced_limit)) k2 = -map_potentials(sym.S(dv(dv(self.knl)), sigma[1], kernel_arguments={"normal_dir": normal_dir}, qbx_forced_limit=qbx_forced_limit)) + \ map_potentials(sym.S(dt(dt(self.knl)), sigma[1], kernel_arguments={"tangent_dir": tangent_dir}, qbx_forced_limit=qbx_forced_limit)) return k1 + k2
def representation(self, sigma, map_potentials=None, qbx_forced_limit=None, **kwargs): """ :param sigma: symbolic variable for the density. :param map_potentials: a callable that can be used to apply additional transformations on all the layer potentials in the representation, e.g. to take a target derivative. :param kwargs: additional keyword arguments passed on to the layer potential constructor. """ if map_potentials is None: def map_potentials(x): # pylint:disable=function-redefined return x def dv(knl): return DirectionalSourceDerivative(knl, "normal_dir") def dt(knl): return DirectionalSourceDerivative(knl, "tangent_dir") normal_dir = sym.normal(self.dim).as_vector() tangent_dir = np.array([-normal_dir[1], normal_dir[0]]) kwargs["qbx_forced_limit"] = qbx_forced_limit k1 = ( map_potentials( sym.S(dv(dv(dv(self.kernel))), sigma[0], kernel_arguments={"normal_dir": normal_dir}, **kwargs) ) + 3 * map_potentials( sym.S(dv(dt(dt(self.kernel))), sigma[0], kernel_arguments={ "normal_dir": normal_dir, "tangent_dir": tangent_dir }, **kwargs) ) ) k2 = ( -map_potentials( sym.S(dv(dv(self.kernel)), sigma[1], kernel_arguments={"normal_dir": normal_dir}, **kwargs) ) + map_potentials( sym.S(dt(dt(self.kernel)), sigma[1], kernel_arguments={"tangent_dir": tangent_dir}, **kwargs) ) ) return k1 + k2
def operator(self, u): from sumpy.kernel import HelmholtzKernel, LaplaceKernel sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) knl = self.kernel lknl = self.laplace_kernel knl_kwargs = {} knl_kwargs["kernel_arguments"] = self.kernel_arguments DpS0u = sym.Dp( knl, # noqa cse(sym.S(lknl, inv_sqrt_w_u)), **knl_kwargs) if self.use_improved_operator: Dp0S0u = -0.25 * u + sym.Sp( # noqa lknl, # noqa sym.Sp(lknl, inv_sqrt_w_u, qbx_forced_limit="avg"), qbx_forced_limit="avg") if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( # noqa sym.Dp( knl - lknl, # noqa cse(sym.S(lknl, inv_sqrt_w_u, qbx_forced_limit=+1)), qbx_forced_limit=+1, **knl_kwargs) + Dp0S0u) elif isinstance(knl, LaplaceKernel): DpS0u = Dp0S0u # noqa else: raise ValueError( f"no improved operator for '{self.kernel}' known") if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. amb_dim = self.kernel.dim ones_contribution = (sym.Ones() * sym.mean(amb_dim, amb_dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 return ( -self.loc_sign * 0.5 * u + sqrt_w * (sym.Sp(knl, inv_sqrt_w_u, qbx_forced_limit="avg", **knl_kwargs) - self.alpha * DpS0u + ones_contribution))
def operator(self, u): sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) if self.is_unique_only_up_to_constant(): # The exterior Dirichlet operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. # # See Hackbusch, https://books.google.com/books?id=Ssnf7SZB0ZMC # Theorem 8.2.18b amb_dim = self.kernel.dim ones_contribution = (sym.Ones() * sym.mean(amb_dim, amb_dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 return ( -self.loc_sign * 0.5 * u + sqrt_w * (self.alpha * sym.S(self.kernel, inv_sqrt_w_u, qbx_forced_limit=+1, kernel_arguments=self.kernel_arguments) - sym.D(self.kernel, inv_sqrt_w_u, qbx_forced_limit="avg", kernel_arguments=self.kernel_arguments) + ones_contribution) )
def scattered_volume_field(self, Jt, rho, qbx_forced_limit=None): """ This will return an object array of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell's equations as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`. """ Jxyz = sym.cse(sym.tangential_to_xyz(Jt), "Jxyz") A = sym.S(self.kernel, Jxyz, k=self.k, qbx_forced_limit=qbx_forced_limit) phi = sym.S(self.kernel, rho, k=self.k, qbx_forced_limit=qbx_forced_limit) E_scat = 1j*self.k*A - sym.grad(3, phi) H_scat = sym.curl(A) return sym.flat_obj_array(E_scat, H_scat)
def test_timing_data_gathering(ctx_factory): """Test that timing data gathering can execute succesfully.""" pytest.importorskip("pyfmmlib") cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) actx = PyOpenCLArrayContext(queue) lpot_source = get_lpot_source(actx, 2) places = GeometryCollection(lpot_source) dofdesc = places.auto_source.to_stage1() density_discr = places.get_discretization(dofdesc.geometry) sigma = get_density(actx, density_discr) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) op_S = bind(places, sym_op_S) timing_data = {} op_S.eval(dict(sigma=sigma), timing_data=timing_data, array_context=actx) assert timing_data print(timing_data)
def representation(self, unknown, i_domain): """ :return: a symbolic expression for the representation of the PDE solution in domain number *i_domain*. """ unk = self._structured_unknown(unknown, with_l2_weights=False) result = [] for field_kind in self.field_kinds: if not self.is_field_present(field_kind): continue field_result = 0 for i_interface, (i_domain_outer, i_domain_inner, interface_id) in (enumerate(self.interfaces)): if i_domain_outer == i_domain: side = self.side_out elif i_domain_inner == i_domain: side = self.side_in else: continue my_unk = unk[side, field_kind, i_interface] if my_unk: field_result += sym.S(self.kernel, my_unk, source=interface_id, k=self.domain_K_exprs[i_domain]) result.append(field_result) from pytools.obj_array import make_obj_array return make_obj_array(result)
def get_bound_op(places): from sumpy.kernel import LaplaceKernel op = sym.S(LaplaceKernel(places.ambient_dim), sym.var("sigma"), qbx_forced_limit=+1) return bind(places, op)
def nxcurlS(qbx_forced_limit): return sym.n_cross( sym.curl( sym.S(knl, sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"), qbx_forced_limit=qbx_forced_limit)))
def get_lpot_cost(queue, geometry_getter, kind): lpot_source = geometry_getter(queue) from pytential import sym, bind sigma_sym = sym.var("sigma") from sumpy.kernel import LaplaceKernel k_sym = LaplaceKernel(lpot_source.ambient_dim) op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) 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]) from pytools import one 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 get_bound_op(lpot_source): from sumpy.kernel import LaplaceKernel sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) return bind(lpot_source, op)
def get_slp_wall_times(lpot_source, fmm_order, qbx_order): queue = cl.CommandQueue(lpot_source.cl_context) lpot_source = lpot_source.copy( qbx_order=qbx_order, fmm_level_to_order=(False if fmm_order is False else lambda *args: fmm_order)) d = lpot_source.ambient_dim u_sym = sym.var("u") from sumpy.kernel import LaplaceKernel S = sym.S(LaplaceKernel(d), u_sym, qbx_forced_limit=-1) density_discr = lpot_source.density_discr u = cl.array.empty(queue, density_discr.nnodes, np.float64) u.fill(1) op = bind(lpot_source, S) # Warmup op(queue, u=u) times = [] from time import perf_counter as curr_time for _ in range(WALL_TIME_EXPERIMENT_TIMING_ROUNDS): t_start = curr_time() op(queue, u=u) t_end = curr_time() times.append(t_end - t_start) return times
def get_zero_op(self, kernel, **knl_kwargs): u_sym = sym.var("u") dn_u_sym = sym.var("dn_u") return (sym.S(kernel, dn_u_sym, qbx_forced_limit=-1, **knl_kwargs) - sym.D(kernel, u_sym, qbx_forced_limit="avg", **knl_kwargs) - 0.5 * u_sym)
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 rho_rhs(self, Jt, Einc_xyz): Jxyz = cse(tangential_to_xyz(Jt), "Jxyz") return (sym.n_dot(Einc_xyz) + 1j*self.k*sym.n_dot(sym.S( self.kernel, Jxyz, k=self.k, # continuous--qbx_forced_limit doesn't really matter qbx_forced_limit="avg")))
def test_cost_model_order_varying_by_level(ctx_factory): """For FMM order varying by level, this checks to ensure that the costs are different. The varying-level case should have larger cost. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ constant level to order def level_to_order_constant(kernel, kernel_args, tree, level): return 1 lpot_source = get_lpot_source(actx, 2).copy( cost_model=QBXCostModel(), fmm_level_to_order=level_to_order_constant) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(2) sym_op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) sigma = get_density(actx, density_discr) cost_constant, metadata = bind(places, sym_op).cost_per_stage( "constant_one", sigma=sigma) cost_constant = one(cost_constant.values()) metadata = one(metadata.values()) # }}} # {{{ varying level to order def level_to_order_varying(kernel, kernel_args, tree, level): return metadata["nlevels"] - level lpot_source = get_lpot_source(actx, 2).copy( cost_model=QBXCostModel(), fmm_level_to_order=level_to_order_varying) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) sigma = get_density(actx, density_discr) cost_varying, _ = bind(lpot_source, sym_op).cost_per_stage( "constant_one", sigma=sigma) cost_varying = one(cost_varying.values()) # }}} assert sum(cost_varying.values()) > sum(cost_constant.values())
def apply_pressure(self, density_vec_sym, mu_sym, qbx_forced_limit): """ Symbolic expression for pressure field associated with the Stokeslet""" from pytential.symbolic.mappers import DerivativeTaker kernel = LaplaceKernel(dim=self.dim) for i in range(self.dim): if i < 1: sym_expr = DerivativeTaker(i).map_int_g( sym.S(kernel, density_vec_sym[i], qbx_forced_limit=qbx_forced_limit)) else: sym_expr = sym_expr + (DerivativeTaker(i).map_int_g( sym.S(kernel, density_vec_sym[i], qbx_forced_limit=qbx_forced_limit))) return sym_expr
def representation(self, u, map_potentials=None, qbx_forced_limit=None, **kwargs): sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) if map_potentials is None: def map_potentials(x): # pylint:disable=function-redefined return x kwargs["qbx_forced_limit"] = qbx_forced_limit kwargs["kernel_arguments"] = self.kernel_arguments return (map_potentials(sym.S(self.kernel, inv_sqrt_w_u, **kwargs)) - self.alpha * map_potentials( sym.D(self.kernel, sym.S(self.laplace_kernel, inv_sqrt_w_u), **kwargs)))
def get_zero_op(self, kernel, **knl_kwargs): d = kernel.dim u_sym = sym.var("u") grad_u_sym = sym.make_sym_mv("grad_u", d) dn_u_sym = sym.var("dn_u") return (d1.resolve( d1.dnabla(d) * d1(sym.S(kernel, dn_u_sym, qbx_forced_limit="avg", **knl_kwargs)) ) - d2.resolve( d2.dnabla(d) * d2(sym.D(kernel, u_sym, qbx_forced_limit="avg", **knl_kwargs))) - 0.5 * grad_u_sym)
def test_cost_model(actx_factory, dim, use_target_specific_qbx, per_box): """Test that cost model gathering can execute successfully.""" actx = actx_factory() lpot_source = get_lpot_source(actx, dim).copy( _use_target_specific_qbx=use_target_specific_qbx, cost_model=QBXCostModel()) 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 = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) op_S = bind(places, sym_op_S) if per_box: cost_S, _ = op_S.cost_per_box("constant_one", sigma=sigma) else: cost_S, _ = op_S.cost_per_stage("constant_one", sigma=sigma) assert len(cost_S) == 1 sym_op_S_plus_D = (sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) + sym.D(k_sym, sigma_sym, qbx_forced_limit="avg")) op_S_plus_D = bind(places, sym_op_S_plus_D) if per_box: cost_S_plus_D, _ = op_S_plus_D.cost_per_box("constant_one", sigma=sigma) else: cost_S_plus_D, _ = op_S_plus_D.cost_per_stage("constant_one", sigma=sigma) assert len(cost_S_plus_D) == 2
def test_cost_model_order_varying_by_level(ctx_factory): """For FMM order varying by level, this checks to ensure that the costs are different. The varying-level case should have larger cost. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ constant level to order def level_to_order_constant(kernel, kernel_args, tree, level): return 1 lpot_source = get_lpot_source(actx, 2).copy( cost_model=CostModel( calibration_params=CONSTANT_ONE_PARAMS), fmm_level_to_order=level_to_order_constant) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(2) sym_op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) sigma = get_density(actx, density_discr) cost_constant = one( bind(places, sym_op) .get_modeled_cost(actx, sigma=sigma).values()) # }}} # {{{ varying level to order varying_order_params = cost_constant.params.copy() nlevels = cost_constant.params["nlevels"] for level in range(nlevels): varying_order_params["p_fmm_lev%d" % level] = nlevels - level cost_varying = cost_constant.with_params(varying_order_params) # }}} assert ( sum(cost_varying.get_predicted_times().values()) > sum(cost_constant.get_predicted_times().values()))
def test_cost_model(ctx_getter, dim, use_target_specific_qbx): """Test that cost model gathering can execute successfully.""" cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) lpot_source = (get_lpot_source(queue, dim).copy( _use_target_specific_qbx=use_target_specific_qbx, cost_model=CostModel())) sigma = get_density(queue, lpot_source) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) op_S = bind(lpot_source, sym_op_S) cost_S = op_S.get_modeled_cost(queue, sigma=sigma) assert len(cost_S) == 1 sym_op_S_plus_D = (sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) + sym.D(k_sym, sigma_sym)) op_S_plus_D = bind(lpot_source, sym_op_S_plus_D) cost_S_plus_D = op_S_plus_D.get_modeled_cost(queue, sigma=sigma) assert len(cost_S_plus_D) == 2
def get_qbx_center_neighborhood_sizes_direct(lpot_source, radius): queue = cl.CommandQueue(lpot_source.cl_context) def inspect_geo_data(insn, bound_expr, geo_data): nonlocal sizes, nsources, ncenters tree = geo_data.tree().with_queue(queue) centers = np.array([axis.get(queue) for axis in geo_data.centers()]) search_radii = radius * geo_data.expansion_radii().get(queue) sources = np.array([axis.get(queue) for axis in tree.sources]) ncenters = len(search_radii) nsources = tree.nsources assert nsources == lpot_source.quad_stage2_density_discr.nnodes center_to_source_dists = ( la.norm( (centers[:, np.newaxis, :] - sources[:, :, np.newaxis]).T, ord=np.inf, axis=-1)) sizes = np.count_nonzero( center_to_source_dists <= search_radii[:, np.newaxis], axis=1) return False # no need to do the actual FMM sizes = None nsources = None ncenters = None lpot_source = lpot_source.copy(geometry_data_inspector=inspect_geo_data) density_discr = lpot_source.density_discr nodes = density_discr.nodes().with_queue(queue) sigma = cl.clmath.sin(10 * nodes[0]) # The kernel doesn't really matter here from sumpy.kernel import LaplaceKernel sigma_sym = sym.var('sigma') k_sym = LaplaceKernel(lpot_source.ambient_dim) sym_op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) bound_op = bind(lpot_source, sym_op) bound_op(queue, sigma=sigma) return (sizes, nsources, ncenters)
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 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))