<h1>Results</h1>


Running the program takes a good while if one doesn't change the flags
in the Makefile: in debug mode (the default) and on only a single
machine, it takes about 3h45min on my Athlon XP 2GHz. Fortunately, but
setting <code>debug-mode = off</code> in the Makefile, this can be
reduced significantly, to about 23 minutes, a much more reasonable time.




If run, the program prints the following output, explaining what it is
doing during all that time:
@code
examples/\step-18> time make run
============================ Running \step-18
Timestep 1 at time 1
  Cycle 0:
    Number of active cells:       3712 (by partition: 3712)
    Number of degrees of freedom: 17226 (by partition: 17226)
    Assembling system... norm of rhs is 2.34224e+10
    Solver converged in 117 iterations.
    Updating quadrature point data...
  Cycle 1:
    Number of active cells:       12812 (by partition: 12812)
    Number of degrees of freedom: 51726 (by partition: 51726)
    Assembling system... norm of rhs is 2.34227e+10
    Solver converged in 130 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 2 at time 2
    Assembling system... norm of rhs is 2.30852e+10
    Solver converged in 131 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 3 at time 3
    Assembling system... norm of rhs is 2.27792e+10
    Solver converged in 126 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 4 at time 4
    Assembling system... norm of rhs is 2.25107e+10
    Solver converged in 124 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 5 at time 5
    Assembling system... norm of rhs is 2.22883e+10
    Solver converged in 122 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 6 at time 6
    Assembling system... norm of rhs is 2.21272e+10
    Solver converged in 118 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 7 at time 7
    Assembling system... norm of rhs is 2.20652e+10
    Solver converged in 117 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 8 at time 8
    Assembling system... norm of rhs is 2.22501e+10
    Solver converged in 127 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 9 at time 9
    Assembling system... norm of rhs is 2.32742e+10
    Solver converged in 144 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 10 at time 10
    Assembling system... norm of rhs is 2.55929e+10
    Solver converged in 149 iterations.
    Updating quadrature point data...
    Moving mesh...
@endcode
In other words, it is computing on 12,000 cells and with some 52,000
unknowns. Not a whole lot, but enough for a coupled three-dimensional
problem to keep a computer busy for a while. At the end of the day,
this is what we have for output:
@code
examples/\step-18> ls -l *.d2
-rw-r--r--  1 bangerth wheeler 8797414 May 25 09:10 solution-0001.0000.d2
-rw-r--r--  1 bangerth wheeler 8788500 May 25 09:32 solution-0002.0000.d2
-rw-r--r--  1 bangerth wheeler 8763718 May 25 09:55 solution-0003.0000.d2
-rw-r--r--  1 bangerth wheeler 8738940 May 25 10:17 solution-0004.0000.d2
-rw-r--r--  1 bangerth wheeler 8710104 May 25 10:39 solution-0005.0000.d2
-rw-r--r--  1 bangerth wheeler 8685388 May 25 11:01 solution-0006.0000.d2
-rw-r--r--  1 bangerth wheeler 8649088 May 25 11:23 solution-0007.0000.d2
-rw-r--r--  1 bangerth wheeler 8585146 May 25 11:45 solution-0008.0000.d2
-rw-r--r--  1 bangerth wheeler 8489764 May 25 12:07 solution-0009.0000.d2
-rw-r--r--  1 bangerth wheeler 8405388 May 25 12:29 solution-0010.0000.d2
@endcode


Let us convert these files in deal.II intermediate format to gmv
format (this assumes that you have already compiled the
step-19 example program):
@code
examples/\step-18> ../\step-19/\step-19

Converter from deal.II intermediate format to other graphics formats.

Usage: ./\step-19 [-p parameter_file] list_of_input_files [-x output_format] -o output_file

examples/\step-18> ../\step-19/\step-19 solution-0001.0000.d2 -x gmv -o solution-0001.0000.gmv
examples/\step-18> ../\step-19/\step-19 solution-0002.0000.d2 -x gmv -o solution-0002.0000.gmv
[...]
@endcode
Of course, since we have run the program only in sequential mode, we
do have only one intermediate file for each time step that we have to
take as input.



If we visualize these files with GMV, we get to see the full picture
of the disaster our forced compression wreaks on the cylinder (colors in the
images encode the norm of the stress in the material):


