<h1>Results</h1>


When running the program, the sparsity pattern is written to an svg file, the solution is written to a vtk file, and some results are printed to the console. With the current setup, the output should read

@code

Number of active cells: 64
Number of degrees of freedom: 576
Assembling the system.............
Done.
DG H2 norm of the error: 0.0151063
DG H1 norm of the error: 0.000399747
   L2 norm of the error: 5.33856e-05

@endcode

This corresponds to the bi-Laplacian problem with the manufactured solution mentioned above for $d=2$, 3 refinements of the mesh, degree $k=2$, and $\gamma_0=\gamma_1=1$ for the penalty coefficients. By changing the number of refinements, we get the following results:

<table align="center" class="doxtable">
  <tr>
    <th>n_ref</th>
    <th>n_cells</th>
    <th>n_dofs</th>
    <th>error H2 </th>
    <th>rate</th>
    <th>error H1</th>
    <th>rate</th>
    <th>error L2</th>
    <th>rate</th>
  </tr>
  <tr>
    <td align="center">1</td>
    <td align="right">4</td>
    <td align="right">36</td>
    <td align="center">5.651e-02</td>
    <td align="center">--</td>
    <td align="center">3.366e-03</td>
    <td align="center">--</td>
    <td align="center">3.473e-04</td>
    <td align="center">--</td>
  </tr>
  <tr>
    <td align="center">2</td>
    <td align="right">16</td>
    <td align="right">144</td>
    <td align="center">3.095e-02</td>
    <td align="center">0.87</td>
    <td align="center">1.284e-03</td>
    <td align="center">1.39</td>
    <td align="center">1.369e-04</td>
    <td align="center">1.34</td>
  </tr>
  <tr>
    <td align="center">3</td>
    <td align="right">64</td>
    <td align="right">576</td>
    <td align="center">1.511e-02</td>
    <td align="center">1.03</td>
    <td align="center">3.997e-04</td>
    <td align="center">1.68</td>
    <td align="center">5.339e-05</td>
    <td align="center">1.36</td>
  </tr>
  <tr>
    <td align="center">4</td>
    <td align="right">256</td>
    <td align="right">2304</td>
    <td align="center">7.353e-03</td>
    <td align="center">1.04</td>
    <td align="center">1.129e-04</td>
    <td align="center">1.82</td>
    <td align="center">1.691e-05</td>
    <td align="center">1.66</td>
  </tr>
  <tr>
    <td align="center">5</td>
    <td align="right">1024</td>
    <td align="right">9216</td>
    <td align="center">3.609e-03</td>
    <td align="center">1.03</td>
    <td align="center">3.024e-05</td>
    <td align="center">1.90</td>
    <td align="center">4.789e-06</td>
    <td align="center">1.82</td>
  </tr>
  <tr>
    <td align="center">6</td>
    <td align="right">4096</td>
    <td align="right">36864</td>
    <td align="center">1.785e-03</td>
    <td align="center">1.02</td>
    <td align="center">7.850e-06</td>
    <td align="center">1.95</td>
    <td align="center">1.277e-06</td>
    <td align="center">1.91</td>
  </tr>
</table>

This matches the expected optimal convergence rates for the $H^2$ and
$H^1$ norms, but is sub-optimal for the $L_2$ norm. Incidentally, this
also matches the results seen in step-47 when using polynomial degree
$k=2$.

Indeed, just like in step-47, we can regain the optimal convergence
order if we set the polynomial degree of the finite elements to $k=3$
or higher. Here are the numbers for $k=3$:

<table align="center" class="doxtable">
  <tr> <th> n_ref </th>	<th> n_cells </th>	<th> n_dofs </th>	<th> error H2 </th>	<th> rate </th>	<th> error H1 </th>	<th> rate </th>	<th> error L2 </th>	<th> rate</th> </tr>
  <tr> <td> 1 </td>	<td> 4 </td>	<td> 36 </td>	<td> 1.451e-02 </td>	<td> -- </td>	<td> 5.494e-04 </td>	<td> -- </td>	<td> 3.035e-05 </td>	<td> --</td> </tr>
  <tr> <td> 2 </td>	<td> 16 </td>	<td> 144 </td>	<td> 3.565e-03 </td>	<td> 2.02 </td>	<td> 6.870e-05 </td>	<td> 3.00 </td>	<td> 2.091e-06 </td>	<td> 3.86</td> </tr>
  <tr> <td> 3 </td>	<td> 64 </td>	<td> 576 </td>	<td> 8.891e-04 </td>	<td> 2.00 </td>	<td> 8.584e-06 </td>	<td> 3.00 </td>	<td> 1.352e-07 </td>	<td> 3.95</td> </tr>
  <tr> <td> 4 </td>	<td> 256 </td>	<td> 2304 </td>	<td> 2.223e-04 </td>	<td> 2.00 </td>	<td> 1.073e-06 </td>	<td> 3.00 </td>	<td> 8.594e-09 </td>	<td> 3.98</td> </tr>
  <tr> <td> 5 </td>	<td> 1024 </td>	<td> 9216 </td>	<td> 5.560e-05 </td>	<td> 2.00 </td>	<td> 1.341e-07 </td>	<td> 3.00 </td>	<td> 5.418e-10 </td>	<td> 3.99</td> </tr>
  <tr> <td> 6 </td>	<td> 4096 </td>	<td> 36864 </td>	<td> 1.390e-05 </td>	<td> 2.00 </td>	<td> 1.676e-08 </td>	<td> 3.00 </td>	<td> 3.245e-11 </td>	<td> 4.06</td> </tr>
