Exemple #1
0
 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)
Exemple #2
0
 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)
Exemple #3
0
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
Exemple #4
0
 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))]
Exemple #5
0
    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