<table width="100%">
  <tr width="100%">
    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0002.0000.png" alt="">
       Time = 2
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0005.0000.png" alt="">
       Time = 5
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0007.0000.png" alt="">
       Time = 7
    </td>
  </tr>

  <tr width="100%">
    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0008.0000.png" alt="">
      Time = 8
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0009.0000.png" alt="">
      Time = 9
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.sequential-0010.0000.png" alt="">
      Time = 10
    </td>
  </tr>
</table>


As is clearly visible, as we keep compressing the cylinder, it starts
to buckle and ultimately collapses. Towards the end of the simulation,
the deflection pattern becomes nonsymmetric (the cylinder top slides
to the right). The model clearly does not provide for this (all our
forces and boundary deflections are symmetric) but the effect is
probably physically correct anyway: in reality, small inhomogeneities
in the body's material properties would lead it to buckle to one side
to evade the forcing; in numerical simulations, small perturbations
such as numerical round-off or an inexact solution of a linear system
by an iterative solver could have the same effect. Another typical source for
asymmetries in adaptive computations is that only a certain fraction of cells
is refined in each step, which may lead to asymmetric meshes even if the
original coarse mesh was symmetric.




Whether the computation is fully converged is a different matter. In order to
see whether it is, we ran the program again with one more global refinement at
the beginning and with the time step halved. This would have taken a very long
time on a single machine, so we used our cluster again and ran it on 16
processors (8 dual-processor machines) in parallel. The beginning of the output
now looks like this:
@code
Timestep 1 at time 0.5
  Cycle 0:
    Number of active cells:       29696 (by partition: 1862+1890+1866+1850+1864+1850+1858+1842+1911+1851+1911+1804+1854+1816+1839+1828)
    Number of degrees of freedom: 113100 (by partition: 7089+7218+6978+6972+7110+6840+7119+7023+7542+7203+7068+6741+6921+6759+7464+7053)
    Assembling system... norm of rhs is 1.05874e+10
    Solver converged in 289 iterations.
    Updating quadrature point data...
  Cycle 1:
    Number of active cells:       102097 (by partition: 6346+6478+6442+6570+6370+6483+6413+6376+6403+6195+6195+6195+6494+6571+6371+6195)
    Number of degrees of freedom: 358875 (by partition: 22257+22161+22554+22482+21759+23361+23040+21609+22347+20937+21801+21678+24126+25149+21321+22293)
    Assembling system... norm of rhs is 3.46364e+10
    Solver converged in 249 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 2 at time 1
    Assembling system... norm of rhs is 3.42269e+10
    Solver converged in 248 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 3 at time 1.5
    Assembling system... norm of rhs is 3.38229e+10
    Solver converged in 247 iterations.
    Updating quadrature point data...
    Moving mesh...

Timestep 4 at time 2
    Assembling system... norm of rhs is 3.34247e+10
    Solver converged in 247 iterations.
    Updating quadrature point data...
    Moving mesh...

[...]

Timestep 20 at time 10
    Assembling system... norm of rhs is 3.2449e+10
    Solver converged in 493 iterations.
    Updating quadrature point data...
    Moving mesh...
@endcode
That's quite a good number of unknowns, given that we are in 3d. The output of
this program are 16 files for each time step:
@code
examples/\step-18> ls -l solution-0001.000*
-rw-r--r--    1 bangerth mfw       4325219 Aug 11 09:44 solution-0001.0000-000.d2
-rw-r--r--    1 bangerth mfw       4454460 Aug 11 09:44 solution-0001.0000-001.d2
-rw-r--r--    1 bangerth mfw       4485242 Aug 11 09:43 solution-0001.0000-002.d2
-rw-r--r--    1 bangerth mfw       4517364 Aug 11 09:43 solution-0001.0000-003.d2
-rw-r--r--    1 bangerth mfw       4462829 Aug 11 09:43 solution-0001.0000-004.d2
-rw-r--r--    1 bangerth mfw       4482487 Aug 11 09:43 solution-0001.0000-005.d2
-rw-r--r--    1 bangerth mfw       4548619 Aug 11 09:43 solution-0001.0000-006.d2
-rw-r--r--    1 bangerth mfw       4522421 Aug 11 09:43 solution-0001.0000-007.d2
-rw-r--r--    1 bangerth mfw       4337529 Aug 11 09:43 solution-0001.0000-008.d2
-rw-r--r--    1 bangerth mfw       4163047 Aug 11 09:43 solution-0001.0000-009.d2
-rw-r--r--    1 bangerth mfw       4288247 Aug 11 09:43 solution-0001.0000-010.d2
-rw-r--r--    1 bangerth mfw       4350410 Aug 11 09:43 solution-0001.0000-011.d2
-rw-r--r--    1 bangerth mfw       4458427 Aug 11 09:43 solution-0001.0000-012.d2
-rw-r--r--    1 bangerth mfw       4466037 Aug 11 09:43 solution-0001.0000-013.d2
-rw-r--r--    1 bangerth mfw       4505679 Aug 11 09:44 solution-0001.0000-014.d2
-rw-r--r--    1 bangerth mfw       4340488 Aug 11 09:44 solution-0001.0000-015.d2
@endcode
We merge and convert these 16 intermediate files into a single gmv file as
follows:
@code
examples/\step-18> time ../\step-19/\step-19 solution-0001.0000-* -x gmv -o solution-0001.0000.gmv

