From 09bbd1b5fd796a22036df2939e8eec392238fb07 Mon Sep 17 00:00:00 2001
From: podlesny <podlesny@zedat.fu-berlin.de>
Date: Fri, 16 Feb 2018 16:21:42 +0100
Subject: [PATCH] .

---
 dune-tectonic.pc.in                           |   2 +-
 dune.module                                   |   2 +-
 dune/tectonic/globalratestatefriction.hh      |   6 +-
 dune/tectonic/myblockproblem.hh               |   2 +-
 src/CMakeLists.txt                            |  28 +
 src/explicitgrid.hh                           |   7 -
 src/gridselector.hh                           |  30 -
 src/hdf5/frictionalboundary-writer.cc         |  14 +-
 src/hdf5/frictionalboundary-writer_tmpl.cc    |  11 -
 src/hdf5/patchinfo-writer.cc                  |   3 +-
 src/hdf5/restart-io.cc                        |  22 +-
 src/hdf5/surface-writer.cc                    |   6 +-
 src/matrices.hh                               |  15 +-
 src/multi-body-problem-2D.cfg                 |  21 +
 src/multi-body-problem-3D.cfg                 |  24 +
 src/multi-body-problem-data/bc.hh             |  18 +
 src/multi-body-problem-data/cuboidgeometry.cc | 190 +++++++
 src/multi-body-problem-data/cuboidgeometry.hh |  77 +++
 src/multi-body-problem-data/geometry.tex      |  68 +++
 src/multi-body-problem-data/midpoint.hh       |  12 +
 src/multi-body-problem-data/mybody.hh         |  55 ++
 .../myglobalfrictiondata.hh                   |  42 ++
 src/multi-body-problem-data/mygrids.cc        | 250 +++++++++
 src/multi-body-problem-data/mygrids.hh        |  91 +++
 src/multi-body-problem-data/mygrids_tmpl.cc   |  22 +
 src/multi-body-problem-data/patchfunction.hh  |  31 ++
 .../segmented-function.hh                     |  34 ++
 src/multi-body-problem-data/weakpatch.hh      |  34 ++
 src/multi-body-problem.cc                     | 523 ++++++++++++++++++
 src/multi-body-problem.cfg                    |  71 +++
 src/one-body-problem.cc                       |  31 +-
 src/program_state.hh                          | 108 ++--
 src/spatial-solving/fixedpointiterator.cc     | 306 +++++++++-
 src/spatial-solving/fixedpointiterator.hh     |  53 --
 src/spatial-solving/solverfactory.cc          |  59 --
 src/spatial-solving/solverfactory.hh          |  48 --
 src/spatial-solving/solverfactory_tmpl.cc     |  22 -
 src/time-stepping/rate.cc                     |  14 +-
 src/time-stepping/rate.hh                     |  10 +-
 src/time-stepping/rate/backward_euler.cc      | 142 ++---
 src/time-stepping/rate/backward_euler.hh      |  15 +-
 src/time-stepping/rate/newmark.cc             | 116 ++--
 src/time-stepping/rate/newmark.hh             |  15 +-
 src/time-stepping/rate/rateupdater.cc         |  24 +-
 src/time-stepping/rate/rateupdater.hh         |  26 +-
 src/time-stepping/rate_tmpl.cc                |  11 +-
 src/time-stepping/state.cc                    |   5 +-
 src/time-stepping/state.hh                    |   4 +-
 .../state/ageinglawstateupdater.cc            |  33 +-
 .../state/ageinglawstateupdater.hh            |  20 +-
 .../state/sliplawstateupdater.cc              |  35 +-
 .../state/sliplawstateupdater.hh              |  28 +-
 src/time-stepping/state/stateupdater.hh       |   4 +-
 src/time-stepping/state_tmpl.cc               |   4 +-
 54 files changed, 2307 insertions(+), 537 deletions(-)
 create mode 100644 src/multi-body-problem-2D.cfg
 create mode 100644 src/multi-body-problem-3D.cfg
 create mode 100644 src/multi-body-problem-data/bc.hh
 create mode 100644 src/multi-body-problem-data/cuboidgeometry.cc
 create mode 100644 src/multi-body-problem-data/cuboidgeometry.hh
 create mode 100644 src/multi-body-problem-data/geometry.tex
 create mode 100644 src/multi-body-problem-data/midpoint.hh
 create mode 100644 src/multi-body-problem-data/mybody.hh
 create mode 100644 src/multi-body-problem-data/myglobalfrictiondata.hh
 create mode 100644 src/multi-body-problem-data/mygrids.cc
 create mode 100644 src/multi-body-problem-data/mygrids.hh
 create mode 100644 src/multi-body-problem-data/mygrids_tmpl.cc
 create mode 100644 src/multi-body-problem-data/patchfunction.hh
 create mode 100644 src/multi-body-problem-data/segmented-function.hh
 create mode 100644 src/multi-body-problem-data/weakpatch.hh
 create mode 100644 src/multi-body-problem.cc
 create mode 100644 src/multi-body-problem.cfg

diff --git a/dune-tectonic.pc.in b/dune-tectonic.pc.in
index d9c759b8..82d6d483 100644
--- a/dune-tectonic.pc.in
+++ b/dune-tectonic.pc.in
@@ -10,6 +10,6 @@ Name: @PACKAGE_NAME@
 Version: @VERSION@
 Description: dune-tectonic module
 URL: http://dune-project.org/
-Requires: dune-common dune-fufem dune-grid dune-istl dune-solvers dune-tnnmg dune-uggrid
+Requires: dune-common dune-contact dune-fufem dune-grid dune-istl dune-solvers dune-tnnmg dune-uggrid
 Libs: -L${libdir}
 Cflags: -I${includedir}
diff --git a/dune.module b/dune.module
index e5c50280..8e630071 100644
--- a/dune.module
+++ b/dune.module
@@ -5,4 +5,4 @@
 Module: dune-tectonic
 Version: 2.5-1
 Maintainer: elias.pipping@fu-berlin.de
