コード例 #1
0
def test_mass_operator_inverse(actx_factory, name):
    actx = actx_factory()

    # {{{ cases

    import mesh_data
    if name == "2-1-ellipse":
        # curve
        builder = mesh_data.EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0)
    elif name == "spheroid":
        # surface
        builder = mesh_data.SpheroidMeshBuilder()
    elif name.startswith("warped_rect"):
        builder = mesh_data.WarpedRectMeshBuilder(dim=int(name[-1]))

    else:
        raise ValueError("unknown geometry name: %s" % name)

    # }}}

    # {{{ inv(m) @ m == id

    from pytools.convergence import EOCRecorder
    eoc = EOCRecorder()

    for resolution in builder.resolutions:
        mesh = builder.get_mesh(resolution, builder.mesh_order)
        dcoll = DiscretizationCollection(actx, mesh, order=builder.order)
        volume_discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME)

        logger.info("ndofs:     %d", volume_discr.ndofs)
        logger.info("nelements: %d", volume_discr.mesh.nelements)

        # {{{ compute inverse mass

        def f(x):
            return actx.np.cos(4.0 * x[0])

        dd = dof_desc.DD_VOLUME
        x_volm = thaw(volume_discr.nodes(), actx)
        f_volm = f(x_volm)
        f_inv = op.inverse_mass(dcoll, op.mass(dcoll, dd, f_volm))

        inv_error = actx.to_numpy(
            op.norm(dcoll, f_volm - f_inv, 2) / op.norm(dcoll, f_volm, 2))

        # }}}

        # compute max element size
        from grudge.dt_utils import h_max_from_volume

        h_max = h_max_from_volume(dcoll)

        eoc.add_data_point(h_max, inv_error)

    logger.info("inverse mass error\n%s", str(eoc))

    # NOTE: both cases give 1.0e-16-ish at the moment, but just to be on the
    # safe side, choose a slightly larger tolerance
    assert eoc.max_error() < 1.0e-14
コード例 #2
0
def test_bessel(actx_factory):
    actx = actx_factory()

    dims = 2

    mesh = mgen.generate_regular_rect_mesh(a=(0.1, ) * dims,
                                           b=(1.0, ) * dims,
                                           nelements_per_axis=(8, ) * dims)

    dcoll = DiscretizationCollection(actx, mesh, order=3)

    nodes = thaw(dcoll.nodes(), actx)
    r = actx.np.sqrt(nodes[0]**2 + nodes[1]**2)

    # FIXME: Bessel functions need to brought out of the symbolic
    # layer. Related issue: https://github.com/inducer/grudge/issues/93
    def bessel_j(actx, n, r):
        from grudge import sym, bind
        return bind(dcoll, sym.bessel_j(n, sym.var("r")))(actx, r=r)

    # https://dlmf.nist.gov/10.6.1
    n = 3
    bessel_zero = (bessel_j(actx, n + 1, r) + bessel_j(actx, n - 1, r) -
                   2 * n / r * bessel_j(actx, n, r))

    z = op.norm(dcoll, bessel_zero, 2)

    assert z < 1e-15
コード例 #3
0
    def flux(self, u_tpair):
        actx = u_tpair.int.array_context
        dd = u_tpair.dd
        normal = thaw(self.dcoll.normal(dd), actx)
        v_dot_normal = np.dot(self.v, normal)

        return u_tpair.int * v_dot_normal - self.weak_flux(u_tpair)
コード例 #4
0
def simple_mpi_communication_entrypoint():
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue, force_device_scalars=True)

    from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis
    from meshmode.mesh import BTAG_ALL

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    num_parts = comm.Get_size()

    mesh_dist = MPIMeshDistributor(comm)

    if mesh_dist.is_mananger_rank():
        from meshmode.mesh.generation import generate_regular_rect_mesh
        mesh = generate_regular_rect_mesh(a=(-1,)*2,
                                          b=(1,)*2,
                                          nelements_per_axis=(2,)*2)

        part_per_element = get_partition_by_pymetis(mesh, num_parts)

        local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts)
    else:
        local_mesh = mesh_dist.receive_mesh_part()

    dcoll = DiscretizationCollection(actx, local_mesh, order=5,
            mpi_communicator=comm)

    x = thaw(dcoll.nodes(), actx)
    myfunc = actx.np.sin(np.dot(x, [2, 3]))

    from grudge.dof_desc import as_dofdesc

    dd_int = as_dofdesc("int_faces")
    dd_vol = as_dofdesc("vol")
    dd_af = as_dofdesc("all_faces")

    all_faces_func = op.project(dcoll, dd_vol, dd_af, myfunc)
    int_faces_func = op.project(dcoll, dd_vol, dd_int, myfunc)
    bdry_faces_func = op.project(dcoll, BTAG_ALL, dd_af,
                                 op.project(dcoll, dd_vol, BTAG_ALL, myfunc))

    hopefully_zero = (
        op.project(
            dcoll, "int_faces", "all_faces",
            dcoll.opposite_face_connection()(int_faces_func)
        )
        + sum(op.project(dcoll, tpair.dd, "all_faces", tpair.int)
              for tpair in op.cross_rank_trace_pairs(dcoll, myfunc))
    ) - (all_faces_func - bdry_faces_func)

    error = actx.to_numpy(flat_norm(hopefully_zero, ord=np.inf))

    print(__file__)
    with np.printoptions(threshold=100000000, suppress=True):
        logger.debug(hopefully_zero)
    logger.info("error: %.5e", error)

    assert error < 1e-14
