def simple_mpi_communication_entrypoint():
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

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

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

    sym_x = sym.nodes(local_mesh.dim)
    myfunc_symb = sym.sin(np.dot(sym_x, [2, 3]))
    myfunc = bind(vol_discr, myfunc_symb)(actx)

    sym_all_faces_func = sym.cse(
        sym.project("vol", "all_faces")(sym.var("myfunc")))
    sym_int_faces_func = sym.cse(
        sym.project("vol", "int_faces")(sym.var("myfunc")))
    sym_bdry_faces_func = sym.cse(
        sym.project(BTAG_ALL,
                    "all_faces")(sym.project("vol",
                                             BTAG_ALL)(sym.var("myfunc"))))

    bound_face_swap = bind(
        vol_discr,
        sym.project("int_faces", "all_faces")(
            sym.OppositeInteriorFaceSwap("int_faces")(sym_int_faces_func)) -
        (sym_all_faces_func - sym_bdry_faces_func))

    hopefully_zero = bound_face_swap(myfunc=myfunc)
    error = actx.np.linalg.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
Exemplo n.º 2
0
def simple_mpi_communication_entrypoint():
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis

    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,
                                          n=(3, ) * 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()

    vol_discr = DGDiscretizationWithBoundaries(cl_ctx,
                                               local_mesh,
                                               order=5,
                                               mpi_communicator=comm)

    sym_x = sym.nodes(local_mesh.dim)
    myfunc_symb = sym.sin(np.dot(sym_x, [2, 3]))
    myfunc = bind(vol_discr, myfunc_symb)(queue)

    sym_all_faces_func = sym.cse(
        sym.interp("vol", "all_faces")(sym.var("myfunc")))
    sym_int_faces_func = sym.cse(
        sym.interp("vol", "int_faces")(sym.var("myfunc")))
    sym_bdry_faces_func = sym.cse(
        sym.interp(sym.BTAG_ALL,
                   "all_faces")(sym.interp("vol",
                                           sym.BTAG_ALL)(sym.var("myfunc"))))

    bound_face_swap = bind(
        vol_discr,
        sym.interp("int_faces", "all_faces")(
            sym.OppositeInteriorFaceSwap("int_faces")(sym_int_faces_func)) -
        (sym_all_faces_func - sym_bdry_faces_func))

    # print(bound_face_swap)
    # 1/0

    hopefully_zero = bound_face_swap(queue, myfunc=myfunc)
    import numpy.linalg as la
    error = la.norm(hopefully_zero.get())

    np.set_printoptions(threshold=100000000, suppress=True)
    print(hopefully_zero)
    print(error)

    assert error < 1e-14
Exemplo n.º 3
0
def test_surface_mass_operator_inverse(actx_factory, name):
    actx = actx_factory()

    # {{{ cases

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

    # }}}

    # {{{ convergence

    from pytools.convergence import EOCRecorder
    eoc = EOCRecorder()

    for resolution in builder.resolutions:
        mesh = builder.get_mesh(resolution, builder.mesh_order)
        discr = DiscretizationCollection(actx, mesh, order=builder.order)
        volume_discr = discr.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

        dd = dof_desc.DD_VOLUME
        sym_f = sym.cos(4.0 * sym.nodes(mesh.ambient_dim, dd)[0])
        sym_op = sym.InverseMassOperator(dd, dd)(sym.MassOperator(dd, dd)(
            sym.var("f")))

        f = bind(discr, sym_f)(actx)
        f_inv = bind(discr, sym_op)(actx, f=f)

        inv_error = bind(
            discr,
            sym.norm(2,
                     sym.var("x") - sym.var("y")) / sym.norm(2, sym.var("y")))(
                         actx, x=f_inv, y=f)

        # }}}

        h_max = bind(
            discr,
            sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim,
                                  dd=dd))(actx)
        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
Exemplo n.º 4
0
def test_1d_mass_mat_trig(ctx_factory):
    """Check the integral of some trig functions on an interval using the mass
    matrix
    """

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

    from meshmode.mesh.generation import generate_regular_rect_mesh

    mesh = generate_regular_rect_mesh(a=(-4 * np.pi, ),
                                      b=(9 * np.pi, ),
                                      n=(17, ),
                                      order=1)

    discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=8)

    x = sym.nodes(1)
    f = bind(discr, sym.cos(x[0])**2)(queue)
    ones = bind(discr, sym.Ones(sym.DD_VOLUME))(queue)

    mass_op = bind(discr, sym.MassOperator()(sym.var("f")))

    num_integral_1 = np.dot(ones.get(), mass_op(queue, f=f))
    num_integral_2 = np.dot(f.get(), mass_op(queue, f=ones))
    num_integral_3 = bind(discr, sym.integral(sym.var("f")))(queue, f=f)

    true_integral = 13 * np.pi / 2
    err_1 = abs(num_integral_1 - true_integral)
    err_2 = abs(num_integral_2 - true_integral)
    err_3 = abs(num_integral_3 - true_integral)

    assert err_1 < 1e-10
    assert err_2 < 1e-10
    assert err_3 < 1e-10
