diff --git a/dumux/common/CMakeLists.txt b/dumux/common/CMakeLists.txt
index 9743710a3e7e12c825c9032a76af3e3b3416acf6..445c4932df251ead8bd512527abc8b4a9c52093c 100644
--- a/dumux/common/CMakeLists.txt
+++ b/dumux/common/CMakeLists.txt
@@ -20,6 +20,7 @@ intersectionmapper.hh
 intrange.hh
 loggingparametertree.hh
 math.hh
+matrixvectorhelper.hh
 optional.hh
 parameters.hh
 pointsource.hh
diff --git a/dumux/common/matrixvectorhelper.hh b/dumux/common/matrixvectorhelper.hh
new file mode 100644
index 0000000000000000000000000000000000000000..44769ee23f09a9b64508bec4eb4ea8d4b14c874b
--- /dev/null
+++ b/dumux/common/matrixvectorhelper.hh
@@ -0,0 +1,97 @@
+// -*- 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
+ * \ingroup Common
+ * \brief Helper functions to be used for memory allocation operations on matrices & vectors
+ *        that can be called for both static and dynamic types. This is useful wherever it
+ *        is not always known if the matrix & vector types at hand are static or dynamic.
+ */
+#ifndef DUMUX_COMMON_MATRIX_VECTOR_HELPER_HH
+#define DUMUX_COMMON_MATRIX_VECTOR_HELPER_HH
+
+#include <type_traits>
+
+namespace Dumux {
+
+//! Determines whether or not a matrix has a resize() function
+template<typename M>
+struct matrix_has_resize_method
+{
+private:
+    typedef std::true_type yes;
+    typedef std::false_type no;
+
+    // resize function is called with two indices for matrices
+    template<typename U> static auto test(int) -> decltype(std::declval<U>().resize(0, 0), yes());
+    template<typename> static no test(...);
+
+public:
+    static constexpr bool value = std::is_same<decltype(test<M>(0)), yes>::value;
+};
+
+//! determines whether or not a vector has a resize() function
+template<typename V>
+struct vector_has_resize_method
+{
+private:
+    typedef std::true_type yes;
+    typedef std::false_type no;
+
+    // resize function is called with one index for vectors
+    template<typename U> static auto test(int) -> decltype(std::declval<U>().resize(0), yes());
+    template<typename> static no test(...);
+
+public:
+    static constexpr bool value = std::is_same<decltype(test<V>(0)), yes>::value;
+};
+
+//! resizes a matrix to the given sizes (specialization for dynamic matrix type)
+template< class Matrix,
+          class size_type,
+          std::enable_if_t<matrix_has_resize_method<Matrix>::value, int> = 0 >
+void resizeMatrix(Matrix& M, size_type rows, size_type cols)
+{
+    M.resize(rows, cols);
+}
+
+//! resizes a matrix to the given sizes (specialization for static matrix type - do nothing)
+template< class Matrix,
+          class size_type,
+          std::enable_if_t<!matrix_has_resize_method<Matrix>::value, int> = 0 >
+void resizeMatrix(Matrix& M, size_type rows, size_type cols) {}
+
+//! resizes a vector to the given size (specialization for dynamic matrix type)
+template< class Vector,
+          class size_type,
+          std::enable_if_t<vector_has_resize_method<Vector>::value, int> = 0 >
+void resizeVector(Vector& v, size_type size)
+{
+    v.resize(size);
+}
+
+//! resizes a vector to the given size (specialization for static vector type - do nothing)
+template< class Vector,
+          class size_type,
+          std::enable_if_t<!vector_has_resize_method<Vector>::value, int> = 0 >
+void resizeVector(Vector& v, size_type rows) {}
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/discretization/cellcentered/mpfa/darcyslaw.hh b/dumux/discretization/cellcentered/mpfa/darcyslaw.hh
index 9f4943bff4f4c77fda79aef01c08391addbbf696..0d7f2a7be9c275cea7faf65ef00b5d8562e03ea2 100644
--- a/dumux/discretization/cellcentered/mpfa/darcyslaw.hh
+++ b/dumux/discretization/cellcentered/mpfa/darcyslaw.hh
@@ -44,7 +44,7 @@ class DarcysLawImplementation;
  * \ingroup CCMpfaDiscretization
  * \brief Darcy's law for cell-centered finite volume schemes with multi-point flux approximation.
  */
-template <class TypeTag>
+template<class TypeTag>
 class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
 {
     using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
@@ -93,73 +93,55 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
         static constexpr int dim = GridView::dimension;
         static constexpr int dimWorld = GridView::dimensionworld;
         static constexpr int numPhases = GET_PROP_VALUE(TypeTag, NumPhases);
-        using GridIndexType = typename GridView::IndexSet::IndexType;
 
-        // In the current implementation of the flux variables cache we cannot
-        // make a disctinction between dynamic (mpfa-o) and static (mpfa-l)
-        // matrix and vector types, as currently the cache class can only be templated
-        // by a type tag (and there can only be one). We use a dynamic vector here to
-        // make sure it works in case one of the two used interaction volume types uses
-        // dynamic types performance is thus lowered for schemes using static types.
-        // TODO: this has to be checked thoroughly as soon as a scheme using static types
-        //       is implemented. One idea to overcome the performance drop could be only
-        //       storing the iv-local index here and obtain tij always from the datahandle
-        //       of the fluxVarsCacheContainer
-        using Vector = Dune::DynamicVector< Scalar >;
-        using Matrix = Dune::DynamicMatrix< Scalar >;
-        using Stencil = std::vector< GridIndexType >;
+        using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
+        static constexpr bool considerSecondaryIVs = MpfaHelper::considerSecondaryIVs();
 
+        // In the current implementation of the flux variables cache we cannot make a
+        // disctinction between dynamic (e.g. mpfa-o unstructured) and static (e.g.mpfa-l)
+        // matrix and vector types, as currently the cache class can only be templated
+        // by a type tag (and there can only be one). Currently, pointers to both the
+        // primary and secondary iv data is stored. Before accessing it has to be checked
+        // whether or not the scvf is embedded in a secondary interaction volume.
         using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
+        using PrimaryIvLocalFaceData = typename PrimaryInteractionVolume::Traits::LocalFaceData;
+        using PrimaryIvDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
         using PrimaryIvVector = typename PrimaryInteractionVolume::Traits::Vector;
         using PrimaryIvMatrix = typename PrimaryInteractionVolume::Traits::Matrix;
-        using PrimaryStencil = typename PrimaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<PrimaryIvVector*, Vector*>::value,
-                       "The vector type used in primary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<PrimaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in primary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<PrimaryStencil*, Stencil*>::value,
-                       "The stencil type used in primary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using PrimaryIvTij = typename PrimaryIvMatrix::row_type;
+        using PrimaryIvStencil = typename PrimaryInteractionVolume::Traits::Stencil;
 
         using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume);
+        using SecondaryIvLocalFaceData = typename SecondaryInteractionVolume::Traits::LocalFaceData;
+        using SecondaryIvDataHandle = typename SecondaryInteractionVolume::Traits::DataHandle;
         using SecondaryIvVector = typename SecondaryInteractionVolume::Traits::Vector;
         using SecondaryIvMatrix = typename SecondaryInteractionVolume::Traits::Matrix;
-        using SecondaryStencil = typename SecondaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<SecondaryIvVector*, Vector*>::value,
-                       "The vector type used in secondary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<SecondaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in secondary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<SecondaryStencil*, Stencil*>::value,
-                       "The stencil type used in secondary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using SecondaryIvTij = typename SecondaryIvMatrix::row_type;
+        using SecondaryIvStencil = typename SecondaryInteractionVolume::Traits::Stencil;
 
     public:
         // export the filler type
         using Filler = MpfaDarcysLawCacheFiller;
 
         /*!
-         * \brief Update cached objects (transmissibilities and gravity)
-         *
-         * \tparam InteractionVolume The (mpfa scheme-specific) interaction volume
-         * \tparam LocalFaceData The object used to store iv-local info on an scvf
-         * \tparam DataHandle The object used to store transmissibility matrices etc.
+         * \brief Update cached objects (transmissibilities and gravity).
+         *        This is used for updates with primary interaction volumes.
          *
          * \param iv The interaction volume this scvf is embedded in
          * \param localFaceData iv-local info on this scvf
          * \param dataHandle Transmissibility matrix & gravity data of this iv
          * \param scvf The sub-control volume face
          */
-        template< class InteractionVolume, class LocalFaceData, class DataHandle >
-        void updateAdvection(const InteractionVolume& iv,
-                             const LocalFaceData& localFaceData,
-                             const DataHandle& dataHandle,
+        void updateAdvection(const PrimaryInteractionVolume& iv,
+                             const PrimaryIvLocalFaceData& localFaceData,
+                             const PrimaryIvDataHandle& dataHandle,
                              const SubControlVolumeFace &scvf)
         {
-            stencil_ = &iv.stencil();
             switchFluxSign_ = localFaceData.isOutside();
+            primaryStencil_ = &iv.stencil();
 
             for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
-                pj_[pIdx] = &dataHandle.pressures(pIdx);
+                primaryPj_[pIdx] = &dataHandle.pressures(pIdx);
 
             static const bool enableGravity = getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Problem.EnableGravity");
             const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
@@ -167,20 +149,17 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
             // standard grids
             if (dim == dimWorld)
             {
-                Tij_ = &dataHandle.advectionT()[ivLocalIdx];
-
+                primaryTij_ = &dataHandle.advectionT()[ivLocalIdx];
                 if (enableGravity)
                     for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
                         g_[phaseIdx] = dataHandle.gravity(phaseIdx)[ivLocalIdx];
             }
-
             // surface grids
             else
             {
                 if (!localFaceData.isOutside())
                 {
-                    Tij_ = &dataHandle.advectionT()[ivLocalIdx];
-
+                    primaryTij_ = &dataHandle.advectionT()[ivLocalIdx];
                     if (enableGravity)
                         for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
                             g_[phaseIdx] = dataHandle.gravity(phaseIdx)[ivLocalIdx];
@@ -188,8 +167,60 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
                 else
                 {
                     const auto idxInOutsideFaces = localFaceData.scvfLocalOutsideScvfIndex();
-                    Tij_ = &dataHandle.advectionTout()[ivLocalIdx][idxInOutsideFaces];
+                    primaryTij_ = &dataHandle.advectionTout()[ivLocalIdx][idxInOutsideFaces];
+                    if (enableGravity)
+                        for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                            g_[phaseIdx] = dataHandle.gravityOutside(phaseIdx)[ivLocalIdx][idxInOutsideFaces];
+                }
+            }
+        }
+
+        /*!
+         * \brief Update cached objects (transmissibilities and gravity).
+         *        This is used for updates with secondary interaction volumes.
+         *
+         * \param iv The interaction volume this scvf is embedded in
+         * \param localFaceData iv-local info on this scvf
+         * \param dataHandle Transmissibility matrix & gravity data of this iv
+         * \param scvf The sub-control volume face
+         */
+        template< bool doSecondary = considerSecondaryIVs, std::enable_if_t<doSecondary, int > = 0 >
+        void updateAdvection(const SecondaryInteractionVolume& iv,
+                             const SecondaryIvLocalFaceData& localFaceData,
+                             const SecondaryIvDataHandle& dataHandle,
+                             const SubControlVolumeFace &scvf)
+        {
+            switchFluxSign_ = localFaceData.isOutside();
+            secondaryStencil_ = &iv.stencil();
+
+            for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
+                secondaryPj_[pIdx] = &dataHandle.pressures(pIdx);
+
+            static const bool enableGravity = getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Problem.EnableGravity");
+            const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
 
+            // standard grids
+            if (dim == dimWorld)
+            {
+                secondaryTij_ = &dataHandle.advectionT()[ivLocalIdx];
+                if (enableGravity)
+                    for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                        g_[phaseIdx] = dataHandle.gravity(phaseIdx)[ivLocalIdx];
+            }
+            // surface grids
+            else
+            {
+                if (!localFaceData.isOutside())
+                {
+                    secondaryTij_ = &dataHandle.advectionT()[ivLocalIdx];
+                    if (enableGravity)
+                        for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                            g_[phaseIdx] = dataHandle.gravity(phaseIdx)[ivLocalIdx];
+                }
+                else
+                {
+                    const auto idxInOutsideFaces = localFaceData.scvfLocalOutsideScvfIndex();
+                    secondaryTij_ = &dataHandle.advectionTout()[ivLocalIdx][idxInOutsideFaces];
                     if (enableGravity)
                         for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
                             g_[phaseIdx] = dataHandle.gravityOutside(phaseIdx)[ivLocalIdx][idxInOutsideFaces];
@@ -197,14 +228,23 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
             }
         }
 
-        //! The stencil corresponding to the transmissibilities
-        const Stencil& advectionStencil() const { return *stencil_; }
+        //! The stencil corresponding to the transmissibilities (primary type)
+        const PrimaryIvStencil& advectionStencilPrimaryIv() const { return *primaryStencil_; }
 
-        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions
-        const Vector& advectionTij() const { return *Tij_; }
+        //! The stencil corresponding to the transmissibilities (secondary type)
+        const SecondaryIvStencil& advectionStencilSecondaryIv() const { return *secondaryStencil_; }
 
-        //! The cell (& Dirichlet) pressures within this interaction volume
-        const Vector& pressures(unsigned int phaseIdx) const { return *pj_[phaseIdx]; }
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (primary type)
+        const PrimaryIvTij& advectionTijPrimaryIv() const { return *primaryTij_; }
+
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (secondary type)
+        const SecondaryIvTij& advectionTijSecondaryIv() const { return *secondaryTij_; }
+
+        //! The cell (& Dirichlet) pressures within this interaction volume (primary type)
+        const PrimaryIvVector& pressuresPrimaryIv(unsigned int phaseIdx) const { return *primaryPj_[phaseIdx]; }
+
+        //! The cell (& Dirichlet) pressures within this interaction volume (secondary type)
+        const SecondaryIvVector& pressuresSecondaryIv(unsigned int phaseIdx) const { return *secondaryPj_[phaseIdx]; }
 
         //! The gravitational acceleration for a phase on this scvf
         Scalar gravity(unsigned int phaseIdx) const { return g_[phaseIdx]; }
@@ -217,10 +257,21 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
 
     private:
         bool switchFluxSign_;
-        const Stencil* stencil_;                  //!< The stencil, i.e. the grid indices j
-        const Vector* Tij_;                       //!< The transmissibilities such that f = Tij*pj
-        std::array< Scalar, numPhases > g_;       //!< Gravitational flux contribution on this face
-        std::array<const Vector*, numPhases> pj_; //!< The interaction-volume wide phase pressures pj
+
+        //! The stencil, i.e. the grid indices j
+        const PrimaryIvStencil* primaryStencil_;
+        const SecondaryIvStencil* secondaryStencil_;
+
+        //! The transmissibilities such that f = Tij*pj
+        const PrimaryIvTij* primaryTij_;
+        const SecondaryIvTij* secondaryTij_;
+
+        //! The interaction-volume wide phase pressures pj
+        std::array<const PrimaryIvVector*, numPhases> primaryPj_;
+        std::array<const SecondaryIvVector*, numPhases> secondaryPj_;
+
+        //! Gravitational flux contribution on this face
+        std::array< Scalar, numPhases > g_;
     };
 
 public:
@@ -240,11 +291,21 @@ public:
                        const ElementFluxVariablesCache& elemFluxVarsCache)
     {
         const auto& fluxVarsCache = elemFluxVarsCache[scvf];
-        const auto& tij = fluxVarsCache.advectionTij();
-        const auto& pj = fluxVarsCache.pressures(phaseIdx);
 
         // compute t_ij*p_j
-        Scalar scvfFlux = tij*pj;
+        Scalar scvfFlux;
+        if (fluxVarsCache.usesSecondaryIv())
+        {
+            const auto& tij = fluxVarsCache.advectionTijSecondaryIv();
+            const auto& pj = fluxVarsCache.pressuresSecondaryIv(phaseIdx);
+            scvfFlux = tij*pj;
+        }
+        else
+        {
+            const auto& tij = fluxVarsCache.advectionTijPrimaryIv();
+            const auto& pj = fluxVarsCache.pressuresPrimaryIv(phaseIdx);
+            scvfFlux = tij*pj;
+        }
 
         // maybe add gravitational acceleration
         static const bool enableGravity = getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Problem.EnableGravity");
diff --git a/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh b/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh
index 313cf34659b7ecd614d60f39fd8e517a13672483..52d4a4568db07b73574042055b9865e479e404be 100644
--- a/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh
+++ b/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh
@@ -123,7 +123,7 @@ class CCMpfaElementFluxVariablesCache<TypeTag, false>
     using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
     using PrimaryIvDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
     using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume);
-    using SecondaryIvDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
+    using SecondaryIvDataHandle = typename SecondaryInteractionVolume::Traits::DataHandle;
 
 public:
     CCMpfaElementFluxVariablesCache(const GridFluxVariablesCache& global)
diff --git a/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh b/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh
index 6269dd5ca43d74fe8bfef2bc84ffc33fd6722a98..cc4ae3707f2d2a6d314aeecaaf38a0a0b731eb1c 100644
--- a/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh
+++ b/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh
@@ -194,7 +194,7 @@ public:
             // Update boundary volume variables in the neighbors
             for (const auto& scvf : scvfs(fvGeometry))
             {
-                if (fvGridGeometry.vertexUsesPrimaryInteractionVolume(scvf.vertexIndex()))
+                if (!fvGridGeometry.vertexUsesSecondaryInteractionVolume(scvf.vertexIndex()))
                 {
                     const auto& nodalIndexSet = gridIvIndexSets.primaryIndexSet(scvf).nodalIndexSet();
                     // if present, insert boundary vol vars
@@ -298,7 +298,7 @@ private:
         std::size_t numBoundaryVolVars = 0;
         for (const auto& scvf : scvfs(fvGeometry))
         {
-            if (fvGridGeometry.vertexUsesPrimaryInteractionVolume(scvf.vertexIndex()))
+            if (!fvGridGeometry.vertexUsesSecondaryInteractionVolume(scvf.vertexIndex()))
                 numBoundaryVolVars += gridIvIndexSets.primaryIndexSet(scvf).nodalIndexSet().numBoundaryScvfs();
             else
                 numBoundaryVolVars += gridIvIndexSets.secondaryIndexSet(scvf).nodalIndexSet().numBoundaryScvfs();
diff --git a/dumux/discretization/cellcentered/mpfa/fickslaw.hh b/dumux/discretization/cellcentered/mpfa/fickslaw.hh
index d3b2d0cc857850c97ccaf277d159ecaf6ccce5d5..be10d54f81548e4e79bf883b6cffde8bf49ad9c2 100644
--- a/dumux/discretization/cellcentered/mpfa/fickslaw.hh
+++ b/dumux/discretization/cellcentered/mpfa/fickslaw.hh
@@ -90,44 +90,30 @@ class FicksLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
     //! The cache used in conjunction with the mpfa Fick's Law
     class MpfaFicksLawCache
     {
-        // In the current implementation of the flux variables cache we cannot
-        // make a disctinction between dynamic (mpfa-o) and static (mpfa-l)
-        // matrix and vector types, as currently the cache class can only be templated
-        // by a type tag (and there can only be one). We use a dynamic vector here to
-        // make sure it works in case one of the two used interaction volume types uses
-        // dynamic types performance is thus lowered for schemes using static types.
-        // TODO: this has to be checked thoroughly as soon as a scheme using static types
-        //       is implemented. One idea to overcome the performance drop could be only
-        //       storing the iv-local index here and obtain tij always from the datahandle
-        //       of the fluxVarsCacheContainer
-        using GridIndexType = typename GridView::IndexSet::IndexType;
-        using Vector = Dune::DynamicVector< Scalar >;
-        using Matrix = Dune::DynamicMatrix< Scalar >;
-        using Stencil = std::vector< GridIndexType >;
+        using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
+        static constexpr bool considerSecondaryIVs = MpfaHelper::considerSecondaryIVs();
 
+        // In the current implementation of the flux variables cache we cannot make a
+        // disctinction between dynamic (e.g. mpfa-o unstructured) and static (e.g.mpfa-l)
+        // matrix and vector types, as currently the cache class can only be templated
+        // by a type tag (and there can only be one). Currently, pointers to both the
+        // primary and secondary iv data is stored. Before accessing it has to be checked
+        // whether or not the scvf is embedded in a secondary interaction volume.
         using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
+        using PrimaryIvLocalFaceData = typename PrimaryInteractionVolume::Traits::LocalFaceData;
+        using PrimaryIvDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
         using PrimaryIvVector = typename PrimaryInteractionVolume::Traits::Vector;
         using PrimaryIvMatrix = typename PrimaryInteractionVolume::Traits::Matrix;
-        using PrimaryStencil = typename PrimaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<PrimaryIvVector*, Vector*>::value,
-                       "The vector type used in primary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<PrimaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in primary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<PrimaryStencil*, Stencil*>::value,
-                       "The stencil type used in primary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using PrimaryIvTij = typename PrimaryIvMatrix::row_type;
+        using PrimaryIvStencil = typename PrimaryInteractionVolume::Traits::Stencil;
 
         using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume);
+        using SecondaryIvLocalFaceData = typename SecondaryInteractionVolume::Traits::LocalFaceData;
+        using SecondaryIvDataHandle = typename SecondaryInteractionVolume::Traits::DataHandle;
         using SecondaryIvVector = typename SecondaryInteractionVolume::Traits::Vector;
         using SecondaryIvMatrix = typename SecondaryInteractionVolume::Traits::Matrix;
-        using SecondaryStencil = typename SecondaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<SecondaryIvVector*, Vector*>::value,
-                       "The vector type used in secondary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<SecondaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in secondary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<SecondaryStencil*, Stencil*>::value,
-                       "The stencil type used in secondary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using SecondaryIvTij = typename SecondaryIvMatrix::row_type;
+        using SecondaryIvStencil = typename SecondaryInteractionVolume::Traits::Stencil;
 
         static constexpr int dim = GridView::dimension;
         static constexpr int dimWorld = GridView::dimensionworld;
@@ -138,36 +124,62 @@ class FicksLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
         using Filler = MpfaFicksLawCacheFiller;
 
         /*!
-         * \brief Update cached objects (transmissibilities)
+         * \brief Update cached objects (transmissibilities).
+         *        This is used for updates with primary interaction volumes.
          *
-         * \tparam InteractionVolume The (mpfa scheme-specific) interaction volume
-         * \tparam LocalFaceData The object used to store iv-local info on an scvf
-         * \tparam DataHandle The object used to store transmissibility matrices etc.
+         * \param iv The interaction volume this scvf is embedded in
+         * \param localFaceData iv-local info on this scvf
+         * \param dataHandle Transmissibility matrix & gravity data of this iv
+         * \param scvf The sub-control volume face
+         */
+        void updateDiffusion(const PrimaryInteractionVolume& iv,
+                             const PrimaryIvLocalFaceData& localFaceData,
+                             const PrimaryIvDataHandle& dataHandle,
+                             const SubControlVolumeFace &scvf,
+                             unsigned int phaseIdx, unsigned int compIdx)
+        {
+            primaryStencil_[phaseIdx][compIdx] = &iv.stencil();
+            switchFluxSign_[phaseIdx][compIdx] = localFaceData.isOutside();
+
+            // store pointer to the mole fraction vector of this iv
+            primaryXj_[phaseIdx][compIdx] = &dataHandle.moleFractions(phaseIdx, compIdx);
+
+            const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
+            if (dim == dimWorld)
+                primaryTij_[phaseIdx][compIdx] = &dataHandle.diffusionT()[ivLocalIdx];
+            else
+                primaryTij_[phaseIdx][compIdx] = localFaceData.isOutside() ? &dataHandle.diffusionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
+                                                                           : &dataHandle.diffusionT()[ivLocalIdx];
+        }
+
+        /*!
+         * \brief Update cached objects (transmissibilities).
+         *        This is used for updates with secondary interaction volumes.
          *
          * \param iv The interaction volume this scvf is embedded in
          * \param localFaceData iv-local info on this scvf
          * \param dataHandle Transmissibility matrix & gravity data of this iv
          * \param scvf The sub-control volume face
          */
-        template<class InteractionVolume, class LocalFaceData, class DataHandle>
-        void updateDiffusion(const InteractionVolume& iv,
-                             const LocalFaceData& localFaceData,
-                             const DataHandle& dataHandle,
+        template< bool doSecondary = considerSecondaryIVs, std::enable_if_t<doSecondary, int > = 0 >
+        void updateDiffusion(const SecondaryInteractionVolume& iv,
+                             const SecondaryIvLocalFaceData& localFaceData,
+                             const SecondaryIvDataHandle& dataHandle,
                              const SubControlVolumeFace &scvf,
                              unsigned int phaseIdx, unsigned int compIdx)
         {
-            stencil_[phaseIdx][compIdx] = &iv.stencil();
+            secondaryStencil_[phaseIdx][compIdx] = &iv.stencil();
             switchFluxSign_[phaseIdx][compIdx] = localFaceData.isOutside();
 
             // store pointer to the mole fraction vector of this iv
-            xj_[phaseIdx][compIdx] = &dataHandle.moleFractions(phaseIdx, compIdx);
+            secondaryXj_[phaseIdx][compIdx] = &dataHandle.moleFractions(phaseIdx, compIdx);
 
             const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
             if (dim == dimWorld)
-                Tij_[phaseIdx][compIdx] = &dataHandle.diffusionT()[ivLocalIdx];
+                secondaryTij_[phaseIdx][compIdx] = &dataHandle.diffusionT()[ivLocalIdx];
             else
-                Tij_[phaseIdx][compIdx] = localFaceData.isOutside() ? &dataHandle.diffusionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
-                                                                    : &dataHandle.diffusionT()[ivLocalIdx];
+                secondaryTij_[phaseIdx][compIdx] = localFaceData.isOutside() ? &dataHandle.diffusionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
+                                                                             : &dataHandle.diffusionT()[ivLocalIdx];
         }
 
         //! In the interaction volume-local system of eq we have one unknown per face.
@@ -177,23 +189,44 @@ class FicksLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
         bool diffusionSwitchFluxSign(unsigned int phaseIdx, unsigned int compIdx) const
         { return switchFluxSign_[phaseIdx][compIdx]; }
 
-        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions
-        const Vector& diffusionTij(unsigned int phaseIdx, unsigned int compIdx) const
-        { return *Tij_[phaseIdx][compIdx]; }
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (primary type)
+        const PrimaryIvTij& diffusionTijPrimaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *primaryTij_[phaseIdx][compIdx]; }
+
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (secondary type)
+        const SecondaryIvTij& diffusionTijSecondaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *secondaryTij_[phaseIdx][compIdx]; }
 
-        //! The cell (& Dirichlet) mole fractions within this interaction volume
-        const Vector& moleFractions(unsigned int phaseIdx, unsigned int compIdx) const
-        { return *xj_[phaseIdx][compIdx]; }
+        //! The cell (& Dirichlet) mole fractions within this interaction volume (primary type)
+        const PrimaryIvVector& moleFractionsPrimaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *primaryXj_[phaseIdx][compIdx]; }
 
-        //! The stencils corresponding to the transmissibilities
-        const Stencil& diffusionStencil(unsigned int phaseIdx, unsigned int compIdx) const
-        { return *stencil_[phaseIdx][compIdx]; }
+        //! The cell (& Dirichlet) mole fractions within this interaction volume (secondary type)
+        const SecondaryIvVector& moleFractionsSecondaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *secondaryXj_[phaseIdx][compIdx]; }
+
+        //! The stencils corresponding to the transmissibilities (primary type)
+        const PrimaryIvStencil& diffusionStencilPrimaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *primaryStencil_[phaseIdx][compIdx]; }
+
+        //! The stencils corresponding to the transmissibilities (secondary type)
+        const SecondaryIvStencil& diffusionStencilSecondaryIv(unsigned int phaseIdx, unsigned int compIdx) const
+        { return *secondaryStencil_[phaseIdx][compIdx]; }
 
     private:
         std::array< std::array<bool, numComponents>, numPhases > switchFluxSign_;
-        std::array< std::array<const Stencil*, numComponents>, numPhases > stencil_;  //!< The stencils, i.e. the grid indices j
-        std::array< std::array<const Vector*, numComponents>, numPhases > Tij_;       //!< The transmissibilities such that f = Tij*xj
-        std::array< std::array<const Vector*, numComponents>, numPhases > xj_;        //!< The interaction-volume wide mole fractions xj
+
+        //! The stencils, i.e. the grid indices j
+        std::array< std::array<const PrimaryIvStencil*, numComponents>, numPhases > primaryStencil_;
+        std::array< std::array<const SecondaryIvStencil*, numComponents>, numPhases > secondaryStencil_;
+
+        //! The transmissibilities such that f = Tij*xj
+        std::array< std::array<const PrimaryIvVector*, numComponents>, numPhases > primaryTij_;
+        std::array< std::array<const SecondaryIvVector*, numComponents>, numPhases > secondaryTij_;
+
+        //! The interaction-volume wide mole fractions xj
+        std::array< std::array<const PrimaryIvVector*, numComponents>, numPhases > primaryXj_;
+        std::array< std::array<const SecondaryIvVector*, numComponents>, numPhases > secondaryXj_;
     };
 
 public:
@@ -212,6 +245,9 @@ public:
                                     const int phaseIdx,
                                     const ElementFluxVariablesCache& elemFluxVarsCache)
     {
+        // obtain this scvf's cache
+        const auto& fluxVarsCache = elemFluxVarsCache[scvf];
+
         ComponentFluxVector componentFlux(0.0);
         for (int compIdx = 0; compIdx < numComponents; compIdx++)
         {
@@ -228,13 +264,21 @@ public:
             // calculate the density at the interface
             const auto rho = interpolateDensity(elemVolVars, scvf, phaseIdx);
 
-            // prepare computations
-            const auto& fluxVarsCache = elemFluxVarsCache[scvf];
-            const auto& tij = fluxVarsCache.diffusionTij(phaseIdx, compIdx);
-            const auto& xj = fluxVarsCache.moleFractions(phaseIdx, compIdx);
-
             // calculate Tij*xj
-            Scalar flux = tij*xj;
+            Scalar flux;
+            if (fluxVarsCache.usesSecondaryIv())
+            {
+                const auto& tij = fluxVarsCache.diffusionTijSecondaryIv(phaseIdx, compIdx);
+                const auto& xj = fluxVarsCache.moleFractionsSecondaryIv(phaseIdx, compIdx);
+                flux = tij*xj;
+            }
+            else
+            {
+                const auto& tij = fluxVarsCache.diffusionTijPrimaryIv(phaseIdx, compIdx);
+                const auto& xj = fluxVarsCache.moleFractionsPrimaryIv(phaseIdx, compIdx);
+                flux = tij*xj;
+            }
+
             if (fluxVarsCache.diffusionSwitchFluxSign(phaseIdx, compIdx))
                 flux *= -1.0;
 
diff --git a/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh b/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh
index 452d0f6883b512869aea2cd05b1e4239bc03f622..f7172553cee3c69c95a8db8a3047dc3a4ff5de9a 100644
--- a/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh
+++ b/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh
@@ -43,6 +43,7 @@ template<class TypeTag>
 class CCMpfaFluxVariablesCacheFiller
 {
     using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
+    using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
     using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
     using Element = typename GridView::template Codim<0>::Entity;
 
@@ -208,6 +209,10 @@ private:
                                         unsigned int ivIndexInContainer,
                                         bool forceUpdateAll = false)
     {
+        // determine if secondary interaction volumes are used here
+        static constexpr bool isSecondary = MpfaHelper::considerSecondaryIVs()
+                                            && std::is_same<InteractionVolume, SecondaryInteractionVolume>::value;
+
         // First we upate data which are not dependent on the physical processes.
         // We store pointers to the other flux var caches, so that we have to obtain
         // this data only once and can use it again in the sub-cache fillers.
@@ -224,6 +229,7 @@ private:
             ivFluxVarCaches[i] = &fluxVarsCacheContainer[scvfJ];
             ivFluxVarCaches[i]->setIvIndexInContainer(ivIndexInContainer);
             ivFluxVarCaches[i]->setUpdateStatus(true);
+            ivFluxVarCaches[i]->setSecondaryIvUsage(isSecondary);
             i++;
         }
 
@@ -253,7 +259,8 @@ private:
         if (AdvectionMethod == DiscretizationMethods::CCMpfa)
         {
             // get instance of the interaction volume-local assembler
-            using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >;
+            using IVTraits = typename InteractionVolume::Traits;
+            using IvLocalAssembler = InteractionVolumeAssembler< IVTraits, InteractionVolume::MpfaMethod >;
             IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars());
 
             // Use different assembly if gravity is enabled
@@ -384,7 +391,8 @@ private:
         static constexpr int numComponents = GET_PROP_VALUE(TypeTag, NumComponents);
 
         // get instance of the interaction volume-local assembler
-        using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >;
+        using IVTraits = typename InteractionVolume::Traits;
+        using IvLocalAssembler = InteractionVolumeAssembler< IVTraits, InteractionVolume::MpfaMethod >;
         IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars());
 
         for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
@@ -486,7 +494,8 @@ private:
         if (HeatConductionMethod == DiscretizationMethods::CCMpfa)
         {
             // get instance of the interaction volume-local assembler
-            using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >;
+            using IVTraits = typename InteractionVolume::Traits;
+            using IvLocalAssembler = InteractionVolumeAssembler< IVTraits, InteractionVolume::MpfaMethod >;
             IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars());
 
             if (forceUpdateAll || soldependentAdvection)
diff --git a/dumux/discretization/cellcentered/mpfa/fourierslaw.hh b/dumux/discretization/cellcentered/mpfa/fourierslaw.hh
index 1c769cf1461ee4dfa385e1d64a2471cd58b96ecf..11e2a4accd03b1fa82106cc6efae2618ee8650fa 100644
--- a/dumux/discretization/cellcentered/mpfa/fourierslaw.hh
+++ b/dumux/discretization/cellcentered/mpfa/fourierslaw.hh
@@ -88,44 +88,30 @@ class FouriersLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
     //! The cache used in conjunction with the mpfa Fourier's Law
     class MpfaFouriersLawCache
     {
-        // In the current implementation of the flux variables cache we cannot
-        // make a disctinction between dynamic (mpfa-o) and static (mpfa-l)
-        // matrix and vector types, as currently the cache class can only be templated
-        // by a type tag (and there can only be one). We use a dynamic vector here to
-        // make sure it works in case one of the two used interaction volume types uses
-        // dynamic types performance is thus lowered for schemes using static types.
-        // TODO: this has to be checked thoroughly as soon as a scheme using static types
-        //       is implemented. One idea to overcome the performance drop could be only
-        //       storing the iv-local index here and obtain tij always from the datahandle
-        //       of the fluxVarsCacheContainer
-        using GridIndexType = typename GridView::IndexSet::IndexType;
-        using Vector = Dune::DynamicVector< Scalar >;
-        using Matrix = Dune::DynamicMatrix< Scalar >;
-        using Stencil = std::vector< GridIndexType >;
+        using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
+        static constexpr bool considerSecondaryIVs = MpfaHelper::considerSecondaryIVs();
 
+        // In the current implementation of the flux variables cache we cannot make a
+        // disctinction between dynamic (e.g. mpfa-o unstructured) and static (e.g.mpfa-l)
+        // matrix and vector types, as currently the cache class can only be templated
+        // by a type tag (and there can only be one). Currently, pointers to both the
+        // primary and secondary iv data is stored. Before accessing it has to be checked
+        // whether or not the scvf is embedded in a secondary interaction volume.
         using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
+        using PrimaryIvLocalFaceData = typename PrimaryInteractionVolume::Traits::LocalFaceData;
+        using PrimaryIvDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
         using PrimaryIvVector = typename PrimaryInteractionVolume::Traits::Vector;
         using PrimaryIvMatrix = typename PrimaryInteractionVolume::Traits::Matrix;
-        using PrimaryStencil = typename PrimaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<PrimaryIvVector*, Vector*>::value,
-                       "The vector type used in primary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<PrimaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in primary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<PrimaryStencil*, Stencil*>::value,
-                       "The stencil type used in primary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using PrimaryIvTij = typename PrimaryIvMatrix::row_type;
+        using PrimaryIvStencil = typename PrimaryInteractionVolume::Traits::Stencil;
 
         using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume);
+        using SecondaryIvLocalFaceData = typename SecondaryInteractionVolume::Traits::LocalFaceData;
+        using SecondaryIvDataHandle = typename SecondaryInteractionVolume::Traits::DataHandle;
         using SecondaryIvVector = typename SecondaryInteractionVolume::Traits::Vector;
         using SecondaryIvMatrix = typename SecondaryInteractionVolume::Traits::Matrix;
-        using SecondaryStencil = typename SecondaryInteractionVolume::Traits::Stencil;
-
-        static_assert( std::is_convertible<SecondaryIvVector*, Vector*>::value,
-                       "The vector type used in secondary interaction volumes is not convertible to Dune::DynamicVector!" );
-        static_assert( std::is_convertible<SecondaryIvMatrix*, Matrix*>::value,
-                       "The matrix type used in secondary interaction volumes is not convertible to Dune::DynamicMatrix!" );
-        static_assert( std::is_convertible<SecondaryStencil*, Stencil*>::value,
-                       "The stencil type used in secondary interaction volumes is not convertible to std::vector<GridIndexType>!" );
+        using SecondaryIvTij = typename SecondaryIvMatrix::row_type;
+        using SecondaryIvStencil = typename SecondaryInteractionVolume::Traits::Stencil;
 
         static constexpr int dim = GridView::dimension;
         static constexpr int dimWorld = GridView::dimensionworld;
@@ -135,42 +121,79 @@ class FouriersLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
         using Filler = MpfaFouriersLawCacheFiller;
 
         /*!
-         * \brief Update cached objects (transmissibilities)
+         * \brief Update cached objects (transmissibilities).
+         *        This is used for updates with primary interaction volumes.
          *
-         * \tparam InteractionVolume The (mpfa scheme-specific) interaction volume
-         * \tparam LocalFaceData The object used to store iv-local info on an scvf
-         * \tparam DataHandle The object used to store transmissibility matrices etc.
+         * \param iv The interaction volume this scvf is embedded in
+         * \param localFaceData iv-local info on this scvf
+         * \param dataHandle Transmissibility matrix & gravity data of this iv
+         * \param scvf The sub-control volume face
+         */
+        void updateHeatConduction(const PrimaryInteractionVolume& iv,
+                                  const PrimaryIvLocalFaceData& localFaceData,
+                                  const PrimaryIvDataHandle& dataHandle,
+                                  const SubControlVolumeFace &scvf)
+        {
+            primaryStencil_ = &iv.stencil();
+            switchFluxSign_ = localFaceData.isOutside();
+
+            // store pointer to the temperature vector of this iv
+            primaryTj_ = &dataHandle.temperatures();
+
+            const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
+            if (dim == dimWorld)
+                primaryTij_ = &dataHandle.heatConductionT()[ivLocalIdx];
+            else
+                primaryTij_ = localFaceData.isOutside() ? &dataHandle.heatConductionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
+                                                        : &dataHandle.heatConductionT()[ivLocalIdx];
+        }
+
+        /*!
+         * \brief Update cached objects (transmissibilities).
+         *        This is used for updates with secondary interaction volumes.
          *
          * \param iv The interaction volume this scvf is embedded in
          * \param localFaceData iv-local info on this scvf
          * \param dataHandle Transmissibility matrix & gravity data of this iv
          * \param scvf The sub-control volume face
          */
-        template<class InteractionVolume, class LocalFaceData, class DataHandle>
-        void updateHeatConduction(const InteractionVolume& iv,
-                                  const LocalFaceData& localFaceData,
-                                  const DataHandle& dataHandle,
+        template< bool doSecondary = considerSecondaryIVs, std::enable_if_t<doSecondary, int > = 0 >
+        void updateHeatConduction(const SecondaryInteractionVolume& iv,
+                                  const SecondaryIvLocalFaceData& localFaceData,
+                                  const SecondaryIvDataHandle& dataHandle,
                                   const SubControlVolumeFace &scvf)
         {
-            stencil_ = &iv.stencil();
+            secondaryStencil_ = &iv.stencil();
             switchFluxSign_ = localFaceData.isOutside();
 
             // store pointer to the temperature vector of this iv
-            Tj_ = &dataHandle.temperatures();
+            secondaryTj_ = &dataHandle.temperatures();
 
             const auto ivLocalIdx = localFaceData.ivLocalScvfIndex();
             if (dim == dimWorld)
-                Tij_ = &dataHandle.heatConductionT()[ivLocalIdx];
+                secondaryTij_ = &dataHandle.heatConductionT()[ivLocalIdx];
             else
-                Tij_ = localFaceData.isOutside() ? &dataHandle.heatConductionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
-                                                 : &dataHandle.heatConductionT()[ivLocalIdx];
+                secondaryTij_ = localFaceData.isOutside() ? &dataHandle.heatConductionTout()[ivLocalIdx][localFaceData.scvfLocalOutsideScvfIndex()]
+                                                          : &dataHandle.heatConductionT()[ivLocalIdx];
         }
 
-        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions
-        const Vector& heatConductionTij() const { return *Tij_; }
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (primary type)
+        const PrimaryIvTij& heatConductionTijPrimaryIv() const { return *primaryTij_; }
+
+        //! Coefficients for the cell (& Dirichlet) unknowns in flux expressions (secondary type)
+        const SecondaryIvTij& heatConductionTijSecondaryIv() const { return *secondaryTij_; }
 
-        //! The stencil corresponding to the transmissibilities
-        const Stencil& heatConductionStencil() const { return *stencil_; }
+        //! The stencil corresponding to the transmissibilities (primary type)
+        const PrimaryIvStencil& heatConductionStencilPrimaryIv() const { return *primaryStencil_; }
+
+        //! The stencil corresponding to the transmissibilities (secondary type)
+        const SecondaryIvStencil& heatConductionStencilSecondaryIv() const { return *secondaryStencil_; }
+
+        //! The cell (& Dirichlet) temperatures within this interaction volume (primary type)
+        const PrimaryIvVector& temperaturesPrimaryIv() const { return *primaryTj_; }
+
+        //! The cell (& Dirichlet) temperatures within this interaction volume (secondary type)
+        const SecondaryIvVector& temperaturesSecondaryIv() const { return *secondaryTj_; }
 
         //! In the interaction volume-local system of eq we have one unknown per face.
         //! On scvfs on this face, but in "outside" (neighbor) elements of it, we have
@@ -178,14 +201,20 @@ class FouriersLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
         //! This function returns whether or not this scvf is an "outside" face in the iv.
         bool heatConductionSwitchFluxSign() const { return switchFluxSign_; }
 
-        //! The cell (& Dirichlet) temperatures within this interaction volume
-        const Vector& temperatures() const { return *Tj_; }
-
     private:
         bool switchFluxSign_;
-        const Stencil* stencil_; //!< The stencil, i.e. the grid indices j
-        const Vector* Tij_;      //!< The transmissibilities such that f = Tij*Tj
-        const Vector* Tj_;       //!< The interaction-volume wide temperature Tj
+
+        //! The stencil, i.e. the grid indices j
+        const PrimaryIvStencil* primaryStencil_;
+        const SecondaryIvStencil* secondaryStencil_;
+
+        //! The transmissibilities such that f = Tij*Tj
+        const PrimaryIvTij* primaryTij_;
+        const SecondaryIvTij* secondaryTij_;
+
+        //! The interaction-volume wide temperature Tj
+        const PrimaryIvVector* primaryTj_;
+        const SecondaryIvVector* secondaryTj_;
     };
 
 public:
@@ -204,11 +233,22 @@ public:
                        const ElementFluxVarsCache& elemFluxVarsCache)
     {
         const auto& fluxVarsCache = elemFluxVarsCache[scvf];
-        const auto& tij = fluxVarsCache.heatConductionTij();
-        const auto& Tj = fluxVarsCache.temperatures();
 
         // compute Tij*tj
-        Scalar flux = tij*Tj;
+        Scalar flux;
+        if (fluxVarsCache.usesSecondaryIv())
+        {
+            const auto& tij = fluxVarsCache.heatConductionTijSecondaryIv();
+            const auto& Tj = fluxVarsCache.temperaturesSecondaryIv();
+            flux = tij*Tj;
+        }
+        else
+        {
+            const auto& tij = fluxVarsCache.heatConductionTijPrimaryIv();
+            const auto& Tj = fluxVarsCache.temperaturesPrimaryIv();
+            flux = tij*Tj;
+        }
+
         if (fluxVarsCache.heatConductionSwitchFluxSign())
             flux *= -1.0;
 
diff --git a/dumux/discretization/cellcentered/mpfa/fvgridgeometry.hh b/dumux/discretization/cellcentered/mpfa/fvgridgeometry.hh
index 6570faf63e0fefc9bdc36783619684bc6250d49a..1900565f2e1773126ff75095ce3117ee49bd9f11 100644
--- a/dumux/discretization/cellcentered/mpfa/fvgridgeometry.hh
+++ b/dumux/discretization/cellcentered/mpfa/fvgridgeometry.hh
@@ -124,22 +124,17 @@ public:
     //! Get an element from a sub control volume contained in it
     Element element(const SubControlVolume& scv) const { return this->elementMap()[scv.elementIndex()]; }
 
-    //! Returns true if primary interaction volumes are used around a given vertex.
-    bool vertexUsesPrimaryInteractionVolume(const Vertex& v) const
-    { return primaryInteractionVolumeVertices_[this->vertexMapper().index(v)]; }
-
-    //!Returns true if primary interaction volumes are used around a vertex (index).
-    bool vertexUsesPrimaryInteractionVolume(GridIndexType vIdxGlobal) const
-    { return primaryInteractionVolumeVertices_[vIdxGlobal]; }
-
-    //! Returns if primary interaction volumes are used around a given vertex.
-    bool vertexUsesSecondaryInteractionVolume(const Vertex& v) const
-    { return secondaryInteractionVolumeVertices_[this->vertexMapper().index(v)]; }
-
-    //! Returns true if primary interaction volumes are used around a given vertex index.
+    //! Returns true if secondary interaction volumes are used around a given vertex (index).
+    //! This specialization is enabled if the use of secondary interaction volumes is active.
+    template<bool useSecondary = MpfaHelper::considerSecondaryIVs(), std::enable_if_t<useSecondary, int> = 0>
     bool vertexUsesSecondaryInteractionVolume(GridIndexType vIdxGlobal) const
     { return secondaryInteractionVolumeVertices_[vIdxGlobal]; }
 
+    //! Returns true if secondary interaction volumes are used around a given vertex (index).
+    //! If the use of secondary interaction volumes is disabled, this can be evaluated at compile time.
+    template<bool useSecondary = MpfaHelper::considerSecondaryIVs(), std::enable_if_t<!useSecondary, int> = 0>
+    constexpr bool vertexUsesSecondaryInteractionVolume(GridIndexType vIdxGlobal) const { return false; }
+
     //! update all fvElementGeometries (do this again after grid adaption)
     void update()
     {
@@ -163,7 +158,6 @@ public:
 
         // Some methods require to use a second type of interaction volume, e.g.
         // around vertices on the boundary or branching points (surface grids)
-        primaryInteractionVolumeVertices_.resize(numVert, true);
         secondaryInteractionVolumeVertices_.resize(numVert, false);
 
         // find vertices on processor boundaries
@@ -245,10 +239,7 @@ public:
 
                     // if this vertex is tagged to use the secondary IVs, store info
                     if (usesSecondaryIV)
-                    {
-                        primaryInteractionVolumeVertices_[vIdxGlobal] = false;
                         secondaryInteractionVolumeVertices_[vIdxGlobal] = true;
-                    }
 
                     // the quadrature point parameterizarion to be used on scvfs
                     static const Scalar q = getParamFromGroup<Scalar>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Mpfa.Q");
@@ -381,7 +372,6 @@ private:
 
     // containers storing the global data
     std::vector<std::vector<GridIndexType>> scvfIndicesOfScv_;
-    std::vector<bool> primaryInteractionVolumeVertices_;
     std::vector<bool> secondaryInteractionVolumeVertices_;
     std::size_t numBoundaryScvf_;
 
@@ -472,22 +462,17 @@ public:
     //! Gets an element from a sub control volume contained in it.
     Element element(const SubControlVolume& scv) const { return this->elementMap()[scv.elementIndex()]; }
 
-    //! Returns true if primary interaction volumes are used around a given vertex.
-    bool vertexUsesPrimaryInteractionVolume(const Vertex& v) const
-    { return primaryInteractionVolumeVertices_[this->vertexMapper().index(v)]; }
-
-    //! Returns true if primary interaction volumes are used around a given vertex (index).
-    bool vertexUsesPrimaryInteractionVolume(GridIndexType vIdxGlobal) const
-    { return primaryInteractionVolumeVertices_[vIdxGlobal]; }
-
-    //! Returns if primary interaction volumes are used around a given vertex.
-    bool vertexUsesSecondaryInteractionVolume(const Vertex& v) const
-    { return secondaryInteractionVolumeVertices_[this->vertexMapper().index(v)]; }
-
-    //! Returns true if primary interaction volumes are used around a given vertex (index).
+    //! Returns true if secondary interaction volumes are used around a given vertex (index).
+    //! This specialization is enabled if the use of secondary interaction volumes is active.
+    template<bool useSecondary = MpfaHelper::considerSecondaryIVs(), std::enable_if_t<useSecondary, int> = 0>
     bool vertexUsesSecondaryInteractionVolume(GridIndexType vIdxGlobal) const
     { return secondaryInteractionVolumeVertices_[vIdxGlobal]; }
 
+    //! Returns true if secondary interaction volumes are used around a given vertex (index).
+    //! If the use of secondary interaction volumes is disabled, this can be evaluated at compile time.
+    template<bool useSecondary = MpfaHelper::considerSecondaryIVs(), std::enable_if_t<!useSecondary, int> = 0>
+    constexpr bool vertexUsesSecondaryInteractionVolume(GridIndexType vIdxGlobal) const { return false; }
+
     //! Returns true if a given vertex lies on a processor boundary inside a ghost element.
     bool isGhostVertex(const Vertex& v) const { return isGhostVertex_[this->vertexMapper().index(v)]; }
 
@@ -510,7 +495,6 @@ public:
         // Some methods require to use a second type of interaction volume, e.g.
         // around vertices on the boundary or branching points (surface grids)
         const auto numVert = this->gridView().size(dim);
-        primaryInteractionVolumeVertices_.resize(numVert, true);
         secondaryInteractionVolumeVertices_.resize(numVert, false);
 
         // find vertices on processor boundaries HERE!!
@@ -588,10 +572,7 @@ public:
 
                     // if this vertex is tagged to use the secondary IVs, store info
                     if (usesSecondaryIV)
-                    {
-                        primaryInteractionVolumeVertices_[vIdxGlobal] = false;
                         secondaryInteractionVolumeVertices_[vIdxGlobal] = true;
-                    }
 
                     // make the scv face (for non-boundary scvfs on network grids, use precalculated outside indices)
                     const auto& outsideScvIndices = [&] ()
@@ -665,7 +646,6 @@ private:
     // containers storing the global data
     std::vector<std::vector<GridIndexType>> scvfIndicesOfScv_;
     std::vector< std::vector< std::vector<GridIndexType> > > neighborVolVarIndices_;
-    std::vector<bool> primaryInteractionVolumeVertices_;
     std::vector<bool> secondaryInteractionVolumeVertices_;
     std::vector<bool> isGhostVertex_;
     std::size_t numScvs_;
diff --git a/dumux/discretization/cellcentered/mpfa/gridinteractionvolumeindexsets.hh b/dumux/discretization/cellcentered/mpfa/gridinteractionvolumeindexsets.hh
index 0cc347bf1b8dbaddd29f667868b7dcdaf6918b4a..5a7a7882f0ddfcfe07e9b4ed0005244d9e0ef336 100644
--- a/dumux/discretization/cellcentered/mpfa/gridinteractionvolumeindexsets.hh
+++ b/dumux/discretization/cellcentered/mpfa/gridinteractionvolumeindexsets.hh
@@ -75,7 +75,7 @@ public:
         for (const auto& vertex : vertices(fvGridGeometry.gridView()))
         {
             const auto vIdxGlobal = fvGridGeometry.vertexMapper().index(vertex);
-            if (fvGridGeometry.vertexUsesPrimaryInteractionVolume(vIdxGlobal))
+            if (!fvGridGeometry.vertexUsesSecondaryInteractionVolume(vIdxGlobal))
                 numPrimaryIV_ += PrimaryIV::numInteractionVolumesAtVertex((*dualGridIndexSet_)[vIdxGlobal]);
             else
                 numSecondaryIV_ += SecondaryIV::numInteractionVolumesAtVertex((*dualGridIndexSet_)[vIdxGlobal]);
@@ -90,7 +90,7 @@ public:
         for (const auto& vertex : vertices(fvGridGeometry.gridView()))
         {
             const auto vIdxGlobal = fvGridGeometry.vertexMapper().index(vertex);
-            if (fvGridGeometry.vertexUsesPrimaryInteractionVolume(vIdxGlobal))
+            if (!fvGridGeometry.vertexUsesSecondaryInteractionVolume(vIdxGlobal))
                 PrimaryIV::addInteractionVolumeIndexSets(primaryIVIndexSets_, scvfIndexMap_, (*dualGridIndexSet_)[vIdxGlobal]);
             else
                 SecondaryIV::addInteractionVolumeIndexSets(secondaryIVIndexSets_, scvfIndexMap_, (*dualGridIndexSet_)[vIdxGlobal]);
diff --git a/dumux/discretization/cellcentered/mpfa/helper.hh b/dumux/discretization/cellcentered/mpfa/helper.hh
index e4ce6f1a1ca66cc0d0fa9cd95b2902638fc2c50c..c103d99aea74775dc5bdc26f2fdf5033a53f4fb8 100644
--- a/dumux/discretization/cellcentered/mpfa/helper.hh
+++ b/dumux/discretization/cellcentered/mpfa/helper.hh
@@ -560,16 +560,16 @@ class CCMpfaHelperImplementation : public MpfaDimensionHelper<TypeTag, dim, dimW
     using Element = typename GridView::template Codim<0>::Entity;
     using IndexType = typename GridView::IndexSet::IndexType;
 
+    using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
+    using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume);
+
     using VertexMapper = typename GET_PROP_TYPE(TypeTag, VertexMapper);
     using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
     using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
     using ScvfCornerVector = typename SubControlVolumeFace::Traits::CornerStorage;
 
-    using InteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
-    using LocalIndexType = typename InteractionVolume::Traits::LocalIndexType;
-    using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>;
-
     using CoordScalar = typename GridView::ctype;
+    using GlobalPosition = Dune::FieldVector<CoordScalar, dimWorld>;
     using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>;
 
 public:
@@ -627,6 +627,11 @@ public:
         return ghostVertices;
     }
 
+    //! Returns whether or not secondary interaction volumes have to be considered in the model.
+    //! This is always the case when the specified types for the interaction volumes differ.
+    static constexpr bool considerSecondaryIVs()
+    { return !std::is_same<PrimaryInteractionVolume, SecondaryInteractionVolume>::value; }
+
     //! returns whether or not a value exists in a vector
     template<typename V1, typename V2>
     static bool vectorContainsValue(const std::vector<V1>& vector, const V2 value)
diff --git a/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh b/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh
index 381d501f304321864e68fbea9a29092590eee16d..51ac0be10eebe29b0c12ce5c8df5480725f1c059 100644
--- a/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh
+++ b/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh
@@ -24,6 +24,8 @@
 #ifndef DUMUX_DISCRETIZATION_CC_MPFA_INTERACTIONVOLUMEBASE_HH
 #define DUMUX_DISCRETIZATION_CC_MPFA_INTERACTIONVOLUMEBASE_HH
 
+#include <dune/common/exceptions.hh>
+
 #include <dumux/common/properties.hh>
 
 namespace Dumux
@@ -59,8 +61,6 @@ namespace Dumux
  * using LocalScvfType = ...;
  * //! export the type of used for the iv-local face data
  * using LocalFaceData = ...;
- * //! export the type of face data container
- * using LocalFaceDataContainer = ...;
  * //! export the type used for iv-local matrices
  * using Matrix = ...;
  * //! export the type used for iv-local vectors
@@ -84,8 +84,8 @@ template< class Impl, class T>
 class CCMpfaInteractionVolumeBase
 {
     // Curiously recurring template pattern
-    Impl & asImp() { return static_cast<Impl&>(*this); }
-    const Impl & asImp() const { return static_cast<const Impl&>(*this); }
+    Impl& asImp() { return static_cast<Impl&>(*this); }
+    const Impl& asImp() const { return static_cast<const Impl&>(*this); }
 
     using Problem = typename T::Problem;
     using FVElementGeometry = typename T::FVElementGeometry;
@@ -116,11 +116,11 @@ public:
     //! returns the number of scvs embedded in this interaction volume
     std::size_t numScvs() const { return asImp().numScvs(); }
 
-    //! returns the number of scvfs embedded in this interaction volume
-    std::size_t numScvfs() const { return asImp().numScvfs(); }
-
-    //! returns a reference to the container with the local face data
-    const typename Traits::LocalFaceDataContainer& localFaceData() const { asImp().localFaceData(); }
+    //! Returns a reference to the container with the local face data. The actual type of
+    //! the container depends on the interaction volume implementation. At this point we throw
+    //! an exception and force the implementation to overload this function.
+    const std::vector<typename Traits::LocalFaceData>& localFaceData() const
+    { DUNE_THROW(Dune::NotImplemented, "Interaction volume implementation does not provide a localFaceData() funtion"); }
 
     //! returns the cell-stencil of this interaction volume
     const typename Traits::Stencil& stencil() const { return asImp().stencil(); }
diff --git a/dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh b/dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh
index f91f5e9e97b9bff3a61488fed65abd01eeab9be3..7a41428485d51205c0db03158f21ed971346ac07 100644
--- a/dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh
+++ b/dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh
@@ -28,6 +28,7 @@
 
 #include <dumux/common/properties.hh>
 #include <dumux/common/parameters.hh>
+#include <dumux/common/matrixvectorhelper.hh>
 
 namespace Dumux
 {
@@ -63,17 +64,17 @@ public:
     void resize(const InteractionVolume& iv)
     {
         // resize transmissibility matrix & pressure vectors
-        T_.resize(iv.numFaces(), iv.numKnowns());
+        resizeMatrix(T_, iv.numFaces(), iv.numKnowns());
         for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
-            p_[pIdx].resize(iv.numKnowns());
+            resizeVector(p_[pIdx], iv.numKnowns());
 
         // maybe resize gravity container
         static const bool enableGravity = getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Problem.EnableGravity");
         if (enableGravity)
         {
-            CA_.resize(iv.numFaces(), iv.numUnknowns());
+            resizeMatrix(CA_, iv.numFaces(), iv.numUnknowns());
             for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
-                g_[pIdx].resize(iv.numFaces());
+                resizeVector(g_[pIdx], iv.numFaces());
         }
     }
 
@@ -86,31 +87,31 @@ public:
 
         if (!enableGravity)
         {
-            T_.resize(iv.numFaces(), iv.numKnowns());
+            resizeMatrix(T_, iv.numFaces(), iv.numKnowns());
             outsideT_.resize(iv.numFaces());
-            for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
-                p_[pIdx].resize(iv.numKnowns());
 
+            for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
+                resizeVector(p_[pIdx], iv.numKnowns());
             for (LocalIndexType i = 0; i < iv.numFaces(); ++i)
             {
                 const auto numNeighbors = iv.localScvf(i).neighboringLocalScvIndices().size() - 1;
                 outsideT_[i].resize(numNeighbors);
                 for (LocalIndexType j = 0; j < numNeighbors; ++j)
-                    outsideT_[i][j].resize(iv.numKnowns());
+                    resizeVector(outsideT_[i][j], iv.numKnowns());
             }
         }
 
         else
         {
-            T_.resize(iv.numFaces(), iv.numKnowns());
-            CA_.resize(iv.numFaces(), iv.numUnknowns());
-            A_.resize(iv.numUnknowns(), iv.numUnknowns());
+            resizeMatrix(T_, iv.numFaces(), iv.numKnowns());
+            resizeMatrix(CA_, iv.numFaces(), iv.numUnknowns());
+            resizeMatrix(A_, iv.numUnknowns(), iv.numUnknowns());
             outsideT_.resize(iv.numFaces());
 
             for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
             {
-                p_[pIdx].resize(iv.numKnowns());
-                g_[pIdx].resize(iv.numFaces());
+                resizeVector(p_[pIdx], iv.numKnowns());
+                resizeVector(g_[pIdx], iv.numFaces());
                 outsideG_[pIdx].resize(iv.numFaces());
             }
 
@@ -120,12 +121,10 @@ public:
                 outsideT_[i].resize(numNeighbors);
 
                 for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
-                    outsideG_[pIdx][i].resize(numNeighbors);
-
+                    resizeVector(outsideG_[pIdx][i], numNeighbors);
                 for (LocalIndexType j = 0; j < numNeighbors; ++j)
-                    outsideT_[i][j].resize(iv.numKnowns());
+                    resizeVector(outsideT_[i][j], iv.numKnowns());
             }
-
         }
     }
 
@@ -208,8 +207,8 @@ public:
                     continue;
 
                 // resize transmissibility matrix & mole fraction vector
-                T_[pIdx][cIdx].resize(iv.numFaces(), iv.numKnowns());
-                xj_[pIdx][cIdx].resize(iv.numKnowns());
+                resizeMatrix(T_[pIdx][cIdx], iv.numFaces(), iv.numKnowns());
+                resizeVector(xj_[pIdx][cIdx], iv.numKnowns());
 
                 // resize outsideTij on surface grids
                 if (dim < dimWorld)
@@ -218,7 +217,12 @@ public:
 
                     using LocalIndexType = typename InteractionVolume::Traits::LocalIndexType;
                     for (LocalIndexType i = 0; i < iv.numFaces(); ++i)
-                      outsideT_[pIdx][cIdx][i].resize(iv.localScvf(i).neighboringLocalScvIndices().size()-1);
+                    {
+                        const auto numNeighbors = iv.localScvf(i).neighboringLocalScvIndices().size() - 1;
+                        outsideT_[pIdx][cIdx][i].resize(numNeighbors);
+                        for (LocalIndexType j = 0; j < numNeighbors; ++j)
+                            resizeVector(outsideT_[pIdx][cIdx][i][j], iv.numKnowns());
+                    }
                 }
             }
         }