real    0m45.929s
user    0m41.290s
sys     0m0.990s
examples/\step-18> ls -l solution-0001.0000.gmv
-rw-r--r--    1 bangerth mfw      68925360 Aug 11 17:04 solution-0001.0000.gmv
@endcode

Doing so for all time steps, we obtain gmv files that we can visualize (albeit
with some difficulty, due to their size gmv isn't exactly fast when plotting
them). Here are first the mesh on which we compute as well as the partitioning
for the 16 processors:


<table width="100%">
  <tr width="100%">
    <td width="49%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-000mesh.png" alt="">
    </td>

    <td width="49%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0002.p.png" alt="">
    </td>
  </tr>
</table>


Finally, here is the same output as we have shown before for the much smaller
sequential case:


<table width="100%">
  <tr width="100%">
    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0002.s.png" alt="">
       Time = 2
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0005.s.png" alt="">
       Time = 5
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0007.s.png" alt="">
       Time = 7
    </td>
  </tr>

  <tr width="100%">
    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0008.s.png" alt="">
       Time = 8
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0009.s.png" alt="">
       Time = 9
    </td>

    <td width="33%">
      <img src="http://www.dealii.org/images/steps/developer/step-18.parallel-0010.s.png" alt="">
       Time = 10
    </td>
  </tr>
</table>


If one compares this with the previous run, the results are qualitatively
similar, but quantitatively definitely different. The previous computation was
therefore certainly not converged, though we can't say for sure anything about
the present one. One would need an even finer computation to find out. However,
the point may be moot: looking at the last picture in detail, it is pretty
obvious that not only is the linear small
deformation model we chose completely inadequate, but for a realistic
simulation we would also need to make sure that the body does not intersect
itself during deformation. Without such a formulation we cannot expect anything
to make physical sense, even if it produces nice pictures!


<h3>Possible directions for extensions</h3>

The program as is does not really solve an equation that has many applications
in practice: quasi-static material deformation based on a purely elastic law
is almost boring. However, the program may serve as the starting point for
more interesting experiments, and that indeed was the initial motivation for
writing it. Here are some suggestions of what the program is missing and in
what direction it may be extended:

<h5>Plasticity models</h5>

 The most obvious extension is to use a more
realistic material model for large-scale quasistatic deformation. The natural
choice for this would be plasticity, in which a nonlinear relationship between
stress and strain replaces equation <a href="#step_18.stress-strain">[stress-strain]</a>. Plasticity
models are usually rather complicated to program since the stress-strain
dependence is generally non-smooth. The material can be thought of being able
to withstand only a maximal stress (the yield stress) after which it starts to
&ldquo;flow&rdquo;. A mathematical description to this can be given in the form of a
variational inequality, which alternatively can be treated as minimizing the
elastic energy
@f[
  E(\mathbf{u}) =
  (\varepsilon(\mathbf{u}), C\varepsilon(\mathbf{u}))_{\Omega}
  - (\mathbf{f}, \mathbf{u})_{\Omega} - (\mathbf{b}, \mathbf{u})_{\Gamma_N},
@f]
subject to the constraint
@f[
  f(\sigma(\mathbf{u})) \le 0
@f]
on the stress. This extension makes the problem to be solved in each time step
nonlinear, so we need another loop within each time step.

Without going into further details of this model, we refer to the excellent
book by Simo and Hughes on &ldquo;Computational Inelasticity&rdquo; for a
comprehensive overview of computational strategies for solving plastic
models. Alternatively, a brief but concise description of an algorithm for
plasticity is given in an article by S. Commend, A. Truty, and Th. Zimmermann,
titled &ldquo;Stabilized finite elements applied to
elastoplasticity: I. Mixed displacement-pressure formulation&rdquo;
(Computer Methods in Applied Mechanics and Engineering, vol. 193,
pp. 3559-3586, 2004).