Exemplo n.º 5
0
def test_stepper_equivalence(ctx_factory, order=4):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(
        queue,
        allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))
    )

    dims = 2

    sym_operator, discr = get_wave_op_with_discr(
            actx, dims=dims, order=order)
    #sym_operator_direct, discr = get_wave_op_with_discr_direct(
    #        actx, dims=dims, order=order)

    if dims == 2:
        dt = 0.04
    elif dims == 3:
        dt = 0.02

    ic = flat_obj_array(discr.zeros(actx),
            [discr.zeros(actx) for i in range(discr.dim)])

    bound_op = bind(discr, sym_operator)

    stepper = RK4TimeStepper(
            discr, "w", bound_op, 1 + discr.dim, get_wave_component)

    fused_stepper = FusedRK4TimeStepper(
            discr, "w", sym_operator, 1 + discr.dim,
            get_wave_component)

    t_start = 0
    t_end = 0.5
    nsteps = int(np.ceil((t_end + 1e-9) / dt))
    print("dt=%g nsteps=%d" % (dt, nsteps))

    step = 0

    norm = bind(discr, sym.norm(2, sym.var("u_ref") - sym.var("u")))

    fused_steps = fused_stepper.run(ic, t_start, dt, t_end)

    for t_ref, (u_ref, _v_ref) in stepper.run(ic, t_start, dt, t_end):
        step += 1
        logger.debug("step %d/%d", step, nsteps)
        t, (u, v) = next(fused_steps)
        assert t == t_ref, step
        assert norm(u=u, u_ref=u_ref) <= 1e-13, step
Exemplo n.º 6
0
    def sym_operator(self):
        from grudge.dof_desc import DOFDesc, DD_VOLUME, DTAG_VOLUME_ALL
        from meshmode.mesh import BTAG_ALL
        from meshmode.discretization.connection import FACE_RESTR_ALL

        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, face_dd)(self.flux(pair))

        face_dd = DOFDesc(FACE_RESTR_ALL, self.quad_tag)
        boundary_dd = DOFDesc(BTAG_ALL, self.quad_tag)
        quad_dd = DOFDesc(DTAG_VOLUME_ALL, self.quad_tag)

        to_quad = sym.project(DD_VOLUME, quad_dd)
        stiff_t_op = sym.stiffness_t(self.ambient_dim,
                                     dd_in=quad_dd,
                                     dd_out=DD_VOLUME)

        quad_v = to_quad(self.v)
        quad_u = to_quad(u)

        return sym.InverseMassOperator()(
            sum(stiff_t_op[n](quad_u * quad_v[n])
                for n in range(self.ambient_dim)) -
            sym.FaceMassOperator(face_dd, DD_VOLUME)(
                flux(sym.int_tpair(u, self.quad_tag)) +
                flux(sym.bv_tpair(boundary_dd, u, self.inflow_u))

                # FIXME: Add back support for inflow/outflow tags
                #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
            ))
Exemplo n.º 7
0
    def sym_operator(self):
        from grudge.dof_desc import DOFDesc, DD_VOLUME, DTAG_VOLUME_ALL
        from meshmode.discretization.connection import FACE_RESTR_ALL

        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, face_dd)(self.flux(pair))

        face_dd = DOFDesc(FACE_RESTR_ALL, self.quad_tag)
        quad_dd = DOFDesc(DTAG_VOLUME_ALL, self.quad_tag)

        to_quad = sym.project(DD_VOLUME, quad_dd)
        stiff_t_op = sym.stiffness_t(self.ambient_dim,
                                     dd_in=quad_dd,
                                     dd_out=DD_VOLUME)

        quad_v = to_quad(self.v)
        quad_u = to_quad(u)

        return sym.InverseMassOperator()(
            sum(stiff_t_op[n](quad_u * quad_v[n])
                for n in range(self.ambient_dim)) -
            sym.FaceMassOperator(face_dd, DD_VOLUME)
            (flux(sym.int_tpair(u, self.quad_tag))))
Exemplo n.º 8
0
def test_function_symbol_array(ctx_factory, array_type):
    ctx = ctx_factory()
    queue = cl.CommandQueue(ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh.generation import generate_regular_rect_mesh
    dim = 2
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                      b=(0.5, ) * dim,
                                      n=(8, ) * dim,
                                      order=4)
    discr = DGDiscretizationWithBoundaries(actx, mesh, order=4)
    volume_discr = discr.discr_from_dd(sym.DD_VOLUME)
    ndofs = sum(grp.ndofs for grp in volume_discr.groups)

    import pyopencl.clrandom  # noqa: F401
    if array_type == "scalar":
        sym_x = sym.var("x")
        x = unflatten(actx, volume_discr,
                      cl.clrandom.rand(queue, ndofs, dtype=np.float))
    elif array_type == "vector":
        sym_x = sym.make_sym_array("x", dim)
        x = make_obj_array([
            unflatten(actx, volume_discr,
                      cl.clrandom.rand(queue, ndofs, dtype=np.float))
            for _ in range(dim)
        ])
    else:
        raise ValueError("unknown array type")

    norm = bind(discr, sym.norm(2, sym_x))(x=x)
    assert isinstance(norm, float)