コード例 #5
0
ファイル: test_mpi_communication.py プロジェクト: sll2/grudge
 def source_f(actx, dcoll, t=0):
     source_center = np.array([0.1, 0.22, 0.33])[:dcoll.dim]
     source_width = 0.05
     source_omega = 3
     nodes = thaw(dcoll.nodes(), actx)
     source_center_dist = flat_obj_array(
         [nodes[i] - source_center[i] for i in range(dcoll.dim)])
     return (np.sin(source_omega * t) * actx.np.exp(
         -np.dot(source_center_dist, source_center_dist) / source_width**2))
コード例 #6
0
    def conv_test(descr, use_quad):
        logger.info("-" * 75)
        logger.info(descr)
        logger.info("-" * 75)
        eoc_rec = EOCRecorder()

        if use_quad:
            qtag = dof_desc.DISCR_TAG_QUAD
        else:
            qtag = None

        ns = [20, 25]
        for n in ns:
            mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dims,
                                                   b=(0.5, ) * dims,
                                                   nelements_per_axis=(n, ) *
                                                   dims,
                                                   order=order)

            if use_quad:
                discr_tag_to_group_factory = {
                    qtag: QuadratureSimplexGroupFactory(order=4 * order)
                }
            else:
                discr_tag_to_group_factory = {}

            dcoll = DiscretizationCollection(
                actx,
                mesh,
                order=order,
                discr_tag_to_group_factory=discr_tag_to_group_factory)

            nodes = thaw(dcoll.nodes(), actx)

            def zero_inflow(dtag, t=0):
                dd = dof_desc.DOFDesc(dtag, qtag)
                return dcoll.discr_from_dd(dd).zeros(actx)

            adv_op = VariableCoefficientAdvectionOperator(
                dcoll,
                flat_obj_array(-1 * nodes[1], nodes[0]),
                inflow_u=lambda t: zero_inflow(BTAG_ALL, t=t),
                flux_type="upwind",
                quad_tag=qtag)

            total_error = op.norm(dcoll,
                                  adv_op.operator(0, gaussian_mode(nodes)), 2)
            eoc_rec.add_data_point(1.0 / n, actx.to_numpy(total_error))

        logger.info(
            "\n%s",
            eoc_rec.pretty_print(abscissa_label="h", error_label="L2 Error"))

        return eoc_rec.order_estimate(), np.array(
            [x[1] for x in eoc_rec.history])
コード例 #7
0
def test_2d_gauss_theorem(actx_factory):
    """Verify Gauss's theorem explicitly on a mesh"""

    pytest.importorskip("meshpy")

    from meshpy.geometry import make_circle, GeometryBuilder
    from meshpy.triangle import MeshInfo, build

    geob = GeometryBuilder()
    geob.add_geometry(*make_circle(1))
    mesh_info = MeshInfo()
    geob.set(mesh_info)

    mesh_info = build(mesh_info)

    from meshmode.mesh.io import from_meshpy
    from meshmode.mesh import BTAG_ALL

    mesh = from_meshpy(mesh_info, order=1)

    actx = actx_factory()

    dcoll = DiscretizationCollection(actx, mesh, order=2)
    volm_disc = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
    x_volm = thaw(volm_disc.nodes(), actx)

    def f(x):
        return flat_obj_array(
            actx.np.sin(3 * x[0]) + actx.np.cos(3 * x[1]),
            actx.np.sin(2 * x[0]) + actx.np.cos(x[1]))

    f_volm = f(x_volm)
    int_1 = op.integral(dcoll, "vol", op.local_div(dcoll, f_volm))

    prj_f = op.project(dcoll, "vol", BTAG_ALL, f_volm)
    normal = thaw(dcoll.normal(BTAG_ALL), actx)
    int_2 = op.integral(dcoll, BTAG_ALL, prj_f.dot(normal))

    assert abs(int_1 - int_2) < 1e-13
