diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6ecbeb2adc28e64b6b14dde7ff4ae6010c86ee4e..c267b3fcc3df8b83b85d45c9fbd11b82185b1b32 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -118,11 +118,12 @@ full-dune-latest-release-clang:
 full-dune-master-gcc-cpp-20:
   extends: .base-trigger
   variables:
-    IMAGE: $IMAGE_REGISTRY_URL/full:dune-master-gcc-ubuntu-22.04
+    IMAGE: $IMAGE_REGISTRY_URL/full:dune-master-gcc-12-ubuntu-22.04
     CXX_MAX_STANDARD: "20"
     DUMUX_ENABLE_CPPCHECK: "true"
     DUMUX_ENABLE_DOXYGEN_BUILD: "true"
     DUMUX_CHECK_EXAMPLE_DOCS: "true"
+    GIT_SUBMODULE_STRATEGY: recursive
 
 ##################################
 # additional scheduled pipelines #
diff --git a/.gitlab-ci/default.yml b/.gitlab-ci/default.yml
index ac0380e44765e578fd03f4f3d92798f1bd177c46..c09c78c31b12f8b456bc9138a457209052bb6a5a 100644
--- a/.gitlab-ci/default.yml
+++ b/.gitlab-ci/default.yml
@@ -33,6 +33,11 @@ configure:
   stage: configure
   script:
     - |
