<h1>Results</h1>


The program generates two kinds of output. The first are the output
files <code>solution-adaptive-q1.vtk</code>,
<code>solution-global-q1.vtk</code>, and
<code>solution-global-q2.vtk</code>. We show the latter in a 3d view
here:


<img src="https://www.dealii.org/images/steps/developer/step-7.solution.png" alt="">




Secondly, the program writes tables not only to disk, but also to the
screen while running. The output looks like the following (recall that
columns labeled as "H1" actually show the $H^1$ <i>semi-</i>norm
of the error, not the full $H^1$ norm):


@code
examples/\step-7> make run
Solving with Q1 elements, adaptive refinement
=============================================

Cycle 0:
   Number of active cells:       64
   Number of degrees of freedom: 81
Cycle 1:
   Number of active cells:       124
   Number of degrees of freedom: 157
Cycle 2:
   Number of active cells:       280
   Number of degrees of freedom: 341
Cycle 3:
   Number of active cells:       577
   Number of degrees of freedom: 690
Cycle 4:
   Number of active cells:       1099
   Number of degrees of freedom: 1264
Cycle 5:
   Number of active cells:       2191
   Number of degrees of freedom: 2452
Cycle 6:
   Number of active cells:       4165
   Number of degrees of freedom: 4510
Cycle 7:
   Number of active cells:       7915
   Number of degrees of freedom: 8440
Cycle 8:
   Number of active cells:       15196
   Number of degrees of freedom: 15912

cycle cells dofs     L2        H1      Linfty
    0    64    81 1.840e+00 2.858e+00 1.835e+00
    1   124   157 5.190e-02 1.200e+00 1.344e-01
    2   280   341 1.439e-02 7.892e-01 7.554e-02
    3   577   690 8.627e-03 5.061e-01 2.805e-02
    4  1099  1264 3.217e-03 3.030e-01 1.073e-02
    5  2191  2452 1.445e-03 2.097e-01 5.073e-03
    6  4165  4510 8.387e-04 1.460e-01 2.013e-03
    7  7915  8440 7.051e-04 1.053e-01 1.804e-03
    8 15196 15912 2.774e-04 7.463e-02 6.911e-04

Solving with Q1 elements, global refinement
===========================================

Cycle 0:
   Number of active cells:       64
   Number of degrees of freedom: 81
Cycle 1:
   Number of active cells:       256
   Number of degrees of freedom: 289
Cycle 2:
   Number of active cells:       1024
   Number of degrees of freedom: 1089
Cycle 3:
   Number of active cells:       4096
   Number of degrees of freedom: 4225
Cycle 4:
   Number of active cells:       16384
   Number of degrees of freedom: 16641

cycle cells dofs     L2        H1      Linfty
    0    64    81 1.840e+00 2.858e+00 1.835e+00
    1   256   289 3.570e-02 1.199e+00 1.307e-01
    2  1024  1089 1.192e-02 7.565e-01 7.168e-02
    3  4096  4225 3.047e-03 3.823e-01 2.128e-02
    4 16384 16641 7.660e-04 1.917e-01 5.554e-03

n cells         H1                   L2
0    64 2.858e+00    -    - 1.840e+00     -    -
1   256 1.199e+00 2.38 1.25 3.570e-02 51.54 5.69
2  1024 7.565e-01 1.58 0.66 1.192e-02  2.99 1.58
3  4096 3.823e-01 1.98 0.98 3.047e-03  3.91 1.97
4 16384 1.917e-01 1.99 1.00 7.660e-04  3.98 1.99

Solving with Q2 elements, global refinement
===========================================

Cycle 0:
   Number of active cells:       64
   Number of degrees of freedom: 289
Cycle 1:
   Number of active cells:       256
   Number of degrees of freedom: 1089
Cycle 2:
   Number of active cells:       1024
   Number of degrees of freedom: 4225
Cycle 3:
   Number of active cells:       4096
   Number of degrees of freedom: 16641
Cycle 4:
   Number of active cells:       16384
   Number of degrees of freedom: 66049