Exemplo n.º 9
0
    def sym_operator(self):
        u = sym.var("u")

        # boundary conditions -------------------------------------------------
        bc_in = self.inflow_u

        all_faces_dd = sym.DOFDesc(sym.FACE_RESTR_ALL, self.quad_tag)
        boundary_dd = sym.DOFDesc(sym.BTAG_ALL, self.quad_tag)

        def flux(pair):
            return sym.interp(pair.dd, all_faces_dd)(
                    self.flux(pair))

        quad_dd = sym.DOFDesc("vol", self.quad_tag)

        to_quad = sym.interp("vol", quad_dd)

        stiff_t = sym.stiffness_t(self.ambient_dim, quad_dd, "vol")
        quad_v = to_quad(self.v)
        quad_u = to_quad(u)

        return sym.InverseMassOperator()(
                (stiff_t[0](quad_u * quad_v[0]) + stiff_t[1](quad_u * quad_v[1]))
                - sym.FaceMassOperator(all_faces_dd, "vol")(
                    flux(sym.int_tpair(u, self.quad_tag))
                    + flux(sym.bv_tpair(boundary_dd, u, bc_in))

                    # FIXME: Add back support for inflow/outflow tags
                    #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                    #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
                ))
Exemplo n.º 10
0
def test_operator_compiler_overwrite(actx_factory):
    """Tests that the same expression in ``eval_code`` and ``discr_code``
    does not confuse the OperatorCompiler in grudge/symbolic/compiler.py.
    """

    actx = actx_factory()

    ambient_dim = 2
    target_order = 4

    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * ambient_dim,
                                      b=(0.5, ) * ambient_dim,
                                      n=(8, ) * ambient_dim,
                                      order=1)
    discr = DiscretizationCollection(actx, mesh, order=target_order)

    # {{{ test

    sym_u = sym.nodes(ambient_dim)
    sym_div_u = sum(d(u) for d, u in zip(sym.nabla(ambient_dim), sym_u))

    div_u = bind(discr, sym_div_u)(actx)
    error = bind(discr, sym.norm(2, sym.var("x")))(actx, x=div_u - discr.dim)
    logger.info("error: %.5e", error)
Exemplo n.º 11
0
    def sym_operator(self):
        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, face_dd)(self.flux(pair))

        face_dd = sym.DOFDesc(sym.FACE_RESTR_ALL, self.quad_tag)
        boundary_dd = sym.DOFDesc(sym.BTAG_ALL, self.quad_tag)
        quad_dd = sym.DOFDesc(sym.DTAG_VOLUME_ALL, self.quad_tag)

        to_quad = sym.project(sym.DD_VOLUME, quad_dd)
        stiff_t_op = sym.stiffness_t(self.ambient_dim,
                dd_in=quad_dd, dd_out=sym.DD_VOLUME)

        quad_v = to_quad(self.v)
        quad_u = to_quad(u)

        return sym.InverseMassOperator()(
                sum(stiff_t_op[n](quad_u * quad_v[n])
                    for n in range(self.ambient_dim))
                - sym.FaceMassOperator(face_dd, sym.DD_VOLUME)(
                    flux(sym.int_tpair(u, self.quad_tag))
                    + flux(sym.bv_tpair(boundary_dd, u, self.inflow_u))

                    # FIXME: Add back support for inflow/outflow tags
                    #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                    #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
                ))
Exemplo n.º 12
0
def test_norm_obj_array(actx_factory, p):
    """Test :func:`grudge.symbolic.operators.norm` for object arrays."""

    actx = actx_factory()

    dim = 2
    mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                           b=(0.5, ) * dim,
                                           nelements_per_axis=(8, ) * dim,
                                           order=1)
    discr = DiscretizationCollection(actx, mesh, order=4)

    w = make_obj_array([1.0, 2.0, 3.0])[:dim]

    # {{ scalar

    sym_w = sym.var("w")
    norm = bind(discr, sym.norm(p, sym_w))(actx, w=w[0])

    norm_exact = w[0]
    logger.info("norm: %.5e %.5e", norm, norm_exact)
    assert abs(norm - norm_exact) < 1.0e-14

    # }}}

    # {{{ vector

    sym_w = sym.make_sym_array("w", dim)
    norm = bind(discr, sym.norm(p, sym_w))(actx, w=w)

    norm_exact = np.sqrt(np.sum(w**2)) if p == 2 else np.max(w)
    logger.info("norm: %.5e %.5e", norm, norm_exact)
    assert abs(norm - norm_exact) < 1.0e-14
