From 08e637de44ff49435fe58d317cc67c0d82409d5b Mon Sep 17 00:00:00 2001
From: AlexanderMueller <rathet@gmail.com>
Date: Tue, 30 Nov 2021 19:53:34 +0100
Subject: [PATCH] Add Simo-Fox shell model
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This commit adds the energy implementat for the nonlinear Reissner-Mindlin
shell model known as the Simo and Fox shell model.

Details may be found in

  "A consistent finite element formulation of the geometrically
   non-linear Reissner-Mindlin shell model [Müller, Bischoff]"
---
 dune/gfe/simofoxenergy.hh                     | 358 ++++++++++++++++
 .../simofox-cantilever-dirichlet-values.py    |  16 +
 problems/simofox-cantilever.parset            |  99 +++++
 src/CMakeLists.txt                            |   3 +-
 src/simofoxshell.cc                           | 390 ++++++++++++++++++
 5 files changed, 865 insertions(+), 1 deletion(-)
 create mode 100644 dune/gfe/simofoxenergy.hh
 create mode 100644 problems/simofox-cantilever-dirichlet-values.py
 create mode 100644 problems/simofox-cantilever.parset
 create mode 100644 src/simofoxshell.cc

diff --git a/dune/gfe/simofoxenergy.hh b/dune/gfe/simofoxenergy.hh
new file mode 100644
index 00000000..853a5fe6
--- /dev/null
+++ b/dune/gfe/simofoxenergy.hh
@@ -0,0 +1,358 @@
+#ifndef DUNE_GFE_SIMOFOX_ENERGY_HH
+#define DUNE_GFE_SIMOFOX_ENERGY_HH
+
+#include <dune/common/fmatrix.hh>
+#include <dune/common/parametertree.hh>
+#if DUNE_VERSION_GTE(DUNE_COMMON, 2, 8)
+#include <dune/common/transpose.hh>
+#endif
+#include <dune/common/tuplevector.hh>
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/geometry/quadraturerules.hh>
+#include <dune/gfe/linearalgebra.hh>
+#include <dune/gfe/localenergy.hh>
+#include <dune/gfe/mixedlocalgeodesicfestiffness.hh>
+#include <dune/gfe/realtuple.hh>
+#include <dune/gfe/unitvector.hh>
+
+namespace Dune::GFE {
+
+  /** \brief Get LocalFiniteElements from a localView, for different tree depths of the local view
+   *
+   * Copied from CosseratEnergyLocalStiffness.hh
+   */
+  template <class Basis, std::size_t i>
+  class LocalFiniteElementFactory {
+  public:
+    static auto get(const typename Basis::LocalView &localView, std::integral_constant<std::size_t, i> iType)
+        -> decltype(localView.tree().child(iType, 0).finiteElement()) {
+      return localView.tree().child(iType, 0).finiteElement();
+    }
+  };
+
+  /** \brief Specialize for scalar bases, here we cannot call tree().child() */
+  template <class GridView, int order, std::size_t i>
+  class LocalFiniteElementFactory<Dune::Functions::LagrangeBasis<GridView, order>, i> {
+  public:
+    static auto get(const typename Dune::Functions::LagrangeBasis<GridView, order>::LocalView &localView,
+                    std::integral_constant<std::size_t, i> iType) -> decltype(localView.tree().finiteElement()) {
+      return localView.tree().finiteElement();
+    }
+  };
+
+
+  /** \brief Implements the energy of a Simo-Fox shell
+   *
+   * Described in:
+   *  "A consistent finite element formulation of the geometrically non-linear Reissner-Mindlin shell model [Müller,
+   * Bischoff]"
+   *
+   * \tparam LocalFEFunction Decides which interpolation function is used for the interpolation of the midsurface
+   * and director field.
+   */
+  template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type = double>
+  class SimoFoxEnergyLocalStiffness
+      : public Dune::GFE::LocalEnergy<Basis, RealTuple<field_type, 3>,
+                                      UnitVector<field_type, 3>>,  // inheritance to allow usage with LocalGeodesicFEADOLCStiffness
+        public MixedLocalGeodesicFEStiffness<Basis, RealTuple<field_type, 3>,
+                                             UnitVector<field_type, 3>>  // inheritance to allow usage with MixedGFEAssembler
+  {
+    // grid types
+    typedef typename Basis::GridView GridView;
+    typedef typename GridView::ctype DT;
+    typedef field_type RT;
+    typedef typename GridView::template Codim<0>::Entity Entity;
+
+    // some other sizes
+    static constexpr int gridDim  = GridView::dimension;
+    static constexpr int dimworld = GridView::dimensionworld;
+
+    // The local finite element type used for midsurface position and displacement interpolation
+    using MidSurfaceElement =
+        typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 0>::get)(typename Basis::LocalView, decltype(Dune::Indices::_0))>::type;
+    // The local finite element type used for the director interpolation
+    using DirectorElement =
+        typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 1>::get)(typename Basis::LocalView, decltype(Dune::Indices::_1))>::type;
+
+    // The local finite element function type to evaluate the midsurface position
+    using LocalMidSurfaceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<field_type, 3>>;
+    // The local finite element function type to evaluate the director
+    using LocalDirectorFunctionType = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<field_type, 3>>;
+
+    // Extra function type for reference quantities since they are unconditionally doubles, i.e. no ADOL-C types
+    using LocalMidSurfaceReferenceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<double, 3>>;
+    using LocalDirectorReferenceFunctionType   = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<double, 3>>;
+
+  public:
+    /** \brief Constructor with a set of material parameters
+     * \param parameters The material parameters
+     * \param x0 reference configuration
+     */
+    SimoFoxEnergyLocalStiffness(const Dune::ParameterTree &parameters, const BoundaryPatch<GridView> *neumannBoundary,
+                                const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, 2>)> neumannFunction,
+                                const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, 2>)> volumeLoad,
+                                const Dune::TupleVector<std::vector<RealTuple<double, 3>>, std::vector<UnitVector<double, 3>>> &x0)
+        : neumannBoundary_(neumannBoundary),
+          neumannFunction_(neumannFunction),
+          thickness_{parameters.template get<double>("thickness")},  // The sheeqll thickness
+          mu_{parameters.template get<double>("mu")},                // Lame constant 1
+          lambda_{parameters.template get<double>("lambda")},        // Lame constant 2
+          kappa_{parameters.template get<double>("kappa")},          // Shear correction factor
+          midSurfaceRefConfig{x0[Dune::Indices::_0]},
+          directorRefConfig{x0[Dune::Indices::_1]}
+    {
+      // Calculate the over the thickness preintegrated St.Venant Kirchhoff material matrix, Paper equation 10.1 */
+      const double Emodul = mu_ * (3 * lambda_ + 2 * mu_) / (lambda_ + mu_);  // Young's modulus
+      const double nu     = lambda_ / (2 * (lambda_ + mu_));                  // Poisson ratio
+
+      // membrane
+      const double fac1 = thickness_ * Emodul / (1 - nu * nu);
+      CMat_[0][0] = CMat_[1][1] = fac1;
+      CMat_[2][2]               = fac1 * (1 - nu) * 0.5;
+      CMat_[1][0] = CMat_[0][1] = fac1 * nu;
+
+      // bending
+      const double fac2 = thickness_ * thickness_ * thickness_ / 12 * Emodul / (1 - nu * nu);
+      CMat_[3][3] = CMat_[4][4] = fac2;
+      CMat_[5][5]               = fac2 * (1 - nu) * 0.5;
+      CMat_[3][4] = CMat_[4][3] = fac2 * nu;
+
+      // transverse shear
+      const double fac3 = kappa_ * thickness_ * Emodul * 0.5 / (1 + nu);
+      CMat_[6][6] = CMat_[7][7] = fac3;
+    }
+
+    /** \brief Assemble the energy for a single element */
+    RT energy(const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3>> &localMidSurfaceConfiguration,
+              const std::vector<UnitVector<field_type, 3>> &localDirectorConfiguration) const override;
+
+  private:
+    /** \brief A structure that contains all quantities to calculate the Lagrangian strains */
+    struct KinematicVariables {
+      // current configuration
+      FieldMatrix<field_type, 2, 3> a1anda2;    // first and second tangent base vector on the current geometry
+      FieldMatrix<field_type, 2, 3> td1Andtd2;  // partial derivative of the director on the current geometry in first and second direction
+      FieldMatrix<field_type, 2, 3> ud1andud2;  // partial derivative of the displacement function in first and second direction
+      FieldVector<field_type, 3> t;             // unit director on the current geometry
+
+      // reference configuration
+      FieldMatrix<double, 2, 3> A1andA2;      // first and second tangent base vector on the reference geometry
+      FieldMatrix<double, 2, 3> t0d1Andt0d2;  // partial derivative of the director on the reference geometry in first and second direction
+      FieldVector<double, 3> t0;              // unit director on the reference geometry
+    };
+
+    auto getReferenceLocalConfigurations(const typename Basis::LocalView &localView) const;
+
+    /** \brief Calculates all kinematic quantities */
+    template <typename Element, typename LocalDirectorFunction, typename LocalMidSurfaceFunction, typename LocalDirectorReferenceFunction,
+              typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition>
+    static auto kinematicVariablesFactory(const Element &element, const LocalDirectorFunction &directorFunction,
+                                          const LocalDirectorReferenceFunction &directorReferenceFunction,
+                                          const LocalMidSurfaceFunction &midSurfaceFunction,
+                                          const LocalMidSurfaceReferenceFunction &midSurfaceReferenceFunction,
+                                          const LocalMidSurfaceFunction &midSurfaceDisplacementFunction, const IntegrationPointPosition &quadPos);
+
+    /** \brief Calculate the Green-Lagrange strain components */
+    static Dune::FieldVector<RT, 8> calculateGreenLagrangianStrains(const KinematicVariables &kin);
+
+    /** \brief Save all tangent base matrices for all nodes in one place*/
+    Dune::BlockVector<Dune::FieldMatrix<field_type, 2, 3>> directorTangentSpaces;
+
+    /** \brief The Neumann boundary */
+    const BoundaryPatch<GridView> *neumannBoundary_;
+
+    /** \brief The function implementing the Neumann data */
+    const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, dimworld>)> neumannFunction_;
+
+    /** \brief The shell thickness */
+    double thickness_;
+
+    /** \brief Lame constants */
+    double mu_, lambda_;
+
+    /** \brief Shear correction factor */
+    double kappa_;
+
+    /** \brief Material tangent matrix */
+    Dune::FieldMatrix<double, 8, 8> CMat_;
+
+    /** \brief The function implementing a volume load */
+    const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, dimworld>)> volumeLoad_;
+
+    /** \brief Stores the reference configuration of the midsurface and the director field */
+    const std::vector<RealTuple<double, 3>> &midSurfaceRefConfig;
+    const std::vector<UnitVector<double, 3>> &directorRefConfig;
+  };
+
+  /** \brief Calculate the Green-Lagrange strain components for the thickness-integrated setting
+   *
+   * The components are returned as [a_11, a_22, a_12, b_11, b_22, b_12, gamma_1, gamma_2]
+   * where a is the midsurface metric,
+   * b is similar to the second fundamental form of the surface, but the unit normal is replaced with the shell director
+   * gamma_1 and gamma_2 are the transverse shear in the two parametric directions
+   * Paper Equation 4.10 */
+  template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type>
+  Dune::FieldVector<field_type, 8> SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::calculateGreenLagrangianStrains(
+      const KinematicVariables &kin)
+  {
+    Dune::FieldVector<RT, 8> egl;
+    // membrane
+    egl[0] = kin.A1andA2[0] * kin.ud1andud2[0] + 0.5 * kin.ud1andud2[0].two_norm2();
+    egl[1] = kin.A1andA2[1] * kin.ud1andud2[1] + 0.5 * kin.ud1andud2[1].two_norm2();
+    egl[2] = kin.a1anda2[0] * kin.a1anda2[1];
+
+    // bending
+    egl[3] = kin.a1anda2[0] * kin.td1Andtd2[0] - kin.A1andA2[0] * kin.t0d1Andt0d2[0];
+    egl[4] = kin.a1anda2[1] * kin.td1Andtd2[1] - kin.A1andA2[1] * kin.t0d1Andt0d2[1];
+    egl[5] = kin.a1anda2[0] * kin.td1Andtd2[1] + kin.a1anda2[1] * kin.td1Andtd2[0] - kin.A1andA2[0] * kin.t0d1Andt0d2[1]
+             - kin.A1andA2[1] * kin.t0d1Andt0d2[0];
+
+    // transverse shear
+    egl[6] = kin.a1anda2[0] * kin.t - kin.A1andA2[0] * kin.t0;
+    egl[7] = kin.a1anda2[1] * kin.t - kin.A1andA2[1] * kin.t0;
+
+    return egl;
+  }
+
+  /** \brief Calculates all kinematic quantities that are needed for strain calculation
+   *
+   * Paper Equations 6.4 - 6.8
+   */
+  template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type>
+  template <typename Element, typename LocalDirectorFunction, typename LocalMidSurfaceFunction, typename LocalDirectorReferenceFunction,
+            typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition>
+  auto SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::kinematicVariablesFactory(
+      const Element &element, const LocalDirectorFunction &directorFunction, const LocalDirectorReferenceFunction &directorReferenceFunction,
+      const LocalMidSurfaceFunction &midSurfaceFunction, const LocalMidSurfaceReferenceFunction &midSurfaceReferenceFunction,
+      const LocalMidSurfaceFunction &midSurfaceDisplacementFunction, const IntegrationPointPosition &quadPos)
+  {
+    KinematicVariables kin{};
+
+    const auto jInvT = element.geometry().jacobianInverseTransposed(quadPos);
+
+    kin.t           = directorFunction.evaluate(quadPos).globalCoordinates();
+    kin.t0          = directorReferenceFunction.evaluate(quadPos).globalCoordinates();
+    kin.a1anda2     = transpose(midSurfaceFunction.evaluateDerivative(quadPos) * transpose(jInvT));
+    kin.A1andA2     = transpose(midSurfaceReferenceFunction.evaluateDerivative(quadPos) * transpose(jInvT));
+    kin.ud1andud2   = transpose(midSurfaceDisplacementFunction.evaluateDerivative(quadPos) * transpose(jInvT));
+    kin.t0d1Andt0d2 = transpose(directorReferenceFunction.evaluateDerivative(quadPos) * transpose(jInvT));
+    kin.td1Andtd2   = transpose(directorFunction.evaluateDerivative(quadPos) * transpose(jInvT));
+
+    return kin;
+  }
+
+  template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type>
+  auto SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::getReferenceLocalConfigurations(
+      const typename Basis::LocalView &localView) const {
+    using namespace Dune::TypeTree::Indices;
+    const int nDofs0 = localView.tree().child(_0, 0).finiteElement().size();
+    const int nDofs1 = localView.tree().child(_1, 0).finiteElement().size();
+
+    std::vector<RealTuple<double, 3>> localConfiguration0(nDofs0);
+    std::vector<UnitVector<double, 3>> localConfiguration1(nDofs1);
+
+    for (int i = 0; i < nDofs0 + nDofs1; i++) {
+      int localIndexI = 0;
+      if (i < nDofs0) {
+        auto &node  = localView.tree().child(_0, 0);
+        localIndexI = node.localIndex(i);
+      } else {
+        auto &node  = localView.tree().child(_1, 0);
+        localIndexI = node.localIndex(i - nDofs0);
+      }
+      auto multiIndex = localView.index(localIndexI);
+
+      // The CompositeBasis number is contained in multiIndex[0]
+      // multiIndex[1] contains the actual index
+      if (multiIndex[0] == 0)
+        localConfiguration0[i] = midSurfaceRefConfig[multiIndex[1]];
+      else if (multiIndex[0] == 1)
+        localConfiguration1[i - nDofs0] = directorRefConfig[multiIndex[1]];
+    }
+    return std::make_tuple(localConfiguration0, localConfiguration1);
+  }
+
+  /** \brief Computes the Simo-Fox shell energy
+   *
+   *  The energy is 0.5 * S * E = 0.5 * transpose(E) * Cmat * E, where
+   *  S are the stress resultants [membrane forces, bending moments, transverse shear forces]
+   *  E are the components of the Green-Lagrangian strains [membrane strains, bending , tranverse shear]
+   *  see for details Paper Equation 4.11,4.10 and 10.1
+   */
+  template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type>
+  typename SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::RT
+  SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::energy(
+      const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3>> &localMidSurfaceConfiguration,
+      const std::vector<UnitVector<field_type, 3>> &localDirectorConfiguration) const
+  {
+    auto element = localView.element();
+
+    using namespace Dune::Indices;
+    const auto &midSurfaceElement = LocalFiniteElementFactory<Basis, 0>::get(localView, _0);
+    const auto &directorElement   = LocalFiniteElementFactory<Basis, 1>::get(localView, _1);
+
+    const auto [localRefMidSurfaceConfiguration, localRefDirectorConfiguration] = getReferenceLocalConfigurations(localView);
+
+    std::vector<RealTuple<field_type, 3>> displacements;
+    displacements.reserve(localMidSurfaceConfiguration.size());
+    for (size_t i = 0; i < localMidSurfaceConfiguration.size(); ++i)
+      displacements.emplace_back(localMidSurfaceConfiguration[i].globalCoordinates() - localRefMidSurfaceConfiguration[i].globalCoordinates());
+
+    const LocalMidSurfaceFunctionType localMidSurfaceFunction(midSurfaceElement, localMidSurfaceConfiguration);
+    const LocalMidSurfaceFunctionType localMidSurfaceDisplacementFunction(midSurfaceElement, displacements);
+    const LocalMidSurfaceReferenceFunctionType localMidSurfaceReferenceFunction(midSurfaceElement, localRefMidSurfaceConfiguration);
+    const LocalDirectorFunctionType localDirectorFunction(directorElement, localDirectorConfiguration);
+    const LocalDirectorReferenceFunctionType localDirectorReferenceFunction(directorElement, localRefDirectorConfiguration);
+
+    const int quadOrder = (element.type().isSimplex()) ? std::max(midSurfaceElement.localBasis().order(), directorElement.localBasis().order())
+                                                       : std::max(midSurfaceElement.localBasis().order(), directorElement.localBasis().order()) + 1;
+
+    const auto &quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder);
+
+    RT energy = 0;
+    for (const auto &curQuad : quad) {
+      const Dune::FieldVector<DT, gridDim> &quadPos = curQuad.position();
+
+      const KinematicVariables kin
+          = kinematicVariablesFactory(element, localDirectorFunction, localDirectorReferenceFunction, localMidSurfaceFunction,
+                                      localMidSurfaceReferenceFunction, localMidSurfaceDisplacementFunction, quadPos);
+
+      const DT integrationElement = element.geometry().integrationElement(quadPos);
+      const FieldVector<field_type, 8> Egl = calculateGreenLagrangianStrains(kin);
+      energy +=  0.5 * Egl * (CMat_ * Egl) * curQuad.weight() * integrationElement;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////
+    //   Assemble boundary contributions
+    //////////////////////////////////////////////////////////////////////////////
+
+    if (not neumannFunction_) return energy;
+
+    for (auto &&it : intersections(neumannBoundary_->gridView(), element))
+    {
+      if (not neumannBoundary_ or not neumannBoundary_->contains(it)) continue;
+
+      const auto &quadLine = QuadratureRules<DT, gridDim - 1>::rule(it.type(), quadOrder);
+
+      for (const auto &curQuad : quadLine)
+      {
+        // Local position of the quadrature point
+        const FieldVector<DT, gridDim> &quadPos = it.geometryInInside().global(curQuad.position());
+
+        const DT integrationElement = it.geometry().integrationElement(curQuad.position());
+
+        // The value of the local function
+        RealTuple<field_type, 3> deformationValue = localMidSurfaceFunction.evaluate(quadPos);
+
+        // Value of the Neumann data at the current position
+        auto neumannValue = neumannFunction_(it.geometry().global(curQuad.position()));
+
+        // Only translational dofs are affected by the Neumann force
+        for (size_t i = 0; i < neumannValue.size(); i++)
+          energy -= (neumannValue[i] * deformationValue.globalCoordinates()[i]) * curQuad.weight() * integrationElement;
+      }
+    }
+    return energy;
+  }
+}  // namespace Dune::GFE
+#endif  // DUNE_GFE_SIMOFOX_ENERGY_HH
diff --git a/problems/simofox-cantilever-dirichlet-values.py b/problems/simofox-cantilever-dirichlet-values.py
new file mode 100644
index 00000000..79695fd4
--- /dev/null
+++ b/problems/simofox-cantilever-dirichlet-values.py
@@ -0,0 +1,16 @@
+import math
+
+class DirichletValues:
+    def __init__(self, homotopyParameter):
+        self.homotopyParameter = homotopyParameter
+
+    def deformation(self, x):
+        # Dirichlet b.c. simply clamp the shell in the reference configuration
+        out = [x[0], x[1], 0]
+
+        return out
+
+    def director(self, x):
+        dir = [0, 0, 1]
+        return dir
+
diff --git a/problems/simofox-cantilever.parset b/problems/simofox-cantilever.parset
new file mode 100644
index 00000000..1eab7e5b
--- /dev/null
+++ b/problems/simofox-cantilever.parset
@@ -0,0 +1,99 @@
+#############################################
+#  Grid parameters
+#############################################
+
+structuredGrid = true
+
+# bounding box
+lower = 0 0
+upper = 10 5
+
+elements = 10 1
+
+# Number of grid levels
+numLevels = 1
+
+#############################################
+#  Solver parameters
+#############################################
+
+# Number of homotopy steps for the Dirichlet boundary conditions
+numHomotopySteps = 10
+
+# Tolerance of the trust region solver
+tolerance = 1e-6
+
+# Max number of steps of the trust region solver
+maxTrustRegionSteps = 200
+
+trustRegionScaling = 1 1 1 0.01 0.01
+
+# Initial trust-region radius
+initialTrustRegionRadius = 10
+
+# Number of multigrid iterations per trust-region step
+numIt = 400
+
+# Number of presmoothing steps
+nu1 = 3
+
+# Number of postsmoothing steps
+nu2 = 3
+
+# Number of coarse grid corrections
+mu = 1
+
+# Number of base solver iterations
+baseIt = 1
+
+# Tolerance of the multigrid solver
+mgTolerance = 1e-6
+
+# Tolerance of the base grid solver
+baseTolerance = 1e-6
+
+# Measure convergence
+instrumented = 0
+
+############################
+#   Material parameters
+############################
+
+
+## For the Wriggers L-shape example
+[materialParameters]
+
+# shell thickness
+thickness = 1
+
+# Lame parameters
+mu = 100000
+lambda = 0
+
+# Shear correction factor
+kappa = 1
+
+[]
+
+#############################################
+#  Boundary values
+#############################################
+
+problem = simofox-cantilever
+
+###  Python predicate specifying all Dirichlet grid vertices
+# x is the vertex coordinate
+dirichletVerticesPredicate = "x[0] < 0.01"
+
+###  Python predicate specifying all Neumann grid vertices
+# x is the vertex coordinate
+neumannVerticesPredicate = "x[0] > 9.99"
+
+###  Neumann values
+neumannValues = 0 0 320
+
+# Initial deformation
+initialDeformation = "[x[0], x[1], 0]"
+
+#startFromFile = yes
+#initialIterateFilename = initial_iterate.vtu
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 33b44cda..4962aa5d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,8 @@
 set(programs compute-disc-error
              cosserat-continuum
              gradient-flow
-             harmonicmaps)
+             harmonicmaps
+             simofoxshell)
 #            rodobstacle)
 if (${DUNE_COMMON_VERSION} VERSION_GREATER_EQUAL 2.8)
   set (programs rod3d ${programs})
