Ejemplo n.º 1
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))))
Ejemplo n.º 2
0
def wave_operator(dcoll, c, w):
    u = w[0]
    v = w[1:]

    dir_u = op.project(dcoll, "vol", BTAG_ALL, u)
    dir_v = op.project(dcoll, "vol", BTAG_ALL, v)
    dir_bval = flat_obj_array(dir_u, dir_v)
    dir_bc = flat_obj_array(-dir_u, dir_v)

    dd_quad = DOFDesc("vol", DISCR_TAG_QUAD)
    c_quad = op.project(dcoll, "vol", dd_quad, c)
    w_quad = op.project(dcoll, "vol", dd_quad, w)
    u_quad = w_quad[0]
    v_quad = w_quad[1:]

    dd_allfaces_quad = DOFDesc("all_faces", DISCR_TAG_QUAD)

    return (op.inverse_mass(
        dcoll,
        flat_obj_array(-op.weak_local_div(dcoll, dd_quad, c_quad * v_quad),
                       -op.weak_local_grad(dcoll, dd_quad, c_quad * u_quad))
        +  # noqa: W504
        op.face_mass(
            dcoll, dd_allfaces_quad,
            wave_flux(dcoll, c=c, w_tpair=op.interior_trace_pair(dcoll, w)) +
            wave_flux(dcoll,
                      c=c,
                      w_tpair=TracePair(
                          BTAG_ALL, interior=dir_bval, exterior=dir_bc)))))
Ejemplo n.º 3
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))
            ))
Ejemplo n.º 4
0
    def operator(self, t, u):
        from grudge.dof_desc import DOFDesc, DD_VOLUME, DTAG_VOLUME_ALL
        from meshmode.discretization.connection import FACE_RESTR_ALL

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

        dcoll = self.dcoll

        def flux(tpair):
            return op.project(dcoll, tpair.dd, face_dd, self.flux(tpair))

        def to_quad(arg):
            return op.project(dcoll, DD_VOLUME, quad_dd, arg)

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

        return (op.inverse_mass(
            dcoll,
            sum(
                op.weak_local_d_dx(dcoll, quad_dd, d, quad_u * quad_v[d])
                for d in range(dcoll.ambient_dim)) - op.face_mass(
                    dcoll, face_dd,
                    sum(
                        flux(quad_tpair) for quad_tpair in to_quad_int_tpairs(
                            dcoll, u, self.quad_tag)))))
Ejemplo n.º 5
0
    def operator(self, t, u):
        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

        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)

        dcoll = self.dcoll

        def flux(tpair):
            return op.project(dcoll, tpair.dd, face_dd, self.flux(tpair))

        def to_quad(arg):
            return op.project(dcoll, DD_VOLUME, quad_dd, arg)

        if self.inflow_u is not None:
            inflow_flux = flux(
                op.bv_trace_pair(dcoll,
                                 boundary_dd,
                                 interior=u,
                                 exterior=self.inflow_u(t)))
        else:
            inflow_flux = 0

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

        return (op.inverse_mass(
            dcoll,
            sum(
                op.weak_local_d_dx(dcoll, quad_dd, d, quad_u * quad_v[d])
                for d in range(dcoll.ambient_dim)) -
            op.face_mass(
                dcoll, face_dd,
                sum(
                    flux(quad_tpair) for quad_tpair in to_quad_int_tpairs(
                        dcoll, u, self.quad_tag)) + inflow_flux

                # FIXME: Add support for inflow/outflow tags
                # + flux(op.bv_trace_pair(dcoll,
                #                         self.inflow_tag,
                #                         interior=u,
                #                         exterior=bc_in))
                # + flux(op.bv_trace_pair(dcoll,
                #                         self.outflow_tag,
                #                         interior=u,
                #                         exterior=bc_out))
            )))
Ejemplo n.º 6
0
def v_dot_n_tpair(velocity, dd=None):
    from grudge.dof_desc import DOFDesc
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

    if dd is None:
        dd = DOFDesc(FACE_RESTR_INTERIOR)

    ambient_dim = len(velocity)
    normal = sym.normal(dd.with_discr_tag(None),
                        ambient_dim,
                        dim=ambient_dim - 2)

    return sym.int_tpair(velocity.dot(normal),
                         qtag=dd.discretization_tag,
                         from_dd=dd.with_discr_tag(None))