Exemplo n.º 13
0
def test_stepper_equivalence(ctx_factory, order=4):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)

    dims = 2

    sym_operator, _ = get_strong_wave_op_with_discr(cl_ctx,
                                                    dims=dims,
                                                    order=order)
    sym_operator_direct, discr = get_strong_wave_op_with_discr_direct(
        cl_ctx, dims=dims, order=order)

    if dims == 2:
        dt = 0.04
    elif dims == 3:
        dt = 0.02

    from pytools.obj_array import join_fields
    ic = join_fields(discr.zeros(queue),
                     [discr.zeros(queue) for i in range(discr.dim)])

    bound_op = bind(discr, sym_operator)

    stepper = RK4TimeStepper(queue, discr, "w", bound_op, 1 + discr.dim,
                             get_strong_wave_component)

    fused_stepper = FusedRK4TimeStepper(queue, discr, "w", sym_operator_direct,
                                        1 + discr.dim,
                                        get_strong_wave_component)

    t_start = 0
    t_end = 0.5
    nsteps = int(np.ceil((t_end + 1e-9) / dt))
    print("dt=%g nsteps=%d" % (dt, nsteps))

    step = 0

    norm = bind(discr, sym.norm(2, sym.var("u_ref") - sym.var("u")))

    fused_steps = fused_stepper.run(ic, t_start, dt, t_end)

    for t_ref, (u_ref, v_ref) in stepper.run(ic, t_start, dt, t_end):
        step += 1
        logger.debug("step %d/%d", step, nsteps)
        t, (u, v) = next(fused_steps)
        assert t == t_ref, step
        assert norm(queue, u=u, u_ref=u_ref) <= 1e-13, step
Exemplo n.º 14
0
    def sym_operator(self):
        d = self.ambient_dim

        w = sym.make_sym_array("w", d + 1)
        u = w[0]
        v = w[1:]

        # boundary conditions -------------------------------------------------

        # dirichlet BCs -------------------------------------------------------
        dir_u = sym.cse(sym.project("vol", self.dirichlet_tag)(u))
        dir_v = sym.cse(sym.project("vol", self.dirichlet_tag)(v))
        if self.dirichlet_bc_f:
            # FIXME
            from warnings import warn
            warn("Inhomogeneous Dirichlet conditions on the wave equation "
                 "are still having issues.")

            dir_g = sym.var("dir_bc_u")
            dir_bc = flat_obj_array(2 * dir_g - dir_u, dir_v)
        else:
            dir_bc = flat_obj_array(-dir_u, dir_v)

        dir_bc = sym.cse(dir_bc, "dir_bc")

        # neumann BCs ---------------------------------------------------------
        neu_u = sym.cse(sym.project("vol", self.neumann_tag)(u))
        neu_v = sym.cse(sym.project("vol", self.neumann_tag)(v))
        neu_bc = sym.cse(flat_obj_array(neu_u, -neu_v), "neu_bc")

        # radiation BCs -------------------------------------------------------
        rad_normal = sym.normal(self.radiation_tag, d)

        rad_u = sym.cse(sym.project("vol", self.radiation_tag)(u))
        rad_v = sym.cse(sym.project("vol", self.radiation_tag)(v))

        rad_bc = sym.cse(
            flat_obj_array(
                0.5 * (rad_u - self.sign * np.dot(rad_normal, rad_v)), 0.5 *
                rad_normal * (np.dot(rad_normal, rad_v) - self.sign * rad_u)),
            "rad_bc")

        # entire operator -----------------------------------------------------
        def flux(pair):
            return sym.project(pair.dd, "all_faces")(self.flux(pair))

        result = sym.InverseMassOperator()(flat_obj_array(
            -self.c * np.dot(sym.stiffness_t(self.ambient_dim), v), -self.c *
            (sym.stiffness_t(self.ambient_dim) * u)) - sym.FaceMassOperator()(
                flux(sym.int_tpair(w)) +
                flux(sym.bv_tpair(self.dirichlet_tag, w, dir_bc)) +
                flux(sym.bv_tpair(self.neumann_tag, w, neu_bc)) +
                flux(sym.bv_tpair(self.radiation_tag, w, rad_bc))))

        result[0] += self.source_f

        return result
Exemplo n.º 15
0
def test_op_collector_order_determinism():
    class TestOperator(sym.Operator):
        def __init__(self):
            sym.Operator.__init__(self, sym.DD_VOLUME, sym.DD_VOLUME)

        mapper_method = "map_test_operator"

    from grudge.symbolic.mappers import BoundOperatorCollector

    class TestBoundOperatorCollector(BoundOperatorCollector):
        def map_test_operator(self, expr):
            return self.map_operator(expr)

    v0 = sym.var("v0")
    ob0 = sym.OperatorBinding(TestOperator(), v0)

    v1 = sym.var("v1")
    ob1 = sym.OperatorBinding(TestOperator(), v1)

    # The output order isn't significant, but it should always be the same.
    assert list(TestBoundOperatorCollector(TestOperator)(ob0 +
                                                         ob1)) == [ob0, ob1]
Exemplo n.º 16
0
    def sym_operator(self):
        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, "all_faces")(self.flux(pair))

        return (-self.v.dot(
            sym.nabla(self.ambient_dim) * u
        ) + sym.InverseMassOperator()(
            sym.FaceMassOperator()(
                flux(sym.int_tpair(u)) +
                flux(sym.bv_tpair(sym.BTAG_ALL, u, self.inflow_u))

                # FIXME: Add back support for inflow/outflow tags
                #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
            )))
