From e495d7ebdf7365cc6fb6b24dd2e410dd53d5ee5b Mon Sep 17 00:00:00 2001
From: podlesny <podlesny@mi.fu-berlin.de>
Date: Fri, 13 Dec 2019 16:45:42 +0100
Subject: [PATCH] major io update

---
 dune/tectonic/assemblers.cc                   |  28 +-
 dune/tectonic/assemblers.hh                   |   7 +-
 dune/tectonic/data-structures/body/body.cc    |   7 +-
 .../data-structures/body/boundarycondition.hh |  21 +-
 .../friction/globalfriction.hh                |   3 +-
 .../friction/globalratestatefriction.hh       |  22 +-
 .../data-structures/friction/localfriction.hh |  29 +-
 .../data-structures/network/contactnetwork.cc |  10 +-
 .../data-structures/network/contactnetwork.hh |   1 +
 dune/tectonic/factories/CMakeLists.txt        |   3 +
 .../factories/stackedblocksfactory.cc         |  13 +-
 dune/tectonic/factories/strikeslipfactory.cc  | 275 ++++++++++
 dune/tectonic/factories/strikeslipfactory.hh  |  87 ++++
 .../factories/strikeslipfactory_tmpl.cc       |   8 +
 dune/tectonic/factories/threeblocksfactory.cc |   4 +-
 dune/tectonic/factories/twoblocksfactory.cc   |  22 +-
 dune/tectonic/io/CMakeLists.txt               |   2 +-
 dune/tectonic/io/hdf5-bodywriter.hh           |  65 ++-
 dune/tectonic/io/hdf5-levelwriter.hh          |  65 ---
 dune/tectonic/io/hdf5-writer.hh               | 108 ++++
 .../io/hdf5/frictionalboundary-writer.cc      |  47 +-
 .../io/hdf5/frictionalboundary-writer.hh      |  23 +-
 .../io/hdf5/frictionalboundary-writer_tmpl.cc |  18 +-
 dune/tectonic/io/hdf5/restrict.hh             |  20 +-
 dune/tectonic/problem-data/CMakeLists.txt     |   2 +
 dune/tectonic/problem-data/bc.hh              |  19 +-
 .../tectonic/problem-data/grid/CMakeLists.txt |   6 +
 .../problem-data/grid/trianglegeometry.cc     | 212 ++++++++
 .../problem-data/grid/trianglegeometry.hh     |  88 ++++
 .../grid/trianglegeometry_tmpl.cc             |   8 +
 .../problem-data/grid/trianglegrids.cc        | 204 ++++++++
 .../problem-data/grid/trianglegrids.hh        |  77 +++
 .../problem-data/grid/trianglegrids_tmpl.cc   |  24 +
 .../problem-data/segmented-function.hh        |   4 +-
 dune/tectonic/problem-data/strikeslipbody.hh  |  89 ++++
 .../contact/dualmortarcoupling.cc             |  22 +-
 .../spatial-solving/contact/nbodyassembler.cc | 101 ++--
 .../spatial-solving/fixedpointiterator.cc     |  22 +-
 .../spatial-solving/fixedpointiterator.hh     |   2 +-
 .../spatial-solving/tnnmg/linearization.hh    |   4 +-
 dune/tectonic/tests/CMakeLists.txt            |   3 +
 .../tectonic/tests/contacttest/CMakeLists.txt |  33 ++
 .../contacttest/staticcontacttest-2D.cfg      |  12 +
 .../contacttest/staticcontacttest-3D.cfg      |  15 +
 .../tests/contacttest/staticcontacttest.cc    | 313 +++++++++++
 .../tests/contacttest/staticcontacttest.cfg   |  65 +++
 dune/tectonic/tests/hdf5test/CMakeLists.txt   |  33 ++
 dune/tectonic/tests/hdf5test/hdf5test-2D.cfg  |  27 +
 dune/tectonic/tests/hdf5test/hdf5test.cc      | 202 ++++++++
 dune/tectonic/tests/hdf5test/hdf5test.cfg     | 105 ++++
 .../time-stepping/adaptivetimestepper.cc      |  95 +++-
 .../state/ageinglawstateupdater.cc            |  12 +-
 .../state/sliplawstateupdater.cc              |   7 +-
 dune/tectonic/utils/reductionfactors.hh       |   1 +
 src/CMakeLists.txt                            |   1 +
 src/foam/foam-2D.cfg                          |   4 +-
 src/foam/foam.cc                              |  40 +-
 src/foam/foam.cfg                             |  59 ++-
 .../multi-body-problem-2D.cfg                 |   2 +-
 src/multi-body-problem/multi-body-problem.cc  |  67 ++-
 src/multi-body-problem/multi-body-problem.cfg |  12 +-
 src/strikeslip/CMakeLists.txt                 |  43 ++
 src/strikeslip/strikeslip-2D.cfg              |  27 +
 src/strikeslip/strikeslip.cc                  | 485 ++++++++++++++++++
 src/strikeslip/strikeslip.cfg                 | 112 ++++
 65 files changed, 3198 insertions(+), 349 deletions(-)
 create mode 100644 dune/tectonic/factories/strikeslipfactory.cc
 create mode 100644 dune/tectonic/factories/strikeslipfactory.hh
 create mode 100644 dune/tectonic/factories/strikeslipfactory_tmpl.cc
 delete mode 100644 dune/tectonic/io/hdf5-levelwriter.hh
 create mode 100644 dune/tectonic/io/hdf5-writer.hh
 create mode 100644 dune/tectonic/problem-data/grid/trianglegeometry.cc
 create mode 100644 dune/tectonic/problem-data/grid/trianglegeometry.hh
 create mode 100644 dune/tectonic/problem-data/grid/trianglegeometry_tmpl.cc
 create mode 100644 dune/tectonic/problem-data/grid/trianglegrids.cc
 create mode 100644 dune/tectonic/problem-data/grid/trianglegrids.hh
 create mode 100644 dune/tectonic/problem-data/grid/trianglegrids_tmpl.cc
 create mode 100644 dune/tectonic/problem-data/strikeslipbody.hh
 create mode 100644 dune/tectonic/tests/contacttest/CMakeLists.txt
 create mode 100644 dune/tectonic/tests/contacttest/staticcontacttest-2D.cfg
 create mode 100644 dune/tectonic/tests/contacttest/staticcontacttest-3D.cfg
 create mode 100644 dune/tectonic/tests/contacttest/staticcontacttest.cc
 create mode 100644 dune/tectonic/tests/contacttest/staticcontacttest.cfg
 create mode 100644 dune/tectonic/tests/hdf5test/CMakeLists.txt
 create mode 100644 dune/tectonic/tests/hdf5test/hdf5test-2D.cfg
 create mode 100644 dune/tectonic/tests/hdf5test/hdf5test.cc
 create mode 100644 dune/tectonic/tests/hdf5test/hdf5test.cfg
 create mode 100644 dune/tectonic/utils/reductionfactors.hh
 create mode 100644 src/strikeslip/CMakeLists.txt
 create mode 100644 src/strikeslip/strikeslip-2D.cfg
 create mode 100644 src/strikeslip/strikeslip.cc
 create mode 100644 src/strikeslip/strikeslip.cfg