<h5>Stabilization issues</h5>

The formulation we have chosen, i.e. using
piecewise (bi-, tri-)linear elements for all components of the displacement
vector, and treating the stress as a variable dependent on the displacement is
appropriate for most materials. However, this so-called displacement-based
formulation becomes unstable and exhibits spurious modes for incompressible or
nearly-incompressible materials. While fluids are usually not elastic (in most
cases, the stress depends on velocity gradients, not displacement gradients,
although there are exceptions such as electro-rheologic fluids), there are a
few solids that are nearly incompressible, for example rubber. Another case is
that many plasticity models ultimately let the material become incompressible,
although this is outside the scope of the present program.

Incompressibility is characterized by Poisson's ratio
@f[
  \nu = \frac{\lambda}{2(\lambda+\mu)},
@f]
where $\lambda,\mu$ are the Lam\'e constants of the material.
Physical constraints indicate that $-1\le \nu\le \frac 12$ (the condition
also follows from mathematical stability considerations). If $\nu$
approaches $\frac 12$, then the material becomes incompressible. In that
case, pure displacement-based formulations are no longer appropriate for the
solution of such problems, and stabilization techniques have to be employed
for a stable and accurate solution. The book and paper cited above give
indications as to how to do this, but there is also a large volume of
literature on this subject; a good start to get an overview of the topic can
be found in the references of the paper by
H.-Y. Duan and Q. Lin on &ldquo;Mixed finite elements of least-squares type for
elasticity&rdquo; (Computer Methods in Applied Mechanics and Engineering, vol. 194,
pp. 1093-1112, 2005).


<h5>Refinement during timesteps</h5>

In the present form, the program
only refines the initial mesh a number of times, but then never again. For any
kind of realistic simulation, one would want to extend this so that the mesh
is refined and coarsened every few time steps instead. This is not hard to do,
in fact, but has been left for future tutorial programs or as an exercise, if
you wish.

The main complication one has to overcome is that one has to
transfer the data that is stored in the quadrature points of the cells of the
old mesh to the new mesh, preferably by some sort of projection scheme. The
general approach to this would go like this:

- At the beginning, the data is only available in the quadrature points of
  individual cells, not as a finite element field that is defined everywhere.

- So let us find a finite element field that <i>is</i> defined everywhere so
  that we can later interpolate it to the quadrature points of the new
  mesh. In general, it will be difficult to find a continuous finite element
  field that matches the values in the quadrature points exactly because the
  number of degrees of freedom of these fields does not match the number of
  quadrature points there are, and the nodal values of this global field will
  either be over- or underdetermined. But it is usually not very difficult to
  find a discontinuous field that matches the values in the quadrature points;
  for example, if you have a QGauss(2) quadrature formula (i.e. 4 points per
  cell in 2d, 8 points in 3d), then one would use a finite element of kind
  FE_DGQ(1), i.e. bi-/tri-linear functions as these have 4 degrees of freedom
  per cell in 2d and 8 in 3d.

