def operator(self, u): sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u/sqrt_w) if self.is_unique_only_up_to_constant(): # The exterior Dirichlet operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. # # See Hackbusch, http://books.google.com/books?id=Ssnf7SZB0ZMC # Theorem 8.2.18b amb_dim = self.kernel.dim ones_contribution = ( sym.Ones() * sym.mean(amb_dim, amb_dim-1, inv_sqrt_w_u)) else: ones_contribution = 0 return (-self.loc_sign*0.5*u + sqrt_w*( self.alpha*sym.S(self.kernel, inv_sqrt_w_u, qbx_forced_limit=+1, kernel_arguments=self.kernel_arguments) - sym.D(self.kernel, inv_sqrt_w_u, qbx_forced_limit="avg", kernel_arguments=self.kernel_arguments) + ones_contribution))
def operator(self, u): sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) if self.is_unique_only_up_to_constant(): # The exterior Dirichlet operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. # # See Hackbusch, https://books.google.com/books?id=Ssnf7SZB0ZMC # Theorem 8.2.18b amb_dim = self.kernel.dim ones_contribution = (sym.Ones() * sym.mean(amb_dim, amb_dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 return ( -self.loc_sign * 0.5 * u + sqrt_w * (self.alpha * sym.S(self.kernel, inv_sqrt_w_u, qbx_forced_limit=+1, kernel_arguments=self.kernel_arguments) - sym.D(self.kernel, inv_sqrt_w_u, qbx_forced_limit="avg", kernel_arguments=self.kernel_arguments) + ones_contribution) )
def operator(self, u): from sumpy.kernel import HelmholtzKernel, LaplaceKernel sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u / sqrt_w) knl = self.kernel lknl = self.laplace_kernel knl_kwargs = {} knl_kwargs["kernel_arguments"] = self.kernel_arguments DpS0u = sym.Dp( knl, # noqa cse(sym.S(lknl, inv_sqrt_w_u)), **knl_kwargs) if self.use_improved_operator: Dp0S0u = -0.25 * u + sym.Sp( # noqa lknl, # noqa sym.Sp(lknl, inv_sqrt_w_u, qbx_forced_limit="avg"), qbx_forced_limit="avg") if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( # noqa sym.Dp( knl - lknl, # noqa cse(sym.S(lknl, inv_sqrt_w_u, qbx_forced_limit=+1)), qbx_forced_limit=+1, **knl_kwargs) + Dp0S0u) elif isinstance(knl, LaplaceKernel): DpS0u = Dp0S0u # noqa else: raise ValueError( f"no improved operator for '{self.kernel}' known") if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. amb_dim = self.kernel.dim ones_contribution = (sym.Ones() * sym.mean(amb_dim, amb_dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 return ( -self.loc_sign * 0.5 * u + sqrt_w * (sym.Sp(knl, inv_sqrt_w_u, qbx_forced_limit="avg", **knl_kwargs) - self.alpha * DpS0u + ones_contribution))
def operator(self, u): from sumpy.kernel import HelmholtzKernel, LaplaceKernel sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = cse(u/sqrt_w) knl = self.kernel lknl = self.laplace_kernel knl_kwargs = {} knl_kwargs["kernel_arguments"] = self.kernel_arguments DpS0u = sym.Dp(knl, # noqa cse(sym.S(lknl, inv_sqrt_w_u)), **knl_kwargs) if self.use_improved_operator: Dp0S0u = -0.25*u + sym.Sp( # noqa lknl, # noqa sym.Sp(lknl, inv_sqrt_w_u, qbx_forced_limit="avg"), qbx_forced_limit="avg") if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( # noqa sym.Dp(knl - lknl, # noqa cse(sym.S(lknl, inv_sqrt_w_u, qbx_forced_limit=+1)), qbx_forced_limit=+1, **knl_kwargs) + Dp0S0u) elif isinstance(knl, LaplaceKernel): DpS0u = Dp0S0u # noqa else: raise ValueError("no improved operator for %s known" % self.kernel) if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. amb_dim = self.kernel.dim ones_contribution = ( sym.Ones() * sym.mean(amb_dim, amb_dim-1, inv_sqrt_w_u)) else: ones_contribution = 0 return (-self.loc_sign*0.5*u + sqrt_w*( sym.Sp(knl, inv_sqrt_w_u, qbx_forced_limit="avg", **knl_kwargs) - self.alpha*DpS0u + ones_contribution ))
def operator(self, u, **kwargs): """ :param u: symbolic variable for the density. :param kwargs: additional keyword arguments passed on to the layer potential constructor. """ sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = sym.cse(u/sqrt_w) if self.is_unique_only_up_to_constant(): # The exterior Dirichlet operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. # # See Hackbusch, https://books.google.com/books?id=Ssnf7SZB0ZMC # Theorem 8.2.18b ones_contribution = ( sym.Ones() * sym.mean(self.dim, self.dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 def S(density): # noqa return sym.S(self.kernel, density, kernel_arguments=self.kernel_arguments, qbx_forced_limit=+1, **kwargs) def D(density): # noqa return sym.D(self.kernel, density, kernel_arguments=self.kernel_arguments, qbx_forced_limit="avg", **kwargs) return ( -0.5 * self.loc_sign * u + sqrt_w * ( self.alpha * S(inv_sqrt_w_u) - D(inv_sqrt_w_u) + ones_contribution ) )
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 operator(self, u, **kwargs): """ :param u: symbolic variable for the density. :param kwargs: additional keyword arguments passed on to the layer potential constructor. """ sqrt_w = self.get_sqrt_weight() inv_sqrt_w_u = sym.cse(u/sqrt_w) laplace_s_inv_sqrt_w_u = sym.cse( sym.S(self.laplace_kernel, inv_sqrt_w_u, qbx_forced_limit=+1) ) kwargs["kernel_arguments"] = self.kernel_arguments # NOTE: the improved operator here is based on right-precondioning # by a single layer and then using some Calderon identities to simplify # the result. The integral equation we start with for Neumann is # I + S' + alpha D' = g # where D' is hypersingular if self.use_improved_operator: def Sp(density): return sym.Sp(self.laplace_kernel, density, qbx_forced_limit="avg") # NOTE: using the Calderon identity # D' S = -u/4 + S'^2 Dp0S0u = -0.25 * u + Sp(Sp(inv_sqrt_w_u)) from sumpy.kernel import HelmholtzKernel, LaplaceKernel if isinstance(self.kernel, HelmholtzKernel): DpS0u = ( sym.Dp( self.kernel - self.laplace_kernel, laplace_s_inv_sqrt_w_u, qbx_forced_limit=+1, **kwargs) + Dp0S0u) elif isinstance(self.kernel, LaplaceKernel): DpS0u = Dp0S0u else: raise ValueError(f"no improved operator for '{self.kernel}' known") else: DpS0u = sym.Dp(self.kernel, laplace_s_inv_sqrt_w_u, **kwargs) if self.is_unique_only_up_to_constant(): # The interior Neumann operator in this representation # has a nullspace. The mean of the density must be matched # to the desired solution separately. As is, this operator # returns a mean that is not well-specified. ones_contribution = ( sym.Ones() * sym.mean(self.dim, self.dim - 1, inv_sqrt_w_u)) else: ones_contribution = 0 kwargs["qbx_forced_limit"] = "avg" return ( -0.5 * self.loc_sign * u + sqrt_w * ( sym.Sp(self.kernel, inv_sqrt_w_u, **kwargs) - self.alpha * DpS0u + ones_contribution ) )
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