コード例 #1
0
def _build_qbx_discr(queue,
        ndim=2,
        nelements=30,
        target_order=7,
        qbx_order=4,
        curve_f=None):

    if curve_f is None:
        curve_f = NArmedStarfish(5, 0.25)

    if ndim == 2:
        mesh = make_curve_mesh(curve_f,
                np.linspace(0, 1, nelements + 1),
                target_order)
    elif ndim == 3:
        mesh = generate_torus(10.0, 2.0, order=target_order)
    else:
        raise ValueError("unsupported ambient dimension")

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    from pytential.qbx import QBXLayerPotentialSource
    density_discr = Discretization(
            queue.context, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))

    qbx, _ = QBXLayerPotentialSource(density_discr,
            fine_order=4 * target_order,
            qbx_order=qbx_order,
            fmm_order=False).with_refinement()

    return qbx
コード例 #2
0
ファイル: test_symbolic.py プロジェクト: inducer/pytential
def test_tangential_onb(ctx_factory):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(5, 2, order=3)

    discr = Discretization(
            cl_ctx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(3))

    tob = sym.tangential_onb(mesh.ambient_dim)
    nvecs = tob.shape[1]

    # make sure tangential_onb is mutually orthogonal and normalized
    orth_check = bind(discr, sym.make_obj_array([
        np.dot(tob[:, i], tob[:, j]) - (1 if i == j else 0)
        for i in range(nvecs) for j in range(nvecs)])
        )(queue)

    for i, orth_i in enumerate(orth_check):
        assert (cl.clmath.fabs(orth_i) < 1e-13).get().all()

    # make sure tangential_onb is orthogonal to normal
    orth_check = bind(discr, sym.make_obj_array([
        np.dot(tob[:, i], sym.normal(mesh.ambient_dim).as_vector())
        for i in range(nvecs)])
        )(queue)

    for i, orth_i in enumerate(orth_check):
        assert (cl.clmath.fabs(orth_i) < 1e-13).get().all()
