<br>

<i>
This program was written for fun by David Neckels (NCAR) while working
at Sandia (on the Wyoming Express bus to and from Corrales each day).
The main purpose was to better understand Euler flow.
The code solves the basic Euler equations of gas dynamics, by using a
fully implicit Newton iteration (inspired by Sandia's Aria code).  The
code may be configured by an input file to run different simulations
on different meshes, with differing boundary conditions.
<br>
The original code and documentation was later slightly modified by Wolfgang
Bangerth to make it more modular and allow replacing the parts that are
specific to the Euler equations by other hyperbolic conservation laws without
too much trouble.
</i>

@note The program uses the <a
href="http://trilinos.org">Trilinos</a> linear solvers (these can be found
in Trilinos in the Aztec/Amesos packages) and an automatic
differentiation package, Sacado, also part of Trilinos. deal.II must
be configured to use Trilinos. Refer to the <a
href="../../readme.html#trilinos">ReadMe</a> file for instructions how to
do this.

@note While this program demonstrates the use of automatic differentiation
well, it does not express the state of the art in Euler equation solvers.
There are much faster and more accurate method for this equation, and
you should take a look at step-67 and step-69 to see how this equation
can be solved more efficiently.



<a name="step-33-Intro"></a> <h1>Introduction</h1>

<h3>Euler flow</h3>

The equations that describe the movement of a compressible, inviscid
gas (the so-called Euler equations of gas dynamics) are
a basic system of conservation laws. In spatial dimension $d$ they read
@f[
\partial_t \mathbf{w} + \nabla \cdot \mathbf{F}(\mathbf{w}) =
\mathbf{G}(\mathbf w),
@f]
with the solution $\mathbf{w}=(\rho v_1,\ldots,\rho v_d,\rho,
E)^{\top}$ consisting of $\rho$ the fluid density, ${\mathbf v}=(v_1,\ldots v_d)^T$ the
flow velocity (and thus $\rho\mathbf v$ being the linear momentum
density), and
$E$ the energy density of the gas. We interpret the equations above as
$\partial_t \mathbf{w}_i + \nabla \cdot \mathbf{F}_i(\mathbf{w}) = \mathbf
G_i(\mathbf w)$, $i=1,\ldots,dim+2$.

For the Euler equations, the flux matrix $\mathbf F$ (or system of flux functions)
is defined as (shown here for the case $d=3$)
@f{eqnarray*}{
  \mathbf F(\mathbf w)
  =
  \left(
  \begin{array}{ccc}
    \rho v_1^2+p & \rho v_2v_1  & \rho v_3v_1 \\
    \rho v_1v_2  & \rho v_2^2+p & \rho v_3v_2 \\
    \rho v_1v_3  & \rho v_2v_3  & \rho v_3^2+p \\
    \rho v_1 & \rho v_2 & \rho v_3 \\
    (E+p) v_1 & (E+p) v_2 & (E+p) v_3
  \end{array}
  \right),
@f}
and we will choose as particular right hand side forcing only the effects of
gravity, described by
@f{eqnarray*}{
  \mathbf G(\mathbf w)
  =
  \left(
  \begin{array}{c}
    g_1\rho \\
    g_2\rho \\
    g_3\rho \\
    0 \\
    \rho \mathbf g \cdot \mathbf v
  \end{array}
  \right),
@f}
where $\mathbf g=(g_1,g_2,g_3)^T$ denotes the gravity vector.
With this, the entire system of equations reads:
@f{eqnarray*}{
  \partial_t (\rho v_i) + \sum_{s=1}^d \frac{\partial(\rho v_i v_s +
  \delta_{is} p)}{\partial x_s} &=& g_i \rho, \qquad i=1,\dots,d, \\
  \partial_t \rho + \sum_{s=1}^d \frac{\partial(\rho v_s)}{\partial x_s} &=& 0,  \\
  \partial_t E + \sum_{s=1}^d \frac{\partial((E+p)v_s)}{\partial x_s} &=&
  \rho \mathbf g \cdot \mathbf v.
@f}
These equations describe, respectively, the conservation of momentum,
mass, and energy.
The system is closed by a relation that defines the pressure: $p =
(\gamma -1)(E-\frac{1}{2} \rho |\mathbf v|^2)$. For the constituents
of air (mainly nitrogen and oxygen) and other diatomic gases, the ratio of
specific heats is $\gamma=1.4$.

This problem obviously falls into the class of vector-valued
problems. A general overview of how to deal with these problems in
deal.II can be found in the @ref vector_valued topic.

<h3>Discretization</h3>

Discretization happens in the usual way, taking into account that this
is a hyperbolic problem in the same style as the simple one discussed
in step-12:
We choose a finite element space $V_h$, and integrate our conservation law against
our (vector-valued) test function $\mathbf{z} \in V_h$.  We then integrate by parts and approximate the
boundary flux with a <i> numerical </i> flux $\mathbf{H}$,
@f{eqnarray*}{
&&\int_{\Omega} (\partial_t \mathbf{w}, \mathbf{z}) + (\nabla \cdot \mathbf{F}(\mathbf{w}), \mathbf{z}) \\
&\approx &\int_{\Omega} (\partial_t \mathbf{w}, \mathbf{z}) - (\mathbf{F}(\mathbf{w}), \nabla \mathbf{z}) + h^{\eta}(\nabla \mathbf{w} , \nabla \mathbf{z}) + \int_{\partial \Omega} (\mathbf{H}(\mathbf{w}^+, \mathbf{w}^-, \mathbf{n}), \mathbf{z}^+),
@f}
where a superscript $+$ denotes the interior trace of a function, and $-$ represents the outer trace.
The diffusion term $h^{\eta}(\nabla \mathbf{w} , \nabla \mathbf{z})$ is introduced strictly for stability,
 where $h$ is the mesh size and $\eta$ is a parameter prescribing how
 much diffusion to add.

On the boundary, we have to say what the outer trace $\mathbf{w}^-$ is.
Depending on the boundary condition, we prescribe either of the following:
<ul>
<li> Inflow boundary: $\mathbf{w}^-$ is prescribed to be the desired value.
<li> Supersonic outflow boundary: $\mathbf{w}^- = \mathbf{w}^+$
<li> Subsonic outflow boundary: $\mathbf{w}^- = \mathbf{w}^+$ except that the energy variable
is modified to support a prescribed pressure $p_o$, i.e.
$\mathbf{w}^- =(\rho^+, \rho v_1^+, \dots, \rho v_d^+, p_o/(\gamma -1) + 0.5 \rho |\mathbf{v}^+|^2)$
<li> Reflective boundary: we set $\mathbf{w}^-$ so that $(\mathbf{v}^+ + \mathbf{v}^-) \cdot \mathbf{n} = 0$ and
$\rho^- = \rho^+,E^-=E^+$.
</ul>

More information on these issues can be found, for example, in Ralf
Hartmann's PhD thesis ("Adaptive Finite Element Methods for the
Compressible Euler Equations", PhD thesis, University of Heidelberg, 2002).

We use a time stepping scheme to substitute the time derivative in the
above equations. For simplicity, we define $ \mathbf{B}({\mathbf{w}_{n}})(\mathbf z) $ as the spatial residual at time step $n$ :

@f{eqnarray*}{
 \mathbf{B}(\mathbf{w}_{n})(\mathbf z)  &=&
- \int_{\Omega} \left(\mathbf{F}(\mathbf{w}_n),
\nabla\mathbf{z}\right) +  h^{\eta}(\nabla \mathbf{w}_n , \nabla \mathbf{z}) \\
&& +
\int_{\partial \Omega} \left(\mathbf{H}(\mathbf{w}_n^+,
\mathbf{w}^-(\mathbf{w}_n^+), \mathbf{n}), \mathbf{z}\right)
-
\int_{\Omega} \left(\mathbf{G}(\mathbf{w}_n),
\mathbf{z}\right) .
@f}

At each time step, our full discretization is thus
that the residual applied to any test
function $\mathbf z$ equals zero:
@f{eqnarray*}{
R(\mathbf{W}_{n+1})(\mathbf z) &=&
\int_{\Omega} \left(\frac{{\mathbf w}_{n+1} - \mathbf{w}_n}{\delta t},
\mathbf{z}\right)+
\theta \mathbf{B}({\mathbf{w}}_{n+1}) +  (1-\theta) \mathbf{B}({\mathbf w}_{n}) \\
&=& 0
@f}
where $ \theta \in [0,1] $ and
$\mathbf{w}_i = \sum_k \mathbf{W}_i^k \mathbf{\phi}_k$. Choosing
$\theta=0$ results in the explicit (forward) Euler scheme, $\theta=1$
in the stable implicit (backward) Euler scheme, and $\theta=\frac 12$
in the Crank-Nicolson scheme.

In the implementation below, we choose the Lax-Friedrichs flux for the
function $\mathbf H$, i.e.  $\mathbf{H}(\mathbf{a},\mathbf{b},\mathbf{n}) =
\frac{1}{2}(\mathbf{F}(\mathbf{a})\cdot \mathbf{n} +
\mathbf{F}(\mathbf{b})\cdot \mathbf{n} + \alpha (\mathbf{a} - \mathbf{b}))$,
where $\alpha$ is either a fixed number specified in the input file, or where
$\alpha$ is a mesh dependent value. In the latter case, it is chosen as
$\frac{h}{2\delta T}$ with $h$ the diameter of the face to which the flux is
applied, and $\delta T$ the current time step.

With these choices, equating the residual to zero results in a
nonlinear system of equations $R(\mathbf{W}_{n+1})=0$. We solve this nonlinear system by a
Newton iteration (in the same way as explained in step-15), i.e. by iterating
@f{eqnarray*}{
R'(\mathbf{W}^k_{n+1},\delta \mathbf{W}_{n+1}^k)(\mathbf z) & = & -
R(\mathbf{W}^{k}_{n+1})(\mathbf z) \qquad \qquad \forall \mathbf z\in V_h \\
\mathbf{W}^{k+1}_{n+1} &=& \mathbf{W}^k_{n+1} + \delta \mathbf{W}^k_{n+1},
@f}
until $|R(\mathbf{W}^k_{n+1})|$ (the residual) is sufficiently small. By
testing with the nodal basis of a finite element space instead of all
$\mathbf z$, we arrive at a linear system for $\delta \mathbf W$:
@f{eqnarray*}{
\mathbf R'(\mathbf{W}^k_{n+1})\delta \mathbf{W}^k_{n+1} & = & -
\mathbf R(\mathbf{W}^{k}_{n+1}).
@f}
This linear system is, in general, neither symmetric nor has any
particular definiteness properties. We will either use a direct solver
or Trilinos' GMRES implementation to solve it. As will become apparent from
the <a href="#Results">results shown below</a>, this fully implicit iteration
converges very rapidly (typically in 3 steps) and with the quadratic
convergence order expected from a Newton method.


<h3> Automatic differentiation </h3>

Since computing the Jacobian matrix $\mathbf R'(\mathbf W^k)$ is a
terrible beast, we use an automatic differentiation package, Sacado,
to do this.  Sacado is a package within the <a
href="http://trilinos.org" target="_top">Trilinos</a> framework
and offers a C++ template class <code>Sacado::Fad::DFad</code>
(<code>Fad</code> standing for "forward automatic
differentiation") that supports basic arithmetic operators and
functions such as <code> sqrt, sin, cos, pow, </code> etc. In order to
use this feature, one declares a collection of variables of this type
and then denotes some of this collection as degrees of freedom, the rest of
the variables being functions of the independent variables.  These
variables are used in an algorithm, and as the variables are used,
their sensitivities with respect to the degrees of freedom are
continuously updated.

One can imagine that for the full Jacobian matrix as a whole,
this could be prohibitively expensive: the number of independent variables are
the $\mathbf W^k$, the dependent variables the elements of the vector $\mathbf
R(\mathbf W^k)$. Both of these vectors can easily have tens of thousands of
elements or more.  However, it is important to note that not all elements of
$\mathbf R$ depend on all elements of $\mathbf W^k$: in fact, an entry in
$\mathbf R$ only depends on an element of $\mathbf W^k$ if the two
corresponding shape functions overlap and couple in the weak form.

Specifically, it is wise to define a minimum set of
independent AD variables that the residual on the current cell may possibly
depend on: on every element, we define those variables as
independent that correspond to the degrees of freedom defined on this
cell (or, if we have to compute jump terms between cells, that
correspond to degrees of freedom defined on either of the two adjacent
cells), and the dependent variables are the elements of the local
residual vector. Not doing this, i.e. defining <i>all</i> elements of
$\mathbf W^k$ as independent, will result a very expensive computation
of a lot of zeros: the elements of the local residual vector are
independent of almost all elements of the solution vector, and
consequently their derivatives are zero; however, trying to compute
these zeros can easily take 90% or more of the compute time of the
entire program, as shown in an experiment inadvertently made by a student a few
years after this program was first written.


Coming back to the question of computing the Jacobian automatically:
The author has used this approach side by side with a hand coded Jacobian for
the incompressible Navier-Stokes problem and found the Sacado approach to be
just as fast as using a hand coded Jacobian, but infinitely simpler and less
error prone: Since using the auto-differentiation requires only that one code
the residual $R(\mathbf{W})$, ensuring code correctness and maintaining code
becomes tremendously more simple -- the Jacobian matrix $\mathbf R'$ is
computed by essentially the same code that also computes the residual $\mathbf
R$.

All this said, here's a very simple example showing how Sacado can be
used:

@code
#include <Sacado.hpp>
#include <iostream>

using fad_double = Sacado::Fad::DFad<double>;

main() {

  fad_double a,b,c;

  a = 1; b = 2;

  a.diff(0,2);  // Set a to be dof 0, in a 2-dof system.

  b.diff(1,2);  // Set b to be dof 1, in a 2-dof system.

  c = 2*a+cos(a*b);

  double *derivs = &c.fastAccessDx(0); // Access derivatives

  std::cout << "dc/da = " << derivs[0] << ", dc/db=" << derivs[1] << std::endl;

}
@endcode

The output are the derivatives $\frac{\partial c(a,b)}{\partial a},
\frac{\partial c(a,b)}{\partial b}$ of $c(a,b)=2a+\cos(ab)$ at $a=1,b=2$.

It should be noted that Sacado provides more auto-differentiation capabilities than the small subset
used in this program.  However, understanding the example above is
enough to understand the use of Sacado in this Euler flow program.

<h3> Trilinos solvers </h3>
The program uses either the Aztec iterative solvers, or the Amesos
sparse direct solver, both provided by
the Trilinos package.  This package is inherently designed to be used in a parallel program, however,
it may be used in serial just as easily, as is done here.  The Epetra package is the basic
vector/matrix library upon which the solvers are built.  This very powerful package can be used
to describe the parallel distribution of a vector, and to define sparse matrices that operate
on these vectors.  Please view the commented code for more details on how these solvers are used
within the example.

<h3> Adaptivity </h3>
The example uses an ad hoc refinement indicator that shows some usefulness in shock-type problems, and
in the downhill flow example included.  We refine according to the squared gradient of the density.
Hanging nodes are handled by computing the numerical flux across cells that are of differing
refinement levels, rather than using the AffineConstraints class as in
all other tutorial programs so far.  In this way, the example combines
the continuous and DG methodologies. It also simplifies the generation
of the Jacobian because we do not have to track constrained degrees of
freedom through the automatic differentiation used to compute it.

@note When this program was written in 2008, we were unaware of any
publication that would actually have used this approach. However, a
more recent paper by A. Dedner, R. Kl&ouml;fkorn, and M. Kr&auml;nkel
("Continuous Finite-Elements on Non-Conforming Grids Using
Discontinuous Galerkin Stabilization", Proceedings of Finite Volumes
for Complex Applications VII - Methods and Theoretical Aspects,
Springer, 2014) comes close.

@note In hindsight, dealing with hanging nodes in this way is perhaps
  not the best choice. deal.II contains many places that assume that
  an element that has degrees of freedom on vertices, edges, and faces
  (like the FE_Q element used here) represents finite element functions
  on a triangulation that have certain continuity properties. Calling
  DoFTools::make_hanging_node_constraints() and putting the corresponding
  output into an AffineConstraints object allows one to enforce
  these assumptions when one calls AffineConstraints::distribute()
  at the end of the function that solves linear systems -- this call
  can be found in all other tutorial programs that use meshes with
  hanging nodes, and ensures that the resulting function's value at
  hanging nodes is correct with regard to the values of the nodes on
  the adjacent parent cell. On the other hand, this program does not do
  this, and this runs afoul of an assertion that was later added to the
  library and that tested that the function is indeed continuous.
  To address the resulting failure due to the assertion not being
  satisfied, we *do* after all enforce continuity at hanging nodes
  just before we transfer the solution from one mesh to another,
  in the `ConservationLaw::refine_grid()` function.

Further, we enforce a maximum number of refinement levels to keep refinement under check.  It is the
author's experience that for adaptivity for a time dependent problem, refinement can easily lead the simulation to
a screeching halt, because of time step restrictions if the mesh
becomes too fine in any part of the domain, if care is not taken.  The amount of refinement is
limited in the example by letting the user specify the
maximum level of refinement that will be present anywhere in the mesh.  In this way, refinement
tends not to slow the simulation to a halt.  This, of course, is purely a heuristic strategy, and
if the author's advisor heard about it, the author would likely be exiled forever from the finite
 element error estimation community.

<h3>Input deck, initial and boundary conditions</h3>

We use an input file deck to drive the simulation.  In this way, we can alter the boundary conditions
and other important properties of the simulation without having to recompile.  For more information on
the format, look at the <a href="#Results">results section</a>, where we
describe an example input file in more detail.

In previous example programs, we have usually hard-coded the initial
and boundary conditions. In this program, we instead use the
expression parser class FunctionParser so that we can specify a
generic expression in the input file and have it parsed at run time &mdash;
this way, we can change initial conditions without the need to
recompile the program. Consequently, no classes named
InitialConditions or BoundaryConditions will be declared in the
program below.


<h3>Implementation</h3>

The implementation of this program is split into three essential parts:
<ul>
  <li>The <code>EulerEquations</code> class that encapsulates everything that
  completely describes the specifics of the Euler equations. This includes the
  flux matrix $\mathbf F(\mathbf W)$, the numerical flux $\mathbf F(\mathbf
  W^+,\mathbf W^-,\mathbf n)$, the right hand side $\mathbf G(\mathbf W)$,
  boundary conditions, refinement indicators, postprocessing the output, and
  similar things that require knowledge of the meaning of the individual
  components of the solution vectors and the equations.

  <li>A namespace that deals with everything that has to do with run-time
  parameters.

  <li>The <code>ConservationLaw</code> class that deals with time stepping,
  outer nonlinear and inner linear solves, assembling the linear systems, and
  the top-level logic that drives all this.
</ul>

The reason for this approach is that it separates the various concerns in a
program: the <code>ConservationLaw</code> is written in such a way that it
would be relatively straightforward to adapt it to a different set of
equations: One would simply re-implement the members of the
<code>EulerEquations</code> class for some other hyperbolic equation, or
augment the existing equations by additional ones (for example by advecting
additional variables, or by adding chemistry, etc). Such modifications,
however, would not affect the time stepping, or the nonlinear solvers if
correctly done, and consequently nothing in the <code>ConservationLaw</code>
would have to be modified.

Similarly, if we wanted to improve on the linear or nonlinear solvers, or on
the time stepping scheme (as hinted at the end of the <a
href="#Results">results section</a>), then this would not require changes in
the <code>EulerEquations</code> at all.
