From cafdbe777bcae7e64975ee75862af2c22e2a3ecc Mon Sep 17 00:00:00 2001
From: Simon Praetorius <simon.praetorius@tu-dresden.de>
Date: Sun, 31 Mar 2019 11:19:29 +0200
Subject: [PATCH] backup and restore for DOFVectors

---
 src/amdis/BackupRestore.hpp                   | 177 ++++++++++++++++++
 src/amdis/ProblemStat.inc.hpp                 |  17 +-
 src/amdis/linearalgebra/DOFVectorBase.hpp     |   6 +
 src/amdis/linearalgebra/DOFVectorBase.inc.hpp |  76 +++++++-
 test/BackupRestoreTest.cpp                    |  53 +++++-
 test/CMakeLists.txt                           |  12 +-
 6 files changed, 329 insertions(+), 12 deletions(-)
 create mode 100644 src/amdis/BackupRestore.hpp

diff --git a/src/amdis/BackupRestore.hpp b/src/amdis/BackupRestore.hpp
new file mode 100644
index 00000000..33fad315
--- /dev/null
+++ b/src/amdis/BackupRestore.hpp
@@ -0,0 +1,177 @@
+#pragma once
+
+#include <cstdint>
+#include <fstream>
+#include <memory>
+#include <vector>
+
+#include <dune/grid/common/backuprestore.hh>
+#include <dune/grid/common/gridfactory.hh>
+
+namespace AMDiS
+{
+  // Fallback Implementation for Grids not supporting direct BackupRestore
+  template <class Grid>
+  class BackupRestoreByGridFactory
+  {
+    template <class GridView>
+    class Writer
+    {
+      enum { dim = GridView::dimension };
+      enum { dow = GridView::dimensionworld };
+
+      using ct = typename GridView::ctype;
+
+    public:
+      Writer(GridView const& gv)
+        : gridView_(gv)
+      {
+        std::int64_t num = 0;
+        indexMap_.resize(gridView_.size(dim));
+        auto const& indexSet = gridView_.indexSet();
+        for (auto const& vertex : vertices(gridView_))
+          indexMap_[indexSet.index(vertex)] = std::int64_t(num++);
+      }
+
+      void writeVertices(std::ostream& out) const
+      {
+        for (auto const& vertex : vertices(gridView_)) {
+          auto v = vertex.geometry().center();
+          out.write((char*)&v, dow*sizeof(ct));
+        }
+      }
+
+      void writeElements(std::ostream& out) const
+      {
+        auto const& indexSet = gridView_.indexSet();
+
+        std::vector<std::int64_t> connectivity;
+        connectivity.reserve(8);
+        for (auto const& e : elements(gridView_)) {
+          unsigned int id = e.type().id();
+          out.write((char*)&id, sizeof(unsigned int));
+
+          connectivity.clear();
+          for (unsigned int j = 0; j < e.subEntities(dim); ++j)
+            connectivity.emplace_back(indexMap_[indexSet.subIndex(e,j,dim)]);
+
+          out.write((char*)connectivity.data(), connectivity.size()*sizeof(std::int64_t));
+        }
+      }
+
+    private:
+      GridView gridView_;
+      std::vector<std::int64_t> indexMap_;
+    };
+
+    class Reader
+    {
+      enum { dim = Grid::dimension };
+      enum { dow = Grid::dimensionworld };
+
+      using ct = typename Grid::ctype;
+      using Factory = Dune::GridFactory<Grid>;
+      using GlobalCoordinates = Dune::FieldVector<ct,dow>;
+
+    public:
+      Reader(Factory& factory, std::istream& in)
+        : factory_(factory)
+      {
+        in.read((char*)&numElements_, sizeof(std::int64_t));
+        in.read((char*)&numVertices_, sizeof(std::int64_t));
+      }
+
+      void readVertices(std::istream& in) const
+      {
+        GlobalCoordinates p;
+        for (std::int64_t i = 0; i < numVertices_; ++i) {
+          in.read((char*)&p[0], dow*sizeof(ct));
+          factory_.insertVertex(p);
+        }
+      }
+
+      void readElements(std::istream& in) const
+      {
+        std::vector<std::int64_t> connectivity(8);
+        std::vector<unsigned int> vertices;
+        vertices.reserve(8);
+
+        for (std::int64_t i = 0; i < numElements_; ++i) {
+          unsigned int id = 0;
+          in.read((char*)&id, sizeof(unsigned int));
+
+          Dune::GeometryType type(id,dim);
+          auto refElem = Dune::referenceElement<ct,dim>(type);
+
+          in.read((char*)connectivity.data(), refElem.size(dim)*sizeof(std::int64_t));
+          vertices.clear();
+          std::copy_n(connectivity.begin(),refElem.size(dim),std::back_inserter(vertices));
+
+          factory_.insertElement(type, vertices);
+        }
+      }
+
+    private:
+      Factory& factory_;
+      std::int64_t numElements_ = 0;
+      std::int64_t numVertices_ = 0;
+    };
+
+  public:
+    /// \brief Write a hierarchic grid to disk
+    template <class GridView>
+    static void backup(GridView const& gv, std::string const& filename)
+    {
+      std::ofstream out(filename, std::ios::binary);
+      backup(gv,out);
+    }
+
+    /// \brief write a hierarchic grid into a stream
+    template <class GridView>
+    static void backup(GridView const& gv, std::ostream& out)
+    {
+      std::int32_t dim = GridView::dimension;
+      std::int32_t dow = GridView::dimensionworld;
+      std::int64_t num_elements = gv.size(0);
+      std::int64_t num_vertices = gv.size(dim);
+
+      out.write((char*)&dim, sizeof(std::int32_t));
+      out.write((char*)&dow, sizeof(std::int32_t));
+      out.write((char*)&num_elements, sizeof(std::int64_t));
+      out.write((char*)&num_vertices, sizeof(std::int64_t));
+
+      Writer writer(gv);
+      writer.writeVertices(out);
+      writer.writeElements(out);
+    }
+
+    /// \brief read a hierarchic grid from disk
+    static Grid* restore(std::string const& filename)
+    {
+      std::ifstream in(filename, std::ios::binary);
+      return restore(in);
+    }
+
+    /// \brief read a hierarchic grid from a stream
+    static Grid* restore(std::istream& in)
+    {
+      Dune::GridFactory<Grid> factory;
+
+      std::int32_t dim = 0, dow = 0;
+
+      in.read((char*)&dim, sizeof(std::int32_t));
+      in.read((char*)&dow, sizeof(std::int32_t));
+
+      assert(Grid::dimension == dim);
+      assert(Grid::dimensionworld == dow);
+
+      Reader reader(factory, in);
+      reader.readVertices(in);
+      reader.readElements(in);
+
+      std::unique_ptr<Grid> ptr(factory.createGrid());
+      return ptr.release();
+    }
+  };
+
+} // end namespace AMDiS
diff --git a/src/amdis/ProblemStat.inc.hpp b/src/amdis/ProblemStat.inc.hpp
index 2cfa566e..142148cd 100644
--- a/src/amdis/ProblemStat.inc.hpp
+++ b/src/amdis/ProblemStat.inc.hpp
@@ -9,6 +9,7 @@
 #include <dune/typetree/childextraction.hh>
 
 #include <amdis/AdaptInfo.hpp>