コード例 #8
0
ファイル: test_op.py プロジェクト: nchristensen/grudge
 def get_flux(u_tpair):
     dd = u_tpair.dd
     dd_allfaces = dd.with_dtag("all_faces")
     normal = thaw(dcoll.normal(dd), actx)
     u_avg = u_tpair.avg
     if vectorize:
         if nested:
             flux = make_obj_array(
                 [u_avg_i * normal for u_avg_i in u_avg])
         else:
             flux = np.outer(u_avg, normal)
     else:
         flux = u_avg * normal
     return op.project(dcoll, dd, dd_allfaces, flux)
コード例 #9
0
def v_dot_n_tpair(actx, dcoll, velocity, trace_dd):
    from grudge.dof_desc import DTAG_BOUNDARY
    from grudge.trace_pair import TracePair
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

    normal = thaw(dcoll.normal(trace_dd.with_discr_tag(None)), actx)
    v_dot_n = velocity.dot(normal)
    i = op.project(dcoll, trace_dd.with_discr_tag(None), trace_dd, v_dot_n)

    if trace_dd.domain_tag is FACE_RESTR_INTERIOR:
        e = dcoll.opposite_face_connection()(i)
    elif isinstance(trace_dd.domain_tag, DTAG_BOUNDARY):
        e = dcoll.distributed_boundary_swap_connection(trace_dd)(i)
    else:
        raise ValueError("Unrecognized domain tag: %s" % trace_dd.domain_tag)

    return TracePair(trace_dd, interior=i, exterior=e)
コード例 #10
0
ファイル: test_grudge.py プロジェクト: nchristensen/grudge
def test_norm_complex(actx_factory, p):
    actx = actx_factory()

    dim = 2
    mesh = mgen.generate_regular_rect_mesh(a=(0, ) * dim,
                                           b=(1, ) * dim,
                                           nelements_per_axis=(8, ) * dim,
                                           order=1)
    dcoll = DiscretizationCollection(actx, mesh, order=4)
    nodes = thaw(dcoll.nodes(), actx)

    norm = op.norm(dcoll, (1 + 1j) * nodes[0], p)
    if p == 2:
        ref_norm = (2 / 3)**0.5
    elif p == np.inf:
        ref_norm = 2**0.5

    logger.info("norm: %.5e %.5e", norm, ref_norm)
    assert abs(norm - ref_norm) / abs(ref_norm) < 1e-13
コード例 #11
0
def advection_weak_flux(dcoll, flux_type, u_tpair, velocity):
    r"""Compute the numerical flux for the advection operator
    $(v \cdot \nabla)u$.
    """
    actx = u_tpair.int.array_context
    dd = u_tpair.dd
    normal = thaw(dcoll.normal(dd), actx)
    v_dot_n = np.dot(velocity, normal)

    flux_type = flux_type.lower()
    if flux_type == "central":
        return u_tpair.avg * v_dot_n
    elif flux_type == "lf":
        norm_v = np.sqrt(sum(velocity**2))
        return u_tpair.avg * v_dot_n + 0.5 * norm_v * (u_tpair.int -
                                                       u_tpair.ext)
    elif flux_type == "upwind":
        u_upwind = actx.np.where(v_dot_n > 0, u_tpair.int, u_tpair.ext)
        return u_upwind * v_dot_n
    else:
        raise ValueError(f"flux '{flux_type}' is not implemented")
コード例 #12
0
def test_tri_diff_mat(actx_factory, dim, order=4):
    """Check differentiation matrix along the coordinate axes on a disk

    Uses sines as the function to differentiate.
    """

    actx = actx_factory()

    from pytools.convergence import EOCRecorder
    axis_eoc_recs = [EOCRecorder() for axis in range(dim)]

    def f(x, axis):
        return actx.np.sin(3 * x[axis])

    def df(x, axis):
        return 3 * actx.np.cos(3 * x[axis])

    for n in [4, 8, 16]:
        mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                               b=(0.5, ) * dim,
                                               nelements_per_axis=(n, ) * dim,
                                               order=4)

        dcoll = DiscretizationCollection(actx, mesh, order=4)
        volume_discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
        x = thaw(volume_discr.nodes(), actx)

        for axis in range(dim):
            df_num = op.local_grad(dcoll, f(x, axis))[axis]
            df_volm = df(x, axis)

            linf_error = flat_norm(df_num - df_volm, ord=np.inf)
            axis_eoc_recs[axis].add_data_point(1 / n,
                                               actx.to_numpy(linf_error))

    for axis, eoc_rec in enumerate(axis_eoc_recs):
        logger.info("axis %d\n%s", axis, eoc_rec)
        assert eoc_rec.order_estimate() > order - 0.25
