.. _fluxspec:

Flux Specification Language
===========================

.. module:: hedge.flux

Summary
-------

An "flux expression" is an expression tree that represents
a numerical DG flux. Flux expressions may be passed to
:func:`hedge.optemplate.get_flux_operator` to integrate
with the :ref:`optemplate`.  The following components can be
used to build flux expressions in :mod:`hedge`:

* Scalar constants, may be of type
  :class:`int`, :class:`float`, :class:`complex`,
  :class:`numpy.number`. These occur directly as part of the
  expression tree. :func:`pymbolic.primitives.is_constant`
  is a predicate to test for constant-ness.

* :class:`numpy.ndarray` with *dtype=:class:`object`* (called
  "object array" in :mod:`numpy`-speak). When processed by
  :func:`hedge.optemplate.get_flux_operator`, vector fluxes
  result in an object array of scalar fluxes.

  Use :func:`hedge.tools.join_fields` to easily create
  object arrays from scalars and other arrays.

* :class:`Normal` represents one axial (i.e. *x*, *y* or *z*)
  component of the face normal for which the flux is to be
  computed.

  Use :func:`make_normal` to generate an object array of
  normal components for more natural, vectorial use of the
  normal.

* :class:`FieldComponent` allows access to the DG-discretized
  fields for which the flux is to be computed. If the
  flux operator is bound to a scalar field, it refers to it
  by the index 0. If it is bound to a vector field, indices
  are subscripts into this vector field.

  Further, :class:`FieldComponent` lets you specify which
  side of an element interface the placeholder stands by
  its `is_interior` parameter.

  .. seealso:: :ref:`flux-simp-placeholder`.

* :class:`PenaltyTerm`: Evaluates to the conventional penalty
  term :math:`(N^2/h)^k`, where the power :math:`k` is adjustable
  by the user, :math:`N` represents the order of the discretization,
  and :math:`h` represents the surface Jacobian.

* :class:`pymbolic.primitives.Sum`,
  :class:`pymbolic.primitives.Product`,
  :class:`pymbolic.primitives.Quotient`,
  :class:`pymbolic.primitives.Power`: These are created implicitly
  when :class:`~pymbolic.primitives.Expression` objects are combined
  using the `+`, `-`, `*`, `/` and `**` operators.
  These are all interpreted in a node-by-node fashion.

* :class:`pymbolic.primitives.IfPositive` offers a simple way
  to build conditionals and is interpreted in a node-by-node
  fashion.

* :class:`pymbolic.primitives.CommonSubexpression` (CSE for short):
  Prevents double evaluation of identical subexpressions
  when the flux expression tree is walked to evaluate
  the operator.

  Use :func:`hedge.optemplate.primitives.make_common_subexpression`
  to wrap each component of an object array in a CSE.

* :class:`hedge.flux.FluxScalarParameter`: A placeholder for a
  user-supplied scalar value, drawn from the same namespace
  as :class:`hedge.optemplate.ScalarParameter`.

* :class:`pymbolic.primitives.Call`: The *function* attribute
  must evaluate to one of a number of predefined
  :class:`pymbolic.primitive.FunctionSymbol` instances
  or be a value of type :class:`hedge.optemplate.primitives.CFunction`.

  .. seealso:: :ref:`flux-function-symbols`.

.. _flux-simp-placeholder:

Simplifying Flux Notation with Placeholders
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Suppose the state vector :math:`[u,v_x,v_y]` is contained
in the variable *q* and the flux to be calculated is

.. math::

    \{v\} \cdot \hat n - \frac 12 (u^- - u^+),

using the convetional flux notation (see Hesthaven/Warburton,
"Nodal Discontinuous Galerkin Methods").

Then this can be expressed as::

    flux = 0.5* (
      (FieldComponent(1, True) + FieldComponent(1, False)) * Normal(0)
      + (FieldComponent(2, True)+ FieldComponent(2, False)) * Normal(1)
      - (FieldComponent(0, True) - FieldComponent(0, False))
    )

and the flux is then bound to the components of *q* using::

    get_flux_operator(flux)*q

This is however rather cumbersome.
We may use :class:`FluxVectorPlaceholder` to simplify the notation.

We begin by introducing flux placeholders, specifying
that our state vector contains three components, as above, and
extract slices to get symbolic, vectorial representations of u, v,
and the normal::

    q_ph = FluxVectorPlaceholder(3)
    u = q_ph[0]
    v = q_ph[1:]
    n = make_normal(2) # normal has two dimensions

Then the flux simplifies to::

    flux = numpy.dot(v.avg, n) - 0.5*(u.int - u.ext)

The resulting flux expression will be the same as above, but
notational effort is greatly reduced.

Detailed Documentation
----------------------

.. autoclass:: FieldComponent

.. autoclass:: FluxScalarParameter

.. autoclass:: Normal

.. autofunction:: make_normal

.. autoclass:: PenaltyTerm

.. autoclass:: FluxZeroPlaceholder
    :members: int, ext, avg
    :undoc-members:

.. autoclass:: FluxScalarPlaceholder
    :members: int, ext, avg
    :undoc-members:

.. autoclass:: FluxVectorPlaceholder
    :members: __len__, __getitem__, int, ext, avg
    :undoc-members:

.. _flux-function-symbols:

Predefined Flux Function Symbols
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. data:: flux_abs

    One argument, returns its absolute value.

.. data:: flux_min

    Two arguments, returns the smaller of the two.

.. data:: flux_max

    Two arguments, returns the larger of the two.

Flux Helpers
------------

.. module:: hedge.flux.tools

.. autofunction:: make_lax_friedrichs_flux