+      # remove submodules (may be present due to caching done by gitlab, although this pipeline may not support the submodule)
+      rm -rf deps/*
+      if [ "${GIT_SUBMODULE_STRATEGY}" == "recursive" ]; then
+          git submodule update --init --recursive
+      fi
       echo "source ${DUNE_OPTS_FILE}" > opts_file.opts
       echo "CMAKE_FLAGS=\"\${CMAKE_FLAGS} -DCXX_MAX_STANDARD=${CXX_MAX_STANDARD} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\"" >> opts_file.opts
     - dunecontrol --opts=opts_file.opts --current configure
@@ -63,7 +68,7 @@ linters:
     # codespell
     - echo "Running codespell"
     - codespell --version
-    - codespell --skip="*format/fmt/*,*io/expression/*,*build-cmake*,*.png,*.svg,*.eps,*.bib,*.tex,patches,.git,*staggered_grid.pdf,*dumux/common/dumuxmessage.hh" --ignore-words-list="ges"
+    - codespell --skip="./deps/**/*,*format/fmt/*,*io/expression/*,*build-cmake*,*.png,*.svg,*.eps,*.bib,*.tex,patches,.git,*staggered_grid.pdf,*dumux/common/dumuxmessage.hh" --ignore-words-list="ges"
     # check examples
     - |
       if [ "${DUMUX_CHECK_EXAMPLE_DOCS}" == "true" ]; then
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..176b9f14a17bd5fac5e51b984d3e5d6d98eaf407
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+# SPDX-License-Identifier: CC0-1.0
+
+[submodule "deps/gridformat"]
+	path = deps/gridformat
+	url = https://github.com/dglaeser/gridformat.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad1c345552201bf93c2672c70a3fde7fd3bf9d14..f1475080ee2d65314f0bd8d62fdca2db0f374e79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,10 @@ endif()
 # enforce C++-17
 target_compile_features(dumux PUBLIC cxx_std_17)
 
+if (DUMUX_HAVE_GRIDFORMAT)
+    dune_register_package_flags(LIBRARIES gridformat::gridformat)
+endif()
+
 add_subdirectory(cmake/modules)
 add_subdirectory(doc)
 add_subdirectory(dumux)
diff --git a/cmake/modules/DumuxMacros.cmake b/cmake/modules/DumuxMacros.cmake
index 01f384076c4666af68929bb4531beebbc9baef62..d1994ed8c1b8f362863e25a0b179dab77e0f9368 100644
--- a/cmake/modules/DumuxMacros.cmake
+++ b/cmake/modules/DumuxMacros.cmake
@@ -95,3 +95,15 @@ else()
 endif()
 
 message(STATUS "Dumux multithreading backend: ${DUMUX_MULTITHREADING_BACKEND}")
+
+# check for optional 3rd-party libraries as submodules
+set(DUMUX_HAVE_GRIDFORMAT false)
+set(DUMUX_HAVE_VTK_HDF false)
+if (EXISTS ${CMAKE_SOURCE_DIR}/deps/gridformat/CMakeLists.txt)
+    message(STATUS "Including gridformat in the source tree")
+    add_subdirectory(${CMAKE_SOURCE_DIR}/deps/gridformat)
+
+    include(${CMAKE_SOURCE_DIR}/deps/gridformat/cmake/modules/GridFormatHaveFeature.cmake)
+    set(DUMUX_HAVE_GRIDFORMAT true)
+    gridformat_have_feature(VTK_HDF DUMUX_HAVE_VTK_HDF)
+endif ()
diff --git a/cmake/modules/DumuxTestMacros.cmake b/cmake/modules/DumuxTestMacros.cmake
index e62bb416fc3725edc5f4530ab80489ca0438c8de..e37bd35b09f6b9548c0d747e730d084ce9934af5 100644
--- a/cmake/modules/DumuxTestMacros.cmake
+++ b/cmake/modules/DumuxTestMacros.cmake
@@ -260,9 +260,23 @@ function(dumux_add_test)
     set(ADDTEST_TARGET ${ADDTEST_NAME})
   endif()
 
-  file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/TestMetaData")
-  file(WRITE "${CMAKE_BINARY_DIR}/TestMetaData/${ADDTEST_NAME}.json"
-             "{\n  \"name\": \"${ADDTEST_NAME}\",\n  \"target\": \"${ADDTEST_TARGET}\",\n  \"source_dir\": \"${CMAKE_CURRENT_SOURCE_DIR}\"\n}\n")
+  if(NOT ADDTEST_MPI_RANKS)
+    set(ADDTEST_MPI_RANKS 1)
+  endif()
+
+  foreach(procnum ${ADDTEST_MPI_RANKS})
+    if((NOT "${procnum}" GREATER "${DUNE_MAX_TEST_CORES}") AND (NOT ADDTEST_COMPILE_ONLY))
+      set(ACTUAL_NAME ${ADDTEST_NAME})
+      # add suffix
+      if(NOT ${procnum} STREQUAL "1")
+        set(ACTUAL_NAME "${ACTUAL_NAME}-mpi-${procnum}")
+      endif()
+
+      file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/TestMetaData")
+      file(WRITE "${CMAKE_BINARY_DIR}/TestMetaData/${ACTUAL_NAME}.json"
+             "{\n  \"name\": \"${ACTUAL_NAME}\",\n  \"target\": \"${ADDTEST_TARGET}\",\n  \"source_dir\": \"${CMAKE_CURRENT_SOURCE_DIR}\"\n}\n")
+    endif()
+  endforeach()
 endfunction()
 
 # Evaluate test guards like dune_add_test internally does
diff --git a/config.h.cmake b/config.h.cmake
index 71bfe1eea6e5a287e9be0dbb32a017810625c417..6345d845efba35b602c0557de8808e3f7e1e0405 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -92,6 +92,9 @@
 /* Set DUMUX_HAVE_CPP_PARALLEL_ALGORITHMS if available */
 #cmakedefine DUMUX_HAVE_CPP_PARALLEL_ALGORITHMS 1
 
+/* Set DUMUX_HAVE_GRIDFORMAT if available */
+#cmakedefine DUMUX_HAVE_GRIDFORMAT 1
+
 /* end dumux
    Everything below here will be overwritten
 */
