Ejemplo n.º 1
0
def euler_operator(discr, eos, boundaries, cv, time=0.0):
    r"""Compute RHS of the Euler flow equations.

    Returns
    -------
    numpy.ndarray
        The right-hand-side of the Euler flow equations:

        .. math::

            \dot{\mathbf{q}} = - \nabla\cdot\mathbf{F} +
                (\mathbf{F}\cdot\hat{n})_{\partial\Omega}

    Parameters
    ----------
    cv: :class:`mirgecom.fluid.ConservedVars`
        Fluid conserved state object with the conserved variables.

    boundaries
        Dictionary of boundary functions, one for each valid btag

    time
        Time

    eos: mirgecom.eos.GasEOS
        Implementing the pressure and temperature functions for
        returning pressure and temperature as a function of the state q.

    Returns
    -------
    numpy.ndarray
        Agglomerated object array of DOF arrays representing the RHS of the Euler
        flow equations.
    """
    inviscid_flux_vol = inviscid_flux(discr, eos, cv)
    inviscid_flux_bnd = (inviscid_facial_flux(
        discr, eos=eos, cv_tpair=interior_trace_pair(discr, cv)) + sum(
            inviscid_facial_flux(
                discr,
                eos=eos,
                cv_tpair=TracePair(
                    part_tpair.dd,
                    interior=make_conserved(discr.dim, q=part_tpair.int),
                    exterior=make_conserved(discr.dim, q=part_tpair.ext)))
            for part_tpair in cross_rank_trace_pairs(discr, cv.join())) +
                         sum(boundaries[btag].inviscid_boundary_flux(
                             discr, btag=btag, cv=cv, eos=eos, time=time)
                             for btag in boundaries))
    q = -div_operator(discr, inviscid_flux_vol.join(),
                      inviscid_flux_bnd.join())
    return make_conserved(discr.dim, q=q)
Ejemplo n.º 2
0
def test_facial_flux(actx_factory, nspecies, order, dim):
    """Check the flux across element faces.

    The flux is checked by prescribing states (q) with known fluxes. Only uniform
    states are tested currently - ensuring that the Lax-Friedrichs flux terms which
    are proportional to jumps in state data vanish.

    Since the returned fluxes use state data which has been interpolated
    to-and-from the element faces, this test is grid-dependent.
    """
    actx = actx_factory()

    tolerance = 1e-14
    p0 = 1.0

    from meshmode.mesh.generation import generate_regular_rect_mesh
    from pytools.convergence import EOCRecorder

    eoc_rec0 = EOCRecorder()
    eoc_rec1 = EOCRecorder()
    for nel_1d in [4, 8, 12]:

        mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                          b=(0.5, ) * dim,
                                          nelements_per_axis=(nel_1d, ) * dim)

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

        discr = EagerDGDiscretization(actx, mesh, order=order)
        zeros = discr.zeros(actx)
        ones = zeros + 1.0

        mass_input = discr.zeros(actx) + 1.0
        energy_input = discr.zeros(actx) + 2.5
        mom_input = flat_obj_array(
            [discr.zeros(actx) for i in range(discr.dim)])
        mass_frac_input = flat_obj_array(
            [ones / ((i + 1) * 10) for i in range(nspecies)])
        species_mass_input = mass_input * mass_frac_input

        cv = make_conserved(dim,
                            mass=mass_input,
                            energy=energy_input,
                            momentum=mom_input,
                            species_mass=species_mass_input)
        from grudge.trace_pair import interior_trace_pairs
        cv_interior_pairs = interior_trace_pairs(discr, cv)
        # Check the boundary facial fluxes as called on an interior boundary
        # eos = IdealSingleGas()
        from mirgecom.gas_model import (GasModel, make_fluid_state)
        gas_model = GasModel(eos=IdealSingleGas())
        from mirgecom.gas_model import make_fluid_state_trace_pairs
        state_tpairs = make_fluid_state_trace_pairs(cv_interior_pairs,
                                                    gas_model)
        interior_state_pair = state_tpairs[0]
        from mirgecom.inviscid import inviscid_facial_flux
        interior_face_flux = \
            inviscid_facial_flux(discr, state_tpair=interior_state_pair)

        def inf_norm(data):
            if len(data) > 0:
                return actx.to_numpy(discr.norm(data, np.inf, dd="all_faces"))
            else:
                return 0.0

        assert inf_norm(interior_face_flux.mass) < tolerance
        assert inf_norm(interior_face_flux.energy) < tolerance
        assert inf_norm(interior_face_flux.species_mass) < tolerance

        # The expected pressure is 1.0 (by design). And the flux diagonal is
        # [rhov_x*v_x + p] (etc) since we have zero velocities it's just p.
        #
        # The off-diagonals are zero. We get a {ndim}-vector for each
        # dimension, the flux for the x-component of momentum (for example) is:
        # f_momx = < 1.0, 0 , 0> , then we return f_momx .dot. normal, which
        # can introduce negative values.
        #
        # (Explanation courtesy of Mike Campbell,
        # https://github.com/illinois-ceesd/mirgecom/pull/44#discussion_r463304292)

        nhat = thaw(actx, discr.normal("int_faces"))
        mom_flux_exact = discr.project("int_faces", "all_faces", p0 * nhat)
        print(f"{mom_flux_exact=}")
        print(f"{interior_face_flux.momentum=}")
        momerr = inf_norm(interior_face_flux.momentum - mom_flux_exact)
        assert momerr < tolerance
        eoc_rec0.add_data_point(1.0 / nel_1d, momerr)

        # Check the boundary facial fluxes as called on a domain boundary
        dir_mass = discr.project("vol", BTAG_ALL, mass_input)
        dir_e = discr.project("vol", BTAG_ALL, energy_input)
        dir_mom = discr.project("vol", BTAG_ALL, mom_input)
        dir_mf = discr.project("vol", BTAG_ALL, species_mass_input)

        dir_bc = make_conserved(dim,
                                mass=dir_mass,
                                energy=dir_e,
                                momentum=dir_mom,
                                species_mass=dir_mf)
        dir_bval = make_conserved(dim,
                                  mass=dir_mass,
                                  energy=dir_e,
                                  momentum=dir_mom,
                                  species_mass=dir_mf)
        state_tpair = TracePair(BTAG_ALL,
                                interior=make_fluid_state(dir_bval, gas_model),
                                exterior=make_fluid_state(dir_bc, gas_model))
        boundary_flux = inviscid_facial_flux(discr, state_tpair=state_tpair)

        assert inf_norm(boundary_flux.mass) < tolerance
        assert inf_norm(boundary_flux.energy) < tolerance
        assert inf_norm(boundary_flux.species_mass) < tolerance

        nhat = thaw(actx, discr.normal(BTAG_ALL))
        mom_flux_exact = discr.project(BTAG_ALL, "all_faces", p0 * nhat)
        momerr = inf_norm(boundary_flux.momentum - mom_flux_exact)
        assert momerr < tolerance

        eoc_rec1.add_data_point(1.0 / nel_1d, momerr)

    logger.info(f"standalone Errors:\n{eoc_rec0}"
                f"boundary Errors:\n{eoc_rec1}")
    assert (eoc_rec0.order_estimate() >= order - 0.5
            or eoc_rec0.max_error() < 1e-9)
    assert (eoc_rec1.order_estimate() >= order - 0.5
            or eoc_rec1.max_error() < 1e-9)
