<h1>Results</h1>

When you run the program, the following output should be printed on screen:

@verbatim
Surface mesh has 1280 cells.
Surface mesh has 5185 degrees of freedom.
H1 error = 0.0217136
@endverbatim


By playing around with the number of global refinements in the
<code>LaplaceBeltrami::make_grid_and_dofs</code> function you increase or decrease mesh
refinement. For example, doing one more refinement and only running the 3d surface
problem yields the following
output:

@verbatim
Surface mesh has 5120 cells.
Surface mesh has 20609 degrees of freedom.
H1 error = 0.00543481
@endverbatim

This is what we expect: make the mesh size smaller by a factor of two and the
error goes down by a factor of four (remember that we use bi-quadratic
elements). The full sequence of errors from one to five refinements looks like
this, neatly following the theoretically predicted pattern:
@verbatim
0.339438
0.0864385
0.0217136
0.00543481
0.00135913
@endverbatim

Finally, the program produces graphical output that we can visualize. Here is
a plot of the results:

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

The program also works for 1d curves in 2d, not just 2d surfaces in 3d. You
can test this by changing the template argument in <code>main()</code> like
so:
@code
      LaplaceBeltramiProblem<2> laplace_beltrami;
@endcode
The domain is a curve in 2d, and we can visualize the solution by using the
third dimension (and color) to denote the value of the function $u(x)$. This
then looks like so (the white curve is the domain, the colored curve is the
solution extruded into the third dimension, clearly showing the change in sign
as the curve moves from one quadrant of the domain into the adjacent one):

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


<a name="extensions"></a>
<h3>Possibilities for extensions</h3>

Computing on surfaces only becomes interesting if the surface is more
interesting than just a half sphere. To achieve this, deal.II can read
meshes that describe surfaces through the usual GridIn class. Or, in case you
have an analytic description, a simple mesh can sometimes be stretched and
bent into a shape we are interested in.

Let us consider a relatively simple example: we take the half sphere we used
before, we stretch it by a factor of 10 in the z-direction, and then we jumble
the x- and y-coordinates a bit. Let's show the computational domain and the
solution first before we go into details of the implementation below:

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

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

The way to produce such a mesh is by using the GridTools::transform()
function. It needs a way to transform each individual mesh point to a
different position. Let us here use the following, rather simple function
(remember: stretch in one direction, jumble in the other two):

@code
template <int spacedim>
Point<spacedim> warp(const Point<spacedim> &p)
{
  Point<spacedim> q = p;
  q[spacedim-1] *= 10;

  if (spacedim >= 2)
    q[0] += 2*std::sin(q[spacedim-1]);
  if (spacedim >= 3)
    q[1] += 2*std::cos(q[spacedim-1]);

  return q;
}
@endcode

If we followed the <code>LaplaceBeltrami::make_grid_and_dofs</code> function, we would
extract the half spherical surface mesh as before, warp it into the shape we
want, and refine as often as necessary. This is not quite as simple as we'd
like here, though: refining requires that we have an appropriate manifold
object attached to the triangulation that describes where new vertices of the
mesh should be located upon refinement. I'm sure it's possible to describe
this manifold in a not-too-complicated way by simply undoing the
transformation above (yielding the spherical surface again), finding the
location of a new point on the sphere, and then re-warping the result. But I'm
a lazy person, and since doing this is not really the point here, let's just
make our lives a bit easier: we'll extract the half sphere, refine it as
often as necessary, get rid of the object that describes the manifold since we
now no longer need it, and then finally warp the mesh. With the function
above, this would look as follows:

@code
template <int spacedim>
void LaplaceBeltrami<spacedim>::make_grid_and_dofs()
{
  {
    Triangulation<spacedim> volume_mesh;
    GridGenerator::half_hyper_ball(volume_mesh);

    volume_mesh.refine_global(4);

    const std::set<types::boundary_id> boundary_ids = {0};

    GridGenerator::extract_boundary_mesh(volume_mesh, triangulation,
                                         boundary_ids);
    GridTools::transform(&warp<spacedim>, triangulation);       /* ** */
    std::ofstream x("x"), y("y");
    GridOut().write_gnuplot(volume_mesh, x);
    GridOut().write_gnuplot(triangulation, y);
  }

  std::cout << "Surface mesh has " << triangulation.n_active_cells()
            << " cells."
            << std::endl;
  ...
}
@endcode

Note that the only essential addition is the line marked with
asterisks. It is worth pointing out one other thing here, though: because we
detach the manifold description from the surface mesh, whenever we use a
mapping object in the rest of the program, it has no curves boundary
description to go on any more. Rather, it will have to use the implicit,
FlatManifold class that is used on all parts of the domain not
explicitly assigned a different manifold object. Consequently, whether we use
MappingQ(2), MappingQ(15) or MappingQ1, each cell of our mesh will be mapped
using a bilinear approximation.

All these drawbacks aside, the resulting pictures are still pretty. The only
other differences to what's in step-38 is that we changed the right hand side
to $f(\mathbf x)=\sin x_3$ and the boundary values (through the
<code>Solution</code> class) to $u(\mathbf x)|_{\partial\Omega}=\cos x_3$. Of
course, we now no longer know the exact solution, so the computation of the
error at the end of <code>LaplaceBeltrami::run</code> will yield a meaningless
number.