Exemplo n.º 17
0
    def sym_operator(self):
        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, "all_faces")(self.flux(pair))

        bc_in = self.inflow_u
        # bc_out = sym.project(sym.DD_VOLUME, self.outflow_tag)(u)

        return sym.InverseMassOperator()(
            np.dot(self.v,
                   sym.stiffness_t(self.ambient_dim) * u) -
            sym.FaceMassOperator()(
                flux(sym.int_tpair(u)) +
                flux(sym.bv_tpair(sym.BTAG_ALL, u, bc_in))

                # FIXME: Add back support for inflow/outflow tags
                #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
            ))
Exemplo n.º 18
0
    def conv_test(descr, use_quad):
        logger.info("-" * 75)
        logger.info(descr)
        logger.info("-" * 75)
        eoc_rec = EOCRecorder()

        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 = {
                    "product": QuadratureSimplexGroupFactory(order=4 * order)
                }
            else:
                discr_tag_to_group_factory = {"product": None}

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

            bound_op = bind(discr, op.sym_operator())
            fields = bind(discr, gaussian_mode())(actx, t=0)
            norm = bind(discr, sym.norm(2, sym.var("u")))

            esc = bound_op(u=fields)
            total_error = norm(u=esc)
            eoc_rec.add_data_point(1.0 / n, 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])
Exemplo n.º 19
0
def test_tri_diff_mat(ctx_factory, dim, order=4):
    """Check differentiation matrix along the coordinate axes on a disk

    Uses sines as the function to differentiate.
    """

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

    from meshmode.mesh.generation import generate_regular_rect_mesh

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

    for n in [10, 20]:
        mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                          b=(0.5, ) * dim,
                                          n=(n, ) * dim,
                                          order=4)

        discr = DGDiscretizationWithBoundaries(actx, mesh, order=4)
        nabla = sym.nabla(dim)

        for axis in range(dim):
            x = sym.nodes(dim)

            f = bind(discr, sym.sin(3 * x[axis]))(actx)
            df = bind(discr, 3 * sym.cos(3 * x[axis]))(actx)

            sym_op = nabla[axis](sym.var("f"))
            bound_op = bind(discr, sym_op)
            df_num = bound_op(f=f)

            linf_error = flat_norm(df_num - df, np.Inf)
            axis_eoc_recs[axis].add_data_point(1 / n, 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
Exemplo n.º 20
0
    def conv_test(descr, use_quad):
        print("-" * 75)
        print(descr)
        print("-" * 75)
        eoc_rec = EOCRecorder()

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

            if use_quad:
                quad_tag_to_group_factory = {
                    "product": QuadratureSimplexGroupFactory(order=4 * order)
                }
            else:
                quad_tag_to_group_factory = {"product": None}

            discr = DGDiscretizationWithBoundaries(
                cl_ctx,
                mesh,
                order=order,
                quad_tag_to_group_factory=quad_tag_to_group_factory)

            bound_op = bind(discr, op.sym_operator())
            fields = bind(discr, gaussian_mode())(queue, t=0)
            norm = bind(discr, sym.norm(2, sym.var("u")))

            esc = bound_op(queue, u=fields)
            total_error = norm(queue, u=esc)
            eoc_rec.add_data_point(1.0 / n, total_error)

        print(
            eoc_rec.pretty_print(abscissa_label="h", error_label="LInf Error"))

        return eoc_rec.order_estimate(), np.array(
            [x[1] for x in eoc_rec.history])
Exemplo n.º 21
0
    def sym_operator(self):
        u = sym.var("u")

        def flux(pair):
            return sym.project(pair.dd, face_dd)(self.flux(pair))

        face_dd = sym.DOFDesc(sym.FACE_RESTR_ALL, self.quad_tag)
        quad_dd = sym.DOFDesc(sym.DTAG_VOLUME_ALL, self.quad_tag)

        to_quad = sym.project(sym.DD_VOLUME, quad_dd)
        stiff_t_op = sym.stiffness_t(self.ambient_dim,
                                     dd_in=quad_dd,
                                     dd_out=sym.DD_VOLUME)

        quad_v = to_quad(self.v)
        quad_u = to_quad(u)

        return sym.InverseMassOperator()(
            sum(stiff_t_op[n](quad_u * quad_v[n])
                for n in range(self.ambient_dim)) -
            sym.FaceMassOperator(face_dd, sym.DD_VOLUME)
            (flux(sym.int_tpair(u, self.quad_tag))))
Exemplo n.º 22
0
    def sym_operator(self):
        u = sym.var("u")

        # boundary conditions -------------------------------------------------
        bc_in = self.inflow_u
        # bc_out = sym.interp("vol", self.outflow_tag)(u)

        def flux(pair):
            return sym.interp(pair.dd, "all_faces")(
                    self.flux(pair))

        return sym.InverseMassOperator()(
                np.dot(
                    self.v, sym.stiffness_t(self.ambient_dim)*u)
                - sym.FaceMassOperator()(
                    flux(sym.int_tpair(u))
                    + flux(sym.bv_tpair(sym.BTAG_ALL, u, bc_in))

                    # FIXME: Add back support for inflow/outflow tags
                    #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
                    #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
                    ))
Exemplo n.º 23
0
def test_function_symbol_array(actx_factory, array_type):
    """Test if `FunctionSymbol` distributed properly over object arrays."""

    actx = actx_factory()

    dim = 2
    mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                           b=(0.5, ) * dim,
                                           nelements_per_axis=(8, ) * dim,
                                           order=4)
    discr = DiscretizationCollection(actx, mesh, order=4)
    volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME)

    if array_type == "scalar":
        sym_x = sym.var("x")
        x = thaw(actx, actx.np.cos(volume_discr.nodes()[0]))
    elif array_type == "vector":
        sym_x = sym.make_sym_array("x", dim)
        x = thaw(actx, volume_discr.nodes())
    else:
        raise ValueError("unknown array type")

    norm = bind(discr, sym.norm(2, sym_x))(x=x)
    assert isinstance(norm, float)