Ejemplo n.º 3
0
def test_slipwall_flux(actx_factory, dim, order):
    """Check for zero boundary flux.

    Check for vanishing flux across the slipwall.
    """
    actx = actx_factory()

    wall = AdiabaticSlipBoundary()
    eos = IdealSingleGas()
    gas_model = GasModel(eos=eos)

    from pytools.convergence import EOCRecorder
    eoc = EOCRecorder()

    for nel_1d in [4, 8, 12]:
        from meshmode.mesh.generation import generate_regular_rect_mesh

        mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                          b=(0.5, ) * dim,
                                          nelements_per_axis=(nel_1d, ) * dim)

        discr = EagerDGDiscretization(actx, mesh, order=order)
        nodes = thaw(actx, discr.nodes())
        nhat = thaw(actx, discr.normal(BTAG_ALL))
        h = 1.0 / nel_1d

        def bnd_norm(vec):
            return actx.to_numpy(discr.norm(vec, p=np.inf, dd=BTAG_ALL))

        logger.info(f"Number of {dim}d elems: {mesh.nelements}")
        # for velocities in each direction
        err_max = 0.0
        for vdir in range(dim):
            vel = np.zeros(shape=(dim, ))

            # for velocity directions +1, and -1
            for parity in [1.0, -1.0]:
                vel[vdir] = parity
                from mirgecom.initializers import Uniform
                initializer = Uniform(dim=dim, velocity=vel)
                uniform_state = initializer(nodes)
                fluid_state = make_fluid_state(uniform_state, gas_model)

                interior_soln = project_fluid_state(discr,
                                                    "vol",
                                                    BTAG_ALL,
                                                    state=fluid_state,
                                                    gas_model=gas_model)

                bnd_soln = wall.adiabatic_slip_state(discr,
                                                     btag=BTAG_ALL,
                                                     gas_model=gas_model,
                                                     state_minus=interior_soln)

                from grudge.trace_pair import TracePair
                bnd_pair = TracePair(BTAG_ALL,
                                     interior=interior_soln.cv,
                                     exterior=bnd_soln.cv)
                state_pair = TracePair(BTAG_ALL,
                                       interior=interior_soln,
                                       exterior=bnd_soln)

                # Check the total velocity component normal
                # to each surface.  It should be zero.  The
                # numerical fluxes cannot be zero.
                avg_state = 0.5 * (bnd_pair.int + bnd_pair.ext)
                err_max = max(err_max,
                              bnd_norm(np.dot(avg_state.momentum, nhat)))

                from mirgecom.inviscid import inviscid_facial_flux
                bnd_flux = inviscid_facial_flux(discr, state_pair, local=True)
                err_max = max(err_max, bnd_norm(bnd_flux.mass),
                              bnd_norm(bnd_flux.energy))

        eoc.add_data_point(h, err_max)

    message = (f"EOC:\n{eoc}")
    logger.info(message)
    assert (eoc.order_estimate() >= order - 0.5 or eoc.max_error() < 1e-12)