-Depends: dune-common dune-fufem dune-grid dune-istl dune-solvers dune-tnnmg dune-uggrid
+Depends: dune-common dune-contact dune-fufem dune-grid dune-istl dune-solvers dune-tnnmg dune-uggrid
diff --git a/dune/tectonic/globalratestatefriction.hh b/dune/tectonic/globalratestatefriction.hh
index d6c61470..e233523d 100644
--- a/dune/tectonic/globalratestatefriction.hh
+++ b/dune/tectonic/globalratestatefriction.hh
@@ -32,7 +32,7 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
       : restrictions(), localToGlobal(), zeroFriction() {
     auto const gridView = frictionalBoundary.gridView();
     Dune::MultipleCodimMultipleGeomTypeMapper<
-        GridView, Dune::MCMGVertexLayout> const vertexMapper(gridView);
+        GridView, Dune::MCMGVertexLayout> const vertexMapper(gridView, Dune::mcmgVertexLayout());
     for (auto it = gridView.template begin<block_size>();
          it != gridView.template end<block_size>(); ++it) {
       auto const i = vertexMapper.index(*it);
@@ -44,8 +44,8 @@ class GlobalRateStateFriction : public GlobalFriction<Matrix, Vector> {
       restrictions.emplace_back(weights[i], weightedNormalStress[i],
                                 frictionInfo(geoToPoint(it->geometry())));
     }
-    assert(restrictions.size() == frictionalBoundary.numVertices());
-    assert(localToGlobal.size() == frictionalBoundary.numVertices());
+    assert(restrictions.size() == (size_t) frictionalBoundary.numVertices());
+    assert(localToGlobal.size() == (size_t) frictionalBoundary.numVertices());
   }
 
   void updateAlpha(ScalarVector const &alpha) override {
diff --git a/dune/tectonic/myblockproblem.hh b/dune/tectonic/myblockproblem.hh
index be3130ae..78cf36f4 100644
--- a/dune/tectonic/myblockproblem.hh
+++ b/dune/tectonic/myblockproblem.hh
@@ -57,7 +57,7 @@ class MyBlockProblem : /* not public */ BlockNonlinearGSProblem<ConvexProblem> {
   MyBlockProblem(Dune::ParameterTree const &parset, ConvexProblem &problem)
       : Base(parset, problem),
         maxEigenvalues_(problem.f.size()),
-        localBisection(0.0, 1.0, 0.0, true, 0.0) {
+        localBisection(0.0, 1.0, 0.0, 0.0) {
     for (size_t i = 0; i < problem.f.size(); ++i) {
       LocalVectorType eigenvalues;
       Dune::FMatrixHelp::eigenValues(problem.A[i][i], eigenvalues);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 384e9885..3bb47317 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -19,6 +19,29 @@ set(SW_SOURCE_FILES
   time-stepping/state.cc
   vtk.cc
 )
+
+set(MSW_SOURCE_FILES
+  assemblers.cc
+  enumparser.cc
+  hdf5/frictionalboundary-writer.cc
+  hdf5/iteration-writer.cc
+  hdf5/patchinfo-writer.cc
+  hdf5/restart-io.cc
+  hdf5/surface-writer.cc
+  hdf5/time-writer.cc
+  multi-body-problem-data/cuboidgeometry.cc
+  multi-body-problem-data/mygrids.cc
+  multi-body-problem.cc
+  spatial-solving/fixedpointiterator.cc
+  spatial-solving/solverfactory.cc
+  time-stepping/adaptivetimestepper.cc
+  time-stepping/coupledtimestepper.cc
+  time-stepping/rate.cc
+  time-stepping/rate/rateupdater.cc
+  time-stepping/state.cc
+  vtk.cc
+)
+
 set(UGW_SOURCE_FILES
   assemblers.cc # FIXME
   one-body-problem-data/mygrid.cc
@@ -28,15 +51,20 @@ set(UGW_SOURCE_FILES
 
 foreach(_dim 2 3)
   set(_sw_target one-body-problem-${_dim}D)
+  set(_msw_target multi-body-problem-${_dim}D)
   set(_ugw_target uniform-grid-writer-${_dim}D)
 
   add_executable(${_sw_target} ${SW_SOURCE_FILES})
+  add_executable(${_msw_target} ${MSW_SOURCE_FILES})
   add_executable(${_ugw_target} ${UGW_SOURCE_FILES})
   add_dune_ug_flags(${_sw_target})
+  add_dune_ug_flags(${_msw_target})
   add_dune_ug_flags(${_ugw_target})
 
   add_dune_hdf5_flags(${_sw_target})
+  add_dune_hdf5_flags(${_msw_target})
 
   set_property(TARGET ${_sw_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
+  set_property(TARGET ${_msw_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
   set_property(TARGET ${_ugw_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
 endforeach()
diff --git a/src/explicitgrid.hh b/src/explicitgrid.hh
index fcbac18c..e69de29b 100644
--- a/src/explicitgrid.hh
+++ b/src/explicitgrid.hh
@@ -1,7 +0,0 @@
-#ifndef SRC_EXPLICITGRID_HH
-#define SRC_EXPLICITGRID_HH
-
-#include "gridselector.hh"
-
-using GridView = Grid::LeafGridView;
-#endif
diff --git a/src/gridselector.hh b/src/gridselector.hh
index 94fb9d85..e69de29b 100644
--- a/src/gridselector.hh
+++ b/src/gridselector.hh
@@ -1,30 +0,0 @@
-#ifndef MY_DIM
-#error MY_DIM unset
-#endif
-
-#define WANT_ALUGRID 0
-#define WANT_UG 1
-
-#define WANT_GRID WANT_UG
-
-#if WANT_GRID == WANT_ALUGRID
-
-#if !HAVE_ALUGRID
-#error ALUGRID was requested but not found
-#endif
-#include <dune/grid/alugrid.hh>
-using Grid = Dune::ALUGrid<MY_DIM, MY_DIM, Dune::simplex, Dune::nonconforming>;
-
-#elif WANT_GRID == WANT_UG
-
-#if !HAVE_UG
-#error UG was requested but not found
-#endif
-#include <dune/grid/uggrid.hh>
-using Grid = Dune::UGGrid<MY_DIM>;
-
-#else
-
-#error requested a grid that does not exist
-
-#endif
diff --git a/src/hdf5/frictionalboundary-writer.cc b/src/hdf5/frictionalboundary-writer.cc
index e78b70f3..08bd86da 100644
--- a/src/hdf5/frictionalboundary-writer.cc
+++ b/src/hdf5/frictionalboundary-writer.cc
@@ -35,23 +35,27 @@ template <class ProgramState, class GridView>
 template <class Friction>
 void FrictionalBoundaryWriter<ProgramState, GridView>::write(
     ProgramState const &programState, Friction &friction) {
+    // TODO
   auto const frictionalBoundaryDisplacements =
-      restrictToSurface(programState.u, frictionalBoundary_);
+      restrictToSurface(programState.u[0], frictionalBoundary_);
   addEntry(frictionalBoundaryDisplacementWriter_, programState.timeStep,
            frictionalBoundaryDisplacements);
 
   auto const frictionalBoundaryVelocities =
-      restrictToSurface(programState.v, frictionalBoundary_);
+          // TODO
+      restrictToSurface(programState.v[0], frictionalBoundary_);
   addEntry(frictionalBoundaryVelocityWriter_, programState.timeStep,
            frictionalBoundaryVelocities);
 
   auto const frictionalBoundaryStates =
-      restrictToSurface(programState.alpha, frictionalBoundary_);
+      // TODO
+      restrictToSurface(programState.alpha[0], frictionalBoundary_);
   addEntry(frictionalBoundaryStateWriter_, programState.timeStep,
            frictionalBoundaryStates);
 
-  friction.updateAlpha(programState.alpha);
-  auto const c = friction.coefficientOfFriction(programState.v);
+  // TODO
+  friction.updateAlpha(programState.alpha[0]);
+  auto const c = friction.coefficientOfFriction(programState.v[0]);
   auto const frictionalBoundaryCoefficient =
       restrictToSurface(c, frictionalBoundary_);
   addEntry(frictionalBoundaryCoefficientWriter_, programState.timeStep,
diff --git a/src/hdf5/frictionalboundary-writer_tmpl.cc b/src/hdf5/frictionalboundary-writer_tmpl.cc
index e39ffe0f..e69de29b 100644
--- a/src/hdf5/frictionalboundary-writer_tmpl.cc
+++ b/src/hdf5/frictionalboundary-writer_tmpl.cc
@@ -1,11 +0,0 @@
-#include "../explicitvectors.hh"
-#include "../explicitgrid.hh"
-
-#include "../program_state.hh"
-
-using MyProgramState = ProgramState<Vector, ScalarVector>;
-using MyFriction = GlobalFriction<Matrix, Vector>;
-
-template class FrictionalBoundaryWriter<MyProgramState, GridView>;
-template void FrictionalBoundaryWriter<MyProgramState, GridView>::write(
-    MyProgramState const &programState, MyFriction &friction);
diff --git a/src/hdf5/patchinfo-writer.cc b/src/hdf5/patchinfo-writer.cc
index 2df15c5f..fc723fc7 100644
--- a/src/hdf5/patchinfo-writer.cc
+++ b/src/hdf5/patchinfo-writer.cc
@@ -87,7 +87,8 @@ PatchInfoWriter<ProgramState, VertexBasis, GridView>::PatchInfoWriter(
 template <class ProgramState, class VertexBasis, class GridView>
 void PatchInfoWriter<ProgramState, VertexBasis, GridView>::write(
     ProgramState const &programState) {
-  BasisGridFunction<VertexBasis, Vector> velocity(vertexBasis_, programState.v);
+    // TODO
+  BasisGridFunction<VertexBasis, Vector> velocity(vertexBasis_, programState.v[0]);
   auto const gridVelocity = gridEvaluator_.evaluate(velocity);
   addEntry(weakPatchGridVelocityWriter_, programState.timeStep, gridVelocity);
 }
diff --git a/src/hdf5/restart-io.cc b/src/hdf5/restart-io.cc
index d12ca34e..c5724e55 100644
--- a/src/hdf5/restart-io.cc
+++ b/src/hdf5/restart-io.cc
@@ -19,12 +19,13 @@ RestartIO<ProgramState>::RestartIO(HDF5::Grouplike &group, size_t vertexCount)
 
 template <class ProgramState>
 void RestartIO<ProgramState>::write(ProgramState const &programState) {
-  addEntry(displacementWriter_, programState.timeStep, programState.u);
-  addEntry(velocityWriter_, programState.timeStep, programState.v);
-  addEntry(accelerationWriter_, programState.timeStep, programState.a);
-  addEntry(stateWriter_, programState.timeStep, programState.alpha);
+    // TODO
+  addEntry(displacementWriter_, programState.timeStep, programState.u[0]);
+  addEntry(velocityWriter_, programState.timeStep, programState.v[0]);
+  addEntry(accelerationWriter_, programState.timeStep, programState.a[0]);
+  addEntry(stateWriter_, programState.timeStep, programState.alpha[0]);
   addEntry(weightedNormalStressWriter_, programState.timeStep,
-           programState.weightedNormalStress);
+           programState.weightedNormalStress[0]);
   addEntry(relativeTimeWriter_, programState.timeStep,
            programState.relativeTime);
   addEntry(relativeTimeIncrementWriter_, programState.timeStep,
@@ -35,12 +36,13 @@ template <class ProgramState>
 void RestartIO<ProgramState>::read(size_t timeStep,
                                    ProgramState &programState) {
   programState.timeStep = timeStep;
-  readEntry(displacementWriter_, timeStep, programState.u);
-  readEntry(velocityWriter_, timeStep, programState.v);
-  readEntry(accelerationWriter_, timeStep, programState.a);
-  readEntry(stateWriter_, timeStep, programState.alpha);
+  // TODO
+  readEntry(displacementWriter_, timeStep, programState.u[0]);
+  readEntry(velocityWriter_, timeStep, programState.v[0]);
+  readEntry(accelerationWriter_, timeStep, programState.a[0]);
+  readEntry(stateWriter_, timeStep, programState.alpha[0]);
   readEntry(weightedNormalStressWriter_, timeStep,
-            programState.weightedNormalStress);
+            programState.weightedNormalStress[0]);
   readEntry(relativeTimeWriter_, timeStep, programState.relativeTime);
   readEntry(relativeTimeIncrementWriter_, timeStep, programState.relativeTau);
 }
diff --git a/src/hdf5/surface-writer.cc b/src/hdf5/surface-writer.cc
index bdab1213..207522bd 100644
--- a/src/hdf5/surface-writer.cc
+++ b/src/hdf5/surface-writer.cc
@@ -24,11 +24,13 @@ SurfaceWriter<ProgramState, GridView>::SurfaceWriter(
 template <class ProgramState, class GridView>
 void SurfaceWriter<ProgramState, GridView>::write(
     ProgramState const &programState) {
-  auto const surfaceDisplacements = restrictToSurface(programState.u, surface_);
+    // TODO
+  auto const surfaceDisplacements = restrictToSurface(programState.u[0], surface_);
   addEntry(surfaceDisplacementWriter_, programState.timeStep,
            surfaceDisplacements);
 
-  auto const surfaceVelocities = restrictToSurface(programState.v, surface_);
+  // TODO
+  auto const surfaceVelocities = restrictToSurface(programState.v[0], surface_);
   addEntry(surfaceVelocityWriter_, programState.timeStep, surfaceVelocities);
 }
 
diff --git a/src/matrices.hh b/src/matrices.hh
index fc51d472..4fcaabfb 100644
--- a/src/matrices.hh
+++ b/src/matrices.hh
@@ -1,9 +1,16 @@
 #ifndef SRC_MATRICES_HH
 #define SRC_MATRICES_HH
 
-template <class Matrix> struct Matrices {
-  Matrix elasticity;
-  Matrix damping;
-  Matrix mass;
+template <class Matrix> class Matrices {
+public:
+  std::vector<std::shared_ptr<Matrix>> elasticity;
+  std::vector<std::shared_ptr<Matrix>> damping;
+  std::vector<std::shared_ptr<Matrix>> mass;
+
+  Matrices(size_t n) {
+      elasticity.resize(n);
+      damping.resize(n);
+      mass.resize(n);
+  }
 };
 #endif
diff --git a/src/multi-body-problem-2D.cfg b/src/multi-body-problem-2D.cfg
new file mode 100644
index 00000000..a61cfba2
--- /dev/null
+++ b/src/multi-body-problem-2D.cfg
@@ -0,0 +1,21 @@
+# -*- mode:conf -*-
+[boundary.friction]
+smallestDiameter= 2e-3  # [m]
+
+[timeSteps]
+refinementTolerance = 1e-5
+
+[u0.solver]
+tolerance         = 1e-8
+
+[a0.solver]
+tolerance         = 1e-8
+
+[v.solver]
+tolerance         = 1e-8
+
+[v.fpi]
+tolerance         = 1e-5
+
+[solver.tnnmg.linear]
+tolerance          = 1e-10
diff --git a/src/multi-body-problem-3D.cfg b/src/multi-body-problem-3D.cfg
new file mode 100644
index 00000000..3ff0794d
--- /dev/null
+++ b/src/multi-body-problem-3D.cfg
@@ -0,0 +1,24 @@
+# -*- mode:conf -*-
+[boundary.friction]
+smallestDiameter= 2e-2  # [m]
+
+[boundary.friction.weakening]
+patchType       = Trapezoidal
+
+[timeSteps]
+refinementTolerance = 1e-5
+
+[u0.solver]
+tolerance         = 1e-6
+
+[a0.solver]
+tolerance         = 1e-6
+
+[v.solver]
+tolerance         = 1e-6
+
+[v.fpi]
+tolerance         = 1e-5
+
+[solver.tnnmg.linear]
+tolerance          = 1e-10
diff --git a/src/multi-body-problem-data/bc.hh b/src/multi-body-problem-data/bc.hh
new file mode 100644
index 00000000..7c29cf08
--- /dev/null
+++ b/src/multi-body-problem-data/bc.hh
@@ -0,0 +1,18 @@
+#ifndef SRC_ONE_BODY_PROBLEM_DATA_BC_HH
+#define SRC_ONE_BODY_PROBLEM_DATA_BC_HH
+
+class VelocityDirichletCondition
+    : public Dune::VirtualFunction<double, double> {
+  void evaluate(double const &relativeTime, double &y) const {
+    // Assumed to vanish at time zero
+    double const finalVelocity = -5e-5;
+    y = (relativeTime <= 0.1)
+            ? finalVelocity * (1.0 - std::cos(relativeTime * M_PI / 0.1)) / 2.0
+            : finalVelocity;
+  }
+};
+
+class NeumannCondition : public Dune::VirtualFunction<double, double> {
+  void evaluate(double const &relativeTime, double &y) const { y = 0.0; }
+};
+#endif
diff --git a/src/multi-body-problem-data/cuboidgeometry.cc b/src/multi-body-problem-data/cuboidgeometry.cc
new file mode 100644
index 00000000..aaf4dfe0
--- /dev/null
+++ b/src/multi-body-problem-data/cuboidgeometry.cc
@@ -0,0 +1,190 @@
+#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 "cuboidgeometry.hh"
+
+/*
+const CuboidGeometry::LocalVector CuboidGeometry::lift(const double v0, const double v1) {
+  vc = 0;
+  vc[0] = vc2D[0];
+  vc[1] = vc2D[1];
+}
+
+const CuboidGeometry::LocalVector& CuboidGeometry::A() const { return A_; }
+const CuboidGeometry::LocalVector& CuboidGeometry::B() const { return B_; }
+const CuboidGeometry::LocalVector& CuboidGeometry::C() const { return C_; }
+const CuboidGeometry::LocalVector& CuboidGeometry::D() const { return D_; }
+const CuboidGeometry::LocalVector& CuboidGeometry::X() const { return X_; }
+const CuboidGeometry::LocalVector& CuboidGeometry::Y() const { return Y_; }
+double CuboidGeometry::depth() const { return depth_; }
+
+void CuboidGeometry::setupWeak(const LocalVector& weakOrigin, const double weakLength) {
+    lift(weakOrigin, X_);
+    const LocalVector2D Y({X_[0]+weakLength, X_[1]});
+    lift(Y, Y_);
+}
+*/
+
+#if MY_DIM == 3
+CuboidGeometry::CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength, const double length, const double width, const double depth_):
+    length_(length*lengthScale),
+    width_(width*lengthScale),
+    A(origin),
+    B({origin[0]+length_, origin[1], 0}),
+    C({origin[0]+length_, origin[1]+width_, 0}),
+    D({origin[0], origin[1]+width_, 0}),
+    X(weakOrigin),
+    Y({X[0]+weakLength, X[1], 0}),
+    depth(depth_*lengthScale) {}
+#else
+CuboidGeometry::CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength, const double length, const double width):
+    length_(length*lengthScale),
+    width_(width*lengthScale),
+    A(origin),
+    B({origin[0]+length_, origin[1]}),
+    C({origin[0]+length_, origin[1]+width_}),
+    D({origin[0], origin[1]+width_}),
+    X(weakOrigin),
+    Y({X[0]+weakLength, X[1]}) {}
+#endif
+
+
+void CuboidGeometry::write() const {
+  std::fstream writer("geometry", std::fstream::out);
+  writer << "A = " << A << std::endl;
+  writer << "B = " << B << std::endl;
+  writer << "C = " << C << std::endl;
+  writer << "D = " << D << std::endl;
+  writer << "X = " << X << std::endl;
+  writer << "Y = " << Y << std::endl;
+}
+
+void CuboidGeometry::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
+}
diff --git a/src/multi-body-problem-data/cuboidgeometry.hh b/src/multi-body-problem-data/cuboidgeometry.hh
new file mode 100644
index 00000000..24245448
--- /dev/null
+++ b/src/multi-body-problem-data/cuboidgeometry.hh
@@ -0,0 +1,77 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_CUBOIDGEOMETRY_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_CUBOIDGEOMETRY_HH
+
+#include <dune/common/fvector.hh>
+
+#include "midpoint.hh"
+
+class CuboidGeometry {
+  public:
+    typedef Dune::FieldVector<double, MY_DIM> LocalVector;
+
+    constexpr static double const lengthScale = 1.0; // scaling factor
+
+  private:
+    const double length_;
+    const double width_;
+    //const double weakLength_;     // length of the weak zone
+
+  public:
+    const LocalVector A;
+    const LocalVector B;
+    const LocalVector C;
+    const LocalVector D;
+    const LocalVector X;
+    const LocalVector Y;
+
+#if MY_DIM == 3
+    const double depth;
+
+    CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength = 0.20, const double length = 1.00, const double width = 0.27, const double depth = 0.60);
+#else
+        CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength = 0.20, const double length = 1.00, const double width = 0.27);
+#endif
+
+    void write() const;
+
+    void render() const;
+};
+#endif
+
+  /* from Elias
+  double const s = 1.0; // scaling factor
+
+  double const rightLeg = 0.27 * s;
+  double const leftLeg = 1.00 * s;
+  double const leftAngle = atan(rightLeg / leftLeg);
+  double const viscoHeight = 0.06 * s; // Height of the viscous bottom layer
+  double const weakLen = 0.20 * s;     // Length of the weak zone
+
+  double const zDistance = 0.35;
+
+  LocalVector2D const A = {0, 0};
+  LocalVector2D const B = {leftLeg, -rightLeg};
+  LocalVector2D const C = {leftLeg, 0};
+
+  LocalVector2D const Z = {zDistance * s, 0};
+  LocalVector2D const Y = {zDistance * s, -zDistance *s / leftLeg *rightLeg};
+  LocalVector2D const X = {Y[0] - weakLen * std::cos(leftAngle),
+                           Y[1] + weakLen *std::sin(leftAngle)};
+
+  LocalVector2D const U = {X[0], 0};
+
+  LocalVector2D const K = {B[0] - leftLeg * viscoHeight / rightLeg,
+                           B[1] + viscoHeight};
+  LocalVector2D const M = {B[0], B[1] + viscoHeight};
+
+  LocalVector2D const G = midPoint(A, X);
+  LocalVector2D const H = midPoint(X, Y);
+  LocalVector2D const J = midPoint(Y, B);
+
+  LocalVector2D const I = {Y[0] + G[0], Y[1] + G[1]};
+
+  LocalVector2D const zenith = {0, 1};
+
+  LocalMatrix2D const rotation = {{std::cos(leftAngle), -std::sin(leftAngle)},
+                                  {std::sin(leftAngle), std::cos(leftAngle)}};
+  */
diff --git a/src/multi-body-problem-data/geometry.tex b/src/multi-body-problem-data/geometry.tex
new file mode 100644
index 00000000..32d63b6e
--- /dev/null
+++ b/src/multi-body-problem-data/geometry.tex
@@ -0,0 +1,68 @@
+\documentclass[tikz]{minimal}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{decorations.pathreplacing}
+
+\usepackage{siunitx}
+
+\begin{document}
+\pgfmathsetmacro{\rightleg}{0.27}
+\pgfmathsetmacro{\leftleg}{1.00}
+\pgfmathsetmacro{\leftangle}{atan(\rightleg/\leftleg)}
+\begin{tikzpicture}[scale=12, rotate=\leftangle]
+  \pgfmathsetmacro{\mysin}{sin(\leftangle)}
+  \pgfmathsetmacro{\mycos}{cos(\leftangle)}
+  \pgfmathsetmacro{\viscoheight}{0.06}
+  \pgfmathsetmacro{\Zx}{0.35}
+  \pgfmathsetmacro{\weaklen}{0.20}
+
+  \coordinate (A) at (0,0);
+  \node at (A) [left] {A};
+  \coordinate (B) at (\leftleg,-\rightleg);
+  \node at (B) [right] {B};
+  \coordinate (C) at (\leftleg,0);
+  \node at (C) [right] {C};
+
+  \draw (A) -- (B) -- (C) -- node [above=.5cm, sloped] {$\overline{AC}=\SI{100}{cm}$} (A);
+
+  \coordinate (Z) at (\Zx,0);
+  \node at (Z) [above] {Z};
+  \coordinate (Y) at ($(\Zx,-\Zx/\leftleg * \rightleg)$);
+  \node at (Y) [below] {Y};
+  \coordinate (X) at ($(Y) + (-\weaklen*\mycos,\weaklen*\mysin)$);
+  \node at (X) [below] {X};
+  \path let \p1 = (X) in coordinate (U) at ($(\x1, 0)$);
+  \node at (U) [above] {U};
+
+  \path (A) -- node [above=.25cm, sloped] {$\overline{AZ} = \SI{35}{cm}$} (Z);
+
+  \draw[color=red, thick] (X) -- node [below=.25cm] {$\overline{XY}=\SI{20}{cm}$} (Y);
+  \draw[dashed] (Y) -- (Z);
+  \draw[dashed] (U) -- (X);
+
+  \coordinate (K) at ($(B) + (-\leftleg * \viscoheight / \rightleg,\viscoheight)$);
+  \node at (K) [below] {K};
+  \coordinate (M) at ($(B) + (0, \viscoheight)$);
+  \node at (M) [right] {M};
+  \path (C) -- node [right=.5cm] {$\overline{CM} = \SI{21}{cm}$} (M);
+
+  \path[fill=blue] (K) -- (B) -- node [right=.75cm] {$\overline{BM}=\SI{6}{cm}$} (M) -- cycle;
+
+  \coordinate (G) at ($(A) ! 0.5 ! (X)$);
+  \node at (G) [below] {G};
+  \coordinate (H) at ($(X) ! 0.5 ! (Y)$);
+  \node at (H) [below] {H};
+  \coordinate (J) at ($(Y) ! 0.5 ! (B)$);
+  \node at (J) [below] {J};
+
+  \coordinate (I) at ($(Y) + (G)$);
+  \node at (I) [below] {I};
+
+  \node[align=left] at (0.5,-0.225) {
+    $Z$: coast line\\
+    $\overline{XY}$: velocity weakening zone\\
+    $BKM$: visco-elastic domain};
+\end{tikzpicture}
+
+\end{document}
diff --git a/src/multi-body-problem-data/midpoint.hh b/src/multi-body-problem-data/midpoint.hh
new file mode 100644
index 00000000..9b12a540
--- /dev/null
+++ b/src/multi-body-problem-data/midpoint.hh
@@ -0,0 +1,12 @@
+#ifndef SRC_MIDPOINT_HH
+#define SRC_MIDPOINT_HH
+
+#include <dune/matrix-vector/axpy.hh>
+
+template <class Vector> Vector midPoint(Vector const &x, Vector const &y) {
+  Vector ret(0);
+  Dune::MatrixVector::addProduct(ret, 0.5, x);
+  Dune::MatrixVector::addProduct(ret, 0.5, y);
+  return ret;
+}
+#endif
diff --git a/src/multi-body-problem-data/mybody.hh b/src/multi-body-problem-data/mybody.hh
new file mode 100644
index 00000000..08b5d4b9
--- /dev/null
+++ b/src/multi-body-problem-data/mybody.hh
@@ -0,0 +1,55 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYBODY_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_MYBODY_HH
+
+#include <dune/common/fvector.hh>
+
+#include <dune/fufem/functions/constantfunction.hh>
+
+#include <dune/tectonic/body.hh>
+#include <dune/tectonic/gravity.hh>
+
+#include "cuboidgeometry.hh"
+#include "segmented-function.hh"
+
+template <int dimension> class MyBody : public Body<dimension> {
+  using typename Body<dimension>::ScalarFunction;
+  using typename Body<dimension>::VectorField;
+
+public:
+  MyBody(Dune::ParameterTree const &parset)
+      : poissonRatio_(parset.get<double>("body.poissonRatio")),
+        youngModulus_(3.0 * parset.get<double>("body.bulkModulus") *
+                      (1.0 - 2.0 * poissonRatio_)),
+        shearViscosityField_(
+            parset.get<double>("body.elastic.shearViscosity"),
+            parset.get<double>("body.viscoelastic.shearViscosity")),
+        bulkViscosityField_(
+            parset.get<double>("body.elastic.bulkViscosity"),
+            parset.get<double>("body.viscoelastic.bulkViscosity")),
+        densityField_(parset.get<double>("body.elastic.density"),
+                      parset.get<double>("body.viscoelastic.density")),
+        gravityField_(densityField_, MyGeometry::zenith,
+                      parset.get<double>("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_;
+  SegmentedFunction const shearViscosityField_;
+  SegmentedFunction const bulkViscosityField_;
+  SegmentedFunction const densityField_;
+  Gravity<dimension> const gravityField_;
+};
+#endif
diff --git a/src/multi-body-problem-data/myglobalfrictiondata.hh b/src/multi-body-problem-data/myglobalfrictiondata.hh
new file mode 100644
index 00000000..d92e7223
--- /dev/null
+++ b/src/multi-body-problem-data/myglobalfrictiondata.hh
@@ -0,0 +1,42 @@
+#ifndef SRC_ONE_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
+#define SRC_ONE_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
+
+#include <dune/common/function.hh>
+
+#include <dune/tectonic/globalfrictiondata.hh>
+
+#include "patchfunction.hh"
+
+template <class LocalVector>
+class MyGlobalFrictionData : public GlobalFrictionData<LocalVector::dimension> {
+private:
+  using typename GlobalFrictionData<LocalVector::dimension>::VirtualFunction;
+
+public:
+  MyGlobalFrictionData(Dune::ParameterTree const &parset,
+                       ConvexPolyhedron<LocalVector> const &segment)
+      : C_(parset.get<double>("C")),
+        L_(parset.get<double>("L")),
+        V0_(parset.get<double>("V0")),
+        a_(parset.get<double>("strengthening.a"),
+           parset.get<double>("weakening.a"), segment),
+        b_(parset.get<double>("strengthening.b"),
+           parset.get<double>("weakening.b"), segment),
+        mu0_(parset.get<double>("mu0")) {}
+
+  double const &C() const override { return C_; }
+  double const &L() const override { return L_; }
+  double const &V0() const override { return V0_; }
+  VirtualFunction const &a() const override { return a_; }
+  VirtualFunction const &b() const override { return b_; }
+  double const &mu0() const override { return mu0_; }
+
+private:
+  double const C_;
+  double const L_;
+  double const V0_;
+  PatchFunction const a_;
+  PatchFunction const b_;
+  double const mu0_;
+};
+#endif
diff --git a/src/multi-body-problem-data/mygrids.cc b/src/multi-body-problem-data/mygrids.cc
new file mode 100644
index 00000000..c7e9cd0f
--- /dev/null
+++ b/src/multi-body-problem-data/mygrids.cc
@@ -0,0 +1,250 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/fufem/geometry/polyhedrondistance.hh>
+
+#include "mygrids.hh"
+#include "midpoint.hh"
+#include "../diameter.hh"
+
+#if MY_DIM == 3
+SimplexManager::SimplexManager(unsigned int shift) : shift_(shift) {}
+#endif
+
+// back-to-front, front-to-back, front-to-back
+void SimplexManager::addFromVerticesFBB(unsigned int U, unsigned int V,
+                                        unsigned int W) {
+#if MY_DIM == 3
+  unsigned int const U2 = U + shift_;
+  unsigned int const V2 = V + shift_;
+  unsigned int const W2 = W + shift_;
+
+  simplices_.push_back({ U, V, W, U2 });
+  simplices_.push_back({ V, V2, W2, U2 });
+  simplices_.push_back({ W, W2, U2, V });
+#else
+  simplices_.push_back({ U, V, W });
+#endif
+}
+
+// back-to-front, back-to-front, front-to-back
+void SimplexManager::addFromVerticesFFB(unsigned int U, unsigned int V,
+                                        unsigned int W) {
+#if MY_DIM == 3
+  unsigned int const U2 = U + shift_;
+  unsigned int const V2 = V + shift_;
+  unsigned int const W2 = W + shift_;
+
+  simplices_.push_back({ U, V, W, U2 });
+  simplices_.push_back({ V, V2, W, U2 });
+  simplices_.push_back({ V2, W, U2, W2 });
+#else
+  simplices_.push_back({ U, V, W });
+#endif
+}
+
+auto SimplexManager::getSimplices() -> SimplexList const & {
+  return simplices_;
+}
+
+// Fix: 3D case (still Elias code)
+template <class Grid> GridsConstructor<Grid>::GridsConstructor(std::vector<std::shared_ptr<CuboidGeometry>> const &cuboidGeometries_) :
+  cuboidGeometries(cuboidGeometries_)
+{
+  size_t const gridCount = cuboidGeometries.size();
+  grids.resize(gridCount);
+  gridFactories.resize(gridCount);
+
+  for (size_t idx=0; idx<grids.size(); idx++) {
+    const auto& cuboidGeometry = *cuboidGeometries[idx];
+
+    const auto& A = cuboidGeometry.A;
+    const auto& B = cuboidGeometry.B;
+    const auto& C = cuboidGeometry.C;
+    const auto& D = cuboidGeometry.D;
+
+    unsigned int const vc = 4;
+
+#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];
+        vertices[k++][i] = D[i];
+        assert(k == j * vc);
+      }
+    }
+
+#if MY_DIM == 3
+    for (size_t k = 0; k < vc; ++k) {
+      vertices[k][2] = -cuboidGeometry.depth / 2.0;
+      vertices[k + vc][2] = cuboidGeometry.depth / 2.0;
+    }
+#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);
+    sm.addFromVerticesFFB(2, 3, 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());
+  }
+}
+
+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, CuboidGeometry const &cuboidGeometry) {
+  return MyFaces<GridView>(gridView, cuboidGeometry);
+}
+
+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 * cuboidGeometry.lengthScale > x[0] or
+      x[0] > minmax0.second + 1e-14 * cuboidGeometry.lengthScale)
+    return false;
+  if (minmax1.first - 1e-14 * cuboidGeometry.lengthScale > x[1] or
+      x[1] > minmax1.second + 1e-14 * cuboidGeometry.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, CuboidGeometry const &cuboidGeometry_)
+      :
+  #if MY_DIM == 3
+        lower(gridView),
+        right(gridView),
+        upper(gridView),
+        left(gridView),
+        front(gridView),
+        back(gridView),
+  #else
+        lower(gridView),
+        right(gridView),
+        upper(gridView),
+        left(gridView),
+  #endif
+        cuboidGeometry(cuboidGeometry_)
+  {
+    lower.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(cuboidGeometry.A, cuboidGeometry.B, in.geometry().center());
+    });
+
+    right.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(cuboidGeometry.B, cuboidGeometry.C, in.geometry().center());
+    });
+
+    upper.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(cuboidGeometry.D, cuboidGeometry.C, in.geometry().center());
+    });
+
+    left.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return xyBetween(cuboidGeometry.A, cuboidGeometry.D, in.geometry().center());
+    });
+
+  #if MY_DIM == 3
+    front.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return isClose(cuboidGeometry.depth / 2.0, in.geometry().center()[2]);
+    });
+
+    back.insertFacesByProperty([&](typename GridView::Intersection const &in) {
+      return isClose(-cuboidGeometry.depth / 2.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 "mygrids_tmpl.cc"
diff --git a/src/multi-body-problem-data/mygrids.hh b/src/multi-body-problem-data/mygrids.hh
new file mode 100644
index 00000000..aa62f7f8
--- /dev/null
+++ b/src/multi-body-problem-data/mygrids.hh
@@ -0,0 +1,91 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYGRIDS_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_MYGRIDS_HH
+
+#include <dune/common/fmatrix.hh>
+#include <dune/grid/common/gridfactory.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+#include "cuboidgeometry.hh"
+
+template <class GridView> struct MyFaces {
+  BoundaryPatch<GridView> lower;
+  BoundaryPatch<GridView> right;
+  BoundaryPatch<GridView> upper;
+  BoundaryPatch<GridView> left;
+
+#if MY_DIM == 3
+  BoundaryPatch<GridView> front;
+  BoundaryPatch<GridView> back;
+#endif
+
+  MyFaces(GridView const &gridView, CuboidGeometry const &cuboidGeometry_);
+
+private:
+  CuboidGeometry const &cuboidGeometry;
+
+  bool isClose(double a, double b) {
+    return std::abs(a - b) < 1e-14 * cuboidGeometry.lengthScale;
+  };
+
+  bool isClose2(double a, double b) {
+    return std::abs(a - b) <
+           1e-14 * cuboidGeometry.lengthScale * cuboidGeometry.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);
+};
+
+class SimplexManager {
+public:
+  using SimplexList = std::vector<std::vector<unsigned int>>;
+
+#if MY_DIM == 3
+  SimplexManager(unsigned int shift);
+#endif
+
+  void addFromVerticesFBB(unsigned int U, unsigned int V, unsigned int W);
+  void addFromVerticesFFB(unsigned int U, unsigned int V, unsigned int W);
+
+  SimplexList const &getSimplices();
+
+private:
+  SimplexList simplices_;
+
+#if MY_DIM == 3
+  unsigned int const shift_;
+#endif
+};
+
+template <class Grid> class GridsConstructor {
+public:
+  GridsConstructor(std::vector<std::shared_ptr<CuboidGeometry>> const &cuboidGeometries_);
+
+  std::vector<std::shared_ptr<Grid>>& getGrids();
+
+  template <class GridView>
+  MyFaces<GridView> constructFaces(GridView const &gridView, CuboidGeometry const &cuboidGeometry);
+
+private:
+  std::vector<std::shared_ptr<CuboidGeometry>> const &cuboidGeometries;
+  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/src/multi-body-problem-data/mygrids_tmpl.cc b/src/multi-body-problem-data/mygrids_tmpl.cc
new file mode 100644
index 00000000..99ae7cd6
--- /dev/null
+++ b/src/multi-body-problem-data/mygrids_tmpl.cc
@@ -0,0 +1,22 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "../explicitgrid.hh"
+#include "../explicitvectors.hh"
+#include "cuboidgeometry.hh"
+
+template class GridsConstructor<Grid>;
+
+template struct MyFaces<GridView>;
+template struct MyFaces<LevelGridView>;
+
+template MyFaces<GridView> GridsConstructor<Grid>::constructFaces(
+    GridView const &gridView, CuboidGeometry const &CuboidGeometry_);
+
+template MyFaces<LevelGridView> GridsConstructor<Grid>::constructFaces(
+    LevelGridView const &gridView, CuboidGeometry const &CuboidGeometry_);
+
+template void refine<Grid, LocalVector>(
+    Grid &grid, ConvexPolyhedron<LocalVector> const &weakPatch,
+    double smallestDiameter, double lengthScale);
diff --git a/src/multi-body-problem-data/patchfunction.hh b/src/multi-body-problem-data/patchfunction.hh
new file mode 100644
index 00000000..72cdc867
--- /dev/null
+++ b/src/multi-body-problem-data/patchfunction.hh
@@ -0,0 +1,31 @@
+#ifndef SRC_ONE_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
+#define SRC_ONE_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
+
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/parametertree.hh>
+
+#include <dune/fufem/geometry/polyhedrondistance.hh>
+
+class PatchFunction
+    : public Dune::VirtualFunction<Dune::FieldVector<double, MY_DIM>,
+                                   Dune::FieldVector<double, 1>> {
+private:
+  using Polyhedron = ConvexPolyhedron<Dune::FieldVector<double, MY_DIM>>;
+
+  double const v1_;
+  double const v2_;
+  Polyhedron const &segment_;
+
+public:
+  PatchFunction(double v1, double v2, Polyhedron const &segment)
+      : v1_(v1), v2_(v2), segment_(segment) {}
+
+  void evaluate(Dune::FieldVector<double, MY_DIM> const &x,
+                Dune::FieldVector<double, 1> &y) const {
+    y = distance(x, segment_, 1e-6 * MyGeometry::lengthScale) <= 1e-5 ? v2_
+                                                                      : v1_;
+  }
+};
+
+#endif
diff --git a/src/multi-body-problem-data/segmented-function.hh b/src/multi-body-problem-data/segmented-function.hh
new file mode 100644
index 00000000..cab2dfef
--- /dev/null
+++ b/src/multi-body-problem-data/segmented-function.hh
@@ -0,0 +1,34 @@
+#ifndef SRC_ONE_BODY_PROBLEM_DATA_SEGMENTED_FUNCTION_HH
+#define SRC_ONE_BODY_PROBLEM_DATA_SEGMENTED_FUNCTION_HH
+
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/parametertree.hh>
+
+#include "cuboidgeometry.hh"
+
+class SegmentedFunction
+    : public Dune::VirtualFunction<Dune::FieldVector<double, MY_DIM>,
+                                   Dune::FieldVector<double, 1>> {
+private:
+  bool liesBelow(Dune::FieldVector<double, MY_DIM> const &x,
+                 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 liesBelow(MyGeometry::K, MyGeometry::M, z);
+  };
+
+  double const _v1;
+  double const _v2;
+
+public:
+  SegmentedFunction(double v1, double v2) : _v1(v1), _v2(v2) {}
+
+  void evaluate(Dune::FieldVector<double, MY_DIM> const &x,
+                Dune::FieldVector<double, 1> &y) const {
+    y = insideRegion2(x) ? _v2 : _v1;
+  }
+};
+#endif
diff --git a/src/multi-body-problem-data/weakpatch.hh b/src/multi-body-problem-data/weakpatch.hh
new file mode 100644
index 00000000..e48f5725
--- /dev/null
+++ b/src/multi-body-problem-data/weakpatch.hh
@@ -0,0 +1,34 @@
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_WEAKPATCH_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_WEAKPATCH_HH
+
+#include "cuboidgeometry.hh"
+
+template <class LocalVector>
+ConvexPolyhedron<LocalVector> getWeakPatch(Dune::ParameterTree const &parset, CuboidGeometry const &cuboidGeometry) {
+  ConvexPolyhedron<LocalVector> weakPatch;
+#if MY_DIM == 3
+  weakPatch.vertices.resize(4);
+  weakPatch.vertices[0] = weakPatch.vertices[2] = cuboidGeometry.X;
+  weakPatch.vertices[1] = weakPatch.vertices[3] = cuboidGeometry.Y;
+  for (size_t k = 0; k < 2; ++k) {
+    weakPatch.vertices[k][2] = -cuboidGeometry.depth / 2.0;
+    weakPatch.vertices[k + 2][2] = cuboidGeometry.depth / 2.0;
+  }
+  switch (parset.get<Config::PatchType>("patchType")) {
+    case Config::Rectangular:
+      break;
+    case Config::Trapezoidal:
+      weakPatch.vertices[1][0] += 0.05 * cuboidGeometry.lengthScale;
+      weakPatch.vertices[3][0] -= 0.05 * cuboidGeometry.lengthScale;
+      break;
+    default:
+      assert(false);
+  }
+#else
+  weakPatch.vertices.resize(2);
+  weakPatch.vertices[0] = cuboidGeometry.X;
+  weakPatch.vertices[1] = cuboidGeometry.Y;
+#endif
+  return weakPatch;
+};
+#endif
diff --git a/src/multi-body-problem.cc b/src/multi-body-problem.cc
new file mode 100644
index 00000000..7533b579
--- /dev/null
+++ b/src/multi-body-problem.cc
@@ -0,0 +1,523 @@
+#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/formatstring.hh>
+
+#include <dune/solvers/norms/energynorm.hh>
+
+
+/*
+#include <dune/solvers/solvers/loopsolver.hh>
+#include <dune/solvers/solvers/solver.hh>*/
+#include <dune/tnnmg/problem-classes/convexproblem.hh>
+
+#include <dune/contact/common/deformedcontinuacomplex.hh>
+#include <dune/contact/common/couplingpair.hh>
+#include <dune/contact/assemblers/nbodyassembler.hh>
+//#include <dune/contact/assemblers/dualmortarcoupling.hh>
+#include <dune/contact/projections/normalprojection.hh>
+
+#include <dune/tectonic/geocoordinate.hh>
+#include <dune/tectonic/myblockproblem.hh>
+#include <dune/tectonic/globalfriction.hh>
+#include <dune/fufem/hdf5/file.hh>
+
+#include "assemblers.hh"
+#include "diameter.hh"
+#include "enumparser.hh"
+#include "enums.hh"
+#include "gridselector.hh"
+#include "hdf5-writer.hh"
+#include "hdf5/restart-io.hh"
+#include "matrices.hh"
+#include "program_state.hh"
+#include "multi-body-problem-data/bc.hh"
+#include "multi-body-problem-data/mybody.hh"
+#include "multi-body-problem-data/cuboidgeometry.hh"
+#include "multi-body-problem-data/myglobalfrictiondata.hh"
+#include "multi-body-problem-data/mygrids.hh"
+#include "multi-body-problem-data/weakpatch.hh"
+#include "spatial-solving/solverfactory.hh"
+#include "time-stepping/adaptivetimestepper.hh"
+#include "time-stepping/rate.hh"
+#include "time-stepping/state.hh"
+#include "time-stepping/updaters.hh"
+#include "vtk.hh"
+
+// for getcwd
+#include <unistd.h>
+
+#include <dune/grid/io/file/vtk/vtkwriter.hh>
+
+#define USE_OLD_TNNMG
+
+size_t const dims = MY_DIM;
+
+template <class GridView>
+void writeToVTK(const GridView& gridView, const std::string path, const std::string name) {
+    Dune::VTKWriter<GridView> vtkwriter(gridView);
+
+    //std::ofstream lStream( "garbage.txt" );
+    std::streambuf* lBufferOld = std::cout.rdbuf();
+    //std::cout.rdbuf(lStream.rdbuf());
+
+    vtkwriter.pwrite(name, path, path);
+
+    std::cout.rdbuf( lBufferOld );
+}
+
+
+Dune::ParameterTree getParameters(int argc, char *argv[]) {
+  Dune::ParameterTree parset;
+  Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/src/multi-body-problem.cfg", parset);
+  Dune::ParameterTreeParser::readINITree(
+      Dune::Fufem::formatString("/home/mi/podlesny/software/dune/dune-tectonic/src/multi-body-problem-%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;
+    }
+
+    auto const parset = getParameters(argc, argv);
+
+    using MyAssembler = MyAssembler<LeafGridView, dims>;
+    using Matrix = MyAssembler::Matrix;
+    using Vector = MyAssembler::Vector;
+    using LocalVector = Vector::block_type;
+    using ScalarMatrix = MyAssembler::ScalarMatrix;
+    using ScalarVector = MyAssembler::ScalarVector;
+    using field_type = Matrix::field_type;
+
+    // set up material properties of all bodies
+    MyBody<dims> const body(parset);
+
+    // set up cuboid geometries
+    const size_t bodyCount = parset.get<int>("problem.bodyCount");
+    std::vector<std::shared_ptr<CuboidGeometry>> cuboidGeometries(bodyCount);
+
+    double const length = 1.00;
+    double const width = 0.27;
+    double const weakLength = 0.20;
+
+#if MY_DIM == 3
+    double const depth = 0.60;
+    // TODO: replace with make_shared
+    cuboidGeometries[0] = std::shared_ptr<CuboidGeometry>(new CuboidGeometry({0,0,0}, {0.2,width,0}, weakLength, length, width, depth));
+    for (size_t i=1; i<bodyCount; i++) {
+        cuboidGeometries[i] = std::shared_ptr<CuboidGeometry>(new CuboidGeometry(cuboidGeometries[i-1]->D, {0.6,i*width,0}, weakLength, length, width, depth));
+    }
+#else
+    // TODO: replace with make_shared
+    cuboidGeometries[0] = std::shared_ptr<CuboidGeometry>(new CuboidGeometry({0,0}, {0.2,width}, weakLength, length, width));
+    for (size_t i=1; i<bodyCount; i++) {
+        cuboidGeometries[i] = std::shared_ptr<CuboidGeometry>(new CuboidGeometry(cuboidGeometries[i-1]->D, {0.6,i*width}, weakLength, length, width));
+    }
+#endif
+
+    // set up (deformed) grids
+    GridsConstructor<Grid> gridConstructor(cuboidGeometries);
+    auto& grids = gridConstructor.getGrids();
+
+    using DeformedGridComplex = DeformedContinuaComplex<Grid, Vector>;
+    using DeformedGrid = DeformedGridComplex::DeformedGridType;
+    Dune::Contact::DeformedContinuaComplex<Grid, Vector> deformedGrids(grids);
+
+    using LeafGridView = DeformedGrid::LeafGridView;
+    using LevelGridView = DeformedGrid::LevelGridView;
+    std::vector<std::shared_ptr<LeafGridView>> leafViews(deformedGrids.size());
+    std::vector<std::shared_ptr<LevelGridView>> levelViews(deformedGrids.size());
+    std::vector<int> leafVertexCounts(deformedGrids.size());
+
+    using LeafFaces = MyFaces<LeafGridView>;
+    using LevelFaces = MyFaces<LevelGridView>;
+    std::vector<std::shared_ptr<LeafFaces>> leafFaces(deformedGrids.size());
+    std::vector<std::shared_ptr<LevelFaces>> levelFaces(deformedGrids.size());
+
+    std::vector<ConvexPolyhedron<LocalVector>> weakPatches(deformedGrids.size());
+
+    for (size_t i=0; i<deformedGrids.size(); i++) {
+        auto const &cuboidGeometry = *cuboidGeometries[i];
+
+        // define weak patch and refine grid
+        weakPatches[i] = getWeakPatch<LocalVector>(parset.sub("boundary.friction.weakening"), cuboidGeometry);
+        refine(*grids[i], weakPatch, parset.get<double>("boundary.friction.smallestDiameter"), cuboidGeometry.lengthScale);
+
+        writeToVTK(grids[i]->leafGridView(), "", "grid" + std::to_string(i));
+
+        // 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;
+
+        leafViews[i] = std::make_shared<LeafGridView>(grids[i]->leafGridView());
+        levelViews[i] = std::make_shared<LevelGridView>(grids[i]->levelGridView(0));
+
+        leafVertexCounts[i] = leafViews[i]->size(dims);
+
+        leafFaces[i] = std::make_shared<LeafFaces>(*leafViews[i], cuboidGeometry);
+        levelFaces[i] = std::make_shared<LevelFaces>(*levelViews[i], cuboidGeometry);
+    }
+
+    // set up contact couplings
+    using LeafBoundaryPatch = BoundaryPatch<LeafGridView>;
+    using LevelBoundaryPatch = BoundaryPatch<LevelGridView>;
+
+    using CouplingPair = Dune::Contact::CouplingPair<DeformedGrid, DeformedGrid, field_type>;
+    std::vector<std::shared_ptr<CouplingPair>> couplings(bodyCount-1);
+
+    for (size_t i=0; i<couplings.size(); i++) {
+      auto nonmortarPatch = std::make_shared<LevelBoundaryPatch>(levelFaces[i]->upper);
+      auto mortarPatch = std::make_shared<LevelBoundaryPatch>(levelFaces[i+1]->lower);
+
+      auto contactProjection = std::make_shared<Dune::Contact::NormalProjection<LeafBoundaryPatch>>();
+      std::shared_ptr<CouplingPair::BackEndType> backend = nullptr;
+      couplings[i]->set(i, i+1, nonmortarPatch, mortarPatch, 0.1, Dune::Contact::CouplingPairBase::STICK_SLIP, contactProjection, backend);
+    }
+
+    // set up dune-contact nBodyAssembler
+    std::vector<const DeformedGrid*> const_grids(bodyCount);
+    for (size_t i=0; i<const_grids.size(); i++) {
+        const_grids[i] = grids[i].get();
+    }
+
+    Dune::Contact::NBodyAssembler<DeformedGrid, Vector> nBodyAssembler(bodyCount, bodyCount-1);
+    nBodyAssembler.setGrids(const_grids);
+
+    for (size_t i=0; i<couplings.size(); i++) {
+        nBodyAssembler.setCoupling(*couplings[i], i);
+    }
+
+    nBodyAssembler.assembleTransferOperator();
+    nBodyAssembler.assembleObstacle();
+
+    // set up boundary conditions
+    std::vector<BoundaryPatch<LeafGridView>> neumannBoundaries(bodyCount);
+    std::vector<BoundaryPatch<LeafGridView>> frictionalBoundaries(bodyCount);
+    std::vector<BoundaryPatch<LeafGridView>> surfaces(bodyCount);
+
+    // Dirichlet boundary
+    std::vector<Dune::BitSetVector<dims>> noNodes(bodyCount);
+    std::vector<Dune::BitSetVector<dims>> dirichletNodes(bodyCount);
+
+    // set up functions for time-dependent boundary conditions
+    using Function = Dune::VirtualFunction<double, double>;
+    const Function& velocityDirichletFunction = VelocityDirichletCondition();
+    const Function& neumannFunction = NeumannCondition();
+
+    for (size_t i=0; i<grids.size(); i++) {
+      const auto& leafVertexCount = leafVertexCounts[i];
+
+      std::cout << "Grid" << i << " Number of DOFs: " << leafVertexCount << std::endl;
+
+      // Neumann boundary
+      auto& neumannBoundary = neumannBoundaries[i];
+      neumannBoundary = BoundaryPatch<LeafGridView>(*leafViews[i]);
+
+      // friction boundary
+      auto& frictionalBoundary = frictionalBoundaries[i];
+      if (i==0) {
+        frictionalBoundary = BoundaryPatch<LeafGridView>(leafFaces[i]->upper);
+      } else if (i==bodyCount-1) {
+        frictionalBoundary = BoundaryPatch<LeafGridView>(leafFaces[i]->lower);
+      } else {
+          frictionalBoundary = BoundaryPatch<LeafGridView>(leafFaces[i]->lower);
+          frictionalBoundary.addPatch(BoundaryPatch<LeafGridView>(leafFaces[i]->upper));
+      }
+      //surfaces[i] = BoundaryPatch<GridView>(myFaces.upper);
+
+      // TODO: Dirichlet Boundary
+      noNodes[i] = Dune::BitSetVector<dims>(leafVertexCount);
+      dirichletNodes[i] = Dune::BitSetVector<dims>(leafVertexCount);
+      auto& gridDirichletNodes = dirichletNodes[i];
+      for (int j=0; j<leafVertexCount; j++) {
+        if (leafFaces[i]->right.containsVertex(j))
+          gridDirichletNodes[j][0] = true;
+
+        if (leafFaces[i]->lower.containsVertex(j))
+          gridDirichletNodes[j][0] = true;
+
+        #if MY_DIM == 3
+        if (leafFaces[i]->front.containsVertex(j) || leafFaces[i]->back.containsVertex(j))
+            gridDirichletNodes[j][2] = true;
+        #endif
+      }
+    }
+
+    // set up individual assemblers for each body, assemble problem (matrices, forces, rhs)
+    std::vector<std::shared_ptr<MyAssembler>> assemblers(bodyCount);
+    Matrices<Matrix> matrices(bodyCount);
+
+    std::vector<Vector> gravityFunctionals(bodyCount);
+    std::vector<std::function<void(double, Vector&)>> externalForces(bodyCount);
+
+    for (size_t i=0; i<assemblers.size(); i++) {
+      auto& assembler = assemblers[i];
+      assembler = std::make_shared<MyAssembler>(*leafViews[i]);
+
+      assembler->assembleElasticity(body.getYoungModulus(), body.getPoissonRatio(), *matrices.elasticity[i]);
+      assembler->assembleViscosity(body.getShearViscosityField(), body.getBulkViscosityField(), *matrices.damping[i]);
+      assembler->assembleMass(body.getDensityField(), *matrices.mass[i]);
+
+      ScalarMatrix relativeFrictionalBoundaryMass;
+      assembler->assembleFrictionalBoundaryMass(frictionalBoundaries[i], relativeFrictionalBoundaryMass);
+      relativeFrictionalBoundaryMass /= frictionalBoundaries[i].area();
+      EnergyNorm<ScalarMatrix, ScalarVector> const stateEnergyNorm(relativeFrictionalBoundaryMass);
+
+      // assemble forces
+      assembler->assembleBodyForce(body.getGravityField(), gravityFunctionals[i]);
+
+      // Problem formulation: right-hand side
+      externalForces[i] =
+            [&](double _relativeTime, Vector &_ell) {
+              assembler->assembleNeumann(neumannBoundaries[i], _ell, neumannFunction,
+                                          _relativeTime);
+              _ell += gravityFunctionals[i];
+      };
+    }
+
+    /* Jonny 2bcontact
+    // make dirichlet bitfields containing dirichlet information for both grids
+           int size = rhs[0].size() + rhs[1].size();
+
+           Dune::BitSetVector<dims> totalDirichletNodes(size);
+
+           for (size_t i=0; i<dirichletNodes[0].size(); i++)
+               for (int j=0; j<dims; j++)
+                   totalDirichletNodes[i][j] = dirichletNodes[0][i][j];
+
+           int offset = rhs[0].size();
+           for (size_t i=0; i<dirichletNodes[1].size(); i++)
+               for (int j=0; j<dims; j++)
+                   totalDirichletNodes[offset + i][j] = dirichletNodes[1][i][j];
+
+           // assemble separate linear elasticity problems
+           std::array<MatrixType,2> stiffnessMatrix;
+           std::array<const MatrixType*, 2> submat;
+
+           for (size_t i=0; i<2; i++) {
+               OperatorAssembler<P1Basis,P1Basis> globalAssembler(*p1Basis[i],*p1Basis[i]);
+
+               double s = (i==0) ? E0 : E1;
+               StVenantKirchhoffAssembler<GridType, P1Basis::LocalFiniteElement, P1Basis::LocalFiniteElement> localAssembler(s, nu);
+
+               globalAssembler.assemble(localAssembler, stiffnessMatrix[i]);
+
+               submat[i] = &stiffnessMatrix[i];
+           }
+
+           MatrixType bilinearForm;
+           contactAssembler.assembleJacobian(submat, bilinearForm);
+ */
+
+    using MyProgramState = ProgramState<Vector, ScalarVector>;
+    MyProgramState programState(leafVertexCounts);
+    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, leafVertexCounts)
+                         : nullptr;*/
+/*
+    if (firstRestart > 0) // automatically adjusts the time and timestep
+      restartIO->read(firstRestart, programState);
+    else
+     programState.setupInitialConditions(parset, nBodyAssembler, externalForces,
+                                          matrices, assemblers, dirichletNodes,
+                                          noNodes, frictionalBoundaries, body);
+
+    */
+
+    // assemble friction
+    std::vector<std::shared_ptr<MyGlobalFrictionData<LocalVector>>> frictionInfo(weakPatches.size());
+    std::vector<std::shared_ptr<GlobalFriction<Matrix, Vector>>> globalFriction(weakPatches.size());
+    for (size_t i=0; i<frictionInfo.size(); i++) {
+        frictionInfo[i] = std::make_shared<MyGlobalFrictionData<LocalVector>>(parset.sub("boundary.friction"), weakPatches[i]);
+        globalFriction[i] = assemblers[i]->assembleFrictionNonlinearity(parset.get<Config::FrictionModel>("boundary.friction.frictionModel"), frictionalBoundary, frictionInfo, programState.weightedNormalStress);
+        globalFriction[i]->updateAlpha(programState.alpha[i]);
+    }
+
+
+    /*
+    Vector vertexCoordinates(leafVertexCount);
+    {
+      Dune::MultipleCodimMultipleGeomTypeMapper<
+          GridView, Dune::MCMGVertexLayout> const vertexMapper(leafView, Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(leafView))
+        vertexCoordinates[vertexMapper.index(v)] = geoToPoint(v.geometry());
+    }
+
+    using MyVertexBasis = typename MyAssembler::VertexBasis;
+    auto dataWriter =
+        writeData ? std::make_unique<
+                        HDF5Writer<MyProgramState, MyVertexBasis, GridView>>(
+                        *dataFile, vertexCoordinates, myAssembler.vertexBasis,
+                        surface, frictionalBoundary, weakPatch)
+                  : nullptr;
+    MyVTKWriter<MyVertexBasis, typename MyAssembler::CellBasis> const vtkWriter(
+        myAssembler.cellBasis, myAssembler.vertexBasis, "obs");
+
+
+    IterationRegister iterationCount;
+    auto const report = [&](bool initial = false) {
+      if (writeData) {
+        dataWriter->reportSolution(programState, *myGlobalFriction);
+        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")) {
+        ScalarVector stress;
+        myAssembler.assembleVonMisesStress(body.getYoungModulus(),
+                                           body.getPoissonRatio(),
+                                           programState.u, stress);
+        vtkWriter.write(programState.timeStep, programState.u, programState.v,
+                        programState.alpha, stress);
+      }
+    };
+    report(true);
+    */
+
+
+    // Set up TNNMG solver
+    using NonlinearFactory = SolverFactory<
+        dims,
+        MyBlockProblem<ConvexProblem<GlobalFriction<Matrix, Vector>, Matrix>>,
+        Grid>;
+    NonlinearFactory factory(parset.sub("solver.tnnmg"), *grid, dirichletNodes);
+
+    using MyUpdater = Updaters<RateUpdater<Vector, Matrix, Function, dims>,
+                               StateUpdater<ScalarVector, Vector>>;
+    MyUpdater current(
+        initRateUpdater(parset.get<Config::scheme>("timeSteps.scheme"),
+                        velocityDirichletFunction, dirichletNodes, matrices,
+                        programState.u, programState.v, programState.a),
+        initStateUpdater<ScalarVector, Vector>(
+            parset.get<Config::stateModel>("boundary.friction.stateModel"),
+            programState.alpha, *frictionalBoundary.getVertices(),
+            parset.get<double>("boundary.friction.L"),
+            parset.get<double>("boundary.friction.V0")));
+
+    auto const refinementTolerance =
+        parset.get<double>("timeSteps.refinementTolerance");
+    auto const mustRefine = [&](MyUpdater &coarseUpdater,
+                                MyUpdater &fineUpdater) {
+      ScalarVector coarseAlpha;
+      coarseUpdater.state_->extractAlpha(coarseAlpha);
+
+      ScalarVector fineAlpha;
+      fineUpdater.state_->extractAlpha(fineAlpha);
+
+      return stateEnergyNorm.diff(fineAlpha, coarseAlpha) > refinementTolerance;
+    };
+
+    std::signal(SIGXCPU, handleSignal);
+    std::signal(SIGINT, handleSignal);
+    std::signal(SIGTERM, handleSignal);
+
+    AdaptiveTimeStepper<NonlinearFactory, MyUpdater,
+                        EnergyNorm<ScalarMatrix, ScalarVector>>
+        adaptiveTimeStepper(factory, parset, myGlobalFriction, current,
+                            programState.relativeTime, programState.relativeTau,
+                            computeExternalForces, stateEnergyNorm, mustRefine);
+    while (!adaptiveTimeStepper.reachedEnd()) {
+      programState.timeStep++;
+
+      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);
+
+      report();
+
+      if (terminationRequested) {
+        std::cerr << "Terminating prematurely" << std::endl;
+        break;
+      }
+    }
+
+  } 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/multi-body-problem.cfg b/src/multi-body-problem.cfg
new file mode 100644
index 00000000..b8358f55
--- /dev/null
+++ b/src/multi-body-problem.cfg
@@ -0,0 +1,71 @@
+# -*- mode:conf -*-
+gravity         = 9.81  # [m/s^2]
+
+[io]
+data.write      = true
+printProgress   = false
+restarts.first  = 0
+restarts.spacing= 20
+restarts.write  = true
+vtk.write       = false
+
+[problem]
+finalTime       = 1000  # [s]
+bodyCount       = 2
+
+[body]
+bulkModulus     = 0.5e5 # [Pa]
+poissonRatio    = 0.3   # [1]
+[body.elastic]
+density         = 900   # [kg/m^3]
+shearViscosity  = 1e3   # [Pas]
+bulkViscosity   = 1e3   # [Pas]
+[body.viscoelastic]
+density         = 1000  # [kg/m^3]
+shearViscosity  = 1e4   # [Pas]
+bulkViscosity   = 1e4   # [Pas]
+
+[boundary.friction]
+C               = 10    # [Pa]
+mu0             = 0.7   # [ ]
+V0              = 5e-5  # [m/s]
+L               = 2.25e-5 # [m]
+initialAlpha    = 0     # [ ]
+stateModel      = AgeingLaw
+frictionModel   = Regularised
+[boundary.friction.weakening]
+a               = 0.002 # [ ]
+b               = 0.017 # [ ]
+[boundary.friction.strengthening]
+a               = 0.020 # [ ]
+b               = 0.005 # [ ]
+
+[timeSteps]
+scheme = newmark
+
+[u0.solver]
+maximumIterations = 100000
+verbosity         = quiet
+
+[a0.solver]
+maximumIterations = 100000
+verbosity         = quiet
+
+[v.solver]
+maximumIterations = 100000
+verbosity         = quiet
+
+[v.fpi]
+maximumIterations = 10000
+lambda            = 0.5
+
+[solver.tnnmg.linear]
+maxiumumIterations = 100000
+pre                = 3
+cycle              = 1  # 1 = V, 2 = W, etc.
+post               = 3
+
+[solver.tnnmg.main]
+pre   = 1
+multi = 5 # number of multigrid steps
+post  = 0
diff --git a/src/one-body-problem.cc b/src/one-body-problem.cc
index d4d5ed56..dedc9162 100644
--- a/src/one-body-problem.cc
+++ b/src/one-body-problem.cc
@@ -31,9 +31,14 @@
 #include <dune/fufem/formatstring.hh>
 
 #include <dune/solvers/norms/energynorm.hh>
+
+
+/*
 #include <dune/solvers/solvers/loopsolver.hh>
 #include <dune/solvers/solvers/solver.hh>
 #include <dune/tnnmg/problem-classes/convexproblem.hh>
+*/
+
 
 #include <dune/tectonic/geocoordinate.hh>
 #include <dune/tectonic/myblockproblem.hh>
@@ -62,13 +67,18 @@
 #include "time-stepping/updaters.hh"
 #include "vtk.hh"
 
+// for getcwd
+#include <unistd.h>
+
+#define USE_OLD_TNNMG
+
 size_t const dims = MY_DIM;
 
 Dune::ParameterTree getParameters(int argc, char *argv[]) {
   Dune::ParameterTree parset;
-  Dune::ParameterTreeParser::readINITree("one-body-problem.cfg", parset);
+  Dune::ParameterTreeParser::readINITree("/home/mi/podlesny/software/dune/dune-tectonic/src/one-body-problem.cfg", parset);
   Dune::ParameterTreeParser::readINITree(
-      Dune::Fufem::formatString("one-body-problem-%dD.cfg", dims), parset);
+      Dune::Fufem::formatString("/home/mi/podlesny/software/dune/dune-tectonic/src/one-body-problem-%dD.cfg", dims), parset);
   Dune::ParameterTreeParser::readOptions(argc, argv, parset);
   return parset;
 }
@@ -79,6 +89,14 @@ 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;
+    }
+
     auto const parset = getParameters(argc, argv);
 
     MyGeometry::render();
@@ -140,6 +158,7 @@ int main(int argc, char *argv[]) {
 #endif
     }
 
+
     // Set up functions for time-dependent boundary conditions
     using Function = Dune::VirtualFunction<double, double>;
     Function const &velocityDirichletFunction = VelocityDirichletCondition();
@@ -184,8 +203,10 @@ int main(int argc, char *argv[]) {
     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",
@@ -214,7 +235,7 @@ int main(int argc, char *argv[]) {
     Vector vertexCoordinates(leafVertexCount);
     {
       Dune::MultipleCodimMultipleGeomTypeMapper<
-          GridView, Dune::MCMGVertexLayout> const vertexMapper(leafView);
+          GridView, Dune::MCMGVertexLayout> const vertexMapper(leafView, Dune::mcmgVertexLayout());
       for (auto &&v : vertices(leafView))
         vertexCoordinates[vertexMapper.index(v)] = geoToPoint(v.geometry());
     }
@@ -229,6 +250,7 @@ int main(int argc, char *argv[]) {
     MyVTKWriter<MyVertexBasis, typename MyAssembler::CellBasis> const vtkWriter(
         myAssembler.cellBasis, myAssembler.vertexBasis, "obs");
 
+
     IterationRegister iterationCount;
     auto const report = [&](bool initial = false) {
       if (writeData) {
@@ -261,6 +283,7 @@ int main(int argc, char *argv[]) {
     };
     report(true);
 
+
     // Set up TNNMG solver
     using NonlinearFactory = SolverFactory<
         dims,
@@ -321,6 +344,8 @@ int main(int argc, char *argv[]) {
         break;
       }
     }
+
+    */
   } catch (Dune::Exception &e) {
     Dune::derr << "Dune reported error: " << e << std::endl;
   } catch (std::exception &e) {
diff --git a/src/program_state.hh b/src/program_state.hh
index 1a63502f..73f9bc2e 100644
--- a/src/program_state.hh
+++ b/src/program_state.hh
@@ -3,10 +3,14 @@
 
 #include <dune/common/parametertree.hh>
 
+#include <dune/matrix-vector/axpy.hh>
+
 #include <dune/fufem/boundarypatch.hh>
 #include <dune/tnnmg/nonlinearities/zerononlinearity.hh>
 #include <dune/tnnmg/problem-classes/blocknonlineartnnmgproblem.hh>
 
+#include <dune/contact/assemblers/nbodyassembler.hh>
+
 #include <dune/tectonic/body.hh>
 
 #include "assemblers.hh"
@@ -18,32 +22,45 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
   using Vector = VectorTEMPLATE;
   using ScalarVector = ScalarVectorTEMPLATE;
 
-  ProgramState(int leafVertexCount)
-      : u(leafVertexCount),
-        v(leafVertexCount),
-        a(leafVertexCount),
-        alpha(leafVertexCount),
-        weightedNormalStress(leafVertexCount) {}
+  ProgramState(const std::vector<int>& leafVertexCounts)
+    : bodyCount(leafVertexCounts.size()) {
+    u.resize(bodyCount);
+    v.resize(bodyCount);
+    a.resize(bodyCount);
+    alpha.resize(bodyCount);
+    weightedNormalStress.resize(bodyCount);
+
+    for (size_t i=0; i<bodyCount; i++) {
+        size_t leafVertexCount = leafVertexCounts[i];
+        u[i].resize(leafVertexCount);
+        v[i].resize(leafVertexCount);
+        a[i].resize(leafVertexCount);
+        alpha[i].resize(leafVertexCount);
+        weightedNormalStress[i].resize(leafVertexCount);
+    }
+  }
 
   // Set up initial conditions
   template <class Matrix, class GridView>
   void setupInitialConditions(
-      Dune::ParameterTree const &parset,
-      std::function<void(double, Vector &)> externalForces,
-      Matrices<Matrix> const matrices,
-      MyAssembler<GridView, Vector::block_type::dimension> const &myAssembler,
-      Dune::BitSetVector<Vector::block_type::dimension> const &dirichletNodes,
-      Dune::BitSetVector<Vector::block_type::dimension> const &noNodes,
-      BoundaryPatch<GridView> const &frictionalBoundary,
-      Body<Vector::block_type::dimension> const &body) {
+      const Dune::ParameterTree& parset,
+      const Dune::Contact::NBodyAssembler<typename GridView::Grid, Vector>& nBodyAssembler,
+      std::vector<std::function<void(double, Vector &)>> externalForces,
+      const Matrices<Matrix>& matrices,
+      const std::vector<std::shared_ptr<MyAssembler<GridView, Vector::block_type::dimension>>>& assemblers,
+      const std::vector<Dune::BitSetVector<Vector::block_type::dimension>>& dirichletNodes,
+      const std::vector<Dune::BitSetVector<Vector::block_type::dimension>>& noNodes,
+      const std::vector<BoundaryPatch<GridView>>& frictionalBoundaries,
+      const Body<Vector::block_type::dimension>& body) {
 
     using LocalVector = typename Vector::block_type;
     using LocalMatrix = typename Matrix::block_type;
     auto constexpr dims = LocalVector::dimension;
 
+    /*
     // Solving a linear problem with a multigrid solver
     auto const solveLinearProblem = [&](
-        Dune::BitSetVector<dims> const &_dirichletNodes, Matrix const &_matrix,
+        Dune::BitSetVector<dims> const &_dirichletNodes, const std::vector<std::shared_ptr<Matrix>>& _matrix,
         Vector const &_rhs, Vector &_x,
         Dune::ParameterTree const &_localParset) {
 
@@ -54,7 +71,7 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
       ZeroNonlinearity<LocalVector, LocalMatrix> zeroNonlinearity;
 
       LinearFactory factory(parset.sub("solver.tnnmg"), // FIXME
-                            myAssembler.gridView.grid(), _dirichletNodes);
+                            assemblers.gridView.grid(), _dirichletNodes);
 
       typename LinearFactory::ConvexProblem convexProblem(
           1.0, _matrix, zeroNonlinearity, _rhs, _x);
@@ -62,7 +79,11 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
       auto multigridStep = factory.getStep();
       multigridStep->setProblem(_x, problem);
+      //multigridStep->setProblem(_x);
       EnergyNorm<Matrix, Vector> const norm(_matrix);
+
+
+
       LoopSolver<Vector> solver(
           multigridStep.get(), _localParset.get<size_t>("maximumIterations"),
           _localParset.get<double>("tolerance"), &norm,
@@ -71,17 +92,28 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
       solver.preprocess();
       solver.solve();
+
+      Vector totalX = multigridStep->getSol();
+
+      // cleanup
+      delete(multigridStep);
+
+      nBodyAssembler.postprocess(totalX, x);
     };
+    */
 
     timeStep = 0;
     relativeTime = 0.0;
     relativeTau = 1e-6;
 
-    Vector ell0(u.size());
-    externalForces(relativeTime, ell0);
+    std::vector<Vector> ell0(bodyCount);
+    for (size_t i=0; i<bodyCount; i++) {
+      // Initial velocity
+      v[i] = 0.0;
 
-    // Initial velocity
-    v = 0.0;
+      ell0[i].resize(u[i].size());
+      externalForces[i](relativeTime, ell0[i]);
+    }
 
     // Initial displacement: Start from a situation of minimal stress,
     // which is automatically attained in the case [v = 0 = a].
@@ -91,29 +123,35 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
     // Initial acceleration: Computed in agreement with Ma = ell0 - Au
     // (without Dirichlet constraints), again assuming dPhi(v = 0) = 0
-    Vector accelerationRHS = ell0;
-    Arithmetic::subtractProduct(accelerationRHS, matrices.elasticity, u);
-    solveLinearProblem(noNodes, matrices.mass, accelerationRHS, a,
-                       parset.sub("a0.solver"));
+    std::vector<Vector> accelerationRHS = ell0;
+    for (size_t i=0; i<bodyCount; i++) {
+      // Initial state
+      alpha[i] = parset.get<double>("boundary.friction.initialAlpha");
+
+      // Initial normal stress
+      assemblers[i]->assembleWeightedNormalStress(
+        frictionalBoundaries[i], weightedNormalStress[i], body.getYoungModulus(),
+        body.getPoissonRatio(), u[i]);
 
-    // Initial state
-    alpha = parset.get<double>("boundary.friction.initialAlpha");
+      Dune::MatrixVector::subtractProduct(accelerationRHS[i], *matrices.elasticity[i], u[i]);
+    }
 
-    // Initial normal stress
-    myAssembler.assembleWeightedNormalStress(
-        frictionalBoundary, weightedNormalStress, body.getYoungModulus(),
-        body.getPoissonRatio(), u);
+    solveLinearProblem(noNodes, matrices.mass, accelerationRHS, a,
+                       parset.sub("a0.solver"));
   }
 
 public:
-  Vector u;
-  Vector v;
-  Vector a;
-  ScalarVector alpha;
-  ScalarVector weightedNormalStress;
+  std::vector<Vector> u;
+  std::vector<Vector> v;
+  std::vector<Vector> a;
+  std::vector<ScalarVector> alpha;
+  std::vector<ScalarVector> weightedNormalStress;
   double relativeTime;
   double relativeTau;
   size_t timeStep;
+
+private:
+  const size_t bodyCount;
 };
 
 #endif
diff --git a/src/spatial-solving/fixedpointiterator.cc b/src/spatial-solving/fixedpointiterator.cc
index a8c03459..02322caf 100644
--- a/src/spatial-solving/fixedpointiterator.cc
+++ b/src/spatial-solving/fixedpointiterator.cc
@@ -8,6 +8,19 @@
 #include <dune/solvers/norms/energynorm.hh>
 #include <dune/solvers/solvers/loopsolver.hh>
 
+#include <dune/contact/assemblers/nbodyassembler.hh>
+#include <dune/contact/common/dualbasisadapter.hh>
+
+#include <dune/localfunctions/lagrange/pqkfactory.hh>
+
+#include <dune/functions/gridfunctions/gridfunction.hh>
+
+#include <dune/geometry/quadraturerules.hh>
+#include <dune/geometry/type.hh>
+#include <dune/geometry/referenceelements.hh>
+
+#include <dune/fufem/functions/basisgridfunction.hh>
+
 #include "../enums.hh"
 #include "../enumparser.hh"
 
@@ -19,11 +32,13 @@ void FixedPointIterationCounter::operator+=(
   multigridIterations += other.multigridIterations;
 }
 
-template <class Factory, class Updaters, class ErrorNorm>
-FixedPointIterator<Factory, Updaters, ErrorNorm>::FixedPointIterator(
+template <class DeformedGrid, class Factory, class Updaters, class ErrorNorm>
+FixedPointIterator<DeformedGrid, Factory, Updaters, ErrorNorm>::FixedPointIterator(
+    const Dune::Contact::NBodyAssembler<DeformedGrid, Vector>& nBodyAssembler,
     Factory &factory, Dune::ParameterTree const &parset,
     std::shared_ptr<Nonlinearity> globalFriction, ErrorNorm const &errorNorm)
-    : step_(factory.getStep()),
+    : nBodyAssembler_(nBodyAssembler),
+      step_(factory.getStep()),
       parset_(parset),
       globalFriction_(globalFriction),
       fixedPointMaxIterations_(parset.get<size_t>("v.fpi.maximumIterations")),
@@ -34,9 +49,9 @@ FixedPointIterator<Factory, Updaters, ErrorNorm>::FixedPointIterator(
       verbosity_(parset.get<Solver::VerbosityMode>("v.solver.verbosity")),
       errorNorm_(errorNorm) {}
 
-template <class Factory, class Updaters, class ErrorNorm>
+template <class DeformedGrid,class Factory, class Updaters, class ErrorNorm>
 FixedPointIterationCounter
-FixedPointIterator<Factory, Updaters, ErrorNorm>::run(
+FixedPointIterator<DeformedGrid, Factory, Updaters, ErrorNorm>::run(
     Updaters updaters, Matrix const &velocityMatrix, Vector const &velocityRHS,
     Vector &velocityIterate) {
   EnergyNorm<Matrix, Vector> energyNorm(velocityMatrix);
@@ -46,7 +61,7 @@ FixedPointIterator<Factory, Updaters, ErrorNorm>::run(
 
   size_t fixedPointIteration;
   size_t multigridIterations = 0;
-  ScalarVector alpha;
+  std::vector<ScalarVector> alpha;
   updaters.state_->extractAlpha(alpha);
   for (fixedPointIteration = 0; fixedPointIteration < fixedPointMaxIterations_;
        ++fixedPointIteration) {
@@ -56,15 +71,22 @@ FixedPointIterator<Factory, Updaters, ErrorNorm>::run(
                                 velocityRHS, velocityIterate);
     BlockProblem velocityProblem(parset_, convexProblem);
     step_->setProblem(velocityIterate, velocityProblem);
+    //step_->setProblem(velocityIterate);
     velocityProblemSolver.preprocess();
     velocityProblemSolver.solve();
 
     multigridIterations += velocityProblemSolver.getResult().iterations;
 
-    Vector v_m;
+    std::vector<Vector> v_m;
     updaters.rate_->extractOldVelocity(v_m);
-    v_m *= 1.0 - lambda_;
-    Arithmetic::addProduct(v_m, lambda_, velocityIterate);
+
+    for (size_t i=0; i<v_m.size(); i++) {
+      v_m[i] *= 1.0 - lambda_;
+      Arithmetic::addProduct(v_m[i], lambda_, velocityIterate[i]);
+    }
+
+    // compute relative velocities on contact boundaries
+    relativeVelocities(v_m);
 
     // solve a state problem
     updaters.state_->solve(v_m);
@@ -97,4 +119,270 @@ std::ostream &operator<<(std::ostream &stream,
                 << ")";
 }
 
+template <class DeformedGrid, class Factory, class Updaters, class ErrorNorm>
+void FixedPointIterator<DeformedGrid, Factory, Updaters, ErrorNorm>::relativeVelocities(std::vector<Vector>& v_m) const {
+  // adaptation of DualMortarCoupling::setup()
+
+  const size_t dim = DeformedGrid::dimension;
+  typedef typename DeformedGrid::LeafGridView GridView;
+
+  //cache of local bases
+  typedef Dune::PQkLocalFiniteElementCache<typename DeformedGrid::ctype, field_type, dim,1> FiniteElementCache1;
+  FiniteElementCache1 cache1;
+
+  // cache for the dual functions on the boundary
+  using DualCache = Dune::Contact::DualBasisAdapter<GridView, field_type>;
+  std::unique_ptr<DualCache> dualCache;
+  dualCache = std::make_unique< Dune::Contact::DualBasisAdapterGlobal<GridView, field_type> >();
+
+  // define FE grid functions
+  std::vector<BasisGridFunction<> > gridFunctions(nBodyAssembler_.nGrids());
+  for (size_t i=0; i<gridFunctions.size(); i++) {
+
+  }
+
+  const auto& contactCouplings = nBodyAssembler_.getContactCouplings();
+  for (size_t i=0; i<contactCouplings.size(); i++) {
+    auto contactCoupling = contactCouplings[i];
+    auto glue = contactCoupling->getGlue();
+
+    // loop over all intersections
+    for (const auto& rIs : intersections(glue)) {
+        const auto& inside = rIs.inside();
+
+        if (!nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside()))
+            continue;
+
+        const auto& outside = rIs.outside();
+
+        // types of the elements supporting the boundary segments in question
+        Dune::GeometryType nonmortarEType = inside.type();
+        Dune::GeometryType mortarEType    = outside.type();
+
+        const auto& domainRefElement = Dune::ReferenceElements<ctype, dim>::general(nonmortarEType);
+        const auto& targetRefElement = Dune::ReferenceElements<ctype, dim>::general(mortarEType);
+
+        int noOfMortarVec = targetRefElement.size(dim);
+
+        Dune::GeometryType nmFaceType = domainRefElement.type(rIs.indexInInside(),1);
+        Dune::GeometryType mFaceType  = targetRefElement.type(rIs.indexInOutside(),1);
+
+        // Select a quadrature rule
+        // 2 in 2d and for integration over triangles in 3d.  If one (or both) of the two faces involved
+        // are quadrilaterals, then the quad order has to be risen to 3 (4).
+        int quadOrder = 2 + (!nmFaceType.isSimplex()) + (!mFaceType.isSimplex());
+        const auto& quadRule = Dune::QuadratureRules<ctype, dim-1>::rule(rIs.type(), quadOrder);
+
+        const auto& mortarFiniteElement = cache1.get(mortarEType);
+        dualCache->bind(inside, rIs.indexInInside());
+
+        std::vector<Dune::FieldVector<field_type,1> > mortarQuadValues, dualQuadValues;
+
+        const auto& rGeom = rIs.geometry();
+        const auto& rGeomOutside = rIs.geometryOutside();
+        const auto& rGeomInInside = rIs.geometryInInside();
+        const auto& rGeomInOutside = rIs.geometryInOutside();
+
+        int nNonmortarFaceNodes = domainRefElement.size(rIs.indexInInside(),1,dim);
+        std::vector<int> nonmortarFaceNodes;
+        for (int i=0; i<nNonmortarFaceNodes; i++) {
+          int faceIdxi = domainRefElement.subEntity(rIs.indexInInside(), 1, i, dim);
+          nonmortarFaceNodes.push_back(faceIdxi);
+        }
+
+        for (const auto& quadPt : quadRule) {
+
+            // compute integration element of overlap
+            ctype integrationElement = rGeom.integrationElement(quadPt.position());
+
+            // quadrature point positions on the reference element
+            Dune::FieldVector<ctype,dim> nonmortarQuadPos = rGeomInInside.global(quadPt.position());
+            Dune::FieldVector<ctype,dim> mortarQuadPos    = rGeomInOutside.global(quadPt.position());
+
+            // The current quadrature point in world coordinates
+            Dune::FieldVector<field_type,dim> nonmortarQpWorld = rGeom.global(quadPt.position());
+            Dune::FieldVector<field_type,dim> mortarQpWorld    = rGeomOutside.global(quadPt.position());;
+
+            // the gap direction (normal * gapValue)
+            Dune::FieldVector<field_type,dim> gapVector = mortarQpWorld  - nonmortarQpWorld;
+
+            //evaluate all shapefunctions at the quadrature point
+            //nonmortarFiniteElement.localBasis().evaluateFunction(nonmortarQuadPos,nonmortarQuadValues);
+            mortarFiniteElement.localBasis().evaluateFunction(mortarQuadPos,mortarQuadValues);
+            dualCache->evaluateFunction(nonmortarQuadPos,dualQuadValues);
+
+            // loop over all Lagrange multiplier shape functions
+            for (int j=0; j<nNonmortarFaceNodes; j++) {
+
+                int globalDomainIdx = indexSet0.subIndex(inside,nonmortarFaceNodes[j],dim);
+                int rowIdx = globalToLocal[globalDomainIdx];
+
+                weakObstacle_[rowIdx][0] += integrationElement * quadPt.weight()
+                    * dualQuadValues[nonmortarFaceNodes[j]] * (gapVector*avNormals[globalDomainIdx]);
+
+                // loop over all mortar shape functions
+                for (int k=0; k<noOfMortarVec; k++) {
+
+                    int colIdx  = indexSet1.subIndex(outside, k, dim);
+                    if (!mortarBoundary_.containsVertex(colIdx))
+                        continue;
+
+                    // Integrate over the product of two shape functions
+                    field_type mortarEntry =  integrationElement* quadPt.weight()* dualQuadValues[nonmortarFaceNodes[j]]* mortarQuadValues[k];
+
+                    Dune::MatrixVector::addToDiagonal(mortarLagrangeMatrix_[rowIdx][colIdx], mortarEntry);
+
+                }
+
+            }
+
+        }
+
+
+
+
+
+
+  }
+
+
+
+
+      ///////////////////////////////////
+      //  reducing nonmortar boundary
+      /////////////////////////////////
+
+      // Get all fine grid boundary segments that are totally covered by the grid-glue segments
+      typedef std::pair<int,int> Pair;
+      std::map<Pair,ctype> coveredArea, fullArea;
+
+      // initialize with area of boundary faces
+      for (const auto& bIt : nonmortarBoundary_) {
+          const Pair p(indexSet0.index(bIt.inside()),bIt.indexInInside());
+          fullArea[p] = bIt.geometry().volume();
+          coveredArea[p] = 0;
+      }
+
+      // sum up the remote intersection areas to find out which are totally covered
+      for (const auto& rIs : intersections(glue))
+          coveredArea[Pair(indexSet0.index(rIs.inside()),rIs.indexInInside())] += rIs.geometry().volume();
+
+      // add all fine grid faces that are totally covered by the contact mapping
+      for (const auto& bIt : nonmortarBoundary_) {
+          const auto& inside = bIt.inside();
+          if(coveredArea[Pair(indexSet0.index(inside),bIt.indexInInside())]/
+              fullArea[Pair(indexSet0.index(inside),bIt.indexInInside())] >= coveredArea_)
+              boundaryWithMapping.addFace(inside, bIt.indexInInside());
+      }
+
+      //writeBoundary(boundaryWithMapping,debugPath_ + "relevantNonmortar");
+
+
+      /** \todo replace by all fine grid segments which are totally covered by the RemoteIntersections. */
+      //for (const auto& rIs : intersections(glue))
+      //    boundaryWithMapping.addFace(rIs.inside(),rIs.indexInInside());
+
+      printf("contact mapping could be built for %d of %d boundary segments.\n",
+             boundaryWithMapping.numFaces(), nonmortarBoundary_.numFaces());
+
+      nonmortarBoundary_ = boundaryWithMapping;
+      mortarBoundary_.setup(gridView1);
+      for (const auto& rIs : intersections(glue))
+          if (nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside()))
+              mortarBoundary_.addFace(rIs.outside(),rIs.indexInOutside());
+
+
+      // Assemble the diagonal matrix coupling the nonmortar side with the lagrange multiplyers there
+      assembleNonmortarLagrangeMatrix();
+
+      // The weak obstacle vector
+      weakObstacle_.resize(nonmortarBoundary_.numVertices());
+      weakObstacle_ = 0;
+
+      // ///////////////////////////////////////////////////////////
+      //   Get the occupation structure for the mortar matrix
+      // ///////////////////////////////////////////////////////////
+
+      /** \todo Also restrict mortar indices and don't use the whole grid level. */
+      Dune::MatrixIndexSet mortarIndices(nonmortarBoundary_.numVertices(), grid1_->size(dim));
+
+      // Create mapping from the global set of block dofs to the ones on the contact boundary
+      std::vector<int> globalToLocal;
+      nonmortarBoundary_.makeGlobalToLocal(globalToLocal);
+
+      // loop over all intersections
+      for (const auto& rIs : intersections(glue)) {
+
+          if (!nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside()))
+              continue;
+
+          const auto& inside = rIs.inside();
+          const auto& outside = rIs.outside();
+
+          const auto& domainRefElement = Dune::ReferenceElements<ctype, dim>::general(inside.type());
+          const auto& targetRefElement = Dune::ReferenceElements<ctype, dim>::general(outside.type());
+
+          int nDomainVertices = domainRefElement.size(dim);
+          int nTargetVertices = targetRefElement.size(dim);
+
+          for (int j=0; j<nDomainVertices; j++) {
+
+              int localDomainIdx = globalToLocal[indexSet0.subIndex(inside,j,dim)];
+
+              // if the vertex is not contained in the restricted contact boundary then dismiss it
+              if (localDomainIdx == -1)
+                  continue;
+
+              for (int k=0; k<nTargetVertices; k++) {
+                  int globalTargetIdx = indexSet1.subIndex(outside,k,dim);
+                  if (!mortarBoundary_.containsVertex(globalTargetIdx))
+                      continue;
+
+                  mortarIndices.add(localDomainIdx, globalTargetIdx);
+              }
+          }
+      }
+
+      mortarIndices.exportIdx(mortarLagrangeMatrix_);
+
+      // Clear it
+      mortarLagrangeMatrix_ = 0;
+
+
+      //cache of local bases
+      FiniteElementCache1 cache1;
+
+      std::unique_ptr<DualCache> dualCache;
+      dualCache = std::make_unique< Dune::Contact::DualBasisAdapterGlobal<GridView0, field_type> >();
+
+      std::vector<Dune::FieldVector<ctype,dim> > avNormals;
+      avNormals = nonmortarBoundary_.getNormals();
+
+
+      }
+
+      // ///////////////////////////////////////
+      //    Compute M = D^{-1} \hat{M}
+      // ///////////////////////////////////////
+
+      Dune::BCRSMatrix<MatrixBlock>& M  = mortarLagrangeMatrix_;
+      Dune::BDMatrix<MatrixBlock>& D    = nonmortarLagrangeMatrix_;
+
+      // First compute D^{-1}
+      D.invert();
+
+      // Then the matrix product D^{-1} \hat{M}
+      for (auto rowIt = M.begin(); rowIt != M.end(); ++rowIt) {
+          const auto rowIndex = rowIt.index();
+          for (auto& entry : *rowIt)
+              entry.leftmultiply(D[rowIndex][rowIndex]);
+      }
+
+      // weakObstacles in transformed basis = D^{-1}*weakObstacle_
+      for(size_t rowIdx=0; rowIdx<weakObstacle_.size(); rowIdx++)
+          weakObstacle_[rowIdx] *= D[rowIdx][rowIdx][0][0];
+
+      gridGlueBackend_->clear();
+}
+
 #include "fixedpointiterator_tmpl.cc"
diff --git a/src/spatial-solving/fixedpointiterator.hh b/src/spatial-solving/fixedpointiterator.hh
index ba31c81e..e69de29b 100644
--- a/src/spatial-solving/fixedpointiterator.hh
+++ b/src/spatial-solving/fixedpointiterator.hh
@@ -1,53 +0,0 @@
-#ifndef SRC_SPATIAL_SOLVING_FIXEDPOINTITERATOR_HH
-#define SRC_SPATIAL_SOLVING_FIXEDPOINTITERATOR_HH
-
-#include <memory>
-
-#include <dune/common/parametertree.hh>
-
-#include <dune/solvers/norms/norm.hh>
-#include <dune/solvers/solvers/solver.hh>
-
-struct FixedPointIterationCounter {
-  size_t iterations = 0;
-  size_t multigridIterations = 0;
-
-  void operator+=(FixedPointIterationCounter const &other);
-};
-
-std::ostream &operator<<(std::ostream &stream,
-                         FixedPointIterationCounter const &fpic);
-
-template <class Factory, class Updaters, class ErrorNorm>
-class FixedPointIterator {
-  using ScalarVector = typename Updaters::StateUpdater::ScalarVector;
-  using Vector = typename Factory::Vector;
-  using Matrix = typename Factory::Matrix;
-  using ConvexProblem = typename Factory::ConvexProblem;
-  using BlockProblem = typename Factory::BlockProblem;
-  using Nonlinearity = typename ConvexProblem::NonlinearityType;
-
-public:
-  FixedPointIterator(Factory &factory, Dune::ParameterTree const &parset,
-                     std::shared_ptr<Nonlinearity> globalFriction,
-                     ErrorNorm const &errorNorm_);
-
-  FixedPointIterationCounter run(Updaters updaters,
-                                 Matrix const &velocityMatrix,
-                                 Vector const &velocityRHS,
-                                 Vector &velocityIterate);
-
-private:
-  std::shared_ptr<typename Factory::Step> step_;
-  Dune::ParameterTree const &parset_;
-  std::shared_ptr<Nonlinearity> globalFriction_;
-
-  size_t fixedPointMaxIterations_;
-  double fixedPointTolerance_;
-  double lambda_;
-  size_t velocityMaxIterations_;
-  double velocityTolerance_;
-  Solver::VerbosityMode verbosity_;
-  ErrorNorm const &errorNorm_;
-};
-#endif
diff --git a/src/spatial-solving/solverfactory.cc b/src/spatial-solving/solverfactory.cc
index 3242724a..e69de29b 100644
--- a/src/spatial-solving/solverfactory.cc
+++ b/src/spatial-solving/solverfactory.cc
@@ -1,59 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef HAVE_IPOPT
-#undef HAVE_IPOPT
-#endif
-
-#include <dune/fufem/assemblers/transferoperatorassembler.hh>
-#include <dune/solvers/solvers/solver.hh>
-
-#include "solverfactory.hh"
-
-template <size_t dim, class BlockProblem, class Grid>
-SolverFactory<dim, BlockProblem, Grid>::SolverFactory(
-    Dune::ParameterTree const &parset, Grid const &grid,
-    Dune::BitSetVector<dim> const &ignoreNodes)
-    : baseEnergyNorm(linearBaseSolverStep),
-      linearBaseSolver(&linearBaseSolverStep,
-                       parset.get<size_t>("linear.maxiumumIterations"),
-                       parset.get<double>("linear.tolerance"), &baseEnergyNorm,
-                       Solver::QUIET),
-      transferOperators(grid.maxLevel()),
-      multigridStep(
-          std::make_shared<Step>(linearIterationStep, nonlinearSmoother)) {
-  // linear iteration step
-  linearIterationStep.setMGType(parset.get<int>("linear.cycle"),
-                                parset.get<int>("linear.pre"),
-                                parset.get<int>("linear.post"));
-  linearIterationStep.basesolver_ = &linearBaseSolver;
-  linearIterationStep.setSmoother(&linearPresmoother, &linearPostsmoother);
-
-  // transfer operators
-  for (auto &&x : transferOperators)
-    x = new CompressedMultigridTransfer<Vector>;
-  TransferOperatorAssembler<Grid>(grid)
-      .assembleOperatorPointerHierarchy(transferOperators);
-  linearIterationStep.setTransferOperators(transferOperators);
-
-  // tnnmg iteration step
-  multigridStep->setSmoothingSteps(parset.get<int>("main.pre"),
-                                   parset.get<int>("main.multi"),
-                                   parset.get<int>("main.post"));
-  multigridStep->ignoreNodes_ = &ignoreNodes;
-}
-
-template <size_t dim, class BlockProblem, class Grid>
-SolverFactory<dim, BlockProblem, Grid>::~SolverFactory() {
-  for (auto &&x : transferOperators)
-    delete x;
-}
-
-template <size_t dim, class BlockProblem, class Grid>
-auto SolverFactory<dim, BlockProblem, Grid>::getStep()
-    -> std::shared_ptr<Step> {
-  return multigridStep;
-}
-
-#include "solverfactory_tmpl.cc"
diff --git a/src/spatial-solving/solverfactory.hh b/src/spatial-solving/solverfactory.hh
index 8f05a572..e69de29b 100644
--- a/src/spatial-solving/solverfactory.hh
+++ b/src/spatial-solving/solverfactory.hh
@@ -1,48 +0,0 @@
-#ifndef SRC_SPATIAL_SOLVING_SOLVERFACTORY_HH
-#define SRC_SPATIAL_SOLVING_SOLVERFACTORY_HH
-
-#include <dune/common/bitsetvector.hh>
-#include <dune/common/parametertree.hh>
-
-#include <dune/solvers/iterationsteps/multigridstep.hh>
-#include <dune/solvers/iterationsteps/truncatedblockgsstep.hh>
-#include <dune/solvers/norms/energynorm.hh>
-#include <dune/solvers/solvers/loopsolver.hh>
-#include <dune/solvers/transferoperators/compressedmultigridtransfer.hh>
-#include <dune/tnnmg/iterationsteps/genericnonlineargs.hh>
-#include <dune/tnnmg/iterationsteps/tnnmgstep.hh>
-
-template <size_t dim, class BlockProblemTEMPLATE, class Grid>
-class SolverFactory {
-public:
-  using BlockProblem = BlockProblemTEMPLATE;
-  using ConvexProblem = typename BlockProblem::ConvexProblemType;
-  using Vector = typename BlockProblem::VectorType;
-  using Matrix = typename BlockProblem::MatrixType;
-
-private:
-  using NonlinearSmoother = GenericNonlinearGS<BlockProblem>;
-
-public:
-  using Step =
-      TruncatedNonsmoothNewtonMultigrid<BlockProblem, NonlinearSmoother>;
-
-  SolverFactory(Dune::ParameterTree const &parset, Grid const &grid,
-                Dune::BitSetVector<dim> const &ignoreNodes);
-
-  ~SolverFactory();
-
-  std::shared_ptr<Step> getStep();
-
-private:
-  TruncatedBlockGSStep<Matrix, Vector> linearBaseSolverStep;
-  EnergyNorm<Matrix, Vector> baseEnergyNorm;
-  LoopSolver<Vector> linearBaseSolver;
-  TruncatedBlockGSStep<Matrix, Vector> linearPresmoother;
-  TruncatedBlockGSStep<Matrix, Vector> linearPostsmoother;
-  MultigridStep<Matrix, Vector> linearIterationStep;
-  std::vector<CompressedMultigridTransfer<Vector> *> transferOperators;
-  NonlinearSmoother nonlinearSmoother;
-  std::shared_ptr<Step> multigridStep;
-};
-#endif
diff --git a/src/spatial-solving/solverfactory_tmpl.cc b/src/spatial-solving/solverfactory_tmpl.cc
index 506d9042..e69de29b 100644
--- a/src/spatial-solving/solverfactory_tmpl.cc
+++ b/src/spatial-solving/solverfactory_tmpl.cc
@@ -1,22 +0,0 @@
-#ifndef MY_DIM
-#error MY_DIM unset
-#endif
-
-#include "../explicitgrid.hh"
-#include "../explicitvectors.hh"
-
-#include <dune/tnnmg/nonlinearities/zerononlinearity.hh>
-#include <dune/tnnmg/problem-classes/blocknonlineartnnmgproblem.hh>
-#include <dune/tnnmg/problem-classes/convexproblem.hh>
-
-#include <dune/tectonic/globalfriction.hh>
-#include <dune/tectonic/myblockproblem.hh>
-
-template class SolverFactory<
-    MY_DIM,
-    MyBlockProblem<ConvexProblem<GlobalFriction<Matrix, Vector>, Matrix>>,
-    Grid>;
-template class SolverFactory<
-    MY_DIM, BlockNonlinearTNNMGProblem<ConvexProblem<
-                ZeroNonlinearity<LocalVector, LocalMatrix>, Matrix>>,
-    Grid>;
diff --git a/src/time-stepping/rate.cc b/src/time-stepping/rate.cc
index 4d60bd9d..fe366fe7 100644
--- a/src/time-stepping/rate.cc
+++ b/src/time-stepping/rate.cc
@@ -9,20 +9,22 @@
 template <class Vector, class Matrix, class Function, int dimension>
 std::shared_ptr<RateUpdater<Vector, Matrix, Function, dimension>>
 initRateUpdater(Config::scheme scheme,
-                Function const &velocityDirichletFunction,
-                Dune::BitSetVector<dimension> const &velocityDirichletNodes,
-                Matrices<Matrix> const &matrices, Vector const &u_initial,
-                Vector const &v_initial, Vector const &a_initial) {
+                const std::vector<Function>& velocityDirichletFunctions,
+                const std::vector<Dune::BitSetVector<dimension>>& velocityDirichletNodes,
+                const Matrices<Matrix>& matrices,
+                const std::vector<Vector>& u_initial,
+                const std::vector<Vector>& v_initial,
+                const std::vector<Vector>& a_initial) {
   switch (scheme) {
     case Config::Newmark:
       return std::make_shared<Newmark<Vector, Matrix, Function, dimension>>(
           matrices, u_initial, v_initial, a_initial, velocityDirichletNodes,
-          velocityDirichletFunction);
+          velocityDirichletFunctions);
     case Config::BackwardEuler:
       return std::make_shared<
           BackwardEuler<Vector, Matrix, Function, dimension>>(
           matrices, u_initial, v_initial, a_initial, velocityDirichletNodes,
-          velocityDirichletFunction);
+          velocityDirichletFunctions);
     default:
       assert(false);
   }
diff --git a/src/time-stepping/rate.hh b/src/time-stepping/rate.hh
index 66b3962b..60268240 100644
--- a/src/time-stepping/rate.hh
+++ b/src/time-stepping/rate.hh
@@ -9,8 +9,10 @@
 template <class Vector, class Matrix, class Function, int dimension>
 std::shared_ptr<RateUpdater<Vector, Matrix, Function, dimension>>
 initRateUpdater(Config::scheme scheme,
-                Function const &velocityDirichletFunction,
-                Dune::BitSetVector<dimension> const &velocityDirichletNodes,
-                Matrices<Matrix> const &matrices, Vector const &u_initial,
-                Vector const &v_initial, Vector const &a_initial);
+                const std::vector<Function>& velocityDirichletFunctions,
+                const std::vector<Dune::BitSetVector<dimension>>& velocityDirichletNodes,
+                const Matrices<Matrix>& matrices,
+                const std::vector<Vector>& u_initial,
+                const std::vector<Vector>& v_initial,
+                const std::vector<Vector>& a_initial);
 #endif
diff --git a/src/time-stepping/rate/backward_euler.cc b/src/time-stepping/rate/backward_euler.cc
index 6664f02b..2694a970 100644
--- a/src/time-stepping/rate/backward_euler.cc
+++ b/src/time-stepping/rate/backward_euler.cc
@@ -1,88 +1,100 @@
-#include <dune/solvers/common/arithmetic.hh>
+#include <dune/matrix-vector/axpy.hh>
+#include <dune/istl/matrixindexset.hh>
 
 #include "backward_euler.hh"
 
 template <class Vector, class Matrix, class Function, size_t dim>
 BackwardEuler<Vector, Matrix, Function, dim>::BackwardEuler(
-    Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-    Vector const &_v_initial, Vector const &_a_initial,
-    Dune::BitSetVector<dim> const &_dirichletNodes,
-    Function const &_dirichletFunction)
+    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const std::vector<Vector>& _v_initial, const std::vector<Vector>& _a_initial,
+    const std::vector<Dune::BitSetVector<dim>>& _dirichletNodes,
+    const std::vector<Function>& _dirichletFunctions)
     : RateUpdater<Vector, Matrix, Function, dim>(
           _matrices, _u_initial, _v_initial, _a_initial, _dirichletNodes,
-          _dirichletFunction) {}
+          _dirichletFunctions) {}
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void BackwardEuler<Vector, Matrix, Function, dim>::setup(
-    Vector const &ell, double _tau, double relativeTime, Vector &rhs,
-    Vector &iterate, Matrix &AM) {
-  this->dirichletFunction.evaluate(relativeTime, this->dirichletValue);
-  this->tau = _tau;
-
-  /* We start out with the formulation
-
-       M a + C v + A u = ell
-
-     Backward Euler means
-
-       a1 = 1.0/tau ( v1 - v0 )
-       u1 = tau v1 + u0
-
-     in summary, we get at time t=1
-
-       M [1.0/tau ( v1 - v0 )] + C v1
-       + A [tau v1 + u0] = ell
-
-     or
-
-       1.0/tau M v1 + C v1 + tau A v1
-       = [1.0/tau M + C + tau A] v1
-       = ell + 1.0/tau M v0 - A u0
-  */
-
-  // set up LHS (for fixed tau, we'd only really have to do this once)
-  {
-    Dune::MatrixIndexSet indices(this->matrices.elasticity.N(),
-                                 this->matrices.elasticity.M());
-    indices.import(this->matrices.elasticity);
-    indices.import(this->matrices.mass);
-    indices.import(this->matrices.damping);
-    indices.exportIdx(AM);
+void BackwardEuler<Vector, Matrix, Function, dim>::setup(const std::vector<Vector>& ell,
+                                                         double _tau,
+                                                         double relativeTime,
+                                                         std::vector<Vector>& rhs, std::vector<Vector>& iterate,
+                                                         std::vector<Matrix>& AM) {
+  for (size_t i=0; i<this->u.size(); i++) {
+    this->dirichletFunctions[i].evaluate(relativeTime, this->dirichletValues[i]);
+    this->tau = _tau;
+
+    /* We start out with the formulation
+
+           M a + C v + A u = ell
+
+         Backward Euler means
+
+           a1 = 1.0/tau ( v1 - v0 )
+           u1 = tau v1 + u0
+
+         in summary, we get at time t=1
+
+           M [1.0/tau ( v1 - v0 )] + C v1
+           + A [tau v1 + u0] = ell
+
+         or
+
+           1.0/tau M v1 + C v1 + tau A v1
+           = [1.0/tau M + C + tau A] v1
+           = ell + 1.0/tau M v0 - A u0
+    */
+
+    // set up LHS (for fixed tau, we'd only really have to do this once)
+    Matrix& LHS = AM[i];
+    {
+      Dune::MatrixIndexSet indices(this->matrices.elasticity[i]->N(),
+                                   this->matrices.elasticity[i]->M());
+      indices.import(*this->matrices.elasticity[i]);
+      indices.import(*this->matrices.mass[i]);
+      indices.import(*this->matrices.damping[i]);
+      indices.exportIdx(LHS);
+    }
+    LHS = 0.0;
+    Dune::MatrixVector::addProduct(LHS, 1.0 / this->tau, *this->matrices.mass[i]);
+    Dune::MatrixVector::addProduct(LHS, 1.0, *this->matrices.damping[i]);
+    Dune::MatrixVector::addProduct(LHS, this->tau, *this->matrices.elasticity[i]);
+
+    // set up RHS
+    {
+      Vector& rhss = rhs[i];
+      rhss = ell[i];
+      Dune::MatrixVector::addProduct(rhss, 1.0 / this->tau, *this->matrices.mass[i],
+                           this->v_o[i]);
+      Dune::MatrixVector::subtractProduct(rhss, *this->matrices.elasticity[i], this->u_o[i]);
+    }
+
+    iterate = this->v_o;
+
+    const Dune::BitSetVector<dim>& dirichletNodess = this->dirichletNodes[i];
+    for (size_t k = 0; k < dirichletNodess.size(); ++k)
+      for (size_t j = 0; j < dim; ++j)
+        if (this->dirichletNodes[k][j])
+          iterate[k][j] = (j == 0) ? this->dirichletValue : 0;
   }
-  AM = 0.0;
-  Arithmetic::addProduct(AM, 1.0 / this->tau, this->matrices.mass);
-  Arithmetic::addProduct(AM, 1.0, this->matrices.damping);
-  Arithmetic::addProduct(AM, this->tau, this->matrices.elasticity);
-
-  // set up RHS
-  {
-    rhs = ell;
-    Arithmetic::addProduct(rhs, 1.0 / this->tau, this->matrices.mass,
-                           this->v_o);
-    Arithmetic::subtractProduct(rhs, this->matrices.elasticity, this->u_o);
-  }
-
-  iterate = this->v_o;
-
-  for (size_t i = 0; i < this->dirichletNodes.size(); ++i)
-    for (size_t j = 0; j < dim; ++j)
-      if (this->dirichletNodes[i][j])
-        iterate[i][j] = (j == 0) ? this->dirichletValue : 0;
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
 void BackwardEuler<Vector, Matrix, Function, dim>::postProcess(
-    Vector const &iterate) {
+    const std::vector<Vector>& iterate) {
   this->postProcessCalled = true;
 
   this->v = iterate;
 
   this->u = this->u_o;
-  Arithmetic::addProduct(this->u, this->tau, this->v);
 
-  this->a = this->v;
-  this->a -= this->v_o;
-  this->a /= this->tau;
+  for (size_t i=0; i<this->u.size(); i++) {
+    Dune::MatrixVector::addProduct(this->u[i], this->tau, this->v[i]);
+
+    Vector& ai = this->a[i];
+    ai = this->v[i];
+    ai -= this->v_o[i];
+    ai /= this->tau;
+  }
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
diff --git a/src/time-stepping/rate/backward_euler.hh b/src/time-stepping/rate/backward_euler.hh
index a9f3b3e7..245bb74b 100644
--- a/src/time-stepping/rate/backward_euler.hh
+++ b/src/time-stepping/rate/backward_euler.hh
@@ -4,14 +4,15 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class BackwardEuler : public RateUpdater<Vector, Matrix, Function, dim> {
 public:
-  BackwardEuler(Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-                Vector const &_v_initial, Vector const &_a_initial,
-                Dune::BitSetVector<dim> const &_dirichletNodes,
-                Function const &_dirichletFunction);
+  BackwardEuler(const Matrices<Matrix> &_matrices, const std::vector<Vector> &_u_initial,
+                const std::vector<Vector> &_v_initial, const std::vector<Vector> &_a_initial,
+                const std::vector<Dune::BitSetVector<dim> > &_dirichletNodes,
+                const std::vector<Function> &_dirichletFunctions);
 
-  void setup(Vector const &, double, double, Vector &, Vector &,
-             Matrix &) override;
-  void postProcess(Vector const &) override;
+  void setup(const std::vector<Vector>&, double, double, std::vector<Vector>&, std::vector<Vector>&,
+             std::vector<Matrix>&) override;
+
+  void postProcess(const std::vector<Vector>&) override;
 
   std::shared_ptr<RateUpdater<Vector, Matrix, Function, dim>> clone()
       const override;
diff --git a/src/time-stepping/rate/newmark.cc b/src/time-stepping/rate/newmark.cc
index 99076858..53bf32c4 100644
--- a/src/time-stepping/rate/newmark.cc
+++ b/src/time-stepping/rate/newmark.cc
@@ -1,27 +1,29 @@
-#include <dune/solvers/common/arithmetic.hh>
+#include <dune/matrix-vector/axpy.hh>
+#include <dune/istl/matrixindexset.hh>
 
 #include "newmark.hh"
 
 template <class Vector, class Matrix, class Function, size_t dim>
 Newmark<Vector, Matrix, Function, dim>::Newmark(
-    Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-    Vector const &_v_initial, Vector const &_a_initial,
-    Dune::BitSetVector<dim> const &_dirichletNodes,
-    Function const &_dirichletFunction)
+    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const std::vector<Vector>& _v_initial, const std::vector<Vector>& _a_initial,
+    const std::vector<Dune::BitSetVector<dim>>& _dirichletNodes,
+    const std::vector<Function>& _dirichletFunctions)
     : RateUpdater<Vector, Matrix, Function, dim>(
           _matrices, _u_initial, _v_initial, _a_initial, _dirichletNodes,
-          _dirichletFunction) {}
+          _dirichletFunctions) {}
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void Newmark<Vector, Matrix, Function, dim>::setup(Vector const &ell,
+void Newmark<Vector, Matrix, Function, dim>::setup(const std::vector<Vector>& ell,
                                                    double _tau,
                                                    double relativeTime,
-                                                   Vector &rhs, Vector &iterate,
-                                                   Matrix &AM) {
-  this->dirichletFunction.evaluate(relativeTime, this->dirichletValue);
-  this->tau = _tau;
+                                                   std::vector<Vector>& rhs, std::vector<Vector>& iterate,
+                                                   std::vector<Matrix>& AM) {
+  for (size_t i=0; i<this->u.size(); i++) {
+    this->dirichletFunctions[i].evaluate(relativeTime, this->dirichletValues[i]);
+    this->tau = _tau;
 
-  /* We start out with the formulation
+    /* We start out with the formulation
 
        M a + C v + A u = ell
 
@@ -41,58 +43,64 @@ void Newmark<Vector, Matrix, Function, dim>::setup(Vector const &ell,
        = [2/tau M + C + tau/2 A] v1
        = ell + 2/tau M v0 + M a0
        - tau/2 A v0 - A u0
-  */
-
-  // set up LHS (for fixed tau, we'd only really have to do this once)
-  {
-    Dune::MatrixIndexSet indices(this->matrices.elasticity.N(),
-                                 this->matrices.elasticity.M());
-    indices.import(this->matrices.elasticity);
-    indices.import(this->matrices.mass);
-    indices.import(this->matrices.damping);
-    indices.exportIdx(AM);
+    */
+
+    // set up LHS (for fixed tau, we'd only really have to do this once)
+    Matrix& LHS = AM[i];
+    {
+      Dune::MatrixIndexSet indices(this->matrices.elasticity[i]->N(),
+                                 this->matrices.elasticity[i]->M());
+      indices.import(*this->matrices.elasticity[i]);
+      indices.import(*this->matrices.mass[i]);
+      indices.import(*this->matrices.damping[i]);
+      indices.exportIdx(LHS);
+    }
+    LHS = 0.0;
+    Dune::MatrixVector::addProduct(LHS, 2.0 / this->tau, *this->matrices.mass[i]);
+    Dune::MatrixVector::addProduct(LHS, 1.0, *this->matrices.damping[i]);
+    Dune::MatrixVector::addProduct(LHS, this->tau / 2.0, *this->matrices.elasticity[i]);
+
+    // set up RHS
+    {
+      Vector& rhss = rhs[i];
+      rhss = ell[i];
+      Dune::MatrixVector::addProduct(rhss, 2.0 / this->tau, *this->matrices.mass[i],
+                           this->v_o[i]);
+      Dune::MatrixVector::addProduct(rhss, *this->matrices.mass[i], this->a_o[i]);
+      Dune::MatrixVector::subtractProduct(rhss, this->tau / 2.0, *this->matrices.elasticity[i],
+                                this->v_o[i]);
+      Dune::MatrixVector::subtractProduct(rhss, *this->matrices.elasticity[i], this->u_o[i]);
+    }
+
+    iterate = this->v_o;
+
+    const Dune::BitSetVector<dim>& dirichletNodess = this->dirichletNodes[i];
+    for (size_t k = 0; k < dirichletNodess.size(); ++k)
+      for (size_t j = 0; j < dim; ++j)
+        if (this->dirichletNodess[k][j])
+          iterate[k][j] = (j == 0) ? this->dirichletValue : 0;
   }
-  AM = 0.0;
-  Arithmetic::addProduct(AM, 2.0 / this->tau, this->matrices.mass);
-  Arithmetic::addProduct(AM, 1.0, this->matrices.damping);
-  Arithmetic::addProduct(AM, this->tau / 2.0, this->matrices.elasticity);
-
-  // set up RHS
-  {
-    rhs = ell;
-    Arithmetic::addProduct(rhs, 2.0 / this->tau, this->matrices.mass,
-                           this->v_o);
-    Arithmetic::addProduct(rhs, this->matrices.mass, this->a_o);
-    Arithmetic::subtractProduct(rhs, this->tau / 2.0, this->matrices.elasticity,
-                                this->v_o);
-    Arithmetic::subtractProduct(rhs, this->matrices.elasticity, this->u_o);
-  }
-
-  iterate = this->v_o;
-
-  for (size_t i = 0; i < this->dirichletNodes.size(); ++i)
-    for (size_t j = 0; j < dim; ++j)
-      if (this->dirichletNodes[i][j])
-        iterate[i][j] = (j == 0) ? this->dirichletValue : 0;
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void Newmark<Vector, Matrix, Function, dim>::postProcess(
-    Vector const &iterate) {
+void Newmark<Vector, Matrix, Function, dim>::postProcess(const std::vector<Vector>& iterate) {
   this->postProcessCalled = true;
 
   this->v = iterate;
 
   // u1 = tau/2 ( v1 + v0 ) + u0
   this->u = this->u_o;
-  Arithmetic::addProduct(this->u, this->tau / 2.0, this->v);
-  Arithmetic::addProduct(this->u, this->tau / 2.0, this->v_o);
-
-  // a1 = 2/tau ( v1 - v0 ) - a0
-  this->a = 0.0;
-  Arithmetic::addProduct(this->a, 2.0 / this->tau, this->v);
-  Arithmetic::subtractProduct(this->a, 2.0 / this->tau, this->v_o);
-  Arithmetic::subtractProduct(this->a, 1.0, this->a_o);
+
+  for (size_t i=0; i<this->u.size(); i++) {
+    Dune::MatrixVector::addProduct(this->u[i], this->tau / 2.0, this->v[i]);
+    Dune::MatrixVector::addProduct(this->u[i], this->tau / 2.0, this->v_o[i]);
+
+    // a1 = 2/tau ( v1 - v0 ) - a0
+    this->a[i] = 0.0;
+    Dune::MatrixVector::addProduct(this->a[i], 2.0 / this->tau, this->v[i]);
+    Dune::MatrixVector::subtractProduct(this->a[i], 2.0 / this->tau, this->v_o[i]);
+    Dune::MatrixVector::subtractProduct(this->a[i], 1.0, this->a_o[i]);
+  }
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
diff --git a/src/time-stepping/rate/newmark.hh b/src/time-stepping/rate/newmark.hh
index 400eff03..be5cb844 100644
--- a/src/time-stepping/rate/newmark.hh
+++ b/src/time-stepping/rate/newmark.hh
@@ -4,14 +4,15 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class Newmark : public RateUpdater<Vector, Matrix, Function, dim> {
 public:
-  Newmark(Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-          Vector const &_v_initial, Vector const &_a_initial,
-          Dune::BitSetVector<dim> const &_dirichletNodes,
-          Function const &_dirichletFunction);
+  Newmark(const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+          const std::vector<Vector>& _v_initial, const std::vector<Vector>& _a_initial,
+          const std::vector<Dune::BitSetVector<dim>>& _dirichletNodes,
+          const std::vector<Function>& _dirichletFunctions);
 
-  void setup(Vector const &, double, double, Vector &, Vector &,
-             Matrix &) override;
-  void postProcess(Vector const &) override;
+  void setup(const std::vector<Vector>&, double, double, std::vector<Vector>&, std::vector<Vector>&,
+             std::vector<Matrix>&) override;
+
+  void postProcess(const std::vector<Vector>&) override;
 
   std::shared_ptr<RateUpdater<Vector, Matrix, Function, dim>> clone()
       const override;
diff --git a/src/time-stepping/rate/rateupdater.cc b/src/time-stepping/rate/rateupdater.cc
index 765b6b64..23d09536 100644
--- a/src/time-stepping/rate/rateupdater.cc
+++ b/src/time-stepping/rate/rateupdater.cc
@@ -6,16 +6,16 @@
 
 template <class Vector, class Matrix, class Function, size_t dim>
 RateUpdater<Vector, Matrix, Function, dim>::RateUpdater(
-    Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-    Vector const &_v_initial, Vector const &_a_initial,
-    Dune::BitSetVector<dim> const &_dirichletNodes,
-    Function const &_dirichletFunction)
+    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const std::vector<Vector>& _v_initial, const std::vector<Vector>& _a_initial,
+    const std::vector<Dune::BitSetVector<dim>>& _dirichletNodes,
+    const std::vector<Function>& _dirichletFunctions)
     : matrices(_matrices),
       u(_u_initial),
       v(_v_initial),
       a(_a_initial),
       dirichletNodes(_dirichletNodes),
-      dirichletFunction(_dirichletFunction) {}
+      dirichletFunctions(_dirichletFunctions) {}
 
 template <class Vector, class Matrix, class Function, size_t dim>
 void RateUpdater<Vector, Matrix, Function, dim>::nextTimeStep() {
@@ -26,17 +26,15 @@ void RateUpdater<Vector, Matrix, Function, dim>::nextTimeStep() {
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void RateUpdater<Vector, Matrix, Function, dim>::extractDisplacement(
-    Vector &displacement) const {
+void RateUpdater<Vector, Matrix, Function, dim>::extractDisplacement(std::vector<Vector>& displacements) const {
   if (!postProcessCalled)
     DUNE_THROW(Dune::Exception, "It seems you forgot to call postProcess!");
 
-  displacement = u;
+  displacements = u;
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void RateUpdater<Vector, Matrix, Function, dim>::extractVelocity(
-    Vector &velocity) const {
+void RateUpdater<Vector, Matrix, Function, dim>::extractVelocity(std::vector<Vector>& velocity) const {
   if (!postProcessCalled)
     DUNE_THROW(Dune::Exception, "It seems you forgot to call postProcess!");
 
@@ -44,14 +42,12 @@ void RateUpdater<Vector, Matrix, Function, dim>::extractVelocity(
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void RateUpdater<Vector, Matrix, Function, dim>::extractOldVelocity(
-    Vector &oldVelocity) const {
+void RateUpdater<Vector, Matrix, Function, dim>::extractOldVelocity(std::vector<Vector>& oldVelocity) const {
   oldVelocity = v_o;
 }
 
 template <class Vector, class Matrix, class Function, size_t dim>
-void RateUpdater<Vector, Matrix, Function, dim>::extractAcceleration(
-    Vector &acceleration) const {
+void RateUpdater<Vector, Matrix, Function, dim>::extractAcceleration(std::vector<Vector>& acceleration) const {
   if (!postProcessCalled)
     DUNE_THROW(Dune::Exception, "It seems you forgot to call postProcess!");
 
diff --git a/src/time-stepping/rate/rateupdater.hh b/src/time-stepping/rate/rateupdater.hh
index 7ba9c7ba..6e2a9863 100644
--- a/src/time-stepping/rate/rateupdater.hh
+++ b/src/time-stepping/rate/rateupdater.hh
@@ -10,10 +10,10 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class RateUpdater {
 protected:
-  RateUpdater(Matrices<Matrix> const &_matrices, Vector const &_u_initial,
-              Vector const &_v_initial, Vector const &_a_initial,
-              Dune::BitSetVector<dim> const &_dirichletNodes,
-              Function const &_dirichletFunction);
+  RateUpdater(const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+              const std::vector<Vector>& _v_initial, const std::vector<Vector>& _a_initial,
+              const std::vector<Dune::BitSetVector<dim>>& _dirichletNodes,
+              const std::vector<Function>& _dirichletFunctions);
 
 public:
   void nextTimeStep();
@@ -21,22 +21,22 @@ class RateUpdater {
                      Vector &rhs, Vector &iterate, Matrix &AB) = 0;
 
   void virtual postProcess(Vector const &iterate) = 0;
-  void extractDisplacement(Vector &displacement) const;
-  void extractVelocity(Vector &velocity) const;
-  void extractOldVelocity(Vector &velocity) const;
-  void extractAcceleration(Vector &acceleration) const;
+  void extractDisplacement(std::vector<Vector>& displacements) const;
+  void extractVelocity(std::vector<Vector>& velocity) const;
+  void extractOldVelocity(std::vector<Vector>& velocity) const;
+  void extractAcceleration(std::vector<Vector>& acceleration) const;
 
   std::shared_ptr<RateUpdater<Vector, Matrix, Function, dim>> virtual clone()
       const = 0;
 
 protected:
-  Matrices<Matrix> const &matrices;
-  Vector u, v, a;
-  Dune::BitSetVector<dim> const &dirichletNodes;
-  Function const &dirichletFunction;
+  const Matrices<Matrix>& matrices;
+  std::vector<Vector> u, v, a;
+  const std::vector<Dune::BitSetVector<dim>>& dirichletNodes;
+  const std::vector<Function>& dirichletFunctions;
   double dirichletValue;
 
-  Vector u_o, v_o, a_o;
+  std::vector<Vector> u_o, v_o, a_o;
   double tau;
 
   bool postProcessCalled = true;
diff --git a/src/time-stepping/rate_tmpl.cc b/src/time-stepping/rate_tmpl.cc
index 728d55c4..92c15bdd 100644
--- a/src/time-stepping/rate_tmpl.cc
+++ b/src/time-stepping/rate_tmpl.cc
@@ -6,7 +6,10 @@ using Function = Dune::VirtualFunction<double, double>;
 
 template std::shared_ptr<RateUpdater<Vector, Matrix, Function, MY_DIM>>
 initRateUpdater<Vector, Matrix, Function, MY_DIM>(
-    Config::scheme scheme, Function const &velocityDirichletFunction,
-    Dune::BitSetVector<MY_DIM> const &velocityDirichletNodes,
-    Matrices<Matrix> const &matrices, Vector const &u_initial,
-    Vector const &v_initial, Vector const &a_initial);
+    Config::scheme scheme,
+    const std::vector<Function>& velocityDirichletFunctions,
+    const std::vector<Dune::BitSetVector<dimension>>& velocityDirichletNodes,
+    const Matrices<Matrix>& matrices,
+    const std::vector<Vector>& u_initial,
+    const std::vector<Vector>& v_initial,
+    const std::vector<Vector>& a_initial);
diff --git a/src/time-stepping/state.cc b/src/time-stepping/state.cc
index 49567d3f..f07a8033 100644
--- a/src/time-stepping/state.cc
+++ b/src/time-stepping/state.cc
@@ -7,9 +7,8 @@
 #include "state/sliplawstateupdater.cc"
 
 template <class ScalarVector, class Vector>
-std::shared_ptr<StateUpdater<ScalarVector, Vector>> initStateUpdater(
-    Config::stateModel model, ScalarVector const &alpha_initial,
-    Dune::BitSetVector<1> const &frictionalNodes, double L, double V0) {
+std::shared_ptr<StateUpdater<ScalarVector, Vector>> initStateUpdater(Config::stateModel model, const std::vector<ScalarVector>& alpha_initial,
+    const std::vector<Dune::BitSetVector<_Tp1>>& frictionalNodes, const std::vector<double>& L, const std::vector<double>& V0) {
   switch (model) {
     case Config::AgeingLaw:
       return std::make_shared<AgeingLawStateUpdater<ScalarVector, Vector>>(
diff --git a/src/time-stepping/state.hh b/src/time-stepping/state.hh
index cb372217..18194643 100644
--- a/src/time-stepping/state.hh
+++ b/src/time-stepping/state.hh
@@ -12,7 +12,7 @@
 
 template <class ScalarVector, class Vector>
 std::shared_ptr<StateUpdater<ScalarVector, Vector>> initStateUpdater(
-    Config::stateModel model, ScalarVector const &alpha_initial,
-    Dune::BitSetVector<1> const &frictionalNodes, double L, double V0);
+    Config::stateModel model, const std::vector<ScalarVector>& alpha_initial,
+    const std::vector<Dune::BitSetVector<1>>& frictionalNodes, const std::vector<double>& L, const std::vector<double>& V0);
 
 #endif
diff --git a/src/time-stepping/state/ageinglawstateupdater.cc b/src/time-stepping/state/ageinglawstateupdater.cc
index bbec51f2..24985bc2 100644
--- a/src/time-stepping/state/ageinglawstateupdater.cc
+++ b/src/time-stepping/state/ageinglawstateupdater.cc
@@ -5,8 +5,10 @@
 
 template <class ScalarVector, class Vector>
 AgeingLawStateUpdater<ScalarVector, Vector>::AgeingLawStateUpdater(
-    ScalarVector const &_alpha_initial, Dune::BitSetVector<1> const &_nodes,
-    double _L, double _V0)
+    const std::vector<ScalarVector>& _alpha_initial,
+    const std::vector<Dune::BitSetVector<1>>& _nodes,
+    const std::vector<double>& _L,
+    const std::vector<double>& _V0)
     : alpha(_alpha_initial), nodes(_nodes), L(_L), V0(_V0) {}
 
 template <class ScalarVector, class Vector>
@@ -32,21 +34,28 @@ double liftSingularity(double c, double x) {
 
 template <class ScalarVector, class Vector>
 void AgeingLawStateUpdater<ScalarVector, Vector>::solve(
-    Vector const &velocity_field) {
-  for (size_t i = 0; i < nodes.size(); ++i) {
-    if (not toBool(nodes[i]))
-      continue;
-
-    double const V = velocity_field[i].two_norm();
-    double const mtoL = -tau / L;
-    alpha[i] = std::log(std::exp(alpha_o[i] + V * mtoL) +
-                        V0 * liftSingularity(mtoL, V));
+  const std::vector<Vector>& velocity_field) {
+  for (size_t i = 0; i < alpha.size(); ++i) {
+    const auto& velocity_fieldi = velocity_field[i];
+    const auto& nodesi = nodes[i];
+    auto& alphai = alpha[i];
+    auto& alpha_oi = alpha_o[i];
+
+    for (size_t j = 0; j < nodesi.size(); ++j) {
+      if (not toBool(nodesi[j]))
+        continue;
+
+      double const V = velocity_fieldi[j].two_norm();
+      double const mtoL = -tau / L[i];
+      alphai[j] = std::log(std::exp(alpha_oi[j] + V * mtoL) +
+                        V0[i] * liftSingularity(mtoL, V));
+    }
   }
 }
 
 template <class ScalarVector, class Vector>
 void AgeingLawStateUpdater<ScalarVector, Vector>::extractAlpha(
-    ScalarVector &_alpha) {
+    std::vector<ScalarVector>& _alpha) {
   _alpha = alpha;
 }
 
diff --git a/src/time-stepping/state/ageinglawstateupdater.hh b/src/time-stepping/state/ageinglawstateupdater.hh
index 874e26d5..4b8ec558 100644
--- a/src/time-stepping/state/ageinglawstateupdater.hh
+++ b/src/time-stepping/state/ageinglawstateupdater.hh
@@ -6,23 +6,23 @@
 template <class ScalarVector, class Vector>
 class AgeingLawStateUpdater : public StateUpdater<ScalarVector, Vector> {
 public:
-  AgeingLawStateUpdater(ScalarVector const &_alpha_initial,
-                        Dune::BitSetVector<1> const &_nodes, double _L,
-                        double _V0);
+  AgeingLawStateUpdater(const std::vector<ScalarVector>& _alpha_initial,
+                        const std::vector<Dune::BitSetVector<1>>& _nodes, const std::vector<double>& _L,
+                        const std::vector<double>& _V0);
 
   void nextTimeStep() override;
   void setup(double _tau) override;
-  void solve(Vector const &velocity_field) override;
-  void extractAlpha(ScalarVector &) override;
+  void solve(const std::vector<Vector>& velocity_field) override;
+  void extractAlpha(std::vector<ScalarVector> &) override;
 
   std::shared_ptr<StateUpdater<ScalarVector, Vector>> clone() const override;
 
 private:
-  ScalarVector alpha_o;
-  ScalarVector alpha;
-  Dune::BitSetVector<1> const &nodes;
-  double const L;
-  double const V0;
+  std::vector<ScalarVector> alpha_o;
+  std::vector<ScalarVector> alpha;
+  const std::vector<Dune::BitSetVector<1>>& nodes;
+  const std::vector<double>& L;
+  const std::vector<double>& V0;
   double tau;
 };
 #endif
diff --git a/src/time-stepping/state/sliplawstateupdater.cc b/src/time-stepping/state/sliplawstateupdater.cc
index a31964d0..6b322d89 100644
--- a/src/time-stepping/state/sliplawstateupdater.cc
+++ b/src/time-stepping/state/sliplawstateupdater.cc
@@ -5,8 +5,9 @@
 
 template <class ScalarVector, class Vector>
 SlipLawStateUpdater<ScalarVector, Vector>::SlipLawStateUpdater(
-    ScalarVector const &_alpha_initial, Dune::BitSetVector<1> const &_nodes,
-    double _L, double _V0)
+    const std::vector<ScalarVector>& _alpha_initial,
+    const std::vector<Dune::BitSetVector<1>>& _nodes, const std::vector<double>& _L,
+    const std::vector<double>& _V0)
     : alpha(_alpha_initial), nodes(_nodes), L(_L), V0(_V0) {}
 
 template <class ScalarVector, class Vector>
@@ -21,21 +22,29 @@ void SlipLawStateUpdater<ScalarVector, Vector>::setup(double _tau) {
 
 template <class ScalarVector, class Vector>
 void SlipLawStateUpdater<ScalarVector, Vector>::solve(
-    Vector const &velocity_field) {
-  for (size_t i = 0; i < nodes.size(); ++i) {
-    if (not toBool(nodes[i]))
-      continue;
-
-    double const V = velocity_field[i].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);
-  }
+  const std::vector<Vector>& velocity_field) {
+
+  for (size_t i = 0; i < alpha.size(); ++i) {
+      const auto& velocity_fieldi = velocity_field[i];
+      const auto& nodesi = nodes[i];
+      auto& alphai = alpha[i];
+      auto& alpha_oi = alpha_o[i];
+
+      for (size_t j = 0; j < nodesi.size(); ++j) {
+        if (not toBool(nodesi[j]))
+          continue;
+
+        double const V = velocity_fieldi[j].two_norm();
+        double const mtoL = -tau * V / L[i];
+        alphai[j] = (V <= 0) ? alpha_oi[j] : std::expm1(mtVoL) * std::log(V / V0[i]) +
+                                               alpha_oi[j] * std::exp(mtVoL);
+      }
+   }
 }
 
 template <class ScalarVector, class Vector>
 void SlipLawStateUpdater<ScalarVector, Vector>::extractAlpha(
-    ScalarVector &_alpha) {
+    std::vector<ScalarVector>& _alpha) {
   _alpha = alpha;
 }
 
diff --git a/src/time-stepping/state/sliplawstateupdater.hh b/src/time-stepping/state/sliplawstateupdater.hh
index ddf2b26e..760044e8 100644
--- a/src/time-stepping/state/sliplawstateupdater.hh
+++ b/src/time-stepping/state/sliplawstateupdater.hh
@@ -6,24 +6,24 @@
 template <class ScalarVector, class Vector>
 class SlipLawStateUpdater : public StateUpdater<ScalarVector, Vector> {
 public:
-  SlipLawStateUpdater(ScalarVector const &_alpha_initial,
-                      Dune::BitSetVector<1> const &_nodes, double _L,
-                      double _V0);
+    SlipLawStateUpdater(const std::vector<ScalarVector>& _alpha_initial,
+                          const std::vector<Dune::BitSetVector<1>>& _nodes, const std::vector<double>& _L,
+                          const std::vector<double>& _V0);
 
-  void nextTimeStep() override;
-  void setup(double _tau) override;
-  void solve(Vector const &velocity_field) override;
-  void extractAlpha(ScalarVector &) override;
+    void nextTimeStep() override;
+    void setup(double _tau) override;
+    void solve(const std::vector<Vector>& velocity_field) override;
+    void extractAlpha(std::vector<ScalarVector> &) override;
 
-  std::shared_ptr<StateUpdater<ScalarVector, Vector>> clone() const override;
+    std::shared_ptr<StateUpdater<ScalarVector, Vector>> clone() const override;
 
 private:
-  ScalarVector alpha_o;
-  ScalarVector alpha;
-  Dune::BitSetVector<1> const &nodes;
-  double const L;
-  double const V0;
-  double tau;
+    std::vector<ScalarVector> alpha_o;
+    std::vector<ScalarVector> alpha;
+    const std::vector<Dune::BitSetVector<1>>& nodes;
+    const std::vector<double>& L;
+    const std::vector<double>& V0;
+    double tau;
 };
 
 #endif
diff --git a/src/time-stepping/state/stateupdater.hh b/src/time-stepping/state/stateupdater.hh
index e1431ac1..f7dd1b1b 100644
--- a/src/time-stepping/state/stateupdater.hh
+++ b/src/time-stepping/state/stateupdater.hh
@@ -7,8 +7,8 @@ template <class ScalarVectorTEMPLATE, class Vector> class StateUpdater {
 
   void virtual nextTimeStep() = 0;
   void virtual setup(double _tau) = 0;
-  void virtual solve(Vector const &velocity_field) = 0;
-  void virtual extractAlpha(ScalarVector &alpha) = 0;
+  void virtual solve(const std::vector<Vector>& velocity_field) = 0;
+  void virtual extractAlpha(std::vector<ScalarVector>& alpha) = 0;
 
   std::shared_ptr<StateUpdater<ScalarVector, Vector>> virtual clone() const = 0;
 };
diff --git a/src/time-stepping/state_tmpl.cc b/src/time-stepping/state_tmpl.cc
index 20f4b755..5703f81a 100644
--- a/src/time-stepping/state_tmpl.cc
+++ b/src/time-stepping/state_tmpl.cc
@@ -2,5 +2,5 @@
 
 template std::shared_ptr<StateUpdater<ScalarVector, Vector>>
 initStateUpdater<ScalarVector, Vector>(
-    Config::stateModel model, ScalarVector const &alpha_initial,
-    Dune::BitSetVector<1> const &frictionalNodes, double L, double V0);
+    Config::stateModel model, const std::vector<ScalarVector>& alpha_initial,
+        const std::vector<Dune::BitSetVector<_Tp1>>& frictionalNodes, const std::vector<double>& L, const std::vector<double>& V0);
-- 
GitLab