コード例 #13
0
def test_mass_mat_trig(actx_factory, ambient_dim, discr_tag):
    """Check the integral of some trig functions on an interval using the mass
    matrix.
    """
    actx = actx_factory()

    nel_1d = 16
    order = 4

    a = -4.0 * np.pi
    b = +9.0 * np.pi
    true_integral = 13 * np.pi / 2 * (b - a)**(ambient_dim - 1)

    from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory
    dd_quad = dof_desc.DOFDesc(dof_desc.DTAG_VOLUME_ALL, discr_tag)
    if discr_tag is dof_desc.DISCR_TAG_BASE:
        discr_tag_to_group_factory = {}
    else:
        discr_tag_to_group_factory = {
            discr_tag: QuadratureSimplexGroupFactory(order=2 * order)
        }

    mesh = mgen.generate_regular_rect_mesh(a=(a, ) * ambient_dim,
                                           b=(b, ) * ambient_dim,
                                           nelements_per_axis=(nel_1d, ) *
                                           ambient_dim,
                                           order=1)
    dcoll = DiscretizationCollection(
        actx,
        mesh,
        order=order,
        discr_tag_to_group_factory=discr_tag_to_group_factory)

    def f(x):
        return actx.np.sin(x[0])**2

    volm_disc = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
    x_volm = thaw(volm_disc.nodes(), actx)
    f_volm = f(x_volm)
    ones_volm = volm_disc.zeros(actx) + 1

    quad_disc = dcoll.discr_from_dd(dd_quad)
    x_quad = thaw(quad_disc.nodes(), actx)
    f_quad = f(x_quad)
    ones_quad = quad_disc.zeros(actx) + 1

    mop_1 = op.mass(dcoll, dd_quad, f_quad)
    num_integral_1 = op.nodal_sum(dcoll, dof_desc.DD_VOLUME, ones_volm * mop_1)

    err_1 = abs(num_integral_1 - true_integral)
    assert err_1 < 2e-9, err_1

    mop_2 = op.mass(dcoll, dd_quad, ones_quad)
    num_integral_2 = op.nodal_sum(dcoll, dof_desc.DD_VOLUME, f_volm * mop_2)

    err_2 = abs(num_integral_2 - true_integral)
    assert err_2 < 2e-9, err_2

    if discr_tag is dof_desc.DISCR_TAG_BASE:
        # NOTE: `integral` always makes a square mass matrix and
        # `QuadratureSimplexGroupFactory` does not have a `mass_matrix` method.
        num_integral_3 = op.nodal_sum(dcoll, dof_desc.DD_VOLUME,
                                      f_quad * mop_2)
        err_3 = abs(num_integral_3 - true_integral)
        assert err_3 < 5e-10, err_3
コード例 #14
0
 def lazy_to_eager(u):
     return thaw(freeze(u, lazy_actx), eager_actx)
コード例 #15
0
 def get_inputs(actx):
     nodes = thaw(discr.nodes(), actx)
     state = init(nodes)
     return state,
コード例 #16
0
 def _my_boundary(discr, btag, gas_model, state_minus, **kwargs):
     actx = state_minus.array_context
     bnd_discr = discr.discr_from_dd(btag)
     nodes = thaw(bnd_discr.nodes(), actx)
     return make_fluid_state(init(x_vec=nodes, eos=gas_model.eos,
                                  **kwargs), gas_model)
コード例 #17
0
 def get_inputs(actx):
     nodes = thaw(discr.nodes(), actx)
     alpha = discr.zeros(actx) + 1
     u = actx.np.cos(np.pi*nodes[0])
     return alpha, u
コード例 #18
0
 def get_inputs(actx):
     nodes = thaw(discr.nodes(), actx)
     u = make_obj_array([actx.np.sin(np.pi*nodes[i]) for i in range(2)])
     return u,
コード例 #19
0
 def get_flux(u_tpair):
     dd = u_tpair.dd
     dd_allfaces = dd.with_dtag("all_faces")
     normal = thaw(discr.normal(dd), u_tpair.int[0].array_context)
     flux = u_tpair.avg @ normal
     return discr.project(dd, dd_allfaces, flux)
コード例 #20
0
ファイル: test_op.py プロジェクト: nchristensen/grudge
 def get_flux(u_tpair):
     dd = u_tpair.dd
     dd_allfaces = dd.with_dtag("all_faces")
     normal = thaw(dcoll.normal(dd), actx)
     flux = u_tpair.avg @ normal
     return op.project(dcoll, dd, dd_allfaces, flux)
