Ejemplo n.º 1
0
def euler_operator(discr, eos, boundaries, cv, t=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

    t
        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.
    """
    vol_weak = discr.weak_div(inviscid_flux(discr=discr, eos=eos, cv=cv).join())

    boundary_flux = (
        _facial_flux(discr=discr, eos=eos, cv_tpair=interior_trace_pair(discr, cv))
        + sum(
            _facial_flux(
                discr, eos=eos,
                cv_tpair=TracePair(
                    part_pair.dd,
                    interior=split_conserved(discr.dim, part_pair.int),
                    exterior=split_conserved(discr.dim, part_pair.ext)))
            for part_pair in cross_rank_trace_pairs(discr, cv.join()))
        + sum(
            _facial_flux(
                discr=discr, eos=eos,
                cv_tpair=boundaries[btag].boundary_pair(
                    discr, eos=eos, btag=btag, t=t, cv=cv)
            )
            for btag in boundaries)
    ).join()

    return split_conserved(
        discr.dim, discr.inverse_mass(vol_weak - discr.face_mass(boundary_flux))
    )
Ejemplo n.º 2
0
def test_inviscid_flux_components(actx_factory, dim):
    """Test uniform pressure case.

    Checks that the Euler-internal inviscid flux routine
    :func:`mirgecom.inviscid.inviscid_flux` returns exactly the expected result
    with a constant pressure and no flow.

    Expected inviscid flux is:
      F(q) = <rhoV, (E+p)V, rho(V.x.V) + pI>

    Checks that only diagonal terms of the momentum flux:
      [ rho(V.x.V) + pI ] are non-zero and return the correctly calculated p.
    """
    actx = actx_factory()

    eos = IdealSingleGas()

    p0 = 1.0

    nel_1d = 4

    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)

    order = 3
    discr = EagerDGDiscretization(actx, mesh, order=order)
    eos = IdealSingleGas()

    logger.info(f"Number of {dim}d elems: {mesh.nelements}")
    # === this next block tests 1,2,3 dimensions,
    # with single and multiple nodes/states. The
    # purpose of this block is to ensure that when
    # all components of V = 0, the flux recovers
    # the expected values (and p0 within tolerance)
    # === with V = 0, fixed P = p0
    tolerance = 1e-15
    nodes = thaw(actx, discr.nodes())
    mass = discr.zeros(actx) + np.dot(nodes, nodes) + 1.0
    mom = make_obj_array([discr.zeros(actx) for _ in range(dim)])
    p_exact = discr.zeros(actx) + p0
    energy = p_exact / 0.4 + 0.5 * np.dot(mom, mom) / mass
    cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom)
    p = eos.pressure(cv)
    flux = inviscid_flux(discr, eos, cv)
    assert discr.norm(p - p_exact, np.inf) < tolerance
    logger.info(f"{dim}d flux = {flux}")

    # for velocity zero, these components should be == zero
    assert discr.norm(flux.mass, 2) == 0.0
    assert discr.norm(flux.energy, 2) == 0.0

    # The momentum diagonal should be p
    # Off-diagonal should be identically 0
    assert discr.norm(flux.momentum - p0 * np.identity(dim),
                      np.inf) < tolerance
Ejemplo n.º 3
0
def test_inviscid_mom_flux_components(actx_factory, dim, livedim):
    r"""Test components of the momentum flux with constant pressure, V != 0.

    Checks that the flux terms are returned in the proper order by running
    only 1 non-zero velocity component at-a-time.
    """
    actx = actx_factory()

    eos = IdealSingleGas()

    p0 = 1.0

    nel_1d = 4

    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)

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

    tolerance = 1e-15
    for livedim in range(dim):
        mass = discr.zeros(actx) + 1.0 + np.dot(nodes, nodes)
        mom = make_obj_array([discr.zeros(actx) for _ in range(dim)])
        mom[livedim] = mass
        p_exact = discr.zeros(actx) + p0
        energy = (p_exact / (eos.gamma() - 1.0) +
                  0.5 * np.dot(mom, mom) / mass)
        cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom)
        p = eos.pressure(cv)
        from mirgecom.gas_model import GasModel, make_fluid_state
        state = make_fluid_state(cv, GasModel(eos=eos))

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

        assert inf_norm(p - p_exact) < tolerance
        flux = inviscid_flux(state)
        logger.info(f"{dim}d flux = {flux}")
        vel_exact = mom / mass

        # first two components should be nonzero in livedim only
        assert inf_norm(flux.mass - mom) == 0
        eflux_exact = (energy + p_exact) * vel_exact
        assert inf_norm(flux.energy - eflux_exact) == 0

        logger.info("Testing momentum")
        xpmomflux = mass * np.outer(vel_exact,
                                    vel_exact) + p_exact * np.identity(dim)
        assert inf_norm(flux.momentum - xpmomflux) < tolerance
Ejemplo n.º 4
0
def test_inviscid_flux(actx_factory, nspecies, dim):
    """Check inviscid flux against exact expected result: Identity test.

    Directly check inviscid flux routine, :func:`mirgecom.inviscid.inviscid_flux`,
    against the exact expected result. This test is designed to fail if the flux
    routine is broken.

    The expected inviscid flux is:
      F(q) = <rhoV, (E+p)V, rho(V.x.V) + pI, rhoY V>
    """
    actx = actx_factory()

    nel_1d = 16

    from meshmode.mesh.generation import generate_regular_rect_mesh

    #    for dim in [1, 2, 3]:
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                      b=(0.5, ) * dim,
                                      nelements_per_axis=(nel_1d, ) * dim)

    order = 3
    discr = EagerDGDiscretization(actx, mesh, order=order)
    eos = IdealSingleGas()

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

    def rand():
        from meshmode.dof_array import DOFArray
        return DOFArray(
            actx,
            tuple(
                actx.from_numpy(np.random.rand(grp.nelements, grp.nunit_dofs))
                for grp in discr.discr_from_dd("vol").groups))

    mass = rand()
    energy = rand()
    mom = make_obj_array([rand() for _ in range(dim)])

    mass_fractions = make_obj_array([rand() for _ in range(nspecies)])
    species_mass = mass * mass_fractions

    cv = make_conserved(dim,
                        mass=mass,
                        energy=energy,
                        momentum=mom,
                        species_mass=species_mass)

    # {{{ create the expected result

    p = eos.pressure(cv)
    escale = (energy + p) / mass

    numeq = dim + 2 + nspecies

    expected_flux = np.zeros((numeq, dim), dtype=object)
    expected_flux[0] = mom
    expected_flux[1] = mom * escale

    for i in range(dim):
        for j in range(dim):
            expected_flux[2 + i,
                          j] = (mom[i] * mom[j] / mass + (p if i == j else 0))

    for i in range(nspecies):
        expected_flux[dim + 2 + i] = mom * mass_fractions[i]

    expected_flux = make_conserved(dim, q=expected_flux)

    # }}}

    from mirgecom.gas_model import GasModel, make_fluid_state
    gas_model = GasModel(eos=eos)
    state = make_fluid_state(cv, gas_model)

    flux = inviscid_flux(state)
    flux_resid = flux - expected_flux

    for i in range(numeq, dim):
        for j in range(dim):
            assert (la.norm(flux_resid[i, j].get())) == 0.0
Ejemplo n.º 5
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)