@@ -261,8 +265,8 @@ public:
     void resize(const InteractionVolume& iv)
     {
         //! resize transmissibility matrix & temperature vector
-        T_.resize(iv.numFaces(), iv.numKnowns());
-        Tj_.resize(iv.numKnowns());
+        resizeMatrix(T_, iv.numFaces(), iv.numKnowns());
+        resizeVector(Tj_, iv.numKnowns());
 
         //! resize outsideTij on surface grids
         if (dim < dimWorld)
@@ -271,7 +275,12 @@ public:
 
             using LocalIndexType = typename InteractionVolume::Traits::LocalIndexType;
             for (LocalIndexType i = 0; i < iv.numFaces(); ++i)
-              outsideT_[i].resize(iv.localScvf(i).neighboringLocalScvIndices().size()-1);
+            {
+                const auto numNeighbors = iv.localScvf(i).neighboringLocalScvIndices().size() - 1;
+                outsideT_[i].resize(numNeighbors);
+                for (LocalIndexType j = 0; j < numNeighbors; ++j)
+                    resizeVector(outsideT_[i][j], iv.numKnowns());
+            }
         }
     }
 
diff --git a/dumux/discretization/cellcentered/mpfa/localassembler.hh b/dumux/discretization/cellcentered/mpfa/localassembler.hh
index 2c4aec915797e28ea9dc8e1381deebf22c33f506..d5eb6bf8289f43f07a475a4cf63aaf24546e5b77 100644
--- a/dumux/discretization/cellcentered/mpfa/localassembler.hh
+++ b/dumux/discretization/cellcentered/mpfa/localassembler.hh
@@ -28,14 +28,16 @@
 
 #include <dune/common/exceptions.hh>
 
