diff --git a/dune/tectonic/frictiondata.hh b/dune/tectonic/frictiondata.hh
index 0262f2df8529be50b73e3dd8d1a3caf89596062b..5f2c43c66a2554ee619f7e32c290b20967c83a04 100644
--- a/dune/tectonic/frictiondata.hh
+++ b/dune/tectonic/frictiondata.hh
@@ -3,12 +3,8 @@
 #include <dune/common/parametertree.hh>
 
 struct FrictionData {
-  FrictionData(Dune::ParameterTree const &parset)
-      : L(parset.get<double>("L")),
-        V0(parset.get<double>("V0")),
-        a(parset.get<double>("a")),
-        b(parset.get<double>("b")),
-        mu0(parset.get<double>("mu0")) {}
+  explicit FrictionData(double _L, double _V0, double _a, double _b, double _mu0)
+      : L(_L), V0(_V0), a(_a), b(_b), mu0(_mu0) {}
 
   double const L;
   double const V0;
diff --git a/dune/tectonic/frictionpotential.hh b/dune/tectonic/frictionpotential.hh
index eb447bfb4880560633e469fbda7c2b430aae83b1..03d74518ddb75ca0afd1994f172f8a806f7b8983 100644
--- a/dune/tectonic/frictionpotential.hh
+++ b/dune/tectonic/frictionpotential.hh
@@ -18,6 +18,7 @@ class FrictionPotentialWrapper {
   double virtual differential(double s) const = 0;
   double virtual second_deriv(double x) const = 0;
   double virtual regularity(double s) const = 0;
+  double virtual coefficientOfFriction(double s) const = 0;
 
   double virtual evaluate(double x) const {
     DUNE_THROW(Dune::NotImplemented, "evaluation not implemented");
@@ -28,16 +29,19 @@ class FrictionPotentialWrapper {
 
 class FrictionPotential : public FrictionPotentialWrapper {
 public:
-  FrictionPotential(double coefficient, double _normalStress,
-                    FrictionData const &_fd)
+  FrictionPotential(double coefficient, double _normalStress, FrictionData _fd)
       : fd(_fd), weight(coefficient), normalStress(_normalStress) {}
 
-  double differential(double V) const {
+  double coefficientOfFriction(double V) const {
     assert(V >= 0.0);
     if (V <= V_cutoff)
       return 0.0;
 
-    return weight * (-normalStress) * fd.a * (std::log(V) - logV_m);
+    return fd.a * (std::log(V) - logV_m);
+  }
+
+  double differential(double V) const {
+    return weight * (-normalStress) * coefficientOfFriction(V);
   }
 
   double second_deriv(double V) const {
@@ -64,7 +68,7 @@ class FrictionPotential : public FrictionPotentialWrapper {
   }
 
 private:
-  FrictionData const &fd;
+  FrictionData const fd;
   double const weight;
   double const normalStress;
   double logV_m;
@@ -75,6 +79,8 @@ class TrivialFunction : public FrictionPotentialWrapper {
 public:
   double evaluate(double) const { return 0; }
 
+  double coefficientOfFriction(double s) const { return 0; }
+
   double differential(double) const { return 0; }
 
   double second_deriv(double) const { return 0; }
diff --git a/dune/tectonic/globalfrictiondata.hh b/dune/tectonic/globalfrictiondata.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6500352399d898aa8139c76d4b88bea7548ab665
--- /dev/null
+++ b/dune/tectonic/globalfrictiondata.hh
@@ -0,0 +1,36 @@
+#ifndef GLOBAL_FRICTION_DATA_HH
+#define GLOBAL_FRICTION_DATA_HH
+
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+
+#include <dune/tectonic/frictiondata.hh>
+
+template <class DomainType>
+double evaluateScalarFunction(
+    Dune::VirtualFunction<DomainType, Dune::FieldVector<double, 1>> const &f,
+    DomainType const &x) {
+  Dune::FieldVector<double, 1> ret;
+  f.evaluate(x, ret);
+  return ret;
+};
+
+template <int dimension> class GlobalFrictionData {
+public:
+  FrictionData operator()(Dune::FieldVector<double, dimension> const &x) const {
+    return FrictionData(L(), V0(), evaluateScalarFunction(a(), x),
+                        evaluateScalarFunction(b(), x), mu0());
+  }
+
+protected:
+  using VirtualFunction = Dune::VirtualFunction<
+      Dune::FieldVector<double, dimension>, Dune::FieldVector<double, 1>>;
+
+  double virtual const &L() const = 0;
+  double virtual const &V0() const = 0;
+  VirtualFunction virtual const &a() const = 0;
+  VirtualFunction virtual const &b() const = 0;
+  double virtual const &mu0() const = 0;
+};
+
+#endif
diff --git a/dune/tectonic/globalruinanonlinearity.hh b/dune/tectonic/globalruinanonlinearity.hh
index c95ed1f7a2bd407cc725efb4771703055cecb3be..4dc996d805c8aa7d9c78dbe7c5faf81728bc9ed3 100644
--- a/dune/tectonic/globalruinanonlinearity.hh
+++ b/dune/tectonic/globalruinanonlinearity.hh
@@ -6,13 +6,16 @@
 #include <dune/common/bitsetvector.hh>
 #include <dune/common/fmatrix.hh>
 #include <dune/common/fvector.hh>
+#include <dune/grid/common/mcmgmapper.hh>
 #include <dune/istl/bcrsmatrix.hh>
 #include <dune/istl/bvector.hh>
 
+#include <dune/tectonic/globalfrictiondata.hh>
+
 #include "globalnonlinearity.hh"
 #include "frictionpotential.hh"
 
-template <class Matrix, class Vector>
+template <class Matrix, class Vector, class GridView>
 class GlobalRuinaNonlinearity : public GlobalNonlinearity<Matrix, Vector> {
 public:
   using GlobalNonlinearity<Matrix, Vector>::block_size;
@@ -22,19 +25,28 @@ class GlobalRuinaNonlinearity : public GlobalNonlinearity<Matrix, Vector> {
   using typename GlobalNonlinearity<Matrix, Vector>::ScalarVector;
 
 public:
-  GlobalRuinaNonlinearity(Dune::BitSetVector<1> const &frictionalNodes,
-                          ScalarVector const &weights, FrictionData const &fd,
+  GlobalRuinaNonlinearity(BoundaryPatch<GridView> const &frictionalBoundary,
+                          GridView const &gridView,
+                          GlobalFrictionData<block_size> const &frictionInfo,
+                          // Note: passing the following two makes no sense
+                          ScalarVector const &weights,
                           ScalarVector const &normalStress)
-      : restrictions(frictionalNodes.size()) {
+      : restrictions(normalStress.size()) {
     auto trivialNonlinearity =
         std::make_shared<Friction>(std::make_shared<TrivialFunction>());
-    for (size_t i = 0; i < restrictions.size(); ++i) {
-      if (not frictionalNodes[i][0]) {
+
+    Dune::MultipleCodimMultipleGeomTypeMapper<
+        GridView, Dune::MCMGVertexLayout> const vertexMapper(gridView);
+    for (auto it = gridView.template begin<block_size>();
+         it != gridView.template end<block_size>(); ++it) {
+      auto const i = vertexMapper.map(*it);
+      auto const coordinate = it->geometry().corner(0);
+      if (not frictionalBoundary.containsVertex(i)) {
         restrictions[i] = trivialNonlinearity;
         continue;
       }
-      auto const fp =
-          std::make_shared<FrictionPotential>(weights[i], normalStress[i], fd);
+      auto const fp = std::make_shared<FrictionPotential>(
+          weights[i], normalStress[i], frictionInfo(coordinate));
       restrictions[i] = std::make_shared<Friction>(fp);
     }
   }
@@ -44,6 +56,13 @@ class GlobalRuinaNonlinearity : public GlobalNonlinearity<Matrix, Vector> {
       restrictions[i]->updateLogState(logState[i]);
   }
 
+  void coefficientOfFriction(Vector const &x, ScalarVector &coefficient) {
+    assert(x.size() == restrictions.size());
+    coefficient.resize(restrictions.size());
+    for (size_t i = 0; i < restrictions.size(); ++i)
+      coefficient[i] = restrictions[i]->coefficientOfFriction(x[i]);
+  }
+
   /*
     Return a restriction of the outer function to the i'th node.
   */
diff --git a/dune/tectonic/localfriction.hh b/dune/tectonic/localfriction.hh
index 04b8121a9b3d06c82a15c44bed2fa4f2471ef67c..5c67dc38e1b496ce2f0306c3f8fb1e73521b0cbd 100644
--- a/dune/tectonic/localfriction.hh
+++ b/dune/tectonic/localfriction.hh
@@ -51,6 +51,10 @@ template <size_t dimension> class LocalFriction {
     return func->regularity(xnorm);
   }
 
+  double coefficientOfFriction(VectorType const &x) const {
+    return func->coefficientOfFriction(x.two_norm());
+  }
+
   // directional subdifferential: at u on the line u + t*v
   // u and v are assumed to be non-zero
   void directionalSubDiff(VectorType const &x, VectorType const &v,
diff --git a/src/assemblers.cc b/src/assemblers.cc
index 205ef090a512c19cd060b7eead81ff729970a0f7..b9a3e76b93f0ebf96e011b61cbbda9b36d6b847a 100644
--- a/src/assemblers.cc
+++ b/src/assemblers.cc
@@ -92,9 +92,10 @@ void MyAssembler<GridView, dimension>::assembleNeumann(
 
 template <class GridView, int dimension>
 auto MyAssembler<GridView, dimension>::assembleFrictionNonlinearity(
-    BoundaryPatch<GridView> const &frictionalBoundary, FrictionData const &fd,
+    BoundaryPatch<GridView> const &frictionalBoundary,
+    GlobalFrictionData<dimension> const &frictionInfo,
     ScalarVector const &normalStress)
-    -> std::shared_ptr<GlobalRuinaNonlinearity<Matrix, Vector>> {
+    -> std::shared_ptr<GlobalRuinaNonlinearity<Matrix, Vector, GridView>> {
   // Lump negative normal stress (kludge)
   ScalarVector weights;
   {
@@ -105,13 +106,8 @@ auto MyAssembler<GridView, dimension>::assembleFrictionNonlinearity(
     vertexAssembler.assembleBoundaryFunctional(frictionalBoundaryAssembler,
                                                weights, frictionalBoundary);
   }
-  for (size_t i = 0; i < weights.size(); ++i)
-    assert(weights[i] >= 0.0);
-
-  Dune::BitSetVector<1> frictionalNodes;
-  frictionalBoundary.getVertices(frictionalNodes);
-  return std::make_shared<GlobalRuinaNonlinearity<Matrix, Vector>>(
-      frictionalNodes, weights, fd, normalStress);
+  return std::make_shared<GlobalRuinaNonlinearity<Matrix, Vector, GridView>>(
+      frictionalBoundary, gridView, frictionInfo, weights, normalStress);
 }
 
 template <class GridView, int dimension>
diff --git a/src/assemblers.hh b/src/assemblers.hh
index 0672350058e848a6a766518a83c9356c8bf6973d..a281251e7f91d76c08c12734253bd2b5a876e13b 100644
--- a/src/assemblers.hh
+++ b/src/assemblers.hh
@@ -13,6 +13,7 @@
 #pragma clang diagnostic pop
 #include <dune/fufem/functionspacebases/p1nodalbasis.hh>
 
+#include <dune/tectonic/globalfrictiondata.hh>
 #include <dune/tectonic/globalruinanonlinearity.hh>
 
 template <class GridView, int dimension> class MyAssembler {
@@ -66,9 +67,10 @@ template <class GridView, int dimension> class MyAssembler {
                        Dune::VirtualFunction<double, double> const &neumann,
                        double relativeTime);
 
-  std::shared_ptr<GlobalRuinaNonlinearity<Matrix, Vector>>
+  std::shared_ptr<GlobalRuinaNonlinearity<Matrix, Vector, GridView>>
   assembleFrictionNonlinearity(
-      BoundaryPatch<GridView> const &frictionalBoundary, FrictionData const &fd,
+      BoundaryPatch<GridView> const &frictionalBoundary,
+      GlobalFrictionData<dimension> const &frictionInfo,
       ScalarVector const &normalStress);
 
   void assembleVonMisesStress(double youngModulus, double poissonRatio,
diff --git a/src/friction_writer.cc b/src/friction_writer.cc
index 46ee0e5bee3c8411c470cc4721484c983282ff6d..22cfe296f887343a1fb0121cd111e1d02ad51455 100644
--- a/src/friction_writer.cc
+++ b/src/friction_writer.cc
@@ -4,20 +4,12 @@
 
 #include "friction_writer.hh"
 
-double computeCOF(FrictionData const &fd, double V, double logState) {
-  double const mu = fd.mu0 + fd.a * std::log(V / fd.V0) +
-                    fd.b * (logState + std::log(fd.V0 / fd.L));
-  return (mu > 0) ? mu : 0;
-}
-
 template <class BitVector>
-FrictionWriter<BitVector>::FrictionWriter(FrictionData const &_fd,
-                                          BitVector const &_boundaryNodes)
+FrictionWriter<BitVector>::FrictionWriter(BitVector const &_boundaryNodes)
     : coefficientWriter("coefficients", std::fstream::out),
       displacementWriter("displacements", std::fstream::out),
       stateWriter("states", std::fstream::out),
       velocityWriter("velocities", std::fstream::out),
-      fd(_fd),
       boundaryNodes(_boundaryNodes) {}
 
 template <class BitVector> FrictionWriter<BitVector>::~FrictionWriter() {
@@ -29,13 +21,14 @@ template <class BitVector> FrictionWriter<BitVector>::~FrictionWriter() {
 
 template <class BitVector>
 template <class ScalarVector, class Vector>
-void FrictionWriter<BitVector>::writeInfo(ScalarVector const &alpha,
+void FrictionWriter<BitVector>::writeInfo(ScalarVector const &coefficient,
+                                          ScalarVector const &alpha,
                                           Vector const &u, Vector const &v) {
   for (size_t i = 0; i < boundaryNodes.size(); ++i) {
     if (!boundaryNodes[i][0])
       continue;
 
-    coefficientWriter << computeCOF(fd, v[i].two_norm(), alpha[i]) << " ";
+    coefficientWriter << coefficient[i] << " ";
     displacementWriter << u[i][0] << " ";
     stateWriter << alpha[i][0] << " ";
     velocityWriter << v[i][0] << " ";
diff --git a/src/friction_writer.hh b/src/friction_writer.hh
index c2af5fffa9a7f893ff113c7b902ed776781c2b48..7494b890537a9eb8f83af0828fdec2ee4ea386d8 100644
--- a/src/friction_writer.hh
+++ b/src/friction_writer.hh
@@ -3,23 +3,21 @@
 
 #include <fstream>
 
-#include <dune/tectonic/frictiondata.hh>
-
 template <class BitVector> class FrictionWriter {
 public:
-  FrictionWriter(FrictionData const &_fd, BitVector const &_boundaryNodes);
+  FrictionWriter(BitVector const &_boundaryNodes);
 
   ~FrictionWriter();
 
   template <class ScalarVector, class Vector>
-  void writeInfo(ScalarVector const &alpha, Vector const &u, Vector const &v);
+  void writeInfo(ScalarVector const &coefficient, ScalarVector const &alpha,
+                 Vector const &u, Vector const &v);
 
 private:
   std::fstream coefficientWriter;
   std::fstream displacementWriter;
   std::fstream stateWriter;
   std::fstream velocityWriter;
-  FrictionData const &fd;
   BitVector const &boundaryNodes;
 };
 #endif
diff --git a/src/friction_writer_tmpl.cc b/src/friction_writer_tmpl.cc
index dbeb7b8b8c5cabb9eda1639f0cd96ab4468aa34c..9a95b2611f6977c56650d6018cfa92514caa1c00 100644
--- a/src/friction_writer_tmpl.cc
+++ b/src/friction_writer_tmpl.cc
@@ -10,6 +10,6 @@ using BitVector = Dune::BitSetVector<1>;
 
 template class FrictionWriter<BitVector>;
 
-template void FrictionWriter<BitVector>::writeInfo(ScalarVector const &alpha,
-                                                   Vector const &u,
-                                                   Vector const &v);
+template void FrictionWriter<BitVector>::writeInfo(
+    ScalarVector const &coefficient, ScalarVector const &alpha, Vector const &u,
+    Vector const &v);
diff --git a/src/mybody.hh b/src/mybody.hh
index ec735642edbca78cf2e0d63f02421c2f4131da77..3b33c93fa023bf208a4bde66327b75d3fa2d49d8 100644
--- a/src/mybody.hh
+++ b/src/mybody.hh
@@ -13,7 +13,6 @@
 template <int dimension> class MyBody : public Body<dimension> {
   using typename Body<dimension>::ScalarFunction;
   using typename Body<dimension>::VectorField;
-
   using MyConstantFunction = ConstantFunction<
       Dune::FieldVector<double, dimension>, Dune::FieldVector<double, 1>>;
 
diff --git a/src/myglobalfrictiondata.hh b/src/myglobalfrictiondata.hh
new file mode 100644
index 0000000000000000000000000000000000000000..43d6b9cfa25d5a697d3f3fc57b0ed8b0a3242515
--- /dev/null
+++ b/src/myglobalfrictiondata.hh
@@ -0,0 +1,43 @@
+#ifndef MY_GLOBAL_FRICTION_DATA_HH
+#define MY_GLOBAL_FRICTION_DATA_HH
+
+#include <dune/common/function.hh>
+#include <dune/common/fvector.hh>
+
+#include <dune/fufem/functions/constantfunction.hh>
+
+#include <dune/tectonic/globalfrictiondata.hh>
+
+#include "mygeometry.hh"
+
+template <int dimension>
+class MyGlobalFrictionData : public GlobalFrictionData<dimension> {
+private:
+  using typename GlobalFrictionData<dimension>::VirtualFunction;
+
+public:
+  MyGlobalFrictionData(Dune::ParameterTree const &parset, MyGeometry const &tri)
+      : L_(parset.get<double>("L")),
+        V0_(parset.get<double>("V0")),
+        a_(parset.get<double>("a")),
+        b_(parset.get<double>("b")),
+        mu0_(parset.get<double>("mu0")) {}
+
+  double virtual const &L() const override { return L_; }
+  double virtual const &V0() const override { return V0_; }
+  VirtualFunction virtual const &a() const override { return a_; }
+  VirtualFunction virtual const &b() const override { return b_; }
+  double virtual const &mu0() const override { return mu0_; }
+
+private:
+  using MyConstantFunction = ConstantFunction<
+      Dune::FieldVector<double, dimension>, Dune::FieldVector<double, 1>>;
+
+  double const L_;
+  double const V0_;
+  MyConstantFunction const a_;
+  MyConstantFunction const b_;
+  double const mu0_;
+};
+
+#endif
diff --git a/src/one-body-sample.cc b/src/one-body-sample.cc
index 651553c44ede700d2f321dc1ac2a4cd7e3c08e3e..0a10471c91cdb280817eebd6ad6c3a99c337ad48 100644
--- a/src/one-body-sample.cc
+++ b/src/one-body-sample.cc
@@ -76,6 +76,7 @@
 #include "friction_writer.hh"
 #include "mybody.hh"
 #include "mygeometry.hh"
+#include "myglobalfrictiondata.hh"
 #include "mygrid.hh"
 #include "solverfactory.hh"
 #include "state.hh"
@@ -193,14 +194,15 @@ int main(int argc, char *argv[]) {
     normalStress = (myGeometry.A[1] - myGeometry.C[1]) *
                    parset.get<double>("gravity") *
                    parset.get<double>("body.density");
-    FrictionData const frictionData(parset.sub("boundary.friction"));
+    MyGlobalFrictionData<dims> frictionInfo(parset.sub("boundary.friction"),
+                                            myGeometry);
 
     // {{{ Initial conditions
     ScalarVector alpha_initial(fineVertexCount);
     alpha_initial =
         std::log(parset.get<double>("boundary.friction.initialState"));
     auto myGlobalNonlinearity = myAssembler.assembleFrictionNonlinearity(
-        frictionalBoundary, frictionData, normalStress);
+        frictionalBoundary, frictionInfo, normalStress);
     myGlobalNonlinearity->updateLogState(alpha_initial);
 
     using LinearFactory = SolverFactory<
@@ -267,8 +269,12 @@ int main(int argc, char *argv[]) {
                          parset.sub("a0.solver"));
     }
     // }}}
-    FrictionWriter<Dune::BitSetVector<1>> writer(frictionData, frictionalNodes);
-    writer.writeInfo(alpha_initial, u_initial, v_initial);
+    FrictionWriter<Dune::BitSetVector<1>> writer(frictionalNodes);
+    {
+      ScalarVector c;
+      myGlobalNonlinearity->coefficientOfFriction(v_initial, c);
+      writer.writeInfo(c, alpha_initial, u_initial, v_initial);
+    }
 
     MyVTKWriter<typename MyAssembler::VertexBasis,
                 typename MyAssembler::CellBasis> const
@@ -310,7 +316,8 @@ int main(int argc, char *argv[]) {
                         u_initial, v_initial, a_initial);
     auto stateUpdater = initStateUpdater<ScalarVector, Vector>(
         parset.get<Config::stateModel>("boundary.friction.stateModel"),
-        alpha_initial, frictionalNodes, frictionData);
+        alpha_initial, frictionalNodes,
+        parset.get<double>("boundary.friction.L"));
 
     Vector v = v_initial;
     ScalarVector alpha(fineVertexCount);
@@ -418,7 +425,11 @@ int main(int argc, char *argv[]) {
       if (printProgress)
         std::cout << std::endl;
 
-      writer.writeInfo(alpha, u, v);
+      {
+        ScalarVector c;
+        myGlobalNonlinearity->coefficientOfFriction(v, c);
+        writer.writeInfo(c, alpha, u, v);
+      }
       iterationWriter << std::endl;
       relaxationWriter << std::endl;
 
diff --git a/src/state.hh b/src/state.hh
index e1d840201fd226fbf8c01caaf3c88572c2f2c139..779640d0a0ff102441e4c653c4dd28a38fd57339 100644
--- a/src/state.hh
+++ b/src/state.hh
@@ -11,14 +11,14 @@
 template <class ScalarVector, class Vector>
 std::shared_ptr<StateUpdater<ScalarVector, Vector>> initStateUpdater(
     Config::stateModel model, ScalarVector const &alpha_initial,
-    Dune::BitSetVector<1> const &frictionalNodes, FrictionData const &fd) {
+    Dune::BitSetVector<1> const &frictionalNodes, double L) {
   switch (model) {
     case Config::Dieterich:
       return std::make_shared<DieterichStateUpdater<ScalarVector, Vector>>(
-          alpha_initial, frictionalNodes, fd.L);
+          alpha_initial, frictionalNodes, L);
     case Config::Ruina:
       return std::make_shared<RuinaStateUpdater<ScalarVector, Vector>>(
-          alpha_initial, frictionalNodes, fd.L);
+          alpha_initial, frictionalNodes, L);
     default:
       assert(false);
   }