<h1>Results</h1>


Running the program takes a good while if one uses debug mode; it takes about
eleven minutes on my i7 desktop. Fortunately, the version compiled with
optimizations is much faster; the program only takes about a minute and a half
after recompiling with the command <tt>make release</tt> on the same machine, a
much more reasonable time.


If run, the program prints the following output, explaining what it is
doing during all that time:
@verbatim
\$ time make run
[ 66%] Built target \step-18
[100%] Run \step-18 with Release configuration
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 1.88062e+10
    Solver converged in 103 iterations.
    Updating quadrature point data...
  Cycle 1:
    Number of active cells:       12812 (by partition: 12812)
    Number of degrees of freedom: 51738 (by partition: 51738)
    Assembling system... norm of rhs is 1.86145e+10
    Solver converged in 121 iterations.
    Updating quadrature point data...
    Moving mesh...

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

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

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

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

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

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

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

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

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

[100%] Built target run
make run  176.82s user 0.15s system 198% cpu 1:28.94 total
@endverbatim
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:
@verbatim
\$ ls -l *vtu *visit
-rw-r--r-- 1 drwells users 1706059 Feb 13 19:36 solution-0010.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:36 solution-0010.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:36 solution-0010.visit
-rw-r--r-- 1 drwells users 1707907 Feb 13 19:36 solution-0009.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:36 solution-0009.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:36 solution-0009.visit
-rw-r--r-- 1 drwells users 1703771 Feb 13 19:35 solution-0008.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0008.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0008.visit
-rw-r--r-- 1 drwells users 1693671 Feb 13 19:35 solution-0007.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0007.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0007.visit
-rw-r--r-- 1 drwells users 1681847 Feb 13 19:35 solution-0006.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0006.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0006.visit
-rw-r--r-- 1 drwells users 1670115 Feb 13 19:35 solution-0005.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0005.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0005.visit
-rw-r--r-- 1 drwells users 1658559 Feb 13 19:35 solution-0004.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0004.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0004.visit
-rw-r--r-- 1 drwells users 1639983 Feb 13 19:35 solution-0003.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0003.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0003.visit
-rw-r--r-- 1 drwells users 1625851 Feb 13 19:35 solution-0002.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:35 solution-0002.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:35 solution-0002.visit
-rw-r--r-- 1 drwells users 1616035 Feb 13 19:34 solution-0001.000.vtu
-rw-r--r-- 1 drwells users     761 Feb 13 19:34 solution-0001.pvtu
-rw-r--r-- 1 drwells users      33 Feb 13 19:34 solution-0001.visit
@endverbatim


If we visualize these files with VisIt or Paraview, 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):


<div class="threecolumn" style="width: 80%">
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0002.0000.png"
           alt="Time = 2"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 2
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0005.0000.png"
           alt="Time = 5"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 5
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0007.0000.png"
           alt="Time = 7"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 7
    </div>
  </div>
</div>


<div class="threecolumn" style="width: 80%">
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0008.0000.png"
           alt="Time = 8"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 8
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0009.0000.png"
           alt="Time = 9"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 9
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.sequential-0010.0000.png"
           alt="Time = 10"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 10
    </div>
  </div>
</div>


As is clearly visible, as we keep compressing the cylinder, it starts
to bow out near the fully constrained bottom surface and, after about eight
time units, buckle in an azimuthally symmetric manner.


Although the result appears plausible for the symmetric geometry and loading,
it is yet to be established whether or not the computation is fully converged.
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 a proper workstation and
ran it on 16 processors in parallel. The beginning of the output now looks like
this:
@verbatim
Timestep 1 at time 0.5
  Cycle 0:
    Number of active cells:       29696 (by partition: 1808+1802+1894+1881+1870+1840+1884+1810+1876+1818+1870+1884+1854+1903+1816+1886)
    Number of degrees of freedom: 113100 (by partition: 6936+6930+7305+7116+7326+6869+7331+6786+7193+6829+7093+7162+6920+7280+6843+7181)
    Assembling system... norm of rhs is 1.10765e+10
    Solver converged in 209 iterations.
    Updating quadrature point data...
  Cycle 1:
    Number of active cells:       102034 (by partition: 6387+6202+6421+6341+6408+6201+6428+6428+6385+6294+6506+6244+6417+6527+6299+6546)
    Number of degrees of freedom: 359337 (by partition: 23255+21308+24774+24019+22304+21415+22430+22184+22298+21796+22396+21592+22325+22553+21977+22711)
    Assembling system... norm of rhs is 1.35759e+10
    Solver converged in 268 iterations.
    Updating quadrature point data...
    Moving mesh...

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

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

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

