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

@dealiiVideoLecture{10}

<h3>The basic set up of finite element methods</h3>

This is the first example where we actually use finite elements to compute
something. We
will solve a simple version of Poisson's equation with zero boundary
values, but a nonzero right hand side:
@f{align*}{
  -\Delta u &= f \qquad\qquad & \text{in}\ \Omega,
  \\
  u &= 0 \qquad\qquad & \text{on}\ \partial\Omega.
@f}
We will solve this equation on the square, $\Omega=[-1,1]^2$, for which
you've already learned how to generate a mesh in step-1 and step-2. In
this program, we will also only consider the particular case
$f(\mathbf x)=1$ and come back to how to implement the more general
case in the next tutorial program, step-4.

If you've learned about the basics of the finite element method, you will
remember the steps we need to take to approximate the solution $u$ by a finite
dimensional approximation. Specifically, we first need to derive the weak form
of the equation above, which we obtain by multiplying the equation by a test
function $\varphi$ <i>from the left</i> (we will come back to the reason for
multiplying from the left and not from the right below) and integrating over
the domain $\Omega$:
@f{align*}{
  -\int_\Omega \varphi \Delta u = \int_\Omega \varphi f.
@f}
This can be integrated by parts:
@f{align*}{
  \int_\Omega \nabla\varphi \cdot \nabla u
  -
  \int_{\partial\Omega} \varphi \mathbf{n}\cdot \nabla u
   = \int_\Omega \varphi f.
@f}
The test function $\varphi$ has to satisfy the same kind of boundary
conditions (in mathematical terms: it needs to come from the tangent space of
the set in which we seek the solution), so on the boundary $\varphi=0$ and
consequently the weak form we are looking for reads
@f{align*}{
  (\nabla\varphi, \nabla u)
   = (\varphi, f),
@f}
where we have used the common notation $(a,b)=\int_\Omega a\; b$. The problem
then asks for a function $u$ for which this statement is true for all test
functions $\varphi$ from the appropriate space (which here is the space
$H^1$).

Of course we can't find such a function on a computer in the general case, and
instead we seek an approximation $u_h(\mathbf x)=\sum_j U_j \varphi_j(\mathbf
x)$, where the $U_j$ are unknown expansion coefficients we need to determine
(the "degrees of freedom" of this problem), and $\varphi_i(\mathbf x)$ are the
finite element shape functions we will use. To define these shape functions,
we need the following:

- A mesh on which to define shape functions. You have already seen how to
  generate and manipulate the objects that describe meshes in step-1 and
  step-2.
- A finite element that describes the shape functions we want to use on the
  reference cell (which in deal.II is always the unit interval $[0,1]$, the
  unit square $[0,1]^2$ or the unit cube $[0,1]^3$, depending on which space
  dimension you work in). In step-2, we had already used an object of type
  FE_Q<2>, which denotes the usual Lagrange elements that define shape
  functions by interpolation on support points. The simplest one is
  FE_Q<2>(1), which uses polynomial degree 1. In 2d, these are often referred
  to as <i>bilinear</i>, since they are linear in each of the two coordinates
  of the reference cell. (In 1d, they would be <i>linear</i> and in 3d
  <i>tri-linear</i>; however, in the deal.II documentation, we will frequently
  not make this distinction and simply always call these functions "linear".)
- A DoFHandler object that enumerates all the degrees of freedom on the mesh,
  taking the reference cell description the finite element object provides as
  the basis. You've also already seen how to do this in step-2.
- A mapping that tells how the shape functions on the real cell are obtained
  from the shape functions defined by the finite element class on the
  reference cell. By default, unless you explicitly say otherwise, deal.II
  will use a (bi-, tri-)linear mapping for this, so in most cases you don't
  have to worry about this step.