diff --git a/deps/gridformat b/deps/gridformat
new file mode 160000
index 0000000000000000000000000000000000000000..fa73387ccbd562034c99fdcf3238ba6a4195d9cf
--- /dev/null
+++ b/deps/gridformat
@@ -0,0 +1 @@
+Subproject commit fa73387ccbd562034c99fdcf3238ba6a4195d9cf
diff --git a/dumux/io/gridwriter.hh b/dumux/io/gridwriter.hh
new file mode 100644
index 0000000000000000000000000000000000000000..13f479183879e6e0561b8c3613aa7edabd779740
--- /dev/null
+++ b/dumux/io/gridwriter.hh
@@ -0,0 +1,235 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+/*!
+ * \file
+ * \ingroup InputOutput
+ * \brief Generic writer for a variety of grid file formats.
+ */
+#ifndef DUMUX_IO_GRID_WRITER_HH
+#define DUMUX_IO_GRID_WRITER_HH
+
+#include <config.h>
+
+#if DUMUX_HAVE_GRIDFORMAT
+
+#include <type_traits>
+
+#include <gridformat/gridformat.hpp>
+#include <gridformat/traits/dune.hpp>
+
+namespace Dumux::IO {
+
+
+#ifndef DOXYGEN
+namespace Detail {
+
+template<typename Grid, typename Format, typename Comm, typename... Args>
+auto makeParallelWriter(const Grid& grid, const Format& fmt, const Dune::Communication<Comm>& comm, Args&&... args)
+{
+    return comm.size() > 1
+        ? GridFormat::Writer<Grid>{fmt, grid, static_cast<Comm>(comm), std::forward<Args>(args)...}
+        : GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...};
+}
+
+template<typename Grid, typename Format, typename... Args>
+auto makeParallelWriter(const Grid& grid, const Format& fmt, const Dune::Communication<Dune::No_Comm>&, Args&&... args)
+{ return GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...}; }
+
+template<typename Grid, typename Format, typename... Args>
+auto makeWriter(const Grid& grid, const Format& fmt, Args&&... args)
+{
+    const auto& comm = GridFormat::Dune::Traits::GridView<Grid>::get(grid).comm();
+    return makeParallelWriter(grid, fmt, comm, std::forward<Args>(args)...);
+}
+
+} // namespace Detail
+#endif // DOXYGEN
+
+
+namespace VTK { using namespace GridFormat::VTK; }
+namespace Format { using namespace GridFormat::Formats; }
+namespace Encoding { using namespace GridFormat::Encoding; }
+namespace Compression { using namespace GridFormat::Compression; using GridFormat::none; }
+namespace Precision {
+    using GridFormat::float32;
+    using GridFormat::float64;
+
+    using GridFormat::uint64;
+    using GridFormat::uint32;
+    using GridFormat::uint16;
+    using GridFormat::uint8;
+
+    using GridFormat::int64;
+    using GridFormat::int32;
+    using GridFormat::int16;
+    using GridFormat::int8;
+} // namespace Precision
+
+
+/*!
+ * \ingroup InputOutput
+ * \brief Represents the interpolation order with which fields are written out.
+ */
+template<int order>
+struct Order { static_assert(order > 0, "order must be > 0"); };
+
+template<int o>
+inline constexpr auto order = Order<o>{};
+
+/*!
+ * \ingroup InputOutput
+ * \brief Generic writer for a variety of grid file formats.
+ *        Supports higher-order output of fields provided as Dune::Function objects.
+ *        To create a writer for higher-order output, you may write
+ *        \code
+ *            GridWriter writer{gridView, order<2>};
+ *        \endcode
+ */
+template<GridFormat::Concepts::Grid GridView, int order = 1>
+class GridWriter
+{
+    using Grid = std::conditional_t<
+        (order > 1),
+        GridFormat::Dune::LagrangePolynomialGrid<GridView>,
+        GridView
+    >;
+    using Cell = GridFormat::Cell<Grid>;
+    using Vertex = typename GridView::template Codim<GridView::dimension>::Entity;
+    using Element = typename GridView::template Codim<0>::Entity;
+    using Coordinate = typename Element::Geometry::GlobalCoordinate;
+    using Writer = GridFormat::Writer<Grid>;
+
+ public:
+    /*!
+     * \brief Constructor for non-transient file formats.
+     * \note This does not compile if the chosen format is a transient file format.
+     */
+    template<typename Format>
+    explicit GridWriter(const Format& fmt,
+                        const GridView& gridView,
+                        const Order<order>& = {})
+    : gridView_{gridView}
+    , grid_{makeGrid_(gridView)}
+    , writer_{Detail::makeWriter(grid_, fmt)}
+    { writer_.set_meta_data("rank", gridView_.comm().rank()); }
+
+    /*!
+     * \brief Constructor for transient file formats, i.e. time series.
+     * \note This does not compile if the chosen format is not a transient file format.
+     */
+    template<typename Format>
+    explicit GridWriter(const Format& fmt,
+                        const GridView& gridView,
+                        const std::string& filename,
+                        const Order<order>& = {})
+    : gridView_{gridView}
+    , grid_{makeGrid_(gridView)}
+    , writer_{Detail::makeWriter(grid_, fmt, filename)}
+    { writer_.set_meta_data("rank", gridView_.comm().rank()); }
+
+    /*!
+     * \ingroup InputOutput
+     * \brief Write the registered fields into the file with the given name.
+     * \note This function throws if the writer was constructed for time series output.
+     */
+    std::string write(const std::string& name) const
+    { return writer_.write(name); }
+
+    /*!
+     * \ingroup InputOutput
+     * \brief Write a step in a time series.
+     * \note This function throws if the writer was not constructed for time series output.
+     */
+    template<std::floating_point T>
+    std::string write(T time) const
+    { return writer_.write(time); }
+
+    //! Set a cell field via a lambda invoked with grid elements
+    template<GridFormat::Concepts::CellFunction<GridView> F,
+             GridFormat::Concepts::Scalar T = GridFormat::FieldScalar<std::invoke_result_t<F, Element>>>
+    void setCellField(const std::string& name, F&& f, const GridFormat::Precision<T>& prec = {})
+    { writer_.set_cell_field(name, std::move(f), prec); }
+
+    //! Set a Dune::Function as cell field
+    template<GridFormat::Dune::Concepts::Function<GridView> F>
+    void setCellField(const std::string& name, F&& f)
+    { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name); }
+
+    //! Set a Dune::Function as cell field with custom precision
+    template<typename F, GridFormat::Concepts::Scalar T>
+    void setCellField(const std::string& name, F&& f, const GridFormat::Precision<T>& prec)
+    { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name, prec); }
+
+    //! Set a point field via a lambda invoked with grid vertices
+    template<GridFormat::Concepts::PointFunction<GridView> F,
+             GridFormat::Concepts::Scalar T = GridFormat::FieldScalar<std::invoke_result_t<F, Vertex>>>
+    void setPointField(const std::string& name, F&& f, const GridFormat::Precision<T>& prec = {})
+    {
+        static_assert(order == 1, "Point lambdas can only be used for order == 1. Use Dune::Functions instead.");
+        writer_.set_point_field(name, std::move(f), prec);
+    }
+
+    //! Set a Dune::Function as point field
+    template<GridFormat::Dune::Concepts::Function<GridView> F>
+    void setPointField(const std::string& name, F&& f)
+    { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name); }
+
+    //! Set a Dune::Function as point data with custom precision
+    template<GridFormat::Dune::Concepts::Function<GridView> F, GridFormat::Concepts::Scalar T>
+    void setPointField(const std::string& name, F&& f, const GridFormat::Precision<T>& prec)
+    { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name, prec); }
+
+    //! Clear all data
+    void clear()
+    { writer_.clear(); }
+
+    //! Update (must be called when the grid has changed)
+    void update()
+    {
+        if constexpr (order > 1)
+            grid_.update(gridView_);
+    }
+
+ private:
+    Grid makeGrid_(const GridView& gv) const
+    {
+        if constexpr (order > 1)
+            return Grid{gv, order};
+        else
+            return gv;
+    }
+
+    GridView gridView_;
+    Grid grid_;
+    Writer writer_;
+};
+
+} // namespace Dumux::IO
+
+#else // DUMUX_HAVE_GRIDFORMAT
+
+namespace Dumux::IO {
+
+template<class... Args>
+class GridWriter
+{
+public:
+    template<class... _Args>
+    GridWriter(_Args&&...)
+    {
+        static_assert(
+            false,
+            "GridWriter only available when the GridFormat library is available. "
+            "Use `git submodule init && git submodule update` to pull it."
+        );
+    }
+};
+
+} // namespace Dumux::IO
+
+#endif // DUMUX_HAVE_GRIDFORMAT
+#endif // DUMUX_IO_GRID_HH
diff --git a/test/io/CMakeLists.txt b/test/io/CMakeLists.txt
index 394a45a794f68f1318aef2572e468e8ff5418d7b..4aa65aa7350f5d760ef2db954ac49cf62896191f 100644
--- a/test/io/CMakeLists.txt
+++ b/test/io/CMakeLists.txt
@@ -9,3 +9,17 @@ add_subdirectory(gridmanager)
 add_subdirectory(inputdata)
 add_subdirectory(rasterimagereader)
 add_subdirectory(vtk)