diff --git a/dune/tectonic/assemblers.cc b/dune/tectonic/assemblers.cc
index 609436ca..8cfc399e 100644
--- a/dune/tectonic/assemblers.cc
+++ b/dune/tectonic/assemblers.cc
@@ -107,13 +107,33 @@ void MyAssembler<GridView, dimension>::assembleBodyForce(
 
 template <class GridView, int dimension>
 void MyAssembler<GridView, dimension>::assembleNeumann(
-        BoundaryPatch<GridView> const &neumannBoundary,
-        Vector &f,
-        Dune::VirtualFunction<double, double> const &neumann,
+        const BoundaryPatch<GridView>& neumannBoundary,
+        const Dune::BitSetVector<dimension>& neumannNodes,
+        Vector& f,
+        const Dune::VirtualFunction<double, double>& neumann,
         double relativeTime) const {
 
+  typename LocalVector::field_type val = 0;
+  neumann.evaluate(relativeTime, val);
+
+  size_t idx = 0;
+  bool neumannIdx = false;
+  for (; idx<neumannNodes.size() && !neumannIdx; idx++) {
+    for (size_t d=0; d<dimension; d++) {
+        if (neumannNodes[idx][d]) {
+            neumannIdx = true;
+            break;
+        }
+    }
+  }
+  idx--;
+
   LocalVector localNeumann(0);
-  neumann.evaluate(relativeTime, localNeumann[0]);
+  for (size_t i=0; i<localNeumann.size(); i++) {
+      if (neumannNodes[idx][i])
+          localNeumann[i] = val;
+  }
+
   NeumannBoundaryAssembler<Grid, LocalVector> neumannBoundaryAssembler(
       std::make_shared<ConstantFunction<LocalVector, LocalVector>>(
           localNeumann));
diff --git a/dune/tectonic/assemblers.hh b/dune/tectonic/assemblers.hh
index df0b3cd6..9ec78078 100644
--- a/dune/tectonic/assemblers.hh
+++ b/dune/tectonic/assemblers.hh
@@ -79,9 +79,10 @@ class MyAssembler {
             Vector &f) const;
 
     void assembleNeumann(
-            BoundaryPatch<GridView> const &neumannBoundary,
-            Vector &f,
-            Dune::VirtualFunction<double, double> const &neumann,
+            const BoundaryPatch<GridView>& neumannBoundary,
+            const Dune::BitSetVector<dimension>& neumannNodes,
+            Vector& f,
+            const Dune::VirtualFunction<double, double>& neumann,
             double relativeTime) const;
 
     void assembleWeightedNormalStress(
diff --git a/dune/tectonic/data-structures/body/body.cc b/dune/tectonic/data-structures/body/body.cc
index 75d92f7d..cd63c1d5 100644
--- a/dune/tectonic/data-structures/body/body.cc
+++ b/dune/tectonic/data-structures/body/body.cc
@@ -34,8 +34,11 @@ LeafBody<GridTEMPLATE, VectorType>::LeafBody(
             const auto& leafNeumannCondition = leafNeumannConditions[i];
 
             VectorType b;
-            assembler_->assembleNeumann(*leafNeumannCondition->boundaryPatch(), b, *leafNeumannCondition->boundaryFunction(),
-                                    relativeTime);
+            assembler_->assembleNeumann(*leafNeumannCondition->boundaryPatch(),
+                                        *leafNeumannCondition->boundaryNodes(),
+                                        b,
+                                        *leafNeumannCondition->boundaryFunction(),
+                                        relativeTime);
             ell += b;
         }
     };
diff --git a/dune/tectonic/data-structures/body/boundarycondition.hh b/dune/tectonic/data-structures/body/boundarycondition.hh
index 088ebc97..974c5ecb 100644
--- a/dune/tectonic/data-structures/body/boundarycondition.hh
+++ b/dune/tectonic/data-structures/body/boundarycondition.hh
@@ -6,6 +6,7 @@
 
 #include <dune/fufem/boundarypatch.hh>
 
+#include <dune/tectonic/utils/tobool.hh>
 
 template <class GridView, int dims>
 class BoundaryCondition {
@@ -28,9 +29,10 @@ class BoundaryCondition {
 
   BoundaryCondition(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function, const std::string& tag = "") :
       tag_(tag),
-      boundaryPatch_(patch),
       boundaryFunction_(function)
-  {}
+  {
+      setBoundaryPatch(patch);
+  }
 
   void setBoundaryPatch(const GridView& gridView, std::shared_ptr<Dune::BitSetVector<dims>> nodes) {
       boundaryNodes_ = nodes;
@@ -39,10 +41,21 @@ class BoundaryCondition {
 
   void setBoundaryPatch(std::shared_ptr<BoundaryPatch> patch) {
       boundaryPatch_ = patch;
+
+      auto nodes = patch->getVertices();
+      boundaryNodes_ = std::make_shared<Dune::BitSetVector<dims>>(nodes->size(), false);
+      for (size_t i=0; i<nodes->size(); i++) {
+          if (toBool((*nodes)[i])) {
+             for (size_t d=0; d<dims; d++) {
+                 (*boundaryNodes_)[i][d] = true;
+             }
+          }
+      }
   }
 
   void setBoundaryPatch(const BoundaryPatch& patch) {
-      boundaryPatch_ = std::make_shared<BoundaryPatch>(patch);
+      auto patch_ptr = std::make_shared<BoundaryPatch>(patch);
+      setBoundaryPatch(patch_ptr);
   }
 
   void setBoundaryFunction(std::shared_ptr<Function> function) {
@@ -50,7 +63,7 @@ class BoundaryCondition {
   }
 
   void set(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function) {
-      boundaryPatch_ = patch;
+      setBoundaryPatch(patch);
       boundaryFunction_ = function;
   }
 
diff --git a/dune/tectonic/data-structures/friction/globalfriction.hh b/dune/tectonic/data-structures/friction/globalfriction.hh
index f3dbabc7..e8796c30 100644
--- a/dune/tectonic/data-structures/friction/globalfriction.hh
+++ b/dune/tectonic/data-structures/friction/globalfriction.hh
@@ -39,8 +39,9 @@ class GlobalFriction {
   LocalNonlinearity const virtual &restriction(size_t i) const = 0;
 
   void addHessian(Vector const &v, Matrix &hessian) const {
-    for (size_t i = 0; i < v.size(); ++i)
+    for (size_t i = 0; i < v.size(); ++i) {
       restriction(i).addHessian(v[i], hessian[i][i]);
+    }
   }
 
   void directionalDomain(Vector const &, Vector const &,
diff --git a/dune/tectonic/data-structures/friction/globalratestatefriction.hh b/dune/tectonic/data-structures/friction/globalratestatefriction.hh
index ec42936b..4663d516 100644
--- a/dune/tectonic/data-structures/friction/globalratestatefriction.hh
+++ b/dune/tectonic/data-structures/friction/globalratestatefriction.hh
@@ -38,8 +38,8 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
   size_t bodyIndex(const size_t globalIdx) {
      size_t i=0;
 
-     for (; i<maxIndex_.size(); i++) {
-         if (globalIdx < maxIndex_[i])
+     for (; i<offSet_.size()-1; i++) {
+         if (globalIdx < offSet_[i])
              break;
      }
      return i;
@@ -56,6 +56,10 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
     assert(weights.size() == weightedNormalStress.size());
 
     const auto nBodies = weights.size();
+    offSet_.resize(nBodies, 0);
+    for (size_t i=1; i<nBodies; i++) {
+        offSet_[i] = weights[i-1].size();
+    }
 
     std::vector<std::vector<int>> nonmortarBodies(nBodies); // first index body, second index coupling
     for (size_t i=0; i<contactCouplings.size(); i++) {
@@ -63,9 +67,6 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
         nonmortarBodies[nonmortarIdx].emplace_back(i);
     }
 
-    maxIndex_.resize(nBodies);
-    size_t globalIdx = 0;
-
     for (size_t bodyIdx=0; bodyIdx<nBodies; bodyIdx++) {
         const auto& couplingIndices = nonmortarBodies[bodyIdx];
 
@@ -86,26 +87,23 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
                 if (not contactCouplings[couplingIdx]->nonmortarBoundary().containsVertex(i))
                   continue;
 
-                localToGlobal_.emplace_back(i);
+                localToGlobal_.emplace_back(offSet_[bodyIdx] + i);
                 restrictions_.emplace_back(weights[bodyIdx][i], weightedNormalStress[bodyIdx][i],
                                           couplings[j]->frictionData()(geoToPoint(it->geometry())));
                 break;
             }
-
-            globalIdx++;
         }
-        maxIndex_[bodyIdx] = globalIdx;
     }
   }
 
   void updateAlpha(const std::vector<ScalarVector>& alpha) override {
     for (size_t j = 0; j < restrictions_.size(); ++j) {
-      const auto bodyIdx = bodyIndex(j);
       const auto globalDof = localToGlobal_[j];
+      const auto bodyIdx = bodyIndex(globalDof);
 
       size_t bodyDof;
       if (bodyIdx>0) {
-          bodyDof = globalDof - maxIndex_[bodyIdx-1];
+          bodyDof = globalDof - offSet_[bodyIdx-1];
       } else {
           bodyDof = globalDof;
       }
@@ -125,7 +123,7 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
 
 private:
   std::vector<WrappedScalarFriction<block_size, ScalarFriction>> restrictions_;
-  std::vector<size_t> maxIndex_; // max global index per body
+  std::vector<size_t> offSet_; // index off-set by body id
   std::vector<size_t> localToGlobal_;
   WrappedScalarFriction<block_size, ZeroFunction> const zeroFriction_;
 };
diff --git a/dune/tectonic/data-structures/friction/localfriction.hh b/dune/tectonic/data-structures/friction/localfriction.hh
index 67b7f282..461dcea9 100644
--- a/dune/tectonic/data-structures/friction/localfriction.hh
+++ b/dune/tectonic/data-structures/friction/localfriction.hh
@@ -94,7 +94,7 @@ class WrappedScalarFriction : public LocalFriction<dimension> {
     //std::cout << A << std::endl;
     //std::cout << x << std::endl;
 
-    double const xnorm2 = x.two_norm2();
+ /*   double const xnorm2 = x.two_norm2();
     double const xnorm = std::sqrt(xnorm2);
     if (xnorm2 <= 0.0)
       return;
@@ -121,6 +121,33 @@ class WrappedScalarFriction : public LocalFriction<dimension> {
     for (size_t k = 0; k < dimension; ++k) {
       double const entry = tensorweight * x[k] * x[k];
       A[k][k] += entry + idweight;
+    }*/
+
+    const double xnorm = x.two_norm();
+    if (xnorm <= 0.0)
+      return;
+
+    VectorType y = x;
+    y /= xnorm;
+
+    double const H1 = func_.differential(xnorm);
+    double const H2 = func_.second_deriv(xnorm);
+
+    double const tensorweight = (H2 - H1 / xnorm);
+    double const idweight = H1 / xnorm;
+
+    //std::cout << tensorweight << " " << idweight << std::endl;
+
+    for (size_t i = 0; i < dimension; ++i)
+      for (size_t j = 0; j < i; ++j) {
+        double const entry = tensorweight * y[i] * y[j];
+        A[i][j] += entry;
+        A[j][i] += entry;
+      }
+
+    for (size_t k = 0; k < dimension; ++k) {
+      double const entry = tensorweight * y[k] * y[k];
+      A[k][k] += entry + idweight;
     }
 
     //std::cout << A << std::endl;
diff --git a/dune/tectonic/data-structures/network/contactnetwork.cc b/dune/tectonic/data-structures/network/contactnetwork.cc
index d9779a74..7e03c549 100644
--- a/dune/tectonic/data-structures/network/contactnetwork.cc
+++ b/dune/tectonic/data-structures/network/contactnetwork.cc
@@ -352,7 +352,7 @@ void ContactNetwork<HostGridType, VectorType>::totalNodes(
                 const auto& nodes = boundaryConditions[bc]->boundaryNodes();
                 for (size_t j=0; j<nodes->size(); j++, idx++)
                     for (int k=0; k<dim; k++)
-                        totalNodes[idx][k] = (*nodes)[j][k];
+                        totalNodes[idx][k] = totalNodes[idx][k] or (*nodes)[j][k];
                 idx = (bc==boundaryConditions.size()-1 ? idx : idxBackup);
             }
         } else {
@@ -421,4 +421,12 @@ void ContactNetwork<HostGridType, VectorType>::frictionNodes(std::vector<const D
     }
 }
 
+template <class HostGridType, class VectorType>
+void ContactNetwork<HostGridType, VectorType>::frictionPatches(std::vector<const BoundaryPatch*>& patches) const {
+    patches.resize(nBodies());
+    for (size_t i=0; i<nBodies(); i++) {
+        patches[i] = frictionBoundaries_[i].get();
+    }
+}
+
 #include "contactnetwork_tmpl.cc"
diff --git a/dune/tectonic/data-structures/network/contactnetwork.hh b/dune/tectonic/data-structures/network/contactnetwork.hh
index 87b01365..6448fc5f 100644
--- a/dune/tectonic/data-structures/network/contactnetwork.hh
+++ b/dune/tectonic/data-structures/network/contactnetwork.hh
@@ -142,5 +142,6 @@ class ContactNetwork {
             const std::string& tag,
             BoundaryFunctions& functions) const;
     void frictionNodes(std::vector<const Dune::BitSetVector<1>*>& nodes) const;
+    void frictionPatches(std::vector<const BoundaryPatch*>& patches) const;
 };
 #endif
diff --git a/dune/tectonic/factories/CMakeLists.txt b/dune/tectonic/factories/CMakeLists.txt
index b069d44e..0a757b9d 100644
--- a/dune/tectonic/factories/CMakeLists.txt
+++ b/dune/tectonic/factories/CMakeLists.txt
@@ -5,6 +5,8 @@ add_custom_target(tectonic_dune_factories SOURCES
   levelcontactnetworkfactory.hh
   stackedblocksfactory.hh
   stackedblocksfactory.cc 
+  strikeslipfactory.hh
+  strikeslipfactory.cc
   threeblocksfactory.hh
   threeblocksfactory.cc 
   twoblocksfactory.hh
@@ -17,6 +19,7 @@ install(FILES
   contactnetworkfactory.hh
   levelcontactnetworkfactory.hh
   stackedblocksfactory.hh
+  strikeslipfactory.hh
   threeblocksfactory.hh
   twoblocksfactory.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/tectonic)
diff --git a/dune/tectonic/factories/stackedblocksfactory.cc b/dune/tectonic/factories/stackedblocksfactory.cc
index 6e6f5428..b4665f04 100644
--- a/dune/tectonic/factories/stackedblocksfactory.cc
+++ b/dune/tectonic/factories/stackedblocksfactory.cc
@@ -81,9 +81,8 @@ void StackedBlocksFactory<HostGridType, VectorType>::setBodies() {
 
             cuboidGeometries_[i] = std::make_shared<CuboidGeometry>(origins[i], length, height_i);
 
-
-            cuboidGeometries_[i]->addWeakeningPatch(subParset, upperWeakOrigin_i, weakBound(upperWeakOrigin_i));
             cuboidGeometries_[i]->addWeakeningPatch(subParset, lowerWeakOrigin_i, weakBound(lowerWeakOrigin_i));
+            cuboidGeometries_[i]->addWeakeningPatch(subParset, upperWeakOrigin_i, weakBound(upperWeakOrigin_i));  
         }
 
         const size_t idx = this->bodyCount_-1;
@@ -145,11 +144,11 @@ void StackedBlocksFactory<HostGridType, VectorType>::setCouplings() {
       auto& coupling = this->couplings_[i];
       coupling = std::make_shared<typename Base::FrictionCouplingPair>();
 
-      nonmortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i]->upper);
-      mortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i+1]->lower);
-      weakPatches_[i] = std::make_shared<WeakeningRegion>(cuboidGeometries_[i]->weakeningRegions()[0]);
+      nonmortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i+1]->lower);
+      mortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i]->upper);
+      weakPatches_[i] = std::make_shared<WeakeningRegion>(cuboidGeometries_[i+1]->weakeningRegions()[0]);
 
-      coupling->set(i, i+1, nonmortarPatch_[i], mortarPatch_[i], 0.1, Base::FrictionCouplingPair::CouplingType::STICK_SLIP, contactProjection, backend);
+      coupling->set(i+1, i, nonmortarPatch_[i], mortarPatch_[i], 0.1, Base::FrictionCouplingPair::CouplingType::STICK_SLIP, contactProjection, backend);
       coupling->setWeakeningPatch(weakPatches_[i]);
       coupling->setFrictionData(std::make_shared<MyGlobalFrictionData<GlobalCoords>>(this->parset_.sub("boundary.friction"), *weakPatches_[i], CuboidGeometry::lengthScale()));
     }
@@ -183,7 +182,7 @@ void StackedBlocksFactory<HostGridType, VectorType>::setBoundaryConditions() {
 
     using Function = Dune::VirtualFunction<double, double>;
     std::shared_ptr<Function> neumannFunction = std::make_shared<NeumannCondition>();
-    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>();
+    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>(this->parset_.template get<double>("boundary.dirichlet.finalVelocity"));
 
     const double lengthScale = CuboidGeometry::lengthScale();
 
diff --git a/dune/tectonic/factories/strikeslipfactory.cc b/dune/tectonic/factories/strikeslipfactory.cc
new file mode 100644
index 00000000..130e6dd2
--- /dev/null
+++ b/dune/tectonic/factories/strikeslipfactory.cc
@@ -0,0 +1,275 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+#include <dune/contact/projections/normalprojection.hh>
+
+#include "../problem-data/bc.hh"
+#include "../problem-data/myglobalfrictiondata.hh"
+
+#include "../utils/diameter.hh"
+
+#include "strikeslipfactory.hh"
+
+template <class HostGridType, class VectorTEMPLATE>
+void StrikeSlipFactory<HostGridType, VectorTEMPLATE>::setBodies() {
+    // set up cuboid geometries
+
+    std::array<double, 2> lengths = {this->parset_.template get<double>("body0.length"), this->parset_.template get<double>("body1.length")};
+    std::array<double, 2> heights = {this->parset_.template get<double>("body0.height"), this->parset_.template get<double>("body1.height")};
+
+    GlobalCoords origin(0);
+
+    const auto& frictionParset = this->parset_.sub("boundary.friction");
+
+#if MY_DIM == 3 // TODO: not implemented
+        std::array<double, 2> depths = {this->parset_.template get<double>("body0.depth"), this->parset_.template get<double>("body1.depth")};
+
+        cuboidGeometries_[0] = std::make_shared<TriangleGeometry>(origins[0], lengths[0], heights[0], depths[0]);
+        cuboidGeometries_[0]->addWeakeningPatch(frictionParset, {origins[0][0], origins[0][1]+ heights[0], 0}, {origins[0][0] + lengths[0], origins[0][1]+ heights[0], 0});
+
+        cuboidGeometries_[1] = std::make_shared<TriangleGeometry>(origins[1], lengths[1], heights[1], depths[1]);
+        cuboidGeometries_[1]->addWeakeningPatch(frictionParset, origins[1], {origins[1][0] + lengths[1], origins[1][1], 0});
+#elif MY_DIM == 2
+        triangleGeometries_[0] = std::make_shared<TriangleGeometry>(origin, lengths[0], heights[0]);
+        triangleGeometries_[0]->addWeakeningPatch(frictionParset, triangleGeometries_[0]->A(), triangleGeometries_[0]->C());
+
+        triangleGeometries_[1] = std::make_shared<TriangleGeometry>(triangleGeometries_[0]->C(), -lengths[1], -heights[1]);
+        triangleGeometries_[1]->addWeakeningPatch(frictionParset, triangleGeometries_[1]->A(), triangleGeometries_[1]->C());
+#else
+#error CuboidGeometry only supports 2D and 3D!"
+#endif
+
+    // set up reference grids
+    gridConstructor_ = std::make_unique<GridsConstructor<HostGridType>>(triangleGeometries_);
+    auto& grids = gridConstructor_->getGrids();
+
+    std::array<double, 2> smallestDiameter = {this->parset_.template get<double>("body0.smallestDiameter"), this->parset_.template get<double>("body1.smallestDiameter")};
+
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        const auto& triangleGeometry = *triangleGeometries_[i];
+
+        // define weak patch and refine grid
+        const auto& weakeningRegions = triangleGeometry.weakeningRegions();
+        for (size_t j=0; j<weakeningRegions.size(); j++) {
+            refine(*grids[i], weakeningRegions[j], smallestDiameter[i], TriangleGeometry::lengthScale());
+        }
+
+        // determine minDiameter and maxDiameter
+        double minDiameter = std::numeric_limits<double>::infinity();
+        double maxDiameter = 0.0;
+        for (auto &&e : elements(grids[i]->leafGridView())) {
+          auto const geometry = e.geometry();
+          auto const diam = diameter(geometry);
+          minDiameter = std::min(minDiameter, diam);
+          maxDiameter = std::max(maxDiameter, diam);
+        }
+        std::cout << "Grid" << i << " min diameter: " << minDiameter << std::endl;
+        std::cout << "Grid" << i << " max diameter: " << maxDiameter << std::endl;
+    }
+
+#if MY_DIM == 2
+    bodyData_[0] = std::make_shared<MyBodyData<dim>>(this->parset_.sub("body0"), 0.0, zenith_());
+    this->bodies_[0] = std::make_shared<typename Base::LeafBody>(bodyData_[0], grids[0]);
+
+    bodyData_[1] = std::make_shared<MyBodyData<dim>>(this->parset_.sub("body1"), 0.0, zenith_());
+    this->bodies_[1] = std::make_shared<typename Base::LeafBody>(bodyData_[1], grids[1]);
+#else
+    bodyData_[0] = std::make_shared<MyBodyData<dim>>(this->parset_.sub("body0"), this->parset_.template get<double>("gravity"), zenith_());
+    this->bodies_[0] = std::make_shared<typename Base::LeafBody>(bodyData_[0], grids[0]);
+
+    bodyData_[1] = std::make_shared<MyBodyData<dim>>(this->parset_.sub("body1"), this->parset_.template get<double>("gravity"), zenith_());
+    this->bodies_[1] = std::make_shared<typename Base::LeafBody>(bodyData_[1], grids[1]);
+#endif
+}
+
+template <class HostGridType, class VectorTEMPLATE>
+void StrikeSlipFactory<HostGridType, VectorTEMPLATE>::setLevelBodies() {
+    const size_t maxLevel = std::max({this->bodies_[0]->grid()->maxLevel(), this->bodies_[1]->grid()->maxLevel()});
+
+    for (size_t l=0; l<=maxLevel; l++) {
+        std::vector<size_t> bodyLevels(2, l);
+        this->contactNetwork_.addLevel(bodyLevels, l);
+    }
+}
+
+template <class HostGridType, class VectorTEMPLATE>
+void StrikeSlipFactory<HostGridType, VectorTEMPLATE>::setCouplings() {
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        const auto& triangleGeometry = *triangleGeometries_[i];
+        leafFaces_[i] = std::make_shared<LeafFaces>(this->bodies_[i]->gridView(), triangleGeometry);
+        levelFaces_[i] = std::make_shared<LevelFaces>(this->bodies_[i]->grid()->levelGridView(0), triangleGeometry);
+    }
+
+    auto contactProjection = std::make_shared<Dune::Contact::NormalProjection<LeafBoundaryPatch>>();
+
+    nonmortarPatch_[0] = std::make_shared<LevelBoundaryPatch>(levelFaces_[1]->b);
+    mortarPatch_[0] = std::make_shared<LevelBoundaryPatch>(levelFaces_[0]->b);
+    weakPatches_[0] = std::make_shared<WeakeningRegion>(triangleGeometries_[1]->weakeningRegions()[0]);
+
+    auto& coupling = this->couplings_[0];
+    coupling = std::make_shared<typename Base::FrictionCouplingPair>();
+
+    coupling->set(1, 0, nonmortarPatch_[0], mortarPatch_[0], 0.1, Base::FrictionCouplingPair::CouplingType::STICK_SLIP, contactProjection, nullptr);
+    coupling->setWeakeningPatch(weakPatches_[0]);
+    coupling->setFrictionData(std::make_shared<MyGlobalFrictionData<GlobalCoords>>(this->parset_.sub("boundary.friction"), *weakPatches_[0], TriangleGeometry::lengthScale()));
+}
+
+template <class HostGridType, class VectorTEMPLATE>
+void StrikeSlipFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
+    using LeafBoundaryCondition = BoundaryCondition<LeafGridView, dim>;
+
+    using Function = Dune::VirtualFunction<double, double>;
+    std::shared_ptr<Function> neumannFunction = std::make_shared<NeumannCondition>();
+    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>(-1.0*this->parset_.template get<double>("boundary.dirichlet.finalVelocity"));
+
+    const double lengthScale = TriangleGeometry::lengthScale();
+
+    // body0
+    const auto& body0 = this->contactNetwork_.body(0);
+    const auto& leafVertexCount0 = body0->nVertices();
+    std::shared_ptr<Dune::BitSetVector<dim>> zeroDirichletNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount0);
+    std::shared_ptr<Dune::BitSetVector<dim>> velocityDirichletNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount0);
+
+    const auto& gridView0 = body0->gridView();
+    const auto& indexSet0 = gridView0.indexSet();
+
+    for (const auto& e : elements(gridView0)) {
+        for (const auto& is : intersections(gridView0, e)) {
+            if (!is.boundary())
+                continue;
+
+            auto isCenter = is.geometry().center();
+
+            // Dirichlet boundary (c)
+            if (leafFaces_[0]->c.contains(is)) {
+                isCenter -=  triangleGeometries_[0]->A();
+
+                if (isCenter.two_norm()/std::abs(triangleGeometries_[0]->length()) > 0.1) {
+                    const auto inside = is.inside();
+
+                    auto refElement = Dune::ReferenceElements<double, dim>::general(inside.type());
+                    int n = refElement.size(is.indexInInside(),1,dim);
+
+                    for (int i=0; i<n; i++) {
+                        int faceIdxi = refElement.subEntity(is.indexInInside(), 1, i, dim);
+                        int globalIdx = indexSet0.template subIndex(inside,faceIdxi,dim);
+                        (*zeroDirichletNodes)[globalIdx][1] = true;
+                    }
+                }
+            }
+
+            // velocity Dirichlet boundary (a)
+            if (leafFaces_[0]->a.contains(is)) {
+                isCenter -=  triangleGeometries_[0]->C();
+
+                if (isCenter.two_norm()/std::abs(triangleGeometries_[0]->height()) > 0.1) {
+                    const auto inside = is.inside();
+
+                    auto refElement = Dune::ReferenceElements<double, dim>::general(inside.type());
+                    int n = refElement.size(is.indexInInside(),1,dim);
+
+                    for (int i=0; i<n; i++) {
+                        int faceIdxi = refElement.subEntity(is.indexInInside(), 1, i, dim);
+                        int globalIdx = indexSet0.template subIndex(inside,faceIdxi,dim);
+                        (*velocityDirichletNodes)[globalIdx][0] = true;
+                    }
+                }
+            }
+        }
+    }
+
+    std::shared_ptr<Function> zeroFunction = std::make_shared<NeumannCondition>();
+
+    std::shared_ptr<LeafBoundaryCondition> zeroDirichletBoundary = std::make_shared<LeafBoundaryCondition>("dirichlet");
+    zeroDirichletBoundary->setBoundaryPatch(body0->gridView(), zeroDirichletNodes);
+    zeroDirichletBoundary->setBoundaryFunction(zeroFunction);
+    body0->addBoundaryCondition(zeroDirichletBoundary);
+
+    std::shared_ptr<LeafBoundaryCondition> velocityDirichletBoundary = std::make_shared<LeafBoundaryCondition>("dirichlet");
+    velocityDirichletBoundary->setBoundaryPatch(body0->gridView(), velocityDirichletNodes);
+    velocityDirichletBoundary->setBoundaryFunction(velocityDirichletFunction);
+    body0->addBoundaryCondition(velocityDirichletBoundary);
+
+    // body1
+    const auto& body1 = this->contactNetwork_.body(1);
+    const auto& leafVertexCount1 = body1->nVertices();
+    std::shared_ptr<Dune::BitSetVector<dim>> zeroDirichletNodes1 = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1);
+    std::shared_ptr<Dune::BitSetVector<dim>> loadNeumannNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1);
+
+    const auto& gridView1 = body1->gridView();
+    const auto& indexSet1 = gridView1.indexSet();
+
+    for (const auto& e : elements(gridView1)) {
+        for (const auto& is : intersections(gridView1, e)) {
+            if (!is.boundary())
+                continue;
+
+            auto isCenter = is.geometry().center();
+
+            // Neumann boundary, normal load (c)
+            if (leafFaces_[1]->c.contains(is)) {
+                isCenter -=  triangleGeometries_[1]->A();
+
+                if (isCenter.two_norm()/std::abs(triangleGeometries_[1]->length()) > 0.1) {
+                    const auto inside = is.inside();
+
+                    auto refElement = Dune::ReferenceElements<double, dim>::general(inside.type());
+                    int n = refElement.size(is.indexInInside(),1,dim);
+
+                    for (int i=0; i<n; i++) {
+                        int faceIdxi = refElement.subEntity(is.indexInInside(), 1, i, dim);
+                        int globalIdx = indexSet1.template subIndex(inside,faceIdxi,dim);
+                        (*loadNeumannNodes)[globalIdx][1] = true;
+                    }
+                }
+            }
+
+            // Dirichlet boundary (a)
+            if (leafFaces_[1]->a.contains(is)) {
+                isCenter -=  triangleGeometries_[1]->C();
+
+                if (isCenter.two_norm()/std::abs(triangleGeometries_[1]->height()) > 0.1) {
+                    const auto inside = is.inside();
+
+                    auto refElement = Dune::ReferenceElements<double, dim>::general(inside.type());
+                    int n = refElement.size(is.indexInInside(),1,dim);
+
+                    for (int i=0; i<n; i++) {
+                        int faceIdxi = refElement.subEntity(is.indexInInside(), 1, i, dim);
+                        int globalIdx = indexSet1.template subIndex(inside,faceIdxi,dim);
+                        (*zeroDirichletNodes1)[globalIdx][0] = true;
+                    }
+                }
+            }
+        }
+    }
+
+    std::shared_ptr<Function> constantFunction = std::make_shared<NeumannCondition>(this->parset_.template get<double>("boundary.neumann.sigmaN"));
+
+    auto loadNeumannBoundary = std::make_shared<LeafBoundaryCondition>("neumann");
+    loadNeumannBoundary->setBoundaryPatch(body1->gridView(), loadNeumannNodes);
+    loadNeumannBoundary->setBoundaryFunction(constantFunction);
+    body1->addBoundaryCondition(loadNeumannBoundary);
+
+    std::shared_ptr<LeafBoundaryCondition> zeroDirichletBoundary1 = std::make_shared<LeafBoundaryCondition>("dirichlet");
+    zeroDirichletBoundary1->setBoundaryPatch(body1->gridView(), zeroDirichletNodes1);
+    zeroDirichletBoundary1->setBoundaryFunction(zeroFunction);
+    body1->addBoundaryCondition(zeroDirichletBoundary1);
+
+    // body0, body1: natural boundary conditions
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        const auto& body = this->contactNetwork_.body(i);
+
+        // Neumann boundary
+        auto neumannBoundary = std::make_shared<LeafBoundaryCondition>("neumann");
+        auto neumannPatch = std::make_shared<LeafBoundaryPatch>(body->gridView(), true);
+        neumannBoundary->setBoundaryPatch(neumannPatch);
+        neumannBoundary->setBoundaryFunction(neumannFunction);
+        body->addBoundaryCondition(neumannBoundary);
+    }
+}
+
+#include "strikeslipfactory_tmpl.cc"
diff --git a/dune/tectonic/factories/strikeslipfactory.hh b/dune/tectonic/factories/strikeslipfactory.hh
new file mode 100644
index 00000000..0c3d551e
--- /dev/null
+++ b/dune/tectonic/factories/strikeslipfactory.hh
@@ -0,0 +1,87 @@
+#ifndef DUNE_TECTONIC_FACTORIES_STRIKESLIPFACTORY_HH
+#define DUNE_TECTONIC_FACTORIES_STRIKESLIPFACTORY_HH
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+
+#include "contactnetworkfactory.hh"
+
+#include "../problem-data/strikeslipbody.hh"
+#include "../problem-data/grid/trianglegrids.hh"
+#include "../problem-data/grid/trianglegeometry.hh"
+
+template <class HostGridType, class VectorType> class StrikeSlipFactory : public ContactNetworkFactory<HostGridType, VectorType>{
+private:
+    using Base = ContactNetworkFactory<HostGridType, VectorType>;
+
+public:
+    using ContactNetwork = typename Base::ContactNetwork;
+
+private:
+    using GlobalCoords = typename ContactNetwork::LocalVector;
+
+    using LeafGridView = typename ContactNetwork::GridView;
+    using LevelGridView = typename ContactNetwork::GridType::LevelGridView;
+
+    using LevelBoundaryPatch = BoundaryPatch<LevelGridView>;
+    using LeafBoundaryPatch = BoundaryPatch<LeafGridView>;
+
+    using LeafFaces = MyFaces<LeafGridView>;
+    using LevelFaces = MyFaces<LevelGridView>;
+
+    using TriangleGeometry= TriangleGeometry<typename GlobalCoords::field_type>;
+    using WeakeningRegion = typename TriangleGeometry::WeakeningRegion;
+
+    static const int dim = ContactNetwork::dim;
+
+    std::vector<std::shared_ptr<MyBodyData<dim>>> bodyData_;     // material properties of bodies
+
+    std::unique_ptr<GridsConstructor<HostGridType>> gridConstructor_;
+
+    std::vector<std::shared_ptr<TriangleGeometry>> triangleGeometries_;
+
+    std::vector<std::shared_ptr<LeafFaces>> leafFaces_;
+    std::vector<std::shared_ptr<LevelFaces>> levelFaces_;
+
+    std::vector<std::shared_ptr<WeakeningRegion>> weakPatches_;
+
+    std::vector<std::shared_ptr<LevelBoundaryPatch>> nonmortarPatch_;
+    std::vector<std::shared_ptr<LevelBoundaryPatch>> mortarPatch_;
+
+public:
+    StrikeSlipFactory(const Dune::ParameterTree& parset) :
+        Base(parset, 2, 1),
+        bodyData_(2),
+        triangleGeometries_(2),
+        leafFaces_(2),
+        levelFaces_(2),
+        weakPatches_(2),
+        nonmortarPatch_(1),
+        mortarPatch_(1)
+    {}
+
+    void setBodies();
+    void setLevelBodies();
+    void setCouplings();
+    void setLevelCouplings() {}
+    void setBoundaryConditions();
+
+    auto& weakPatches() {
+        return weakPatches_;
+    }
+
+private:
+    static constexpr Dune::FieldVector<double, MY_DIM> zenith_() {
+        #if MY_DIM == 2
+            return {0, 0};
+        #else
+            return {0, 0, 1};
+        #endif
+    }
+};
+#endif
+
+
diff --git a/dune/tectonic/factories/strikeslipfactory_tmpl.cc b/dune/tectonic/factories/strikeslipfactory_tmpl.cc
new file mode 100644
index 00000000..ee3446fb
--- /dev/null
+++ b/dune/tectonic/factories/strikeslipfactory_tmpl.cc
@@ -0,0 +1,8 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "../explicitgrid.hh"
+#include "../explicitvectors.hh"
+
+template class StrikeSlipFactory<Grid, Vector>;
diff --git a/dune/tectonic/factories/threeblocksfactory.cc b/dune/tectonic/factories/threeblocksfactory.cc
index 6c395565..2f6fd5cf 100644
--- a/dune/tectonic/factories/threeblocksfactory.cc
+++ b/dune/tectonic/factories/threeblocksfactory.cc
@@ -17,7 +17,7 @@ template <class HostGridType, class VectorTEMPLATE>
 void ThreeBlocksFactory<HostGridType, VectorTEMPLATE>::setBodies() {
     // set up cuboid geometries
 
-    std::array<double, 3> lengths = {3.0, 1.0, 1.0};
+    std::array<double, 3> lengths = {2.0, 1.0, 1.0};
     std::array<double, 3> heights = {1.0, 1.0, 1.0};
 
     std::array<GlobalCoords, 3> origins;
@@ -147,7 +147,7 @@ void ThreeBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
 
     using Function = Dune::VirtualFunction<double, double>;
     std::shared_ptr<Function> neumannFunction = std::make_shared<NeumannCondition>();
-    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>();
+    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>(this->parset_.template get<double>("boundary.dirichlet.finalVelocity"));
 
     const double lengthScale = CuboidGeometry::lengthScale();
 
diff --git a/dune/tectonic/factories/twoblocksfactory.cc b/dune/tectonic/factories/twoblocksfactory.cc
index 39c47cd7..44070552 100644
--- a/dune/tectonic/factories/twoblocksfactory.cc
+++ b/dune/tectonic/factories/twoblocksfactory.cc
@@ -121,7 +121,7 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
 
     using Function = Dune::VirtualFunction<double, double>;
     std::shared_ptr<Function> neumannFunction = std::make_shared<NeumannCondition>();
-    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>();
+    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>(this->parset_.template get<double>("boundary.dirichlet.finalVelocity"));
 
     const double lengthScale = CuboidGeometry::lengthScale();
 
@@ -148,7 +148,7 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
     const auto& body1 = this->contactNetwork_.body(1);
     const auto& leafVertexCount1 = body1->nVertices();
     std::shared_ptr<Dune::BitSetVector<dim>> velocityDirichletNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1);