+#include <amdis/BackupRestore.hpp>
 #include <amdis/FileWriter.hpp>
 #include <amdis/Assembler.hpp>
 #include <amdis/GridFunctionOperator.hpp>
@@ -491,7 +492,12 @@ backup(AdaptInfo& adaptInfo)
   auto param = Parameters::get<std::string>(name_ + "->backup filename");
   std::string filename = param ? *param : name_ + "_" + std::to_string(adaptInfo.timestepNumber()) + ".backup";
 
-  Dune::BackupRestoreFacility<Grid>::backup(*grid_, filename);
+  try {
+    Dune::BackupRestoreFacility<Grid>::backup(*grid_, filename);
+  } catch (Dune::NotImplemented const&) {
+    msg("Falling back to backup of gridview.");
+    BackupRestoreByGridFactory<Grid>::backup(gridView(), filename);
+  }
   // TODO: backup data
 
   msg("Problem backuped to file '{}'.", filename);
@@ -506,8 +512,13 @@ restore(Flag initFlag)
   test_exit(filesystem::exists(filename), "Restore file '{}' not found.", filename);
 
   // restore grid from file
-  std::shared_ptr<Grid> grid(Dune::BackupRestoreFacility<Grid>::restore(filename));
-  adoptGrid(grid);
+  std::shared_ptr<Grid> grid;
+  try {
+    grid.reset(Dune::BackupRestoreFacility<Grid>::restore(filename));
+  } catch (Dune::NotImplemented const&) {
+    grid.reset(BackupRestoreByGridFactory<Grid>::restore(filename));
+  }
+  adoptGrid(std::move(grid));
 
   // create fespace
   if (initFlag.isSet(INIT_FE_SPACE) || initFlag.isSet(INIT_SYSTEM))
