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 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_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_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_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_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 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 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_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="torus", visualize=False): import logging 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 == "torus": rout = 10 rin = 1 from meshmode.mesh.generation import generate_torus base_mesh = generate_torus( rout, rin, 40, 4, mesh_order) from meshmode.mesh.processing import affine_map, merge_disjoint_meshes # nx = 1 # ny = 1 nz = 1 dz = 0 meshes = [ affine_map( base_mesh, A=np.diag([1, 1, 1]), b=np.array([0, 0, iz*dz])) for iz in range(nz)] 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(3), extent=20, npoints=50) targets = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ "qbx": qbx, "qbx_target_assoc": qbx.copy(target_association_tolerance=0.2), "targets": PointsTarget(targets) }, auto_where="qbx") density_discr = places.get_discretization("qbx") # {{{ describe bvp from sumpy.kernel import LaplaceKernel kernel = LaplaceKernel(3) sigma_sym = sym.var("sigma") #sqrt_w = sym.sqrt_jac_q_weight(3) sqrt_w = 1 inv_sqrt_w_sigma = sym.cse(sigma_sym/sqrt_w) # -1 for interior Dirichlet # +1 for exterior Dirichlet loc_sign = +1 bdry_op_sym = (loc_sign*0.5*sigma_sym + sqrt_w*( sym.S(kernel, inv_sqrt_w_sigma, qbx_forced_limit=+1) + sym.D(kernel, inv_sqrt_w_sigma, qbx_forced_limit="avg") )) # }}} bound_op = bind(places, bdry_op_sym) # {{{ fix rhs and solve from meshmode.dof_array import thaw, flatten, unflatten nodes = thaw(actx, density_discr.nodes()) source = np.array([rout, 0, 0]) def u_incoming_func(x): from pytools.obj_array import obj_array_vectorize x = obj_array_vectorize(actx.to_numpy, flatten(x)) x = np.array(list(x)) # return 1/cl.clmath.sqrt( (x[0] - source[0])**2 # +(x[1] - source[1])**2 # +(x[2] - source[2])**2 ) return 1.0/la.norm(x - source[:, None], axis=0) bc = unflatten(actx, density_discr, actx.from_numpy(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", dtype=np.float64), bvp_rhs, tol=1e-14, progress=True, stall_iterations=0, hard_failure=True) sigma = bind(places, sym.var("sigma")/sqrt_w)( actx, sigma=gmres_result.solution) # }}} from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, density_discr, 20) bdry_vis.write_vtk_file("laplace.vtu", [ ("sigma", sigma), ]) # {{{ postprocess/visualize repr_kwargs = dict( source="qbx_target_assoc", target="targets", qbx_forced_limit=None) representation_sym = ( sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs) + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs)) try: fld_in_vol = actx.to_numpy( bind(places, representation_sym)(actx, sigma=sigma)) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file("laplace-dirichlet-3d-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("laplace-dirichlet-3d-potential.vts", [ ("potential", fld_in_vol), ])
def run_exterior_stokes( ctx_factory, *, ambient_dim, target_order, qbx_order, resolution, fmm_order=False, # FIXME: FMM is slower than direct evaluation source_ovsmp=None, radius=1.5, mu=1.0, visualize=False, _target_association_tolerance=0.05, _expansions_in_tree_have_extent=True): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ geometry if source_ovsmp is None: source_ovsmp = 4 if ambient_dim == 2 else 8 places = {} if ambient_dim == 2: from meshmode.mesh.generation import make_curve_mesh, ellipse mesh = make_curve_mesh(lambda t: radius * ellipse(1.0, t), np.linspace(0.0, 1.0, resolution + 1), target_order) elif ambient_dim == 3: from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(radius, target_order + 1, uniform_refinement_rounds=resolution) else: raise ValueError(f"unsupported dimension: {ambient_dim}") pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource( pre_density_discr, fine_order=source_ovsmp * target_order, qbx_order=qbx_order, fmm_order=fmm_order, target_association_tolerance=_target_association_tolerance, _expansions_in_tree_have_extent=_expansions_in_tree_have_extent) places["source"] = qbx from extra_int_eq_data import make_source_and_target_points point_source, point_target = make_source_and_target_points( side=+1, inner_radius=0.5 * radius, outer_radius=2.0 * radius, ambient_dim=ambient_dim, ) places["point_source"] = point_source places["point_target"] = point_target if visualize: from sumpy.visualization import make_field_plotter_from_bbox from meshmode.mesh.processing import find_bounding_box fplot = make_field_plotter_from_bbox(find_bounding_box(mesh), h=0.1, extend_factor=1.0) mask = np.linalg.norm(fplot.points, ord=2, axis=0) > (radius + 0.25) from pytential.target import PointsTarget plot_target = PointsTarget(fplot.points[:, mask].copy()) places["plot_target"] = plot_target del mask places = GeometryCollection(places, auto_where="source") density_discr = places.get_discretization("source") logger.info("ndofs: %d", density_discr.ndofs) logger.info("nelements: %d", density_discr.mesh.nelements) # }}} # {{{ symbolic sym_normal = sym.make_sym_vector("normal", ambient_dim) sym_mu = sym.var("mu") if ambient_dim == 2: from pytential.symbolic.stokes import HsiaoKressExteriorStokesOperator sym_omega = sym.make_sym_vector("omega", ambient_dim) op = HsiaoKressExteriorStokesOperator(omega=sym_omega) elif ambient_dim == 3: from pytential.symbolic.stokes import HebekerExteriorStokesOperator op = HebekerExteriorStokesOperator() else: assert False sym_sigma = op.get_density_var("sigma") sym_bc = op.get_density_var("bc") sym_op = op.operator(sym_sigma, normal=sym_normal, mu=sym_mu) sym_rhs = op.prepare_rhs(sym_bc, mu=mu) sym_velocity = op.velocity(sym_sigma, normal=sym_normal, mu=sym_mu) sym_source_pot = op.stokeslet.apply(sym_sigma, sym_mu, qbx_forced_limit=None) # }}} # {{{ boundary conditions normal = bind(places, sym.normal(ambient_dim).as_vector())(actx) np.random.seed(42) charges = make_obj_array([ actx.from_numpy(np.random.randn(point_source.ndofs)) for _ in range(ambient_dim) ]) if ambient_dim == 2: total_charge = make_obj_array([actx.np.sum(c) for c in charges]) omega = bind(places, total_charge * sym.Ones())(actx) if ambient_dim == 2: bc_context = {"mu": mu, "omega": omega} op_context = {"mu": mu, "omega": omega, "normal": normal} else: bc_context = {} op_context = {"mu": mu, "normal": normal} bc = bind(places, sym_source_pot, auto_where=("point_source", "source"))(actx, sigma=charges, mu=mu) rhs = bind(places, sym_rhs)(actx, bc=bc, **bc_context) bound_op = bind(places, sym_op) # }}} # {{{ solve from pytential.solve import gmres gmres_tol = 1.0e-9 result = gmres(bound_op.scipy_op(actx, "sigma", np.float64, **op_context), rhs, x0=rhs, tol=gmres_tol, progress=visualize, stall_iterations=0, hard_failure=True) sigma = result.solution # }}} # {{{ check velocity at "point_target" def rnorm2(x, y): y_norm = actx.np.linalg.norm(y.dot(y), ord=2) if y_norm < 1.0e-14: y_norm = 1.0 d = x - y return actx.np.linalg.norm(d.dot(d), ord=2) / y_norm ps_velocity = bind(places, sym_velocity, auto_where=("source", "point_target"))(actx, sigma=sigma, **op_context) ex_velocity = bind(places, sym_source_pot, auto_where=("point_source", "point_target"))(actx, sigma=charges, mu=mu) v_error = rnorm2(ps_velocity, ex_velocity) h_max = bind(places, sym.h_max(ambient_dim))(actx) logger.info("resolution %4d h_max %.5e error %.5e", resolution, h_max, v_error) # }}}} # {{{ visualize if not visualize: return h_max, v_error from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, density_discr, target_order) filename = "stokes_solution_{}d_{}_ovsmp_{}.vtu".format( ambient_dim, resolution, source_ovsmp) vis.write_vtk_file(filename, [ ("density", sigma), ("bc", bc), ("rhs", rhs), ], overwrite=True) # }}} return h_max, v_error
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 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 main(curve_fn=starfish, visualize=True): import logging logging.basicConfig(level=logging.WARNING) # INFO for more progress info import pyopencl as cl cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, force_device_scalars=True) from meshmode.mesh.generation import make_curve_mesh mesh = make_curve_mesh( curve_fn, np.linspace(0, 1, nelements+1), target_order) from pytential.qbx import QBXLayerPotentialSource from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, qbx_order, fmm_order=qbx_order+3, target_association_tolerance=0.005, #fmm_backend="fmmlib", ) from pytential.target import PointsTarget fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1000) targets_dev = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ "qbx": qbx, "targets": PointsTarget(targets_dev), }, auto_where="qbx") density_discr = places.get_discretization("qbx") nodes = thaw(density_discr.nodes(), actx) angle = actx.np.arctan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(2) kernel_kwargs = {"k": sym.var("k")} else: kernel = LaplaceKernel(2) kernel_kwargs = {} def op(**kwargs): kwargs.update(kernel_kwargs) #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), **kwargs)) return sym.D(kernel, sym.var("sigma"), **kwargs) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) if 0: from random import randrange sigma = actx.zeros(density_discr.ndofs, angle.entry_dtype) for _ in range(5): sigma[randrange(len(sigma))] = 1 from arraycontext import unflatten sigma = unflatten(angle, sigma, actx) else: sigma = actx.np.cos(mode_nr*angle) if isinstance(kernel, HelmholtzKernel): for i, elem in np.ndenumerate(sigma): sigma[i] = elem.astype(np.complex128) bound_bdry_op = bind(places, op()) if visualize: fld_in_vol = actx.to_numpy( bind(places, op( source="qbx", target="targets", qbx_forced_limit=None))(actx, sigma=sigma, k=k)) if enable_mayavi: fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) else: fplot.write_vtk_file("layerpot-potential.vts", [ ("potential", fld_in_vol) ]) if 0: apply_op = bound_bdry_op.scipy_op(actx, "sigma", np.float64, k=k) from sumpy.tools import build_matrix mat = build_matrix(apply_op) import matplotlib.pyplot as pt pt.imshow(mat) pt.colorbar() pt.show() if enable_mayavi: # {{{ plot boundary field from arraycontext import flatten fld_on_bdry = actx.to_numpy( flatten(bound_bdry_op(actx, sigma=sigma, k=k), actx)) nodes_host = actx.to_numpy( flatten(density_discr.nodes(), actx) ).reshape(density_discr.ambient_dim, -1) mlab.points3d(nodes_host[0], nodes_host[1], fld_on_bdry.real, scale_factor=0.03) mlab.colorbar() mlab.show()
def 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(): import logging logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 16 qbx_order = 3 nelements = 60 mode_nr = 0 k = 0 if k: kernel = HelmholtzKernel(2) else: kernel = LaplaceKernel(2) mesh = make_curve_mesh( #lambda t: ellipse(1, t), starfish, 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)) unaccel_qbx = QBXLayerPotentialSource( pre_density_discr, fine_order=2 * target_order, qbx_order=qbx_order, fmm_order=False, target_association_tolerance=.05, ) from pytential.target import PointsTarget fplot = FieldPlotter(np.zeros(2), extent=5, npoints=600) from pytential import GeometryCollection places = GeometryCollection({ "unaccel_qbx": unaccel_qbx, "qbx": unaccel_qbx.copy(fmm_order=10), "targets": PointsTarget(fplot.points) }) density_discr = places.get_discretization("unaccel_qbx") nodes = thaw(actx, density_discr.nodes()) angle = actx.np.arctan2(nodes[1], nodes[0]) from pytential import bind, sym if k: kernel_kwargs = {"k": sym.var("k")} else: kernel_kwargs = {} def get_op(): kwargs = dict(qbx_forced_limit=None) kwargs.update(kernel_kwargs) # return sym.d_dx(2, sym.S(kernel, sym.var("sigma"), **kwargs)) # return sym.D(kernel, sym.var("sigma"), **kwargs) return sym.S(kernel, sym.var("sigma"), **kwargs) op = get_op() sigma = actx.np.cos(mode_nr * angle) if isinstance(kernel, HelmholtzKernel): for i, elem in np.ndenumerate(sigma): sigma[i] = elem.astype(np.complex128) fld_in_vol = bind(places, op, auto_where=("unaccel_qbx", "targets"))(actx, sigma=sigma, k=k).get() fmm_fld_in_vol = bind(places, op, auto_where=("qbx", "targets"))(actx, sigma=sigma, k=k).get() err = fmm_fld_in_vol - fld_in_vol try: import matplotlib except ImportError: return matplotlib.use("Agg") im = fplot.show_scalar_in_matplotlib(np.log10(np.abs(err) + 1e-17)) from matplotlib.colors import Normalize im.set_norm(Normalize(vmin=-12, vmax=0)) import matplotlib.pyplot as pt from matplotlib.ticker import NullFormatter pt.gca().xaxis.set_major_formatter(NullFormatter()) pt.gca().yaxis.set_major_formatter(NullFormatter()) cb = pt.colorbar(shrink=0.9) cb.set_label(r"$\log_{10}(\mathrm{Error})$") pt.savefig("fmm-error-order-%d.pdf" % qbx_order)
def test_3d_jump_relations(actx_factory, relation, visualize=False): # logging.basicConfig(level=logging.INFO) actx = actx_factory() if relation == "div_s": target_order = 3 else: target_order = 4 qbx_order = target_order if relation == "sp": resolutions = [10, 14, 18] else: resolutions = [6, 10, 14] from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_factor in resolutions: from meshmode.mesh.generation import generate_torus mesh = generate_torus( 5, 2, n_major=2 * nel_factor, n_minor=nel_factor, order=target_order, ) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(pre_density_discr, fine_order=5 * target_order, qbx_order=qbx_order, fmm_order=qbx_order + 5, fmm_backend="fmmlib") places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from sumpy.kernel import LaplaceKernel knl = LaplaceKernel(places.ambient_dim) def nxcurlS(qbx_forced_limit): sigma_sym = sym.cse(sym.tangential_to_xyz(density_sym), "jxyz") return sym.n_cross( sym.curl( sym.S(knl, sigma_sym, qbx_forced_limit=qbx_forced_limit))) x, y, z = thaw(density_discr.nodes(), actx) if relation == "nxcurls": density_sym = sym.make_sym_vector("density", 2) jump_identity_sym = (nxcurlS(+1) - nxcurlS("avg") - 0.5 * sym.tangential_to_xyz(density_sym)) # The tangential coordinate system is element-local, so we can't just # conjure up some globally smooth functions, interpret their values # in the tangential coordinate system, and be done. Instead, generate # an XYZ function and project it. jxyz = sym.make_obj_array([ actx.np.cos(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.cos(0.5 * z), actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.sin(0.5 * z), actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) * actx.np.cos(0.5 * z), ]) density = bind( places, sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))(actx, jxyz=jxyz) elif relation == "sp": density_sym = sym.var("density") jump_identity_sym = ( 0.5 * density_sym + sym.Sp(knl, density_sym, qbx_forced_limit=+1) - sym.Sp(knl, density_sym, qbx_forced_limit="avg")) density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z) elif relation == "div_s": density_sym = sym.var("density") sigma_sym = sym.normal( places.ambient_dim).as_vector() * density_sym jump_identity_sym = ( sym.div(sym.S(knl, sigma_sym, qbx_forced_limit="avg")) + sym.D(knl, density_sym, qbx_forced_limit="avg")) density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z) else: raise ValueError(f"unexpected value of 'relation': '{relation}'") bound_jump_identity = bind(places, jump_identity_sym) jump_identity = bound_jump_identity(actx, density=density) h_max = actx.to_numpy( bind(places, sym.h_max(places.ambient_dim))(actx)) err = actx.to_numpy( norm(density_discr, jump_identity, np.inf) / norm(density_discr, density, np.inf)) eoc_rec.add_data_point(h_max, err) logging.info("error: nel %d h_max %.5e %.5e", nel_factor, h_max, err) # {{{ visualization if not visualize: continue from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, density_discr, target_order) normals = bind(places, sym.normal(places.ambient_dim).as_vector())(actx) error = actx.np.log10(actx.np.abs(jump_identity) + 1.0e-15) if relation == "nxcurls": nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density) nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density) jtxyz = bind(places, sym.tangential_to_xyz(density_sym))(actx, density=density) vis.write_vtk_file(f"source-nxcurls-{nel_factor:03d}.vtu", [ ("jt", jtxyz), ("nxcurlS_ext", nxcurlS_ext), ("nxcurlS_avg", nxcurlS_avg), ("bdry_normals", normals), ("error", error), ]) elif relation == "sp": op = sym.Sp(knl, density_sym, qbx_forced_limit=+1) sp_ext = bind(places, op)(actx, density=density) op = sym.Sp(knl, density_sym, qbx_forced_limit="avg") sp_avg = bind(places, op)(actx, density=density) vis.write_vtk_file(f"source-sp-{nel_factor:03d}.vtu", [ ("density", density), ("sp_ext", sp_ext), ("sp_avg", sp_avg), ("bdry_normals", normals), ("error", error), ]) elif relation == "div_s": vis.write_vtk_file(f"source-div-{nel_factor:03d}.vtu", [ ("density", density), ("bdry_normals", normals), ("error", error), ]) # }}} logger.info("\n%s", str(eoc_rec)) assert eoc_rec.order_estimate() >= qbx_order - 1.5
def test_single_plus_double_with_single_fmm(actx_factory, do_plot=False): logging.basicConfig(level=logging.INFO) actx = actx_factory() # prevent cache 'splosion from sympy.core.cache import clear_cache clear_cache() nelements = 300 target_order = 8 qbx_order = 3 mesh = mgen.make_curve_mesh(mgen.WobblyCircle.random(8, seed=30), 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)) direct_qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=False, target_association_tolerance=0.05, ) fmm_qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=qbx_order + 3, _expansions_in_tree_have_extent=True, target_association_tolerance=0.05, ) fplot = FieldPlotter(np.zeros(2), extent=5, npoints=500) from pytential.target import PointsTarget ptarget = PointsTarget(actx.freeze(actx.from_numpy(fplot.points))) from sumpy.kernel import LaplaceKernel places = GeometryCollection( { "direct_qbx": direct_qbx, "fmm_qbx": fmm_qbx, "target": ptarget, }, auto_where=("fmm_qbx", "target")) direct_density_discr = places.get_discretization("direct_qbx") fmm_density_discr = places.get_discretization("fmm_qbx") knl = LaplaceKernel(2) from pytential.qbx import QBXTargetAssociationFailedException op_d = sym.D(knl, sym.var("sigma"), qbx_forced_limit=None) op_s = sym.S(knl, sym.var("sigma"), qbx_forced_limit=None) op = op_d + op_s * 0.5 try: direct_sigma = direct_density_discr.zeros(actx) + 1 direct_fld_in_vol = bind(places, op, auto_where=("direct_qbx", "target"))(actx, sigma=direct_sigma) except QBXTargetAssociationFailedException as e: fplot.show_scalar_in_matplotlib( actx.to_numpy(actx.thaw(e.failed_target_flags))) import matplotlib.pyplot as pt pt.show() raise fmm_sigma = fmm_density_discr.zeros(actx) + 1 fmm_bound_op = bind(places, op, auto_where=("fmm_qbx", "target")) print(fmm_bound_op.code) fmm_fld_in_vol = fmm_bound_op(actx, sigma=fmm_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) if do_plot: fplot.write_vtk_file( "potential.vts", [("fmm_fld_in_vol", actx.to_numpy(fmm_fld_in_vol)), ("direct_fld_in_vol", actx.to_numpy(direct_fld_in_vol))]) assert linf_err < 1e-3 # check that using one FMM works op = op_d.copy(source_kernels=op_d.source_kernels + (knl, ), densities=op_d.densities + (sym.var("sigma") * 0.5, )) single_fmm_bound_op = bind(places, op, auto_where=("fmm_qbx", "target")) print(single_fmm_bound_op.code) single_fmm_fld_in_vol = fmm_bound_op(actx, sigma=fmm_sigma) err = actx.np.fabs(fmm_fld_in_vol - single_fmm_fld_in_vol) linf_err = actx.to_numpy(err).max() print("l_inf error:", linf_err) assert linf_err < 1e-15
def timing_run(nx, ny, visualize=False): import logging logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) mesh = make_mesh(nx=nx, ny=ny, visualize=visualize) density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) from pytential.qbx import (QBXLayerPotentialSource, QBXTargetAssociationFailedException) qbx = QBXLayerPotentialSource(density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=fmm_order) places = {"qbx": qbx} if visualize: from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1500) targets = PointsTarget(actx.from_numpy(fplot.points)) places.update({ "plot-targets": targets, "qbx-indicator": qbx.copy(target_association_tolerance=0.05, fmm_level_to_order=lambda lev: 7, qbx_order=2), "qbx-target-assoc": qbx.copy(target_association_tolerance=0.1) }) from pytential import GeometryCollection places = GeometryCollection(places, auto_where="qbx") density_discr = places.get_discretization("qbx") # {{{ describe bvp from sumpy.kernel import 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") S_sym = sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit=+1) D_sym = sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit="avg") bdry_op_sym = -loc_sign * 0.5 * sigma_sym + sqrt_w * (alpha * S_sym + D_sym) # }}} bound_op = bind(places, bdry_op_sym) # {{{ fix rhs and solve mode_nr = 3 from meshmode.dof_array import thaw nodes = thaw(actx, density_discr.nodes()) angle = actx.np.arctan2(nodes[1], nodes[0]) sigma = actx.np.cos(mode_nr * angle) # }}} # {{{ postprocess/visualize repr_kwargs = dict(k=sym.var("k"), qbx_forced_limit=+1) sym_op = sym.S(kernel, sym.var("sigma"), **repr_kwargs) bound_op = bind(places, sym_op) print("FMM WARM-UP RUN 1: %5d elements" % mesh.nelements) bound_op(actx, sigma=sigma, k=k) queue.finish() print("FMM WARM-UP RUN 2: %5d elements" % mesh.nelements) bound_op(actx, sigma=sigma, k=k) queue.finish() from time import time t_start = time() bound_op(actx, sigma=sigma, k=k) actx.queue.finish() elapsed = time() - t_start print("FMM TIMING RUN: %5d elements -> %g s" % (mesh.nelements, elapsed)) if visualize: ones_density = density_discr.zeros(queue) ones_density.fill(1) indicator = bind(places, sym_op, auto_where=("qbx-indicator", "plot-targets"))( queue, sigma=ones_density).get() try: fld_in_vol = bind(places, sym_op, auto_where=("qbx-target-assoc", "plot-targets"))(queue, sigma=sigma, k=k).get() except QBXTargetAssociationFailedException as e: fplot.write_vtk_file("scaling-study-failed-targets.vts", [ ("failed", e.failed_target_flags.get(queue)), ]) raise fplot.write_vtk_file("scaling-study-potential.vts", [ ("potential", fld_in_vol), ("indicator", indicator), ]) return (mesh.nelements, elapsed)
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)), ])
plot_targets = PointsTarget(fplot.points) places.update({ "qbx_target_tol": qbx.copy(target_association_tolerance=0.15), "plot_targets": plot_targets }) places = GeometryCollection(places, auto_where=case.name) if case.use_refinement: from pytential.qbx.refinement import refine_geometry_collection places = refine_geometry_collection(places, **refiner_extra_kwargs) dd = sym.as_dofdesc(case.name).to_stage1() density_discr = places.get_discretization(dd.geometry) logger.info("nelements: %d", density_discr.mesh.nelements) logger.info("ndofs: %d", density_discr.ndofs) if case.use_refinement: logger.info("%d elements before refinement", qbx.density_discr.mesh.nelements) discr = places.get_discretization(dd.geometry, sym.QBX_SOURCE_STAGE1) logger.info("%d stage-1 elements after refinement", discr.mesh.nelements) discr = places.get_discretization(dd.geometry, sym.QBX_SOURCE_STAGE2) logger.info("%d stage-2 elements after refinement", discr.mesh.nelements)
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_off_surface_eval_vs_direct(ctx_factory, do_plot=False): logging.basicConfig(level=logging.INFO) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache clear_cache() nelements = 300 target_order = 8 qbx_order = 3 mesh = make_curve_mesh(WobblyCircle.random(8, seed=30), 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)) direct_qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, qbx_order, fmm_order=False, target_association_tolerance=0.05, ) fmm_qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, qbx_order, fmm_order=qbx_order + 3, _expansions_in_tree_have_extent=True, target_association_tolerance=0.05, ) fplot = FieldPlotter(np.zeros(2), extent=5, npoints=500) from pytential.target import PointsTarget ptarget = PointsTarget(fplot.points) from sumpy.kernel import LaplaceKernel places = GeometryCollection({ "direct_qbx": direct_qbx, "fmm_qbx": fmm_qbx, "target": ptarget}) direct_density_discr = places.get_discretization("direct_qbx") fmm_density_discr = places.get_discretization("fmm_qbx") from pytential.qbx import QBXTargetAssociationFailedException op = sym.D(LaplaceKernel(2), sym.var("sigma"), qbx_forced_limit=None) try: direct_sigma = direct_density_discr.zeros(actx) + 1 direct_fld_in_vol = bind(places, op, auto_where=("direct_qbx", "target"))( actx, sigma=direct_sigma) except QBXTargetAssociationFailedException as e: fplot.show_scalar_in_matplotlib( actx.to_numpy(actx.thaw(e.failed_target_flags))) import matplotlib.pyplot as pt pt.show() raise fmm_sigma = fmm_density_discr.zeros(actx) + 1 fmm_fld_in_vol = bind(places, op, auto_where=("fmm_qbx", "target"))( actx, sigma=fmm_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) if do_plot: #fplot.show_scalar_in_mayavi(0.1*.get(queue)) fplot.write_vtk_file("potential.vts", [ ("fmm_fld_in_vol", actx.to_numpy(fmm_fld_in_vol)), ("direct_fld_in_vol", actx.to_numpy(direct_fld_in_vol)) ]) assert linf_err < 1e-3
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_3d_jump_relations(ctx_factory, relation, visualize=False): # logging.basicConfig(level=logging.INFO) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) if relation == "div_s": target_order = 3 else: target_order = 4 qbx_order = target_order from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_factor in [6, 10, 14]: from meshmode.mesh.generation import generate_torus mesh = generate_torus( 5, 2, order=target_order, n_major=2*nel_factor, n_minor=nel_factor) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(3)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource( pre_discr, fine_order=4*target_order, qbx_order=qbx_order, fmm_order=qbx_order + 5, fmm_backend="fmmlib" ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from sumpy.kernel import LaplaceKernel knl = LaplaceKernel(3) 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))) from meshmode.dof_array import thaw x, y, z = thaw(actx, density_discr.nodes()) m = actx.np if relation == "nxcurls": density_sym = sym.make_sym_vector("density", 2) jump_identity_sym = ( nxcurlS(+1) - (nxcurlS("avg") + 0.5*sym.tangential_to_xyz(density_sym))) # The tangential coordinate system is element-local, so we can't just # conjure up some globally smooth functions, interpret their values # in the tangential coordinate system, and be done. Instead, generate # an XYZ function and project it. density = bind(places, sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))( actx, jxyz=sym.make_obj_array([ m.cos(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z), m.sin(0.5*x) * m.cos(0.5*y) * m.sin(0.5*z), m.sin(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z), ])) elif relation == "sp": density = m.cos(2*x) * m.cos(2*y) * m.cos(z) density_sym = sym.var("density") jump_identity_sym = ( sym.Sp(knl, density_sym, qbx_forced_limit=+1) - (sym.Sp(knl, density_sym, qbx_forced_limit="avg") - 0.5*density_sym)) elif relation == "div_s": density = m.cos(2*x) * m.cos(2*y) * m.cos(z) density_sym = sym.var("density") jump_identity_sym = ( sym.div(sym.S(knl, sym.normal(3).as_vector()*density_sym, qbx_forced_limit="avg")) + sym.D(knl, density_sym, qbx_forced_limit="avg")) else: raise ValueError("unexpected value of 'relation': %s" % relation) bound_jump_identity = bind(places, jump_identity_sym) jump_identity = bound_jump_identity(actx, density=density) h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) err = ( norm(density_discr, jump_identity, np.inf) / norm(density_discr, density, np.inf)) print("ERROR", h_max, err) eoc_rec.add_data_point(h_max, err) # {{{ visualization if visualize and relation == "nxcurls": nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density) nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density) jtxyz = bind(places, sym.tangential_to_xyz(density_sym))( actx, density=density) from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) bdry_normals = bind(places, sym.normal(3))(actx)\ .as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ ("jt", jtxyz), ("nxcurlS_ext", nxcurlS_ext), ("nxcurlS_avg", nxcurlS_avg), ("bdry_normals", bdry_normals), ]) if visualize and relation == "sp": op = sym.Sp(knl, density_sym, qbx_forced_limit=+1) sp_ext = bind(places, op)(actx, density=density) op = sym.Sp(knl, density_sym, qbx_forced_limit="avg") sp_avg = bind(places, op)(actx, density=density) from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) bdry_normals = bind(places, sym.normal(3))(actx).as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ ("density", density), ("sp_ext", sp_ext), ("sp_avg", sp_avg), ("bdry_normals", bdry_normals), ]) # }}} print(eoc_rec) assert eoc_rec.order_estimate() >= qbx_order - 1.5