Exemplo n.º 1
0
    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))
Exemplo n.º 2
0
    def get_operator(self, ambient_dim, qbx_forced_limit="avg"):
        knl = self.knl_class(ambient_dim)
        kwargs = self.knl_sym_kwargs.copy()
        kwargs["qbx_forced_limit"] = qbx_forced_limit

        if self.op_type == "scalar":
            sym_u = sym.var("u")
            sym_op = sym.S(knl, sym_u, **kwargs)
        elif self.op_type == "scalar_mixed":
            sym_u = sym.var("u")
            sym_op = sym.S(knl, 0.3 * sym_u, **kwargs) \
                    + sym.D(knl, 0.5 * sym_u, **kwargs)
        elif self.op_type == "vector":
            sym_u = sym.make_sym_vector("u", ambient_dim)

            sym_op = make_obj_array([
                sym.Sp(knl, sym_u[0], **kwargs) +
                sym.D(knl, sym_u[1], **kwargs),
                sym.S(knl, 0.4 * sym_u[0], **kwargs) +
                0.3 * sym.D(knl, sym_u[0], **kwargs)
            ])
        else:
            raise ValueError(f"unknown operator type: '{self.op_type}'")

        sym_op = 0.5 * sym_u + sym_op
        return sym_u, sym_op
Exemplo n.º 3
0
def _build_op(lpot_id,
              k=0,
              ndim=2,
              source=sym.DEFAULT_SOURCE,
              target=sym.DEFAULT_TARGET,
              qbx_forced_limit="avg"):

    from sumpy.kernel import LaplaceKernel, HelmholtzKernel
    if k:
        knl = HelmholtzKernel(ndim)
        knl_kwargs = {"k": k}
    else:
        knl = LaplaceKernel(ndim)
        knl_kwargs = {}

    lpot_kwargs = {
            "qbx_forced_limit": qbx_forced_limit,
            "source": source,
            "target": target}
    lpot_kwargs.update(knl_kwargs)
    if lpot_id == 1:
        # scalar single-layer potential
        u_sym = sym.var("u")
        op = sym.S(knl, u_sym, **lpot_kwargs)
    elif lpot_id == 2:
        # scalar combination of layer potentials
        u_sym = sym.var("u")
        op = sym.S(knl, 0.3 * u_sym, **lpot_kwargs) \
             + sym.D(knl, 0.5 * u_sym, **lpot_kwargs)
    elif lpot_id == 3:
        # vector potential
        u_sym = sym.make_sym_vector("u", 2)
        u0_sym, u1_sym = u_sym

        op = make_obj_array([
            sym.Sp(knl, u0_sym, **lpot_kwargs)
            + sym.D(knl, u1_sym, **lpot_kwargs),
            sym.S(knl, 0.4 * u0_sym, **lpot_kwargs)
            + 0.3 * sym.D(knl, u0_sym, **lpot_kwargs)
            ])
    else:
        raise ValueError("Unknown lpot_id: {}".format(lpot_id))

    op = 0.5 * u_sym + op

    return op, u_sym, knl_kwargs