diff --git a/src/amdis/linearalgebra/DOFVectorBase.hpp b/src/amdis/linearalgebra/DOFVectorBase.hpp
index 91e1c57d..fc293862 100644
--- a/src/amdis/linearalgebra/DOFVectorBase.hpp
+++ b/src/amdis/linearalgebra/DOFVectorBase.hpp
@@ -189,6 +189,12 @@ namespace AMDiS
     /// Assemble all vector operators
     void assemble();
 
+    /// Write DOFVector to file
+    void backup(std::string const& filename);
+
+    /// Read backup data from file
+    void restore(std::string const& filename);
+
     /// Return the associated DataTransfer object
     std::shared_ptr<DataTransfer const> dataTransfer() const
     {
diff --git a/src/amdis/linearalgebra/DOFVectorBase.inc.hpp b/src/amdis/linearalgebra/DOFVectorBase.inc.hpp
index 4795c768..610b54b9 100644
--- a/src/amdis/linearalgebra/DOFVectorBase.inc.hpp
+++ b/src/amdis/linearalgebra/DOFVectorBase.inc.hpp
@@ -1,5 +1,9 @@
 #pragma once
 
+#include <cstdint>
+#include <fstream>
+#include <functional>
+
 #include <amdis/Assembler.hpp>
 #include <amdis/LocalOperator.hpp>
 #include <amdis/typetree/Traversal.hpp>
@@ -89,9 +93,79 @@ assemble()
   for (auto const& element : elements(basis_->gridView())) {
     localView.bind(element);
     assemble(localView);
-    localView.unbind(element);
+    localView.unbind();
   }
   finish(true);
 }
 
+
+template <class GB, class B>
+void DOFVectorBase<GB,B>::
+backup(std::string const& filename)
+{
+  std::ofstream out(filename, std::ios::binary);
+
+  std::int64_t numElements = basis_->gridView().size(0);
+  out.write((char*)&numElements, sizeof(std::int64_t));
+
+  auto localView = basis_->localView();
+  std::vector<value_type> data;
+  for (auto const& element : elements(basis_->gridView()))
+  {
+    localView.bind(element);
+    data.clear();
+    for_each_leaf_node(localView.tree(), [&](auto const& node, auto) -> void {
+      auto const& fe = node.finiteElement();
+      std::size_t size = fe.size();
+
+      for (std::size_t i = 0; i < size; ++i)
+        data.push_back((*this)[localView.index(node.localIndex(i))]);
+    });
+
+    std::uint64_t len = data.size();
+    out.write((char*)&len, sizeof(std::uint64_t));
+    out.write((char*)data.data(), len*sizeof(value_type));
+
+    localView.unbind();
+  }
+}
+
+template <class GB, class B>
+void DOFVectorBase<GB,B>::
+restore(std::string const& filename)
+{
+  std::ifstream in(filename, std::ios::binary);
+
+  std::int64_t numElements = 0;
+  in.read((char*)&numElements, sizeof(std::int64_t));
+  assert(numElements == basis_->gridView().size(0));
+
+  // assume the order of element traversal is not changed
+  auto localView = basis_->localView();
+  std::vector<value_type> data;
+  for (auto const& element : elements(basis_->gridView()))
+  {
+    std::uint64_t len = 0;
+    in.read((char*)&len, sizeof(std::uint64_t));
+    data.resize(len);
+
+    in.read((char*)data.data(), len*sizeof(value_type));
+
+    localView.bind(element);
+    std::size_t shift = 0;
+    for_each_leaf_node(localView.tree(), [&](auto const& node, auto) -> void {
+      auto const& fe = node.finiteElement();
+      std::size_t size = fe.size();
+
+      assert(data.size() >= shift+size);
+      for (std::size_t i = 0; i < size; ++i)
+        (*this)[localView.index(node.localIndex(i))] = data[shift + i];
+
+      shift += size;
+    });
+
+    localView.unbind();
+  }
+}
+
 } // end namespace AMDiS
diff --git a/test/BackupRestoreTest.cpp b/test/BackupRestoreTest.cpp
index 7471e87b..7e29077b 100644
--- a/test/BackupRestoreTest.cpp
+++ b/test/BackupRestoreTest.cpp
@@ -1,6 +1,17 @@
 #include <amdis/AMDiS.hpp>
+#include <amdis/Integrate.hpp>
 #include <amdis/ProblemStat.hpp>
 
+#if HAVE_DUNE_SPGRID
+#include <dune/grid/spgrid.hh>
+#endif
+#if HAVE_DUNE_ALUGRID
+#include <dune/alugrid/grid.hh>
+#endif
+#if HAVE_DUNE_FOAMGRID
+#include <dune/foamgrid/foamgrid.hh>
+#endif
+
 #include "Tests.hpp"
 
 using namespace AMDiS;