+#include <dumux/discretization/cellcentered/mpfa/methods.hh>
+
 namespace Dumux
 {
 //! Forward declaration of the implementation
-template< class InteractionVolume > class InteractionVolumeAssemblerImpl;
+template< class IVTraits, MpfaMethods M > class InteractionVolumeAssemblerImpl;
 
 //! Alias to select the right implementation.
-template< class InteractionVolume >
-using InteractionVolumeAssembler = InteractionVolumeAssemblerImpl< InteractionVolume >;
+template< class IVTraits, MpfaMethods M >
+using InteractionVolumeAssembler = InteractionVolumeAssemblerImpl< IVTraits, M >;
 
 /*!
  * \ingroup CCMpfaDiscretization
@@ -45,19 +47,17 @@ using InteractionVolumeAssembler = InteractionVolumeAssemblerImpl< InteractionVo
  *        for the available interaction volume implementations. these
  *        should derive from this base clases.
  *
- * \tparam Interaction Volume The interaction volume implementation
- *                            used by the mpfa scheme
+ * \tparam IVTraits The Traits class used by the interaction volume
  */
-template< class InteractionVolume >
+template< class IVTraits >
 class InteractionVolumeAssemblerBase
 {
-    using Traits = typename InteractionVolume::Traits;
-    using Matrix = typename Traits::Matrix;
-    using Vector = typename Traits::Vector;
+    using Matrix = typename IVTraits::Matrix;
+    using Vector = typename IVTraits::Vector;
 
-    using Problem = typename Traits::Problem;
-    using FVElementGeometry = typename Traits::FVElementGeometry;
-    using ElementVolumeVariables = typename Traits::ElementVolumeVariables;
+    using Problem = typename IVTraits::Problem;
+    using FVElementGeometry = typename IVTraits::FVElementGeometry;
+    using ElementVolumeVariables = typename IVTraits::ElementVolumeVariables;
 
   public:
     /*!
@@ -86,15 +86,16 @@ class InteractionVolumeAssemblerBase
      * \brief General interface of a function assembling the
      *        interaction volume-local transmissibility matrix.
      *
-     * \tparam GetTensorFunction Lambda to obtain the tensor w.r.t.
-     *                           which the local system is to be solved
+     * \tparam IV Interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
      *
      * \param T The transmissibility matrix to be assembled
      * \param iv The interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GetTensorFunction >
-    void assemble(Matrix& T, InteractionVolume& iv, const GetTensorFunction& getTensor)
+    template< class IV, class GetTensor >
+    void assemble(Matrix& T, IV& iv, const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assemble() function");
     }
@@ -102,16 +103,21 @@ class InteractionVolumeAssemblerBase
     /*!
      * \brief General interface of a function assembling the interaction
      *        volume-local transmissibilities matrix for surface grids. The
-     *        transmissibility associated with "outside" faces are stored
+     *        transmissibilities associated with "outside" faces are stored
      *        in a separate container.
      *
+     * \tparam TOutside Container to store the "outside" transmissibilities
+     * \tparam IV Interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param outsideTij tij on "outside" faces to be assembled
      * \param T The transmissibility matrix tij to be assembled
      * \param iv The mpfa-o interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class OutsideTijContainer, class GetTensorFunction >
-    void assemble(OutsideTijContainer& outsideTij, Matrix& T, InteractionVolume& iv, const GetTensorFunction& getTensor)
+    template< class TOutside, class IV, class GetTensor >
+    void assemble(TOutside& outsideTij, Matrix& T, IV& iv, const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assemble() function to be used on surface grids");
     }
@@ -121,10 +127,11 @@ class InteractionVolumeAssemblerBase
      *        volume-local transmissibility matrix in the case that gravity
      *        is to be considered in the local system of equations.
      *
-     * \tparam GravityContainer The type of container used to store the
-     *                          gravitational acceleration per scvf & phase
-     * \tparam GetTensorFunction Lambda to obtain the tensor w.r.t.
-     *                           which the local system is to be solved
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
      *
      * \param T The transmissibility matrix to be assembled
      * \param g Container to assemble gravity per scvf & phase
@@ -132,12 +139,8 @@ class InteractionVolumeAssemblerBase
      * \param iv The interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GravityContainer, class GetTensorFunction >
-    void assembleWithGravity(Matrix& T,
-                             GravityContainer& g,
-                             Matrix& CA,
-                             InteractionVolume& iv,
-                             const GetTensorFunction& getTensor)
+    template< class GC, class IV, class GetTensor >
+    void assembleWithGravity(Matrix& T, GC& g, Matrix& CA, IV& iv, const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assembleWithGravity() function");
     }
@@ -150,6 +153,14 @@ class InteractionVolumeAssemblerBase
      *        gravitational flux contributions on "outside" faces are stored
      *        in a separate container.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GOut Type of container used to store gravity on "outside" faces
+     * \tparam TOutside Container to store the "outside" transmissibilities
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param outsideTij tij on "outside" faces to be assembled
      * \param T The transmissibility matrix to be assembled
      * \param outsideG Container to assemble gravity on "outside" faces
@@ -159,18 +170,15 @@ class InteractionVolumeAssemblerBase
      * \param iv The interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GravityContainer,
-              class OutsideGravityContainer,
-              class OutsideTijContainer,
-              class GetTensorFunction >
-    void assembleWithGravity(OutsideTijContainer& outsideTij,
+    template< class GC, class GOut, class TOutside, class IV, class GetTensor >
+    void assembleWithGravity(TOutside& outsideTij,
                              Matrix& T,
-                             OutsideGravityContainer& outsideG,
-                             GravityContainer& g,
+                             GOut& outsideG,
+                             GC& g,
                              Matrix& CA,
                              Matrix& A,
-                             InteractionVolume& iv,
-                             const GetTensorFunction& getTensor)
+                             IV& iv,
+                             const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assembleWithGravity() function to be used on surface grids");
     }
@@ -180,14 +188,15 @@ class InteractionVolumeAssemblerBase
      *        primary (cell) unknowns and (maybe) Dirichlet boundary
      *        conditions within an interaction volume.
      *
+     * \tparam IV The interaction volume type implementation
      * \tparam GetU Lambda to obtain the cell unknowns from grid indices
      *
      * \param u The vector to be filled with the cell unknowns
      * \param iv The interaction volume
      * \param getU Lambda to obtain the desired cell value from grid indices
      */
-    template< class GetU >
-    void assemble(Vector& u, const InteractionVolume& iv, const GetU& getU)
+    template< class IV, class GetU >
+    void assemble(Vector& u, const IV& iv, const GetU& getU)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assemble() function for the cell unknowns");
     }
@@ -200,17 +209,19 @@ class InteractionVolumeAssemblerBase
      *        evaluated. Thus, make sure to only call this with a lambda that returns the
      *        hydraulic conductivity.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param g Container to assemble gravity per scvf & phase
      * \param iv The interaction volume
      * \param CA Projection matrix transforming the gravity terms in the local system of
      *        equations to the entire set of faces within the interaction volume
      * \param getTensor Lambda to evaluate scv-wise hydraulic conductivities
      */
-    template< class GravityContainer, class GetTensorFunction >
-    void assembleGravity(GravityContainer& g,
-                         const InteractionVolume& iv,
-                         const Matrix& CA,
-                         const GetTensorFunction& getTensor)
+    template< class GC, class IV, class GetTensor >
+    void assembleGravity(GC& g, const IV& iv, const Matrix& CA, const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assembleGravity() function");
     }
@@ -225,6 +236,13 @@ class InteractionVolumeAssemblerBase
      *        evaluated. Thus, make sure to only call this with a lambda that returns the
      *        hydraulic conductivity.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GOut Type of container used to store gravity on "outside" faces
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param g Container to store gravity per scvf & phase
      * \param outsideG Container to store gravity per "outside" scvf & phase
      * \param iv The mpfa-o interaction volume
@@ -233,15 +251,13 @@ class InteractionVolumeAssemblerBase
      * \param A Matrix needed for the "reconstruction" of face unknowns as a function of gravity
      * \param getTensor Lambda to evaluate scv-wise hydraulic conductivities
      */
-    template< class GravityContainer,
-              class OutsideGravityContainer,
-              class GetTensorFunction >
-    void assembleGravity(GravityContainer& g,
-                         OutsideGravityContainer& outsideG,
-                         const InteractionVolume& iv,
+    template< class GC, class GOut, class IV, class GetTensor >
+    void assembleGravity(GC& g,
+                         GOut& outsideG,
+                         const IV& iv,
                          const Matrix& CA,
                          const Matrix& A,
-                         const GetTensorFunction& getTensor)
+                         const GetTensor& getTensor)
     {
         DUNE_THROW(Dune::NotImplemented, "Implementation does not provide a assembleGravity() function to be used on surface grids");
     }
diff --git a/dumux/discretization/cellcentered/mpfa/localfacedata.hh b/dumux/discretization/cellcentered/mpfa/localfacedata.hh
index 886677085a732e72ee3531954facd04e3ecd5eac..1fe6489b2deb3729fc1da0f0f70f76f8f8811a9c 100644
--- a/dumux/discretization/cellcentered/mpfa/localfacedata.hh
+++ b/dumux/discretization/cellcentered/mpfa/localfacedata.hh
@@ -47,6 +47,9 @@ class InteractionVolumeLocalFaceData
     bool isOutside_;                           //!< indicates if this face maps to the iv-local index from "outside"
 
 public:
+    //! Default constructor
+    InteractionVolumeLocalFaceData() = default;
+
     //! Constructor
     InteractionVolumeLocalFaceData(LocalIndexType faceIndex,
                                    LocalIndexType scvIndex,
diff --git a/dumux/discretization/cellcentered/mpfa/omethod/CMakeLists.txt b/dumux/discretization/cellcentered/mpfa/omethod/CMakeLists.txt
index d2c61c2a7b45d6ba942dfb77a7933b45875f6e52..9b738e874ef3af320d2723311d20062f1b75d976 100644
--- a/dumux/discretization/cellcentered/mpfa/omethod/CMakeLists.txt
+++ b/dumux/discretization/cellcentered/mpfa/omethod/CMakeLists.txt
@@ -3,4 +3,5 @@ interactionvolume.hh
 interactionvolumeindexset.hh
 localassembler.hh
 localsubcontrolentities.hh
+staticinteractionvolume.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/discretization/cellcentered/mpfa/omethod)
diff --git a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh
index 0e8cd035849e40762e10acbbd009997d8317a4f5..298ec506bd6461955c8b68fe6ee4c25d3417d8ab 100644
--- a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh
+++ b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh
@@ -30,6 +30,7 @@
 
 #include <dumux/common/math.hh>
 #include <dumux/common/properties.hh>
+#include <dumux/common/matrixvectorhelper.hh>
 
 #include <dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh>
 #include <dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh>
@@ -77,8 +78,6 @@ public:
     using LocalScvfType = CCMpfaOInteractionVolumeLocalScvf< IndexSet >;
     //! export the type of used for the iv-local face data
     using LocalFaceData = InteractionVolumeLocalFaceData<GridIndexType, LocalIndexType>;
-    //! export the type of face data container (use dynamic container here)
-    using LocalFaceDataContainer = std::vector< LocalFaceData >;
     //! export the type used for iv-local matrices
     using Matrix = Dune::DynamicMatrix< ScalarType >;
     //! export the type used for iv-local vectors
@@ -92,6 +91,8 @@ public:
 /*!
  * \ingroup CCMpfaDiscretization
  * \brief Class for the interaction volume of the mpfa-o method.
+ *        This implementation creates dynamic objects of the local geometries
+ *        and can be used at boundaries and on unstructured grids.
  */
 template< class Traits >
 class CCMpfaOInteractionVolume : public CCMpfaInteractionVolumeBase< CCMpfaOInteractionVolume<Traits>, Traits >
@@ -120,6 +121,14 @@ class CCMpfaOInteractionVolume : public CCMpfaInteractionVolumeBase< CCMpfaOInte
     using LocalIndexType = typename Traits::LocalIndexType;
     using Stencil = typename Traits::Stencil;
 
+    //! For the o method, the interaction volume stencil can be taken directly
+    //! from the nodal index set, which always uses dynamic types to be compatible
+    //! on the boundaries and unstructured grids. Thus, we have to make sure that
+    //! the type set for the stencils in the traits is castable.
+    static_assert( std::is_convertible<Stencil*, typename IndexSet::GridIndexContainer*>::value,
+                   "The o-method uses the (dynamic) nodal index set's stencil as the interaction volume stencil. "
+                   "Using a different type is not permissive here." );
+
     //! Data attached to scvf touching Dirichlet boundaries.
     //! For the default o-scheme, we only store the corresponding vol vars index.
     class DirichletData
@@ -240,10 +249,10 @@ public:
             }
         }
 
