Пример #1
0
 def get_diffusion_flux(self, discr, quad_tag, dd, alpha,
                        grad_u):  # noqa: D102
     alpha_int = discr.project("vol", dd, alpha)
     alpha_tpair = TracePair(dd, interior=alpha_int, exterior=alpha_int)
     grad_u_int = discr.project("vol", dd, grad_u)
     grad_u_tpair = TracePair(dd, interior=grad_u_int, exterior=grad_u_int)
     return diffusion_flux(discr, quad_tag, alpha_tpair, grad_u_tpair)
Пример #2
0
def diffusion_flux(discr, quad_tag, alpha_tpair, grad_u_tpair):
    r"""Compute the numerical flux for $\nabla \cdot (\alpha \nabla u)$."""
    actx = grad_u_tpair.int[0].array_context

    dd = grad_u_tpair.dd
    dd_quad = dd.with_discr_tag(quad_tag)
    dd_allfaces_quad = dd_quad.with_dtag("all_faces")

    normal_quad = thaw(actx, discr.normal(dd_quad))

    def to_quad(a):
        return discr.project(dd, dd_quad, a)

    def flux(alpha, grad_u, normal):
        return -alpha * np.dot(grad_u, normal)

    flux_tpair = TracePair(dd_quad,
                           interior=flux(to_quad(alpha_tpair.int),
                                         to_quad(grad_u_tpair.int),
                                         normal_quad),
                           exterior=flux(to_quad(alpha_tpair.ext),
                                         to_quad(grad_u_tpair.ext),
                                         normal_quad))

    return discr.project(dd_quad, dd_allfaces_quad, flux_tpair.avg)
Пример #3
0
    def adiabatic_slip_pair(self, discr, cv, btag, **kwargs):
        """Get the interior and exterior solution on the boundary.

        The exterior solution is set such that there will be vanishing
        flux through the boundary, preserving mass, momentum (magnitude) and
        energy.
        rho_plus = rho_minus
        v_plus = v_minus - 2 * (v_minus . n_hat) * n_hat
        mom_plus = rho_plus * v_plus
        E_plus = E_minus
        """
        # Grab some boundary-relevant data
        dim = discr.dim
        actx = cv.mass.array_context

        # Grab a unit normal to the boundary
        nhat = thaw(actx, discr.normal(btag))

        # Get the interior/exterior solns
        int_cv = discr.project("vol", btag, cv)

        # Subtract out the 2*wall-normal component
        # of velocity from the velocity at the wall to
        # induce an equal but opposite wall-normal (reflected) wave
        # preserving the tangential component
        mom_normcomp = np.dot(int_cv.momentum, nhat)  # wall-normal component
        wnorm_mom = nhat * mom_normcomp  # wall-normal mom vec
        ext_mom = int_cv.momentum - 2.0 * wnorm_mom  # prescribed ext momentum

        # Form the external boundary solution with the new momentum
        ext_cv = make_conserved(dim=dim, mass=int_cv.mass, energy=int_cv.energy,
                                momentum=ext_mom, species_mass=int_cv.species_mass)
        return TracePair(btag, interior=int_cv, exterior=ext_cv)
Пример #4
0
def test_trace_pair(actx_factory):
    """Simple smoke test for :class:`grudge.trace_pair.TracePair`."""
    actx = actx_factory()
    dim = 3
    order = 1
    n = 4

    mesh = mgen.generate_regular_rect_mesh(
        a=(-1,)*dim, b=(1,)*dim,
        nelements_per_axis=(n,)*dim)

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

    def rand():
        return DOFArray(
                actx,
                tuple(actx.from_numpy(
                    np.random.rand(grp.nelements, grp.nunit_dofs))
                    for grp in dcoll.discr_from_dd("vol").groups))

    interior = rand()
    exterior = rand()
    tpair = TracePair("vol", interior=interior, exterior=exterior)

    import grudge.op as op
    assert op.norm(dcoll, tpair.avg - 0.5*(exterior + interior), np.inf) == 0
    assert op.norm(dcoll, tpair.diff - (exterior - interior), np.inf) == 0
    assert op.norm(dcoll, tpair.int - interior, np.inf) == 0
    assert op.norm(dcoll, tpair.ext - exterior, np.inf) == 0
Пример #5
0
 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))
