diff --git a/src/assemblers.cc b/src/assemblers.cc
index 029c1767e39dea37bdf240eceff0ec5dfd53548b..160169fda8fc9fb4f676b87e64cf1137d8de3514 100644
--- a/src/assemblers.cc
+++ b/src/assemblers.cc
@@ -2,56 +2,113 @@
 #include "config.h"
 #endif
 
+#include <dune/istl/scaledidmatrix.hh>
+
 #include <dune/fufem/boundarypatch.hh>
 #include <dune/fufem/functions/constantfunction.hh>
+#include <dune/fufem/assemblers/localassemblers/boundarymassassembler.hh>
+#include <dune/fufem/assemblers/localassemblers/l2functionalassembler.hh>
+#include <dune/fufem/assemblers/localassemblers/massassembler.hh>
 #include <dune/fufem/assemblers/localassemblers/neumannboundaryassembler.hh>
+#include <dune/fufem/assemblers/localassemblers/stvenantkirchhoffassembler.hh>
+#include <dune/fufem/assemblers/localassemblers/viscosityassembler.hh>
 
 #include <dune/tectonic/globalruinanonlinearity.hh>
 
 #include "assemblers.hh"
 
-// Assembles Neumann boundary term in f
-template <class GridView, class LocalVector, class Assembler>
-void assembleNeumann(GridView const &gridView, Assembler const &assembler,
-                     Dune::BitSetVector<1> const &neumannNodes,
-                     Dune::BlockVector<LocalVector> &f,
-                     Dune::VirtualFunction<double, double> const &neumann,
-                     double relativeTime) { // constant sample function on
-                                            // neumann boundary
-  BoundaryPatch<GridView> const neumannBoundary(gridView, neumannNodes);
+template <class GridView, int dimension>
+MyAssembler<GridView, dimension>::MyAssembler(GridView const &_gridView)
+    : vertexBasis(_gridView),
+      gridView(_gridView),
+      assembler(vertexBasis, vertexBasis) {}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleFrictionalBoundaryMass(
+    BoundaryPatch<GridView> const &frictionalBoundary,
+    ScalarMatrix &frictionalBoundaryMass) {
+  BoundaryMassAssembler<Grid, BoundaryPatch<GridView>, LocalVertexBasis,
+                        LocalVertexBasis, Dune::FieldMatrix<double, 1, 1>> const
+  frictionalBoundaryMassAssembler(frictionalBoundary);
+  assembler.assembleOperator(frictionalBoundaryMassAssembler,
+                             frictionalBoundaryMass);
+}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleMass(double density, Matrix &M) {
+  MassAssembler<Grid, LocalVertexBasis, LocalVertexBasis,
+                Dune::ScaledIdentityMatrix<double, dimension>> const localMass;
+  assembler.assembleOperator(localMass, M);
+  M *= density;
+}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleElasticity(double E, double nu,
+                                                          Matrix &A) {
+  StVenantKirchhoffAssembler<Grid, LocalVertexBasis, LocalVertexBasis> const
+  localStiffness(E, nu);
+  assembler.assembleOperator(localStiffness, A);
+}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleViscosity(double shearViscosity,
+                                                         double bulkViscosity,
+                                                         Matrix &C) {
+  ViscosityAssembler<Grid, LocalVertexBasis, LocalVertexBasis> const
+  localViscosity(shearViscosity, bulkViscosity);
+  assembler.assembleOperator(localViscosity, C);
+}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleBodyForce(double gravity,
+                                                         double density,
+                                                         LocalVector zenith,
+                                                         Vector &f) {
+  LocalVector weightedGravitationalDirection = zenith;
+  weightedGravitationalDirection *= density * gravity;
+  weightedGravitationalDirection *= -1;
+
+  ConstantFunction<LocalVector, LocalVector> const gravityFunction(
+      weightedGravitationalDirection);
+  L2FunctionalAssembler<Grid, LocalVertexBasis, LocalVector>
+  gravityFunctionalAssembler(gravityFunction);
+  assembler.assembleFunctional(gravityFunctionalAssembler, f);
+}
+
+template <class GridView, int dimension>
+void MyAssembler<GridView, dimension>::assembleNeumann(
+    BoundaryPatch<GridView> const &neumannBoundary, Vector &f,
+    Dune::VirtualFunction<double, double> const &neumann, double relativeTime) {
   LocalVector localNeumann(0);
   neumann.evaluate(relativeTime, localNeumann[0]);
   ConstantFunction<LocalVector, LocalVector> const fNeumann(localNeumann);
-  NeumannBoundaryAssembler<typename GridView::Grid, LocalVector>
-  neumannBoundaryAssembler(fNeumann);
+  NeumannBoundaryAssembler<Grid, LocalVector> neumannBoundaryAssembler(
+      fNeumann);
   assembler.assembleBoundaryFunctional(neumannBoundaryAssembler, f,
                                        neumannBoundary);
 }
 
-// Assembles constant 1-function on frictional boundary in nodalIntegrals
-template <class GridView, class LocalVector, class Assembler>
-Dune::shared_ptr<Dune::BlockVector<Dune::FieldVector<double, 1>>>
-assembleFrictionWeightsal(GridView const &gridView, Assembler const &assembler,
-                          Dune::BitSetVector<1> const &frictionalNodes) {
-  using Scalar = Dune::FieldVector<double, 1>;
-  BoundaryPatch<GridView> const frictionalBoundary(gridView, frictionalNodes);
-  ConstantFunction<LocalVector, Scalar> const constantOneFunction(1);
-  NeumannBoundaryAssembler<typename GridView::Grid, Scalar>
-  frictionalBoundaryAssembler(constantOneFunction);
-
-  auto const nodalIntegrals = Dune::make_shared<Dune::BlockVector<Scalar>>();
-  assembler.assembleBoundaryFunctional(frictionalBoundaryAssembler,
-                                       *nodalIntegrals, frictionalBoundary);
-  return nodalIntegrals;
-}
+template <class GridView, int dimension>
+auto MyAssembler<GridView, dimension>::assembleFrictionNonlinearity(
+    BoundaryPatch<GridView> const &frictionalBoundary, FrictionData const &fd)
+    -> std::shared_ptr<Dune::GlobalNonlinearity<Matrix, Vector>> {
+  // Lump negative normal stress (kludge)
+  ScalarVector weights;
+  {
+    ConstantFunction<LocalVector, typename ScalarVector::block_type> const
+    constantOneFunction(1);
+    NeumannBoundaryAssembler<Grid, typename ScalarVector::block_type>
+    frictionalBoundaryAssembler(constantOneFunction);
+    assembler.assembleBoundaryFunctional(frictionalBoundaryAssembler, weights,
+                                         frictionalBoundary);
+  }
+  for (size_t i = 0; i < weights.size(); ++i)
+    assert(weights[i] >= 0.0);
 
-template <class Matrix, class Vector>
-Dune::shared_ptr<Dune::GlobalNonlinearity<Matrix, Vector>> assembleNonlinearity(
-    Dune::BitSetVector<1> const &frictionalNodes,
-    Dune::BlockVector<Dune::FieldVector<double, 1>> const &nodalIntegrals,
-    FrictionData const &fd) {
-  return Dune::make_shared<Dune::GlobalRuinaNonlinearity<Matrix, Vector>>(
-      frictionalNodes, nodalIntegrals, fd);
+  Dune::BitSetVector<1> frictionalNodes;
+  frictionalBoundary.getVertices(frictionalNodes);
+  return std::make_shared<Dune::GlobalRuinaNonlinearity<Matrix, Vector>>(
+      frictionalNodes, weights, fd);
 }
 
 #include "assemblers_tmpl.cc"
diff --git a/src/assemblers.hh b/src/assemblers.hh
index 75d3dd487b5341aae740cb4e952144df9663bb92..a7e0721fac2601e2b882f3825858b0d872704940 100644
--- a/src/assemblers.hh
+++ b/src/assemblers.hh
@@ -3,29 +3,59 @@
 
 #include <dune/common/bitsetvector.hh>
 #include <dune/common/function.hh>
-#include <dune/common/fvector.hh>
-#include <dune/common/shared_ptr.hh>
+#include <dune/istl/bcrsmatrix.hh>
 #include <dune/istl/bvector.hh>
 
 #include <dune/fufem/assemblers/assembler.hh>
+#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
 
 #include <dune/tectonic/globalnonlinearity.hh>
 
-template <class GridView, class LocalVector, class Assembler>
-void assembleNeumann(GridView const &gridView, Assembler const &assembler,
-                     Dune::BitSetVector<1> const &neumannNodes,
-                     Dune::BlockVector<LocalVector> &f,
-                     Dune::VirtualFunction<double, double> const &neumann,
-                     double relativeTime);
-
-template <class GridView, class LocalVector, class Assembler>
-Dune::shared_ptr<Dune::BlockVector<Dune::FieldVector<double, 1>>>
-assembleFrictionWeightsal(GridView const &gridView, Assembler const &assembler,
-                          Dune::BitSetVector<1> const &frictionalNodes);
-
-template <class Matrix, class Vector>
-Dune::shared_ptr<Dune::GlobalNonlinearity<Matrix, Vector>> assembleNonlinearity(
-    Dune::BitSetVector<1> const &frictionalNodes,
-    Dune::BlockVector<Dune::FieldVector<double, 1>> const &nodalIntegrals,
-    FrictionData const &fd);
+template <class GridView, int dimension> class MyAssembler {
+public:
+  using ScalarMatrix = Dune::BCRSMatrix<Dune::FieldMatrix<double, 1, 1>>;
+  using Matrix =
+      Dune::BCRSMatrix<Dune::FieldMatrix<double, dimension, dimension>>;
+  using ScalarVector = Dune::BlockVector<Dune::FieldVector<double, 1>>;
+  using Vector = Dune::BlockVector<Dune::FieldVector<double, dimension>>;
+
+  using VertexBasis = P1NodalBasis<GridView, double>;
+  VertexBasis vertexBasis;
+
+private:
+  using Grid = typename GridView::Grid;
+  using LocalVector = typename Vector::block_type;
+  using LocalVertexBasis = typename VertexBasis::LocalFiniteElement;
+
+  GridView const &gridView;
+  Assembler<VertexBasis, VertexBasis> assembler;
+
+public:
+  MyAssembler(GridView const &gridView);
+
+  void assembleFrictionalBoundaryMass(
+      BoundaryPatch<GridView> const &frictionalBoundary,
+      ScalarMatrix &frictionalBoundaryMass);
+
+  void assembleMass(double density, Matrix &M);
+
+  void assembleElasticity(double E, double nu, Matrix &A);
+
+  void assembleViscosity(double shearViscosity, double bulkViscosity,
+                         Matrix &C);
+
+  void assembleBodyForce(double gravity, double density, LocalVector zenith,
+                         Vector &f);
+
+  void assembleNeumann(BoundaryPatch<GridView> const &neumannBoundary,
+                       Vector &f,
+                       Dune::VirtualFunction<double, double> const &neumann,
+                       double relativeTime);
+
+  std::shared_ptr<Dune::GlobalNonlinearity<Matrix, Vector>>
+  assembleFrictionNonlinearity(
+      BoundaryPatch<GridView> const &frictionalBoundary,
+      FrictionData const &fd);
+};
+
 #endif
diff --git a/src/assemblers_tmpl.cc b/src/assemblers_tmpl.cc
index 35cddb4a47c413fe367237e3c20c856394ffd2a2..36628ed9d1a317fe9c90dd089f081c4912d1e69e 100644
--- a/src/assemblers_tmpl.cc
+++ b/src/assemblers_tmpl.cc
@@ -2,27 +2,6 @@
 #error DIM unset
 #endif
 
-#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
-
 #include "explicitgrid.hh"
-#include "explicitvectors.hh"
-
-using P1Basis = P1NodalBasis<GridView, double>;
-using MyAssembler = Assembler<P1Basis, P1Basis>;
-
-template void assembleNeumann<GridView, SmallVector, MyAssembler>(
-    GridView const &gridView, MyAssembler const &assembler,
-    Dune::BitSetVector<1> const &neumannNodes,
-    Dune::BlockVector<SmallVector> &f,
-    Dune::VirtualFunction<double, double> const &neumann, double relativeTime);
-
-template Dune::shared_ptr<Dune::BlockVector<Dune::FieldVector<double, 1>>>
-assembleFrictionWeightsal<GridView, SmallVector, MyAssembler>(
-    GridView const &gridView, MyAssembler const &assembler,
-    Dune::BitSetVector<1> const &frictionalNodes);
 
-template Dune::shared_ptr<Dune::GlobalNonlinearity<Matrix, Vector>>
-assembleNonlinearity<Matrix, Vector>(
-    Dune::BitSetVector<1> const &frictionalNodes,
-    Dune::BlockVector<Dune::FieldVector<double, 1>> const &nodalIntegrals,
-    FrictionData const &fd);
+template class MyAssembler<GridView, DIM>;
diff --git a/src/one-body-sample.cc b/src/one-body-sample.cc
index 8d96bc6849043e13c7dc3dcd0840c38b5a8d2472..2d25e7d1515471662dcbc0a5ccfb0eadd2d9db73 100644
--- a/src/one-body-sample.cc
+++ b/src/one-body-sample.cc
@@ -49,19 +49,12 @@
 #include <dune/grid/utility/structuredgridfactory.hh>
 #include <dune/istl/bcrsmatrix.hh>
 #include <dune/istl/bvector.hh>
-#include <dune/istl/scaledidmatrix.hh>
 
 #include <dune/fufem/assemblers/assembler.hh>
-#include <dune/fufem/assemblers/localassemblers/boundarymassassembler.hh>
-#include <dune/fufem/assemblers/localassemblers/l2functionalassembler.hh>
-#include <dune/fufem/assemblers/localassemblers/massassembler.hh>
-#include <dune/fufem/assemblers/localassemblers/stvenantkirchhoffassembler.hh>
-#include <dune/fufem/assemblers/localassemblers/viscosityassembler.hh>
 #include <dune/fufem/assemblers/localassemblers/vonmisesstressassembler.hh>
 #include <dune/fufem/boundarypatch.hh>
 #include <dune/fufem/dunepython.hh>
 #include <dune/fufem/functions/basisgridfunction.hh>
-#include <dune/fufem/functions/constantfunction.hh>
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wsign-compare"
@@ -163,6 +156,8 @@ int main(int argc, char *argv[]) {
 
     auto const E = parset.get<double>("body.E");
     auto const nu = parset.get<double>("body.nu");
+    auto const density = parset.get<double>("body.density");
+    double const gravity = 9.81;
 
     // {{{ Set up grid
     using Grid = Dune::ALUGrid<dims, dims, Dune::simplex, Dune::nonconforming>;
@@ -182,6 +177,8 @@ int main(int argc, char *argv[]) {
       grid = Dune::StructuredGridFactory<Grid>::createSimplexGrid(
           lowerLeft, upperRight, elements);
     }
+    SmallVector zenith(0);
+    zenith[1] = 1;
 
     auto const refinements = parset.get<size_t>("grid.refinements");
     grid->globalRefine(refinements);
@@ -193,11 +190,8 @@ int main(int argc, char *argv[]) {
 
     // Set up bases
     using P0Basis = P0Basis<GridView, double>;
-    using P1Basis = P1NodalBasis<GridView, double>;
     P0Basis const p0Basis(leafView);
-    P1Basis const p1Basis(leafView);
     Assembler<P0Basis, P0Basis> const p0Assembler(p0Basis, p0Basis);
-    Assembler<P1Basis, P1Basis> const p1Assembler(p1Basis, p1Basis);
 
     // Set up the boundary
     Dune::BitSetVector<dims> velocityDirichletNodes(fineVertexCount, false);
@@ -205,9 +199,10 @@ int main(int argc, char *argv[]) {
         velocityDirichletNodes;
     Dune::BitSetVector<dims> accelerationDirichletNodes(fineVertexCount, false);
     Dune::BitSetVector<1> neumannNodes(fineVertexCount, false);
-    Dune::BitSetVector<1> frictionalNodes(fineVertexCount, false);
+    BoundaryPatch<GridView> const neumannBoundary(leafView, neumannNodes);
 
     Vector vertexCoordinates(fineVertexCount);
+    Dune::BitSetVector<1> frictionalNodes(fineVertexCount, false);
     {
       Dune::MultipleCodimMultipleGeomTypeMapper<
           GridView, Dune::MCMGVertexLayout> const vertexMapper(leafView);
@@ -254,89 +249,54 @@ int main(int argc, char *argv[]) {
 
     // Set up normal stress, mass matrix, and gravity functional
     double normalStress;
-    Matrix M;
-    EnergyNorm<Matrix, Vector> const MNorm(M);
-    Vector gravityFunctional;
     {
-      double const gravity = 9.81;
-      double const density = parset.get<double>("body.density");
-      {
-        MassAssembler<Grid, P1Basis::LocalFiniteElement,
-                      P1Basis::LocalFiniteElement,
-                      Dune::ScaledIdentityMatrix<double, dims>> const localMass;
-        p1Assembler.assembleOperator(localMass, M);
-        M *= density;
-      }
-      {
-        double volume = 1.0;
-        for (size_t i = 0; i < dims; ++i)
-          volume *= (upperRight[i] - lowerLeft[i]);
-
-        double area = 1.0;
-        for (size_t i = 0; i < dims; ++i)
-          if (i != 1)
-            area *= (upperRight[i] - lowerLeft[i]);
-
-        // volume * gravity * density / area    = normal stress
-        // V      * g       * rho     / A       = sigma_n
-        // m^d    * N/kg    * kg/m^d  / m^(d-1) = N/m^(d-1)
-        normalStress = volume * gravity * density / area;
-      }
-      {
-        SmallVector weightedGravitationalDirection(0);
-        weightedGravitationalDirection[1] = -density * gravity;
-        ConstantFunction<SmallVector, SmallVector> const gravityFunction(
-            weightedGravitationalDirection);
-        L2FunctionalAssembler<Grid, P1Basis::LocalFiniteElement, SmallVector>
-        gravityFunctionalAssembler(gravityFunction);
-        p1Assembler.assembleFunctional(gravityFunctionalAssembler,
-                                       gravityFunctional);
-      }
+      double volume = 1.0;
+      for (size_t i = 0; i < dims; ++i)
+        volume *= (upperRight[i] - lowerLeft[i]);
+
+      double area = 1.0;
+      for (size_t i = 0; i < dims; ++i)
+        if (i != 1)
+          area *= (upperRight[i] - lowerLeft[i]);
+
+      // volume * gravity * density / area    = normal stress
+      // V      * g       * rho     / A       = sigma_n
+      // m^d    * N/kg    * kg/m^d  / m^(d-1) = N/m^(d-1)
+      normalStress = volume * gravity * density / area;
     }
     FrictionData const frictionData(parset.sub("boundary.friction"),
                                     normalStress);
 
-    Matrix A;
-    EnergyNorm<Matrix, Vector> const ANorm(A);
-    {
-      StVenantKirchhoffAssembler<Grid, P1Basis::LocalFiniteElement,
-                                 P1Basis::LocalFiniteElement> const
-      localStiffness(E, nu);
-      p1Assembler.assembleOperator(localStiffness, A);
-    }
-    Matrix C;
-    {
-      ViscosityAssembler<Grid, P1Basis::LocalFiniteElement,
-                         P1Basis::LocalFiniteElement> const
-      localViscosity(parset.get<double>("body.shearViscosity"),
-                     parset.get<double>("body.bulkViscosity"));
-      p1Assembler.assembleOperator(localViscosity, C);
-    }
+    using MyAssembler = MyAssembler<GridView, dims>;
 
-    ScalarMatrix frictionalBoundaryMassMatrix;
-    EnergyNorm<ScalarMatrix, ScalarVector> const stateEnergyNorm(
-        frictionalBoundaryMassMatrix);
-    {
-      BoundaryMassAssembler<
-          Grid, BoundaryPatch<GridView>, P1Basis::LocalFiniteElement,
-          P1Basis::LocalFiniteElement, SmallScalarMatrix> const
-      frictionalBoundaryMassAssembler(frictionalBoundary);
-      p1Assembler.assembleOperator(frictionalBoundaryMassAssembler,
-                                   frictionalBoundaryMassMatrix);
-    }
+    MyAssembler myAssembler(leafView);
+
+    Matrix A, C, M;
+    myAssembler.assembleElasticity(E, nu, A);
+    myAssembler.assembleViscosity(parset.get<double>("body.shearViscosity"),
+                                  parset.get<double>("body.bulkViscosity"), C);
+    myAssembler.assembleMass(density, M);
+    EnergyNorm<Matrix, Vector> const ANorm(A), MNorm(M);
     // Q: Does it make sense to weigh them in this manner?
     SumNorm<Vector> const AMNorm(1.0, ANorm, 1.0, MNorm);
 
-    auto const nodalIntegrals =
-        assembleFrictionWeightsal<GridView, SmallVector>(leafView, p1Assembler,
-                                                         frictionalNodes);
-    auto myGlobalNonlinearity = assembleNonlinearity<Matrix, Vector>(
-        frictionalNodes, *nodalIntegrals, frictionData);
+    ScalarMatrix frictionalBoundaryMass;
+    myAssembler.assembleFrictionalBoundaryMass(frictionalBoundary,
+                                               frictionalBoundaryMass);
+    EnergyNorm<ScalarMatrix, ScalarVector> const stateEnergyNorm(
+        frictionalBoundaryMass);
+
+    // Assemble forces
+    Vector gravityFunctional;
+    myAssembler.assembleBodyForce(gravity, density, zenith, gravityFunctional);
+
+    auto myGlobalNonlinearity = myAssembler.assembleFrictionNonlinearity(
+        frictionalBoundary, frictionData);
 
     // Problem formulation: right-hand side
     auto const createRHS = [&](double _relativeTime, Vector &_ell) {
-      assembleNeumann(leafView, p1Assembler, neumannNodes, _ell,
-                      neumannFunction, _relativeTime);
+      myAssembler.assembleNeumann(neumannBoundary, _ell, neumannFunction,
+                                  _relativeTime);
       _ell += gravityFunctional;
     };
     Vector ell(fineVertexCount);
@@ -569,15 +529,15 @@ int main(int argc, char *argv[]) {
       relaxationWriter << std::endl;
 
       if (parset.get<bool>("io.writeVTK")) {
-        auto const gridDisplacement =
-            Dune::make_shared<BasisGridFunction<P1Basis, Vector> const>(p1Basis,
-                                                                        u);
+        auto const gridDisplacement = Dune::make_shared<
+            BasisGridFunction<typename MyAssembler::VertexBasis, Vector> const>(
+            myAssembler.vertexBasis, u);
         ScalarVector vonMisesStress;
         VonMisesStressAssembler<Grid, P0Basis::LocalFiniteElement>
         localStressAssembler(E, nu, gridDisplacement);
         p0Assembler.assembleFunctional(localStressAssembler, vonMisesStress);
 
-        writeVtk(p1Basis, u, alpha, p0Basis, vonMisesStress,
+        writeVtk(myAssembler.vertexBasis, u, alpha, p0Basis, vonMisesStress,
                  (boost::format("obs%d") % run).str());
       }
     }