From baea9c82587cb3c070be72a468c65c304dae5c31 Mon Sep 17 00:00:00 2001
From: Elias Pipping <elias.pipping@fu-berlin.de>
Date: Sun, 29 Mar 2015 15:26:17 +0200
Subject: [PATCH] [Output]  Use HDF5, change output dramatically

---
 CMakeLists.txt                             |  3 +
 dune/tectonic/globalfriction.hh            |  7 +-
 src/CMakeLists.txt                         | 21 +++--
 src/adaptivetimestepper.cc                 | 97 ++++++++++------------
 src/adaptivetimestepper.hh                 | 27 ++++--
 src/boundary_writer.cc                     | 39 ---------
 src/boundary_writer.hh                     | 27 ------
 src/boundary_writer_tmpl.cc                |  7 --
 src/fixedpointiterator.cc                  |  6 ++
 src/fixedpointiterator.hh                  |  2 +
 src/friction_writer.cc                     | 32 -------
 src/friction_writer.hh                     | 28 -------
 src/friction_writer_tmpl.cc                |  7 --
 src/hdf5-writer.hh                         | 73 ++++++++++++++++
 src/hdf5/frictionalboundary-writer.cc      | 59 +++++++++++++
 src/hdf5/frictionalboundary-writer.hh      | 31 +++++++
 src/hdf5/frictionalboundary-writer_tmpl.cc | 11 +++
 src/hdf5/iteration-writer.cc               | 26 ++++++
 src/hdf5/iteration-writer.hh               | 24 ++++++
 src/hdf5/patchinfo-writer.cc               | 94 +++++++++++++++++++++
 src/hdf5/patchinfo-writer.hh               | 54 ++++++++++++
 src/hdf5/patchinfo-writer_tmpl.cc          | 14 ++++
 src/hdf5/restart-io.cc                     | 49 +++++++++++
 src/hdf5/restart-io.hh                     | 29 +++++++
 src/hdf5/restart-io_tmpl.cc                |  7 ++
 src/hdf5/restrict.hh                       | 23 +++++
 src/hdf5/surface-writer.cc                 | 35 ++++++++
 src/hdf5/surface-writer.hh                 | 27 ++++++
 src/hdf5/surface-writer_tmpl.cc            |  8 ++
 src/hdf5/time-writer.cc                    | 21 +++++
 src/hdf5/time-writer.hh                    | 17 ++++
 src/hdf5/time-writer_tmpl.cc               |  7 ++
 src/program_state.hh                       | 23 +----
 src/sand-wedge-data/mygeometry.cc          |  7 --
 src/sand-wedge-data/mygeometry.hh          |  5 --
 src/sand-wedge-data/special_writer.hh      | 83 ------------------
 src/sand-wedge.cc                          | 86 +++++--------------
 src/updaters.hh                            |  4 +
 38 files changed, 734 insertions(+), 386 deletions(-)
 delete mode 100644 src/boundary_writer.cc
 delete mode 100644 src/boundary_writer.hh
 delete mode 100644 src/boundary_writer_tmpl.cc
 delete mode 100644 src/friction_writer.cc
 delete mode 100644 src/friction_writer.hh
 delete mode 100644 src/friction_writer_tmpl.cc
 create mode 100644 src/hdf5-writer.hh
 create mode 100644 src/hdf5/frictionalboundary-writer.cc
 create mode 100644 src/hdf5/frictionalboundary-writer.hh
 create mode 100644 src/hdf5/frictionalboundary-writer_tmpl.cc
 create mode 100644 src/hdf5/iteration-writer.cc
 create mode 100644 src/hdf5/iteration-writer.hh
 create mode 100644 src/hdf5/patchinfo-writer.cc
 create mode 100644 src/hdf5/patchinfo-writer.hh
 create mode 100644 src/hdf5/patchinfo-writer_tmpl.cc
 create mode 100644 src/hdf5/restart-io.cc
 create mode 100644 src/hdf5/restart-io.hh
 create mode 100644 src/hdf5/restart-io_tmpl.cc
 create mode 100644 src/hdf5/restrict.hh
 create mode 100644 src/hdf5/surface-writer.cc
 create mode 100644 src/hdf5/surface-writer.hh
 create mode 100644 src/hdf5/surface-writer_tmpl.cc
 create mode 100644 src/hdf5/time-writer.cc
 create mode 100644 src/hdf5/time-writer.hh
 create mode 100644 src/hdf5/time-writer_tmpl.cc
 delete mode 100644 src/sand-wedge-data/special_writer.hh

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 653046b1..f8e6fc72 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,9 @@ include(DuneMacros)
 # start a dune project with information from dune.module
 dune_project()
 
+find_package(Boost REQUIRED system filesystem)
+find_package(HDF5 REQUIRED)
+
 add_subdirectory("src")
 add_subdirectory("dune")
 add_subdirectory("doc")
diff --git a/dune/tectonic/globalfriction.hh b/dune/tectonic/globalfriction.hh
index 3fb7c399..78ba058d 100644
--- a/dune/tectonic/globalfriction.hh
+++ b/dune/tectonic/globalfriction.hh
@@ -82,10 +82,11 @@ template <class Matrix, class Vector> class GlobalFriction {
     return res->regularity(x);
   }
 