Ejemplo n.º 4
0
def euler_operator(discr,
                   state,
                   gas_model,
                   boundaries,
                   time=0.0,
                   quadrature_tag=None):
    r"""Compute RHS of the Euler flow equations.

    Returns
    -------
    numpy.ndarray
        The right-hand-side of the Euler flow equations:

        .. math::

            \dot{\mathbf{q}} = - \nabla\cdot\mathbf{F} +
                (\mathbf{F}\cdot\hat{n})_{\partial\Omega}

    Parameters
    ----------
    state: :class:`~mirgecom.gas_model.FluidState`

        Fluid state object with the conserved state, and dependent
        quantities.

    boundaries

        Dictionary of boundary functions, one for each valid btag

    time

        Time

    gas_model: :class:`~mirgecom.gas_model.GasModel`

        Physical gas model including equation of state, transport,
        and kinetic properties as required by fluid state

    quadrature_tag

        An optional identifier denoting a particular quadrature
        discretization to use during operator evaluations.
        The default value is *None*.

    Returns
    -------
    :class:`mirgecom.fluid.ConservedVars`
    """
    dd_base_vol = DOFDesc("vol")
    dd_quad_vol = DOFDesc("vol", quadrature_tag)
    dd_quad_faces = DOFDesc("all_faces", quadrature_tag)

    # project pair to the quadrature discretization and update dd to quad
    def _interp_to_surf_quad(utpair):
        local_dd = utpair.dd
        local_dd_quad = local_dd.with_discr_tag(quadrature_tag)
        return TracePair(local_dd_quad,
                         interior=op.project(discr, local_dd, local_dd_quad,
                                             utpair.int),
                         exterior=op.project(discr, local_dd, local_dd_quad,
                                             utpair.ext))

    boundary_states_quad = {
        btag:
        project_fluid_state(discr, dd_base_vol,
                            as_dofdesc(btag).with_discr_tag(quadrature_tag),
                            state, gas_model)
        for btag in boundaries
    }

    # performs MPI communication of CV if needed
    cv_interior_pairs = [
        # Get the interior trace pairs onto the surface quadrature
        # discretization (if any)
        _interp_to_surf_quad(tpair)
        for tpair in interior_trace_pairs(discr, state.cv)
    ]

    tseed_interior_pairs = None
    if state.is_mixture:
        # If this is a mixture, we need to exchange the temperature field because
        # mixture pressure (used in the inviscid flux calculations) depends on
        # temperature and we need to seed the temperature calculation for the
        # (+) part of the partition boundary with the remote temperature data.
        tseed_interior_pairs = [
            # Get the interior trace pairs onto the surface quadrature
            # discretization (if any)
            _interp_to_surf_quad(tpair)
            for tpair in interior_trace_pairs(discr, state.temperature)
        ]

    interior_states_quad = make_fluid_state_trace_pairs(
        cv_interior_pairs, gas_model, tseed_interior_pairs)

    # Interpolate the fluid state to the volume quadrature grid
    # (this includes the conserved and dependent quantities)
    vol_state_quad = project_fluid_state(discr, dd_base_vol, dd_quad_vol,
                                         state, gas_model)

    # Compute volume contributions
    inviscid_flux_vol = inviscid_flux(vol_state_quad)
    # Compute interface contributions
    inviscid_flux_bnd = (

        # Interior faces
        sum(
            inviscid_facial_flux(discr, state_pair)
            for state_pair in interior_states_quad)

        # Domain boundary faces
        + sum(boundaries[btag].inviscid_divergence_flux(
            discr,
            as_dofdesc(btag).with_discr_tag(quadrature_tag),
            gas_model,
            state_minus=boundary_states_quad[btag],
            time=time) for btag in boundaries))

    return -div_operator(discr, dd_quad_vol, dd_quad_faces, inviscid_flux_vol,
                         inviscid_flux_bnd)