-    for (int j=0; j<leafVertexCount1; j++) {
+    for (size_t j=0; j<leafVertexCount1; j++) {
         if (leafFaces_[1]->upper.containsVertex(j))
             (*velocityDirichletNodes)[j][0] = true;
 
@@ -167,17 +167,21 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
     // body0, body1: natural boundary conditions
     for (size_t i=0; i<this->bodyCount_; i++) {
         const auto& body = this->contactNetwork_.body(i);
-        const auto& leafVertexCount = body->nVertices();
 
         // Neumann boundary
-        std::shared_ptr<LeafBoundaryCondition> neumannBoundary = std::make_shared<LeafBoundaryCondition>(std::make_shared<LeafBoundaryPatch>(body->gridView()), neumannFunction, "neumann");
+        auto neumannBoundary = std::make_shared<LeafBoundaryCondition>("neumann");
+        auto neumannPatch = std::make_shared<LeafBoundaryPatch>(body->gridView(), true);
+        neumannBoundary->setBoundaryPatch(neumannPatch);
+        neumannBoundary->setBoundaryFunction(neumannFunction);
         body->addBoundaryCondition(neumannBoundary);
     }
 
     // body1: Neumann boundary (upper)
     // normal load
-    /*std::shared_ptr<Dune::BitSetVector<dim>> loadNeumannNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1);
-    for (int j=0; j<leafVertexCount1; j++) {
+    std::shared_ptr<Function> constantFunction = std::make_shared<NeumannCondition>(this->parset_.template get<double>("boundary.neumann.sigmaN"));
+
+    std::shared_ptr<Dune::BitSetVector<dim>> loadNeumannNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1);
+    for (size_t j=0; j<leafVertexCount1; j++) {
         if (leafFaces_[1]->upper.containsVertex(j))
             (*loadNeumannNodes)[j][1] = true;
 
@@ -187,12 +191,10 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() {
         #endif
     }
 
-    std::shared_ptr<LeafBoundaryCondition> loadNeumannBoundary = std::make_shared<LeafBoundaryCondition>("neumann");
-    std::shared_ptr<Function> constantFunction = std::make_shared<NeumannCondition>(this->parset_.template get<double>("boundary.neumann.sigmaN"));
-
+    auto loadNeumannBoundary = std::make_shared<LeafBoundaryCondition>("neumann");
     loadNeumannBoundary->setBoundaryPatch(body1->gridView(), loadNeumannNodes);
     loadNeumannBoundary->setBoundaryFunction(constantFunction);
-    body1->addBoundaryCondition(loadNeumannBoundary);*/
+    body1->addBoundaryCondition(loadNeumannBoundary);
 }
 
 #include "twoblocksfactory_tmpl.cc"
diff --git a/dune/tectonic/io/CMakeLists.txt b/dune/tectonic/io/CMakeLists.txt
index c1aada25..07045307 100644
--- a/dune/tectonic/io/CMakeLists.txt
+++ b/dune/tectonic/io/CMakeLists.txt
@@ -2,7 +2,7 @@ add_subdirectory("hdf5")
 
 add_custom_target(tectonic_dune_io SOURCES
   hdf5-bodywriter.hh
-  hdf5-levelwriter.hh 
+  hdf5-writer.hh 
   uniform-grid-writer.cc
   vtk.hh
   vtk.cc
diff --git a/dune/tectonic/io/hdf5-bodywriter.hh b/dune/tectonic/io/hdf5-bodywriter.hh
index dfb2b3aa..91dd5ede 100644
--- a/dune/tectonic/io/hdf5-bodywriter.hh
+++ b/dune/tectonic/io/hdf5-bodywriter.hh
@@ -6,16 +6,18 @@
 #include <dune/fufem/hdf5/file.hh>
 
 #include "hdf5/frictionalboundary-writer.hh"
-#include "hdf5/patchinfo-writer.hh"
+//#include "hdf5/patchinfo-writer.hh"
 
 template <class ProgramState, class VertexBasis, class GridView>
 class HDF5BodyWriter {
 private:
-    using PatchInfoWriter = PatchInfoWriter<ProgramState, VertexBasis, GridView>;
-    using FrictionalBoundaryWriter = FrictionalBoundaryWriter<ProgramState, GridView>;
+    using Vector = typename ProgramState::Vector;
+    using ScalarVector = typename ProgramState::ScalarVector;
+    //using PatchInfoWriter = PatchInfoWriter<ProgramState, VertexBasis, GridView>;
+
 public:
     using VertexCoordinates = typename ProgramState::Vector;
-    using FrictionPatches = std::vector<const BoundaryPatch<GridView>* >;
+    using Patch = typename FrictionalBoundaryWriter<GridView>::Patch;
     using WeakPatches = std::vector<const ConvexPolyhedron<LocalVector>* >;
 
     using LocalVector = typename VertexCoordinates::block_type;
@@ -23,50 +25,45 @@ class HDF5BodyWriter {
     //friend class HDF5LevelWriter<ProgramState, VertexBasis, GridView>;
 
     HDF5BodyWriter(
-            HDF5::Grouplike &file,
+            HDF5::File& file,
+            const size_t bodyID,
             const VertexCoordinates& vertexCoordinates,
             const VertexBasis& vertexBasis,
-            const FrictionPatches& frictionPatches,
+            const Patch& frictionPatch,
             const WeakPatches& weakPatches) :
-        patchCount_(frictionPatches.size()),
-        file_(file),
-#if MY_DIM == 3
-        patchInfoWriters_(patchCount_),
-#endif
-        frictionBoundaryWriters_(patchCount_) {
+        id_(bodyID),
+        /*#if MY_DIM == 3
+                patchInfoWriters_(patchCount_),
+        #endif*/
+        group_(file, "body"+std::to_string(id_)) {
 
-        assert(patchCount_ == weakPatches.size());
+        frictionBoundaryWriter_ = std::make_unique<FrictionalBoundaryWriter<GridView>>(group_, vertexCoordinates, frictionPatch);
 
-        for (size_t i=0; i<patchCount_; i++) {
-#if MY_DIM == 3
+        /*#if MY_DIM == 3
             patchInfoWriters_[i] = std::make_unique<PatchInfoWriter>(file_, vertexBasis, *frictionPatches[i], *weakPatches[i], i);
-#endif
-            frictionBoundaryWriters_[i] = std::make_unique<FrictionalBoundaryWriter>(file_, vertexCoordinates, *frictionPatches[i], i);
-        }
+        #endif*/
     }
 
-    template <class Friction>
-    void reportSolution(ProgramState const &programState,
-                      // for the friction coefficient
-                      Friction& friction) {
+    void reportSolution(const ProgramState& programState, const Vector& v, const ScalarVector& frictionCoeff) {
+        frictionBoundaryWriter_->write(programState.timeStep, programState.u[id_], v, programState.alpha[id_], frictionCoeff);
 
-        for (size_t i=0; i<patchCount_; i++) {
-#if MY_DIM == 3
-            patchInfoWriters_[i]->write(programState);
-#endif
-            frictionBoundaryWriters_[i]->write(programState, friction);
-        }
+        /*#if MY_DIM == 3
+        patchInfoWriters_[i]->write(programState);
+        #endif*/
     }
 
-private:
-    const size_t patchCount_;
+    auto id() const {
+        return id_;
+    }
 
-    HDF5::Grouplike &file_;
+private:
+    const size_t id_;
+    HDF5::Group group_;
 
-#if MY_DIM == 3
+/*#if MY_DIM == 3
     std::vector<std::unique_ptr<PatchInfoWriter>> patchInfoWriters_;
-#endif
+#endif*/
 
-    std::vector<std::unique_ptr<FrictionalBoundaryWriter>> frictionBoundaryWriters_;
+    std::unique_ptr<FrictionalBoundaryWriter<GridView>> frictionBoundaryWriter_;
 };
 #endif
diff --git a/dune/tectonic/io/hdf5-levelwriter.hh b/dune/tectonic/io/hdf5-levelwriter.hh
deleted file mode 100644
index 4edabd8b..00000000
--- a/dune/tectonic/io/hdf5-levelwriter.hh
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef SRC_HDF5_LEVELWRITER_HH
-#define SRC_HDF5_LEVELWRITER_HH
-
-#include <dune/fufem/hdf5/file.hh>
-
-#include "hdf5/iteration-writer.hh"
-#include "hdf5/time-writer.hh"
-#include "hdf5-bodywriter.hh"
-
-template <class ProgramState, class VertexBasis, class GridView>
-class HDF5LevelWriter {
-public:
-    using HDF5BodyWriter = HDF5BodyWriter<ProgramState, VertexBasis, GridView>;
-    using VertexCoordinates = std::vector<typename HDF5BodyWriter::VertexCoordinates>;
-    using VertexBases = std::vector<const VertexBasis*>;
-    using FrictionPatches = std::vector<typename HDF5BodyWriter::FrictionPatches>;
-    using WeakPatches = std::vector<typename HDF5BodyWriter::WeakPatches>;
-
-    //friend class HDF5NetworkWriter<ProgramState, VertexBasis, GridView>;
-
-    HDF5LevelWriter(HDF5::Grouplike &file,
-             const VertexCoordinates& vertexCoordinates,
-             const VertexBases& vertexBases,
-             const FrictionPatches& frictionBoundaries,
-             const WeakPatches& weakPatches)
-      : bodyCount_(vertexCoordinates.size()),
-        file_(file),
-        iterationWriter_(file_),
-        timeWriter_(file_),
-        bodyWriters_(bodyCount_) {
-
-        for (size_t i=0; i<bodyCount_; i++) {
-            bodyWriters_[i] = std::make_unique<HDF5BodyWriter>(file_, vertexCoordinates[i], *vertexBases[i], frictionBoundaries[i], weakPatches[i]);
-        }
-    }
-
-    template <class Friction>
-    void reportSolution(
-            const ProgramState& programState,
-            // for the friction coefficient
-            Friction& friction) {
-        timeWriter_.write(programState);
-
-        for (size_t i=0; i<bodyCount_; i++) {
-            bodyWriters_[i]->reportSolution(programState, friction); //TODO
-        }
-    }
-
-    void reportIterations(
-            const ProgramState& programState,
-            const IterationRegister& iterationCount) {
-        iterationWriter_.write(programState.timeStep, iterationCount);
-  }
-
-private:
-  const size_t bodyCount_;
-
-  HDF5::Grouplike &file_;
-
-  IterationWriter iterationWriter_;
-  TimeWriter<ProgramState> timeWriter_;
-
-  std::vector<std::unique_ptr<HDF5BodyWriter>> bodyWriters_;
-};
-#endif
diff --git a/dune/tectonic/io/hdf5-writer.hh b/dune/tectonic/io/hdf5-writer.hh
new file mode 100644
index 00000000..2a8ecc2f
--- /dev/null
+++ b/dune/tectonic/io/hdf5-writer.hh
@@ -0,0 +1,108 @@
+#ifndef SRC_HDF5_WRITER_HH
+#define SRC_HDF5_WRITER_HH
+
+#include <dune/fufem/hdf5/file.hh>
+
+#include "hdf5/iteration-writer.hh"
+#include "hdf5/time-writer.hh"
+#include "hdf5-bodywriter.hh"
+
+template <class ProgramState, class VertexBasis, class GridView>
+class HDF5Writer {
+public:
+    using HDF5BodyWriter = HDF5BodyWriter<ProgramState, VertexBasis, GridView>;
+    using VertexCoordinates = std::vector<typename HDF5BodyWriter::VertexCoordinates>;
+    using VertexBases = std::vector<const VertexBasis*>;
+    using FrictionPatches = std::vector<const typename HDF5BodyWriter::Patch*>;
+    using WeakPatches = std::vector<typename HDF5BodyWriter::WeakPatches>;
+
+    //friend class HDF5NetworkWriter<ProgramState, VertexBasis, GridView>;
+
+    HDF5Writer(HDF5::File& file,
+             const VertexCoordinates& vertexCoordinates,
+             const VertexBases& vertexBases,
+             const FrictionPatches& frictionPatches,
+             const WeakPatches& weakPatches)
+      : file_(file),
+        iterationWriter_(file_),
+        timeWriter_(file_),
+        frictionPatches_(frictionPatches) {
+
+        for (size_t i=0; i<frictionPatches_.size(); i++) {
+            if (frictionPatches_[i]->numVertices() > 0)
+                bodyWriters_.push_back(std::make_unique<HDF5BodyWriter>(file_, i, vertexCoordinates[i], *vertexBases[i], *frictionPatches_[i], weakPatches[i]));
+        }
+    }
+
+    template <class ContactNetwork, class Friction>
+    void reportSolution(const ProgramState& programState, const ContactNetwork& contactNetwork, Friction& friction) {
+
+        timeWriter_.write(programState);
+
+        friction.updateAlpha(programState.alpha);
+
+        // extract relative velocities
+        using Vector = typename ProgramState::Vector;
+        Vector mortarV;
+        contactNetwork.nBodyAssembler().nodalToTransformed(programState.v, mortarV);
+
+        std::vector<Vector> v_rel;
+        split(mortarV, v_rel);
+
+        using ScalarVector = typename ProgramState::ScalarVector;
+        const auto frictionCoeff = friction.coefficientOfFriction(mortarV);
+
+        double norm = 0;
+        const auto& bodyNodes = *frictionPatches_[0]->getVertices();
+        for (size_t i=bodyNodes.size(); i<frictionCoeff.size(); i++) {
+            norm += frictionCoeff[i].two_norm();
+        }
+        std::cout << std::setprecision(10) << "friction coefficients norm: " << norm << std::endl;
+
+        std::vector<ScalarVector> splitCoeff;
+        split(frictionCoeff, splitCoeff);
+
+        //print(v_rel, "v_rel: ");
+
+        for (size_t i=0; i<bodyWriters_.size(); i++) {
+            auto bodyID = bodyWriters_[i]->id();
+            bodyWriters_[i]->reportSolution(programState, v_rel[bodyID], splitCoeff[bodyID]);
+        }
+    }
+
+    void reportIterations(const ProgramState& programState, const IterationRegister& iterationCount) {
+        iterationWriter_.write(programState.timeStep, iterationCount);
+    }
+
+    template <class VectorType>
+    void split(const VectorType& v, std::vector<VectorType>& splitV) const {
+
+        const size_t nBodies = frictionPatches_.size();
+
+        size_t globalIdx = 0;
+        splitV.resize(nBodies);
+        for (size_t bodyIdx=0; bodyIdx<nBodies; bodyIdx++) {
+            const auto& bodyNodes = *frictionPatches_[bodyIdx]->getVertices();
+
+            auto& splitV_ = splitV[bodyIdx];
+            splitV_.resize(bodyNodes.size());
+
+            for (size_t i=0; i<splitV_.size(); i++) {
+                if (toBool(bodyNodes[i])) {
+                    splitV_[i] = v[globalIdx];
+                }
+                globalIdx++;
+            }
+        }
+    }
+
+private:
+  HDF5::File& file_;
+
+  IterationWriter iterationWriter_;
+  TimeWriter<ProgramState> timeWriter_;
+
+  const FrictionPatches& frictionPatches_;
+  std::vector<std::unique_ptr<HDF5BodyWriter>> bodyWriters_;
+};
+#endif
diff --git a/dune/tectonic/io/hdf5/frictionalboundary-writer.cc b/dune/tectonic/io/hdf5/frictionalboundary-writer.cc
index 0cb5e01a..6bcfa353 100644
--- a/dune/tectonic/io/hdf5/frictionalboundary-writer.cc
+++ b/dune/tectonic/io/hdf5/frictionalboundary-writer.cc
@@ -7,12 +7,12 @@
 #include "frictionalboundary-writer.hh"
 #include "restrict.hh"
 
-template <class ProgramState, class GridView>
-FrictionalBoundaryWriter<ProgramState, GridView>::FrictionalBoundaryWriter(
-    HDF5::Grouplike &file, Vector const &vertexCoordinates,
-    Patch const &frictionalBoundary, size_t const id)
-    : id_(id),
-      group_(file, "frictionalBoundary"+std::to_string(id_)),
+template <class GridView>
+template <class Vector>
+FrictionalBoundaryWriter<GridView>::FrictionalBoundaryWriter(
+    HDF5::Group& group, const Vector& vertexCoordinates,
+    const Patch& frictionalBoundary)
+    : group_(group),
       frictionalBoundary_(frictionalBoundary),
       frictionalBoundaryDisplacementWriter_(group_, "displacement",
                                             frictionalBoundary.numVertices(),
@@ -32,33 +32,22 @@ FrictionalBoundaryWriter<ProgramState, GridView>::FrictionalBoundaryWriter(
   setEntry(frictionalBoundaryCoordinateWriter, frictionalBoundaryCoordinates);
 }
 
-template <class ProgramState, class GridView>
-template <class Friction>
-void FrictionalBoundaryWriter<ProgramState, GridView>::write(
-    ProgramState const &programState, Friction &friction) {
+template <class GridView>
+template <class Vector, class ScalarVector>
+void FrictionalBoundaryWriter<GridView>::write(
+    const size_t timeStep, const Vector& u, const Vector& v, const ScalarVector& alpha, const ScalarVector& frictionCoeff) {
 
-  auto const frictionalBoundaryDisplacements =
-      restrictToSurface(programState.u[id_], frictionalBoundary_);
-  addEntry(frictionalBoundaryDisplacementWriter_, programState.timeStep,
-           frictionalBoundaryDisplacements);
+  auto const frictionalBoundaryDisplacements = restrictToSurface(u, frictionalBoundary_);
+  addEntry(frictionalBoundaryDisplacementWriter_, timeStep, frictionalBoundaryDisplacements);
 
-  auto const frictionalBoundaryVelocities =
-      restrictToSurface(programState.v[id_], frictionalBoundary_);
-  addEntry(frictionalBoundaryVelocityWriter_, programState.timeStep,
-           frictionalBoundaryVelocities);
+  auto const frictionalBoundaryVelocities = restrictToSurface(v, frictionalBoundary_);
+  addEntry(frictionalBoundaryVelocityWriter_, timeStep, frictionalBoundaryVelocities);
 
-  auto const frictionalBoundaryStates =
-      restrictToSurface(programState.alpha[id_], frictionalBoundary_);
-  addEntry(frictionalBoundaryStateWriter_, programState.timeStep,
-           frictionalBoundaryStates);
+  auto const frictionalBoundaryStates = restrictToSurface(alpha, frictionalBoundary_);
+  addEntry(frictionalBoundaryStateWriter_, timeStep, frictionalBoundaryStates);
 
-  //TODO: needs to transform programState.v to relative velocities with nBodyAssembler
-  /*friction.updateAlpha(programState.alpha);
-  auto const c = friction.coefficientOfFriction(programState.v[id_]);
-  auto const frictionalBoundaryCoefficient =
-      restrictToSurface(c, frictionalBoundary_);
-  addEntry(frictionalBoundaryCoefficientWriter_, programState.timeStep,
-           frictionalBoundaryCoefficient);*/
+  auto const frictionalBoundaryCoefficient = restrictToSurface(frictionCoeff, frictionalBoundary_);
+  addEntry(frictionalBoundaryCoefficientWriter_, timeStep, frictionalBoundaryCoefficient);
 }
 
 #include "frictionalboundary-writer_tmpl.cc"
diff --git a/dune/tectonic/io/hdf5/frictionalboundary-writer.hh b/dune/tectonic/io/hdf5/frictionalboundary-writer.hh
index 09774013..a7ca0855 100644
--- a/dune/tectonic/io/hdf5/frictionalboundary-writer.hh
+++ b/dune/tectonic/io/hdf5/frictionalboundary-writer.hh
@@ -1,28 +1,25 @@
 #ifndef SRC_HDF_FRICTIONALBOUNDARY_WRITER_HH
 #define SRC_HDF_FRICTIONALBOUNDARY_WRITER_HH
 
-#include <dune/fufem/boundarypatch.hh>
 
+#include <dune/fufem/boundarypatch.hh>
 #include <dune/fufem/hdf5/sequenceio.hh>
 
-template <class ProgramState, class GridView> class FrictionalBoundaryWriter {
-  using ScalarVector = typename ProgramState::ScalarVector;
-  using Vector = typename ProgramState::Vector;
+template <class GridView> class FrictionalBoundaryWriter {
+public:
   using Patch = BoundaryPatch<GridView>;
 
-public:
-  FrictionalBoundaryWriter(HDF5::Grouplike& file, Vector const &vertexCoordinates,
-                           Patch const &frictionalBoundary, size_t const id);
+  template <class Vector>
+  FrictionalBoundaryWriter(HDF5::Group& group, const Vector& vertexCoordinates,
+                           const Patch& frictionalBoundary);
 
-  template <class Friction>
-  void write(ProgramState const &programState, Friction &friction);
+  template <class Vector, class ScalarVector>
+  void write(const size_t timeStep, const Vector& u, const Vector& v, const ScalarVector& alpha, const ScalarVector& frictionCoeff);
 
 private:
-  size_t const id_;
-
-  HDF5::Group group_;
+  HDF5::Group& group_;
 
-  Patch const &frictionalBoundary_;
+  const Patch& frictionalBoundary_;
 
   HDF5::SequenceIO<2> frictionalBoundaryDisplacementWriter_;
   HDF5::SequenceIO<2> frictionalBoundaryVelocityWriter_;
diff --git a/dune/tectonic/io/hdf5/frictionalboundary-writer_tmpl.cc b/dune/tectonic/io/hdf5/frictionalboundary-writer_tmpl.cc
index 883c59ba..51633c69 100644
--- a/dune/tectonic/io/hdf5/frictionalboundary-writer_tmpl.cc
+++ b/dune/tectonic/io/hdf5/frictionalboundary-writer_tmpl.cc
@@ -1,13 +1,19 @@
 #include "../../explicitvectors.hh"
 #include "../../explicitgrid.hh"
 
-#include "../../data-structures/program_state.hh"
 
-using MyProgramState = ProgramState<Vector, ScalarVector>;
-using MyFriction = GlobalFriction<Matrix, Vector>;
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/fufem/hdf5/sequenceio.hh>
 
-template class FrictionalBoundaryWriter<MyProgramState, DeformedGrid::LeafGridView>;
-template void FrictionalBoundaryWriter<MyProgramState, DeformedGrid::LeafGridView>::write(
-    MyProgramState const &programState, MyFriction &friction);
+//#include "../../data-structures/program_state.hh"
+
+//using MyProgramState = ProgramState<Vector, ScalarVector>;
+
+template class FrictionalBoundaryWriter<DefLeafGridView>;
+
+template FrictionalBoundaryWriter<DefLeafGridView>::FrictionalBoundaryWriter(
+    HDF5::Group& group, const Vector& vertexCoordinates, const BoundaryPatch<DefLeafGridView>& frictionalBoundary);
+template void FrictionalBoundaryWriter<DefLeafGridView>::write(
+    const size_t timeStep, const Vector& u, const Vector& v, const ScalarVector& alpha, const ScalarVector& frictionCoeff);
 
 
diff --git a/dune/tectonic/io/hdf5/restrict.hh b/dune/tectonic/io/hdf5/restrict.hh
index 78639f1b..efcdaffc 100644
--- a/dune/tectonic/io/hdf5/restrict.hh
+++ b/dune/tectonic/io/hdf5/restrict.hh
@@ -8,16 +8,16 @@
 #include "../../utils/tobool.hh"
 
 template <class Vector, class Patch>
-Vector restrictToSurface(Vector const &v1, Patch const &patch) {
-  auto const &vertices = *patch.getVertices();
-  assert(vertices.size() == v1.size());
+Vector restrictToSurface(const Vector& v1, const Patch& patch) {
+    auto const &vertices = *patch.getVertices();
+    assert(vertices.size() == v1.size());
 
-  Vector ret(vertices.count());
-  auto target = ret.begin();
-  for (size_t i = 0; i < v1.size(); ++i)
-    if (toBool(vertices[i]))
-      *(target++) = v1[i];
-  assert(target == ret.end());
-  return ret;
+    Vector ret(vertices.count());
+    auto target = ret.begin();
+    for (size_t i = 0; i < v1.size(); ++i)
+      if (toBool(vertices[i]))
+        *(target++) = v1[i];
+    assert(target == ret.end());
+    return ret;
 }
 #endif
diff --git a/dune/tectonic/problem-data/CMakeLists.txt b/dune/tectonic/problem-data/CMakeLists.txt
index 8b975765..e59f2e04 100644
--- a/dune/tectonic/problem-data/CMakeLists.txt
+++ b/dune/tectonic/problem-data/CMakeLists.txt
@@ -8,6 +8,7 @@ add_custom_target(tectonic_dune_problem-data SOURCES
   myglobalfrictiondata.hh
   patchfunction.hh
   segmented-function.hh
+  strikeslipbody.hh
 )
 
 #install headers
@@ -19,4 +20,5 @@ install(FILES
   myglobalfrictiondata.hh
   patchfunction.hh
   segmented-function.hh
+  strikeslipbody.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/tectonic)
diff --git a/dune/tectonic/problem-data/bc.hh b/dune/tectonic/problem-data/bc.hh
index 2d478035..ac82e47f 100644
--- a/dune/tectonic/problem-data/bc.hh
+++ b/dune/tectonic/problem-data/bc.hh
@@ -5,9 +5,14 @@
 
 class VelocityDirichletCondition
     : public Dune::VirtualFunction<double, double> {
+
+public:
+  VelocityDirichletCondition(const double finalVelocity = 5e-5) :
+      finalVelocity_(finalVelocity)
+  {}
+
   void evaluate(double const &relativeTime, double &y) const {
     // Assumed to vanish at time zero
-      double const finalVelocity = 5e-5;
     
     //std::cout << "VelocityDirichletCondition::evaluate()" << std::endl;
     
@@ -17,15 +22,21 @@ class VelocityDirichletCondition
         std::cout << "- final velocity reached" << std::endl;*/
     
     y = (relativeTime <= 0.1)
-            ? finalVelocity * (1.0 - std::cos(relativeTime * M_PI / 0.1)) / 2.0
-            : finalVelocity;
+            ? finalVelocity_ * (1.0 - std::cos(relativeTime * M_PI / 0.1)) / 2.0
+            : finalVelocity_;
+
+    //y = relativeTime * finalVelocity;
+
   }
+
+private:
+  const double finalVelocity_;
 };
 
 class NeumannCondition : public Dune::VirtualFunction<double, double> {
   public:
     NeumannCondition(double c = 0.0) : c_(c) {}
-    void evaluate(double const &relativeTime, double &y) const { y = c_; }
+    void evaluate(double const &relativeTime, double &y) const { y = -c_; }
 
   private:
     double c_;
diff --git a/dune/tectonic/problem-data/grid/CMakeLists.txt b/dune/tectonic/problem-data/grid/CMakeLists.txt
index e0fd59ba..b7abfed5 100644
--- a/dune/tectonic/problem-data/grid/CMakeLists.txt
+++ b/dune/tectonic/problem-data/grid/CMakeLists.txt
@@ -11,6 +11,10 @@ add_custom_target(tectonic_dune_problem-data_grid SOURCES
   mygrids.cc
   simplexmanager.hh
   simplexmanager.cc
+  trianglegeometry.hh
+  trianglegeometry.cc
+  trianglegrids.hh
+  trianglegrids.cc
 )
 
 #install headers
@@ -22,4 +26,6 @@ install(FILES
   gridconstructor.hh
   mygrids.hh
   simplexmanager.hh
+  trianglegeometry.hh
+  trianglegrids.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/tectonic)
diff --git a/dune/tectonic/problem-data/grid/trianglegeometry.cc b/dune/tectonic/problem-data/grid/trianglegeometry.cc
new file mode 100644
index 00000000..7c24275f
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegeometry.cc
@@ -0,0 +1,212 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fstream>
+
+#ifdef HAVE_CAIROMM
+#include <cairomm/context.h>
+#include <cairomm/fontface.h>
+#include <cairomm/surface.h>
+#endif
+
+#include "trianglegeometry.hh"
+
+#if MY_DIM == 3
+template <class ctype>
+TriangleGeometry<ctype>::TriangleGeometry(const GlobalCoords& origin,
+                                      const double length, const double height, const double depth) :
+    length_(length*lengthScale()),
+    height_(height*lengthScale()),
+    depth_(depth*lengthScale()),
+    A_(origin),
+    B_({origin[0]+length_, origin[1], 0}),
+    C_({origin[0]+length_, origin[1]+height_, 0})
+    {}
+#else
+
+template <class ctype>
+TriangleGeometry<ctype>::TriangleGeometry(const GlobalCoords& origin,
+                                      const double length, const double height) :
+    length_(length*lengthScale()),
+    height_(height*lengthScale()),
+    A_(origin),
+    B_({origin[0]+length_, origin[1]}),
+    C_({origin[0]+length_, origin[1]+height_})
+    {}
+#endif
+
+template <class ctype>
+void TriangleGeometry<ctype>::addWeakeningRegion(const WeakeningRegion& weakeningRegion) {
+    weakeningRegions_.emplace_back(weakeningRegion);
+}
+
+template <class ctype>
+void TriangleGeometry<ctype>::addWeakeningPatch(const Dune::ParameterTree& parset, const GlobalCoords& vertex0, const GlobalCoords& vertex1) {
+    WeakeningRegion weakPatch;
+
+#if MY_DIM == 3 // TODO: Does not work yet
+    if (vertex0 != vertex1) {
+        weakPatch.vertices.resize(4);
+        weakPatch.vertices[0] = weakPatch.vertices[2] = vertex0;
+        weakPatch.vertices[1] = weakPatch.vertices[3] = vertex1;
+
+        for (size_t k = 0; k < 2; ++k) {
+            weakPatch.vertices[k][2] = -depth_ / 2.0;
+            weakPatch.vertices[k + 2][2] = depth_ / 2.0;
+        }
+        switch (parset.get<Config::PatchType>("patchType")) {
+            case Config::Rectangular:
+                break;
+            case Config::Trapezoidal:
+                weakPatch.vertices[1][0] += 0.05 * lengthScale();
+                weakPatch.vertices[3][0] -= 0.05 * lengthScale();
+                break;
+            default:
+                assert(false);
+        }
+        addWeakeningRegion(weakPatch);
+    }
+#else
+    if (vertex0 != vertex1) {
+        weakPatch.vertices.resize(2);
+        weakPatch.vertices[0] = vertex0;
+        weakPatch.vertices[1] = vertex1;
+        addWeakeningRegion(weakPatch);
+    }
+#endif
+}
+
+template <class ctype>
+void TriangleGeometry<ctype>::write() const {
+    std::fstream writer("geometry", std::fstream::out);
+    writer << "A = " << A_ << std::endl;
+    writer << "B = " << B_ << std::endl;
+    writer << "C = " << C_ << std::endl;
+}
+
+/*
+template <class ctype>
+void CuboidGeometry<ctype>::render() const {
+#ifdef HAVE_CAIROMM
+  std::string const filename = "geometry.png";
+  double const width = 600;
+  double const height = 400;
+  double const widthScale = 400;
+  double const heightScale = 400;
+
+  auto surface =
+      Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
+  auto cr = Cairo::Context::create(surface);
+
+  auto const setRGBColor = [&](int colour) {
+    cr->set_source_rgb(((colour & 0xFF0000) >> 16) / 255.0,
+                       ((colour & 0x00FF00) >> 8) / 255.0,
+                       ((colour & 0x0000FF) >> 0) / 255.0);
+  };
+  auto const moveTo = [&](LocalVector2D const &v) { cr->move_to(v[0], -v[1]); };
+  auto const lineTo = [&](LocalVector2D const &v) { cr->line_to(v[0], -v[1]); };
+
+  cr->scale(widthScale, heightScale);
+  cr->translate(0.1, 0.1);
+  cr->set_line_width(0.0025);
+
+  // triangle
+  {
+    moveTo(reference::A);
+    lineTo(reference::B);
+    lineTo(reference::C);
+    cr->close_path();
+    cr->stroke();
+  }
+
+  // dashed lines
+  {
+    cr->save();
+    std::vector<double> dashPattern = { 0.005 };
+    cr->set_dash(dashPattern, 0);
+    moveTo(reference::Z);
+    lineTo(reference::Y);
+    moveTo(reference::U);
+    lineTo(reference::X);
+    cr->stroke();
+    cr->restore();
+  }
+
+  // fill viscoelastic region
+  {
+    cr->save();
+    setRGBColor(0x0097E0);
+    moveTo(reference::B);
+    lineTo(reference::K);
+    lineTo(reference::M);
+    cr->fill();
+    cr->restore();
+  }
+
+  // mark weakening region
+  {
+    cr->save();
+    setRGBColor(0x7AD3FF);
+    cr->set_line_width(0.005);
+    moveTo(reference::X);
+    lineTo(reference::Y);
+    cr->stroke();
+    cr->restore();
+  }
+
+  // mark points
+  {
+    auto const drawCircle = [&](LocalVector2D const &v) {
+      cr->arc(v[0], -v[1], 0.0075, -M_PI, M_PI); // x,y,radius,angle1,angle2
+      cr->fill();
+    };
+
+    cr->save();
+    setRGBColor(0x002F47);
+    drawCircle(reference::A);
+    drawCircle(reference::B);
+    drawCircle(reference::C);
+    drawCircle(reference::Y);
+    drawCircle(reference::X);
+    drawCircle(reference::Z);
+    drawCircle(reference::U);
+    drawCircle(reference::K);
+    drawCircle(reference::M);
+    drawCircle(reference::G);
+    drawCircle(reference::H);
+    drawCircle(reference::J);
+    drawCircle(reference::I);
+    cr->restore();
+  }
+
+  // labels
+  {
+    auto const label = [&](LocalVector2D const &v, std::string l) {
+      moveTo(v);
+      cr->rel_move_to(0.005, -0.02);
+      cr->show_text(l);
+    };
+    auto font = Cairo::ToyFontFace::create(
+        "monospace", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL);
+
+    cr->save();
+    cr->set_font_face(font);
+    cr->set_font_size(0.03);
+
+    label(A, "A");
+    label(B, "B");
+    label(C, "C");
+    label(D, "D");
+    label(X, "X");
+    label(Y, "Y");
+
+    cr->restore();
+  }
+
+  surface->write_to_png(filename);
+#endif
+}
+*/
+
+#include "trianglegeometry_tmpl.cc"
diff --git a/dune/tectonic/problem-data/grid/trianglegeometry.hh b/dune/tectonic/problem-data/grid/trianglegeometry.hh
new file mode 100644
index 00000000..92f178a6
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegeometry.hh
@@ -0,0 +1,88 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_TRIANGLEGEOMETRY_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_TRIANGLEGEOMETRY_HH
+
+#include <vector>
+
+#include <dune/common/fvector.hh>
+#include <dune/common/parametertree.hh>
+
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+//            C
+//          / |
+//         /  |
+//        /   |height
+//       /    |
+//      A --- B
+//       length
+
+template <class ctype = double>
+class TriangleGeometry {
+public:
+    typedef Dune::FieldVector<ctype, MY_DIM> GlobalCoords;
+    using WeakeningRegion = ConvexPolyhedron<GlobalCoords>;
+
+    static constexpr double lengthScale() {
+        return 1.0;
+    } // scaling factor
+
+private:
+    const ctype length_;
+    const ctype height_;
+#if MY_DIM == 3
+    const ctype depth_;
+#endif
+
+    // corners of triangle
+    const GlobalCoords A_;
+    const GlobalCoords B_;
+    const GlobalCoords C_;
+
+    // weakening regions
+    std::vector<WeakeningRegion> weakeningRegions_;
+
+public:
+#if MY_DIM == 3
+    TriangleGeometry(const GlobalCoords& origin,
+                   const double length = 0.5, const double height = 0.5, const double depth = 0.12);
+
+    const auto& depth() const {
+        return depth_;
+    }
+#else
+    TriangleGeometry(const GlobalCoords& origin,
+                       const double length = 0.5, const double height = 0.5);
+#endif
+
+    void addWeakeningRegion(const WeakeningRegion& weakeningRegion);
+    void addWeakeningPatch(const Dune::ParameterTree& parset, const GlobalCoords& vertex0, const GlobalCoords& vertex1);
+
+    const auto& A() const {
+        return A_;
+    }
+
+    const auto& B() const {
+        return B_;
+    }
+
+    const auto& C() const {
+        return C_;
+    }
+
+    const auto& weakeningRegions() const {
+        return weakeningRegions_;
+    }
+
+    const auto& length() const {
+        return length_;
+    }
+
+    const auto& height() const {
+        return height_;
+    }
+
+    void write() const;
+
+   // void render() const;
+};
+#endif
diff --git a/dune/tectonic/problem-data/grid/trianglegeometry_tmpl.cc b/dune/tectonic/problem-data/grid/trianglegeometry_tmpl.cc
new file mode 100644
index 00000000..c67a450f
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegeometry_tmpl.cc
@@ -0,0 +1,8 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "../../explicitgrid.hh"
+#include "trianglegeometry.hh"
+
+template class TriangleGeometry<typename Grid::ctype>;
diff --git a/dune/tectonic/problem-data/grid/trianglegrids.cc b/dune/tectonic/problem-data/grid/trianglegrids.cc
new file mode 100644
index 00000000..f45ada06
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegrids.cc
@@ -0,0 +1,204 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/fufem/geometry/polyhedrondistance.hh>
+
+#include "trianglegrids.hh"
+#include "../midpoint.hh"
+#include "../../utils/diameter.hh"
+
+#include "simplexmanager.hh"
+
+// Fix: 3D case (still Elias code)
+template <class Grid> GridsConstructor<Grid>::GridsConstructor(const std::vector<std::shared_ptr<TriangleGeometry<typename Grid::ctype>>>& triangleGeometries) :
+  triangleGeometries_(triangleGeometries)
+{
+  size_t const gridCount = triangleGeometries.size();
+  grids.resize(gridCount);
+  gridFactories.resize(gridCount);
+
+  for (size_t idx=0; idx<grids.size(); idx++) {
+    const auto& triangleGeometry = *triangleGeometries[idx];
+
+    const auto& A = triangleGeometry.A();
+    const auto& B = triangleGeometry.B();
+    const auto& C = triangleGeometry.C();
+
+    const size_t vc = 3;
+
+#if MY_DIM == 3
+    Dune::FieldMatrix<double, 2 * vc, MY_DIM> vertices;
+#else
+    Dune::FieldMatrix<double, vc, MY_DIM> vertices;
+#endif
+    for (size_t i = 0; i < 2; ++i) {
+#if MY_DIM == 3
+      size_t numXYplanes = 2;
+#else
+      size_t numXYplanes = 1;
+#endif
+      size_t k = 0;
+      for (size_t j = 1; j <= numXYplanes; ++j) {
+        vertices[k++][i] = A[i];
+        vertices[k++][i] = B[i];
+        vertices[k++][i] = C[i];
+        assert(k == j * vc);
+      }
+    }
+
+#if MY_DIM == 3
+    for (size_t k = 0; k < vc; ++k) {
+      vertices[k][2] = 0;
+      vertices[k + vc][2] = triangleGeometry.depth();
+    }
+#endif
+
+    for (size_t i = 0; i < vertices.N(); ++i)
+      gridFactories[idx].insertVertex(vertices[i]);
+
+    Dune::GeometryType cell;
+#if MY_DIM == 3
+    cell = Dune::GeometryTypes::tetrahedron;
+#else
+    cell = Dune::GeometryTypes::triangle;
+#endif
+
+#if MY_DIM == 3
+    SimplexManager sm(vc);
+#else
+    SimplexManager sm;
+#endif
+    sm.addFromVerticesFFB(1, 2, 0);
+    auto const &simplices = sm.getSimplices();
+
+    // sanity-check choices of simplices
+    for (size_t i = 0; i < simplices.size(); ++i) {
+      Dune::FieldMatrix<double, MY_DIM, MY_DIM> check;
+      for (size_t j = 0; j < MY_DIM; ++j)
+        check[j] = vertices[simplices[i][j + 1]] - vertices[simplices[i][j]];
+      assert(check.determinant() > 0);
+      gridFactories[idx].insertElement(cell, simplices[i]);
+    }
+
+    grids[idx] = std::shared_ptr<Grid>(gridFactories[idx].createGrid());
+    grids[idx]->setRefinementType(Grid::RefinementType::COPY);
+  }
+}
+
+template <class Grid>
+std::vector<std::shared_ptr<Grid>>& GridsConstructor<Grid>::getGrids() {
+  return grids;
+}
+
+template <class Grid>
+template <class GridView>
+MyFaces<GridView> GridsConstructor<Grid>::constructFaces(
+    GridView const &gridView, const TriangleGeometry<typename Grid::ctype>& triangleGeometry) {
+  return MyFaces<GridView>(gridView, triangleGeometry);
+}
+
+template <class GridView>
+template <class Vector>
+bool MyFaces<GridView>::xyCollinear(Vector const &a, Vector const &b,
+                                    Vector const &c) {
+  return isClose2((b[0] - a[0]) * (c[1] - a[1]), (b[1] - a[1]) * (c[0] - a[0]));
+}
+
+template <class GridView>
+template <class Vector>
+bool MyFaces<GridView>::xyBoxed(Vector const &v1, Vector const &v2,
+                                Vector const &x) {
+  auto const minmax0 = std::minmax(v1[0], v2[0]);
+  auto const minmax1 = std::minmax(v1[1], v2[1]);
+
+  if (minmax0.first - 1e-14 * triangleGeometry_.lengthScale() > x[0] or
+      x[0] > minmax0.second + 1e-14 * triangleGeometry_.lengthScale())
+    return false;
+  if (minmax1.first - 1e-14 * triangleGeometry_.lengthScale() > x[1] or
+      x[1] > minmax1.second + 1e-14 * triangleGeometry_.lengthScale())
+    return false;
+
+  return true;
+}
+
+template <class GridView>
+template <class Vector>
+bool MyFaces<GridView>::xyBetween(Vector const &v1, Vector const &v2,
+                                  Vector const &x) {
+  return xyCollinear(v1, v2, x) && xyBoxed(v1, v2, x);
+}
+
+template <class GridView>
+MyFaces<GridView>::MyFaces(GridView const &gridView, const TriangleGeometry<typename GridView::ctype>& triangleGeometry)
+      :
+  #if MY_DIM == 3
+        a(gridView),
+        b(gridView),
+        c(gridView),
+        top(gridView),
+        bottom(gridView),
+  #else
+        a(gridView),
+        b(gridView),
+        c(gridView),
+  #endif
+        triangleGeometry_(triangleGeometry)
+  {
+    a.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(triangleGeometry_.B(), triangleGeometry_.C(), in.geometry().center());
+    });
+
+    b.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(triangleGeometry_.A(), triangleGeometry_.C(), in.geometry().center());
+    });
+
+    c.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(triangleGeometry_.A(), triangleGeometry_.B(), in.geometry().center());
+    });
+
+  #if MY_DIM == 3
+    top.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return isClose(triangleGeometry_.depth(), in.geometry().center()[2]);
+    });
+
+    bottom.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return isClose(0.0, in.geometry().center()[2]);
+    });
+  #endif
+  }
+
+double computeAdmissibleDiameter(double distance, double smallestDiameter, double lengthScale) {
+  return (distance / 0.0125 / lengthScale + 1.0) * smallestDiameter;
+}
+
+template <class Grid, class LocalVector>
+void refine(Grid &grid, ConvexPolyhedron<LocalVector> const &weakPatch,
+            double smallestDiameter, double lengthScale) {
+  bool needRefine = true;
+  while (true) {
+    needRefine = false;
+    for (auto &&e : elements(grid.leafGridView())) {
+      auto const geometry = e.geometry();
+
+      auto const weakeningRegionDistance =
+          distance(weakPatch, geometry, 1e-6 * lengthScale);
+      auto const admissibleDiameter =
+          computeAdmissibleDiameter(weakeningRegionDistance, smallestDiameter, lengthScale);
+
+      if (diameter(geometry) <= admissibleDiameter)
+        continue;
+
+      needRefine = true;
+      grid.mark(1, e);
+    }
+    if (!needRefine)
+      break;
+
+    grid.preAdapt();
+    grid.adapt();
+    grid.postAdapt();
+  }
+}
+
+#include "trianglegrids_tmpl.cc"
diff --git a/dune/tectonic/problem-data/grid/trianglegrids.hh b/dune/tectonic/problem-data/grid/trianglegrids.hh
new file mode 100644
index 00000000..032cfa7d
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegrids.hh
@@ -0,0 +1,77 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_TRIANGLEGRIDS_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_TRIANGLEGRIDS_HH
+
+#include <dune/common/fmatrix.hh>
+#include <dune/grid/common/gridfactory.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+#include "trianglegeometry.hh"
+
+//            C
+//          / |
+//         /  |
+//      b /   | a
+//       /    |
+//      A --- B
+//         c
+
+template <class GridView> struct MyFaces {
+  BoundaryPatch<GridView> a;
+  BoundaryPatch<GridView> b;
+  BoundaryPatch<GridView> c;
+
+#if MY_DIM == 3
+  BoundaryPatch<GridView> top;
+  BoundaryPatch<GridView> bottom;
+#endif
+
+  MyFaces(GridView const &gridView, const TriangleGeometry<typename GridView::ctype>& triangleGeometry);
+
+private:
+  const TriangleGeometry<typename GridView::ctype>& triangleGeometry_;
+
+  bool isClose(double a, double b) {
+    return std::abs(a - b) < 1e-14 * triangleGeometry_.lengthScale();
+  }
+
+  bool isClose2(double a, double b) {
+    return std::abs(a - b) <
+           1e-14 * triangleGeometry_.lengthScale() * triangleGeometry_.lengthScale();
+  }
+
+  template <class Vector>
+  bool xyBoxed(Vector const &v1, Vector const &v2, Vector const &x);
+
+  template <class Vector>
+  bool xyCollinear(Vector const &a, Vector const &b, Vector const &c);
+
+  template <class Vector>
+  bool xyBetween(Vector const &v1, Vector const &v2, Vector const &x);
+};
+
+template <class Grid> class GridsConstructor {
+public:
+  GridsConstructor(const std::vector<std::shared_ptr<TriangleGeometry<typename Grid::ctype>>>& triangleGeometries);
+
+  std::vector<std::shared_ptr<Grid>>& getGrids();
+
+  template <class GridView>
+  MyFaces<GridView> constructFaces(const GridView& gridView, const TriangleGeometry<typename Grid::ctype>& triangleGeometry);
+
+private:
+  const std::vector<std::shared_ptr<TriangleGeometry<typename Grid::ctype>>>& triangleGeometries_;
+  std::vector<Dune::GridFactory<Grid>> gridFactories;
+  std::vector<std::shared_ptr<Grid>> grids;
+};
+
+double computeAdmissibleDiameter(double distance, double smallestDiameter, double lengthScale);
+
+template <class Grid, class LocalVector>
+void refine(Grid &grid, ConvexPolyhedron<LocalVector> const &weakPatch,
+            double smallestDiameter, double lengthScale);
+
+#endif
+
+
diff --git a/dune/tectonic/problem-data/grid/trianglegrids_tmpl.cc b/dune/tectonic/problem-data/grid/trianglegrids_tmpl.cc
new file mode 100644
index 00000000..d76f933c
--- /dev/null
+++ b/dune/tectonic/problem-data/grid/trianglegrids_tmpl.cc
@@ -0,0 +1,24 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "../../explicitgrid.hh"
+#include "../../explicitvectors.hh"
+#include "trianglegeometry.hh"
+
+template class GridsConstructor<Grid>;
+
+template struct MyFaces<DeformedGrid::LeafGridView>;
+template struct MyFaces<DeformedGrid::LevelGridView>;
+
+template MyFaces<DeformedGrid::LeafGridView> GridsConstructor<Grid>::constructFaces(
+    const DeformedGrid::LeafGridView&,
+    const TriangleGeometry<typename Grid::ctype>&);
+
+template MyFaces<DeformedGrid::LevelGridView> GridsConstructor<Grid>::constructFaces(
+    const DeformedGrid::LevelGridView&,
+    const TriangleGeometry<typename Grid::ctype>&);
+
+template void refine<Grid, LocalVector>(
+    Grid &grid, ConvexPolyhedron<LocalVector> const &weakPatch,
+    double smallestDiameter, double lengthScale);
diff --git a/dune/tectonic/problem-data/segmented-function.hh b/dune/tectonic/problem-data/segmented-function.hh
index 866415af..a3bc7b7d 100644
--- a/dune/tectonic/problem-data/segmented-function.hh
+++ b/dune/tectonic/problem-data/segmented-function.hh
@@ -15,10 +15,10 @@ class SegmentedFunction
                  Dune::FieldVector<double, MY_DIM> const &y,
                  Dune::FieldVector<double, MY_DIM> const &z) const {
     return x[1] + (z[0] - x[0]) * (y[1] - x[1]) / (y[0] - x[0]) >= z[1];
-  };
+  }
   bool insideRegion2(Dune::FieldVector<double, MY_DIM> const &z) const {
     return false; //TODO liesBelow(MyGeometry::K, MyGeometry::M, z);
-  };
+  }
 
   double const _v1;
   double const _v2;