Exemplo n.º 24
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)]

    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)

        discr = DiscretizationCollection(actx, mesh, order=4)
        nabla = sym.nabla(dim)

        for axis in range(dim):
            x = sym.nodes(dim)

            f = bind(discr, sym.sin(3 * x[axis]))(actx)
            df = bind(discr, 3 * sym.cos(3 * x[axis]))(actx)

            sym_op = nabla[axis](sym.var("f"))
            bound_op = bind(discr, sym_op)
            df_num = bound_op(f=f)

            linf_error = actx.np.linalg.norm(df_num - df, ord=np.inf)
            axis_eoc_recs[axis].add_data_point(1 / n, 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
Exemplo n.º 25
0
def main(ctx_factory, dim=2, order=4, product_tag=None, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ parameters

    # sphere radius
    radius = 1.0
    # sphere resolution
    resolution = 64 if dim == 2 else 1

    # cfl
    dt_factor = 2.0
    # final time
    final_time = np.pi

    # velocity field
    sym_x = sym.nodes(dim)
    c = make_obj_array([-sym_x[1], sym_x[0], 0.0])[:dim]
    # flux
    flux_type = "lf"

    # }}}

    # {{{ discretization

    if dim == 2:
        from meshmode.mesh.generation import make_curve_mesh, ellipse
        mesh = make_curve_mesh(lambda t: radius * ellipse(1.0, t),
                               np.linspace(0.0, 1.0, resolution + 1), order)
    elif dim == 3:
        from meshmode.mesh.generation import generate_icosphere
        mesh = generate_icosphere(radius,
                                  order=4 * order,
                                  uniform_refinement_rounds=resolution)
    else:
        raise ValueError("unsupported dimension")

    discr_tag_to_group_factory = {}
    if product_tag == "none":
        product_tag = None
    else:
        product_tag = dof_desc.DISCR_TAG_QUAD

    from meshmode.discretization.poly_element import \
            PolynomialWarpAndBlendGroupFactory, \
            QuadratureSimplexGroupFactory

    discr_tag_to_group_factory[dof_desc.DISCR_TAG_BASE] = \
        PolynomialWarpAndBlendGroupFactory(order)

    if product_tag:
        discr_tag_to_group_factory[product_tag] = \
            QuadratureSimplexGroupFactory(order=4*order)

    from grudge import DiscretizationCollection
    discr = DiscretizationCollection(
        actx, mesh, discr_tag_to_group_factory=discr_tag_to_group_factory)

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

    # }}}

    # {{{ symbolic operators

    def f_initial_condition(x):
        return x[0]

    from grudge.models.advection import SurfaceAdvectionOperator
    op = SurfaceAdvectionOperator(c, flux_type=flux_type, quad_tag=product_tag)

    bound_op = bind(discr, op.sym_operator())
    u0 = bind(discr, f_initial_condition(sym_x))(actx, t=0)

    def rhs(t, u):
        return bound_op(actx, t=t, u=u)

    # check velocity is tangential
    sym_normal = sym.surface_normal(dim, dim=dim - 1,
                                    dd=dof_desc.DD_VOLUME).as_vector()
    error = bind(discr, sym.norm(2, c.dot(sym_normal)))(actx)
    logger.info("u_dot_n:   %.5e", error)

    # }}}

    # {{{ time stepping

    # compute time step
    h_min = bind(discr, sym.h_max_from_volume(discr.ambient_dim,
                                              dim=discr.dim))(actx)
    dt = dt_factor * h_min / order**2
    nsteps = int(final_time // dt) + 1
    dt = final_time / nsteps + 1.0e-15

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

    from grudge.shortcuts import set_up_rk4
    dt_stepper = set_up_rk4("u", dt, u0, rhs)
    plot = Plotter(actx, discr, order, visualize=visualize)

    norm = bind(discr, sym.norm(2, sym.var("u")))
    norm_u = norm(actx, u=u0)

    step = 0

    event = dt_stepper.StateComputed(0.0, 0, 0, u0)
    plot(event, "fld-surface-%04d" % 0)

    if visualize and dim == 3:
        from grudge.shortcuts import make_visualizer
        vis = make_visualizer(discr)
        vis.write_vtk_file("fld-surface-velocity.vtu",
                           [("u", bind(discr, c)(actx)),
                            ("n", bind(discr, sym_normal)(actx))],
                           overwrite=True)

        df = dof_desc.DOFDesc(FACE_RESTR_INTERIOR)
        face_discr = discr.connection_from_dds(dof_desc.DD_VOLUME, df).to_discr

        face_normal = bind(
            discr, sym.normal(df, face_discr.ambient_dim,
                              dim=face_discr.dim))(actx)

        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, face_discr)
        vis.write_vtk_file("fld-surface-face-normals.vtu",
                           [("n", face_normal)],
                           overwrite=True)

    for event in dt_stepper.run(t_end=final_time, max_steps=nsteps + 1):
        if not isinstance(event, dt_stepper.StateComputed):
            continue

        step += 1
        if step % 10 == 0:
            norm_u = norm(actx, u=event.state_component)
            plot(event, "fld-surface-%04d" % step)

        logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u)

    plot(event, "fld-surface-%04d" % step)
Exemplo n.º 26
0
def main(dims, write_output=True, order=4):
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(a=(0.0, ) * dims,
                                      b=(1.0, ) * dims,
                                      nelements_per_axis=(4, ) * dims)

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

    if 0:
        epsilon0 = 8.8541878176e-12  # C**2 / (N m**2)
        mu0 = 4 * np.pi * 1e-7  # N/A**2.
        epsilon = 1 * epsilon0
        mu = 1 * mu0
    else:
        epsilon = 1
        mu = 1

    from grudge.models.em import MaxwellOperator
    op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims)

    if dims == 3:
        sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2))
        fields = bind(discr, sym_mode)(actx, t=0, epsilon=epsilon, mu=mu)
    else:
        sym_mode = get_rectangular_cavity_mode(1, (2, 3))
        fields = bind(discr, sym_mode)(actx, t=0)

    # FIXME
    #dt = op.estimate_rk4_timestep(discr, fields=fields)

    op.check_bc_coverage(mesh)

    # print(sym.pretty(op.sym_operator()))
    bound_op = bind(discr, op.sym_operator())

    def rhs(t, w):
        return bound_op(t=t, w=w)

    if mesh.dim == 2:
        dt = 0.004
    elif mesh.dim == 3:
        dt = 0.002

    dt_stepper = set_up_rk4("w", dt, fields, rhs)

    final_t = dt * STEPS
    nsteps = int(final_t / dt)

    print("dt=%g nsteps=%d" % (dt, nsteps))

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

    step = 0

    norm = bind(discr, sym.norm(2, sym.var("u")))

    from time import time
    t_last_step = time()

    e, h = op.split_eh(fields)

    if 1:
        vis.write_vtk_file("fld-cavities-%04d.vtu" % step, [
            ("e", e),
            ("h", h),
        ])

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

            step += 1

            print(step, event.t, norm(u=e[0]), norm(u=e[1]), norm(u=h[0]),
                  norm(u=h[1]),
                  time() - t_last_step)
            if step % 10 == 0:
                e, h = op.split_eh(event.state_component)
                vis.write_vtk_file("fld-cavities-%04d.vtu" % step, [
                    ("e", e),
                    ("h", h),
                ])
            t_last_step = time()