[...]

Timestep 20 at time 10
    Assembling system... norm of rhs is 1.47755e+10
    Solver converged in 425 iterations.
    Updating quadrature point data...
    Moving mesh...
@endverbatim
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:
@verbatim
\$ ls -l solution-0001*
-rw-r--r-- 1 wellsd2 user 761065 Feb 13 21:09 solution-0001.000.vtu
-rw-r--r-- 1 wellsd2 user 759277 Feb 13 21:09 solution-0001.001.vtu
-rw-r--r-- 1 wellsd2 user 761217 Feb 13 21:09 solution-0001.002.vtu
-rw-r--r-- 1 wellsd2 user 761605 Feb 13 21:09 solution-0001.003.vtu
-rw-r--r-- 1 wellsd2 user 756917 Feb 13 21:09 solution-0001.004.vtu
-rw-r--r-- 1 wellsd2 user 752669 Feb 13 21:09 solution-0001.005.vtu
-rw-r--r-- 1 wellsd2 user 735217 Feb 13 21:09 solution-0001.006.vtu
-rw-r--r-- 1 wellsd2 user 750065 Feb 13 21:09 solution-0001.007.vtu
-rw-r--r-- 1 wellsd2 user 760273 Feb 13 21:09 solution-0001.008.vtu
-rw-r--r-- 1 wellsd2 user 777265 Feb 13 21:09 solution-0001.009.vtu
-rw-r--r-- 1 wellsd2 user 772469 Feb 13 21:09 solution-0001.010.vtu
-rw-r--r-- 1 wellsd2 user 760833 Feb 13 21:09 solution-0001.011.vtu
-rw-r--r-- 1 wellsd2 user 782241 Feb 13 21:09 solution-0001.012.vtu
-rw-r--r-- 1 wellsd2 user 748905 Feb 13 21:09 solution-0001.013.vtu
-rw-r--r-- 1 wellsd2 user 738413 Feb 13 21:09 solution-0001.014.vtu
-rw-r--r-- 1 wellsd2 user 762133 Feb 13 21:09 solution-0001.015.vtu
-rw-r--r-- 1 wellsd2 user   1421 Feb 13 21:09 solution-0001.pvtu
-rw-r--r-- 1 wellsd2 user    364 Feb 13 21:09 solution-0001.visit
@endverbatim


Here are first the mesh on which we compute as well as the partitioning
for the 16 processors:


<div class="twocolumn" style="width: 80%">
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-000mesh.png"
           alt="Discretization"
           width="400">
    </div>
    <div class="text" align="center">
      Discretization
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0002.p.png"
           alt="Parallel partitioning"
           width="400">
    </div>
    <div class="text" align="center">
      Parallel partitioning
    </div>
  </div>
</div>


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

<div class="threecolumn" style="width: 80%">
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0002.s.png"
           alt="Time = 2"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 2
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0005.s.png"
           alt="Time = 5"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 5
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0007.s.png"
           alt="Time = 7"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 7
    </div>
  </div>
</div>


<div class="threecolumn" style="width: 80%">
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0008.s.png"
           alt="Time = 8"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 8
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0009.s.png"
           alt="Time = 9"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 9
    </div>
  </div>
  <div class="parent">
    <div class="img" align="center">
      <img src="https://www.dealii.org/images/steps/developer/step-18.parallel-0010.s.png"
           alt="Time = 10"
           width="400">
    </div>
    <div class="text" align="center">
      Time = 10
    </div>
  </div>
</div>


As before, we observe that at high axial compression the cylinder begins
to buckle, but this time ultimately collapses on itself. In contrast to our
first run, towards the end of the simulation the deflection pattern becomes
nonsymmetric (the central bulge deflects laterally). 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.


If one compares this with the previous run, the results both qualitatively
and quantitatively 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 (if we continued
compressing the cylinder we would observe some self-intersection).
Without such a formulation we cannot expect anything to make physical sense,
even if it produces nice pictures!


<h3>Possibilities 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;
@cite CTZ04.


<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&eacute; 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; @cite DL05.


<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.n_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.n_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.