</table>


<h3>Possible extensions</h3>

The code can be easily adapted to deal with the following cases:

<ol>
  <li>Non-homogeneous Dirichlet boundary conditions on (part of) the boundary $\partial \Omega$ of $\Omega$.</li>
  <li>Hanging-nodes (proceed as in step-14 to not visit a sub-face twice when computing the lifting terms in <code>compute_discrete_hessians</code> and the penalty terms in <code>assemble_matrix</code>).</li>
  <li>LDG method for the Poisson problem (use the discrete gradient consisting of the broken gradient and the lifting of the jump of $u_h$).</li>
</ol>

We give below additional details for the first of these points.


<h4>Non-homogeneous Dirichlet boundary conditions</h4>
If we prescribe non-homogeneous Dirichlet conditions, say
@f[
\nabla u=\mathbf{g} \quad \mbox{and} \quad u=g \qquad \mbox{on } \partial \Omega,
@f]
then the right-hand side $\boldsymbol{F}$ of the linear system needs to be modified as follows
@f[
F_i:=\int_{\Omega}f\varphi_i-\sum_{e\in\mathcal{E}_h^b}\int_{\Omega}r_e(\mathbf{g}):H_h(\varphi_i)+\sum_{e\in\mathcal{E}_h^b}\int_{\Omega}b_e(g):H_h(\varphi_i)+\gamma_1\sum_{e\in\mathcal{E}_h^b}h_e^{-1}\int_e\mathbf{g}\cdot\nabla\varphi_i+\gamma_0\sum_{e\in\mathcal{E}_h^b}h_e^{-3}\int_e g\varphi_i, \qquad 1\leq i \leq N_h.
@f]
Note that for any given index $i$, many of the terms are zero. Indeed, for $e\in \mathcal{E}_h^b$ we have ${\rm supp}\,(r_e(\mathbf{g}))={\rm supp}\,(b_e(g))=K$, where $K$ is the element for which $e\subset\partial K$. Therefore, the liftings $r_e(\mathbf{g})$ and $b_e(g)$ contribute to $F_i$ only if $\varphi_i$ has support on $K$ or a neighbor of $K$. In other words, when integrating on a cell $K$, we need to add
@f[
\int_{K}f\varphi_i+\sum_{e\in\mathcal{E}_h^b, e\subset\partial K}\left[-\int_{K}r_e(\mathbf{g}):H_h(\varphi_i)+\int_{K}b_e(g):H_h(\varphi_i)+\gamma_1h_e^{-1}\int_e\mathbf{g}\cdot\nabla\varphi_i+\gamma_0h_e^{-3}\int_e g\varphi_i\right]
@f]
to $F_i$ for the indices $i$ such that $\varphi_i$ has support on $K$ and
@f[
\sum_{e\in\mathcal{E}_h^b, e\subset\partial K}\left[-\int_{K}r_e(\mathbf{g}):H_h(\varphi_i)+\int_{K}b_e(g):H_h(\varphi_i)\right]
@f]
to $F_i$ for the indices $i$ such that $\varphi_i$ has support on a neighbor of $K$.

@note
Note that we can easily consider the case where Dirichlet boundary conditions are imposed only on a subset $\emptyset\neq\Gamma_D\subset\partial \Omega$. In this case, we simply need to replace $\mathcal{E}_h^b$ by $\mathcal{E}_h^D\subset\mathcal{E}_h^b$ consisting of the faces belonging to $\Gamma_D$. This also affects the matrix $A$ (simply replace $\mathcal{E}_h=\mathcal{E}_h^0\cup\mathcal{E}_h^b$ by $\mathcal{E}_h=\mathcal{E}_h^0\cup\mathcal{E}_h^D$).
