コード例 #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_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
コード例 #3
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
コード例 #4
0
def test_mass_surface_area(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)
        surface_area = _ellipse_surface_area(builder.radius,
                                             builder.aspect_ratio)
    elif name == "spheroid":
        from mesh_data import SpheroidMeshBuilder
        builder = SpheroidMeshBuilder()
        surface_area = _spheroid_surface_area(builder.radius,
                                              builder.aspect_ratio)
    elif name == "box2d":
        from mesh_data import BoxMeshBuilder
        builder = BoxMeshBuilder(ambient_dim=2)
        surface_area = 1.0
    elif name == "box3d":
        from mesh_data import BoxMeshBuilder
        builder = BoxMeshBuilder(ambient_dim=3)
        surface_area = 1.0
    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)
        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 area

        dd = dof_desc.DD_VOLUME
        ones_volm = volume_discr.zeros(actx) + 1
        approx_surface_area = actx.to_numpy(op.integral(dcoll, dd, ones_volm))

        logger.info("surface: got {:.5e} / expected {:.5e}".format(
            approx_surface_area, surface_area))
        area_error = abs(approx_surface_area -
                         surface_area) / abs(surface_area)

        # }}}

        # 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, area_error)

    # }}}

    logger.info("surface area error\n%s", str(eoc))

    assert eoc.max_error() < 3e-13 or eoc.order_estimate() > builder.order
コード例 #5
0
def test_grad_operator(actx_factory, dim, order, sym_test_func_factory):
    """Test the gradient operator for sanity.

    Check whether we get the right answers for gradients of analytic functions with
    some simple input fields and states:
    - constant
    - multilinear funcs
    - quadratic funcs
    - trig funcs
    - :class:`~mirgecom.fluid.ConservedVars` composed of funcs from above
    """
    actx = actx_factory()

    sym_test_func = sym_test_func_factory(dim)

    tol = 1e-10 if dim < 3 else 1e-9
    from pytools.convergence import EOCRecorder
    eoc = EOCRecorder()

    for nfac in [1, 2, 4]:

        npts_axis = (nfac * 4, ) * dim
        box_ll = (0, ) * dim
        box_ur = (1, ) * dim
        mesh = _get_box_mesh(dim, a=box_ll, b=box_ur, n=npts_axis)

        logger.info(f"Number of {dim}d elements: {mesh.nelements}")

        discr = EagerDGDiscretization(actx, mesh, order=order)
        # compute max element size
        from grudge.dt_utils import h_max_from_volume
        h_max = h_max_from_volume(discr)

        def sym_eval(expr, x_vec):
            # FIXME: When pymbolic supports array containers
            mapper = sym.EvaluationMapper({"x": x_vec})
            from arraycontext import rec_map_array_container
            result = rec_map_array_container(mapper, expr)
            # If expressions don't depend on coords (e.g., order 0), evaluated result
            # will be scalar-valued, so promote to DOFArray(s) before returning
            return result * (0 * x_vec[0] + 1)

        test_func = partial(sym_eval, sym_test_func)
        grad_test_func = partial(sym_eval, sym_grad(dim, sym_test_func))

        nodes = thaw(actx, discr.nodes())
        int_flux = partial(central_flux_interior, actx, discr)
        bnd_flux = partial(central_flux_boundary, actx, discr, test_func)

        test_data = test_func(nodes)
        exact_grad = grad_test_func(nodes)

        from mirgecom.simutil import componentwise_norms
        from arraycontext import flatten

        err_scale = max(
            flatten(componentwise_norms(discr, exact_grad, np.inf), actx))

        if err_scale <= 1e-16:
            err_scale = 1

        print(f"{test_data=}")
        print(f"{exact_grad=}")

        test_data_int_tpair = interior_trace_pair(discr, test_data)
        boundaries = [BTAG_ALL]
        test_data_flux_bnd = _elbnd_flux(discr, int_flux, bnd_flux,
                                         test_data_int_tpair, boundaries)

        from mirgecom.operators import grad_operator
        from grudge.dof_desc import as_dofdesc
        dd_vol = as_dofdesc("vol")
        dd_faces = as_dofdesc("all_faces")
        test_grad = grad_operator(discr, dd_vol, dd_faces, test_data,
                                  test_data_flux_bnd)

        print(f"{test_grad=}")
        grad_err = \
            max(flatten(componentwise_norms(discr, test_grad - exact_grad, np.inf),
                        actx)) / err_scale

        eoc.add_data_point(actx.to_numpy(h_max), actx.to_numpy(grad_err))

    assert (eoc.order_estimate() >= order - 0.5 or eoc.max_error() < tol)
