From df015a54ba3f86dbf64f22797ee72b16d9d218e0 Mon Sep 17 00:00:00 2001
From: Lisa Julia Nebel <lisa_julia.nebel@tu-dresden.de>
Date: Fri, 19 Mar 2021 14:47:18 +0100
Subject: [PATCH] Clean up the usage of MIXED_SPACE in cosserat-continuum

Now all combinations of dim = dimworld or dim != dimworld and MIXED_SPACE = 0 or MIXED_SPACE = 1 at least compile.
---
 src/cosserat-continuum.cc | 431 +++++++++++++++++++++-----------------
 1 file changed, 243 insertions(+), 188 deletions(-)

diff --git a/src/cosserat-continuum.cc b/src/cosserat-continuum.cc
index 1bde7dd5..6c38993b 100644
--- a/src/cosserat-continuum.cc
+++ b/src/cosserat-continuum.cc
@@ -1,3 +1,5 @@
+#define MIXED_SPACE 0
+
 #include <config.h>
 
 #include <fenv.h>
@@ -37,7 +39,6 @@
 #include <dune/solvers/solvers/iterativesolver.hh>
 #include <dune/solvers/norms/energynorm.hh>
 
-#include <dune/gfe/rigidbodymotion.hh>
 #include <dune/gfe/localgeodesicfeadolcstiffness.hh>
 #include <dune/gfe/mixedlocalgfeadolcstiffness.hh>
 #include <dune/gfe/cosseratenergystiffness.hh>
@@ -45,10 +46,16 @@
 #include <dune/gfe/cosseratvtkwriter.hh>
 #include <dune/gfe/cosseratvtkreader.hh>
 #include <dune/gfe/geodesicfeassembler.hh>
-#include <dune/gfe/riemanniantrsolver.hh>
 #include <dune/gfe/embeddedglobalgfefunction.hh>
 #include <dune/gfe/mixedgfeassembler.hh>
+
+#if MIXED_SPACE
 #include <dune/gfe/mixedriemanniantrsolver.hh>
+#else
+#include <dune/gfe/geodesicfeassemblerwrapper.hh>
+#include <dune/gfe/riemanniantrsolver.hh>
+#include <dune/gfe/rigidbodymotion.hh>
+#endif
 
 #if HAVE_DUNE_VTK
 #include <dune/vtk/vtkreader.hh>
@@ -59,22 +66,17 @@
 const int dim = 2;
 const int dimworld = 2;
 
-//#define MIXED_SPACE
-
 // Order of the approximation space for the displacement
 const int displacementOrder = 2;
 
 // Order of the approximation space for the microrotations
 const int rotationOrder = 2;
 
-#ifndef MIXED_SPACE
+#if !MIXED_SPACE
 static_assert(displacementOrder==rotationOrder, "displacement and rotation order do not match!");
 
 // Image space of the geodesic fe functions
-typedef RigidBodyMotion<double,3> TargetSpace;
-
-// Tangent vector of the image space
-const int blocksize = TargetSpace::TangentVector::dimension;
+using TargetSpace = RigidBodyMotion<double, 3>;
 #endif
 
 using namespace Dune;
@@ -84,6 +86,15 @@ int main (int argc, char *argv[]) try
     // initialize MPI, finalize is done automatically on exit
     Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv);
 
+    if (mpiHelper.rank()==0) {
+        std::cout << "DISPLACEMENTORDER = " << displacementOrder << ", ROTATIONORDER = " << rotationOrder << std::endl;
+        std::cout << "dim = " << dim << ", dimworld = " << dimworld << std::endl;
+    #if MIXED_SPACE
+        std::cout << "MIXED_SPACE = 1" << std::endl;
+    #else
+        std::cout << "MIXED_SPACE = 0" << std::endl;
+    #endif
+    }
     // Start Python interpreter
     Python::start();
     Python::Reference main = Python::import("__main__");
@@ -97,12 +108,8 @@ int main (int argc, char *argv[]) try
         << std::endl;
 
     using namespace TypeTree::Indices;
-#ifdef MIXED_SPACE
     using SolutionType = TupleVector<std::vector<RealTuple<double,3> >,
                                      std::vector<Rotation<double,3> > >;
-#else
-    typedef std::vector<TargetSpace> SolutionType;
-#endif
 
     // parse data file
     ParameterTree parameterSet;