Through these steps, we now have a set of functions $\varphi_i$, and we can
define the weak form of the discrete problem: Find a function $u_h$, i.e., find
the expansion coefficients $U_j$ mentioned above, so that
@f{align*}{
  (\nabla\varphi_i, \nabla u_h)
   = (\varphi_i, f),
   \qquad\qquad
   i=0\ldots N-1.
@f}
Note that we here follow the convention that everything is counted starting at
zero, as common in C and C++. This equation can be rewritten as a linear
system if you insert the representation $u_h(\mathbf x)=\sum_j U_j
\varphi_j(\mathbf x)$ and then observe that
@f{align*}{
  (\nabla\varphi_i, \nabla u_h)
  &= \left(\nabla\varphi_i, \nabla \Bigl[\sum_j U_j \varphi_j\Bigr]\right)
\\
  &= \sum_j \left(\nabla\varphi_i, \nabla \left[U_j \varphi_j\right]\right)
\\
  &= \sum_j \left(\nabla\varphi_i, \nabla \varphi_j \right) U_j.
@f}
With this, the problem reads: Find a vector $U$ so that
@f{align*}{
  A U = F,
@f}
where the matrix $A$ and the right hand side $F$ are defined as
@f{align*}{
  A_{ij} &= (\nabla\varphi_i, \nabla \varphi_j),
  \\
  F_i &= (\varphi_i, f).
@f}


<h3> Should we multiply by a test function from the left or from the right? </h3>

Before we move on with describing how these quantities can be computed, note
that if we had multiplied the original equation from the <i>right</i> by a
test function rather than from the left, then we would have obtained a linear
system of the form
@f{align*}{
  U^T A = F^T
@f}
with a row vector $F^T$. By transposing this system, this is of course
equivalent to solving
@f{align*}{
  A^T U = F
@f}
which here is the same as above since $A=A^T$. But in general is not,
and in order to avoid
any sort of confusion, experience has shown that simply getting into the habit
of multiplying the equation from the left rather than from the right (as is
often done in the mathematical literature) avoids a common class of errors as
the matrix is automatically correct and does not need to be transposed when
comparing theory and implementation. See step-9 for the first example in this
tutorial where we have a non-symmetric bilinear form for which it makes a
difference whether we multiply from the right or from the left.


<h3> *Assembling* the matrix and right hand side vector </h3>

Now we know what we need (namely: objects that hold the matrix and
vectors, as well as ways to compute $A_{ij},F_i$), and we can look at what it
takes to make that happen:

- The object for $A$ is of type SparseMatrix while those for $U$ and $F$ are of
  type Vector. We will see in the program below what classes are used to solve
  linear systems.
- We need a way to form the integrals. In the finite element method, this is
  most commonly done using quadrature, i.e. the integrals are replaced by a
  weighted sum over a set of *quadrature points* on each cell. That is, we first split the
  integral over $\Omega$ into integrals over all cells,
  @f{align*}{
    A_{ij} &= (\nabla\varphi_i, \nabla \varphi_j)
    = \sum_{K \in {\mathbb T}} \int_K \nabla\varphi_i \cdot \nabla \varphi_j,
    \\
    F_i &= (\varphi_i, f)
    = \sum_{K \in {\mathbb T}} \int_K \varphi_i f,
  @f}
  and then approximate each cell's contribution by quadrature:
  @f{align*}{
    A^K_{ij} &=
    \int_K \nabla\varphi_i \cdot \nabla \varphi_j
    \approx
    \sum_q \nabla\varphi_i(\mathbf x^K_q) \cdot \nabla
    \varphi_j(\mathbf x^K_q) w_q^K,
    \\
    F^K_i &=
    \int_K \varphi_i f
    \approx
    \sum_q \varphi_i(\mathbf x^K_q) f(\mathbf x^K_q) w^K_q,
  @f}
  where $\mathbb{T} \approx \Omega$ is a Triangulation approximating the domain,
  $\mathbf x^K_q$ is the $q$th quadrature point on cell $K$, and $w^K_q$
  the $q$th quadrature weight. There are different parts to what is needed in
  doing this, and we will discuss them in turn next.