diff --git a/dune/tectonic/problem-data/strikeslipbody.hh b/dune/tectonic/problem-data/strikeslipbody.hh
new file mode 100644
index 00000000..2e63a6af
--- /dev/null
+++ b/dune/tectonic/problem-data/strikeslipbody.hh
@@ -0,0 +1,89 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYBODYDATA_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_MYBODYDATA_HH
+
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+
+#include <dune/fufem/functions/constantfunction.hh>
+
+#include "../data-structures/body/bodydata.hh"
+#include "gravity.hh"
+#include "grid/cuboidgeometry.hh"
+
+
+class SegmentedFunction
+    : public Dune::VirtualFunction<Dune::FieldVector<double, MY_DIM>,
+                                   Dune::FieldVector<double, 1>> {
+private:
+  bool distFromDiagonal(const Dune::FieldVector<double, MY_DIM>& z) const {
+    return std::abs(z[0] - z[1])/std::sqrt(2);
+  }
+
+  bool insideViscousRegion(Dune::FieldVector<double, MY_DIM> const &z) const {
+    return distFromDiagonal(z) > distFromDiag_;
+  }
+
+  const double distFromDiag_;
+
+  const double v1_;
+  const double v2_;
+
+public:
+  SegmentedFunction(double v1, double v2, double distFromDiag = 0.1) :
+      distFromDiag_(distFromDiag) , v1_(v1), v2_(v2) {}
+
+  void evaluate(Dune::FieldVector<double, MY_DIM> const &x,
+                Dune::FieldVector<double, 1> &y) const {
+    y = insideViscousRegion(x) ? v2_ : v1_;
+  }
+};
+
+
+template <int dimension> class MyBodyData : public BodyData<dimension> {
+  using typename BodyData<dimension>::ScalarFunction;
+  using typename BodyData<dimension>::VectorField;
+
+public:
+  MyBodyData(Dune::ParameterTree const &parset, const double gravity, const Dune::FieldVector<double, dimension>& zenith)
+      : poissonRatio_(parset.get<double>("poissonRatio")),
+        youngModulus_(3.0 * parset.get<double>("bulkModulus") *
+                      (1.0 - 2.0 * poissonRatio_)),
+        zenith_(zenith),
+        shearViscosityField_(
+            parset.get<double>("elastic.shearViscosity"),
+            parset.get<double>("viscoelastic.shearViscosity"),
+            parset.get<double>("elastic.distFromDiag")),
+        bulkViscosityField_(
+            parset.get<double>("elastic.bulkViscosity"),
+            parset.get<double>("viscoelastic.bulkViscosity"),
+            parset.get<double>("elastic.distFromDiag")),
+        densityField_(parset.get<double>("elastic.density"),
+                      parset.get<double>("viscoelastic.density"),
+                      parset.get<double>("elastic.distFromDiag")),
+        gravityField_(densityField_, zenith_,
+                      gravity) {}
+
+  double getPoissonRatio() const override { return poissonRatio_; }
+  double getYoungModulus() const override { return youngModulus_; }
+  ScalarFunction const &getShearViscosityField() const override {
+    return shearViscosityField_;
+  }
+  ScalarFunction const &getBulkViscosityField() const override {
+    return bulkViscosityField_;
+  }
+  ScalarFunction const &getDensityField() const override {
+    return densityField_;
+  }
+  VectorField const &getGravityField() const override { return gravityField_; }
+
+private:
+  double const poissonRatio_;
+  double const youngModulus_;
+  Dune::FieldVector<double, dimension> zenith_;
+
+  SegmentedFunction const shearViscosityField_;
+  SegmentedFunction const bulkViscosityField_;
+  SegmentedFunction const densityField_;
+  Gravity<dimension> const gravityField_;
+};
+#endif
diff --git a/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc
index 303e1ea0..c077470e 100644
--- a/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc
+++ b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc
@@ -121,7 +121,7 @@ template <class field_type, class GridType0, class GridType1>
 void DualMortarCoupling<field_type, GridType0, GridType1>::assembleNonmortarLagrangeMatrix()
 {  
     // clear matrix
-    const auto nNonmortarVertices = nonmortarBoundary_.numVertices() - crosspoints_.size();
+    const auto nNonmortarVertices = nonmortarBoundary_.numVertices(); // - crosspoints_.size();
     nonmortarLagrangeMatrix_ = Dune::BDMatrix<MatrixBlock>(nNonmortarVertices);
     nonmortarLagrangeMatrix_ = 0;
 
@@ -184,7 +184,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
     hasObstacle_.resize(vertices.size());
     int idx = 0;
     for (size_t i=0; i<globalToLocal_.size(); i++) {
-        bool nonmortarVertex = vertices[i][0] and crosspoints_.count(i)==0;
+        bool nonmortarVertex = vertices[i][0]; // and crosspoints_.count(i)==0;
         globalToLocal_[i] = nonmortarVertex ? idx++ : -1;
         hasObstacle_[i][0] = nonmortarVertex ? true : false;
     }
@@ -193,7 +193,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
     assembleNonmortarLagrangeMatrix();
 
     // The weak obstacle vector
-    const auto nNonmortarVertices = nonmortarBoundary_.numVertices()-crosspoints_.size();
+    const auto nNonmortarVertices = nonmortarBoundary_.numVertices();// - crosspoints_.size();
     weakObstacle_.resize(nNonmortarVertices);
     weakObstacle_ = 0;
 
@@ -254,7 +254,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
     std::vector<Dune::FieldVector<ctype,dim> > avNormals;
     avNormals = nonmortarBoundary_.getNormals();
 
-    // loop over all intersections and compute the matrix entries
+    // loop over all intersections and compute the matrix entriescrosspoints_
     for (const auto& rIs : intersections(*glue_)) {
         const auto& inside = rIs.inside();
 
@@ -301,13 +301,15 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
           int faceIdxi = domainRefElement.subEntity(rIs.indexInInside(), 1, i, dim);
           int globalIdx = indexSet0.subIndex(inside,faceIdxi,dim);
 
-          if (crosspoints_.count(globalIdx)>0) {
+          /*if (crosspoints_.count(globalIdx)>0) {
             isCrosspointFace = true;
-          } else {
+          } else {*/
             nonmortarFaceNodes.push_back(faceIdxi);
-          }
+          //}
         }
 
+
+
         for (const auto& quadPt : quadRule) {
 
             // compute integration element of overlap
@@ -328,7 +330,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
             //nonmortarFiniteElement.localBasis().evaluateFunction(nonmortarQuadPos,nonmortarQuadValues);
             mortarFiniteElement.localBasis().evaluateFunction(mortarQuadPos,mortarQuadValues);
 
-            if (isCrosspointFace) {
+            /*if (isCrosspointFace) {
               constantDualFE.localBasis().evaluateFunction(nonmortarQuadPos,dualQuadValues);
 
               // loop over all Lagrange multiplier shape functions
@@ -355,7 +357,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
                   }
 
                }
-            } else {
+            } else {*/
               dualCache->evaluateFunction(nonmortarQuadPos,dualQuadValues);
 
               // loop over all Lagrange multiplier shape functions
@@ -382,7 +384,7 @@ void DualMortarCoupling<field_type, GridType0,GridType1>::setup()
                   }
 
                }
-            }
+            //}
         }
 
     }