def test_ellipse_eigenvalues(ctx_factory,
                             ellipse_aspect,
                             mode_nr,
                             qbx_order,
                             force_direct,
                             visualize=False):
    logging.basicConfig(level=logging.INFO)

    print("ellipse_aspect: %s, mode_nr: %d, qbx_order: %d" %
          (ellipse_aspect, mode_nr, qbx_order))

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    target_order = 8

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    from pytential.qbx import QBXLayerPotentialSource
    from pytools.convergence import EOCRecorder

    s_eoc_rec = EOCRecorder()
    d_eoc_rec = EOCRecorder()
    sp_eoc_rec = EOCRecorder()

    if ellipse_aspect != 1:
        nelements_values = [60, 100, 150, 200]
    else:
        nelements_values = [30, 70]

    # See
    #
    # [1] G. J. Rodin and O. Steinbach, "Boundary Element Preconditioners
    # for Problems Defined on Slender Domains", SIAM Journal on Scientific
    # Computing, Vol. 24, No. 4, pg. 1450, 2003.
    # https://dx.doi.org/10.1137/S1064827500372067

    for nelements in nelements_values:
        mesh = make_curve_mesh(partial(ellipse, ellipse_aspect),
                               np.linspace(0, 1, nelements + 1), target_order)

        fmm_order = 12
        if force_direct:
            fmm_order = False

        pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))
        qbx = QBXLayerPotentialSource(
            pre_density_discr,
            4 * target_order,
            qbx_order,
            fmm_order=fmm_order,
            _expansions_in_tree_have_extent=True,
        )
        places = GeometryCollection(qbx)

        density_discr = places.get_discretization(places.auto_source.geometry)
        from meshmode.dof_array import thaw, flatten
        nodes = thaw(actx, density_discr.nodes())

        if visualize:
            # plot geometry, centers, normals
            centers = bind(places, sym.expansion_centers(qbx.ambient_dim,
                                                         +1))(actx)
            normals = bind(places,
                           sym.normal(qbx.ambient_dim))(actx).as_vector(object)

            nodes_h = np.array(
                [actx.to_numpy(axis) for axis in flatten(nodes)])
            centers_h = np.array(
                [actx.to_numpy(axis) for axis in flatten(centers)])
            normals_h = np.array(
                [actx.to_numpy(axis) for axis in flatten(normals)])

            pt.plot(nodes_h[0], nodes_h[1], "x-")
            pt.plot(centers_h[0], centers_h[1], "o")
            pt.quiver(nodes_h[0], nodes_h[1], normals_h[0], normals_h[1])
            pt.gca().set_aspect("equal")
            pt.show()

        angle = actx.np.arctan2(nodes[1] * ellipse_aspect, nodes[0])

        ellipse_fraction = ((1 - ellipse_aspect) /
                            (1 + ellipse_aspect))**mode_nr

        # (2.6) in [1]
        J = actx.np.sqrt(  # noqa
            actx.np.sin(angle)**2 +
            (1 / ellipse_aspect)**2 * actx.np.cos(angle)**2)

        from sumpy.kernel import LaplaceKernel
        lap_knl = LaplaceKernel(2)

        # {{{ single layer

        sigma_sym = sym.var("sigma")
        s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1)

        sigma = actx.np.cos(mode_nr * angle) / J
        s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma)

        # SIGN BINGO! :)
        s_eigval = 1 / (2 * mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction)

        # (2.12) in [1]
        s_sigma_ref = s_eigval * J * sigma

        if 0:
            #pt.plot(s_sigma.get(), label="result")
            #pt.plot(s_sigma_ref.get(), label="ref")
            pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err")
            pt.legend()
            pt.show()

        h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx)
        s_err = (norm(density_discr, s_sigma - s_sigma_ref) /
                 norm(density_discr, s_sigma_ref))
        s_eoc_rec.add_data_point(h_max, s_err)

        # }}}

        # {{{ double layer

        d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg")

        sigma = actx.np.cos(mode_nr * angle)
        d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma)

        # SIGN BINGO! :)
        d_eigval = -(-1)**mode_nr * 1 / 2 * ellipse_fraction

        d_sigma_ref = d_eigval * sigma

        if 0:
            pt.plot(actx.to_numpy(flatten(d_sigma)), label="result")
            pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref")
            pt.legend()
            pt.show()

        if ellipse_aspect == 1:
            d_ref_norm = norm(density_discr, sigma)
        else:
            d_ref_norm = norm(density_discr, d_sigma_ref)

        d_err = (norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm)
        d_eoc_rec.add_data_point(h_max, d_err)

        # }}}

        if ellipse_aspect == 1:
            # {{{ S'

            sp_sigma_op = sym.Sp(lap_knl,
                                 sym.var("sigma"),
                                 qbx_forced_limit="avg")

            sigma = actx.np.cos(mode_nr * angle)
            sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma)
            sp_eigval = 0

            sp_sigma_ref = sp_eigval * sigma

            sp_err = (norm(density_discr, sp_sigma - sp_sigma_ref) /
                      norm(density_discr, sigma))
            sp_eoc_rec.add_data_point(h_max, sp_err)

            # }}}

    print("Errors for S:")
    print(s_eoc_rec)
    required_order = qbx_order + 1
    assert s_eoc_rec.order_estimate() > required_order - 1.5

    print("Errors for D:")
    print(d_eoc_rec)
    required_order = qbx_order
    assert d_eoc_rec.order_estimate() > required_order - 1.5

    if ellipse_aspect == 1:
        print("Errors for S':")
        print(sp_eoc_rec)
        required_order = qbx_order
        assert sp_eoc_rec.order_estimate() > required_order - 1.5