-  void coefficientOfFriction(Vector const &x, ScalarVector &coefficient) const {
-    coefficient.resize(x.size());
+  ScalarVector coefficientOfFriction(Vector const &x) const {
+    ScalarVector ret(x.size());
     for (size_t i = 0; i < x.size(); ++i)
-      coefficient[i] = restriction(i)->coefficientOfFriction(x[i]);
+      ret[i] = restriction(i)->coefficientOfFriction(x[i]);
+    return ret;
   }
 
   void virtual updateAlpha(ScalarVector const &alpha) = 0;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7da8041f..ad428776 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,11 +1,15 @@
 set(SOURCE_FILES
   adaptivetimestepper.cc
   assemblers.cc
-  boundary_writer.cc
   coupledtimestepper.cc
   enumparser.cc
   fixedpointiterator.cc
-  friction_writer.cc
+  hdf5/frictionalboundary-writer.cc
+  hdf5/iteration-writer.cc
+  hdf5/patchinfo-writer.cc
+  hdf5/restart-io.cc
+  hdf5/surface-writer.cc
+  hdf5/time-writer.cc
   rate.cc
   rate/rateupdater.cc
   sand-wedge.cc
@@ -22,11 +26,6 @@ dune_symlink_to_source_files("sand-wedge-data/parset.cfg")
 dune_symlink_to_source_files("sand-wedge-data/parset-2D.cfg")
 dune_symlink_to_source_files("sand-wedge-data/parset-3D.cfg")
 
-find_package(Boost REQUIRED system filesystem serialization)
-
-# dataio.hh expects this to be set
-set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_BOOST_SERIALIZATION")
-
 foreach(_dim 2 3)
   set(_target sand-wedge-${_dim}D)
   add_executable(${_target} ${SOURCE_FILES})
@@ -36,8 +35,14 @@ foreach(_dim 2 3)
   set_property(TARGET ${_target} APPEND PROPERTY INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS})
 
   target_link_libraries(${_target} ${Boost_FILESYSTEM_LIBRARY})
-  target_link_libraries(${_target} ${Boost_SERIALIZATION_LIBRARY})
   target_link_libraries(${_target} ${Boost_SYSTEM_LIBRARY})
 
+  set_property(TARGET ${_target} APPEND PROPERTY INCLUDE_DIRECTORIES ${HDF5_INCLUDE_DIR})
+  target_link_libraries(${_target} ${HDF5_LIBRARIES})
+  target_link_libraries(${_target} "dl") # FIXME: missing from HDF5_LIBRARIES
+
   set_property(TARGET ${_target} APPEND PROPERTY COMPILE_DEFINITIONS "MY_DIM=${_dim}")
 endforeach()
+
+# Mark UG as required
+find_package(UG REQUIRED)
diff --git a/src/adaptivetimestepper.cc b/src/adaptivetimestepper.cc
index 1be9b399..8059e28e 100644
--- a/src/adaptivetimestepper.cc
+++ b/src/adaptivetimestepper.cc
@@ -4,6 +4,19 @@
 
 #include "adaptivetimestepper.hh"
 
+void IterationRegister::registerCount(FixedPointIterationCounter count) {
+  totalCount += count;
+}
+
+void IterationRegister::registerFinalCount(FixedPointIterationCounter count) {
+  finalCount = count;
+}
+
+void IterationRegister::reset() {
+  totalCount = FixedPointIterationCounter();
+  finalCount = FixedPointIterationCounter();
+}
+
 template <class Factory, class Updaters, class ErrorNorm>
 AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::AdaptiveTimeStepper(
     Factory &factory, Dune::ParameterTree const &parset,
@@ -19,17 +32,9 @@ AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::AdaptiveTimeStepper(
       parset_(parset),
       globalFriction_(globalFriction),
       current_(current),
-      R1_(current_.clone()),
       externalForces_(externalForces),
       mustRefine_(mustRefine),
-      iterationWriter_("iterations", std::fstream::out),
-      errorNorm_(errorNorm) {
-  MyCoupledTimeStepper coupledTimeStepper(finalTime_, factory_, parset_,
-                                          globalFriction_, R1_, errorNorm,
-                                          externalForces_);
-  stepAndReport("R1", coupledTimeStepper, relativeTime_, relativeTau_);
-  iterationWriter_ << std::endl;
-}
+      errorNorm_(errorNorm) {}
 
 template <class Factory, class Updaters, class ErrorNorm>
 bool AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::reachedEnd() {
@@ -39,28 +44,15 @@ bool AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::reachedEnd() {
 template <class Factory, class Updaters, class ErrorNorm>
 bool AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::coarsen() {
   bool didCoarsen = false;
-
   while (relativeTime_ + relativeTau_ < 1.0) {
-    R2_ = R1_.clone();
-    {
-      MyCoupledTimeStepper coupledTimeStepper(finalTime_, factory_, parset_,
-                                              globalFriction_, R2_, errorNorm_,
-                                              externalForces_);
-      stepAndReport("R2", coupledTimeStepper, relativeTime_ + relativeTau_,
-                    relativeTau_);
-    }
-
-    Updaters C = current_.clone();
-    {
-      MyCoupledTimeStepper coupledTimeStepper(finalTime_, factory_, parset_,
-                                              globalFriction_, C, errorNorm_,
-                                              externalForces_);
+    R2_ = { R1_.updaters.clone() };
+    step(R2_, relativeTime_ + relativeTau_, relativeTau_);
 
-      stepAndReport("C", coupledTimeStepper, relativeTime_, 2.0 * relativeTau_);
-    }
+    UpdatersWithCount C{ current_.clone() };
+    step(C, relativeTime_, 2.0 * relativeTau_);
 
-    if (!mustRefine_(C, R2_)) {
-      R2_ = { nullptr, nullptr };
+    if (!mustRefine_(C.updaters, R2_.updaters)) {
+      R2_ = {};
       R1_ = C;
       relativeTau_ *= 2.0;
       didCoarsen = true;
@@ -74,21 +66,12 @@ bool AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::coarsen() {
 template <class Factory, class Updaters, class ErrorNorm>
 void AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::refine() {
   while (true) {
-    Updaters F1 = current_.clone();
-    Updaters F2;
-    {
-      MyCoupledTimeStepper coupledTimeStepper(finalTime_, factory_, parset_,
-                                              globalFriction_, F1, errorNorm_,
-                                              externalForces_);
-      stepAndReport("F1", coupledTimeStepper, relativeTime_,
-                    relativeTau_ / 2.0);
-
-      F2 = F1.clone();
-      stepAndReport("F2", coupledTimeStepper,
-                    relativeTime_ + relativeTau_ / 2.0, relativeTau_ / 2.0);
-    }
+    UpdatersWithCount F1{ current_.clone() };
+    step(F1, relativeTime_, relativeTau_ / 2.0);
+    UpdatersWithCount F2{ F1.updaters.clone() };
+    step(F2, relativeTime_ + relativeTau_ / 2.0, relativeTau_ / 2.0);
 
-    if (!mustRefine_(R1_, F2)) {
+    if (!mustRefine_(R1_.updaters, F2.updaters)) {
       break;
     } else {
       R1_ = F1;
@@ -99,23 +82,33 @@ void AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::refine() {
 }
 
 template <class Factory, class Updaters, class ErrorNorm>
-void AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::advance() {
+IterationRegister AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::advance() {
+  if (R2_.updaters == Updaters()) {
+    R1_ = { current_.clone() };
+    step(R1_, relativeTime_, relativeTau_); // counting again upon restart
+  } else {
+    R1_ = R2_;
+  }
+
+  iterationRegister_.reset();
   if (!coarsen())
     refine();
 
-  iterationWriter_ << std::endl;
-
-  current_ = R1_;
-  R1_ = R2_;
+  current_ = R1_.updaters;
+  iterationRegister_.registerFinalCount(R1_.count);
   relativeTime_ += relativeTau_;
+
+  return iterationRegister_;
 }
 
 template <class Factory, class Updaters, class ErrorNorm>
-void AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::stepAndReport(
-    std::string type, MyCoupledTimeStepper &stepper, double rTime,
-    double rTau) {
-  iterationWriter_ << type << " " << stepper.step(rTime, rTau) << " "
-                   << std::flush;
+void AdaptiveTimeStepper<Factory, Updaters, ErrorNorm>::step(
+    UpdatersWithCount &updatersAndCount, double rTime, double rTau) {
+  updatersAndCount.count =
+      MyCoupledTimeStepper(finalTime_, factory_, parset_, globalFriction_,
+                           updatersAndCount.updaters, errorNorm_,
+                           externalForces_).step(rTime, rTau);
+  iterationRegister_.registerCount(updatersAndCount.count);
 }
 
 #include "adaptivetimestepper_tmpl.cc"
diff --git a/src/adaptivetimestepper.hh b/src/adaptivetimestepper.hh
index 7fa6298c..dcda38d0 100644
--- a/src/adaptivetimestepper.hh
+++ b/src/adaptivetimestepper.hh
@@ -5,8 +5,23 @@
 
 #include "coupledtimestepper.hh"
 
+struct IterationRegister {
+  void registerCount(FixedPointIterationCounter count);
+  void registerFinalCount(FixedPointIterationCounter count);
+
+  void reset();
+
+  FixedPointIterationCounter totalCount;
+  FixedPointIterationCounter finalCount;
+};
+
 template <class Factory, class Updaters, class ErrorNorm>
 class AdaptiveTimeStepper {
+  struct UpdatersWithCount {
+    Updaters updaters;
+    FixedPointIterationCounter count;
+  };
+
   using Vector = typename Factory::Vector;
   using ConvexProblem = typename Factory::ConvexProblem;
   using Nonlinearity = typename ConvexProblem::NonlinearityType;
@@ -25,25 +40,25 @@ class AdaptiveTimeStepper {
   bool reachedEnd();
   bool coarsen();
   void refine();
-  void advance();
+  IterationRegister advance();
 
   double relativeTime_;
   double relativeTau_;
 
 private:
-  void stepAndReport(std::string type, MyCoupledTimeStepper &stepper,
-                     double rTime, double rTau);
+  void step(UpdatersWithCount &updatersWithCount, double rTime, double rTau);
 
   double finalTime_;
   Factory &factory_;
   Dune::ParameterTree const &parset_;
   std::shared_ptr<Nonlinearity> globalFriction_;
   Updaters &current_;
-  Updaters R1_;
-  Updaters R2_;
+  UpdatersWithCount R1_;
+  UpdatersWithCount R2_;
   std::function<void(double, Vector &)> externalForces_;
   std::function<bool(Updaters &, Updaters &)> mustRefine_;
-  std::fstream iterationWriter_;
   ErrorNorm const &errorNorm_;
+
+  IterationRegister iterationRegister_;
 };
 #endif
diff --git a/src/boundary_writer.cc b/src/boundary_writer.cc
deleted file mode 100644
index 82d5e2fb..00000000
--- a/src/boundary_writer.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "boundary_writer.hh"
-#include "tobool.hh"
-
-template <class ScalarVector, class Vector>
-BoundaryWriter<ScalarVector, Vector>::BoundaryWriter(
-    Vector const &vertexCoordinates,
-    Dune::BitSetVector<1> const &_boundaryNodes, std::string prefix,
-    Projector projector)
-    : displacementWriter(prefix + "Displacements", std::fstream::out),
-      velocityWriter(prefix + "Velocities", std::fstream::out),
-      boundaryNodes(_boundaryNodes),
-      projector_(projector) {
-  std::fstream vertexCoordinateWriter(prefix + "Coordinates",
-                                      std::fstream::out);
-  for (size_t i = 0; i < boundaryNodes.size(); ++i)
-    if (toBool(boundaryNodes[i]))
-      vertexCoordinateWriter << vertexCoordinates[i] << std::endl;
-}
-
-template <class ScalarVector, class Vector>
-void BoundaryWriter<ScalarVector, Vector>::writeKinetics(Vector const &u,
-                                                         Vector const &v) {
-  for (size_t i = 0; i < boundaryNodes.size(); ++i) {
-    if (!toBool(boundaryNodes[i]))
-      continue;
-
-    displacementWriter << projector_(u[i]) << " ";
-    velocityWriter << projector_(v[i]) << " ";
-  }
-
-  displacementWriter << std::endl;
-  velocityWriter << std::endl;
-}
-
-#include "boundary_writer_tmpl.cc"
diff --git a/src/boundary_writer.hh b/src/boundary_writer.hh
deleted file mode 100644
index 9f0680f3..00000000
--- a/src/boundary_writer.hh
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef SRC_BOUNDARY_WRITER_HH
-#define SRC_BOUNDARY_WRITER_HH
-
-#include <fstream>
-#include <string>
-
-#include <dune/common/bitsetvector.hh>
-
-template <class ScalarVector, class Vector> class BoundaryWriter {
-protected:
-  using Projector = std::function<double(typename Vector::block_type const &)>;
-
-public:
-  BoundaryWriter(Vector const &vertexCoordinates,
-                 Dune::BitSetVector<1> const &_boundaryNodes,
-                 std::string prefix, Projector projector);
-
-  void writeKinetics(Vector const &u, Vector const &v);
-
-protected:
-  std::fstream displacementWriter;
-  std::fstream velocityWriter;
-  Dune::BitSetVector<1> const &boundaryNodes;
-
-  Projector projector_;
-};
-#endif
diff --git a/src/boundary_writer_tmpl.cc b/src/boundary_writer_tmpl.cc
deleted file mode 100644
index 378584de..00000000
--- a/src/boundary_writer_tmpl.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef MY_DIM
-#error MY_DIM unset
-#endif
-
-#include "explicitvectors.hh"
-
-template class BoundaryWriter<ScalarVector, Vector>;
diff --git a/src/fixedpointiterator.cc b/src/fixedpointiterator.cc
index cff97d96..68dfd4fe 100644
--- a/src/fixedpointiterator.cc
+++ b/src/fixedpointiterator.cc
@@ -13,6 +13,12 @@
 
 #include "fixedpointiterator.hh"
 
+void FixedPointIterationCounter::operator+=(
+    FixedPointIterationCounter const &other) {
+  iterations += other.iterations;
+  multigridIterations += other.multigridIterations;
+}
+
 template <class Factory, class Updaters, class ErrorNorm>
 FixedPointIterator<Factory, Updaters, ErrorNorm>::FixedPointIterator(
     Factory &factory, Dune::ParameterTree const &parset,
diff --git a/src/fixedpointiterator.hh b/src/fixedpointiterator.hh
index 9a4a336f..4833b496 100644
--- a/src/fixedpointiterator.hh
+++ b/src/fixedpointiterator.hh
@@ -11,6 +11,8 @@
 struct FixedPointIterationCounter {
   size_t iterations;
   size_t multigridIterations;
+
+  void operator+=(FixedPointIterationCounter const &other);
 };
 
 std::ostream &operator<<(std::ostream &stream,
diff --git a/src/friction_writer.cc b/src/friction_writer.cc
deleted file mode 100644
index ce71828d..00000000
--- a/src/friction_writer.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "friction_writer.hh"
-#include "tobool.hh"
-
-template <class ScalarVector, class Vector>
-FrictionWriter<ScalarVector, Vector>::FrictionWriter(
-    Vector const &vertexCoordinates,
-    Dune::BitSetVector<1> const &_boundaryNodes, std::string prefix,
-    typename BW::Projector projector)
-    : BW(vertexCoordinates, _boundaryNodes, prefix, projector),
-      coefficientWriter(prefix + "Coefficients", std::fstream::out),
-      stateWriter(prefix + "Alpha", std::fstream::out) {}
-
-template <class ScalarVector, class Vector>
-void FrictionWriter<ScalarVector, Vector>::writeOther(
-    ScalarVector const &coefficient, ScalarVector const &alpha) {
-  for (size_t i = 0; i < boundaryNodes.size(); ++i) {
-    if (!toBool(boundaryNodes[i]))
-      continue;
-
-    coefficientWriter << coefficient[i] << " ";
-    stateWriter << alpha[i] << " ";
-  }
-
-  stateWriter << std::endl;
-  coefficientWriter << std::endl;
-}
-
-#include "friction_writer_tmpl.cc"
diff --git a/src/friction_writer.hh b/src/friction_writer.hh
deleted file mode 100644
index 957f5f9f..00000000
--- a/src/friction_writer.hh
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef SRC_FRICTION_WRITER_HH
-#define SRC_FRICTION_WRITER_HH
-
-#include <fstream>
-#include <string>
-
-#include <dune/common/bitsetvector.hh>
-
-#include "boundary_writer.hh"
-
-template <class ScalarVector, class Vector>
-class FrictionWriter : public BoundaryWriter<ScalarVector, Vector> {
-  using BW = BoundaryWriter<ScalarVector, Vector>;
-
-public:
-  FrictionWriter(Vector const &vertexCoordinates,
-                 Dune::BitSetVector<1> const &_boundaryNodes,
-                 std::string prefix, typename BW::Projector projector);
-
-  void writeOther(ScalarVector const &coefficient, ScalarVector const &alpha);
-
-private:
-  std::fstream coefficientWriter;
-  std::fstream stateWriter;
-  using BW::boundaryNodes;
-};
-
-#endif
diff --git a/src/friction_writer_tmpl.cc b/src/friction_writer_tmpl.cc
deleted file mode 100644
index 5ac5e7d5..00000000
--- a/src/friction_writer_tmpl.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef MY_DIM
-#error MY_DIM unset
-#endif
-
-#include "explicitvectors.hh"
-
-template class FrictionWriter<ScalarVector, Vector>;
diff --git a/src/hdf5-writer.hh b/src/hdf5-writer.hh
new file mode 100644
index 00000000..e4e5358e
--- /dev/null
+++ b/src/hdf5-writer.hh
@@ -0,0 +1,73 @@
+#ifndef SRC_HDF5_WRITER_HH
+#define SRC_HDF5_WRITER_HH
+
+#include <dune/fufem/functions/basisgridfunction.hh>
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+#include <dune/fufem/hdf5/hdf5file.hh>
+
+#include "hdf5/frictionalboundary-writer.hh"
+#include "hdf5/iteration-writer.hh"
+#include "hdf5/patchinfo-writer.hh"
+#include "hdf5/restart-io.hh"
+#include "hdf5/surface-writer.hh"
+#include "hdf5/time-writer.hh"
+
+template <class ProgramState, class VertexBasis, class GridView>
+class HDF5Writer {
+private:
+  using Vector = typename ProgramState::Vector;
+  using Patch = BoundaryPatch<GridView>;
+
+  using LocalVector = typename Vector::block_type;
+
+public:
+  HDF5Writer(HDF5Grouplike &file, Vector const &vertexCoordinates,
+             VertexBasis const &vertexBasis, Patch const &surface,
+             Patch const &frictionalBoundary,
+             ConvexPolyhedron<LocalVector> const &weakPatch)
+      : file_(file),
+        iterationWriter_(file_),
+        timeWriter_(file_),
+#if MY_DIM == 3
+        patchInfoWriter_(file_, vertexBasis, frictionalBoundary, weakPatch),
+#endif
+        surfaceWriter_(file_, vertexCoordinates, surface),
+        frictionalBoundaryWriter_(file_, vertexCoordinates, frictionalBoundary),
+        restartWriter_(file_, vertexCoordinates.size()) {
+  }
+
+  template <class Friction>
+  void reportSolution(ProgramState const &programState,
+                      // for the friction coefficient
+                      Friction &friction) {
+    timeWriter_.write(programState);
+#if MY_DIM == 3
+    patchInfoWriter_.write(programState);
+#endif
+    surfaceWriter_.write(programState);
+    frictionalBoundaryWriter_.write(programState, friction);
+  }
+
+  void reportIterations(ProgramState const &programState,
+                        IterationRegister const &iterationCount) {
+    iterationWriter_.write(programState.timeStep, iterationCount);
+  }
+
+  void writeRestart(ProgramState const &programState) {
+    restartWriter_.write(programState);
+  }
+
+private:
+  HDF5Grouplike &file_;
+
+  IterationWriter iterationWriter_;
+  TimeWriter<ProgramState> timeWriter_;
+#if MY_DIM == 3
+  PatchInfoWriter<ProgramState, VertexBasis, GridView> patchInfoWriter_;
+#endif
+  SurfaceWriter<ProgramState, GridView> surfaceWriter_;
+  FrictionalBoundaryWriter<ProgramState, GridView> frictionalBoundaryWriter_;
+
+  RestartIO<ProgramState> restartWriter_;
+};
+#endif
diff --git a/src/hdf5/frictionalboundary-writer.cc b/src/hdf5/frictionalboundary-writer.cc
new file mode 100644
index 00000000..b98290df
--- /dev/null
+++ b/src/hdf5/frictionalboundary-writer.cc
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "frictionalboundary-writer.hh"
+#include "restrict.hh"
+
+template <class ProgramState, class GridView>
+FrictionalBoundaryWriter<ProgramState, GridView>::FrictionalBoundaryWriter(
+    HDF5Grouplike &file, Vector const &vertexCoordinates,
+    Patch const &frictionalBoundary)
+    : group_(file, "frictionalBoundary"),
+      frictionalBoundary_(frictionalBoundary),
+      frictionalBoundaryDisplacementWriter_(group_, "displacement",
+                                            frictionalBoundary.numVertices(),
+                                            Vector::block_type::dimension),
+      frictionalBoundaryVelocityWriter_(group_, "velocity",
+                                        frictionalBoundary.numVertices(),
+                                        Vector::block_type::dimension),
+      frictionalBoundaryStateWriter_(group_, "state",
+                                     frictionalBoundary.numVertices()),
+      frictionalBoundaryCoefficientWriter_(group_, "coefficient",
+                                           frictionalBoundary.numVertices()) {
+  auto const frictionalBoundaryCoordinates =
+      restrictToSurface(vertexCoordinates, frictionalBoundary);
+  HDF5SingletonWriter<2> frictionalBoundaryCoordinateWriter(
+      group_, "coordinates", frictionalBoundaryCoordinates.size(),
+      Vector::block_type::dimension);
+  setEntry(frictionalBoundaryCoordinateWriter, frictionalBoundaryCoordinates);
+}
+
+template <class ProgramState, class GridView>
+template <class Friction>
+void FrictionalBoundaryWriter<ProgramState, GridView>::write(
+    ProgramState const &programState, Friction &friction) {
+  auto const frictionalBoundaryDisplacements =
+      restrictToSurface(programState.u, frictionalBoundary_);
+  addEntry(frictionalBoundaryDisplacementWriter_, programState.timeStep,
+           frictionalBoundaryDisplacements);
+
+  auto const frictionalBoundaryVelocities =
+      restrictToSurface(programState.v, frictionalBoundary_);
+  addEntry(frictionalBoundaryVelocityWriter_, programState.timeStep,
+           frictionalBoundaryVelocities);
+
+  auto const frictionalBoundaryStates =
+      restrictToSurface(programState.alpha, frictionalBoundary_);
+  addEntry(frictionalBoundaryStateWriter_, programState.timeStep,
+           frictionalBoundaryStates);
+
+  friction.updateAlpha(programState.alpha);
+  auto const c = friction.coefficientOfFriction(programState.v);
+  auto const frictionalBoundaryCoefficient =
+      restrictToSurface(c, frictionalBoundary_);
+  addEntry(frictionalBoundaryCoefficientWriter_, programState.timeStep,
+           frictionalBoundaryCoefficient);
+}
+
+#include "frictionalboundary-writer_tmpl.cc"
diff --git a/src/hdf5/frictionalboundary-writer.hh b/src/hdf5/frictionalboundary-writer.hh
new file mode 100644
index 00000000..772b82df
--- /dev/null
+++ b/src/hdf5/frictionalboundary-writer.hh
@@ -0,0 +1,31 @@
+#ifndef SRC_HDF_FRICTIONALBOUNDARY_WRITER_HH
+#define SRC_HDF_FRICTIONALBOUNDARY_WRITER_HH
+
+#include <dune/fufem/boundarypatch.hh>
+
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+#include <dune/fufem/hdf5/hdf5-singleton-writer.hh>
+
+template <class ProgramState, class GridView> class FrictionalBoundaryWriter {
+  using ScalarVector = typename ProgramState::ScalarVector;
+  using Vector = typename ProgramState::Vector;
+  using Patch = BoundaryPatch<GridView>;
+
+public:
+  FrictionalBoundaryWriter(HDF5Grouplike &file, Vector const &vertexCoordinates,
+                           Patch const &frictionalBoundary);
+
+  template <class Friction>
+  void write(ProgramState const &programState, Friction &friction);
+
+private:
+  HDF5Group group_;
+
+  Patch const &frictionalBoundary_;
+
+  HDF5SequenceIO<2> frictionalBoundaryDisplacementWriter_;
+  HDF5SequenceIO<2> frictionalBoundaryVelocityWriter_;
+  HDF5SequenceIO<1> frictionalBoundaryStateWriter_;
+  HDF5SequenceIO<1> frictionalBoundaryCoefficientWriter_;
+};
+#endif
diff --git a/src/hdf5/frictionalboundary-writer_tmpl.cc b/src/hdf5/frictionalboundary-writer_tmpl.cc
new file mode 100644
index 00000000..e39ffe0f
--- /dev/null
+++ b/src/hdf5/frictionalboundary-writer_tmpl.cc
@@ -0,0 +1,11 @@
+#include "../explicitvectors.hh"
+#include "../explicitgrid.hh"
+
+#include "../program_state.hh"
+
+using MyProgramState = ProgramState<Vector, ScalarVector>;
+using MyFriction = GlobalFriction<Matrix, Vector>;
+
+template class FrictionalBoundaryWriter<MyProgramState, GridView>;
+template void FrictionalBoundaryWriter<MyProgramState, GridView>::write(
+    MyProgramState const &programState, MyFriction &friction);
diff --git a/src/hdf5/iteration-writer.cc b/src/hdf5/iteration-writer.cc
new file mode 100644
index 00000000..e48f091b
--- /dev/null
+++ b/src/hdf5/iteration-writer.cc
@@ -0,0 +1,26 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iteration-writer.hh"
+
+IterationWriter::IterationWriter(HDF5Grouplike &file)
+    : group_(file, "iterations"),
+      fpiSubGroup_(group_, "fixedPoint"),
+      mgSubGroup_(group_, "multiGrid"),
+      finalMGIterationWriter_(mgSubGroup_, "final"),
+      finalFPIIterationWriter_(fpiSubGroup_, "final"),
+      totalMGIterationWriter_(mgSubGroup_, "total"),
+      totalFPIIterationWriter_(fpiSubGroup_, "total") {}
+
+void IterationWriter::write(size_t timeStep,
+                            IterationRegister const &iterationCount) {
+  addEntry(finalMGIterationWriter_, timeStep,
+           iterationCount.finalCount.multigridIterations);
+  addEntry(finalFPIIterationWriter_, timeStep,
+           iterationCount.finalCount.iterations);
+  addEntry(totalMGIterationWriter_, timeStep,
+           iterationCount.totalCount.multigridIterations);
+  addEntry(totalFPIIterationWriter_, timeStep,
+           iterationCount.totalCount.iterations);
+}
diff --git a/src/hdf5/iteration-writer.hh b/src/hdf5/iteration-writer.hh
new file mode 100644
index 00000000..cdcd80d3
--- /dev/null
+++ b/src/hdf5/iteration-writer.hh
@@ -0,0 +1,24 @@
+#ifndef SRC_HDF_ITERATION_WRITER_HH
+#define SRC_HDF_ITERATION_WRITER_HH
+
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+
+#include "../adaptivetimestepper.hh"
+
+class IterationWriter {
+public:
+  IterationWriter(HDF5Grouplike &file);
+
+  void write(size_t timeStep, IterationRegister const &iterationCount);
+
+private:
+  HDF5Group group_;
+  HDF5Group fpiSubGroup_;
+  HDF5Group mgSubGroup_;
+
+  HDF5SequenceIO<0, size_t> finalMGIterationWriter_;
+  HDF5SequenceIO<0, size_t> finalFPIIterationWriter_;
+  HDF5SequenceIO<0, size_t> totalMGIterationWriter_;
+  HDF5SequenceIO<0, size_t> totalFPIIterationWriter_;
+};
+#endif
diff --git a/src/hdf5/patchinfo-writer.cc b/src/hdf5/patchinfo-writer.cc
new file mode 100644
index 00000000..f0195b1a
--- /dev/null
+++ b/src/hdf5/patchinfo-writer.cc
@@ -0,0 +1,94 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/fufem/grid/hierarchic-approximation.hh>
+
+#include "patchinfo-writer.hh"
+
+template <class LocalVector, class GridView>
+GridEvaluator<LocalVector, GridView>::GridEvaluator(
+    ConvexPolyhedron<LocalVector> const &weakPatch, GridView const &gridView) {
+  double const bufferWidth = 0.05 * MyGeometry::lengthScale;
+  auto const xminmax = std::minmax_element(
+      weakPatch.vertices.begin(), weakPatch.vertices.end(),
+      [](LocalVector const &a, LocalVector const &b) { return a[0] < b[0]; });
+  double const xmin = (*xminmax.first)[0] - bufferWidth;
+  double const xspan = (*xminmax.second)[0] + bufferWidth - xmin;
+
+  double const zmin = -MyGeometry::depth / 2.0;
+  double const zspan = MyGeometry::depth;
+
+  double spatialResolution = 0.01 * MyGeometry::lengthScale;
+  size_t const xsteps = std::round(xspan / spatialResolution);
+  size_t const zsteps = std::round(zspan / spatialResolution);
+
+  xCoordinates.resize(xsteps + 1);
+  for (size_t xi = 0; xi <= xsteps; xi++)
+    xCoordinates[xi] = xmin + xi * xspan / xsteps;
+  zCoordinates.resize(zsteps + 1);
+  for (size_t zi = 0; zi <= zsteps; zi++)
+    zCoordinates[zi] = zmin + zi * zspan / zsteps;
+
+  HierarchicApproximation<typename GridView::Grid, GridView> const
+      hApproximation(gridView.grid(), gridView, 1e-6 * MyGeometry::lengthScale);
+
+  LocalVector global(0);
+  localInfo.resize(xsteps + 1);
+  for (size_t xi = 0; xi < xCoordinates.size(); ++xi) {
+    localInfo[xi].resize(zsteps + 1);
+    for (size_t zi = 0; zi < zCoordinates.size(); ++zi) {
+      global[0] = xCoordinates[xi];
+      global[2] = zCoordinates[zi];
+      auto const element = hApproximation.findEntity(global);
+      localInfo[xi][zi] =
+          std::make_pair(element, element->geometry().local(global));
+    }
+  }
+}
+
+template <class LocalVector, class GridView>
+template <class Function>
+Dune::Matrix<typename Function::RangeType>
+GridEvaluator<LocalVector, GridView>::evaluate(Function const &f) const {
+  Dune::Matrix<typename Function::RangeType> ret(xCoordinates.size(),
+                                                 zCoordinates.size());
+  for (size_t xi = 0; xi < localInfo.size(); ++xi) {
+    auto const &localInfoX = localInfo[xi];
+    for (size_t zi = 0; zi < localInfoX.size(); ++zi) {
+      auto const &localInfoXZ = localInfoX[zi];
+      f.evaluateLocal(localInfoXZ.first, localInfoXZ.second, ret[xi][zi]);
+    }
+  }
+  return ret;
+}
+
+template <class ProgramState, class VertexBasis, class GridView>
+PatchInfoWriter<ProgramState, VertexBasis, GridView>::PatchInfoWriter(
+    HDF5Grouplike &file, VertexBasis const &vertexBasis,
+    Patch const &frictionalBoundary,
+    ConvexPolyhedron<LocalVector> const &weakPatch)
+    : group_(file, "weakPatchGrid"),
+      vertexBasis_(vertexBasis),
+      gridEvaluator_(weakPatch, frictionalBoundary.gridView()),
+      weakPatchGridVelocityWriter_(
+          group_, "velocity", gridEvaluator_.xCoordinates.size(),
+          gridEvaluator_.zCoordinates.size(), Vector::block_type::dimension) {
+  HDF5SingletonWriter<1> weakPatchGridXCoordinateWriter(
+      group_, "xCoordinates", gridEvaluator_.xCoordinates.size());
+  setEntry(weakPatchGridXCoordinateWriter, gridEvaluator_.xCoordinates);
+
+  HDF5SingletonWriter<1> weakPatchGridZCoordinateWriter(
+      group_, "zCoordinates", gridEvaluator_.zCoordinates.size());
+  setEntry(weakPatchGridZCoordinateWriter, gridEvaluator_.zCoordinates);
+}
+
+template <class ProgramState, class VertexBasis, class GridView>
+void PatchInfoWriter<ProgramState, VertexBasis, GridView>::write(
+    ProgramState const &programState) {
+  BasisGridFunction<VertexBasis, Vector> velocity(vertexBasis_, programState.v);
+  auto const gridVelocity = gridEvaluator_.evaluate(velocity);
+  addEntry(weakPatchGridVelocityWriter_, programState.timeStep, gridVelocity);
+}
+
+#include "patchinfo-writer_tmpl.cc"
diff --git a/src/hdf5/patchinfo-writer.hh b/src/hdf5/patchinfo-writer.hh
new file mode 100644
index 00000000..c091f449
--- /dev/null
+++ b/src/hdf5/patchinfo-writer.hh
@@ -0,0 +1,54 @@
+#ifndef SRC_HDF_PATCHINFO_WRITER_HH
+#define SRC_HDF_PATCHINFO_WRITER_HH
+
+#include <dune/istl/matrix.hh>
+
+#include <dune/fufem/boundarypatch.hh>
+#include <dune/fufem/functions/basisgridfunction.hh>
+#include <dune/fufem/geometry/convexpolyhedron.hh>
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+#include <dune/fufem/hdf5/hdf5-singleton-writer.hh>
+
+#include "../sand-wedge-data/mygeometry.hh"
+
+template <class LocalVector, class GridView> class GridEvaluator {
+  using Element = typename GridView::Grid::template Codim<0>::Entity;
+  using ElementPointer =
+      typename GridView::Grid::template Codim<0>::EntityPointer;
+
+public:
+  GridEvaluator(ConvexPolyhedron<LocalVector> const &weakPatch,
+                GridView const &gridView);
+
+  template <class Function>
+  Dune::Matrix<typename Function::RangeType> evaluate(Function const &f) const;
+
+  Dune::BlockVector<Dune::FieldVector<double, 1>> xCoordinates;
+  Dune::BlockVector<Dune::FieldVector<double, 1>> zCoordinates;
+
+private:
+  std::vector<std::vector<std::pair<ElementPointer, LocalVector>>> localInfo;
+};
+
+template <class ProgramState, class VertexBasis, class GridView>
+class PatchInfoWriter {
+  using Vector = typename ProgramState::Vector;
+  using LocalVector = typename Vector::block_type;
+  using Patch = BoundaryPatch<GridView>;
+
+public:
+  PatchInfoWriter(HDF5Grouplike &file, VertexBasis const &vertexBasis,
+                  Patch const &frictionalBoundary,
+                  ConvexPolyhedron<LocalVector> const &weakPatch);
+
+  void write(ProgramState const &programState);
+
+private:
+  HDF5Group group_;
+
+  VertexBasis const &vertexBasis_;
+
+  GridEvaluator<LocalVector, GridView> const gridEvaluator_;
+  HDF5SequenceIO<3> weakPatchGridVelocityWriter_;
+};
+#endif
diff --git a/src/hdf5/patchinfo-writer_tmpl.cc b/src/hdf5/patchinfo-writer_tmpl.cc
new file mode 100644
index 00000000..ef346e10
--- /dev/null
+++ b/src/hdf5/patchinfo-writer_tmpl.cc
@@ -0,0 +1,14 @@
+#include "../explicitvectors.hh"
+#include "../explicitgrid.hh"
+
+#include "../program_state.hh"
+
+using MyProgramState = ProgramState<Vector, ScalarVector>;
+using P1Basis = P1NodalBasis<GridView, double>;
+using MyFunction = BasisGridFunction<P1Basis, Vector>;
+
+template class GridEvaluator<LocalVector, GridView>;
+template Dune::Matrix<typename MyFunction::RangeType>
+GridEvaluator<LocalVector, GridView>::evaluate(MyFunction const &f) const;
+
+template class PatchInfoWriter<MyProgramState, P1Basis, GridView>;
diff --git a/src/hdf5/restart-io.cc b/src/hdf5/restart-io.cc
new file mode 100644
index 00000000..c7fc7422
--- /dev/null
+++ b/src/hdf5/restart-io.cc
@@ -0,0 +1,49 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "restart-io.hh"
+
+template <class ProgramState>
+RestartIO<ProgramState>::RestartIO(HDF5Grouplike &file, size_t vertexCount)
+    : group_(file, "restarts"),
+      displacementWriter_(group_, "displacement", vertexCount,
+                          Vector::block_type::dimension),
+      velocityWriter_(group_, "velocity", vertexCount,
+                      Vector::block_type::dimension),
+      accelerationWriter_(group_, "acceleration", vertexCount,
+                          Vector::block_type::dimension),
+      stateWriter_(group_, "state", vertexCount),
+      weightedNormalStressWriter_(group_, "weightedNormalStress", vertexCount),
+      relativeTimeWriter_(group_, "relativeTime"),
+      relativeTimeIncrementWriter_(group_, "relativeTimeIncrement") {}
+
+template <class ProgramState>
+void RestartIO<ProgramState>::write(ProgramState const &programState) {
+  addEntry(displacementWriter_, programState.timeStep, programState.u);
+  addEntry(velocityWriter_, programState.timeStep, programState.v);
+  addEntry(accelerationWriter_, programState.timeStep, programState.u);
+  addEntry(stateWriter_, programState.timeStep, programState.alpha);
+  addEntry(weightedNormalStressWriter_, programState.timeStep,
+           programState.weightedNormalStress);
+  addEntry(relativeTimeWriter_, programState.timeStep,
+           programState.relativeTime);
+  addEntry(relativeTimeIncrementWriter_, programState.timeStep,
+           programState.relativeTau);
+}
+
+template <class ProgramState>
+void RestartIO<ProgramState>::read(size_t timeStep,
+                                   ProgramState &programState) {
+  programState.timeStep = timeStep;
+  readEntry(displacementWriter_, timeStep, programState.u);
+  readEntry(velocityWriter_, timeStep, programState.v);
+  readEntry(accelerationWriter_, timeStep, programState.u);
+  readEntry(stateWriter_, timeStep, programState.alpha);
+  readEntry(weightedNormalStressWriter_, timeStep,
+            programState.weightedNormalStress);
+  readEntry(relativeTimeWriter_, timeStep, programState.relativeTime);
+  readEntry(relativeTimeIncrementWriter_, timeStep, programState.relativeTau);
+}
+
+#include "restart-io_tmpl.cc"
diff --git a/src/hdf5/restart-io.hh b/src/hdf5/restart-io.hh
new file mode 100644
index 00000000..c42b1022
--- /dev/null
+++ b/src/hdf5/restart-io.hh
@@ -0,0 +1,29 @@
+#ifndef SRC_HDF_RESTART_HDF_HH
+#define SRC_HDF_RESTART_HDF_HH
+
+#include <dune/fufem/hdf5/hdf5file.hh>
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+
+template <class ProgramState> class RestartIO {
+  using ScalarVector = typename ProgramState::ScalarVector;
+  using Vector = typename ProgramState::Vector;
+
+public:
+  RestartIO(HDF5Grouplike &file, size_t vertexCount);
+
+  void write(ProgramState const &programState);
+
+  void read(size_t timeStep, ProgramState &programState);
+
+private:
+  HDF5Group group_;
+
+  HDF5SequenceIO<2> displacementWriter_;
+  HDF5SequenceIO<2> velocityWriter_;
+  HDF5SequenceIO<2> accelerationWriter_;
+  HDF5SequenceIO<1> stateWriter_;
+  HDF5SequenceIO<1> weightedNormalStressWriter_;
+  HDF5SequenceIO<0> relativeTimeWriter_;
+  HDF5SequenceIO<0> relativeTimeIncrementWriter_;
+};
+#endif
diff --git a/src/hdf5/restart-io_tmpl.cc b/src/hdf5/restart-io_tmpl.cc
new file mode 100644
index 00000000..da72bb42
--- /dev/null
+++ b/src/hdf5/restart-io_tmpl.cc
@@ -0,0 +1,7 @@
+#include "../explicitvectors.hh"
+
+#include "../program_state.hh"
+
+using MyProgramState = ProgramState<Vector, ScalarVector>;
+
+template class RestartIO<MyProgramState>;
diff --git a/src/hdf5/restrict.hh b/src/hdf5/restrict.hh
new file mode 100644
index 00000000..497c5890
--- /dev/null
+++ b/src/hdf5/restrict.hh
@@ -0,0 +1,23 @@
+#ifndef SRC_HDF5_RESTRICT_HH
+#define SRC_HDF5_RESTRICT_HH
+
+#include <cassert>
+
+#include <dune/common/bitsetvector.hh>
+
+#include "../tobool.hh"
+
+template <class Vector, class Patch>
+Vector restrictToSurface(Vector const &v1, Patch const &patch) {
+  auto const &vertices = *patch.getVertices();
+  assert(vertices.size() == v1.size());
+
+  Vector ret(vertices.count());
+  auto target = ret.begin();
+  for (size_t i = 0; i < v1.size(); ++i)
+    if (toBool(vertices[i]))
+      *(target++) = v1[i];
+  assert(target == ret.end());
+  return ret;
+}
+#endif
diff --git a/src/hdf5/surface-writer.cc b/src/hdf5/surface-writer.cc
new file mode 100644
index 00000000..51b9508a
--- /dev/null
+++ b/src/hdf5/surface-writer.cc
@@ -0,0 +1,35 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "restrict.hh"
+#include "surface-writer.hh"
+
+template <class ProgramState, class GridView>
+SurfaceWriter<ProgramState, GridView>::SurfaceWriter(
+    HDF5Grouplike &file, Vector const &vertexCoordinates, Patch const &surface)
+    : group_(file, "surface"),
+      surface_(surface),
+      surfaceDisplacementWriter_(group_, "displacement", surface.numVertices(),
+                                 Vector::block_type::dimension),
+      surfaceVelocityWriter_(group_, "velocity", surface.numVertices(),
+                             Vector::block_type::dimension) {
+  auto const surfaceCoordinates = restrictToSurface(vertexCoordinates, surface);
+  HDF5SingletonWriter<2> surfaceCoordinateWriter(group_, "coordinates",
+                                                 surfaceCoordinates.size(),
+                                                 Vector::block_type::dimension);
+  setEntry(surfaceCoordinateWriter, surfaceCoordinates);
+}
+
+template <class ProgramState, class GridView>
+void SurfaceWriter<ProgramState, GridView>::write(
+    ProgramState const &programState) {
+  auto const surfaceDisplacements = restrictToSurface(programState.u, surface_);
+  addEntry(surfaceDisplacementWriter_, programState.timeStep,
+           surfaceDisplacements);
+
+  auto const surfaceVelocities = restrictToSurface(programState.v, surface_);
+  addEntry(surfaceVelocityWriter_, programState.timeStep, surfaceVelocities);
+}
+
+#include "surface-writer_tmpl.cc"
diff --git a/src/hdf5/surface-writer.hh b/src/hdf5/surface-writer.hh
new file mode 100644
index 00000000..7ac92a4c
--- /dev/null
+++ b/src/hdf5/surface-writer.hh
@@ -0,0 +1,27 @@
+#ifndef SRC_HDF_SURFACE_WRITER_HH
+#define SRC_HDF_SURFACE_WRITER_HH
+
+#include <dune/fufem/boundarypatch.hh>
+
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+#include <dune/fufem/hdf5/hdf5-singleton-writer.hh>
+
+template <class ProgramState, class GridView> class SurfaceWriter {
+  using Vector = typename ProgramState::Vector;
+  using Patch = BoundaryPatch<GridView>;
+
+public:
+  SurfaceWriter(HDF5Grouplike &file, Vector const &vertexCoordinates,
+                Patch const &surface);
+
+  void write(ProgramState const &programState);
+
+private:
+  HDF5Group group_;
+
+  Patch const &surface_;
+
+  HDF5SequenceIO<2> surfaceDisplacementWriter_;
+  HDF5SequenceIO<2> surfaceVelocityWriter_;
+};
+#endif
diff --git a/src/hdf5/surface-writer_tmpl.cc b/src/hdf5/surface-writer_tmpl.cc
new file mode 100644
index 00000000..e4570aa7
--- /dev/null
+++ b/src/hdf5/surface-writer_tmpl.cc
@@ -0,0 +1,8 @@
+#include "../explicitvectors.hh"
+#include "../explicitgrid.hh"
+
+#include "../program_state.hh"
+
+using MyProgramState = ProgramState<Vector, ScalarVector>;
+
+template class SurfaceWriter<MyProgramState, GridView>;
diff --git a/src/hdf5/time-writer.cc b/src/hdf5/time-writer.cc
new file mode 100644
index 00000000..1c141946
--- /dev/null
+++ b/src/hdf5/time-writer.cc
@@ -0,0 +1,21 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "time-writer.hh"
+
+template <class ProgramState>
+TimeWriter<ProgramState>::TimeWriter(HDF5Grouplike &file)
+    : file_(file),
+      relativeTimeWriter_(file_, "relativeTime"),
+      relativeTimeIncrementWriter_(file_, "relativeTimeIncrement") {}
+
+template <class ProgramState>
+void TimeWriter<ProgramState>::write(ProgramState const &programState) {
+  addEntry(relativeTimeWriter_, programState.timeStep,
+           programState.relativeTime);
+  addEntry(relativeTimeIncrementWriter_, programState.timeStep,
+           programState.relativeTau);
+}
+
+#include "time-writer_tmpl.cc"
diff --git a/src/hdf5/time-writer.hh b/src/hdf5/time-writer.hh
new file mode 100644
index 00000000..e8f72417
--- /dev/null
+++ b/src/hdf5/time-writer.hh
@@ -0,0 +1,17 @@
+#ifndef SRC_HDF_TIME_WRITER_HH
+#define SRC_HDF_TIME_WRITER_HH
+
+#include <dune/fufem/hdf5/hdf5file.hh>
+#include <dune/fufem/hdf5/hdf5-sequence-io.hh>
+
+template <class ProgramState> class TimeWriter {
+public:
+  TimeWriter(HDF5Grouplike &file);
+  void write(ProgramState const &programState);
+
+private:
+  HDF5Grouplike &file_;
+  HDF5SequenceIO<0> relativeTimeWriter_;
+  HDF5SequenceIO<0> relativeTimeIncrementWriter_;
+};
+#endif
diff --git a/src/hdf5/time-writer_tmpl.cc b/src/hdf5/time-writer_tmpl.cc
new file mode 100644
index 00000000..7b14edeb
--- /dev/null
+++ b/src/hdf5/time-writer_tmpl.cc
@@ -0,0 +1,7 @@
+#include "../explicitvectors.hh"
+
+#include "../program_state.hh"
+
+using MyProgramState = ProgramState<Vector, ScalarVector>;
+
+template class TimeWriter<MyProgramState>;
diff --git a/src/program_state.hh b/src/program_state.hh
index c8b02248..a1734168 100644
--- a/src/program_state.hh
+++ b/src/program_state.hh
@@ -4,7 +4,6 @@
 #include <dune/common/parametertree.hh>
 
 #include <dune/fufem/boundarypatch.hh>
-#include <dune/fufem/dunedataio.hh>
 
 #include <dune/tectonic/body.hh>
 
@@ -12,8 +11,11 @@
 #include "matrices.hh"
 #include "solverfactory.hh"
 
-template <class Vector, class ScalarVector> class ProgramState {
+template <class VectorTEMPLATE, class ScalarVectorTEMPLATE> class ProgramState {
 public:
+  using Vector = VectorTEMPLATE;
+  using ScalarVector = ScalarVectorTEMPLATE;
+
   ProgramState(int leafVertexCount)
       : u(leafVertexCount),
         v(leafVertexCount),
@@ -112,21 +114,4 @@ template <class Vector, class ScalarVector> class ProgramState {
   size_t timeStep;
 };
 
-namespace boost {
-namespace serialization {
-  template <class Archive, class Vector, class ScalarVector>
-  void serialize(Archive &ar, ProgramState<Vector, ScalarVector> &ps,
-                 const unsigned int version) {
-    ar &ps.u;
-    ar &ps.v;
-    ar &ps.a;
-    ar &ps.alpha;
-    ar &ps.weightedNormalStress;
-    ar &ps.relativeTime;
-    ar &ps.relativeTau;
-    ar &ps.timeStep;
-  }
-}
-}
-
 #endif
diff --git a/src/sand-wedge-data/mygeometry.cc b/src/sand-wedge-data/mygeometry.cc
index ede4a864..fef8452a 100644
--- a/src/sand-wedge-data/mygeometry.cc
+++ b/src/sand-wedge-data/mygeometry.cc
@@ -12,13 +12,6 @@
 
 #include "mygeometry.hh"
 
-double MyGeometry::verticalProjection(LocalVector const &x) {
-  return zenith * x;
-}
-double MyGeometry::horizontalProjection(LocalVector const &x) {
-  return orthogonalZenith * x;
-}
-
 void MyGeometry::write() {
   std::fstream writer("geometry", std::fstream::out);
   writer << "A = " << A << std::endl;
diff --git a/src/sand-wedge-data/mygeometry.hh b/src/sand-wedge-data/mygeometry.hh
index b6eac051..720a5bf3 100644
--- a/src/sand-wedge-data/mygeometry.hh
+++ b/src/sand-wedge-data/mygeometry.hh
@@ -65,7 +65,6 @@ namespace reference {
   LocalVector2D const I = createVector(Y[0] + G[0], Y[1] + G[1]);
 
   LocalVector2D const zenith = createVector(0, 1);
-  LocalVector2D const orthogonalZenith = createVector(-1, 0);
 
   LocalMatrix2D const rotation =
       createMatrix(std::cos(leftAngle), -std::sin(leftAngle),
@@ -102,10 +101,6 @@ LocalVector const Y = rotate(reference::Y);
 LocalVector const Z = rotate(reference::Z);
 
 LocalVector const zenith = rotate(reference::zenith);
-LocalVector const orthogonalZenith = rotate(reference::orthogonalZenith);
-
-double verticalProjection(LocalVector const &);
-double horizontalProjection(LocalVector const &);
 
 void write();
 
diff --git a/src/sand-wedge-data/special_writer.hh b/src/sand-wedge-data/special_writer.hh
deleted file mode 100644
index efd476f4..00000000
--- a/src/sand-wedge-data/special_writer.hh
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef SRC_SPECIAL_WRITER_HH
-#define SRC_SPECIAL_WRITER_HH
-
-#include <fstream>
-#include <utility>
-
-#include <dune/common/fvector.hh>
-#include <dune/grid/utility/hierarchicsearch.hh>
-#include <dune/fufem/functions/virtualgridfunction.hh>
-
-#include "mygeometry.hh"
-
-template <class GridView, int dimension> class SpecialWriter {
-  using LocalVector = Dune::FieldVector<double, dimension>;
-  using Element = typename GridView::Grid::template Codim<0>::Entity;
-  using ElementPointer =
-      typename GridView::Grid::template Codim<0>::EntityPointer;
-
-  void writeHorizontal(LocalVector const &v) {
-    writer_ << MyGeometry::horizontalProjection(v) << " ";
-  }
-  void writeVertical(LocalVector const &v) {
-    writer_ << MyGeometry::verticalProjection(v) << " ";
-  }
-
-  std::pair<ElementPointer, LocalVector> globalToLocal(
-      LocalVector const &x) const {
-    auto const element = hsearch_.findEntity(x);
-    return std::make_pair(element, element->geometry().local(x));
-  }
-
-  std::fstream writer_;
-
-  Dune::HierarchicSearch<typename GridView::Grid, GridView> const hsearch_;
-
-  std::pair<ElementPointer, LocalVector> const G;
-  std::pair<ElementPointer, LocalVector> const H;
-  std::pair<ElementPointer, LocalVector> const J;
-  std::pair<ElementPointer, LocalVector> const I;
-  std::pair<ElementPointer, LocalVector> const U;
-  std::pair<ElementPointer, LocalVector> const Z;
-
-public:
-  SpecialWriter(std::string filename, GridView const &gridView)
-      : writer_(filename, std::fstream::out),
-        hsearch_(gridView.grid(), gridView),
-        G(globalToLocal(MyGeometry::G)),
-        H(globalToLocal(MyGeometry::H)),
-        J(globalToLocal(MyGeometry::J)),
-        I(globalToLocal(MyGeometry::I)),
-        U(globalToLocal(MyGeometry::U)),
-        Z(globalToLocal(MyGeometry::Z)) {
-    writer_ << "Gh Hh Jh Ih Uv Uh Zv Zh" << std::endl;
-  }
-
-  void write(VirtualGridFunction<typename GridView::Grid, LocalVector> const &
-                 specialField) {
-    LocalVector value;
-
-    specialField.evaluateLocal(*G.first, G.second, value);
-    writeHorizontal(value);
-
-    specialField.evaluateLocal(*H.first, H.second, value);
-    writeHorizontal(value);
-
-    specialField.evaluateLocal(*J.first, J.second, value);
-    writeHorizontal(value);
-
-    specialField.evaluateLocal(*I.first, I.second, value);
-    writeHorizontal(value);
-
-    specialField.evaluateLocal(*U.first, U.second, value);
-    writeVertical(value);
-    writeHorizontal(value);
-
-    specialField.evaluateLocal(*Z.first, Z.second, value);
-    writeVertical(value);
-    writeHorizontal(value);
-
-    writer_ << std::endl;
-  }
-};
-#endif
diff --git a/src/sand-wedge.cc b/src/sand-wedge.cc
index 3b7aa548..adc33009 100644
--- a/src/sand-wedge.cc
+++ b/src/sand-wedge.cc
@@ -39,9 +39,7 @@
 #include <dune/fufem/boundarypatch.hh>
 #pragma clang diagnostic pop
 
-#include <dune/fufem/dataio.hh>
 #include <dune/fufem/dunepython.hh>
-#include <dune/fufem/functions/basisgridfunction.hh>
 #include <dune/fufem/sharedpointermap.hh>
 #include <dune/solvers/norms/energynorm.hh>
 #include <dune/solvers/solvers/loopsolver.hh>
@@ -52,14 +50,16 @@
 
 #include <dune/tectonic/myblockproblem.hh>
 #include <dune/tectonic/globalfriction.hh>
+#include <dune/fufem/hdf5/hdf5file.hh>
 
 #include "adaptivetimestepper.hh"
 #include "assemblers.hh"
 #include "diameter.hh"
 #include "enumparser.hh"
 #include "enums.hh"
-#include "friction_writer.hh"
 #include "gridselector.hh"
+#include "hdf5-writer.hh"
+#include "hdf5/restart-io.hh"
 #include "matrices.hh"
 #include "program_state.hh"
 #include "rate.hh"
@@ -67,7 +67,6 @@
 #include "sand-wedge-data/mygeometry.hh"
 #include "sand-wedge-data/myglobalfrictiondata.hh"
 #include "sand-wedge-data/mygrid.hh"
-#include "sand-wedge-data/special_writer.hh"
 #include "sand-wedge-data/weakpatch.hh"
 #include "solverfactory.hh"
 #include "state.hh"
@@ -148,18 +147,9 @@ int main(int argc, char *argv[]) {
 
     auto myFaces = gridConstructor.constructFaces(leafView);
 
-    // Neumann boundary
     BoundaryPatch<GridView> const neumannBoundary(leafView);
-
-    // Frictional Boundary
     BoundaryPatch<GridView> const &frictionalBoundary = myFaces.lower;
-    Dune::BitSetVector<1> frictionalNodes(leafVertexCount);
-    frictionalBoundary.getVertices(frictionalNodes);
-
-    // Surface
     BoundaryPatch<GridView> const &surface = myFaces.upper;
-    Dune::BitSetVector<1> surfaceNodes(leafVertexCount);
-    surface.getVertices(surfaceNodes);
 
     // Dirichlet Boundary
     Dune::BitSetVector<dims> noNodes(leafVertexCount);
@@ -228,14 +218,14 @@ int main(int argc, char *argv[]) {
     auto const restartTemplate = parset.get<std::string>("restarts.template");
     auto const restartDirectory = fs::path(restartTemplate).parent_path();
 
+    HDF5File ioFile("output.h5");
     if (firstRestart != 0)
-      DataIO::loadData(programState,
-                       str(boost::format(restartTemplate) % firstRestart));
+      RestartIO<ProgramState<Vector, ScalarVector>>(ioFile, leafVertexCount)
+          .read(firstRestart, programState);
     else
       programState.setupInitialConditions(parset, computeExternalForces,
                                           matrices, myAssembler, dirichletNodes,
                                           noNodes, frictionalBoundary, body);
-    assert(programState.timeStep == firstRestart);
 
     MyGlobalFrictionData<LocalVector> frictionInfo(
         parset.sub("boundary.friction"), weakPatch);
@@ -255,54 +245,24 @@ int main(int argc, char *argv[]) {
       }
     }
 
-    // Set up writers
-    FrictionWriter<ScalarVector, Vector> frictionWriter(
-        vertexCoordinates, frictionalNodes, "friction",
-        MyGeometry::horizontalProjection);
-    BoundaryWriter<ScalarVector, Vector> verticalSurfaceWriter(
-        vertexCoordinates, surfaceNodes, "verticalSurface",
-        MyGeometry::verticalProjection);
-    BoundaryWriter<ScalarVector, Vector> horizontalSurfaceWriter(
-        vertexCoordinates, surfaceNodes, "horizontalSurface",
-        MyGeometry::horizontalProjection);
-    SpecialWriter<GridView, dims> specialVelocityWriter("specialVelocities",
-                                                        leafView);
-    SpecialWriter<GridView, dims> specialDisplacementWriter(
-        "specialDisplacements", leafView);
-
-    std::fstream timeStepWriter("timeSteps", std::fstream::out);
-    timeStepWriter << std::fixed << std::setprecision(10);
-
+    HDF5Writer<ProgramState<Vector, ScalarVector>,
+               typename MyAssembler::VertexBasis, GridView>
+        hdf5Writer(ioFile, vertexCoordinates, myAssembler.vertexBasis, surface,
+                   frictionalBoundary, weakPatch);
     MyVTKWriter<typename MyAssembler::VertexBasis,
                 typename MyAssembler::CellBasis> const
         vtkWriter(myAssembler.cellBasis, myAssembler.vertexBasis, "obs");
 
-    auto const report = [&]() {
-      if (programState.timeStep != 0)
-        timeStepWriter << programState.relativeTime << " "
-                       << programState.relativeTau << std::endl;
-
-      horizontalSurfaceWriter.writeKinetics(programState.u, programState.v);
-      verticalSurfaceWriter.writeKinetics(programState.u, programState.v);
-
-      ScalarVector c;
-      myGlobalFriction->coefficientOfFriction(programState.v, c);
-      frictionWriter.writeKinetics(programState.u, programState.v);
-      frictionWriter.writeOther(c, programState.alpha);
-
-      BasisGridFunction<typename MyAssembler::VertexBasis, Vector> velocity(
-          myAssembler.vertexBasis, programState.v);
-      BasisGridFunction<typename MyAssembler::VertexBasis, Vector> displacement(
-          myAssembler.vertexBasis, programState.u);
-      specialVelocityWriter.write(velocity);
-      specialDisplacementWriter.write(displacement);
-
-      if (programState.timeStep % restartSpacing == 0) {
-        if (!fs::is_directory(restartDirectory))
-          fs::create_directories(restartDirectory);
-        DataIO::writeData(programState, str(boost::format(restartTemplate) %
-                                            programState.timeStep));
-      }
+    IterationRegister iterationCount;
+    auto const report = [&](bool initial = false) {
+      hdf5Writer.reportSolution(programState, *myGlobalFriction);
+      if (!initial)
+        hdf5Writer.reportIterations(programState, iterationCount);
+
+      if (!initial and programState.timeStep % restartSpacing == 0)
+        hdf5Writer.writeRestart(programState);
+
+      ioFile.flush();
 
       if (parset.get<bool>("io.writeVTK")) {
         ScalarVector stress;
@@ -313,7 +273,7 @@ int main(int argc, char *argv[]) {
                         programState.alpha, stress);
       }
     };
-    report();
+    report(true);
 
     // Set up TNNMG solver
     using NonlinearFactory = SolverFactory<
@@ -330,7 +290,7 @@ int main(int argc, char *argv[]) {
                         programState.u, programState.v, programState.a),
         initStateUpdater<ScalarVector, Vector>(
             parset.get<Config::stateModel>("boundary.friction.stateModel"),
-            programState.alpha, frictionalNodes,
+            programState.alpha, *frictionalBoundary.getVertices(),
             parset.get<double>("boundary.friction.L"),
             parset.get<double>("boundary.friction.V0")));
 
@@ -355,7 +315,7 @@ int main(int argc, char *argv[]) {
     while (!adaptiveTimeStepper.reachedEnd()) {
       programState.timeStep++;
 
-      adaptiveTimeStepper.advance();
+      iterationCount = adaptiveTimeStepper.advance();
 
       programState.relativeTime = adaptiveTimeStepper.relativeTime_;
       programState.relativeTau = adaptiveTimeStepper.relativeTau_;
diff --git a/src/updaters.hh b/src/updaters.hh
index 6d27f445..f72c4a71 100644
--- a/src/updaters.hh
+++ b/src/updaters.hh
@@ -12,6 +12,10 @@ struct Updaters {
            std::shared_ptr<StateUpdaterTEMPLATE> stateUpdater)
       : rate_(rateUpdater), state_(stateUpdater) {}
 
+  bool operator==(Updaters const &other) const {
+    return other.rate_ == rate_ and other.state_ == state_;
+  }
+
   Updaters<RateUpdater, StateUpdater> clone() const {
     return Updaters<RateUpdater, StateUpdater>(rate_->clone(), state_->clone());
   }
-- 
GitLab