diff --git a/dune/tectonic/spatial-solving/contact/nbodyassembler.cc b/dune/tectonic/spatial-solving/contact/nbodyassembler.cc
index 0c0d7ca9..75ede91c 100644
--- a/dune/tectonic/spatial-solving/contact/nbodyassembler.cc
+++ b/dune/tectonic/spatial-solving/contact/nbodyassembler.cc
@@ -15,7 +15,7 @@
 
 template <class GridType, class VectorType>
 void NBodyAssembler<GridType, VectorType>::setCrosspoints() {
-   /* std::vector<Dune::BitSetVector<1>> bodywiseBoundaries(nGrids());
+    /*std::vector<Dune::BitSetVector<1>> bodywiseBoundaries(nGrids());
     for (size_t i=0; i<nGrids(); i++) {
         bodywiseBoundaries[i] = *dirichletVertices_[i];
     }
@@ -57,9 +57,9 @@ void NBodyAssembler<GridType, VectorType>::setCrosspoints() {
     }
 
 
-    for (size_t i=0; i<crosspoints_.size(); i++) {
+    /*for (size_t i=0; i<crosspoints_.size(); i++) {
         print(crosspoints_[i], "crosspoints " + std::to_string(i));
-    }
+    }*/
 }
 
 template <class GridType, class VectorType>
@@ -94,18 +94,18 @@ void NBodyAssembler<GridType, VectorType>::assembleTransferOperator()
         contactCoupling_[i]->reducePatches();
     }
 
-    setCrosspoints();
+    //setCrosspoints();
 