diff --git a/src/simofoxshell.cc b/src/simofoxshell.cc
new file mode 100644
index 00000000..eda66c50
--- /dev/null
+++ b/src/simofoxshell.cc
@@ -0,0 +1,390 @@
+#include <config.h>
+
+#include <array>
+#include <dune/fufem/utilities/adolcnamespaceinjections.hh>
+
+#include <dune/common/typetraits.hh>
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+#include <dune/common/tuplevector.hh>
+
+#include <dune/grid/utility/structuredgridfactory.hh>
+#include <dune/grid/io/file/gmshreader.hh>
+#include <dune/grid/uggrid.hh>
+
+#include <dune/functions/functionspacebases/lagrangebasis.hh>
+#include <dune/functions/functionspacebases/compositebasis.hh>
+#include <dune/functions/functionspacebases/powerbasis.hh>
+
+#include <dune/fufem/functiontools/boundarydofs.hh>
+#include <dune/fufem/dunepython.hh>
+
+#include <dune/gfe/mixedlocalgfeadolcstiffness.hh>
+#include <dune/gfe/simofoxenergy.hh>
+#include <dune/gfe/cosseratvtkwriter.hh>
+#include <dune/gfe/embeddedglobalgfefunction.hh>
+#include <dune/gfe/mixedgfeassembler.hh>
+#include <dune/gfe/mixedriemanniantrsolver.hh>
+#include <dune/gfe/unitvector.hh>
+#include <dune/gfe/localgeodesicfefunction.hh>
+#include <dune/gfe/localprojectedfefunction.hh>
+
+#if HAVE_DUNE_VTK
+#include <dune/vtk/vtkreader.hh>
+#endif
+
+template <int dim, class ctype, class LocalFiniteElement, class TS>
+using LocalFEFunction = Dune::GFE::LocalProjectedFEFunction<dim,ctype,LocalFiniteElement,TS>;
+//using LocalFEFunction = LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TS>;
+
+// Order of the approximation space for the midsurface position
+const int midsurfaceOrder = 1;
+
+// Order of the approximation space for the director
+const int directorOrder = 1;
+
+using namespace Dune;
+
+int main(int argc, char *argv[]) try
+{
+  // Initialize MPI, finalize is done automatically on exit
+  Dune::MPIHelper &mpiHelper = MPIHelper::instance(argc, argv);
+
+  // Start Python interpreter
+  Python::start();
+  Python::Reference main = Python::import("__main__");
+  Python::run("import math");
+
+  Python::runStream()
+    << std::endl << "import sys"
+    << std::endl << "import os"
+    << std::endl << "sys.path.append(os.getcwd() + '/../../problems/')"
+    << std::endl;
+
+  using namespace TypeTree::Indices;
+  using SolutionType = TupleVector<std::vector<RealTuple<double,3> >, std::vector<UnitVector<double,3> > >;
+
+  // parse data file
+  ParameterTree parameterSet;
+  if (argc < 2)
+    DUNE_THROW(Exception, "Usage: ./simofoxshell inputfile.dat");
+
+  ParameterTreeParser::readINITree(argv[1], parameterSet);
+
+  ParameterTreeParser::readOptions(argc, argv, parameterSet);
+
+  // read solver settings
+  const auto numLevels = parameterSet.get<int>("numLevels");
+  const auto totalLoadSteps = parameterSet.get<int>("numHomotopySteps");
+  const auto tolerance = parameterSet.get<double>("tolerance");
+  const auto maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps");
+  const auto initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius");
+  const auto multigridIterations = parameterSet.get<int>("numIt");
+  const auto nu1 = parameterSet.get<int>("nu1");
+  const auto nu2 = parameterSet.get<int>("nu2");
+  const auto mu = parameterSet.get<int>("mu");
+  const auto baseIterations = parameterSet.get<int>("baseIt");
+  const auto mgTolerance = parameterSet.get<double>("mgTolerance");
+  const auto baseTolerance = parameterSet.get<double>("baseTolerance");
+  const auto instrumented = parameterSet.get<bool>("instrumented");
+  std::string resultPath = parameterSet.get("resultPath", "");
+
+  /////////////////////////////////////////
+  //    Create the grid
+  /////////////////////////////////////////
+  using Grid = UGGrid<2>;
+
+  std::shared_ptr<Grid> grid;
+
+  FieldVector<double, 2> lower(0), upper(1);
+
+  std::string structuredGridType = parameterSet["structuredGrid"];
+  if (parameterSet.get<bool>("structuredGrid"))
+  {
+    lower = parameterSet.get<FieldVector<double, 2> >("lower");
+    upper = parameterSet.get<FieldVector<double, 2> >("upper");
+
+    auto elements = parameterSet.get<std::array<unsigned int, 2> >("elements");
+    grid = StructuredGridFactory<Grid>::createCubeGrid(lower, upper, elements);
+
+  } else {
+    auto path = parameterSet.get<std::string>("path");
+    auto gridFile = parameterSet.get<std::string>("gridFile");
+
+    // Guess the grid file format by looking at the file name suffix
+    auto dotPos = gridFile.rfind('.');
+    if (dotPos == std::string::npos)
+      DUNE_THROW(IOError, "Could not determine grid input file format");
+    std::string suffix = gridFile.substr(dotPos, gridFile.length() - dotPos);
+
+    if (suffix == ".msh")
+      grid = std::shared_ptr<Grid>(GmshReader<Grid>::read(path + "/" + gridFile));
+    else if (suffix == ".vtu" or suffix == ".vtp")
+#if HAVE_DUNE_VTK
+      grid = VtkReader<Grid>::createGridFromFile(path + "/" + gridFile);
+#else
+      DUNE_THROW(NotImplemented, "Please install dune-vtk for VTK reading support!");
+#endif
+  }
+
+  grid->globalRefine(numLevels - 1);
+  grid->loadBalance();
+
+  if (mpiHelper.rank() == 0)
+    std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl;
+
+  using GridView = Grid::LeafGridView;
+  GridView gridView = grid->leafGridView();
+
+  using namespace Dune::Functions::BasisFactory;
+
+  auto compositeBasis = makeBasis(gridView,
+                                  composite(
+                                    power<2>(lagrange<midsurfaceOrder>()),
+                                    power<2>(lagrange<directorOrder>())));
+
+  typedef Dune::Functions::LagrangeBasis<GridView, midsurfaceOrder> MidsurfaceFEBasis;
+  typedef Dune::Functions::LagrangeBasis<GridView, directorOrder> DirectorFEBasis;
+
+  MidsurfaceFEBasis midsurfaceFEBasis(gridView);
+  DirectorFEBasis directorFEBasis(gridView);
+
+  ///////////////////////////////////////////
+  //   Read Dirichlet values
+  ///////////////////////////////////////////
+
+  BitSetVector<1> dirichletVertices(gridView.size(2), false);
+  BitSetVector<1> neumannVertices(gridView.size(2), false);
+
+  const GridView::IndexSet &indexSet = gridView.indexSet();
+
+  // Make Python function that computes which vertices are on the Dirichlet boundary,
+  // based on the vertex positions.
+  std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")");
+  auto pythonDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda));
+
+  // Same for the Neumann boundary
+  lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")");
+  auto pythonNeumannVertices = Python::make_function<bool>(Python::evaluate(lambda));
+
+  for (auto &&vertex: vertices(gridView))
+  {
+    bool isDirichlet = pythonDirichletVertices(vertex.geometry().corner(0));
+    dirichletVertices[indexSet.index(vertex)] = isDirichlet;
+
+    bool isNeumann = pythonNeumannVertices(vertex.geometry().corner(0));
+    neumannVertices[indexSet.index(vertex)] = isNeumann;
+  }
+
+  BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices);
+  BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices);
+
+  if (mpiHelper.rank() == 0)
+    std::cout << "Neumann boundary has " << neumannBoundary.numFaces() << " faces\n";
+
+  BitSetVector<1> deformationDirichletNodes(midsurfaceFEBasis.size(), false);
+  constructBoundaryDofs(dirichletBoundary, midsurfaceFEBasis, deformationDirichletNodes);
+
+  BitSetVector<1> neumannNodes(midsurfaceFEBasis.size(), false);
+  constructBoundaryDofs(neumannBoundary, directorFEBasis, neumannNodes);
+
+  BitSetVector<3> deformationDirichletDofs(midsurfaceFEBasis.size(), false);
+  for (size_t i = 0; i < midsurfaceFEBasis.size(); i++)
+    if (deformationDirichletNodes[i][0])
+      for (int j = 0; j < 3; j++)
+        deformationDirichletDofs[i][j] = true;
+
+  BitSetVector<1> orientationDirichletNodes(directorFEBasis.size(), false);
+  constructBoundaryDofs(dirichletBoundary, directorFEBasis, orientationDirichletNodes);
+
+  BitSetVector<2> orientationDirichletDofs(directorFEBasis.size(), false);
+  for (size_t i = 0; i < directorFEBasis.size(); i++)
+    if (orientationDirichletNodes[i][0])
+      for (int j = 0; j < 2; j++)
+        orientationDirichletDofs[i][j] = true;
+
+  ////////////////////////////
+  //   Initial iterate
+  ////////////////////////////
+
+  SolutionType x;
+
+  x[_0].resize(midsurfaceFEBasis.size());
+
+  lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
+  auto pythonInitialDeformation = Python::make_function<FieldVector<double, 3> >(Python::evaluate(lambda));
+
+  auto deformationPowerBasis = makeBasis(gridView,power<3>(lagrange<midsurfaceOrder>()));
+
+  std::vector<FieldVector<double, 3> > v;
+  Functions::interpolate(deformationPowerBasis, v, pythonInitialDeformation);
+  std::copy(v.begin(), v.end(), x[_0].begin());
+
+  x[_1].resize(directorFEBasis.size());
+
+  // The code currently assumes that the grid resides in the x-y plane
+  // Therefore, all references directors point into z direction
+  // If the reference is not planar one has to calculate the reference nodal directors.
+  // E.g. for bilinear grid elements this can be just the average of the 4 element normals
+  const FieldVector<double, 3> referenceDirectorVector({0, 0, 1});   //only plain reference!
+  std::fill(x[_1].begin(), x[_1].end(), referenceDirectorVector);
+
+  const SolutionType x0 = x;
+
+  ////////////////////////////////////////////////////////
+  //   Main homotopy loop
+  ////////////////////////////////////////////////////////
+
+  for (int loadStep = 0; loadStep < totalLoadSteps; loadStep++)
+  {
+    const double loadFactor = (loadStep + 1) * (1.0 / totalLoadSteps);
+    if (mpiHelper.rank() == 0)
+      std::cout << "Homotopy step: " << loadStep << ",    parameter: " << loadFactor << std::endl;
+
+    //////////////////////////////////////////////////////////////
+    //   Create an assembler for the energy functional
+    //////////////////////////////////////////////////////////////
+
+    const ParameterTree &materialParameters = parameterSet.sub("materialParameters");
+    FieldVector<double, 3> neumannValues{0, 0, 0};
+    if (parameterSet.hasKey("neumannValues"))
+      neumannValues = parameterSet.get<FieldVector<double, 3> >("neumannValues");
+
+    auto neumannFunction = [&](FieldVector<double, 2>) {
+                             auto nV = neumannValues;
+                             nV *= loadFactor;
+                             return nV;
+                           };
+
+    // Output initial iterate (of homotopy loop)
+    auto directorPowerBasis = makeBasis(gridView, power<3>(lagrange<directorOrder>()));
+
+    BlockVector<FieldVector<double, 3> > displacementInitial(compositeBasis.size({0}));
+    //calculates the global coordinates of midsurface displacements into displacementInitial
+    std::transform(x[_0].begin(),x[_0].end(),x0[_0].begin(),displacementInitial.begin(),[](const auto& x_0Entry,const auto& x0_0Entry){
+      return x_0Entry.globalCoordinates()-x0_0Entry.globalCoordinates();
+    });
+
+    //copies the global coordinates of the directors to directorInitial for a VTKWriter usable format
+    BlockVector<FieldVector<double, 3> > directorInitial(compositeBasis.size({1}));
+    std::transform(x[_1].begin(),x[_1].end(),directorInitial.begin(),[](const auto& x_1Entry){
+      return x_1Entry.globalCoordinates();
+    });
+
+    auto displacementFunctionInitial = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(deformationPowerBasis,
+                                                                                                                displacementInitial);
+    auto directorFunctionInitial = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(directorPowerBasis,
+                                                                                                            directorInitial);
+    //  We need to subsample, because VTK cannot natively display real second-order functions
+    SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(midsurfaceOrder - 1));
+    vtkWriter.addVertexData(displacementFunctionInitial, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, 3));
+    vtkWriter.addVertexData(directorFunctionInitial, VTK::FieldInfo("director", VTK::FieldInfo::Type::scalar, 3));
+    vtkWriter.write(resultPath + "simo-fox_homotopy_" + "_" + std::to_string(neumannValues[2]) + "_" + std::to_string(0));
+
+    if (mpiHelper.rank() == 0) {
+      std::cout << "Material parameters:" << std::endl;
+      materialParameters.report();
+    }
+
+    // Assembler using ADOL-C
+    Dune::GFE::SimoFoxEnergyLocalStiffness<decltype(compositeBasis), LocalFEFunction,adouble> simoFoxEnergyADOLCLocalStiffness(materialParameters,
+                                                                                                    &neumannBoundary,
+                                                                                                    neumannFunction,
+                                                                                                    nullptr, x0);
+
+    MixedLocalGFEADOLCStiffness<decltype(compositeBasis),
+        RealTuple<double,3>,
+        UnitVector<double,3> > localGFEADOLCStiffness(&simoFoxEnergyADOLCLocalStiffness);
+
+    MixedGFEAssembler<decltype(compositeBasis),
+        RealTuple<double,3>, UnitVector<double,3> > assembler(compositeBasis, &localGFEADOLCStiffness);
+
+    // /////////////////////////////////////////////////
+    //   Create a Riemannian trust-region solver
+    // /////////////////////////////////////////////////
+
+    MixedRiemannianTrustRegionSolver<Grid,
+        decltype(compositeBasis),
+        MidsurfaceFEBasis, RealTuple<double,3>,
+        DirectorFEBasis, UnitVector<double,3> > solver;
+
+    solver.setup(*grid,
+                 &assembler,
+                 midsurfaceFEBasis,
+                 directorFEBasis,
+                 x,
+                 deformationDirichletDofs,
+                 orientationDirichletDofs,
+                 tolerance,
+                 maxTrustRegionSteps,
+                 initialTrustRegionRadius,
+                 multigridIterations,
+                 mgTolerance,
+                 mu, nu1, nu2,
+                 baseIterations,
+                 baseTolerance,
+                 instrumented);
+
+    solver.setScaling(parameterSet.get<FieldVector<double, 5> >("trustRegionScaling"));
+
+    ////////////////////////////////////////////////////////
+    //   Set Dirichlet values
+    ////////////////////////////////////////////////////////
+
+    Python::Reference dirichletValuesClass = Python::import(parameterSet.get<std::string>("problem") + "-dirichlet-values");
+
+    Python::Callable C = dirichletValuesClass.get("DirichletValues");
+
+    // Call a constructor.
+    Python::Reference dirichletValuesPythonObject = C(loadFactor);
+
+    // Extract object member functions as Dune functions
+    auto deformationDirichletValues = Python::make_function<FieldVector<double, 3> >(dirichletValuesPythonObject.get("deformation"));
+
+    std::vector<FieldVector<double, 3> > ddV;
+    Functions::interpolate(deformationPowerBasis, ddV, deformationDirichletValues, deformationDirichletDofs);
+
+    for (size_t j = 0; j < x[_0].size(); j++)
+      if (deformationDirichletNodes[j][0])
+        x[_0][j] = ddV[j];
+
+    // /////////////////////////////////////////////////////
+    //   Solve!
+    // /////////////////////////////////////////////////////
+
+    solver.setInitialIterate(x);
+    solver.solve();
+
+    x = solver.getSol();
+
+    // Output result of each load step
+    std::stringstream iAsAscii;
+    iAsAscii << loadStep + 1;
+
+    //calculates the global coordinates of midsurface displacements into displacementInitial
+    BlockVector<FieldVector<double, 3> > displacement(compositeBasis.size({0}));
+    std::transform(x[_0].begin(),x[_0].end(),x0[_0].begin(),displacement.begin(),[](const auto& x_0Entry,const auto& x0_0Entry){
+      return x_0Entry.globalCoordinates()-x0_0Entry.globalCoordinates();
+    });
+
+    //copies the global coordinates of the directors to directorInitial for a VTKWriter usable format
+    BlockVector<FieldVector<double, 3> > director(compositeBasis.size({1}));
+    std::transform(x[_1].begin(),x[_1].end(),director.begin(),[](const auto& x_1Entry){
+      return x_1Entry.globalCoordinates();
+    });
+
+    auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(deformationPowerBasis,
+                                                                                                          displacement);
+    auto directorFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(directorPowerBasis,
+                                                                                                      director);
+    //  We need to subsample, because VTK cannot natively display real second-order functions
+    //  SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(midsurfaceOrder - 1));
+    vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, 3));
+    vtkWriter.addVertexData(directorFunction, VTK::FieldInfo("director", VTK::FieldInfo::Type::scalar, 3));
+    vtkWriter.write(resultPath + "simo-fox_homotopy_" + "_" + std::to_string(neumannValues[2]) + "_" + std::to_string(loadStep + 1));
+  }
+}
+catch (Exception &e) {
+  std::cout << e.what() << std::endl;
+}
-- 
GitLab