@@ -21,11 +32,15 @@ void test(Factory factory)
     prob.initialize(INIT_ALL);
     prob.globalRefine(2);
 
+    prob.solution(Dune::Indices::_0) << [](auto const& x) { return x; };
+    prob.solution(Dune::Indices::_1) << [](auto const& x) { return two_norm(x); };
+
     num_elements = prob.grid()->size(0);
     num_vertices = prob.grid()->size(Grid::dimension);
 
     AdaptInfo adaptInfo("adapt");
     prob.backup(adaptInfo);
+    prob.solutionVector()->backup("solution.backup");
   }
 
   // restore
@@ -35,6 +50,20 @@ void test(Factory factory)
 
     AMDIS_TEST_EQ(num_elements, prob.grid()->size(0));
     AMDIS_TEST_EQ(num_vertices, prob.grid()->size(Grid::dimension));
+
+    auto vec = *prob.solutionVector();
+    vec.restore("solution.backup");
+
+    prob.solution(Dune::Indices::_0) << [](auto const& x) { return x; };
+    prob.solution(Dune::Indices::_1) << [](auto const& x) { return two_norm(x); };
+
+    double error0 = std::sqrt(integrate(
+      unary_dot(prob.solution(Dune::Indices::_0) - makeDiscreteFunction(vec, Dune::Indices::_0)), prob.gridView()));
+    AMDIS_TEST(error0 < 1.e-10);
+
+    double error1 = std::sqrt(integrate(
+      pow<2>(prob.solution(Dune::Indices::_1) - makeDiscreteFunction(vec, Dune::Indices::_1)), prob.gridView()));
+    AMDIS_TEST(error1 < 1.e-10);
   }
 }
 
@@ -60,12 +89,26 @@ int main(int argc, char** argv)
   Parameters::set("test->backup filename", filename);
   Parameters::set("test->restore filename", filename);
 
+#if GRID_ID == 0
   test_cube<Dune::YaspGrid<2>>();
-  // test_cube<Dune::UGGrid<2>>();
-
-  // test_simplex<Dune::AlbertaGrid<2,2>>();
-  // test_simplex<Dune::UGGrid<2>>();
+#elif GRID_ID == 1 && HAVE_DUNE_UGGRID
+  test_cube<Dune::UGGrid<2>>();
+#elif GRID_ID == 2 && HAVE_DUNE_ALUGRID
+  test_cube<Dune::ALUGrid<2,2,Dune::cube,Dune::nonconforming>>();
+#elif GRID_ID == 3 && HAVE_DUNE_SPGRID
+  test<Dune::SPGrid<double,2>>([]() {
+    return std::unique_ptr<Dune::SPGrid<double,2>>(
+      new Dune::SPGrid<double,2>(Dune::SPDomain<double, 2>({0.0,0.0}, {1.0,1.0}), Dune::SPMultiIndex<2>({2,2}))); });
+#elif GRID_ID == 4 && HAVE_DUNE_UGGRID
+  test_simplex<Dune::UGGrid<2>>();
+#elif GRID_ID == 5 && HAVE_DUNE_ALUGRID
+  test_simplex<Dune::ALUGrid<2,2,Dune::simplex,Dune::nonconforming>>();
+#elif GRID_ID == 6 && HAVE_DUNE_ALUGRID
+  test_simplex<Dune::ALUGrid<2,2,Dune::simplex,Dune::conforming>>();
+#endif
+  // test_simplex<Dune::AlbertaGrid<2,2>>(); // Segmentation fault
+  // test_simplex<Dune::FoamGrid<2,2>>(); // Segmentation fault
 
   AMDiS::finalize();
-  return 0;
+  return report_errors();
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f5ef52b2..515a07b5 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -2,9 +2,15 @@
 dune_add_test(SOURCES AdaptInfoTest.cpp
   LINK_LIBRARIES amdis)
 
-dune_add_test(SOURCES BackupRestoreTest.cpp
-  LINK_LIBRARIES amdis)
-add_dune_alberta_flags(GRIDDIM 2 WORLDDIM 2 BackupRestoreTest)
+foreach(_GRID RANGE 6)
+  dune_add_test(NAME "BackupRestoreTest_${_GRID}"
+    SOURCES BackupRestoreTest.cpp
+    COMPILE_DEFINITIONS "GRID_ID=${_GRID}"
+    LABELS "BackupRestore"
+    LINK_LIBRARIES amdis)
+  add_dune_alberta_flags(GRIDDIM 2 WORLDDIM 2 "BackupRestoreTest_${_GRID}")
+endforeach()
+unset(_GRID)
 
 dune_add_test(SOURCES ConceptsTest.cpp
   LINK_LIBRARIES amdis)
-- 
GitLab