@@ -145,6 +152,8 @@ int main (int argc, char *argv[]) try
 
     std::string structuredGridType = parameterSet["structuredGrid"];
     if (structuredGridType == "cube") {
+        if (dim!=dimworld)
+            DUNE_THROW(GridError, "Please use FoamGrid and read in a grid for problems with dim != dimworld.");
 
         lower = parameterSet.get<FieldVector<double,dimworld> >("lower");
         upper = parameterSet.get<FieldVector<double,dimworld> >("upper");
@@ -183,7 +192,7 @@ int main (int argc, char *argv[]) try
     GridView gridView = grid->leafGridView();
 
     using namespace Dune::Functions::BasisFactory;
-#ifdef MIXED_SPACE
+
     const int dimRotation = Rotation<double,dim>::embeddedDim;
     auto compositeBasis = makeBasis(
       gridView,
@@ -195,6 +204,13 @@ int main (int argc, char *argv[]) try
             lagrange<rotationOrder>()
         )
     ));
+    using CompositeBasis = decltype(compositeBasis);
+
+    auto deformationPowerBasis = makeBasis(
+        gridView,
+        power<3>(
+            lagrange<displacementOrder>()
+    ));
 
     typedef Dune::Functions::LagrangeBasis<GridView,displacementOrder> DeformationFEBasis;
     typedef Dune::Functions::LagrangeBasis<GridView,rotationOrder> OrientationFEBasis;
@@ -202,10 +218,6 @@ int main (int argc, char *argv[]) try
     DeformationFEBasis deformationFEBasis(gridView);
     OrientationFEBasis orientationFEBasis(gridView);
 
-#else
-    typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, displacementOrder> FEBasis;
-    FEBasis feBasis(gridView);
-#endif
 
     // /////////////////////////////////////////
     //   Read Dirichlet values
@@ -261,43 +273,26 @@ int main (int argc, char *argv[]) try
       if (orientationDirichletNodes[i][0])
         for (int j=0; j<3; j++)
           orientationDirichletDofs[i][j] = true;
-#else
-    BitSetVector<1> dirichletNodes(feBasis.size(), false);
-    constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes);
-
-    BitSetVector<1> neumannNodes(feBasis.size(), false);
-    constructBoundaryDofs(neumannBoundary,feBasis,neumannNodes);
-
-
-    BitSetVector<blocksize> dirichletDofs(feBasis.size(), false);
-    for (size_t i=0; i<feBasis.size(); i++)
-      if (dirichletNodes[i][0])
-        for (int j=0; j<5; j++)
-          dirichletDofs[i][j] = true;
-#endif
 
     // //////////////////////////
     //   Initial iterate
     // //////////////////////////
 
-#ifdef MIXED_SPACE
     SolutionType x;
 
-    x[_0].resize(deformationFEBasis.size());
+    x[_0].resize(compositeBasis.size({0}));
 
     lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
     auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda));
 
     std::vector<FieldVector<double,3> > v;
-    Functions::interpolate(deformationFEBasis, v, pythonInitialDeformation);
+    Functions::interpolate(deformationPowerBasis, v, pythonInitialDeformation);
 
     for (size_t i=0; i<x[_0].size(); i++)
       x[_0][i] = v[i];
 
-    x[_1].resize(orientationFEBasis.size());
-#else
-    SolutionType x(feBasis.size());
-
+    x[_1].resize(compositeBasis.size({1}));
+#if !MIXED_SPACE
     if (parameterSet.hasKey("startFromFile"))
     {
       std::shared_ptr<GridType> initialIterateGrid;
@@ -314,35 +309,32 @@ int main (int argc, char *argv[]) try
         initialIterateGrid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + initialIterateGridFilename));
       }
 
-      SolutionType initialIterate;
+      std::vector<TargetSpace> initialIterate;
       GFE::CosseratVTKReader::read(initialIterate, parameterSet.get<std::string>("initialIterateFilename"));
 
       typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, 2> InitialBasis;
       InitialBasis initialBasis(initialIterateGrid->leafGridView());
 
 #ifdef PROJECTED_INTERPOLATION
-      using LocalInterpolationRule  = LocalProjectedFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
+      using LocalInterpolationRule  = LocalProjectedFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
 #else
-      using LocalInterpolationRule  = LocalGeodesicFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
+      using LocalInterpolationRule  = LocalGeodesicFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
 #endif
       GFE::EmbeddedGlobalGFEFunction<InitialBasis,LocalInterpolationRule,TargetSpace> initialFunction(initialBasis,initialIterate);
