def operator(self, sigma): """ Returns the two second kind integral equations. """ rep = self.representation(sigma, qbx_forced_limit="avg") rep_diff = sym.normal_derivative(2, rep) int_eq1 = sigma[0] / 2 + rep int_eq2 = -sym.mean_curvature(2) * sigma[0] + sigma[1] / 2 + rep_diff return np.array([int_eq1, int_eq2])
def operator(self, sigma, **kwargs): """ :param u: symbolic variable for the density. :param kwargs: additional keyword arguments passed on to the layer potential constructor. :returns: the second kind integral operator for the clamped plate problem from [Farkas1990]_. """ rep = self.representation(sigma, qbx_forced_limit="avg", **kwargs) drep_dn = sym.normal_derivative(self.dim, rep) int_eq1 = sigma[0] / 2 + rep int_eq2 = -sym.mean_curvature(self.dim) * sigma[0] + sigma[1] / 2 + drep_dn return np.array([int_eq1, int_eq2])
def run_int_eq_test(cl_ctx, queue, case, resolution, visualize): mesh = case.get_mesh(resolution, case.target_order) print("%d elements" % mesh.nelements) from pytential.qbx import QBXLayerPotentialSource from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(case.target_order)) source_order = 4*case.target_order refiner_extra_kwargs = {} qbx_lpot_kwargs = {} if case.fmm_backend is None: qbx_lpot_kwargs["fmm_order"] = False else: if hasattr(case, "fmm_tol"): from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder qbx_lpot_kwargs["fmm_level_to_order"] = SimpleExpansionOrderFinder( case.fmm_tol) elif hasattr(case, "fmm_order"): qbx_lpot_kwargs["fmm_order"] = case.fmm_order else: qbx_lpot_kwargs["fmm_order"] = case.qbx_order + 5 qbx = QBXLayerPotentialSource( pre_density_discr, fine_order=source_order, qbx_order=case.qbx_order, _box_extent_norm=getattr(case, "box_extent_norm", None), _from_sep_smaller_crit=getattr(case, "from_sep_smaller_crit", None), _from_sep_smaller_min_nsources_cumul=30, fmm_backend=case.fmm_backend, **qbx_lpot_kwargs) if case.use_refinement: if case.k != 0 and getattr(case, "refine_on_helmholtz_k", True): refiner_extra_kwargs["kernel_length_scale"] = 5/case.k if hasattr(case, "scaled_max_curvature_threshold"): refiner_extra_kwargs["_scaled_max_curvature_threshold"] = \ case.scaled_max_curvature_threshold if hasattr(case, "expansion_disturbance_tolerance"): refiner_extra_kwargs["_expansion_disturbance_tolerance"] = \ case.expansion_disturbance_tolerance if hasattr(case, "refinement_maxiter"): refiner_extra_kwargs["maxiter"] = case.refinement_maxiter #refiner_extra_kwargs["visualize"] = True print("%d elements before refinement" % pre_density_discr.mesh.nelements) qbx, _ = qbx.with_refinement(**refiner_extra_kwargs) print("%d stage-1 elements after refinement" % qbx.density_discr.mesh.nelements) print("%d stage-2 elements after refinement" % qbx.stage2_density_discr.mesh.nelements) print("quad stage-2 elements have %d nodes" % qbx.quad_stage2_density_discr.groups[0].nunit_nodes) density_discr = qbx.density_discr if hasattr(case, "visualize_geometry") and case.visualize_geometry: bdry_normals = bind( density_discr, sym.normal(mesh.ambient_dim) )(queue).as_vector(dtype=object) bdry_vis = make_visualizer(queue, density_discr, case.target_order) bdry_vis.write_vtk_file("geometry.vtu", [ ("normals", bdry_normals) ]) # {{{ plot geometry if 0: if mesh.ambient_dim == 2: # show geometry, centers, normals nodes_h = density_discr.nodes().get(queue=queue) pt.plot(nodes_h[0], nodes_h[1], "x-") normal = bind(density_discr, sym.normal(2))(queue).as_vector(np.object) pt.quiver(nodes_h[0], nodes_h[1], normal[0].get(queue), normal[1].get(queue)) pt.gca().set_aspect("equal") pt.show() elif mesh.ambient_dim == 3: bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) bdry_normals = bind(density_discr, sym.normal(3))(queue)\ .as_vector(dtype=object) bdry_vis.write_vtk_file("pre-solve-source-%s.vtu" % resolution, [ ("bdry_normals", bdry_normals), ]) else: raise ValueError("invalid mesh dim") # }}} # {{{ set up operator from pytential.symbolic.pde.scalar import ( DirichletOperator, NeumannOperator) from sumpy.kernel import LaplaceKernel, HelmholtzKernel if case.k: knl = HelmholtzKernel(mesh.ambient_dim) knl_kwargs = {"k": sym.var("k")} concrete_knl_kwargs = {"k": case.k} else: knl = LaplaceKernel(mesh.ambient_dim) knl_kwargs = {} concrete_knl_kwargs = {} if knl.is_complex_valued: dtype = np.complex128 else: dtype = np.float64 loc_sign = +1 if case.prob_side in [+1, "scat"] else -1 if case.bc_type == "dirichlet": op = DirichletOperator(knl, loc_sign, use_l2_weighting=True, kernel_arguments=knl_kwargs) elif case.bc_type == "neumann": op = NeumannOperator(knl, loc_sign, use_l2_weighting=True, use_improved_operator=False, kernel_arguments=knl_kwargs) else: assert False op_u = op.operator(sym.var("u")) # }}} # {{{ set up test data if case.prob_side == -1: test_src_geo_radius = case.outer_radius test_tgt_geo_radius = case.inner_radius elif case.prob_side == +1: test_src_geo_radius = case.inner_radius test_tgt_geo_radius = case.outer_radius elif case.prob_side == "scat": test_src_geo_radius = case.outer_radius test_tgt_geo_radius = case.outer_radius else: raise ValueError("unknown problem_side") point_sources = make_circular_point_group( mesh.ambient_dim, 10, test_src_geo_radius, func=lambda x: x**1.5) test_targets = make_circular_point_group( mesh.ambient_dim, 20, test_tgt_geo_radius) np.random.seed(22) source_charges = np.random.randn(point_sources.shape[1]) source_charges[-1] = -np.sum(source_charges[:-1]) source_charges = source_charges.astype(dtype) assert np.sum(source_charges) < 1e-15 source_charges_dev = cl.array.to_device(queue, source_charges) # }}} # {{{ establish BCs from pytential.source import PointPotentialSource from pytential.target import PointsTarget point_source = PointPotentialSource(cl_ctx, point_sources) pot_src = sym.IntG( # FIXME: qbx_forced_limit--really? knl, sym.var("charges"), qbx_forced_limit=None, **knl_kwargs) test_direct = bind((point_source, PointsTarget(test_targets)), pot_src)( queue, charges=source_charges_dev, **concrete_knl_kwargs) if case.bc_type == "dirichlet": bc = bind((point_source, density_discr), pot_src)( queue, charges=source_charges_dev, **concrete_knl_kwargs) elif case.bc_type == "neumann": bc = bind( (point_source, density_discr), sym.normal_derivative( qbx.ambient_dim, pot_src, where=sym.DEFAULT_TARGET) )(queue, charges=source_charges_dev, **concrete_knl_kwargs) # }}} # {{{ solve bound_op = bind(qbx, op_u) rhs = bind(density_discr, op.prepare_rhs(sym.var("bc")))(queue, bc=bc) try: from pytential.solve import gmres gmres_result = gmres( bound_op.scipy_op(queue, "u", dtype, **concrete_knl_kwargs), rhs, tol=case.gmres_tol, progress=True, hard_failure=True, stall_iterations=50, no_progress_factor=1.05) except QBXTargetAssociationFailedException as e: bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) bdry_vis.write_vtk_file("failed-targets-%s.vtu" % resolution, [ ("failed_targets", e.failed_target_flags), ]) raise print("gmres state:", gmres_result.state) weighted_u = gmres_result.solution # }}} # {{{ build matrix for spectrum check if 0: from sumpy.tools import build_matrix mat = build_matrix( bound_op.scipy_op( queue, arg_name="u", dtype=dtype, k=case.k)) w, v = la.eig(mat) if 0: pt.imshow(np.log10(1e-20+np.abs(mat))) pt.colorbar() pt.show() #assert abs(s[-1]) < 1e-13, "h #assert abs(s[-2]) > 1e-7 #from pudb import set_trace; set_trace() # }}} if case.prob_side != "scat": # {{{ error check points_target = PointsTarget(test_targets) bound_tgt_op = bind((qbx, points_target), op.representation(sym.var("u"))) test_via_bdry = bound_tgt_op(queue, u=weighted_u, k=case.k) err = test_via_bdry - test_direct err = err.get() test_direct = test_direct.get() test_via_bdry = test_via_bdry.get() # {{{ remove effect of net source charge if case.k == 0 and case.bc_type == "neumann" and loc_sign == -1: # remove constant offset in interior Laplace Neumann error tgt_ones = np.ones_like(test_direct) tgt_ones = tgt_ones/la.norm(tgt_ones) err = err - np.vdot(tgt_ones, err)*tgt_ones # }}} rel_err_2 = la.norm(err)/la.norm(test_direct) rel_err_inf = la.norm(err, np.inf)/la.norm(test_direct, np.inf) # }}} print("rel_err_2: %g rel_err_inf: %g" % (rel_err_2, rel_err_inf)) else: rel_err_2 = None rel_err_inf = None # {{{ test gradient if case.check_gradient and case.prob_side != "scat": bound_grad_op = bind((qbx, points_target), op.representation( sym.var("u"), map_potentials=lambda pot: sym.grad(mesh.ambient_dim, pot), qbx_forced_limit=None)) #print(bound_t_deriv_op.code) grad_from_src = bound_grad_op( queue, u=weighted_u, **concrete_knl_kwargs) grad_ref = (bind( (point_source, points_target), sym.grad(mesh.ambient_dim, pot_src) )(queue, charges=source_charges_dev, **concrete_knl_kwargs) ) grad_err = (grad_from_src - grad_ref) rel_grad_err_inf = ( la.norm(grad_err[0].get(), np.inf) / la.norm(grad_ref[0].get(), np.inf)) print("rel_grad_err_inf: %g" % rel_grad_err_inf) # }}} # {{{ test tangential derivative if case.check_tangential_deriv and case.prob_side != "scat": bound_t_deriv_op = bind(qbx, op.representation( sym.var("u"), map_potentials=lambda pot: sym.tangential_derivative(2, pot), qbx_forced_limit=loc_sign)) #print(bound_t_deriv_op.code) tang_deriv_from_src = bound_t_deriv_op( queue, u=weighted_u, **concrete_knl_kwargs).as_scalar().get() tang_deriv_ref = (bind( (point_source, density_discr), sym.tangential_derivative(2, pot_src) )(queue, charges=source_charges_dev, **concrete_knl_kwargs) .as_scalar().get()) if 0: pt.plot(tang_deriv_ref.real) pt.plot(tang_deriv_from_src.real) pt.show() td_err = (tang_deriv_from_src - tang_deriv_ref) rel_td_err_inf = la.norm(td_err, np.inf)/la.norm(tang_deriv_ref, np.inf) print("rel_td_err_inf: %g" % rel_td_err_inf) else: rel_td_err_inf = None # }}} # {{{ any-D file plotting if visualize: bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) bdry_normals = bind(density_discr, sym.normal(qbx.ambient_dim))(queue)\ .as_vector(dtype=object) sym_sqrt_j = sym.sqrt_jac_q_weight(density_discr.ambient_dim) u = bind(density_discr, sym.var("u")/sym_sqrt_j)(queue, u=weighted_u) bdry_vis.write_vtk_file("source-%s.vtu" % resolution, [ ("u", u), ("bc", bc), #("bdry_normals", bdry_normals), ]) from sumpy.visualization import make_field_plotter_from_bbox # noqa from meshmode.mesh.processing import find_bounding_box vis_grid_spacing = (0.1, 0.1, 0.1)[:qbx.ambient_dim] if hasattr(case, "vis_grid_spacing"): vis_grid_spacing = case.vis_grid_spacing vis_extend_factor = 0.2 if hasattr(case, "vis_extend_factor"): vis_grid_spacing = case.vis_grid_spacing fplot = make_field_plotter_from_bbox( find_bounding_box(mesh), h=vis_grid_spacing, extend_factor=vis_extend_factor) qbx_tgt_tol = qbx.copy(target_association_tolerance=0.15) from pytential.target import PointsTarget try: solved_pot = bind( (qbx_tgt_tol, PointsTarget(fplot.points)), op.representation(sym.var("u")) )(queue, u=weighted_u, k=case.k) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file( "failed-targets.vts", [ ("failed_targets", e.failed_target_flags.get(queue)) ]) raise from sumpy.kernel import LaplaceKernel ones_density = density_discr.zeros(queue) ones_density.fill(1) indicator = bind( (qbx_tgt_tol, PointsTarget(fplot.points)), -sym.D(LaplaceKernel(density_discr.ambient_dim), sym.var("sigma"), qbx_forced_limit=None))( queue, sigma=ones_density).get() solved_pot = solved_pot.get() true_pot = bind((point_source, PointsTarget(fplot.points)), pot_src)( queue, charges=source_charges_dev, **concrete_knl_kwargs).get() #fplot.show_scalar_in_mayavi(solved_pot.real, max_val=5) if case.prob_side == "scat": fplot.write_vtk_file( "potential-%s.vts" % resolution, [ ("pot_scattered", solved_pot), ("pot_incoming", -true_pot), ("indicator", indicator), ] ) else: fplot.write_vtk_file( "potential-%s.vts" % resolution, [ ("solved_pot", solved_pot), ("true_pot", true_pot), ("indicator", indicator), ] ) # }}} class Result(Record): pass return Result( h_max=qbx.h_max, rel_err_2=rel_err_2, rel_err_inf=rel_err_inf, rel_td_err_inf=rel_td_err_inf, gmres_result=gmres_result)
test_direct = bind((point_source, PointsTarget(test_targets)), pot_src)(queue, charges=source_charges_dev, **concrete_knl_kwargs) if case.bc_type == "dirichlet": bc = bind((point_source, density_discr), pot_src)(queue, charges=source_charges_dev, **concrete_knl_kwargs) elif case.bc_type == "neumann": bc = bind((point_source, density_discr), sym.normal_derivative(qbx.ambient_dim, pot_src, dofdesc=sym.DEFAULT_TARGET))( queue, charges=source_charges_dev, **concrete_knl_kwargs) # }}} # {{{ solve bound_op = bind(qbx, op_u) rhs = bind(density_discr, op.prepare_rhs(sym.var("bc")))(queue, bc=bc) try: from pytential.solve import gmres gmres_result = gmres(bound_op.scipy_op(queue, "u", dtype,
def bc_term_to_operator_contrib(self, term, side, raw_potential_op, density, discrete): potential_op = raw_potential_op side_sign = self.side_to_sign[side] domain_outer, domain_inner, interface_id = \ self.interfaces[term.i_interface] if side == self.side_in: K_expr = self.domain_K_exprs[domain_inner] # noqa bc_coeff = term.coeff_inner elif side == self.side_out: K_expr = self.domain_K_exprs[domain_outer] # noqa bc_coeff = term.coeff_outer else: raise ValueError("invalid value of 'side'") potential_op = potential_op(self.kernel, density, source=interface_id, k=K_expr) if term.direction == self.dir_none: if raw_potential_op is sym.S: jump_term = 0 elif raw_potential_op is sym.D: jump_term = (side_sign * 0.5) * discrete else: assert False, raw_potential_op elif term.direction == self.dir_normal: potential_op = sym.normal_derivative(potential_op, interface_id) if raw_potential_op is sym.S: # S' jump_term = (-side_sign * 0.5) * discrete elif raw_potential_op is sym.D: jump_term = 0 else: assert False, raw_potential_op elif term.direction == self.dir_tangential: potential_op = sym.tangential_derivative( raw_potential_op(self.kernel, density, source=interface_id, k=K_expr, qbx_forced_limit=side_sign), interface_id).a.as_scalar() # Some of these may have jumps, but QBX does the dirty # work here by directly computing the limit. jump_term = 0 else: raise ValueError("invalid direction") potential_op = (jump_term + self.get_sqrt_weight(interface_id) * potential_op) del jump_term contrib = bc_coeff * potential_op if (raw_potential_op is sym.D and term.direction == self.dir_normal): # FIXME The hypersingular part should perhaps be # treated specially to avoid cancellation. pass return contrib
def Sn(dom, density): # noqa return sym.normal_derivative( 2, S(dom, density, qbx_forced_limit="avg"))
auto_where=("point_source", "point_target"))(actx, charges=source_charges_dev, **case.knl_concrete_kwargs) if case.bc_type == "dirichlet": bc = bind(places, pot_src, auto_where=("point_source", case.name))(actx, charges=source_charges_dev, **case.knl_concrete_kwargs) elif case.bc_type == "neumann": bc = bind(places, sym.normal_derivative(ambient_dim, pot_src, dofdesc=case.name), auto_where=("point_source", case.name))(actx, charges=source_charges_dev, **case.knl_concrete_kwargs) elif case.bc_type == "clamped_plate": bc_u = bind(places, pot_src, auto_where=("point_source", case.name))(actx, charges=source_charges_dev, **case.knl_concrete_kwargs) bc_du = bind(places, sym.normal_derivative(ambient_dim, pot_src,
def get_bvp_error(lpot_source, fmm_order, qbx_order, k=0): # This returns a tuple (err_l2, err_linf, nit). queue = cl.CommandQueue(lpot_source.cl_context) lpot_source = lpot_source.copy( qbx_order=qbx_order, fmm_level_to_order=(False if fmm_order is False else lambda *args: fmm_order)) d = lpot_source.ambient_dim assert k == 0 # Helmholtz would require a different representation from sumpy.kernel import LaplaceKernel, HelmholtzKernel lap_k_sym = LaplaceKernel(d) if k == 0: k_sym = lap_k_sym knl_kwargs = {} else: k_sym = HelmholtzKernel(d) knl_kwargs = {"k": sym.var("k")} density_discr = lpot_source.density_discr # {{{ find source and target points source_angles = (np.pi / 2 + np.linspace(0, 2 * np.pi * BVP_EXPERIMENT_N_ARMS, BVP_EXPERIMENT_N_ARMS, endpoint=False)) / BVP_EXPERIMENT_N_ARMS source_points = 0.75 * np.array([ np.cos(source_angles), np.sin(source_angles), ]) target_angles = (np.pi + np.pi / 2 + np.linspace(0, 2 * np.pi * BVP_EXPERIMENT_N_ARMS, BVP_EXPERIMENT_N_ARMS, endpoint=False)) / BVP_EXPERIMENT_N_ARMS target_points = 1.5 * np.array([ np.cos(target_angles), np.sin(target_angles), ]) np.random.seed(17) source_charges = np.random.randn(BVP_EXPERIMENT_N_ARMS) source_points_dev = cl.array.to_device(queue, source_points) target_points_dev = cl.array.to_device(queue, target_points) source_charges_dev = cl.array.to_device(queue, source_charges) from pytential.source import PointPotentialSource from pytential.target import PointsTarget point_source = PointPotentialSource(lpot_source.cl_context, source_points_dev) pot_src = sym.IntG( # FIXME: qbx_forced_limit--really? k_sym, sym.var("charges"), qbx_forced_limit=None, **knl_kwargs) ref_direct = bind((point_source, PointsTarget(target_points_dev)), pot_src)(queue, charges=source_charges_dev, **knl_kwargs).get() sym_sqrt_j = sym.sqrt_jac_q_weight(density_discr.ambient_dim) bc = bind((point_source, density_discr), sym.normal_derivative(density_discr.ambient_dim, pot_src, where=sym.DEFAULT_TARGET))( queue, charges=source_charges_dev, **knl_kwargs) rhs = bind(density_discr, sym.var("bc") * sym_sqrt_j)(queue, bc=bc) # }}} # {{{ solve bound_op = bind( lpot_source, -0.5 * sym.var("u") + sym_sqrt_j * sym.Sp(k_sym, sym.var("u") / sym_sqrt_j, qbx_forced_limit="avg", **knl_kwargs)) from pytential.solve import gmres gmres_result = gmres(bound_op.scipy_op(queue, "u", np.float64, **knl_kwargs), rhs, tol=1e-10, stall_iterations=100, progress=True, hard_failure=True) u = gmres_result.solution # }}} points_target = PointsTarget(target_points_dev) bound_tgt_op = bind((lpot_source, points_target), sym.S(k_sym, sym.var("u") / sym_sqrt_j, qbx_forced_limit=None)) test_via_bdry = bound_tgt_op(queue, u=u).get() err = ref_direct - test_via_bdry err_l2 = la.norm(err, 2) / la.norm(ref_direct, 2) err_linf = la.norm(err, np.inf) / la.norm(ref_direct, np.inf) return err_l2, err_linf, gmres_result.iteration_count
def bc_term_to_operator_contrib(self, term, side, raw_potential_op, density, discrete): potential_op = raw_potential_op side_sign = self.side_to_sign[side] domain_outer, domain_inner, interface_id = \ self.interfaces[term.i_interface] if side == self.side_in: K_expr = self.domain_K_exprs[domain_inner] # noqa bc_coeff = term.coeff_inner elif side == self.side_out: K_expr = self.domain_K_exprs[domain_outer] # noqa bc_coeff = term.coeff_outer else: raise ValueError("invalid value of 'side'") potential_op = potential_op( self.kernel, density, source=interface_id, k=K_expr) if term.direction == self.dir_none: if raw_potential_op is sym.S: jump_term = 0 elif raw_potential_op is sym.D: jump_term = (side_sign*0.5) * discrete else: assert False, raw_potential_op elif term.direction == self.dir_normal: potential_op = sym.normal_derivative( potential_op, interface_id) if raw_potential_op is sym.S: # S' jump_term = (-side_sign*0.5) * discrete elif raw_potential_op is sym.D: jump_term = 0 else: assert False, raw_potential_op elif term.direction == self.dir_tangential: potential_op = sym.tangential_derivative( raw_potential_op( self.kernel, density, source=interface_id, k=K_expr, qbx_forced_limit=side_sign), interface_id).a.as_scalar() # Some of these may have jumps, but QBX does the dirty # work here by directly computing the limit. jump_term = 0 else: raise ValueError("invalid direction") potential_op = ( jump_term + self.get_sqrt_weight(interface_id)*potential_op) del jump_term contrib = bc_coeff * potential_op if (raw_potential_op is sym.D and term.direction == self.dir_normal): # FIXME The hypersingular part should perhaps be # treated specially to avoid cancellation. pass return contrib
test_direct = bind((point_source, PointsTarget(test_targets)), pot_src)(queue, charges=source_charges_dev, **concrete_knl_kwargs) if case.bc_type == "dirichlet": bc = bind((point_source, density_discr), pot_src)(queue, charges=source_charges_dev, **concrete_knl_kwargs) elif case.bc_type == "neumann": bc = bind((point_source, density_discr), sym.normal_derivative(qbx.ambient_dim, pot_src, where=sym.DEFAULT_TARGET))( queue, charges=source_charges_dev, **concrete_knl_kwargs) # }}} # {{{ solve bound_op = bind(qbx, op_u) rhs = bind(density_discr, op.prepare_rhs(sym.var("bc")))(queue, bc=bc) try: from pytential.solve import gmres