def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order,
                            fmm_backend):
    logging.basicConfig(level=logging.INFO)

    special = pytest.importorskip("scipy.special")

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    target_order = 8

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    from pytential.qbx import QBXLayerPotentialSource
    from pytools.convergence import EOCRecorder

    s_eoc_rec = EOCRecorder()
    d_eoc_rec = EOCRecorder()
    sp_eoc_rec = EOCRecorder()
    dp_eoc_rec = EOCRecorder()

    def rel_err(comp, ref):
        return (norm(density_discr, comp - ref) / norm(density_discr, ref))

    for nrefinements in [0, 1]:
        from meshmode.mesh.generation import generate_icosphere
        mesh = generate_icosphere(1, target_order)
        from meshmode.mesh.refinement import Refiner

        refiner = Refiner(mesh)
        for i in range(nrefinements):
            flags = np.ones(mesh.nelements, dtype=bool)
            refiner.refine(flags)
            mesh = refiner.get_current_mesh()

        pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))
        qbx = QBXLayerPotentialSource(
            pre_density_discr,
            4 * target_order,
            qbx_order,
            fmm_order=6,
            fmm_backend=fmm_backend,
        )
        places = GeometryCollection(qbx)

        from meshmode.dof_array import flatten, unflatten, thaw

        density_discr = places.get_discretization(places.auto_source.geometry)
        nodes = thaw(actx, density_discr.nodes())
        r = actx.np.sqrt(nodes[0] * nodes[0] + nodes[1] * nodes[1] +
                         nodes[2] * nodes[2])
        phi = actx.np.arccos(nodes[2] / r)
        theta = actx.np.arctan2(nodes[0], nodes[1])

        ymn = unflatten(
            actx, density_discr,
            actx.from_numpy(
                special.sph_harm(mode_m, mode_n, actx.to_numpy(flatten(theta)),
                                 actx.to_numpy(flatten(phi)))))

        from sumpy.kernel import LaplaceKernel
        lap_knl = LaplaceKernel(3)

        # {{{ single layer

        s_sigma_op = bind(
            places, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1))
        s_sigma = s_sigma_op(actx, sigma=ymn)
        s_eigval = 1 / (2 * mode_n + 1)

        h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx)
        s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval * ymn))

        # }}}

        # {{{ double layer

        d_sigma_op = bind(
            places, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        d_sigma = d_sigma_op(actx, sigma=ymn)
        d_eigval = -1 / (2 * (2 * mode_n + 1))
        d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval * ymn))

        # }}}

        # {{{ S'

        sp_sigma_op = bind(
            places, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        sp_sigma = sp_sigma_op(actx, sigma=ymn)
        sp_eigval = -1 / (2 * (2 * mode_n + 1))

        sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval * ymn))

        # }}}

        # {{{ D'

        dp_sigma_op = bind(
            places, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        dp_sigma = dp_sigma_op(actx, sigma=ymn)
        dp_eigval = -(mode_n * (mode_n + 1)) / (2 * mode_n + 1)

        dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval * ymn))

        # }}}

    print("Errors for S:")
    print(s_eoc_rec)
    required_order = qbx_order + 1
    assert s_eoc_rec.order_estimate() > required_order - 1.5

    print("Errors for D:")
    print(d_eoc_rec)
    required_order = qbx_order
    assert d_eoc_rec.order_estimate() > required_order - 0.5

    print("Errors for S':")
    print(sp_eoc_rec)
    required_order = qbx_order
    assert sp_eoc_rec.order_estimate() > required_order - 1.5

    print("Errors for D':")
    print(dp_eoc_rec)
    required_order = qbx_order
    assert dp_eoc_rec.order_estimate() > required_order - 1.5