Exemplo n.º 27
0
def transcribe_phase(dag, field_var_name, field_components, phase_name,
                     sym_operator):
    """Generate a Grudge operator for a Dagrt time integrator phase.

    Arguments:

        dag: The Dagrt code object for the time integrator

        field_var_name: The name of the simulation variable

        field_components: The number of components (fields) in the variable

        phase_name: The name of the phase to transcribe

        sym_operator: The Grudge symbolic expression to substitue for the
            right-hand side evaluation in the Dagrt code
    """
    sym_operator = gmap.OperatorBinder()(sym_operator)
    phase = dag.phases[phase_name]

    ctx = {
            "<t>": sym.var("input_t", dof_desc.DD_SCALAR),
            "<dt>": sym.var("input_dt", dof_desc.DD_SCALAR),
            f"<state>{field_var_name}": sym.make_sym_array(
                f"input_{field_var_name}", field_components),
            "<p>residual": sym.make_sym_array(
                "input_residual", field_components),
    }

    rhs_name = f"<func>{field_var_name}"
    output_vars = [v for v in ctx]
    yielded_states = []

    ordered_stmts = topological_sort(
            isolate_function_calls_in_phase(
                phase,
                dag.get_stmt_id_generator(),
                dag.get_var_name_generator()).statements,
            phase.depends_on)

    for stmt in ordered_stmts:
        if stmt.condition is not True:
            raise NotImplementedError(
                "non-True condition (in statement '%s') not supported"
                % stmt.id)

        if isinstance(stmt, lang.Nop):
            pass

        elif isinstance(stmt, lang.Assign):
            if not isinstance(stmt.lhs, p.Variable):
                raise NotImplementedError("lhs of statement %s is not a variable: %s"
                        % (stmt.id, stmt.lhs))
            ctx[stmt.lhs.name] = sym.cse(
                    DagrtToGrudgeRewriter(ctx)(stmt.rhs),
                    (
                        stmt.lhs.name
                        .replace("<", "")
                        .replace(">", "")))

        elif isinstance(stmt, lang.AssignFunctionCall):
            if stmt.function_id != rhs_name:
                raise NotImplementedError(
                        "statement '%s' calls unsupported function '%s'"
                        % (stmt.id, stmt.function_id))

            if stmt.parameters:
                raise NotImplementedError(
                    "statement '%s' calls function '%s' with positional arguments"
                    % (stmt.id, stmt.function_id))

            kwargs = {name: sym.cse(DagrtToGrudgeRewriter(ctx)(arg))
                      for name, arg in stmt.kw_parameters.items()}

            if len(stmt.assignees) != 1:
                raise NotImplementedError(
                    "statement '%s' calls function '%s' "
                    "with more than one LHS"
                    % (stmt.id, stmt.function_id))

            assignee, = stmt.assignees
            ctx[assignee] = GrudgeArgSubstitutor(kwargs)(sym_operator)

        elif isinstance(stmt, lang.YieldState):
            d2g = DagrtToGrudgeRewriter(ctx)
            yielded_states.append(
                    (
                        stmt.time_id,
                        d2g(stmt.time),
                        stmt.component_id,
                        d2g(stmt.expression)))

        else:
            raise NotImplementedError("statement %s is of unsupported type ''%s'"
                        % (stmt.id, type(stmt).__name__))

    return output_vars, [ctx[ov] for ov in output_vars], yielded_states