コード例 #3
0
def test_vtk_overwrite(ctx_getter):
    pytest.importorskip("pyvisfile")

    def _try_write_vtk(writer, obj):
        import os
        from meshmode import FileExistsError

        filename = "test_vtk_overwrite.vtu"
        if os.path.exists(filename):
            os.remove(filename)

        writer(filename, [])
        with pytest.raises(FileExistsError):
            writer(filename, [])

        writer(filename, [], overwrite=True)
        if os.path.exists(filename):
            os.remove(filename)

    ctx = ctx_getter()
    queue = cl.CommandQueue(ctx)

    target_order = 7

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(10.0, 2.0, order=target_order)

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    discr = Discretization(
        queue.context, mesh,
        InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from meshmode.discretization.visualization import make_visualizer
    from meshmode.discretization.visualization import \
            write_nodal_adjacency_vtk_file
    from meshmode.mesh.visualization import write_vertex_vtk_file

    vis = make_visualizer(queue, discr, 1)
    _try_write_vtk(vis.write_vtk_file, discr)

    _try_write_vtk(
        lambda x, y, **kwargs: write_vertex_vtk_file(discr.mesh, x, **kwargs),
        discr.mesh)
    _try_write_vtk(
        lambda x, y, **kwargs: write_nodal_adjacency_vtk_file(
            x, discr.mesh, **kwargs), discr.mesh)
コード例 #4
0
ファイル: test_meshmode.py プロジェクト: inducer/meshmode
def test_vtk_overwrite(ctx_getter):
    pytest.importorskip("pyvisfile")

    def _try_write_vtk(writer, obj):
        import os
        from meshmode import FileExistsError

        filename = "test_vtk_overwrite.vtu"
        if os.path.exists(filename):
            os.remove(filename)

        writer(filename, [])
        with pytest.raises(FileExistsError):
            writer(filename, [])

        writer(filename, [], overwrite=True)
        if os.path.exists(filename):
            os.remove(filename)

    ctx = ctx_getter()
    queue = cl.CommandQueue(ctx)

    target_order = 7

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(10.0, 2.0, order=target_order)

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    discr = Discretization(
            queue.context, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from meshmode.discretization.visualization import make_visualizer
    from meshmode.discretization.visualization import \
            write_nodal_adjacency_vtk_file
    from meshmode.mesh.visualization import write_vertex_vtk_file

    vis = make_visualizer(queue, discr, 1)
    _try_write_vtk(vis.write_vtk_file, discr)

    _try_write_vtk(lambda x, y, **kwargs:
            write_vertex_vtk_file(discr.mesh, x, **kwargs), discr.mesh)
    _try_write_vtk(lambda x, y, **kwargs:
            write_nodal_adjacency_vtk_file(x, discr.mesh, **kwargs), discr.mesh)
コード例 #5
0
def create_discretization(queue, ndim,
                          nelements=42,
                          mesh_name=None,
                          order=4):
    ctx = queue.context

    # construct mesh
    if ndim == 2:
        from functools import partial
        from meshmode.mesh.generation import make_curve_mesh, ellipse, starfish

        if mesh_name is None:
            mesh_name = "ellipse"

        t = np.linspace(0.0, 1.0, nelements + 1)
        if mesh_name == "ellipse":
            mesh = make_curve_mesh(partial(ellipse, 2), t, order=order)
        elif mesh_name == "starfish":
            mesh = make_curve_mesh(starfish, t, order=order)
        else:
            raise ValueError('unknown mesh name: {}'.format(mesh_name))
    elif ndim == 3:
        from meshmode.mesh.generation import generate_torus
        from meshmode.mesh.generation import generate_warped_rect_mesh

        if mesh_name is None:
            mesh_name = "torus"

        if mesh_name == "torus":
            mesh = generate_torus(10.0, 5.0, order=order,
                    n_minor=nelements, n_major=nelements)
        elif mesh_name == "warp":
            mesh = generate_warped_rect_mesh(ndim, order=order, n=nelements)
        else:
            raise ValueError("unknown mesh name: {}".format(mesh_name))
    else:
        raise ValueError("unsupported dimension: {}".format(ndim))

    # create discretization
    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    discr = Discretization(ctx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(order))

    return discr
コード例 #6
0
ファイル: test_chained.py プロジェクト: inducer/meshmode
def create_discretization(queue, ndim,
                          nelements=42,
                          mesh_name=None,
                          order=4):
    ctx = queue.context

    # construct mesh
    if ndim == 2:
        from functools import partial
        from meshmode.mesh.generation import make_curve_mesh, ellipse, starfish

        if mesh_name is None:
            mesh_name = "ellipse"

        t = np.linspace(0.0, 1.0, nelements + 1)
        if mesh_name == "ellipse":
            mesh = make_curve_mesh(partial(ellipse, 2), t, order=order)
        elif mesh_name == "starfish":
            mesh = make_curve_mesh(starfish, t, order=order)
        else:
            raise ValueError('unknown mesh name: {}'.format(mesh_name))
    elif ndim == 3:
        from meshmode.mesh.generation import generate_torus
        from meshmode.mesh.generation import generate_warped_rect_mesh

        if mesh_name is None:
            mesh_name = "torus"

        if mesh_name == "torus":
            mesh = generate_torus(10.0, 5.0, order=order,
                    n_inner=nelements, n_outer=nelements)
        elif mesh_name == "warp":
            mesh = generate_warped_rect_mesh(ndim, order=order, n=nelements)
        else:
            raise ValueError("unknown mesh name: {}".format(mesh_name))
    else:
        raise ValueError("unsupported dimension: {}".format(ndim))

    # create discretization
    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    discr = Discretization(ctx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(order))

    return discr
コード例 #7
0
ファイル: test_mesh.py プロジェクト: MTCam/meshmode
def test_is_affine_group_check(mesh_name):
    order = 4
    nelements = 16

    if mesh_name.startswith("box"):
        dim = int(mesh_name[-2])
        is_affine = True
        mesh = mgen.generate_regular_rect_mesh(
            a=(-0.5, ) * dim,
            b=(0.5, ) * dim,
            nelements_per_axis=(nelements, ) * dim,
            order=order)
    elif mesh_name.startswith("warped_box"):
        dim = int(mesh_name[-2])
        is_affine = False
        mesh = mgen.generate_warped_rect_mesh(dim,
                                              order,
                                              nelements_side=nelements)
    elif mesh_name == "cross_warped_box":
        dim = 2
        is_affine = False
        mesh = _generate_cross_warped_rect_mesh(dim, order, nelements)
    elif mesh_name == "circle":
        is_affine = False
        mesh = mgen.make_curve_mesh(lambda t: mgen.ellipse(1.0, t),
                                    np.linspace(0.0, 1.0, nelements + 1),
                                    order=order)
    elif mesh_name == "ellipse":
        is_affine = False
        mesh = mgen.make_curve_mesh(lambda t: mgen.ellipse(2.0, t),
                                    np.linspace(0.0, 1.0, nelements + 1),
                                    order=order)
    elif mesh_name == "sphere":
        is_affine = False
        mesh = mgen.generate_icosphere(r=1.0, order=order)
    elif mesh_name == "torus":
        is_affine = False
        mesh = mgen.generate_torus(10.0, 2.0, order=order)
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    assert all(grp.is_affine for grp in mesh.groups) == is_affine
コード例 #8
0
ファイル: test_visualization.py プロジェクト: MTCam/meshmode
def test_vtk_overwrite(actx_factory):
    pytest.importorskip("pyvisfile")

    def _try_write_vtk(writer, obj):
        import os

        filename = "vtk_overwrite_temp.vtu"
        if os.path.exists(filename):
            os.remove(filename)

        writer(filename, [])
        with pytest.raises(FileExistsError):
            writer(filename, [])

        writer(filename, [], overwrite=True)
        if os.path.exists(filename):
            os.remove(filename)

    actx = actx_factory()
    target_order = 7

    mesh = mgen.generate_torus(10.0, 2.0, order=target_order)

    from meshmode.discretization import Discretization
    discr = Discretization(
        actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from meshmode.discretization.visualization import make_visualizer
    from meshmode.discretization.visualization import \
            write_nodal_adjacency_vtk_file
    from meshmode.mesh.visualization import write_vertex_vtk_file

    vis = make_visualizer(actx, discr, 1)
    _try_write_vtk(vis.write_vtk_file, discr)

    _try_write_vtk(
        lambda x, y, **kwargs: write_vertex_vtk_file(discr.mesh, x, **kwargs),
        discr.mesh)
    _try_write_vtk(
        lambda x, y, **kwargs: write_nodal_adjacency_vtk_file(
            x, discr.mesh, **kwargs), discr.mesh)
コード例 #9
0
def test_refine_surfaces(actx_factory, mesh_name, visualize=False):
    if mesh_name == "torus":
        mesh = mgen.generate_torus(10, 1, 40, 4, order=4)
    elif mesh_name == "icosphere":
        mesh = mgen.generate_icosphere(1, order=4)
    else:
        raise ValueError(f"invalid mesh name '{mesh_name}'")

    if visualize:
        actx = actx_factory()
        from meshmode.mesh.visualization import vtk_visualize_mesh
        vtk_visualize_mesh(actx, mesh, "surface.vtu")

    # check for absence of node-vertex consistency error
    from meshmode.mesh.refinement import refine_uniformly
    refined_mesh = refine_uniformly(mesh, 1)

    if visualize:
        actx = actx_factory()
        from meshmode.mesh.visualization import vtk_visualize_mesh
        vtk_visualize_mesh(actx, refined_mesh, "surface-refined.vtu")
コード例 #10
0
def test_tangential_onb(ctx_factory):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(5, 2, order=3)

    discr = Discretization(actx, mesh,
                           InterpolatoryQuadratureSimplexGroupFactory(3))

    tob = sym.tangential_onb(mesh.ambient_dim)
    nvecs = tob.shape[1]

    # make sure tangential_onb is mutually orthogonal and normalized
    orth_check = bind(
        discr,
        sym.make_obj_array([
            np.dot(tob[:, i], tob[:, j]) - (1 if i == j else 0)
            for i in range(nvecs) for j in range(nvecs)
        ]))(actx)

    from meshmode.dof_array import flatten
    orth_check = flatten(orth_check)
    for i, orth_i in enumerate(orth_check):
        assert (cl.clmath.fabs(orth_i) < 1e-13).get().all()

    # make sure tangential_onb is orthogonal to normal
    orth_check = bind(
        discr,
        sym.make_obj_array([
            np.dot(tob[:, i],
                   sym.normal(mesh.ambient_dim).as_vector())
            for i in range(nvecs)
        ]))(actx)

    orth_check = flatten(orth_check)
    for i, orth_i in enumerate(orth_check):
        assert (cl.clmath.fabs(orth_i) < 1e-13).get().all()
コード例 #11
0
ファイル: test_cost_model.py プロジェクト: mattwala/pytential
def get_lpot_source(queue, dim):
    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import (
        InterpolatoryQuadratureSimplexGroupFactory)

    target_order = TARGET_ORDER

    if dim == 2:
        from meshmode.mesh.generation import starfish, make_curve_mesh
        mesh = make_curve_mesh(starfish,
                               np.linspace(0, 1, 50),
                               order=target_order)
    elif dim == 3:
        from meshmode.mesh.generation import generate_torus
        mesh = generate_torus(2, 1, order=target_order)
    else:
        raise ValueError("unsupported dimension: %d" % dim)

    pre_density_discr = Discretization(
        queue.context, mesh,
        InterpolatoryQuadratureSimplexGroupFactory(target_order))

    lpot_kwargs = DEFAULT_LPOT_KWARGS.copy()
    lpot_kwargs.update(
        _expansion_stick_out_factor=TCF,
        fmm_order=FMM_ORDER,
        qbx_order=QBX_ORDER,
        fmm_backend="fmmlib",
    )

    from pytential.qbx import QBXLayerPotentialSource
    lpot_source = QBXLayerPotentialSource(pre_density_discr,
                                          OVSMP_FACTOR * target_order,
                                          **lpot_kwargs)

    lpot_source, _ = lpot_source.with_refinement()

    return lpot_source
コード例 #12
0
def get_torus_with_ref_mean_curvature(cl_ctx, h):
    order = 4
    r_minor = 1.0
    r_major = 3.0

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(r_major, r_minor, n_major=h, n_minor=h, order=order)
    discr = Discretization(cl_ctx, mesh,
                           InterpolatoryQuadratureSimplexGroupFactory(order))

    with cl.CommandQueue(cl_ctx) as queue:
        nodes = discr.nodes().get(queue=queue)

    # copied from meshmode.mesh.generation.generate_torus
    a = r_major
    b = r_minor

    u = np.arctan2(nodes[1], nodes[0])
    rvec = np.array([np.cos(u), np.sin(u), np.zeros_like(u)])
    rvec = np.sum(nodes * rvec, axis=0) - a
    cosv = np.cos(np.arctan2(nodes[2], rvec))

    return discr, (a + 2.0 * b * cosv) / (2 * b * (a + b * cosv))
コード例 #13
0
def get_torus_with_ref_mean_curvature(actx, h):
    order = 4
    r_minor = 1.0
    r_major = 3.0

    from meshmode.mesh.generation import generate_torus
    mesh = generate_torus(r_major, r_minor, n_major=h, n_minor=h, order=order)
    discr = Discretization(actx, mesh,
                           InterpolatoryQuadratureSimplexGroupFactory(order))

    from meshmode.dof_array import thaw
    nodes = thaw(actx, discr.nodes())

    # copied from meshmode.mesh.generation.generate_torus
    a = r_major
    b = r_minor

    u = actx.np.arctan2(nodes[1], nodes[0])
    from pytools.obj_array import flat_obj_array
    rvec = flat_obj_array(actx.np.cos(u), actx.np.sin(u), 0 * u)
    rvec = sum(nodes * rvec) - a
    cosv = actx.np.cos(actx.np.arctan2(nodes[2], rvec))

    return discr, (a + 2.0 * b * cosv) / (2 * b * (a + b * cosv))
コード例 #14
0
def main():
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)

    from meshmode.mesh.generation import generate_torus

    rout = 10
    rin = 1
    if 1:
        base_mesh = generate_torus(rout, rin, 40, 4, mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        # nx = 1
        # ny = 1
        nz = 1
        dz = 0
        meshes = [
            affine_map(base_mesh,
                       A=np.diag([1, 1, 1]),
                       b=np.array([0, 0, iz * dz])) for iz in range(nz)
        ]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if 0:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()

    pre_density_discr = Discretization(
        cl_ctx, mesh,
        InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (QBXLayerPotentialSource,
                               QBXTargetAssociationFailedException)
    qbx, _ = QBXLayerPotentialSource(
        pre_density_discr,
        fine_order=bdry_ovsmp_quad_order,
        qbx_order=qbx_order,
        fmm_order=fmm_order,
    ).with_refinement()
    density_discr = qbx.density_discr

    # {{{ describe bvp

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

    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)

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    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

    nodes = density_discr.nodes().with_queue(queue)
    source = np.array([rout, 0, 0])

    def u_incoming_func(x):
        #        return 1/cl.clmath.sqrt( (x[0] - source[0])**2
        #                                +(x[1] - source[1])**2
        #                                +(x[2] - source[2])**2 )
        return 1.0 / la.norm(x.get() - source[:, None], axis=0)

    bc = cl.array.to_device(queue, u_incoming_func(nodes))

    bvp_rhs = bind(qbx, sqrt_w * sym.var("bc"))(queue, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(bound_op.scipy_op(queue, "sigma", dtype=np.float64),
                         bvp_rhs,
                         tol=1e-14,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)

    sigma = bind(qbx,
                 sym.var("sigma") / sqrt_w)(queue, sigma=gmres_result.solution)

    # }}}

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(queue, density_discr, 20)
    bdry_vis.write_vtk_file("laplace.vtu", [
        ("sigma", sigma),
    ])

    # {{{ postprocess/visualize

    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))

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50)

    targets = cl.array.to_device(queue, fplot.points)

    qbx_stick_out = qbx.copy(target_stick_out_factor=0.2)

    try:
        fld_in_vol = bind((qbx_stick_out, PointsTarget(targets)),
                          representation_sym)(queue, sigma=sigma).get()
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file("failed-targets.vts",
                             [("failed", e.failed_target_flags.get(queue))])
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("potential-laplace-3d.vts", [
        ("potential", fld_in_vol),
    ])
コード例 #15
0
ファイル: extra_int_eq_data.py プロジェクト: isuruf/pytential
    def get_mesh(self, resolution, mesh_order):
        from meshmode.mesh.generation import generate_torus
        mesh = generate_torus(self.r_major, self.r_minor, order=mesh_order)

        from meshmode.mesh.refinement import refine_uniformly
        return refine_uniformly(mesh, resolution)
コード例 #16
0
ファイル: test_layer_pot.py プロジェクト: inducer/pytential
def test_3d_jump_relations(ctx_factory, relation, visualize=False):
    # logging.basicConfig(level=logging.INFO)

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

    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_outer=2*nel_factor, n_inner=nel_factor)

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
        pre_discr = Discretization(
                cl_ctx, 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"
                ).with_refinement()

        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)))

        x, y, z = qbx.density_discr.nodes().with_queue(queue)
        m = cl.clmath

        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(
                    qbx,
                    sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))(
                            queue,
                            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(qbx, jump_identity_sym)
        jump_identity = bound_jump_identity(queue, density=density)

        err = (
                norm(qbx, queue, jump_identity, np.inf)
                / norm(qbx, queue, density, np.inf))
        print("ERROR", qbx.h_max, err)

        eoc_rec.add_data_point(qbx.h_max, err)

        # {{{ visualization

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

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

            bdry_normals = bind(qbx, sym.normal(3))(queue)\
                    .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":
            sp_ext = bind(qbx, sym.Sp(knl, density_sym, qbx_forced_limit=+1))(
                    queue, density=density)
            sp_avg = bind(qbx, sym.Sp(knl, density_sym, qbx_forced_limit="avg"))(
                    queue, density=density)

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

            bdry_normals = bind(qbx, sym.normal(3))(queue)\
                    .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
コード例 #17
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
コード例 #18
0
ファイル: test_meshmode.py プロジェクト: MTCam/meshmode
        eoc_rec.add_data_point(h, err)

    print(eoc_rec)
    assert (eoc_rec.order_estimate() >= order - 0.5
            or eoc_rec.max_error() < 1.7e-13)


# }}}

# {{{ element orientation: canned 3D meshes


# python test_meshmode.py "test_sanity_balls(cl._csc, "disk-radius-1.step", 2, 2, visualize=True)"  # noqa
@pytest.mark.parametrize(("what", "mesh_gen_func"), [
    ("ball", lambda: mgen.generate_icosahedron(1, 1)),
    ("torus", lambda: mgen.generate_torus(5, 1)),
])
def test_orientation_3d(actx_factory, what, mesh_gen_func, visualize=False):
    pytest.importorskip("pytential")

    logging.basicConfig(level=logging.INFO)
    actx = actx_factory()

    mesh = mesh_gen_func()

    logger.info("%d elements", mesh.nelements)

    from meshmode.discretization import Discretization
    discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3))

    from pytential import bind, sym