-        // Resize local matrices
-        A_.resize(numUnknowns_, numUnknowns_);
-        B_.resize(numUnknowns_, numKnowns_);
-        C_.resize(numFaces_, numUnknowns_);
+        // Maybe resize local matrices if dynamic types are used
+        resizeMatrix(A_, numUnknowns_, numUnknowns_);
+        resizeMatrix(B_, numUnknowns_, numKnowns_);
+        resizeMatrix(C_, numFaces_, numUnknowns_);
     }
 
     //! returns the number of primary scvfs of this interaction volume
@@ -258,9 +267,6 @@ public:
     //! returns the number of scvs embedded in this interaction volume
     std::size_t numScvs() const { return scvs_.size(); }
 
-    //! returns the number of scvfs embedded in this interaction volume
-    std::size_t numScvfs() const { return scvfs_.size(); }
-
     //! returns the cell-stencil of this interaction volume
     const Stencil& stencil() const { return *stencil_; }
 
@@ -271,7 +277,7 @@ public:
     const LocalScvfType& localScvf(const LocalIndexType ivLocalScvfIdx) const { return scvfs_[ivLocalScvfIdx]; }
 
     //! returns the local scv entity corresponding to a given iv-local scv idx
-    const LocalScvType& localScv(const LocalIndexType ivLocalScvfIdx) const { return scvs_[ivLocalScvfIdx]; }
+    const LocalScvType& localScv(const LocalIndexType ivLocalScvIdx) const { return scvs_[ivLocalScvIdx]; }
 
     //! returns a reference to the container with the local face data
     const std::vector<LocalFaceData>& localFaceData() const { return localFaceData_; }