Ejemplo n.º 7
0
    def _modal_discr(self, domain_tag):
        from meshmode.discretization import Discretization

        discr_base = self.discr_from_dd(DOFDesc(domain_tag, DISCR_TAG_BASE))
        return Discretization(
            self._setup_actx, discr_base.mesh,
            self.group_factory_for_discretization_tag(DISCR_TAG_MODAL))
Ejemplo n.º 8
0
    def discr_from_dd(self, dd):
        dd = as_dofdesc(dd)

        discr_tag = dd.discretization_tag

        if discr_tag is DISCR_TAG_MODAL:
            return self._modal_discr(dd.domain_tag)

        if dd.is_volume():
            if discr_tag is not DISCR_TAG_BASE:
                return self._discr_tag_volume_discr(discr_tag)
            return self._volume_discr

        if discr_tag is not DISCR_TAG_BASE:
            no_quad_discr = self.discr_from_dd(DOFDesc(dd.domain_tag))

            from meshmode.discretization import Discretization
            return Discretization(
                self._setup_actx, no_quad_discr.mesh,
                self.group_factory_for_discretization_tag(discr_tag))

        assert discr_tag is DISCR_TAG_BASE

        if dd.domain_tag is FACE_RESTR_ALL:
            return self._all_faces_volume_connection().to_discr
        elif dd.domain_tag is FACE_RESTR_INTERIOR:
            return self._interior_faces_connection().to_discr
        elif dd.is_boundary_or_partition_interface():
            return self._boundary_connection(dd.domain_tag.tag).to_discr
        else:
            raise ValueError("DOF desc tag not understood: " + str(dd))
Ejemplo n.º 9
0
    def map_signed_face_ones(self, expr):
        from grudge.dof_desc import DOFDesc, DD_VOLUME

        assert expr.dd.is_trace()
        face_discr = self.dcoll.discr_from_dd(expr.dd)
        assert face_discr.dim == 0

        # NOTE: ignore quadrature_tags on expr.dd, since we only care about
        # the face_id here
        all_faces_conn = self.dcoll.connection_from_dds(
            DD_VOLUME, DOFDesc(expr.dd.domain_tag))

        field = face_discr.empty(self.array_context,
                                 dtype=self.dcoll.real_dtype)
        for grp_ary in field:
            grp_ary.fill(1)

        for igrp, grp in enumerate(all_faces_conn.groups):
            for batch in grp.batches:
                i = self.array_context.thaw(batch.to_element_indices)
                grp_field = field[igrp].reshape(-1)
                grp_field[i] = \
                        (2.0 * (batch.to_element_face % 2) - 1.0) * grp_field[i]

        return field
Ejemplo n.º 10
0
def wave_operator(dcoll, c, w):
    u = w[0]
    v = w[1:]

    dir_u = op.project(dcoll, "vol", BTAG_ALL, u)
    dir_v = op.project(dcoll, "vol", BTAG_ALL, v)
    dir_bval = flat_obj_array(dir_u, dir_v)
    dir_bc = flat_obj_array(-dir_u, dir_v)

    dd_quad = DOFDesc("vol", DISCR_TAG_QUAD)
    c_quad = op.project(dcoll, "vol", dd_quad, c)
    w_quad = op.project(dcoll, "vol", dd_quad, w)
    u_quad = w_quad[0]
    v_quad = w_quad[1:]

    dd_allfaces_quad = DOFDesc("all_faces", DISCR_TAG_QUAD)

    return (
        op.inverse_mass(
            dcoll,
            flat_obj_array(
                -op.weak_local_div(dcoll, dd_quad, c_quad*v_quad),
                -op.weak_local_grad(dcoll, dd_quad, c_quad*u_quad) \
                # pylint: disable=invalid-unary-operand-type
            ) + op.face_mass(
                dcoll,
                dd_allfaces_quad,
                wave_flux(
                    dcoll, c=c,
                    w_tpair=op.bdry_trace_pair(dcoll,
                                               BTAG_ALL,
                                               interior=dir_bval,
                                               exterior=dir_bc)
                ) + sum(
                    wave_flux(dcoll, c=c, w_tpair=tpair)
                    for tpair in op.interior_trace_pairs(dcoll, w)
                )
            )
        )
    )
