def get_extension_bie_symbolic_operator(loc_sign=1): """ loc_sign: -1 for interior Dirichlet +1 for exterior Dirichlet """ logger = logging.getLogger("SETUP") logger.info(locals()) dim = 2 cse = sym.cse sigma_sym = sym.make_sym_vector("sigma", dim) int_sigma = sym.Ones() * sym.integral(2, 1, sigma_sym) nvec_sym = sym.make_sym_vector("normal", dim) mu_sym = sym.var("mu") stresslet_obj = StressletWrapper(dim=dim) stokeslet_obj = StokesletWrapper(dim=dim) bdry_op_sym = ( loc_sign * 0.5 * sigma_sym - stresslet_obj.apply( sigma_sym, nvec_sym, mu_sym, qbx_forced_limit='avg') - stokeslet_obj.apply(sigma_sym, mu_sym, qbx_forced_limit='avg') + int_sigma) return bdry_op_sym
def _operator(self, sigma, normal, mu, qbx_forced_limit): slp_qbx_forced_limit = qbx_forced_limit if slp_qbx_forced_limit == "avg": slp_qbx_forced_limit = +1 # NOTE: we set a dofdesc here to force the evaluation of this integral # on the source instead of the target when using automatic tagging # see :meth:`pytential.symbolic.mappers.LocationTagger._default_dofdesc` dd = sym.DOFDescriptor(None, discr_stage=sym.QBX_SOURCE_STAGE1) int_sigma = sym.integral(self.ambient_dim, self.dim, sigma, dofdesc=dd) meanless_sigma = sym.cse(sigma - sym.mean(self.ambient_dim, self.dim, sigma)) op_k = self.stresslet.apply(sigma, normal, mu, qbx_forced_limit=qbx_forced_limit) op_s = ( self.alpha / (2.0 * np.pi) * int_sigma - self.stokeslet.apply(meanless_sigma, mu, qbx_forced_limit=slp_qbx_forced_limit) ) return op_k + self.eta * op_s
def _farfield(self, mu, qbx_forced_limit): length = sym.integral(self.ambient_dim, self.dim, 1) return self.stokeslet.apply( -self.omega / length, mu, qbx_forced_limit=qbx_forced_limit)
def run_exterior_stokes_2d(ctx_factory, nelements, mesh_order=4, target_order=4, qbx_order=4, fmm_order=10, mu=1, circle_rad=1.5, do_plot=False): # This program tests an exterior Stokes flow in 2D using the # compound representation given in Hsiao & Kress, # ``On an integral equation for the two-dimensional exterior Stokes problem,'' # Applied Numerical Mathematics 1 (1985). logging.basicConfig(level=logging.INFO) cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) ovsmp_target_order = 4 * target_order from meshmode.mesh.generation import ( # noqa make_curve_mesh, starfish, ellipse, drop) mesh = make_curve_mesh(lambda t: circle_rad * ellipse(1, t), np.linspace(0, 1, nelements + 1), target_order) coarse_density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource target_association_tolerance = 0.05 qbx, _ = QBXLayerPotentialSource( coarse_density_discr, fine_order=ovsmp_target_order, qbx_order=qbx_order, fmm_order=fmm_order, target_association_tolerance=target_association_tolerance, _expansions_in_tree_have_extent=True, ).with_refinement() density_discr = qbx.density_discr normal = bind(density_discr, sym.normal(2).as_vector())(queue) path_length = bind(density_discr, sym.integral(2, 1, 1))(queue) # {{{ describe bvp from pytential.symbolic.stokes import StressletWrapper, StokesletWrapper dim = 2 cse = sym.cse sigma_sym = sym.make_sym_vector("sigma", dim) meanless_sigma_sym = cse(sigma_sym - sym.mean(2, 1, sigma_sym)) int_sigma = sym.Ones() * sym.integral(2, 1, sigma_sym) nvec_sym = sym.make_sym_vector("normal", dim) mu_sym = sym.var("mu") # -1 for interior Dirichlet # +1 for exterior Dirichlet loc_sign = 1 stresslet_obj = StressletWrapper(dim=2) stokeslet_obj = StokesletWrapper(dim=2) bdry_op_sym = (-loc_sign * 0.5 * sigma_sym - stresslet_obj.apply( sigma_sym, nvec_sym, mu_sym, qbx_forced_limit='avg') + stokeslet_obj.apply( meanless_sigma_sym, mu_sym, qbx_forced_limit='avg') - (0.5 / np.pi) * int_sigma) # }}} bound_op = bind(qbx, bdry_op_sym) # {{{ fix rhs and solve def fund_soln(x, y, loc, strength): #with direction (1,0) for point source r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) scaling = strength / (4 * np.pi * mu) xcomp = (-cl.clmath.log(r) + (x - loc[0])**2 / r**2) * scaling ycomp = ((x - loc[0]) * (y - loc[1]) / r**2) * scaling return [xcomp, ycomp] def rotlet_soln(x, y, loc): r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) xcomp = -(y - loc[1]) / r**2 ycomp = (x - loc[0]) / r**2 return [xcomp, ycomp] def fund_and_rot_soln(x, y, loc, strength): #with direction (1,0) for point source r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) scaling = strength / (4 * np.pi * mu) xcomp = ((-cl.clmath.log(r) + (x - loc[0])**2 / r**2) * scaling - (y - loc[1]) * strength * 0.125 / r**2 + 3.3) ycomp = (((x - loc[0]) * (y - loc[1]) / r**2) * scaling + (x - loc[0]) * strength * 0.125 / r**2 + 1.5) return [xcomp, ycomp] nodes = density_discr.nodes().with_queue(queue) fund_soln_loc = np.array([0.5, -0.2]) strength = 100. bc = fund_and_rot_soln(nodes[0], nodes[1], fund_soln_loc, strength) omega_sym = sym.make_sym_vector("omega", dim) u_A_sym_bdry = stokeslet_obj.apply( # noqa: N806 omega_sym, mu_sym, qbx_forced_limit=1) omega = [ cl.array.to_device(queue, (strength / path_length) * np.ones(len(nodes[0]))), cl.array.to_device(queue, np.zeros(len(nodes[0]))) ] bvp_rhs = bind(qbx, sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(queue, bc=bc, mu=mu, omega=omega) gmres_result = gmres(bound_op.scipy_op(queue, "sigma", np.float64, mu=mu, normal=normal), bvp_rhs, x0=bvp_rhs, tol=1e-9, progress=True, stall_iterations=0, hard_failure=True) # }}} # {{{ postprocess/visualize sigma = gmres_result.solution sigma_int_val_sym = sym.make_sym_vector("sigma_int_val", 2) int_val = bind(qbx, sym.integral(2, 1, sigma_sym))(queue, sigma=sigma) int_val = -int_val / (2 * np.pi) print("int_val = ", int_val) u_A_sym_vol = stokeslet_obj.apply( # noqa: N806 omega_sym, mu_sym, qbx_forced_limit=2) representation_sym = ( -stresslet_obj.apply(sigma_sym, nvec_sym, mu_sym, qbx_forced_limit=2) + stokeslet_obj.apply(meanless_sigma_sym, mu_sym, qbx_forced_limit=2) - u_A_sym_vol + sigma_int_val_sym) nsamp = 30 eval_points_1d = np.linspace(-3., 3., nsamp) eval_points = np.zeros((2, len(eval_points_1d)**2)) eval_points[0, :] = np.tile(eval_points_1d, len(eval_points_1d)) eval_points[1, :] = np.repeat(eval_points_1d, len(eval_points_1d)) def circle_mask(test_points, radius): return (test_points[0, :]**2 + test_points[1, :]**2 > radius**2) def outside_circle(test_points, radius): mask = circle_mask(test_points, radius) return np.array([row[mask] for row in test_points]) eval_points = outside_circle(eval_points, radius=circle_rad) from pytential.target import PointsTarget vel = bind((qbx, PointsTarget(eval_points)), representation_sym)(queue, sigma=sigma, mu=mu, normal=normal, sigma_int_val=int_val, omega=omega) print("@@@@@@@@") fplot = FieldPlotter(np.zeros(2), extent=6, npoints=100) plot_pts = outside_circle(fplot.points, radius=circle_rad) plot_vel = bind((qbx, PointsTarget(plot_pts)), representation_sym)(queue, sigma=sigma, mu=mu, normal=normal, sigma_int_val=int_val, omega=omega) def get_obj_array(obj_array): return make_obj_array([ary.get() for ary in obj_array]) exact_soln = fund_and_rot_soln(cl.array.to_device(queue, eval_points[0]), cl.array.to_device(queue, eval_points[1]), fund_soln_loc, strength) vel = get_obj_array(vel) err = vel - get_obj_array(exact_soln) # FIXME: Pointwise relative errors don't make sense! rel_err = err / (get_obj_array(exact_soln)) if 0: print("@@@@@@@@") print("vel[0], err[0], rel_err[0] ***** vel[1], err[1], rel_err[1]: ") for i in range(len(vel[0])): print("%15.8e, %15.8e, %15.8e ***** %15.8e, %15.8e, %15.8e\n" % (vel[0][i], err[0][i], rel_err[0][i], vel[1][i], err[1][i], rel_err[1][i])) print("@@@@@@@@") l2_err = np.sqrt((6. / (nsamp - 1))**2 * np.sum(err[0] * err[0]) + (6. / (nsamp - 1))**2 * np.sum(err[1] * err[1])) l2_rel_err = np.sqrt((6. / (nsamp - 1))**2 * np.sum(rel_err[0] * rel_err[0]) + (6. / (nsamp - 1))**2 * np.sum(rel_err[1] * rel_err[1])) print("L2 error estimate: ", l2_err) print("L2 rel error estimate: ", l2_rel_err) print("max error at sampled points: ", max(abs(err[0])), max(abs(err[1]))) print("max rel error at sampled points: ", max(abs(rel_err[0])), max(abs(rel_err[1]))) if do_plot: import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt full_pot = np.zeros_like(fplot.points) * float("nan") mask = circle_mask(fplot.points, radius=circle_rad) for i, vel in enumerate(plot_vel): full_pot[i, mask] = vel.get() plt.quiver(fplot.points[0], fplot.points[1], full_pot[0], full_pot[1], linewidth=0.1) plt.savefig("exterior-2d-field.pdf") # }}} h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) return h_max, l2_err
def Wr(operand: sym.Expression) -> sym.Expression: # noqa: N802 return sym.Ones() * sym.integral(self.ambient_dim, self.dim, operand)
def build_kernel_exterior_normalizer_table(self, cl_ctx, queue, pool=None, ncpus=None, mesh_order=5, quad_order=10, mesh_size=0.03, remove_tmp_files=True, **kwargs): r"""Build the kernel exterior normalizer table for fractional Laplacians. An exterior normalizer for kernel :math:`G(r)` and target :math:`x` is defined as .. math:: \int_{B^c} G(\lVert x - y \rVert) dy where :math:`B` is the source box :math:`[0, source_box_extent]^dim`. """ logger.warn("this method is currently under construction.") if not self.inverse_droste: raise ValueError() if ncpus is None: import multiprocessing ncpus = multiprocessing.cpu_count() if pool is None: from multiprocessing import Pool pool = Pool(ncpus) def fl_scaling(k, s): # scaling constant from scipy.special import gamma return (2**(2 * s) * s * gamma(s + k / 2)) / (np.pi**(k / 2) * gamma(1 - s)) # Directly compute and return in 1D if self.dim == 1: s = self.integral_knl.s targets = np.array(self.q_points).reshape(-1) r1 = targets r2 = self.source_box_extent - targets self.kernel_exterior_normalizers = 1 / (2 * s) * ( 1 / r1**(2 * s) + 1 / r2**(2 * s)) * fl_scaling(k=self.dim, s=s) return from meshmode.array_context import PyOpenCLArrayContext from meshmode.dof_array import thaw, flatten from meshmode.mesh.io import read_gmsh from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory # {{{ gmsh processing import gmsh gmsh.initialize() gmsh.option.setNumber("General.Terminal", 1) # meshmode does not support other versions gmsh.option.setNumber("Mesh.MshFileVersion", 2) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", mesh_size) gmsh.option.setNumber("Mesh.ElementOrder", mesh_order) if mesh_order > 1: gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 1) # radius of source box hs = self.source_box_extent / 2 # radius of bouding sphere r = hs * np.sqrt(self.dim) logger.debug("r_inner = %f, r_outer = %f" % (hs, r)) if self.dim == 2: tag_box = gmsh.model.occ.addRectangle(x=0, y=0, z=0, dx=2 * hs, dy=2 * hs, tag=-1) elif self.dim == 3: tag_box = gmsh.model.occ.addBox(x=0, y=0, z=0, dx=2 * hs, dy=2 * hs, dz=2 * hs, tag=-1) else: raise NotImplementedError() if self.dim == 2: tag_ball = gmsh.model.occ.addDisk(xc=hs, yc=hs, zc=0, rx=r, ry=r, tag=-1) elif self.dim == 3: tag_sphere = gmsh.model.occ.addSphere(xc=hs, yc=hs, zc=hs, radius=r, tag=-1) tag_ball = gmsh.model.occ.addVolume([tag_sphere], tag=-1) else: raise NotImplementedError() dimtags_ints, dimtags_map_ints = gmsh.model.occ.cut( objectDimTags=[(self.dim, tag_ball)], toolDimTags=[(self.dim, tag_box)], tag=-1, removeObject=True, removeTool=True) gmsh.model.occ.synchronize() gmsh.model.mesh.generate(self.dim) from tempfile import mkdtemp from os.path import join temp_dir = mkdtemp(prefix="tmp_volumential_nft") msh_filename = join(temp_dir, 'chinese_lucky_coin.msh') gmsh.write(msh_filename) gmsh.finalize() mesh = read_gmsh(msh_filename) if remove_tmp_files: import shutil shutil.rmtree(temp_dir) # }}} End gmsh processing arr_ctx = PyOpenCLArrayContext(queue) discr = Discretization( arr_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order=quad_order)) from pytential import bind, sym # {{{ optional checks if 1: if self.dim == 2: arerr = np.abs((np.pi * r**2 - (2 * hs)**2) - bind(discr, sym.integral(self.dim, self.dim, 1)) (queue)) / (np.pi * r**2 - (2 * hs)**2) if arerr > 1e-12: log_to = logger.warn else: log_to = logger.debug log_to("the numerical error when computing the measure of a " "unit ball is %e" % arerr) elif self.dim == 3: arerr = np.abs((4 / 3 * np.pi * r**3 - (2 * hs)**3) - bind(discr, sym.integral(self.dim, self.dim, 1)) (queue)) / (4 / 3 * np.pi * r**3 - (2 * hs)**3) if arerr > 1e-12: log_to = logger.warn else: log_to = logger.debug logger.warn( "The numerical error when computing the measure of a " "unit ball is %e" % arerr) # }}} End optional checks # {{{ kernel evaluation # TODO: take advantage of symmetry if this is too slow from volumential.droste import InverseDrosteReduced # only for getting kernel evaluation related stuff drf = InverseDrosteReduced(self.integral_knl, self.quad_order, self.interaction_case_vecs, n_brick_quad_points=0, knl_symmetry_tags=[], auto_windowing=False) # uses "dist[dim]", assigned to "knl_val" knl_insns = drf.get_sumpy_kernel_insns() eval_kernel_insns = [ insn.copy(within_inames=insn.within_inames | frozenset(["iqpt"])) for insn in knl_insns ] from sumpy.symbolic import SympyToPymbolicMapper sympy_conv = SympyToPymbolicMapper() scaling_assignment = lp.Assignment( id=None, assignee="knl_scaling", expression=sympy_conv( self.integral_knl.get_global_scaling_const()), temp_var_type=lp.Optional(), ) extra_kernel_kwarg_types = () if "extra_kernel_kwarg_types" in kwargs: extra_kernel_kwarg_types = kwargs["extra_kernel_kwarg_types"] lpknl = lp.make_kernel( # NOQA "{ [iqpt, iaxis]: 0<=iqpt<n_q_points and 0<=iaxis<dim }", [ """ for iqpt for iaxis <> dist[iaxis] = (quad_points[iaxis, iqpt] - target_point[iaxis]) end end """ ] + eval_kernel_insns + [scaling_assignment] + [ """ for iqpt result[iqpt] = knl_val * knl_scaling end """ ], [ lp.ValueArg("dim, n_q_points", np.int32), lp.GlobalArg("quad_points", np.float64, "dim, n_q_points"), lp.GlobalArg("target_point", np.float64, "dim") ] + list(extra_kernel_kwarg_types) + [ "...", ], name="eval_kernel_lucky_coin", lang_version=(2018, 2), ) lpknl = lp.fix_parameters(lpknl, dim=self.dim) lpknl = lp.set_options(lpknl, write_cl=False) lpknl = lp.set_options(lpknl, return_dict=True) # }}} End kernel evaluation node_coords = flatten(thaw(arr_ctx, discr.nodes())) nodes = cl.array.to_device( queue, np.vstack([crd.get() for crd in node_coords])) int_vals = [] for target in self.q_points: evt, res = lpknl(queue, quad_points=nodes, target_point=target) knl_vals = res['result'] integ = bind( discr, sym.integral(self.dim, self.dim, sym.var("integrand")))(queue, integrand=knl_vals) queue.finish() int_vals.append(integ) int_vals_coins = np.array(int_vals) int_vals_inf = np.zeros(self.n_q_points) # {{{ integrate over the exterior of the ball if self.dim == 2: def rho_0(theta, target, radius): rho_x = np.linalg.norm(target, ord=2) return (-1 * rho_x * np.cos(theta) + np.sqrt(radius**2 - rho_x**2 * (np.sin(theta)**2))) def ext_inf_integrand(theta, s, target, radius): _rho_0 = rho_0(theta, target=target, radius=radius) return _rho_0**(-2 * s) def compute_ext_inf_integral(target, s, radius): # target: target point # s: fractional order # radius: radius of the circle import scipy.integrate as sint val, _ = sint.quadrature(partial(ext_inf_integrand, s=s, target=target, radius=radius), a=0, b=2 * np.pi) return val * (1 / (2 * s)) * fl_scaling(k=self.dim, s=s) if 1: # optional test target = [0, 0] s = 0.5 radius = 1 scaling = fl_scaling(k=self.dim, s=s) val = compute_ext_inf_integral(target, s, radius) test_err = np.abs(val - radius**(-2 * s) * 2 * np.pi * (1 / (2 * s)) * scaling) / (radius**(-2 * s) * 2 * np.pi * (1 / (2 * s)) * scaling) if test_err > 1e-12: logger.warn("Error evaluating at origin = %f" % test_err) for tid, target in enumerate(self.q_points): # The formula assumes that the source box is centered at origin int_vals_inf[tid] = compute_ext_inf_integral( target=target - hs, s=self.integral_knl.s, radius=r) elif self.dim == 3: # FIXME raise NotImplementedError("3D not yet implemented.") else: raise NotImplementedError("Unsupported dimension") # }}} End integrate over the exterior of the ball self.kernel_exterior_normalizers = int_vals_coins + int_vals_inf return
def get_path_length(queue, density_discr): return bind(density_discr, sym.integral(2, 1, 1))(queue)
def compute_biharmonic_extension(queue, target_discr, qbx, density_discr, f, fx, fy, target_association_tolerance=0.05): """Biharmoc extension. Currently only support interior domains in 2D (i.e., extension is on the exterior). """ # pylint: disable=invalid-unary-operand-type dim = 2 queue = setup_command_queue(queue=queue) qbx_forced_limit = 1 normal = get_normal_vectors(queue, density_discr, loc_sign=1) bdry_op_sym = get_extension_bie_symbolic_operator(loc_sign=1) bound_op = bind(qbx, bdry_op_sym) bc = [fy, -fx] bvp_rhs = bind(qbx, sym.make_sym_vector("bc", dim))(queue, bc=bc) gmres_result = gmres(bound_op.scipy_op(queue, "sigma", np.float64, mu=1., normal=normal), bvp_rhs, tol=1e-9, progress=True, stall_iterations=0, hard_failure=True) mu = gmres_result.solution arclength_parametrization_derivatives_sym = sym.make_sym_vector( "arclength_parametrization_derivatives", dim) density_mu_sym = sym.make_sym_vector("mu", dim) dxids_sym = arclength_parametrization_derivatives_sym[0] + \ 1j * arclength_parametrization_derivatives_sym[1] dxids_conj_sym = arclength_parametrization_derivatives_sym[0] - \ 1j * arclength_parametrization_derivatives_sym[1] density_rho_sym = density_mu_sym[1] - 1j * density_mu_sym[0] density_conj_rho_sym = density_mu_sym[1] + 1j * density_mu_sym[0] # convolutions GS1 = sym.IntG( # noqa: N806 ComplexLinearLogKernel(dim), density_rho_sym, qbx_forced_limit=None) GS2 = sym.IntG( # noqa: N806 ComplexLinearKernel(dim), density_conj_rho_sym, qbx_forced_limit=None) GD1 = sym.IntG( # noqa: N806 ComplexFractionalKernel(dim), density_rho_sym * dxids_sym, qbx_forced_limit=None) GD2 = [ sym.IntG( # noqa: N806 AxisTargetDerivative(iaxis, ComplexLogKernel(dim)), density_conj_rho_sym * dxids_sym + density_rho_sym * dxids_conj_sym, qbx_forced_limit=qbx_forced_limit) for iaxis in range(dim) ] GS1_bdry = sym.IntG( # noqa: N806 ComplexLinearLogKernel(dim), density_rho_sym, qbx_forced_limit=qbx_forced_limit) GS2_bdry = sym.IntG( # noqa: N806 ComplexLinearKernel(dim), density_conj_rho_sym, qbx_forced_limit=qbx_forced_limit) GD1_bdry = sym.IntG( # noqa: N806 ComplexFractionalKernel(dim), density_rho_sym * dxids_sym, qbx_forced_limit=qbx_forced_limit) xp, yp = get_arclength_parametrization_derivative(queue, density_discr) xp = -xp yp = -yp tangent = get_tangent_vectors(queue, density_discr, loc_sign=qbx_forced_limit) # check and fix the direction of parametrization # logger.info("Fix all negative signs in:" + # str(xp * tangent[0] + yp * tangent[1])) grad_v2 = [ bind(qbx, GD2[iaxis])(queue, mu=mu, arclength_parametrization_derivatives=make_obj_array( [xp, yp])).real for iaxis in range(dim) ] v2_tangent_der = sum(tangent[iaxis] * grad_v2[iaxis] for iaxis in range(dim)) from pytential.symbolic.pde.scalar import NeumannOperator from sumpy.kernel import LaplaceKernel operator_v1 = NeumannOperator(LaplaceKernel(dim), loc_sign=qbx_forced_limit) bound_op_v1 = bind(qbx, operator_v1.operator(var("sigma"))) # FIXME: the positive sign works here rhs_v1 = operator_v1.prepare_rhs(1 * v2_tangent_der) gmres_result = gmres(bound_op_v1.scipy_op(queue, "sigma", dtype=np.float64), rhs_v1, tol=1e-9, progress=True, stall_iterations=0, hard_failure=True) sigma = gmres_result.solution qbx_stick_out = qbx.copy( target_association_tolerance=target_association_tolerance) v1 = bind((qbx_stick_out, target_discr), operator_v1.representation(var("sigma"), qbx_forced_limit=None))(queue, sigma=sigma) grad_v1 = bind( (qbx_stick_out, target_discr), operator_v1.representation( var("sigma"), qbx_forced_limit=None, map_potentials=lambda pot: sym.grad(dim, pot)))(queue, sigma=sigma) v1_bdry = bind( qbx, operator_v1.representation(var("sigma"), qbx_forced_limit=qbx_forced_limit))( queue, sigma=sigma) z_conj = target_discr.nodes()[0] - 1j * target_discr.nodes()[1] z_conj_bdry = density_discr.nodes().with_queue(queue)[0] \ - 1j * density_discr.nodes().with_queue(queue)[1] int_rho = 1 / (8 * np.pi) * bind( qbx, sym.integral(dim, dim - 1, density_rho_sym))(queue, mu=mu) omega_S1 = bind( # noqa: N806 (qbx_stick_out, target_discr), GS1)(queue, mu=mu).real omega_S2 = -1 * bind( # noqa: N806 (qbx_stick_out, target_discr), GS2)(queue, mu=mu).real omega_S3 = (z_conj * int_rho).real # noqa: N806 omega_S = -(omega_S1 + omega_S2 + omega_S3) # noqa: N806 grad_omega_S1 = bind( # noqa: N806 (qbx_stick_out, target_discr), sym.grad(dim, GS1))(queue, mu=mu).real grad_omega_S2 = -1 * bind( # noqa: N806 (qbx_stick_out, target_discr), sym.grad(dim, GS2))(queue, mu=mu).real grad_omega_S3 = (int_rho * make_obj_array([1., -1.])).real # noqa: N806 grad_omega_S = -(grad_omega_S1 + grad_omega_S2 + grad_omega_S3 ) # noqa: N806 omega_S1_bdry = bind(qbx, GS1_bdry)(queue, mu=mu).real # noqa: N806 omega_S2_bdry = -1 * bind(qbx, GS2_bdry)(queue, mu=mu).real # noqa: N806 omega_S3_bdry = (z_conj_bdry * int_rho).real # noqa: N806 omega_S_bdry = -(omega_S1_bdry + omega_S2_bdry + omega_S3_bdry ) # noqa: N806 omega_D1 = bind( # noqa: N806 (qbx_stick_out, target_discr), GD1)(queue, mu=mu, arclength_parametrization_derivatives=make_obj_array([xp, yp])).real omega_D = (omega_D1 + v1) # noqa: N806 grad_omega_D1 = bind( # noqa: N806 (qbx_stick_out, target_discr), sym.grad(dim, GD1))( queue, mu=mu, arclength_parametrization_derivatives=make_obj_array([xp, yp])).real grad_omega_D = grad_omega_D1 + grad_v1 # noqa: N806 omega_D1_bdry = bind( # noqa: N806 qbx, GD1_bdry)(queue, mu=mu, arclength_parametrization_derivatives=make_obj_array( [xp, yp])).real omega_D_bdry = (omega_D1_bdry + v1_bdry) # noqa: N806 int_bdry_mu = bind( qbx, sym.integral(dim, dim - 1, sym.make_sym_vector("mu", dim)))(queue, mu=mu) omega_W = ( # noqa: N806 int_bdry_mu[0] * target_discr.nodes()[1] - int_bdry_mu[1] * target_discr.nodes()[0]) grad_omega_W = make_obj_array( # noqa: N806 [-int_bdry_mu[1], int_bdry_mu[0]]) omega_W_bdry = ( # noqa: N806 int_bdry_mu[0] * density_discr.nodes().with_queue(queue)[1] - int_bdry_mu[1] * density_discr.nodes().with_queue(queue)[0]) int_bdry = bind(qbx, sym.integral(dim, dim - 1, var("integrand")))( queue, integrand=omega_S_bdry + omega_D_bdry + omega_W_bdry) debugging_info = {} debugging_info['omega_S'] = omega_S debugging_info['omega_D'] = omega_D debugging_info['omega_W'] = omega_W debugging_info['omega_v1'] = v1 debugging_info['omega_D1'] = omega_D1 int_interior_func_bdry = bind(qbx, sym.integral(2, 1, var("integrand")))(queue, integrand=f) path_length = get_path_length(queue, density_discr) ext_f = omega_S + omega_D + omega_W + (int_interior_func_bdry - int_bdry) / path_length grad_f = grad_omega_S + grad_omega_D + grad_omega_W return ext_f, grad_f[0], grad_f[1], debugging_info
def compute_harmonic_extension(queue, target_discr, qbx, density_discr, f, loc_sign=1, target_association_tolerance=0.05, gmres_tolerance=1e-14): """Harmonic extension. :param: loc_sign indicates the domain for extension, which equals to the negation of the loc_sign for the original problem. """ dim = qbx.ambient_dim queue = setup_command_queue(queue=queue) # {{{ describe bvp from sumpy.kernel import LaplaceKernel kernel = LaplaceKernel(dim) cse = sym.cse sigma_sym = sym.var("sigma") #sqrt_w = sym.sqrt_jac_q_weight(3) sqrt_w = 1 inv_sqrt_w_sigma = cse(sigma_sym / sqrt_w) bdry_op_sym = ( loc_sign * 0.5 * sigma_sym + sqrt_w * (sym.S(kernel, inv_sqrt_w_sigma) + sym.D(kernel, inv_sqrt_w_sigma))) # }}} bound_op = bind(qbx, bdry_op_sym) # {{{ fix rhs and solve bvp_rhs = bind(qbx, sqrt_w * sym.var("bc"))(queue, bc=f) from pytential.solve import gmres gmres_result = gmres(bound_op.scipy_op(queue, "sigma", dtype=np.float64), bvp_rhs, tol=gmres_tolerance, progress=True, stall_iterations=0, hard_failure=True) sigma = bind(qbx, sym.var("sigma") / sqrt_w)(queue, sigma=gmres_result.solution) # }}} debugging_info = {} debugging_info['gmres_result'] = gmres_result # {{{ postprocess repr_kwargs = dict(qbx_forced_limit=None) representation_sym = (sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs) + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs)) qbx_stick_out = qbx.copy( target_association_tolerance=target_association_tolerance) debugging_info['qbx'] = qbx_stick_out debugging_info['representation'] = representation_sym debugging_info['density'] = sigma ext_f = bind((qbx_stick_out, target_discr), representation_sym)(queue, sigma=sigma).real # }}} # NOTE: matching is needed if using # pytential.symbolic.pde.scalar.DirichletOperator # but here we are using a representation that does not have null # space for exterior Dirichlet problem if loc_sign == 1 and False: bdry_measure = bind(density_discr, sym.integral(dim, dim - 1, 1))(queue) int_func_bdry = bind(qbx, sym.integral(dim, dim - 1, var("integrand")))(queue, integrand=f) solu_bdry = bind((qbx, density_discr), representation_sym)(queue, sigma=sigma).real int_solu_bdry = bind(qbx, sym.integral( dim, dim - 1, var("integrand")))(queue, integrand=solu_bdry) matching_const = (int_func_bdry - int_solu_bdry) / bdry_measure else: matching_const = 0. ext_f = ext_f + matching_const def eval_ext_f(target_discr): return bind((qbx_stick_out, target_discr), representation_sym)( queue, sigma=sigma).real + matching_const debugging_info['eval_ext_f'] = eval_ext_f return ext_f, debugging_info