@@ -334,7 +340,7 @@ private:
     Matrix B_;
     Matrix C_;
 
-    // The omega factors are stored during assemble of local system
+    // The omega factors are stored during assembly of local system
     std::vector< std::vector< DimVector > > wijk_;
 
     // sizes involved in the local system equations
diff --git a/dumux/discretization/cellcentered/mpfa/omethod/localassembler.hh b/dumux/discretization/cellcentered/mpfa/omethod/localassembler.hh
index aabba86066a1ab1dd05ddb45f1a62f3006210c49..4b5f11aa27cf41a64c3a2cb8b0124f6db21bfb4d 100644
--- a/dumux/discretization/cellcentered/mpfa/omethod/localassembler.hh
+++ b/dumux/discretization/cellcentered/mpfa/omethod/localassembler.hh
@@ -20,17 +20,18 @@
  * \file
  * \ingroup CCMpfaDiscretization
  * \brief Class for the assembly of the local systems of equations
- *        involved in the transmissibility computaion in the mpfa-o scheme.
+ *        involved in the transmissibility computaion in an mpfa-o type manner.
  */
 #ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_LOCAL_ASSEMBLER_HH
 #define DUMUX_DISCRETIZATION_CC_MPFA_O_LOCAL_ASSEMBLER_HH
 
 #include <dumux/common/math.hh>
 #include <dumux/common/properties.hh>
+#include <dumux/common/matrixvectorhelper.hh>
 
+#include <dumux/discretization/cellcentered/mpfa/methods.hh>
 #include <dumux/discretization/cellcentered/mpfa/localassembler.hh>
 #include <dumux/discretization/cellcentered/mpfa/computetransmissibility.hh>