Exemplo n.º 6
0
def test_3d_jump_relations(ctx_factory, relation, visualize=False):
    # logging.basicConfig(level=logging.INFO)

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    if relation == "div_s":
        target_order = 3
    else:
        target_order = 4

    qbx_order = target_order

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    for nel_factor in [6, 10, 14]:
        from meshmode.mesh.generation import generate_torus
        mesh = generate_torus(
                5, 2, order=target_order,
                n_major=2*nel_factor, n_minor=nel_factor)

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
        pre_discr = Discretization(
                actx, mesh,
                InterpolatoryQuadratureSimplexGroupFactory(3))

        from pytential.qbx import QBXLayerPotentialSource
        qbx = QBXLayerPotentialSource(
                pre_discr, fine_order=4*target_order,
                qbx_order=qbx_order,
                fmm_order=qbx_order + 5,
                fmm_backend="fmmlib"
                )

        places = GeometryCollection(qbx)
        density_discr = places.get_discretization(places.auto_source.geometry)

        from sumpy.kernel import LaplaceKernel
        knl = LaplaceKernel(3)

        def nxcurlS(qbx_forced_limit):

            return sym.n_cross(sym.curl(sym.S(
                knl,
                sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"),
                qbx_forced_limit=qbx_forced_limit)))

        from meshmode.dof_array import thaw
        x, y, z = thaw(actx, density_discr.nodes())
        m = actx.np

        if relation == "nxcurls":
            density_sym = sym.make_sym_vector("density", 2)

            jump_identity_sym = (
                    nxcurlS(+1)
                    - (nxcurlS("avg") + 0.5*sym.tangential_to_xyz(density_sym)))

            # The tangential coordinate system is element-local, so we can't just
            # conjure up some globally smooth functions, interpret their values
            # in the tangential coordinate system, and be done. Instead, generate
            # an XYZ function and project it.
            density = bind(places,
                    sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))(
                            actx,
                            jxyz=sym.make_obj_array([
                                m.cos(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z),
                                m.sin(0.5*x) * m.cos(0.5*y) * m.sin(0.5*z),
                                m.sin(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z),
                                ]))

        elif relation == "sp":

            density = m.cos(2*x) * m.cos(2*y) * m.cos(z)
            density_sym = sym.var("density")

            jump_identity_sym = (
                    sym.Sp(knl, density_sym, qbx_forced_limit=+1)
                    - (sym.Sp(knl, density_sym, qbx_forced_limit="avg")
                        - 0.5*density_sym))

        elif relation == "div_s":

            density = m.cos(2*x) * m.cos(2*y) * m.cos(z)
            density_sym = sym.var("density")

            jump_identity_sym = (
                    sym.div(sym.S(knl, sym.normal(3).as_vector()*density_sym,
                        qbx_forced_limit="avg"))
                    + sym.D(knl, density_sym, qbx_forced_limit="avg"))

        else:
            raise ValueError("unexpected value of 'relation': %s" % relation)

        bound_jump_identity = bind(places, jump_identity_sym)
        jump_identity = bound_jump_identity(actx, density=density)

        h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx)
        err = (
                norm(density_discr, jump_identity, np.inf)
                / norm(density_discr, density, np.inf))
        print("ERROR", h_max, err)

        eoc_rec.add_data_point(h_max, err)

        # {{{ visualization

        if visualize and relation == "nxcurls":
            nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density)
            nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density)
            jtxyz = bind(places, sym.tangential_to_xyz(density_sym))(
                    actx, density=density)

            from meshmode.discretization.visualization import make_visualizer
            bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3)

            bdry_normals = bind(places, sym.normal(3))(actx)\
                    .as_vector(dtype=object)

            bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [
                ("jt", jtxyz),
                ("nxcurlS_ext", nxcurlS_ext),
                ("nxcurlS_avg", nxcurlS_avg),
                ("bdry_normals", bdry_normals),
                ])

        if visualize and relation == "sp":
            op = sym.Sp(knl, density_sym, qbx_forced_limit=+1)
            sp_ext = bind(places, op)(actx, density=density)
            op = sym.Sp(knl, density_sym, qbx_forced_limit="avg")
            sp_avg = bind(places, op)(actx, density=density)

            from meshmode.discretization.visualization import make_visualizer
            bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3)

            bdry_normals = bind(places,
                    sym.normal(3))(actx).as_vector(dtype=object)

            bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [
                ("density", density),
                ("sp_ext", sp_ext),
                ("sp_avg", sp_avg),
                ("bdry_normals", bdry_normals),
                ])

        # }}}

    print(eoc_rec)

    assert eoc_rec.order_estimate() >= qbx_order - 1.5
Exemplo n.º 7
0
 def Sp(density):
     return sym.Sp(self.laplace_kernel,
             density,
             qbx_forced_limit="avg")
Exemplo n.º 8
0
    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
                    )
                )
Exemplo n.º 9
0
 def rho_operator(self, loc, rho):
     return loc*0.5*rho+sym.Sp(
             self.kernel, rho, k=self.k, qbx_forced_limit="avg")