cycle cells dofs     L2        H1      Linfty
    0    64   289 1.606e-01 1.278e+00 3.029e-01
    1   256  1089 7.638e-03 5.248e-01 4.816e-02
    2  1024  4225 8.601e-04 1.086e-01 4.827e-03
    3  4096 16641 1.107e-04 2.756e-02 7.802e-04
    4 16384 66049 1.393e-05 6.915e-03 9.971e-05

n cells         H1                   L2
0    64 1.278e+00    -    - 1.606e-01     -    -
1   256 5.248e-01 2.43 1.28 7.638e-03 21.03 4.39
2  1024 1.086e-01 4.83 2.27 8.601e-04  8.88 3.15
3  4096 2.756e-02 3.94 1.98 1.107e-04  7.77 2.96
4 16384 6.915e-03 3.99 1.99 1.393e-05  7.94 2.99

Solving with Q2 elements, adaptive refinement
===========================================

Cycle 0:
   Number of active cells:       64
   Number of degrees of freedom: 289
Cycle 1:
   Number of active cells:       124
   Number of degrees of freedom: 577
Cycle 2:
   Number of active cells:       289
   Number of degrees of freedom: 1353
Cycle 3:
   Number of active cells:       547
   Number of degrees of freedom: 2531
Cycle 4:
   Number of active cells:       1057
   Number of degrees of freedom: 4919
Cycle 5:
   Number of active cells:       2059
   Number of degrees of freedom: 9223
Cycle 6:
   Number of active cells:       3913
   Number of degrees of freedom: 17887
Cycle 7:
   Number of active cells:       7441
   Number of degrees of freedom: 33807
Cycle 8:
   Number of active cells:       14212
   Number of degrees of freedom: 64731

cycle cells dofs     L2        H1      Linfty
    0    64   289 1.606e-01 1.278e+00 3.029e-01
    1   124   577 7.891e-03 5.256e-01 4.852e-02
    2   289  1353 1.070e-03 1.155e-01 4.868e-03
    3   547  2531 5.962e-04 5.101e-02 1.876e-03
    4  1057  4919 1.977e-04 3.094e-02 7.923e-04
    5  2059  9223 7.738e-05 1.974e-02 7.270e-04
    6  3913 17887 2.925e-05 8.772e-03 1.463e-04
    7  7441 33807 1.024e-05 4.121e-03 8.567e-05
    8 14212 64731 3.761e-06 2.108e-03 2.167e-05
@endcode


One can see the error reduction upon grid refinement, and for the
cases where global refinement was performed, also the convergence
rates can be seen. The linear and quadratic convergence rates of Q1
and Q2 elements in the $H^1$ semi-norm can clearly be seen, as
are the quadratic and cubic rates in the $L_2$ norm.




Finally, the program also generated LaTeX versions of the tables (not shown
here) that is written into a file in a way so that it could be
copy-pasted into a LaTeX document.


<h3> When is the error "small"? </h3>

What we showed above is how to determine the size of the error
$\|u-u_h\|$ in a number of different norms. We did this primarily
because we were interested in testing that our solutions *converge*.
But from an engineering perspective, the question is often more
practical: How fine do I have to make my mesh so that the error is
"small enough"? In other words, if in the table above the $H^1$
semi-norm has been reduced to `4.121e-03`, is this good enough for me
to sign the blueprint and declare that our numerical simulation showed
that the bridge is strong enough?

In practice, we are rarely in this situation because I can not
typically compare the numerical solution $u_h$ against the exact
solution $u$ in situations that matter -- if I knew $u$, I would not
have to compute $u_h$. But even if I could, the question to ask in
general is then: `4.121e-03` *what*? The solution will have physical
units, say kg-times-meter-squared, and I'm integrating a function with
units square of the above over the domain, and then take the square
root. So if the domain is two-dimensional, the units of
$\|u-u_h\|_{L_2}$ are kg-times-meter-cubed. The question is then: Is
$4.121\times 10^{-3}$ kg-times-meter-cubed small? That depends on what
you're trying to simulate: If you're an astronomer used to masses
measured in solar masses and distances in light years, then yes, this
is a fantastically small number. But if you're doing atomic physics,
then no: That's not small, and your error is most certainly not
sufficiently small; you need a finer mesh.

