diff --git a/dune/tectonic/globallaursennonlinearity.hh b/dune/tectonic/globallaursennonlinearity.hh
index c4701b9f00cd172e6237498f56aee406c3e232d3..0b69101c1dd557064d93e4859b279e1ac889a8ad 100644
--- a/dune/tectonic/globallaursennonlinearity.hh
+++ b/dune/tectonic/globallaursennonlinearity.hh
@@ -13,8 +13,11 @@
 #include "nicefunction.hh"
 
 namespace Dune {
-template <int dim, class OuterFunctionType>
-class GlobalLaursenNonlinearity : public GlobalNonlinearity<dim> {
+template <int dim, class OuterFunctionType,
+          class VectorType = BlockVector<FieldVector<double, dim>>,
+          class MatrixType = BCRSMatrix<FieldMatrix<double, dim, dim>>>
+class GlobalLaursenNonlinearity
+    : public GlobalNonlinearity<dim, VectorType, MatrixType> {
 public:
   GlobalLaursenNonlinearity(
       shared_ptr<BlockVector<FieldVector<double, 1>> const> mu,
diff --git a/dune/tectonic/globalnonlinearity.hh b/dune/tectonic/globalnonlinearity.hh
index 3a1bc3739fb37cf286c9c475948d411cae08427f..73042d741c95c8ac8f30c4bca6e31d977f28ea50 100644
--- a/dune/tectonic/globalnonlinearity.hh
+++ b/dune/tectonic/globalnonlinearity.hh
@@ -10,12 +10,22 @@
 #include "localnonlinearity.hh"
 
 namespace Dune {
-template <int dim> class GlobalNonlinearity {
+template <int dim, class VectorType = BlockVector<FieldVector<double, dim>>,
+          class MatrixType = BCRSMatrix<FieldMatrix<double, dim, dim>>>
+class GlobalNonlinearity {
 public:
   /*
     Return a restriction of the outer function to the i'th node.
   */
   virtual shared_ptr<LocalNonlinearity<dim> const> restriction(int i) const = 0;
+
+  virtual void addHessian(const VectorType& v, MatrixType& hessian) const {
+    // TODO: is this correct?
+    for (size_t i = 0; i < v.size(); ++i) {
+      auto res = restriction(i);
+      res->addHessian(v[i], hessian[i][i]);
+    }
+  }
 };
 }
 #endif
diff --git a/dune/tectonic/globalruinanonlinearity.hh b/dune/tectonic/globalruinanonlinearity.hh
index 90e8af367feb0bc683eaaf22050a66a35ee20d32..2ff49ef05284dd1d2efbc98571aafdbe5c0e1471 100644
--- a/dune/tectonic/globalruinanonlinearity.hh
+++ b/dune/tectonic/globalruinanonlinearity.hh
@@ -13,8 +13,10 @@
 #include "nicefunction.hh"
 
 namespace Dune {
-template <int dim>
-class GlobalRuinaNonlinearity : public GlobalNonlinearity<dim> {
+template <int dim, class VectorType = BlockVector<FieldVector<double, dim>>,
+          class MatrixType = BCRSMatrix<FieldMatrix<double, dim, dim>>>
+class GlobalRuinaNonlinearity
+    : public GlobalNonlinearity<dim, VectorType, MatrixType> {
 public:
   GlobalRuinaNonlinearity(
       shared_ptr<BlockVector<FieldVector<double, 1>> const> nodalIntegrals,
diff --git a/dune/tectonic/localnonlinearity.hh b/dune/tectonic/localnonlinearity.hh
index 34e595f0a49970a98cc5f68650b75468afb8df76..3dc5e53b149d2483c169e1fc9cea27ea04f5de90 100644
--- a/dune/tectonic/localnonlinearity.hh
+++ b/dune/tectonic/localnonlinearity.hh
@@ -1,6 +1,7 @@
 #ifndef DUNE_TECTONIC_LOCAL_NONLINEARITY_HH
 #define DUNE_TECTONIC_LOCAL_NONLINEARITY_HH
 
+#include <cmath>
 #include <limits>
 
 #include <dune/common/fvector.hh>
@@ -45,6 +46,38 @@ template <int dimension> class LocalNonlinearity {
     }
   }
 
+  /*
+    H''(|x|) x \otimes x / |x|^2
+    + H'(|x|) [|x|^2 id - x \otimes x] / |x|^3
+
+    For i == j, this is (writing k and using the notation from below)
+
+    h2 * x[k] * x[k] / normX2 + h1 [normX2 - x[k]^2]/normX^3
+    = h2 * x[k] * x[k] / normX2 + h1ox - h1 x[k]^2/normX^3
+    = (h2-h1ox) * x[k] * x[k] / normX2 + h1ox
+
+    For i != j, this is (again, using the notation from below)
+
+    h2 * x[i] * x[j] / normX2 - h1 * x[i] * x[j]/normX^3
+    = (h2 - h1ox) * x[i] * x[j] / normX2
+  */
+  void addHessian(VectorType const &x, MatrixType &A) const {
+    double const normX2 = x.two_norm2();
+    double const normX = sqrt(normX2);
+    double const h1ox = func_->rightDifferential(normX) / normX;
+    double const h2 = func_->second_deriv(normX);
+
+    for (int i = 0; i < dimension; ++i)
+      for (int j = 0; j < i; ++j) {
+        double const entry = (h2 - h1ox) * x[i] * x[j] / normX2;
+        A[i][j] += entry;
+        A[j][i] += entry;
+      }
+
+    for (int k = 0; k < dimension; ++k)
+      A[k][k] += (h2 - h1ox) * x[k] * x[k] / normX2 + h1ox;
+  }
+
   void upperGradient(VectorType const &x, VectorType &ret) const {
     ret = x;
     ret *= func_->rightDifferential(x.two_norm()) / x.two_norm();
diff --git a/dune/tectonic/nicefunction.hh b/dune/tectonic/nicefunction.hh
index 4b926f9c6d116ae259fca2dccf8973120f88dd5e..ed6dc388752cbb74aa59e8916d29dc3867b9a00a 100644
--- a/dune/tectonic/nicefunction.hh
+++ b/dune/tectonic/nicefunction.hh
@@ -14,6 +14,8 @@ class NiceFunction : public VirtualFunction<double, double> {
 
   virtual double leftDifferential(double s) const = 0;
   virtual double rightDifferential(double s) const = 0;
+
+  double virtual second_deriv(double s) const = 0;
 };
 
 class RuinaFunction : public NiceFunction {
@@ -110,6 +112,8 @@ class LinearFunction : public NiceFunction {
 
   double virtual rightDifferential(double s) const { return coefficient; }
 
+  double virtual second_deriv(double) const { return 0; }
+
 private:
   double coefficient;
 };
@@ -145,6 +149,8 @@ class TrivialFunction : public NiceFunction {
   double virtual leftDifferential(double) const { return 0; }
 
   double virtual rightDifferential(double) const { return 0; }
+
+  double virtual second_deriv(double) const { return 0; }
 };
 
 // slope in [n-1,n] is n