コード例 #6
0
ファイル: test_viscous.py プロジェクト: dreamer2368/mirgecom
def test_poiseuille_fluxes(actx_factory, order, kappa):
    """Test the viscous fluxes using a Poiseuille input state."""
    actx = actx_factory()
    dim = 2

    from pytools.convergence import EOCRecorder
    e_eoc_rec = EOCRecorder()
    p_eoc_rec = EOCRecorder()

    base_pressure = 100000.0
    pressure_ratio = 1.001
    mu = 42  # arbitrary
    left_boundary_location = 0
    right_boundary_location = 0.1
    ybottom = 0.
    ytop = .02
    nspecies = 0
    spec_diffusivity = 0 * np.ones(nspecies)
    transport_model = SimpleTransport(viscosity=mu,
                                      thermal_conductivity=kappa,
                                      species_diffusivity=spec_diffusivity)

    xlen = right_boundary_location - left_boundary_location
    p_low = base_pressure
    p_hi = pressure_ratio * base_pressure
    dpdx = (p_low - p_hi) / xlen
    rho = 1.0

    eos = IdealSingleGas()
    gas_model = GasModel(eos=eos, transport=transport_model)

    from mirgecom.initializers import PlanarPoiseuille
    initializer = PlanarPoiseuille(density=rho, mu=mu)

    def _elbnd_flux(discr, compute_interior_flux, compute_boundary_flux,
                    int_tpair, boundaries):
        return (compute_interior_flux(int_tpair) +
                sum(compute_boundary_flux(btag) for btag in boundaries))

    from mirgecom.flux import gradient_flux_central

    def cv_flux_interior(int_tpair):
        normal = thaw(actx, discr.normal(int_tpair.dd))
        flux_weak = gradient_flux_central(int_tpair, normal)
        dd_all_faces = int_tpair.dd.with_dtag("all_faces")
        return discr.project(int_tpair.dd, dd_all_faces, flux_weak)

    def cv_flux_boundary(btag):
        boundary_discr = discr.discr_from_dd(btag)
        bnd_nodes = thaw(actx, boundary_discr.nodes())
        cv_bnd = initializer(x_vec=bnd_nodes, eos=eos)
        bnd_nhat = thaw(actx, discr.normal(btag))
        from grudge.trace_pair import TracePair
        bnd_tpair = TracePair(btag, interior=cv_bnd, exterior=cv_bnd)
        flux_weak = gradient_flux_central(bnd_tpair, bnd_nhat)
        dd_all_faces = bnd_tpair.dd.with_dtag("all_faces")
        return discr.project(bnd_tpair.dd, dd_all_faces, flux_weak)

    for nfac in [1, 2, 4]:

        npts_axis = nfac * (11, 21)
        box_ll = (left_boundary_location, ybottom)
        box_ur = (right_boundary_location, ytop)
        mesh = _get_box_mesh(2, a=box_ll, b=box_ur, n=npts_axis)

        logger.info(f"Number of {dim}d elements: {mesh.nelements}")

        discr = EagerDGDiscretization(actx, mesh, order=order)
        nodes = thaw(actx, discr.nodes())

        def inf_norm(x):
            return actx.to_numpy(discr.norm(x, np.inf))

        # compute max element size
        from grudge.dt_utils import h_max_from_volume
        h_max = h_max_from_volume(discr)

        # form exact cv
        cv = initializer(x_vec=nodes, eos=eos)
        cv_int_tpair = interior_trace_pair(discr, cv)
        boundaries = [BTAG_ALL]
        cv_flux_bnd = _elbnd_flux(discr, cv_flux_interior, cv_flux_boundary,
                                  cv_int_tpair, boundaries)
        from mirgecom.operators import grad_operator
        from grudge.dof_desc import as_dofdesc
        dd_vol = as_dofdesc("vol")
        dd_faces = as_dofdesc("all_faces")
        grad_cv = grad_operator(discr, dd_vol, dd_faces, cv, cv_flux_bnd)

        xp_grad_cv = initializer.exact_grad(x_vec=nodes, eos=eos, cv_exact=cv)
        xp_grad_v = 1 / cv.mass * xp_grad_cv.momentum
        xp_tau = mu * (xp_grad_v + xp_grad_v.transpose())

        # sanity check the gradient:
        relerr_scale_e = 1.0 / inf_norm(xp_grad_cv.energy)
        relerr_scale_p = 1.0 / inf_norm(xp_grad_cv.momentum)
        graderr_e = inf_norm(grad_cv.energy - xp_grad_cv.energy)
        graderr_p = inf_norm(grad_cv.momentum - xp_grad_cv.momentum)
        graderr_e *= relerr_scale_e
        graderr_p *= relerr_scale_p
        assert graderr_e < 5e-7
        assert graderr_p < 5e-11

        zeros = discr.zeros(actx)
        ones = zeros + 1
        pressure = eos.pressure(cv)
        # grad of p should be dp/dx
        xp_grad_p = make_obj_array([dpdx * ones, zeros])
        grad_p = op.local_grad(discr, pressure)
        dpscal = 1.0 / np.abs(dpdx)

        temperature = eos.temperature(cv)
        tscal = rho * eos.gas_const() * dpscal
        xp_grad_t = xp_grad_p / (cv.mass * eos.gas_const())
        grad_t = op.local_grad(discr, temperature)

        # sanity check
        assert inf_norm(grad_p - xp_grad_p) * dpscal < 5e-9
        assert inf_norm(grad_t - xp_grad_t) * tscal < 5e-9

        fluid_state = make_fluid_state(cv, gas_model)
        # verify heat flux
        from mirgecom.viscous import conductive_heat_flux
        heat_flux = conductive_heat_flux(fluid_state, grad_t)
        xp_heat_flux = -kappa * xp_grad_t
        assert inf_norm(heat_flux - xp_heat_flux) < 2e-8

        xp_e_flux = np.dot(xp_tau, cv.velocity) - xp_heat_flux
        xp_mom_flux = xp_tau
        from mirgecom.viscous import viscous_flux
        vflux = viscous_flux(fluid_state, grad_cv, grad_t)

        efluxerr = (inf_norm(vflux.energy - xp_e_flux) / inf_norm(xp_e_flux))
        momfluxerr = (inf_norm(vflux.momentum - xp_mom_flux) /
                      inf_norm(xp_mom_flux))

        assert inf_norm(vflux.mass) == 0
        e_eoc_rec.add_data_point(actx.to_numpy(h_max), efluxerr)
        p_eoc_rec.add_data_point(actx.to_numpy(h_max), momfluxerr)

    assert (e_eoc_rec.order_estimate() >= order - 0.5
            or e_eoc_rec.max_error() < 3e-9)
    assert (p_eoc_rec.order_estimate() >= order - 0.5
            or p_eoc_rec.max_error() < 2e-12)