diff --git a/dune/tectonic/CMakeLists.txt b/dune/tectonic/CMakeLists.txt
index 7e6ac59121e6adcd6e5f28333526e99e862bf3c0..525c9d2bfe70b60425a80194ac6b5b808a9def07 100644
--- a/dune/tectonic/CMakeLists.txt
+++ b/dune/tectonic/CMakeLists.txt
@@ -1,5 +1,5 @@
 install(FILES
-  body.hh
+  bodydata.hh
   frictiondata.hh
   frictionpotential.hh
   globalfrictiondata.hh
diff --git a/dune/tectonic/body.hh b/dune/tectonic/bodydata.hh
similarity index 85%
rename from dune/tectonic/body.hh
rename to dune/tectonic/bodydata.hh
index 2c2ed7214d673c3f91b1448463834634b111ffb9..eee19ff11ec6a919de1339ff09a29a0ae7238edd 100644
--- a/dune/tectonic/body.hh
+++ b/dune/tectonic/bodydata.hh
@@ -1,7 +1,7 @@
-#ifndef DUNE_TECTONIC_BODY_HH
-#define DUNE_TECTONIC_BODY_HH
+#ifndef DUNE_TECTONIC_BODY_DATA_HH
+#define DUNE_TECTONIC_BODY_DATA_HH
 
-template <int dimension> struct Body {
+template <int dimension> struct BodyData {
   using ScalarFunction =
       Dune::VirtualFunction<Dune::FieldVector<double, dimension>,
                             Dune::FieldVector<double, 1>>;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3bb473174c2eff23faa233d711fc63793dca8b54..f2b048493b5f2de31b439f2023f7e211f8c76054 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(SW_SOURCE_FILES
   assemblers.cc
+  body.cc
   enumparser.cc
   hdf5/frictionalboundary-writer.cc
   hdf5/iteration-writer.cc
@@ -22,7 +23,9 @@ set(SW_SOURCE_FILES
 
 set(MSW_SOURCE_FILES
   assemblers.cc
+  body.cc
   enumparser.cc
+  levelcontactnetwork.cc
   hdf5/frictionalboundary-writer.cc
   hdf5/iteration-writer.cc
   hdf5/patchinfo-writer.cc
@@ -34,6 +37,7 @@ set(MSW_SOURCE_FILES
   multi-body-problem.cc
   spatial-solving/fixedpointiterator.cc
   spatial-solving/solverfactory.cc
+  stackedblocksfactory.cc
   time-stepping/adaptivetimestepper.cc
   time-stepping/coupledtimestepper.cc
   time-stepping/rate.cc
diff --git a/src/assemblers_tmpl.cc b/src/assemblers_tmpl.cc
index 0d5ea0c609ac287f67b8df5e79277f17772ce894..036dbc9ee59e8e5c5d964e06d0477d693cbb71b1 100644
--- a/src/assemblers_tmpl.cc
+++ b/src/assemblers_tmpl.cc
@@ -3,5 +3,8 @@
 #endif
 
 #include "explicitgrid.hh"
+#include "explicitvectors.hh"
 
-template class MyAssembler<DeformedGrid::LeafGridView, MY_DIM>;
+#include "body.hh"
+
+template class MyAssembler<Body<Grid, Vector, MY_DIM>::DeformedLeafGridView, MY_DIM>;
diff --git a/src/body.cc b/src/body.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f110eec13b143b1b619a35c5ac4f248cef133785
--- /dev/null
+++ b/src/body.cc
@@ -0,0 +1,81 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "body.hh"
+
+template <class GridTEMPLATE, class VectorTEMPLATE, int dims>
+Body<GridTEMPLATE, VectorTEMPLATE, dims>::Body(const std::shared_ptr<BodyData<dims>>& bodyData, const std::shared_ptr<GridType>& grid) :
+    bodyData_(bodyData),
+    grid_(grid),
+    deformation_(std::make_shared<DeformationFunction>(grid_->leafGridView())),
+    deformedGrid_(std::make_shared<DeformedGridType>(*grid_, *deformation_)),
+    leafView_(deformedGrid_->leafGridView()),
+    leafVertexCount_(leafView_.size(dims)),
+    assembler_(std::make_shared<Assembler>(leafView_)),
+    matrices_()
+{
+    leafBoundaryConditions_.resize(0);
+
+    levelBoundaryConditions_.resize(grid_->maxLevel()+1);
+    for (size_t i=0; i<levelBoundaryConditions_.size(); i++) {
+        levelBoundaryConditions_[i].resize(0);
+    }
+}
+
+
+template <class GridTEMPLATE, class VectorTEMPLATE, int dims>
+void Body<GridTEMPLATE, VectorTEMPLATE, dims>::assemble() {
+
+    // assemble matrices
+    assembler_->assembleElasticity(bodyData_->getYoungModulus(), bodyData_->getPoissonRatio(), *matrices_.elasticity);
+    assembler_->assembleViscosity(bodyData_->getShearViscosityField(), bodyData_->getBulkViscosityField(), *matrices_.damping);
+    assembler_->assembleMass(bodyData_->getDensityField(), *matrices_.mass);
+
+    // assemble state energy norm
+    typename FrictionBoundaryCondition::BoundaryPatch frictionBoundary(this->leafView(), false); // boundary patch containing all friction boundaries (possibly disconnected)
+    for (size_t i=0; i<frictionBoundaryConditions_.size(); i++) {
+        const auto& frictionCondition = frictionBoundaryConditions_[i];
+        //std::cout << "grid Pointer " << frictionCondition->boundaryPatch()-> << std::endl;
+        frictionBoundary.addPatch(*frictionCondition->boundaryPatch());
+    }
+
+    ScalarMatrix relativeFrictionBoundaryMass;
+    assembler_->assembleFrictionalBoundaryMass(frictionBoundary, relativeFrictionBoundaryMass);
+    relativeFrictionBoundaryMass /= frictionBoundary.area(); // TODO: weight by individual friction patches?
+    stateEnergyNorm_ = std::make_shared<const EnergyNorm<ScalarMatrix, ScalarVector>>(relativeFrictionBoundaryMass);
+
+    // assemble forces
+    assembler_->assembleBodyForce(bodyData_->getGravityField(), gravityFunctional_);
+}
+
+template <class GridTEMPLATE, class VectorTEMPLATE, int dims>
+void Body<GridTEMPLATE, VectorTEMPLATE, dims>::assembleFriction(const Config::FrictionModel& frictionModel, const ScalarVector& weightedNormalStress) {
+    globalFriction_.resize(frictionBoundaryConditions_.size());
+
+    for (size_t i=0; i<globalFriction_.size(); i++) {
+        const auto& frictionBoundaryCondition = frictionBoundaryConditions_[i];
+        globalFriction_[i] = assembler_->assembleFrictionNonlinearity(frictionModel, *frictionBoundaryCondition->boundaryPatch(), *frictionBoundaryCondition->frictionData(), weightedNormalStress);
+    }
+}
+
+template <class GridTEMPLATE, class VectorTEMPLATE, int dims>
+void Body<GridTEMPLATE, VectorTEMPLATE, dims>::externalForce(double relativeTime, VectorType& ell) const {
+    // Problem formulation: right-hand side
+    std::vector<std::shared_ptr<LeafBoundaryCondition>> leafNeumannConditions;
+    leafBoundaryConditions("neumann", leafNeumannConditions);
+    ell.resize(gravityFunctional_.size());
+    ell = gravityFunctional_;
+
+    for (size_t i=0; i<leafNeumannConditions.size(); i++) {
+        const auto& leafNeumannCondition = leafNeumannConditions[i];
+
+        VectorType b;
+        assembler_->assembleNeumann(*leafNeumannCondition->boundaryPatch(), b, *leafNeumannCondition->boundaryFunction(),
+                                relativeTime);
+        ell += b;
+    }
+}
+
+#include "body_tmpl.cc"
diff --git a/src/body.hh b/src/body.hh
new file mode 100644
index 0000000000000000000000000000000000000000..df86f2fa62f571ee793741666fac454609289870
--- /dev/null
+++ b/src/body.hh
@@ -0,0 +1,180 @@
+#ifndef SRC_BODY_HH
+#define SRC_BODY_HH
+
+#include <functional>
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/function.hh>
+#include <dune/common/shared_ptr.hh>
+
+#include <dune/istl/bcrsmatrix.hh>
+#include <dune/istl/bvector.hh>
+
+//#include <dune/fufem/assemblers/assembler.hh>
+//#pragma clang diagnostic push
+//#pragma clang diagnostic ignored "-Wsign-compare"
+//#include <dune/fufem/functionspacebases/p0basis.hh>
+//#pragma clang diagnostic pop
+//#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
+
+#include <dune/grid/geometrygrid/grid.hh>
+
+#include <dune/fufem/functions/deformationfunction.hh>
+
+#include <dune/solvers/norms/energynorm.hh>
+
+#include <dune/tectonic/bodydata.hh>
+#include <dune/tectonic/globalfriction.hh>
+#include <dune/tectonic/globalfrictiondata.hh>
+
+#include "assemblers.hh"
+#include "boundarycondition.hh"
+#include "enums.hh"
+#include "matrices.hh"
+
+
+
+template <class GridTEMPLATE, class VectorTEMPLATE, int dims> class Body {
+public:
+  using GridType = GridTEMPLATE;
+  using VectorType = VectorTEMPLATE;
+
+  using DeformationFunction = DeformationFunction<typename GridType::LeafGridView, VectorType>;
+  using DeformedGridType = Dune::GeometryGrid<GridType, DeformationFunction>;
+  using DeformedLeafGridView = typename DeformedGridType::LeafGridView;
+  using DeformedLevelGridView = typename DeformedGridType::LevelGridView;
+
+  using Assembler = MyAssembler<DeformedLeafGridView, dims>;
+  using Matrix = typename Assembler::Matrix;
+  using LocalVector = typename VectorType::block_type;
+
+  using ScalarMatrix = Dune::BCRSMatrix<Dune::FieldMatrix<double, 1, 1>>;
+  using ScalarVector = Dune::BlockVector<Dune::FieldVector<double, 1>>;
+
+  using Function = Dune::VirtualFunction<double, double>;
+
+  using LeafBoundaryCondition = BoundaryCondition<DeformedLeafGridView, dims>;
+  using FrictionBoundaryCondition = FrictionBoundaryCondition<DeformedLeafGridView, LocalVector, dims>;
+  using LevelBoundaryCondition = BoundaryCondition<DeformedLevelGridView, dims>;
+
+private:
+  std::shared_ptr<BodyData<dims>> bodyData_;
+
+  std::shared_ptr<GridType> grid_;
+  std::shared_ptr<DeformationFunction> deformation_;
+  std::shared_ptr<DeformedGridType> deformedGrid_;
+
+  DeformedLeafGridView leafView_;
+  int leafVertexCount_;
+
+  std::shared_ptr<Assembler> assembler_;
+  Matrices<Matrix,1> matrices_;
+
+  VectorType gravityFunctional_;
+  std::shared_ptr<const EnergyNorm<ScalarMatrix, ScalarVector>> stateEnergyNorm_;
+
+  // boundary conditions
+  std::vector<std::shared_ptr<LeafBoundaryCondition>> leafBoundaryConditions_;
+  std::vector<std::vector<std::shared_ptr<LevelBoundaryCondition>>> levelBoundaryConditions_; // first index: level, second index: bc on lvl
+
+  // friction boundary conditions
+  std::vector<std::shared_ptr<FrictionBoundaryCondition>> frictionBoundaryConditions_;
+  std::vector<std::shared_ptr<GlobalFriction<Matrix, VectorType>>> globalFriction_;
+
+public:
+  Body(const std::shared_ptr<BodyData<dims>>& bodyData, const std::shared_ptr<GridType>& grid);
+
+  void assemble();
+  void assembleFriction(const Config::FrictionModel& frictionModel, const ScalarVector& weightedNormalStress);
+
+  const std::shared_ptr<Assembler>& assembler() const {
+      return assembler_;
+  }
+
+  const std::shared_ptr<BodyData<dims>>& data() const {
+      return bodyData_;
+  }
+
+  void addBoundaryCondition(std::shared_ptr<LeafBoundaryCondition> leafBoundaryCondition) {
+      leafBoundaryConditions_.push_back(leafBoundaryCondition);
+  }
+
+  const std::vector<std::shared_ptr<LeafBoundaryCondition>>& leafBoundaryConditions() const {
+      return leafBoundaryConditions_;
+  }
+
+  void leafBoundaryConditions(const std::string& tag, std::vector<std::shared_ptr<LeafBoundaryCondition>>& selectedConditions) const {
+      selectedConditions.resize(0);
+      for (size_t i=0; i<leafBoundaryConditions_.size(); i++) {
+          if (leafBoundaryConditions_[i]->tag() == tag)
+              selectedConditions.push_back(leafBoundaryConditions_[i]);
+      }
+  }
+
+  void addBoundaryCondition(std::shared_ptr<FrictionBoundaryCondition> frictionBoundaryCondition) {
+      frictionBoundaryConditions_.push_back(frictionBoundaryCondition);
+  }
+
+  const std::vector<std::shared_ptr<FrictionBoundaryCondition>>& frictionBoundaryConditions() const {
+      return frictionBoundaryConditions_;
+  }
+
+  void addBoundaryCondition(std::shared_ptr<LevelBoundaryCondition> levelBoundaryCondition, int level) {
+      levelBoundaryConditions_[level].push_back(levelBoundaryCondition);
+  }
+
+  const std::vector<std::vector<std::shared_ptr<LevelBoundaryCondition>>>& levelBoundaryConditions() const {
+      return levelBoundaryConditions_;
+  }
+
+  void levelBoundaryConditions(const std::string& tag, std::vector<std::vector<std::shared_ptr<LevelBoundaryCondition>>>& selectedConditions) const {
+      selectedConditions.resize(levelBoundaryConditions_.size());
+      for (size_t level=0; level<levelBoundaryConditions_.size(); level++) {
+          const std::vector<std::shared_ptr<LevelBoundaryCondition>>& levelBoundaryConditions = levelBoundaryConditions_[level];
+          for (size_t i=0; i<levelBoundaryConditions.size(); i++) {
+              if (levelBoundaryConditions[i]->tag() == tag)
+                  selectedConditions[level].push_back(levelBoundaryConditions[i]);
+          }
+      }
+  }
+
+  const std::vector<std::shared_ptr<LevelBoundaryCondition>>& levelBoundaryConditions(int level) const {
+      return levelBoundaryConditions_[level];
+  }
+
+  void levelBoundaryConditions(const std::string& tag, int level, std::vector<std::shared_ptr<LevelBoundaryCondition>>& selectedConditions) const {
+      selectedConditions.resize(0);
+      const std::vector<std::shared_ptr<LevelBoundaryCondition>>& levelBoundaryConditions = levelBoundaryConditions_[level];
+      for (size_t i=0; i<levelBoundaryConditions.size(); i++) {
+          if (levelBoundaryConditions[i]->tag() == tag)
+              selectedConditions.push_back(levelBoundaryConditions[i]);
+      }
+  }
+
+  const DeformedGridType* deformedGrid() const {
+      return deformedGrid_.get();
+  }
+
+  DeformationFunction& deformation() {
+      return *deformation_;
+  }
+
+  void setDeformation(const VectorType& def) {
+      deformation_->setDeformation(def);
+  }
+
+  const DeformedLeafGridView& leafView() const {
+      return leafView_;
+  }
+
+  int leafVertexCount() const {
+      return leafVertexCount_;
+  }
+
+  const Matrices<Matrix,1>& matrices() const {
+      return matrices_;
+  }
+
+  void externalForce(double relativeTime, VectorType& ell) const;
+};
+#endif
diff --git a/src/body_tmpl.cc b/src/body_tmpl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..25f890ffcff142320e4854264e6c2e9b564a832b
--- /dev/null
+++ b/src/body_tmpl.cc
@@ -0,0 +1,8 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "explicitgrid.hh"
+#include "explicitvectors.hh"
+
+template class Body<Grid, Vector, MY_DIM>;
diff --git a/src/boundarycondition.hh b/src/boundarycondition.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2ab5f2b758d1e86086f2275c7d21f9a3964e1ca9
--- /dev/null
+++ b/src/boundarycondition.hh
@@ -0,0 +1,116 @@
+#ifndef SRC_BOUNDARYCONDITION_HH
+#define SRC_BOUNDARYCONDITION_HH
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/function.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+
+
+template <class GridView, int dims>
+class BoundaryCondition {
+public:
+  using BoundaryPatch = BoundaryPatch<GridView>;
+
+private:
+  using Function = Dune::VirtualFunction<double, double>;
+
+  const std::string tag_; // store type of boundary condition, e.g. dirichlet, neumann, friction, etc
+
+  std::shared_ptr<BoundaryPatch> boundaryPatch_;
+  std::shared_ptr<Dune::BitSetVector<dims>> boundaryNodes_;
+  std::shared_ptr<Function> boundaryFunction_;
+
+public:
+  BoundaryCondition(const std::string& tag = "") :
+      tag_(tag)
+  {}
+
+  BoundaryCondition(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function, const std::string& tag = "") :
+      tag_(tag),
+      boundaryPatch_(patch),
+      boundaryFunction_(function)
+  {}
+
+  void setBoundaryPatch(const GridView& gridView, std::shared_ptr<Dune::BitSetVector<dims>> nodes) {
+      boundaryNodes_ = nodes;
+      boundaryPatch_ = std::make_shared<BoundaryPatch>(gridView, *nodes);
+  }
+
+  void setBoundaryPatch(std::shared_ptr<BoundaryPatch> patch) {
+      boundaryPatch_ = patch;
+  }
+
+  void setBoundaryPatch(const BoundaryPatch& patch) {
+      boundaryPatch_ = std::make_shared<BoundaryPatch>(patch);
+  }
+
+  void setBoundaryFunction(std::shared_ptr<Function> function) {
+      boundaryFunction_ = function;
+  }
+
+  void set(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function) {
+      boundaryPatch_ = patch;
+      boundaryFunction_ = function;
+  }
+
+  const std::string& tag() const {
+      return tag_;
+  }
+
+  const std::shared_ptr<BoundaryPatch>& boundaryPatch() const {
+      return boundaryPatch_;
+  }
+
+  const std::shared_ptr<Dune::BitSetVector<dims>>& boundaryNodes() const {
+      return boundaryNodes_;
+  }
+
+  const std::shared_ptr<Function>& boundaryFunction() const {
+      return boundaryFunction_;
+  }
+};
+
+
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+#include <dune/tectonic/globalfrictiondata.hh>
+
+template <class GridView, class LocalVectorType, int dims>
+class FrictionBoundaryCondition : public BoundaryCondition<GridView, dims>{
+private:
+    using Base = BoundaryCondition<GridView, dims>;
+    using LocalVector = LocalVectorType;
+
+    // friction data
+    std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch_;
+    std::shared_ptr<GlobalFrictionData<dims>> frictionData_;
+
+public:
+  FrictionBoundaryCondition() :
+      Base("friction")
+  {}
+
+  FrictionBoundaryCondition(std::shared_ptr<typename Base::BoundaryPatch> patch, std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch, std::shared_ptr<GlobalFrictionData<dims>> frictionData) :
+      Base(patch, nullptr, "friction"),
+      weakeningPatch_(weakeningPatch),
+      frictionData_(frictionData)
+  {}
+
+  void setWeakeningPatch(std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch) {
+      weakeningPatch_ = weakeningPatch;
+  }
+
+  void setFrictionData(std::shared_ptr<GlobalFrictionData<dims>> frictionData) {
+      frictionData_ = frictionData;
+  }
+
+  const std::shared_ptr<ConvexPolyhedron<LocalVector>>& weakeningPatch() const {
+      return weakeningPatch_;
+  }
+
+  const std::shared_ptr<GlobalFrictionData<dims>>& frictionData() const {
+      return frictionData_;
+  }
+};
+#endif
diff --git a/src/explicitgrid.hh b/src/explicitgrid.hh
index 685d2a49b434598a9be69ea4841e8251b55ab571..beefb64cc800f06604d3ac12484eb0be305e7902 100644
--- a/src/explicitgrid.hh
+++ b/src/explicitgrid.hh
@@ -11,4 +11,5 @@ using LevelGridView = Grid::LevelGridView;
 
 using DeformedGridComplex = typename Dune::Contact::DeformedContinuaComplex<Grid, Vector>;
 using DeformedGrid = DeformedGridComplex::DeformedGridType;
+
 #endif
diff --git a/src/levelcontactnetwork.cc b/src/levelcontactnetwork.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8f58d4d250fa29abe3c268667db81a1fd9da675a
--- /dev/null
+++ b/src/levelcontactnetwork.cc
@@ -0,0 +1,125 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/contact/projections/normalprojection.hh>
+
+#include "levelcontactnetwork.hh"
+
+template <class GridType, int dimension>
+LevelContactNetwork<GridType, dimension>::LevelContactNetwork(int nBodies, int nCouplings, int level) :
+    level_(level),
+    bodies_(nBodies),
+    couplings_(nCouplings),
+    deformedGrids_(nBodies),
+    leafViews_(nBodies),
+    levelViews_(nBodies),
+    leafVertexCounts_(nBodies),
+    nBodyAssembler_(nBodies, nCouplings)
+{}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::assemble() {
+    for (size_t i=0; i<nBodies(); i++) {
+        bodies_[i]->assemble();
+    }
+
+    totalNodes("dirichlet", totalDirichletNodes_);
+
+    // set up dune-contact nBodyAssembler
+    nBodyAssembler_.setGrids(deformedGrids_);
+
+    for (size_t i=0; i<nCouplings(); i++) {
+      nBodyAssembler_.setCoupling(*couplings_[i], i);
+    }
+
+    nBodyAssembler_.assembleTransferOperator();
+    nBodyAssembler_.assembleObstacle();
+}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::assembleFriction(const Config::FrictionModel& frictionModel, const std::vector<ScalarVector>& weightedNormalStress) {
+    for (size_t i=0; i<nBodies(); i++) {
+        bodies_[i]->assembleFriction(frictionModel, weightedNormalStress[i]);
+    }
+}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::setBodies(const std::vector<std::shared_ptr<Body>>& bodies) {
+    assert(nBodies()==bodies.size());
+    bodies_ = bodies;
+
+    matrices_.elasticity.resize(nBodies());
+    matrices_.damping.resize(nBodies());
+    matrices_.mass.resize(nBodies());
+
+    for (size_t i=0; i<nBodies(); i++) {
+        deformedGrids_[i] = bodies_[i]->deformedGrid();
+
+        leafViews_[i] = std::make_unique<DeformedLeafGridView>(deformedGrids_[i]->leafGridView());
+        levelViews_[i] = std::make_unique<DeformedLevelGridView>(deformedGrids_[i]->levelGridView(0));
+
+        leafVertexCounts_[i] = leafViews_[i]->size(dimension);
+
+        matrices_.elasticity[i] = bodies_[i]->matrices().elasticity;
+        matrices_.damping[i] = bodies_[i]->matrices().damping;
+        matrices_.mass[i] = bodies_[i]->matrices().mass;
+    }
+}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::setCouplings(const std::vector<std::shared_ptr<CouplingPair>>& couplings) {
+    assert(this->nCouplings()==couplings.size());
+    couplings_ = couplings;
+}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::constructBody(const std::shared_ptr<BodyData<dimension>>& bodyData,
+                                   const std::shared_ptr<GridType>& grid,
+                                   std::shared_ptr<Body>& body) const
+{
+    body = std::make_shared<Body>(bodyData, grid);
+}
+
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::constructCoupling(int nonMortarBodyIdx, int mortarBodyIdx,
+                                 const std::shared_ptr<DeformedLevelBoundaryPatch>& nonmortarPatch,
+                                 const std::shared_ptr<DeformedLevelBoundaryPatch>& mortarPatch,
+                                 std::shared_ptr<CouplingPair>& coupling) const
+{
+    coupling = std::make_shared<CouplingPair>();
+
+    auto contactProjection = std::make_shared<Dune::Contact::NormalProjection<DeformedLeafBoundaryPatch>>();
+    std::shared_ptr<typename CouplingPair::BackEndType> backend = nullptr;
+    coupling->set(nonMortarBodyIdx, mortarBodyIdx, nonmortarPatch, mortarPatch, 0.1, Dune::Contact::CouplingPairBase::STICK_SLIP, contactProjection, backend);
+}
+
+// collects all leaf boundary nodes from the different bodies identified by "tag" in a single BitSetVector
+template <class GridType, int dimension>
+void LevelContactNetwork<GridType, dimension>::totalNodes(const std::string& tag, Dune::BitSetVector<dimension>& totalNodes) {
+
+    int totalSize = 0;
+    for (size_t i=0; i<nBodies(); i++) {
+        totalSize += this->body(i)->leafVertexCount();
+    }
+
+    totalNodes.resize(totalSize);
+
+    int idx=0;
+    for (size_t i=0; i<nBodies(); i++) {
+        const auto& body = this->body(i);
+        std::vector<std::shared_ptr<typename Body::LeafBoundaryCondition>> boundaryConditions;
+        body->leafBoundaryConditions(tag, boundaryConditions);
+
+        const int idxBackup = idx;
+        for (size_t bc = 0; bc<boundaryConditions.size(); bc++) {
+            const auto& nodes = boundaryConditions[bc]->boundaryNodes();
+            for (size_t j=0; j<nodes->size(); j++, idx++)
+                for (int k=0; k<dimension; k++)
+                    totalNodes[idx][k] = (*nodes)[j][k];
+            idx = (bc==boundaryConditions.size()-1 ? idx : idxBackup);
+        }
+    }
+}
+
+#include "levelcontactnetwork_tmpl.cc"
diff --git a/src/levelcontactnetwork.hh b/src/levelcontactnetwork.hh
new file mode 100644
index 0000000000000000000000000000000000000000..be726c996f7cf0ca0300c4058534dbbf885047a2
--- /dev/null
+++ b/src/levelcontactnetwork.hh
@@ -0,0 +1,145 @@
+#ifndef SRC_LEVELCONTACTNETWORK_HH
+#define SRC_LEVELCONTACTNETWORK_HH
+
+#include <dune/common/fvector.hh>
+#include <dune/common/bitsetvector.hh>
+
+#include <dune/istl/bvector.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/bodydata.hh>
+#include <dune/tectonic/globalfriction.hh>
+
+#include "assemblers.hh"
+#include "body.hh"
+#include "enums.hh"
+#include "matrices.hh"
+//#include "multi-body-problem-data/myglobalfrictiondata.hh"
+
+template <class GridType, int dimension> class LevelContactNetwork {
+  private:
+    using Vector = Dune::BlockVector<Dune::FieldVector<double, dimension>>;
+
+  public:
+    using Body = Body<GridType, Vector, dimension>;
+
+    using DeformedGridType = typename Body::DeformedGridType;
+    using DeformedLeafGridView = typename Body::DeformedLeafGridView;
+    using DeformedLevelGridView = typename Body::DeformedLevelGridView;
+
+    using DeformedLeafBoundaryPatch = BoundaryPatch<DeformedLeafGridView>;
+    using DeformedLevelBoundaryPatch = BoundaryPatch<DeformedLevelGridView>;
+
+
+
+
+   using Assembler = typename Body::Assembler;
+   using Matrix = typename Assembler::Matrix;
+   using LocalVector = typename Vector::block_type;
+   using ScalarMatrix = typename Assembler::ScalarMatrix;
+   using ScalarVector = typename Assembler::ScalarVector;
+   using field_type = typename Matrix::field_type;
+    
+   using CouplingPair = Dune::Contact::CouplingPair<DeformedGridType, DeformedGridType, field_type>;
+    
+   using Function = Dune::VirtualFunction<double, double>;
+
+  private:
+	const int level_;
+	
+    std::vector<std::shared_ptr<Body>> bodies_;
+	std::vector<std::shared_ptr<CouplingPair>> couplings_;
+	
+    std::vector<const DeformedGridType*> deformedGrids_;
+    std::vector<std::unique_ptr<const DeformedLeafGridView>> leafViews_;
+    std::vector<std::unique_ptr<const DeformedLevelGridView>> levelViews_;
+    std::vector<size_t> leafVertexCounts_;
+
+    Dune::Contact::NBodyAssembler<DeformedGridType, Vector> nBodyAssembler_;
+    Dune::BitSetVector<dimension> totalDirichletNodes_;
+
+    Matrices<Matrix,2> matrices_;
+   // std::vector<std::shared_ptr<MyGlobalFrictionData<LocalVector>>> frictionData_;
+   std::vector<std::shared_ptr<GlobalFriction<Matrix, Vector>>> globalFriction_;
+
+	public:
+        LevelContactNetwork(int nBodies, int nCouplings, int level = 0);
+
+        void assemble();
+        void assembleFriction(const Config::FrictionModel& frictionModel, const std::vector<ScalarVector>& weightedNormalStress);
+
+		int level() const {
+			return level_;
+		} 
+		
+        void setBodies(const std::vector<std::shared_ptr<Body>>& bodies);
+        void setCouplings(const std::vector<std::shared_ptr<CouplingPair>>& couplings);
+
+        void constructBody(const std::shared_ptr<BodyData<dimension>>& bodyData,
+                           const std::shared_ptr<GridType>& grid,
+                           std::shared_ptr<Body>& body) const;
+        void constructCoupling(int nonMortarBodyIdx, int mortarBodyIdx,
+                               const std::shared_ptr<DeformedLevelBoundaryPatch>& nonmortarPatch,
+                               const std::shared_ptr<DeformedLevelBoundaryPatch>& mortarPatch,
+                               std::shared_ptr<CouplingPair>& coupling) const;
+
+        void totalNodes(const std::string& tag, Dune::BitSetVector<dimension>& totalNodes);
+
+        size_t nBodies() const {
+            return bodies_.size();
+        }
+
+        size_t nCouplings() const {
+            return couplings_.size();
+        }
+
+        const std::vector<size_t>& leafVertexCounts() const {
+            return leafVertexCounts_;
+        }
+
+        const std::shared_ptr<Body>& body(int i) const {
+            return bodies_[i];
+        }
+
+        const std::vector<const DeformedGridType*>& deformedGrids() const {
+            return deformedGrids_;
+        }
+
+        const DeformedLeafGridView& leafView(int i) const {
+            return *leafViews_[i];
+        }
+
+        const DeformedLevelGridView& levelView(int i) const {
+            return *levelViews_[i];
+        }
+
+        int leafVertexCount(int i) const {
+            return leafVertexCounts_[i];
+        }
+
+        Dune::Contact::NBodyAssembler<DeformedGridType, Vector>& nBodyAssembler() {
+            return nBodyAssembler_;
+        }
+
+        const Dune::Contact::NBodyAssembler<DeformedGridType, Vector>& nBodyAssembler() const {
+            return nBodyAssembler_;
+        }
+
+        const Matrices<Matrix,2>& matrices() const {
+            return matrices_;
+        }
+
+        const std::vector<std::shared_ptr<GlobalFriction<Matrix, Vector>>>& globalFriction() const {
+            return globalFriction_;
+        }
+
+        const Dune::BitSetVector<dimension>& totalDirichletNodes() const {
+            return totalDirichletNodes_;
+        }
+};
+#endif
diff --git a/src/levelcontactnetwork_tmpl.cc b/src/levelcontactnetwork_tmpl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ee900a40719fa08df64fe9fb92838da6fc11fe05
--- /dev/null
+++ b/src/levelcontactnetwork_tmpl.cc
@@ -0,0 +1,7 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "explicitgrid.hh"
+
+template class LevelContactNetwork<Grid, MY_DIM>;
diff --git a/src/levelcontactnetworkfactory.hh b/src/levelcontactnetworkfactory.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f9fb4e94ebd6c8396e12fe8ab49604150a1c5d86
--- /dev/null
+++ b/src/levelcontactnetworkfactory.hh
@@ -0,0 +1,54 @@
+#ifndef SRC_LEVELCONTACTNETWORKFACTORY_HH
+#define SRC_LEVELCONTACTNETWORKFACTORY_HH
+
+#include <dune/common/parametertree.hh>
+
+#include "levelcontactnetwork.hh"
+
+template <class GridType, int dims>
+class LevelContactNetworkFactory {
+public:
+    using LevelContactNetwork = LevelContactNetwork<GridType, dims>;
+
+protected:
+    using Body = typename LevelContactNetwork::Body;
+    using CouplingPair = typename LevelContactNetwork::CouplingPair;
+
+    const Dune::ParameterTree& parset_;
+    const size_t bodyCount_;
+    const size_t couplingCount_;
+
+    std::vector<std::shared_ptr<Body>> bodies_;
+    std::vector<std::shared_ptr<CouplingPair>> couplings_;
+
+    LevelContactNetwork levelContactNetwork_;
+
+private:
+    virtual void setBodies() = 0;
+    virtual void setCouplings() = 0;
+    virtual void setBoundaryConditions() = 0;
+
+public:
+    LevelContactNetworkFactory(const Dune::ParameterTree& parset, size_t bodyCount, size_t couplingCount) :
+        parset_(parset),
+        bodyCount_(bodyCount),
+        couplingCount_(couplingCount),
+        bodies_(bodyCount_),
+        couplings_(couplingCount_),
+        levelContactNetwork_(bodyCount_, couplingCount_) {}
+
+    void build() {
+        setBodies();
+        levelContactNetwork_.setBodies(bodies_);
+
+        setCouplings();
+        levelContactNetwork_.setCouplings(couplings_);
+
+        setBoundaryConditions();
+    }
+
+    LevelContactNetwork& levelContactNetwork() {
+        return levelContactNetwork_;
+    }
+};
+#endif
diff --git a/src/matrices.hh b/src/matrices.hh
index 01fc682dcbaff5b21b88a03f8bb958faa3c52be2..fa8b65d2681f15f549c13b336c27803bde90196f 100644
--- a/src/matrices.hh
+++ b/src/matrices.hh
@@ -1,13 +1,14 @@
 #ifndef SRC_MATRICES_HH
 #define SRC_MATRICES_HH
 
-template <class Matrix> class Matrices {
+template <class Matrix, size_t n>
+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) {
+  Matrices() {
     elasticity.resize(n);
     damping.resize(n);
     mass.resize(n);
@@ -19,4 +20,18 @@ template <class Matrix> class Matrices {
       }
   }
 };
+
+template <class Matrix> 
+class Matrices<Matrix, 1> {
+public:
+  std::shared_ptr<Matrix> elasticity;
+  std::shared_ptr<Matrix> damping;
+  std::shared_ptr<Matrix> mass;
+
+  Matrices() :
+    elasticity(std::make_shared<Matrix>()),
+    damping(std::make_shared<Matrix>()),
+    mass(std::make_shared<Matrix>())
+  {}
+};
 #endif
diff --git a/src/multi-body-problem-data/bc.hh b/src/multi-body-problem-data/bc.hh
index 7c29cf087571f8aca3e98d987ad3e711e991122d..ebc6f744a34a261bd4b829a47679b294e3b36cb8 100644
--- a/src/multi-body-problem-data/bc.hh
+++ b/src/multi-body-problem-data/bc.hh
@@ -1,6 +1,8 @@
 #ifndef SRC_ONE_BODY_PROBLEM_DATA_BC_HH
 #define SRC_ONE_BODY_PROBLEM_DATA_BC_HH
 
+#include <dune/common/function.hh>
+
 class VelocityDirichletCondition
     : public Dune::VirtualFunction<double, double> {
   void evaluate(double const &relativeTime, double &y) const {
diff --git a/src/multi-body-problem-data/cuboidgeometry.cc b/src/multi-body-problem-data/cuboidgeometry.cc
index aaf4dfe0683a53ca567f50affd2840afcb1cdd6d..77cd62f9e7636f5c7bed8e2b150973cbf92c9f50 100644
--- a/src/multi-body-problem-data/cuboidgeometry.cc
+++ b/src/multi-body-problem-data/cuboidgeometry.cc
@@ -35,26 +35,36 @@ void CuboidGeometry::setupWeak(const LocalVector& weakOrigin, const double weakL
 */
 
 #if MY_DIM == 3
-CuboidGeometry::CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength, const double length, const double width, const double depth_):
+CuboidGeometry::CuboidGeometry(const LocalVector& origin,
+                               const LocalVector& lowerWeakOrigin, const LocalVector& upperWeakOrigin,
+                               const double lowerWeakLength, const double upperWeakLength,
+                               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}),
+    V(lowerWeakOrigin),
+    W({V[0]+lowerWeakLength*lengthScale, V[1], 0}),
+    X(upperWeakOrigin),
+    Y({X[0]+upperWeakLength*lengthScale, X[1], 0}),
     depth(depth_*lengthScale) {}
 #else
-CuboidGeometry::CuboidGeometry(const LocalVector& origin, const LocalVector& weakOrigin, const double weakLength, const double length, const double width):
+CuboidGeometry::CuboidGeometry(const LocalVector& origin,
+                               const LocalVector& lowerWeakOrigin, const LocalVector& upperWeakOrigin,
+                               const double lowerWeakLength, const double upperWeakLength,
+                               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]}) {}
+    V(lowerWeakOrigin),
+    W({V[0]+lowerWeakLength*lengthScale, V[1]}),
+    X(upperWeakOrigin),
+    Y({X[0]+upperWeakLength*lengthScale, X[1]}) {}
 #endif
 
 
@@ -64,10 +74,13 @@ void CuboidGeometry::write() const {
   writer << "B = " << B << std::endl;
   writer << "C = " << C << std::endl;
   writer << "D = " << D << std::endl;
+  writer << "V = " << V << std::endl;
+  writer << "W = " << W << 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";
@@ -188,3 +201,4 @@ void CuboidGeometry::render() const {
   surface->write_to_png(filename);
 #endif
 }
+*/
diff --git a/src/multi-body-problem-data/cuboidgeometry.hh b/src/multi-body-problem-data/cuboidgeometry.hh
index 242454483d07285ad0d2a32332884dbee9572b34..d189502eda9e007c7ebab67697c2e8a3eaba4eb7 100644
--- a/src/multi-body-problem-data/cuboidgeometry.hh
+++ b/src/multi-body-problem-data/cuboidgeometry.hh
@@ -14,64 +14,40 @@ class CuboidGeometry {
   private:
     const double length_;
     const double width_;
-    //const double weakLength_;     // length of the weak zone
+   // const double lowerWeakLength_;     // length of the lower weak zone
+   // const double upperWeakLength_;     // length of the lower weak zone
 
   public:
+    // corners of cube
     const LocalVector A;
     const LocalVector B;
     const LocalVector C;
     const LocalVector D;
+
+    // corners of lower weakening patch
+    const LocalVector V;
+    const LocalVector W;
+
+    // corners of upper weakening patch
     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);
+    CuboidGeometry(const LocalVector& origin,
+                   const LocalVector& lowerWeakOrigin, const LocalVector& upperWeakOrigin,
+                   const double lowerWeakLength = 0.20, const double upperWeakLength = 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);
+        CuboidGeometry(const LocalVector& origin,
+                       const LocalVector& lowerWeakOrigin, const LocalVector& upperWeakOrigin,
+                       const double lowerWeakLength = 0.20, const double upperWeakLength = 0.20,
+                       const double length = 1.00, const double width = 0.27);
 #endif
 
     void write() const;
 
-    void render() 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/mybody.hh b/src/multi-body-problem-data/mybody.hh
index 08b5d4b9bd9d177fdb82965c7ac2de7ac7b05d3e..a79e359ee7e6670fed506bd4a64c2885cad7e5e8 100644
--- a/src/multi-body-problem-data/mybody.hh
+++ b/src/multi-body-problem-data/mybody.hh
@@ -1,25 +1,26 @@
-#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYBODY_HH
-#define SRC_MULTI_BODY_PROBLEM_DATA_MYBODY_HH
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYBODYDATA_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_MYBODYDATA_HH
 
 #include <dune/common/fvector.hh>
 
 #include <dune/fufem/functions/constantfunction.hh>
 
-#include <dune/tectonic/body.hh>
+#include <dune/tectonic/bodydata.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;
+template <int dimension> class MyBodyData : public BodyData<dimension> {
+  using typename BodyData<dimension>::ScalarFunction;
+  using typename BodyData<dimension>::VectorField;
 
 public:
-  MyBody(Dune::ParameterTree const &parset)
+  MyBodyData(Dune::ParameterTree const &parset, const Dune::FieldVector<double, dimension>& zenith)
       : poissonRatio_(parset.get<double>("body.poissonRatio")),
         youngModulus_(3.0 * parset.get<double>("body.bulkModulus") *
                       (1.0 - 2.0 * poissonRatio_)),
+        zenith_(zenith),
         shearViscosityField_(
             parset.get<double>("body.elastic.shearViscosity"),
             parset.get<double>("body.viscoelastic.shearViscosity")),
@@ -28,7 +29,7 @@ template <int dimension> class MyBody : public Body<dimension> {
             parset.get<double>("body.viscoelastic.bulkViscosity")),
         densityField_(parset.get<double>("body.elastic.density"),
                       parset.get<double>("body.viscoelastic.density")),
-        gravityField_(densityField_, MyGeometry::zenith,
+        gravityField_(densityField_, zenith_,
                       parset.get<double>("gravity")) {}
 
   double getPoissonRatio() const override { return poissonRatio_; }
@@ -47,6 +48,8 @@ template <int dimension> class MyBody : public Body<dimension> {
 private:
   double const poissonRatio_;
   double const youngModulus_;
+  Dune::FieldVector<double, dimension> zenith_;
+
   SegmentedFunction const shearViscosityField_;
   SegmentedFunction const bulkViscosityField_;
   SegmentedFunction const densityField_;
diff --git a/src/multi-body-problem-data/myglobalfrictiondata.hh b/src/multi-body-problem-data/myglobalfrictiondata.hh
index d92e7223b20d6e257b5dc3168169b93cdbfa8eb2..7666a467ccfff0472f6bd71d5fe76f27d7441800 100644
--- a/src/multi-body-problem-data/myglobalfrictiondata.hh
+++ b/src/multi-body-problem-data/myglobalfrictiondata.hh
@@ -1,5 +1,5 @@
-#ifndef SRC_ONE_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
-#define SRC_ONE_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_MYGLOBALFRICTIONDATA_HH
 
 #include <dune/common/function.hh>
 
@@ -14,14 +14,15 @@ class MyGlobalFrictionData : public GlobalFrictionData<LocalVector::dimension> {
 
 public:
   MyGlobalFrictionData(Dune::ParameterTree const &parset,
-                       ConvexPolyhedron<LocalVector> const &segment)
+                       ConvexPolyhedron<LocalVector> const &segment,
+                       double lengthScale = 1.0)
       : 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),
+           parset.get<double>("weakening.a"), segment, lengthScale),
         b_(parset.get<double>("strengthening.b"),
-           parset.get<double>("weakening.b"), segment),
+           parset.get<double>("weakening.b"), segment, lengthScale),
         mu0_(parset.get<double>("mu0")) {}
 
   double const &C() const override { return C_; }
diff --git a/src/multi-body-problem-data/patchfunction.hh b/src/multi-body-problem-data/patchfunction.hh
index 72cdc867cffd4eba8091a0a2da0fa366e196627d..1f7e9b08f007ad498025922417cd1cf93f47a93c 100644
--- a/src/multi-body-problem-data/patchfunction.hh
+++ b/src/multi-body-problem-data/patchfunction.hh
@@ -1,5 +1,5 @@
-#ifndef SRC_ONE_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
-#define SRC_ONE_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
+#ifndef SRC_MULTI_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
+#define SRC_MULTI_BODY_PROBLEM_DATA_PATCHFUNCTION_HH
 
 #include <dune/common/function.hh>
 #include <dune/common/fvector.hh>
@@ -17,13 +17,15 @@ class PatchFunction
   double const v2_;
   Polyhedron const &segment_;
 
+  double const lengthScale_;
+
 public:
-  PatchFunction(double v1, double v2, Polyhedron const &segment)
-      : v1_(v1), v2_(v2), segment_(segment) {}
+  PatchFunction(double v1, double v2, Polyhedron const &segment, double lengthScale = 1.0)
+      : v1_(v1), v2_(v2), segment_(segment), lengthScale_(lengthScale) {}
 
   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_
+    y = distance(x, segment_, 1e-6 * lengthScale_) <= 1e-5 ? v2_
                                                                       : v1_;
   }
 };
diff --git a/src/multi-body-problem-data/segmented-function.hh b/src/multi-body-problem-data/segmented-function.hh
index cab2dfef6e7bd5b764bd04048e9f43d9593b2ba9..a75a7199559a5a47c8b8527a9a9fef13a6e842f0 100644
--- a/src/multi-body-problem-data/segmented-function.hh
+++ b/src/multi-body-problem-data/segmented-function.hh
@@ -17,7 +17,7 @@ class SegmentedFunction
     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);
+    return false; //TODO liesBelow(MyGeometry::K, MyGeometry::M, z);
   };
 
   double const _v1;
diff --git a/src/multi-body-problem-data/weakpatch.hh b/src/multi-body-problem-data/weakpatch.hh
index e48f5725a93ac97f8e0fbfd1bfde0532e3f292d6..4e54105bc293a3142033c1e34b04f58ad58c9540 100644
--- a/src/multi-body-problem-data/weakpatch.hh
+++ b/src/multi-body-problem-data/weakpatch.hh
@@ -1,34 +1,67 @@
 #ifndef SRC_MULTI_BODY_PROBLEM_DATA_WEAKPATCH_HH
 #define SRC_MULTI_BODY_PROBLEM_DATA_WEAKPATCH_HH
 
+#include <dune/common/parametertree.hh>
+
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
 #include "cuboidgeometry.hh"
 
 template <class LocalVector>
-ConvexPolyhedron<LocalVector> getWeakPatch(Dune::ParameterTree const &parset, CuboidGeometry const &cuboidGeometry) {
-  ConvexPolyhedron<LocalVector> weakPatch;
+void getWeakPatch(Dune::ParameterTree const &parset, CuboidGeometry const &cuboidGeometry, ConvexPolyhedron<LocalVector>& lowerWeakPatch, ConvexPolyhedron<LocalVector>& upperWeakPatch) {
+
 #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;
+  if (cuboidGeometry.V != cuboidGeometry.W) {
+    lowerWeakPatch.vertices.resize(4);
+    lowerWeakPatch.vertices[0] = lowerWeakPatch.vertices[2] = cuboidGeometry.V;
+    lowerWeakPatch.vertices[1] = lowerWeakPatch.vertices[3] = cuboidGeometry.W;
+    for (size_t k = 0; k < 2; ++k) {
+        lowerWeakPatch.vertices[k][2] = -cuboidGeometry.depth / 2.0;
+        lowerWeakPatch.vertices[k + 2][2] = cuboidGeometry.depth / 2.0;
+    }
+    switch (parset.get<Config::PatchType>("patchType")) {
+        case Config::Rectangular:
+            break;
+        case Config::Trapezoidal:
+            lowerWeakPatch.vertices[1][0] += 0.05 * cuboidGeometry.lengthScale;
+            lowerWeakPatch.vertices[3][0] -= 0.05 * cuboidGeometry.lengthScale;
+            break;
+        default:
+            assert(false);
+    }
   }
-  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);
+
+  if (cuboidGeometry.X != cuboidGeometry.Y) {
+    upperWeakPatch.vertices.resize(4);
+    upperWeakPatch.vertices[0] = upperWeakPatch.vertices[2] = cuboidGeometry.X;
+    upperWeakPatch.vertices[1] = upperWeakPatch.vertices[3] = cuboidGeometry.Y;
+    for (size_t k = 0; k < 2; ++k) {
+        upperWeakPatch.vertices[k][2] = -cuboidGeometry.depth / 2.0;
+        upperWeakPatch.vertices[k + 2][2] = cuboidGeometry.depth / 2.0;
+    }
+    switch (parset.get<Config::PatchType>("patchType")) {
+        case Config::Rectangular:
+            break;
+        case Config::Trapezoidal:
+            upperWeakPatch.vertices[1][0] += 0.05 * cuboidGeometry.lengthScale;
+            upperWeakPatch.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;
+  if (cuboidGeometry.V != cuboidGeometry.W) {
+    lowerWeakPatch.vertices.resize(2);
+    lowerWeakPatch.vertices[0] = cuboidGeometry.V;
+    lowerWeakPatch.vertices[1] = cuboidGeometry.W;
+  }
+
+  if (cuboidGeometry.X != cuboidGeometry.Y) {
+    upperWeakPatch.vertices.resize(2);
+    upperWeakPatch.vertices[0] = cuboidGeometry.X;
+    upperWeakPatch.vertices[1] = cuboidGeometry.Y;
+  }
 #endif
-  return weakPatch;
 };
 #endif
diff --git a/src/multi-body-problem.cc b/src/multi-body-problem.cc
index a352da55283f59f131df1d88a7c04bf16e820273..ca941176059bcbe7a0280ebcb286639b829ea868 100644
--- a/src/multi-body-problem.cc
+++ b/src/multi-body-problem.cc
@@ -57,15 +57,16 @@
 #include "gridselector.hh"
 #include "hdf5-writer.hh"
 #include "hdf5/restart-io.hh"
+#include "levelcontactnetwork.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 "stackedblocksfactory.hh"
 //#include "time-stepping/adaptivetimestepper.hh"
 #include "time-stepping/rate.hh"
 #include "time-stepping/state.hh"
@@ -81,6 +82,20 @@
 
 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);
@@ -120,257 +135,35 @@ int main(int argc, char *argv[]) {
     using ScalarVector = Assembler::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 grids
-    // ------------
-    GridsConstructor<Grid> gridConstructor(cuboidGeometries);
-    auto& grids = gridConstructor.getGrids();
-
-    std::vector<ConvexPolyhedron<LocalVector>> weakPatches(grids.size());
-
-    for (size_t i=0; i<grids.size(); i++) {
-        const auto& cuboidGeometry = *cuboidGeometries[i];
-
-        // define weak patch and refine grid
-        auto& weakPatch = weakPatches[i];
-        weakPatch = getWeakPatch<LocalVector>(parset.sub("boundary.friction.weakening"), cuboidGeometry);
-        refine(*grids[i], weakPatch, parset.get<double>("boundary.friction.smallestDiameter"), cuboidGeometry.lengthScale);
-
-        // determine minDiameter and maxDiameter
-        double minDiameter = std::numeric_limits<double>::infinity();
-        double maxDiameter = 0.0;
-        for (auto &&e : elements(grids[i]->leafGridView())) {
-          auto const geometry = e.geometry();
-          auto const diam = diameter(geometry);
-          minDiameter = std::min(minDiameter, diam);
-          maxDiameter = std::max(maxDiameter, diam);
-        }
-        std::cout << "Grid" << i << " min diameter: " << minDiameter << std::endl;
-        std::cout << "Grid" << i << " max diameter: " << maxDiameter << std::endl;
-    }
-
-    // set up deformed grids
-    DeformedGridComplex deformedGridComplex;
-    for (size_t i=0; i<grids.size(); i++) {
-        deformedGridComplex.addGrid(*grids[i], "body" + std::to_string(i));
-    }
-
-    const auto deformedGrids = deformedGridComplex.gridVector();
-
-    // ------------------------
-    // set up contact couplings
-    // ------------------------
-    std::vector<std::shared_ptr<DefLeafGridView>> leafViews(deformedGrids.size());
-    std::vector<std::shared_ptr<DefLevelGridView>> levelViews(deformedGrids.size());
-    std::vector<size_t> leafVertexCounts(deformedGrids.size());
-
-    using LeafFaces = MyFaces<DefLeafGridView>;
-    using LevelFaces = MyFaces<DefLevelGridView>;
-    std::vector<std::shared_ptr<LeafFaces>> leafFaces(deformedGrids.size());
-    std::vector<std::shared_ptr<LevelFaces>> levelFaces(deformedGrids.size());
-
-    for (size_t i=0; i<deformedGrids.size(); i++) {
-        leafViews[i] = std::make_shared<DefLeafGridView>(deformedGrids[i]->leafGridView());
-        levelViews[i] = std::make_shared<DefLevelGridView>(deformedGrids[i]->levelGridView(0));
-
-        leafVertexCounts[i] = leafViews[i]->size(dims);
-
-        const auto& cuboidGeometry = *cuboidGeometries[i];
-        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<DefLeafGridView>;
-    using LevelBoundaryPatch = BoundaryPatch<DefLevelGridView>;
-
-    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++) {
-      couplings[i] = std::make_shared<CouplingPair>();
-
-      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
-    // ----------------------------------
-    Dune::Contact::NBodyAssembler<DeformedGrid, Vector> nBodyAssembler(bodyCount, bodyCount-1);
-    nBodyAssembler.setGrids(deformedGrids);
-
-    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<DefLeafGridView>> neumannBoundaries(bodyCount);
-    std::vector<BoundaryPatch<DefLeafGridView>> surfaces(bodyCount);
-
-    // friction boundary
-    std::vector<BoundaryPatch<DefLeafGridView>> frictionalBoundaries(bodyCount);
-    std::vector<Dune::BitSetVector<1>> frictionalNodes(bodyCount);
-
-    // Dirichlet boundary
-    std::vector<Dune::BitSetVector<dims>> noNodes(bodyCount);
-    std::vector<std::vector<Dune::BitSetVector<dims>>> dirichletNodes(bodyCount);
-
-    // set up functions for time-dependent boundary conditions
-    using Function = Dune::VirtualFunction<double, double>;
 
-    std::vector<const Function*> velocityDirichletFunctions(grids.size());
-    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
-      neumannBoundaries[i] = BoundaryPatch<DefLeafGridView>(*leafViews[i]);
-
-      // friction boundary
-      auto& frictionalBoundary = frictionalBoundaries[i];
-      if (i==0) {
-        frictionalBoundary = BoundaryPatch<DefLeafGridView>(leafFaces[i]->upper);
-      } else if (i==bodyCount-1) {
-        frictionalBoundary = BoundaryPatch<DefLeafGridView>(leafFaces[i]->lower);
-      } else {
-          frictionalBoundary = BoundaryPatch<DefLeafGridView>(leafFaces[i]->lower);
-          frictionalBoundary.addPatch(BoundaryPatch<DefLeafGridView>(leafFaces[i]->upper));
-      }
-      frictionalNodes[i] = *frictionalBoundary.getVertices();
-      //surfaces[i] = BoundaryPatch<GridView>(myFaces.upper);
+    // ----------------------
+    // set up contact network
+    // ----------------------
+    StackedBlocksFactory<Grid, dims> stackedBlocksFactory(parset);
+    using LevelContactNetwork = typename StackedBlocksFactory<Grid, dims>::LevelContactNetwork;
+    stackedBlocksFactory.build();
 
-      // TODO: Dirichlet Boundary
-      velocityDirichletFunctions[i] = new VelocityDirichletCondition();
+    LevelContactNetwork& levelContactNetwork = stackedBlocksFactory.levelContactNetwork();
+    const size_t bodyCount = levelContactNetwork.nBodies();
 
-      noNodes[i] = Dune::BitSetVector<dims>(leafVertexCount);
+    for (size_t i=0; i<levelContactNetwork.deformedGrids().size(); i++) {
+     /* Vector def(levelContactNetwork.deformedGrids()[i]->size(dims));
+      def = 1;
+      deformedGridComplex.setDeformation(def, i);*/
 
-      auto& gridDirichletNodes = dirichletNodes[i];
-      gridDirichletNodes = Dune::BitSetVector<dims>(leafVertexCount);
-
-      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
-      }
+      writeToVTK(levelContactNetwork.leafView(i), "", "deformedGrid" + std::to_string(i));
     }
 
-    // extend Dirichlet information to all grid levels
-    /*            BoundaryPatchProlongator<GridType>::prolong(dirichletBoundary[i]);
-                dirichletNodes[i].resize(toplevel+1);
-
-                for (int j=0; j<=toplevel; j++) {
-
-                    int fSSize = grids[i]->size(j,dim);
-                    dirichletNodes[i][j].resize(fSSize);
-                    for (int k=0; k<fSSize; k++)
-                        for (int l=0; l<dim; l++)
-                            dirichletNodes[i][j][k][l] = dirichletBoundary[i][j].containsVertex(k);
-
-                }
-
-                sampleOnBitField(*grids[i], dirichletValues[i], dirichletNodes[i]);*/
-
-    const int toplevel = deformedGrids[0]->maxLevel();
-    std::vector<Dune::BitSetVector<dims> > totalDirichletNodes(toplevel+1);
+    // ----------------------------
+    // assemble levelContactNetwork
+    // ----------------------------
 
-    for (int i=0; i<=toplevel; i++) {
-      int totalSize = 0;
-      for (int j=0; j<deformedGrids.size(); j++)
-        totalSize += deformedGrids[j]->size(i, dims);
-
-        totalDirichletNodes[i].resize(totalSize);
-
-        int idx=0;
-        for (int j=0; j<deformedGrids.size(); j++)
-          for (size_t k=0; k<dirichletNodes[j][i].size(); k++)
-            for (int l=0; l<dims; l++)
-              totalDirichletNodes[i][idx++][l] = dirichletNodes[j][i][k][l];
-     }
-
-    // ------------------------------------------------------------------------------------
-    // set up individual assemblers for each body, assemble problem (matrices, forces, rhs)
-    // ------------------------------------------------------------------------------------
-    std::vector<std::shared_ptr<Assembler>> assemblers(bodyCount);
-    Matrices<Matrix> matrices(bodyCount);
-
-    std::vector<Vector> gravityFunctionals(bodyCount);
-    std::vector<std::function<void(double, Vector&)>> externalForces(bodyCount);
-    std::vector<const EnergyNorm<ScalarMatrix, ScalarVector>* > stateEnergyNorms(bodyCount);
-
-    for (size_t i=0; i<assemblers.size(); i++) {
-      auto& assembler = assemblers[i];
-      assembler = std::make_shared<Assembler>(*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();
-      stateEnergyNorms[i] = new EnergyNorm<ScalarMatrix, ScalarVector>(relativeFrictionalBoundaryMass);
-
-      // assemble forces
-      assembler->assembleBodyForce(body.getGravityField(), gravityFunctionals[i]);
-
-      // Problem formulation: right-hand side
-      externalForces[i] =
-            [&](double _relativeTime, Vector &_ell) {
-              assemblers[i]->assembleNeumann(neumannBoundaries[i], _ell, neumannFunction,
-                                          _relativeTime);
-              _ell += gravityFunctionals[i];
-      };
-    }
+    levelContactNetwork.assemble();
 
     // -----------------
     // init input/output
     // -----------------
+    const auto& leafVertexCounts = levelContactNetwork.leafVertexCounts();
     using MyProgramState = ProgramState<Vector, ScalarVector>;
     MyProgramState programState(leafVertexCounts);
     auto const firstRestart = parset.get<size_t>("io.restarts.first");
@@ -399,36 +192,31 @@ int main(int argc, char *argv[]) {
     if (firstRestart > 0) // automatically adjusts the time and timestep
       restartIO->read(firstRestart, programState);
     else
-     programState.setupInitialConditions(parset, nBodyAssembler, externalForces,
-                                          matrices, assemblers, totalDirichletNodes,
-                                          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"), frictionalBoundaries[i], *frictionInfo[i], programState.weightedNormalStress[i]);
-        globalFriction[i]->updateAlpha(programState.alpha[i]);
-    }
+     programState.setupInitialConditions(parset, levelContactNetwork);
+
+    // ------------------------
+    // assemble global friction
+    // ------------------------
+    levelContactNetwork.assembleFriction(parset.get<Config::FrictionModel>("boundary.friction.frictionModel"), programState.weightedNormalStress);
 
+    /*
     using MyVertexBasis = typename Assembler::VertexBasis;
     using MyCellBasis = typename Assembler::CellBasis;
-    std::vector<Vector> vertexCoordinates(leafVertexCounts.size());
-    std::vector<const MyVertexBasis* > vertexBases(leafVertexCounts.size());
-    std::vector<const MyCellBasis* > cellBases(leafVertexCounts.size());
+    std::vector<Vector> vertexCoordinates(bodyCount);
+    std::vector<const MyVertexBasis* > vertexBases(bodyCount);
+    std::vector<const MyCellBasis* > cellBases(bodyCount);
 
-    for (size_t i=0; i<vertexCoordinates.size(); i++) {
-      vertexBases[i] = &(assemblers[i]->vertexBasis);
-      cellBases[i] = &(assemblers[i]->cellBasis);
+    for (size_t i=0; i<bodyCount; i++) {
+      const auto& body = levelContactNetwork.body(i);
+      vertexBases[i] = &(body->assembler()->vertexBasis);
+      cellBases[i] = &(body->assembler()->cellBasis);
 
       auto& vertexCoords = vertexCoordinates[i];
       vertexCoords.resize(leafVertexCounts[i]);
 
       Dune::MultipleCodimMultipleGeomTypeMapper<
-          DefLeafGridView, Dune::MCMGVertexLayout> const vertexMapper(*leafViews[i], Dune::mcmgVertexLayout());
-      for (auto &&v : vertices(*leafViews[i]))
+          DefLeafGridView, Dune::MCMGVertexLayout> const vertexMapper(body->leafView(), Dune::mcmgVertexLayout());
+      for (auto &&v : vertices(body->leafView()))
         vertexCoords[vertexMapper.index(v)] = geoToPoint(v.geometry());
     }
 
@@ -446,7 +234,7 @@ int main(int argc, char *argv[]) {
 
     auto const report = [&](bool initial = false) {
       if (writeData) {
-        dataWriter->reportSolution(programState, globalFriction);
+        dataWriter->reportSolution(programState, levelContactNetwork.globalFriction());
         if (!initial)
           dataWriter->reportIterations(programState, iterationCount);
         dataFile->flush();
@@ -465,11 +253,12 @@ int main(int argc, char *argv[]) {
                   << std::endl;
 
       if (parset.get<bool>("io.vtk.write")) {
-        std::vector<ScalarVector> stress(assemblers.size());
+        std::vector<ScalarVector> stress(bodyCount);
 
-        for (size_t i=0; i<stress.size(); i++) {
-          assemblers[i]->assembleVonMisesStress(body.getYoungModulus(),
-                                           body.getPoissonRatio(),
+        for (size_t i=0; i<bodyCount; i++) {
+          const auto& body = levelContactNetwork.body(i);
+          body->assembler()->assembleVonMisesStress(body->data()->getYoungModulus(),
+                                           body->data()->getPoissonRatio(),
                                            programState.u[i], stress[i]);
         }
 
@@ -479,15 +268,18 @@ int main(int argc, char *argv[]) {
     };
     report(true);
 
+
     // -------------------
     // Set up TNNMG solver
     // -------------------
+    auto& nBodyAssembler = levelContactNetwork.nBodyAssembler();
+    const auto& totalDirichletNodes = levelContactNetwork.totalDirichletNodes();
     using NonlinearFactory = SolverFactory<DeformedGrid, GlobalFriction<Matrix, Vector>, Matrix, Vector>;
     NonlinearFactory factory(parset.sub("solver.tnnmg"), nBodyAssembler, totalDirichletNodes);
 
-    using MyUpdater = Updaters<RateUpdater<Vector, Matrix, Function, dims>,
+    using MyUpdater = Updaters<RateUpdater<Vector, Matrix, typename LevelContactNetwork::Body::Function, dims>,
                                StateUpdater<ScalarVector, Vector>>;
-
+/*
     std::vector<double> L(bodyCount, parset.get<double>("boundary.friction.L"));
     std::vector<double> V0(bodyCount, parset.get<double>("boundary.friction.V0"));
     MyUpdater current(
@@ -538,8 +330,8 @@ int main(int argc, char *argv[]) {
       current.rate_->extractAcceleration(programState.a);
       current.state_->extractAlpha(programState.alpha);
 
-      for (size_t i=0; i<deformedGridComplex.size() ; i++) {
-        deformedGridComplex.setDeformation(programState.u[i], i);
+      for (size_t i=0; i<bodyCount; i++) {
+        levelContactNetwork.body(i)->setDeformation(programState.u[i]);
       }
       nBodyAssembler.assembleTransferOperator();
       nBodyAssembler.assembleObstacle();
@@ -551,7 +343,7 @@ 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/multi-body-problem.cfg b/src/multi-body-problem.cfg
index b850b5cc829734e50342d1e812254778f74ad97b..03c7084ae23e1d8d4a14c76f78504531efe42000 100644
--- a/src/multi-body-problem.cfg
+++ b/src/multi-body-problem.cfg
@@ -11,7 +11,7 @@ vtk.write       = false
 
 [problem]
 finalTime       = 1000  # [s]
-bodyCount       = 2
+bodyCount       = 3
 
 [body]
 bulkModulus     = 0.5e5 # [Pa]
diff --git a/src/one-body-problem.cc b/src/one-body-problem.cc
index dedc9162466ba3a15476db11dbbaa9dd7fc120e8..a4548bdf22a8ed436e4815c8762558b614410af0 100644
--- a/src/one-body-problem.cc
+++ b/src/one-body-problem.cc
@@ -46,6 +46,7 @@
 #include <dune/fufem/hdf5/file.hh>
 
 #include "assemblers.hh"
+#include "boundarycondition.hh"
 #include "diameter.hh"
 #include "enumparser.hh"
 #include "enums.hh"
diff --git a/src/program_state.hh b/src/program_state.hh
index dfacce35338ca3ab701274a3ff8d2c80619d35d3..ac5c10e322e4223f0423a92dc71e71156ea29f13 100644
--- a/src/program_state.hh
+++ b/src/program_state.hh
@@ -11,9 +11,10 @@
 
 #include <dune/contact/assemblers/nbodyassembler.hh>
 
-#include <dune/tectonic/body.hh>
+#include <dune/tectonic/bodydata.hh>
 
 #include "assemblers.hh"
+#include "levelcontactnetwork.hh"
 #include "matrices.hh"
 #include "spatial-solving/solverfactory.hh"
 
@@ -44,15 +45,20 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
   using ScalarVector = ScalarVectorTEMPLATE;
   using BodyState = BodyState<Vector, ScalarVector>;
 
+private:
+    using LocalVector = typename Vector::block_type;
+    //using LocalMatrix = typename Matrix::block_type;
+    const static int dims = LocalVector::dimension;
+
+public:
   ProgramState(const std::vector<size_t>& leafVertexCounts)
     : bodyCount_(leafVertexCounts.size()),
-      bodies(bodyCount_),
+      bodyStates(bodyCount_),
       u(bodyCount_),
       v(bodyCount_),
       a(bodyCount_),
       alpha(bodyCount_),
       weightedNormalStress(bodyCount_) {
-
     for (size_t i=0; i<bodyCount_; i++) {
       size_t leafVertexCount = leafVertexCounts[i];
 
@@ -62,13 +68,13 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
       alpha[i].resize(leafVertexCount),
       weightedNormalStress[i].resize(leafVertexCount),
 
-      bodies[i] = new BodyState(&u[i], &v[i], &a[i], &alpha[i], &weightedNormalStress[i]);
+      bodyStates[i] = new BodyState(&u[i], &v[i], &a[i], &alpha[i], &weightedNormalStress[i]);
     }
   }
 
   ~ProgramState() {
     for (size_t i=0; i<bodyCount_; i++) {
-      delete bodies[i];
+      delete bodyStates[i];
     }
   }
 
@@ -78,26 +84,14 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
 
   // Set up initial conditions
-  template <class Matrix, class GridView>
-  void setupInitialConditions(
-      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;
-
+  template <class GridType>
+  void setupInitialConditions(const Dune::ParameterTree& parset, const LevelContactNetwork<GridType, dims>& levelContactNetwork) {
+    using Matrix = typename LevelContactNetwork<GridType, dims>::Matrix;
+    const auto& nBodyAssembler = levelContactNetwork.nBodyAssembler();
 
     // Solving a linear problem with a multigrid solver
     auto const solveLinearProblem = [&](
-        const std::vector<Dune::BitSetVector<dims>>& _dirichletNodes, const std::vector<std::shared_ptr<Matrix>>& _matrices,
+        const Dune::BitSetVector<dims>& _dirichletNodes, const std::vector<std::shared_ptr<Matrix>>& _matrices,
         const std::vector<Vector>& _rhs, std::vector<Vector>& _x,
         Dune::ParameterTree const &_localParset) {
 
@@ -120,6 +114,7 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
       // assemble full global contact problem
       Matrix bilinearForm;
+
       nBodyAssembler.assembleJacobian(matrices_ptr, bilinearForm);
 
       Vector totalRhs;
@@ -128,7 +123,8 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
       Vector totalX;
       nBodyAssembler.nodalToTransformed(_x, totalX);
 
-      using LinearFactory = SolverFactory<typename GridView::Grid, GlobalFriction<Matrix, Vector>, Matrix, Vector>;
+      using DeformedGridType = typename LevelContactNetwork<GridType, dims>::DeformedGridType;
+      using LinearFactory = SolverFactory<DeformedGridType, GlobalFriction<Matrix, Vector>, Matrix, Vector>;
       LinearFactory factory(parset.sub("solver.tnnmg"), nBodyAssembler, _dirichletNodes);
 
       auto multigridStep = factory.getStep();
@@ -159,14 +155,14 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 
       ell0[i].resize(u[i].size());
       ell0[i] = 0.0;
-      // TODO
-      //externalForces[i](relativeTime, ell0[i]);
+
+      levelContactNetwork.body(i)->externalForce(relativeTime, ell0[i]);
     }
 
     // Initial displacement: Start from a situation of minimal stress,
     // which is automatically attained in the case [v = 0 = a].
     // Assuming dPhi(v = 0) = 0, we thus only have to solve Au = ell0
-    solveLinearProblem(dirichletNodes, matrices.elasticity, ell0, u,
+    solveLinearProblem(levelContactNetwork.totalDirichletNodes(), levelContactNetwork.matrices().elasticity, ell0, u,
                        parset.sub("u0.solver"));
 
     // Initial acceleration: Computed in agreement with Ma = ell0 - Au
@@ -177,14 +173,25 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
       alpha[i] = parset.get<double>("boundary.friction.initialAlpha");
 
       // Initial normal stress
-      assemblers[i]->assembleWeightedNormalStress(
-        frictionalBoundaries[i], weightedNormalStress[i], body.getYoungModulus(),
-        body.getPoissonRatio(), u[i]);
 
-      Dune::MatrixVector::subtractProduct(accelerationRHS[i], *matrices.elasticity[i], u[i]);
+      const auto& body = levelContactNetwork.body(i);
+      std::vector<std::shared_ptr<typename LevelContactNetwork<GridType, dims>::Body::LeafBoundaryCondition>> frictionBoundaryConditions;
+      body->leafBoundaryConditions("friction", frictionBoundaryConditions);
+      for (size_t j=0; j<frictionBoundaryConditions.size(); j++) {
+          ScalarVector frictionBoundaryStress(weightedNormalStress[i].size());
+
+          body->assembler()->assembleWeightedNormalStress(
+            *frictionBoundaryConditions[j]->boundaryPatch(), frictionBoundaryStress, body->data()->getYoungModulus(),
+            body->data()->getPoissonRatio(), u[i]);
+
+          weightedNormalStress[i] += frictionBoundaryStress;
+      }
+
+      Dune::MatrixVector::subtractProduct(accelerationRHS[i], *body->matrices().elasticity, u[i]);
     }
 
-    solveLinearProblem(noNodes, matrices.mass, accelerationRHS, a,
+    Dune::BitSetVector<dims> noNodes(levelContactNetwork.totalDirichletNodes().size());
+    solveLinearProblem(noNodes, levelContactNetwork.matrices().mass, accelerationRHS, a,
                        parset.sub("a0.solver"));
   }
 
@@ -192,7 +199,7 @@ template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
   const size_t bodyCount_;
 
 public:
-  std::vector<BodyState* > bodies;
+  std::vector<BodyState* > bodyStates;
   std::vector<Vector> u;
   std::vector<Vector> v;
   std::vector<Vector> a;
diff --git a/src/spatial-solving/solverfactory.cc b/src/spatial-solving/solverfactory.cc
index 726592df93cd52a2356843051ecee65f5fb33eb8..d5f996fdbb23b0a5073c407d9b2144fee91ba014 100644
--- a/src/spatial-solving/solverfactory.cc
+++ b/src/spatial-solving/solverfactory.cc
@@ -13,10 +13,11 @@
 
 #include "solverfactory.hh"
 
-template <class DeformedGrid, class Nonlinearity, class Matrix, class Vector>
-SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::SolverFactory(
-    const Dune::ParameterTree& parset, const Dune::Contact::NBodyAssembler<DeformedGrid, Vector>& nBodyAssembler,
-    const std::vector<std::vector<Dune::BitSetVector<DeformedGrid::dimension>>>& ignoreNodes)
+
+template <class DeformedGrid, class Nonlinearity, class MatrixType, class VectorType>
+SolverFactory<DeformedGrid, Nonlinearity, MatrixType, VectorType>::SolverFactory(
+    const Dune::ParameterTree& parset, const Dune::Contact::NBodyAssembler<DeformedGrid, VectorType>& nBodyAssembler,
+    const Dune::BitSetVector<DeformedGrid::dimension>& ignoreNodes)
     : nBodyAssembler_(nBodyAssembler),
       baseEnergyNorm_(baseSolverStep_),
       baseSolver_(&baseSolverStep_,
@@ -34,6 +35,7 @@ SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::SolverFactory(
   multigridStep_->setHasObstacle(nBodyAssembler_.totalHasObstacle_);
   multigridStep_->setObstacles(nBodyAssembler_.totalObstacles_);
 
+  /*
   // create the transfer operators
   const int nCouplings = nBodyAssembler_.nCouplings();
   const auto grids = nBodyAssembler_.getGrids();
@@ -41,10 +43,9 @@ SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::SolverFactory(
     std::vector<const Dune::BitSetVector<1>*> coarseHasObstacle(nCouplings);
     std::vector<const Dune::BitSetVector<1>*> fineHasObstacle(nCouplings);
 
-    std::vector<const Matrix*> mortarTransfer(nCouplings);
+    std::vector<const MatrixType*> mortarTransfer(nCouplings);
     std::vector<std::array<int,2> > gridIdx(nCouplings);
 
-    /*
     for (int j=0; j<nCouplings; j++) {
       coarseHasObstacle[j]  = nBodyAssembler_.contactCoupling_[j]->nonmortarBoundary_[i].getVertices();
       fineHasObstacle[j]    = nBodyAssembler_.nonmortarBoundary_[j][i+1].getVertices();
@@ -59,8 +60,8 @@ SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::SolverFactory(
                                     nBodyAssembler_.localCoordSystems_[i+1],
                                     coarseHasObstacle, fineHasObstacle,
                                     mortarTransfer,
-                                    gridIdx); */
-  }
+                                    gridIdx);
+  }*/
 /*
   grids[0], *grids[1], i,
                           contactAssembler.contactCoupling_[0]->mortarLagrangeMatrix(),
@@ -70,14 +71,14 @@ SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::SolverFactory(
   multigridStep_->setTransferOperators(transferOperators_);
 }
 
-template <class DeformedGrid, class Nonlinearity, class Matrix, class Vector>
-SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::~SolverFactory() {
+template <class DeformedGrid, class Nonlinearity, class MatrixType, class VectorType>
+SolverFactory<DeformedGrid, Nonlinearity, MatrixType, VectorType>::~SolverFactory() {
   for (auto &&x : transferOperators_)
     delete x;
 }
 
-template <class DeformedGrid, class Nonlinearity, class Matrix, class Vector>
-auto SolverFactory<DeformedGrid, Nonlinearity, Matrix, Vector>::getStep()
+template <class DeformedGrid, class Nonlinearity, class MatrixType, class VectorType>
+auto SolverFactory<DeformedGrid, Nonlinearity, MatrixType, VectorType>::getStep()
     -> std::shared_ptr<Step> {
   return multigridStep_;
 }
diff --git a/src/spatial-solving/solverfactory.hh b/src/spatial-solving/solverfactory.hh
index 932ad8db5256c66ecf2738df778f101e8a1df183..ad4ecbf9b1af5be9334a5af705e077ce31b5c855 100644
--- a/src/spatial-solving/solverfactory.hh
+++ b/src/spatial-solving/solverfactory.hh
@@ -29,37 +29,36 @@ class SolverFactory {
 //  const size_t dim = DeformedGrid::dimension;
 
 public:
-  using Vector = VectorType;
   using Matrix = MatrixType;
-
+  using Vector = VectorType;
   using DeformedGrid = DeformedGridTEMPLATE;
   using Nonlinearity = NonlinearityTEMPLATE;
 
 public:
-  using Step = Dune::Contact::NonSmoothNewtonMGStep<Matrix, Vector>;
+  using Step = Dune::Contact::NonSmoothNewtonMGStep<MatrixType, VectorType>;
 
-  SolverFactory(Dune::ParameterTree const &parset, const Dune::Contact::NBodyAssembler<DeformedGrid, Vector>& nBodyAssembler,
-                const std::vector<Dune::BitSetVector<DeformedGrid::dimension>>& ignoreNodes);
+  SolverFactory(Dune::ParameterTree const &parset, const Dune::Contact::NBodyAssembler<DeformedGrid, VectorType>& nBodyAssembler,
+                const Dune::BitSetVector<DeformedGrid::dimension>& ignoreNodes);
 
   ~SolverFactory();
 
   std::shared_ptr<Step> getStep();
 
-  const Dune::Contact::NBodyAssembler<DeformedGrid, Vector>& getNBodyAssembler() const {
+  const Dune::Contact::NBodyAssembler<DeformedGrid, VectorType>& getNBodyAssembler() const {
     return nBodyAssembler_;
   }
 
 private:
-  const Dune::Contact::NBodyAssembler<DeformedGrid, Vector>& nBodyAssembler_;
+  const Dune::Contact::NBodyAssembler<DeformedGrid, VectorType>& nBodyAssembler_;
 
-  ProjectedBlockGSStep<Matrix, Vector> baseSolverStep_;
-  EnergyNorm<Matrix, Vector> baseEnergyNorm_;
-  LoopSolver<Vector> baseSolver_;
+  ProjectedBlockGSStep<MatrixType, VectorType> baseSolverStep_;
+  EnergyNorm<MatrixType, VectorType> baseEnergyNorm_;
+  LoopSolver<VectorType> baseSolver_;
 
-  ProjectedBlockGSStep<Matrix, Vector> presmoother_;
-  ProjectedBlockGSStep<Matrix, Vector> postsmoother_;
+  ProjectedBlockGSStep<MatrixType, VectorType> presmoother_;
+  ProjectedBlockGSStep<MatrixType, VectorType> postsmoother_;
   std::shared_ptr<Step> multigridStep_;
 
-  std::vector<Dune::Contact::ContactMGTransfer<Vector>* > transferOperators_;
+  std::vector<Dune::Contact::ContactMGTransfer<VectorType>* > transferOperators_;
 };
 #endif
diff --git a/src/stackedblocksfactory.cc b/src/stackedblocksfactory.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4c31d3a98c229bea5f6cd4640d1178cd49332b80
--- /dev/null
+++ b/src/stackedblocksfactory.cc
@@ -0,0 +1,189 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+
+#include <dune/contact/projections/normalprojection.hh>
+
+#include "diameter.hh"
+
+#include "multi-body-problem-data/bc.hh"
+#include "multi-body-problem-data/cuboidgeometry.hh"
+#include "multi-body-problem-data/myglobalfrictiondata.hh"
+#include "multi-body-problem-data/weakpatch.hh"
+
+#include "stackedblocksfactory.hh"
+
+template <class GridType, int dims>
+void StackedBlocksFactory<GridType, dims>::setBodies() {
+    // set up cuboid geometries
+
+    double const length = 1.00;
+    double const width = 0.27;
+    double const weakLength = 0.20;
+
+    std::vector<LocalVector> origins(this->bodyCount_);
+    std::vector<LocalVector> lowerWeakOrigins(this->bodyCount_);
+    std::vector<LocalVector> upperWeakOrigins(this->bodyCount_);
+
+#if MY_DIM == 3
+        double const depth = 0.60;
+
+        origins[0] = {0,0,0};
+        lowerWeakOrigins[0] = {0.2,0,0};
+        upperWeakOrigins[0] = {0.2,width,0};
+
+        cuboidGeometries_[0] = std::make_shared<CuboidGeometry>(origins[0], lowerWeakOrigins[0], upperWeakOrigins[0], 0.0, weakLength, length, width, depth);
+        for (size_t i=1; i<this->bodyCount_-1; i++) {
+            origins[i] = cuboidGeometries_[i-1]->D;
+            lowerWeakOrigins[i] = {0.6,i*width,0};
+            upperWeakOrigins[i] = {0.6,(i+1)*width,0};
+
+            cuboidGeometries_[i] = std::make_shared<CuboidGeometry>(origins[i], lowerWeakOrigins[i], upperWeakOrigins[i], weakLength, weakLength, length, width, depth);
+        }
+        const size_t idx = this->bodyCount_-1;
+        origins[idx] = cuboidGeometries_[idx-1]->D;
+        lowerWeakOrigins[idx] = {0.6,idx*width,0};
+        upperWeakOrigins[idx] = {0.6,(idx+1)*width,0};
+
+        cuboidGeometries_[idx] = std::make_shared<CuboidGeometry>(origins[idx], lowerWeakOrigins[idx], upperWeakOrigins[idx], weakLength, 0.0, length, width, depth);
+#elif MY_DIM == 2
+        origins[0] = {0,0};
+        lowerWeakOrigins[0] = {0.2,0};
+        upperWeakOrigins[0] = {0.2,width};
+
+        cuboidGeometries_[0] = std::make_shared<CuboidGeometry>(origins[0], lowerWeakOrigins[0], upperWeakOrigins[0], 0.0, weakLength, length, width);
+        for (size_t i=1; i<this->bodyCount_-1; i++) {
+            origins[i] = cuboidGeometries_[i-1]->D;
+            lowerWeakOrigins[i] = {0.6,i*width};
+            upperWeakOrigins[i] = {0.6,(i+1)*width};
+
+            cuboidGeometries_[i] = std::make_shared<CuboidGeometry>(origins[i], lowerWeakOrigins[i], upperWeakOrigins[i], weakLength, weakLength, length, width);
+        }
+        const size_t idx = this->bodyCount_-1;
+        origins[idx] = cuboidGeometries_[idx-1]->D;
+        lowerWeakOrigins[idx] = {0.6,idx*width};
+        upperWeakOrigins[idx] = {0.6,(idx+1)*width};
+
+        cuboidGeometries_[idx] = std::make_shared<CuboidGeometry>(origins[idx], lowerWeakOrigins[idx], upperWeakOrigins[idx], weakLength, 0.0, length, width);
+#else
+#error CuboidGeometry only supports 2D and 3D!"
+#endif
+
+    // set up reference grids
+    gridConstructor_ = new GridsConstructor<GridType>(cuboidGeometries_);
+    auto& grids = gridConstructor_->getGrids();
+
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        const auto& cuboidGeometry = *cuboidGeometries_[i];
+
+        // define weak patch and refine grid
+        auto lowerWeakPatch = lowerWeakPatches_[i] = std::make_shared<ConvexPolyhedron<LocalVector>>();
+        auto upperWeakPatch = upperWeakPatches_[i] = std::make_shared<ConvexPolyhedron<LocalVector>>();
+        getWeakPatch<LocalVector>(this->parset_.sub("boundary.friction.weakening"), cuboidGeometry, *lowerWeakPatch, *upperWeakPatch);
+        refine(*grids[i], *lowerWeakPatch, this->parset_.template get<double>("boundary.friction.smallestDiameter"), cuboidGeometry.lengthScale);
+        refine(*grids[i], *upperWeakPatch, this->parset_.template get<double>("boundary.friction.smallestDiameter"), cuboidGeometry.lengthScale);
+
+        // determine minDiameter and maxDiameter
+        double minDiameter = std::numeric_limits<double>::infinity();
+        double maxDiameter = 0.0;
+        for (auto &&e : elements(grids[i]->leafGridView())) {
+          auto const geometry = e.geometry();
+          auto const diam = diameter(geometry);
+          minDiameter = std::min(minDiameter, diam);
+          maxDiameter = std::max(maxDiameter, diam);
+        }
+        std::cout << "Grid" << i << " min diameter: " << minDiameter << std::endl;
+        std::cout << "Grid" << i << " max diameter: " << maxDiameter << std::endl;
+    }
+
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        this->bodies_[i] = std::make_shared<typename Base::Body>(bodyData_, grids[i]);
+    }
+}
+
+template <class GridType, int dims>
+void StackedBlocksFactory<GridType, dims>::setCouplings() {
+    const auto deformedGrids = this->levelContactNetwork_.deformedGrids();
+
+    for (size_t i=0; i<this->bodyCount_; i++) {
+        const auto& cuboidGeometry = *cuboidGeometries_[i];
+        leafFaces_[i] = std::make_shared<LeafFaces>(this->levelContactNetwork_.leafView(i), cuboidGeometry);
+        levelFaces_[i] = std::make_shared<LevelFaces>(this->levelContactNetwork_.levelView(i), cuboidGeometry);
+    }
+
+    auto contactProjection = std::make_shared<Dune::Contact::NormalProjection<LeafBoundaryPatch>>();
+    std::shared_ptr<typename Base::CouplingPair::BackEndType> backend = nullptr;
+    for (size_t i=0; i<this->couplings_.size(); i++) {
+      auto& coupling = this->couplings_[i];
+      coupling = std::make_shared<typename Base::CouplingPair>();
+
+      nonmortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i]->upper);
+      mortarPatch_[i] = std::make_shared<LevelBoundaryPatch>(levelFaces_[i+1]->lower);
+
+      coupling->set(i, i+1, nonmortarPatch_[i], mortarPatch_[i], 0.1, Base::CouplingPair::CouplingType::STICK_SLIP, contactProjection, backend);
+    }
+}
+
+template <class GridType, int dims>
+void StackedBlocksFactory<GridType, dims>::setBoundaryConditions() {
+    using LeafBoundaryCondition = BoundaryCondition<DeformedLeafGridView, dims>;
+    using FrictionBoundaryCondition = FrictionBoundaryCondition<DeformedLeafGridView, LocalVector, dims>;
+
+    using Function = Dune::VirtualFunction<double, double>;
+    std::shared_ptr<Function> neumannFunction = std::make_shared<NeumannCondition>();
+    std::shared_ptr<Function> velocityDirichletFunction = std::make_shared<VelocityDirichletCondition>();
+
+    const double lengthScale = CuboidGeometry::lengthScale;
+
+    for (size_t i=0; i<this->bodyCount_; i++) {
+      const auto& body = this->levelContactNetwork_.body(i);
+      const auto& leafVertexCount = body->leafVertexCount();
+
+      std::cout << "Grid" << i << " Number of DOFs: " << leafVertexCount << std::endl;
+
+      // friction boundary
+      if (i<this->bodyCount_-1 && upperWeakPatches_[i]->vertices.size()>0) {
+        std::shared_ptr<FrictionBoundaryCondition> frictionBoundary = std::make_shared<FrictionBoundaryCondition>();
+        frictionBoundary->setBoundaryPatch(leafFaces_[i]->upper);
+        frictionBoundary->setWeakeningPatch(upperWeakPatches_[i]);
+        frictionBoundary->setFrictionData(std::make_shared<MyGlobalFrictionData<LocalVector>>(this->parset_.sub("boundary.friction"), *upperWeakPatches_[i], lengthScale));
+        body->addBoundaryCondition(frictionBoundary);
+      }
+      if (i>0 && lowerWeakPatches_[i]->vertices.size()>0) {
+        std::shared_ptr<FrictionBoundaryCondition> frictionBoundary = std::make_shared<FrictionBoundaryCondition>();
+        frictionBoundary->setBoundaryPatch(leafFaces_[i]->lower);
+        frictionBoundary->setWeakeningPatch(lowerWeakPatches_[i]);
+        frictionBoundary->setFrictionData(std::make_shared<MyGlobalFrictionData<LocalVector>>(this->parset_.sub("boundary.friction"), *lowerWeakPatches_[i], lengthScale));
+        body->addBoundaryCondition(frictionBoundary);
+      }
+
+      // Neumann boundary
+      std::shared_ptr<LeafBoundaryCondition> neumannBoundary = std::make_shared<LeafBoundaryCondition>(std::make_shared<LeafBoundaryPatch>(body->leafView()), neumannFunction, "neumann");
+      body->addBoundaryCondition(neumannBoundary);
+
+      // Dirichlet Boundary
+      // identify Dirichlet nodes on leaf level
+      std::shared_ptr<Dune::BitSetVector<dims>> dirichletNodes = std::make_shared<Dune::BitSetVector<dims>>(leafVertexCount);
+      for (int j=0; j<leafVertexCount; j++) {
+        if (leafFaces_[i]->right.containsVertex(j))
+          (*dirichletNodes)[j][0] = true;
+
+        if (leafFaces_[i]->lower.containsVertex(j))
+          (*dirichletNodes)[j][0] = true;
+
+        #if MY_DIM == 3
+        if (leafFaces_[i]->front.containsVertex(j) || leafFaces_[i]->back.containsVertex(j))
+            dirichletNodes->at(j)[2] = true;
+        #endif
+      }
+
+      std::shared_ptr<LeafBoundaryCondition> dirichletBoundary = std::make_shared<LeafBoundaryCondition>("dirichlet");
+      dirichletBoundary->setBoundaryPatch(body->leafView(), dirichletNodes);
+      dirichletBoundary->setBoundaryFunction(velocityDirichletFunction);
+      body->addBoundaryCondition(dirichletBoundary);
+    }
+}
+
+#include "stackedblocksfactory_tmpl.cc"
diff --git a/src/stackedblocksfactory.hh b/src/stackedblocksfactory.hh
new file mode 100644
index 0000000000000000000000000000000000000000..572019d7234ca2c2ecb722a265056e4344f89ed4
--- /dev/null
+++ b/src/stackedblocksfactory.hh
@@ -0,0 +1,77 @@
+#ifndef SRC_STACKEDBLOCKSFACTORY_HH
+#define SRC_STACKEDBLOCKSFACTORY_HH
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+
+#include "levelcontactnetworkfactory.hh"
+#include "multi-body-problem-data/mybody.hh"
+#include "multi-body-problem-data/mygrids.hh"
+
+template <class GridType, int dims> class StackedBlocksFactory : public LevelContactNetworkFactory<GridType, dims>{
+private:
+    using Base = LevelContactNetworkFactory<GridType, dims>;
+
+    using LocalVector = typename Base::LevelContactNetwork::LocalVector;
+
+    using DeformedLeafGridView = typename Base::LevelContactNetwork::DeformedLeafGridView;
+    using DeformedLevelGridView = typename Base::LevelContactNetwork::DeformedLevelGridView;
+
+    using LevelBoundaryPatch = BoundaryPatch<DeformedLevelGridView>;
+    using LeafBoundaryPatch = BoundaryPatch<DeformedLeafGridView>;
+
+    using LeafFaces = MyFaces<DeformedLeafGridView>;
+    using LevelFaces = MyFaces<DeformedLevelGridView>;
+
+private:
+    const std::shared_ptr<MyBodyData<dims>> bodyData_;     // material properties of bodies
+
+    GridsConstructor<GridType>* gridConstructor_;
+
+    std::vector<std::shared_ptr<CuboidGeometry>> cuboidGeometries_;
+
+    std::vector<std::shared_ptr<LeafFaces>> leafFaces_;
+    std::vector<std::shared_ptr<LevelFaces>> levelFaces_;
+
+    std::vector<std::shared_ptr<ConvexPolyhedron<LocalVector>>> lowerWeakPatches_;
+    std::vector<std::shared_ptr<ConvexPolyhedron<LocalVector>>> upperWeakPatches_;
+
+    std::vector<std::shared_ptr<LevelBoundaryPatch>> nonmortarPatch_;
+    std::vector<std::shared_ptr<LevelBoundaryPatch>> mortarPatch_;
+
+public:
+    StackedBlocksFactory(const Dune::ParameterTree& parset) :
+        Base(parset, parset.get<size_t>("problem.bodyCount"), parset.get<size_t>("problem.bodyCount")-1),
+        bodyData_(std::make_shared<MyBodyData<dims>>(this->parset_, zenith_())),
+        cuboidGeometries_(this->bodyCount_),
+        leafFaces_(this->bodyCount_),
+        levelFaces_(this->bodyCount_),
+        lowerWeakPatches_(this->bodyCount_),
+        upperWeakPatches_(this->bodyCount_),
+        nonmortarPatch_(this->couplingCount_),
+        mortarPatch_(this->couplingCount_)
+    {}
+
+    ~StackedBlocksFactory() {
+        delete gridConstructor_;
+    }
+
+    void setBodies();
+    void setCouplings();
+    void setBoundaryConditions();
+
+private:
+    static constexpr Dune::FieldVector<double, MY_DIM> zenith_() {
+        #if MY_DIM == 2
+        return {0, 1};
+        #elif MY_DIM == 3
+        return {0, 1, 0};
+        #endif
+    }
+};
+#endif
+
+
diff --git a/src/stackedblocksfactory_tmpl.cc b/src/stackedblocksfactory_tmpl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..173ea904917e531507cada5f42d8c72d86ea7258
--- /dev/null
+++ b/src/stackedblocksfactory_tmpl.cc
@@ -0,0 +1,7 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "explicitgrid.hh"
+
+template class StackedBlocksFactory<Grid, MY_DIM>;
diff --git a/src/time-stepping/rate.cc b/src/time-stepping/rate.cc
index ce4050c7e0f339564010d0f943902edbb7b9bdb1..b94a2a353cef977101784289a2a88633119a65eb 100644
--- a/src/time-stepping/rate.cc
+++ b/src/time-stepping/rate.cc
@@ -11,7 +11,7 @@ std::shared_ptr<RateUpdater<Vector, Matrix, Function, dimension>>
 initRateUpdater(Config::scheme scheme,
                 const std::vector<const Function*>& velocityDirichletFunctions,
                 const std::vector<Dune::BitSetVector<dimension>>& velocityDirichletNodes,
-                const Matrices<Matrix>& matrices,
+                const Matrices<Matrix,2>& 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/rate.hh b/src/time-stepping/rate.hh
index 5d2ae42809488b41e1021dbc56c29e4313560216..bef6b561b123e012960fe32eed1f72d0fcf0175f 100644
--- a/src/time-stepping/rate.hh
+++ b/src/time-stepping/rate.hh
@@ -10,8 +10,8 @@ template <class Vector, class Matrix, class Function, int dimension>
 std::shared_ptr<RateUpdater<Vector, Matrix, Function, dimension>>
 initRateUpdater(Config::scheme scheme,
                 const std::vector<const Function* >& velocityDirichletFunctions,
-                const std::vector<Dune::BitSetVector<dimension>>& velocityDirichletNodes,
-                const Matrices<Matrix>& matrices,
+                const std::vector<std::vector<Dune::BitSetVector<dimension>>>& velocityDirichletNodes,
+                const Matrices<Matrix,2>& 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/rate/backward_euler.cc b/src/time-stepping/rate/backward_euler.cc
index 11b7907f7dc46add2283a415c47583ae3bcae7f7..c4845782846180f98dabf1283415bef265ec6ad3 100644
--- a/src/time-stepping/rate/backward_euler.cc
+++ b/src/time-stepping/rate/backward_euler.cc
@@ -5,7 +5,7 @@
 
 template <class Vector, class Matrix, class Function, size_t dim>
 BackwardEuler<Vector, Matrix, Function, dim>::BackwardEuler(
-    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const Matrices<Matrix,2>& _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<const Function*>& _dirichletFunctions)
diff --git a/src/time-stepping/rate/backward_euler.hh b/src/time-stepping/rate/backward_euler.hh
index 169323d6fcecea70ef0fc9f49efe26053dd44d5e..26e5831d26a184a51d17af729837225294dca122 100644
--- a/src/time-stepping/rate/backward_euler.hh
+++ b/src/time-stepping/rate/backward_euler.hh
@@ -4,7 +4,7 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class BackwardEuler : public RateUpdater<Vector, Matrix, Function, dim> {
 public:
-  BackwardEuler(const Matrices<Matrix> &_matrices, const std::vector<Vector> &_u_initial,
+  BackwardEuler(const Matrices<Matrix,2> &_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<const Function*> &_dirichletFunctions);
diff --git a/src/time-stepping/rate/newmark.cc b/src/time-stepping/rate/newmark.cc
index c88136d77457ff1a153daae13b870e54fecee6f8..b3fb46699fb58f959a6e0ef891ffc0cdca3bae06 100644
--- a/src/time-stepping/rate/newmark.cc
+++ b/src/time-stepping/rate/newmark.cc
@@ -5,7 +5,7 @@
 
 template <class Vector, class Matrix, class Function, size_t dim>
 Newmark<Vector, Matrix, Function, dim>::Newmark(
-    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const Matrices<Matrix,2>& _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<const Function*>& _dirichletFunctions)
diff --git a/src/time-stepping/rate/newmark.hh b/src/time-stepping/rate/newmark.hh
index 25e014ea2b88821c5945b5f90371ea9435342786..9041e01dc066dde3a03b9285930aaf595598af95 100644
--- a/src/time-stepping/rate/newmark.hh
+++ b/src/time-stepping/rate/newmark.hh
@@ -4,7 +4,7 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class Newmark : public RateUpdater<Vector, Matrix, Function, dim> {
 public:
-  Newmark(const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+  Newmark(const Matrices<Matrix,2>& _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<const Function*>& _dirichletFunctions);
diff --git a/src/time-stepping/rate/rateupdater.cc b/src/time-stepping/rate/rateupdater.cc
index 6c095235963b39e540e03ccd928c002296b391c8..fb2e22a2d78caa630774df09d9d1aa79cd1b52c2 100644
--- a/src/time-stepping/rate/rateupdater.cc
+++ b/src/time-stepping/rate/rateupdater.cc
@@ -6,7 +6,7 @@
 
 template <class Vector, class Matrix, class Function, size_t dim>
 RateUpdater<Vector, Matrix, Function, dim>::RateUpdater(
-    const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+    const Matrices<Matrix,2>& _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<const Function*>& _dirichletFunctions)
diff --git a/src/time-stepping/rate/rateupdater.hh b/src/time-stepping/rate/rateupdater.hh
index 8b78e1d30cf5b97ebad4db18d454d1e703443651..6a2c73cb1a210c090a257ed136ec6ead3bf9bfba 100644
--- a/src/time-stepping/rate/rateupdater.hh
+++ b/src/time-stepping/rate/rateupdater.hh
@@ -10,7 +10,7 @@
 template <class Vector, class Matrix, class Function, size_t dim>
 class RateUpdater {
 protected:
-  RateUpdater(const Matrices<Matrix>& _matrices, const std::vector<Vector>& _u_initial,
+  RateUpdater(const Matrices<Matrix,2>& _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<const Function*>& _dirichletFunctions);
@@ -30,7 +30,7 @@ class RateUpdater {
       const = 0;
 
 protected:
-  const Matrices<Matrix>& matrices;
+  const Matrices<Matrix,2>& matrices;
   std::vector<Vector> u, v, a;
   const std::vector<Dune::BitSetVector<dim>>& dirichletNodes;
   const std::vector<const Function*>& dirichletFunctions;
diff --git a/src/time-stepping/rate_tmpl.cc b/src/time-stepping/rate_tmpl.cc
index c81bc04481353db945e2d92d3a8b283d34851bd5..b71b59eb3e85b3e0255f0bff47bcbd5a92adba37 100644
--- a/src/time-stepping/rate_tmpl.cc
+++ b/src/time-stepping/rate_tmpl.cc
@@ -9,7 +9,7 @@ initRateUpdater<Vector, Matrix, Function, MY_DIM>(
     Config::scheme scheme,
     const std::vector<const Function* >&  velocityDirichletFunctions,
     const std::vector<Dune::BitSetVector<MY_DIM>>& velocityDirichletNodes,
-    const Matrices<Matrix>& matrices,
+    const Matrices<Matrix, 2>& matrices,
     const std::vector<Vector>& u_initial,
     const std::vector<Vector>& v_initial,
     const std::vector<Vector>& a_initial);