-
+      auto powerBasis = makeBasis(
+          gridView,
+          power<7>(
+              lagrange<displacementOrder>()
+      ));
       std::vector<FieldVector<double,7> > v;
-      Dune::Functions::interpolate(feBasis,v,initialFunction);
-      DUNE_THROW(NotImplemented, "Replace scalar basis by power basis!");
-
-      for (size_t i=0; i<x.size(); i++)
-        x[i] = TargetSpace(v[i]);
+      //TODO: Interpolate does not work with an GFE:EmbeddedGlobalGFEFunction
+      //Dune::Functions::interpolate(powerBasis,v,initialFunction);
 
-    } else {
-    lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
-      auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda));
-
-    std::vector<FieldVector<double,3> > v;
-      Functions::interpolate(feBasis, v, pythonInitialDeformation);
-
-    for (size_t i=0; i<x.size(); i++)
-      x[i].r = v[i];
+      for (size_t i=0; i<x.size(); i++) {
+        auto vTargetSpace = TargetSpace(v[i]);
+        x[_0][i] = vTargetSpace.r;
+        x[_1][i] = vTargetSpace.q;
+      }
     }
 #endif
 
@@ -351,14 +343,30 @@ int main (int argc, char *argv[]) try
     ////////////////////////////////////////////////////////
 
     // Output initial iterate (of homotopy loop)
-#ifdef MIXED_SPACE
+    BlockVector<FieldVector<double,3> > identity(compositeBasis.size({0}));
+    Dune::Functions::interpolate(deformationPowerBasis, identity, [&](FieldVector<double,dimworld> x){ return x;});
+    BlockVector<FieldVector<double,3> > displacement(compositeBasis.size({0}));
+
+    if (dim == dimworld) {
     CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0],
                                                                                    orientationFEBasis,x[_1],
                                                                                    resultPath + "mixed-cosserat_homotopy_0");
+    } else {
+#if MIXED_SPACE
+        for (int i = 0; i < displacement.size(); i++) {
+            for (int j = 0; j < 3; j++)
+                displacement[i][j] = x[_0][i][j];
+            displacement[i] -= identity[i];
+        }
+        auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3>>(deformationPowerBasis, displacement);
+    
+        SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(0));
+        vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dimworld));
+        vtkWriter.write(resultPath + "mixed-cosserat_homotopy_0");
 #else
-    CosseratVTKWriter<GridType>::write<FEBasis>(feBasis,x, resultPath + "cosserat_homotopy_0");
-#endif
-
+    CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x, resultPath + "cosserat_homotopy_0");
+#endif   
+    }
     for (int i=0; i<numHomotopySteps; i++) {
 
         double homotopyParameter = (i+1)*(1.0/numHomotopySteps);
@@ -396,83 +404,6 @@ int main (int argc, char *argv[]) try
         materialParameters.report();
     }
 
