diff --git a/cmake/modules/DumuxMacros.cmake b/cmake/modules/DumuxMacros.cmake
index a8d3041bff592689a9822d4cde8ba85d24d54d84..b3e7328fe322bd418bc3868ec9876ee20c19e031 100644
--- a/cmake/modules/DumuxMacros.cmake
+++ b/cmake/modules/DumuxMacros.cmake
@@ -9,3 +9,4 @@ find_package(Gnuplot)
 set(HAVE_GNUPLOT ${GNUPLOT_FOUND})
 
 find_package(Valgrind)
+find_package(PTScotch)
diff --git a/dumux/common/CMakeLists.txt b/dumux/common/CMakeLists.txt
index d768ab6afc3860d7069d55f1bfed9815293d7949..ae96888e3071fb347b4e0cc6a830849bef5ae9e6 100644
--- a/dumux/common/CMakeLists.txt
+++ b/dumux/common/CMakeLists.txt
@@ -13,6 +13,7 @@ math.hh
 parameters.hh
 propertysystem.hh
 quad.hh
+reorderingdofmapper.hh
 splinecommon_.hh
 spline.hh
 start.hh
diff --git a/dumux/common/reorderingdofmapper.hh b/dumux/common/reorderingdofmapper.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4765efa2c29f8483b574ce4446893fe2263ceaa6
--- /dev/null
+++ b/dumux/common/reorderingdofmapper.hh
@@ -0,0 +1,184 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 2 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+
+/*!
+ * \file An SCSG element mapper that sorts the indices in order to optimize the matrix sparsity pattern
+ * \brief An SCSG element mapper that sorts the indices in order to optimize the matrix sparsity pattern
+ * \note The reordering needs the SCOTCH library
+ */
+#ifndef DUMUX_REORDERING_DOF_MAPPER_HH
+#define DUMUX_REORDERING_DOF_MAPPER_HH
+
+#include <dune/common/timer.hh>
+#include <dune/grid/common/mapper.hh>
+
+#include <dumux/linear/scotchbackend.hh>
+
+namespace Dumux
+{
+
+template<class GridView, int codimension>
+class ReorderingDofMapper
+: public Dune::Mapper<typename GridView::Grid, ReorderingDofMapper<GridView, codimension>, typename GridView::IndexSet::IndexType>
+{
+    using Index = typename GridView::IndexSet::IndexType;
+    using ParentType = typename Dune::Mapper<typename GridView::Grid, ReorderingDofMapper<GridView, codimension>, Index>;
+    using Element = typename GridView::template Codim<0>::Entity;
+
+public:
+
+    /*!
+     * \brief Construct mapper from grid and one of its index sets.
+     * \param gridView A Dune GridView object.
+     */
+    ReorderingDofMapper (const GridView& gridView)
+    : gridView_(gridView),
+      indexSet_(gridView.indexSet())
+    {
+        static_assert(codimension == 0 || codimension == GridView::dimension, "The reordering dofMapper is only implemented for element or vertex dofs");
+        update();
+    }
+
+    /*!
+     * \brief Map entity to array index.
+     *
+     * \tparam EntityType
+     * \param e Reference to codim \a EntityType entity.
+     * \return An index in the range 0 ... Max number of entities in set - 1.
+     */
+    template<class EntityType>
+    Index index (const EntityType& e) const
+    {
+        // map the index using the permutation obtained from the reordering algorithm
+        return static_cast<Index>(permutation_[indexSet_.index(e)]);
+    }
+
+    /** @brief Map subentity of codim 0 entity to array index.
+
+       \param e Reference to codim 0 entity.
+       \param i Number of subentity of e
+       \param codim Codimension of the subentity
+       \return An index in the range 0 ... Max number of entities in set - 1.
+     */
+    Index subIndex (const Element& e, int i, unsigned int codim) const
+    {
+        return indexSet_.subIndex(e, i, codim);
+    }
+
+    /** @brief Return total number of entities in the entity set managed by the mapper.
+
+       This number can be used to allocate a vector of data elements associated with the
+       entities of the set. In the parallel case this number is per process (i.e. it
+       may be different in different processes).
+
+       \return Size of the entity set.
+     */
+    std::size_t size () const
+    {
+        return indexSet_.size(codimension);
+    }
+
+    /** @brief Returns true if the entity is contained in the index set
+
+       \param e Reference to entity
+       \param result integer reference where corresponding index is  stored if true
+       \return true if entity is in entity set of the mapper
+     */
+    template<class EntityType>
+    bool contains (const EntityType& e, Index& result) const
+    {
+        result = index(e);
+        return true;
+    }
+
+    /** @brief Returns true if the entity is contained in the index set
+
+       \param e Reference to codim 0 entity
+       \param i subentity number
+       \param cc subentity codim
+       \param result integer reference where corresponding index is  stored if true
+       \return true if entity is in entity set of the mapper
+     */
+    bool contains (const Element& e, int i, int cc, Index& result) const
+    {
+        result = indexSet_.subIndex(e, i, cc);
+        return true;
+    }
+
+    /*!
+     * \brief Recalculates map after mesh adaptation
+     */
+    void update ()
+    {
+        // Compute scotch reordering
+        Dune::Timer watch;
+        // Create the graph as an adjacency list
+        std::vector<std::vector<Index>> graph(size());
+
+        // dofs on element centers (cell-centered methods)
+        if (codimension == 0)
+        {
+            for (const auto& element : elements(gridView_))
+            {
+                auto eIdx = indexSet_.index(element);
+                for (const auto& intersection : intersections(gridView_, element))
+                {
+                    if (intersection.neighbor())
+                        graph[eIdx].push_back(indexSet_.index(intersection.outside()));
+                }
+            }
+        }
+        // dof on vertices (box method)
+        else
+        {
+            for (const auto& element : elements(gridView_))
+            {
+                auto eIdx = indexSet_.index(element);
+                for (int vIdxLocal = 0; vIdxLocal < element.subEntities(codimension); ++vIdxLocal)
+                {
+                    auto vIdxGlobal = indexSet_.subIndex(element, vIdxLocal, codimension);
+                    graph[vIdxGlobal].push_back(eIdx);
+                }
+            }
+        }
+
+        permutation_ = ScotchBackend<Index>::computeGPSReordering(graph);
+        std::cout << "Scotch backend reordered index set of size " << size()
+                  << " in " << watch.elapsed() << " seconds." << std::endl;
+    }
+
+private:
+    // GridView is needed to keep the IndexSet valid
+    const GridView gridView_;
+    const typename GridView::IndexSet& indexSet_;
+    // the map resulting from the reordering
+    std::vector<int> permutation_;
+};
+
+//! Redordering dof mapper for vertex-centered methods, box method
+template<class GridView>
+using BoxReorderingDofMapper = ReorderingDofMapper<GridView, GridView::dimension>;
+
+//! Reordering dof mapper for the cell-centered methods
+template<class GridView>
+using CCReorderingDofMapper = ReorderingDofMapper<GridView, 0>;
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/implicit/model.hh b/dumux/implicit/model.hh
index e8c11b989882e608f75383e4138357295e1d3671..c8bec031c46a065c6e859b5b8d208df22e046cb8 100644
--- a/dumux/implicit/model.hh
+++ b/dumux/implicit/model.hh
@@ -535,10 +535,7 @@ public:
     template <class Restarter>
     void serialize(Restarter &res)
     {
-        if (isBox)
-            res.template serializeEntities<dim>(asImp_(), this->gridView_());
-        else
-            res.template serializeEntities<0>(asImp_(), this->gridView_());
+        res.template serializeEntities<dofCodim>(asImp_(), this->gridView_());
     }
 
     /*!
@@ -551,11 +548,7 @@ public:
     template <class Restarter>
     void deserialize(Restarter &res)
     {
-        if (isBox)
-            res.template deserializeEntities<dim>(asImp_(), this->gridView_());
-        else
-            res.template deserializeEntities<0>(asImp_(), this->gridView_());
-
+        res.template deserializeEntities<dofCodim>(asImp_(), this->gridView_());
         prevSol() = curSol();
     }
 
@@ -617,10 +610,7 @@ public:
      */
     size_t numDofs() const
     {
-        if (isBox)
-            return gridView_().size(dim);
-        else
-            return gridView_().size(0);
+        return gridView_().size(dofCodim);
     }
 
     /*!
diff --git a/dumux/linear/CMakeLists.txt b/dumux/linear/CMakeLists.txt
index c8ce338dbc029c6e609463b284871fa8bb8ee5b3..d2fd63b09377a1a5e8e6902d9e37593393cdacde 100644
--- a/dumux/linear/CMakeLists.txt
+++ b/dumux/linear/CMakeLists.txt
@@ -1,4 +1,3 @@
-
 #install headers
 install(FILES
 amgbackend.hh
@@ -6,6 +5,7 @@ amgparallelhelpers.hh
 amgproperties.hh
 linearsolverproperties.hh
 pardisobackend.hh
+scotchbackend.hh
 seqsolverbackend.hh
 vectorexchange.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/linear)
diff --git a/dumux/linear/scotchbackend.hh b/dumux/linear/scotchbackend.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d93c0f900dd911aa271202aa24ca6e5f2917fab9
--- /dev/null
+++ b/dumux/linear/scotchbackend.hh
@@ -0,0 +1,169 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 2 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+
+/*!
+ * \file An interface to the scotch library for matrix reordering
+ * \brief An interface to the scotch library for matrix reordering
+ * \note This is adapted from the FEniCS implementation of the scotch interface
+ */
+#ifndef DUMUX_SCOTCH_BACKEND_HH
+#define DUMUX_SCOTCH_BACKEND_HH
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+#if HAVE_PTSCOTCH
+extern "C"
+{
+#include <stdint.h>
+#include <ptscotch.h>
+}
+#endif
+
+namespace Dumux
+{
+
+template<class IndexType>
+class ScotchBackend
+{
+    using Graph = std::vector<std::vector<IndexType>>;
+
+public:
+    //! Compute reordering (map[old] -> new) using
+    //! Gibbs-Poole-Stockmeyer (GPS) re-ordering
+    static std::vector<int> computeGPSReordering(const Graph& graph,
+                                                 std::size_t numPasses = 5)
+    {
+        // Create strategy string for Gibbs-Poole-Stockmeyer ordering
+        std::string strategy = "g{pass= " + std::to_string(numPasses) + "}";
+        return computeReordering(graph, strategy);
+    }
+
+    // Compute graph re-ordering
+    static std::vector<int> computeReordering(const Graph& graph,
+                                              std::string scotchStrategy = "")
+    {
+        std::vector<int> permutation, inversePermutation;
+        computeReordering(graph, permutation, inversePermutation, scotchStrategy);
+        return permutation;
+    }
+
+    // Compute graph re-ordering
+    static void computeReordering(const Graph& graph,
+                                   std::vector<int>& permutation,
+                                   std::vector<int>& inversePermutation,
+                                   std::string scotchStrategy = "")
+    {
+        // Number of local graph vertices (cells)
+        const SCOTCH_Num vertnbr = graph.size();
+
+        // Data structures for graph input to SCOTCH (add 1 for case that
+        // graph size is zero)
+        std::vector<SCOTCH_Num> verttab;
+        verttab.reserve(vertnbr + 1);
+        std::vector<SCOTCH_Num> edgetab;
+        edgetab.reserve(20*vertnbr);
+
+        // Build local graph input for SCOTCH
+        // (number of graph vertices (cells),
+        //  number of edges connecting two vertices)
+        SCOTCH_Num edgenbr = 0;
+        verttab.push_back(0);
+        typename Graph::const_iterator vertex;
+        for (vertex = graph.begin(); vertex != graph.end(); ++vertex)
+        {
+            edgenbr += vertex->size();
+            verttab.push_back(verttab.back() + vertex->size());
+            edgetab.insert(edgetab.end(), vertex->begin(), vertex->end());
+        }
+
+        // Shrink vectors to hopefully recover an unused memory
+        verttab.shrink_to_fit();
+        edgetab.shrink_to_fit();
+
+        // Create SCOTCH graph
+        SCOTCH_Graph scotchGraph;
+
+        // C-style array indexing
+        const SCOTCH_Num baseval = 0;
+
+        // Create SCOTCH graph and initialise
+        if (SCOTCH_graphInit(&scotchGraph) != 0)
+        {
+            std::cerr << "Error initializing SCOTCH graph!" << std::endl;
+            exit(1);
+        }
+
+        // Build SCOTCH graph
+        if (SCOTCH_graphBuild(&scotchGraph, baseval,
+                              vertnbr, &verttab[0], &verttab[1], NULL, NULL,
+                              edgenbr, &edgetab[0], NULL))
+        {
+            std::cerr << "Error building SCOTCH graph!" << std::endl;
+            exit(1);
+        }
+
+        // Check graph data for consistency
+        // if (SCOTCH_graphCheck(&scotchGraph))
+        // {
+        //     std::cerr << "Consistency error in SCOTCH graph!" << std::endl;
+        //     exit(1);
+        // }
+
+        // Re-ordering strategy
+        SCOTCH_Strat strat;
+        SCOTCH_stratInit(&strat);
+
+        // Set SCOTCH strategy (if provided)
+        if (!scotchStrategy.empty())
+        SCOTCH_stratGraphOrder(&strat, scotchStrategy.c_str());
+
+        // Vector to hold permutation vectors
+        std::vector<SCOTCH_Num> permutationIndices(vertnbr);
+        std::vector<SCOTCH_Num> inversePermutationIndices(vertnbr);
+
+        // Reset SCOTCH random number generator to produce deterministic partitions
+        SCOTCH_randomReset();
+
+        // Compute re-ordering
+        if (SCOTCH_graphOrder(&scotchGraph, &strat, permutationIndices.data(),
+                              inversePermutationIndices.data(), NULL, NULL, NULL))
+        {
+            std::cerr << "SCOTCH: Error during reordering graph!" << std::endl;
+            exit(1);
+        }
+
+        // Clean up SCOTCH objects
+        SCOTCH_graphExit(&scotchGraph);
+        SCOTCH_stratExit(&strat);
+
+        // Copy permutation vectors
+        permutation.resize(vertnbr);
+        inversePermutation.resize(vertnbr);
+        std::copy(permutationIndices.begin(), permutationIndices.end(),
+                  permutation.begin());
+        std::copy(inversePermutationIndices.begin(), inversePermutationIndices.end(),
+                  inversePermutation.begin());
+    }
+};
+
+} // end namespace Dumux
+
+#endif