Ejemplo n.º 11
0
    def _set_up_distributed_communication(self, mpi_communicator,
                                          array_context):
        from_dd = DOFDesc("vol", DISCR_TAG_BASE)

        boundary_connections = {}

        from meshmode.distributed import get_connected_partitions
        connected_parts = get_connected_partitions(self._volume_discr.mesh)

        if connected_parts:
            if mpi_communicator is None:
                raise RuntimeError(
                    "must supply an MPI communicator when using a "
                    "distributed mesh")

            grp_factory = \
                self.group_factory_for_discretization_tag(DISCR_TAG_BASE)

            local_boundary_connections = {}
            for i_remote_part in connected_parts:
                local_boundary_connections[
                    i_remote_part] = self.connection_from_dds(
                        from_dd,
                        DOFDesc(BTAG_PARTITION(i_remote_part), DISCR_TAG_BASE))

            from meshmode.distributed import MPIBoundaryCommSetupHelper
            with MPIBoundaryCommSetupHelper(mpi_communicator, array_context,
                                            local_boundary_connections,
                                            grp_factory) as bdry_setup_helper:
                while True:
                    conns = bdry_setup_helper.complete_some()
                    if not conns:
                        break
                    for i_remote_part, conn in conns.items():
                        boundary_connections[i_remote_part] = conn

        return boundary_connections
Ejemplo n.º 12
0
def _signed_face_ones(actx: ArrayContext, dcoll: DiscretizationCollection,
                      dd) -> DOFArray:

    assert dd.is_trace()

    # NOTE: ignore quadrature_tags on dd, since we only care about
    # the face_id here
    all_faces_conn = dcoll.connection_from_dds(DD_VOLUME,
                                               DOFDesc(dd.domain_tag))
    signed_face_ones = dcoll.discr_from_dd(dd).zeros(
        actx, dtype=dcoll.real_dtype) + 1
    for igrp, grp in enumerate(all_faces_conn.groups):
        for batch in grp.batches:
            i = actx.thaw(batch.to_element_indices)
            grp_field = signed_face_ones[igrp].reshape(-1)
            grp_field[i] = \
                (2.0 * (batch.to_element_face % 2) - 1.0) * grp_field[i]

    return signed_face_ones
Ejemplo n.º 13
0
    def connection_from_dds(self, from_dd, to_dd):
        """Provides a mapping (connection) from one discretization to
        another, e.g. from the volume to the boundary, or from the
        base to the an overintegrated quadrature discretization, or from
        a nodal representation to a modal representation.

        :arg from_dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value
            convertible to one.
        :arg to_dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value
            convertible to one.
        """
        from_dd = as_dofdesc(from_dd)
        to_dd = as_dofdesc(to_dd)

        to_discr_tag = to_dd.discretization_tag
        from_discr_tag = from_dd.discretization_tag

        # {{{ mapping between modal and nodal representations

        if (from_discr_tag is DISCR_TAG_MODAL
                and to_discr_tag is not DISCR_TAG_MODAL):
            return self._modal_to_nodal_connection(to_dd)

        if (to_discr_tag is DISCR_TAG_MODAL
                and from_discr_tag is not DISCR_TAG_MODAL):
            return self._nodal_to_modal_connection(from_dd)

        # }}}

        assert (to_discr_tag is not DISCR_TAG_MODAL
                and from_discr_tag is not DISCR_TAG_MODAL)

        if (not from_dd.is_volume() and from_discr_tag == to_discr_tag
                and to_dd.domain_tag is FACE_RESTR_ALL):
            faces_conn = self.connection_from_dds(DOFDesc("vol"),
                                                  DOFDesc(from_dd.domain_tag))

            from meshmode.discretization.connection import \
                    make_face_to_all_faces_embedding

            return make_face_to_all_faces_embedding(
                self._setup_actx, faces_conn, self.discr_from_dd(to_dd),
                self.discr_from_dd(from_dd))

        # {{{ simplify domain + discr_tag change into chained

        if (from_dd.domain_tag != to_dd.domain_tag
                and from_discr_tag is DISCR_TAG_BASE
                and to_discr_tag is not DISCR_TAG_BASE):

            from meshmode.discretization.connection import \
                    ChainedDiscretizationConnection
            intermediate_dd = DOFDesc(to_dd.domain_tag)
            return ChainedDiscretizationConnection([
                # first change domain
                self.connection_from_dds(from_dd, intermediate_dd),

                # then go to quad grid
                self.connection_from_dds(intermediate_dd, to_dd)
            ])

        # }}}

        # {{{ generic to-quad

        # DISCR_TAG_MODAL is handled above
        if (from_dd.domain_tag == to_dd.domain_tag
                and from_discr_tag is DISCR_TAG_BASE
                and to_discr_tag is not DISCR_TAG_BASE):

            from meshmode.discretization.connection.same_mesh import \
                    make_same_mesh_connection

            return make_same_mesh_connection(self._setup_actx,
                                             self.discr_from_dd(to_dd),
                                             self.discr_from_dd(from_dd))

        # }}}

        if from_discr_tag is not DISCR_TAG_BASE:
            raise ValueError("cannot interpolate *from* a "
                             "(non-interpolatory) quadrature grid")

        assert to_discr_tag is DISCR_TAG_BASE

        if from_dd.is_volume():
            if to_dd.domain_tag is FACE_RESTR_ALL:
                return self._all_faces_volume_connection()
            if to_dd.domain_tag is FACE_RESTR_INTERIOR:
                return self._interior_faces_connection()
            elif to_dd.is_boundary_or_partition_interface():
                assert from_discr_tag is DISCR_TAG_BASE
                return self._boundary_connection(to_dd.domain_tag.tag)
            elif to_dd.is_volume():
                from meshmode.discretization.connection import \
                        make_same_mesh_connection
                to_discr = self._discr_tag_volume_discr(to_discr_tag)
                from_discr = self._volume_discr
                return make_same_mesh_connection(self._setup_actx, to_discr,
                                                 from_discr)

            else:
                raise ValueError("cannot interpolate from volume to: " +
                                 str(to_dd))

        else:
            raise ValueError("cannot interpolate from: " + str(from_dd))