- First, we need a way to describe the location $\mathbf x_q^K$ of quadrature
  points and their weights $w^K_q$. They are usually mapped from the reference
  cell in the same way as shape functions, i.e., implicitly using the
  MappingQ1 class or, if you explicitly say so, through one of the other
  classes derived from Mapping. The locations and weights on the reference
  cell are described by objects derived from the Quadrature base
  class. Typically, one chooses a quadrature formula (i.e. a set of points and
  weights) so that the quadrature exactly equals the integral in the matrix;
  this can be achieved because all factors in the integral are polynomial, and
  is done by Gaussian quadrature formulas, implemented in the QGauss class.
- We then need something that can help us evaluate $\varphi_i(\mathbf x^K_q)$
  on cell $K$. This is what the FEValues class does: it takes a finite element
  objects to describe $\varphi$ on the reference cell, a quadrature object to
  describe the quadrature points and weights, and a mapping object (or
  implicitly takes the MappingQ1 class) and provides values and derivatives of
  the shape functions on the real cell $K$ as well as all sorts of other
  information needed for integration, at the quadrature points located on $K$.

The process of computing the matrix and right hand side as a sum over all
cells (and then a sum over quadrature points) is usually called *assembling
the linear system*, or *assembly* for short, using the meaning of the word
related to [assembly line](https://en.wikipedia.org/wiki/Assembly_line),
meaning ["the act of putting together a set of pieces, fragments, or
elements"](https://en.wiktionary.org/wiki/assembly).

FEValues really is the central class in the assembly process. One way you can
view it is as follows: The FiniteElement and derived classes describe shape
<i>functions</i>, i.e., infinite dimensional objects: functions have values at
every point. We need this for theoretical reasons because we want to perform
our analysis with integrals over functions. However, for a computer, this is a
very difficult concept, since they can in general only deal with a finite
amount of information, and so we replace integrals by sums over quadrature
points that we obtain by mapping (the Mapping object) using  points defined on
a reference cell (the Quadrature object) onto points on the real cell. In
essence, we reduce the problem to one where we only need a finite amount of
information, namely shape function values and derivatives, quadrature weights,
normal vectors, etc, exclusively at a finite set of points. The FEValues class
is the one that brings the three components together and provides this finite
set of information on a particular cell $K$. You will see it in action when we
assemble the linear system below.

It is noteworthy that all of this could also be achieved if you simply created
these three objects yourself in an application program, and juggled the
information yourself. However, this would neither be simpler (the FEValues
class provides exactly the kind of information you actually need) nor faster:
the FEValues class is highly optimized to only compute on each cell the
particular information you need; if anything can be re-used from the previous
cell, then it will do so, and there is a lot of code in that class to make
sure things are cached wherever this is advantageous.

The final piece of this introduction is to mention that after a linear
system is obtained, it is solved using an iterative solver and then
postprocessed: we create an output file using the DataOut class that can then
be visualized using one of the common visualization programs.

@note The preceding overview of all the important steps of any finite element
implementation has its counterpart in deal.II: The library can naturally be
grouped into a number of "topics" that cover the basic concepts just
outlined. You can access these topics through the "Topics" tab at the top of this
page. An overview of the most fundamental groups of concepts is also available
on the <a href="index.html">front page of the deal.II manual</a>.


<h3> Solving the linear system </h3>

For a finite element program, the linear system we end up with here is
relatively small: The matrix has size $1089 \times 1089$, owing to the
fact that the mesh we use is $32\times 32$ and so there are $33^2=1089$ vertices
in the mesh. In many of the
later tutorial programs, matrix sizes in the range of tens of thousands
to hundreds of thousands will not be uncommon, and with codes such
as [ASPECT](https://aspect.geodynamics.org) that build on deal.II, we
regularly solve problems with more than a hundred million equations (albeit
using parallel computers). In any case, even for the small system here, the
matrix is much larger than what one typically encounters in an undergraduate
or most graduate courses, and so the question arises how we can solve such
linear systems.

The first method one typically learns for solving linear systems is
[Gaussian elimination](https://en.wikipedia.org/wiki/Gaussian_elimination).
The problem with this method is that it requires a number of operations that
is proportional to $N^3$, where $N$ is the number of equations or unknowns
in the linear system -- more specifically, the number of operations is
$\frac 23 N^3$, give or take a few. With $N=1089$, this means that we would
have to do around $861$ million operations. This is a number that is quite
feasible and it would take modern processors less than 0.1 seconds
to do this. But it is clear that this isn't going to scale: If we have twenty times
as many equations in the linear system (that is, twenty times as many unknowns),
then it would already take 1000-10,000 seconds or on the order of an hour. Make
the linear system another ten times larger, and it is clear that we can not solve
it any more on a single computer.

One can rescue the situation somewhat by realizing that only a relatively
small number of entries in the matrix are nonzero -- that is, the matrix
is [sparse](https://en.wikipedia.org/wiki/Sparse_matrix). Variations of
Gaussian elimination can exploit this, making the process substantially
faster; we will use one such method -- implemented in the SparseDirectUMFPACK
class -- in step-29 for the first time, among several others than come after
that. These variations of Gaussian elimination might get us to problem
sizes on the order of 100,000 or 200,000, but not all that much beyond that.

Instead, what we will do here is take up an idea from 1952: the
[Conjugate Gradient method](https://en.wikipedia.org/wiki/Conjugate_gradient_method),
or in short "CG". CG is an "iterative" solver in that it forms a sequence
of vectors that *converge* to the exact solution; in fact, after $N$ such iterations
in the absence of roundoff errors it finds the exact solution if the matrix is symmetric and positive definite.
The method was originally developed as another way
to solve a linear system exactly, like Gaussian elimination, but as such
it had few advantages and was largely forgotten for a few decades. But,
when computers became powerful enough to solve problems of a size where
Gaussian elimination doesn't work well any more (sometime in the 1980s),
CG was rediscovered as people realized that it is well suited for
large and sparse systems like the ones we get from the finite element method.
This is because (i) the vectors it computes *converge* to the exact solution,
and consequently we do not actually have to do all $N$ iterations to
find the exact solution as long as we're happy with reasonably good approximations;
and (ii) it only ever requires matrix-vector products, which is very useful
for sparse matrices because a sparse matrix has, by definition, only
${\cal O}(N)$ entries and so a matrix-vector product can be done with
${\cal O}(N)$ effort whereas it costs $N^2$ operations to do the same for
dense matrices. As a consequence, we can hope to solve linear systems
with at most ${\cal O}(N^2)$ operations, and in many cases substantially
fewer.

Finite element codes therefore almost always use iterative solvers such as
CG for the solution of the linear systems, and we will
do so in this code as well. (We note that the CG method is only usable
for matrices that are symmetric and positive definite; for other equations,
the matrix may not have these properties and we will have to use other
variations of iterative solvers such as
[BiCGStab](https://en.wikipedia.org/wiki/Biconjugate_gradient_stabilized_method)
or [GMRES](https://en.wikipedia.org/wiki/Generalized_minimal_residual_method)
that are applicable to more general matrices.)

An important component of these iterative solvers is that we specify
the *tolerance* with which we want to solve the linear system -- in essence,
a statement about the error we are willing to accept in our approximate
solution. The error in an approximate solution $\tilde x$ obtained to the
exact solution $x$ of a linear system $Ax=b$ is defined as $\|x-\tilde x\|$,
but this is a quantity we cannot compute because we don't know the
exact solution $x$. Instead, we typically consider the *residual*, defined
as $\|b-A\tilde x\|=\|A(x-\tilde x)\|$, as a computable measure. We then let
the iterative solver compute more and more accurate solutions $\tilde x$,
until $\|b-A\tilde x\|\le \tau$. A practical question is what value $\tau$
should have. In most applications, setting
@f{align*}{
  \tau = 10^{-6} \|b\|
@f}
is a reasonable choice. The fact that we make $\tau$ proportional to the size
(norm) of $b$ makes sure that our expectations of the accuracy in the solution
are relative to the size of the solution. This makes sense: If we make the
right hand side $b$ ten times larger, then the solution $x$ of $Ax=b$ will
also be ten times larger, and so will $\tilde x$; we want the same number
of accurate digits in $\tilde x$ as before, which means that we should also
terminate when the residual $\|b-A\tilde x\|$ is ten times the original
size -- which is exactly what we get if we make $\tau$ proportional to $\|b\|$.

All of this will be implemented in the `Step3::solve()` function in this
program. As you will see, it is quite simple to set up linear solvers with
deal.II: The whole function will have only three lines.


<h3>About the implementation</h3>

Although this is the simplest possible equation you can solve using the finite
element method, this program shows the basic structure of most finite
element programs and also serves as the template that almost all of the
following programs will essentially follow. Specifically, the main class of
this program looks like this:
@code
class Step3
{
  public:
    Step3 ();
    void run ();

  private:
    void make_grid ();
    void setup_system ();
    void assemble_system ();
    void solve ();
    void output_results () const;

    Triangulation<2>     triangulation;
    FE_Q<2>              fe;
    DoFHandler<2>        dof_handler;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;
    Vector<double>       solution;
    Vector<double>       system_rhs;
};
@endcode

This follows the object oriented programming mantra of <a
href="http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)">data
encapsulation</a>, i.e. we do our best to hide almost all internal details of
this class in private members that are not accessible to the outside.

Let's start with the member variables: These follow the building blocks we
have outlined above in the bullet points, namely we need a Triangulation and a
DoFHandler object, and a finite element object that describes the kinds of
shape functions we want to use. The second group of objects relate to the
linear algebra: the system matrix and right hand side as well as the solution
vector, and an object that describes the sparsity pattern of the matrix. This
is all this class needs (and the essentials that any solver for a stationary
PDE requires) and that needs to survive throughout the entire program. In
contrast to this, the FEValues object we need for assembly is only required
throughout assembly, and so we create it as a local object in the function
that does that and destroy it again at its end.

Secondly, let's look at the member functions. These, as well, already form the
common structure that almost all following tutorial programs will use:
<ul>
  <li> <code>make_grid()</code>: This is what one could call a
       <i>preprocessing function</i>. As its name suggests, it sets up the
       object that stores the triangulation. In later examples, it could also
       deal with boundary conditions, geometries, etc.
  <li> <code>setup_system()</code>: This then is the function in which all the
       other data structures are set up that are needed to solve the
       problem. In particular, it will initialize the DoFHandler object and
       correctly size the various objects that have to do with the linear
       algebra. This function is often separated from the preprocessing
       function above because, in a time dependent program, it may be called
       at least every few time steps whenever the mesh
       is adaptively refined (something we will see how to do in step-6). On
       the other hand, setting up the mesh itself in the preprocessing
       function above is done only once at the beginning of the program and
       is, therefore, separated into its own function.
  <li> <code>assemble_system()</code>: This, then is where the contents of the
       matrix and right hand side are computed, as discussed at length in the
       introduction above. Since doing something with this linear system is
       conceptually very different from computing its entries, we separate it
       from the following function.
  <li> <code>solve()</code>: This then is the function in which we compute the
       solution $U$ of the linear system $AU=F$. In the current program, this
       is a simple task since the matrix is so simple, but it will become a
       significant part of a program's size whenever the problem is not so
       trivial any more (see, for example, step-20, step-22, or step-31 once
       you've learned a bit more about the library).
  <li> <code>output_results()</code>: Finally, when you have computed a
       solution, you probably want to do something with it. For example, you
       may want to output it in a format that can be visualized, or you may
       want to compute quantities you are interested in: say, heat fluxes in a
       heat exchanger, air friction coefficients of a wing, maximum bridge
       loads, or simply the value of the numerical solution at a point. This
       function is therefore the place for postprocessing your solution.
</ul>
All of this is held together by the single public function (other than the
constructor), namely the <code>run()</code> function. It is the one that is
called from the place where an object of this type is created, and it is the
one that calls all the other functions in their proper order. Encapsulating
this operation into the <code>run()</code> function, rather than calling all
the other functions from <code>main()</code> makes sure that you
can change how the separation of concerns within this class is
implemented. For example, if one of the functions becomes too big, you can
split it up into two, and the only places you have to be concerned about
changing as a consequence are within this very same class, and not anywhere
else.

As mentioned above, you will see this general structure &mdash; sometimes with
variants in spelling of the functions' names, but in essentially this order of
separation of functionality &mdash; again in many of the
following tutorial programs.


<h3> A note on types </h3>

deal.II defines a number of integral %types via alias in namespace dealii::types.
(In the previous sentence, the word "integral" is used as the <i>adjective</i>
that corresponds to the noun "integer". It shouldn't be confused with the
<i>noun</i> "integral" that represents the area or volume under a curve
or surface. The adjective "integral" is widely used in the C++ world in
contexts such as "integral type", "integral constant", etc.)
In particular, in this program you will see types::global_dof_index in a couple of
places: an integer type that is used to denote the <i>global</i> index of a
degree of freedom, i.e., the index of a particular degree of freedom within the
DoFHandler object that is defined on top of a triangulation (as opposed to the
index of a particular degree of freedom within a particular cell). For the
current program (as well as almost all of the tutorial programs), you will have
a few thousand to maybe a few million unknowns globally (and, for $Q_1$
elements, you will have 4 <i>locally on each cell</i> in 2d and 8 in 3d).
Consequently, a data type that allows to store sufficiently large numbers for
global DoF indices is <code>unsigned int</code> given that it allows to store
numbers between 0 and slightly more than 4 billion (on most systems, where
integers are 32-bit). In fact, this is what types::global_dof_index is.

So, why not just use <code>unsigned int</code> right away? deal.II used to do
this until version 7.3. However, deal.II supports very large computations (via
the framework discussed in step-40) that may have more than 4 billion unknowns
when spread across a few thousand processors. Consequently, there are
situations where <code>unsigned int</code> is not sufficiently large and we
need a 64-bit unsigned integral type. To make this possible, we introduced
types::global_dof_index which by default is defined as simply <code>unsigned
int</code> whereas it is possible to define it as <code>unsigned long long
int</code> if necessary, by passing a particular flag during configuration
(see the ReadMe file).

This covers the technical aspect. But there is also a documentation purpose:
everywhere in the library and codes that are built on it, if you see a place
using the data type types::global_dof_index, you immediately know that the
quantity that is being referenced is, in fact, a global dof index. No such
meaning would be apparent if we had just used <code>unsigned int</code> (which
may also be a local index, a boundary indicator, a material id,
etc.). Immediately knowing what a variable refers to also helps avoid errors:
it's quite clear that there must be a bug if you see an object of type
types::global_dof_index being assigned to variable of type
types::subdomain_id, even though they are both represented by unsigned
integers and the compiler will, consequently, not complain.

In more practical terms what the presence of this type means is that during
assembly, we create a $4\times 4$ matrix (in 2d, using a $Q_1$ element) of the
contributions of the cell we are currently sitting on, and then we need to add
the elements of this matrix to the appropriate elements of the global (system)
matrix. For this, we need to get at the global indices of the degrees of
freedom that are local to the current cell, for which we will always use the
following piece of the code:
@code
  cell->get_dof_indices (local_dof_indices);
@endcode
where <code>local_dof_indices</code> is declared as
@code
  std::vector<types::global_dof_index> local_dof_indices (fe.n_dofs_per_cell());
@endcode
The name of this variable might be a bit of a misnomer -- it stands for "the
global indices of those degrees of freedom locally defined on the current
cell" -- but variables that hold this information are universally named this
way throughout the library.

@note types::global_dof_index is not the only type defined in this namespace.
Rather, there is a whole family, including types::subdomain_id,
types::boundary_id, and types::material_id. All of these are alias for integer
data types but, as explained above, they are used throughout the library so that
(i) the intent of a variable becomes more easily discerned, and (ii) so that it
becomes possible to change the actual type to a larger one if necessary without
having to go through the entire library and figure out whether a particular use
of <code>unsigned int</code> corresponds to, say, a material indicator.