-    // Assembler using ADOL-C
-#ifdef MIXED_SPACE
-    CosseratEnergyLocalStiffness<decltype(compositeBasis),
-                        3,adouble> cosseratEnergyADOLCLocalStiffness(materialParameters,
-                                                                     &neumannBoundary,
-                                                                     neumannFunction,
-                                                                     volumeLoad);
-
-    MixedLocalGFEADOLCStiffness<decltype(compositeBasis),
-                                RealTuple<double,3>,
-                                Rotation<double,3> > localGFEADOLCStiffness(&cosseratEnergyADOLCLocalStiffness);
-
-    MixedGFEAssembler<decltype(compositeBasis),
-                      RealTuple<double,3>,
-                      Rotation<double,3> > assembler(compositeBasis, &localGFEADOLCStiffness);
-#else
-    std::shared_ptr<GFE::LocalEnergy<FEBasis,RigidBodyMotion<adouble,3> > > localCosseratEnergy;
-
-    if (dim==dimworld)
-    {
-      localCosseratEnergy = std::make_shared<CosseratEnergyLocalStiffness<FEBasis,3,adouble> >(materialParameters,
-                                                                                               &neumannBoundary,
-                                                                                               neumannFunction,
-                                                                                               volumeLoad);
-    }
-    else
-    {
-      localCosseratEnergy = std::make_shared<NonplanarCosseratShellEnergy<FEBasis,3,adouble> >(materialParameters,
-                                                                                               nullptr,
-                                                                                               &neumannBoundary,
-                                                                                               neumannFunction,
-                                                                                               volumeLoad);
-    }
-
-    LocalGeodesicFEADOLCStiffness<FEBasis,
-                                  TargetSpace> localGFEADOLCStiffness(localCosseratEnergy.get());
-
-    GeodesicFEAssembler<FEBasis,TargetSpace> assembler(gridView, localGFEADOLCStiffness);
-#endif
-
-    // /////////////////////////////////////////////////
-    //   Create a Riemannian trust-region solver
-    // /////////////////////////////////////////////////
-
-#ifdef MIXED_SPACE
-    MixedRiemannianTrustRegionSolver<GridType,
-                                     decltype(compositeBasis),
-                                     DeformationFEBasis, RealTuple<double,3>,
-                                     OrientationFEBasis, Rotation<double,3> > solver;
-#else
-    RiemannianTrustRegionSolver<FEBasis,TargetSpace> solver;
-#endif
-    solver.setup(*grid,
-                 &assembler,
-#ifdef MIXED_SPACE
-                 deformationFEBasis,
-                 orientationFEBasis,
-#endif
-                 x,
-#ifdef MIXED_SPACE
-                 deformationDirichletDofs,
-                 orientationDirichletDofs,
-#else
-                 dirichletDofs,
-#endif
-                 tolerance,
-                 maxTrustRegionSteps,
-                 initialTrustRegionRadius,
-                 multigridIterations,
-                 mgTolerance,
-                 mu, nu1, nu2,
-                 baseIterations,
-                 baseTolerance,
-                 instrumented);
-
-        solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling"));
-
         ////////////////////////////////////////////////////////
         //   Set Dirichlet values
         ////////////////////////////////////////////////////////
@@ -485,85 +416,208 @@ int main (int argc, char *argv[]) try
         Python::Reference dirichletValuesPythonObject = C(homotopyParameter);
 
         // Extract object member functions as Dune functions
-        auto deformationDirichletValues = Python::make_function<FieldVector<double,3> >(dirichletValuesPythonObject.get("deformation"));
-        auto orientationDirichletValues = Python::make_function<FieldMatrix<double,3,3> >(dirichletValuesPythonObject.get("orientation"));
-
-        std::vector<FieldVector<double,3> > ddV;
-        std::vector<FieldMatrix<double,3,3> > dOV;
-
-#ifdef MIXED_SPACE
-        Functions::interpolate(deformationFEBasis, ddV, deformationDirichletValues, deformationDirichletDofs);
-        Functions::interpolate(orientationFEBasis, dOV, orientationDirichletValues, orientationDirichletDofs);
-
-        for (size_t j=0; j<x[_0].size(); j++)
-          if (deformationDirichletNodes[j][0])
-            x[_0][j] = ddV[j];
-
-        for (size_t j=0; j<x[_1].size(); j++)
-          if (orientationDirichletNodes[j][0])
-            x[_1][j].set(dOV[j]);
+        auto deformationDirichletValues = Python::make_function<FieldVector<double,3> >   (dirichletValuesPythonObject.get("deformation"));
+        auto orientationDirichletValues = Python::make_function<FieldMatrix<double,3,3> > (dirichletValuesPythonObject.get("orientation"));
+    
+        BlockVector<FieldVector<double,3> > ddV;
+        Dune::Functions::interpolate(deformationPowerBasis, ddV, deformationDirichletValues, deformationDirichletDofs);
+    
+        BlockVector<FieldMatrix<double,3,3> > dOV;
+        Dune::Functions::interpolate(orientationFEBasis, dOV, orientationDirichletValues, orientationDirichletDofs);
+    
+        for (int i = 0; i < compositeBasis.size({0}); i++) {
+          if (deformationDirichletDofs[i][0])
+            x[_0][i] = ddV[i];
+        }
+        for (int i = 0; i < compositeBasis.size({1}); i++)
+          if (orientationDirichletDofs[i][0])
+            x[_1][i].set(dOV[i]);
+
+        if (dim==dimworld) {
+            CosseratEnergyLocalStiffness<CompositeBasis, 3,adouble> localCosseratEnergy(materialParameters,
+                                                                                            &neumannBoundary,
+                                                                                            neumannFunction,
+                                                                                            volumeLoad);
+            MixedLocalGFEADOLCStiffness<CompositeBasis,
+                                RealTuple<double,3>,
+                                Rotation<double,3> > localGFEADOLCStiffness(&localCosseratEnergy);
+            MixedGFEAssembler<CompositeBasis,
+                      RealTuple<double,3>,
+                      Rotation<double,3> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness);
+#if MIXED_SPACE
+            MixedRiemannianTrustRegionSolver<GridType,
+                                         CompositeBasis,
+                                         DeformationFEBasis, RealTuple<double,3>,
+                                         OrientationFEBasis, Rotation<double,3> > solver;
+            solver.setup(*grid,
+                     &mixedAssembler,
+                     deformationFEBasis,
+                     orientationFEBasis,
+                     x,
+                     deformationDirichletDofs,
+                     orientationDirichletDofs, tolerance,
+                     maxTrustRegionSteps,
+                     initialTrustRegionRadius,
+                     multigridIterations,
+                     mgTolerance,
+                     mu, nu1, nu2,
+                     baseIterations,
+                     baseTolerance,
+                     instrumented);
+
+            solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling"));
+
+            solver.setInitialIterate(x);
+            solver.solve();
+            x = solver.getSol();
 #else