Пример #6
0
def viscous_facial_flux(discr,
                        state_tpair,
                        grad_cv_tpair,
                        grad_t_tpair,
                        local=False):
    """Return the viscous flux across a face given the solution on both sides.

    Parameters
    ----------
    discr: :class:`~grudge.eager.EagerDGDiscretization`

        The discretization to use

    state_tpair: :class:`~grudge.trace_pair.TracePair`

        Trace pair of :class:`~mirgecom.gas_model.FluidState` with the full fluid
        conserved and thermal state on the faces

    grad_cv_tpair: :class:`~grudge.trace_pair.TracePair`

        Trace pair of :class:`~mirgecom.fluid.ConservedVars` with the gradient of the
        fluid solution on the faces

    grad_t_tpair: :class:`~grudge.trace_pair.TracePair`

        Trace pair of temperature gradient on the faces.

    local: bool

        Indicates whether to skip projection of fluxes to "all_faces" or not. If
        set to *False* (the default), the returned fluxes are projected to
        "all_faces".  If set to *True*, the returned fluxes are not projected to
        "all_faces"; remaining instead on the boundary restriction.

    Returns
    -------
    :class:`~mirgecom.fluid.ConservedVars`

        The viscous transport flux in the face-normal direction on "all_faces" or
        local to the sub-discretization depending on *local* input parameter
    """
    actx = state_tpair.int.array_context
    dd = state_tpair.dd
    dd_all_faces = dd.with_dtag("all_faces")

    normal = thaw(actx, discr.normal(dd))

    f_int = viscous_flux(state_tpair.int, grad_cv_tpair.int, grad_t_tpair.int)
    f_ext = viscous_flux(state_tpair.ext, grad_cv_tpair.ext, grad_t_tpair.ext)
    f_tpair = TracePair(dd, interior=f_int, exterior=f_ext)

    # todo: user-supplied flux routine
    # note: Hard-code central flux here for BR1
    flux_weak = divergence_flux_central(f_tpair, normal)

    if not local:
        return discr.project(dd, dd_all_faces, flux_weak)
    return flux_weak
Пример #7
0
    def boundary_pair(self, discr, cv, btag, **kwargs):
        """Get the interior and exterior solution on the boundary."""
        actx = cv.array_context

        boundary_discr = discr.discr_from_dd(btag)
        nodes = thaw(actx, boundary_discr.nodes())
        ext_soln = self._userfunc(nodes, **kwargs)
        int_soln = discr.project("vol", btag, cv)
        return TracePair(btag, interior=int_soln, exterior=ext_soln)
Пример #8
0
 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)
     return discr.project(bnd_tpair.dd, "all_faces", flux_weak)
Пример #9
0
def central_flux_boundary(actx, discr, soln_func, btag):
    """Compute a central flux for boundary faces."""
    boundary_discr = discr.discr_from_dd(btag)
    bnd_nodes = thaw(actx, boundary_discr.nodes())
    soln_bnd = soln_func(x_vec=bnd_nodes)
    bnd_nhat = thaw(actx, discr.normal(btag))
    from grudge.trace_pair import TracePair
    bnd_tpair = TracePair(btag, interior=soln_bnd, exterior=soln_bnd)
    flux_weak = gradient_flux_central(bnd_tpair, bnd_nhat)
    return discr.project(bnd_tpair.dd, "all_faces", flux_weak)
Пример #10
0
def bdry_tpair(dd, interior, exterior):
    """
    :arg interior: an expression that already lives on the boundary
        representing the interior value to be used
        for the flux.
    :arg exterior: an expression that already lives on the boundary
        representing the exterior value to be used
        for the flux.
    """
    return TracePair(dd, interior=interior, exterior=exterior)
Пример #11
0
def inviscid_facial_flux(discr, eos, cv_tpair, local=False):
    r"""Return the flux across a face given the solution on both sides *q_tpair*.

    This flux is currently hard-coded to use a Rusanov-type  local Lax-Friedrichs
    (LFR) numerical flux at element boundaries. The numerical inviscid flux $F^*$ is
    calculated as:

    .. math::

        \mathbf{F}^{*}_{\mathtt{LFR}} = \frac{1}{2}(\mathbf{F}(q^-)
        +\mathbf{F}(q^+)) \cdot \hat{n} + \frac{\lambda}{2}(q^{-} - q^{+}),

    where $q^-, q^+$ are the fluid solution state on the interior and the
    exterior of the face on which the LFR flux is to be calculated, $\mathbf{F}$ is
    the inviscid fluid flux, $\hat{n}$ is the face normal, and $\lambda$ is the
    *local* maximum fluid wavespeed.

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

    q_tpair: :class:`grudge.trace_pair.TracePair`
        Trace pair for the face upon which flux calculation is to be performed

    local: bool
        Indicates whether to skip projection of fluxes to "all_faces" or not. If
        set to *False* (the default), the returned fluxes are projected to
        "all_faces."  If set to *True*, the returned fluxes are not projected to
        "all_faces"; remaining instead on the boundary restriction.
    """
    actx = cv_tpair.int.array_context

    flux_tpair = TracePair(cv_tpair.dd,
                           interior=inviscid_flux(discr, eos, cv_tpair.int),
                           exterior=inviscid_flux(discr, eos, cv_tpair.ext))

    # This calculates the local maximum eigenvalue of the flux Jacobian
    # for a single component gas, i.e. the element-local max wavespeed |v| + c.
    lam = actx.np.maximum(compute_wavespeed(eos=eos, cv=cv_tpair.int),
                          compute_wavespeed(eos=eos, cv=cv_tpair.ext))

    normal = thaw(actx, discr.normal(cv_tpair.dd))

    # todo: user-supplied flux routine
    flux_weak = divergence_flux_lfr(cv_tpair,
                                    flux_tpair,
                                    normal=normal,
                                    lam=lam)

    if local is False:
        return discr.project(cv_tpair.dd, "all_faces", flux_weak)

    return flux_weak