コード例 #21
0
ファイル: test_op.py プロジェクト: nchristensen/grudge
def test_gradient(actx_factory,
                  form,
                  dim,
                  order,
                  vectorize,
                  nested,
                  visualize=False):
    actx = actx_factory()

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    for n in [4, 6, 8]:
        mesh = mgen.generate_regular_rect_mesh(a=(-1, ) * dim,
                                               b=(1, ) * dim,
                                               nelements_per_axis=(n, ) * dim)

        dcoll = DiscretizationCollection(actx, mesh, order=order)

        def f(x):
            result = dcoll.zeros(actx) + 1
            for i in range(dim - 1):
                result = result * actx.np.sin(np.pi * x[i])
            result = result * actx.np.cos(np.pi / 2 * x[dim - 1])
            return result

        def grad_f(x):
            result = make_obj_array(
                [dcoll.zeros(actx) + 1 for _ in range(dim)])
            for i in range(dim - 1):
                for j in range(i):
                    result[i] = result[i] * actx.np.sin(np.pi * x[j])
                result[i] = result[i] * np.pi * actx.np.cos(np.pi * x[i])
                for j in range(i + 1, dim - 1):
                    result[i] = result[i] * actx.np.sin(np.pi * x[j])
                result[i] = result[i] * actx.np.cos(np.pi / 2 * x[dim - 1])
            for j in range(dim - 1):
                result[dim - 1] = result[dim - 1] * actx.np.sin(np.pi * x[j])
            result[dim -
                   1] = result[dim - 1] * (-np.pi / 2 *
                                           actx.np.sin(np.pi / 2 * x[dim - 1]))
            return result

        x = thaw(dcoll.nodes(), actx)

        if vectorize:
            u = make_obj_array([(i + 1) * f(x) for i in range(dim)])
        else:
            u = f(x)

        def get_flux(u_tpair):
            dd = u_tpair.dd
            dd_allfaces = dd.with_dtag("all_faces")
            normal = thaw(dcoll.normal(dd), actx)
            u_avg = u_tpair.avg
            if vectorize:
                if nested:
                    flux = make_obj_array(
                        [u_avg_i * normal for u_avg_i in u_avg])
                else:
                    flux = np.outer(u_avg, normal)
            else:
                flux = u_avg * normal
            return op.project(dcoll, dd, dd_allfaces, flux)

        dd_allfaces = DOFDesc("all_faces")

        if form == "strong":
            grad_u = (
                op.local_grad(dcoll, u, nested=nested)
                # No flux terms because u doesn't have inter-el jumps
            )
        elif form == "weak":
            grad_u = op.inverse_mass(
                dcoll,
                -op.weak_local_grad(dcoll, u, nested=nested)  # pylint: disable=E1130
                +  # noqa: W504
                op.face_mass(
                    dcoll,
                    dd_allfaces,
                    # Note: no boundary flux terms here because u_ext == u_int == 0
                    sum(
                        get_flux(utpair)
                        for utpair in op.interior_trace_pairs(dcoll, u))))
        else:
            raise ValueError("Invalid form argument.")

        if vectorize:
            expected_grad_u = make_obj_array([(i + 1) * grad_f(x)
                                              for i in range(dim)])
            if not nested:
                expected_grad_u = np.stack(expected_grad_u, axis=0)
        else:
            expected_grad_u = grad_f(x)

        if visualize:
            from grudge.shortcuts import make_visualizer
            vis = make_visualizer(dcoll,
                                  vis_order=order if dim == 3 else dim + 3)

            filename = (
                f"test_gradient_{form}_{dim}_{order}"
                f"{'_vec' if vectorize else ''}{'_nested' if nested else ''}.vtu"
            )
            vis.write_vtk_file(filename, [
                ("u", u),
                ("grad_u", grad_u),
                ("expected_grad_u", expected_grad_u),
            ],
                               overwrite=True)

        rel_linf_err = actx.to_numpy(
            op.norm(dcoll, grad_u - expected_grad_u, np.inf) /
            op.norm(dcoll, expected_grad_u, np.inf))
        eoc_rec.add_data_point(1. / n, rel_linf_err)

    print("L^inf error:")
    print(eoc_rec)
    assert (eoc_rec.order_estimate() >= order - 0.5
            or eoc_rec.max_error() < 1e-11)