コード例 #19
0
ファイル: test_meshmode.py プロジェクト: inducer/meshmode
    mesh = perform_flips(mesh, flippy, skip_tests=True)

    mesh_orient = find_volume_mesh_element_orientations(mesh)

    assert ((mesh_orient < 0) == (flippy > 0)).all()

# }}}


# {{{ element orientation: canned 3D meshes

# python test_meshmode.py 'test_sanity_balls(cl._csc, "disk-radius-1.step", 2, 2, visualize=True)'  # noqa
@pytest.mark.parametrize(("what", "mesh_gen_func"), [
    ("ball", lambda: mgen.generate_icosahedron(1, 1)),
    ("torus", lambda: mgen.generate_torus(5, 1)),
    ])
def test_3d_orientation(ctx_factory, what, mesh_gen_func, visualize=False):
    pytest.importorskip("pytential")

    logging.basicConfig(level=logging.INFO)

    ctx = ctx_factory()
    queue = cl.CommandQueue(ctx)

    mesh = mesh_gen_func()

    logger.info("%d elements" % mesh.nelements)

    from meshmode.discretization import Discretization
    discr = Discretization(ctx, mesh,
コード例 #20
0
def main():
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)

    from meshmode.mesh.generation import generate_torus

    rout = 10
    rin = 1
    if 1:
        base_mesh = generate_torus(
                rout, rin, 40, 4,
                mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        # nx = 1
        # ny = 1
        nz = 1
        dz = 0
        meshes = [
                affine_map(
                    base_mesh,
                    A=np.diag([1, 1, 1]),
                    b=np.array([0, 0, iz*dz]))
                for iz in range(nz)]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if 0:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()

    pre_density_discr = Discretization(
            cl_ctx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (
            QBXLayerPotentialSource, QBXTargetAssociationFailedException)
    qbx, _ = QBXLayerPotentialSource(
            pre_density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order,
            fmm_order=fmm_order
            ).with_refinement()
    density_discr = qbx.density_discr

    # {{{ describe bvp

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

    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)

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    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

    nodes = density_discr.nodes().with_queue(queue)
    source = np.array([rout, 0, 0])

    def u_incoming_func(x):
        #        return 1/cl.clmath.sqrt( (x[0] - source[0])**2
        #                                +(x[1] - source[1])**2
        #                                +(x[2] - source[2])**2 )
        return 1.0/la.norm(x.get()-source[:, None], axis=0)

    bc = cl.array.to_device(queue, u_incoming_func(nodes))

    bvp_rhs = bind(qbx, sqrt_w*sym.var("bc"))(queue, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(
            bound_op.scipy_op(queue, "sigma", dtype=np.float64),
            bvp_rhs, tol=1e-14, progress=True,
            stall_iterations=0,
            hard_failure=True)

    sigma = bind(qbx, sym.var("sigma")/sqrt_w)(queue, sigma=gmres_result.solution)

    # }}}

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(queue, density_discr, 20)
    bdry_vis.write_vtk_file("laplace.vtu", [
        ("sigma", sigma),
        ])

    # {{{ postprocess/visualize

    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))

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50)

    targets = cl.array.to_device(queue, fplot.points)

    qbx_stick_out = qbx.copy(target_stick_out_factor=0.2)

    try:
        fld_in_vol = bind(
                (qbx_stick_out, PointsTarget(targets)),
                representation_sym)(queue, sigma=sigma).get()
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file(
                "failed-targets.vts",
                [
                    ("failed", e.failed_target_flags.get(queue))
                    ]
                )
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file(
            "potential-laplace-3d.vts",
            [
                ("potential", fld_in_vol),
                ]
            )