-#include <dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh>
 
 namespace Dumux
 {
@@ -38,16 +39,15 @@ namespace Dumux
 /*!
  * \ingroup CCMpfaDiscretization
  * \brief Specialization of the interaction volume-local
- *        assembler class for the mpfa-o scheme.
+ *        assembler class for the schemes using an mpfa-o type assembly.
  *
- * \tparam IVTraits The traits class of the interaction volume
+ * \tparam IVTraits The traits class used by the interaction volume implemetation
  */
 template< class IVTraits >
-class InteractionVolumeAssemblerImpl< CCMpfaOInteractionVolume<IVTraits> >
-      : public InteractionVolumeAssemblerBase< CCMpfaOInteractionVolume<IVTraits> >
+class InteractionVolumeAssemblerImpl< IVTraits, MpfaMethods::oMethod >
+      : public InteractionVolumeAssemblerBase< IVTraits >
 {
-    using InteractionVolume = CCMpfaOInteractionVolume< IVTraits >;
-    using ParentType = InteractionVolumeAssemblerBase< InteractionVolume >;
+    using ParentType = InteractionVolumeAssemblerBase< IVTraits >;
 
     using LocalIndexType = typename IVTraits::LocalIndexType;
     using Matrix = typename IVTraits::Matrix;
@@ -61,15 +61,19 @@ public:
     using ParentType::ParentType;
 
     /*!
-     * \brief Assembles the transmissibility matrix
-     *        within an interaction volume for the mpfa-o scheme.
+     * \brief Assembles the transmissibility matrix within an
+     *        interaction volume in an mpfa-o type way.
+     *
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
      *
      * \param T The transmissibility matrix to be assembled
-     * \param iv The mpfa-o interaction volume
+     * \param iv The interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GetTensorFunction >
-    void assemble(Matrix& T, InteractionVolume& iv, const GetTensorFunction& getTensor)
+    template< class IV, class GetTensor >
+    void assemble(Matrix& T, IV& iv, const GetTensor& getTensor)
     {
         // assemble D into T directly
         assembleLocalMatrices_(iv.A(), iv.B(), iv.C(), T, iv, getTensor);
@@ -89,13 +93,18 @@ public:
      *        matrix for surface grids. The transmissibilities associated
      *        with "outside" faces are stored in a separate container.
      *
+     * \tparam TOutside Container to store the "outside" transmissibilities
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param outsideTij tij on "outside" faces to be assembled
      * \param T The transmissibility matrix tij to be assembled
-     * \param iv The mpfa-o interaction volume
+     * \param iv The interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class OutsideTijContainer, class GetTensorFunction >
-    void assemble(OutsideTijContainer& outsideTij, Matrix& T, InteractionVolume& iv, const GetTensorFunction& getTensor)
+    template< class TOutside, class IV, class GetTensor >
+    void assemble(TOutside& outsideTij, Matrix& T, IV& iv, const GetTensor& getTensor)
     {
         // assemble D into T directly
         assembleLocalMatrices_(iv.A(), iv.B(), iv.C(), T, iv, getTensor);
@@ -154,18 +163,20 @@ public:
      *        computation in the case that gravity is to be considered in
      *        the local system of equations.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param T The transmissibility matrix to be assembled
      * \param g Container to assemble gravity per scvf & phase
      * \param CA Matrix to store matrix product C*A^-1
      * \param iv The mpfa-o interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GravityContainer, class GetTensorFunction >
-    void assembleWithGravity(Matrix& T,
-                             GravityContainer& g,
-                             Matrix& CA,
-                             InteractionVolume& iv,
-                             const GetTensorFunction& getTensor)
+    template< class GC, class IV, class GetTensor >
+    void assembleWithGravity(Matrix& T, GC& g, Matrix& CA, IV& iv, const GetTensor& getTensor)
     {
         // assemble D into T & C into CA directly
         assembleLocalMatrices_(iv.A(), iv.B(), CA, T, iv, getTensor);
@@ -190,6 +201,14 @@ public:
      *        on surface grids, where the gravitational flux contributions
      *        on "outside" faces are stored in a separate container.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GOut Type of container used to store gravity on "outside" faces
+     * \tparam TOutside Container to store the "outside" transmissibilities
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param outsideTij tij on "outside" faces to be assembled
      * \param T The transmissibility matrix to be assembled
      * \param outsideG Container to assemble gravity on "outside" faces
@@ -199,18 +218,15 @@ public:
      * \param iv The mpfa-o interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GravityContainer,
-              class OutsideGravityContainer,
-              class OutsideTijContainer,
-              class GetTensorFunction >
-    void assembleWithGravity(OutsideTijContainer& outsideTij,
+    template< class GC, class GOut, class TOutside, class IV, class GetTensor >
+    void assembleWithGravity(TOutside& outsideTij,
                              Matrix& T,
-                             OutsideGravityContainer& outsideG,
-                             GravityContainer& g,
+                             GOut& outsideG,
+                             GC& g,
                              Matrix& CA,
                              Matrix& A,
-                             InteractionVolume& iv,
-                             const GetTensorFunction& getTensor)
+                             IV& iv,
+                             const GetTensor& getTensor)
     {
         // assemble D into T directly
         assembleLocalMatrices_(iv.A(), iv.B(), iv.C(), T, iv, getTensor);
@@ -271,18 +287,17 @@ public:
      * \brief Assembles the vector of primary (cell) unknowns and (maybe)
      *        Dirichlet boundary conditions within an interaction volume.
      *
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetU Lambda to obtain the cell unknowns from grid indices
+     *
      * \param u The vector to be filled with the cell unknowns
      * \param iv The mpfa-o interaction volume
      * \param getU Lambda to obtain the desired cell/Dirichlet value from grid index
      */
-    template< class GetU >
-    void assemble(Vector& u, const InteractionVolume& iv, const GetU& getU)
+    template< class IV, class GetU >
+    void assemble(Vector& u, const IV& iv, const GetU& getU)
     {
-        // resize given container
-        u.resize(iv.numKnowns());
-
         // put the cell pressures first
-
         for (LocalIndexType i = 0; i < iv.numScvs(); ++i)
             u[i] = getU( iv.localScv(i).globalScvIndex() );
 
@@ -300,17 +315,19 @@ public:
      *        evaluated. Thus, make sure to only call this with a lambda that returns the
      *        hydraulic conductivity.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param g Container to assemble gravity per scvf & phase
      * \param iv The mpfa-o interaction volume
      * \param CA Projection matrix transforming the gravity terms in the local system of
      *        equations to the entire set of faces within the interaction volume
      * \param getTensor Lambda to evaluate scv-wise hydraulic conductivities
      */
-    template< class GravityContainer, class GetTensorFunction >
-    void assembleGravity(GravityContainer& g,
-                         const InteractionVolume& iv,
-                         const Matrix& CA,
-                         const GetTensorFunction& getTensor)
+    template< class GC, class IV, class GetTensor >
+    void assembleGravity(GC& g, const IV& iv, const Matrix& CA, const GetTensor& getTensor)
     {
         //! We require the gravity container to be a two-dimensional vector/array type, structured as follows:
         //! - first index adresses the respective phases
@@ -329,8 +346,14 @@ public:
 
         // reset gravity containers to zero
         const auto numPhases = g.size();
-        std::vector< Vector > sum_alphas(numPhases, Vector(iv.numUnknowns(), 0.0));
-        std::for_each(g.begin(), g.end(), [&iv](auto& v) { v = 0.0; });
+        std::vector< Vector > sum_alphas(numPhases);
+
+        for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
+        {
+            resizeVector(sum_alphas[pIdx], iv.numUnknowns());
+            sum_alphas[pIdx] = 0.0;
+            g[pIdx] = 0.0;
+        }
 
         for (LocalIndexType faceIdx = 0; faceIdx < iv.numFaces(); ++faceIdx)
         {
@@ -412,6 +435,13 @@ public:
      *        evaluated. Thus, make sure to only call this with a lambda that returns the
      *        hydraulic conductivity.
      *
+     * \tparam GC The type of container used to store the
+     *            gravitational acceleration per scvf & phase
+     * \tparam GOut Type of container used to store gravity on "outside" faces
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param g Container to store gravity per scvf & phase
      * \param outsideG Container to store gravity per "outside" scvf & phase
      * \param iv The mpfa-o interaction volume
@@ -420,15 +450,13 @@ public:
      * \param A Matrix needed for the "reconstruction" of face unknowns as a function of gravity
      * \param getTensor Lambda to evaluate scv-wise hydraulic conductivities
      */
-    template< class GravityContainer,
-              class OutsideGravityContainer,
-              class GetTensorFunction >
-    void assembleGravity(GravityContainer& g,
-                         OutsideGravityContainer& outsideG,
-                         const InteractionVolume& iv,
+    template< class GC, class GOut, class IV, class GetTensor >
+    void assembleGravity(GC& g,
+                         GOut& outsideG,
+                         const IV& iv,
                          const Matrix& CA,
                          const Matrix& A,
-                         const GetTensorFunction& getTensor)
+                         const GetTensor& getTensor)
     {
         //! We require the gravity container to be a two-dimensional vector/array type, structured as follows:
         //! - first index adresses the respective phases
@@ -453,10 +481,12 @@ public:
 
         // reset everything to zero
         const auto numPhases = g.size();
-        std::vector< Vector > sum_alphas(numPhases, Vector(iv.numUnknowns(), 0.0));
+        std::vector< Vector > sum_alphas(numPhases);
 
         for (unsigned int pIdx = 0; pIdx < numPhases; ++pIdx)
         {
+            resizeVector(sum_alphas[pIdx], iv.numUnknowns());
+            sum_alphas[pIdx] = 0.0;
             g[pIdx] = 0.0;
             std::for_each(outsideG[pIdx].begin(), outsideG[pIdx].end(), [] (auto& v) { v = 0.0; });
         }
@@ -585,6 +615,10 @@ private:
      *
      * \note  The matrices are expected to have been resized beforehand.
      *
+     * \tparam IV The interaction volume type implementation
+     * \tparam GetTensor Lambda to obtain the tensor w.r.t.
+     *                   which the local system is to be solved
+     *
      * \param A The A matrix of the iv-local equation system
      * \param B The B matrix of the iv-local equation system
      * \param C The C matrix of the iv-local flux expressions
@@ -592,10 +626,9 @@ private:
      * \param iv The mpfa-o interaction volume
      * \param getTensor Lambda to evaluate the scv-wise tensors
      */
-    template< class GetTensorFunction >
+    template< class IV, class GetTensor >
     void assembleLocalMatrices_(Matrix& A, Matrix& B,  Matrix& C, Matrix& D,
-                                InteractionVolume& iv,
-                                const GetTensorFunction& getTensor)
+                                IV& iv, const GetTensor& getTensor)
     {
         // Matrix D is assumed to have the right size already
         assert(D.rows() == iv.numFaces() && D.cols() == iv.numKnowns() && "Matrix D does not have the correct size");
diff --git a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh
index 51ab083d0fa7e72d407da8d8925192a8edc490f5..7ddded18c3abba12cc578707e0358606da02597d 100644
--- a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh
+++ b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh
@@ -54,6 +54,9 @@ public:
     static constexpr int myDimension = dim;
     static constexpr int worldDimension = dimWorld;
 
+    //! The default constructor
+    CCMpfaOInteractionVolumeLocalScv() = default;
+
     /*!
      * \brief The constructor
      *
@@ -69,7 +72,7 @@ public:
                                      const SubControlVolume& scv,
                                      const LocalIndexType localIndex,
                                      const IvIndexSet& indexSet)
-    : indexSet_(indexSet)
+    : indexSet_(&indexSet)
     , globalScvIndex_(scv.dofIndex())
     , localDofIndex_(localIndex)
     {
@@ -103,7 +106,7 @@ public:
     LocalIndexType scvfIdxLocal(unsigned int coordDir) const
     {
         assert(coordDir < myDimension);
-        return indexSet_.scvfIdxLocal(localDofIndex_, coordDir);
+        return indexSet_->scvfIdxLocal(localDofIndex_, coordDir);
     }
 
     //! the nu vectors are needed for setting up the omegas of the iv
@@ -114,7 +117,7 @@ public:
     }
 
 private:
-    const IvIndexSet& indexSet_;
+    const IvIndexSet* indexSet_;
     GridIndexType globalScvIndex_;
     LocalIndexType localDofIndex_;
     LocalBasis nus_;
@@ -138,6 +141,9 @@ public:
     using GridIndexType = typename IvIndexSet::GridIndexType;
     using LocalIndexType = typename IvIndexSet::LocalIndexType;
 
+    //! The default constructor
+    CCMpfaOInteractionVolumeLocalScvf() = default;
+
     /*!
      * \brief The constructor
      *
@@ -154,7 +160,7 @@ public:
     : isDirichlet_(isDirichlet)
     , scvfIdxGlobal_(scvf.index())
     , localDofIndex_(localDofIdx)
-    , neighborScvIndicesLocal_(localScvIndices)
+    , neighborScvIndicesLocal_(&localScvIndices)
     {}
 
     //! This is either the iv-local index of the intermediate unknown (interior/Neumann face)
@@ -165,7 +171,7 @@ public:
     GridIndexType globalScvfIndex() const { return scvfIdxGlobal_; }
 
     //! Returns the local indices of the scvs neighboring this scvf
-    const LocalIndexContainer& neighboringLocalScvIndices() const { return neighborScvIndicesLocal_; }
+    const LocalIndexContainer& neighboringLocalScvIndices() const { return *neighborScvIndicesLocal_; }
 
     //! states if this is scvf is on a Dirichlet boundary
     bool isDirichlet() const { return isDirichlet_; }
@@ -174,7 +180,7 @@ private:
     bool isDirichlet_;
     GridIndexType scvfIdxGlobal_;
     LocalIndexType localDofIndex_;
-    const LocalIndexContainer& neighborScvIndicesLocal_;
+    const LocalIndexContainer* neighborScvIndicesLocal_;
 };
 
 } // end namespace Dumux
diff --git a/dumux/discretization/cellcentered/mpfa/omethod/staticinteractionvolume.hh b/dumux/discretization/cellcentered/mpfa/omethod/staticinteractionvolume.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d4401e178678120e2d3c807aa683281c052f6a35
--- /dev/null
+++ b/dumux/discretization/cellcentered/mpfa/omethod/staticinteractionvolume.hh
@@ -0,0 +1,317 @@
+// -*- 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
+ * \ingroup CCMpfaDiscretization
+ * \brief Class for the interaction volume of the mpfa-o scheme to be used when the
+ *        sizes are known at compile time, e.g. for non-boundary interaction volumes
+ *        on structured grids.
+ */
+#ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_STATIC_INTERACTIONVOLUME_HH
+#define DUMUX_DISCRETIZATION_CC_MPFA_O_STATIC_INTERACTIONVOLUME_HH
+
+#include <dune/common/fmatrix.hh>
+#include <dune/common/fvector.hh>
+
+#include <dumux/common/math.hh>
+#include <dumux/common/properties.hh>
+#include <dumux/common/matrixvectorhelper.hh>
+
+#include <dumux/discretization/cellcentered/mpfa/interactionvolumedatahandle.hh>
+#include <dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh>
+#include <dumux/discretization/cellcentered/mpfa/dualgridindexset.hh>
+#include <dumux/discretization/cellcentered/mpfa/localfacedata.hh>
+#include <dumux/discretization/cellcentered/mpfa/methods.hh>
+
+#include "localsubcontrolentities.hh"
+#include "interactionvolumeindexset.hh"
+
+namespace Dumux
+{
+//! Forward declaration of the o-method's static interaction volume
+template< class Traits, int localSize > class CCMpfaOStaticInteractionVolume;
+
+//! Specialization of the default interaction volume traits class for the mpfa-o method
+template< class TypeTag, int localSize >
+struct CCMpfaODefaultStaticInteractionVolumeTraits
+{
+private:
+    using GridIndexType = typename GET_PROP_TYPE(TypeTag, GridView)::IndexSet::IndexType;
+    static constexpr int dim = GET_PROP_TYPE(TypeTag, GridView)::dimension;
+    static constexpr int dimWorld = GET_PROP_TYPE(TypeTag, GridView)::dimensionworld;
+
+public:
+    //! export the problem type (needed for iv-local assembly)
+    using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
+    //! export the type of the local view on the finite volume grid geometry
+    using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
+    //! export the type of the local view on the grid volume variables
+    using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables);
+    //! export the type of grid view
+    using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
+    //! export the type used for scalar values
+    using ScalarType = typename GET_PROP_TYPE(TypeTag, Scalar);
+    //! export the type of the mpfa helper class
+    using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
+    //! export the type used for local indices
+    using LocalIndexType = std::uint8_t;
+    //! export the type for the interaction volume index set
+    using IndexSet = CCMpfaOInteractionVolumeIndexSet< DualGridNodalIndexSet<GridIndexType, LocalIndexType, dim> >;
+    //! export the type of interaction-volume local scvs
+    using LocalScvType = CCMpfaOInteractionVolumeLocalScv< IndexSet, ScalarType, dim, dimWorld >;
+    //! export the type of interaction-volume local scvfs
+    using LocalScvfType = CCMpfaOInteractionVolumeLocalScvf< IndexSet >;
+    //! export the type of used for the iv-local face data
+    using LocalFaceData = InteractionVolumeLocalFaceData<GridIndexType, LocalIndexType>;
+    //! export the type used for iv-local matrices
+    using Matrix = Dune::FieldMatrix< ScalarType, localSize, localSize >;
+    //! export the type used for iv-local vectors
+    using Vector = Dune::FieldVector< ScalarType, localSize >;
+    //! export the type used for the iv-stencils
+    using Stencil = std::vector< GridIndexType >;
+    //! export the data handle type for this iv
+    using DataHandle = InteractionVolumeDataHandle< TypeTag, Matrix, Vector, LocalIndexType >;
+};
+
+/*!
+ * \ingroup CCMpfaDiscretization
+ * \brief Class for the interaction volume of the mpfa-o method.
+ *        This implementation creates static objects of the local geometries
+ *        and can only be used for interior interaction volumes with a static
+ *        size known at compile time. This size has to match the sizes of the
+ *        vectors/matrices defined in the traits class in case static types are used.
+ *
+ * \tparam Traits The type traits class to be used
+ * \tparam localSize The size of the local eq system
+ *                   This also is the number of local scvs/scvfs
+ */
+template< class Traits, int localSize >
+class CCMpfaOStaticInteractionVolume : public CCMpfaInteractionVolumeBase< CCMpfaOInteractionVolume<Traits>, Traits >
+{
+    using ThisType = CCMpfaOInteractionVolume< Traits >;
+    using ParentType = CCMpfaInteractionVolumeBase< CCMpfaOInteractionVolume<Traits>, Traits >;
+
+    using Scalar = typename Traits::ScalarType;
+    using Helper = typename Traits::MpfaHelper;
+    using Problem = typename Traits::Problem;
+    using FVElementGeometry = typename Traits::FVElementGeometry;
+
+    using GridView = typename Traits::GridView;
+    using Element = typename GridView::template Codim<0>::Entity;
+
+    static constexpr int dim = GridView::dimension;
+    using DimVector = Dune::FieldVector<Scalar, dim>;
+
+    using Matrix = typename Traits::Matrix;
+    using LocalScvType = typename Traits::LocalScvType;
+    using LocalScvfType = typename Traits::LocalScvfType;
+    using LocalFaceData = typename Traits::LocalFaceData;
+
+    using IndexSet = typename Traits::IndexSet;
+    using GridIndexType = typename GridView::IndexSet::IndexType;
+    using LocalIndexType = typename Traits::LocalIndexType;
+    using Stencil = typename Traits::Stencil;
+
+    //! For the o method, the interaction volume stencil can be taken directly
+    //! from the nodal index set, which always uses dynamic types to be compatible
+    //! on the boundaries and unstructured grids. Thus, we have to make sure that
+    //! the type set for the stencils in the traits is castable.
+    static_assert( std::is_convertible<Stencil*, typename IndexSet::GridIndexContainer*>::value,
+                   "The o-method uses the (dynamic) nodal index set's stencil as the interaction volume stencil. "
+                   "Using a different type is not permissive here." );
+
+    //! Data attached to scvf touching Dirichlet boundaries.
+    //! For the default o-scheme, we only store the corresponding vol vars index.
+    class DirichletData
+    {
+        GridIndexType volVarIndex_;
+
+    public:
+        //! Constructor
+        DirichletData(const GridIndexType index) : volVarIndex_(index) {}
+
+        //! Return corresponding vol var index
+        GridIndexType volVarIndex() const { return volVarIndex_; }
+    };
+
+public:
+    //! publicly state the mpfa-scheme this interaction volume is associated with
+    static constexpr MpfaMethods MpfaMethod = MpfaMethods::oMethod;
+
+    //! Sets up the local scope for a given iv index set
+    void setUpLocalScope(const IndexSet& indexSet,
+                         const Problem& problem,
+                         const FVElementGeometry& fvGeometry)
+    {
+        // for the o-scheme, the stencil is equal to the scv
+        // index set of the dual grid's nodal index set
+        stencil_ = &indexSet.nodalIndexSet().globalScvIndices();
+
+        // set up local geometry containers
+        const auto& scvIndices = indexSet.globalScvIndices();
+        for (LocalIndexType localIdx = 0; localIdx < localSize; localIdx++)
+        {
+            // scv-related quantities
+            scvs_[localIdx] = LocalScvType(Helper(),
+                                              fvGeometry,
+                                              fvGeometry.scv( scvIndices[localIdx] ),
+                                              localIdx,
+                                              indexSet);
+            elements_[localIdx] = fvGeometry.fvGridGeometry().element( scvIndices[localIdx] );
+
+            // scvf-related quantities
+            const auto& scvf = fvGeometry.scvf(indexSet.scvfIdxGlobal(localIdx));
+
+            // this interaction volume implementation does not work on boundaries
+            assert(!scvf.boundary() && "static mpfa-o interaction volume cannot be used on boundaries");
+
+            const auto& neighborScvIndicesLocal = indexSet.neighboringLocalScvIndices(localIdx);
+            scvfs_[localIdx] = LocalScvfType(scvf, neighborScvIndicesLocal, localIdx, /*isDirichlet*/false);
+            localFaceData_[localIdx*2] = LocalFaceData(localIdx, neighborScvIndicesLocal[0], scvf.index());
+
+            const auto outsideLocalScvIdx = neighborScvIndicesLocal[1];
+            // loop over scvfs in outside scv until we find the one coinciding with current scvf
+            for (int coord = 0; coord < dim; ++coord)
+            {
+                if (indexSet.scvfIdxLocal(outsideLocalScvIdx, coord) == localIdx)
+                {
+                    const auto globalScvfIdx = indexSet.nodalIndexSet().scvfIdxGlobal(outsideLocalScvIdx, coord);
+                    const auto& flipScvf = fvGeometry.scvf(globalScvfIdx);
+                    localFaceData_[localIdx*2 + 1] = LocalFaceData(localIdx,           // iv-local scvf idx
+                                                                   outsideLocalScvIdx, // iv-local scv index
+                                                                   0,                  // scvf-local index in outside faces
+                                                                   flipScvf.index());  // global scvf index
+                }
+            }
+        }
+
+        // Maybe resize local matrices if dynamic types are used
+        resizeMatrix(A_, localSize, localSize);
+        resizeMatrix(B_, localSize, localSize);
+        resizeMatrix(C_, localSize, localSize);
+    }
+
+    //! returns the number of primary scvfs of this interaction volume
+    static constexpr std::size_t numFaces() { return localSize; }
+
+    //! returns the number of intermediate unknowns within this interaction volume
+    static constexpr std::size_t numUnknowns() { return localSize; }
+
+    //! returns the number of (in this context) known solution values within this interaction volume
+    static constexpr std::size_t numKnowns() { return localSize; }
+
+    //! returns the number of scvs embedded in this interaction volume
+    static constexpr std::size_t numScvs() { return localSize; }
+
+    //! returns the cell-stencil of this interaction volume
+    const Stencil& stencil() const { return *stencil_; }
+
+    //! returns the grid element corresponding to a given iv-local scv idx
+    const Element& element(const LocalIndexType ivLocalScvIdx) const
+    {
+        assert(ivLocalScvIdx < localSize);
+        return elements_[ivLocalScvIdx];
+    }
+
+    //! returns the local scvf entity corresponding to a given iv-local scvf idx
+    const LocalScvfType& localScvf(const LocalIndexType ivLocalScvfIdx) const
+    {
+        assert(ivLocalScvfIdx < localSize);
+        return scvfs_[ivLocalScvfIdx];
+    }
+
+    //! returns the local scv entity corresponding to a given iv-local scv idx
+    const LocalScvType& localScv(const LocalIndexType ivLocalScvIdx) const
+    {
+        assert(ivLocalScvIdx < localSize);
+        return scvs_[ivLocalScvIdx];
+    }
+
+    //! returns a reference to the container with the local face data
+    const std::array<LocalFaceData, 2*localSize>& localFaceData() const { return localFaceData_; }
+
+    //! Returns a reference to the information container on Dirichlet BCs within this iv.
+    //! Here, we return an empty container as this implementation cannot be used on boundaries.
+    const std::array<DirichletData, 0>& dirichletData() const { return dirichletData_; }
+
+    //! returns the matrix associated with face unknowns in local equation system
+    const Matrix& A() const { return A_; }
+    Matrix& A() { return A_; }
+
+    //! returns the matrix associated with cell unknowns in local equation system
+    const Matrix& B() const { return B_; }
+    Matrix& B() { return B_; }
+
+    //! returns the matrix associated with face unknowns in flux expressions
+    const Matrix& C() const { return C_; }
+    Matrix& C() { return C_; }
+
+    //! returns container storing the transmissibilities for each face & coordinate
+    const std::array< std::array<DimVector, 2>, localSize >& omegas() const { return wijk_; }
+    std::array< std::array<DimVector, 2>, localSize >& omegas() { return wijk_; }
+
+    //! returns the number of interaction volumes living around a vertex
+    //! the mpfa-o scheme always constructs one iv per vertex
+    template< class NodalIndexSet >
+    static std::size_t numInteractionVolumesAtVertex(const NodalIndexSet& nodalIndexSet) { return 1; }
+
+    //! adds the iv index sets living around a vertex to a given container
+    //! and stores the the corresponding index in a map for each scvf
+    template<class IvIndexSetContainer, class ScvfIndexMap, class NodalIndexSet>
+    static void addInteractionVolumeIndexSets(IvIndexSetContainer& ivIndexSetContainer,
+                                              ScvfIndexMap& scvfIndexMap,
+                                              const NodalIndexSet& nodalIndexSet)
+    {
+        // the global index of the iv index set that is about to be created
+        const auto curGlobalIndex = ivIndexSetContainer.size();
+
+        // make the one index set for this node
+        ivIndexSetContainer.emplace_back(nodalIndexSet);
+
+        // store the index mapping
+        for (const auto scvfIdx : nodalIndexSet.globalScvfIndices())
+            scvfIndexMap[scvfIdx] = curGlobalIndex;
+    }
+
+private:
+    // pointer to cell stencil (in iv index set)
+    const Stencil* stencil_;
+
+    // Variables defining the local scope
+    std::array<Element, localSize> elements_;
+    std::array<LocalScvType, localSize> scvs_;
+    std::array<LocalScvfType, localSize> scvfs_;
+    std::array<LocalFaceData, 2*localSize> localFaceData_;
+
+    // Empty Dirichlet container to be compatible with dynamic assembly
+    std::array<DirichletData, 0> dirichletData_;
+
+    // Matrices needed for computation of transmissibilities
+    Matrix A_;
+    Matrix B_;
+    Matrix C_;
+
+    // The omega factors are stored during assembly of local system
+    // we assume one "outside" face per scvf (does not work on surface grids)
+    std::array< std::array<DimVector, 2>, localSize > wijk_;
+};
+
+} // end namespace
+
+#endif
diff --git a/dumux/discretization/cellcentered/mpfa/properties.hh b/dumux/discretization/cellcentered/mpfa/properties.hh
index a2f6f6cba86b0d0300fc6a2dee44f4ed2409fa83..1a5e96d98a389f00c0976233008b749cc5f4e1d0 100644
--- a/dumux/discretization/cellcentered/mpfa/properties.hh
+++ b/dumux/discretization/cellcentered/mpfa/properties.hh
@@ -50,6 +50,7 @@
 #include <dumux/discretization/cellcentered/mpfa/helper.hh>
 
 #include <dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh>
+#include <dumux/discretization/cellcentered/mpfa/omethod/staticinteractionvolume.hh>
 
 namespace Dumux
 {
@@ -73,7 +74,7 @@ SET_PROP(CCMpfaModel, MpfaMethod)
 //! The mpfa helper class
 SET_TYPE_PROP(CCMpfaModel, MpfaHelper, CCMpfaHelper<TypeTag>);
 
-//! Per default, we use the mpfa-o interaction volume
+//! Per default, we use the dynamic mpfa-o interaction volume
 SET_PROP(CCMpfaModel, PrimaryInteractionVolume)
 {
 private:
@@ -83,10 +84,15 @@ public:
     using type = CCMpfaOInteractionVolume< Traits >;
 };
 
-//! Per default, we use the mpfa-o interaction volume everywhere (pure mpfa-o scheme)
-SET_TYPE_PROP(CCMpfaModel,
-              SecondaryInteractionVolume,
-              typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume));
+//! Per default, we use the dynamic mpfa-o interaction volume as secondary type
+SET_PROP(CCMpfaModel, SecondaryInteractionVolume)
+{
+private:
+    //! use the default traits
+    using Traits = CCMpfaODefaultInteractionVolumeTraits< TypeTag >;
+public:
+    using type = CCMpfaOInteractionVolume< Traits >;
+};
 
 //! Set the default for the global finite volume geometry
 SET_TYPE_PROP(CCMpfaModel,
diff --git a/dumux/porousmediumflow/1p/incompressiblelocalresidual.hh b/dumux/porousmediumflow/1p/incompressiblelocalresidual.hh
index 03c99d3a6bacdfc531e8fcf93ad6d78664be603e..08376c67351fd442482c051b2485d83af5ed76d1 100644
--- a/dumux/porousmediumflow/1p/incompressiblelocalresidual.hh
+++ b/dumux/porousmediumflow/1p/incompressiblelocalresidual.hh
@@ -123,21 +123,40 @@ public:
         static_assert(FluidSystem::viscosityIsConstant(0),
                       "1p/incompressiblelocalresidual.hh: Only fluids with constant viscosities are allowed!");
 
-        const auto& fluxVarsCache = elemFluxVarsCache[scvf];
-        const auto& volVarIndices = fluxVarsCache.advectionStencil();
-        const auto& tij = fluxVarsCache.advectionTij();
-
         // we know the "upwind factor" is constant, get inner one here and compute derivatives
         static const Scalar up = curElemVolVars[scvf.insideScvIdx()].density()
                                  / curElemVolVars[scvf.insideScvIdx()].viscosity();
 
-        // add partial derivatives to the respective given matrices
-        for (unsigned int i = 0; i < volVarIndices.size();++i)
+        const auto& fluxVarsCache = elemFluxVarsCache[scvf];
+        if (fluxVarsCache.usesSecondaryIv())
         {
-            if (fluxVarsCache.advectionSwitchFluxSign())
-                derivativeMatrices[volVarIndices[i]][conti0EqIdx][pressureIdx] -= tij[i]*up;
-            else
-                derivativeMatrices[volVarIndices[i]][conti0EqIdx][pressureIdx] += tij[i]*up;
+            const auto& stencil = fluxVarsCache.advectionStencilSecondaryIv();
+            const auto& tij = fluxVarsCache.advectionTijSecondaryIv();
+            assert(stencil.size() == tij.size());
+
+            // add partial derivatives to the respective given matrices
+            for (unsigned int i = 0; i < stencil.size();++i)
+            {
+                if (fluxVarsCache.advectionSwitchFluxSign())
+                    derivativeMatrices[stencil[i]][conti0EqIdx][pressureIdx] -= tij[i]*up;
+                else
+                    derivativeMatrices[stencil[i]][conti0EqIdx][pressureIdx] += tij[i]*up;
+            }
+        }
+        else
+        {
+            const auto& stencil = fluxVarsCache.advectionStencilPrimaryIv();
+            const auto& tij = fluxVarsCache.advectionTijPrimaryIv();
+            assert(stencil.size() == tij.size());
+
+            // add partial derivatives to the respective given matrices
+            for (unsigned int i = 0; i < stencil.size();++i)
+            {
+                if (fluxVarsCache.advectionSwitchFluxSign())
+                    derivativeMatrices[stencil[i]][conti0EqIdx][pressureIdx] -= tij[i]*up;
+                else
+                    derivativeMatrices[stencil[i]][conti0EqIdx][pressureIdx] += tij[i]*up;
+            }
         }
     }
 
@@ -210,22 +229,7 @@ public:
                                   const ElementFluxVariablesCache& elemFluxVarsCache,
                                   const SubControlVolumeFace& scvf) const
     {
-        const auto& fluxVarsCache = elemFluxVarsCache[scvf];
-        const auto& volVarIndices = fluxVarsCache.advectionStencil();
-        const auto& tij = fluxVarsCache.advectionTij();
-
-        // we know the "upwind factor" is constant, get inner one here and compute derivatives
-        static const Scalar up = curElemVolVars[scvf.insideScvIdx()].density()
-                                 / curElemVolVars[scvf.insideScvIdx()].viscosity();
-
-        // add partial derivatives to the respective given matrices
-        for (unsigned int i = 0; i < volVarIndices.size();++i)
-        {
-            if (fluxVarsCache.advectionSwitchFluxSign())
-                derivativeMatrices[volVarIndices[i]][conti0EqIdx][pressureIdx] -= tij[i]*up;
-            else
-                derivativeMatrices[volVarIndices[i]][conti0EqIdx][pressureIdx] += tij[i]*up;
-        }
+        addFluxDerivatives(derivativeMatrices, problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
     }
 
     //! Robin-type flux derivatives
diff --git a/dumux/porousmediumflow/fluxvariablescache.hh b/dumux/porousmediumflow/fluxvariablescache.hh
index 4edfbaf182bc0229ca31e84e6d2395362a129239..51c5ae3bfac45e0b62969c96b6639984d8e28c5f 100644
--- a/dumux/porousmediumflow/fluxvariablescache.hh
+++ b/dumux/porousmediumflow/fluxvariablescache.hh
@@ -151,13 +151,29 @@ class PorousMediumFluxVariablesCacheImplementation<TypeTag, DiscretizationMethod
 , public EnergyCacheChooser<TypeTag, GET_PROP_VALUE(TypeTag, EnableEnergyBalance)>
 {
     using GridIndexType = typename GET_PROP_TYPE(TypeTag, GridView)::IndexSet::IndexType;
+
+    using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper);
+    static constexpr bool considerSecondary = MpfaHelper::considerSecondaryIVs();
 public:
     //! Returns whether or not this cache has been updated
     bool isUpdated() const { return isUpdated_; }
 
+    //! Returns whether or not this cache is associated with a secondary interaction volume
+    //! Specialization for deactivated secondary interaction volumes
+    template< bool doSecondary = considerSecondary, std::enable_if_t<!doSecondary, int> = 0>
+    constexpr bool usesSecondaryIv() const { return false; }
+
+    //! Returns whether or not this cache is associated with a secondary interaction volume
+    //! Specialization for activated secondary interaction volumes
+    template< bool doSecondary = considerSecondary, std::enable_if_t<doSecondary, int> = 0>
+    bool usesSecondaryIv() const { return usesSecondaryIv_; }
+
     //! Sets the update status. When set to true, consecutive updates will be skipped
     void setUpdateStatus(bool status) { isUpdated_ = status; }
 
+    //! Sets if this cache is associated witha secondary iv
+    void setSecondaryIvUsage(bool status) { usesSecondaryIv_ = status; }
+
     //! Sets the index of the iv (this scvf is embedded in) in its container
     void setIvIndexInContainer(GridIndexType ivIndex) { ivIndexInContainer_ = ivIndex; }
 
@@ -168,6 +184,9 @@ private:
     //! indicates if cache has been fully updated
     bool isUpdated_ = false;
 
+    //! indicates if cache is associated with secondary interaction volume
+    bool usesSecondaryIv_ = false;
+
     //! the index of the iv (this scvf is embedded in) in its container
     GridIndexType ivIndexInContainer_;
 };