Exemplo n.º 28
0
def main(ctx_factory, dim=2, order=4, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ parameters

    # domain [-d/2, d/2]^dim
    d = 1.0
    # number of points in each dimension
    npoints = 20
    # grid spacing
    h = d / npoints

    # cfl
    dt_factor = 2.0
    # final time
    final_time = 1.0
    # compute number of steps
    dt = dt_factor * h / order**2
    nsteps = int(final_time // dt) + 1
    dt = final_time / nsteps + 1.0e-15

    # velocity field
    c = np.array([0.5] * dim)
    norm_c = la.norm(c)
    # flux
    flux_type = "central"

    # }}}

    # {{{ discretization

    from meshmode.mesh.generation import generate_box_mesh
    mesh = generate_box_mesh(
        [np.linspace(-d / 2, d / 2, npoints) for _ in range(dim)], order=order)

    from grudge import DiscretizationCollection
    discr = DiscretizationCollection(actx, mesh, order=order)

    # }}}

    # {{{ symbolic operators

    def f(x):
        return sym.sin(3 * x)

    def u_analytic(x):
        t = sym.var("t", dof_desc.DD_SCALAR)
        return f(-np.dot(c, x) / norm_c + t * norm_c)

    from grudge.models.advection import WeakAdvectionOperator
    op = WeakAdvectionOperator(c,
                               inflow_u=u_analytic(sym.nodes(dim, BTAG_ALL)),
                               flux_type=flux_type)

    bound_op = bind(discr, op.sym_operator())
    u = bind(discr, u_analytic(sym.nodes(dim)))(actx, t=0)

    def rhs(t, u):
        return bound_op(t=t, u=u)

    # }}}

    # {{{ time stepping

    from grudge.shortcuts import set_up_rk4
    dt_stepper = set_up_rk4("u", dt, u, rhs)
    plot = Plotter(actx, discr, order, visualize=visualize, ylim=[-1.1, 1.1])

    norm = bind(discr, sym.norm(2, sym.var("u")))

    step = 0
    norm_u = 0.0
    for event in dt_stepper.run(t_end=final_time):
        if not isinstance(event, dt_stepper.StateComputed):
            continue

        if step % 10 == 0:
            norm_u = norm(u=event.state_component)
            plot(event, "fld-weak-%04d" % step)

        step += 1
        logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u)
Exemplo n.º 29
0
 def u_analytic(x):
     t = sym.var("t", dof_desc.DD_SCALAR)
     return f(-np.dot(c, x) / norm_c + t * norm_c)
Exemplo n.º 30
0
def test_convergence_maxwell(ctx_factory, order):
    """Test whether 3D Maxwell's actually converges"""

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

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

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

        discr = DGDiscretizationWithBoundaries(actx, mesh, order=order)

        epsilon = 1
        mu = 1

        from grudge.models.em import get_rectangular_cavity_mode
        sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2))

        analytic_sol = bind(discr, sym_mode)
        fields = analytic_sol(actx, t=0, epsilon=epsilon, mu=mu)

        from grudge.models.em import MaxwellOperator
        op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims)
        op.check_bc_coverage(mesh)
        bound_op = bind(discr, op.sym_operator())

        def rhs(t, w):
            return bound_op(t=t, w=w)

        dt = 0.002
        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)

        norm = bind(discr, sym.norm(2, sym.var("u")))

        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(actx, mu=mu, epsilon=epsilon, t=step * dt)
        vals = [norm(u=(esc[i] - sol[i])) / norm(u=sol[i])
                for i in range(5)]  # noqa E501
        total_error = sum(vals)
        eoc_rec.add_data_point(1.0 / n, total_error)

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

    assert eoc_rec.order_estimate() > order