]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Standard transformations, and kinematics and tensors for elasticity.
authorJean-Paul Pelteret <jppelteret@gmail.com>
Mon, 5 Dec 2016 07:36:09 +0000 (08:36 +0100)
committerJean-Paul Pelteret <jppelteret@gmail.com>
Fri, 9 Dec 2016 17:52:10 +0000 (18:52 +0100)
This commit adds a set of functions to perform various push forward /
pull back operations that regularly feature in finite deformation
mechanics. It also adds some standard tensor definitions for elasticity,
as well as functions that compute kinematic quantities typically used in
finite strain elasticity.

Refers to discussion in dealii/code-gallery/#7.

27 files changed:
doc/doxygen/headers/physics.h [new file with mode: 0644]
doc/doxygen/options.dox.in
doc/news/changes.h
include/deal.II/physics/elasticity/kinematics.h [new file with mode: 0644]
include/deal.II/physics/elasticity/standard_tensors.h [new file with mode: 0644]
include/deal.II/physics/transformations.h [new file with mode: 0644]
source/CMakeLists.txt
source/physics/CMakeLists.txt [new file with mode: 0644]
source/physics/elasticity/CMakeLists.txt [new file with mode: 0644]
source/physics/elasticity/kinematics.cc [new file with mode: 0644]
source/physics/elasticity/kinematics.inst.in [new file with mode: 0644]
source/physics/elasticity/standard_tensors.cc [new file with mode: 0644]
source/physics/elasticity/standard_tensors.inst.in [new file with mode: 0644]
source/physics/transformations.cc [new file with mode: 0644]
source/physics/transformations.inst.in [new file with mode: 0644]
tests/physics/CMakeLists.txt [new file with mode: 0644]
tests/physics/elasticity-kinematics_01.cc [new file with mode: 0644]
tests/physics/elasticity-kinematics_01.output [new file with mode: 0644]
tests/physics/elasticity-standard_tensors_01.cc [new file with mode: 0644]
tests/physics/elasticity-standard_tensors_01.output [new file with mode: 0644]
tests/physics/prm/parameters-step-44.prm [new file with mode: 0644]
tests/physics/step-44-standard_tensors-material_push_forward.cc [new file with mode: 0644]
tests/physics/step-44-standard_tensors-material_push_forward.output [new file with mode: 0644]
tests/physics/step-44-standard_tensors-spatial.cc [new file with mode: 0644]
tests/physics/step-44-standard_tensors-spatial.output [new file with mode: 0644]
tests/physics/step-44.cc [new file with mode: 0644]
tests/physics/step-44.output [new file with mode: 0644]

diff --git a/doc/doxygen/headers/physics.h b/doc/doxygen/headers/physics.h
new file mode 100644 (file)
index 0000000..eda1175
--- /dev/null
@@ -0,0 +1,165 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+/**
+ * @defgroup physics Physics
+ *
+ * @brief A module dedicated to the implementation of functions and
+ * classes that relate to continuum physics, physical fields and materials.
+ */
+
+/**
+ * A collection of namespaces and utilities to assist in the
+ * definition, construction and manipulation of data related to
+ * physical fields and materials.
+ */
+namespace Physics
+{
+
+  /**
+  * A collection of operations to assist in the transformation of tensor
+  * quantities from the reference to spatial configuration, and vice versa.
+  * These types of transformation are typically used to re-express quantities
+  * measured or computed in one configuration in terms of a second configuration.
+  *
+  * <h3>Notation</h3>
+  *
+  * We will use the same notation for the coordinates $\mathbf{X}, \mathbf{x}$,
+  * transformations $\varphi$, differential operator $\nabla_{0}$ and deformation
+  * gradient $\mathbf{F}$ as discussed for namespace Physics::Elasticity.
+  *
+  * As a further point on notation, we will follow Holzapfel (2007) and denote
+  * the push forward transformation as $\chi\left(\bullet\right)$ and
+  * the pull back transformation as $\chi^{-1}\left(\bullet\right)$.
+  * We will also use the annotation $\left(\bullet\right)^{\sharp}$ to indicate
+  * that a tensor $\left(\bullet\right)$ is a contravariant tensor,
+  * and $\left(\bullet\right)^{\flat}$ that it is covariant. In other
+  * words, these indices do not actually change the tensor, they just indicate
+  * the <i>kind</i> of object a particular tensor is.
+  *
+  * @note For these transformations, unless otherwise stated, we will strictly
+  * assume that all indices of the transformed tensors derive from one coordinate
+  * system; that is to say that they are not multi-point tensors (such as the
+  * Piola stress in elasticity).
+  *
+  * @ingroup physics
+  *
+  * @author Jean-Paul Pelteret, Andrew McBride, 2016
+  */
+  namespace Transformations
+  {
+  }
+
+  /**
+   * This namespace provides a collection of definitions that
+   * conform to standard notation used in (nonlinear) elasticity.
+   *
+   * <h3>Notation</h3>
+   *
+   * References for this notation include:
+   * @code{.bib}
+       @Book{Holzapfel2007a,
+          title =     {Nonlinear solid mechanics. A Continuum Approach for Engineering},
+          publisher = {John Wiley \& Sons Ltd.},
+          year =      {2007},
+          author =    {Holzapfel, G. A.},
+          address =   {West Sussex, England},
+          note =      {ISBN: 0-471-82304-X}
+        }
+        @Book{Wriggers2008a,
+          title =     {Nonlinear finite element methods},
+          publisher = {Springer Berlin Heidelberg},
+          year =      {2008},
+          author =    {Wriggers, P.},
+          volume =    {4},
+          address =   {Berlin, Germany},
+          note =      {ISBN: 978-3-540-71000-4},
+          doi =       {10.1007/978-3-540-71001-1}
+        }
+   * @endcode
+   *
+   * For convenience we will predefine some commonly referenced tensors and
+   * operations.
+   * Considering the position vector $\mathbf{X}$ in the referential (material)
+   * configuration, points $\mathbf{X}$ are transformed to points $\mathbf{x}$
+   * in the current (spatial) configuration through the nonlinear map
+   * @f[
+   *  \mathbf{x}
+   *   := \boldsymbol{\varphi} \left( \mathbf{X} \right)
+   *    = \mathbf{X} + \mathbf{u}(\mathbf{X}) \, ,
+   * @f]
+   * where the $\mathbf{u}(\mathbf{X})$ represents the displacement vector.
+   * From this we can compute the deformation gradient tensor as
+   * @f[
+   *  \mathbf{F} := \mathbf{I} + \nabla_{0}\mathbf{u} \, ,
+   * @f]
+   * wherein the differential operator $\nabla_{0}$ is defined as
+   * $\frac{\partial}{\partial \mathbf{X}}$ and $\mathbf{I}$ is the identity
+   * tensor.
+   *
+   * Finally, two common tensor operators are represented by $\cdot$ and $:$
+   * operators. These respectively represent a single and double contraction over
+   * the inner tensor indices.
+   * Vectors and second-order tensors are highlighted by bold font, while
+   * fourth-order tensors are denoted by calliagraphic font.
+   *
+   * One can think of fourth-order tensors as linear operators mapping second-order
+   * tensors (matrices) onto themselves in much the same way as matrices map
+   * vectors onto vectors.
+   * To provide some context to the implemented class members and functions,
+   * consider the following fundamental operations performed on tensors with special
+   * properties:
+   *
+   * If we represent a general second-order tensor as $\mathbf{A}$, then the general
+   * fourth-order unit tensors $\mathcal{I}$ and $\overline{\mathcal{I}}$ are
+   * defined by
+   * @f[
+   *  \mathbf{A} = \mathcal{I}:\mathbf{A}
+   *        \qquad \text{and} \qquad
+   *      \mathbf{A}^T = \overline{\mathcal{I}}:\mathbf{A} \, ,
+   * @f]
+   * or, in indicial notation,
+   * @f[
+   *   I_{ijkl} = \delta_{ik}\delta_{jl}
+   *        \qquad \text{and} \qquad
+   *     \overline I_{ijkl} = \delta_{il}\delta_{jk}
+   * @f]
+   * with the Kronecker deltas taking their common definition.
+   * Note that $\mathcal{I} \neq \overline{\mathcal{I}}^T$.
+   *
+   * We then define the symmetric and skew-symmetric fourth-order unit tensors by
+   * @f[
+   *      \mathcal{S} := \dfrac{1}{2}[\mathcal{I} + \overline{\mathcal{I}}]
+   *    \qquad \text{and} \qquad
+   *      \mathcal{W} := \dfrac{1}{2}[\mathcal{I} - \overline{\mathcal{I}}] \, ,
+   * @f]
+   * such that
+   * @f[
+   *      \mathcal{S}:\mathbf{A} = \dfrac{1}{2}[\mathbf{A} + \mathbf{A}^T]
+   *    \qquad \text{and} \qquad
+   *      \mathcal{W}:\mathbf{A} = \dfrac{1}{2}[\mathbf{A} - \mathbf{A}^T] \, .
+   * @f]
+   * The fourth-order symmetric tensor returned by identity_tensor() is
+   * $\mathcal{S}$.
+   *
+   * @author Jean-Paul Pelteret, Andrew McBride, 2016
+   *
+   * @ingroup physics
+   */
+  namespace Elasticity
+  {
+  }
+
+}
index 98ab862678e11c4dd6208893ac5e858a64a1453a..e1c010deae3c5f31c50698a48d538e82ea8094d0 100644 (file)
@@ -85,6 +85,10 @@ ALIASES += dealiiOperationIsMultithreaded="@note If deal.II is configured with t
 
 ALIASES += dealiiExceptionMessage{1}="@note The message that will be printed by this exception reads: <div class='doxygen-generated-exception-message'> \1 </div> "
 
+ALIASES += dealiiWriggersA{2}="@note For a discussion of the background of this function, see P. Wriggers: \"Nonlinear finite element methods\" (2008), and in particular formula (\2) on p. \1 (or thereabouts). "
+
+ALIASES += dealiiHolzapfelA{2}="@note For a discussion of the background of this function, see G. A. Holzapfel: \"Nonlinear solid mechanics. A Continuum Approach for Engineering\" (2007), and in particular formula (\2) on p. \1 (or thereabouts). "
+
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
index 633937745c596f296460b20937ff0556d20b4146..f3931b48951ab36904700d5bc77c687f58754664 100644 (file)
@@ -235,6 +235,28 @@ inconvenience this causes.
 
 <ol>
 
+ <li> New: Implemented some standard tensor definitions for elasticity in
+ Physics::Elasticity::StandardTensors. Within the newly implemented
+ Physics::Elasticity::Kinematics namespace are some standard
+ definitions of kinematic quantities commonly used in elasticity.
+ <br>
+ (Jean-Paul Pelteret, Andrew McBride, 2016/12/05)
+ </li>
+
+ <li> New: The Physics::Transformations namespace defines a number of operations
+ that can be used to push forward and pull back quantities to and from different
+ body configurations.
+ <br>
+ (Jean-Paul Pelteret, Andrew McBride, 2016/12/05)
+ </li>
+
+ <li> New: The Physics namespace is dedicated to defining useful
+ functions and other quantities that are regularly used in the implementation
+ of classical (multi-)physics problems.
+ <br>
+ (Jean-Paul Pelteret, 2016/12/05)
+ </li>
+
 <li> Improved: The step-37 tutorial program now shows the matrix-free multigrid
      solver based on MPI parallelization rather than only a serial version.
      Moreover, support for adaptively refined meshes has been added.