+
+dumux_add_test(
+    NAME test_grid_writer
+    SOURCES test_grid_writer.cc
+    CMAKE_GUARD DUMUX_HAVE_GRIDFORMAT
+)
+
+dumux_add_test(
+    NAME test_grid_writer_parallel
+    SOURCES test_grid_writer_parallel.cc
+    CMAKE_GUARD DUMUX_HAVE_GRIDFORMAT
+    MPI_RANKS 1 2
+    TIMEOUT 100
+)
diff --git a/test/io/test_grid_writer.cc b/test/io/test_grid_writer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ee746d015b83cd9f8934cd3e4e9f81c6b003fb04
--- /dev/null
+++ b/test/io/test_grid_writer.cc
@@ -0,0 +1,80 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+/*!
+ * \file
+ * \brief Test for reading/writing from/to different formats with the generic grid writers & readers.
+ */
+#include <config.h>
+
+#include <dune/grid/yaspgrid.hh>
+#if HAVE_DUNE_FUNCTIONS
+#include <dune/functions/gridfunctions/analyticgridviewfunction.hh>
+#endif
+
+#include <dumux/common/initialize.hh>
+#include <dumux/io/gridwriter.hh>
+
+using namespace Dumux::IO;
+
+template<class Writer>
+void setFields(Writer& writer)
+{
+    writer.setPointField("pcoords", [] (const auto& p) {
+        return p.geometry().center();
+    });
+    writer.setCellField("ccoords", [] (const auto& e) {
+        return e.geometry().center();
+    });
+}
+
+template<typename GridView>
+auto writeFirstOrder(const GridView& gridView)
+{
+    GridWriter writer{Format::vtu, gridView, order<1>};
+    setFields(writer);
+    std::cout << "Wrote " << writer.write("test_grid_writer_first_order") << std::endl;
+}
+
+template<typename GridView>
+auto writeSecondOrder(const GridView& gridView)
+{
+#if HAVE_DUNE_FUNCTIONS
+    GridWriter writer{Format::vtu, gridView, order<2>};
+    auto f = Dune::Functions::makeAnalyticGridViewFunction([&] (const auto& x) {
+        return x[0]*x[1];
+    }, gridView);
+    writer.setPointField("point_function", f);
+    writer.setCellField("cell_function", f);
+    std::cout << "Wrote " << writer.write("test_grid_writer_second_order") << std::endl;
+#endif
+}
+
+template<typename GridView>
+auto writeSpecifiedFormat(const GridView& gridView)
+{
+    GridWriter writer{Format::vtu.with({.encoder = Encoding::raw}), gridView};
+    setFields(writer);
+    std::cout << "Wrote " << writer.write("test_grid_writer_format_selection") << std::endl;;
+}
+
+int main(int argc, char** argv)
+{
+    Dumux::initialize(argc, argv);
+
+    Dune::YaspGrid<2> grid{
+        {1.0, 1.0},
+        {20, 20},
+        std::bitset<2>(), // periodicity
+        0                // overlap
+    };
+
+    writeFirstOrder(grid.leafGridView());
+    writeSecondOrder(grid.leafGridView());
+    writeSpecifiedFormat(grid.leafGridView());
+
+    return 0;
+}
diff --git a/test/io/test_grid_writer_parallel.cc b/test/io/test_grid_writer_parallel.cc
new file mode 100644
index 0000000000000000000000000000000000000000..fac3c2aa1c3ff99b12c62146e974d7585ee8ffb5
--- /dev/null
+++ b/test/io/test_grid_writer_parallel.cc
@@ -0,0 +1,91 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+/*!
+ * \file
+ * \brief Test for writing parallel files with the generic grid file format writer.
+ */
+#include <config.h>
+
+#include <dune/grid/yaspgrid.hh>
+#if HAVE_DUNE_FUNCTIONS
+#include <dune/functions/gridfunctions/analyticgridviewfunction.hh>
+#endif
+
+#include <dumux/common/initialize.hh>
+#include <dumux/io/gridwriter.hh>
+
+// The rank is added as cell field, although the writer
+// writes it out as meta data per default (which is much
+// more space-efficient). However, VTK seems to have issues
+// displaying field data in parallel, and I could not find
+// any helpful info in the code or documentation.
+
+using namespace Dumux::IO;
+
+template<class GlobalPosition>
+double testFunction(const GlobalPosition& x)
+{ return x[0]*x[1]; }
+
+template<class Writer>
+void addFields(Writer& writer, int rank)
+{
+    writer.setPointField("pfield", [=] (const auto& p) {
+        return testFunction(p.geometry().center());
+    });
+    writer.setCellField("cfield", [=] (const auto& e) {
+        return testFunction(e.geometry().center());
+    });
+    writer.setCellField("rank", [=] (const auto& e) {
+        return rank;
+    });
+}
+
+template<class GridView>
+void testFirstOrder(const GridView& gridView)
+{
+    GridWriter writer{Format::vti, gridView};
+    addFields(writer, gridView.comm().rank());
+    std::cout << "Wrote " << writer.write("test_grid_writer_parallel") << std::endl;
+}
+
+template<class GridView>
+void testSecondOrder(const GridView& gridView)
+{
+#if HAVE_DUNE_FUNCTIONS
+    GridWriter writer{Format::vtu, gridView, order<2>};
+    auto f = Dune::Functions::makeAnalyticGridViewFunction([] (const auto& x) {
+        return testFunction(x);
+    }, gridView);
+    auto rank = Dune::Functions::makeAnalyticGridViewFunction([rank=gridView.comm().rank()] (const auto& x) {
+        return rank;
+    }, gridView);
+    writer.setPointField("pfunc", f);
+    writer.setCellField("cfunc", f);
+    writer.setCellField("rank", rank);
+    std::cout << "Wrote " << writer.write("test_grid_writer_parallel_second_order") << std::endl;
+#endif
+}
+
+int main(int argc, char** argv)
+{
+    Dumux::initialize(argc, argv);
+
+    // write a parallel vti file
+    Dune::YaspGrid<2> grid{
+        {1.0, 1.0},
+        {20, 20},
+        std::bitset<2>(), // periodicity
+        0                 // overlap
+    };
+    grid.loadBalance();
+    const auto& gridView = grid.leafGridView();
+
+    testFirstOrder(gridView);
+    testSecondOrder(gridView);
+
+    return 0;
+}