Exemplo n.º 10
0
def test_3d_jump_relations(actx_factory, relation, visualize=False):
    # logging.basicConfig(level=logging.INFO)
    actx = actx_factory()

    if relation == "div_s":
        target_order = 3
    else:
        target_order = 4

    qbx_order = target_order

    if relation == "sp":
        resolutions = [10, 14, 18]
    else:
        resolutions = [6, 10, 14]

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    for nel_factor in resolutions:
        from meshmode.mesh.generation import generate_torus
        mesh = generate_torus(
            5,
            2,
            n_major=2 * nel_factor,
            n_minor=nel_factor,
            order=target_order,
        )

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
        pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))

        from pytential.qbx import QBXLayerPotentialSource
        qbx = QBXLayerPotentialSource(pre_density_discr,
                                      fine_order=5 * target_order,
                                      qbx_order=qbx_order,
                                      fmm_order=qbx_order + 5,
                                      fmm_backend="fmmlib")

        places = GeometryCollection(qbx)
        density_discr = places.get_discretization(places.auto_source.geometry)

        from sumpy.kernel import LaplaceKernel
        knl = LaplaceKernel(places.ambient_dim)

        def nxcurlS(qbx_forced_limit):
            sigma_sym = sym.cse(sym.tangential_to_xyz(density_sym), "jxyz")
            return sym.n_cross(
                sym.curl(
                    sym.S(knl, sigma_sym, qbx_forced_limit=qbx_forced_limit)))

        x, y, z = thaw(density_discr.nodes(), actx)
        if relation == "nxcurls":
            density_sym = sym.make_sym_vector("density", 2)
            jump_identity_sym = (nxcurlS(+1) - nxcurlS("avg") -
                                 0.5 * sym.tangential_to_xyz(density_sym))

            # The tangential coordinate system is element-local, so we can't just
            # conjure up some globally smooth functions, interpret their values
            # in the tangential coordinate system, and be done. Instead, generate
            # an XYZ function and project it.
            jxyz = sym.make_obj_array([
                actx.np.cos(0.5 * x) * actx.np.cos(0.5 * y) *
                actx.np.cos(0.5 * z),
                actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) *
                actx.np.sin(0.5 * z),
                actx.np.sin(0.5 * x) * actx.np.cos(0.5 * y) *
                actx.np.cos(0.5 * z),
            ])
            density = bind(
                places,
                sym.xyz_to_tangential(sym.make_sym_vector("jxyz",
                                                          3)))(actx, jxyz=jxyz)

        elif relation == "sp":
            density_sym = sym.var("density")
            jump_identity_sym = (
                0.5 * density_sym +
                sym.Sp(knl, density_sym, qbx_forced_limit=+1) -
                sym.Sp(knl, density_sym, qbx_forced_limit="avg"))

            density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z)

        elif relation == "div_s":
            density_sym = sym.var("density")
            sigma_sym = sym.normal(
                places.ambient_dim).as_vector() * density_sym
            jump_identity_sym = (
                sym.div(sym.S(knl, sigma_sym, qbx_forced_limit="avg")) +
                sym.D(knl, density_sym, qbx_forced_limit="avg"))

            density = actx.np.cos(2 * x) * actx.np.cos(2 * y) * actx.np.cos(z)

        else:
            raise ValueError(f"unexpected value of 'relation': '{relation}'")

        bound_jump_identity = bind(places, jump_identity_sym)
        jump_identity = bound_jump_identity(actx, density=density)

        h_max = actx.to_numpy(
            bind(places, sym.h_max(places.ambient_dim))(actx))
        err = actx.to_numpy(
            norm(density_discr, jump_identity, np.inf) /
            norm(density_discr, density, np.inf))
        eoc_rec.add_data_point(h_max, err)

        logging.info("error: nel %d h_max %.5e %.5e", nel_factor, h_max, err)

        # {{{ visualization

        if not visualize:
            continue

        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, density_discr, target_order)
        normals = bind(places,
                       sym.normal(places.ambient_dim).as_vector())(actx)
        error = actx.np.log10(actx.np.abs(jump_identity) + 1.0e-15)

        if relation == "nxcurls":
            nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density)
            nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density)
            jtxyz = bind(places,
                         sym.tangential_to_xyz(density_sym))(actx,
                                                             density=density)

            vis.write_vtk_file(f"source-nxcurls-{nel_factor:03d}.vtu", [
                ("jt", jtxyz),
                ("nxcurlS_ext", nxcurlS_ext),
                ("nxcurlS_avg", nxcurlS_avg),
                ("bdry_normals", normals),
                ("error", error),
            ])

        elif relation == "sp":
            op = sym.Sp(knl, density_sym, qbx_forced_limit=+1)
            sp_ext = bind(places, op)(actx, density=density)
            op = sym.Sp(knl, density_sym, qbx_forced_limit="avg")
            sp_avg = bind(places, op)(actx, density=density)

            vis.write_vtk_file(f"source-sp-{nel_factor:03d}.vtu", [
                ("density", density),
                ("sp_ext", sp_ext),
                ("sp_avg", sp_avg),
                ("bdry_normals", normals),
                ("error", error),
            ])

        elif relation == "div_s":
            vis.write_vtk_file(f"source-div-{nel_factor:03d}.vtu", [
                ("density", density),
                ("bdry_normals", normals),
                ("error", error),
            ])

        # }}}

    logger.info("\n%s", str(eoc_rec))
    assert eoc_rec.order_estimate() >= qbx_order - 1.5
Exemplo n.º 11
0
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