Ejemplo n.º 14
0
 def map_nodal_sum(self, expr, enclosing_prec):
     return DOFDesc(DTAG_SCALAR)
Ejemplo n.º 15
0
 def map_constant(self, expr):
     return DOFDesc(DTAG_SCALAR)
Ejemplo n.º 16
0
def diffusion_operator(discr,
                       quad_tag,
                       alpha,
                       boundaries,
                       u,
                       return_grad_u=False):
    r"""
    Compute the diffusion operator.

    The diffusion operator is defined as
    $\nabla\cdot(\alpha\nabla u)$, where $\alpha$ is the diffusivity and
    $u$ is a scalar field.

    Uses unstabilized central numerical fluxes.

    Parameters
    ----------
    discr: grudge.eager.EagerDGDiscretization
        the discretization to use
    quad_tag:
        quadrature tag indicating which discretization in *discr* to use for
        overintegration
    alpha: numbers.Number or meshmode.dof_array.DOFArray
        the diffusivity value(s)
    boundaries:
        dictionary (or list of dictionaries) mapping boundary tags to
        :class:`DiffusionBoundary` instances
    u: meshmode.dof_array.DOFArray or numpy.ndarray
        the DOF array (or object array of DOF arrays) to which the operator should be
        applied
    return_grad_u: bool
        an optional flag indicating whether $\nabla u$ should also be returned

    Returns
    -------
    diff_u: meshmode.dof_array.DOFArray or numpy.ndarray
        the diffusion operator applied to *u*
    grad_u: numpy.ndarray
        the gradient of *u*; only returned if *return_grad_u* is True
    """
    if isinstance(u, np.ndarray):
        if not isinstance(boundaries, list):
            raise TypeError(
                "boundaries must be a list if u is an object array")
        if len(boundaries) != len(u):
            raise TypeError("boundaries must be the same length as u")
        return obj_array_vectorize_n_args(
            lambda boundaries_i, u_i: diffusion_operator(discr,
                                                         quad_tag,
                                                         alpha,
                                                         boundaries_i,
                                                         u_i,
                                                         return_grad_u=
                                                         return_grad_u),
            make_obj_array(boundaries), u)

    for btag, bdry in boundaries.items():
        if not isinstance(bdry, DiffusionBoundary):
            raise TypeError(f"Unrecognized boundary type for tag {btag}. "
                            "Must be an instance of DiffusionBoundary.")

    dd_quad = DOFDesc("vol", quad_tag)
    dd_allfaces_quad = DOFDesc("all_faces", quad_tag)

    grad_u = discr.inverse_mass(
        discr.weak_grad(-u) -  # noqa: W504
        discr.face_mass(
            dd_allfaces_quad,
            gradient_flux(discr, quad_tag, interior_trace_pair(discr, u)) +
            sum(
                bdry.get_gradient_flux(discr, quad_tag, as_dofdesc(btag),
                                       alpha, u)
                for btag, bdry in boundaries.items()) + sum(
                    gradient_flux(discr, quad_tag, u_tpair)
                    for u_tpair in cross_rank_trace_pairs(discr, u))))

    alpha_quad = discr.project("vol", dd_quad, alpha)
    grad_u_quad = discr.project("vol", dd_quad, grad_u)

    diff_u = discr.inverse_mass(
        discr.weak_div(dd_quad, -alpha_quad * grad_u_quad) -  # noqa: W504
        discr.face_mass(
            dd_allfaces_quad,
            diffusion_flux(discr, quad_tag, interior_trace_pair(discr, alpha),
                           interior_trace_pair(discr, grad_u)) +
            sum(
                bdry.get_diffusion_flux(discr, quad_tag, as_dofdesc(btag),
                                        alpha, grad_u)
                for btag, bdry in boundaries.items()) + sum(
                    diffusion_flux(discr, quad_tag, alpha_tpair, grad_u_tpair)
                    for alpha_tpair, grad_u_tpair in zip(
                        cross_rank_trace_pairs(discr, alpha),
                        cross_rank_trace_pairs(discr, grad_u)))))

    if return_grad_u:
        return diff_u, grad_u
    else:
        return diff_u