- There are functions that can make this conversion from individual points to
  a global field simpler. The following piece of pseudo-code should help if
  you use a QGauss(2) quadrature formula. Note that the multiplication by the
  projection matrix below takes a vector of scalar components, i.e., we can only
  convert one set of scalars at a time from the quadrature points to the degrees
  of freedom and vice versa. So we need to store each component of stress separately,
  which requires <code>dim*dim</code> vectors. We'll store this set of vectors in a 2D array to
  make it easier to read off components in the same way you would the stress tensor.
  Thus, we'll loop over the components of stress on each cell and store
  these values in the global history field. (The prefix <code>history_</code>
  indicates that we work with quantities related to the history variables defined
  in the quadrature points.)
  @code
    FE_DGQ<dim>     history_fe (1);
    DoFHandler<dim> history_dof_handler (triangulation);
    history_dof_handler.distribute_dofs (history_fe);

    std::vector< std::vector< Vector<double> > >
                 history_field (dim, std::vector< Vector<double> >(dim)),
                 local_history_values_at_qpoints (dim, std::vector< Vector<double> >(dim)),
                 local_history_fe_values (dim, std::vector< Vector<double> >(dim));

    for (unsigned int i=0; i<dim; i++)
      for (unsigned int j=0; j<dim; j++)
      {
        history_field[i][j].reinit(history_dof_handler.n_dofs());
	local_history_values_at_qpoints[i][j].reinit(quadrature.size());
	local_history_fe_values[i][j].reinit(history_fe.dofs_per_cell);
      }

    FullMatrix<double> qpoint_to_dof_matrix (history_fe.dofs_per_cell,
                                             quadrature.size());
    FETools::compute_projection_from_quadrature_points_matrix
              (history_fe,
	       quadrature, quadrature,
	       qpoint_to_dof_matrix);

    typename DoFHandler<dim>::active_cell_iterator cell = dof_handler.begin_active(),
                                                   endc = dof_handler.end(),
                                                   dg_cell = history_dof_handler.begin_active();

    for (; cell!=endc; ++cell, ++dg_cell)
      {

        PointHistory<dim> *local_quadrature_points_history
	       = reinterpret_cast<PointHistory<dim> *>(cell->user_pointer());

        Assert (local_quadrature_points_history >=
                    &quadrature_point_history.front(),
		    ExcInternalError());
        Assert (local_quadrature_points_history <
                    &quadrature_point_history.back(),
		    ExcInternalError());

        for (unsigned int i=0; i<dim; i++)
          for (unsigned int j=0; j<dim; j++)
          {
            for (unsigned int q=0; q<quadrature.size(); ++q)
              local_history_values_at_qpoints[i][j](q)
                       = local_quadrature_points_history[q].old_stress[i][j];

            qpoint_to_dof_matrix.vmult (local_history_fe_values[i][j],
                                        local_history_values_at_qpoints[i][j]);

            dg_cell->set_dof_values (local_history_fe_values[i][j],
                                     history_field[i][j]);
          }
      }
  @endcode

- Now that we have a global field, we can refine the mesh and transfer the
  history_field vector as usual using the SolutionTransfer class. This will
  interpolate everything from the old to the new mesh.

- In a final step, we have to get the data back from the now interpolated
  global field to the quadrature points on the new mesh. The following code
  will do that:
  @code
    FullMatrix<double> dof_to_qpoint_matrix (quadrature.size(),
                                             history_fe.dofs_per_cell);
    FETools::compute_interpolation_to_quadrature_points_matrix
              (history_fe,
	       quadrature,
	       dof_to_qpoint_matrix);

    typename DoFHandler<dim>::active_cell_iterator cell = dof_handler.begin_active(),
                                                   endc = dof_handler.end(),
                                                   dg_cell = history_dof_handler.begin_active();

    for (; cell != endc; ++cell, ++dg_cell)
    {
      PointHistory<dim> *local_quadrature_points_history
	     = reinterpret_cast<PointHistory<dim> *>(cell->user_pointer());

      Assert (local_quadrature_points_history >=
                  &quadrature_point_history.front(),
		  ExcInternalError());
      Assert (local_quadrature_points_history <
                  &quadrature_point_history.back(),
		  ExcInternalError());

      for (unsigned int i=0; i<dim; i++)
        for (unsigned int j=0; j<dim; j++)
        {
          dg_cell->get_dof_values (history_field[i][j],
	                           local_history_fe_values[i][j]);

          dof_to_qpoint_matrix.vmult (local_history_values_at_qpoints[i][j],
                                      local_history_fe_values[i][j]);

          for (unsigned int q=0; q<quadrature.size(); ++q)
            local_quadrature_points_history[q].old_stress[i][j]
                       = local_history_values_at_qpoints[i][j](q);
      }
  @endcode

It becomes a bit more complicated once we run the program in parallel, since
then each process only stores this data for the cells it owned on the old
mesh. That said, using a parallel vector for <code>history_field</code> will
do the trick if you put a call to <code>compress</code> after the transfer
from quadrature points into the global vector.


<h5>Ensuring mesh regularity</h5>

At present, the program makes no attempt
to make sure that a cell, after moving its vertices at the end of the time
step, still has a valid geometry (i.e. that its Jacobian determinant is
positive and bounded away from zero everywhere). It is, in fact, not very hard
to set boundary values and forcing terms in such a way that one gets distorted
and inverted cells rather quickly. Certainly, in some cases of large
deformation, this is unavoidable with a mesh of finite mesh size, but in some
other cases this should be preventable by appropriate mesh refinement and/or a
reduction of the time step size. The program does not do that, but a more
sophisticated version definitely should employ some sort of heuristic defining
what amount of deformation of cells is acceptable, and what isn't.