Пример #12
0
def bv_tpair(dd, interior, exterior):
    """
    :arg interior: an expression that lives in the volume
        and will be restricted to the boundary identified by
        *tag* before use.
    :arg exterior: an expression that already lives on the boundary
        representing the exterior value to be used
        for the flux.
    """
    from grudge.symbolic.operators import project
    interior = cse(project("vol", dd)(interior))
    return TracePair(dd, interior=interior, exterior=exterior)
Пример #13
0
 def boundary_pair(self, discr, btag, cv, **kwargs):
     """Get the interior and exterior solution on the boundary."""
     if self._bnd_pair_func:
         return self._bnd_pair_func(discr, cv=cv, btag=btag, **kwargs)
     if not self._fluid_soln_func:
         raise NotImplementedError()
     actx = cv.array_context
     boundary_discr = discr.discr_from_dd(btag)
     nodes = thaw(actx, boundary_discr.nodes())
     nhat = thaw(actx, discr.normal(btag))
     int_soln = discr.project("vol", btag, cv)
     ext_soln = self._fluid_soln_func(nodes, cv=int_soln, normal=nhat, **kwargs)
     return TracePair(btag, interior=int_soln, exterior=ext_soln)
Пример #14
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)
Пример #15
0
def make_fluid_state_trace_pairs(cv_pairs,
                                 gas_model,
                                 temperature_seed_pairs=None):
    """Create a fluid state from the conserved vars and equation of state.

    This routine helps create a thermally consistent fluid state out of a collection
    of  CV (:class:`~mirgecom.fluid.ConservedVars`) pairs.  It is useful for creating
    consistent boundary states for partition boundaries.

    Parameters
    ----------
    cv_pairs: list of :class:`~grudge.trace_pair.TracePair`

        List of tracepairs of fluid CV (:class:`~mirgecom.fluid.ConservedVars`) for
        each boundary on which the thermally consistent state is desired

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

        The physical model constructs for the gas_model

    temperature_seed_pairs: list of :class:`~grudge.trace_pair.TracePair`

        List of tracepairs of :class:`~meshmode.dof_array.DOFArray` with the
        temperature seeds to use in creation of the thermally consistent states.

    Returns
    -------
    List of :class:`~grudge.trace_pair.TracePair`

        List of tracepairs of thermally consistent states
        (:class:`~mirgecom.gas_model.FluidState`) for each boundary in the input set
    """
    from grudge.trace_pair import TracePair
    if temperature_seed_pairs is None:
        temperature_seed_pairs = [None] * len(cv_pairs)
    return [
        TracePair(cv_pair.dd,
                  interior=make_fluid_state(cv_pair.int,
                                            gas_model,
                                            temperature_seed=_getattr_ish(
                                                tseed_pair, "int")),
                  exterior=make_fluid_state(cv_pair.ext,
                                            gas_model,
                                            temperature_seed=_getattr_ish(
                                                tseed_pair, "ext")))
        for cv_pair, tseed_pair in zip(cv_pairs, temperature_seed_pairs)
    ]
Пример #16
0
def v_dot_n_tpair(actx, dcoll, velocity, trace_dd):
    from grudge.dof_desc import DTAG_BOUNDARY
    from grudge.trace_pair import TracePair
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

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

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

    return TracePair(trace_dd, interior=i, exterior=e)