Ejemplo n.º 17
0
def dt_geometric_factors(dcoll: DiscretizationCollection, dd=None) -> DOFArray:
    r"""Computes a geometric scaling factor for each cell following [Hesthaven_2008]_,
    section 6.4, defined as the inradius (radius of an inscribed circle/sphere).

    Specifically, the inradius for each element is computed using the following
    formula from [Shewchuk_2002]_, Table 1, for simplicial cells
    (triangles/tetrahedra):

    .. math::

        r_D = \frac{d V}{\sum_{i=1}^{N_{faces}} F_i},

    where :math:`d` is the topological dimension, :math:`V` is the cell volume,
    and :math:`F_i` are the areas of each face of the cell.

    :arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one.
        Defaults to the base volume discretization if not provided.
    :returns: a frozen :class:`~meshmode.dof_array.DOFArray` containing the
        geometric factors for each cell and at each nodal location.
    """
    from meshmode.discretization.poly_element import SimplexElementGroupBase

    if dd is None:
        dd = DD_VOLUME

    actx = dcoll._setup_actx
    volm_discr = dcoll.discr_from_dd(dd)

    if any(not isinstance(grp, SimplexElementGroupBase)
           for grp in volm_discr.groups):
        raise NotImplementedError(
            "Geometric factors are only implemented for simplex element groups"
        )

    if volm_discr.dim != volm_discr.ambient_dim:
        from warnings import warn
        warn(
            "The geometric factor for the characteristic length scale in "
            "time step estimation is not necessarily valid for non-volume-"
            "filling discretizations. Continuing anyway.",
            stacklevel=3)

    cell_vols = abs(
        op.elementwise_integral(dcoll, dd,
                                volm_discr.zeros(actx) + 1.0))

    if dcoll.dim == 1:
        return freeze(cell_vols)

    dd_face = DOFDesc("all_faces", dd.discretization_tag)
    face_discr = dcoll.discr_from_dd(dd_face)

    # To get a single value for the total surface area of a cell, we
    # take the sum over the averaged face areas on each face.
    # NOTE: The face areas are the *same* at each face nodal location.
    # This assumes there are the *same* number of face nodes on each face.
    surface_areas = abs(
        op.elementwise_integral(dcoll, dd_face,
                                face_discr.zeros(actx) + 1.0))
    surface_areas = DOFArray(
        actx,
        data=tuple(
            actx.einsum("fej->e",
                        face_ae_i.reshape(vgrp.mesh_el_group.nfaces,
                                          vgrp.nelements, afgrp.nunit_dofs),
                        tagged=(FirstAxisIsElementsTag(), )) / afgrp.nunit_dofs
            for vgrp, afgrp, face_ae_i in zip(
                volm_discr.groups, face_discr.groups, surface_areas)))

    return freeze(
        DOFArray(actx,
                 data=tuple(
                     actx.einsum("e,ei->ei",
                                 1 / sae_i,
                                 cv_i,
                                 tagged=(FirstAxisIsElementsTag(), )) *
                     dcoll.dim
                     for cv_i, sae_i in zip(cell_vols, surface_areas))))