コード例 #21
0
ファイル: test_visualization.py プロジェクト: MTCam/meshmode
def test_visualizers(actx_factory, dim, group_cls):
    actx = actx_factory()

    nelements = 64
    target_order = 4

    is_simplex = issubclass(group_cls, SimplexElementGroup)
    if dim == 1:
        mesh = mgen.make_curve_mesh(mgen.NArmedStarfish(5, 0.25),
                                    np.linspace(0.0, 1.0, nelements + 1),
                                    target_order)
    elif dim == 2:
        if is_simplex:
            mesh = mgen.generate_torus(5.0, 1.0, order=target_order)
        else:
            mesh = mgen.generate_regular_rect_mesh(a=(0, ) * dim,
                                                   b=(1, ) * dim,
                                                   nelements_per_axis=(4, ) *
                                                   dim,
                                                   group_cls=group_cls,
                                                   order=target_order)
    elif dim == 3:
        if is_simplex:
            mesh = mgen.generate_warped_rect_mesh(dim,
                                                  target_order,
                                                  nelements_side=4)
        else:
            mesh = mgen.generate_regular_rect_mesh(a=(0, ) * dim,
                                                   b=(1, ) * dim,
                                                   nelements_per_axis=(4, ) *
                                                   dim,
                                                   group_cls=group_cls,
                                                   order=target_order)
    else:
        raise ValueError("unknown dimensionality")

    if is_simplex:
        group_factory = InterpolatoryQuadratureSimplexGroupFactory
    else:
        group_factory = LegendreGaussLobattoTensorProductGroupFactory

    from meshmode.discretization import Discretization
    discr = Discretization(actx, mesh, group_factory(target_order))

    nodes = thaw(discr.nodes(), actx)
    f = actx.np.sqrt(sum(nodes**2)) + 1j * nodes[0]
    g = VisualizerData(g=f)
    names_and_fields = [("f", f), ("g", g)]
    names_and_fields = [("f", f)]

    from meshmode.discretization.visualization import make_visualizer
    vis = make_visualizer(actx, discr, target_order)

    eltype = "simplex" if is_simplex else "box"
    basename = f"visualizer_vtk_{eltype}_{dim}d"
    vis.write_vtk_file(f"{basename}_linear.vtu",
                       names_and_fields,
                       overwrite=True)

    with pytest.raises(RuntimeError):
        vis.write_vtk_file(f"{basename}_lagrange.vtu",
                           names_and_fields,
                           overwrite=True,
                           use_high_order=True)

    try:
        basename = f"visualizer_xdmf_{eltype}_{dim}d"
        vis.write_xdmf_file(f"{basename}.xmf",
                            names_and_fields,
                            overwrite=True)
    except ImportError:
        logger.info("h5py not available")

    if mesh.dim == 2 and is_simplex:
        try:
            vis.show_scalar_in_matplotlib_3d(f, do_show=False)
        except ImportError:
            logger.info("matplotlib not available")

    if mesh.dim <= 2 and is_simplex:
        try:
            vis.show_scalar_in_mayavi(f, do_show=False)
        except ImportError:
            logger.info("mayavi not available")

    vis = make_visualizer(actx, discr, target_order, force_equidistant=True)

    basename = f"visualizer_vtk_{eltype}_{dim}d"
    vis.write_vtk_file(f"{basename}_lagrange.vtu",
                       names_and_fields,
                       overwrite=True,
                       use_high_order=True)