Пример #17
0
def to_quad_int_tpairs(dcoll, u, quad_tag):
    from grudge.dof_desc import DISCR_TAG_QUAD
    from grudge.trace_pair import TracePair

    if issubclass(quad_tag, DISCR_TAG_QUAD):
        return [
            TracePair(tpair.dd.with_discr_tag(quad_tag),
                      interior=op.project(dcoll, tpair.dd,
                                          tpair.dd.with_discr_tag(quad_tag),
                                          tpair.int),
                      exterior=op.project(dcoll, tpair.dd,
                                          tpair.dd.with_discr_tag(quad_tag),
                                          tpair.ext))
            for tpair in op.interior_trace_pairs(dcoll, u)
        ]
    else:
        return op.interior_trace_pairs(dcoll, u)
Пример #18
0
    def inviscid_divergence_flux(self, discr, btag, gas_model, state_minus,
                                 **kwargs):
        """Get the inviscid boundary flux for the divergence operator."""
        # This one is when the user specified a function that directly
        # prescribes the flux components at the boundary
        if self._inviscid_bnd_flux_func:
            return self._inviscid_bnd_flux_func(discr, btag, gas_model,
                                                state_minus, **kwargs)

        state_plus = self._bnd_state_func(discr=discr,
                                          btag=btag,
                                          gas_model=gas_model,
                                          state_minus=state_minus,
                                          **kwargs)
        boundary_state_pair = TracePair(btag,
                                        interior=state_minus,
                                        exterior=state_plus)

        return self._inviscid_div_flux_func(discr,
                                            state_tpair=boundary_state_pair)
Пример #19
0
def int_tpair(expression, qtag=None, from_dd=None):
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR
    from grudge.symbolic.operators import project, OppositeInteriorFaceSwap
    import grudge.dof_desc as dof_desc

    if from_dd is None:
        from_dd = dof_desc.DD_VOLUME
    assert not from_dd.uses_quadrature()

    trace_dd = dof_desc.DOFDesc(FACE_RESTR_INTERIOR, qtag)
    if from_dd.domain_tag == trace_dd.domain_tag:
        i = expression
    else:
        i = project(from_dd, trace_dd.with_qtag(None))(expression)
    e = cse(OppositeInteriorFaceSwap()(i))

    if trace_dd.uses_quadrature():
        i = cse(project(trace_dd.with_qtag(None), trace_dd)(i))
        e = cse(project(trace_dd.with_qtag(None), trace_dd)(e))

    return TracePair(trace_dd, interior=i, exterior=e)
Пример #20
0
def inviscid_facial_flux(discr, eos, cv_tpair, local=False):
    """Return the flux across a face given the solution on both sides *q_tpair*.

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

    q_tpair: :class:`grudge.trace_pair.TracePair`
        Trace pair for the face upon which flux calculation is to be performed

    local: bool
        Indicates whether to skip projection of fluxes to "all_faces" or not. If
        set to *False* (the default), the returned fluxes are projected to
        "all_faces."  If set to *True*, the returned fluxes are not projected to
        "all_faces"; remaining instead on the boundary restriction.
    """
    actx = cv_tpair.int.array_context

    flux_tpair = TracePair(cv_tpair.dd,
                           interior=inviscid_flux(discr, eos, cv_tpair.int),
                           exterior=inviscid_flux(discr, eos, cv_tpair.ext))

    lam = actx.np.maximum(compute_wavespeed(eos=eos, cv=cv_tpair.int),
                          compute_wavespeed(eos=eos, cv=cv_tpair.ext))

    normal = thaw(actx, discr.normal(cv_tpair.dd))

    # todo: user-supplied flux routine
    flux_weak = lfr_flux(cv_tpair, flux_tpair, normal=normal, lam=lam)

    if local is False:
        return discr.project(cv_tpair.dd, "all_faces", flux_weak)

    return flux_weak
