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 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 test_off_surface_eval(actx_factory, use_fmm, visualize=False): logging.basicConfig(level=logging.INFO) actx = actx_factory() # prevent cache 'splosion from sympy.core.cache import clear_cache clear_cache() nelements = 30 target_order = 8 qbx_order = 3 if use_fmm: fmm_order = qbx_order else: fmm_order = False mesh = mgen.make_curve_mesh(partial(mgen.ellipse, 3), 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=fmm_order, ) from pytential.target import PointsTarget fplot = FieldPlotter(np.zeros(2), extent=0.54, npoints=30) targets = PointsTarget(actx.freeze(actx.from_numpy(fplot.points))) places = GeometryCollection((qbx, targets)) density_discr = places.get_discretization(places.auto_source.geometry) from sumpy.kernel import LaplaceKernel op = sym.D(LaplaceKernel(2), sym.var("sigma"), qbx_forced_limit=-2) sigma = density_discr.zeros(actx) + 1 fld_in_vol = bind(places, op)(actx, sigma=sigma) fld_in_vol_exact = -1 linf_err = actx.to_numpy( actx.np.linalg.norm(fld_in_vol - fld_in_vol_exact, ord=np.inf)) logger.info("l_inf error: %.12e", linf_err) if visualize: fplot.show_scalar_in_matplotlib(actx.to_numpy(fld_in_vol)) import matplotlib.pyplot as pt pt.colorbar() pt.show() assert linf_err < 1e-3
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_interpolation(actx_factory, name, source_discr_stage, target_granularity): actx = actx_factory() nelements = 32 target_order = 7 qbx_order = 4 where = sym.as_dofdesc("test_interpolation") from_dd = sym.DOFDescriptor( geometry=where.geometry, discr_stage=source_discr_stage, granularity=sym.GRANULARITY_NODE) to_dd = sym.DOFDescriptor( geometry=where.geometry, discr_stage=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) mesh = mgen.make_curve_mesh(mgen.starfish, np.linspace(0.0, 1.0, nelements + 1), target_order) discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(discr, fine_order=4 * target_order, qbx_order=qbx_order, fmm_order=False) from pytential import GeometryCollection places = GeometryCollection(qbx, auto_where=where) sigma_sym = sym.var("sigma") op_sym = sym.sin(sym.interp(from_dd, to_dd, sigma_sym)) bound_op = bind(places, op_sym, auto_where=where) def discr_and_nodes(stage): density_discr = places.get_discretization(where.geometry, stage) return density_discr, actx.to_numpy( flatten(density_discr.nodes(), actx) ).reshape(density_discr.ambient_dim, -1) _, target_nodes = discr_and_nodes(sym.QBX_SOURCE_QUAD_STAGE2) source_discr, source_nodes = discr_and_nodes(source_discr_stage) sigma_target = np.sin(la.norm(target_nodes, axis=0)) sigma_dev = unflatten( thaw(source_discr.nodes()[0], actx), actx.from_numpy(la.norm(source_nodes, axis=0)), actx) sigma_target_interp = actx.to_numpy( flatten(bound_op(actx, sigma=sigma_dev), actx) ) if name in ("default", "default_explicit", "stage2", "quad"): error = la.norm(sigma_target_interp - sigma_target) / la.norm(sigma_target) assert error < 1.0e-10 elif name in ("stage2_center",): assert len(sigma_target_interp) == 2 * len(sigma_target) else: raise ValueError(f"unknown test case name: {name}")
def test_cost_model(ctx, calibration_params): queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue, force_device_scalars=True) cost_model = QBXCostModel() for lpot_source in test_geometries(actx): lpot_source = lpot_source.copy(cost_model=cost_model) from pytential import GeometryCollection places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) bound_op = get_bound_op(places) sigma = get_test_density(actx, density_discr) cost_S, _ = bound_op.cost_per_stage(calibration_params, sigma=sigma) model_result = one(cost_S.values()) # Warm-up run. bound_op.eval({"sigma": sigma}, array_context=actx) temp_timing_results = [] for _ in range(RUNS): timing_data = {} bound_op.eval({"sigma": sigma}, array_context=actx, timing_data=timing_data) temp_timing_results.append(one(timing_data.values())) timing_result = {} for param in model_result: timing_result[param] = (sum( temp_timing_result[param]["process_elapsed"] for temp_timing_result in temp_timing_results)) / RUNS from pytools import Table table = Table() table.add_row(["stage", "actual (s)", "predicted (s)"]) for stage in model_result: row = [ stage, f"{timing_result[stage]:.2f}", f"{model_result[stage]:.2f}", ] table.add_row(row) print(table)
def test_unregularized_off_surface_fmm_vs_direct(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nelements = 300 target_order = 8 fmm_order = 4 # {{{ geometry mesh = make_curve_mesh(WobblyCircle.random(8, seed=30), np.linspace(0, 1, nelements+1), target_order) from pytential.unregularized import UnregularizedLayerPotentialSource from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) direct = UnregularizedLayerPotentialSource( density_discr, fmm_order=False, ) fmm = direct.copy( fmm_level_to_order=lambda kernel, kernel_args, tree, level: fmm_order) sigma = density_discr.zeros(actx) + 1 fplot = FieldPlotter(np.zeros(2), extent=5, npoints=100) from pytential.target import PointsTarget ptarget = PointsTarget(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ "unregularized_direct": direct, "unregularized_fmm": fmm, "targets": ptarget}) # }}} # {{{ check from sumpy.kernel import LaplaceKernel op = sym.D(LaplaceKernel(2), sym.var("sigma"), qbx_forced_limit=None) direct_fld_in_vol = bind(places, op, auto_where=("unregularized_direct", "targets"))( actx, sigma=sigma) fmm_fld_in_vol = bind(places, op, auto_where=("unregularized_fmm", "targets"))(actx, sigma=sigma) err = actx.np.fabs(fmm_fld_in_vol - direct_fld_in_vol) linf_err = actx.to_numpy(err).max() print("l_inf error:", linf_err) assert linf_err < 5e-3
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 __init__(self, places, approx_nproxy=None, radius_factor=None): from pytential import GeometryCollection if not isinstance(places, GeometryCollection): places = GeometryCollection(places) self.places = places self.ambient_dim = places.ambient_dim self.radius_factor = 1.1 if radius_factor is None else radius_factor approx_nproxy = 32 if approx_nproxy is None else approx_nproxy self.ref_points = \ _generate_unit_sphere(self.ambient_dim, approx_nproxy)
def test_target_association_failure(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ generate circle order = 5 nelements = 40 # Make the curve mesh. curve_f = partial(ellipse, 1) mesh = make_curve_mesh(curve_f, np.linspace(0, 1, nelements + 1), order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory factory = InterpolatoryQuadratureSimplexGroupFactory(order) discr = Discretization(actx, mesh, factory) lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in target association fine_order=order) places = GeometryCollection(lpot_source) # }}} # {{{ generate targets and check close_circle = 0.999 * np.exp( 2j * np.pi * np.linspace(0, 1, 500, endpoint=False)) from pytential.target import PointsTarget close_circle_target = (PointsTarget( actx.from_numpy(np.array([close_circle.real, close_circle.imag])))) targets = ((close_circle_target, 0), ) from pytential.qbx.target_assoc import (TargetAssociationCodeContainer, associate_targets_to_qbx_centers, QBXTargetAssociationFailedException ) from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer(actx, TreeCodeContainer(actx)) with pytest.raises(QBXTargetAssociationFailedException): associate_targets_to_qbx_centers(places, places.auto_source, code_container.get_wrangler(actx), targets, target_association_tolerance=1e-10)
def calibrate_cost_model(ctx): queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) from pytential.qbx.cost import CostModel, estimate_calibration_params cost_model = CostModel() model_results = [] timing_results = [] for lpot_source in training_geometries(actx): lpot_source = lpot_source.copy(cost_model=cost_model) from pytential import GeometryCollection places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) bound_op = get_bound_op(places) sigma = get_test_density(actx, density_discr) cost_S = bound_op.get_modeled_cost(actx, sigma=sigma) # Warm-up run. bound_op.eval({"sigma": sigma}, array_context=actx) for _ in range(RUNS): timing_data = {} bound_op.eval({"sigma": sigma}, array_context=actx, timing_data=timing_data) model_results.append(one(cost_S.values())) timing_results.append(one(timing_data.values())) calibration_params = (estimate_calibration_params(model_results, timing_results)) return cost_model.with_calibration_params(calibration_params)
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=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 calibrate_cost_model(ctx): queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue, force_device_scalars=True) cost_model = QBXCostModel() model_results = [] timing_results = [] for lpot_source in training_geometries(actx): lpot_source = lpot_source.copy(cost_model=cost_model) from pytential import GeometryCollection places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) bound_op = get_bound_op(places) sigma = get_test_density(actx, density_discr) modeled_cost, _ = bound_op.cost_per_stage("constant_one", sigma=sigma) # Warm-up run. bound_op.eval({"sigma": sigma}, array_context=actx) for _ in range(RUNS): timing_data = {} bound_op.eval({"sigma": sigma}, array_context=actx, timing_data=timing_data) model_results.append(modeled_cost) timing_results.append(timing_data) calibration_params = cost_model.estimate_kernel_specific_calibration_params( model_results, timing_results, time_field_name="process_elapsed") return calibration_params
def test_unregularized_with_ones_kernel(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nelements = 10 order = 8 mesh = make_curve_mesh(partial(ellipse, 1), np.linspace(0, 1, nelements+1), order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) from pytential.unregularized import UnregularizedLayerPotentialSource lpot_source = UnregularizedLayerPotentialSource(discr) from pytential.target import PointsTarget targets = PointsTarget(np.zeros((2, 1), dtype=float)) places = GeometryCollection({ sym.DEFAULT_SOURCE: lpot_source, sym.DEFAULT_TARGET: lpot_source, "target_non_self": targets}) from sumpy.kernel import one_kernel_2d sigma_sym = sym.var("sigma") op = sym.IntG(one_kernel_2d, sigma_sym, qbx_forced_limit=None) sigma = discr.zeros(actx) + 1 result_self = bind(places, op, auto_where=places.auto_where)( actx, sigma=sigma) result_nonself = bind(places, op, auto_where=(places.auto_source, "target_non_self"))( actx, sigma=sigma) from meshmode.dof_array import flatten assert np.allclose(actx.to_numpy(flatten(result_self)), 2 * np.pi) assert np.allclose(actx.to_numpy(result_nonself), 2 * np.pi)
def __init__( self, places, approx_nproxy: Optional[int] = None, radius_factor: Optional[float] = None, norm_type: str = "linf", _generate_ref_proxies: Optional[Callable[[int], np.ndarray]] = None, ) -> None: """ :param approx_nproxy: desired number of proxy points. In higher dimensions, it is not always possible to construct a proxy ball with exactly this number of proxy points. The exact number of proxy points can be retrieved with :attr:`nproxy`. :param radius_factor: Factor multiplying the block radius (i.e radius of the bounding box) to get the proxy ball radius. :param norm_type: type of the norm used to compute the centers of each block. Supported values are ``"linf"`` and ``"l2"``. """ if _generate_ref_proxies is None: from functools import partial _generate_ref_proxies = partial(_generate_unit_sphere, places.ambient_dim) from pytential import GeometryCollection if not isinstance(places, GeometryCollection): places = GeometryCollection(places) if norm_type not in ("l2", "linf"): raise ValueError(f"unsupported norm type: '{norm_type}' " + "(expected one of 'l2' or 'linf')") if radius_factor is None: # FIXME: this is a fairly arbitrary value radius_factor = 1.1 if approx_nproxy is None: # FIXME: this is a fairly arbitrary value approx_nproxy = 32 self.places = places self.radius_factor = radius_factor self.norm_type = norm_type self.ref_points = _generate_ref_proxies(approx_nproxy)
def bind(places, expr, auto_where=None): """ :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection`. Alternatively, any list or mapping that is a valid argument for its constructor can also be used. :arg auto_where: for simple source-to-self or source-to-target evaluations, find 'where' attributes automatically. :arg expr: one or multiple expressions consisting of primitives form :mod:`pytential.symbolic.primitives` (aka :mod:`pytential.sym`). Multiple expressions can be combined into one object to pass here in the form of a :mod:`numpy` object array :returns: a :class:`BoundExpression` """ if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) auto_where = places.auto_where expr = _prepare_expr(places, expr, auto_where=auto_where) return BoundExpression(places, expr)
def _make_temporary_collection(lpot_source, stage1_density_discr=None, stage2_density_discr=None): from pytential import sym from pytential import GeometryCollection name = "_tmp_refine_source" places = GeometryCollection(lpot_source, auto_where=name) if stage1_density_discr is not None: places._add_discr_to_cache(stage1_density_discr, name, sym.QBX_SOURCE_STAGE1) if stage2_density_discr is not None: quad_stage2_density_discr = \ _make_quad_stage2_discr(lpot_source, stage2_density_discr) places._add_discr_to_cache(stage2_density_discr, name, sym.QBX_SOURCE_STAGE2) places._add_discr_to_cache(quad_stage2_density_discr, name, sym.QBX_SOURCE_QUAD_STAGE2) return places
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()
if visualize: qbx_tgt_tol = qbx.copy(target_association_tolerance=0.2) fplot = make_field_plotter_from_bbox(find_bounding_box( scat_discr.mesh), h=(0.05, 0.05, 0.3), extend_factor=0.3) fplot_tgt = PointsTarget(actx.from_numpy(fplot.points)) places.update({ "qbx_target_tol": qbx_tgt_tol, "plot_targets": fplot_tgt, }) from pytential import GeometryCollection places = GeometryCollection(places) density_discr = places.get_discretization(places.auto_source.geometry) # {{{ system solve h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) pde_test_inc = EHField( vector_from_device( actx.queue, eval_inc_field_at(places, target="patch_target"))) source_maxwell_resids = [ calc_patch.norm(x, np.inf) / calc_patch.norm(pde_test_inc.e, np.inf) for x in frequency_domain_maxwell(calc_patch, pde_test_inc.e, pde_test_inc.h, case.k)
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 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 test_cost_model_correctness(ctx_factory, dim, off_surface, use_target_specific_qbx): """Check that computed cost matches that of a constant-one FMM.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) cost_model = QBXCostModel( translation_cost_model_factory=OpCountingTranslationCostModel) lpot_source = get_lpot_source(actx, dim).copy( cost_model=cost_model, _use_target_specific_qbx=use_target_specific_qbx) # Construct targets. if off_surface: from pytential.target import PointsTarget from boxtree.tools import make_uniform_particle_array ntargets = 10 ** 3 targets = PointsTarget( make_uniform_particle_array(queue, ntargets, dim, np.float)) target_discrs_and_qbx_sides = ((targets, 0),) qbx_forced_limit = None else: targets = lpot_source.density_discr target_discrs_and_qbx_sides = ((targets, 1),) qbx_forced_limit = 1 places = GeometryCollection((lpot_source, targets)) source_dd = places.auto_source density_discr = places.get_discretization(source_dd.geometry) # Construct bound op, run cost model. sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=qbx_forced_limit) op_S = bind(places, sym_op_S) sigma = get_density(actx, density_discr) from pytools import one modeled_time, _ = op_S.cost_per_stage("constant_one", sigma=sigma) modeled_time = one(modeled_time.values()) # Run FMM with ConstantOneWrangler. This can't be done with pytential's # high-level interface, so call the FMM driver directly. from pytential.qbx.fmm import drive_fmm geo_data = lpot_source.qbx_fmm_geometry_data( places, source_dd.geometry, target_discrs_and_qbx_sides=target_discrs_and_qbx_sides) wrangler = ConstantOneQBXExpansionWrangler( queue, geo_data, use_target_specific_qbx) quad_stage2_density_discr = places.get_discretization( source_dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) ndofs = quad_stage2_density_discr.ndofs src_weights = np.ones(ndofs) timing_data = {} potential = drive_fmm(wrangler, (src_weights,), timing_data, traversal=wrangler.trav)[0][geo_data.ncenters:] # Check constant one wrangler for correctness. assert (potential == ndofs).all() # Check that the cost model matches the timing data returned by the # constant one wrangler. mismatches = [] for stage in timing_data: if stage not in modeled_time: assert timing_data[stage]["ops_elapsed"] == 0 else: if timing_data[stage]["ops_elapsed"] != modeled_time[stage]: mismatches.append( (stage, timing_data[stage]["ops_elapsed"], modeled_time[stage])) assert not mismatches, "\n".join(str(s) for s in mismatches) # {{{ Test per-box cost total_cost = 0.0 for stage in timing_data: total_cost += timing_data[stage]["ops_elapsed"] per_box_cost, _ = op_S.cost_per_box("constant_one", sigma=sigma) print(per_box_cost) per_box_cost = one(per_box_cost.values()) total_aggregate_cost = cost_model.aggregate_over_boxes(per_box_cost) assert total_cost == ( total_aggregate_cost + modeled_time["coarsen_multipoles"] + modeled_time["refine_locals"] )
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), ])
def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, force_direct, visualize=False): logging.basicConfig(level=logging.INFO) print("ellipse_aspect: %s, mode_nr: %d, qbx_order: %d" % (ellipse_aspect, mode_nr, qbx_order)) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() if ellipse_aspect != 1: nelements_values = [60, 100, 150, 200] else: nelements_values = [30, 70] # See # # [1] G. J. Rodin and O. Steinbach, "Boundary Element Preconditioners # for Problems Defined on Slender Domains", SIAM Journal on Scientific # Computing, Vol. 24, No. 4, pg. 1450, 2003. # https://dx.doi.org/10.1137/S1064827500372067 for nelements in nelements_values: mesh = make_curve_mesh(partial(ellipse, ellipse_aspect), np.linspace(0, 1, nelements + 1), target_order) fmm_order = 12 if force_direct: fmm_order = False pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=fmm_order, _expansions_in_tree_have_extent=True, ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from meshmode.dof_array import thaw, flatten nodes = thaw(actx, density_discr.nodes()) if visualize: # plot geometry, centers, normals centers = bind(places, sym.expansion_centers(qbx.ambient_dim, +1))(actx) normals = bind(places, sym.normal(qbx.ambient_dim))(actx).as_vector(object) nodes_h = np.array( [actx.to_numpy(axis) for axis in flatten(nodes)]) centers_h = np.array( [actx.to_numpy(axis) for axis in flatten(centers)]) normals_h = np.array( [actx.to_numpy(axis) for axis in flatten(normals)]) pt.plot(nodes_h[0], nodes_h[1], "x-") pt.plot(centers_h[0], centers_h[1], "o") pt.quiver(nodes_h[0], nodes_h[1], normals_h[0], normals_h[1]) pt.gca().set_aspect("equal") pt.show() angle = actx.np.arctan2(nodes[1] * ellipse_aspect, nodes[0]) ellipse_fraction = ((1 - ellipse_aspect) / (1 + ellipse_aspect))**mode_nr # (2.6) in [1] J = actx.np.sqrt( # noqa actx.np.sin(angle)**2 + (1 / ellipse_aspect)**2 * actx.np.cos(angle)**2) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(2) # {{{ single layer sigma_sym = sym.var("sigma") s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1) sigma = actx.np.cos(mode_nr * angle) / J s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) s_eigval = 1 / (2 * mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction) # (2.12) in [1] s_sigma_ref = s_eigval * J * sigma if 0: #pt.plot(s_sigma.get(), label="result") #pt.plot(s_sigma_ref.get(), label="ref") pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err") pt.legend() pt.show() h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_err = (norm(density_discr, s_sigma - s_sigma_ref) / norm(density_discr, s_sigma_ref)) s_eoc_rec.add_data_point(h_max, s_err) # }}} # {{{ double layer d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) d_eigval = -(-1)**mode_nr * 1 / 2 * ellipse_fraction d_sigma_ref = d_eigval * sigma if 0: pt.plot(actx.to_numpy(flatten(d_sigma)), label="result") pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref") pt.legend() pt.show() if ellipse_aspect == 1: d_ref_norm = norm(density_discr, sigma) else: d_ref_norm = norm(density_discr, d_sigma_ref) d_err = (norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm) d_eoc_rec.add_data_point(h_max, d_err) # }}} if ellipse_aspect == 1: # {{{ S' sp_sigma_op = sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma) sp_eigval = 0 sp_sigma_ref = sp_eigval * sigma sp_err = (norm(density_discr, sp_sigma - sp_sigma_ref) / norm(density_discr, sigma)) sp_eoc_rec.add_data_point(h_max, sp_err) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 1.5 if ellipse_aspect == 1: print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5
def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, fmm_backend): logging.basicConfig(level=logging.INFO) special = pytest.importorskip("scipy.special") cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() dp_eoc_rec = EOCRecorder() def rel_err(comp, ref): return (norm(density_discr, comp - ref) / norm(density_discr, ref)) for nrefinements in [0, 1]: from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(1, target_order) from meshmode.mesh.refinement import Refiner refiner = Refiner(mesh) for i in range(nrefinements): flags = np.ones(mesh.nelements, dtype=bool) refiner.refine(flags) mesh = refiner.get_current_mesh() pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=6, fmm_backend=fmm_backend, ) places = GeometryCollection(qbx) from meshmode.dof_array import flatten, unflatten, thaw density_discr = places.get_discretization(places.auto_source.geometry) nodes = thaw(actx, density_discr.nodes()) r = actx.np.sqrt(nodes[0] * nodes[0] + nodes[1] * nodes[1] + nodes[2] * nodes[2]) phi = actx.np.arccos(nodes[2] / r) theta = actx.np.arctan2(nodes[0], nodes[1]) ymn = unflatten( actx, density_discr, actx.from_numpy( special.sph_harm(mode_m, mode_n, actx.to_numpy(flatten(theta)), actx.to_numpy(flatten(phi))))) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(3) # {{{ single layer s_sigma_op = bind( places, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1)) s_sigma = s_sigma_op(actx, sigma=ymn) s_eigval = 1 / (2 * mode_n + 1) h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval * ymn)) # }}} # {{{ double layer d_sigma_op = bind( places, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) d_sigma = d_sigma_op(actx, sigma=ymn) d_eigval = -1 / (2 * (2 * mode_n + 1)) d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval * ymn)) # }}} # {{{ S' sp_sigma_op = bind( places, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) sp_sigma = sp_sigma_op(actx, sigma=ymn) sp_eigval = -1 / (2 * (2 * mode_n + 1)) sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval * ymn)) # }}} # {{{ D' dp_sigma_op = bind( places, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) dp_sigma = dp_sigma_op(actx, sigma=ymn) dp_eigval = -(mode_n * (mode_n + 1)) / (2 * mode_n + 1) dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval * ymn)) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 0.5 print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D':") print(dp_eoc_rec) required_order = qbx_order assert dp_eoc_rec.order_estimate() > required_order - 1.5
def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ initial geometry from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in refinement fine_order=order) places = GeometryCollection(lpot_source) # }}} # {{{ refined geometry kernel_length_scale = 5 / helmholtz_k if helmholtz_k else None expansion_disturbance_tolerance = 0.025 from pytential.qbx.refinement import refine_geometry_collection places = refine_geometry_collection( places, kernel_length_scale=kernel_length_scale, expansion_disturbance_tolerance=expansion_disturbance_tolerance, visualize=visualize) # }}} dd = places.auto_source stage1_density_discr = places.get_discretization(dd.geometry) from meshmode.dof_array import thaw stage1_density_nodes = dof_array_to_numpy( actx, thaw(actx, stage1_density_discr.nodes())) quad_stage2_density_discr = places.get_discretization( dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) quad_stage2_density_nodes = dof_array_to_numpy( actx, thaw(actx, quad_stage2_density_discr.nodes())) int_centers = dof_array_to_numpy( actx, bind(places, sym.expansion_centers(lpot_source.ambient_dim, -1))(actx)) ext_centers = dof_array_to_numpy( actx, bind(places, sym.expansion_centers(lpot_source.ambient_dim, +1))(actx)) expansion_radii = dof_array_to_numpy( actx, bind(places, sym.expansion_radii(lpot_source.ambient_dim))(actx)) dd = dd.copy(granularity=sym.GRANULARITY_ELEMENT) source_danger_zone_radii = dof_array_to_numpy( actx, bind( places, sym._source_danger_zone_radii(lpot_source.ambient_dim, dofdesc=dd.to_stage2()))(actx)) quad_res = dof_array_to_numpy( actx, bind(places, sym._quad_resolution(lpot_source.ambient_dim, dofdesc=dd))(actx)) # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_panel, sources_panel): if centers_panel.element_nr == sources_panel.element_nr: # Same panel return my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = stage1_density_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # A center cannot be closer to another panel than to its originating # panel. rad = expansion_radii[centers_panel.discr_slice] assert (dist >= rad * (1-expansion_disturbance_tolerance)).all(), \ (dist, rad, centers_panel.element_nr, sources_panel.element_nr) def check_sufficient_quadrature_resolution(centers_panel, sources_panel): dz_radius = source_danger_zone_radii[sources_panel.element_nr] my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = quad_stage2_density_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # The quadrature contribution from each panel is as accurate # as from the center's own source panel. assert dist >= dz_radius, \ (dist, dz_radius, centers_panel.element_nr, sources_panel.element_nr) def check_quad_res_to_helmholtz_k_ratio(panel): # Check wavenumber to panel size ratio. assert quad_res[panel.element_nr] * helmholtz_k <= 5 for i, panel_1 in enumerate(iter_elements(stage1_density_discr)): for panel_2 in iter_elements(stage1_density_discr): check_disk_undisturbed_by_sources(panel_1, panel_2) for panel_2 in iter_elements(quad_stage2_density_discr): check_sufficient_quadrature_resolution(panel_1, panel_2) if helmholtz_k is not None: check_quad_res_to_helmholtz_k_ratio(panel_1)
def test_target_association(ctx_factory, curve_name, curve_f, nelements, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ generate lpot source order = 16 # Make the curve mesh. mesh = make_curve_mesh(curve_f, np.linspace(0, 1, nelements + 1), order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory factory = InterpolatoryQuadratureSimplexGroupFactory(order) discr = Discretization(actx, mesh, factory) lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in target association fine_order=order) places = GeometryCollection(lpot_source) # }}} # {{{ generate targets from pyopencl.clrandom import PhiloxGenerator rng = PhiloxGenerator(cl_ctx, seed=RNG_SEED) dd = places.auto_source.to_stage1() centers = dof_array_to_numpy( actx, bind( places, sym.interleaved_expansion_centers(lpot_source.ambient_dim, dofdesc=dd))(actx)) density_discr = places.get_discretization(dd.geometry) noise = actx.to_numpy( rng.uniform(queue, density_discr.ndofs, dtype=np.float, a=0.01, b=1.0)) tunnel_radius = dof_array_to_numpy( actx, bind( places, sym._close_target_tunnel_radii(lpot_source.ambient_dim, dofdesc=dd))(actx)) def targets_from_sources(sign, dist, dim=2): nodes = dof_array_to_numpy( actx, bind(places, sym.nodes(dim, dofdesc=dd))(actx).as_vector(np.object)) normals = dof_array_to_numpy( actx, bind(places, sym.normal(dim, dofdesc=dd))(actx).as_vector(np.object)) return actx.from_numpy(nodes + normals * sign * dist) from pytential.target import PointsTarget int_targets = PointsTarget(targets_from_sources(-1, noise * tunnel_radius)) ext_targets = PointsTarget(targets_from_sources(+1, noise * tunnel_radius)) far_targets = PointsTarget( targets_from_sources(+1, FAR_TARGET_DIST_FROM_SOURCE)) # Create target discretizations. target_discrs = ( # On-surface targets, interior (density_discr, -1), # On-surface targets, exterior (density_discr, +1), # Interior close targets (int_targets, -2), # Exterior close targets (ext_targets, +2), # Far targets, should not need centers (far_targets, 0), ) sizes = np.cumsum([discr.ndofs for discr, _ in target_discrs]) ( surf_int_slice, surf_ext_slice, vol_int_slice, vol_ext_slice, far_slice, ) = [slice(start, end) for start, end in zip(np.r_[0, sizes], sizes)] # }}} # {{{ run target associator and check from pytential.qbx.target_assoc import (TargetAssociationCodeContainer, associate_targets_to_qbx_centers) from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer(actx, TreeCodeContainer(actx)) target_assoc = (associate_targets_to_qbx_centers( places, places.auto_source, code_container.get_wrangler(actx), target_discrs, target_association_tolerance=1e-10).get(queue=queue)) expansion_radii = dof_array_to_numpy( actx, bind( places, sym.expansion_radii(lpot_source.ambient_dim, granularity=sym.GRANULARITY_CENTER))(actx)) from meshmode.dof_array import thaw surf_targets = dof_array_to_numpy(actx, thaw(actx, density_discr.nodes())) int_targets = actx.to_numpy(int_targets.nodes()) ext_targets = actx.to_numpy(ext_targets.nodes()) def visualize_curve_and_assoc(): import matplotlib.pyplot as plt from meshmode.mesh.visualization import draw_curve draw_curve(density_discr.mesh) targets = int_targets tgt_slice = surf_int_slice plt.plot(centers[0], centers[1], "+", color="orange") ax = plt.gca() for tx, ty, tcenter in zip(targets[0, tgt_slice], targets[1, tgt_slice], target_assoc.target_to_center[tgt_slice]): if tcenter >= 0: ax.add_artist( plt.Line2D( (tx, centers[0, tcenter]), (ty, centers[1, tcenter]), )) ax.set_aspect("equal") plt.show() if visualize: visualize_curve_and_assoc() # Checks that the targets match with centers on the appropriate side and # within the allowable distance. def check_close_targets(centers, targets, true_side, target_to_center, target_to_side_result, tgt_slice): targets_have_centers = (target_to_center >= 0).all() assert targets_have_centers assert (target_to_side_result == true_side).all() TOL = 1e-3 dists = la.norm((targets.T - centers.T[target_to_center]), axis=1) assert (dists <= (1 + TOL) * expansion_radii[target_to_center]).all() # Center side order = -1, 1, -1, 1, ... target_to_center_side = 2 * (target_assoc.target_to_center % 2) - 1 # interior surface check_close_targets(centers, surf_targets, -1, target_assoc.target_to_center[surf_int_slice], target_to_center_side[surf_int_slice], surf_int_slice) # exterior surface check_close_targets(centers, surf_targets, +1, target_assoc.target_to_center[surf_ext_slice], target_to_center_side[surf_ext_slice], surf_ext_slice) # interior volume check_close_targets(centers, int_targets, -1, target_assoc.target_to_center[vol_int_slice], target_to_center_side[vol_int_slice], vol_int_slice) # exterior volume check_close_targets(centers, ext_targets, +1, target_assoc.target_to_center[vol_ext_slice], target_to_center_side[vol_ext_slice], vol_ext_slice) # Checks that far targets are not assigned a center. assert (target_assoc.target_to_center[far_slice] == -1).all()
def build_matrix(actx, places, exprs, input_exprs, domains=None, auto_where=None, context=None): """ :arg actx: a :class:`~meshmode.array_context.ArrayContext`. :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection`. Alternatively, any list or mapping that is a valid argument for its constructor can also be used. :arg exprs: an array of expressions corresponding to the output block rows of the matrix. May also be a single expression. :arg input_exprs: an array of expressions corresponding to the input block columns of the matrix. May also be a single expression. :arg domains: a list of discretization identifiers (see 'places') or *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *None*, *auto_where* or, if it is not provided, :class:`~pytential.symbolic.primitives.DEFAULT_SOURCE` is required to be a key in :attr:`places`. :arg auto_where: For simple source-to-self or source-to-target evaluations, find 'where' attributes automatically. """ if context is None: context = {} from pytential import GeometryCollection if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) exprs = _prepare_expr(places, exprs, auto_where=auto_where) if not (isinstance(exprs, np.ndarray) and exprs.dtype.char == "O"): from pytools.obj_array import make_obj_array exprs = make_obj_array([exprs]) try: input_exprs = list(input_exprs) except TypeError: # not iterable, wrap in a list input_exprs = [input_exprs] domains = _prepare_domains(len(input_exprs), places, domains, places.auto_source) from pytential.symbolic.matrix import MatrixBuilder, is_zero nblock_rows = len(exprs) nblock_columns = len(input_exprs) blocks = np.zeros((nblock_rows, nblock_columns), dtype=np.object) dtypes = [] for ibcol in range(nblock_columns): dep_source = places.get_geometry(domains[ibcol].geometry) dep_discr = places.get_discretization(domains[ibcol].geometry, domains[ibcol].discr_stage) mbuilder = MatrixBuilder(actx, dep_expr=input_exprs[ibcol], other_dep_exprs=(input_exprs[:ibcol] + input_exprs[ibcol + 1:]), dep_source=dep_source, dep_discr=dep_discr, places=places, context=context) for ibrow in range(nblock_rows): block = mbuilder(exprs[ibrow]) assert is_zero(block) or isinstance(block, np.ndarray) blocks[ibrow, ibcol] = block if isinstance(block, np.ndarray): dtypes.append(block.dtype) return actx.from_numpy(_bmat(blocks, dtypes))
def test_compare_cl_and_py_cost_model(ctx_factory): nelements = 3600 target_order = 16 fmm_order = 5 qbx_order = fmm_order ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) # {{{ Construct geometry from meshmode.mesh.generation import make_curve_mesh, starfish mesh = make_curve_mesh(starfish, np.linspace(0, 1, nelements), target_order) 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=fmm_order ) places = GeometryCollection(qbx) from pytential.qbx.refinement import refine_geometry_collection places = refine_geometry_collection(places) target_discrs_and_qbx_sides = tuple([(qbx.density_discr, 0)]) geo_data_dev = qbx.qbx_fmm_geometry_data( places, places.auto_source.geometry, target_discrs_and_qbx_sides ) from pytential.qbx.utils import ToHostTransferredGeoDataWrapper geo_data = ToHostTransferredGeoDataWrapper(queue, geo_data_dev) # }}} # {{{ Construct cost models cl_cost_model = QBXCostModel() python_cost_model = _PythonQBXCostModel() tree = geo_data.tree() xlat_cost = make_pde_aware_translation_cost_model( tree.targets.shape[0], tree.nlevels ) constant_one_params = QBXCostModel.get_unit_calibration_params() constant_one_params["p_qbx"] = 5 for ilevel in range(tree.nlevels): constant_one_params["p_fmm_lev%d" % ilevel] = 10 cl_cost_factors = cl_cost_model.qbx_cost_factors_for_kernels_from_model( queue, tree.nlevels, xlat_cost, constant_one_params ) python_cost_factors = python_cost_model.qbx_cost_factors_for_kernels_from_model( None, tree.nlevels, xlat_cost, constant_one_params ) # }}} # {{{ Test process_form_qbxl cl_ndirect_sources_per_target_box = ( cl_cost_model.get_ndirect_sources_per_target_box( queue, geo_data_dev.traversal() ) ) queue.finish() start_time = time.time() cl_p2qbxl = cl_cost_model.process_form_qbxl( queue, geo_data_dev, cl_cost_factors["p2qbxl_cost"], cl_ndirect_sources_per_target_box ) queue.finish() logger.info("OpenCL time for process_form_qbxl: {}".format( str(time.time() - start_time) )) python_ndirect_sources_per_target_box = ( python_cost_model.get_ndirect_sources_per_target_box( queue, geo_data.traversal() ) ) start_time = time.time() python_p2qbxl = python_cost_model.process_form_qbxl( queue, geo_data, python_cost_factors["p2qbxl_cost"], python_ndirect_sources_per_target_box ) logger.info("Python time for process_form_qbxl: {}".format( str(time.time() - start_time) )) assert np.array_equal(cl_p2qbxl.get(), python_p2qbxl) # }}} # {{{ Test process_m2qbxl queue.finish() start_time = time.time() cl_m2qbxl = cl_cost_model.process_m2qbxl( queue, geo_data_dev, cl_cost_factors["m2qbxl_cost"] ) queue.finish() logger.info("OpenCL time for process_m2qbxl: {}".format( str(time.time() - start_time) )) start_time = time.time() python_m2qbxl = python_cost_model.process_m2qbxl( queue, geo_data, python_cost_factors["m2qbxl_cost"] ) logger.info("Python time for process_m2qbxl: {}".format( str(time.time() - start_time) )) assert np.array_equal(cl_m2qbxl.get(), python_m2qbxl) # }}} # {{{ Test process_l2qbxl queue.finish() start_time = time.time() cl_l2qbxl = cl_cost_model.process_l2qbxl( queue, geo_data_dev, cl_cost_factors["l2qbxl_cost"] ) queue.finish() logger.info("OpenCL time for process_l2qbxl: {}".format( str(time.time() - start_time) )) start_time = time.time() python_l2qbxl = python_cost_model.process_l2qbxl( queue, geo_data, python_cost_factors["l2qbxl_cost"] ) logger.info("Python time for process_l2qbxl: {}".format( str(time.time() - start_time) )) assert np.array_equal(cl_l2qbxl.get(), python_l2qbxl) # }}} # {{{ Test process_eval_qbxl queue.finish() start_time = time.time() cl_eval_qbxl = cl_cost_model.process_eval_qbxl( queue, geo_data_dev, cl_cost_factors["qbxl2p_cost"] ) queue.finish() logger.info("OpenCL time for process_eval_qbxl: {}".format( str(time.time() - start_time) )) start_time = time.time() python_eval_qbxl = python_cost_model.process_eval_qbxl( queue, geo_data, python_cost_factors["qbxl2p_cost"] ) logger.info("Python time for process_eval_qbxl: {}".format( str(time.time() - start_time) )) assert np.array_equal(cl_eval_qbxl.get(), python_eval_qbxl) # }}} # {{{ Test eval_target_specific_qbxl queue.finish() start_time = time.time() cl_eval_target_specific_qbxl = cl_cost_model.process_eval_target_specific_qbxl( queue, geo_data_dev, cl_cost_factors["p2p_tsqbx_cost"], cl_ndirect_sources_per_target_box ) queue.finish() logger.info("OpenCL time for eval_target_specific_qbxl: {}".format( str(time.time() - start_time) )) start_time = time.time() python_eval_target_specific_qbxl = \ python_cost_model.process_eval_target_specific_qbxl( queue, geo_data, python_cost_factors["p2p_tsqbx_cost"], python_ndirect_sources_per_target_box ) logger.info("Python time for eval_target_specific_qbxl: {}".format( str(time.time() - start_time) )) assert np.array_equal( cl_eval_target_specific_qbxl.get(), python_eval_target_specific_qbxl )