def get_layer_potential(self, actx, resolution, mesh_order): pre_density_discr = self.get_discretization(actx, resolution, mesh_order) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder fmm_kwargs = {} if self.fmm_backend is None: fmm_kwargs["fmm_order"] = False else: if self.fmm_tol is not None: fmm_kwargs["fmm_order"] = SimpleExpansionOrderFinder( self.fmm_tol) elif self.fmm_order is not None: fmm_kwargs["fmm_order"] = self.fmm_order else: fmm_kwargs["fmm_order"] = self.qbx_order + 5 from pytential.qbx import QBXLayerPotentialSource return QBXLayerPotentialSource( pre_density_discr, fine_order=self.source_ovsmp * self.target_order, qbx_order=self.qbx_order, fmm_backend=self.fmm_backend, **fmm_kwargs, _disable_refinement=not self.use_refinement, _box_extent_norm=getattr(self, "box_extent_norm", None), _from_sep_smaller_crit=getattr(self, "from_sep_smaller_crit", None), _from_sep_smaller_min_nsources_cumul=30, )
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)
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 assert (np.diff(orders) <= 0).all()
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 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 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 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)
def run_method(trial, method, cl_ctx=None, queue=None, clear_memoized_objects=False, true_sol_name="True Solution", comp_sol_name="Computed Solution", **kwargs): """ Returns (true solution, computed solution, snes_or_ksp) :arg clear_memoized_objects: Destroy memoized objects if true. :arg trial: A dict mapping each trial option to a valid value :arg method: A valid method (see the keys of *method_options*) :arg cl_ctx: the computing context :arg queue: the computing queue for the context kwargs should include the boundary id of the scatterer as 'scatterer_bdy_id' and the boundary id of the outer boundary as 'outer_bdy_id' kwargs should include the method options for :arg:`trial['method']`. for the given method. """ if clear_memoized_objects: global memoized_objects memoized_objects = {} if cl_ctx is None: raise ValueError("Missing cl_ctx") if queue is None: raise ValueError("Missing queue") # Get boundary ids scatterer_bdy_id = kwargs['scatterer_bdy_id'] outer_bdy_id = kwargs['outer_bdy_id'] # Get degree and wave number degree = trial['degree'] wave_number = trial['kappa'] # Get options prefix and solver parameters, if any options_prefix = kwargs.get('options_prefix', None) solver_parameters = dict(kwargs.get('solver_parameters', None)) # Get prepared trial args in kwargs prepared_trial = prepare_trial(trial, true_sol_name, cl_ctx, queue) mesh, fspace, vfspace, true_sol, true_sol_grad_expr = prepared_trial # Create a place to memoize any objects if necessary tuple_trial = trial_to_tuple(trial) memo_key = tuple_trial[:2] if memo_key not in memoized_objects: memoized_objects[memo_key] = {} comp_sol = None # Handle any special kwargs and get computed solution if method == 'pml': # Get required objects pml_max = kwargs['pml_max'] pml_min = kwargs['pml_min'] # Get optional argumetns pml_type = kwargs.get('pml_type', None) quad_const = kwargs.get('quad_const', None) speed = kwargs.get('speed', None) # Make tensor function space if 'tfspace' not in memoized_objects[memo_key]: memoized_objects[memo_key]['tfspace'] = \ TensorFunctionSpace(mesh, 'CG', degree) tfspace = memoized_objects[memo_key]['tfspace'] snes, comp_sol = pml( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=options_prefix, solver_parameters=solver_parameters, fspace=fspace, tfspace=tfspace, true_sol_grad_expr=true_sol_grad_expr, pml_type=pml_type, quad_const=quad_const, speed=speed, pml_min=pml_min, pml_max=pml_max, ) snes_or_ksp = snes elif method == 'nonlocal': # Build DG spaces if not already built if 'dgfspace' not in memoized_objects[memo_key]: memoized_objects[memo_key]['dgfspace'] = \ FunctionSpace(mesh, 'DG', degree) if 'dgvfspace' not in memoized_objects[memo_key]: memoized_objects[memo_key]['dgvfspace'] = \ VectorFunctionSpace(mesh, 'DG', degree) dgfspace = memoized_objects[memo_key]['dgfspace'] dgvfspace = memoized_objects[memo_key]['dgvfspace'] # Get opencl array context from meshmode.array_context import PyOpenCLArrayContext actx = PyOpenCLArrayContext(queue) # Build connection fd -> meshmode if not already built if 'meshmode_src_connection' not in memoized_objects[memo_key]: from meshmode.interop.firedrake import build_connection_from_firedrake memoized_objects[memo_key]['meshmode_src_connection'] = \ build_connection_from_firedrake( actx, dgfspace, grp_factory=None, restrict_to_boundary=scatterer_bdy_id) meshmode_src_connection = memoized_objects[memo_key][ 'meshmode_src_connection'] # Set defaults for qbx kwargs qbx_order = kwargs.get('qbx_order', degree + 2) fine_order = kwargs.get('fine_order', 4 * degree) fmm_order = kwargs.get('FMM Order', None) fmm_tol = kwargs.get('FMM Tol', None) # make sure got either fmm_order xor fmm_tol if fmm_order is None and fmm_tol is None: raise ValueError("At least one of 'fmm_order', 'fmm_tol' must not " "be *None*") if fmm_order is not None and fmm_tol is not None: raise ValueError("At most one of 'fmm_order', 'fmm_tol' must not " "be *None*") # if got fmm_tol, make a level-to-order fmm_level_to_order = None if fmm_tol is not None: if not isinstance(fmm_tol, float): raise TypeError("fmm_tol of type '%s' is not of type float" % type(fmm_tol)) if fmm_tol <= 0.0: raise ValueError( "fmm_tol of '%s' is less than or equal to 0.0" % fmm_tol) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder fmm_level_to_order = SimpleExpansionOrderFinder(fmm_tol) # Otherwise, make sure we got a valid fmm_order else: if not isinstance(fmm_order, int): if fmm_order != False: raise TypeError( "fmm_order of type '%s' is not of type int" % type(fmm_order)) if fmm_order != False and fmm_order < 1: raise ValueError("fmm_order of '%s' is less than 1" % fmm_order) qbx_kwargs = { 'qbx_order': qbx_order, 'fine_order': fine_order, 'fmm_order': fmm_order, 'fmm_level_to_order': fmm_level_to_order, 'fmm_backend': 'fmmlib', } # }}} ksp, comp_sol = nonlocal_integral_eq( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=options_prefix, solver_parameters=solver_parameters, fspace=fspace, vfspace=vfspace, true_sol_grad_expr=true_sol_grad_expr, actx=actx, dgfspace=dgfspace, dgvfspace=dgvfspace, meshmode_src_connection=meshmode_src_connection, qbx_kwargs=qbx_kwargs, ) snes_or_ksp = ksp elif method == 'transmission': snes, comp_sol = transmission( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=options_prefix, solver_parameters=solver_parameters, fspace=fspace, true_sol_grad_expr=true_sol_grad_expr, ) snes_or_ksp = snes else: raise ValueError("Invalid method") comp_sol.rename(name=comp_sol_name) return true_sol, comp_sol, snes_or_ksp