-    for (int i=0; i<nGrids(); i++) {
+    /*for (int i=0; i<nGrids(); i++) {
         print(crosspoints_[i], "crosspoints " + std::to_string(i));
-    }
+    }*/
 
     for (size_t i=0; i<contactCoupling_.size(); i++) {
         contactCoupling_[i]->setCrosspoints(crosspoints_[coupling_[i].gridIdx_[0]]);
         contactCoupling_[i]->setup();
 
-        print(contactCoupling_[i]->nonmortarLagrangeMatrix() , "nonmortarLagrangeMatrix " + std::to_string(i) + ": ");
-                print(contactCoupling_[i]->mortarLagrangeMatrix() , "mortarLagrangeMatrix " + std::to_string(i) + ": ");
+        //print(contactCoupling_[i]->nonmortarLagrangeMatrix() , "nonmortarLagrangeMatrix " + std::to_string(i) + ": ");
+        //print(contactCoupling_[i]->mortarLagrangeMatrix() , "mortarLagrangeMatrix " + std::to_string(i) + ": ");
     }
 
     // setup Householder reflections
@@ -122,17 +122,35 @@ void NBodyAssembler<GridType, VectorType>::assembleHouseholderReflections()
     //   Compute local coordinate systems for the dofs with obstacles
     // //////////////////////////////////////////////////////////////////
 
-    std::vector<std::vector<MatrixBlock> > coordinateSystems(nCouplings());
+    // first canonical basis vector
+    using CoordinateType = Dune::FieldVector<field_type,dim>;
+    CoordinateType e0(0);
+    e0[0] = 1;
+
+    // local identity
+    Dune::ScaledIdentityMatrix<field_type,dim> id(1);
+
+    // bodywise local coordinate systems: initialise with identity
+    std::vector<std::vector<MatrixBlock> > coordinateSystems(nGrids());
+    for (size_t i=0; i<coordinateSystems.size(); i++) {
+        coordinateSystems[i].resize(grids_[i]->size(dim));
+        for (size_t j=0; j<coordinateSystems[i].size(); j++) {
+            coordinateSystems[i][j] = id;
+        }
+    }
+
+    std::vector<std::map<size_t, CoordinateType>> crosspointDirections(nGrids());
 
     for (int i=0; i<nCouplings(); i++) {
+        auto& coupling = coupling_[i];
 
-        double dist = coupling_[i].obsDistance_;
-        auto projection = coupling_[i].projection();
+        double dist = coupling.obsDistance_;
+        auto projection = coupling.projection();
 
         if (!projection)
             DUNE_THROW(Dune::Exception, "You have to supply a contact projection for the " << i << "-th coupling!");
 
-        std::vector<Dune::FieldVector<field_type,dim> > directions;
+        std::vector<CoordinateType> directions;
 
         const auto& nonmortarBoundary = contactCoupling_[i]->nonmortarBoundary();
         const auto& mortarBoundary    = contactCoupling_[i]->mortarBoundary();
@@ -140,7 +158,31 @@ void NBodyAssembler<GridType, VectorType>::assembleHouseholderReflections()
         projection->project(nonmortarBoundary, mortarBoundary,dist);
         projection->getObstacleDirections(directions);
 
-        this->computeLocalCoordinateSystems(nonmortarBoundary,coordinateSystems[i], directions);
+        const auto nonmortarIdx = coupling_[i].gridIdx_[0];
+        auto globalToLocal = nonmortarBoundary.makeGlobalToLocal();
+
+        for (size_t j=0; j<globalToLocal.size(); j++) {
+            if (globalToLocal[j] > -1) {
+                // There is an obstacle at this vertex --> the coordinate system
+                // will consist of the surface normal and tangential vectors
+
+                if (crosspoints_[nonmortarIdx].count(j)>0) {
+                    crosspointDirections[nonmortarIdx][j] += directions[globalToLocal[j]];
+                } else {
+                    this->householderReflection(e0, directions[globalToLocal[j]], coordinateSystems[nonmortarIdx][j]);
+                }
+            }
+        }
+    }
+
+    // local coordinate systems at crosspoints
+    for (size_t i=0; i<crosspoints_.size(); i++) {
+        for (auto c : crosspoints_[i]) {
+            auto& direction = crosspointDirections[i][c];
+            direction /= direction.two_norm();
+
+            this->householderReflection(e0, direction, coordinateSystems[i][c]);
+        }
     }
 
     // ///////////////////////////////////////////////////////////////
@@ -148,28 +190,17 @@ void NBodyAssembler<GridType, VectorType>::assembleHouseholderReflections()
     // ///////////////////////////////////////////////////////////////
     this->localCoordSystems_.resize(numDofs());
 
-    // initialise with identity
-    Dune::ScaledIdentityMatrix<field_type,dim> id(1);
-    for (size_t i=0; i<this->localCoordSystems_.size(); i++)
-        this->localCoordSystems_[i] = id;
-
-    for (int j=0; j<nCouplings(); j++) {
-        int grid0Idx = coupling_[j].gridIdx_[0];
-
-        // compute offset
-        int idx = 0;
-        for (int k=0;k<grid0Idx;k++)
-            idx += grids_[k]->size(dim);
-
-        const auto& globalToLocal = contactCoupling_[j]->globalToLocal();
+    size_t offSet = 0;
+    for (size_t i=0; i<nGrids(); i++) {
+        for (std::size_t k=0; k<coordinateSystems[i].size(); k++) {
+            this->localCoordSystems_[offSet+k] = coordinateSystems[i][k];
+        }
 
-        // if a body is non-mortar more than once, one has to be careful with not overwriting entries
-        for (std::size_t k=0; k<coordinateSystems[j].size(); k++)
-          if(globalToLocal[k] > -1 ) //or crosspoints.count(k) > 0)
-            this->localCoordSystems_[idx+k] = coordinateSystems[j][k];
+        offSet += grids_[i]->size(dim);
     }
 
     //print(this->localCoordSystems_, "localCoordSystems:");
+    //std::cout << "done" << std::endl;
 }
 
 template <class GridType, class VectorType>
@@ -239,7 +270,7 @@ void NBodyAssembler<GridType, VectorType>::assembleObstacle()
         for (const auto& v : vertices(leafView)) {
 
             int vIdx = indexSet.index(v);
-            if (globalToLocal[vIdx]<0)
+            if (globalToLocal[vIdx]<0) // and crosspoints_[grid0Idx].count(vIdx)<1)
                 continue;
 
             // Set the obstacle
@@ -252,6 +283,12 @@ void NBodyAssembler<GridType, VectorType>::assembleObstacle()
             case Dune::Contact::CouplingPairBase::STICK_SLIP:
                 totalObstacles_[idx+vIdx].lower(0) = 0;
                 totalObstacles_[idx+vIdx].upper(0) = 0;
+
+                /*if (crosspoints_[grid0Idx].count(vIdx) > 0) {
+                    totalObstacles_[idx+vIdx].lower(1) = 0;
+                    totalObstacles_[idx+vIdx].upper(1) = 0;
+                }*/
+
                 break;
 
             case Dune::Contact::CouplingPairBase::GLUE:
diff --git a/dune/tectonic/spatial-solving/fixedpointiterator.cc b/dune/tectonic/spatial-solving/fixedpointiterator.cc
index 39610f48..0a9ad5c1 100644
--- a/dune/tectonic/spatial-solving/fixedpointiterator.cc
+++ b/dune/tectonic/spatial-solving/fixedpointiterator.cc
@@ -39,6 +39,8 @@
 #include "tnnmg/functional.hh"
 #include "tnnmg/zerononlinearity.hh"
 
+#include <dune/tectonic/utils/reductionfactors.hh>
+
 #include "fixedpointiterator.hh"
 
 void FixedPointIterationCounter::operator+=(
@@ -225,7 +227,7 @@ FixedPointIterator<Factory, NBodyAssembler, Updaters, ErrorNorms>::run(
 
     // extract relative velocities in mortar basis
     std::vector<Vector> v_rel;
-    relativeVelocities(v_m, v_rel);
+    split(v_m, v_rel);
 
     //print(v_m, "v_m: ");
 
@@ -292,25 +294,21 @@ FixedPointIterator<Factory, NBodyAssembler, Updaters, ErrorNorms>::run(
 }*/
 
 template <class Factory, class NBodyAssembler, class Updaters, class ErrorNorms>
-void FixedPointIterator<Factory, NBodyAssembler, Updaters, ErrorNorms>::relativeVelocities(
+void FixedPointIterator<Factory, NBodyAssembler, Updaters, ErrorNorms>::split(
     const Vector& v,
-    std::vector<Vector>& v_rel) const {
+    std::vector<Vector>& splitV) const {
 
     const size_t nBodies = nBodyAssembler_.nGrids();
-   // const auto& contactCouplings = nBodyAssembler.getContactCouplings();
 
     size_t globalIdx = 0;
-    v_rel.resize(nBodies);
+    splitV.resize(nBodies);
     for (size_t bodyIdx=0; bodyIdx<nBodies; bodyIdx++) {
-        const auto& nonmortarBoundary = *bodywiseNonmortarBoundaries_[bodyIdx];
-        auto& v_rel_ = v_rel[bodyIdx];
+        auto& splitV_ = splitV[bodyIdx];
 
-        v_rel_.resize(nonmortarBoundary.size());
+        splitV_.resize(nBodyAssembler_.grids_[bodyIdx]->size(dims));
 
-        for (size_t i=0; i<v_rel_.size(); i++) {
-            if (toBool(nonmortarBoundary[i])) {
-                v_rel_[i] = v[globalIdx];
-            }
+        for (size_t i=0; i<splitV_.size(); i++) {
+            splitV_[i] = v[globalIdx];
             globalIdx++;
         }
     }
diff --git a/dune/tectonic/spatial-solving/fixedpointiterator.hh b/dune/tectonic/spatial-solving/fixedpointiterator.hh
index ae0912ad..183f2f8d 100644
--- a/dune/tectonic/spatial-solving/fixedpointiterator.hh
+++ b/dune/tectonic/spatial-solving/fixedpointiterator.hh
@@ -47,7 +47,7 @@ class FixedPointIterator {
   using BitVector = Dune::BitSetVector<1>;
 
 private:
-  void relativeVelocities(const Vector& v, std::vector<Vector>& v_rel) const;
+  void split(const Vector& v, std::vector<Vector>& v_rel) const;
 
 public:
   FixedPointIterator(const Dune::ParameterTree& parset,
diff --git a/dune/tectonic/spatial-solving/tnnmg/linearization.hh b/dune/tectonic/spatial-solving/tnnmg/linearization.hh
index 556987c1..8abd074b 100644
--- a/dune/tectonic/spatial-solving/tnnmg/linearization.hh
+++ b/dune/tectonic/spatial-solving/tnnmg/linearization.hh
@@ -160,7 +160,7 @@ class Linearization {
       //print(hessian_, "Hessian: ");
       //std::cout << "--------" << (double) hessian_[21][21] << "------------" << std::endl;
 
-      auto B = hessian_;
+      //auto B = hessian_;
 
       //print(x, "x: ");
 
@@ -168,7 +168,7 @@ class Linearization {
       phi.addHessian(x, hessian_);
       //truncateMatrix(hessian_, regularityTruncationFlags, regularityTruncationFlags);
 
-      B -= hessian_;
+      //B -= hessian_;
       //print(B, "phi hessian: ");
 
       // compute gradient
diff --git a/dune/tectonic/tests/CMakeLists.txt b/dune/tectonic/tests/CMakeLists.txt
index ef87c47f..69e6b8f1 100644
--- a/dune/tectonic/tests/CMakeLists.txt
+++ b/dune/tectonic/tests/CMakeLists.txt
@@ -1,3 +1,6 @@
+add_subdirectory("contacttest")
+add_subdirectory("hdf5test")
+
 dune_add_test(SOURCES globalfrictioncontainertest.cc)
 dune_add_test(SOURCES gridgluefrictiontest.cc)
 dune_add_test(SOURCES nodalweightstest.cc)
diff --git a/dune/tectonic/tests/contacttest/CMakeLists.txt b/dune/tectonic/tests/contacttest/CMakeLists.txt
new file mode 100644
index 00000000..fc6644b8
--- /dev/null
+++ b/dune/tectonic/tests/contacttest/CMakeLists.txt
@@ -0,0 +1,33 @@
+add_custom_target(tectonic_tests_contacttest SOURCES
+  staticcontacttest.cfg
+  staticcontacttest-2D.cfg
+  staticcontacttest-3D.cfg
+)
+
+set(CONTACTTEST_SOURCE_FILES
+  ../../assemblers.cc
+  ../../data-structures/body/body.cc
+  ../../data-structures/network/levelcontactnetwork.cc
+  ../../data-structures/network/contactnetwork.cc
+  ../../data-structures/enumparser.cc
+  ../../factories/twoblocksfactory.cc
+  ../../factories/threeblocksfactory.cc
+  ../../factories/stackedblocksfactory.cc
+  ../../io/vtk.cc
+  ../../problem-data/grid/cuboidgeometry.cc
+  ../../problem-data/grid/mygrids.cc
+  ../../problem-data/grid/simplexmanager.cc
+  ../../spatial-solving/solverfactory.cc
+  staticcontacttest.cc
+)
+
+foreach(_dim 2 3)
+  set(_contacttest_target staticcontacttest-${_dim}D)
+
+  dune_add_test(NAME ${_contacttest_target} SOURCES ${CONTACTTEST_SOURCE_FILES})
+
+  add_dune_ug_flags(${_contacttest_target})
+  add_dune_hdf5_flags(${_contacttest_target})
+
+  set_property(TARGET ${_contacttest_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
+endforeach()
diff --git a/dune/tectonic/tests/contacttest/staticcontacttest-2D.cfg b/dune/tectonic/tests/contacttest/staticcontacttest-2D.cfg
new file mode 100644
index 00000000..81b43367
--- /dev/null
+++ b/dune/tectonic/tests/contacttest/staticcontacttest-2D.cfg
@@ -0,0 +1,12 @@
+# -*- mode:conf -*-
+[boundary.friction]
+smallestDiameter = 0.3  # 2e-3 [m]
+
+[u0.solver]
+tolerance         = 1e-8
+
+[solver.tnnmg.preconditioner.basesolver]
+tolerance          = 1e-10
+
+[solver.tnnmg.preconditioner.patchsolver]
+tolerance          = 1e-10
diff --git a/dune/tectonic/tests/contacttest/staticcontacttest-3D.cfg b/dune/tectonic/tests/contacttest/staticcontacttest-3D.cfg
new file mode 100644
index 00000000..2e0af675
--- /dev/null
+++ b/dune/tectonic/tests/contacttest/staticcontacttest-3D.cfg
@@ -0,0 +1,15 @@
+# -*- mode:conf -*-
+[body0]
+smallestDiameter = 0.005  # 2e-3 [m]
+
+[body1]
+smallestDiameter = 0.005  # 2e-3 [m]
+
+[u0.solver]
+tolerance         = 1e-8
+
+[solver.tnnmg.preconditioner.basesolver]
+tolerance          = 1e-10
+
+[solver.tnnmg.preconditioner.patchsolver]
+tolerance          = 1e-10
diff --git a/dune/tectonic/tests/contacttest/staticcontacttest.cc b/dune/tectonic/tests/contacttest/staticcontacttest.cc
new file mode 100644
index 00000000..c074fe1a
--- /dev/null
+++ b/dune/tectonic/tests/contacttest/staticcontacttest.cc
@@ -0,0 +1,313 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_IPOPT
+#undef HAVE_IPOPT
+#endif
+
+#define MY_DIM 2
+
+#include <atomic>
+#include <cmath>
+#include <csignal>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <memory>
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/exceptions.hh>
+#include <dune/common/fmatrix.hh>
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+
+#include <dune/grid/common/mcmgmapper.hh>
+#include <dune/istl/bcrsmatrix.hh>
+#include <dune/istl/bvector.hh>
+
+
+#include <dune/fufem/formatstring.hh>
+
+#include <dune/solvers/norms/energynorm.hh>
+#include <dune/solvers/solvers/loopsolver.hh>
+#include <dune/solvers/iterationsteps/blockgssteps.hh>
+#include <dune/solvers/iterationsteps/truncatedblockgsstep.hh>
+#include <dune/solvers/iterationsteps/multigridstep.hh>
+
+#include <dune/tectonic/assemblers.hh>
+#include <dune/tectonic/gridselector.hh>
+#include <dune/tectonic/explicitgrid.hh>
+#include <dune/tectonic/explicitvectors.hh>
+
+#include <dune/tectonic/data-structures/enumparser.hh>
+#include <dune/tectonic/data-structures/enums.hh>
+#include <dune/tectonic/data-structures/network/contactnetwork.hh>
+#include <dune/tectonic/data-structures/matrices.hh>
+
+#include <dune/tectonic/factories/twoblocksfactory.hh>
+#include <dune/tectonic/factories/threeblocksfactory.hh>
+#include <dune/tectonic/factories/stackedblocksfactory.hh>
+
+#include <dune/tectonic/io/vtk.hh>
+
+#include <dune/tectonic/spatial-solving/tnnmg/functional.hh>
+#include <dune/tectonic/spatial-solving/tnnmg/zerononlinearity.hh>
+#include <dune/tectonic/spatial-solving/tnnmg/localbisectionsolver.hh>
+#include <dune/tectonic/spatial-solving/solverfactory.hh>
+#include <dune/tectonic/spatial-solving/contact/nbodycontacttransfer.hh>
+
+#include <dune/tectonic/utils/debugutils.hh>
+
+// for getcwd
+#include <unistd.h>
+
+size_t const dims = MY_DIM;
+
+Dune::ParameterTree getParameters(int argc, char *argv[]) {
+  Dune::ParameterTree parset;
+  Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/dune/tectonic/tests/contacttest/staticcontacttest.cfg", parset);
+  Dune::ParameterTreeParser::readINITree(
+      Dune::Fufem::formatString("/home/mi/podlesny/software/dune/dune-tectonic/dune/tectonic/tests/contacttest/staticcontacttest-%dD.cfg", dims), parset);
+  Dune::ParameterTreeParser::readOptions(argc, argv, parset);
+  return parset;
+}
+
+int main(int argc, char *argv[]) {
+  try {
+    Dune::MPIHelper::instance(argc, argv);
+
+    char buffer[256];
+    char *val = getcwd(buffer, sizeof(buffer));
+    if (val) {
+        std::cout << buffer << std::endl;
+        std::cout << argv[0] << std::endl;
+    }
+
+    std::ofstream out("staticcontacttest.log");
+    std::streambuf *coutbuf = std::cout.rdbuf(); //save old buffer
+    std::cout.rdbuf(out.rdbuf()); //redirect std::cout to log.txt
+
+    auto const parset = getParameters(argc, argv);
+
+    // ----------------------
+    // set up contact network
+    // ----------------------
+    using BlocksFactory = StackedBlocksFactory<Grid, Vector>;
+    BlocksFactory blocksFactory(parset);
+
+    using ContactNetwork = typename BlocksFactory::ContactNetwork;
+    blocksFactory.build();
+
+    auto& contactNetwork = blocksFactory.contactNetwork();
+
+    const size_t bodyCount = contactNetwork.nBodies();
+
+   /* for (size_t i=0; i<contactNetwork.nLevels(); i++) {
+        // printDofLocation(contactNetwork.body(i)->gridView());
+
+        //Vector def(contactNetwork.deformedGrids()[i]->size(dims));
+        //def = 1;
+        //deformedGridComplex.setDeformation(def, i);
+
+        const auto& level = *contactNetwork.level(i);
+
+        for (size_t j=0; j<level.nBodies(); j++) {
+            writeToVTK(level.body(j)->gridView(), "../debug_print/bodies/", "body_" + std::to_string(j) + "_level_" + std::to_string(i));
+        }
+    }*/
+
+    for (size_t i=0; i<bodyCount; i++) {
+        writeToVTK(contactNetwork.body(i)->gridView(), "", "initial_body_" + std::to_string(i) + "_leaf");
+    }
+
+    // ----------------------------
+    // assemble contactNetwork
+    // ----------------------------
+    contactNetwork.assemble();
+
+    //printMortarBasis<Vector>(contactNetwork.nBodyAssembler());
+
+
+    // ----------------------------
+    // compute minimal stress solution
+    // ----------------------------
+    using Matrix = typename ContactNetwork::Matrix;
+    const auto& nBodyAssembler = contactNetwork.nBodyAssembler();
+
+    // Initial displacement: Start from a situation of minimal stress,
+    // which is automatically attained in the case [v = 0 = a].
+    // Assuming dPhi(v = 0) = 0, we thus only have to solve Au = ell0
+    BitVector dirichletNodes;
+    contactNetwork.totalNodes("dirichlet", dirichletNodes);
+
+    // minimal stress displacement
+    std::vector<Vector> u(bodyCount);
+    for (size_t i=0; i<bodyCount; i++) {
+        u[i].resize(contactNetwork.body(i)->nVertices());
+        u[i] = 0.0;
+    }
+
+    // set rhs
+    std::vector<Vector> ell0(bodyCount);
+    for (size_t i=0; i<bodyCount; i++) {
+        ell0[i].resize(u[i].size());
+        ell0[i] = 0.0;
+
+        contactNetwork.body(i)->externalForce()(0.0, ell0[i]);
+    }
+
+    // set elasticity operator
+    const auto& matrices = contactNetwork.matrices().elasticity;
+    std::vector<const Matrix*> matrices_ptr(matrices.size());
+    for (size_t i=0; i<matrices_ptr.size(); i++) {
+        matrices_ptr[i] = matrices[i].get();
+    }
+
+    // assemble full global contact problem
+    Matrix bilinearForm;
+
+    nBodyAssembler.assembleJacobian(matrices_ptr, bilinearForm);
+
+    Vector totalRhs;
+    nBodyAssembler.assembleRightHandSide(ell0, totalRhs);
+
+    Vector totalX;
+    nBodyAssembler.nodalToTransformed(u, totalX);
+
+    // get lower and upper obstacles
+    const auto& totalObstacles = nBodyAssembler.totalObstacles_;
+    Vector lower(totalObstacles.size());
+    Vector upper(totalObstacles.size());
+
+    for (size_t j=0; j<totalObstacles.size(); ++j) {
+        const auto& totalObstaclesj = totalObstacles[j];
+        auto& lowerj = lower[j];
+        auto& upperj = upper[j];
+        for (size_t d=0; d<dims; ++d) {
+            lowerj[d] = totalObstaclesj[d][0];
+            upperj[d] = totalObstaclesj[d][1];
+        }
+    }
+
+    // print problem
+    print(bilinearForm, "bilinearForm");
+    print(totalRhs, "totalRhs");
+    print(dirichletNodes, "ignore");
+
+    std::vector<const Dune::BitSetVector<1>*> frictionNodes;
+    contactNetwork.frictionNodes(frictionNodes);
+
+    for (size_t i=0; i<frictionNodes.size(); i++) {
+        print(*frictionNodes[i], "frictionNodes_body_" + std::to_string(i));
+    }
+
+    print(totalObstacles, "totalObstacles");
+    print(lower, "lower");
+    print(upper, "upper");
+
+    // set up functional
+    using Functional = Functional<Matrix&, Vector&, ZeroNonlinearity&, Vector&, Vector&, typename Matrix::field_type>;
+    Functional J(bilinearForm, totalRhs, ZeroNonlinearity(), lower, upper); //TODO
+
+       /* std::vector<BitVector> bodyDirichletNodes;
+        nBodyAssembler.postprocess(dirichletNodes, bodyDirichletNodes);
+        for (size_t i=0; i<bodyDirichletNodes.size(); i++) {
+          print(bodyDirichletNodes[i], "bodyDirichletNodes_" + std::to_string(i) + ": ");
+        }*/
+
+       /* print(bilinearForm, "matrix: ");
+        print(totalX, "totalX: ");
+        print(totalRhs, "totalRhs: ");*/
+
+    // make linear solver for linear correction in TNNMGStep
+    const auto& solverParset = parset.sub("u0.solver");
+
+    using Norm =  EnergyNorm<Matrix, Vector>;
+    using LinearSolver = typename Dune::Solvers::LoopSolver<Vector>;
+
+    // set multigrid solver
+    auto smoother = TruncatedBlockGSStep<Matrix, Vector>();
+
+    using TransferOperator = NBodyContactTransfer<ContactNetwork, Vector>;
+    using TransferOperators = std::vector<std::shared_ptr<TransferOperator>>;
+
+    TransferOperators transfer(contactNetwork.nLevels()-1);
+    for (size_t i=0; i<transfer.size(); i++) {
+        transfer[i] = std::make_shared<TransferOperator>();
+        transfer[i]->setup(contactNetwork, i, i+1);
+    }
+
+    // Remove any recompute filed so that initially the full transferoperator is assembled
+    for (size_t i=0; i<transfer.size(); i++)
+        std::dynamic_pointer_cast<TruncatedMGTransfer<Vector> >(transfer[i])->setRecomputeBitField(nullptr);
+
+    auto linearMultigridStep = std::make_shared<Dune::Solvers::MultigridStep<Matrix, Vector> >();
+    linearMultigridStep->setMGType(1, 3, 3);
+    linearMultigridStep->setSmoother(smoother);
+    linearMultigridStep->setTransferOperators(transfer);
+
+    Norm norm(*linearMultigridStep);
+
+    auto linearSolver = std::make_shared<LinearSolver>(linearMultigridStep, parset.get<int>("solver.tnnmg.main.multi"), parset.get<double>("solver.tnnmg.preconditioner.basesolver.tolerance"), norm, Solver::QUIET);
+
+    // set up TNMMG solver
+    using Factory = SolverFactory<Functional, BitVector>;
+    Factory factory(parset.sub("solver.tnnmg"), J, dirichletNodes);
+
+    factory.build(linearSolver);
+    auto tnnmgStep = factory.step();
+    factory.setProblem(totalX);
+
+    //const EnergyNorm<Matrix, Vector> norm(bilinearForm);
+
+    std::cout << "solving linear problem for u..." << std::endl;
+    LoopSolver<Vector> solver(
+            *tnnmgStep.get(), solverParset.get<size_t>("maximumIterations"),
+            solverParset.get<double>("tolerance"), norm,
+            solverParset.get<Solver::VerbosityMode>("verbosity")); // absolute error
+
+    solver.preprocess();
+    solver.solve();
+
+    nBodyAssembler.postprocess(tnnmgStep->getSol(), u);
+
+    print(tnnmgStep->getSol(), "totalX:");
+    print(u, "u:");
+
+    for (size_t i=0; i<bodyCount; i++) {
+      auto& body = contactNetwork.body(i);
+      body->setDeformation(u[i]);
+      writeToVTK(body->gridView(), "", "body_" + std::to_string(i) + "_leaf");
+    }
+
+    /*
+    using BoundaryFunctions = typename ContactNetwork::BoundaryFunctions;
+    using BoundaryNodes = typename ContactNetwork::BoundaryNodes;
+    using Updaters = Updaters<RateUpdater<Vector, Matrix, BoundaryFunctions, BoundaryNodes>,
+                               StateUpdater<ScalarVector, Vector>>;
+
+    BoundaryFunctions velocityDirichletFunctions;
+    contactNetwork.boundaryFunctions("dirichlet", velocityDirichletFunctions);
+
+    BoundaryNodes dirichletNodes;
+    contactNetwork.boundaryNodes("dirichlet", dirichletNodes);
+
+    for (size_t i=0; i<dirichletNodes.size(); i++) {
+        for (size_t j=0; j<dirichletNodes[i].size(); j++) {
+        print(*dirichletNodes[i][j], "dirichletNodes_body_" + std::to_string(i) + "_boundary_" + std::to_string(j));
+        }
+    }*/
+
+    std::cout.rdbuf(coutbuf); //reset to standard output again
+
+  } catch (Dune::Exception &e) {
+    Dune::derr << "Dune reported error: " << e << std::endl;
+  } catch (std::exception &e) {
+    std::cerr << "Standard exception: " << e.what() << std::endl;
+  }
+}
diff --git a/dune/tectonic/tests/contacttest/staticcontacttest.cfg b/dune/tectonic/tests/contacttest/staticcontacttest.cfg
new file mode 100644
index 00000000..f16fa5be
--- /dev/null
+++ b/dune/tectonic/tests/contacttest/staticcontacttest.cfg
@@ -0,0 +1,65 @@
+# -*- mode:conf -*-
+gravity         = 9.81     # [m/s^2]
+
+[body]
+length          = 0.4      # [m]
+height          = 0.04     # [m]
+depth           = 0.04     # [m]
+bulkModulus     = 2190    # [Pa] #2190
+poissonRatio    = 0.11     # [1]  #0.11
+[body.elastic]
+density         = 750      # [kg/m^3] #750
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+[body.viscoelastic]
+density         = 1000     # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+
+[boundary.friction]
+C               = 6       # [Pa]
+mu0             = 0.48      # [ ]
+V0              = 1e-3     # [m/s]
+L               = 1e-6  # [m]
+initialAlpha    = 0        # [ ]
+stateModel      = AgeingLaw
+frictionModel   = Truncated #Regularised
+[boundary.friction.weakening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+[boundary.friction.strengthening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+
+[boundary.neumann]
+sigmaN          = 0.0      # [Pa]
+
+[boundary.dirichlet]
+finalVelocity   = 5e-5     # [m/s]
+
+[problem]
+bodyCount       = 2
+
+[u0.solver]
+maximumIterations = 100
+verbosity         = full
+
+[solver.tnnmg.preconditioner]
+mode         = additive
+patchDepth   = 1
+maximumIterations = 2
+verbosity         = quiet
+[solver.tnnmg.preconditioner.patchsolver]
+maximumIterations = 100
+verbosity         = quiet
+[solver.tnnmg.preconditioner.basesolver]
+maximumIterations = 10000
+verbosity         = quiet
+
+[solver.tnnmg.main]
+pre   = 1
+multi = 5 # number of multigrid steps
+post  = 0
+
+
+
diff --git a/dune/tectonic/tests/hdf5test/CMakeLists.txt b/dune/tectonic/tests/hdf5test/CMakeLists.txt
new file mode 100644
index 00000000..27378cc4
--- /dev/null
+++ b/dune/tectonic/tests/hdf5test/CMakeLists.txt
@@ -0,0 +1,33 @@
+add_custom_target(tectonic_tests_hdf5test SOURCES
+  hdf5test.cfg
+  hdf5test-2D.cfg
+)
+
+set(HDF5TEST_SOURCE_FILES
+  ../../assemblers.cc
+  ../../data-structures/body/body.cc
+  ../../data-structures/network/levelcontactnetwork.cc
+  ../../data-structures/network/contactnetwork.cc
+  ../../data-structures/enumparser.cc
+  ../../factories/strikeslipfactory.cc
+  ../../io/vtk.cc
+  ../../io/hdf5/frictionalboundary-writer.cc
+  ../../io/hdf5/iteration-writer.cc
+  ../../io/hdf5/time-writer.cc
+  ../../problem-data/grid/simplexmanager.cc
+  ../../problem-data/grid/trianglegeometry.cc
+  ../../problem-data/grid/trianglegrids.cc
+  ../../spatial-solving/solverfactory.cc
+  hdf5test.cc
+)
+
+foreach(_dim 2)
+  set(_hdf5test_target hdf5test-${_dim}D)
+
+  dune_add_test(NAME ${_hdf5test_target} SOURCES ${HDF5TEST_SOURCE_FILES})
+
+  add_dune_ug_flags(${_hdf5test_target})
+  add_dune_hdf5_flags(${_hdf5test_target})
+
+  set_property(TARGET ${_hdf5test_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
+endforeach()
diff --git a/dune/tectonic/tests/hdf5test/hdf5test-2D.cfg b/dune/tectonic/tests/hdf5test/hdf5test-2D.cfg
new file mode 100644
index 00000000..f212cdea
--- /dev/null
+++ b/dune/tectonic/tests/hdf5test/hdf5test-2D.cfg
@@ -0,0 +1,27 @@
+# -*- mode:conf -*-
+[body0]
+smallestDiameter = 0.3  # 2e-3 [m]
+
+[body1]
+smallestDiameter = 0.3 # 2e-3 [m]
+
+[timeSteps]
+refinementTolerance = 1e-5 # 1e-5
+
+[u0.solver]
+tolerance         = 1e-8
+
+[a0.solver]
+tolerance         = 1e-8
+
+[v.solver]
+tolerance         = 1e-8
+
+[v.fpi]
+tolerance         = 1e-5
+
+[solver.tnnmg.preconditioner.basesolver]
+tolerance          = 1e-10
+
+[solver.tnnmg.preconditioner.patchsolver]
+tolerance          = 1e-10
diff --git a/dune/tectonic/tests/hdf5test/hdf5test.cc b/dune/tectonic/tests/hdf5test/hdf5test.cc
new file mode 100644
index 00000000..4953ae6e
--- /dev/null
+++ b/dune/tectonic/tests/hdf5test/hdf5test.cc
@@ -0,0 +1,202 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_IPOPT
+#undef HAVE_IPOPT
+#endif
+
+#define MY_DIM 2
+
+#include <atomic>
+#include <cmath>
+#include <csignal>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <memory>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+
+#include <dune/fufem/formatstring.hh>
+#include <dune/fufem/hdf5/file.hh>
+
+#include <dune/tectonic/assemblers.hh>
+#include <dune/tectonic/gridselector.hh>
+#include <dune/tectonic/explicitgrid.hh>
+#include <dune/tectonic/explicitvectors.hh>
+
+#include <dune/tectonic/data-structures/enumparser.hh>
+#include <dune/tectonic/data-structures/enums.hh>
+#include <dune/tectonic/data-structures/network/contactnetwork.hh>
+#include <dune/tectonic/data-structures/program_state.hh>
+
+#include <dune/tectonic/factories/strikeslipfactory.hh>
+
+#include <dune/tectonic/io/vtk.hh>
+#include <dune/tectonic/io/hdf5-writer.hh>
+
+#include <dune/tectonic/utils/geocoordinate.hh>
+#include <dune/tectonic/utils/debugutils.hh>
+
+// for getcwd
+#include <unistd.h>
+
+size_t const dims = MY_DIM;
+
+Dune::ParameterTree getParameters(int argc, char *argv[]) {
+  Dune::ParameterTree parset;
+  Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/dune/tectonic/tests/hdf5test/hdf5test.cfg", parset);
+  Dune::ParameterTreeParser::readINITree(
+      Dune::Fufem::formatString("/home/mi/podlesny/software/dune/dune-tectonic/dune/tectonic/tests/hdf5test/hdf5test-%dD.cfg", dims), parset);
+  Dune::ParameterTreeParser::readOptions(argc, argv, parset);
+  return parset;
+}
+
+int main(int argc, char *argv[]) {
+  try {
+    Dune::MPIHelper::instance(argc, argv);
+
+    char buffer[256];
+    char *val = getcwd(buffer, sizeof(buffer));
+    if (val) {
+        std::cout << buffer << std::endl;
+        std::cout << argv[0] << std::endl;
+    }
+
+    std::ofstream out("hdf5test.log");
+    std::streambuf *coutbuf = std::cout.rdbuf(); //save old buffer
+    std::cout.rdbuf(out.rdbuf()); //redirect std::cout to log.txt
+
+    auto const parset = getParameters(argc, argv);
+
+    // ----------------------
+    // set up contact network
+    // ----------------------
+    using BlocksFactory = StrikeSlipFactory<Grid, Vector>;
+    BlocksFactory blocksFactory(parset);
+
+    using ContactNetwork = typename BlocksFactory::ContactNetwork;
+    blocksFactory.build();
+
+    auto& contactNetwork = blocksFactory.contactNetwork();
+
+    const size_t bodyCount = contactNetwork.nBodies();
+
+   /* for (size_t i=0; i<contactNetwork.nLevels(); i++) {
+        // printDofLocation(contactNetwork.body(i)->gridView());
+
+        //Vector def(contactNetwork.deformedGrids()[i]->size(dims));
+        //def = 1;
+        //deformedGridComplex.setDeformation(def, i);
+
+        const auto& level = *contactNetwork.level(i);
+
+        for (size_t j=0; j<level.nBodies(); j++) {
+            writeToVTK(level.body(j)->gridView(), "../debug_print/bodies/", "body_" + std::to_string(j) + "_level_" + std::to_string(i));
+        }
+    }*/
+
+    for (size_t i=0; i<bodyCount; i++) {
+        writeToVTK(contactNetwork.body(i)->gridView(), "", "initial_body_" + std::to_string(i) + "_leaf");
+    }
+
+    // ----------------------------
+    // assemble contactNetwork
+    // ----------------------------
+    contactNetwork.assemble();
+
+    //printMortarBasis<Vector>(contactNetwork.nBodyAssembler());
+
+    std::vector<size_t> nVertices(bodyCount);
+    for (size_t i=0; i<bodyCount; i++) {
+        nVertices[i] = contactNetwork.body(i)->nVertices();
+    }
+
+    using MyProgramState = ProgramState<Vector, ScalarVector>;
+    MyProgramState programState(nVertices);
+    programState.setupInitialConditions(parset, contactNetwork);
+
+    auto& nBodyAssembler = contactNetwork.nBodyAssembler();
+    for (size_t i=0; i<bodyCount; i++) {
+      contactNetwork.body(i)->setDeformation(programState.u[i]);
+    }
+    nBodyAssembler.assembleTransferOperator();
+    nBodyAssembler.assembleObstacle();
+
+    // ------------------------
+    // assemble global friction
+    // ------------------------
+    contactNetwork.assembleFriction(parset.get<Config::FrictionModel>("boundary.friction.frictionModel"), programState.weightedNormalStress);
+
+    auto& globalFriction = contactNetwork.globalFriction();
+    globalFriction.updateAlpha(programState.alpha);
+
+    // -----------------
+    // init input/output
+    // -----------------
+    using Assembler = MyAssembler<DefLeafGridView, dims>;
+    using MyVertexBasis = typename Assembler::VertexBasis;
+    using MyCellBasis = typename Assembler::CellBasis;
+    std::vector<Vector> vertexCoordinates(bodyCount);
+    std::vector<const MyVertexBasis* > vertexBases(bodyCount);
+    std::vector<const MyCellBasis* > cellBases(bodyCount);
+
+    auto& wPatches = blocksFactory.weakPatches();
+    std::vector<std::vector<const ConvexPolyhedron<LocalVector>*>> weakPatches(bodyCount);
+
+    for (size_t i=0; i<bodyCount; i++) {
+      const auto& body = contactNetwork.body(i);
+      vertexBases[i] = &(body->assembler()->vertexBasis);
+      cellBases[i] = &(body->assembler()->cellBasis);
+
+      weakPatches[i].resize(1);
+      weakPatches[i][0] = wPatches[i].get();
+
+      auto& vertexCoords = vertexCoordinates[i];
+      vertexCoords.resize(nVertices[i]);
+
+      auto hostLeafView = body->grid()->hostGrid().leafGridView();
+      Dune::MultipleCodimMultipleGeomTypeMapper<
+          LeafGridView, Dune::MCMGVertexLayout> const vertexMapper(hostLeafView, Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(hostLeafView))
+        vertexCoords[vertexMapper.index(v)] = geoToPoint(v.geometry());
+    }
+
+    std::vector<const typename ContactNetwork::BoundaryPatch*> frictionPatches;
+    contactNetwork.frictionPatches(frictionPatches);
+
+    auto const writeData = parset.get<bool>("io.data.write");
+    auto dataFile = writeData ? std::make_unique<HDF5::File>("output.h5") : nullptr;
+
+    auto dataWriter =
+        writeData ? std::make_unique<
+                        HDF5Writer<MyProgramState, MyVertexBasis, DefLeafGridView>>(
+                        *dataFile, vertexCoordinates, vertexBases,
+                        frictionPatches, weakPatches)
+                  : nullptr;
+
+    IterationRegister iterationCount;
+
+    auto const report = [&](bool initial = false) {
+      if (writeData) {
+        dataWriter->reportSolution(programState, contactNetwork, globalFriction);
+        if (!initial)
+          dataWriter->reportIterations(programState, iterationCount);
+        dataFile->flush();
+      }
+    };
+    report(true);
+
+    std::cout.rdbuf(coutbuf); //reset to standard output again
+
+  } catch (Dune::Exception &e) {
+    Dune::derr << "Dune reported error: " << e << std::endl;
+  } catch (std::exception &e) {
+    std::cerr << "Standard exception: " << e.what() << std::endl;
+  }
+}
diff --git a/dune/tectonic/tests/hdf5test/hdf5test.cfg b/dune/tectonic/tests/hdf5test/hdf5test.cfg
new file mode 100644
index 00000000..818e378d
--- /dev/null
+++ b/dune/tectonic/tests/hdf5test/hdf5test.cfg
@@ -0,0 +1,105 @@
+# -*- mode:conf -*-
+gravity         = 9.81     # [m/s^2]
+
+[body0]
+length          = 0.5      # [m]
+height          = 0.5     # [m]
+depth           = 0.12     # [m]
+bulkModulus     = 1.5e5    # [Pa] #2190
+poissonRatio    = 0.11     # [1]  #0.11
+[body0.elastic]
+density         = 1300      # [kg/m^3] #750
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+[body0.viscoelastic]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+
+[body1]
+length          = 0.5     # [m]
+height          = 0.5     # [m]
+depth           = 0.12     # [m]
+bulkModulus     = 1.5e5    # [Pa]
+poissonRatio    = 0.11     # [1]
+[body1.elastic]
+density         = 1300      # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+[body1.viscoelastic]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+
+[boundary.friction]
+C               = 6       # [Pa]
+mu0             = 0.48      # [ ]
+V0              = 1e-3     # [m/s]
+L               = 1e-6  # [m]
+initialAlpha    = 0        # [ ]
+stateModel      = AgeingLaw
+frictionModel   = Truncated #Regularised
+[boundary.friction.weakening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+[boundary.friction.strengthening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+
+[boundary.neumann]
+sigmaN          = 200.0      # [Pa]
+
+[boundary.dirichlet]
+finalVelocity   = 1e-5     # [m/s]
+
+[io]
+data.write      = true
+
+[problem]
+finalTime       = 1000     # [s] #1000
+bodyCount       = 2
+
+[initialTime]
+timeStep = 0
+relativeTime = 0.0
+relativeTau = 2e-2 # 1e-6
+
+[timeSteps]
+scheme = newmark
+timeSteps = 1000
+
+[u0.solver]
+maximumIterations = 100
+verbosity         = full
+
+[a0.solver]
+maximumIterations = 100
+verbosity         = full
+
+[v.solver]
+maximumIterations = 100
+verbosity         = quiet
+
+[v.fpi]
+maximumIterations = 10000
+lambda            = 0.5
+
+[solver.tnnmg.preconditioner]
+mode         = additive
+patchDepth   = 1
+maximumIterations = 2
+verbosity         = quiet
+[solver.tnnmg.preconditioner.patchsolver]
+maximumIterations = 100
+verbosity         = quiet
+[solver.tnnmg.preconditioner.basesolver]
+maximumIterations = 10000
+verbosity         = quiet
+
+[solver.tnnmg.main]
+pre   = 1
+multi = 5 # number of multigrid steps
+post  = 0
+
+
+
diff --git a/dune/tectonic/time-stepping/adaptivetimestepper.cc b/dune/tectonic/time-stepping/adaptivetimestepper.cc
index 79bad14f..4c5b9185 100644
--- a/dune/tectonic/time-stepping/adaptivetimestepper.cc
+++ b/dune/tectonic/time-stepping/adaptivetimestepper.cc
@@ -8,8 +8,81 @@
 
 #include "../spatial-solving/preconditioners/multilevelpatchpreconditioner.hh"
 
+#include <dune/tectonic/utils/reductionfactors.hh>
+
 #include "adaptivetimestepper.hh"
 
+template<class IterationStepType, class NormType, class ReductionFactorContainer>
+Dune::Solvers::Criterion reductionFactorCriterion(
+      IterationStepType& iterationStep,
+      const NormType& norm,
+      ReductionFactorContainer& reductionFactors)
+{
+  double normOfOldCorrection = 1;
+  auto lastIterate = std::make_shared<typename IterationStepType::Vector>(*iterationStep.getIterate());
+
+  return Dune::Solvers::Criterion(
+      [&, lastIterate, normOfOldCorrection] () mutable {
+        double normOfCorrection = norm.diff(*lastIterate, *iterationStep.getIterate());
+        double convRate = (normOfOldCorrection > 0) ? normOfCorrection / normOfOldCorrection : 0.0;
+
+        if (convRate>1.0)
+            std::cout << "Solver convergence rate of " << convRate << std::endl;
+
+        normOfOldCorrection = normOfCorrection;
+        *lastIterate = *iterationStep.getIterate();
+
+        reductionFactors.push_back(convRate);
+        return std::make_tuple(convRate < 0, Dune::formatString(" % '.5f", convRate));
+      },
+      " reductionFactor");
+}
+
+
+template<class IterationStepType, class Functional, class ReductionFactorContainer>
+Dune::Solvers::Criterion energyCriterion(
+      const IterationStepType& iterationStep,
+      const Functional& f,
+      ReductionFactorContainer& reductionFactors)
+{
+  double normOfOldCorrection = 1;
+  auto lastIterate = std::make_shared<typename IterationStepType::Vector>(*iterationStep.getIterate());
+
+  return Dune::Solvers::Criterion(
+      [&, lastIterate, normOfOldCorrection] () mutable {
+        double normOfCorrection = std::abs(f(*lastIterate) - f(*iterationStep.getIterate())); //norm.diff(*lastIterate, *iterationStep.getIterate());
+
+        double convRate = (normOfOldCorrection != 0.0) ? 1.0 - (normOfCorrection / normOfOldCorrection) : 0.0;
+
+        if (convRate>1.0)
+            std::cout << "Solver convergence rate of " << convRate << std::endl;
+
+        normOfOldCorrection = normOfCorrection;
+        *lastIterate = *iterationStep.getIterate();
+
+        reductionFactors.push_back(convRate);
+        return std::make_tuple(convRate < 0, Dune::formatString(" % '.5f", convRate));
+      },
+      " reductionFactor");
+}
+
+template <class ReductionFactorContainer>
+void updateReductionFactors(ReductionFactorContainer& reductionFactors) {
+    const size_t s = reductionFactors.size();
+
+    //print(reductionFactors, "reduction factors: ");
+
+    if (s>allReductionFactors.size()) {
+        allReductionFactors.resize(s);
+    }
+
+    for (size_t i=0; i<reductionFactors.size(); i++) {
+        allReductionFactors[i].push_back(reductionFactors[i]);
+    }
+
+    reductionFactors.clear();
+}
+
 void IterationRegister::registerCount(FixedPointIterationCounter count) {
   totalCount += count;
 }
@@ -88,7 +161,7 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
     of size tau/2 with one of size tau. The method makes multiple
     coarsening/refining attempts, with coarsening coming first. */
 
-  std::cout << "AdaptiveTimeStepper::advance()" << std::endl;
+  //std::cout << "AdaptiveTimeStepper::advance()" << std::endl;
 
   // patch preconditioner only needs to be computed once per advance()
   // make linear solver for linear correction in TNNMGStep
@@ -109,7 +182,8 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
   Norm norm(*cgStep);
 
   auto linearSolver = std::make_shared<LinearSolver>(cgStep, parset_.template get<int>("solver.tnnmg.main.multi"), parset_.template get<double>("solver.tnnmg.preconditioner.basesolver.tolerance"), norm, Solver::QUIET);
-*/
+  */
+
   // set multigrid solver
   auto smoother = TruncatedBlockGSStep<Matrix, Vector>();
 
@@ -135,13 +209,26 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
 
   auto linearSolver = std::make_shared<LinearSolver>(linearMultigridStep, parset_.template get<int>("solver.tnnmg.main.multi"), parset_.template get<double>("solver.tnnmg.preconditioner.basesolver.tolerance"), norm, Solver::QUIET);
 
+  Vector x;
+  x.resize(contactNetwork_.nBodyAssembler().totalHasObstacle_.size());
+  x = 0;
+  dynamic_cast<Dune::Solvers::IterationStep<Vector>*>(linearMultigridStep.get())->setProblem(x);
+  //dynamic_cast<Dune::Solvers::IterationStep<Vector>*>(cgStep.get())->setProblem(x);
+
   const auto& currentNBodyAssembler = contactNetwork_.nBodyAssembler();
 
+   std::vector<double> reductionFactors;
+   linearSolver->addCriterion(reductionFactorCriterion(*linearMultigridStep, norm, reductionFactors));
+   //linearSolver->addCriterion(reductionFactorCriterion(*cgStep, norm, reductionFactors));
+
   if (R1_.updaters == Updaters()) {
     //setDeformation(current_);
     R1_ = step(current_, currentNBodyAssembler, linearSolver, relativeTime_, relativeTau_);
+    updateReductionFactors(reductionFactors);
   }
 
+
+
   //std::cout << "AdaptiveTimeStepper Step 1" << std::endl;
 
   size_t coarseningCount = 0;
@@ -157,6 +244,7 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
 
     setDeformation(current_);
     C = step(current_, currentNBodyAssembler, linearSolver, relativeTime_, 2 * relativeTau_);
+    updateReductionFactors(reductionFactors);
     std::cout << "AdaptiveTimeStepper C computed!" << std::endl << std::endl;
 
     /*using ScalarVector = typename Updaters::StateUpdater::ScalarVector;
@@ -167,6 +255,7 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
     setDeformation(R1_.updaters);
     auto&& nBodyAssembler = step(currentNBodyAssembler);
     R2 = step(R1_.updaters, nBodyAssembler, linearSolver, relativeTime_ + relativeTau_, relativeTau_);
+    updateReductionFactors(reductionFactors);
     std::cout << "AdaptiveTimeStepper R2 computed!" << std::endl << std::endl;
 
 
@@ -191,12 +280,14 @@ IterationRegister AdaptiveTimeStepper<Factory, ContactNetwork, Updaters, ErrorNo
     while (true) {
       setDeformation(current_);
       F1 = step(current_, currentNBodyAssembler, linearSolver, relativeTime_, relativeTau_ / 2.0);
+      updateReductionFactors(reductionFactors);
       std::cout << "AdaptiveTimeStepper F1 computed!" << std::endl << std::endl;
 
       setDeformation(F1.updaters);
       auto&& nBodyAssembler = step(currentNBodyAssembler);
       F2 = step(F1.updaters, nBodyAssembler, linearSolver, relativeTime_ + relativeTau_ / 2.0,
                 relativeTau_ / 2.0);
+      updateReductionFactors(reductionFactors);
       std::cout << "AdaptiveTimeStepper F2 computed!" << std::endl << std::endl;
       if (!mustRefine_(R1_.updaters, F2.updaters)) {
         std::cout << "Sufficiently refined!" << std::endl;
diff --git a/dune/tectonic/time-stepping/state/ageinglawstateupdater.cc b/dune/tectonic/time-stepping/state/ageinglawstateupdater.cc
index 1a7dcea3..3d1feef2 100644
--- a/dune/tectonic/time-stepping/state/ageinglawstateupdater.cc
+++ b/dune/tectonic/time-stepping/state/ageinglawstateupdater.cc
@@ -54,10 +54,14 @@ auto liftSingularity(
 
 template <class ScalarVector, class Vector>
 void AgeingLawStateUpdater<ScalarVector, Vector>::solve(const Vector& velocity_field) {
-    for (size_t i=0; i<alpha_.size(); i++) {
-      double const V = velocity_field[i].two_norm();
-      double const mtoL = -tau_ / L_;
-      alpha_[i] = std::log(std::exp(alpha_o_[i] + V * mtoL) +
+
+    for (size_t i=0; i<alpha_.size(); ++i) {
+        auto tangentVelocity = velocity_field[localToGlobal_[i]];
+        tangentVelocity[0] = 0;
+
+        double const V = tangentVelocity.two_norm();
+        double const mtoL = -tau_ / L_;
+        alpha_[i] = std::log(std::exp(alpha_o_[i] + V * mtoL) +
                           V0_ * liftSingularity(mtoL, V));
     }
 }
diff --git a/dune/tectonic/time-stepping/state/sliplawstateupdater.cc b/dune/tectonic/time-stepping/state/sliplawstateupdater.cc
index 3b06f055..a024744c 100644
--- a/dune/tectonic/time-stepping/state/sliplawstateupdater.cc
+++ b/dune/tectonic/time-stepping/state/sliplawstateupdater.cc
@@ -41,8 +41,11 @@ template <class ScalarVector, class Vector>
 void SlipLawStateUpdater<ScalarVector, Vector>::solve(
     const Vector& velocity_field) {
 
-    for (size_t i=0; i<alpha_.size(); i++) {
-        double const V = velocity_field[i].two_norm();
+    for (size_t i=0; i<alpha_.size(); ++i) {
+        auto tangentVelocity = velocity_field[localToGlobal_[i]];
+        tangentVelocity[0] = 0;
+
+        double const V = tangentVelocity.two_norm();
         double const mtVoL = -tau_ * V / L_;
         alpha_[i] = (V <= 0) ? alpha_o_[i] : std::expm1(mtVoL) * std::log(V / V0_) +
                                                alpha_o_[i] * std::exp(mtVoL);
diff --git a/dune/tectonic/utils/reductionfactors.hh b/dune/tectonic/utils/reductionfactors.hh
new file mode 100644
index 00000000..cc813928
--- /dev/null
+++ b/dune/tectonic/utils/reductionfactors.hh
@@ -0,0 +1 @@
+extern std::vector<std::vector<double>> allReductionFactors;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e41c1f9a..080ed358 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_subdirectory("foam")
 add_subdirectory("multi-body-problem")
+add_subdirectory("strikeslip")
 
 set(UGW_SOURCE_FILES
   ../dune/tectonic/assemblers.cc # FIXME
diff --git a/src/foam/foam-2D.cfg b/src/foam/foam-2D.cfg
index 1d8c5160..d911706c 100644
--- a/src/foam/foam-2D.cfg
+++ b/src/foam/foam-2D.cfg
@@ -1,9 +1,9 @@
 # -*- mode:conf -*-
 [body0]
-smallestDiameter = 0.02  # 2e-3 [m]
+smallestDiameter = 0.05  # 2e-3 [m]
 
 [body1]
-smallestDiameter = 0.02  # 2e-3 [m]
+smallestDiameter = 0.01 # 2e-3 [m]
 
 [timeSteps]
 refinementTolerance = 1e-5 # 1e-5
diff --git a/src/foam/foam.cc b/src/foam/foam.cc
index 9e4c7e56..d78fc270 100644
--- a/src/foam/foam.cc
+++ b/src/foam/foam.cc
@@ -84,6 +84,9 @@
 //#include <tbb/tbb.h> //TODO multi threading preconditioner?
 //#include <pthread.h>
 
+#include <dune/tectonic/utils/reductionfactors.hh>
+std::vector<std::vector<double>> allReductionFactors;
+
 size_t const dims = MY_DIM;
 
 Dune::ParameterTree getParameters(int argc, char *argv[]) {
@@ -121,17 +124,14 @@ int main(int argc, char *argv[]) {
     // ----------------------
     // set up contact network
     // ----------------------
-    TwoBlocksFactory<Grid, Vector> twoBlocksFactory(parset);
-    using ContactNetwork = typename TwoBlocksFactory<Grid, Vector>::ContactNetwork;
-    twoBlocksFactory.build();
+    using BlocksFactory = TwoBlocksFactory<Grid, Vector>;
+    BlocksFactory blocksFactory(parset);
+    using ContactNetwork = typename BlocksFactory::ContactNetwork;
+    blocksFactory.build();
 
-    ContactNetwork& contactNetwork = twoBlocksFactory.contactNetwork();
+    ContactNetwork& contactNetwork = blocksFactory.contactNetwork();
 
-    /*ThreeBlocksFactory<Grid, Vector> threeBlocksFactory(parset);
-    using ContactNetwork = typename ThreeBlocksFactory<Grid, Vector>::ContactNetwork;
-    threeBlocksFactory.build();
 
-    ContactNetwork& contactNetwork = threeBlocksFactory.contactNetwork(); */
 
     const size_t bodyCount = contactNetwork.nBodies();
 
@@ -219,7 +219,7 @@ int main(int argc, char *argv[]) {
     std::vector<const MyVertexBasis* > vertexBases(bodyCount);
     std::vector<const MyCellBasis* > cellBases(bodyCount);
 
-    auto& wPatches = twoBlocksFactory.weakPatches();
+    auto& wPatches = blocksFactory.weakPatches();
     std::vector<std::vector<const ConvexPolyhedron<LocalVector>*>> weakPatches(bodyCount);
 
 
@@ -234,20 +234,24 @@ int main(int argc, char *argv[]) {
       auto& vertexCoords = vertexCoordinates[i];
       vertexCoords.resize(nVertices[i]);
 
+      auto hostLeafView = body->grid()->hostGrid().leafGridView();
       Dune::MultipleCodimMultipleGeomTypeMapper<
-          DefLeafGridView, Dune::MCMGVertexLayout> const vertexMapper(body->gridView(), Dune::mcmgVertexLayout());
-      for (auto &&v : vertices(body->gridView()))
+          LeafGridView, Dune::MCMGVertexLayout> const vertexMapper(hostLeafView, Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(hostLeafView))
         vertexCoords[vertexMapper.index(v)] = geoToPoint(v.geometry());
     }
 
     typename ContactNetwork::BoundaryPatches frictionBoundaries;
     contactNetwork.boundaryPatches("friction", frictionBoundaries);
 
+    std::vector<const typename ContactNetwork::BoundaryPatch*> frictionPatches;
+    contactNetwork.frictionPatches(frictionPatches);
+
     auto dataWriter =
         writeData ? std::make_unique<
                         HDF5LevelWriter<MyProgramState, MyVertexBasis, DefLeafGridView>>(
                         *dataFile, vertexCoordinates, vertexBases,
-                        frictionBoundaries, weakPatches)
+                        frictionPatches, weakPatches)
                   : nullptr;
 
     const MyVTKWriter<MyVertexBasis, MyCellBasis> vtkWriter(cellBases, vertexBases, "/storage/mi/podlesny/software/dune/dune-tectonic/body");
@@ -256,7 +260,7 @@ int main(int argc, char *argv[]) {
 
     auto const report = [&](bool initial = false) {
       if (writeData) {
-        dataWriter->reportSolution(programState, globalFriction);
+        dataWriter->reportSolution(programState, contactNetwork, globalFriction);
         if (!initial)
           dataWriter->reportIterations(programState, iterationCount);
         dataFile->flush();
@@ -327,20 +331,20 @@ int main(int argc, char *argv[]) {
     BoundaryNodes dirichletNodes;
     contactNetwork.boundaryNodes("dirichlet", dirichletNodes);
 
-    for (size_t i=0; i<dirichletNodes.size(); i++) {
+    /*for (size_t i=0; i<dirichletNodes.size(); i++) {
         for (size_t j=0; j<dirichletNodes[i].size(); j++) {
         print(*dirichletNodes[i][j], "dirichletNodes_body_" + std::to_string(i) + "_boundary_" + std::to_string(j));
         }
-    }
+    }*/
 
     std::vector<const Dune::BitSetVector<1>*> frictionNodes;
     contactNetwork.frictionNodes(frictionNodes);
 
-    for (size_t i=0; i<frictionNodes.size(); i++) {
+    /*for (size_t i=0; i<frictionNodes.size(); i++) {
         print(*frictionNodes[i], "frictionNodes_body_" + std::to_string(i));
-    }
+    }*/
 
-    DUNE_THROW(Dune::Exception, "Just need to stop here!");
+    //DUNE_THROW(Dune::Exception, "Just need to stop here!");
 
     Updaters current(
         initRateUpdater(
diff --git a/src/foam/foam.cfg b/src/foam/foam.cfg
index c8604d62..b5f4216c 100644
--- a/src/foam/foam.cfg
+++ b/src/foam/foam.cfg
@@ -2,52 +2,55 @@
 gravity         = 9.81     # [m/s^2]
 
 [body0]
-length          = 0.08      # [m]
+length          = 0.4      # [m]
 height          = 0.04     # [m]
 depth           = 0.04     # [m]
-bulkModulus     = 0.5e5    # [Pa] #2190
-poissonRatio    = 0.30     # [1]  #0.11
+bulkModulus     = 1.5e5    # [Pa] #2190
+poissonRatio    = 0.11     # [1]  #0.11
 [body0.elastic]
-density         = 900      # [kg/m^3] #750
-shearViscosity  = 1e20     # [Pas]
-bulkViscosity   = 1e20     # [Pas]
+density         = 1300      # [kg/m^3] #750
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
 [body0.viscoelastic]
-density         = 1000     # [kg/m^3]
-shearViscosity  = 1e20     # [Pas]
-bulkViscosity   = 1e20     # [Pas]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
 
 [body1]
 length          = 0.04     # [m]
 height          = 0.04     # [m]
 depth           = 0.04     # [m]
-bulkModulus     = 0.5e5    # [Pa]
-poissonRatio    = 0.30     # [1]
+bulkModulus     = 1.5e5    # [Pa]
+poissonRatio    = 0.11     # [1]
 [body1.elastic]
-density         = 900      # [kg/m^3]
-shearViscosity  = 1e20     # [Pas]
-bulkViscosity   = 1e20     # [Pas]
+density         = 1300      # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
 [body1.viscoelastic]
-density         = 1000     # [kg/m^3]
-shearViscosity  = 1e20     # [Pas]
-bulkViscosity   = 1e20     # [Pas]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
 
 [boundary.friction]
-C               = 10       # [Pa]
-mu0             = 0.7      # [ ]
-V0              = 5e-5     # [m/s]
-L               = 2.25e-5  # [m]
+C               = 6       # [Pa]
+mu0             = 0.48      # [ ]
+V0              = 1e-3     # [m/s]
+L               = 1e-6  # [m]
 initialAlpha    = 0        # [ ]
 stateModel      = AgeingLaw
 frictionModel   = Truncated #Regularised
 [boundary.friction.weakening]
-a               = 0.002    # [ ]
-b               = 0.017    # [ ]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
 [boundary.friction.strengthening]
-a               = 0.020    # [ ]
-b               = 0.005    # [ ]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
 
 [boundary.neumann]
-sigmaN          = 0.0      # [Pa]
+sigmaN          = 10000.0      # [Pa]
+
+[boundary.dirichlet]
+finalVelocity   = 3e-3     # [m/s]
 
 [io]
 data.write      = false
@@ -64,11 +67,11 @@ bodyCount       = 2
 [initialTime]
 timeStep = 0
 relativeTime = 0.0
-relativeTau = 5e-4 # 1e-6
+relativeTau = 5e-6 # 1e-6
 
 [timeSteps]
 scheme = newmark
-timeSteps = 100000
+timeSteps = 1
 
 [u0.solver]
 maximumIterations = 100
diff --git a/src/multi-body-problem/multi-body-problem-2D.cfg b/src/multi-body-problem/multi-body-problem-2D.cfg
index cfee4140..641078a7 100644
--- a/src/multi-body-problem/multi-body-problem-2D.cfg
+++ b/src/multi-body-problem/multi-body-problem-2D.cfg
@@ -1,6 +1,6 @@
 # -*- mode:conf -*-
 [boundary.friction]
-smallestDiameter = 0.1  # 2e-3 [m]
+smallestDiameter = 0.05  # 2e-3 [m]
 
 [timeSteps]
 refinementTolerance = 1e-5 # 1e-5
diff --git a/src/multi-body-problem/multi-body-problem.cc b/src/multi-body-problem/multi-body-problem.cc
index eb2fd28f..62da56ba 100644
--- a/src/multi-body-problem/multi-body-problem.cc
+++ b/src/multi-body-problem/multi-body-problem.cc
@@ -87,6 +87,9 @@
 
 size_t const dims = MY_DIM;
 
+#include <dune/tectonic/utils/reductionfactors.hh>
+std::vector<std::vector<double>> allReductionFactors;
+
 Dune::ParameterTree getParameters(int argc, char *argv[]) {
   Dune::ParameterTree parset;
   Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/src/multi-body-problem/multi-body-problem.cfg", parset);
@@ -122,17 +125,15 @@ int main(int argc, char *argv[]) {
     // ----------------------
     // set up contact network
     // ----------------------
-    StackedBlocksFactory<Grid, Vector> stackedBlocksFactory(parset);
-    using ContactNetwork = typename StackedBlocksFactory<Grid, Vector>::ContactNetwork;
-    stackedBlocksFactory.build();
 
-    ContactNetwork& contactNetwork = stackedBlocksFactory.contactNetwork();
+    //using BlocksFactory = StackedBlocksFactory<Grid, Vector>;
+    using BlocksFactory = ThreeBlocksFactory<Grid, Vector>;
+    BlocksFactory blocksFactory(parset);
 
-    /*ThreeBlocksFactory<Grid, Vector> threeBlocksFactory(parset);
-    using ContactNetwork = typename ThreeBlocksFactory<Grid, Vector>::ContactNetwork;
-    threeBlocksFactory.build();
+    using ContactNetwork = typename BlocksFactory::ContactNetwork;
+    blocksFactory.build();
 
-    ContactNetwork& contactNetwork = threeBlocksFactory.contactNetwork();*/
+    ContactNetwork& contactNetwork = blocksFactory.contactNetwork();
 
     const size_t bodyCount = contactNetwork.nBodies();
 
@@ -150,10 +151,10 @@ int main(int argc, char *argv[]) {
         }*/
     }
 
-    for (size_t i=0; i<bodyCount; i++) {
+    /*for (size_t i=0; i<bodyCount; i++) {
         printDofLocation(contactNetwork.body(i)->gridView());
         writeToVTK(contactNetwork.body(i)->gridView(), "../debug_print/bodies/", "body_" + std::to_string(i) + "_leaf");
-    }
+    } */
 
     // ----------------------------
     // assemble contactNetwork
@@ -223,8 +224,7 @@ int main(int argc, char *argv[]) {
     std::vector<const MyVertexBasis* > vertexBases(bodyCount);
     std::vector<const MyCellBasis* > cellBases(bodyCount);
 
-    auto& wPatches = stackedBlocksFactory.weakPatches();
-    //auto& wPatches = threeBlocksFactory.weakPatches();
+    auto& wPatches = blocksFactory.weakPatches();
     std::vector<std::vector<const ConvexPolyhedron<LocalVector>*>> weakPatches(bodyCount);
 
 
@@ -239,20 +239,24 @@ int main(int argc, char *argv[]) {
       auto& vertexCoords = vertexCoordinates[i];
       vertexCoords.resize(nVertices[i]);
 
+      auto hostLeafView = body->grid()->hostGrid().leafGridView();
       Dune::MultipleCodimMultipleGeomTypeMapper<
-          DefLeafGridView, Dune::MCMGVertexLayout> const vertexMapper(body->gridView(), Dune::mcmgVertexLayout());
-      for (auto &&v : vertices(body->gridView()))
+          LeafGridView, Dune::MCMGVertexLayout> const vertexMapper(hostLeafView, Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(hostLeafView))
         vertexCoords[vertexMapper.index(v)] = geoToPoint(v.geometry());
     }
 
     typename ContactNetwork::BoundaryPatches frictionBoundaries;
     contactNetwork.boundaryPatches("friction", frictionBoundaries);
 
+    std::vector<const typename ContactNetwork::BoundaryPatch*> frictionPatches;
+    contactNetwork.frictionPatches(frictionPatches);
+
     auto dataWriter =
         writeData ? std::make_unique<
                         HDF5LevelWriter<MyProgramState, MyVertexBasis, DefLeafGridView>>(
                         *dataFile, vertexCoordinates, vertexBases,
-                        frictionBoundaries, weakPatches)
+                        frictionPatches, weakPatches)
                   : nullptr;
 
     const MyVTKWriter<MyVertexBasis, MyCellBasis> vtkWriter(cellBases, vertexBases, "/storage/mi/podlesny/software/dune/dune-tectonic/body");
@@ -261,7 +265,7 @@ int main(int argc, char *argv[]) {
 
     auto const report = [&](bool initial = false) {
       if (writeData) {
-        dataWriter->reportSolution(programState, globalFriction);
+        dataWriter->reportSolution(programState, contactNetwork, globalFriction);
         if (!initial)
           dataWriter->reportIterations(programState, iterationCount);
         dataFile->flush();
@@ -317,7 +321,7 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    print(totalDirichletNodes, "totalDirichletNodes:");
+    //print(totalDirichletNodes, "totalDirichletNodes:");
 
     //using Functional = Functional<Matrix&, Vector&, ZeroNonlinearity&, Vector&, Vector&, field_type>;
     using Functional = Functional<Matrix&, Vector&, GlobalFriction<Matrix, Vector>&, Vector&, Vector&, field_type>;
@@ -334,18 +338,18 @@ int main(int argc, char *argv[]) {
     BoundaryNodes dirichletNodes;
     contactNetwork.boundaryNodes("dirichlet", dirichletNodes);
 
-    for (size_t i=0; i<dirichletNodes.size(); i++) {
+    /*for (size_t i=0; i<dirichletNodes.size(); i++) {
         for (size_t j=0; j<dirichletNodes[i].size(); j++) {
         print(*dirichletNodes[i][j], "dirichletNodes_body_" + std::to_string(i) + "_boundary_" + std::to_string(j));
         }
-    }
+    }*/
 
     std::vector<const Dune::BitSetVector<1>*> frictionNodes;
     contactNetwork.frictionNodes(frictionNodes);
 
-    for (size_t i=0; i<frictionNodes.size(); i++) {
+    /*for (size_t i=0; i<frictionNodes.size(); i++) {
         print(*frictionNodes[i], "frictionNodes_body_" + std::to_string(i));
-    }
+    }*/
 
     Updaters current(
         initRateUpdater(
@@ -476,6 +480,27 @@ int main(int argc, char *argv[]) {
 
     }
 
+    // output reduction factors
+    std::vector<double> avgReductionFactors(allReductionFactors.size());
+    //std::cout << "allReductionFactors.size: " << allReductionFactors.size() << std::endl;
+
+    for (size_t i=0; i<avgReductionFactors.size(); i++) {
+        avgReductionFactors[i] = 1;
+        size_t c = 0;
+
+        //std::cout << "allReductionFactors[i].size: " << allReductionFactors[i].size() << std::endl;
+        //print(allReductionFactors[i], "all reduction factors: ");
+
+        for (size_t j=0; j<allReductionFactors[i].size(); j++) {
+            avgReductionFactors[i] *= allReductionFactors[i][j];
+            c++;
+        }
+
+        avgReductionFactors[i] = std::pow(avgReductionFactors[i], 1.0/((double)c));
+    }
+
+    print(avgReductionFactors, "average reduction factors: ");
+
 
     std::cout.rdbuf(coutbuf); //reset to standard output again
 
diff --git a/src/multi-body-problem/multi-body-problem.cfg b/src/multi-body-problem/multi-body-problem.cfg
index 4be02c1c..53f96e97 100644
--- a/src/multi-body-problem/multi-body-problem.cfg
+++ b/src/multi-body-problem/multi-body-problem.cfg
@@ -10,8 +10,8 @@ restarts.write  = false #true
 vtk.write       = true
 
 [problem]
-finalTime       = 10000  # [s] #1000
-bodyCount       = 2
+finalTime       = 1000  # [s] #1000
+bodyCount       = 3
 
 [body]
 bulkModulus     = 0.5e5 # [Pa]
@@ -40,6 +40,12 @@ b               = 0.017 # [ ]
 a               = 0.020 # [ ]
 b               = 0.005 # [ ]
 
+[boundary.neumann]
+sigmaN          = 0.0      # [Pa]
+
+[boundary.dirichlet]
+finalVelocity   = 5e-5     # [m/s]
+
 [initialTime]
 timeStep = 0
 relativeTime = 0.0
@@ -47,7 +53,7 @@ relativeTau = 5e-4 # 1e-6
 
 [timeSteps]
 scheme = newmark
-timeSteps = 100000
+timeSteps = 1
 
 [u0.solver]
 maximumIterations = 100
diff --git a/src/strikeslip/CMakeLists.txt b/src/strikeslip/CMakeLists.txt
new file mode 100644
index 00000000..ac71e9a9
--- /dev/null
+++ b/src/strikeslip/CMakeLists.txt
@@ -0,0 +1,43 @@
+add_custom_target(tectonic_src_strikeslip SOURCES
+  strikeslip.cfg
+  strikeslip-2D.cfg
+) 
+
+set(STRIKESLIP_SOURCE_FILES
+  ../../dune/tectonic/assemblers.cc
+  ../../dune/tectonic/data-structures/body/body.cc
+  ../../dune/tectonic/data-structures/network/levelcontactnetwork.cc
+  ../../dune/tectonic/data-structures/network/contactnetwork.cc
+  ../../dune/tectonic/data-structures/enumparser.cc
+  ../../dune/tectonic/factories/strikeslipfactory.cc
+  ../../dune/tectonic/io/vtk.cc
+  ../../dune/tectonic/io/hdf5/frictionalboundary-writer.cc
+  ../../dune/tectonic/io/hdf5/iteration-writer.cc
+  #../../dune/tectonic/io/hdf5/patchinfo-writer.cc
+  ../../dune/tectonic/io/hdf5/restart-io.cc
+  ../../dune/tectonic/io/hdf5/surface-writer.cc
+  ../../dune/tectonic/io/hdf5/time-writer.cc
+  ../../dune/tectonic/problem-data/grid/simplexmanager.cc
+  ../../dune/tectonic/problem-data/grid/trianglegeometry.cc
+  ../../dune/tectonic/problem-data/grid/trianglegrids.cc
+  ../../dune/tectonic/spatial-solving/solverfactory.cc
+  ../../dune/tectonic/spatial-solving/fixedpointiterator.cc
+  ../../dune/tectonic/time-stepping/coupledtimestepper.cc
+  ../../dune/tectonic/time-stepping/adaptivetimestepper.cc
+  ../../dune/tectonic/time-stepping/rate.cc
+  ../../dune/tectonic/time-stepping/rate/rateupdater.cc
+  ../../dune/tectonic/time-stepping/state.cc
+  strikeslip.cc
+)
+
+foreach(_dim 2 3)
+  set(_strikeslip_target strikeslip-${_dim}D)
+
+  add_executable(${_strikeslip_target} ${STRIKESLIP_SOURCE_FILES})
+
+  add_dune_ug_flags(${_strikeslip_target})
+  add_dune_hdf5_flags(${_strikeslip_target})
+
+  set_property(TARGET ${_strikeslip_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
+  #set_property(TARGET ${_strikeslip_target} APPEND PROPERTY COMPILE_DEFINITIONS "NEW_TNNMG_COMPUTE_ITERATES_DIRECTLY=1")
+endforeach()
diff --git a/src/strikeslip/strikeslip-2D.cfg b/src/strikeslip/strikeslip-2D.cfg
new file mode 100644
index 00000000..05590e22
--- /dev/null
+++ b/src/strikeslip/strikeslip-2D.cfg
@@ -0,0 +1,27 @@
+# -*- mode:conf -*-
+[body0]
+smallestDiameter = 0.05 # 2e-3 [m]
+
+[body1]
+smallestDiameter = 0.05  # 2e-3 [m]
+
+[timeSteps]
+refinementTolerance = 1e-4 # 1e-5
+
+[u0.solver]
+tolerance         = 1e-8
+
+[a0.solver]
+tolerance         = 1e-8
+
+[v.solver]
+tolerance         = 1e-8
+
+[v.fpi]
+tolerance         = 1e-4    # 1e-5
+
+[solver.tnnmg.preconditioner.basesolver]
+tolerance          = 1e-10
+
+[solver.tnnmg.preconditioner.patchsolver]
+tolerance          = 1e-10
diff --git a/src/strikeslip/strikeslip.cc b/src/strikeslip/strikeslip.cc
new file mode 100644
index 00000000..412d1097
--- /dev/null
+++ b/src/strikeslip/strikeslip.cc
@@ -0,0 +1,485 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_IPOPT
+#undef HAVE_IPOPT
+#endif
+
+#include <atomic>
+#include <cmath>
+#include <csignal>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <memory>
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/exceptions.hh>
+#include <dune/common/fmatrix.hh>
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+
+#include <dune/grid/common/mcmgmapper.hh>
+#include <dune/istl/bcrsmatrix.hh>
+#include <dune/istl/bvector.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+#include <dune/fufem/formatstring.hh>
+#include <dune/fufem/hdf5/file.hh>
+
+#include <dune/solvers/norms/energynorm.hh>
+#include <dune/solvers/solvers/loopsolver.hh>
+#include <dune/solvers/iterationsteps/blockgssteps.hh>
+
+#include <dune/contact/common/deformedcontinuacomplex.hh>
+#include <dune/contact/common/couplingpair.hh>
+#include <dune/contact/projections/normalprojection.hh>
+
+
+#include <dune/tectonic/assemblers.hh>
+#include <dune/tectonic/gridselector.hh>
+#include <dune/tectonic/explicitgrid.hh>
+#include <dune/tectonic/explicitvectors.hh>
+
+#include <dune/tectonic/data-structures/enumparser.hh>
+#include <dune/tectonic/data-structures/enums.hh>
+#include <dune/tectonic/data-structures/network/contactnetwork.hh>
+#include <dune/tectonic/data-structures/matrices.hh>
+#include <dune/tectonic/data-structures/program_state.hh>
+#include <dune/tectonic/data-structures/friction/globalfriction.hh>
+
+#include <dune/tectonic/factories/strikeslipfactory.hh>
+
+#include <dune/tectonic/io/hdf5-writer.hh>
+#include <dune/tectonic/io/hdf5/restart-io.hh>
+#include <dune/tectonic/io/vtk.hh>
+
+#include <dune/tectonic/problem-data/bc.hh>
+#include <dune/tectonic/problem-data/mybody.hh>
+
+#include <dune/tectonic/spatial-solving/tnnmg/functional.hh>
+//#include <dune/tectonic/spatial-solving/preconditioners/multilevelpatchpreconditioner.hh>
+#include <dune/tectonic/spatial-solving/tnnmg/localbisectionsolver.hh>
+#include <dune/tectonic/spatial-solving/solverfactory.hh>
+
+#include <dune/tectonic/time-stepping/adaptivetimestepper.hh>
+#include <dune/tectonic/time-stepping/rate.hh>
+#include <dune/tectonic/time-stepping/state.hh>
+#include <dune/tectonic/time-stepping/updaters.hh>
+
+#include <dune/tectonic/utils/debugutils.hh>
+#include <dune/tectonic/utils/diameter.hh>
+#include <dune/tectonic/utils/geocoordinate.hh>
+
+// for getcwd
+#include <unistd.h>
+
+//#include <tbb/tbb.h> //TODO multi threading preconditioner?
+//#include <pthread.h>
+
+#include <dune/tectonic/utils/reductionfactors.hh>
+std::vector<std::vector<double>> allReductionFactors;
+
+size_t const dims = MY_DIM;
+
+Dune::ParameterTree getParameters(int argc, char *argv[]) {
+  Dune::ParameterTree parset;
+  Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/src/strikeslip/strikeslip.cfg", parset);
+  Dune::ParameterTreeParser::readINITree(
+      Dune::Fufem::formatString("/home/mi/podlesny/software/dune/dune-tectonic/src/strikeslip/strikeslip-%dD.cfg", dims), parset);
+  Dune::ParameterTreeParser::readOptions(argc, argv, parset);
+  return parset;
+}
+
+static std::atomic<bool> terminationRequested(false);
+void handleSignal(int signum) { terminationRequested = true; }
+
+int main(int argc, char *argv[]) {
+  try {
+    Dune::MPIHelper::instance(argc, argv);
+
+    char buffer[256];
+    char *val = getcwd(buffer, sizeof(buffer));
+    if (val) {
+        std::cout << buffer << std::endl;
+        std::cout << argv[0] << std::endl;
+    }
+
+    std::ofstream out("strikeslip.log");
+    std::streambuf *coutbuf = std::cout.rdbuf(); //save old buffer
+    std::cout.rdbuf(out.rdbuf()); //redirect std::cout to log.txt
+
+    auto const parset = getParameters(argc, argv);
+
+    using Assembler = MyAssembler<DefLeafGridView, dims>;
+    using field_type = Matrix::field_type;
+
+    // ----------------------
+    // set up contact network
+    // ----------------------
+    using BlocksFactory = StrikeSlipFactory<Grid, Vector>;
+    BlocksFactory blocksFactory(parset);
+    using ContactNetwork = typename BlocksFactory::ContactNetwork;
+    blocksFactory.build();
+
+    ContactNetwork& contactNetwork = blocksFactory.contactNetwork();
+
+
+
+    const size_t bodyCount = contactNetwork.nBodies();
+
+    for (size_t i=0; i<contactNetwork.nLevels(); i++) {
+         //printDofLocation(contactNetwork.body(i)->gridView());
+
+        //Vector def(contactNetwork.deformedGrids()[i]->size(dims));
+        //def = 1;
+        //deformedGridComplex.setDeformation(def, i);
+
+        const auto& level = *contactNetwork.level(i);
+
+        for (size_t j=0; j<level.nBodies(); j++) {
+            writeToVTK(level.body(j)->gridView(), "../debug_print/bodies/", "body_" + std::to_string(j) + "_level_" + std::to_string(i));
+        }
+    }
+
+    for (size_t i=0; i<bodyCount; i++) {
+        writeToVTK(contactNetwork.body(i)->gridView(), "../debug_print/bodies/", "body_" + std::to_string(i) + "_leaf");
+    }
+
+    // ----------------------------
+    // assemble contactNetwork
+    // ----------------------------
+    contactNetwork.assemble();
+
+    //printMortarBasis<Vector>(contactNetwork.nBodyAssembler());
+
+    // -----------------
+    // init input/output
+    // -----------------
+    std::vector<size_t> nVertices(bodyCount);
+    for (size_t i=0; i<bodyCount; i++) {
+        nVertices[i] = contactNetwork.body(i)->nVertices();
+    }
+
+    using MyProgramState = ProgramState<Vector, ScalarVector>;
+    MyProgramState programState(nVertices);
+    auto const firstRestart = parset.get<size_t>("io.restarts.first");
+    auto const restartSpacing = parset.get<size_t>("io.restarts.spacing");
+    auto const writeRestarts = parset.get<bool>("io.restarts.write");
+    auto const writeData = parset.get<bool>("io.data.write");
+    bool const handleRestarts = writeRestarts or firstRestart > 0;
+
+
+    auto dataFile =
+        writeData ? std::make_unique<HDF5::File>("output.h5") : nullptr;
+
+    auto restartFile = handleRestarts
+                           ? std::make_unique<HDF5::File>(
+                                 "restarts.h5",
+                                 writeRestarts ? HDF5::Access::READWRITE
+                                               : HDF5::Access::READONLY)
+                           : nullptr;
+
+
+    auto restartIO = handleRestarts
+                         ? std::make_unique<RestartIO<MyProgramState>>(
+                               *restartFile, nVertices)
+                         : nullptr;
+
+    if (firstRestart > 0) // automatically adjusts the time and timestep
+      restartIO->read(firstRestart, programState);
+    else
+     programState.setupInitialConditions(parset, contactNetwork);
+
+    auto& nBodyAssembler = contactNetwork.nBodyAssembler();
+    for (size_t i=0; i<bodyCount; i++) {
+      contactNetwork.body(i)->setDeformation(programState.u[i]);
+    }
+    nBodyAssembler.assembleTransferOperator();
+    nBodyAssembler.assembleObstacle();
+
+    // ------------------------
+    // assemble global friction
+    // ------------------------
+    contactNetwork.assembleFriction(parset.get<Config::FrictionModel>("boundary.friction.frictionModel"), programState.weightedNormalStress);
+
+    auto& globalFriction = contactNetwork.globalFriction();
+    globalFriction.updateAlpha(programState.alpha);
+
+    using MyVertexBasis = typename Assembler::VertexBasis;
+    using MyCellBasis = typename Assembler::CellBasis;
+    std::vector<Vector> vertexCoordinates(bodyCount);
+    std::vector<const MyVertexBasis* > vertexBases(bodyCount);
+    std::vector<const MyCellBasis* > cellBases(bodyCount);
+
+    auto& wPatches = blocksFactory.weakPatches();
+    std::vector<std::vector<const ConvexPolyhedron<LocalVector>*>> weakPatches(bodyCount);
+
+
+    for (size_t i=0; i<bodyCount; i++) {
+      const auto& body = contactNetwork.body(i);
+      vertexBases[i] = &(body->assembler()->vertexBasis);
+      cellBases[i] = &(body->assembler()->cellBasis);
+
+      weakPatches[i].resize(1);
+      weakPatches[i][0] = wPatches[i].get();
+
+      auto& vertexCoords = vertexCoordinates[i];
+      vertexCoords.resize(nVertices[i]);
+
+      auto hostLeafView = body->grid()->hostGrid().leafGridView();
+      Dune::MultipleCodimMultipleGeomTypeMapper<
+          LeafGridView, Dune::MCMGVertexLayout> const vertexMapper(hostLeafView, Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(hostLeafView))
+        vertexCoords[vertexMapper.index(v)] = geoToPoint(v.geometry());
+    }
+
+    typename ContactNetwork::BoundaryPatches frictionBoundaries;
+    contactNetwork.boundaryPatches("friction", frictionBoundaries);
+
+    std::vector<const typename ContactNetwork::BoundaryPatch*> frictionPatches;
+    contactNetwork.frictionPatches(frictionPatches);
+
+    auto dataWriter =
+        writeData ? std::make_unique<
+                        HDF5Writer<MyProgramState, MyVertexBasis, DefLeafGridView>>(
+                        *dataFile, vertexCoordinates, vertexBases,
+                        frictionPatches, weakPatches)
+                  : nullptr;
+
+    const MyVTKWriter<MyVertexBasis, MyCellBasis> vtkWriter(cellBases, vertexBases, "/storage/mi/podlesny/software/dune/dune-tectonic/body");
+
+    IterationRegister iterationCount;
+
+    auto const report = [&](bool initial = false) {
+      if (writeData) {
+        dataWriter->reportSolution(programState, contactNetwork, globalFriction);
+        if (!initial)
+          dataWriter->reportIterations(programState, iterationCount);
+        dataFile->flush();
+      }
+
+      if (writeRestarts and !initial and
+          programState.timeStep % restartSpacing == 0) {
+        restartIO->write(programState);
+        restartFile->flush();
+      }
+
+      if (parset.get<bool>("io.printProgress"))
+        std::cout << "timeStep = " << std::setw(6) << programState.timeStep
+                  << ", time = " << std::setw(12) << programState.relativeTime
+                  << ", tau = " << std::setw(12) << programState.relativeTau
+                  << std::endl;
+
+      if (parset.get<bool>("io.vtk.write")) {
+        std::vector<ScalarVector> stress(bodyCount);
+
+        for (size_t i=0; i<bodyCount; i++) {
+          const auto& body = contactNetwork.body(i);
+          body->assembler()->assembleVonMisesStress(body->data()->getYoungModulus(),
+                                           body->data()->getPoissonRatio(),
+                                           programState.u[i], stress[i]);
+
+        }
+
+        vtkWriter.write(programState.timeStep, programState.u, programState.v,
+                        programState.alpha, stress);
+      }
+    };
+    report(true);
+
+    // -------------------
+    // Set up TNNMG solver
+    // -------------------
+
+    BitVector totalDirichletNodes;
+    contactNetwork.totalNodes("dirichlet", totalDirichletNodes);
+
+    /*for (size_t i=0; i<totalDirichletNodes.size(); i++) {
+        bool val = false;
+        for (size_t d=0; d<dims; d++) {
+            val = val || totalDirichletNodes[i][d];
+        }
+
+        totalDirichletNodes[i] = val;
+        for (size_t d=0; d<dims; d++) {
+            totalDirichletNodes[i][d] = val;
+        }
+    }*/
+
+    //print(totalDirichletNodes, "totalDirichletNodes:");
+
+    //using Functional = Functional<Matrix&, Vector&, ZeroNonlinearity&, Vector&, Vector&, field_type>;
+    using Functional = Functional<Matrix&, Vector&, GlobalFriction<Matrix, Vector>&, Vector&, Vector&, field_type>;
+    using NonlinearFactory = SolverFactory<Functional, BitVector>;
+
+    using BoundaryFunctions = typename ContactNetwork::BoundaryFunctions;
+    using BoundaryNodes = typename ContactNetwork::BoundaryNodes;
+    using Updaters = Updaters<RateUpdater<Vector, Matrix, BoundaryFunctions, BoundaryNodes>,
+                               StateUpdater<ScalarVector, Vector>>;
+
+    BoundaryFunctions velocityDirichletFunctions;
+    contactNetwork.boundaryFunctions("dirichlet", velocityDirichletFunctions);
+
+    BoundaryNodes dirichletNodes;
+    contactNetwork.boundaryNodes("dirichlet", dirichletNodes);
+
+    /*for (size_t i=0; i<dirichletNodes.size(); i++) {
+        for (size_t j=0; j<dirichletNodes[i].size(); j++) {
+        print(*dirichletNodes[i][j], "dirichletNodes_body_" + std::to_string(i) + "_boundary_" + std::to_string(j));
+        }
+    }*/
+
+    std::vector<const Dune::BitSetVector<1>*> frictionNodes;
+    contactNetwork.frictionNodes(frictionNodes);
+
+    /*for (size_t i=0; i<frictionNodes.size(); i++) {
+        print(*frictionNodes[i], "frictionNodes_body_" + std::to_string(i));
+    }*/
+
+    //DUNE_THROW(Dune::Exception, "Just need to stop here!");
+
+    Updaters current(
+        initRateUpdater(
+            parset.get<Config::scheme>("timeSteps.scheme"),
+            velocityDirichletFunctions,
+            dirichletNodes,
+            contactNetwork.matrices(),
+            programState.u,
+            programState.v,
+            programState.a),
+        initStateUpdater<ScalarVector, Vector>(
+            parset.get<Config::stateModel>("boundary.friction.stateModel"),
+            programState.alpha,
+            nBodyAssembler.getContactCouplings(),
+            contactNetwork.couplings())
+            );
+
+
+    auto const refinementTolerance = parset.get<double>("timeSteps.refinementTolerance");
+
+    const auto& stateEnergyNorms = contactNetwork.stateEnergyNorms();
+
+    auto const mustRefine = [&](Updaters &coarseUpdater,
+                                Updaters &fineUpdater) {
+
+        //return false;
+      //std::cout << "Step 1" << std::endl;
+
+      std::vector<ScalarVector> coarseAlpha;
+      coarseAlpha.resize(bodyCount);
+      coarseUpdater.state_->extractAlpha(coarseAlpha);
+
+      //print(coarseAlpha, "coarseAlpha:");
+
+      std::vector<ScalarVector> fineAlpha;
+      fineAlpha.resize(bodyCount);
+      fineUpdater.state_->extractAlpha(fineAlpha);
+
+      //print(fineAlpha, "fineAlpha:");
+
+      //std::cout << "Step 3" << std::endl;
+
+      ScalarVector::field_type energyNorm = 0;
+      for (size_t i=0; i<stateEnergyNorms.size(); i++) {
+          //std::cout << "for " << i << std::endl;
+
+          //std::cout << not stateEnergyNorms[i] << std::endl;
+
+          if (coarseAlpha[i].size()==0 || fineAlpha[i].size()==0)
+              continue;
+
+          energyNorm += stateEnergyNorms[i]->diff(fineAlpha[i], coarseAlpha[i]);
+      }
+      std::cout << "energy norm: " << energyNorm << " tol: " << refinementTolerance <<  std::endl;
+      std::cout << "must refine: " << (energyNorm > refinementTolerance) <<  std::endl;
+      return energyNorm > refinementTolerance;
+    };
+
+
+    std::signal(SIGXCPU, handleSignal);
+    std::signal(SIGINT, handleSignal);
+    std::signal(SIGTERM, handleSignal);
+
+/*
+    // set patch preconditioner for linear correction in TNNMG method
+    using PatchSolver = typename Dune::Solvers::LoopSolver<Vector, BitVector>;
+    using Preconditioner = MultilevelPatchPreconditioner<ContactNetwork, PatchSolver, Matrix, Vector>;
+
+    const auto& preconditionerParset = parset.sub("solver.tnnmg.linear.preconditioner");
+
+    auto gsStep = Dune::Solvers::BlockGSStepFactory<Matrix, Vector>::create(Dune::Solvers::BlockGS::LocalSolvers::direct(0.0));
+    PatchSolver patchSolver(gsStep,
+                               preconditionerParset.get<size_t>("maximumIterations"),
+                               preconditionerParset.get<double>("tolerance"),
+                               nullptr,
+                               preconditionerParset.get<Solver::VerbosityMode>("verbosity"),
+                               false); // absolute error
+
+    Dune::BitSetVector<1> activeLevels(contactNetwork.nLevels(), true);
+    Preconditioner preconditioner(contactNetwork, activeLevels, preconditionerParset.get<Preconditioner::Mode>("mode"));
+    preconditioner.setPatchSolver(patchSolver);
+    preconditioner.setPatchDepth(preconditionerParset.get<size_t>("patchDepth"));
+*/
+    // set adaptive time stepper
+    typename ContactNetwork::ExternalForces externalForces;
+    contactNetwork.externalForces(externalForces);
+
+    AdaptiveTimeStepper<NonlinearFactory, std::decay_t<decltype(contactNetwork)>, Updaters, std::decay_t<decltype(stateEnergyNorms)>>
+        adaptiveTimeStepper(parset, contactNetwork, totalDirichletNodes, globalFriction, frictionNodes, current,
+                            programState.relativeTime, programState.relativeTau,
+                            externalForces, stateEnergyNorms, mustRefine);
+
+    size_t timeSteps = parset.get<size_t>("timeSteps.timeSteps");
+
+    while (!adaptiveTimeStepper.reachedEnd()) {
+      programState.timeStep++;
+
+      //preconditioner.build();
+      iterationCount = adaptiveTimeStepper.advance();
+
+      programState.relativeTime = adaptiveTimeStepper.relativeTime_;
+      programState.relativeTau = adaptiveTimeStepper.relativeTau_;
+      current.rate_->extractDisplacement(programState.u);
+      current.rate_->extractVelocity(programState.v);
+      current.rate_->extractAcceleration(programState.a);
+      current.state_->extractAlpha(programState.alpha);
+      globalFriction.updateAlpha(programState.alpha);
+
+      /*print(programState.u, "current u:");
+      print(programState.v, "current v:");
+      print(programState.a, "current a:");
+      print(programState.alpha, "current alpha:");*/
+
+      contactNetwork.setDeformation(programState.u);
+
+      report();
+
+      if (programState.timeStep==timeSteps) {
+        std::cout << "limit of timeSteps reached!" << std::endl;
+        break; // TODO remove after debugging
+      }
+
+      if (terminationRequested) {
+        std::cerr << "Terminating prematurely" << std::endl;
+        break;
+      }
+
+
+    }
+
+
+    std::cout.rdbuf(coutbuf); //reset to standard output again
+
+  } catch (Dune::Exception &e) {
+    Dune::derr << "Dune reported error: " << e << std::endl;
+  } catch (std::exception &e) {
+    std::cerr << "Standard exception: " << e.what() << std::endl;
+  }
+}
diff --git a/src/strikeslip/strikeslip.cfg b/src/strikeslip/strikeslip.cfg
new file mode 100644
index 00000000..294e3ea3
--- /dev/null
+++ b/src/strikeslip/strikeslip.cfg
@@ -0,0 +1,112 @@
+# -*- mode:conf -*-
+gravity         = 9.81     # [m/s^2]
+
+[body0]
+length          = 0.5      # [m]
+height          = 0.5     # [m]
+depth           = 0.12     # [m]
+bulkModulus     = 1.5e5    # [Pa] #2190
+poissonRatio    = 0.11     # [1]  #0.11
+[body0.elastic]
+distFromDiag    = 0.2
+density         = 1300      # [kg/m^3] #750
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+[body0.viscoelastic]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 1e4     # [Pas]
+bulkViscosity   = 1e4     # [Pas]
+
+[body1]
+length          = 0.5     # [m]
+height          = 0.5     # [m]
+depth           = 0.12     # [m]
+bulkModulus     = 1.5e5    # [Pa]
+poissonRatio    = 0.11     # [1]
+[body1.elastic]
+distFromDiag    = 0.2
+density         = 1300      # [kg/m^3]
+shearViscosity  = 0     # [Pas]
+bulkViscosity   = 0     # [Pas]
+[body1.viscoelastic]
+density         = 1300     # [kg/m^3]
+shearViscosity  = 1e4     # [Pas]
+bulkViscosity   = 1e4     # [Pas]
+
+[boundary.friction]
+C               = 6       # [Pa]
+mu0             = 0.48      # [ ]
+V0              = 1e-3     # [m/s]
+L               = 1e-6  # [m]
+initialAlpha    = 0        # [ ]
+stateModel      = AgeingLaw
+frictionModel   = Truncated #Regularised
+[boundary.friction.weakening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+[boundary.friction.strengthening]
+a               = 0.054    # [ ]
+b               = 0.074    # [ ]
+
+[boundary.neumann]
+sigmaN          = 200.0      # [Pa]
+
+[boundary.dirichlet]
+finalVelocity   = 1e-4     # [m/s]
+
+[io]
+data.write      = true
+printProgress   = true
+restarts.first  = 0
+restarts.spacing= 20
+restarts.write  = false #true
+vtk.write       = true
+
+[problem]
+finalTime       = 100     # [s] #1000
+bodyCount       = 2
+
+[initialTime]
+timeStep = 0
+relativeTime = 0.0
+relativeTau = 2e-4 # 1e-6
+
+[timeSteps]
+scheme = newmark
+timeSteps = 5000
+
+[u0.solver]
+maximumIterations = 100
+verbosity         = full
+
+[a0.solver]
+maximumIterations = 100
+verbosity         = full
+
+[v.solver]
+maximumIterations = 100
+verbosity         = quiet
+
+[v.fpi]
+maximumIterations = 10000
+lambda            = 0.5
+
+[solver.tnnmg.preconditioner]
+mode         = additive
+patchDepth   = 1
+maximumIterations = 2
+verbosity         = quiet
+[solver.tnnmg.preconditioner.patchsolver]
+maximumIterations = 100
+verbosity         = quiet
+[solver.tnnmg.preconditioner.basesolver]
+maximumIterations = 10000
+verbosity         = quiet
+
+[solver.tnnmg.main]
+pre   = 1
+multi = 5 # number of multigrid steps
+post  = 0
+
+
+
-- 
GitLab