diff --git a/include/deal.II/physics/elasticity/kinematics.h b/include/deal.II/physics/elasticity/kinematics.h
new file mode 100644 (file)
index 0000000..28fd56f
--- /dev/null
@@ -0,0 +1,401 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii__elasticity__kinematics_h
+#define dealii__elasticity__kinematics_h
+
+
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace Physics
+{
+
+  namespace Elasticity
+  {
+
+    /**
+     * A collection of tensor definitions for deformation and strain measures,
+     * as well as a few special transformations, that conform to notation used in
+     * standard scientific literature, in particular the books of
+     * Holzapfel (2007) and Wriggers (2008). The citation for these references,
+     * as well as other notation used here, can be found in the description for
+     * the Physics::Elasticity namespace.
+
+     * @note These hold specifically for the codimension
+     * 0 case, where the metric tensor is the identity tensor.
+     *
+     * @author Jean-Paul Pelteret, Andrew McBride, 2016
+    */
+    namespace Kinematics
+    {
+
+      /**
+       * @name Deformation tensors
+       */
+//@{
+
+      /**
+       * Returns the deformation gradient tensor,
+       * as constructed from the material displacement gradient
+       * tensor @p Grad_u.
+       * The result is expressed as
+       * @f[
+       *  \mathbf{F}
+       *    := \nabla_{0} \boldsymbol{\varphi} \left( \mathbf{X} \right)
+       *     =\mathbf{I} + \nabla_{0}\mathbf{u}
+       * @f]
+       * where $\mathbf{u} = \mathbf{u}\left(\mathbf{X}\right)$ is the
+       * displacement at position $\mathbf{X}$ in the referential configuration.
+       * The differential operator $\nabla_{0}$ is defined as
+       * $\frac{\partial}{\partial \mathbf{X}}$.
+       *
+       * @dealiiWriggersA{23,3.14}
+       * @dealiiHolzapfelA{71,2.39}
+       */
+      template <int dim, typename Number>
+      Tensor<2, dim, Number>
+      F (const Tensor<2, dim, Number> &Grad_u);
+
+      /**
+       * Returns the isochoric counterpart of the deformation gradient
+       * tensor @p F .
+       * The result is expressed as
+       * @f[
+       *  \mathbf{F}^{\text{iso}} := J^{-1/\textrm{dim}} \mathbf{F}
+       * @f]
+       * where $J = \text{det}\left(\mathbf{F}\right)$.
+       *
+       * @dealiiWriggersA{29,3.28}
+       * @dealiiHolzapfelA{228,6.79}
+       */
+      template <int dim, typename Number>
+      Tensor<2, dim, Number>
+      F_iso (const Tensor<2, dim, Number> &F);
+
+      /**
+       * Returns the volumetric counterpart of the deformation gradient
+       * tensor @p F .
+       * The result is expressed as
+       * @f[
+       *  \mathbf{F}^{\text{vol}} := J^{1/\textrm{dim}} \mathbf{I}
+       * @f]
+       * where $J = \text{det}\left(\mathbf{F}\right)$.
+       *
+       * @dealiiWriggersA{29,3.28}
+       * @dealiiHolzapfelA{228,6.79}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      F_vol (const Tensor<2, dim, Number> &F);
+
+      /**
+       * Returns the symmetric right Cauchy-Green deformation tensor,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result is expressed as
+       * @f[
+       *  \mathbf{C} := \mathbf{F}^{T}\cdot\mathbf{F} \, .
+       * @f]
+       *
+       * @dealiiWriggersA{23,3.15}
+       * @dealiiHolzapfelA{78,2.65}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      C (const Tensor<2, dim, Number> &F);
+
+      /**
+       * Returns the symmetric left Cauchy-Green deformation tensor,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result is expressed as
+       * @f[
+       *  \mathbf{b} := \mathbf{F}\cdot\mathbf{F}^{T} \, .
+       * @f]
+       *
+       * @dealiiWriggersA{28,3.25}
+       * @dealiiHolzapfelA{81,2.79}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      b (const Tensor<2, dim, Number> &F);
+
+//@}
+
+      /**
+       * @name Strain tensors
+       */
+//@{
+
+      /**
+       * Returns the symmetric Green-Lagrange strain tensor,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result is expressed as
+       * @f[
+       *  \mathbf{E} := \frac{1}{2}[\mathbf{F}^{T}\cdot\mathbf{F} - \mathbf{I}] \, .
+       * @f]
+       *
+       * @dealiiWriggersA{23,3.15}
+       * @dealiiHolzapfelA{79,6.29}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      E (const Tensor<2, dim, Number> &F);
+
+      /**
+       * Returns the symmetric small strain tensor,
+       * as constructed from the displacement gradient tensor @p Grad_u.
+       * The result is expressed as
+       * @f[
+       *  \boldsymbol{\varepsilon} := \frac{1}{2} \left[ \nabla_{0}\mathbf{u}
+       *   + [\nabla_{0}\mathbf{u}]^{T} \right] \, .
+       * @f]
+       * where $\mathbf{u} = \mathbf{u}(\mathbf{X})$ is the displacement at position
+       * $\mathbf{X}$ in the referential configuration.
+       * The differential operator $\nabla_{0}$ is defined as
+       * $\frac{\partial}{\partial \mathbf{X}}$.
+       *
+       * @dealiiWriggersA{24,3.17}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      epsilon (const Tensor<2, dim, Number> &Grad_u);
+
+      /**
+       * Returns the symmetric Almansi strain tensor,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result is expressed as
+       * @f[
+       *  \mathbf{e} := \frac{1}{2} \left[ \mathbf{I}
+       *   - \mathbf{F}^{-T}\cdot\mathbf{F}^{-1} \right] \, .
+       * @f]
+       *
+       * @dealiiWriggersA{30,3.35}
+       * @dealiiHolzapfelA{81,2.83}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      e (const Tensor<2, dim, Number> &F);
+
+//@}
+
+      /**
+       * @name Strain rate tensors
+       */
+//@{
+
+      /**
+       * Returns the spatial velocity gradient tensor,
+       * as constructed from the deformation gradient tensor @p F
+       * and its material time derivative @p dF_dt (the material velocity
+       * gradient).
+       * The result is expressed as
+       * @f[
+       *  \mathbf{l} := \dot{\mathbf{F}}\cdot\mathbf{F}^{-1} \, .
+       * @f]
+       *
+       * @dealiiWriggersA{32,3.47}
+       * @dealiiHolzapfelA{96,2.141}
+       */
+      template <int dim, typename Number>
+      Tensor<2, dim, Number>
+      l (const Tensor<2, dim, Number> &F,
+         const Tensor<2, dim, Number> &dF_dt);
+
+      /**
+       * Returns the rate of deformation tensor (also known as the rate of strain
+       * tensor), as constructed from the deformation gradient tensor @p F
+       * and its material time derivative @p dF_dt (the material velocity
+       * gradient).
+       * The result is expressed as
+       * @f[
+       *  \mathbf{d} := \frac{1}{2} \left[ \mathbf{l} + \mathbf{l}^{T} \right] \, .
+       * @f]
+       * where
+       * @f[
+       *  \mathbf{l} = \dot{\mathbf{F}}\cdot\mathbf{F}^{-1}
+       * @f]
+       * is the spatial velocity gradient tensor.
+       *
+       * @dealiiWriggersA{32,3.49}
+       * @dealiiHolzapfelA{97,2.148}
+       */
+      template <int dim, typename Number>
+      SymmetricTensor<2, dim, Number>
+      d (const Tensor<2, dim, Number> &F,
+         const Tensor<2, dim, Number> &dF_dt);
+
+      /**
+       * Returns the rate of rotation tensor (also known as the vorticity
+       * tensor), as constructed from the deformation gradient tensor @p F
+       * and its material time derivative @p dF_dt (the material velocity
+       * gradient).
+       * The result is expressed as
+       * @f[
+       *  \mathbf{w} := \frac{1}{2} \left[ \mathbf{l} - \mathbf{l}^{T} \right] \, .
+       * @f]
+       * where
+       * @f[
+       *  \mathbf{l} = \dot{\mathbf{F}}\cdot\mathbf{F}^{-1}
+       * @f]
+       * is the spatial velocity gradient tensor.
+       *
+       * @dealiiHolzapfelA{97,2.149}
+       */
+      template <int dim, typename Number>
+      Tensor<2, dim, Number>
+      w (const Tensor<2, dim, Number> &F,
+         const Tensor<2, dim, Number> &dF_dt);
+
+//@}
+    };
+
+  }
+}
+
+
+
+#ifndef DOXYGEN
+
+// ------------------------- inline functions ------------------------
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2, dim, Number>
+Physics::Elasticity::Kinematics::F (const Tensor<2, dim, Number> &Grad_u)
+{
+  return StandardTensors<dim>::I + Grad_u;
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2, dim, Number>
+Physics::Elasticity::Kinematics::F_iso (const Tensor<2, dim, Number> &F)
+{
+  return std::pow(determinant(F),-1.0/dim)*F;
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::F_vol (const Tensor<2, dim, Number> &F)
+{
+  return Number(std::pow(determinant(F),1.0/dim))*static_cast< SymmetricTensor<2,dim,Number> >(unit_symmetric_tensor<dim>());
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::C (const Tensor<2, dim, Number> &F)
+{
+  return symmetrize(transpose(F)*F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::b (const Tensor<2, dim, Number> &F)
+{
+  return symmetrize(F*transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::E (const Tensor<2, dim, Number> &F)
+{
+  return Number(0.5)*(C(F) - static_cast<SymmetricTensor<2,dim,Number> >(StandardTensors<dim>::I));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::epsilon (const Tensor<2, dim, Number> &Grad_u)
+{
+// This is the equivalent to 0.5*symmetrize(Grad_u + transpose(Grad_u));
+  return symmetrize(Grad_u);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::e (const Tensor<2, dim, Number> &F)
+{
+  const Tensor<2, dim, Number> F_inv = invert(F);
+  return Number(0.5)*symmetrize(static_cast<SymmetricTensor<2,dim,Number> >(StandardTensors<dim>::I) - transpose(F_inv)*F_inv);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2, dim, Number>
+Physics::Elasticity::Kinematics::l (
+  const Tensor<2, dim, Number> &F,
+  const Tensor<2, dim, Number> &dF_dt)
+{
+  return dF_dt*invert(F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::Kinematics::d (
+  const Tensor<2, dim, Number> &F,
+  const Tensor<2, dim, Number> &dF_dt)
+{
+  return symmetrize(l(F,dF_dt));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2, dim, Number>
+Physics::Elasticity::Kinematics::w (
+  const Tensor<2, dim, Number> &F,
+  const Tensor<2, dim, Number> &dF_dt)
+{
+  // This could be implemented as w = l-d, but that would mean computing "l"
+  // a second time.
+  const Tensor<2,dim> grad_v = l(F,dF_dt);
+  return 0.5*(grad_v - transpose(grad_v)) ;
+}
+
+#endif // DOXYGEN
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
diff --git a/include/deal.II/physics/elasticity/standard_tensors.h b/include/deal.II/physics/elasticity/standard_tensors.h
new file mode 100644 (file)
index 0000000..4a58ca0
--- /dev/null
@@ -0,0 +1,369 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii__elasticity__standard_tensors_h
+#define dealii__elasticity__standard_tensors_h
+
+
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace Physics
+{
+
+  namespace Elasticity
+  {
+
+    /**
+     * A collection of tensor definitions that mostly conform to notation used
+     * in standard scientific literature, in particular the book of
+     * Wriggers (2008). The citation for this reference, as well as other
+     * notation used here, can be found in the description for the Physics::Elasticity
+     * namespace.
+     *
+     * @note These hold specifically for the codimension 0 case with a
+     * Cartesian basis, where the metric tensor is the identity tensor.
+     *
+     * @relates Tensor
+     * @relates SymmetricTensor
+     * @author Jean-Paul Pelteret, Andrew McBride, 2016
+    */
+    template <int dim>
+    class StandardTensors
+    {
+    public:
+
+      /**
+       * @name Metric tensors
+       */
+//@{
+
+      /**
+       * The second-order referential/spatial symmetric identity (metric) tensor
+       * $\mathbf{I}$.
+       *
+       * This is defined such that, for any rank-2 tensor or symmetric tensor,
+       * the following holds:
+       * @f[
+       *  \mathbf{I} \cdot \{ \bullet \} = \{ \bullet \} \cdot \mathbf{I} =
+       * \{ \bullet \}
+       *    \qquad \text{and} \qquad
+       * \mathbf{I} : \{ \bullet \} = \textrm{trace} \{ \bullet \} \, .
+       * @f]
+       *
+       * This definition aligns with the rank-2 symmetric tensor returned by
+       * unit_symmetric_tensor(). If one is to interpret the tensor as a
+       * matrix, then this simply corresponds to the identity matrix.
+       */
+      static const SymmetricTensor<2, dim> I;
+
+      /**
+       * The fourth-order referential/spatial unit symmetric tensor $\mathcal{S}$.
+       *
+       * This is defined such that for a general rank-2 tensor $\{ \hat{\bullet} \}$
+       * the following holds:
+       * @f[
+       *   \mathcal{S} : \{ \hat{\bullet} \}
+       *     := \dfrac{1}{2}[\{ \hat{\bullet} \} + \{ \hat{\bullet} \}^T] \, .
+       * @f]
+       *
+       * As a corollary to this, for any second-order symmetric tensor $\{ \bullet \}$
+       * @f[
+       *  \mathcal{S} : \{ \bullet \}
+       *    = \{ \bullet \} : \mathcal{S} = \{ \bullet \} \, .
+       * @f]
+       *
+       * This definition aligns with the fourth-order symmetric tensor $\mathcal{S}$
+       * introduced in the Physics::Elasticity namespace description and that
+       * which is returned by identity_tensor().
+       *
+       * @note If you apply this to a standard tensor then it doesn't behave like
+       * the fourth-order identity tensor, but rather as a symmetrization operator.
+       */
+      static const SymmetricTensor<4, dim> S;
+
+      /**
+       * The fourth-order referential/spatial tensor $\mathbf{I} \otimes \mathbf{I}$.
+       *
+       * This is defined such that, for any rank-2 tensor, the following holds:
+       * @f[
+       *  [\mathbf{I} \otimes \mathbf{I}] : \{ \bullet \} =
+       *  \textrm{trace}\{ \bullet \} \mathbf{I} \, .
+       * @f]
+       */
+      static const SymmetricTensor<4, dim> IxI;
+
+//@}
+
+      /**
+       * @name Projection operators
+       */
+//@{
+
+      /**
+       * The fourth-order spatial deviatoric tensor. Also known as the deviatoric
+       * operator, this tensor projects a second-order symmetric tensor onto a
+       * a deviatoric space (for which the hydrostatic component is removed).
+       *
+       * This is defined as
+       * @f[
+       *   \mathcal{P}
+       *     := \mathcal{S} - \frac{1}{\textrm{dim}} \mathbf{I} \otimes \mathbf{I}
+       * @f]
+       * where $\mathcal{S}$ is the fourth-order unit symmetric tensor and
+       * $\mathbf{I}$ is the second-order identity tensor.
+       *
+       * For any second-order (spatial) symmetric tensor the following holds:
+       * @f[
+       *  \mathcal{P} : \{ \bullet \}
+       *  := \{ \bullet \} - \frac{1}{\textrm{dim}} \left[ \{ \bullet \} : \mathbf{I} \right]\mathbf{I}
+       *   = \mathcal{P}^{T} : \{ \bullet \}
+       *   = \texttt{dev_P} \left( \{ \bullet \} \right)
+       * @f]
+       * and, therefore,
+       * @f[
+       * \texttt{dev_P} \left( \{ \bullet \} \right) : \mathbf{I}
+       *   = \textrm{trace}(\texttt{dev_P} \left( \{ \bullet \} \right)) = 0 \, .
+       * @f]
+       *
+       * This definition aligns with the fourth-order symmetric tensor that
+       * is returned by deviator_tensor().
+       *
+       * @dealiiWriggersA{47,3.129}
+       * @dealiiHolzapfelA{232,6.105}
+       */
+      static const SymmetricTensor<4, dim> dev_P;
+
+      /**
+       * Returns the fourth-order referential deviatoric tensor, as constructed from
+       * the deformation gradient tensor @p F.
+       * Also known as the deviatoric operator, this tensor projects a second-order
+       * symmetric tensor onto a deviatoric space (for which the hydrostatic
+       * component is removed).
+       *
+       * This referential isochoric projection tensor is defined as
+       * @f[
+       *   \hat{\mathcal{P}}
+       *     := \frac{\partial \bar{\mathbf{C}}}{\partial \mathbf{C}}
+       * @f]
+       * with
+       * @f[
+       *  \bar{\mathbf{C}} := J^{-2/\textrm{dim}} \mathbf{C}
+       *    \qquad \text{,} \qquad
+       *  \mathbf{C} = \mathbf{F}^{T}\cdot\mathbf{F}
+       *    \qquad \text{and} \qquad
+       *  J = \textrm{det}\mathbf{F}
+       * @f]
+       * such that, for any second-order (referential) symmetric tensor,
+       * the following holds:
+       * @f[
+       *  \{ \bullet \} : \hat{\mathcal{P}}
+       *    := J^{-2/\textrm{dim}} \left[ \{ \bullet \} - \frac{1}{\textrm{dim}}\left[\mathbf{C} : \{ \bullet \}\right] \mathbf{C}^{-1} \right]
+       *    = \texttt{Dev_P} \left( \{ \bullet \} \right) \, .
+       * @f]
+       * It can therefore be readily shown that
+       * @f[
+       *  \texttt{Dev_P} \left( \{ \bullet \} \right) : \mathbf{C} = 0 \, .
+       * @f]
+       *
+       * @note It may be observed that we have defined the tensor as the
+       * transpose of that adopted by Wriggers (2008). We have done this so that
+       * it may be strictly applied through the chain rule to achieve the
+       * definition of the second Piola-Kirchhoff stress, i.e.
+       * @f[
+       *   \mathbf{S}
+       *     = 2\frac{\partial \psi \left( \bar{\mathbf{C}} \right)}{\partial \mathbf{C}}
+       *     = 2\frac{\partial \psi \left( \bar{\mathbf{C}} \right)}{\partial \bar{\mathbf{C}}}
+       *     : \frac{\partial \bar{\mathbf{C}}}{\partial \mathbf{C}}
+       *     = \bar{\mathbf{S}} : \hat{\mathcal{P}}
+       *     \equiv \hat{\mathcal{P}}^{T} : \bar{\mathbf{S}} \, .
+       * @f]
+       *
+       * @note Comparing the definition of this tensor in Holzapfel (2001) to that
+       * adopted here, the inclusion of the extra factor $J^{-2/\textrm{dim}}$ does not,
+       * at the outset, seem to be a reasonable choice.
+       * However, in the author's view it makes direct implementation of the
+       * expressions for isochoric (referential) stress contributions and their
+       * linearization simpler in practise.
+       *
+       * @dealiiWriggersA{46,3.125}
+       * @dealiiHolzapfelA{229,6.83}
+       */
+      template <typename Number>
+      static SymmetricTensor<4, dim, Number>
+      Dev_P (const Tensor<2, dim, Number> &F);
+
+      /**
+       * Returns the transpose of the fourth-order referential deviatoric tensor,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result performs the following operation:
+       * @f[
+       *  \texttt{Dev_P_T} \{ \bullet \} = J^{-2/\textrm{dim}} \left[ \{ \bullet \} -
+       *  \frac{1}{\textrm{dim}} \left[\mathbf{C}^{-1} : \{ \bullet \}\right] \mathbf{C} \right]
+       *  := \hat{\mathcal{P}}^{T} : \{ \bullet \}
+       * @f]
+       */
+      template <typename Number>
+      static SymmetricTensor<4, dim, Number>
+      Dev_P_T (const Tensor<2, dim, Number> &F);
+
+//@}
+
+      /**
+       * @name Scalar derivatives
+       */
+//@{
+      /**
+       * Returns the derivative of the volumetric Jacobian
+       * $J = \text{det} \mathbf{F}$ with respect to the right Cauchy-Green
+       * tensor, as constructed from the deformation gradient tensor @p F.
+       * The computed result is
+       * @f[
+       *  \frac{\partial J}{\partial \mathbf{C}}
+       *   = \frac{1}{2} J \mathbf{C}^{-1}
+       * @f]
+       * with
+       * @f[
+       *  \mathbf{C} = \mathbf{F}^{T}\cdot\mathbf{F} \, .
+       * @f]
+       *
+       * @dealiiWriggersA{46,3.124}
+       * @dealiiHolzapfelA{228,6.82}
+       */
+      template <typename Number>
+      static SymmetricTensor<2, dim, Number>
+      ddet_F_dC (const Tensor<2, dim, Number> &F);
+
+//@}
+
+      /**
+       * @name Tensor derivatives
+       */
+//@{
+
+      /**
+       * Returns the derivative of the inverse of the right Cauchy-Green
+       * tensor with respect to the right Cauchy-Green tensor itself,
+       * as constructed from the deformation gradient tensor @p F.
+       * The result, accounting for symmetry, is defined in index notation as
+       * @f[
+       *  \left[ \frac{\partial \mathbf{C}^{-1}}{\partial \mathbf{C}} \right]_{IJKL}
+       *    := -\frac{1}{2}[ C^{-1}_{IK}C^{-1}_{JL}
+       *     + C^{-1}_{IL}C^{-1}_{JK}  ]
+       * @f]
+       *
+       * @dealiiWriggersA{76,3.255}
+       */
+      template <typename Number>
+      static SymmetricTensor<4, dim, Number>
+      dC_inv_dC (const Tensor<2, dim, Number> &F);
+
+//@}
+    };
+
+  }
+}
+
+
+
+#ifndef DOXYGEN
+
+// ------------------------- inline functions ------------------------
+
+
+template <int dim>
+template <typename Number>
+inline
+SymmetricTensor<4, dim, Number>
+Physics::Elasticity::StandardTensors<dim>::Dev_P (const Tensor<2, dim, Number> &F)
+{
+  const Number det_F = determinant(F);
+  Assert(det_F > Number(0.0),
+         ExcMessage("Deformation gradient has a negative determinant."));
+  const Tensor<2,dim,Number> C_ns = transpose(F)*F;
+  const SymmetricTensor<2,dim,Number> C = symmetrize(C_ns);
+  const SymmetricTensor<2,dim,Number> C_inv = symmetrize(invert(C_ns));
+
+  // See Wriggers p46 equ 3.125 (but transpose indices)
+  SymmetricTensor<4,dim,Number> Dev_P = outer_product(C,C_inv);  // Dev_P = C_x_C_inv
+  Dev_P /= -dim;                                                 // Dev_P = -[1/dim]C_x_C_inv
+  Dev_P += SymmetricTensor<4,dim,Number>(S);                     // Dev_P = S - [1/dim]C_x_C_inv
+  Dev_P *= std::pow(det_F, -2.0/dim);                            // Dev_P = J^{-2/dim} [S - [1/dim]C_x_C_inv]
+
+  return Dev_P;
+}
+
+
+
+template <int dim>
+template <typename Number>
+inline
+SymmetricTensor<4, dim, Number>
+Physics::Elasticity::StandardTensors<dim>::Dev_P_T (const Tensor<2, dim, Number> &F)
+{
+  const Number det_F = determinant(F);
+  Assert(det_F > Number(0.0),
+         ExcMessage("Deformation gradient has a negative determinant."));
+  const Tensor<2,dim,Number> C_ns = transpose(F)*F;
+  const SymmetricTensor<2,dim,Number> C = symmetrize(C_ns);
+  const SymmetricTensor<2,dim,Number> C_inv = symmetrize(invert(C_ns));
+
+  // See Wriggers p46 equ 3.125 (not transposed)
+  SymmetricTensor<4,dim,Number> Dev_P_T = outer_product(C_inv,C);  // Dev_P = C_inv_x_C
+  Dev_P_T /= -dim;                                                 // Dev_P = -[1/dim]C_inv_x_C
+  Dev_P_T += SymmetricTensor<4,dim,Number>(S);                     // Dev_P = S - [1/dim]C_inv_x_C
+  Dev_P_T *= std::pow(det_F, -2.0/dim);                            // Dev_P = J^{-2/dim} [S - [1/dim]C_inv_x_C]
+
+  return Dev_P_T;
+}
+
+
+
+template <int dim>
+template <typename Number>
+inline
+SymmetricTensor<2, dim, Number>
+Physics::Elasticity::StandardTensors<dim>::ddet_F_dC (const Tensor<2, dim, Number> &F)
+{
+  return Number(0.5)*determinant(F)*symmetrize(invert(transpose(F)*F));
+}
+
+
+
+template <int dim>
+template <typename Number>
+inline
+SymmetricTensor<4, dim, Number>
+Physics::Elasticity::StandardTensors<dim>::dC_inv_dC (const Tensor<2, dim, Number> &F)
+{
+  const SymmetricTensor<2,dim,Number> C_inv = symmetrize(invert(transpose(F)*F));
+
+  SymmetricTensor<4,dim,Number> dC_inv_dC;
+  for (unsigned int A=0; A<dim; ++A)
+    for (unsigned int B=A; B<dim; ++B)
+      for (unsigned int C=0; C<dim; ++C)
+        for (unsigned int D=C; D<dim; ++D)
+          dC_inv_dC[A][B][C][D] -= 0.5*(C_inv[A][C] * C_inv[B][D] + C_inv[A][D] * C_inv[B][C] );
+
+  return dC_inv_dC;
+}
+
+#endif // DOXYGEN
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
diff --git a/include/deal.II/physics/transformations.h b/include/deal.II/physics/transformations.h
new file mode 100644 (file)
index 0000000..bda767f
--- /dev/null
@@ -0,0 +1,1135 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii__transformations_h
+#define dealii__transformations_h
+
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/symmetric_tensor.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+
+namespace Physics
+{
+
+  namespace Transformations
+  {
+
+    /**
+     * Transformation of tensors that are defined in terms of a set of
+     * contravariant bases. Rank-1 and rank-2 contravariant tensors
+     * $\left(\bullet\right)^{\sharp} = \mathbf{T}$ (and its spatial counterpart
+     * $\mathbf{t}$) typically satisfy the relation
+     * @f[
+     *    \int_{V_{0}} \nabla_{0} \cdot \mathbf{T} \; dV
+     *      = \int_{\partial V_{0}} \mathbf{T} \cdot \mathbf{N} \; dA
+     *      = \int_{\partial V_{t}} \mathbf{T} \cdot \mathbf{n} \; da
+     *      = \int_{V_{t}} \nabla \cdot \mathbf{t} \; dv
+     * @f]
+     * where $V_{0}$ and $V_{t}$ are respectively control volumes in the
+     * reference and spatial configurations, and their surfaces $\partial V_{0}$
+     * and $\partial V_{t}$ have the outwards facing normals $\mathbf{N}$ and
+     * $\mathbf{n}$.
+     *
+     * @author Jean-Paul Pelteret, Andrew McBride, 2016
+    */
+    namespace Contravariant
+    {
+
+      /**
+       * @name Push forward operations
+       */
+//@{
+
+      /**
+      * Returns the result of the push forward transformation on a
+      * contravariant vector, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F} \cdot \left(\bullet\right)^{\sharp}
+      * @f]
+      *
+      * @param[in] V The (referential) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{V} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      push_forward (const Tensor<1,dim,Number> &V,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * contravariant tensor, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{T}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      push_forward (const Tensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * contravariant symmetric tensor, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{T}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      push_forward (const SymmetricTensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * contravariant tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi\left(\bullet\right)^{\sharp} \right]_{ijkl}
+      *    := F_{iI} F_{jJ} \left(\bullet\right)^{\sharp}_{IJKL} F_{kK} F_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      push_forward (const Tensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * contravariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi\left(\bullet\right)^{\sharp} \right]_{ijkl}
+      *    := F_{iI} F_{jJ} \left(\bullet\right)^{\sharp}_{IJKL} F_{kK} F_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      push_forward (const SymmetricTensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number>          &F);
+
+//@}
+
+      /**
+       * @name Pull back operations
+       */
+//@{
+
+      /**
+      * Returns the result of the pull back transformation on a
+      * contravariant vector, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp}
+      * @f]
+      *
+      * @param[in] v The (spatial) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{v} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      pull_back (const Tensor<1,dim,Number> &v,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * contravariant tensor, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{-T}
+      * @f]
+      *
+      * @param[in] t The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      pull_back (const Tensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * contravariant symmetric tensor, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{-T}
+      * @f]
+      *
+      * @param[in] t The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      pull_back (const SymmetricTensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi^{-1}\left(\bullet\right)^{\sharp} \right]_{IJKL}
+      *    := F^{-1}_{Ii} F^{-1}_{Jj} \left(\bullet\right)^{\sharp}_{ijkl} F^{-1}_{Kk} F^{-1}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      pull_back (const Tensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi^{-1}\left(\bullet\right)^{\sharp} \right]_{IJKL}
+      *    := F^{-1}_{Ii} F^{-1}_{Jj} \left(\bullet\right)^{\sharp}_{ijkl} F^{-1}_{Kk} F^{-1}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      pull_back (const SymmetricTensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number>          &F);
+
+//@}
+    }
+
+    /**
+     * Transformation of tensors that are defined in terms of a set of
+     * covariant basis vectors. Rank-1 and rank-2 covariant tensors
+     * $\left(\bullet\right)^{\flat} = \mathbf{T}$ (and its spatial counterpart
+     * $\mathbf{t}$) typically satisfy the relation
+     * @f[
+     *    \int_{\partial V_{0}} \left[ \nabla_{0} \times \mathbf{T} \right] \cdot \mathbf{N} \; dA
+     *      = \oint_{\partial A_{0}} \mathbf{T} \cdot \mathbf{L} \; dL
+     *      = \oint_{\partial A_{t}} \mathbf{t} \cdot \mathbf{l} \; dl
+     *      = \int_{\partial V_{t}} \left[ \nabla \times \mathbf{t} \right] \cdot \mathbf{n} \; da
+     * @f]
+     * where the control surfaces $\partial V_{0}$ and $\partial V_{t}$ with
+     * outwards facing normals $\mathbf{N}$ and $\mathbf{n}$ are bounded by the
+     * curves $\partial A_{0}$ and $\partial A_{0}$ that are, respectively,
+     * associated with the line directors $\mathbf{L}$ and $\mathbf{l}$.
+     *
+     * @author Jean-Paul Pelteret, Andrew McBride, 2016
+    */
+    namespace Covariant
+    {
+
+      /**
+       * @name Push forward operations
+       */
+//@{
+
+      /**
+      * Returns the result of the push forward transformation on a covariant
+      * vector, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{-T} \cdot \left(\bullet\right)^{\flat}
+      * @f]
+      *
+      * @param[in] V The (referential) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{V} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      push_forward (const Tensor<1,dim,Number> &V,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * covariant tensor, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{-T} \cdot \left(\bullet\right)^{\flat} \cdot \mathbf{F}^{-1}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      push_forward (const Tensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * covariant symmetric tensor, i.e.
+      * @f[
+      *  \chi\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{-T} \cdot \left(\bullet\right)^{\flat} \cdot \mathbf{F}^{-1}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      push_forward (const SymmetricTensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * covariant tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi\left(\bullet\right)^{\flat} \right]_{ijkl}
+      *    := F^{-T}_{iI} F^{-T}_{jJ} \left(\bullet\right)^{\flat}_{IJKL} F^{-T}_{kK} F^{-T}_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      push_forward (const Tensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * covariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi\left(\bullet\right)^{\flat} \right]_{ijkl}
+      *    := F^{-T}_{iI} F^{-T}_{jJ} \left(\bullet\right)^{\flat}_{IJKL} F^{-T}_{kK} F^{-T}_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      push_forward (const SymmetricTensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number>          &F);
+
+//@}
+
+      /**
+       * @name Pull back operations
+       */
+//@{
+
+      /**
+      * Returns the result of the pull back transformation on a
+      * covariant vector, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{T} \cdot \left(\bullet\right)^{\flat}
+      * @f]
+      *
+      * @param[in] v The (spatial) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{v} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      pull_back (const Tensor<1,dim,Number> &v,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * covariant tensor, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{T} \cdot \left(\bullet\right)^{\flat} \cdot \mathbf{F}
+      * @f]
+      *
+      * @param[in] t The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      pull_back (const Tensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * covariant symmetric tensor, i.e.
+      * @f[
+      *  \chi^{-1}\left(\bullet\right)^{\flat}
+      *    := \mathbf{F}^{T} \cdot \left(\bullet\right)^{\flat} \cdot \mathbf{F}
+      * @f]
+      *
+      * @param[in] t The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      pull_back (const SymmetricTensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi^{-1}\left(\bullet\right)^{\flat} \right]_{IJKL}
+      *    := F^{T}_{Ii} F^{T}_{Jj} \left(\bullet\right)^{\flat}_{ijkl} F^{T}_{Kk} F^{T}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      pull_back (const Tensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \left[ \chi^{-1}\left(\bullet\right)^{\flat} \right]_{IJKL}
+      *    := F^{T}_{Ii} F^{T}_{Jj} \left(\bullet\right)^{\flat}_{ijkl} F^{T}_{Kk} F^{T}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      pull_back (const SymmetricTensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number>          &F);
+
+//@}
+    }
+
+    /**
+     * Transformation of tensors that are defined in terms of a set of
+     * contravariant basis vectors and scale with the inverse of the volume
+     * change associated with the mapping.
+     *
+     * @author Jean-Paul Pelteret, Andrew McBride, 2016
+    */
+    namespace Piola
+    {
+
+      /**
+       * @name Push forward operations
+       */
+//@{
+
+      /**
+      * Returns the result of the push forward transformation on a
+      * contravariant vector, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F}^{-1} \; \chi\left(\bullet\right)^{\sharp}
+      *    := \frac{1}{\textrm{det} \mathbf{F}} \; \mathbf{F} \cdot \left(\bullet\right)^{\sharp}
+      * @f]
+      *
+      * @param[in] V The (referential) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\frac{1}{\textrm{det} \mathbf{F}} \; \chi\left( \mathbf{V} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      push_forward (const Tensor<1,dim,Number> &V,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * contravariant tensor, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F}^{-1} \; \chi\left(\bullet\right)^{\sharp}
+      *    := \frac{1}{\textrm{det} \mathbf{F}} \; \mathbf{F} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{T}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\frac{1}{\textrm{det} \mathbf{F}} \; \chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      push_forward (const Tensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-2
+      * contravariant symmetric tensor, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F}^{-1} \; \chi\left(\bullet\right)^{\sharp}
+      *    := \frac{1}{\textrm{det} \mathbf{F}} \; \mathbf{F} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{T}
+      * @f]
+      *
+      * @param[in] T The (referential) rank-2 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\frac{1}{\textrm{det} \mathbf{F}} \; \chi\left( \mathbf{T} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      push_forward (const SymmetricTensor<2,dim,Number> &T,
+                    const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * contravariant tensor, i.e. (in index notation)
+      * @f[
+      *  \textrm{det} \mathbf{F}^{-1} \; \left[ \chi\left(\bullet\right)^{\sharp} \right]_{ijkl}
+      *    := \frac{1}{\textrm{det} \mathbf{F}} \; F_{iI} F_{jJ} \left(\bullet\right)^{\sharp}_{IJKL} F_{kK} F_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\frac{1}{\textrm{det} \mathbf{F}} \; \chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      push_forward (const Tensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the push forward transformation on a rank-4
+      * contravariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \textrm{det} \mathbf{F}^{-1} \; \left[ \chi\left(\bullet\right)^{\sharp} \right]_{ijkl}
+      *    := \frac{1}{\textrm{det} \mathbf{F}} \; F_{iI} F_{jJ} \left(\bullet\right)^{\sharp}_{IJKL} F_{kK} F_{lL}
+      * @f]
+      *
+      * @param[in] H The (referential) rank-4 symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\frac{1}{\textrm{det} \mathbf{F}} \; \chi\left( \mathbf{H} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      push_forward (const SymmetricTensor<4,dim,Number> &H,
+                    const Tensor<2,dim,Number>          &F);
+
+//@}
+
+      /**
+       * @name Pull back operations
+       */
+//@{
+
+      /**
+      * Returns the result of the pull back transformation on a
+      * contravariant vector, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F} \; \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \textrm{det} \mathbf{F} \; \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp}
+      * @f]
+      *
+      * @param[in] v The (spatial) vector to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\textrm{det} \mathbf{F} \; \chi^{-1}\left( \mathbf{v} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<1,dim,Number>
+      pull_back (const Tensor<1,dim,Number> &v,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * contravariant tensor, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F} \; \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \textrm{det} \mathbf{F} \; \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{-T}
+      * @f]
+      *
+      * @param[in] t The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\textrm{det} \mathbf{F} \; \chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<2,dim,Number>
+      pull_back (const Tensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-2
+      * contravariant symmetric tensor, i.e.
+      * @f[
+      *  \textrm{det} \mathbf{F} \; \chi^{-1}\left(\bullet\right)^{\sharp}
+      *    := \textrm{det} \mathbf{F} \; \mathbf{F}^{-1} \cdot \left(\bullet\right)^{\sharp} \cdot \mathbf{F}^{-T}
+      * @f]
+      *
+      * @param[in] t The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\textrm{det} \mathbf{F} \; \chi^{-1}\left( \mathbf{t} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<2,dim,Number>
+      pull_back (const SymmetricTensor<2,dim,Number> &t,
+                 const Tensor<2,dim,Number>          &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant tensor, i.e. (in index notation)
+      * @f[
+      *  \textrm{det} \mathbf{F} \; \left[ \chi^{-1}\left(\bullet\right)^{\sharp} \right]_{IJKL}
+      *    := \textrm{det} \mathbf{F} \; F^{-1}_{Ii} F^{-1}_{Jj} \left(\bullet\right)^{\sharp}_{ijkl} F^{-1}_{Kk} F^{-1}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\textrm{det} \mathbf{F} \; \chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      Tensor<4,dim,Number>
+      pull_back (const Tensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number> &F);
+
+      /**
+      * Returns the result of the pull back transformation on a rank-4
+      * contravariant symmetric tensor, i.e. (in index notation)
+      * @f[
+      *  \textrm{det} \mathbf{F} \; \left[ \chi^{-1}\left(\bullet\right)^{\sharp} \right]_{IJKL}
+      *    := \textrm{det} \mathbf{F} \; F^{-1}_{Ii} F^{-1}_{Jj} \left(\bullet\right)^{\sharp}_{ijkl} F^{-1}_{Kk} F^{-1}_{Ll}
+      * @f]
+      *
+      * @param[in] h The (spatial) symmetric tensor to be operated on
+      * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+      * @return      $\textrm{det} \mathbf{F} \; \chi^{-1}\left( \mathbf{h} \right)$
+      */
+      template <int dim, typename Number>
+      SymmetricTensor<4,dim,Number>
+      pull_back (const SymmetricTensor<4,dim,Number> &h,
+                 const Tensor<2,dim,Number>          &F);
+
+//@}
+    }
+
+    /**
+     * @name Special operations
+     */
+//@{
+
+    /**
+    * Returns the result of applying Nanson's formula for the transformation of
+    * the material surface area element $d\mathbf{A}$ to the current surfaces
+    * area element $d\mathbf{a}$ under the nonlinear transformation map
+    * $\mathbf{x} = \boldsymbol{\varphi} \left( \mathbf{X} \right)$.
+    *
+    * The returned result is the spatial normal scaled by the ratio of areas
+    * between the reference and spatial surface elements, i.e.
+    * @f[
+    *  \mathbf{n} \frac{da}{dA}
+    *    := \textrm{det} \mathbf{F} \, \mathbf{F}^{-T} \cdot \mathbf{N}
+    *     = \textrm{cof} \mathbf{F} \cdot \mathbf{N} \, .
+    * @f]
+    *
+    * @param[in] N The referential normal unit vector $\mathbf{N}$
+    * @param[in] F The deformation gradient tensor $\mathbf{F} \left( \mathbf{X} \right)$
+    * @return      The scaled spatial normal vector $\mathbf{n} \frac{da}{dA}$
+    *
+    * @dealiiHolzapfelA{75,2.55}
+    * @dealiiWriggersA{23,3.11}
+    */
+    template<int dim, typename Number>
+    Tensor<1,dim,Number>
+    nansons_formula (const Tensor<1,dim,Number> &N,
+                     const Tensor<2,dim,Number> &F);
+
+//@}
+  };
+
+}
+
+
+
+#ifndef DOXYGEN
+
+// ------------------------- inline functions ------------------------
+
+namespace internal
+{
+  namespace Physics
+  {
+    namespace
+    {
+      template <int dim, typename Number>
+      inline
+      Tensor<1,dim,Number>
+      transformation_contraction (const Tensor<1,dim,Number> &V,
+                                  const Tensor<2,dim,Number> &F)
+      {
+        return contract<1,0>(F, V);
+      }
+
+
+
+      template <int dim, typename Number>
+      inline
+      Tensor<2,dim,Number>
+      transformation_contraction (const Tensor<2,dim,Number> &T,
+                                  const Tensor<2,dim,Number> &F)
+      {
+        return contract<1,1>(F,contract<1,0>(F, T));
+      }
+
+
+
+      template <int dim, typename Number>
+      inline
+      dealii::SymmetricTensor<2,dim,Number>
+      transformation_contraction (const dealii::SymmetricTensor<2,dim,Number> &T,
+                                  const Tensor<2,dim,Number>                  &F)
+      {
+        Tensor<2,dim,Number> tmp_1;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int J=0; J<dim; ++J)
+            for (unsigned int I=0; I<dim; ++I)
+              tmp_1[i][J] += F[i][I] * T[I][J];
+
+        dealii::SymmetricTensor<2,dim,Number> out;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int j=i; j<dim; ++j)
+            for (unsigned int J=0; J<dim; ++J)
+              out[i][j] += F[j][J] * tmp_1[i][J];
+
+        return out;
+      }
+
+
+
+      template <int dim, typename Number>
+      inline
+      Tensor<4,dim,Number>
+      transformation_contraction (const Tensor<4,dim,Number> &H,
+                                  const Tensor<2,dim,Number> &F)
+      {
+        // Its significantly quicker (in 3d) to push forward
+        // each index individually
+        return contract<1,3>(F,contract<1,2>(F,contract<1,1>(F,contract<1,0>(F, H))));
+      }
+
+
+
+      template <int dim, typename Number>
+      inline
+      dealii::SymmetricTensor<4,dim,Number>
+      transformation_contraction (const dealii::SymmetricTensor<4,dim,Number> &H,
+                                  const Tensor<2,dim,Number>                  &F)
+      {
+        // Its significantly quicker (in 3d) to push forward
+        // each index individually
+
+        Tensor<4,dim,Number> tmp_1;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int J=0; J<dim; ++J)
+            for (unsigned int K=0; K<dim; ++K)
+              for (unsigned int L=0; L<dim; ++L)
+                for (unsigned int I=0; I<dim; ++I)
+                  tmp_1[i][J][K][L] += F[i][I] * H[I][J][K][L];
+
+        Tensor<4,dim,Number> tmp_2;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int j=0; j<dim; ++j)
+            for (unsigned int K=0; K<dim; ++K)
+              for (unsigned int L=0; L<dim; ++L)
+                for (unsigned int J=0; J<dim; ++J)
+                  tmp_2[i][j][K][L] += F[j][J] * tmp_1[i][J][K][L];
+
+        tmp_1 = 0.0;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int j=0; j<dim; ++j)
+            for (unsigned int k=0; k<dim; ++k)
+              for (unsigned int L=0; L<dim; ++L)
+                for (unsigned int K=0; K<dim; ++K)
+                  tmp_1[i][j][k][L] += F[k][K] * tmp_2[i][j][K][L];
+
+        dealii::SymmetricTensor<4,dim,Number> out;
+        for (unsigned int i=0; i<dim; ++i)
+          for (unsigned int j=i; j<dim; ++j)
+            for (unsigned int k=0; k<dim; ++k)
+              for (unsigned int l=k; l<dim; ++l)
+                for (unsigned int L=0; L<dim; ++L)
+                  out[i][j][k][l] += F[l][L] * tmp_1[i][j][k][L];
+
+        return out;
+      }
+    }
+  }
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Contravariant::push_forward (const Tensor<1,dim,Number> &V,
+                                                       const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(V,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Contravariant::push_forward (const Tensor<2,dim,Number> &T,
+                                                       const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(T,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Contravariant::push_forward (const SymmetricTensor<2,dim,Number> &T,
+                                                       const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(T,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Contravariant::push_forward (const Tensor<4,dim,Number> &H,
+                                                       const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(H,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Contravariant::push_forward (const SymmetricTensor<4,dim,Number> &H,
+                                                       const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(H,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Contravariant::pull_back (const Tensor<1,dim,Number> &v,
+                                                    const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(v,invert(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Contravariant::pull_back (const Tensor<2,dim,Number> &t,
+                                                    const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(t,invert(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Contravariant::pull_back (const SymmetricTensor<2,dim,Number> &t,
+                                                    const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(t,invert(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Contravariant::pull_back (const Tensor<4,dim,Number> &h,
+                                                    const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(h,invert(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Contravariant::pull_back (const SymmetricTensor<4,dim,Number> &h,
+                                                    const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(h,invert(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Covariant::push_forward (const Tensor<1,dim,Number> &V,
+                                                   const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(V,transpose(invert(F)));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Covariant::push_forward (const Tensor<2,dim,Number> &T,
+                                                   const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(T,transpose(invert(F)));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Covariant::push_forward (const SymmetricTensor<2,dim,Number> &T,
+                                                   const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(T,transpose(invert(F)));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Covariant::push_forward (const Tensor<4,dim,Number> &H,
+                                                   const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(H,transpose(invert(F)));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Covariant::push_forward (const SymmetricTensor<4,dim,Number> &H,
+                                                   const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(H,transpose(invert(F)));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Covariant::pull_back (const Tensor<1,dim,Number> &v,
+                                                const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(v,transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Covariant::pull_back (const Tensor<2,dim,Number> &t,
+                                                const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(t,transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Covariant::pull_back (const SymmetricTensor<2,dim,Number> &t,
+                                                const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(t,transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Covariant::pull_back (const Tensor<4,dim,Number> &h,
+                                                const Tensor<2,dim,Number> &F)
+{
+  return internal::Physics::transformation_contraction(h,transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Covariant::pull_back (const SymmetricTensor<4,dim,Number> &h,
+                                                const Tensor<2,dim,Number>          &F)
+{
+  return internal::Physics::transformation_contraction(h,transpose(F));
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Piola::push_forward (const Tensor<1,dim,Number> &V,
+                                               const Tensor<2,dim,Number> &F)
+{
+  return Number(1.0/determinant(F))*Contravariant::push_forward(V,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Piola::push_forward (const Tensor<2,dim,Number> &T,
+                                               const Tensor<2,dim,Number> &F)
+{
+  return Number(1.0/determinant(F))*Contravariant::push_forward(T,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Piola::push_forward (const SymmetricTensor<2,dim,Number> &T,
+                                               const Tensor<2,dim,Number>          &F)
+{
+  return Number(1.0/determinant(F))*Contravariant::push_forward(T,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Piola::push_forward (const Tensor<4,dim,Number> &H,
+                                               const Tensor<2,dim,Number> &F)
+{
+  return Number(1.0/determinant(F))*Contravariant::push_forward(H,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Piola::push_forward (const SymmetricTensor<4,dim,Number> &H,
+                                               const Tensor<2,dim,Number>          &F)
+{
+  return Number(1.0/determinant(F))*Contravariant::push_forward(H,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<1,dim,Number>
+Physics::Transformations::Piola::pull_back (const Tensor<1,dim,Number> &v,
+                                            const Tensor<2,dim,Number> &F)
+{
+  return Number(determinant(F))*Contravariant::pull_back(v,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<2,dim,Number>
+Physics::Transformations::Piola::pull_back (const Tensor<2,dim,Number> &t,
+                                            const Tensor<2,dim,Number> &F)
+{
+  return Number(determinant(F))*Contravariant::pull_back(t,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<2,dim,Number>
+Physics::Transformations::Piola::pull_back (const SymmetricTensor<2,dim,Number> &t,
+                                            const Tensor<2,dim,Number>          &F)
+{
+  return Number(determinant(F))*Contravariant::pull_back(t,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+Tensor<4,dim,Number>
+Physics::Transformations::Piola::pull_back (const Tensor<4,dim,Number> &h,
+                                            const Tensor<2,dim,Number> &F)
+{
+  return Number(determinant(F))*Contravariant::pull_back(h,F);
+}
+
+
+
+template <int dim, typename Number>
+inline
+SymmetricTensor<4,dim,Number>
+Physics::Transformations::Piola::pull_back (const SymmetricTensor<4,dim,Number> &h,
+                                            const Tensor<2,dim,Number>          &F)
+{
+  return Number(determinant(F))*Contravariant::pull_back(h,F);
+}
+
+
+
+template<int dim, typename Number>
+inline Tensor<1,dim,Number>
+Physics::Transformations::nansons_formula (const Tensor<1,dim,Number> &N,
+                                           const Tensor<2,dim,Number> &F)
+{
+  return cofactor(F)*N;
+}
+
+#endif // DOXYGEN
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
index 4d3e8f7b4fd90281125145a93809e50a70fd3384..b78b0a0f1bcc030cf4cf744d10ec6fcc4edc2de8 100644 (file)
@@ -48,6 +48,7 @@ ADD_SUBDIRECTORY(integrators)
 ADD_SUBDIRECTORY(matrix_free)
 ADD_SUBDIRECTORY(meshworker)
 ADD_SUBDIRECTORY(opencascade)
+ADD_SUBDIRECTORY(physics)
 
 FOREACH(build ${DEAL_II_BUILD_TYPES})
   STRING(TOLOWER ${build} build_lowercase)
diff --git a/source/physics/CMakeLists.txt b/source/physics/CMakeLists.txt
new file mode 100644 (file)
index 0000000..392846d
--- /dev/null
@@ -0,0 +1,34 @@
+## ---------------------------------------------------------------------
+##
+## Copyright (C) 2016 by the deal.II authors
+##
+## This file is part of the deal.II library.
+##
+## The deal.II library is free software; you can use it, redistribute
+## it, and/or modify it under the terms of the GNU Lesser General
+## Public License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+## The full text of the license can be found in the file LICENSE at
+## the top level of the deal.II distribution.
+##
+## ---------------------------------------------------------------------
+
+# Expand instantiations in subdirectories
+ADD_SUBDIRECTORY("elasticity")
+
+INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
+
+SET(_src
+  transformations.cc
+  )
+
+SET(_inst
+  transformations.inst.in
+  )
+
+FILE(GLOB _header
+  ${CMAKE_SOURCE_DIR}/include/deal.II/physics/*.h
+  )
+
+DEAL_II_ADD_LIBRARY(obj_physics OBJECT ${_src} ${_header} ${_inst})
+EXPAND_INSTANTIATIONS(obj_physics "${_inst}")
diff --git a/source/physics/elasticity/CMakeLists.txt b/source/physics/elasticity/CMakeLists.txt
new file mode 100644 (file)
index 0000000..62d884a
--- /dev/null
@@ -0,0 +1,34 @@
+## ---------------------------------------------------------------------
+##
+## Copyright (C) 2016 by the deal.II authors
+##
+## This file is part of the deal.II library.
+##
+## The deal.II library is free software; you can use it, redistribute
+## it, and/or modify it under the terms of the GNU Lesser General
+## Public License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+## The full text of the license can be found in the file LICENSE at
+## the top level of the deal.II distribution.
+##
+## ---------------------------------------------------------------------
+
+
+INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
+
+SET(_src
+  kinematics.cc
+  standard_tensors.cc
+  )
+
+SET(_inst
+  kinematics.inst.in
+  standard_tensors.inst.in
+  )
+
+FILE(GLOB _header
+  ${CMAKE_SOURCE_DIR}/include/deal.II/physics/elasticity/*.h
+  )
+
+DEAL_II_ADD_LIBRARY(obj_physics_elasticity OBJECT ${_src} ${_header} ${_inst})
+EXPAND_INSTANTIATIONS(obj_physics_elasticity "${_inst}")
diff --git a/source/physics/elasticity/kinematics.cc b/source/physics/elasticity/kinematics.cc
new file mode 100644 (file)
index 0000000..c3c6fc0
--- /dev/null
@@ -0,0 +1,23 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#include <deal.II/physics/elasticity/kinematics.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+// explicit instantiations
+#include "kinematics.inst"
+
+DEAL_II_NAMESPACE_CLOSE
diff --git a/source/physics/elasticity/kinematics.inst.in b/source/physics/elasticity/kinematics.inst.in
new file mode 100644 (file)
index 0000000..8c34b48
--- /dev/null
@@ -0,0 +1,78 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+for (deal_II_dimension : DIMENSIONS; number : REAL_SCALARS)
+{
+
+    namespace Physics
+    \{
+    namespace Elasticity
+    \{
+    namespace Kinematics
+    \{
+    template
+    Tensor<2,deal_II_dimension,number>
+    F<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    F_iso<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    F_vol<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    C<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    b<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    E<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    epsilon<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    e<deal_II_dimension,number>(const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    l<deal_II_dimension,number>(
+        const Tensor<2,deal_II_dimension,number>&,
+        const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    d<deal_II_dimension,number>(
+        const Tensor<2,deal_II_dimension,number>&,
+        const Tensor<2,deal_II_dimension,number>&);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    w<deal_II_dimension,number>(
+        const Tensor<2,deal_II_dimension,number>&,
+        const Tensor<2,deal_II_dimension,number>&);
+    \}
+    \}
+    \}
+}
diff --git a/source/physics/elasticity/standard_tensors.cc b/source/physics/elasticity/standard_tensors.cc
new file mode 100644 (file)
index 0000000..02db673
--- /dev/null
@@ -0,0 +1,49 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+
+template <int dim>
+const SymmetricTensor<2, dim>
+Physics::Elasticity::StandardTensors<dim>::I = unit_symmetric_tensor<dim>();
+
+
+
+template <int dim>
+const SymmetricTensor<4, dim>
+Physics::Elasticity::StandardTensors<dim>::S = identity_tensor<dim>();
+
+
+
+template <int dim>
+const SymmetricTensor<4, dim>
+Physics::Elasticity::StandardTensors<dim>::IxI = outer_product(unit_symmetric_tensor<dim>(),
+                                                 unit_symmetric_tensor<dim>());
+
+
+
+template <int dim>
+const SymmetricTensor<4, dim>
+Physics::Elasticity::StandardTensors<dim>::dev_P = deviator_tensor<dim>();
+
+
+// explicit instantiations
+#include "standard_tensors.inst"
+
+DEAL_II_NAMESPACE_CLOSE
diff --git a/source/physics/elasticity/standard_tensors.inst.in b/source/physics/elasticity/standard_tensors.inst.in
new file mode 100644 (file)
index 0000000..fd415e2
--- /dev/null
@@ -0,0 +1,53 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+for (deal_II_dimension : DIMENSIONS)
+{
+    namespace Physics
+    \{
+    namespace Elasticity
+    \{
+    template
+    class StandardTensors<deal_II_dimension>;
+    \}
+    \}
+}
+
+
+for (deal_II_dimension : DIMENSIONS; number : REAL_SCALARS)
+{
+    namespace Physics
+    \{
+    namespace Elasticity
+    \{
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    StandardTensors<deal_II_dimension>::Dev_P<number>(const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    StandardTensors<deal_II_dimension>::Dev_P_T<number>(const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    StandardTensors<deal_II_dimension>::ddet_F_dC<number>(const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    StandardTensors<deal_II_dimension>::dC_inv_dC<number>(const Tensor<2,deal_II_dimension,number> &);
+    \}
+    \}
+}
diff --git a/source/physics/transformations.cc b/source/physics/transformations.cc
new file mode 100644 (file)
index 0000000..cf5fe4e
--- /dev/null
@@ -0,0 +1,23 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+#include <deal.II/physics/transformations.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+// explicit instantiations
+#include "transformations.inst"
+
+DEAL_II_NAMESPACE_CLOSE
diff --git a/source/physics/transformations.inst.in b/source/physics/transformations.inst.in
new file mode 100644 (file)
index 0000000..be63264
--- /dev/null
@@ -0,0 +1,221 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+for (deal_II_dimension : DIMENSIONS; number : REAL_SCALARS)
+{
+
+    namespace Physics
+    \{
+    namespace Transformations
+    \{
+    template
+    Tensor<1,deal_II_dimension,number>
+    nansons_formula (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    namespace Contravariant
+    \{
+    template
+    Tensor<1,deal_II_dimension,number>
+    push_forward (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    push_forward (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    push_forward (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<1,deal_II_dimension,number>
+    pull_back (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    pull_back (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    pull_back (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+    \}
+
+    namespace Covariant
+    \{
+    template
+    Tensor<1,deal_II_dimension,number>
+    push_forward (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    push_forward (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    push_forward (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<1,deal_II_dimension,number>
+    pull_back (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    pull_back (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    pull_back (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+    \}
+
+    namespace Piola
+    \{
+    template
+    Tensor<1,deal_II_dimension,number>
+    push_forward (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    push_forward (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    push_forward (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    push_forward (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<1,deal_II_dimension,number>
+    pull_back (
+        const Tensor<1,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<2,deal_II_dimension,number>
+    pull_back (
+        const Tensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<2,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<2,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    Tensor<4,deal_II_dimension,number>
+    pull_back (
+        const Tensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+
+    template
+    SymmetricTensor<4,deal_II_dimension,number>
+    pull_back (
+        const SymmetricTensor<4,deal_II_dimension,number> &,
+        const Tensor<2,deal_II_dimension,number> &);
+    \}
+
+    \}
+    \}
+}
diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f8ac3eb
--- /dev/null
@@ -0,0 +1,4 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9)
+INCLUDE(../setup_testsubproject.cmake)
+PROJECT(testsuite CXX)
+DEAL_II_PICKUP_TESTS()
diff --git a/tests/physics/elasticity-kinematics_01.cc b/tests/physics/elasticity-kinematics_01.cc
new file mode 100644 (file)
index 0000000..1b79215
--- /dev/null
@@ -0,0 +1,190 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+// test kinematic tensor definitions
+
+#include "../tests.h"
+#include <deal.II/base/logstream.h>
+
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_accessor.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/fe_values_extractors.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_accessor.h>
+#include <deal.II/grid/tria_iterator.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/lac/vector.h>
+
+#include <deal.II/physics/elasticity/kinematics.h>
+#include <deal.II/physics/transformations.h>
+
+#include <fstream>
+#include <iomanip>
+
+using namespace dealii;
+using namespace dealii::Physics;
+using namespace dealii::Physics::Elasticity;
+
+template<int dim>
+void
+test_kinematic_tensors ()
+{
+  const FESystem<dim> fe (FE_Q<dim>(1), dim);
+  const QGauss<dim> qf (2);
+  Triangulation<dim> tria;
+  GridGenerator::hyper_cube(tria, -1, 1);
+  DoFHandler<dim> dof_handler (tria);
+  dof_handler.distribute_dofs(fe);
+
+  Vector<double> soln_t (dof_handler.n_dofs());
+  Vector<double> soln_t1 (dof_handler.n_dofs());
+
+  for (typename DoFHandler<dim>::active_cell_iterator
+       cell = dof_handler.begin_active();
+       cell != dof_handler.end(); ++cell)
+    {
+      for (unsigned int v=0; v<GeometryInfo<dim>::vertices_per_cell; ++v)
+        if (std::abs(cell->vertex(v)[0] - 1.0) < 1e-9)
+          soln_t[cell->vertex_dof_index(v,0)] = 1.0;
+    }
+
+  const double delta_t = 2.0;
+  Vector<double> dot_soln_t = soln_t;
+  dot_soln_t -= soln_t1;
+  dot_soln_t *= (1.0/delta_t);
+
+  FEValuesExtractors::Vector u_fe (0);
+  std::vector< Tensor<2,dim> > qp_Grad_u_t;
+  std::vector< Tensor<2,dim> > qp_Grad_u_t1;
+  std::vector< Tensor<2,dim> > qp_dot_Grad_u_t;
+  std::vector< Tensor<2,dim> > qp_dot_grad_u_t;
+
+  FEValues<dim> fe_values (fe, qf, update_gradients);
+  MappingQEulerian<dim> q1_mapping(1, dof_handler, soln_t);
+  FEValues<dim> fe_values_mapped (q1_mapping, fe, qf, update_gradients);
+
+  for (typename DoFHandler<dim>::active_cell_iterator
+       cell = dof_handler.begin_active();
+       cell != dof_handler.end(); ++cell)
+    {
+      fe_values.reinit(cell);
+      fe_values_mapped.reinit(cell);
+
+      const unsigned int n_q_points = fe_values.get_quadrature().size();
+      qp_Grad_u_t.resize(n_q_points);
+      qp_Grad_u_t1.resize(n_q_points);
+      qp_dot_Grad_u_t.resize(n_q_points);
+      qp_dot_grad_u_t.resize(n_q_points);
+
+      fe_values[u_fe].get_function_gradients(soln_t, qp_Grad_u_t);
+      fe_values[u_fe].get_function_gradients(soln_t1, qp_Grad_u_t1);
+      fe_values[u_fe].get_function_gradients(dot_soln_t, qp_dot_Grad_u_t);
+
+      fe_values_mapped[u_fe].get_function_gradients(dot_soln_t, qp_dot_grad_u_t);
+
+      for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+        {
+          static const double tol = 1e-12;
+
+          // Material gradients
+          const Tensor<2,dim> &Grad_u = qp_Grad_u_t[q_point];
+          const Tensor<2,dim> &Grad_u_t1 = qp_Grad_u_t1[q_point];
+
+          // --- Rate independent ---
+
+          // Deformation gradient tensor
+          const Tensor<2,dim> F_t1 = Kinematics::F(Grad_u_t1);
+          Assert((F_t1 - unit_symmetric_tensor<dim>()).norm() < tol,
+                 ExcMessage("Incorrect computation of F_t1"));
+          const Tensor<2,dim> F = Kinematics::F(Grad_u);
+          Assert((F - (static_cast< Tensor<2,dim> >(unit_symmetric_tensor<dim>()) + Grad_u)).norm() < tol,
+                 ExcMessage("Incorrect computation of F"));
+
+          // Volumetric / isochoric split of deformation gradient
+          Assert(determinant(F) != 1.0,
+                 ExcMessage("No volume change - cannot test vol/iso split"));
+          Assert(std::abs(determinant(Kinematics::F_iso(F)) - 1.0) < tol,
+                 ExcMessage("F_iso is not volume preserving"));
+          Assert(std::abs(determinant(Kinematics::F_vol(F)) - determinant(F)) < tol,
+                 ExcMessage("F_vol has no dilatating action"));
+
+          // Right Cauchy-Green tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::C(F)) - transpose(F)*F).norm() < tol,
+                 ExcMessage("Incorrect computation of C"));
+
+          // Left Cauchy-Green tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::b(F)) - F*transpose(F)).norm() < tol,
+                 ExcMessage("Incorrect computation of b"));
+
+          // Small strain tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::epsilon(Grad_u)) - 0.5*(Grad_u + transpose(Grad_u))).norm() < tol,
+                 ExcMessage("Incorrect computation of epsilon"));
+
+          // Green-Lagrange strain tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::E(F)) - 0.5*(Grad_u + transpose(Grad_u) + transpose(Grad_u)*Grad_u)).norm() < tol,
+                 ExcMessage("Incorrect computation of E"));
+
+          // Almansi strain tensor
+          // Holzapfel 2.82
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::e(F)) - transpose(invert(F))*static_cast< Tensor<2,dim> >(Kinematics::E(F))*invert(F)).norm() < tol,
+                 ExcMessage("Incorrect computation of e"));
+
+          // --- Rate dependent ---
+
+          // Material rates
+          const Tensor<2,dim> &F_dot = qp_dot_Grad_u_t[q_point];
+
+          // Material rate of deformation gradient tensor
+          Assert((F_dot - (1.0/delta_t)*(Grad_u-Grad_u_t1)).norm() < tol,
+                 ExcMessage("Incorrect computation of F_dot"));
+
+          // Spatial gradients
+          const Tensor<2,dim> &dot_grad_u = qp_dot_grad_u_t[q_point];
+
+          // Spatial velocity gradient
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::l(F,F_dot)) - dot_grad_u).norm() < tol,
+                 ExcMessage("Incorrect computation of l"));
+
+          // Rate of deformation tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::d(F,F_dot)) - 0.5*(dot_grad_u + transpose(dot_grad_u))).norm() < tol,
+                 ExcMessage("Incorrect computation of d"));
+
+          // Rate of spin tensor
+          Assert((static_cast< Tensor<2,dim> >(Kinematics::w(F,F_dot)) - 0.5*(dot_grad_u - transpose(dot_grad_u))).norm() < tol,
+                 ExcMessage("Incorrect computation of w"));
+        }
+    }
+}
+
+int main ()
+{
+  std::ofstream logfile("output");
+  deallog << std::setprecision(3);
+  deallog.attach(logfile);
+  deallog.threshold_double(1.e-10);
+
+  test_kinematic_tensors<2> ();
+  test_kinematic_tensors<3> ();
+
+  deallog << "OK" << std::endl;
+}
diff --git a/tests/physics/elasticity-kinematics_01.output b/tests/physics/elasticity-kinematics_01.output
new file mode 100644 (file)
index 0000000..0fd8fc1
--- /dev/null
@@ -0,0 +1,2 @@
+
+DEAL::OK
diff --git a/tests/physics/elasticity-standard_tensors_01.cc b/tests/physics/elasticity-standard_tensors_01.cc
new file mode 100644 (file)
index 0000000..2e9b08c
--- /dev/null
@@ -0,0 +1,140 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+// test standard tensor definitions
+
+#include "../tests.h"
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+#include <fstream>
+#include <iomanip>
+
+using namespace dealii::Physics::Elasticity;
+
+const double c10 = 10.0;
+const double c01 = 20.0;
+
+// Consider a Mooney-Rivlin material:
+// psi = c10.(I1 - dim) + c01.(I2 - dim)
+// where I1 = I1(C), I2 = I2(C)
+// Then dI1/dC = I and dI2/dC = I1.I - C
+// and S = 2 dpsi/dC
+template<int dim>
+SymmetricTensor<2,dim>
+get_S (const Tensor<2,dim> &F)
+{
+  const SymmetricTensor<2,dim> C = symmetrize(transpose(F)*F);
+  const double I1 = first_invariant(C);
+  return 2.0*c10*StandardTensors<dim>::I
+         + 2.0*c01*(I1*StandardTensors<dim>::I - C);
+}
+// For isotropic media, tau = 2.b.dpsi/db == 2.dpsi/db . b
+// where b = F.F^{T}, I1(b) == I1(C) and
+//  I2(b) == I2(C)
+template<int dim>
+SymmetricTensor<2,dim>
+get_tau (const Tensor<2,dim> &F)
+{
+  const SymmetricTensor<2,dim> b = symmetrize(F*transpose(F));
+  const double I1 = first_invariant(b);
+  const SymmetricTensor<2,dim>
+  tmp = 2.0*c10*StandardTensors<dim>::I
+        + 2.0*c01*(I1*StandardTensors<dim>::I - b);
+  return symmetrize(static_cast< Tensor<2,dim> >(tmp)*static_cast< Tensor<2,dim> >(b));
+}
+
+template<int dim>
+void
+test_standard_tensors ()
+{
+  SymmetricTensor<2,dim> t;
+  for (unsigned int i=0; i<dim; ++i)
+    for (unsigned int j=i; j<dim; ++j)
+      t[i][j] = dim*i+j+1.0;
+
+  // Check second-order identity tensor I:
+  AssertThrow (std::fabs (StandardTensors<dim>::I*t - trace(t)) < 1e-14,
+               ExcInternalError());
+  AssertThrow (std::fabs (t*StandardTensors<dim>::I - trace(t)) < 1e-14,
+               ExcInternalError());
+
+  // Check fourth-order identity tensor II:
+  AssertThrow (std::fabs ((StandardTensors<dim>::S*t - t).norm()) < 1e-14,
+               ExcInternalError());
+  AssertThrow (std::fabs ((t*StandardTensors<dim>::S - t).norm()) < 1e-14,
+               ExcInternalError());
+
+  // Check fourth-order tensor IxI:
+  AssertThrow (std::fabs ((StandardTensors<dim>::IxI*t - trace(t)*unit_symmetric_tensor<dim>()).norm()) < 1e-14,
+               ExcInternalError());
+  AssertThrow (std::fabs ((t*StandardTensors<dim>::IxI - trace(t)*unit_symmetric_tensor<dim>()).norm()) < 1e-14,
+               ExcInternalError());
+
+  // Check spatial deviatoric tensor dev_P:
+  AssertThrow (std::fabs ((StandardTensors<dim>::dev_P*t - (t - (trace(t)/dim)*unit_symmetric_tensor<dim>())).norm()) < 1e-14,
+               ExcInternalError());
+  AssertThrow (std::fabs ((t*StandardTensors<dim>::dev_P - (t - (trace(t)/dim)*unit_symmetric_tensor<dim>())).norm()) < 1e-14,
+               ExcInternalError());
+
+  // Check referential deviatoric tensor Dev_P:
+  Tensor<2,dim> F (unit_symmetric_tensor<dim>());
+  F[0][1] = 0.5;
+  F[1][0] = 0.25;
+  const Tensor<2,dim> F_inv = invert(F);
+
+  // Pull-back a fictitious stress tensor, project it onto a deviatoric space, and
+  // then push it forward again
+  // tau = F.S.F^{T} --> S = F^{-1}*tau*F^{-T}
+  const SymmetricTensor<2,dim> s = symmetrize(F_inv*static_cast< Tensor<2,dim> >(t)*transpose(F_inv));
+  const SymmetricTensor<2,dim> Dev_P_T_x_s = StandardTensors<dim>::Dev_P_T(F)*s;
+  const SymmetricTensor<2,dim> s_x_Dev_P = s*StandardTensors<dim>::Dev_P(F);
+
+  // Note: The extra factor J^{2/dim} arises due to the definition of Dev_P
+  //       including the factor J^{-2/dim}. Ultimately the stress definitions
+  //       for s,t do not align with those required to have the direct relationship
+  //       s*Dev_P == dev_P*t. For this we would need S = 2.dW/dC|_{C=\bar{C}} and
+  //       t = F.S.F^{T} and \bar{C} = det(F)^{-2/dim} F^{T}.F .
+  AssertThrow (std::fabs ((symmetrize(std::pow(determinant(F), 2.0/dim)*F*static_cast< Tensor<2,dim> >(s_x_Dev_P)*transpose(F)) - StandardTensors<dim>::dev_P*t).norm()) < 1e-14,
+               ExcInternalError());
+  AssertThrow (std::fabs ((symmetrize(std::pow(determinant(F), 2.0/dim)*F*static_cast< Tensor<2,dim> >(Dev_P_T_x_s)*transpose(F)) - StandardTensors<dim>::dev_P*t).norm()) < 1e-14,
+               ExcInternalError());
+
+  // Repeat the above exercise for a "real" material response
+  const Tensor<2,dim> F_bar = std::pow(determinant(F), -1.0/dim)*F;
+  const SymmetricTensor<2,dim> S_bar = get_S(F_bar);
+  const SymmetricTensor<2,dim> tau_bar = symmetrize(F_bar*static_cast< Tensor<2,dim> >(S_bar)*transpose(F_bar)); // Note: tau_bar = tau(F) |_{F = F_bar}
+  AssertThrow (std::fabs ((tau_bar - get_tau(F_bar)).norm()) < 1e-9,
+               ExcInternalError());
+  const SymmetricTensor<2,dim> S_iso = S_bar*StandardTensors<dim>::Dev_P(F);
+  const SymmetricTensor<2,dim> tau_iso = StandardTensors<dim>::dev_P*tau_bar;
+  AssertThrow (std::fabs ((symmetrize(F*static_cast< Tensor<2,dim> >(S_iso)*transpose(F)) - tau_iso).norm()) < 1e-9,
+               ExcInternalError());
+}
+
+int main ()
+{
+  std::ofstream logfile("output");
+  deallog << std::setprecision(3);
+  deallog.attach(logfile);
+  deallog.threshold_double(1.e-10);
+
+  test_standard_tensors<2> ();
+  test_standard_tensors<3> ();
+
+  deallog << "OK" << std::endl;
+}
diff --git a/tests/physics/elasticity-standard_tensors_01.output b/tests/physics/elasticity-standard_tensors_01.output
new file mode 100644 (file)
index 0000000..0fd8fc1
--- /dev/null
@@ -0,0 +1,2 @@
+
+DEAL::OK
diff --git a/tests/physics/prm/parameters-step-44.prm b/tests/physics/prm/parameters-step-44.prm
new file mode 100644 (file)
index 0000000..d803fc8
--- /dev/null
@@ -0,0 +1,75 @@
+# Listing of Parameters
+# ---------------------
+subsection Finite element system
+  # Displacement system polynomial order
+  set Polynomial degree = 1
+
+  # Gauss quadrature order
+  set Quadrature order  = 2
+end
+
+
+subsection Geometry
+  # Global refinement level
+  set Global refinement   = 2
+
+  # Global grid scaling factor
+  set Grid scale          = 1e-3
+
+  # Ratio of applied pressure to reference pressure
+  set Pressure ratio p/p0 = 100
+end
+
+
+subsection Linear solver
+  # Linear solver iterations (multiples of the system matrix size)
+  # In 2-d, this value is best set at 2. In 3-d, a value of 1 work fine.
+  set Max iteration multiplier = 2
+
+  # Linear solver residual (scaled by residual norm)
+  set Residual                 = 1e-6
+
+  # Use static condensation and solve a 1-block system, or solve
+  # the full 3-block system using Linear Operators and the Schur
+  # complement
+  set Use static condensation = true
+
+  # Preconditioner type
+  set Preconditioner type  = ssor
+
+  # Preconditioner relaxation value
+  set Preconditioner relaxation  = 0.65
+
+  # Type of solver used to solve the linear system
+  set Solver type              = CG
+end
+
+
+subsection Material properties
+  # Poisson's ratio
+  set Poisson's ratio = 0.4999
+
+  # Shear modulus
+  set Shear modulus   = 80.194e6
+end
+
+
+subsection Nonlinear solver
+  # Number of Newton-Raphson iterations allowed
+  set Max iterations Newton-Raphson = 10
+
+  # Displacement error tolerance
+  set Tolerance displacement        = 1.0e-6
+
+  # Force residual tolerance
+  set Tolerance force               = 1.0e-9
+end
+
+
+subsection Time
+  # End time
+  set End time       = 1
+
+  # Time step size
+  set Time step size = 0.1
+end
diff --git a/tests/physics/step-44-standard_tensors-material_push_forward.cc b/tests/physics/step-44-standard_tensors-material_push_forward.cc
new file mode 100644 (file)
index 0000000..5fec894
--- /dev/null
@@ -0,0 +1,2085 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+// A version of step-44 that checks that some of the definitions for standard
+// tensors and kinematic quantities for elasticity are computed correctly
+// Here the material is defined using referential quantities which are then
+// pushed forward through the predefined transformations
+
+#include "../tests.h"
+
+#include <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/quadrature_point_data.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/precondition_selector.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/sparse_direct.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+#include <deal.II/lac/iterative_inverse.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <deal.II/physics/transformations.h>
+#include <deal.II/physics/elasticity/kinematics.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+
+#include <iostream>
+#include <fstream>
+namespace Step44
+{
+  using namespace dealii;
+  namespace Parameters
+  {
+    struct FESystem
+    {
+      unsigned int poly_degree;
+      unsigned int quad_order;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void FESystem::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        prm.declare_entry("Polynomial degree", "2",
+                          Patterns::Integer(0),
+                          "Displacement system polynomial order");
+        prm.declare_entry("Quadrature order", "3",
+                          Patterns::Integer(0),
+                          "Gauss quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    void FESystem::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        poly_degree = prm.get_integer("Polynomial degree");
+        quad_order = prm.get_integer("Quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    struct Geometry
+    {
+      unsigned int global_refinement;
+      double       scale;
+      double       p_p0;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Geometry::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        prm.declare_entry("Global refinement", "2",
+                          Patterns::Integer(0),
+                          "Global refinement level");
+        prm.declare_entry("Grid scale", "1e-3",
+                          Patterns::Double(0.0),
+                          "Global grid scaling factor");
+        prm.declare_entry("Pressure ratio p/p0", "100",
+                          Patterns::Selection("20|40|60|80|100"),
+                          "Ratio of applied pressure to reference pressure");
+      }
+      prm.leave_subsection();
+    }
+    void Geometry::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        global_refinement = prm.get_integer("Global refinement");
+        scale = prm.get_double("Grid scale");
+        p_p0 = prm.get_double("Pressure ratio p/p0");
+      }
+      prm.leave_subsection();
+    }
+    struct Materials
+    {
+      double nu;
+      double mu;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Materials::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        prm.declare_entry("Poisson's ratio", "0.4999",
+                          Patterns::Double(-1.0,0.5),
+                          "Poisson's ratio");
+        prm.declare_entry("Shear modulus", "80.194e6",
+                          Patterns::Double(),
+                          "Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    void Materials::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        nu = prm.get_double("Poisson's ratio");
+        mu = prm.get_double("Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    struct LinearSolver
+    {
+      std::string type_lin;
+      double      tol_lin;
+      double      max_iterations_lin;
+      bool        use_static_condensation;
+      std::string preconditioner_type;
+      double      preconditioner_relaxation;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void LinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        prm.declare_entry("Solver type", "CG",
+                          Patterns::Selection("CG|Direct"),
+                          "Type of solver used to solve the linear system");
+        prm.declare_entry("Residual", "1e-6",
+                          Patterns::Double(0.0),
+                          "Linear solver residual (scaled by residual norm)");
+        prm.declare_entry("Max iteration multiplier", "1",
+                          Patterns::Double(0.0),
+                          "Linear solver iterations (multiples of the system matrix size)");
+        prm.declare_entry("Use static condensation", "true",
+                          Patterns::Bool(),
+                          "Solve the full block system or a reduced problem");
+        prm.declare_entry("Preconditioner type", "ssor",
+                          Patterns::Selection("jacobi|ssor"),
+                          "Type of preconditioner");
+        prm.declare_entry("Preconditioner relaxation", "0.65",
+                          Patterns::Double(0.0),
+                          "Preconditioner relaxation value");
+      }
+      prm.leave_subsection();
+    }
+    void LinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        type_lin = prm.get("Solver type");
+        tol_lin = prm.get_double("Residual");
+        max_iterations_lin = prm.get_double("Max iteration multiplier");
+        use_static_condensation = prm.get_bool("Use static condensation");
+        preconditioner_type = prm.get("Preconditioner type");
+        preconditioner_relaxation = prm.get_double("Preconditioner relaxation");
+      }
+      prm.leave_subsection();
+    }
+    struct NonlinearSolver
+    {
+      unsigned int max_iterations_NR;
+      double       tol_f;
+      double       tol_u;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void NonlinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        prm.declare_entry("Max iterations Newton-Raphson", "10",
+                          Patterns::Integer(0),
+                          "Number of Newton-Raphson iterations allowed");
+        prm.declare_entry("Tolerance force", "1.0e-9",
+                          Patterns::Double(0.0),
+                          "Force residual tolerance");
+        prm.declare_entry("Tolerance displacement", "1.0e-6",
+                          Patterns::Double(0.0),
+                          "Displacement error tolerance");
+      }
+      prm.leave_subsection();
+    }
+    void NonlinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
+        tol_f = prm.get_double("Tolerance force");
+        tol_u = prm.get_double("Tolerance displacement");
+      }
+      prm.leave_subsection();
+    }
+    struct Time
+    {
+      double delta_t;
+      double end_time;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Time::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        prm.declare_entry("End time", "1",
+                          Patterns::Double(),
+                          "End time");
+        prm.declare_entry("Time step size", "0.1",
+                          Patterns::Double(),
+                          "Time step size");
+      }
+      prm.leave_subsection();
+    }
+    void Time::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        end_time = prm.get_double("End time");
+        delta_t = prm.get_double("Time step size");
+      }
+      prm.leave_subsection();
+    }
+    struct AllParameters : public FESystem,
+      public Geometry,
+      public Materials,
+      public LinearSolver,
+      public NonlinearSolver,
+      public Time
+    {
+      AllParameters(const std::string &input_file);
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    AllParameters::AllParameters(const std::string &input_file)
+    {
+      ParameterHandler prm;
+      declare_parameters(prm);
+      prm.read_input(input_file);
+      parse_parameters(prm);
+    }
+    void AllParameters::declare_parameters(ParameterHandler &prm)
+    {
+      FESystem::declare_parameters(prm);
+      Geometry::declare_parameters(prm);
+      Materials::declare_parameters(prm);
+      LinearSolver::declare_parameters(prm);
+      NonlinearSolver::declare_parameters(prm);
+      Time::declare_parameters(prm);
+    }
+    void AllParameters::parse_parameters(ParameterHandler &prm)
+    {
+      FESystem::parse_parameters(prm);
+      Geometry::parse_parameters(prm);
+      Materials::parse_parameters(prm);
+      LinearSolver::parse_parameters(prm);
+      NonlinearSolver::parse_parameters(prm);
+      Time::parse_parameters(prm);
+    }
+  }
+  class Time
+  {
+  public:
+    Time (const double time_end,
+          const double delta_t)
+      :
+      timestep(0),
+      time_current(0.0),
+      time_end(time_end),
+      delta_t(delta_t)
+    {}
+    virtual ~Time()
+    {}
+    double current() const
+    {
+      return time_current;
+    }
+    double end() const
+    {
+      return time_end;
+    }
+    double get_delta_t() const
+    {
+      return delta_t;
+    }
+    unsigned int get_timestep() const
+    {
+      return timestep;
+    }
+    void increment()
+    {
+      time_current += delta_t;
+      ++timestep;
+    }
+  private:
+    unsigned int timestep;
+    double       time_current;
+    const double time_end;
+    const double delta_t;
+  };
+  template <int dim>
+  class Material_Compressible_Neo_Hook_Three_Field
+  {
+  public:
+    Material_Compressible_Neo_Hook_Three_Field(const double mu,
+                                               const double nu)
+      :
+      kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))),
+      c_1(mu / 2.0),
+      p_tilde(0.0),
+      J_tilde(1.0),
+      det_F(1.0),
+      F(Physics::Elasticity::StandardTensors<dim>::I),
+      C_inv(Physics::Elasticity::StandardTensors<dim>::I)
+    {
+      Assert(kappa > 0, ExcInternalError());
+    }
+    ~Material_Compressible_Neo_Hook_Three_Field()
+    {}
+    void update_material_data(const Tensor<2, dim> &F_in,
+                              const double p_tilde_in,
+                              const double J_tilde_in)
+    {
+      F = F_in;
+      det_F = determinant(F);
+      const Tensor<2,dim> F_iso = Physics::Elasticity::Kinematics::F_iso(F);
+      C_inv = symmetrize(invert(transpose(F)*F));
+      p_tilde = p_tilde_in;
+      J_tilde = J_tilde_in;
+      Assert(det_F > 0, ExcInternalError());
+    }
+    SymmetricTensor<2, dim> get_tau() const
+    {
+      // Zero strain --> zero stress
+      if (std::abs(det_F-1.0) > 1e-9)
+        {
+          static const double tol = 1e-12;
+
+          // Verify the push-forward transformation
+          Assert((Physics::Transformations::Contravariant::push_forward(get_S_vol(), F)-get_tau_vol()).norm()/get_tau_vol().norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::push_forward(get_S_iso(), F)-get_tau_iso()).norm()/get_tau_iso().norm() < tol, ExcInternalError());
+
+          // Verify the pull-back transformation
+          Assert((Physics::Transformations::Contravariant::pull_back(get_tau_vol(), F)-get_S_vol()).norm()/get_S_vol().norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::pull_back(get_tau_iso(), F)-get_S_iso()).norm()/get_S_iso().norm() < tol, ExcInternalError());
+        }
+      else
+        {
+          static const double tol = 1e-9;
+
+          // Verify the push-forward transformation
+          Assert((Physics::Transformations::Contravariant::push_forward(get_S_vol(), F)-get_tau_vol()).norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::push_forward(get_S_iso(), F)-get_tau_iso()).norm() < tol, ExcInternalError());
+
+          // Verify the pull-back transformation
+          Assert((Physics::Transformations::Contravariant::pull_back(get_tau_vol(), F)-get_S_vol()).norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::pull_back(get_tau_iso(), F)-get_S_iso()).norm() < tol, ExcInternalError());
+        }
+
+      return Physics::Transformations::Contravariant::push_forward(get_S_iso() + get_S_vol(), F);
+    }
+    SymmetricTensor<4, dim> get_Jc() const
+    {
+      // Zero strain --> zero stress
+      if (std::abs(det_F-1.0) > 1e-9)
+        {
+          static const double tol = 1e-9;
+
+          // Verify the push-forward transformation
+          Assert((Physics::Transformations::Contravariant::push_forward(get_H_vol(), F)-get_Jc_vol()).norm()/get_Jc_vol().norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::push_forward(get_H_iso(), F)-get_Jc_iso()).norm()/get_Jc_iso().norm() < tol, ExcInternalError());
+
+          // Verify the pull-back transformation
+          Assert((Physics::Transformations::Contravariant::pull_back(get_Jc_vol(), F)-get_H_vol()).norm()/get_H_vol().norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::pull_back(get_Jc_iso(), F)-get_H_iso()).norm()/get_H_iso().norm() < tol, ExcInternalError());
+        }
+      else
+        {
+          static const double tol = 1e-6;
+
+          // Verify the push-forward transformation
+          Assert((Physics::Transformations::Contravariant::push_forward(get_H_vol(), F)-get_Jc_vol()).norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::push_forward(get_H_iso(), F)-get_Jc_iso()).norm() < tol, ExcInternalError());
+
+          // Verify the pull-back transformation
+          Assert((Physics::Transformations::Contravariant::pull_back(get_Jc_vol(), F)-get_H_vol()).norm() < tol, ExcInternalError());
+          Assert((Physics::Transformations::Contravariant::pull_back(get_Jc_iso(), F)-get_H_iso()).norm() < tol, ExcInternalError());
+        }
+
+      return Physics::Transformations::Contravariant::push_forward(get_H_vol() + get_H_iso(), F);
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde);
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return ( (kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde)));
+    }
+    double get_det_F() const
+    {
+      return det_F;
+    }
+    double get_p_tilde() const
+    {
+      return p_tilde;
+    }
+    double get_J_tilde() const
+    {
+      return J_tilde;
+    }
+  protected:
+    const double kappa;
+    const double c_1;
+    double p_tilde;
+    double J_tilde;
+    double det_F;
+    Tensor<2, dim> F;
+    SymmetricTensor<2, dim> C_inv;
+    SymmetricTensor<2, dim> get_S_vol() const
+    {
+      // See Wriggers2008 equ 3.304 (3.126 == one field)
+      return p_tilde * det_F * C_inv;
+    }
+    SymmetricTensor<2, dim> get_S_iso() const
+    {
+      // Expansion of stress defintion via chain rule
+      return get_S_bar()*Physics::Elasticity::StandardTensors<dim>::Dev_P(F);
+    }
+    SymmetricTensor<2, dim> get_S_bar() const
+    {
+      // Derived from energy function
+      return 2.0 * c_1 * Physics::Elasticity::StandardTensors<dim>::I;
+    }
+    SymmetricTensor<4, dim> get_H_vol() const
+    {
+      const SymmetricTensor<4, dim> C_inv_x_C_inv
+        = outer_product(C_inv,C_inv);
+      return p_tilde * det_F
+             * ( C_inv_x_C_inv
+                 + (2.0 * Physics::Elasticity::StandardTensors<dim>::dC_inv_dC(F)) );
+    }
+    SymmetricTensor<4, dim> get_H_iso() const
+    {
+      // See Wriggers2008 (equ 3.253 == one field)
+      const SymmetricTensor<2,dim> C = Physics::Elasticity::Kinematics::C(F);
+      const SymmetricTensor<4, dim> C_inv_x_C_inv
+        = outer_product(C_inv,C_inv);
+      const SymmetricTensor<2, dim> S_bar = get_S_bar();
+      const SymmetricTensor<2, dim> S_iso = get_S_iso();
+      const SymmetricTensor<4, dim> S_iso_x_C_inv
+        = outer_product(S_iso,C_inv);
+      const SymmetricTensor<4, dim> C_inv_x_S_iso
+        = outer_product(C_inv,S_iso);
+      const SymmetricTensor<4, dim> H_bar = get_H_bar();
+
+      // Note: The is an unfortunate mistake in Wriggers2008 equ 3.253 relating
+      // to the first term here (the second term in the Wriggers2008 equation).
+      // --> The reference formula has the wrong sign for this term.
+      // See Pelteret2013 equ A.37 for the correct expression
+      // https://open.uct.ac.za/handle/11427/9519
+      // http://jean-paul.pelteret.co.za/wp-content/papercite-data/pdf/pelteret2013b-phd_thesis.pdf
+      return - (2.0 / dim) * (S_bar*(std::pow(det_F, -2.0/dim)*C))
+             * ((1.0 / dim)*C_inv_x_C_inv + Physics::Elasticity::StandardTensors<dim>::dC_inv_dC(F))
+             - (2.0 / dim) * (S_iso_x_C_inv + C_inv_x_S_iso)
+             + Physics::Elasticity::StandardTensors<dim>::Dev_P_T(F) * H_bar
+             * Physics::Elasticity::StandardTensors<dim>::Dev_P(F);
+    }
+    SymmetricTensor<4, dim> get_H_bar() const
+    {
+      return SymmetricTensor<4, dim>();
+    }
+
+    //=== ALTERNATIVE IMPLEMENTATION ===
+    SymmetricTensor<2, dim> get_tau_vol() const
+    {
+      return p_tilde * det_F * Physics::Elasticity::StandardTensors<dim>::I;
+    }
+    SymmetricTensor<2, dim> get_tau_iso() const
+    {
+      return Physics::Elasticity::StandardTensors<dim>::dev_P * get_tau_bar();
+    }
+    SymmetricTensor<2, dim> get_tau_bar() const
+    {
+      const Tensor<2,dim> F_iso = Physics::Elasticity::Kinematics::F_iso(F);
+      const SymmetricTensor<2,dim> b_bar = Physics::Elasticity::Kinematics::b(F_iso);
+      return 2.0 * c_1 * b_bar;
+    }
+    SymmetricTensor<4, dim> get_Jc_vol() const
+    {
+      return p_tilde * det_F
+             * ( Physics::Elasticity::StandardTensors<dim>::IxI
+                 - (2.0 * Physics::Elasticity::StandardTensors<dim>::S) );
+    }
+    SymmetricTensor<4, dim> get_Jc_iso() const
+    {
+      const SymmetricTensor<2, dim> tau_bar = get_tau_bar();
+      const SymmetricTensor<2, dim> tau_iso = get_tau_iso();
+      const SymmetricTensor<4, dim> tau_iso_x_I
+        = outer_product(tau_iso,
+                        Physics::Elasticity::StandardTensors<dim>::I);
+      const SymmetricTensor<4, dim> I_x_tau_iso
+        = outer_product(Physics::Elasticity::StandardTensors<dim>::I,
+                        tau_iso);
+      const SymmetricTensor<4, dim> c_bar = get_c_bar();
+      return (2.0 / dim) * trace(tau_bar)
+             * Physics::Elasticity::StandardTensors<dim>::dev_P
+             - (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso)
+             + Physics::Elasticity::StandardTensors<dim>::dev_P * c_bar
+             * Physics::Elasticity::StandardTensors<dim>::dev_P;
+    }
+    SymmetricTensor<4, dim> get_c_bar() const
+    {
+      return SymmetricTensor<4, dim>();
+    }
+  };
+  template <int dim>
+  class PointHistory
+  {
+  public:
+    PointHistory()
+      :
+      F_inv(Physics::Elasticity::StandardTensors<dim>::I),
+      tau(SymmetricTensor<2, dim>()),
+      d2Psi_vol_dJ2(0.0),
+      dPsi_vol_dJ(0.0),
+      Jc(SymmetricTensor<4, dim>())
+    {}
+    virtual ~PointHistory()
+    {}
+    void setup_lqp (const Parameters::AllParameters &parameters)
+    {
+      material.reset(new Material_Compressible_Neo_Hook_Three_Field<dim>(parameters.mu,
+                     parameters.nu));
+      update_values(Tensor<2, dim>(), 0.0, 1.0);
+    }
+    void update_values (const Tensor<2, dim> &Grad_u_n,
+                        const double p_tilde,
+                        const double J_tilde)
+    {
+      const Tensor<2, dim> F = Physics::Elasticity::Kinematics::F(Grad_u_n);
+      material->update_material_data(F, p_tilde, J_tilde);
+      F_inv = invert(F);
+      tau = material->get_tau();
+      Jc = material->get_Jc();
+      dPsi_vol_dJ = material->get_dPsi_vol_dJ();
+      d2Psi_vol_dJ2 = material->get_d2Psi_vol_dJ2();
+    }
+    double get_J_tilde() const
+    {
+      return material->get_J_tilde();
+    }
+    double get_det_F() const
+    {
+      return material->get_det_F();
+    }
+    const Tensor<2, dim> &get_F_inv() const
+    {
+      return F_inv;
+    }
+    double get_p_tilde() const
+    {
+      return material->get_p_tilde();
+    }
+    const SymmetricTensor<2, dim> &get_tau() const
+    {
+      return tau;
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return dPsi_vol_dJ;
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return d2Psi_vol_dJ2;
+    }
+    const SymmetricTensor<4, dim> &get_Jc() const
+    {
+      return Jc;
+    }
+  private:
+    std_cxx11::shared_ptr< Material_Compressible_Neo_Hook_Three_Field<dim> > material;
+    Tensor<2, dim> F_inv;
+    SymmetricTensor<2, dim> tau;
+    double                  d2Psi_vol_dJ2;
+    double                  dPsi_vol_dJ;
+    SymmetricTensor<4, dim> Jc;
+  };
+  template <int dim>
+  class Solid
+  {
+  public:
+    Solid(const std::string &input_file);
+    virtual
+    ~Solid();
+    void
+    run();
+  private:
+    struct PerTaskData_K;
+    struct ScratchData_K;
+    struct PerTaskData_RHS;
+    struct ScratchData_RHS;
+    struct PerTaskData_SC;
+    struct ScratchData_SC;
+    struct PerTaskData_UQPH;
+    struct ScratchData_UQPH;
+    void
+    make_grid();
+    void
+    system_setup();
+    void
+    determine_component_extractors();
+    void
+    assemble_system_tangent();
+    void
+    assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                     ScratchData_K &scratch,
+                                     PerTaskData_K &data) const;
+    void
+    copy_local_to_global_K(const PerTaskData_K &data);
+    void
+    assemble_system_rhs();
+    void
+    assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                 ScratchData_RHS &scratch,
+                                 PerTaskData_RHS &data) const;
+    void
+    copy_local_to_global_rhs(const PerTaskData_RHS &data);
+    void
+    assemble_sc();
+    void
+    assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                         ScratchData_SC &scratch,
+                         PerTaskData_SC &data);
+    void
+    copy_local_to_global_sc(const PerTaskData_SC &data);
+    void
+    make_constraints(const int &it_nr);
+    void
+    setup_qph();
+    void
+    update_qph_incremental(const BlockVector<double> &solution_delta);
+    void
+    update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                    ScratchData_UQPH &scratch,
+                                    PerTaskData_UQPH &data);
+    void
+    copy_local_to_global_UQPH(const PerTaskData_UQPH &/*data*/)
+    {}
+    void
+    solve_nonlinear_timestep(BlockVector<double> &solution_delta);
+    std::pair<unsigned int, double>
+    solve_linear_system(BlockVector<double> &newton_update);
+    BlockVector<double>
+    get_total_solution(const BlockVector<double> &solution_delta) const;
+    void
+    output_results() const;
+    Parameters::AllParameters parameters;
+    double                    vol_reference;
+    Triangulation<dim>        triangulation;
+    Time                      time;
+    mutable TimerOutput       timer;
+    CellDataStorage<typename Triangulation<dim>::cell_iterator,
+                    PointHistory<dim> > quadrature_point_history;
+    const unsigned int               degree;
+    const FESystem<dim>              fe;
+    DoFHandler<dim>                  dof_handler_ref;
+    const unsigned int               dofs_per_cell;
+    const FEValuesExtractors::Vector u_fe;
+    const FEValuesExtractors::Scalar p_fe;
+    const FEValuesExtractors::Scalar J_fe;
+    static const unsigned int n_blocks = 3;
+    static const unsigned int n_components = dim + 2;
+    static const unsigned int first_u_component = 0;
+    static const unsigned int p_component = dim;
+    static const unsigned int J_component = dim + 1;
+    enum
+    {
+      u_dof = 0,
+      p_dof = 1,
+      J_dof = 2
+    };
+    std::vector<types::global_dof_index> dofs_per_block;
+    std::vector<types::global_dof_index> element_indices_u;
+    std::vector<types::global_dof_index> element_indices_p;
+    std::vector<types::global_dof_index> element_indices_J;
+    const QGauss<dim>     qf_cell;
+    const QGauss<dim - 1> qf_face;
+    const unsigned int    n_q_points;
+    const unsigned int    n_q_points_f;
+    ConstraintMatrix          constraints;
+    BlockSparsityPattern      sparsity_pattern;
+    BlockSparseMatrix<double> tangent_matrix;
+    BlockVector<double>       system_rhs;
+    BlockVector<double>       solution_n;
+    ConditionalOStream        pcout;
+    struct Errors
+    {
+      Errors()
+        :
+        norm(1.0), u(1.0), p(1.0), J(1.0)
+      {}
+      void reset()
+      {
+        norm = 1.0;
+        u = 1.0;
+        p = 1.0;
+        J = 1.0;
+      }
+      void normalise(const Errors &rhs)
+      {
+        if (rhs.norm != 0.0)
+          norm /= rhs.norm;
+        if (rhs.u != 0.0)
+          u /= rhs.u;
+        if (rhs.p != 0.0)
+          p /= rhs.p;
+        if (rhs.J != 0.0)
+          J /= rhs.J;
+      }
+      double norm, u, p, J;
+    };
+    Errors error_residual, error_residual_0, error_residual_norm, error_update,
+           error_update_0, error_update_norm;
+    void
+    get_error_residual(Errors &error_residual);
+    void
+    get_error_update(const BlockVector<double> &newton_update,
+                     Errors &error_update);
+    std::pair<double, double>
+    get_error_dilation() const;
+    double
+    compute_vol_current () const;
+    void
+    print_conv_header();
+    void
+    print_conv_footer();
+  };
+  template <int dim>
+  Solid<dim>::Solid(const std::string &input_file)
+    :
+    parameters(input_file),
+    triangulation(Triangulation<dim>::maximum_smoothing),
+    time(parameters.end_time, parameters.delta_t),
+    timer(deallog.get_file_stream(),
+          TimerOutput::never,
+          TimerOutput::wall_times),
+    degree(parameters.poly_degree),
+    fe(FE_Q<dim>(parameters.poly_degree), dim, // displacement
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1, // pressure
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1), // dilatation
+    dof_handler_ref(triangulation),
+    dofs_per_cell (fe.dofs_per_cell),
+    u_fe(first_u_component),
+    p_fe(p_component),
+    J_fe(J_component),
+    dofs_per_block(n_blocks),
+    qf_cell(parameters.quad_order),
+    qf_face(parameters.quad_order),
+    n_q_points (qf_cell.size()),
+    n_q_points_f (qf_face.size()),
+    pcout(deallog.get_file_stream())
+  {
+    Assert(dim==2 || dim==3, ExcMessage("This problem only works in 2 or 3 space dimensions."));
+    determine_component_extractors();
+  }
+  template <int dim>
+  Solid<dim>::~Solid()
+  {
+    dof_handler_ref.clear();
+  }
+  template <int dim>
+  void Solid<dim>::run()
+  {
+    make_grid();
+    system_setup();
+    {
+      ConstraintMatrix constraints;
+      constraints.close();
+      const ComponentSelectFunction<dim>
+      J_mask (J_component, n_components);
+      VectorTools::project (dof_handler_ref,
+                            constraints,
+                            QGauss<dim>(degree+2),
+                            J_mask,
+                            solution_n);
+    }
+    output_results();
+    time.increment();
+    BlockVector<double> solution_delta(dofs_per_block);
+    while (time.current() < time.end())
+      {
+        solution_delta = 0.0;
+        solve_nonlinear_timestep(solution_delta);
+        solution_n += solution_delta;
+        output_results();
+        time.increment();
+      }
+  }
+  template <int dim>
+  struct Solid<dim>::PerTaskData_K
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_K(const unsigned int dofs_per_cell)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_matrix = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_K
+  {
+    FEValues<dim> fe_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<Tensor<2, dim> > >          grad_Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_K(const FiniteElement<dim> &fe_cell,
+                  const QGauss<dim> &qf_cell,
+                  const UpdateFlags uf_cell)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      grad_Nx(qf_cell.size(),
+              std::vector<Tensor<2, dim> >(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_K(const ScratchData_K &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      grad_Nx(rhs.grad_Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              grad_Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_RHS
+  {
+    Vector<double>            cell_rhs;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_RHS(const unsigned int dofs_per_cell)
+      :
+      cell_rhs(dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_rhs = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_RHS
+  {
+    FEValues<dim>     fe_values_ref;
+    FEFaceValues<dim> fe_face_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_RHS(const FiniteElement<dim> &fe_cell,
+                    const QGauss<dim> &qf_cell, const UpdateFlags uf_cell,
+                    const QGauss<dim - 1> & qf_face, const UpdateFlags uf_face)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      fe_face_values_ref(fe_cell, qf_face, uf_face),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_RHS(const ScratchData_RHS &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
+                         rhs.fe_face_values_ref.get_quadrature(),
+                         rhs.fe_face_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points      = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_SC
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    FullMatrix<double>        k_orig;
+    FullMatrix<double>        k_pu;
+    FullMatrix<double>        k_pJ;
+    FullMatrix<double>        k_JJ;
+    FullMatrix<double>        k_pJ_inv;
+    FullMatrix<double>        k_bbar;
+    FullMatrix<double>        A;
+    FullMatrix<double>        B;
+    FullMatrix<double>        C;
+    PerTaskData_SC(const unsigned int dofs_per_cell,
+                   const unsigned int n_u,
+                   const unsigned int n_p,
+                   const unsigned int n_J)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell),
+      k_orig(dofs_per_cell, dofs_per_cell),
+      k_pu(n_p, n_u),
+      k_pJ(n_p, n_J),
+      k_JJ(n_J, n_J),
+      k_pJ_inv(n_p, n_J),
+      k_bbar(n_u, n_u),
+      A(n_J,n_u),
+      B(n_J, n_u),
+      C(n_p, n_u)
+    {}
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_SC
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_UQPH
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_UQPH
+  {
+    const BlockVector<double>   &solution_total;
+    std::vector<Tensor<2, dim> > solution_grads_u_total;
+    std::vector<double>          solution_values_p_total;
+    std::vector<double>          solution_values_J_total;
+    FEValues<dim>                fe_values_ref;
+    ScratchData_UQPH(const FiniteElement<dim> &fe_cell,
+                     const QGauss<dim> &qf_cell,
+                     const UpdateFlags uf_cell,
+                     const BlockVector<double> &solution_total)
+      :
+      solution_total(solution_total),
+      solution_grads_u_total(qf_cell.size()),
+      solution_values_p_total(qf_cell.size()),
+      solution_values_J_total(qf_cell.size()),
+      fe_values_ref(fe_cell, qf_cell, uf_cell)
+    {}
+    ScratchData_UQPH(const ScratchData_UQPH &rhs)
+      :
+      solution_total(rhs.solution_total),
+      solution_grads_u_total(rhs.solution_grads_u_total),
+      solution_values_p_total(rhs.solution_values_p_total),
+      solution_values_J_total(rhs.solution_values_J_total),
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags())
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = solution_grads_u_total.size();
+      for (unsigned int q = 0; q < n_q_points; ++q)
+        {
+          solution_grads_u_total[q] = 0.0;
+          solution_values_p_total[q] = 0.0;
+          solution_values_J_total[q] = 0.0;
+        }
+    }
+  };
+  template <int dim>
+  void Solid<dim>::make_grid()
+  {
+    GridGenerator::hyper_rectangle(triangulation,
+                                   (dim==3 ? Point<dim>(0.0, 0.0, 0.0) : Point<dim>(0.0, 0.0)),
+                                   (dim==3 ? Point<dim>(1.0, 1.0, 1.0) : Point<dim>(1.0, 1.0)),
+                                   true);
+    GridTools::scale(parameters.scale, triangulation);
+    triangulation.refine_global(std::max (1U, parameters.global_refinement));
+    vol_reference = GridTools::volume(triangulation);
+    pcout << "Grid:\n\t Reference volume: " << vol_reference << std::endl;
+    typename Triangulation<dim>::active_cell_iterator cell =
+      triangulation.begin_active(), endc = triangulation.end();
+    for (; cell != endc; ++cell)
+      for (unsigned int face = 0;
+           face < GeometryInfo<dim>::faces_per_cell; ++face)
+        {
+          if (cell->face(face)->at_boundary() == true
+              &&
+              cell->face(face)->center()[1] == 1.0 * parameters.scale)
+            {
+              if (dim==3)
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale
+                      &&
+                      cell->face(face)->center()[2] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+              else
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::system_setup()
+  {
+    timer.enter_subsection("Setup system");
+    std::vector<unsigned int> block_component(n_components, u_dof); // Displacement
+    block_component[p_component] = p_dof; // Pressure
+    block_component[J_component] = J_dof; // Dilatation
+    dof_handler_ref.distribute_dofs(fe);
+    DoFRenumbering::Cuthill_McKee(dof_handler_ref);
+    DoFRenumbering::component_wise(dof_handler_ref, block_component);
+    DoFTools::count_dofs_per_block(dof_handler_ref, dofs_per_block,
+                                   block_component);
+    pcout << "Triangulation:"
+          << "\n\t Number of active cells: " << triangulation.n_active_cells()
+          << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
+          << std::endl;
+    tangent_matrix.clear();
+    {
+      const types::global_dof_index n_dofs_u = dofs_per_block[u_dof];
+      const types::global_dof_index n_dofs_p = dofs_per_block[p_dof];
+      const types::global_dof_index n_dofs_J = dofs_per_block[J_dof];
+      BlockDynamicSparsityPattern dsp(n_blocks, n_blocks);
+      dsp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u);
+      dsp.block(u_dof, p_dof).reinit(n_dofs_u, n_dofs_p);
+      dsp.block(u_dof, J_dof).reinit(n_dofs_u, n_dofs_J);
+      dsp.block(p_dof, u_dof).reinit(n_dofs_p, n_dofs_u);
+      dsp.block(p_dof, p_dof).reinit(n_dofs_p, n_dofs_p);
+      dsp.block(p_dof, J_dof).reinit(n_dofs_p, n_dofs_J);
+      dsp.block(J_dof, u_dof).reinit(n_dofs_J, n_dofs_u);
+      dsp.block(J_dof, p_dof).reinit(n_dofs_J, n_dofs_p);
+      dsp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J);
+      dsp.collect_sizes();
+      Table<2, DoFTools::Coupling> coupling(n_components, n_components);
+      for (unsigned int ii = 0; ii < n_components; ++ii)
+        for (unsigned int jj = 0; jj < n_components; ++jj)
+          if (((ii < p_component) && (jj == J_component))
+              || ((ii == J_component) && (jj < p_component))
+              || ((ii == p_component) && (jj == p_component)))
+            coupling[ii][jj] = DoFTools::none;
+          else
+            coupling[ii][jj] = DoFTools::always;
+      DoFTools::make_sparsity_pattern(dof_handler_ref,
+                                      coupling,
+                                      dsp,
+                                      constraints,
+                                      false);
+      sparsity_pattern.copy_from(dsp);
+    }
+    tangent_matrix.reinit(sparsity_pattern);
+    system_rhs.reinit(dofs_per_block);
+    system_rhs.collect_sizes();
+    solution_n.reinit(dofs_per_block);
+    solution_n.collect_sizes();
+    setup_qph();
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::determine_component_extractors()
+  {
+    element_indices_u.clear();
+    element_indices_p.clear();
+    element_indices_J.clear();
+    for (unsigned int k = 0; k < fe.dofs_per_cell; ++k)
+      {
+        const unsigned int k_group = fe.system_to_base_index(k).first.first;
+        if (k_group == u_dof)
+          element_indices_u.push_back(k);
+        else if (k_group == p_dof)
+          element_indices_p.push_back(k);
+        else if (k_group == J_dof)
+          element_indices_J.push_back(k);
+        else
+          {
+            Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+  }
+  template <int dim>
+  void Solid<dim>::setup_qph()
+  {
+    pcout << "    Setting up quadrature point data..." << std::endl;
+    quadrature_point_history.initialize(triangulation.begin_active(),
+                                        triangulation.end(),
+                                        n_q_points);
+    for (typename Triangulation<dim>::active_cell_iterator cell =
+           triangulation.begin_active(); cell != triangulation.end(); ++cell)
+      {
+        const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          lqph[q_point]->setup_lqp(parameters);
+      }
+  }
+  template <int dim>
+  void Solid<dim>::update_qph_incremental(const BlockVector<double> &solution_delta)
+  {
+    timer.enter_subsection("Update QPH data");
+    pcout << " UQPH " << std::flush;
+    const BlockVector<double> solution_total(get_total_solution(solution_delta));
+    const UpdateFlags uf_UQPH(update_values | update_gradients);
+    PerTaskData_UQPH per_task_data_UQPH;
+    ScratchData_UQPH scratch_data_UQPH(fe, qf_cell, uf_UQPH, solution_total);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::update_qph_incremental_one_cell,
+                    &Solid::copy_local_to_global_UQPH,
+                    scratch_data_UQPH,
+                    per_task_data_UQPH);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                              ScratchData_UQPH &scratch,
+                                              PerTaskData_UQPH &/*data*/)
+  {
+    const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    Assert(scratch.solution_grads_u_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_p_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_J_total.size() == n_q_points,
+           ExcInternalError());
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total,
+                                                       scratch.solution_grads_u_total);
+    scratch.fe_values_ref[p_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_p_total);
+    scratch.fe_values_ref[J_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_J_total);
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      lqph[q_point]->update_values(scratch.solution_grads_u_total[q_point],
+                                   scratch.solution_values_p_total[q_point],
+                                   scratch.solution_values_J_total[q_point]);
+  }
+  template <int dim>
+  void
+  Solid<dim>::solve_nonlinear_timestep(BlockVector<double> &solution_delta)
+  {
+    pcout << std::endl << "Timestep " << time.get_timestep() << " @ "
+          << time.current() << "s" << std::endl;
+    BlockVector<double> newton_update(dofs_per_block);
+    error_residual.reset();
+    error_residual_0.reset();
+    error_residual_norm.reset();
+    error_update.reset();
+    error_update_0.reset();
+    error_update_norm.reset();
+    print_conv_header();
+    unsigned int newton_iteration = 0;
+    for (; newton_iteration < parameters.max_iterations_NR;
+         ++newton_iteration)
+      {
+        pcout << " " << std::setw(2) << newton_iteration << " " << std::flush;
+        tangent_matrix = 0.0;
+        system_rhs = 0.0;
+        assemble_system_rhs();
+        get_error_residual(error_residual);
+        if (newton_iteration == 0)
+          error_residual_0 = error_residual;
+        error_residual_norm = error_residual;
+        error_residual_norm.normalise(error_residual_0);
+        if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u
+            && error_residual_norm.u <= parameters.tol_f)
+          {
+            pcout << " CONVERGED! " << std::endl;
+            print_conv_footer();
+            break;
+          }
+        assemble_system_tangent();
+        make_constraints(newton_iteration);
+        constraints.condense(tangent_matrix, system_rhs);
+        const std::pair<unsigned int, double>
+        lin_solver_output = solve_linear_system(newton_update);
+        get_error_update(newton_update, error_update);
+        if (newton_iteration == 0)
+          error_update_0 = error_update;
+        error_update_norm = error_update;
+        error_update_norm.normalise(error_update_0);
+        solution_delta += newton_update;
+        update_qph_incremental(solution_delta);
+        pcout << " | " << std::fixed << std::setprecision(3) << std::setw(7)
+              << std::scientific << lin_solver_output.first << "  "
+              << lin_solver_output.second << "  " << error_residual_norm.norm
+              << "  " << error_residual_norm.u << "  "
+              << error_residual_norm.p << "  " << error_residual_norm.J
+              << "  " << error_update_norm.norm << "  " << error_update_norm.u
+              << "  " << error_update_norm.p << "  " << error_update_norm.J
+              << "  " << std::endl;
+      }
+    AssertThrow (newton_iteration <= parameters.max_iterations_NR,
+                 ExcMessage("No convergence in nonlinear solver!"));
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_header()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    pcout << "                 SOLVER STEP                  "
+          << " |  LIN_IT   LIN_RES    RES_NORM    "
+          << " RES_U     RES_P      RES_J     NU_NORM     "
+          << " NU_U       NU_P       NU_J " << std::endl;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_footer()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    const std::pair<double,double> error_dil = get_error_dilation();
+    pcout << "Relative errors:" << std::endl
+          << "Displacement:\t" << error_update.u / error_update_0.u << std::endl
+          << "Force: \t\t" << error_residual.u / error_residual_0.u << std::endl
+          << "Dilatation:\t" << error_dil.first << std::endl
+          << "v / V_0:\t" << error_dil.second *vol_reference << " / " << vol_reference
+          << " = " << error_dil.second << std::endl;
+  }
+  template <int dim>
+  double
+  Solid<dim>::compute_vol_current() const
+  {
+    double vol_current = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double JxW = fe_values_ref.JxW(q_point);
+            vol_current += det_F_qp * JxW;
+          }
+      }
+    Assert(vol_current > 0.0, ExcInternalError());
+    return vol_current;
+  }
+  template <int dim>
+  std::pair<double, double>
+  Solid<dim>::get_error_dilation() const
+  {
+    double dil_L2_error = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double J_tilde_qp = lqph[q_point]->get_J_tilde();
+            const double the_error_qp_squared = std::pow((det_F_qp - J_tilde_qp),
+                                                         2);
+            const double JxW = fe_values_ref.JxW(q_point);
+            dil_L2_error += the_error_qp_squared * JxW;
+          }
+      }
+    return std::make_pair(std::sqrt(dil_L2_error),
+                          compute_vol_current() / vol_reference);
+  }
+  template <int dim>
+  void Solid<dim>::get_error_residual(Errors &error_residual)
+  {
+    BlockVector<double> error_res(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_res(i) = system_rhs(i);
+    error_residual.norm = error_res.l2_norm();
+    error_residual.u = error_res.block(u_dof).l2_norm();
+    error_residual.p = error_res.block(p_dof).l2_norm();
+    error_residual.J = error_res.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  void Solid<dim>::get_error_update(const BlockVector<double> &newton_update,
+                                    Errors &error_update)
+  {
+    BlockVector<double> error_ud(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_ud(i) = newton_update(i);
+    error_update.norm = error_ud.l2_norm();
+    error_update.u = error_ud.block(u_dof).l2_norm();
+    error_update.p = error_ud.block(p_dof).l2_norm();
+    error_update.J = error_ud.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  BlockVector<double>
+  Solid<dim>::get_total_solution(const BlockVector<double> &solution_delta) const
+  {
+    BlockVector<double> solution_total(solution_n);
+    solution_total += solution_delta;
+    return solution_total;
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_tangent()
+  {
+    timer.enter_subsection("Assemble tangent matrix");
+    pcout << " ASM_K " << std::flush;
+    tangent_matrix = 0.0;
+    const UpdateFlags uf_cell(update_values    |
+                              update_gradients |
+                              update_JxW_values);
+    PerTaskData_K per_task_data(dofs_per_cell);
+    ScratchData_K scratch_data(fe, qf_cell, uf_cell);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_tangent_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_K,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_K(const PerTaskData_K &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                               ScratchData_K &scratch,
+                                               PerTaskData_K &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              {
+                scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                                              * F_inv;
+                scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
+              }
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> tau         = lqph[q_point]->get_tau();
+        const SymmetricTensor<4, dim> Jc = lqph[q_point]->get_Jc();
+        const double d2Psi_vol_dJ2       = lqph[q_point]->get_d2Psi_vol_dJ2();
+        const double det_F               = lqph[q_point]->get_det_F();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const std::vector<Tensor<2, dim> >
+        &grad_Nx = scratch.grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int component_i = fe.system_to_component_index(i).first;
+            const unsigned int i_group     = fe.system_to_base_index(i).first.first;
+            for (unsigned int j = 0; j <= i; ++j)
+              {
+                const unsigned int component_j = fe.system_to_component_index(j).first;
+                const unsigned int j_group     = fe.system_to_base_index(j).first.first;
+                if ((i_group == j_group) && (i_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution:
+                                              * symm_grad_Nx[j] * JxW;
+                    if (component_i == component_j) // geometrical stress contribution
+                      data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau
+                                                * grad_Nx[j][component_j] * JxW;
+                  }
+                else if ((i_group == p_dof) && (j_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += N[i] * det_F
+                                              * (symm_grad_Nx[j]
+                                                 * Physics::Elasticity::StandardTensors<dim>::I)
+                                              * JxW;
+                  }
+                else if ((i_group == J_dof) && (j_group == p_dof))
+                  data.cell_matrix(i, j) -= N[i] * N[j] * JxW;
+                else if ((i_group == j_group) && (i_group == J_dof))
+                  data.cell_matrix(i, j) += N[i] * d2Psi_vol_dJ2 * N[j] * JxW;
+                else
+                  Assert((i_group <= J_dof) && (j_group <= J_dof),
+                         ExcInternalError());
+              }
+          }
+      }
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
+        data.cell_matrix(i, j) = data.cell_matrix(j, i);
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_rhs()
+  {
+    timer.enter_subsection("Assemble system right-hand side");
+    pcout << " ASM_R " << std::flush;
+    system_rhs = 0.0;
+    const UpdateFlags uf_cell(update_values |
+                              update_gradients |
+                              update_JxW_values);
+    const UpdateFlags uf_face(update_values |
+                              update_normal_vectors |
+                              update_JxW_values);
+    PerTaskData_RHS per_task_data(dofs_per_cell);
+    ScratchData_RHS scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_rhs_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_rhs,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_rhs(const PerTaskData_RHS &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      system_rhs(data.local_dof_indices[i]) += data.cell_rhs(i);
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                           ScratchData_RHS &scratch,
+                                           PerTaskData_RHS &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              scratch.symm_grad_Nx[q_point][k]
+                = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                             * F_inv);
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const SymmetricTensor<2, dim> tau = lqph[q_point]->get_tau();
+        const double det_F = lqph[q_point]->get_det_F();
+        const double J_tilde = lqph[q_point]->get_J_tilde();
+        const double p_tilde = lqph[q_point]->get_p_tilde();
+        const double dPsi_vol_dJ = lqph[q_point]->get_dPsi_vol_dJ();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int i_group = fe.system_to_base_index(i).first.first;
+            if (i_group == u_dof)
+              data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW;
+            else if (i_group == p_dof)
+              data.cell_rhs(i) -= N[i] * (det_F - J_tilde) * JxW;
+            else if (i_group == J_dof)
+              data.cell_rhs(i) -= N[i] * (dPsi_vol_dJ - p_tilde) * JxW;
+            else
+              Assert(i_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell;
+         ++face)
+      if (cell->face(face)->at_boundary() == true
+          && cell->face(face)->boundary_id() == 6)
+        {
+          scratch.fe_face_values_ref.reinit(cell, face);
+          for (unsigned int f_q_point = 0; f_q_point < n_q_points_f;
+               ++f_q_point)
+            {
+              const Tensor<1, dim> &N =
+                scratch.fe_face_values_ref.normal_vector(f_q_point);
+              static const double  p0        = -4.0
+                                               /
+                                               (parameters.scale * parameters.scale);
+              const double         time_ramp = (time.current() / time.end());
+              const double         pressure  = p0 * parameters.p_p0 * time_ramp;
+              const Tensor<1, dim> traction  = pressure * N;
+              for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                {
+                  const unsigned int i_group =
+                    fe.system_to_base_index(i).first.first;
+                  if (i_group == u_dof)
+                    {
+                      const unsigned int component_i =
+                        fe.system_to_component_index(i).first;
+                      const double Ni =
+                        scratch.fe_face_values_ref.shape_value(i,
+                                                               f_q_point);
+                      const double JxW = scratch.fe_face_values_ref.JxW(
+                                           f_q_point);
+                      data.cell_rhs(i) += (Ni * traction[component_i])
+                                          * JxW;
+                    }
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::make_constraints(const int &it_nr)
+  {
+    pcout << " CST " << std::flush;
+    if (it_nr > 1)
+      return;
+    constraints.clear();
+    const bool apply_dirichlet_bc = (it_nr == 0);
+    const FEValuesExtractors::Scalar x_displacement(0);
+    const FEValuesExtractors::Scalar y_displacement(1);
+    {
+      const int boundary_id = 0;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+    }
+    {
+      const int boundary_id = 2;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+    }
+    if (dim==3)
+      {
+        const FEValuesExtractors::Scalar z_displacement(2);
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+        {
+          const int boundary_id = 4;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+      }
+    else
+      {
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+      }
+    constraints.close();
+  }
+  template <int dim>
+  void Solid<dim>::assemble_sc()
+  {
+    timer.enter_subsection("Perform static condensation");
+    pcout << " ASM_SC " << std::flush;
+    PerTaskData_SC per_task_data(dofs_per_cell, element_indices_u.size(),
+                                 element_indices_p.size(),
+                                 element_indices_J.size());
+    ScratchData_SC scratch_data;
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::assemble_sc_one_cell,
+                    &Solid::copy_local_to_global_sc,
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_sc(const PerTaskData_SC &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                   ScratchData_SC &scratch,
+                                   PerTaskData_SC &data)
+  {
+    data.reset();
+    scratch.reset();
+    cell->get_dof_indices(data.local_dof_indices);
+    data.k_orig.extract_submatrix_from(tangent_matrix,
+                                       data.local_dof_indices,
+                                       data.local_dof_indices);
+    data.k_pu.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_u);
+    data.k_pJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_J);
+    data.k_JJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_J,
+                                     element_indices_J);
+    data.k_pJ_inv.invert(data.k_pJ);
+    data.k_pJ_inv.mmult(data.A, data.k_pu);
+    data.k_JJ.mmult(data.B, data.A);
+    data.k_pJ_inv.Tmmult(data.C, data.B);
+    data.k_pu.Tmmult(data.k_bbar, data.C);
+    data.k_bbar.scatter_matrix_to(element_indices_u,
+                                  element_indices_u,
+                                  data.cell_matrix);
+    data.k_pJ_inv.add(-1.0, data.k_pJ);
+    data.k_pJ_inv.scatter_matrix_to(element_indices_p,
+                                    element_indices_J,
+                                    data.cell_matrix);
+  }
+  template <int dim>
+  std::pair<unsigned int, double>
+  Solid<dim>::solve_linear_system(BlockVector<double> &newton_update)
+  {
+    unsigned int lin_it = 0;
+    double lin_res = 0.0;
+    if (parameters.use_static_condensation == true)
+      {
+        BlockVector<double> A(dofs_per_block);
+        BlockVector<double> B(dofs_per_block);
+        {
+          assemble_sc();
+          tangent_matrix.block(p_dof, J_dof).vmult(A.block(J_dof),
+                                                   system_rhs.block(p_dof));
+          tangent_matrix.block(J_dof, J_dof).vmult(B.block(J_dof),
+                                                   A.block(J_dof));
+          A.block(J_dof) = system_rhs.block(J_dof);
+          A.block(J_dof) -= B.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(A.block(p_dof),
+                                                    A.block(J_dof));
+          tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof),
+                                                   A.block(p_dof));
+          system_rhs.block(u_dof) -= A.block(u_dof);
+          timer.enter_subsection("Linear solver");
+          pcout << " SLV " << std::flush;
+          if (parameters.type_lin == "CG")
+            {
+              const int solver_its = tangent_matrix.block(u_dof, u_dof).m()
+                                     * parameters.max_iterations_lin;
+              const double tol_sol = parameters.tol_lin
+                                     * system_rhs.block(u_dof).l2_norm();
+              SolverControl solver_control(solver_its, tol_sol);
+              GrowingVectorMemory<Vector<double> > GVM;
+              SolverCG<Vector<double> > solver_CG(solver_control, GVM);
+              PreconditionSelector<SparseMatrix<double>, Vector<double> >
+              preconditioner (parameters.preconditioner_type,
+                              parameters.preconditioner_relaxation);
+              preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof));
+              solver_CG.solve(tangent_matrix.block(u_dof, u_dof),
+                              newton_update.block(u_dof),
+                              system_rhs.block(u_dof),
+                              preconditioner);
+              lin_it = solver_control.last_step();
+              lin_res = solver_control.last_value();
+            }
+          else if (parameters.type_lin == "Direct")
+            {
+              SparseDirectUMFPACK A_direct;
+              A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
+              A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof));
+              lin_it = 1;
+              lin_res = 0.0;
+            }
+          else
+            Assert (false, ExcMessage("Linear solver type not implemented"));
+          timer.leave_subsection();
+        }
+        constraints.distribute(newton_update);
+        timer.enter_subsection("Linear solver postprocessing");
+        pcout << " PP " << std::flush;
+        {
+          tangent_matrix.block(p_dof, u_dof).vmult(A.block(p_dof),
+                                                   newton_update.block(u_dof));
+          A.block(p_dof) *= -1.0;
+          A.block(p_dof) += system_rhs.block(p_dof);
+          tangent_matrix.block(p_dof, J_dof).vmult(newton_update.block(J_dof),
+                                                   A.block(p_dof));
+        }
+        constraints.distribute(newton_update);
+        {
+          tangent_matrix.block(J_dof, J_dof).vmult(A.block(J_dof),
+                                                   newton_update.block(J_dof));
+          A.block(J_dof) *= -1.0;
+          A.block(J_dof) += system_rhs.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(newton_update.block(p_dof),
+                                                    A.block(J_dof));
+        }
+        constraints.distribute(newton_update);
+        timer.leave_subsection();
+      }
+    else
+      {
+        pcout << " ------ " << std::flush;
+        timer.enter_subsection("Linear solver");
+        pcout << " SLV " << std::flush;
+        if (parameters.type_lin == "CG")
+          {
+            const Vector<double> &f_u = system_rhs.block(u_dof);
+            const Vector<double> &f_p = system_rhs.block(p_dof);
+            const Vector<double> &f_J = system_rhs.block(J_dof);
+            Vector<double> &d_u = newton_update.block(u_dof);
+            Vector<double> &d_p = newton_update.block(p_dof);
+            Vector<double> &d_J = newton_update.block(J_dof);
+            const auto K_uu = linear_operator(tangent_matrix.block(u_dof, u_dof));
+            const auto K_up = linear_operator(tangent_matrix.block(u_dof, p_dof));
+            const auto K_pu = linear_operator(tangent_matrix.block(p_dof, u_dof));
+            const auto K_Jp = linear_operator(tangent_matrix.block(J_dof, p_dof));
+            const auto K_JJ = linear_operator(tangent_matrix.block(J_dof, J_dof));
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_Jp_inv ("jacobi");
+            preconditioner_K_Jp_inv.use_matrix(tangent_matrix.block(J_dof, p_dof));
+            ReductionControl solver_control_K_Jp_inv (tangent_matrix.block(J_dof, p_dof).m() * parameters.max_iterations_lin,
+                                                      1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_Jp_inv;
+            solver_K_Jp_inv.select("cg");
+            solver_K_Jp_inv.set_control(solver_control_K_Jp_inv);
+            const auto K_Jp_inv = inverse_operator(K_Jp,
+                                                   solver_K_Jp_inv,
+                                                   preconditioner_K_Jp_inv);
+            const auto K_pJ_inv     = transpose_operator(K_Jp_inv);
+            const auto K_pp_bar     = K_Jp_inv * K_JJ * K_pJ_inv;
+            const auto K_uu_bar_bar = K_up * K_pp_bar * K_pu;
+            const auto K_uu_con     = K_uu + K_uu_bar_bar;
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_con_inv (parameters.preconditioner_type,
+                                      parameters.preconditioner_relaxation);
+            preconditioner_K_con_inv.use_matrix(tangent_matrix.block(u_dof, u_dof));
+            ReductionControl solver_control_K_con_inv (tangent_matrix.block(u_dof, u_dof).m() * parameters.max_iterations_lin,
+                                                       1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_con_inv;
+            solver_K_con_inv.select("cg");
+            solver_K_con_inv.set_control(solver_control_K_con_inv);
+            const auto K_uu_con_inv = inverse_operator(K_uu_con,
+                                                       solver_K_con_inv,
+                                                       preconditioner_K_con_inv);
+            d_u = K_uu_con_inv*(f_u - K_up*(K_Jp_inv*f_J - K_pp_bar*f_p));
+            timer.leave_subsection();
+            timer.enter_subsection("Linear solver postprocessing");
+            pcout << " PP " << std::flush;
+            d_J = K_pJ_inv*(f_p - K_pu*d_u);
+            d_p = K_Jp_inv*(f_J - K_JJ*d_J);
+            lin_it = solver_control_K_con_inv.last_step();
+            lin_res = solver_control_K_con_inv.last_value();
+          }
+        else if (parameters.type_lin == "Direct")
+          {
+            SparseDirectUMFPACK A_direct;
+            A_direct.initialize(tangent_matrix);
+            A_direct.vmult(newton_update, system_rhs);
+            lin_it = 1;
+            lin_res = 0.0;
+            pcout << " -- " << std::flush;
+          }
+        else
+          Assert (false, ExcMessage("Linear solver type not implemented"));
+        timer.leave_subsection();
+        constraints.distribute(newton_update);
+      }
+    return std::make_pair(lin_it, lin_res);
+  }
+  template <int dim>
+  void Solid<dim>::output_results() const
+  {
+    DataOut<dim> data_out;
+    std::vector<DataComponentInterpretation::DataComponentInterpretation>
+    data_component_interpretation(dim,
+                                  DataComponentInterpretation::component_is_part_of_vector);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    std::vector<std::string> solution_name(dim, "displacement");
+    solution_name.push_back("pressure");
+    solution_name.push_back("dilatation");
+    data_out.attach_dof_handler(dof_handler_ref);
+    data_out.add_data_vector(solution_n,
+                             solution_name,
+                             DataOut<dim>::type_dof_data,
+                             data_component_interpretation);
+    Vector<double> soln(solution_n.size());
+    for (unsigned int i = 0; i < soln.size(); ++i)
+      soln(i) = solution_n(i);
+    MappingQEulerian<dim> q_mapping(degree, dof_handler_ref, soln);
+    data_out.build_patches(q_mapping, degree);
+    std::ostringstream filename;
+    filename << "solution-" << dim << "d-" << time.get_timestep() << ".vtk";
+    std::ofstream output(filename.str().c_str());
+    data_out.write_vtk(output);
+  }
+}
+int main (int argc,char **argv)
+{
+  std::ofstream logfile("output");
+  deallog.attach(logfile);
+  deallog.depth_file(0);
+  deallog.threshold_double(1.e-10);
+
+  Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, testing_max_num_threads());
+
+  using namespace dealii;
+  using namespace Step44;
+  try
+    {
+      const unsigned int dim = 3;
+      Solid<dim> solid(SOURCE_DIR "/prm/parameters-step-44.prm");
+      solid.run();
+    }
+  catch (std::exception &exc)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Exception on processing: " << std::endl << exc.what()
+                << std::endl << "Aborting!" << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  catch (...)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Unknown exception!" << std::endl << "Aborting!"
+                << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  return 0;
+}
diff --git a/tests/physics/step-44-standard_tensors-material_push_forward.output b/tests/physics/step-44-standard_tensors-material_push_forward.output
new file mode 100644 (file)
index 0000000..d0fa524
--- /dev/null
@@ -0,0 +1,167 @@
+
+Grid:
+        Reference volume: 1.00000e-09
+Triangulation:
+        Number of active cells: 64
+        Number of degrees of freedom: 503
+    Setting up quadrature point data...
+
+Timestep 1 @ 0.100000s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      1.879e-06  1.000e+00  1.000e+00  0.000e+00  0.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       8.427e-04  8.085e-02  8.085e-02  7.076e-13  3.053e-08  9.395e-02  3.598e-02  9.395e-02  9.397e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 122      4.107e-06  8.861e-04  8.861e-04  2.254e-15  1.997e-10  8.585e-04  1.975e-04  8.585e-04  8.586e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      4.885e-11  1.098e-06  1.098e-06  4.211e-20  2.153e-14  9.953e-07  1.498e-07  9.953e-07  9.953e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  1.498e-07
+Force:                 1.303e-11
+Dilatation:    3.484e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 2 @ 2.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      3.594e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       8.305e-04  8.472e-02  8.472e-02  2.288e+13  6.462e+06  1.096e-01  4.023e-02  1.096e-01  1.096e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 114      5.935e-06  9.844e-04  9.844e-04  8.970e+10  6.258e+04  1.154e-03  2.984e-04  1.154e-03  1.154e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 152      8.425e-11  1.584e-06  1.584e-06  2.131e+06  8.412e+00  2.765e-06  4.710e-07  2.765e-06  2.765e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  4.710e-07
+Force:                 2.247e-11
+Dilatation:    7.035e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 3 @ 3.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      2.941e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 92       1.169e-03  9.356e-02  9.356e-02  4.455e+12  4.460e+06  1.131e-01  4.961e-02  1.131e-01  1.131e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 111      3.956e-06  9.355e-04  9.355e-04  2.153e+10  4.528e+04  1.417e-03  4.916e-04  1.417e-03  1.417e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 147      2.074e-10  1.047e-06  1.047e-06  1.149e+06  7.409e+00  1.115e-06  2.439e-07  1.115e-06  1.115e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  2.439e-07
+Force:                 5.530e-11
+Dilatation:    1.059e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 4 @ 4.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      2.007e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 91       1.042e-03  1.069e-01  1.069e-01  8.138e+12  4.014e+06  9.761e-02  6.072e-02  9.761e-02  9.762e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 110      6.023e-06  1.449e-03  1.449e-03  4.399e+10  2.545e+04  1.897e-03  8.054e-04  1.897e-03  1.897e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 143      9.161e-10  1.660e-06  1.660e-06  6.590e+06  1.212e+01  1.847e-06  7.736e-07  1.847e-06  1.848e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  7.736e-07
+Force:                 2.443e-10
+Dilatation:    1.407e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 5 @ 5.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      2.191e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 94       6.965e-04  1.179e-01  1.179e-01  1.267e+12  3.437e+06  7.328e-02  6.923e-02  7.328e-02  7.329e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 114      4.389e-06  2.309e-03  2.309e-03  7.096e+09  8.901e+03  2.289e-03  1.160e-03  2.289e-03  2.289e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 138      1.580e-09  1.557e-06  1.557e-06  2.454e+06  1.600e+01  1.620e-06  5.738e-07  1.620e-06  1.620e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  5.738e-07
+Force:                 4.213e-10
+Dilatation:    1.742e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 6 @ 6.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      1.414e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 97       6.939e-04  1.230e-01  1.230e-01  2.558e+12  3.978e+06  5.486e-02  7.377e-02  5.486e-02  5.488e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 118      5.820e-06  2.713e-03  2.713e-03  1.385e+10  4.458e+03  2.263e-03  1.405e-03  2.263e-03  2.263e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 142      2.281e-09  2.218e-06  2.218e-06  7.015e+06  1.794e+01  2.396e-06  6.484e-07  2.396e-06  2.397e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.484e-07
+Force:                 6.081e-10
+Dilatation:    2.057e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 7 @ 7.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      2.699e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 93       7.507e-04  1.234e-01  1.234e-01  1.426e+12  3.501e+06  4.453e-02  7.478e-02  4.453e-02  4.455e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 116      4.617e-06  2.691e-03  2.691e-03  7.526e+09  2.451e+03  2.060e-03  1.491e-03  2.060e-03  2.060e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 147      1.854e-09  1.977e-06  1.977e-06  4.103e+06  1.274e+01  2.026e-06  7.866e-07  2.026e-06  2.026e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  7.866e-07
+Force:                 4.943e-10
+Dilatation:    2.353e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 8 @ 8.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 135      3.661e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 89       5.904e-04  1.207e-01  1.207e-01  1.432e+12  3.128e+06  3.820e-02  7.299e-02  3.820e-02  3.822e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 123      1.810e-06  2.483e-03  2.483e-03  7.548e+09  1.749e+03  1.678e-03  1.443e-03  1.678e-03  1.678e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      9.941e-10  1.233e-06  1.233e-06  3.771e+06  7.563e+00  1.039e-06  6.005e-07  1.039e-06  1.039e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.005e-07
+Force:                 2.651e-10
+Dilatation:    2.632e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 9 @ 9.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 131      3.539e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 92       4.407e-04  1.155e-01  1.155e-01  1.759e+12  3.580e+06  3.352e-02  6.924e-02  3.352e-02  3.353e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      1.937e-06  2.235e-03  2.235e-03  8.944e+09  1.778e+03  1.331e-03  1.252e-03  1.331e-03  1.331e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 154      9.421e-10  1.043e-06  1.043e-06  3.554e+06  5.187e+00  1.008e-06  4.678e-07  1.008e-06  1.008e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  4.678e-07
+Force:                 2.512e-10
+Dilatation:    2.899e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 10 @ 1.000e+00s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 137      1.947e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 92       3.365e-04  1.085e-01  1.085e-01  2.423e+12  3.045e+06  3.001e-02  6.447e-02  3.001e-02  3.003e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      1.208e-06  1.904e-03  1.904e-03  1.130e+10  1.284e+03  9.595e-04  1.011e-03  9.595e-04  9.595e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 159      4.322e-10  6.904e-07  6.904e-07  3.233e+06  2.368e+00  5.895e-07  3.490e-07  5.895e-07  5.897e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  3.490e-07
+Force:                 1.153e-10
+Dilatation:    3.157e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
diff --git a/tests/physics/step-44-standard_tensors-spatial.cc b/tests/physics/step-44-standard_tensors-spatial.cc
new file mode 100644 (file)
index 0000000..fd86f3c
--- /dev/null
@@ -0,0 +1,1970 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+// A version of step-44 that checks that some of the definitions for standard
+// tensors and kinematic quantities for elasticity are computed correctly
+
+#include "../tests.h"
+
+#include <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/quadrature_point_data.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/precondition_selector.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/sparse_direct.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+#include <deal.II/lac/iterative_inverse.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <deal.II/physics/transformations.h>
+#include <deal.II/physics/elasticity/kinematics.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+
+#include <iostream>
+#include <fstream>
+namespace Step44
+{
+  using namespace dealii;
+  namespace Parameters
+  {
+    struct FESystem
+    {
+      unsigned int poly_degree;
+      unsigned int quad_order;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void FESystem::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        prm.declare_entry("Polynomial degree", "2",
+                          Patterns::Integer(0),
+                          "Displacement system polynomial order");
+        prm.declare_entry("Quadrature order", "3",
+                          Patterns::Integer(0),
+                          "Gauss quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    void FESystem::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        poly_degree = prm.get_integer("Polynomial degree");
+        quad_order = prm.get_integer("Quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    struct Geometry
+    {
+      unsigned int global_refinement;
+      double       scale;
+      double       p_p0;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Geometry::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        prm.declare_entry("Global refinement", "2",
+                          Patterns::Integer(0),
+                          "Global refinement level");
+        prm.declare_entry("Grid scale", "1e-3",
+                          Patterns::Double(0.0),
+                          "Global grid scaling factor");
+        prm.declare_entry("Pressure ratio p/p0", "100",
+                          Patterns::Selection("20|40|60|80|100"),
+                          "Ratio of applied pressure to reference pressure");
+      }
+      prm.leave_subsection();
+    }
+    void Geometry::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        global_refinement = prm.get_integer("Global refinement");
+        scale = prm.get_double("Grid scale");
+        p_p0 = prm.get_double("Pressure ratio p/p0");
+      }
+      prm.leave_subsection();
+    }
+    struct Materials
+    {
+      double nu;
+      double mu;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Materials::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        prm.declare_entry("Poisson's ratio", "0.4999",
+                          Patterns::Double(-1.0,0.5),
+                          "Poisson's ratio");
+        prm.declare_entry("Shear modulus", "80.194e6",
+                          Patterns::Double(),
+                          "Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    void Materials::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        nu = prm.get_double("Poisson's ratio");
+        mu = prm.get_double("Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    struct LinearSolver
+    {
+      std::string type_lin;
+      double      tol_lin;
+      double      max_iterations_lin;
+      bool        use_static_condensation;
+      std::string preconditioner_type;
+      double      preconditioner_relaxation;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void LinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        prm.declare_entry("Solver type", "CG",
+                          Patterns::Selection("CG|Direct"),
+                          "Type of solver used to solve the linear system");
+        prm.declare_entry("Residual", "1e-6",
+                          Patterns::Double(0.0),
+                          "Linear solver residual (scaled by residual norm)");
+        prm.declare_entry("Max iteration multiplier", "1",
+                          Patterns::Double(0.0),
+                          "Linear solver iterations (multiples of the system matrix size)");
+        prm.declare_entry("Use static condensation", "true",
+                          Patterns::Bool(),
+                          "Solve the full block system or a reduced problem");
+        prm.declare_entry("Preconditioner type", "ssor",
+                          Patterns::Selection("jacobi|ssor"),
+                          "Type of preconditioner");
+        prm.declare_entry("Preconditioner relaxation", "0.65",
+                          Patterns::Double(0.0),
+                          "Preconditioner relaxation value");
+      }
+      prm.leave_subsection();
+    }
+    void LinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        type_lin = prm.get("Solver type");
+        tol_lin = prm.get_double("Residual");
+        max_iterations_lin = prm.get_double("Max iteration multiplier");
+        use_static_condensation = prm.get_bool("Use static condensation");
+        preconditioner_type = prm.get("Preconditioner type");
+        preconditioner_relaxation = prm.get_double("Preconditioner relaxation");
+      }
+      prm.leave_subsection();
+    }
+    struct NonlinearSolver
+    {
+      unsigned int max_iterations_NR;
+      double       tol_f;
+      double       tol_u;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void NonlinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        prm.declare_entry("Max iterations Newton-Raphson", "10",
+                          Patterns::Integer(0),
+                          "Number of Newton-Raphson iterations allowed");
+        prm.declare_entry("Tolerance force", "1.0e-9",
+                          Patterns::Double(0.0),
+                          "Force residual tolerance");
+        prm.declare_entry("Tolerance displacement", "1.0e-6",
+                          Patterns::Double(0.0),
+                          "Displacement error tolerance");
+      }
+      prm.leave_subsection();
+    }
+    void NonlinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
+        tol_f = prm.get_double("Tolerance force");
+        tol_u = prm.get_double("Tolerance displacement");
+      }
+      prm.leave_subsection();
+    }
+    struct Time
+    {
+      double delta_t;
+      double end_time;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Time::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        prm.declare_entry("End time", "1",
+                          Patterns::Double(),
+                          "End time");
+        prm.declare_entry("Time step size", "0.1",
+                          Patterns::Double(),
+                          "Time step size");
+      }
+      prm.leave_subsection();
+    }
+    void Time::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        end_time = prm.get_double("End time");
+        delta_t = prm.get_double("Time step size");
+      }
+      prm.leave_subsection();
+    }
+    struct AllParameters : public FESystem,
+      public Geometry,
+      public Materials,
+      public LinearSolver,
+      public NonlinearSolver,
+      public Time
+    {
+      AllParameters(const std::string &input_file);
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    AllParameters::AllParameters(const std::string &input_file)
+    {
+      ParameterHandler prm;
+      declare_parameters(prm);
+      prm.read_input(input_file);
+      parse_parameters(prm);
+    }
+    void AllParameters::declare_parameters(ParameterHandler &prm)
+    {
+      FESystem::declare_parameters(prm);
+      Geometry::declare_parameters(prm);
+      Materials::declare_parameters(prm);
+      LinearSolver::declare_parameters(prm);
+      NonlinearSolver::declare_parameters(prm);
+      Time::declare_parameters(prm);
+    }
+    void AllParameters::parse_parameters(ParameterHandler &prm)
+    {
+      FESystem::parse_parameters(prm);
+      Geometry::parse_parameters(prm);
+      Materials::parse_parameters(prm);
+      LinearSolver::parse_parameters(prm);
+      NonlinearSolver::parse_parameters(prm);
+      Time::parse_parameters(prm);
+    }
+  }
+  class Time
+  {
+  public:
+    Time (const double time_end,
+          const double delta_t)
+      :
+      timestep(0),
+      time_current(0.0),
+      time_end(time_end),
+      delta_t(delta_t)
+    {}
+    virtual ~Time()
+    {}
+    double current() const
+    {
+      return time_current;
+    }
+    double end() const
+    {
+      return time_end;
+    }
+    double get_delta_t() const
+    {
+      return delta_t;
+    }
+    unsigned int get_timestep() const
+    {
+      return timestep;
+    }
+    void increment()
+    {
+      time_current += delta_t;
+      ++timestep;
+    }
+  private:
+    unsigned int timestep;
+    double       time_current;
+    const double time_end;
+    const double delta_t;
+  };
+  template <int dim>
+  class Material_Compressible_Neo_Hook_Three_Field
+  {
+  public:
+    Material_Compressible_Neo_Hook_Three_Field(const double mu,
+                                               const double nu)
+      :
+      kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))),
+      c_1(mu / 2.0),
+      det_F(1.0),
+      p_tilde(0.0),
+      J_tilde(1.0),
+      b_bar(Physics::Elasticity::StandardTensors<dim>::I)
+    {
+      Assert(kappa > 0, ExcInternalError());
+    }
+    ~Material_Compressible_Neo_Hook_Three_Field()
+    {}
+    void update_material_data(const Tensor<2, dim> &F,
+                              const double p_tilde_in,
+                              const double J_tilde_in)
+    {
+      det_F = determinant(F);
+      const Tensor<2,dim> F_iso = Physics::Elasticity::Kinematics::F_iso(F);
+      b_bar = Physics::Elasticity::Kinematics::b(F_iso);
+      p_tilde = p_tilde_in;
+      J_tilde = J_tilde_in;
+      Assert(det_F > 0, ExcInternalError());
+    }
+    SymmetricTensor<2, dim> get_tau()
+    {
+      return get_tau_iso() + get_tau_vol();
+    }
+    SymmetricTensor<4, dim> get_Jc() const
+    {
+      return get_Jc_vol() + get_Jc_iso();
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde);
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return ( (kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde)));
+    }
+    double get_det_F() const
+    {
+      return det_F;
+    }
+    double get_p_tilde() const
+    {
+      return p_tilde;
+    }
+    double get_J_tilde() const
+    {
+      return J_tilde;
+    }
+  protected:
+    const double kappa;
+    const double c_1;
+    double det_F;
+    double p_tilde;
+    double J_tilde;
+    SymmetricTensor<2, dim> b_bar;
+    SymmetricTensor<2, dim> get_tau_vol() const
+    {
+      return p_tilde * det_F * Physics::Elasticity::StandardTensors<dim>::I;
+    }
+    SymmetricTensor<2, dim> get_tau_iso() const
+    {
+      return Physics::Elasticity::StandardTensors<dim>::dev_P * get_tau_bar();
+    }
+    SymmetricTensor<2, dim> get_tau_bar() const
+    {
+      return 2.0 * c_1 * b_bar;
+    }
+    SymmetricTensor<4, dim> get_Jc_vol() const
+    {
+      return p_tilde * det_F
+             * ( Physics::Elasticity::StandardTensors<dim>::IxI
+                 - (2.0 * Physics::Elasticity::StandardTensors<dim>::S) );
+    }
+    SymmetricTensor<4, dim> get_Jc_iso() const
+    {
+      const SymmetricTensor<2, dim> tau_bar = get_tau_bar();
+      const SymmetricTensor<2, dim> tau_iso = get_tau_iso();
+      const SymmetricTensor<4, dim> tau_iso_x_I
+        = outer_product(tau_iso,
+                        Physics::Elasticity::StandardTensors<dim>::I);
+      const SymmetricTensor<4, dim> I_x_tau_iso
+        = outer_product(Physics::Elasticity::StandardTensors<dim>::I,
+                        tau_iso);
+      const SymmetricTensor<4, dim> c_bar = get_c_bar();
+      return (2.0 / dim) * trace(tau_bar)
+             * Physics::Elasticity::StandardTensors<dim>::dev_P
+             - (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso)
+             + Physics::Elasticity::StandardTensors<dim>::dev_P * c_bar
+             * Physics::Elasticity::StandardTensors<dim>::dev_P;
+    }
+    SymmetricTensor<4, dim> get_c_bar() const
+    {
+      return SymmetricTensor<4, dim>();
+    }
+  };
+  template <int dim>
+  class PointHistory
+  {
+  public:
+    PointHistory()
+      :
+      F_inv(Physics::Elasticity::StandardTensors<dim>::I),
+      tau(SymmetricTensor<2, dim>()),
+      d2Psi_vol_dJ2(0.0),
+      dPsi_vol_dJ(0.0),
+      Jc(SymmetricTensor<4, dim>())
+    {}
+    virtual ~PointHistory()
+    {}
+    void setup_lqp (const Parameters::AllParameters &parameters)
+    {
+      material.reset(new Material_Compressible_Neo_Hook_Three_Field<dim>(parameters.mu,
+                     parameters.nu));
+      update_values(Tensor<2, dim>(), 0.0, 1.0);
+    }
+    void update_values (const Tensor<2, dim> &Grad_u_n,
+                        const double p_tilde,
+                        const double J_tilde)
+    {
+      const Tensor<2, dim> F = Physics::Elasticity::Kinematics::F(Grad_u_n);
+      material->update_material_data(F, p_tilde, J_tilde);
+      F_inv = invert(F);
+      tau = material->get_tau();
+      Jc = material->get_Jc();
+      dPsi_vol_dJ = material->get_dPsi_vol_dJ();
+      d2Psi_vol_dJ2 = material->get_d2Psi_vol_dJ2();
+    }
+    double get_J_tilde() const
+    {
+      return material->get_J_tilde();
+    }
+    double get_det_F() const
+    {
+      return material->get_det_F();
+    }
+    const Tensor<2, dim> &get_F_inv() const
+    {
+      return F_inv;
+    }
+    double get_p_tilde() const
+    {
+      return material->get_p_tilde();
+    }
+    const SymmetricTensor<2, dim> &get_tau() const
+    {
+      return tau;
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return dPsi_vol_dJ;
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return d2Psi_vol_dJ2;
+    }
+    const SymmetricTensor<4, dim> &get_Jc() const
+    {
+      return Jc;
+    }
+  private:
+    std_cxx11::shared_ptr< Material_Compressible_Neo_Hook_Three_Field<dim> > material;
+    Tensor<2, dim> F_inv;
+    SymmetricTensor<2, dim> tau;
+    double                  d2Psi_vol_dJ2;
+    double                  dPsi_vol_dJ;
+    SymmetricTensor<4, dim> Jc;
+  };
+  template <int dim>
+  class Solid
+  {
+  public:
+    Solid(const std::string &input_file);
+    virtual
+    ~Solid();
+    void
+    run();
+  private:
+    struct PerTaskData_K;
+    struct ScratchData_K;
+    struct PerTaskData_RHS;
+    struct ScratchData_RHS;
+    struct PerTaskData_SC;
+    struct ScratchData_SC;
+    struct PerTaskData_UQPH;
+    struct ScratchData_UQPH;
+    void
+    make_grid();
+    void
+    system_setup();
+    void
+    determine_component_extractors();
+    void
+    assemble_system_tangent();
+    void
+    assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                     ScratchData_K &scratch,
+                                     PerTaskData_K &data) const;
+    void
+    copy_local_to_global_K(const PerTaskData_K &data);
+    void
+    assemble_system_rhs();
+    void
+    assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                 ScratchData_RHS &scratch,
+                                 PerTaskData_RHS &data) const;
+    void
+    copy_local_to_global_rhs(const PerTaskData_RHS &data);
+    void
+    assemble_sc();
+    void
+    assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                         ScratchData_SC &scratch,
+                         PerTaskData_SC &data);
+    void
+    copy_local_to_global_sc(const PerTaskData_SC &data);
+    void
+    make_constraints(const int &it_nr);
+    void
+    setup_qph();
+    void
+    update_qph_incremental(const BlockVector<double> &solution_delta);
+    void
+    update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                    ScratchData_UQPH &scratch,
+                                    PerTaskData_UQPH &data);
+    void
+    copy_local_to_global_UQPH(const PerTaskData_UQPH &/*data*/)
+    {}
+    void
+    solve_nonlinear_timestep(BlockVector<double> &solution_delta);
+    std::pair<unsigned int, double>
+    solve_linear_system(BlockVector<double> &newton_update);
+    BlockVector<double>
+    get_total_solution(const BlockVector<double> &solution_delta) const;
+    void
+    output_results() const;
+    Parameters::AllParameters parameters;
+    double                    vol_reference;
+    Triangulation<dim>        triangulation;
+    Time                      time;
+    mutable TimerOutput       timer;
+    CellDataStorage<typename Triangulation<dim>::cell_iterator,
+                    PointHistory<dim> > quadrature_point_history;
+    const unsigned int               degree;
+    const FESystem<dim>              fe;
+    DoFHandler<dim>                  dof_handler_ref;
+    const unsigned int               dofs_per_cell;
+    const FEValuesExtractors::Vector u_fe;
+    const FEValuesExtractors::Scalar p_fe;
+    const FEValuesExtractors::Scalar J_fe;
+    static const unsigned int n_blocks = 3;
+    static const unsigned int n_components = dim + 2;
+    static const unsigned int first_u_component = 0;
+    static const unsigned int p_component = dim;
+    static const unsigned int J_component = dim + 1;
+    enum
+    {
+      u_dof = 0,
+      p_dof = 1,
+      J_dof = 2
+    };
+    std::vector<types::global_dof_index> dofs_per_block;
+    std::vector<types::global_dof_index> element_indices_u;
+    std::vector<types::global_dof_index> element_indices_p;
+    std::vector<types::global_dof_index> element_indices_J;
+    const QGauss<dim>     qf_cell;
+    const QGauss<dim - 1> qf_face;
+    const unsigned int    n_q_points;
+    const unsigned int    n_q_points_f;
+    ConstraintMatrix          constraints;
+    BlockSparsityPattern      sparsity_pattern;
+    BlockSparseMatrix<double> tangent_matrix;
+    BlockVector<double>       system_rhs;
+    BlockVector<double>       solution_n;
+    ConditionalOStream        pcout;
+    struct Errors
+    {
+      Errors()
+        :
+        norm(1.0), u(1.0), p(1.0), J(1.0)
+      {}
+      void reset()
+      {
+        norm = 1.0;
+        u = 1.0;
+        p = 1.0;
+        J = 1.0;
+      }
+      void normalise(const Errors &rhs)
+      {
+        if (rhs.norm != 0.0)
+          norm /= rhs.norm;
+        if (rhs.u != 0.0)
+          u /= rhs.u;
+        if (rhs.p != 0.0)
+          p /= rhs.p;
+        if (rhs.J != 0.0)
+          J /= rhs.J;
+      }
+      double norm, u, p, J;
+    };
+    Errors error_residual, error_residual_0, error_residual_norm, error_update,
+           error_update_0, error_update_norm;
+    void
+    get_error_residual(Errors &error_residual);
+    void
+    get_error_update(const BlockVector<double> &newton_update,
+                     Errors &error_update);
+    std::pair<double, double>
+    get_error_dilation() const;
+    double
+    compute_vol_current () const;
+    void
+    print_conv_header();
+    void
+    print_conv_footer();
+  };
+  template <int dim>
+  Solid<dim>::Solid(const std::string &input_file)
+    :
+    parameters(input_file),
+    triangulation(Triangulation<dim>::maximum_smoothing),
+    time(parameters.end_time, parameters.delta_t),
+    timer(deallog.get_file_stream(),
+          TimerOutput::never,
+          TimerOutput::wall_times),
+    degree(parameters.poly_degree),
+    fe(FE_Q<dim>(parameters.poly_degree), dim, // displacement
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1, // pressure
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1), // dilatation
+    dof_handler_ref(triangulation),
+    dofs_per_cell (fe.dofs_per_cell),
+    u_fe(first_u_component),
+    p_fe(p_component),
+    J_fe(J_component),
+    dofs_per_block(n_blocks),
+    qf_cell(parameters.quad_order),
+    qf_face(parameters.quad_order),
+    n_q_points (qf_cell.size()),
+    n_q_points_f (qf_face.size()),
+    pcout(deallog.get_file_stream())
+  {
+    Assert(dim==2 || dim==3, ExcMessage("This problem only works in 2 or 3 space dimensions."));
+    determine_component_extractors();
+  }
+  template <int dim>
+  Solid<dim>::~Solid()
+  {
+    dof_handler_ref.clear();
+  }
+  template <int dim>
+  void Solid<dim>::run()
+  {
+    make_grid();
+    system_setup();
+    {
+      ConstraintMatrix constraints;
+      constraints.close();
+      const ComponentSelectFunction<dim>
+      J_mask (J_component, n_components);
+      VectorTools::project (dof_handler_ref,
+                            constraints,
+                            QGauss<dim>(degree+2),
+                            J_mask,
+                            solution_n);
+    }
+    output_results();
+    time.increment();
+    BlockVector<double> solution_delta(dofs_per_block);
+    while (time.current() < time.end())
+      {
+        solution_delta = 0.0;
+        solve_nonlinear_timestep(solution_delta);
+        solution_n += solution_delta;
+        output_results();
+        time.increment();
+      }
+  }
+  template <int dim>
+  struct Solid<dim>::PerTaskData_K
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_K(const unsigned int dofs_per_cell)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_matrix = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_K
+  {
+    FEValues<dim> fe_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<Tensor<2, dim> > >          grad_Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_K(const FiniteElement<dim> &fe_cell,
+                  const QGauss<dim> &qf_cell,
+                  const UpdateFlags uf_cell)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      grad_Nx(qf_cell.size(),
+              std::vector<Tensor<2, dim> >(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_K(const ScratchData_K &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      grad_Nx(rhs.grad_Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              grad_Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_RHS
+  {
+    Vector<double>            cell_rhs;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_RHS(const unsigned int dofs_per_cell)
+      :
+      cell_rhs(dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_rhs = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_RHS
+  {
+    FEValues<dim>     fe_values_ref;
+    FEFaceValues<dim> fe_face_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_RHS(const FiniteElement<dim> &fe_cell,
+                    const QGauss<dim> &qf_cell, const UpdateFlags uf_cell,
+                    const QGauss<dim - 1> & qf_face, const UpdateFlags uf_face)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      fe_face_values_ref(fe_cell, qf_face, uf_face),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_RHS(const ScratchData_RHS &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
+                         rhs.fe_face_values_ref.get_quadrature(),
+                         rhs.fe_face_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points      = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_SC
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    FullMatrix<double>        k_orig;
+    FullMatrix<double>        k_pu;
+    FullMatrix<double>        k_pJ;
+    FullMatrix<double>        k_JJ;
+    FullMatrix<double>        k_pJ_inv;
+    FullMatrix<double>        k_bbar;
+    FullMatrix<double>        A;
+    FullMatrix<double>        B;
+    FullMatrix<double>        C;
+    PerTaskData_SC(const unsigned int dofs_per_cell,
+                   const unsigned int n_u,
+                   const unsigned int n_p,
+                   const unsigned int n_J)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell),
+      k_orig(dofs_per_cell, dofs_per_cell),
+      k_pu(n_p, n_u),
+      k_pJ(n_p, n_J),
+      k_JJ(n_J, n_J),
+      k_pJ_inv(n_p, n_J),
+      k_bbar(n_u, n_u),
+      A(n_J,n_u),
+      B(n_J, n_u),
+      C(n_p, n_u)
+    {}
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_SC
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_UQPH
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_UQPH
+  {
+    const BlockVector<double>   &solution_total;
+    std::vector<Tensor<2, dim> > solution_grads_u_total;
+    std::vector<double>          solution_values_p_total;
+    std::vector<double>          solution_values_J_total;
+    FEValues<dim>                fe_values_ref;
+    ScratchData_UQPH(const FiniteElement<dim> &fe_cell,
+                     const QGauss<dim> &qf_cell,
+                     const UpdateFlags uf_cell,
+                     const BlockVector<double> &solution_total)
+      :
+      solution_total(solution_total),
+      solution_grads_u_total(qf_cell.size()),
+      solution_values_p_total(qf_cell.size()),
+      solution_values_J_total(qf_cell.size()),
+      fe_values_ref(fe_cell, qf_cell, uf_cell)
+    {}
+    ScratchData_UQPH(const ScratchData_UQPH &rhs)
+      :
+      solution_total(rhs.solution_total),
+      solution_grads_u_total(rhs.solution_grads_u_total),
+      solution_values_p_total(rhs.solution_values_p_total),
+      solution_values_J_total(rhs.solution_values_J_total),
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags())
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = solution_grads_u_total.size();
+      for (unsigned int q = 0; q < n_q_points; ++q)
+        {
+          solution_grads_u_total[q] = 0.0;
+          solution_values_p_total[q] = 0.0;
+          solution_values_J_total[q] = 0.0;
+        }
+    }
+  };
+  template <int dim>
+  void Solid<dim>::make_grid()
+  {
+    GridGenerator::hyper_rectangle(triangulation,
+                                   (dim==3 ? Point<dim>(0.0, 0.0, 0.0) : Point<dim>(0.0, 0.0)),
+                                   (dim==3 ? Point<dim>(1.0, 1.0, 1.0) : Point<dim>(1.0, 1.0)),
+                                   true);
+    GridTools::scale(parameters.scale, triangulation);
+    triangulation.refine_global(std::max (1U, parameters.global_refinement));
+    vol_reference = GridTools::volume(triangulation);
+    pcout << "Grid:\n\t Reference volume: " << vol_reference << std::endl;
+    typename Triangulation<dim>::active_cell_iterator cell =
+      triangulation.begin_active(), endc = triangulation.end();
+    for (; cell != endc; ++cell)
+      for (unsigned int face = 0;
+           face < GeometryInfo<dim>::faces_per_cell; ++face)
+        {
+          if (cell->face(face)->at_boundary() == true
+              &&
+              cell->face(face)->center()[1] == 1.0 * parameters.scale)
+            {
+              if (dim==3)
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale
+                      &&
+                      cell->face(face)->center()[2] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+              else
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::system_setup()
+  {
+    timer.enter_subsection("Setup system");
+    std::vector<unsigned int> block_component(n_components, u_dof); // Displacement
+    block_component[p_component] = p_dof; // Pressure
+    block_component[J_component] = J_dof; // Dilatation
+    dof_handler_ref.distribute_dofs(fe);
+    DoFRenumbering::Cuthill_McKee(dof_handler_ref);
+    DoFRenumbering::component_wise(dof_handler_ref, block_component);
+    DoFTools::count_dofs_per_block(dof_handler_ref, dofs_per_block,
+                                   block_component);
+    pcout << "Triangulation:"
+          << "\n\t Number of active cells: " << triangulation.n_active_cells()
+          << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
+          << std::endl;
+    tangent_matrix.clear();
+    {
+      const types::global_dof_index n_dofs_u = dofs_per_block[u_dof];
+      const types::global_dof_index n_dofs_p = dofs_per_block[p_dof];
+      const types::global_dof_index n_dofs_J = dofs_per_block[J_dof];
+      BlockDynamicSparsityPattern dsp(n_blocks, n_blocks);
+      dsp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u);
+      dsp.block(u_dof, p_dof).reinit(n_dofs_u, n_dofs_p);
+      dsp.block(u_dof, J_dof).reinit(n_dofs_u, n_dofs_J);
+      dsp.block(p_dof, u_dof).reinit(n_dofs_p, n_dofs_u);
+      dsp.block(p_dof, p_dof).reinit(n_dofs_p, n_dofs_p);
+      dsp.block(p_dof, J_dof).reinit(n_dofs_p, n_dofs_J);
+      dsp.block(J_dof, u_dof).reinit(n_dofs_J, n_dofs_u);
+      dsp.block(J_dof, p_dof).reinit(n_dofs_J, n_dofs_p);
+      dsp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J);
+      dsp.collect_sizes();
+      Table<2, DoFTools::Coupling> coupling(n_components, n_components);
+      for (unsigned int ii = 0; ii < n_components; ++ii)
+        for (unsigned int jj = 0; jj < n_components; ++jj)
+          if (((ii < p_component) && (jj == J_component))
+              || ((ii == J_component) && (jj < p_component))
+              || ((ii == p_component) && (jj == p_component)))
+            coupling[ii][jj] = DoFTools::none;
+          else
+            coupling[ii][jj] = DoFTools::always;
+      DoFTools::make_sparsity_pattern(dof_handler_ref,
+                                      coupling,
+                                      dsp,
+                                      constraints,
+                                      false);
+      sparsity_pattern.copy_from(dsp);
+    }
+    tangent_matrix.reinit(sparsity_pattern);
+    system_rhs.reinit(dofs_per_block);
+    system_rhs.collect_sizes();
+    solution_n.reinit(dofs_per_block);
+    solution_n.collect_sizes();
+    setup_qph();
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::determine_component_extractors()
+  {
+    element_indices_u.clear();
+    element_indices_p.clear();
+    element_indices_J.clear();
+    for (unsigned int k = 0; k < fe.dofs_per_cell; ++k)
+      {
+        const unsigned int k_group = fe.system_to_base_index(k).first.first;
+        if (k_group == u_dof)
+          element_indices_u.push_back(k);
+        else if (k_group == p_dof)
+          element_indices_p.push_back(k);
+        else if (k_group == J_dof)
+          element_indices_J.push_back(k);
+        else
+          {
+            Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+  }
+  template <int dim>
+  void Solid<dim>::setup_qph()
+  {
+    pcout << "    Setting up quadrature point data..." << std::endl;
+    quadrature_point_history.initialize(triangulation.begin_active(),
+                                        triangulation.end(),
+                                        n_q_points);
+    for (typename Triangulation<dim>::active_cell_iterator cell =
+           triangulation.begin_active(); cell != triangulation.end(); ++cell)
+      {
+        const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          lqph[q_point]->setup_lqp(parameters);
+      }
+  }
+  template <int dim>
+  void Solid<dim>::update_qph_incremental(const BlockVector<double> &solution_delta)
+  {
+    timer.enter_subsection("Update QPH data");
+    pcout << " UQPH " << std::flush;
+    const BlockVector<double> solution_total(get_total_solution(solution_delta));
+    const UpdateFlags uf_UQPH(update_values | update_gradients);
+    PerTaskData_UQPH per_task_data_UQPH;
+    ScratchData_UQPH scratch_data_UQPH(fe, qf_cell, uf_UQPH, solution_total);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::update_qph_incremental_one_cell,
+                    &Solid::copy_local_to_global_UQPH,
+                    scratch_data_UQPH,
+                    per_task_data_UQPH);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                              ScratchData_UQPH &scratch,
+                                              PerTaskData_UQPH &/*data*/)
+  {
+    const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    Assert(scratch.solution_grads_u_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_p_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_J_total.size() == n_q_points,
+           ExcInternalError());
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total,
+                                                       scratch.solution_grads_u_total);
+    scratch.fe_values_ref[p_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_p_total);
+    scratch.fe_values_ref[J_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_J_total);
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      lqph[q_point]->update_values(scratch.solution_grads_u_total[q_point],
+                                   scratch.solution_values_p_total[q_point],
+                                   scratch.solution_values_J_total[q_point]);
+  }
+  template <int dim>
+  void
+  Solid<dim>::solve_nonlinear_timestep(BlockVector<double> &solution_delta)
+  {
+    pcout << std::endl << "Timestep " << time.get_timestep() << " @ "
+          << time.current() << "s" << std::endl;
+    BlockVector<double> newton_update(dofs_per_block);
+    error_residual.reset();
+    error_residual_0.reset();
+    error_residual_norm.reset();
+    error_update.reset();
+    error_update_0.reset();
+    error_update_norm.reset();
+    print_conv_header();
+    unsigned int newton_iteration = 0;
+    for (; newton_iteration < parameters.max_iterations_NR;
+         ++newton_iteration)
+      {
+        pcout << " " << std::setw(2) << newton_iteration << " " << std::flush;
+        tangent_matrix = 0.0;
+        system_rhs = 0.0;
+        assemble_system_rhs();
+        get_error_residual(error_residual);
+        if (newton_iteration == 0)
+          error_residual_0 = error_residual;
+        error_residual_norm = error_residual;
+        error_residual_norm.normalise(error_residual_0);
+        if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u
+            && error_residual_norm.u <= parameters.tol_f)
+          {
+            pcout << " CONVERGED! " << std::endl;
+            print_conv_footer();
+            break;
+          }
+        assemble_system_tangent();
+        make_constraints(newton_iteration);
+        constraints.condense(tangent_matrix, system_rhs);
+        const std::pair<unsigned int, double>
+        lin_solver_output = solve_linear_system(newton_update);
+        get_error_update(newton_update, error_update);
+        if (newton_iteration == 0)
+          error_update_0 = error_update;
+        error_update_norm = error_update;
+        error_update_norm.normalise(error_update_0);
+        solution_delta += newton_update;
+        update_qph_incremental(solution_delta);
+        pcout << " | " << std::fixed << std::setprecision(3) << std::setw(7)
+              << std::scientific << lin_solver_output.first << "  "
+              << lin_solver_output.second << "  " << error_residual_norm.norm
+              << "  " << error_residual_norm.u << "  "
+              << error_residual_norm.p << "  " << error_residual_norm.J
+              << "  " << error_update_norm.norm << "  " << error_update_norm.u
+              << "  " << error_update_norm.p << "  " << error_update_norm.J
+              << "  " << std::endl;
+      }
+    AssertThrow (newton_iteration <= parameters.max_iterations_NR,
+                 ExcMessage("No convergence in nonlinear solver!"));
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_header()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    pcout << "                 SOLVER STEP                  "
+          << " |  LIN_IT   LIN_RES    RES_NORM    "
+          << " RES_U     RES_P      RES_J     NU_NORM     "
+          << " NU_U       NU_P       NU_J " << std::endl;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_footer()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    const std::pair<double,double> error_dil = get_error_dilation();
+    pcout << "Relative errors:" << std::endl
+          << "Displacement:\t" << error_update.u / error_update_0.u << std::endl
+          << "Force: \t\t" << error_residual.u / error_residual_0.u << std::endl
+          << "Dilatation:\t" << error_dil.first << std::endl
+          << "v / V_0:\t" << error_dil.second *vol_reference << " / " << vol_reference
+          << " = " << error_dil.second << std::endl;
+  }
+  template <int dim>
+  double
+  Solid<dim>::compute_vol_current() const
+  {
+    double vol_current = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double JxW = fe_values_ref.JxW(q_point);
+            vol_current += det_F_qp * JxW;
+          }
+      }
+    Assert(vol_current > 0.0, ExcInternalError());
+    return vol_current;
+  }
+  template <int dim>
+  std::pair<double, double>
+  Solid<dim>::get_error_dilation() const
+  {
+    double dil_L2_error = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double J_tilde_qp = lqph[q_point]->get_J_tilde();
+            const double the_error_qp_squared = std::pow((det_F_qp - J_tilde_qp),
+                                                         2);
+            const double JxW = fe_values_ref.JxW(q_point);
+            dil_L2_error += the_error_qp_squared * JxW;
+          }
+      }
+    return std::make_pair(std::sqrt(dil_L2_error),
+                          compute_vol_current() / vol_reference);
+  }
+  template <int dim>
+  void Solid<dim>::get_error_residual(Errors &error_residual)
+  {
+    BlockVector<double> error_res(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_res(i) = system_rhs(i);
+    error_residual.norm = error_res.l2_norm();
+    error_residual.u = error_res.block(u_dof).l2_norm();
+    error_residual.p = error_res.block(p_dof).l2_norm();
+    error_residual.J = error_res.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  void Solid<dim>::get_error_update(const BlockVector<double> &newton_update,
+                                    Errors &error_update)
+  {
+    BlockVector<double> error_ud(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_ud(i) = newton_update(i);
+    error_update.norm = error_ud.l2_norm();
+    error_update.u = error_ud.block(u_dof).l2_norm();
+    error_update.p = error_ud.block(p_dof).l2_norm();
+    error_update.J = error_ud.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  BlockVector<double>
+  Solid<dim>::get_total_solution(const BlockVector<double> &solution_delta) const
+  {
+    BlockVector<double> solution_total(solution_n);
+    solution_total += solution_delta;
+    return solution_total;
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_tangent()
+  {
+    timer.enter_subsection("Assemble tangent matrix");
+    pcout << " ASM_K " << std::flush;
+    tangent_matrix = 0.0;
+    const UpdateFlags uf_cell(update_values    |
+                              update_gradients |
+                              update_JxW_values);
+    PerTaskData_K per_task_data(dofs_per_cell);
+    ScratchData_K scratch_data(fe, qf_cell, uf_cell);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_tangent_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_K,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_K(const PerTaskData_K &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                               ScratchData_K &scratch,
+                                               PerTaskData_K &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              {
+                scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                                              * F_inv;
+                scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
+              }
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> tau         = lqph[q_point]->get_tau();
+        const SymmetricTensor<4, dim> Jc = lqph[q_point]->get_Jc();
+        const double d2Psi_vol_dJ2       = lqph[q_point]->get_d2Psi_vol_dJ2();
+        const double det_F               = lqph[q_point]->get_det_F();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const std::vector<Tensor<2, dim> >
+        &grad_Nx = scratch.grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int component_i = fe.system_to_component_index(i).first;
+            const unsigned int i_group     = fe.system_to_base_index(i).first.first;
+            for (unsigned int j = 0; j <= i; ++j)
+              {
+                const unsigned int component_j = fe.system_to_component_index(j).first;
+                const unsigned int j_group     = fe.system_to_base_index(j).first.first;
+                if ((i_group == j_group) && (i_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution:
+                                              * symm_grad_Nx[j] * JxW;
+                    if (component_i == component_j) // geometrical stress contribution
+                      data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau
+                                                * grad_Nx[j][component_j] * JxW;
+                  }
+                else if ((i_group == p_dof) && (j_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += N[i] * det_F
+                                              * (symm_grad_Nx[j]
+                                                 * Physics::Elasticity::StandardTensors<dim>::I)
+                                              * JxW;
+                  }
+                else if ((i_group == J_dof) && (j_group == p_dof))
+                  data.cell_matrix(i, j) -= N[i] * N[j] * JxW;
+                else if ((i_group == j_group) && (i_group == J_dof))
+                  data.cell_matrix(i, j) += N[i] * d2Psi_vol_dJ2 * N[j] * JxW;
+                else
+                  Assert((i_group <= J_dof) && (j_group <= J_dof),
+                         ExcInternalError());
+              }
+          }
+      }
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
+        data.cell_matrix(i, j) = data.cell_matrix(j, i);
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_rhs()
+  {
+    timer.enter_subsection("Assemble system right-hand side");
+    pcout << " ASM_R " << std::flush;
+    system_rhs = 0.0;
+    const UpdateFlags uf_cell(update_values |
+                              update_gradients |
+                              update_JxW_values);
+    const UpdateFlags uf_face(update_values |
+                              update_normal_vectors |
+                              update_JxW_values);
+    PerTaskData_RHS per_task_data(dofs_per_cell);
+    ScratchData_RHS scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_rhs_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_rhs,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_rhs(const PerTaskData_RHS &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      system_rhs(data.local_dof_indices[i]) += data.cell_rhs(i);
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                           ScratchData_RHS &scratch,
+                                           PerTaskData_RHS &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              scratch.symm_grad_Nx[q_point][k]
+                = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                             * F_inv);
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const SymmetricTensor<2, dim> tau = lqph[q_point]->get_tau();
+        const double det_F = lqph[q_point]->get_det_F();
+        const double J_tilde = lqph[q_point]->get_J_tilde();
+        const double p_tilde = lqph[q_point]->get_p_tilde();
+        const double dPsi_vol_dJ = lqph[q_point]->get_dPsi_vol_dJ();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int i_group = fe.system_to_base_index(i).first.first;
+            if (i_group == u_dof)
+              data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW;
+            else if (i_group == p_dof)
+              data.cell_rhs(i) -= N[i] * (det_F - J_tilde) * JxW;
+            else if (i_group == J_dof)
+              data.cell_rhs(i) -= N[i] * (dPsi_vol_dJ - p_tilde) * JxW;
+            else
+              Assert(i_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell;
+         ++face)
+      if (cell->face(face)->at_boundary() == true
+          && cell->face(face)->boundary_id() == 6)
+        {
+          scratch.fe_face_values_ref.reinit(cell, face);
+          for (unsigned int f_q_point = 0; f_q_point < n_q_points_f;
+               ++f_q_point)
+            {
+              const Tensor<1, dim> &N =
+                scratch.fe_face_values_ref.normal_vector(f_q_point);
+              static const double  p0        = -4.0
+                                               /
+                                               (parameters.scale * parameters.scale);
+              const double         time_ramp = (time.current() / time.end());
+              const double         pressure  = p0 * parameters.p_p0 * time_ramp;
+              const Tensor<1, dim> traction  = pressure * N;
+              for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                {
+                  const unsigned int i_group =
+                    fe.system_to_base_index(i).first.first;
+                  if (i_group == u_dof)
+                    {
+                      const unsigned int component_i =
+                        fe.system_to_component_index(i).first;
+                      const double Ni =
+                        scratch.fe_face_values_ref.shape_value(i,
+                                                               f_q_point);
+                      const double JxW = scratch.fe_face_values_ref.JxW(
+                                           f_q_point);
+                      data.cell_rhs(i) += (Ni * traction[component_i])
+                                          * JxW;
+                    }
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::make_constraints(const int &it_nr)
+  {
+    pcout << " CST " << std::flush;
+    if (it_nr > 1)
+      return;
+    constraints.clear();
+    const bool apply_dirichlet_bc = (it_nr == 0);
+    const FEValuesExtractors::Scalar x_displacement(0);
+    const FEValuesExtractors::Scalar y_displacement(1);
+    {
+      const int boundary_id = 0;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+    }
+    {
+      const int boundary_id = 2;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+    }
+    if (dim==3)
+      {
+        const FEValuesExtractors::Scalar z_displacement(2);
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+        {
+          const int boundary_id = 4;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+      }
+    else
+      {
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+      }
+    constraints.close();
+  }
+  template <int dim>
+  void Solid<dim>::assemble_sc()
+  {
+    timer.enter_subsection("Perform static condensation");
+    pcout << " ASM_SC " << std::flush;
+    PerTaskData_SC per_task_data(dofs_per_cell, element_indices_u.size(),
+                                 element_indices_p.size(),
+                                 element_indices_J.size());
+    ScratchData_SC scratch_data;
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::assemble_sc_one_cell,
+                    &Solid::copy_local_to_global_sc,
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_sc(const PerTaskData_SC &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                   ScratchData_SC &scratch,
+                                   PerTaskData_SC &data)
+  {
+    data.reset();
+    scratch.reset();
+    cell->get_dof_indices(data.local_dof_indices);
+    data.k_orig.extract_submatrix_from(tangent_matrix,
+                                       data.local_dof_indices,
+                                       data.local_dof_indices);
+    data.k_pu.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_u);
+    data.k_pJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_J);
+    data.k_JJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_J,
+                                     element_indices_J);
+    data.k_pJ_inv.invert(data.k_pJ);
+    data.k_pJ_inv.mmult(data.A, data.k_pu);
+    data.k_JJ.mmult(data.B, data.A);
+    data.k_pJ_inv.Tmmult(data.C, data.B);
+    data.k_pu.Tmmult(data.k_bbar, data.C);
+    data.k_bbar.scatter_matrix_to(element_indices_u,
+                                  element_indices_u,
+                                  data.cell_matrix);
+    data.k_pJ_inv.add(-1.0, data.k_pJ);
+    data.k_pJ_inv.scatter_matrix_to(element_indices_p,
+                                    element_indices_J,
+                                    data.cell_matrix);
+  }
+  template <int dim>
+  std::pair<unsigned int, double>
+  Solid<dim>::solve_linear_system(BlockVector<double> &newton_update)
+  {
+    unsigned int lin_it = 0;
+    double lin_res = 0.0;
+    if (parameters.use_static_condensation == true)
+      {
+        BlockVector<double> A(dofs_per_block);
+        BlockVector<double> B(dofs_per_block);
+        {
+          assemble_sc();
+          tangent_matrix.block(p_dof, J_dof).vmult(A.block(J_dof),
+                                                   system_rhs.block(p_dof));
+          tangent_matrix.block(J_dof, J_dof).vmult(B.block(J_dof),
+                                                   A.block(J_dof));
+          A.block(J_dof) = system_rhs.block(J_dof);
+          A.block(J_dof) -= B.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(A.block(p_dof),
+                                                    A.block(J_dof));
+          tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof),
+                                                   A.block(p_dof));
+          system_rhs.block(u_dof) -= A.block(u_dof);
+          timer.enter_subsection("Linear solver");
+          pcout << " SLV " << std::flush;
+          if (parameters.type_lin == "CG")
+            {
+              const int solver_its = tangent_matrix.block(u_dof, u_dof).m()
+                                     * parameters.max_iterations_lin;
+              const double tol_sol = parameters.tol_lin
+                                     * system_rhs.block(u_dof).l2_norm();
+              SolverControl solver_control(solver_its, tol_sol);
+              GrowingVectorMemory<Vector<double> > GVM;
+              SolverCG<Vector<double> > solver_CG(solver_control, GVM);
+              PreconditionSelector<SparseMatrix<double>, Vector<double> >
+              preconditioner (parameters.preconditioner_type,
+                              parameters.preconditioner_relaxation);
+              preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof));
+              solver_CG.solve(tangent_matrix.block(u_dof, u_dof),
+                              newton_update.block(u_dof),
+                              system_rhs.block(u_dof),
+                              preconditioner);
+              lin_it = solver_control.last_step();
+              lin_res = solver_control.last_value();
+            }
+          else if (parameters.type_lin == "Direct")
+            {
+              SparseDirectUMFPACK A_direct;
+              A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
+              A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof));
+              lin_it = 1;
+              lin_res = 0.0;
+            }
+          else
+            Assert (false, ExcMessage("Linear solver type not implemented"));
+          timer.leave_subsection();
+        }
+        constraints.distribute(newton_update);
+        timer.enter_subsection("Linear solver postprocessing");
+        pcout << " PP " << std::flush;
+        {
+          tangent_matrix.block(p_dof, u_dof).vmult(A.block(p_dof),
+                                                   newton_update.block(u_dof));
+          A.block(p_dof) *= -1.0;
+          A.block(p_dof) += system_rhs.block(p_dof);
+          tangent_matrix.block(p_dof, J_dof).vmult(newton_update.block(J_dof),
+                                                   A.block(p_dof));
+        }
+        constraints.distribute(newton_update);
+        {
+          tangent_matrix.block(J_dof, J_dof).vmult(A.block(J_dof),
+                                                   newton_update.block(J_dof));
+          A.block(J_dof) *= -1.0;
+          A.block(J_dof) += system_rhs.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(newton_update.block(p_dof),
+                                                    A.block(J_dof));
+        }
+        constraints.distribute(newton_update);
+        timer.leave_subsection();
+      }
+    else
+      {
+        pcout << " ------ " << std::flush;
+        timer.enter_subsection("Linear solver");
+        pcout << " SLV " << std::flush;
+        if (parameters.type_lin == "CG")
+          {
+            const Vector<double> &f_u = system_rhs.block(u_dof);
+            const Vector<double> &f_p = system_rhs.block(p_dof);
+            const Vector<double> &f_J = system_rhs.block(J_dof);
+            Vector<double> &d_u = newton_update.block(u_dof);
+            Vector<double> &d_p = newton_update.block(p_dof);
+            Vector<double> &d_J = newton_update.block(J_dof);
+            const auto K_uu = linear_operator(tangent_matrix.block(u_dof, u_dof));
+            const auto K_up = linear_operator(tangent_matrix.block(u_dof, p_dof));
+            const auto K_pu = linear_operator(tangent_matrix.block(p_dof, u_dof));
+            const auto K_Jp = linear_operator(tangent_matrix.block(J_dof, p_dof));
+            const auto K_JJ = linear_operator(tangent_matrix.block(J_dof, J_dof));
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_Jp_inv ("jacobi");
+            preconditioner_K_Jp_inv.use_matrix(tangent_matrix.block(J_dof, p_dof));
+            ReductionControl solver_control_K_Jp_inv (tangent_matrix.block(J_dof, p_dof).m() * parameters.max_iterations_lin,
+                                                      1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_Jp_inv;
+            solver_K_Jp_inv.select("cg");
+            solver_K_Jp_inv.set_control(solver_control_K_Jp_inv);
+            const auto K_Jp_inv = inverse_operator(K_Jp,
+                                                   solver_K_Jp_inv,
+                                                   preconditioner_K_Jp_inv);
+            const auto K_pJ_inv     = transpose_operator(K_Jp_inv);
+            const auto K_pp_bar     = K_Jp_inv * K_JJ * K_pJ_inv;
+            const auto K_uu_bar_bar = K_up * K_pp_bar * K_pu;
+            const auto K_uu_con     = K_uu + K_uu_bar_bar;
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_con_inv (parameters.preconditioner_type,
+                                      parameters.preconditioner_relaxation);
+            preconditioner_K_con_inv.use_matrix(tangent_matrix.block(u_dof, u_dof));
+            ReductionControl solver_control_K_con_inv (tangent_matrix.block(u_dof, u_dof).m() * parameters.max_iterations_lin,
+                                                       1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_con_inv;
+            solver_K_con_inv.select("cg");
+            solver_K_con_inv.set_control(solver_control_K_con_inv);
+            const auto K_uu_con_inv = inverse_operator(K_uu_con,
+                                                       solver_K_con_inv,
+                                                       preconditioner_K_con_inv);
+            d_u = K_uu_con_inv*(f_u - K_up*(K_Jp_inv*f_J - K_pp_bar*f_p));
+            timer.leave_subsection();
+            timer.enter_subsection("Linear solver postprocessing");
+            pcout << " PP " << std::flush;
+            d_J = K_pJ_inv*(f_p - K_pu*d_u);
+            d_p = K_Jp_inv*(f_J - K_JJ*d_J);
+            lin_it = solver_control_K_con_inv.last_step();
+            lin_res = solver_control_K_con_inv.last_value();
+          }
+        else if (parameters.type_lin == "Direct")
+          {
+            SparseDirectUMFPACK A_direct;
+            A_direct.initialize(tangent_matrix);
+            A_direct.vmult(newton_update, system_rhs);
+            lin_it = 1;
+            lin_res = 0.0;
+            pcout << " -- " << std::flush;
+          }
+        else
+          Assert (false, ExcMessage("Linear solver type not implemented"));
+        timer.leave_subsection();
+        constraints.distribute(newton_update);
+      }
+    return std::make_pair(lin_it, lin_res);
+  }
+  template <int dim>
+  void Solid<dim>::output_results() const
+  {
+    DataOut<dim> data_out;
+    std::vector<DataComponentInterpretation::DataComponentInterpretation>
+    data_component_interpretation(dim,
+                                  DataComponentInterpretation::component_is_part_of_vector);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    std::vector<std::string> solution_name(dim, "displacement");
+    solution_name.push_back("pressure");
+    solution_name.push_back("dilatation");
+    data_out.attach_dof_handler(dof_handler_ref);
+    data_out.add_data_vector(solution_n,
+                             solution_name,
+                             DataOut<dim>::type_dof_data,
+                             data_component_interpretation);
+    Vector<double> soln(solution_n.size());
+    for (unsigned int i = 0; i < soln.size(); ++i)
+      soln(i) = solution_n(i);
+    MappingQEulerian<dim> q_mapping(degree, dof_handler_ref, soln);
+    data_out.build_patches(q_mapping, degree);
+    std::ostringstream filename;
+    filename << "solution-" << dim << "d-" << time.get_timestep() << ".vtk";
+    std::ofstream output(filename.str().c_str());
+    data_out.write_vtk(output);
+  }
+}
+int main (int argc,char **argv)
+{
+  std::ofstream logfile("output");
+  deallog.attach(logfile);
+  deallog.depth_file(0);
+  deallog.threshold_double(1.e-10);
+
+  Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, testing_max_num_threads());
+  using namespace dealii;
+  using namespace Step44;
+  try
+    {
+      const unsigned int dim = 3;
+      Solid<dim> solid(SOURCE_DIR "/prm/parameters-step-44.prm");
+      solid.run();
+    }
+  catch (std::exception &exc)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Exception on processing: " << std::endl << exc.what()
+                << std::endl << "Aborting!" << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  catch (...)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Unknown exception!" << std::endl << "Aborting!"
+                << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  return 0;
+}
diff --git a/tests/physics/step-44-standard_tensors-spatial.output b/tests/physics/step-44-standard_tensors-spatial.output
new file mode 100644 (file)
index 0000000..546f183
--- /dev/null
@@ -0,0 +1,167 @@
+
+Grid:
+        Reference volume: 1.00000e-09
+Triangulation:
+        Number of active cells: 64
+        Number of degrees of freedom: 503
+    Setting up quadrature point data...
+
+Timestep 1 @ 0.100000s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      1.879e-06  1.000e+00  1.000e+00  0.000e+00  0.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       1.114e-03  8.085e-02  8.085e-02  7.076e-13  3.053e-08  9.396e-02  3.598e-02  9.396e-02  9.397e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 123      2.213e-06  9.284e-04  9.284e-04  2.254e-15  1.996e-10  8.786e-04  1.975e-04  8.786e-04  8.788e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      3.555e-11  5.917e-07  5.917e-07  4.211e-20  2.312e-14  6.259e-07  1.495e-07  6.259e-07  6.259e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  1.495e-07
+Force:                 9.480e-12
+Dilatation:    3.484e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 2 @ 2.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      1.864e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 92       1.055e-03  8.472e-02  8.472e-02  2.306e+13  6.107e+06  1.096e-01  4.023e-02  1.096e-01  1.097e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 115      4.858e-06  9.620e-04  9.620e-04  9.042e+10  5.919e+04  1.142e-03  3.104e-04  1.142e-03  1.142e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      8.675e-11  1.296e-06  1.296e-06  2.300e+06  7.668e+00  2.098e-06  4.733e-07  2.098e-06  2.098e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  4.733e-07
+Force:                 2.313e-11
+Dilatation:    7.035e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 3 @ 3.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      2.213e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 95       1.501e-03  9.356e-02  9.356e-02  4.481e+12  4.677e+06  1.130e-01  4.961e-02  1.130e-01  1.131e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 110      7.323e-06  1.013e-03  1.013e-03  2.165e+10  4.746e+04  1.479e-03  4.834e-04  1.479e-03  1.480e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 143      2.610e-10  1.948e-06  1.948e-06  1.157e+06  7.005e+00  2.141e-06  2.425e-07  2.141e-06  2.142e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  2.425e-07
+Force:                 6.959e-11
+Dilatation:    1.059e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 4 @ 4.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      1.859e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 91       1.185e-03  1.069e-01  1.069e-01  8.200e+12  3.902e+06  9.765e-02  6.072e-02  9.765e-02  9.767e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 110      6.518e-06  1.440e-03  1.440e-03  4.432e+10  2.477e+04  1.897e-03  8.054e-04  1.897e-03  1.897e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 143      9.331e-10  1.764e-06  1.764e-06  6.641e+06  1.187e+01  2.206e-06  7.739e-07  2.206e-06  2.207e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  7.739e-07
+Force:                 2.488e-10
+Dilatation:    1.407e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 5 @ 5.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      3.277e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 94       7.466e-04  1.179e-01  1.179e-01  1.282e+12  3.596e+06  7.328e-02  6.923e-02  7.328e-02  7.329e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 113      6.856e-06  2.300e-03  2.300e-03  7.177e+09  9.311e+03  2.297e-03  1.160e-03  2.297e-03  2.297e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 140      1.920e-09  2.134e-06  2.134e-06  2.482e+06  1.673e+01  5.052e-06  5.744e-07  5.052e-06  5.053e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  5.744e-07
+Force:                 5.119e-10
+Dilatation:    1.742e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 6 @ 6.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      1.418e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 98       5.446e-04  1.230e-01  1.230e-01  2.598e+12  3.341e+06  5.486e-02  7.377e-02  5.486e-02  5.488e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 118      4.807e-06  2.689e-03  2.689e-03  1.406e+10  3.742e+03  2.236e-03  1.405e-03  2.236e-03  2.236e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 144      3.117e-09  1.929e-06  1.929e-06  7.125e+06  1.493e+01  2.063e-06  6.477e-07  2.063e-06  2.063e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.477e-07
+Force:                 8.310e-10
+Dilatation:    2.057e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 7 @ 7.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      2.877e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 93       7.438e-04  1.234e-01  1.234e-01  1.458e+12  3.481e+06  4.451e-02  7.478e-02  4.451e-02  4.453e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 124      1.896e-06  2.709e-03  2.709e-03  7.695e+09  2.434e+03  2.080e-03  1.491e-03  2.080e-03  2.080e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 147      1.532e-09  1.449e-06  1.449e-06  4.195e+06  1.295e+01  1.160e-06  6.105e-07  1.160e-06  1.160e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.105e-07
+Force:                 4.084e-10
+Dilatation:    2.353e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 8 @ 8.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      3.355e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       4.568e-04  1.207e-01  1.207e-01  1.347e+12  2.889e+06  3.818e-02  7.299e-02  3.818e-02  3.820e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 124      1.981e-06  2.507e-03  2.507e-03  7.100e+09  1.614e+03  1.731e-03  1.428e-03  1.731e-03  1.731e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      9.525e-10  1.147e-06  1.147e-06  3.504e+06  7.450e+00  1.053e-06  6.004e-07  1.053e-06  1.053e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.004e-07
+Force:                 2.540e-10
+Dilatation:    2.632e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 9 @ 9.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 138      2.296e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       3.492e-04  1.155e-01  1.155e-01  1.888e+12  3.080e+06  3.352e-02  6.924e-02  3.352e-02  3.353e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      1.002e-06  2.234e-03  2.234e-03  9.602e+09  1.530e+03  1.336e-03  1.247e-03  1.336e-03  1.336e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 153      6.417e-10  8.777e-07  8.777e-07  3.805e+06  4.375e+00  6.542e-07  4.685e-07  6.542e-07  6.543e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  4.685e-07
+Force:                 1.711e-10
+Dilatation:    2.899e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 10 @ 1.000e+00s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 137      2.024e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       2.728e-04  1.085e-01  1.085e-01  2.721e+12  3.072e+06  3.002e-02  6.447e-02  3.002e-02  3.004e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      1.029e-06  1.897e-03  1.897e-03  1.269e+10  1.296e+03  9.517e-04  1.008e-03  9.517e-04  9.517e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 159      3.020e-10  6.524e-07  6.524e-07  3.633e+06  2.365e+00  5.342e-07  3.493e-07  5.342e-07  5.344e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  3.493e-07
+Force:                 8.052e-11
+Dilatation:    3.157e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
diff --git a/tests/physics/step-44.cc b/tests/physics/step-44.cc
new file mode 100644 (file)
index 0000000..ff475be
--- /dev/null
@@ -0,0 +1,1988 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+// This is a copy of step-44 (git rev 3f7e617) to use as a base-line for
+// results produced via different approaches to be compared to.
+
+#include "../tests.h"
+
+#include <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/base/quadrature_point_data.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/precondition_selector.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/sparse_direct.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+#include <deal.II/lac/iterative_inverse.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/vector_tools.h>
+#include <iostream>
+#include <fstream>
+namespace Step44
+{
+  using namespace dealii;
+  namespace Parameters
+  {
+    struct FESystem
+    {
+      unsigned int poly_degree;
+      unsigned int quad_order;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void FESystem::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        prm.declare_entry("Polynomial degree", "2",
+                          Patterns::Integer(0),
+                          "Displacement system polynomial order");
+        prm.declare_entry("Quadrature order", "3",
+                          Patterns::Integer(0),
+                          "Gauss quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    void FESystem::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Finite element system");
+      {
+        poly_degree = prm.get_integer("Polynomial degree");
+        quad_order = prm.get_integer("Quadrature order");
+      }
+      prm.leave_subsection();
+    }
+    struct Geometry
+    {
+      unsigned int global_refinement;
+      double       scale;
+      double       p_p0;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Geometry::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        prm.declare_entry("Global refinement", "2",
+                          Patterns::Integer(0),
+                          "Global refinement level");
+        prm.declare_entry("Grid scale", "1e-3",
+                          Patterns::Double(0.0),
+                          "Global grid scaling factor");
+        prm.declare_entry("Pressure ratio p/p0", "100",
+                          Patterns::Selection("20|40|60|80|100"),
+                          "Ratio of applied pressure to reference pressure");
+      }
+      prm.leave_subsection();
+    }
+    void Geometry::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Geometry");
+      {
+        global_refinement = prm.get_integer("Global refinement");
+        scale = prm.get_double("Grid scale");
+        p_p0 = prm.get_double("Pressure ratio p/p0");
+      }
+      prm.leave_subsection();
+    }
+    struct Materials
+    {
+      double nu;
+      double mu;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Materials::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        prm.declare_entry("Poisson's ratio", "0.4999",
+                          Patterns::Double(-1.0,0.5),
+                          "Poisson's ratio");
+        prm.declare_entry("Shear modulus", "80.194e6",
+                          Patterns::Double(),
+                          "Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    void Materials::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Material properties");
+      {
+        nu = prm.get_double("Poisson's ratio");
+        mu = prm.get_double("Shear modulus");
+      }
+      prm.leave_subsection();
+    }
+    struct LinearSolver
+    {
+      std::string type_lin;
+      double      tol_lin;
+      double      max_iterations_lin;
+      bool        use_static_condensation;
+      std::string preconditioner_type;
+      double      preconditioner_relaxation;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void LinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        prm.declare_entry("Solver type", "CG",
+                          Patterns::Selection("CG|Direct"),
+                          "Type of solver used to solve the linear system");
+        prm.declare_entry("Residual", "1e-6",
+                          Patterns::Double(0.0),
+                          "Linear solver residual (scaled by residual norm)");
+        prm.declare_entry("Max iteration multiplier", "1",
+                          Patterns::Double(0.0),
+                          "Linear solver iterations (multiples of the system matrix size)");
+        prm.declare_entry("Use static condensation", "true",
+                          Patterns::Bool(),
+                          "Solve the full block system or a reduced problem");
+        prm.declare_entry("Preconditioner type", "ssor",
+                          Patterns::Selection("jacobi|ssor"),
+                          "Type of preconditioner");
+        prm.declare_entry("Preconditioner relaxation", "0.65",
+                          Patterns::Double(0.0),
+                          "Preconditioner relaxation value");
+      }
+      prm.leave_subsection();
+    }
+    void LinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Linear solver");
+      {
+        type_lin = prm.get("Solver type");
+        tol_lin = prm.get_double("Residual");
+        max_iterations_lin = prm.get_double("Max iteration multiplier");
+        use_static_condensation = prm.get_bool("Use static condensation");
+        preconditioner_type = prm.get("Preconditioner type");
+        preconditioner_relaxation = prm.get_double("Preconditioner relaxation");
+      }
+      prm.leave_subsection();
+    }
+    struct NonlinearSolver
+    {
+      unsigned int max_iterations_NR;
+      double       tol_f;
+      double       tol_u;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void NonlinearSolver::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        prm.declare_entry("Max iterations Newton-Raphson", "10",
+                          Patterns::Integer(0),
+                          "Number of Newton-Raphson iterations allowed");
+        prm.declare_entry("Tolerance force", "1.0e-9",
+                          Patterns::Double(0.0),
+                          "Force residual tolerance");
+        prm.declare_entry("Tolerance displacement", "1.0e-6",
+                          Patterns::Double(0.0),
+                          "Displacement error tolerance");
+      }
+      prm.leave_subsection();
+    }
+    void NonlinearSolver::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Nonlinear solver");
+      {
+        max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
+        tol_f = prm.get_double("Tolerance force");
+        tol_u = prm.get_double("Tolerance displacement");
+      }
+      prm.leave_subsection();
+    }
+    struct Time
+    {
+      double delta_t;
+      double end_time;
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    void Time::declare_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        prm.declare_entry("End time", "1",
+                          Patterns::Double(),
+                          "End time");
+        prm.declare_entry("Time step size", "0.1",
+                          Patterns::Double(),
+                          "Time step size");
+      }
+      prm.leave_subsection();
+    }
+    void Time::parse_parameters(ParameterHandler &prm)
+    {
+      prm.enter_subsection("Time");
+      {
+        end_time = prm.get_double("End time");
+        delta_t = prm.get_double("Time step size");
+      }
+      prm.leave_subsection();
+    }
+    struct AllParameters : public FESystem,
+      public Geometry,
+      public Materials,
+      public LinearSolver,
+      public NonlinearSolver,
+      public Time
+    {
+      AllParameters(const std::string &input_file);
+      static void
+      declare_parameters(ParameterHandler &prm);
+      void
+      parse_parameters(ParameterHandler &prm);
+    };
+    AllParameters::AllParameters(const std::string &input_file)
+    {
+      ParameterHandler prm;
+      declare_parameters(prm);
+      prm.read_input(input_file);
+      parse_parameters(prm);
+    }
+    void AllParameters::declare_parameters(ParameterHandler &prm)
+    {
+      FESystem::declare_parameters(prm);
+      Geometry::declare_parameters(prm);
+      Materials::declare_parameters(prm);
+      LinearSolver::declare_parameters(prm);
+      NonlinearSolver::declare_parameters(prm);
+      Time::declare_parameters(prm);
+    }
+    void AllParameters::parse_parameters(ParameterHandler &prm)
+    {
+      FESystem::parse_parameters(prm);
+      Geometry::parse_parameters(prm);
+      Materials::parse_parameters(prm);
+      LinearSolver::parse_parameters(prm);
+      NonlinearSolver::parse_parameters(prm);
+      Time::parse_parameters(prm);
+    }
+  }
+  template <int dim>
+  class StandardTensors
+  {
+  public:
+    static const SymmetricTensor<2, dim> I;
+    static const SymmetricTensor<4, dim> IxI;
+    static const SymmetricTensor<4, dim> II;
+    static const SymmetricTensor<4, dim> dev_P;
+  };
+  template <int dim>
+  const SymmetricTensor<2, dim>
+  StandardTensors<dim>::I = unit_symmetric_tensor<dim>();
+  template <int dim>
+  const SymmetricTensor<4, dim>
+  StandardTensors<dim>::IxI = outer_product(I, I);
+  template <int dim>
+  const SymmetricTensor<4, dim>
+  StandardTensors<dim>::II = identity_tensor<dim>();
+  template <int dim>
+  const SymmetricTensor<4, dim>
+  StandardTensors<dim>::dev_P = deviator_tensor<dim>();
+  class Time
+  {
+  public:
+    Time (const double time_end,
+          const double delta_t)
+      :
+      timestep(0),
+      time_current(0.0),
+      time_end(time_end),
+      delta_t(delta_t)
+    {}
+    virtual ~Time()
+    {}
+    double current() const
+    {
+      return time_current;
+    }
+    double end() const
+    {
+      return time_end;
+    }
+    double get_delta_t() const
+    {
+      return delta_t;
+    }
+    unsigned int get_timestep() const
+    {
+      return timestep;
+    }
+    void increment()
+    {
+      time_current += delta_t;
+      ++timestep;
+    }
+  private:
+    unsigned int timestep;
+    double       time_current;
+    const double time_end;
+    const double delta_t;
+  };
+  template <int dim>
+  class Material_Compressible_Neo_Hook_Three_Field
+  {
+  public:
+    Material_Compressible_Neo_Hook_Three_Field(const double mu,
+                                               const double nu)
+      :
+      kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))),
+      c_1(mu / 2.0),
+      det_F(1.0),
+      p_tilde(0.0),
+      J_tilde(1.0),
+      b_bar(StandardTensors<dim>::I)
+    {
+      Assert(kappa > 0, ExcInternalError());
+    }
+    ~Material_Compressible_Neo_Hook_Three_Field()
+    {}
+    void update_material_data(const Tensor<2, dim> &F,
+                              const double p_tilde_in,
+                              const double J_tilde_in)
+    {
+      det_F = determinant(F);
+      b_bar = std::pow(det_F, -2.0 / dim) * symmetrize(F * transpose(F));
+      p_tilde = p_tilde_in;
+      J_tilde = J_tilde_in;
+      Assert(det_F > 0, ExcInternalError());
+    }
+    SymmetricTensor<2, dim> get_tau()
+    {
+      return get_tau_iso() + get_tau_vol();
+    }
+    SymmetricTensor<4, dim> get_Jc() const
+    {
+      return get_Jc_vol() + get_Jc_iso();
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde);
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return ( (kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde)));
+    }
+    double get_det_F() const
+    {
+      return det_F;
+    }
+    double get_p_tilde() const
+    {
+      return p_tilde;
+    }
+    double get_J_tilde() const
+    {
+      return J_tilde;
+    }
+  protected:
+    const double kappa;
+    const double c_1;
+    double det_F;
+    double p_tilde;
+    double J_tilde;
+    SymmetricTensor<2, dim> b_bar;
+    SymmetricTensor<2, dim> get_tau_vol() const
+    {
+      return p_tilde * det_F * StandardTensors<dim>::I;
+    }
+    SymmetricTensor<2, dim> get_tau_iso() const
+    {
+      return StandardTensors<dim>::dev_P * get_tau_bar();
+    }
+    SymmetricTensor<2, dim> get_tau_bar() const
+    {
+      return 2.0 * c_1 * b_bar;
+    }
+    SymmetricTensor<4, dim> get_Jc_vol() const
+    {
+      return p_tilde * det_F
+             * ( StandardTensors<dim>::IxI
+                 - (2.0 * StandardTensors<dim>::II) );
+    }
+    SymmetricTensor<4, dim> get_Jc_iso() const
+    {
+      const SymmetricTensor<2, dim> tau_bar = get_tau_bar();
+      const SymmetricTensor<2, dim> tau_iso = get_tau_iso();
+      const SymmetricTensor<4, dim> tau_iso_x_I
+        = outer_product(tau_iso,
+                        StandardTensors<dim>::I);
+      const SymmetricTensor<4, dim> I_x_tau_iso
+        = outer_product(StandardTensors<dim>::I,
+                        tau_iso);
+      const SymmetricTensor<4, dim> c_bar = get_c_bar();
+      return (2.0 / dim) * trace(tau_bar)
+             * StandardTensors<dim>::dev_P
+             - (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso)
+             + StandardTensors<dim>::dev_P * c_bar
+             * StandardTensors<dim>::dev_P;
+    }
+    SymmetricTensor<4, dim> get_c_bar() const
+    {
+      return SymmetricTensor<4, dim>();
+    }
+  };
+  template <int dim>
+  class PointHistory
+  {
+  public:
+    PointHistory()
+      :
+      F_inv(StandardTensors<dim>::I),
+      tau(SymmetricTensor<2, dim>()),
+      d2Psi_vol_dJ2(0.0),
+      dPsi_vol_dJ(0.0),
+      Jc(SymmetricTensor<4, dim>())
+    {}
+    virtual ~PointHistory()
+    {}
+    void setup_lqp (const Parameters::AllParameters &parameters)
+    {
+      material.reset(new Material_Compressible_Neo_Hook_Three_Field<dim>(parameters.mu,
+                     parameters.nu));
+      update_values(Tensor<2, dim>(), 0.0, 1.0);
+    }
+    void update_values (const Tensor<2, dim> &Grad_u_n,
+                        const double p_tilde,
+                        const double J_tilde)
+    {
+      const Tensor<2, dim> F
+        = (Tensor<2, dim>(StandardTensors<dim>::I) +
+           Grad_u_n);
+      material->update_material_data(F, p_tilde, J_tilde);
+      F_inv = invert(F);
+      tau = material->get_tau();
+      Jc = material->get_Jc();
+      dPsi_vol_dJ = material->get_dPsi_vol_dJ();
+      d2Psi_vol_dJ2 = material->get_d2Psi_vol_dJ2();
+    }
+    double get_J_tilde() const
+    {
+      return material->get_J_tilde();
+    }
+    double get_det_F() const
+    {
+      return material->get_det_F();
+    }
+    const Tensor<2, dim> &get_F_inv() const
+    {
+      return F_inv;
+    }
+    double get_p_tilde() const
+    {
+      return material->get_p_tilde();
+    }
+    const SymmetricTensor<2, dim> &get_tau() const
+    {
+      return tau;
+    }
+    double get_dPsi_vol_dJ() const
+    {
+      return dPsi_vol_dJ;
+    }
+    double get_d2Psi_vol_dJ2() const
+    {
+      return d2Psi_vol_dJ2;
+    }
+    const SymmetricTensor<4, dim> &get_Jc() const
+    {
+      return Jc;
+    }
+  private:
+    std_cxx11::shared_ptr< Material_Compressible_Neo_Hook_Three_Field<dim> > material;
+    Tensor<2, dim> F_inv;
+    SymmetricTensor<2, dim> tau;
+    double                  d2Psi_vol_dJ2;
+    double                  dPsi_vol_dJ;
+    SymmetricTensor<4, dim> Jc;
+  };
+  template <int dim>
+  class Solid
+  {
+  public:
+    Solid(const std::string &input_file);
+    virtual
+    ~Solid();
+    void
+    run();
+  private:
+    struct PerTaskData_K;
+    struct ScratchData_K;
+    struct PerTaskData_RHS;
+    struct ScratchData_RHS;
+    struct PerTaskData_SC;
+    struct ScratchData_SC;
+    struct PerTaskData_UQPH;
+    struct ScratchData_UQPH;
+    void
+    make_grid();
+    void
+    system_setup();
+    void
+    determine_component_extractors();
+    void
+    assemble_system_tangent();
+    void
+    assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                     ScratchData_K &scratch,
+                                     PerTaskData_K &data) const;
+    void
+    copy_local_to_global_K(const PerTaskData_K &data);
+    void
+    assemble_system_rhs();
+    void
+    assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                 ScratchData_RHS &scratch,
+                                 PerTaskData_RHS &data) const;
+    void
+    copy_local_to_global_rhs(const PerTaskData_RHS &data);
+    void
+    assemble_sc();
+    void
+    assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                         ScratchData_SC &scratch,
+                         PerTaskData_SC &data);
+    void
+    copy_local_to_global_sc(const PerTaskData_SC &data);
+    void
+    make_constraints(const int &it_nr);
+    void
+    setup_qph();
+    void
+    update_qph_incremental(const BlockVector<double> &solution_delta);
+    void
+    update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                    ScratchData_UQPH &scratch,
+                                    PerTaskData_UQPH &data);
+    void
+    copy_local_to_global_UQPH(const PerTaskData_UQPH &/*data*/)
+    {}
+    void
+    solve_nonlinear_timestep(BlockVector<double> &solution_delta);
+    std::pair<unsigned int, double>
+    solve_linear_system(BlockVector<double> &newton_update);
+    BlockVector<double>
+    get_total_solution(const BlockVector<double> &solution_delta) const;
+    void
+    output_results() const;
+    Parameters::AllParameters parameters;
+    double                    vol_reference;
+    Triangulation<dim>        triangulation;
+    Time                      time;
+    mutable TimerOutput       timer;
+    CellDataStorage<typename Triangulation<dim>::cell_iterator,
+                    PointHistory<dim> > quadrature_point_history;
+    const unsigned int               degree;
+    const FESystem<dim>              fe;
+    DoFHandler<dim>                  dof_handler_ref;
+    const unsigned int               dofs_per_cell;
+    const FEValuesExtractors::Vector u_fe;
+    const FEValuesExtractors::Scalar p_fe;
+    const FEValuesExtractors::Scalar J_fe;
+    static const unsigned int n_blocks = 3;
+    static const unsigned int n_components = dim + 2;
+    static const unsigned int first_u_component = 0;
+    static const unsigned int p_component = dim;
+    static const unsigned int J_component = dim + 1;
+    enum
+    {
+      u_dof = 0,
+      p_dof = 1,
+      J_dof = 2
+    };
+    std::vector<types::global_dof_index> dofs_per_block;
+    std::vector<types::global_dof_index> element_indices_u;
+    std::vector<types::global_dof_index> element_indices_p;
+    std::vector<types::global_dof_index> element_indices_J;
+    const QGauss<dim>     qf_cell;
+    const QGauss<dim - 1> qf_face;
+    const unsigned int    n_q_points;
+    const unsigned int    n_q_points_f;
+    ConstraintMatrix          constraints;
+    BlockSparsityPattern      sparsity_pattern;
+    BlockSparseMatrix<double> tangent_matrix;
+    BlockVector<double>       system_rhs;
+    BlockVector<double>       solution_n;
+    ConditionalOStream        pcout;
+    struct Errors
+    {
+      Errors()
+        :
+        norm(1.0), u(1.0), p(1.0), J(1.0)
+      {}
+      void reset()
+      {
+        norm = 1.0;
+        u = 1.0;
+        p = 1.0;
+        J = 1.0;
+      }
+      void normalise(const Errors &rhs)
+      {
+        if (rhs.norm != 0.0)
+          norm /= rhs.norm;
+        if (rhs.u != 0.0)
+          u /= rhs.u;
+        if (rhs.p != 0.0)
+          p /= rhs.p;
+        if (rhs.J != 0.0)
+          J /= rhs.J;
+      }
+      double norm, u, p, J;
+    };
+    Errors error_residual, error_residual_0, error_residual_norm, error_update,
+           error_update_0, error_update_norm;
+    void
+    get_error_residual(Errors &error_residual);
+    void
+    get_error_update(const BlockVector<double> &newton_update,
+                     Errors &error_update);
+    std::pair<double, double>
+    get_error_dilation() const;
+    double
+    compute_vol_current () const;
+    void
+    print_conv_header();
+    void
+    print_conv_footer();
+  };
+  template <int dim>
+  Solid<dim>::Solid(const std::string &input_file)
+    :
+    parameters(input_file),
+    triangulation(Triangulation<dim>::maximum_smoothing),
+    time(parameters.end_time, parameters.delta_t),
+    timer(deallog.get_file_stream(),
+          TimerOutput::never,
+          TimerOutput::wall_times),
+    degree(parameters.poly_degree),
+    fe(FE_Q<dim>(parameters.poly_degree), dim, // displacement
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1, // pressure
+       FE_DGPMonomial<dim>(parameters.poly_degree - 1), 1), // dilatation
+    dof_handler_ref(triangulation),
+    dofs_per_cell (fe.dofs_per_cell),
+    u_fe(first_u_component),
+    p_fe(p_component),
+    J_fe(J_component),
+    dofs_per_block(n_blocks),
+    qf_cell(parameters.quad_order),
+    qf_face(parameters.quad_order),
+    n_q_points (qf_cell.size()),
+    n_q_points_f (qf_face.size()),
+    pcout(deallog.get_file_stream())
+  {
+    Assert(dim==2 || dim==3, ExcMessage("This problem only works in 2 or 3 space dimensions."));
+    determine_component_extractors();
+  }
+  template <int dim>
+  Solid<dim>::~Solid()
+  {
+    dof_handler_ref.clear();
+  }
+  template <int dim>
+  void Solid<dim>::run()
+  {
+    make_grid();
+    system_setup();
+    {
+      ConstraintMatrix constraints;
+      constraints.close();
+      const ComponentSelectFunction<dim>
+      J_mask (J_component, n_components);
+      VectorTools::project (dof_handler_ref,
+                            constraints,
+                            QGauss<dim>(degree+2),
+                            J_mask,
+                            solution_n);
+    }
+    output_results();
+    time.increment();
+    BlockVector<double> solution_delta(dofs_per_block);
+    while (time.current() < time.end())
+      {
+        solution_delta = 0.0;
+        solve_nonlinear_timestep(solution_delta);
+        solution_n += solution_delta;
+        output_results();
+        time.increment();
+      }
+  }
+  template <int dim>
+  struct Solid<dim>::PerTaskData_K
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_K(const unsigned int dofs_per_cell)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_matrix = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_K
+  {
+    FEValues<dim> fe_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<Tensor<2, dim> > >          grad_Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_K(const FiniteElement<dim> &fe_cell,
+                  const QGauss<dim> &qf_cell,
+                  const UpdateFlags uf_cell)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      grad_Nx(qf_cell.size(),
+              std::vector<Tensor<2, dim> >(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_K(const ScratchData_K &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      grad_Nx(rhs.grad_Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              grad_Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_RHS
+  {
+    Vector<double>            cell_rhs;
+    std::vector<types::global_dof_index> local_dof_indices;
+    PerTaskData_RHS(const unsigned int dofs_per_cell)
+      :
+      cell_rhs(dofs_per_cell),
+      local_dof_indices(dofs_per_cell)
+    {}
+    void reset()
+    {
+      cell_rhs = 0.0;
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_RHS
+  {
+    FEValues<dim>     fe_values_ref;
+    FEFaceValues<dim> fe_face_values_ref;
+    std::vector<std::vector<double> >                   Nx;
+    std::vector<std::vector<SymmetricTensor<2, dim> > > symm_grad_Nx;
+    ScratchData_RHS(const FiniteElement<dim> &fe_cell,
+                    const QGauss<dim> &qf_cell, const UpdateFlags uf_cell,
+                    const QGauss<dim - 1> & qf_face, const UpdateFlags uf_face)
+      :
+      fe_values_ref(fe_cell, qf_cell, uf_cell),
+      fe_face_values_ref(fe_cell, qf_face, uf_face),
+      Nx(qf_cell.size(),
+         std::vector<double>(fe_cell.dofs_per_cell)),
+      symm_grad_Nx(qf_cell.size(),
+                   std::vector<SymmetricTensor<2, dim> >
+                   (fe_cell.dofs_per_cell))
+    {}
+    ScratchData_RHS(const ScratchData_RHS &rhs)
+      :
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags()),
+      fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
+                         rhs.fe_face_values_ref.get_quadrature(),
+                         rhs.fe_face_values_ref.get_update_flags()),
+      Nx(rhs.Nx),
+      symm_grad_Nx(rhs.symm_grad_Nx)
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points      = Nx.size();
+      const unsigned int n_dofs_per_cell = Nx[0].size();
+      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+        {
+          Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+          Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
+                  ExcInternalError());
+          for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+            {
+              Nx[q_point][k] = 0.0;
+              symm_grad_Nx[q_point][k] = 0.0;
+            }
+        }
+    }
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_SC
+  {
+    FullMatrix<double>        cell_matrix;
+    std::vector<types::global_dof_index> local_dof_indices;
+    FullMatrix<double>        k_orig;
+    FullMatrix<double>        k_pu;
+    FullMatrix<double>        k_pJ;
+    FullMatrix<double>        k_JJ;
+    FullMatrix<double>        k_pJ_inv;
+    FullMatrix<double>        k_bbar;
+    FullMatrix<double>        A;
+    FullMatrix<double>        B;
+    FullMatrix<double>        C;
+    PerTaskData_SC(const unsigned int dofs_per_cell,
+                   const unsigned int n_u,
+                   const unsigned int n_p,
+                   const unsigned int n_J)
+      :
+      cell_matrix(dofs_per_cell, dofs_per_cell),
+      local_dof_indices(dofs_per_cell),
+      k_orig(dofs_per_cell, dofs_per_cell),
+      k_pu(n_p, n_u),
+      k_pJ(n_p, n_J),
+      k_JJ(n_J, n_J),
+      k_pJ_inv(n_p, n_J),
+      k_bbar(n_u, n_u),
+      A(n_J,n_u),
+      B(n_J, n_u),
+      C(n_p, n_u)
+    {}
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_SC
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::PerTaskData_UQPH
+  {
+    void reset()
+    {}
+  };
+  template <int dim>
+  struct Solid<dim>::ScratchData_UQPH
+  {
+    const BlockVector<double>   &solution_total;
+    std::vector<Tensor<2, dim> > solution_grads_u_total;
+    std::vector<double>          solution_values_p_total;
+    std::vector<double>          solution_values_J_total;
+    FEValues<dim>                fe_values_ref;
+    ScratchData_UQPH(const FiniteElement<dim> &fe_cell,
+                     const QGauss<dim> &qf_cell,
+                     const UpdateFlags uf_cell,
+                     const BlockVector<double> &solution_total)
+      :
+      solution_total(solution_total),
+      solution_grads_u_total(qf_cell.size()),
+      solution_values_p_total(qf_cell.size()),
+      solution_values_J_total(qf_cell.size()),
+      fe_values_ref(fe_cell, qf_cell, uf_cell)
+    {}
+    ScratchData_UQPH(const ScratchData_UQPH &rhs)
+      :
+      solution_total(rhs.solution_total),
+      solution_grads_u_total(rhs.solution_grads_u_total),
+      solution_values_p_total(rhs.solution_values_p_total),
+      solution_values_J_total(rhs.solution_values_J_total),
+      fe_values_ref(rhs.fe_values_ref.get_fe(),
+                    rhs.fe_values_ref.get_quadrature(),
+                    rhs.fe_values_ref.get_update_flags())
+    {}
+    void reset()
+    {
+      const unsigned int n_q_points = solution_grads_u_total.size();
+      for (unsigned int q = 0; q < n_q_points; ++q)
+        {
+          solution_grads_u_total[q] = 0.0;
+          solution_values_p_total[q] = 0.0;
+          solution_values_J_total[q] = 0.0;
+        }
+    }
+  };
+  template <int dim>
+  void Solid<dim>::make_grid()
+  {
+    GridGenerator::hyper_rectangle(triangulation,
+                                   (dim==3 ? Point<dim>(0.0, 0.0, 0.0) : Point<dim>(0.0, 0.0)),
+                                   (dim==3 ? Point<dim>(1.0, 1.0, 1.0) : Point<dim>(1.0, 1.0)),
+                                   true);
+    GridTools::scale(parameters.scale, triangulation);
+    triangulation.refine_global(std::max (1U, parameters.global_refinement));
+    vol_reference = GridTools::volume(triangulation);
+    pcout << "Grid:\n\t Reference volume: " << vol_reference << std::endl;
+    typename Triangulation<dim>::active_cell_iterator cell =
+      triangulation.begin_active(), endc = triangulation.end();
+    for (; cell != endc; ++cell)
+      for (unsigned int face = 0;
+           face < GeometryInfo<dim>::faces_per_cell; ++face)
+        {
+          if (cell->face(face)->at_boundary() == true
+              &&
+              cell->face(face)->center()[1] == 1.0 * parameters.scale)
+            {
+              if (dim==3)
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale
+                      &&
+                      cell->face(face)->center()[2] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+              else
+                {
+                  if (cell->face(face)->center()[0] < 0.5 * parameters.scale)
+                    cell->face(face)->set_boundary_id(6);
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::system_setup()
+  {
+    timer.enter_subsection("Setup system");
+    std::vector<unsigned int> block_component(n_components, u_dof); // Displacement
+    block_component[p_component] = p_dof; // Pressure
+    block_component[J_component] = J_dof; // Dilatation
+    dof_handler_ref.distribute_dofs(fe);
+    DoFRenumbering::Cuthill_McKee(dof_handler_ref);
+    DoFRenumbering::component_wise(dof_handler_ref, block_component);
+    DoFTools::count_dofs_per_block(dof_handler_ref, dofs_per_block,
+                                   block_component);
+    pcout << "Triangulation:"
+          << "\n\t Number of active cells: " << triangulation.n_active_cells()
+          << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
+          << std::endl;
+    tangent_matrix.clear();
+    {
+      const types::global_dof_index n_dofs_u = dofs_per_block[u_dof];
+      const types::global_dof_index n_dofs_p = dofs_per_block[p_dof];
+      const types::global_dof_index n_dofs_J = dofs_per_block[J_dof];
+      BlockDynamicSparsityPattern dsp(n_blocks, n_blocks);
+      dsp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u);
+      dsp.block(u_dof, p_dof).reinit(n_dofs_u, n_dofs_p);
+      dsp.block(u_dof, J_dof).reinit(n_dofs_u, n_dofs_J);
+      dsp.block(p_dof, u_dof).reinit(n_dofs_p, n_dofs_u);
+      dsp.block(p_dof, p_dof).reinit(n_dofs_p, n_dofs_p);
+      dsp.block(p_dof, J_dof).reinit(n_dofs_p, n_dofs_J);
+      dsp.block(J_dof, u_dof).reinit(n_dofs_J, n_dofs_u);
+      dsp.block(J_dof, p_dof).reinit(n_dofs_J, n_dofs_p);
+      dsp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J);
+      dsp.collect_sizes();
+      Table<2, DoFTools::Coupling> coupling(n_components, n_components);
+      for (unsigned int ii = 0; ii < n_components; ++ii)
+        for (unsigned int jj = 0; jj < n_components; ++jj)
+          if (((ii < p_component) && (jj == J_component))
+              || ((ii == J_component) && (jj < p_component))
+              || ((ii == p_component) && (jj == p_component)))
+            coupling[ii][jj] = DoFTools::none;
+          else
+            coupling[ii][jj] = DoFTools::always;
+      DoFTools::make_sparsity_pattern(dof_handler_ref,
+                                      coupling,
+                                      dsp,
+                                      constraints,
+                                      false);
+      sparsity_pattern.copy_from(dsp);
+    }
+    tangent_matrix.reinit(sparsity_pattern);
+    system_rhs.reinit(dofs_per_block);
+    system_rhs.collect_sizes();
+    solution_n.reinit(dofs_per_block);
+    solution_n.collect_sizes();
+    setup_qph();
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::determine_component_extractors()
+  {
+    element_indices_u.clear();
+    element_indices_p.clear();
+    element_indices_J.clear();
+    for (unsigned int k = 0; k < fe.dofs_per_cell; ++k)
+      {
+        const unsigned int k_group = fe.system_to_base_index(k).first.first;
+        if (k_group == u_dof)
+          element_indices_u.push_back(k);
+        else if (k_group == p_dof)
+          element_indices_p.push_back(k);
+        else if (k_group == J_dof)
+          element_indices_J.push_back(k);
+        else
+          {
+            Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+  }
+  template <int dim>
+  void Solid<dim>::setup_qph()
+  {
+    pcout << "    Setting up quadrature point data..." << std::endl;
+    quadrature_point_history.initialize(triangulation.begin_active(),
+                                        triangulation.end(),
+                                        n_q_points);
+    for (typename Triangulation<dim>::active_cell_iterator cell =
+           triangulation.begin_active(); cell != triangulation.end(); ++cell)
+      {
+        const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          lqph[q_point]->setup_lqp(parameters);
+      }
+  }
+  template <int dim>
+  void Solid<dim>::update_qph_incremental(const BlockVector<double> &solution_delta)
+  {
+    timer.enter_subsection("Update QPH data");
+    pcout << " UQPH " << std::flush;
+    const BlockVector<double> solution_total(get_total_solution(solution_delta));
+    const UpdateFlags uf_UQPH(update_values | update_gradients);
+    PerTaskData_UQPH per_task_data_UQPH;
+    ScratchData_UQPH scratch_data_UQPH(fe, qf_cell, uf_UQPH, solution_total);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::update_qph_incremental_one_cell,
+                    &Solid::copy_local_to_global_UQPH,
+                    scratch_data_UQPH,
+                    per_task_data_UQPH);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void
+  Solid<dim>::update_qph_incremental_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                              ScratchData_UQPH &scratch,
+                                              PerTaskData_UQPH &/*data*/)
+  {
+    const std::vector<std::shared_ptr<PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    Assert(scratch.solution_grads_u_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_p_total.size() == n_q_points,
+           ExcInternalError());
+    Assert(scratch.solution_values_J_total.size() == n_q_points,
+           ExcInternalError());
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total,
+                                                       scratch.solution_grads_u_total);
+    scratch.fe_values_ref[p_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_p_total);
+    scratch.fe_values_ref[J_fe].get_function_values(scratch.solution_total,
+                                                    scratch.solution_values_J_total);
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      lqph[q_point]->update_values(scratch.solution_grads_u_total[q_point],
+                                   scratch.solution_values_p_total[q_point],
+                                   scratch.solution_values_J_total[q_point]);
+  }
+  template <int dim>
+  void
+  Solid<dim>::solve_nonlinear_timestep(BlockVector<double> &solution_delta)
+  {
+    pcout << std::endl << "Timestep " << time.get_timestep() << " @ "
+          << time.current() << "s" << std::endl;
+    BlockVector<double> newton_update(dofs_per_block);
+    error_residual.reset();
+    error_residual_0.reset();
+    error_residual_norm.reset();
+    error_update.reset();
+    error_update_0.reset();
+    error_update_norm.reset();
+    print_conv_header();
+    unsigned int newton_iteration = 0;
+    for (; newton_iteration < parameters.max_iterations_NR;
+         ++newton_iteration)
+      {
+        pcout << " " << std::setw(2) << newton_iteration << " " << std::flush;
+        tangent_matrix = 0.0;
+        system_rhs = 0.0;
+        assemble_system_rhs();
+        get_error_residual(error_residual);
+        if (newton_iteration == 0)
+          error_residual_0 = error_residual;
+        error_residual_norm = error_residual;
+        error_residual_norm.normalise(error_residual_0);
+        if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u
+            && error_residual_norm.u <= parameters.tol_f)
+          {
+            pcout << " CONVERGED! " << std::endl;
+            print_conv_footer();
+            break;
+          }
+        assemble_system_tangent();
+        make_constraints(newton_iteration);
+        constraints.condense(tangent_matrix, system_rhs);
+        const std::pair<unsigned int, double>
+        lin_solver_output = solve_linear_system(newton_update);
+        get_error_update(newton_update, error_update);
+        if (newton_iteration == 0)
+          error_update_0 = error_update;
+        error_update_norm = error_update;
+        error_update_norm.normalise(error_update_0);
+        solution_delta += newton_update;
+        update_qph_incremental(solution_delta);
+        pcout << " | " << std::fixed << std::setprecision(3) << std::setw(7)
+              << std::scientific << lin_solver_output.first << "  "
+              << lin_solver_output.second << "  " << error_residual_norm.norm
+              << "  " << error_residual_norm.u << "  "
+              << error_residual_norm.p << "  " << error_residual_norm.J
+              << "  " << error_update_norm.norm << "  " << error_update_norm.u
+              << "  " << error_update_norm.p << "  " << error_update_norm.J
+              << "  " << std::endl;
+      }
+    AssertThrow (newton_iteration <= parameters.max_iterations_NR,
+                 ExcMessage("No convergence in nonlinear solver!"));
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_header()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    pcout << "                 SOLVER STEP                  "
+          << " |  LIN_IT   LIN_RES    RES_NORM    "
+          << " RES_U     RES_P      RES_J     NU_NORM     "
+          << " NU_U       NU_P       NU_J " << std::endl;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+  }
+  template <int dim>
+  void Solid<dim>::print_conv_footer()
+  {
+    static const unsigned int l_width = 155;
+    for (unsigned int i = 0; i < l_width; ++i)
+      pcout << "_";
+    pcout << std::endl;
+    const std::pair<double,double> error_dil = get_error_dilation();
+    pcout << "Relative errors:" << std::endl
+          << "Displacement:\t" << error_update.u / error_update_0.u << std::endl
+          << "Force: \t\t" << error_residual.u / error_residual_0.u << std::endl
+          << "Dilatation:\t" << error_dil.first << std::endl
+          << "v / V_0:\t" << error_dil.second *vol_reference << " / " << vol_reference
+          << " = " << error_dil.second << std::endl;
+  }
+  template <int dim>
+  double
+  Solid<dim>::compute_vol_current() const
+  {
+    double vol_current = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double JxW = fe_values_ref.JxW(q_point);
+            vol_current += det_F_qp * JxW;
+          }
+      }
+    Assert(vol_current > 0.0, ExcInternalError());
+    return vol_current;
+  }
+  template <int dim>
+  std::pair<double, double>
+  Solid<dim>::get_error_dilation() const
+  {
+    double dil_L2_error = 0.0;
+    FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+    for (typename Triangulation<dim>::active_cell_iterator
+         cell = triangulation.begin_active();
+         cell != triangulation.end(); ++cell)
+      {
+        fe_values_ref.reinit(cell);
+        const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+          quadrature_point_history.get_data(cell);
+        Assert(lqph.size() == n_q_points, ExcInternalError());
+        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+          {
+            const double det_F_qp = lqph[q_point]->get_det_F();
+            const double J_tilde_qp = lqph[q_point]->get_J_tilde();
+            const double the_error_qp_squared = std::pow((det_F_qp - J_tilde_qp),
+                                                         2);
+            const double JxW = fe_values_ref.JxW(q_point);
+            dil_L2_error += the_error_qp_squared * JxW;
+          }
+      }
+    return std::make_pair(std::sqrt(dil_L2_error),
+                          compute_vol_current() / vol_reference);
+  }
+  template <int dim>
+  void Solid<dim>::get_error_residual(Errors &error_residual)
+  {
+    BlockVector<double> error_res(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_res(i) = system_rhs(i);
+    error_residual.norm = error_res.l2_norm();
+    error_residual.u = error_res.block(u_dof).l2_norm();
+    error_residual.p = error_res.block(p_dof).l2_norm();
+    error_residual.J = error_res.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  void Solid<dim>::get_error_update(const BlockVector<double> &newton_update,
+                                    Errors &error_update)
+  {
+    BlockVector<double> error_ud(dofs_per_block);
+    for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
+      if (!constraints.is_constrained(i))
+        error_ud(i) = newton_update(i);
+    error_update.norm = error_ud.l2_norm();
+    error_update.u = error_ud.block(u_dof).l2_norm();
+    error_update.p = error_ud.block(p_dof).l2_norm();
+    error_update.J = error_ud.block(J_dof).l2_norm();
+  }
+  template <int dim>
+  BlockVector<double>
+  Solid<dim>::get_total_solution(const BlockVector<double> &solution_delta) const
+  {
+    BlockVector<double> solution_total(solution_n);
+    solution_total += solution_delta;
+    return solution_total;
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_tangent()
+  {
+    timer.enter_subsection("Assemble tangent matrix");
+    pcout << " ASM_K " << std::flush;
+    tangent_matrix = 0.0;
+    const UpdateFlags uf_cell(update_values    |
+                              update_gradients |
+                              update_JxW_values);
+    PerTaskData_K per_task_data(dofs_per_cell);
+    ScratchData_K scratch_data(fe, qf_cell, uf_cell);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_tangent_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_K,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_K(const PerTaskData_K &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_tangent_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                               ScratchData_K &scratch,
+                                               PerTaskData_K &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              {
+                scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                                              * F_inv;
+                scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
+              }
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> tau         = lqph[q_point]->get_tau();
+        const SymmetricTensor<4, dim> Jc = lqph[q_point]->get_Jc();
+        const double d2Psi_vol_dJ2       = lqph[q_point]->get_d2Psi_vol_dJ2();
+        const double det_F               = lqph[q_point]->get_det_F();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const std::vector<Tensor<2, dim> >
+        &grad_Nx = scratch.grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int component_i = fe.system_to_component_index(i).first;
+            const unsigned int i_group     = fe.system_to_base_index(i).first.first;
+            for (unsigned int j = 0; j <= i; ++j)
+              {
+                const unsigned int component_j = fe.system_to_component_index(j).first;
+                const unsigned int j_group     = fe.system_to_base_index(j).first.first;
+                if ((i_group == j_group) && (i_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution:
+                                              * symm_grad_Nx[j] * JxW;
+                    if (component_i == component_j) // geometrical stress contribution
+                      data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau
+                                                * grad_Nx[j][component_j] * JxW;
+                  }
+                else if ((i_group == p_dof) && (j_group == u_dof))
+                  {
+                    data.cell_matrix(i, j) += N[i] * det_F
+                                              * (symm_grad_Nx[j]
+                                                 * StandardTensors<dim>::I)
+                                              * JxW;
+                  }
+                else if ((i_group == J_dof) && (j_group == p_dof))
+                  data.cell_matrix(i, j) -= N[i] * N[j] * JxW;
+                else if ((i_group == j_group) && (i_group == J_dof))
+                  data.cell_matrix(i, j) += N[i] * d2Psi_vol_dJ2 * N[j] * JxW;
+                else
+                  Assert((i_group <= J_dof) && (j_group <= J_dof),
+                         ExcInternalError());
+              }
+          }
+      }
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
+        data.cell_matrix(i, j) = data.cell_matrix(j, i);
+  }
+  template <int dim>
+  void Solid<dim>::assemble_system_rhs()
+  {
+    timer.enter_subsection("Assemble system right-hand side");
+    pcout << " ASM_R " << std::flush;
+    system_rhs = 0.0;
+    const UpdateFlags uf_cell(update_values |
+                              update_gradients |
+                              update_JxW_values);
+    const UpdateFlags uf_face(update_values |
+                              update_normal_vectors |
+                              update_JxW_values);
+    PerTaskData_RHS per_task_data(dofs_per_cell);
+    ScratchData_RHS scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face);
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    std_cxx11::bind(&Solid<dim>::assemble_system_rhs_one_cell,
+                                    this,
+                                    std_cxx11::_1,
+                                    std_cxx11::_2,
+                                    std_cxx11::_3),
+                    std_cxx11::bind(&Solid<dim>::copy_local_to_global_rhs,
+                                    this,
+                                    std_cxx11::_1),
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_rhs(const PerTaskData_RHS &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      system_rhs(data.local_dof_indices[i]) += data.cell_rhs(i);
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_system_rhs_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                           ScratchData_RHS &scratch,
+                                           PerTaskData_RHS &data) const
+  {
+    data.reset();
+    scratch.reset();
+    scratch.fe_values_ref.reinit(cell);
+    cell->get_dof_indices(data.local_dof_indices);
+    const std::vector<std::shared_ptr<const PointHistory<dim> > > lqph =
+      quadrature_point_history.get_data(cell);
+    Assert(lqph.size() == n_q_points, ExcInternalError());
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv();
+        for (unsigned int k = 0; k < dofs_per_cell; ++k)
+          {
+            const unsigned int k_group = fe.system_to_base_index(k).first.first;
+            if (k_group == u_dof)
+              scratch.symm_grad_Nx[q_point][k]
+                = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point)
+                             * F_inv);
+            else if (k_group == p_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k,
+                                                                         q_point);
+            else if (k_group == J_dof)
+              scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k,
+                                                                         q_point);
+            else
+              Assert(k_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+      {
+        const SymmetricTensor<2, dim> tau = lqph[q_point]->get_tau();
+        const double det_F = lqph[q_point]->get_det_F();
+        const double J_tilde = lqph[q_point]->get_J_tilde();
+        const double p_tilde = lqph[q_point]->get_p_tilde();
+        const double dPsi_vol_dJ = lqph[q_point]->get_dPsi_vol_dJ();
+        const std::vector<double>
+        &N = scratch.Nx[q_point];
+        const std::vector<SymmetricTensor<2, dim> >
+        &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
+        const double JxW = scratch.fe_values_ref.JxW(q_point);
+        for (unsigned int i = 0; i < dofs_per_cell; ++i)
+          {
+            const unsigned int i_group = fe.system_to_base_index(i).first.first;
+            if (i_group == u_dof)
+              data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW;
+            else if (i_group == p_dof)
+              data.cell_rhs(i) -= N[i] * (det_F - J_tilde) * JxW;
+            else if (i_group == J_dof)
+              data.cell_rhs(i) -= N[i] * (dPsi_vol_dJ - p_tilde) * JxW;
+            else
+              Assert(i_group <= J_dof, ExcInternalError());
+          }
+      }
+    for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell;
+         ++face)
+      if (cell->face(face)->at_boundary() == true
+          && cell->face(face)->boundary_id() == 6)
+        {
+          scratch.fe_face_values_ref.reinit(cell, face);
+          for (unsigned int f_q_point = 0; f_q_point < n_q_points_f;
+               ++f_q_point)
+            {
+              const Tensor<1, dim> &N =
+                scratch.fe_face_values_ref.normal_vector(f_q_point);
+              static const double  p0        = -4.0
+                                               /
+                                               (parameters.scale * parameters.scale);
+              const double         time_ramp = (time.current() / time.end());
+              const double         pressure  = p0 * parameters.p_p0 * time_ramp;
+              const Tensor<1, dim> traction  = pressure * N;
+              for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                {
+                  const unsigned int i_group =
+                    fe.system_to_base_index(i).first.first;
+                  if (i_group == u_dof)
+                    {
+                      const unsigned int component_i =
+                        fe.system_to_component_index(i).first;
+                      const double Ni =
+                        scratch.fe_face_values_ref.shape_value(i,
+                                                               f_q_point);
+                      const double JxW = scratch.fe_face_values_ref.JxW(
+                                           f_q_point);
+                      data.cell_rhs(i) += (Ni * traction[component_i])
+                                          * JxW;
+                    }
+                }
+            }
+        }
+  }
+  template <int dim>
+  void Solid<dim>::make_constraints(const int &it_nr)
+  {
+    pcout << " CST " << std::flush;
+    if (it_nr > 1)
+      return;
+    constraints.clear();
+    const bool apply_dirichlet_bc = (it_nr == 0);
+    const FEValuesExtractors::Scalar x_displacement(0);
+    const FEValuesExtractors::Scalar y_displacement(1);
+    {
+      const int boundary_id = 0;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(x_displacement));
+    }
+    {
+      const int boundary_id = 2;
+      if (apply_dirichlet_bc == true)
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+      else
+        VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                 boundary_id,
+                                                 ZeroFunction<dim>(n_components),
+                                                 constraints,
+                                                 fe.component_mask(y_displacement));
+    }
+    if (dim==3)
+      {
+        const FEValuesExtractors::Scalar z_displacement(2);
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+        {
+          const int boundary_id = 4;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     fe.component_mask(z_displacement));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)
+                                                      |
+                                                      fe.component_mask(z_displacement)));
+        }
+      }
+    else
+      {
+        {
+          const int boundary_id = 3;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+        {
+          const int boundary_id = 6;
+          if (apply_dirichlet_bc == true)
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+          else
+            VectorTools::interpolate_boundary_values(dof_handler_ref,
+                                                     boundary_id,
+                                                     ZeroFunction<dim>(n_components),
+                                                     constraints,
+                                                     (fe.component_mask(x_displacement)));
+        }
+      }
+    constraints.close();
+  }
+  template <int dim>
+  void Solid<dim>::assemble_sc()
+  {
+    timer.enter_subsection("Perform static condensation");
+    pcout << " ASM_SC " << std::flush;
+    PerTaskData_SC per_task_data(dofs_per_cell, element_indices_u.size(),
+                                 element_indices_p.size(),
+                                 element_indices_J.size());
+    ScratchData_SC scratch_data;
+    WorkStream::run(dof_handler_ref.begin_active(),
+                    dof_handler_ref.end(),
+                    *this,
+                    &Solid::assemble_sc_one_cell,
+                    &Solid::copy_local_to_global_sc,
+                    scratch_data,
+                    per_task_data);
+    timer.leave_subsection();
+  }
+  template <int dim>
+  void Solid<dim>::copy_local_to_global_sc(const PerTaskData_SC &data)
+  {
+    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+      for (unsigned int j = 0; j < dofs_per_cell; ++j)
+        tangent_matrix.add(data.local_dof_indices[i],
+                           data.local_dof_indices[j],
+                           data.cell_matrix(i, j));
+  }
+  template <int dim>
+  void
+  Solid<dim>::assemble_sc_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
+                                   ScratchData_SC &scratch,
+                                   PerTaskData_SC &data)
+  {
+    data.reset();
+    scratch.reset();
+    cell->get_dof_indices(data.local_dof_indices);
+    data.k_orig.extract_submatrix_from(tangent_matrix,
+                                       data.local_dof_indices,
+                                       data.local_dof_indices);
+    data.k_pu.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_u);
+    data.k_pJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_p,
+                                     element_indices_J);
+    data.k_JJ.extract_submatrix_from(data.k_orig,
+                                     element_indices_J,
+                                     element_indices_J);
+    data.k_pJ_inv.invert(data.k_pJ);
+    data.k_pJ_inv.mmult(data.A, data.k_pu);
+    data.k_JJ.mmult(data.B, data.A);
+    data.k_pJ_inv.Tmmult(data.C, data.B);
+    data.k_pu.Tmmult(data.k_bbar, data.C);
+    data.k_bbar.scatter_matrix_to(element_indices_u,
+                                  element_indices_u,
+                                  data.cell_matrix);
+    data.k_pJ_inv.add(-1.0, data.k_pJ);
+    data.k_pJ_inv.scatter_matrix_to(element_indices_p,
+                                    element_indices_J,
+                                    data.cell_matrix);
+  }
+  template <int dim>
+  std::pair<unsigned int, double>
+  Solid<dim>::solve_linear_system(BlockVector<double> &newton_update)
+  {
+    unsigned int lin_it = 0;
+    double lin_res = 0.0;
+    if (parameters.use_static_condensation == true)
+      {
+        BlockVector<double> A(dofs_per_block);
+        BlockVector<double> B(dofs_per_block);
+        {
+          assemble_sc();
+          tangent_matrix.block(p_dof, J_dof).vmult(A.block(J_dof),
+                                                   system_rhs.block(p_dof));
+          tangent_matrix.block(J_dof, J_dof).vmult(B.block(J_dof),
+                                                   A.block(J_dof));
+          A.block(J_dof) = system_rhs.block(J_dof);
+          A.block(J_dof) -= B.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(A.block(p_dof),
+                                                    A.block(J_dof));
+          tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof),
+                                                   A.block(p_dof));
+          system_rhs.block(u_dof) -= A.block(u_dof);
+          timer.enter_subsection("Linear solver");
+          pcout << " SLV " << std::flush;
+          if (parameters.type_lin == "CG")
+            {
+              const int solver_its = tangent_matrix.block(u_dof, u_dof).m()
+                                     * parameters.max_iterations_lin;
+              const double tol_sol = parameters.tol_lin
+                                     * system_rhs.block(u_dof).l2_norm();
+              SolverControl solver_control(solver_its, tol_sol);
+              GrowingVectorMemory<Vector<double> > GVM;
+              SolverCG<Vector<double> > solver_CG(solver_control, GVM);
+              PreconditionSelector<SparseMatrix<double>, Vector<double> >
+              preconditioner (parameters.preconditioner_type,
+                              parameters.preconditioner_relaxation);
+              preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof));
+              solver_CG.solve(tangent_matrix.block(u_dof, u_dof),
+                              newton_update.block(u_dof),
+                              system_rhs.block(u_dof),
+                              preconditioner);
+              lin_it = solver_control.last_step();
+              lin_res = solver_control.last_value();
+            }
+          else if (parameters.type_lin == "Direct")
+            {
+              SparseDirectUMFPACK A_direct;
+              A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
+              A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof));
+              lin_it = 1;
+              lin_res = 0.0;
+            }
+          else
+            Assert (false, ExcMessage("Linear solver type not implemented"));
+          timer.leave_subsection();
+        }
+        constraints.distribute(newton_update);
+        timer.enter_subsection("Linear solver postprocessing");
+        pcout << " PP " << std::flush;
+        {
+          tangent_matrix.block(p_dof, u_dof).vmult(A.block(p_dof),
+                                                   newton_update.block(u_dof));
+          A.block(p_dof) *= -1.0;
+          A.block(p_dof) += system_rhs.block(p_dof);
+          tangent_matrix.block(p_dof, J_dof).vmult(newton_update.block(J_dof),
+                                                   A.block(p_dof));
+        }
+        constraints.distribute(newton_update);
+        {
+          tangent_matrix.block(J_dof, J_dof).vmult(A.block(J_dof),
+                                                   newton_update.block(J_dof));
+          A.block(J_dof) *= -1.0;
+          A.block(J_dof) += system_rhs.block(J_dof);
+          tangent_matrix.block(p_dof, J_dof).Tvmult(newton_update.block(p_dof),
+                                                    A.block(J_dof));
+        }
+        constraints.distribute(newton_update);
+        timer.leave_subsection();
+      }
+    else
+      {
+        pcout << " ------ " << std::flush;
+        timer.enter_subsection("Linear solver");
+        pcout << " SLV " << std::flush;
+        if (parameters.type_lin == "CG")
+          {
+            const Vector<double> &f_u = system_rhs.block(u_dof);
+            const Vector<double> &f_p = system_rhs.block(p_dof);
+            const Vector<double> &f_J = system_rhs.block(J_dof);
+            Vector<double> &d_u = newton_update.block(u_dof);
+            Vector<double> &d_p = newton_update.block(p_dof);
+            Vector<double> &d_J = newton_update.block(J_dof);
+            const auto K_uu = linear_operator(tangent_matrix.block(u_dof, u_dof));
+            const auto K_up = linear_operator(tangent_matrix.block(u_dof, p_dof));
+            const auto K_pu = linear_operator(tangent_matrix.block(p_dof, u_dof));
+            const auto K_Jp = linear_operator(tangent_matrix.block(J_dof, p_dof));
+            const auto K_JJ = linear_operator(tangent_matrix.block(J_dof, J_dof));
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_Jp_inv ("jacobi");
+            preconditioner_K_Jp_inv.use_matrix(tangent_matrix.block(J_dof, p_dof));
+            ReductionControl solver_control_K_Jp_inv (tangent_matrix.block(J_dof, p_dof).m() * parameters.max_iterations_lin,
+                                                      1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_Jp_inv;
+            solver_K_Jp_inv.select("cg");
+            solver_K_Jp_inv.set_control(solver_control_K_Jp_inv);
+            const auto K_Jp_inv = inverse_operator(K_Jp,
+                                                   solver_K_Jp_inv,
+                                                   preconditioner_K_Jp_inv);
+            const auto K_pJ_inv     = transpose_operator(K_Jp_inv);
+            const auto K_pp_bar     = K_Jp_inv * K_JJ * K_pJ_inv;
+            const auto K_uu_bar_bar = K_up * K_pp_bar * K_pu;
+            const auto K_uu_con     = K_uu + K_uu_bar_bar;
+            PreconditionSelector< SparseMatrix<double>, Vector<double> >
+            preconditioner_K_con_inv (parameters.preconditioner_type,
+                                      parameters.preconditioner_relaxation);
+            preconditioner_K_con_inv.use_matrix(tangent_matrix.block(u_dof, u_dof));
+            ReductionControl solver_control_K_con_inv (tangent_matrix.block(u_dof, u_dof).m() * parameters.max_iterations_lin,
+                                                       1.0e-30, parameters.tol_lin);
+            SolverSelector< Vector<double> > solver_K_con_inv;
+            solver_K_con_inv.select("cg");
+            solver_K_con_inv.set_control(solver_control_K_con_inv);
+            const auto K_uu_con_inv = inverse_operator(K_uu_con,
+                                                       solver_K_con_inv,
+                                                       preconditioner_K_con_inv);
+            d_u = K_uu_con_inv*(f_u - K_up*(K_Jp_inv*f_J - K_pp_bar*f_p));
+            timer.leave_subsection();
+            timer.enter_subsection("Linear solver postprocessing");
+            pcout << " PP " << std::flush;
+            d_J = K_pJ_inv*(f_p - K_pu*d_u);
+            d_p = K_Jp_inv*(f_J - K_JJ*d_J);
+            lin_it = solver_control_K_con_inv.last_step();
+            lin_res = solver_control_K_con_inv.last_value();
+          }
+        else if (parameters.type_lin == "Direct")
+          {
+            SparseDirectUMFPACK A_direct;
+            A_direct.initialize(tangent_matrix);
+            A_direct.vmult(newton_update, system_rhs);
+            lin_it = 1;
+            lin_res = 0.0;
+            pcout << " -- " << std::flush;
+          }
+        else
+          Assert (false, ExcMessage("Linear solver type not implemented"));
+        timer.leave_subsection();
+        constraints.distribute(newton_update);
+      }
+    return std::make_pair(lin_it, lin_res);
+  }
+  template <int dim>
+  void Solid<dim>::output_results() const
+  {
+    DataOut<dim> data_out;
+    std::vector<DataComponentInterpretation::DataComponentInterpretation>
+    data_component_interpretation(dim,
+                                  DataComponentInterpretation::component_is_part_of_vector);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar);
+    std::vector<std::string> solution_name(dim, "displacement");
+    solution_name.push_back("pressure");
+    solution_name.push_back("dilatation");
+    data_out.attach_dof_handler(dof_handler_ref);
+    data_out.add_data_vector(solution_n,
+                             solution_name,
+                             DataOut<dim>::type_dof_data,
+                             data_component_interpretation);
+    Vector<double> soln(solution_n.size());
+    for (unsigned int i = 0; i < soln.size(); ++i)
+      soln(i) = solution_n(i);
+    MappingQEulerian<dim> q_mapping(degree, dof_handler_ref, soln);
+    data_out.build_patches(q_mapping, degree);
+    std::ostringstream filename;
+    filename << "solution-" << dim << "d-" << time.get_timestep() << ".vtk";
+    std::ofstream output(filename.str().c_str());
+    data_out.write_vtk(output);
+  }
+}
+int main (int argc,char **argv)
+{
+  std::ofstream logfile("output");
+  deallog.attach(logfile);
+  deallog.depth_file(0);
+  deallog.threshold_double(1.e-10);
+
+  Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, testing_max_num_threads());
+
+  using namespace dealii;
+  using namespace Step44;
+  try
+    {
+      const unsigned int dim = 3;
+      Solid<dim> solid(SOURCE_DIR "/prm/parameters-step-44.prm");
+      solid.run();
+    }
+  catch (std::exception &exc)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Exception on processing: " << std::endl << exc.what()
+                << std::endl << "Aborting!" << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  catch (...)
+    {
+      std::cerr << std::endl << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      std::cerr << "Unknown exception!" << std::endl << "Aborting!"
+                << std::endl
+                << "----------------------------------------------------"
+                << std::endl;
+      return 1;
+    }
+  return 0;
+}
diff --git a/tests/physics/step-44.output b/tests/physics/step-44.output
new file mode 100644 (file)
index 0000000..a5bd04f
--- /dev/null
@@ -0,0 +1,167 @@
+
+Grid:
+        Reference volume: 1.00000e-09
+Triangulation:
+        Number of active cells: 64
+        Number of degrees of freedom: 503
+    Setting up quadrature point data...
+
+Timestep 1 @ 0.100000s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      1.879e-06  1.000e+00  1.000e+00  0.000e+00  0.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       9.785e-04  8.085e-02  8.085e-02  7.076e-13  3.053e-08  9.397e-02  3.598e-02  9.397e-02  9.399e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 119      4.017e-06  8.826e-04  8.826e-04  2.254e-15  1.998e-10  8.493e-04  1.975e-04  8.493e-04  8.494e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 149      7.738e-11  1.069e-06  1.069e-06  4.212e-20  2.171e-14  9.915e-07  2.729e-07  9.915e-07  9.915e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  2.729e-07
+Force:                 2.063e-11
+Dilatation:    3.484e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 2 @ 2.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 131      3.051e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 92       1.172e-03  8.472e-02  8.472e-02  8.757e+12  5.853e+06  1.096e-01  4.023e-02  1.096e-01  1.097e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 111      5.758e-06  9.375e-04  9.375e-04  3.433e+10  5.674e+04  1.112e-03  3.103e-04  1.112e-03  1.112e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      1.003e-10  1.537e-06  1.537e-06  8.734e+05  6.606e+00  1.426e-06  7.544e-07  1.426e-06  1.426e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  7.544e-07
+Force:                 2.675e-11
+Dilatation:    7.035e-07
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 3 @ 3.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 127      2.879e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 91       1.498e-03  9.356e-02  9.356e-02  1.684e+12  4.976e+06  1.131e-01  4.961e-02  1.131e-01  1.131e-01  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 107      6.289e-06  9.500e-04  9.500e-04  8.139e+09  5.059e+04  1.448e-03  4.916e-04  1.448e-03  1.448e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 147      2.200e-10  1.687e-06  1.687e-06  4.346e+05  8.460e+00  1.502e-06  5.180e-07  1.502e-06  1.502e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  5.180e-07
+Force:                 5.866e-11
+Dilatation:    1.059e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 4 @ 4.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 130      1.876e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 91       1.124e-03  1.069e-01  1.069e-01  2.260e+12  4.315e+06  9.763e-02  6.072e-02  9.763e-02  9.764e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 110      6.459e-06  1.409e-03  1.409e-03  1.222e+10  2.736e+04  1.896e-03  8.054e-04  1.896e-03  1.896e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 144      7.557e-10  1.771e-06  1.771e-06  1.831e+06  1.301e+01  1.957e-06  7.733e-07  1.957e-06  1.957e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  7.733e-07
+Force:                 2.015e-10
+Dilatation:    1.407e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 5 @ 5.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 133      1.921e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 94       7.758e-04  1.179e-01  1.179e-01  1.270e+12  3.762e+06  7.329e-02  6.923e-02  7.329e-02  7.330e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 114      6.837e-06  2.265e-03  2.265e-03  7.113e+09  9.735e+03  2.289e-03  1.160e-03  2.289e-03  2.289e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      2.491e-09  2.033e-06  2.033e-06  2.460e+06  1.721e+01  2.811e-06  5.735e-07  2.811e-06  2.811e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  5.735e-07
+Force:                 6.642e-10
+Dilatation:    1.742e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 6 @ 6.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      1.570e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 93       8.254e-04  1.230e-01  1.230e-01  2.574e+12  3.411e+06  5.485e-02  7.377e-02  5.485e-02  5.486e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 118      6.328e-06  2.737e-03  2.737e-03  1.393e+10  3.815e+03  2.295e-03  1.409e-03  2.295e-03  2.295e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 141      1.786e-09  2.421e-06  2.421e-06  7.040e+06  1.550e+01  2.731e-06  6.528e-07  2.731e-06  2.731e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.528e-07
+Force:                 4.763e-10
+Dilatation:    2.057e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 7 @ 7.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      2.942e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 93       7.359e-04  1.234e-01  1.234e-01  1.412e+12  3.061e+06  4.450e-02  7.478e-02  4.450e-02  4.452e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 120      3.420e-06  2.699e-03  2.699e-03  7.451e+09  2.142e+03  2.079e-03  1.491e-03  2.079e-03  2.079e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 148      1.688e-09  1.625e-06  1.625e-06  4.062e+06  1.135e+01  2.104e-06  6.589e-07  2.104e-06  2.105e-06  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.589e-07
+Force:                 4.502e-10
+Dilatation:    2.353e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 8 @ 8.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 137      2.965e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       5.552e-04  1.207e-01  1.207e-01  1.543e+12  3.613e+06  3.817e-02  7.299e-02  3.817e-02  3.818e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 124      1.818e-06  2.512e-03  2.512e-03  8.134e+09  2.018e+03  1.745e-03  1.428e-03  1.745e-03  1.746e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 151      1.101e-09  1.149e-06  1.149e-06  4.014e+06  9.162e+00  9.495e-07  6.011e-07  9.495e-07  9.496e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  6.011e-07
+Force:                 2.936e-10
+Dilatation:    2.632e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 9 @ 9.000e-01s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      3.726e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 96       3.272e-04  1.155e-01  1.155e-01  1.793e+12  3.058e+06  3.352e-02  6.924e-02  3.352e-02  3.354e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 129      1.004e-06  2.232e-03  2.232e-03  9.116e+09  1.519e+03  1.335e-03  1.247e-03  1.335e-03  1.335e-03  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 153      9.268e-10  8.594e-07  8.594e-07  3.613e+06  4.382e+00  6.229e-07  4.688e-07  6.229e-07  6.230e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  4.688e-07
+Force:                 2.472e-10
+Dilatation:    2.899e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00
+
+Timestep 10 @ 1.000e+00s
+___________________________________________________________________________________________________________________________________________________________
+                 SOLVER STEP                   |  LIN_IT   LIN_RES    RES_NORM     RES_U     RES_P      RES_J     NU_NORM      NU_U       NU_P       NU_J 
+___________________________________________________________________________________________________________________________________________________________
+ 0   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 134      3.218e-06  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  1.000e+00  
+ 1   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 95       3.148e-04  1.085e-01  1.085e-01  2.377e+12  3.129e+06  3.002e-02  6.447e-02  3.002e-02  3.004e-02  
+ 2   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 132      9.070e-07  1.905e-03  1.905e-03  1.109e+10  1.319e+03  9.672e-04  1.008e-03  9.672e-04  9.672e-04  
+ 3   ASM_R  ASM_K  CST  ASM_SC  SLV  PP  UQPH  | 156      5.143e-10  6.087e-07  6.087e-07  3.174e+06  2.465e+00  4.277e-07  3.161e-07  4.277e-07  4.278e-07  
+ 4   ASM_R  CONVERGED! 
+___________________________________________________________________________________________________________________________________________________________
+Relative errors:
+Displacement:  3.161e-07
+Force:                 1.372e-10
+Dilatation:    3.157e-06
+v / V_0:       1.000e-09 / 1.000e-09 = 1.000e+00

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.