Ejemplo n.º 18
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)
Ejemplo n.º 19
0
def test_gradient(actx_factory,
                  form,
                  dim,
                  order,
                  vectorize,
                  nested,
                  visualize=False):
    actx = actx_factory()

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    for n in [4, 6, 8]:
        mesh = mgen.generate_regular_rect_mesh(a=(-1, ) * dim,
                                               b=(1, ) * dim,
                                               nelements_per_axis=(n, ) * dim)

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

        def f(x):
            result = dcoll.zeros(actx) + 1
            for i in range(dim - 1):
                result = result * actx.np.sin(np.pi * x[i])
            result = result * actx.np.cos(np.pi / 2 * x[dim - 1])
            return result

        def grad_f(x):
            result = make_obj_array(
                [dcoll.zeros(actx) + 1 for _ in range(dim)])
            for i in range(dim - 1):
                for j in range(i):
                    result[i] = result[i] * actx.np.sin(np.pi * x[j])
                result[i] = result[i] * np.pi * actx.np.cos(np.pi * x[i])
                for j in range(i + 1, dim - 1):
                    result[i] = result[i] * actx.np.sin(np.pi * x[j])
                result[i] = result[i] * actx.np.cos(np.pi / 2 * x[dim - 1])
            for j in range(dim - 1):
                result[dim - 1] = result[dim - 1] * actx.np.sin(np.pi * x[j])
            result[dim -
                   1] = result[dim - 1] * (-np.pi / 2 *
                                           actx.np.sin(np.pi / 2 * x[dim - 1]))
            return result

        x = thaw(dcoll.nodes(), actx)

        if vectorize:
            u = make_obj_array([(i + 1) * f(x) for i in range(dim)])
        else:
            u = f(x)

        def get_flux(u_tpair):
            dd = u_tpair.dd
            dd_allfaces = dd.with_dtag("all_faces")
            normal = thaw(dcoll.normal(dd), actx)
            u_avg = u_tpair.avg
            if vectorize:
                if nested:
                    flux = make_obj_array(
                        [u_avg_i * normal for u_avg_i in u_avg])
                else:
                    flux = np.outer(u_avg, normal)
            else:
                flux = u_avg * normal
            return op.project(dcoll, dd, dd_allfaces, flux)

        dd_allfaces = DOFDesc("all_faces")

        if form == "strong":
            grad_u = (
                op.local_grad(dcoll, u, nested=nested)
                # No flux terms because u doesn't have inter-el jumps
            )
        elif form == "weak":
            grad_u = op.inverse_mass(
                dcoll,
                -op.weak_local_grad(dcoll, u, nested=nested)  # pylint: disable=E1130
                +  # noqa: W504
                op.face_mass(
                    dcoll,
                    dd_allfaces,
                    # Note: no boundary flux terms here because u_ext == u_int == 0
                    sum(
                        get_flux(utpair)
                        for utpair in op.interior_trace_pairs(dcoll, u))))
        else:
            raise ValueError("Invalid form argument.")

        if vectorize:
            expected_grad_u = make_obj_array([(i + 1) * grad_f(x)
                                              for i in range(dim)])
            if not nested:
                expected_grad_u = np.stack(expected_grad_u, axis=0)
        else:
            expected_grad_u = grad_f(x)

        if visualize:
            from grudge.shortcuts import make_visualizer
            vis = make_visualizer(dcoll,
                                  vis_order=order if dim == 3 else dim + 3)

            filename = (
                f"test_gradient_{form}_{dim}_{order}"
                f"{'_vec' if vectorize else ''}{'_nested' if nested else ''}.vtu"
            )
            vis.write_vtk_file(filename, [
                ("u", u),
                ("grad_u", grad_u),
                ("expected_grad_u", expected_grad_u),
            ],
                               overwrite=True)

        rel_linf_err = actx.to_numpy(
            op.norm(dcoll, grad_u - expected_grad_u, np.inf) /
            op.norm(dcoll, expected_grad_u, np.inf))
        eoc_rec.add_data_point(1. / n, rel_linf_err)

    print("L^inf error:")
    print(eoc_rec)
    assert (eoc_rec.order_estimate() >= order - 0.5
            or eoc_rec.max_error() < 1e-11)