Пример #21
0
def inviscid_facial_flux(discr, state_tpair, local=False):
    r"""Return the flux across a face given the solution on both sides *q_tpair*.

    This flux is currently hard-coded to use a Rusanov-type  local Lax-Friedrichs
    (LFR) numerical flux at element boundaries. The numerical inviscid flux $F^*$ is
    calculated as:

    .. math::

        \mathbf{F}^{*}_{\mathtt{LFR}} = \frac{1}{2}(\mathbf{F}(q^-)
        +\mathbf{F}(q^+)) \cdot \hat{n} + \frac{\lambda}{2}(q^{-} - q^{+}),

    where $q^-, q^+$ are the fluid solution state on the interior and the
    exterior of the face on which the LFR flux is to be calculated, $\mathbf{F}$ is
    the inviscid fluid flux, $\hat{n}$ is the face normal, and $\lambda$ is the
    *local* maximum fluid wavespeed.

    Parameters
    ----------
    discr: :class:`~grudge.eager.EagerDGDiscretization`

        The discretization collection to use

    state_tpair: :class:`~grudge.trace_pair.TracePair`

        Trace pair of :class:`~mirgecom.gas_model.FluidState` for the face upon
        which the flux calculation is to be performed

    local: bool

        Indicates whether to skip projection of fluxes to "all_faces" or not. If
        set to *False* (the default), the returned fluxes are projected to
        "all_faces."  If set to *True*, the returned fluxes are not projected to
        "all_faces"; remaining instead on the boundary restriction.

    Returns
    -------
    :class:`~mirgecom.fluid.ConservedVars`

        A CV object containing the scalar numerical fluxes at the input faces.
        The returned fluxes are scalar because they've already been dotted with
        the face normals as required by the divergence operator for which they
        are being computed.
    """
    actx = state_tpair.int.array_context
    dd = state_tpair.dd
    dd_all_faces = dd.with_dtag("all_faces")

    flux_tpair = TracePair(dd,
                           interior=inviscid_flux(state_tpair.int),
                           exterior=inviscid_flux(state_tpair.ext))

    # This calculates the local maximum eigenvalue of the flux Jacobian
    # for a single component gas, i.e. the element-local max wavespeed |v| + c.
    w_int = state_tpair.int.speed_of_sound + state_tpair.int.speed
    w_ext = state_tpair.ext.speed_of_sound + state_tpair.ext.speed
    lam = actx.np.maximum(w_int, w_ext)

    normal = thaw(actx, discr.normal(dd))
    cv_tpair = TracePair(dd,
                         interior=state_tpair.int.cv,
                         exterior=state_tpair.ext.cv)

    # todo: user-supplied flux routine
    flux_weak = divergence_flux_lfr(cv_tpair,
                                    flux_tpair,
                                    normal=normal,
                                    lam=lam)

    if local is False:
        return discr.project(dd, dd_all_faces, flux_weak)

    return flux_weak
Пример #22
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)
Пример #23
0
 def dummy_pair(self, discr, cv, btag, **kwargs):
     """Get the interior and exterior solution on the boundary."""
     dir_soln = self.exterior_q(discr, cv, btag, **kwargs)
     return TracePair(btag, interior=dir_soln, exterior=dir_soln)
Пример #24
0
def test_slipwall_identity(actx_factory, dim):
    """Identity test - check for the expected boundary solution.

    Checks that the slipwall implements the expected boundary solution:
    rho_plus = rho_minus
    v_plus = v_minus - 2 * (n_hat . v_minus) * n_hat
    mom_plus = rho_plus * v_plus
    E_plus = E_minus
    """
    actx = actx_factory()

    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())
    eos = IdealSingleGas()
    orig = np.zeros(shape=(dim, ))
    nhat = thaw(actx, discr.normal(BTAG_ALL))
    gas_model = GasModel(eos=eos)

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

    # for velocity going along each direction
    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  # Check incoming normal
            initializer = Lump(dim=dim, center=orig, velocity=vel, rhoamp=0.0)
            wall = AdiabaticSlipBoundary()

            uniform_state = initializer(nodes)
            fluid_state = make_fluid_state(uniform_state, gas_model)

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

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

            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)

            # check that mass and energy are preserved
            mass_resid = bnd_pair.int.mass - bnd_pair.ext.mass
            mass_err = bnd_norm(mass_resid)
            assert mass_err == 0.0

            energy_resid = bnd_pair.int.energy - bnd_pair.ext.energy
            energy_err = bnd_norm(energy_resid)
            assert energy_err == 0.0

            # check that exterior momentum term is mom_interior - 2 * mom_normal
            mom_norm_comp = np.dot(bnd_pair.int.momentum, nhat)
            mom_norm = nhat * mom_norm_comp
            expected_mom_ext = bnd_pair.int.momentum - 2.0 * mom_norm
            mom_resid = bnd_pair.ext.momentum - expected_mom_ext
            mom_err = bnd_norm(mom_resid)

            assert mom_err == 0.0
Пример #25
0
 def get_gradient_flux(self, discr, quad_tag, dd, alpha, u):  # noqa: D102
     u_int = discr.project("vol", dd, u)
     u_tpair = TracePair(dd, interior=u_int, exterior=u_int)
     return gradient_flux(discr, quad_tag, u_tpair)
Пример #26
0
 def boundary_pair(self, discr, cv, btag, **kwargs):
     """Get the interior and exterior solution on the boundary."""
     dir_soln = discr.project("vol", btag, cv)
     return TracePair(btag, interior=dir_soln, exterior=dir_soln)