コード例 #22
0
def main(mesh_name="torus", visualize=False):
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

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

    if mesh_name == "torus":
        rout = 10
        rin = 1

        from meshmode.mesh.generation import generate_torus
        base_mesh = generate_torus(
                rout, rin, 40, 4,
                mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        # nx = 1
        # ny = 1
        nz = 1
        dz = 0
        meshes = [
                affine_map(
                    base_mesh,
                    A=np.diag([1, 1, 1]),
                    b=np.array([0, 0, iz*dz]))
                for iz in range(nz)]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if visualize:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (
            QBXLayerPotentialSource, QBXTargetAssociationFailedException)
    qbx = QBXLayerPotentialSource(
            pre_density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order,
            fmm_order=fmm_order,
            )

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50)
    targets = actx.from_numpy(fplot.points)

    from pytential import GeometryCollection
    places = GeometryCollection({
        "qbx": qbx,
        "qbx_target_assoc": qbx.copy(target_association_tolerance=0.2),
        "targets": PointsTarget(targets)
        }, auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    # {{{ describe bvp

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

    sigma_sym = sym.var("sigma")
    #sqrt_w = sym.sqrt_jac_q_weight(3)
    sqrt_w = 1
    inv_sqrt_w_sigma = sym.cse(sigma_sym/sqrt_w)

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    bdry_op_sym = (loc_sign*0.5*sigma_sym
            + sqrt_w*(
                sym.S(kernel, inv_sqrt_w_sigma, qbx_forced_limit=+1)
                + sym.D(kernel, inv_sqrt_w_sigma, qbx_forced_limit="avg")
                ))

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    from meshmode.dof_array import thaw, flatten, unflatten
    nodes = thaw(actx, density_discr.nodes())
    source = np.array([rout, 0, 0])

    def u_incoming_func(x):
        from pytools.obj_array import obj_array_vectorize
        x = obj_array_vectorize(actx.to_numpy, flatten(x))
        x = np.array(list(x))
        #        return 1/cl.clmath.sqrt( (x[0] - source[0])**2
        #                                +(x[1] - source[1])**2
        #                                +(x[2] - source[2])**2 )
        return 1.0/la.norm(x - source[:, None], axis=0)

    bc = unflatten(actx,
            density_discr,
            actx.from_numpy(u_incoming_func(nodes)))

    bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(actx, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(
            bound_op.scipy_op(actx, "sigma", dtype=np.float64),
            bvp_rhs, tol=1e-14, progress=True,
            stall_iterations=0,
            hard_failure=True)

    sigma = bind(places, sym.var("sigma")/sqrt_w)(
            actx, sigma=gmres_result.solution)

    # }}}

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(actx, density_discr, 20)
    bdry_vis.write_vtk_file("laplace.vtu", [
        ("sigma", sigma),
        ])

    # {{{ postprocess/visualize

    repr_kwargs = dict(
            source="qbx_target_assoc",
            target="targets",
            qbx_forced_limit=None)
    representation_sym = (
            sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs)
            + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs))

    try:
        fld_in_vol = actx.to_numpy(
                bind(places, representation_sym)(actx, sigma=sigma))
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file("laplace-dirichlet-3d-failed-targets.vts", [
            ("failed", e.failed_target_flags.get(queue)),
            ])
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("laplace-dirichlet-3d-potential.vts", [
        ("potential", fld_in_vol),
        ])
コード例 #23
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