-        Functions::interpolate(feBasis, ddV, deformationDirichletValues, dirichletDofs);
-        Functions::interpolate(feBasis, dOV, orientationDirichletValues, dirichletDofs);
-
-        for (size_t j=0; j<x.size(); j++)
-          if (dirichletNodes[j][0])
-          {
-            x[j].r = ddV[j];
-            x[j].q.set(dOV[j]);
-          }
+            //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones
+            //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion
+            //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler
+            std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0}));
+            BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false);
+            for (int i = 0; i < compositeBasis.size({0}); i++) {
+              for (int j = 0; j < 3; j ++) { // Displacement part
+                xTargetSpace[i].r[j] = x[_0][i][j];
+                dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j];
+              }
+              xTargetSpace[i].q = x[_1][i]; // Rotation part
+              for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++)
+                dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3];
+            }
+
+            using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, TargetSpace, RealTuple<double, 3>, Rotation<double,3>>;
+            GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis);
+            RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver;
+            solver.setup(*grid,
+                     &assembler,
+                     xTargetSpace,
+                     dirichletDofsTargetSpace,
+                     tolerance,
+                     maxTrustRegionSteps,
+                     initialTrustRegionRadius,
+                     multigridIterations,
+                     mgTolerance,
+                     mu, nu1, nu2,
+                     baseIterations,
+                     baseTolerance,
+                     instrumented);
+
+            solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling"));
+            solver.setInitialIterate(xTargetSpace);
+            solver.solve();
+            xTargetSpace = solver.getSol();
+            for (int i = 0; i < xTargetSpace.size(); i++) {
+              x[_0][i] = xTargetSpace[i].r;
+              x[_1][i] = xTargetSpace[i].q;
+            }
 #endif
-
-        // /////////////////////////////////////////////////////
-        //   Solve!
-        // /////////////////////////////////////////////////////
-
-        solver.setInitialIterate(x);
-        solver.solve();
-
-        x = solver.getSol();
-
+        } else {
+#if MIXED_SPACE
+            DUNE_THROW(Exception, "Problems with dim != dimworld do not work for MIXED_SPACE = 1!");
+#else
+            std::shared_ptr<LocalGeodesicFEADOLCStiffness<DeformationFEBasis, TargetSpace>> localGFEADOLCStiffness;
+
+            NonplanarCosseratShellEnergy<DeformationFEBasis, 3, adouble> localCosseratEnergyPlanar(materialParameters,
+                                                                                                    nullptr,
+                                                                                                    &neumannBoundary,
+                                                                                                    neumannFunction,
+                                                                                                    volumeLoad);
+              localGFEADOLCStiffness = std::make_shared<LocalGeodesicFEADOLCStiffness<DeformationFEBasis, TargetSpace>>(&localCosseratEnergyPlanar);
+
+            GeodesicFEAssembler<DeformationFEBasis,TargetSpace> assembler(gridView, *localGFEADOLCStiffness);
+            //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones
+            //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion
+            //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler
+            std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0}));
+            BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false);
+            for (int i = 0; i < compositeBasis.size({0}); i++) {
+              for (int j = 0; j < 3; j ++) { // Displacement part
+                xTargetSpace[i].r[j] = x[_0][i][j];
+                dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j];
+              }
+              xTargetSpace[i].q = x[_1][i]; // Rotation part
+              for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++)
+                dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3];
+            }
+
+            RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace> solver;
+            solver.setup(*grid,
+                     &assembler,
+                     xTargetSpace,
+                     dirichletDofsTargetSpace,
+                     tolerance,
+                     maxTrustRegionSteps,
+                     initialTrustRegionRadius,
+                     multigridIterations,
+                     mgTolerance,
+                     mu, nu1, nu2,
+                     baseIterations,
+                     baseTolerance,
+                     instrumented);
+
+            solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling"));
+            solver.setInitialIterate(xTargetSpace);
+            solver.solve();
+            xTargetSpace = solver.getSol();
+
+            for (int i = 0; i < xTargetSpace.size(); i++) {
+              x[_0][i] = xTargetSpace[i].r;
+              x[_1][i] = xTargetSpace[i].q;
+            }
+#endif
+        }
         // Output result of each homotopy step
         std::stringstream iAsAscii;
         iAsAscii << i+1;
