diff --git a/src/sand-wedge-data/parset.cfg b/src/sand-wedge-data/parset.cfg
index adb0b1ff4e4b95a95f46298e224d9c6e57b56081..5d2544408dcc6f20ea4cd17fac68ff6e022272df 100644
--- a/src/sand-wedge-data/parset.cfg
+++ b/src/sand-wedge-data/parset.cfg
@@ -36,6 +36,7 @@ a               = 0.030 # [1]      ?
 b               = 0.015 # [1]      ?
 
 [timeSteps]
+refinementTolerance = 1e-5
 number = 100000
 scheme = newmark
 
diff --git a/src/sand-wedge.cc b/src/sand-wedge.cc
index 62890d3ffd5a675dc361e529cc398da5e33122b4..c8a73172eb948dbcb0cbe64f4829b2b011b616f1 100644
--- a/src/sand-wedge.cc
+++ b/src/sand-wedge.cc
@@ -488,17 +488,119 @@ int main(int argc, char *argv[]) {
         parset.get<double>("boundary.friction.V0"));
 
     auto const finalTime = parset.get<double>("problem.finalTime");
-    auto const relativeTau = 1.0 / parset.get<size_t>("timeSteps.number");
     double relativeTime = 0.0;
+
+    double relativeTau = 1e-6; // FIXME (not really important, though)
+
+    using MyStateUpdater = StateUpdater<ScalarVector, Vector>;
+    using MyTimeStepper = TimeSteppingScheme<Vector, Matrix, Function, dims>;
+
+    auto const refinementTolerance =
+        parset.get<double>("timeSteps.refinementTolerance");
+
     size_t timeStep = 1;
-    while (relativeTime < 1.0 - 1e-10) {
 
-      CoupledTimeStepper<NonlinearFactory, StateUpdater<ScalarVector, Vector>,
-                         TimeSteppingScheme<Vector, Matrix, Function, dims>>
+    auto stateUpdaterR1 = stateUpdater->clone();
+    auto velocityUpdaterR1 = velocityUpdater->clone();
+    {
+      CoupledTimeStepper<NonlinearFactory, MyStateUpdater, MyTimeStepper>
       coupledTimeStepper(finalTime, factory, parset, myGlobalFriction,
-                         stateUpdater, velocityUpdater, computeExternalForces);
+                         stateUpdaterR1, velocityUpdaterR1,
+                         computeExternalForces);
       coupledTimeStepper.step(relativeTime, relativeTau);
+    }
+
+    std::shared_ptr<MyStateUpdater> stateUpdaterR2 = nullptr;
+    std::shared_ptr<MyTimeStepper> velocityUpdaterR2 = nullptr;
+
+    while (relativeTime < 1.0 - 1e-10) {
+      bool didCoarsen = false;
+
+      while (true) {
+        stateUpdaterR2 = stateUpdaterR1->clone();
+        velocityUpdaterR2 = velocityUpdaterR1->clone();
+
+        ScalarVector alphaR2;
+        {
+          CoupledTimeStepper<NonlinearFactory, MyStateUpdater, MyTimeStepper>
+          coupledTimeStepper(finalTime, factory, parset, myGlobalFriction,
+                             stateUpdaterR2, velocityUpdaterR2,
+                             computeExternalForces);
+          coupledTimeStepper.step(relativeTime + relativeTau, relativeTau);
+        }
+        stateUpdaterR2->extractAlpha(alphaR2);
+
+        auto stateUpdaterC = stateUpdater->clone();
+        auto velocityUpdaterC = velocityUpdater->clone();
+
+        ScalarVector alphaC;
+        {
+          CoupledTimeStepper<NonlinearFactory, MyStateUpdater, MyTimeStepper>
+          coupledTimeStepper(finalTime, factory, parset, myGlobalFriction,
+                             stateUpdaterC, velocityUpdaterC,
+                             computeExternalForces);
+          coupledTimeStepper.step(relativeTime, 2.0 * relativeTau);
+        }
+        stateUpdaterC->extractAlpha(alphaC);
+
+        auto const coarseningError = stateEnergyNorm.diff(alphaC, alphaR2);
+
+        if (coarseningError < refinementTolerance) {
+          stateUpdaterR2 = nullptr;
+          velocityUpdaterR2 = nullptr;
+
+          stateUpdaterR1 = stateUpdaterC;
+          velocityUpdaterR1 = velocityUpdaterC;
+          relativeTau *= 2.0;
+          didCoarsen = true;
+        } else {
+          break;
+        }
+      }
+
+      if (!didCoarsen) {
+        ScalarVector alphaR1;
+        while (true) {
+          auto stateUpdaterF2 = stateUpdater->clone();
+          auto velocityUpdaterF2 = velocityUpdater->clone();
+
+          std::shared_ptr<MyStateUpdater> stateUpdaterF1;
+          std::shared_ptr<MyTimeStepper> velocityUpdaterF1;
+
+          ScalarVector alphaF2;
+          {
+            CoupledTimeStepper<NonlinearFactory, MyStateUpdater, MyTimeStepper>
+            coupledTimeStepper(finalTime, factory, parset, myGlobalFriction,
+                               stateUpdaterF2, velocityUpdaterF2,
+                               computeExternalForces);
+            coupledTimeStepper.step(relativeTime, relativeTau / 2.0);
+
+            stateUpdaterF1 = stateUpdaterF2->clone();
+            velocityUpdaterF1 = velocityUpdaterF2->clone();
+
+            coupledTimeStepper.step(relativeTime + relativeTau / 2.0,
+                                    relativeTau / 2.0);
+          }
+          stateUpdaterF2->extractAlpha(alphaF2);
+          stateUpdaterR1->extractAlpha(alphaR1);
+          auto const refinementError = stateEnergyNorm.diff(alphaR1, alphaF2);
+
+          if (refinementError < refinementTolerance) {
+            break;
+          } else {
+            stateUpdaterR1 = stateUpdaterF1;
+            velocityUpdaterR1 = velocityUpdaterF1;
+            stateUpdaterR2 = stateUpdaterF2;
+            velocityUpdaterR2 = velocityUpdaterF2;
+            relativeTau /= 2.0;
+          }
+        }
+      }
 
+      stateUpdater = stateUpdaterR1;
+      velocityUpdater = velocityUpdaterR1;
+      stateUpdaterR1 = stateUpdaterR2;
+      velocityUpdaterR1 = velocityUpdaterR2;
       relativeTime += relativeTau;
 
       Vector u, ur, vr;