def not_supported(self, expr): if isinstance(expr, int): return expr elif getattr(expr, "is_Function", False): func_name = SympyToPymbolicMapperBase.function_name(self, expr) return prim.Variable(func_name)( *tuple(self.rec(arg) for arg in expr.args)) else: return SympyToPymbolicMapperBase.not_supported(self, expr)
def test_line_taylor_coeff_growth(): # Regression test for LineTaylorLocalExpansion. # See https://gitlab.tiker.net/inducer/pytential/merge_requests/12 from sumpy.kernel import LaplaceKernel from sumpy.expansion.local import LineTaylorLocalExpansion from sumpy.symbolic import make_sym_vector, SympyToPymbolicMapper import numpy as np order = 10 expn = LineTaylorLocalExpansion(LaplaceKernel(2), order) avec = make_sym_vector("a", 2) bvec = make_sym_vector("b", 2) coeffs = expn.coefficients_from_source(avec, bvec, rscale=1) sym2pymbolic = SympyToPymbolicMapper() coeffs_pymbolic = [sym2pymbolic(c) for c in coeffs] from pymbolic.mapper.flop_counter import FlopCounter flop_counter = FlopCounter() counts = [flop_counter(c) for c in coeffs_pymbolic] indices = np.arange(1, order + 2) max_order = 2 assert np.polyfit(np.log(indices), np.log(counts), deg=1)[0] < max_order
def get_kernel_scaling_assignment(self): from sumpy.symbolic import SympyToPymbolicMapper sympy_conv = SympyToPymbolicMapper() return [lp.Assignment(id=None, assignee="kernel_scaling", expression=sympy_conv( self.expansion.kernel.get_global_scaling_const()), temp_var_type=lp.Optional(None))]
def get_kernel_scaling_assignments(self): from sumpy.symbolic import SympyToPymbolicMapper sympy_conv = SympyToPymbolicMapper() import loopy as lp return [ lp.Assignment(id=None, assignee="knl_%d_scaling" % i, expression=sympy_conv(kernel.get_global_scaling_const()), temp_var_type=lp.Optional(dtype)) for i, (kernel, dtype) in enumerate( zip(self.kernels, self.value_dtypes))]
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