-#ifdef MIXED_SPACE
+        if (dim == dimworld) {
         CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0],
                                                                                        orientationFEBasis,x[_1],
                                                                                        resultPath + "mixed-cosserat_homotopy_" + iAsAscii.str());
-#else
-        CosseratVTKWriter<GridType>::write<FEBasis>(feBasis,x, resultPath + "cosserat_homotopy_" + iAsAscii.str());
+        } else {
+#if MIXED_SPACE
+            for (int i = 0; i < displacement.size(); i++) {
+               for (int j = 0; j  < 3; j++) {
+                displacement[i][j] = x[_0][i][j];
+              }
+              displacement[i] -= identity[i];
+            }
+            auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3>>(deformationPowerBasis, displacement);
+    
+            //  We need to subsample, because VTK cannot natively display real second-order functions
+            SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1));
+            vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, 3));
+            vtkWriter.write(resultPath + "cosserat_homotopy_" + std::to_string(i+1));
+#else 
+        CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x, resultPath + "cosserat_homotopy_" + std::to_string(i+1));
 #endif
-
+        }
     }
 
     // //////////////////////////////
     //   Output result
     // //////////////////////////////
 
-#ifndef MIXED_SPACE
+#if !MIXED_SPACE
     // Write the corresponding coefficient vector: verbatim in binary, to be completely lossless
     // This data may be used by other applications measuring the discretization error
-    BlockVector<TargetSpace::CoordinateType> xEmbedded(x.size());
-    for (size_t i=0; i<x.size(); i++)
-      xEmbedded[i] = x[i].globalCoordinates();
+    BlockVector<TargetSpace::CoordinateType> xEmbedded(compositeBasis.size({0}));
+    for (size_t i=0; i<compositeBasis.size({0}); i++)
+        for (size_t j=0; j<TargetSpace::TangentVector::dimension; j++)
+            xEmbedded[i][j] = j < 3 ? x[_0][i][j] : x[_1][i].globalCoordinates()[j-3];
 
     std::ofstream outFile("cosserat-continuum-result-" + std::to_string(numLevels) + ".data", std::ios_base::binary);
     MatrixVector::Generic::writeBinary(outFile, xEmbedded);
     outFile.close();
 #endif
 
+    if (parameterSet.get<bool>("computeAverageNeumannDeflection", false) and parameterSet.hasKey("neumannValues")) {
     // finally: compute the average deformation of the Neumann boundary
     // That is what we need for the locking tests
     FieldVector<double,3> averageDef(0);
-#ifdef MIXED_SPACE
     for (size_t i=0; i<x[_0].size(); i++)
         if (neumannNodes[i][0])
             averageDef += x[_0][i].globalCoordinates();
-#else
-    for (size_t i=0; i<x.size(); i++)
-        if (neumannNodes[i][0])
-            averageDef += x[i].r;
-#endif
     averageDef /= neumannNodes.count();
 
     if (mpiHelper.rank()==0 and parameterSet.hasKey("neumannValues"))
@@ -571,6 +625,7 @@ int main (int argc, char *argv[]) try
       std::cout << "Neumann values = " << parameterSet.get<FieldVector<double, 3> >("neumannValues") << "  "
                 << ",  average deflection: " << averageDef << std::endl;
     }
+    }
 
 } catch (Exception& e)
 {
-- 
GitLab