コード例 #22
0
def test_face_normal_surface(actx_factory, mesh_name):
    """Check that face normals are orthogonal to the surface normal"""
    actx = actx_factory()

    # {{{ geometry

    if mesh_name == "2-1-ellipse":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0)
    elif mesh_name == "spheroid":
        from mesh_data import SpheroidMeshBuilder
        builder = SpheroidMeshBuilder()
    else:
        raise ValueError("unknown mesh name: %s" % mesh_name)

    mesh = builder.get_mesh(builder.resolutions[0], builder.mesh_order)
    dcoll = DiscretizationCollection(actx, mesh, order=builder.order)

    volume_discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
    logger.info("ndofs:    %d", volume_discr.ndofs)
    logger.info("nelements: %d", volume_discr.mesh.nelements)

    # }}}

    # {{{ Compute surface and face normals
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR
    from grudge.geometry import normal

    dv = dof_desc.DD_VOLUME
    df = dof_desc.as_dofdesc(FACE_RESTR_INTERIOR)

    ambient_dim = mesh.ambient_dim

    surf_normal = op.project(dcoll, dv, df, normal(actx, dcoll, dd=dv))
    surf_normal = surf_normal / actx.np.sqrt(sum(surf_normal**2))

    face_normal_i = thaw(dcoll.normal(df), actx)
    face_normal_e = dcoll.opposite_face_connection()(face_normal_i)

    if mesh.ambient_dim == 3:
        from grudge.geometry import pseudoscalar, area_element
        # NOTE: there's only one face tangent in 3d
        face_tangent = (pseudoscalar(actx, dcoll, dd=df) /
                        area_element(actx, dcoll, dd=df)).as_vector(
                            dtype=object)

    # }}}

    # {{{ checks

    def _eval_error(x):
        return op.norm(dcoll, x, np.inf, dd=df)

    rtol = 1.0e-14

    # check interpolated surface normal is orthogonal to face normal
    error = _eval_error(surf_normal.dot(face_normal_i))
    logger.info("error[n_dot_i]:    %.5e", error)
    assert error < rtol

    # check angle between two neighboring elements
    error = _eval_error(face_normal_i.dot(face_normal_e) + 1.0)
    logger.info("error[i_dot_e]:    %.5e", error)
    assert error > rtol

    # check orthogonality with face tangent
    if ambient_dim == 3:
        error = _eval_error(face_tangent.dot(face_normal_i))
        logger.info("error[t_dot_i]:  %.5e", error)
        assert error < 5 * rtol
コード例 #23
0
def test_surface_divergence_theorem(actx_factory, mesh_name, visualize=False):
    r"""Check the surface divergence theorem.

        .. math::

            \int_Sigma \phi \nabla_i f_i =
            \int_\Sigma \nabla_i \phi f_i +
            \int_\Sigma \kappa \phi f_i n_i +
            \int_{\partial \Sigma} \phi f_i m_i

        where :math:`n_i` is the surface normal and :class:`m_i` is the
        face normal (which should be orthogonal to both the surface normal
        and the face tangent).
    """
    actx = actx_factory()

    # {{{ cases

    if mesh_name == "2-1-ellipse":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0)
    elif mesh_name == "spheroid":
        from mesh_data import SpheroidMeshBuilder
        builder = SpheroidMeshBuilder()
    elif mesh_name == "circle":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=1.0, aspect_ratio=1.0)
    elif mesh_name == "starfish":
        from mesh_data import StarfishMeshBuilder
        builder = StarfishMeshBuilder()
    elif mesh_name == "sphere":
        from mesh_data import SphereMeshBuilder
        builder = SphereMeshBuilder(radius=1.0, mesh_order=16)
    else:
        raise ValueError("unknown mesh name: %s" % mesh_name)

    # }}}

    # {{{ convergence

    def f(x):
        return flat_obj_array(
            actx.np.sin(3 * x[1]) + actx.np.cos(3 * x[0]) + 1.0,
            actx.np.sin(2 * x[0]) + actx.np.cos(x[1]),
            3.0 * actx.np.cos(x[0] / 2) + actx.np.cos(x[1]),
        )[:ambient_dim]

    from pytools.convergence import EOCRecorder
    eoc_global = EOCRecorder()
    eoc_local = EOCRecorder()

    theta = np.pi / 3.33
    ambient_dim = builder.ambient_dim
    if ambient_dim == 2:
        mesh_rotation = np.array([
            [np.cos(theta), -np.sin(theta)],
            [np.sin(theta), np.cos(theta)],
        ])
    else:
        mesh_rotation = np.array([
            [1.0, 0.0, 0.0],
            [0.0, np.cos(theta), -np.sin(theta)],
            [0.0, np.sin(theta), np.cos(theta)],
        ])

    mesh_offset = np.array([0.33, -0.21, 0.0])[:ambient_dim]

    for i, resolution in enumerate(builder.resolutions):
        from meshmode.mesh.processing import affine_map
        from meshmode.discretization.connection import FACE_RESTR_ALL

        mesh = builder.get_mesh(resolution, builder.mesh_order)
        mesh = affine_map(mesh, A=mesh_rotation, b=mesh_offset)

        from meshmode.discretization.poly_element import \
                QuadratureSimplexGroupFactory

        qtag = dof_desc.DISCR_TAG_QUAD
        dcoll = DiscretizationCollection(actx,
                                         mesh,
                                         order=builder.order,
                                         discr_tag_to_group_factory={
                                             qtag:
                                             QuadratureSimplexGroupFactory(
                                                 2 * builder.order)
                                         })

        volume = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
        logger.info("ndofs:     %d", volume.ndofs)
        logger.info("nelements: %d", volume.mesh.nelements)

        dd = dof_desc.DD_VOLUME
        dq = dd.with_discr_tag(qtag)
        df = dof_desc.as_dofdesc(FACE_RESTR_ALL)
        ambient_dim = dcoll.ambient_dim

        # variables
        f_num = f(thaw(dcoll.nodes(dd=dd), actx))
        f_quad_num = f(thaw(dcoll.nodes(dd=dq), actx))

        from grudge.geometry import normal, summed_curvature

        kappa = summed_curvature(actx, dcoll, dd=dq)
        normal = normal(actx, dcoll, dd=dq)
        face_normal = thaw(dcoll.normal(df), actx)
        face_f = op.project(dcoll, dd, df, f_num)

        # operators
        stiff = op.mass(
            dcoll,
            sum(
                op.local_d_dx(dcoll, i, f_num_i)
                for i, f_num_i in enumerate(f_num)))
        stiff_t = sum(
            op.weak_local_d_dx(dcoll, i, f_num_i)
            for i, f_num_i in enumerate(f_num))
        kterm = op.mass(dcoll, dq, kappa * f_quad_num.dot(normal))
        flux = op.face_mass(dcoll, face_f.dot(face_normal))

        # sum everything up
        op_global = op.nodal_sum(dcoll, dd, stiff - (stiff_t + kterm))
        op_local = op.elementwise_sum(dcoll, dd,
                                      stiff - (stiff_t + kterm + flux))

        err_global = abs(op_global)
        err_local = op.norm(dcoll, op_local, np.inf)
        logger.info("errors: global %.5e local %.5e", err_global, err_local)

        # compute max element size
        from grudge.dt_utils import h_max_from_volume

        h_max = h_max_from_volume(dcoll)

        eoc_global.add_data_point(h_max, actx.to_numpy(err_global))
        eoc_local.add_data_point(h_max, err_local)

        if visualize:
            from grudge.shortcuts import make_visualizer
            vis = make_visualizer(dcoll)

            filename = f"surface_divergence_theorem_{mesh_name}_{i:04d}.vtu"
            vis.write_vtk_file(filename, [("r", actx.np.log10(op_local))],
                               overwrite=True)

    # }}}

    order = min(builder.order, builder.mesh_order) - 0.5
    logger.info("\n%s", str(eoc_global))
    logger.info("\n%s", str(eoc_local))

    assert eoc_global.max_error() < 1.0e-12 \
            or eoc_global.order_estimate() > order - 0.5

    assert eoc_local.max_error() < 1.0e-12 \
            or eoc_local.order_estimate() > order - 0.5