In other words, when we look at these sorts of numbers, we generally
need to compare against a "scale". One way to do that is to not look
at the *absolute* error $\|u-u_h\|$ in whatever norm, but at the
*relative* error $\|u-u_h\|/\|u\|$. If this ratio is $10^{-5}$, then
you know that *on average*, the difference between $u$ and $u_h$ is
0.001 per cent -- probably small enough for engineering purposes.

How do we compute $\|u\|$? We just need to do an integration loop over
all cells, quadrature points on these cells, and then sum things up
and take the square root at the end. But there is a simpler way often
used: You can call
@code
    Vector<double> zero_vector (dof_handler.n_dofs());
    Vector<float> norm_per_cell(triangulation.n_active_cells());
    VectorTools::integrate_difference(dof_handler,
                                      zero_vector,
                                      Solution<dim>(),
                                      norm_per_cell,
                                      QGauss<dim>(fe->degree + 1),
                                      VectorTools::L2_norm);
@endcode
which computes $\|u-0\|_{L_2}$. Alternatively, if you're particularly
lazy and don't feel like creating the `zero_vector`, you could use
that if the mesh is not too coarse, then $\|u\| \approx \|u_h\|$, and
we can compute $\|u\| \approx \|u_h\|=\|0-u_h\|$ by calling
@code
    Vector<float> norm_per_cell(triangulation.n_active_cells());
    VectorTools::integrate_difference(dof_handler,
                                      solution,
                                      Functions::ZeroFunction<dim>(),
                                      norm_per_cell,
                                      QGauss<dim>(fe->degree + 1),
                                      VectorTools::L2_norm);
@endcode
In both cases, one then only has to combine the vector of cellwise
norms into one global norm as we already do in the program, by calling
@code
    const double L2_norm =
      VectorTools::compute_global_error(triangulation,
                                        norm_per_cell,
                                        VectorTools::L2_norm);
@endcode



<h3> Possibilities for extensions </h3>

<h4> Higher Order Elements </h4>

Go ahead and run the program with higher order elements ($Q_3$, $Q_4$, ...). You
will notice that assertions in several parts of the code will trigger (for
example in the generation of the filename for the data output). You might have to address these,
but it should not be very hard to get the program to work!

<h4> Convergence Comparison </h4>

Is $Q_1$ or $Q_2$ better? What about adaptive versus global refinement? A (somewhat
unfair but typical) metric to compare them, is to look at the error as a
function of the number of unknowns.

To see this, create a plot in log-log style with the number of unknowns on the
$x$ axis and the $L_2$ error on the $y$ axis. You can add reference lines for
$h^2=N^{-1}$ and $h^3=N^{-3/2}$ and check that global and adaptive refinement
follow those. If one makes the (not completely unreasonable)
assumption that with a good linear solver, the computational effort is
proportional to the number of unknowns $N$, then it is clear that an
error reduction of ${\cal O}(N^{-3/2})$ is substantially better than a
reduction of the form ${\cal O}(N^{-1})$: That is, that adaptive
refinement gives us the desired error level with less computational
work than if we used global refinement. This is not a particularly
surprising conclusion, but it's worth checking these sorts of
assumptions in practice.

Of course, a fairer comparison would be to plot runtime (switch to
release mode first!) instead of number of unknowns on the $x$ axis. If
you plotted run time (check out the Timer class!) against the number
of unknowns by timing each refinement step, you will notice that the
linear system solver we use in this program is not perfect -- its run
time grows faster than proportional to the linear system size -- and
picking a better linear solver might be appropriate for this kind of
comparison.

To see how a comparison of this kind could work, take a look at
@cite KronbichlerWall2018 , and specifically Figure 5
that illustrates the error as a function of compute time for
a number of polynomial degrees (as well as a number of different ways
to discretize the equation used there).