コード例 #24
0
def test_convergence_advec(actx_factory,
                           mesh_name,
                           mesh_pars,
                           op_type,
                           flux_type,
                           order,
                           visualize=False):
    """Test whether 2D advection actually converges"""

    actx = actx_factory()

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    for mesh_par in mesh_pars:
        if mesh_name == "segment":
            mesh = mgen.generate_box_mesh([np.linspace(-1.0, 1.0, mesh_par)],
                                          order=order)

            dim = 1
            dt_factor = 1.0
        elif mesh_name == "disk":
            pytest.importorskip("meshpy")

            from meshpy.geometry import make_circle, GeometryBuilder
            from meshpy.triangle import MeshInfo, build

            geob = GeometryBuilder()
            geob.add_geometry(*make_circle(1))
            mesh_info = MeshInfo()
            geob.set(mesh_info)

            mesh_info = build(mesh_info, max_volume=mesh_par)

            from meshmode.mesh.io import from_meshpy
            mesh = from_meshpy(mesh_info, order=1)
            dim = 2
            dt_factor = 4
        elif mesh_name.startswith("rect"):
            dim = int(mesh_name[-1:])
            mesh = mgen.generate_regular_rect_mesh(
                a=(-0.5, ) * dim,
                b=(0.5, ) * dim,
                nelements_per_axis=(mesh_par, ) * dim,
                order=4)

            if dim == 2:
                dt_factor = 4
            elif dim == 3:
                dt_factor = 2
            else:
                raise ValueError("dt_factor not known for %dd" % dim)
        elif mesh_name.startswith("warped"):
            dim = int(mesh_name[-1:])
            mesh = mgen.generate_warped_rect_mesh(dim,
                                                  order=order,
                                                  nelements_side=mesh_par)

            if dim == 2:
                dt_factor = 4
            elif dim == 3:
                dt_factor = 2
            else:
                raise ValueError("dt_factor not known for %dd" % dim)
        else:
            raise ValueError("invalid mesh name: " + mesh_name)

        v = np.array([0.27, 0.31, 0.1])[:dim]
        norm_v = la.norm(v)

        def f(x):
            return actx.np.sin(10 * x)

        def u_analytic(x, t=0):
            return f(-v.dot(x) / norm_v + t * norm_v)

        from grudge.models.advection import (StrongAdvectionOperator,
                                             WeakAdvectionOperator)
        from meshmode.mesh import BTAG_ALL

        dcoll = DiscretizationCollection(actx, mesh, order=order)
        op_class = {
            "strong": StrongAdvectionOperator,
            "weak": WeakAdvectionOperator
        }[op_type]
        adv_operator = op_class(dcoll,
                                v,
                                inflow_u=lambda t: u_analytic(
                                    thaw(dcoll.nodes(dd=BTAG_ALL), actx), t=t),
                                flux_type=flux_type)

        nodes = thaw(dcoll.nodes(), actx)
        u = u_analytic(nodes, t=0)

        def rhs(t, u):
            return adv_operator.operator(t, u)

        if dim == 3:
            final_time = 0.1
        else:
            final_time = 0.2

        from grudge.dt_utils import h_max_from_volume

        h_max = h_max_from_volume(dcoll, dim=dcoll.ambient_dim)
        dt = dt_factor * h_max / order**2
        nsteps = (final_time // dt) + 1
        dt = final_time / nsteps + 1e-15

        from grudge.shortcuts import set_up_rk4
        dt_stepper = set_up_rk4("u", dt, u, rhs)

        last_u = None

        from grudge.shortcuts import make_visualizer
        vis = make_visualizer(dcoll)

        step = 0

        for event in dt_stepper.run(t_end=final_time):
            if isinstance(event, dt_stepper.StateComputed):
                step += 1
                logger.debug("[%04d] t = %.5f", step, event.t)

                last_t = event.t
                last_u = event.state_component

                if visualize:
                    vis.write_vtk_file("fld-%s-%04d.vtu" % (mesh_par, step),
                                       [("u", event.state_component)])

        error_l2 = op.norm(dcoll, last_u - u_analytic(nodes, t=last_t), 2)
        logger.info("h_max %.5e error %.5e", h_max, error_l2)
        eoc_rec.add_data_point(h_max, actx.to_numpy(error_l2))

    logger.info(
        "\n%s", eoc_rec.pretty_print(abscissa_label="h",
                                     error_label="L2 Error"))

    if mesh_name.startswith("warped"):
        # NOTE: curvilinear meshes are hard
        assert eoc_rec.order_estimate() > order - 0.5
    else:
        assert eoc_rec.order_estimate() > order
コード例 #25
0
def test_convergence_maxwell(actx_factory, order):
    """Test whether 3D Maxwell's actually converges"""

    actx = actx_factory()

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    dims = 3
    ns = [4, 6, 8]
    for n in ns:
        mesh = mgen.generate_regular_rect_mesh(a=(0.0, ) * dims,
                                               b=(1.0, ) * dims,
                                               nelements_per_axis=(n, ) * dims)

        dcoll = DiscretizationCollection(actx, mesh, order=order)

        epsilon = 1
        mu = 1

        from grudge.models.em import get_rectangular_cavity_mode

        def analytic_sol(x, t=0):
            return get_rectangular_cavity_mode(actx, x, t, 1, (1, 2, 2))

        nodes = thaw(dcoll.nodes(), actx)
        fields = analytic_sol(nodes, t=0)

        from grudge.models.em import MaxwellOperator

        maxwell_operator = MaxwellOperator(dcoll,
                                           epsilon,
                                           mu,
                                           flux_type=0.5,
                                           dimensions=dims)
        maxwell_operator.check_bc_coverage(mesh)

        def rhs(t, w):
            return maxwell_operator.operator(t, w)

        dt = maxwell_operator.estimate_rk4_timestep(actx, dcoll)
        final_t = dt * 5
        nsteps = int(final_t / dt)

        from grudge.shortcuts import set_up_rk4
        dt_stepper = set_up_rk4("w", dt, fields, rhs)

        logger.info("dt %.5e nsteps %5d", dt, nsteps)

        step = 0
        for event in dt_stepper.run(t_end=final_t):
            if isinstance(event, dt_stepper.StateComputed):
                assert event.component_id == "w"
                esc = event.state_component

                step += 1
                logger.debug("[%04d] t = %.5e", step, event.t)

        sol = analytic_sol(nodes, t=step * dt)
        total_error = op.norm(dcoll, esc - sol, 2)
        eoc_rec.add_data_point(1.0 / n, actx.to_numpy(total_error))

    logger.info(
        "\n%s", eoc_rec.pretty_print(abscissa_label="h",
                                     error_label="L2 Error"))

    assert eoc_rec.order_estimate() > order