diff --git a/dumux/common/properties.hh b/dumux/common/properties.hh
index d3ce6cfa36857a00499795f0246b9e25c741615c..c32c8e1a4dcc42da83d8451e4948a02b00e6c6ab 100644
--- a/dumux/common/properties.hh
+++ b/dumux/common/properties.hh
@@ -233,7 +233,9 @@ struct BoundaryValues { using type = UndefinedProperty; };
 template<class TypeTag, class MyTypeTag>
 struct StaggeredFaceSolution { using type = UndefinedProperty; };               //!< A vector containing the solution for a face (similar to ElementSolution)
 template<class TypeTag, class MyTypeTag>
-struct EnableGridFaceVariablesCache { using type = UndefinedProperty; };      //!< Switch on/off caching of face variables
+struct EnableGridFaceVariablesCache { using type = UndefinedProperty; };        //!< Switch on/off caching of face variables
+template<class TypeTag, class MyTypeTag>
+struct UpwindSchemeOrder { using type = UndefinedProperty; };                   //!< Specifies the order of the upwinding scheme (1 == first order, 2 == second order(tvd methods))
 
 /////////////////////////////////////////////////////////////
 // Properties used by the mpnc model
diff --git a/dumux/discretization/staggered.hh b/dumux/discretization/staggered.hh
index 84040d5ec2507a9d497656ebb71a326d30e2f5b9..92696168fc0a94fb3c0da39ce5e6d5f276adf261 100644
--- a/dumux/discretization/staggered.hh
+++ b/dumux/discretization/staggered.hh
@@ -81,12 +81,13 @@ template<class TypeTag>
 struct GridFluxVariablesCache<TypeTag, TTag::StaggeredModel>
 {
 private:
-    static constexpr auto enableCache = getPropValue<TypeTag, Properties::EnableGridFluxVariablesCache>();
     using Problem = GetPropType<TypeTag, Properties::Problem>;
     using FluxVariablesCache = GetPropType<TypeTag, Properties::FluxVariablesCache>;
     using FluxVariablesCacheFiller = FluxVariablesCaching::EmptyCacheFiller;
+    static constexpr auto enableCache = getPropValue<TypeTag, Properties::EnableGridFluxVariablesCache>();
+    static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
 public:
-    using type = StaggeredGridFluxVariablesCache<Problem, FluxVariablesCache, FluxVariablesCacheFiller, enableCache>;
+    using type = StaggeredGridFluxVariablesCache<Problem, FluxVariablesCache, FluxVariablesCacheFiller, enableCache, upwindSchemeOrder>;
 };
 
 //! Set the face solution type
diff --git a/dumux/discretization/staggered/freeflow/connectivitymap.hh b/dumux/discretization/staggered/freeflow/connectivitymap.hh
index e43321f1c0c68767972c7212d5b5002febda09c7..1aa82d24fe1e852e2225648e240e1eb4ac79be2b 100644
--- a/dumux/discretization/staggered/freeflow/connectivitymap.hh
+++ b/dumux/discretization/staggered/freeflow/connectivitymap.hh
@@ -52,8 +52,13 @@ class StaggeredFreeFlowConnectivityMap
     using FaceToCellCenterMap = std::vector<std::vector<GridIndexType>>;
     using FaceToFaceMap = std::vector<std::vector<GridIndexType>>;
 
+    using SmallLocalIndex = typename IndexTraits<GridView>::SmallLocalIndex;
+
     using Stencil = std::vector<GridIndexType>;
 
+    static constexpr SmallLocalIndex upwindSchemeOrder = FVGridGeometry::upwindSchemeOrder;
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
+
 public:
 
     //! Update the map and prepare the stencils
@@ -92,13 +97,6 @@ public:
         }
     }
 
-    //! \brief Set the order needed by the scheme
-    void setStencilOrder(const int stencilOrder)
-    {
-        stencilOrder_ = stencilOrder;
-    }
-
-
     //! Returns the stencil of a cell center dof w.r.t. other cell center dofs
     const std::vector<GridIndexType>& operator() (CellCenterIdxType, CellCenterIdxType, const GridIndexType globalI) const
     {
@@ -186,24 +184,9 @@ private:
     {
         if(stencil.empty())
         {
-            for(int i = 0; i < stencilOrder_ - 1; i++)
-            {
-                if(scvf.hasBackwardNeighbor(i))
-                {
-                    stencil.push_back(scvf.axisData().inAxisBackwardDofs[i]);
-                }
-            }
-
             stencil.push_back(scvf.axisData().selfDof);
             stencil.push_back(scvf.axisData().oppositeDof);
-
-            for(int i = 0; i < stencilOrder_ - 1; i++)
-            {
-                if(scvf.hasForwardNeighbor(i))
-                {
-                    stencil.push_back(scvf.axisData().inAxisForwardDofs[i]);
-                }
-            }
+            addHigherOrderInAxisDofs_(scvf, stencil, std::integral_constant<bool, useHigherOrder>{});
         }
 
         for(const auto& data : scvf.pairData())
@@ -214,21 +197,33 @@ private:
                 stencil.push_back(data.normalPair.second);
 
             // add parallel dofs
-            for (int i = 0; i < stencilOrder_ /*data.parallelDofs.size()*/; i++)
+            for (SmallLocalIndex i = 0; i < upwindSchemeOrder; i++)
             {
-                if(!(data.parallelDofs[i] < 0))
-                {
+                if (data.hasParallelNeighbor[i])
                     stencil.push_back(data.parallelDofs[i]);
-                }
             }
         }
     }
 
+    void addHigherOrderInAxisDofs_(const SubControlVolumeFace& scvf, Stencil& stencil, std::false_type) {}
+
+    void addHigherOrderInAxisDofs_(const SubControlVolumeFace& scvf, Stencil& stencil, std::true_type)
+    {
+        for (SmallLocalIndex i = 0; i < upwindSchemeOrder - 1; i++)
+        {
+            if (scvf.hasBackwardNeighbor(i))
+                stencil.push_back(scvf.axisData().inAxisBackwardDofs[i]);
+
+            if (scvf.hasForwardNeighbor(i))
+                stencil.push_back(scvf.axisData().inAxisForwardDofs[i]);
+        }
+    }
+
+
     CellCenterToCellCenterMap cellCenterToCellCenterMap_;
     CellCenterToFaceMap cellCenterToFaceMap_;
     FaceToCellCenterMap faceToCellCenterMap_;
     FaceToFaceMap faceToFaceMap_;
-    int stencilOrder_;
 };
 
 } // end namespace Dumux
diff --git a/dumux/discretization/staggered/freeflow/facevariables.hh b/dumux/discretization/staggered/freeflow/facevariables.hh
index e227cfdc046c20788df80bdd921032862069b858..4645f66c52013ef06d216e19ec2e33b9b1fdd660 100644
--- a/dumux/discretization/staggered/freeflow/facevariables.hh
+++ b/dumux/discretization/staggered/freeflow/facevariables.hh
@@ -29,17 +29,42 @@
 
 namespace Dumux {
 
+namespace Detail {
+
+template<class Scalar, int upwindSchemeOrder>
+struct InAxisVelocities
+{
+    Scalar self = 0.0;
+    Scalar opposite = 0.0;
+    std::array<Scalar, upwindSchemeOrder-1> forward{};
+    std::array<Scalar, upwindSchemeOrder-1> backward{};
+};
+
+template<class Scalar>
+struct InAxisVelocities<Scalar, 1>
+{
+    Scalar self = 0.0;
+    Scalar opposite = 0.0;
+};
+
+} // end namespace Detail
+
 /*!
  * \ingroup StaggeredDiscretization
  * \brief The face variables class for free flow staggered grid models.
  *        Contains all relevant velocities for the assembly of the momentum balance.
+ *        When the upwindSchemeOrder is set to 2, additional velocities located at Dofs
+ *        further from the central stencil will be added and used when calculating the
+ *        advective term. When the order remains at 1, these velocities will not be provided.
  */
-template<class FacePrimaryVariables, int dim>
+template<class FacePrimaryVariables, int dim, int upwindSchemeOrder>
 class StaggeredFaceVariables
 {
     static constexpr int numPairs = (dim == 2) ? 2 : 4;
-    static constexpr int order = 2;
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
+
     using Scalar = typename FacePrimaryVariables::block_type;
+    using InAxisVelocities = Detail::InAxisVelocities<Scalar, upwindSchemeOrder>;
 
 public:
 
@@ -50,7 +75,7 @@ public:
     */
     void updateOwnFaceOnly(const FacePrimaryVariables& priVars)
     {
-        velocitySelf_ = priVars[0];
+        inAxisVelocities_.self = priVars[0];
     }
 
     /*!
@@ -71,52 +96,30 @@ public:
                 const FVElementGeometry& fvGeometry,
                 const SubControlVolumeFace& scvf)
     {
-        velocitySelf_ = faceSol[scvf.dofIndex()];
-        velocityOpposite_ = faceSol[scvf.dofIndexOpposingFace()];
+        inAxisVelocities_.self = faceSol[scvf.dofIndex()];
+        inAxisVelocities_.opposite = faceSol[scvf.dofIndexOpposingFace()];
 
-        // treat the velocity forward of the self face i.e. the face that is
-        // forward wrt the self face by degree i
-        velocityForward_.fill(0.0);
-        for (int i = 0; i < scvf.axisData().inAxisForwardDofs.size(); i++)
-        {
-             if(!(scvf.axisData().inAxisForwardDofs[i] < 0))
-             {
-                 velocityForward_[i]= faceSol[scvf.axisData().inAxisForwardDofs[i]];
-             }
-        }
-
-        // treat the velocity at the first backward face i.e. the face that is
-        // behind the opposite face by degree i
-        velocityBackward_.fill(0.0);
-        for (int i = 0; i < scvf.axisData().inAxisBackwardDofs.size(); i++)
-        {
-             if(!(scvf.axisData().inAxisBackwardDofs[i] < 0))
-             {
-                 velocityBackward_[i] = faceSol[scvf.axisData().inAxisBackwardDofs[i]];
-             }
-        }
+        addHigherOrderInAxisVelocities_(faceSol, scvf, std::integral_constant<bool, useHigherOrder>{});
 
         // handle all sub faces
-        for(int i = 0; i < velocityParallel_.size(); ++i)
+        for (int i = 0; i < velocityParallel_.size(); ++i)
             velocityParallel_[i].fill(0.0);
 
-        for(int i = 0; i < scvf.pairData().size(); ++i)
+        for (int i = 0; i < scvf.pairData().size(); ++i)
         {
             const auto& subFaceData = scvf.pairData(i);
 
             // treat the velocities normal to the face
             velocityNormalInside_[i] = faceSol[subFaceData.normalPair.first];
 
-            if(scvf.hasOuterNormal(i))
+            if (scvf.hasOuterNormal(i))
                 velocityNormalOutside_[i] = faceSol[subFaceData.normalPair.second];
 
             // treat the velocities parallel to the self face
-            for(int j = 0; j < subFaceData.parallelDofs.size(); j++)
+            for (int j = 0; j < upwindSchemeOrder; j++)
             {
-                if(scvf.hasParallelNeighbor(i,j))
-                {
+                if (scvf.hasParallelNeighbor(i,j))
                     velocityParallel_[i][j] = faceSol[subFaceData.parallelDofs[j]];
-                }
             }
         }
     }
@@ -126,7 +129,7 @@ public:
     */
     Scalar velocitySelf() const
     {
-        return velocitySelf_;
+        return inAxisVelocities_.self;
     }
 
     /*!
@@ -134,7 +137,7 @@ public:
     */
     Scalar velocityOpposite() const
     {
-        return velocityOpposite_;
+        return inAxisVelocities_.opposite;
     }
 
     /*!
@@ -142,9 +145,10 @@ public:
     *
     * \param backwardIdx The index describing how many faces backward this dof is from the opposite face
     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
     Scalar velocityBackward(const int backwardIdx) const
     {
-        return velocityBackward_[backwardIdx];
+        return inAxisVelocities_.backward[backwardIdx];
     }
 
     /*!
@@ -152,9 +156,10 @@ public:
     *
     * \param forwardIdx The index describing how many faces forward this dof is of the self face
     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
     Scalar velocityForward(const int forwardIdx) const
     {
-        return velocityForward_[forwardIdx];
+        return inAxisVelocities_.forward[forwardIdx];
     }
 
     /*!
@@ -163,7 +168,7 @@ public:
     * \param localSubFaceIdx The local index of the subface
     * \param parallelDegreeIdx The index describing how many faces parallel this dof is of the parallel face
     */
-    Scalar velocityParallel(const int localSubFaceIdx, const int parallelDegreeIdx) const
+    Scalar velocityParallel(const int localSubFaceIdx, const int parallelDegreeIdx = 0) const
     {
         return velocityParallel_[localSubFaceIdx][parallelDegreeIdx];
     }
@@ -189,11 +194,33 @@ public:
     }
 
 private:
-    Scalar velocitySelf_;
-    Scalar velocityOpposite_;
-    std::array<Scalar, order-1> velocityForward_;
-    std::array<Scalar, order-1> velocityBackward_;
-    std::array<std::array<Scalar, order>, numPairs> velocityParallel_;
+
+    template<class SolVector, class SubControlVolumeFace>
+    void addHigherOrderInAxisVelocities_(const SolVector& faceSol, const SubControlVolumeFace& scvf, std::false_type) {}
+
+    template<class SolVector, class SubControlVolumeFace>
+    void addHigherOrderInAxisVelocities_(const SolVector& faceSol, const SubControlVolumeFace& scvf, std::true_type)
+    {
+
+        // treat the velocity forward of the self face i.e. the face that is
+        // forward wrt the self face by degree i
+        for (int i = 0; i < scvf.axisData().inAxisForwardDofs.size(); i++)
+        {
+             if (scvf.hasForwardNeighbor(i))
+                 inAxisVelocities_.forward[i]= faceSol[scvf.axisData().inAxisForwardDofs[i]];
+        }
+
+        // treat the velocity at the first backward face i.e. the face that is
+        // behind the opposite face by degree i
+        for (int i = 0; i < scvf.axisData().inAxisBackwardDofs.size(); i++)
+        {
+             if (scvf.hasBackwardNeighbor(i))
+                 inAxisVelocities_.backward[i] = faceSol[scvf.axisData().inAxisBackwardDofs[i]];
+        }
+    }
+
+    InAxisVelocities inAxisVelocities_;
+    std::array<std::array<Scalar, upwindSchemeOrder>, numPairs> velocityParallel_;
     std::array<Scalar, numPairs>  velocityNormalInside_;
     std::array<Scalar, numPairs>  velocityNormalOutside_;
 
diff --git a/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh b/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh
index 4b88982ba9b18d9fd11d11b7e7b0bf032f1c9877..c9e88535066c1de09024b420d6427001c08d74ea 100644
--- a/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh
+++ b/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh
@@ -39,14 +39,15 @@ namespace Dumux {
  * \ingroup StaggeredDiscretization
  * \brief Default traits for the finite volume grid geometry.
  */
-template<class GridView, class MapperTraits = DefaultMapperTraits<GridView>>
+template<class GridView, int upwOrder, class MapperTraits = DefaultMapperTraits<GridView>>
 struct StaggeredFreeFlowDefaultFVGridGeometryTraits
 : public MapperTraits
 {
     using SubControlVolume = CCSubControlVolume<GridView>;
-    using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView>;
+    using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView, upwOrder>;
     using IntersectionMapper = ConformingGridIntersectionMapper<GridView>;
-    using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView>;
+    using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView, upwOrder>;
+    static constexpr int upwindSchemeOrder = upwOrder;
 
     struct DofTypeIndices
     {
diff --git a/dumux/discretization/staggered/freeflow/properties.hh b/dumux/discretization/staggered/freeflow/properties.hh
index 3075fc93c6690e07c2fe0bd4bcbe31c8e367ba74..1588ca21e4783c0eeeee8c980c99fe339ccce426 100644
--- a/dumux/discretization/staggered/freeflow/properties.hh
+++ b/dumux/discretization/staggered/freeflow/properties.hh
@@ -81,9 +81,10 @@ template<class TypeTag>
 struct FVGridGeometry<TypeTag, TTag::StaggeredFreeFlowModel>
 {
 private:
-    using GridView = GetPropType<TypeTag, Properties::GridView>;
-    using Traits = StaggeredFreeFlowDefaultFVGridGeometryTraits<GridView>;
+    static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
     static constexpr bool enableCache = getPropValue<TypeTag, Properties::EnableFVGridGeometryCache>();
+    using GridView = GetPropType<TypeTag, Properties::GridView>;
+    using Traits = StaggeredFreeFlowDefaultFVGridGeometryTraits<GridView, upwindSchemeOrder>;
 public:
     using type = StaggeredFVGridGeometry<GridView, enableCache, Traits>;
 };
@@ -95,8 +96,9 @@ struct FaceVariables<TypeTag, TTag::StaggeredFreeFlowModel>
 private:
     using FacePrimaryVariables = GetPropType<TypeTag, Properties::FacePrimaryVariables>;
     using GridView = GetPropType<TypeTag, Properties::GridView>;
+    static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
 public:
-    using type = StaggeredFaceVariables<FacePrimaryVariables, GridView::dimension>;
+    using type = StaggeredFaceVariables<FacePrimaryVariables, GridView::dimension, upwindSchemeOrder>;
 };
 
 //! Set the default global volume variables cache vector class
@@ -128,6 +130,12 @@ struct VelocityOutput<TypeTag, TTag::StaggeredFreeFlowModel>
                                                  GetPropType<TypeTag, Properties::SolutionVector>>;
 };
 
+/*!
+ * \brief  Set the order of the upwinding scheme to 1 by default.
+ */
+template<class TypeTag>
+struct UpwindSchemeOrder<TypeTag, TTag::StaggeredFreeFlowModel> { static constexpr int value = 1; };
+
 } // namespace Properties
 } // namespace Dumux
 
diff --git a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
index 6990c0daed38f81762929ddeedec7f1f85febbbd..071888dfddb0381fe66e3ae4b540fe40dadb856d 100644
--- a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
+++ b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
@@ -25,47 +25,86 @@
 #define DUMUX_DISCRETIZATION_STAGGERED_GEOMETRY_HELPER_HH
 
 #include <dune/geometry/multilineargeometry.hh>
-#include <dune/geometry/referenceelements.hh>
 
 #include <dumux/common/math.hh>
+#include <dumux/common/indextraits.hh>
 #include <type_traits>
 #include <algorithm>
 #include <array>
+#include <bitset>
 
 namespace Dumux
 {
 
+namespace Detail {
+
 /*!
  * \ingroup StaggeredDiscretization
  * \brief Parallel Data stored per sub face
  */
-template<class Scalar, class GlobalPosition, int geometryOrder>
+template<class GridView, int upwindSchemeOrder>
 struct PairData
 {
-    std::array<int, geometryOrder> parallelDofs;
-    std::array<Scalar, geometryOrder+1> parallelDistances;
-    std::pair<signed int,signed int> normalPair;
-    int localNormalFaceIdx;
+    using Scalar = typename GridView::ctype;
+    using GlobalPosition = typename GridView::template Codim<0>::Entity::Geometry::GlobalCoordinate;
+    using GridIndexType = typename IndexTraits<GridView>::GridIndex;
+    using SmallLocalIndexType = typename IndexTraits<GridView>::SmallLocalIndex;
+
+    std::bitset<upwindSchemeOrder> hasParallelNeighbor;
+    std::array<GridIndexType, upwindSchemeOrder> parallelDofs;
+    std::array<Scalar, upwindSchemeOrder+1> parallelDistances; // TODO store only two distances, not three.
+    bool hasNormalNeighbor = false;
+    std::pair<GridIndexType, GridIndexType> normalPair;
+    SmallLocalIndexType localNormalFaceIdx;
     Scalar normalDistance;
     GlobalPosition virtualFirstParallelFaceDofPos;
 };
 
+
+/*!
+ * \ingroup StaggeredDiscretization
+ * \brief In Axis Data stored per sub face
+ */
+template<class GridView, int upwindSchemeOrder>
+struct AxisData;
 /*!
  * \ingroup StaggeredDiscretization
  * \brief In Axis Data stored per sub face
  */
-template<class Scalar, int geometryOrder>
+template<class GridView, int upwindSchemeOrder>
 struct AxisData
 {
-    int selfDof;
-    int oppositeDof;
-    std::array<int, geometryOrder-1> inAxisForwardDofs;
-    std::array<int, geometryOrder-1> inAxisBackwardDofs;
+    using Scalar = typename GridView::ctype;
+    using GridIndexType = typename IndexTraits<GridView>::GridIndex;
+
+    GridIndexType selfDof;
+    GridIndexType oppositeDof;
+    std::bitset<upwindSchemeOrder-1> hasForwardNeighbor;
+    std::bitset<upwindSchemeOrder-1> hasBackwardNeighbor;
+    std::array<GridIndexType, upwindSchemeOrder-1> inAxisForwardDofs;
+    std::array<GridIndexType, upwindSchemeOrder-1> inAxisBackwardDofs;
     Scalar selfToOppositeDistance;
-    std::array<Scalar, geometryOrder-1> inAxisForwardDistances;
-    std::array<Scalar, geometryOrder-1> inAxisBackwardDistances;
+    std::array<Scalar, upwindSchemeOrder-1> inAxisForwardDistances;
+    std::array<Scalar, upwindSchemeOrder-1> inAxisBackwardDistances;
 };
 
+/*!
+ * \ingroup StaggeredDiscretization
+ * \brief In Axis Data stored per sub face
+ */
+template<class GridView>
+struct AxisData<GridView, 1>
+{
+    using Scalar = typename GridView::ctype;
+    using GridIndexType = typename IndexTraits<GridView>::GridIndex;
+
+    GridIndexType selfDof;
+    GridIndexType oppositeDof;
+    Scalar selfToOppositeDistance;
+};
+
+} // namespace Detail
+
 /*!
  * \ingroup StaggeredDiscretization
  * \brief Returns the dirction index of the facet (0 = x, 1 = y, 2 = z)
@@ -74,8 +113,7 @@ template<class Vector>
 inline static unsigned int directionIndex(Vector&& vector)
 {
     const auto eps = 1e-8;
-    const int idx = std::find_if(vector.begin(), vector.end(), [eps](const auto& x) { return std::abs(x) > eps; } ) - vector.begin();
-    return idx;
+    return std::find_if(vector.begin(), vector.end(), [eps](const auto& x) { return std::abs(x) > eps; } ) - vector.begin();
 }
 
 /*!
@@ -83,31 +121,32 @@ inline static unsigned int directionIndex(Vector&& vector)
  * \brief Helper class constructing the dual grid finite volume geometries
  *        for the free flow staggered discretization method.
  */
-template<class GridView>
+template<class GridView, int upwindSchemeOrder>
 class FreeFlowStaggeredGeometryHelper
 {
     using Scalar = typename GridView::ctype;
-    static constexpr int dim = GridView::dimension;
-    static constexpr int dimWorld = GridView::dimensionworld;
-
-    using ScvGeometry = Dune::CachedMultiLinearGeometry<Scalar, dim, dimWorld>;
-    using ScvfGeometry = Dune::CachedMultiLinearGeometry<Scalar, dim-1, dimWorld>;
-
-    using GlobalPosition = typename ScvGeometry::GlobalCoordinate;
+    static constexpr auto dim = GridView::dimension;
+    static constexpr auto dimWorld = GridView::dimensionworld;
 
     using Element = typename GridView::template Codim<0>::Entity;
     using Intersection = typename GridView::Intersection;
+    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
 
-    using ReferenceElements = typename Dune::ReferenceElements<Scalar, dim>;
+    using GridIndexType = typename IndexTraits<GridView>::GridIndex;
+    using SmallLocalIndexType = typename IndexTraits<GridView>::SmallLocalIndex;
 
     //TODO include assert that checks for quad geometry
-    static constexpr int codimIntersection =  1;
-    static constexpr int codimCommonEntity = 2;
-    static constexpr int numFacetSubEntities = (dim == 2) ? 2 : 4;
-    static constexpr int numfacets = dimWorld * 2;
-    static constexpr int numPairs = 2 * (dimWorld - 1);
+    static constexpr auto codimIntersection =  1;
+    static constexpr auto codimCommonEntity = 2;
+    static constexpr auto numFacetSubEntities = (dim == 2) ? 2 : 4;
+    static constexpr auto numfacets = dimWorld * 2;
+    static constexpr auto numPairs = 2 * (dimWorld - 1);
+
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
 
 public:
+    using PairData = Detail::PairData<GridView, upwindSchemeOrder>;
+    using AxisData = Detail::AxisData<GridView, upwindSchemeOrder>;
 
     FreeFlowStaggeredGeometryHelper(const Element& element, const GridView& gridView) : element_(element), elementGeometry_(element.geometry()), gridView_(gridView)
     { }
@@ -117,15 +156,14 @@ public:
     void updateLocalFace(const IntersectionMapper& intersectionMapper_, const Intersection& intersection)
     {
         intersection_ = intersection;
-        innerNormalFacePos_.clear();
-        fillPairData_();
         fillAxisData_();
+        fillPairData_();
     }
 
     /*!
      * \brief Returns the local index of the face (i.e. the intersection)
      */
-    int localFaceIndex() const
+    SmallLocalIndexType localFaceIndex() const
     {
         return intersection_.indexInInside();
     }
@@ -133,7 +171,7 @@ public:
     /*!
      * \brief Returns a copy of the axis data
      */
-    auto axisData() const
+    AxisData axisData() const
     {
         return axisData_;
     }
@@ -141,7 +179,7 @@ public:
     /*!
      * \brief Returns a copy of the pair data
      */
-    auto pairData() const
+    std::array<PairData, numPairs> pairData() const
     {
         return pairData_;
     }
@@ -149,49 +187,65 @@ public:
     /*!
      * \brief Returns the dirction index of the primary facet (0 = x, 1 = y, 2 = z)
      */
-    int directionIndex() const
+    unsigned int directionIndex() const
     {
-        return Dumux::directionIndex(std::move(intersection_.centerUnitOuterNormal()));
+        return Dumux::directionIndex(intersection_.centerUnitOuterNormal());
     }
 
     /*!
      * \brief Returns the dirction index of the facet passed as an argument (0 = x, 1 = y, 2 = z)
      */
-    int directionIndex(const Intersection& intersection) const
+    unsigned int directionIndex(const Intersection& intersection) const
     {
         return Dumux::directionIndex(std::move(intersection.centerUnitOuterNormal()));
     }
 
-    static constexpr int geometryOrder = 2;
-
 private:
+
     /*!
      * \brief Fills all entries of the in axis data
+     *        Calls a function to extend the axis data for higher order upwind methods if required.
      */
     void fillAxisData_()
     {
-        unsigned int numForwardBackwardAxisDofs = axisData_.inAxisForwardDofs.size();
+        fillOuterAxisData_(std::integral_constant<bool, useHigherOrder>{});
+
         const auto inIdx = intersection_.indexInInside();
         const auto oppIdx = localOppositeIdx_(inIdx);
 
-        this->axisData_.inAxisForwardDofs.fill(-1);
-        this->axisData_.inAxisBackwardDofs.fill(-1);
-        this->axisData_.inAxisForwardDistances.fill(0);
-        this->axisData_.inAxisBackwardDistances.fill(0);
-
         // Set the self Dof
-        this->axisData_.selfDof = gridView_.indexSet().subIndex(this->intersection_.inside(), inIdx, codimIntersection);
-        const auto self = getFacet_(inIdx, element_);
+        axisData_.selfDof = gridView_.indexSet().subIndex(intersection_.inside(), inIdx, codimIntersection);
 
         // Set the opposite Dof
-        this->axisData_.oppositeDof = gridView_.indexSet().subIndex(this->intersection_.inside(), oppIdx, codimIntersection);
-        const auto opposite = getFacet_(oppIdx, element_);
+        axisData_.oppositeDof = gridView_.indexSet().subIndex(intersection_.inside(), oppIdx, codimIntersection);
 
         // Set the Self to Opposite Distance
-        this->axisData_.selfToOppositeDistance = (self.geometry().center() - opposite.geometry().center()).two_norm();
+        const auto self = getFacet_(inIdx, element_);
+        const auto opposite = getFacet_(oppIdx, element_);
+        axisData_.selfToOppositeDistance = (self.geometry().center() - opposite.geometry().center()).two_norm();
+
+    }
+
+    /*!
+     * \brief Fills the axis data with the outer Dofs and Distances.
+     *        For first order upwind methods no outer information is required.
+     */
+    void fillOuterAxisData_(std::false_type) {}
+
+    /*!
+     * \brief Fills the axis data with the outer dofs and distances.
+     *        This function builds and extended stencil and is therefore only called when higher order upwinding methods are prescribed.
+     */
+    void fillOuterAxisData_(std::true_type)
+    {
+        // reset the axis data struct
+        axisData_ = {};
+
+        const auto numForwardBackwardAxisDofs = axisData_.inAxisForwardDofs.size();
 
         // Set the Forward Dofs
         std::stack<Element> inAxisForwardElementStack;
+        const auto inIdx = intersection_.indexInInside();
         auto selfFace = getFacet_(inIdx, element_);
         if(intersection_.neighbor())
         {
@@ -222,26 +276,25 @@ private:
         while(!inAxisForwardElementStack.empty())
         {
             int forwardIdx = inAxisForwardElementStack.size()-1;
-            this->axisData_.inAxisForwardDofs[forwardIdx] = gridView_.indexSet().subIndex(inAxisForwardElementStack.top(), inIdx, codimIntersection);
+            axisData_.hasForwardNeighbor.set(forwardIdx, true);
+            axisData_.inAxisForwardDofs[forwardIdx] = gridView_.indexSet().subIndex(inAxisForwardElementStack.top(), inIdx, codimIntersection);
             selfFace = getFacet_(inIdx, inAxisForwardElementStack.top());
             forwardFaceCoordinates[forwardIdx] = selfFace.geometry().center();
             inAxisForwardElementStack.pop();
         }
 
-        for(int i = 0; i< forwardFaceCoordinates.size(); i++)
+        const auto self = getFacet_(inIdx, element_);
+        for (int i = 0; i< forwardFaceCoordinates.size(); i++)
         {
-            if(i == 0)
-            {
-                this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i] - self.geometry().center()).two_norm();
-            }
+            if (i == 0)
+                axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i] - self.geometry().center()).two_norm();
             else
-            {
-                this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i]- forwardFaceCoordinates[i-1]).two_norm();
-            }
+                axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i]- forwardFaceCoordinates[i-1]).two_norm();
         }
 
         // Set the Backward Dofs
         std::stack<Element> inAxisBackwardElementStack;
+        const auto oppIdx = localOppositeIdx_(inIdx);
         auto oppFace = getFacet_(oppIdx, element_);
         for(const auto& intersection : intersections(gridView_, element_))
         {
@@ -279,78 +332,61 @@ private:
         while(!inAxisBackwardElementStack.empty())
         {
             int backwardIdx = inAxisBackwardElementStack.size()-1;
-            this->axisData_.inAxisBackwardDofs[backwardIdx] = gridView_.indexSet().subIndex(inAxisBackwardElementStack.top(), oppIdx, codimIntersection);
+            axisData_.hasBackwardNeighbor.set(backwardIdx, true);
+            axisData_.inAxisBackwardDofs[backwardIdx] = gridView_.indexSet().subIndex(inAxisBackwardElementStack.top(), oppIdx, codimIntersection);
             oppFace = getFacet_(oppIdx, inAxisBackwardElementStack.top());
             backwardFaceCoordinates[backwardIdx] = oppFace.geometry().center();
             inAxisBackwardElementStack.pop();
         }
 
-        for(int i = 0; i< backwardFaceCoordinates.size(); i++)
+        const auto opposite = getFacet_(oppIdx, element_);
+        for (int i = 0; i< backwardFaceCoordinates.size(); i++)
         {
-            if(i == 0)
-            {
-                this->axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - opposite.geometry().center()).two_norm();
-            }
+            if (i == 0)
+                axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - opposite.geometry().center()).two_norm();
             else
-            {
-                this->axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - backwardFaceCoordinates[i-1]).two_norm();
-            }
+                axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - backwardFaceCoordinates[i-1]).two_norm();
         }
     }
 
     /*!
-     * \brief Fills all entries of the pair data
+     * \brief Fills the pair data with the normal dofs and distances
+     *        and calls a further function to collect the parallel dofs and distances
      */
     void fillPairData_()
     {
-        // initialize values that could remain unitialized if the intersection lies on a boundary
-        for(auto& data : pairData_)
-        {
-            // parallel Dofs
-            data.parallelDofs.fill(-1);
-
-            // parallel Distances
-            data.parallelDistances.fill(0.0);
-
-            // outer normals
-            data.normalPair.second = -1;
-        }
-
-        unsigned int numParallelFaces = pairData_[0].parallelDistances.size();
+        // reset the pair data structs
+        pairData_ = {};
 
         // set basic global positions
-        const auto& elementCenter = this->element_.geometry().center();
+        const auto& elementCenter = element_.geometry().center();
         const auto& FacetCenter = intersection_.geometry().center();
 
         // get the inner normal Dof Index
-        int numPairInnerNormalIdx = 0;
-        for(const auto& innerElementIntersection : intersections(gridView_, element_))
+        SmallLocalIndexType numPairInnerNormalIdx = 0;
+        for (const auto& innerElementIntersection : intersections(gridView_, element_))
         {
-            if(facetIsNormal_(innerElementIntersection.indexInInside(), intersection_.indexInInside()))
+            if (facetIsNormal_(innerElementIntersection.indexInInside(), intersection_.indexInInside()))
             {
-                const int innerElementIntersectionIdx = innerElementIntersection.indexInInside();
+                const auto innerElementIntersectionIdx = innerElementIntersection.indexInInside();
                 setNormalPairFirstInfo_(innerElementIntersectionIdx, element_, numPairInnerNormalIdx);
                 numPairInnerNormalIdx++;
-
-                innerNormalFacePos_.reserve(numPairs);
-                const auto& innerNormalFacet = getFacet_(innerElementIntersection.indexInInside(), element_);
-                innerNormalFacePos_.emplace_back(innerNormalFacet.geometry().center());
             }
         }
 
         // get the outer normal Dof Index
-        int numPairOuterNormalIdx = 0;
-        if(intersection_.neighbor())
+        SmallLocalIndexType numPairOuterNormalIdx = 0;
+        if (intersection_.neighbor())
         {
             // the direct neighbor element and the respective intersection index
             const auto& directNeighborElement = intersection_.outside();
 
-            for(const auto& directNeighborElementIntersection : intersections(gridView_, directNeighborElement))
+            for (const auto& directNeighborElementIntersection : intersections(gridView_, directNeighborElement))
             {
                 // skip the directly neighboring face itself and its opposing one
-                if(facetIsNormal_(directNeighborElementIntersection.indexInInside(), intersection_.indexInOutside()))
+                if (facetIsNormal_(directNeighborElementIntersection.indexInInside(), intersection_.indexInOutside()))
                 {
-                    const int directNeighborElemIsIdx = directNeighborElementIntersection.indexInInside();
+                    const auto directNeighborElemIsIdx = directNeighborElementIntersection.indexInInside();
                     setNormalPairSecondInfo_(directNeighborElemIsIdx, directNeighborElement, numPairOuterNormalIdx);
                     numPairOuterNormalIdx++;
                 }
@@ -359,18 +395,74 @@ private:
         else // intersection is on boundary
         {
             // fill the normal pair entries
-            for(int pairIdx = 0; pairIdx < numPairs; ++pairIdx)
+            for(SmallLocalIndexType pairIdx = 0; pairIdx < numPairs; ++pairIdx)
             {
-                assert(pairData_[pairIdx].normalPair.second == -1);
+                assert(!pairData_[pairIdx].hasNormalNeighbor);
                 const auto distance = FacetCenter - elementCenter;
-                this->pairData_[pairIdx].normalDistance = std::move(distance.two_norm());
+                pairData_[pairIdx].normalDistance = std::move(distance.two_norm());
                 numPairOuterNormalIdx++;
             }
         }
 
+        fillParallelPairData_(std::integral_constant<bool, useHigherOrder>{});
+    }
+
+    /*!
+     * \brief Fills the pair data with the parallel dofs and distances
+     *        This function is only called when the simple first order upwind methods are used.
+     */
+    void fillParallelPairData_(std::false_type)
+    {
+        // set basic global positions and stencil size definitions
+        const auto& elementCenter = element_.geometry().center();
+
+        // get the parallel Dofs
+        const auto parallelLocalIdx = intersection_.indexInInside();
+        SmallLocalIndexType numPairParallelIdx = 0;
+
+        for (const auto& intersection : intersections(gridView_, element_))
+        {
+            if (facetIsNormal_(intersection.indexInInside(), parallelLocalIdx))
+            {
+                // Store the parallel dimension of self cell in the direction of the axis
+                auto parallelAxisIdx = directionIndex(intersection);
+                pairData_[numPairParallelIdx].parallelDistances[0] = setParallelPairDistances_(element_, parallelAxisIdx);
+
+                if (intersection.neighbor())
+                {
+                    // If the normal intersection has a neighboring cell, go in and store the parallel information.
+                    const auto& outerElement = intersection.outside();
+                    pairData_[numPairParallelIdx].hasParallelNeighbor.set(0, true);
+                    pairData_[numPairParallelIdx].parallelDofs[0] = gridView_.indexSet().subIndex(outerElement, parallelLocalIdx, codimIntersection);
+                    pairData_[numPairParallelIdx].parallelDistances[1] = setParallelPairDistances_(outerElement, parallelAxisIdx);
+                }
+                else  // No parallel neighbor available
+                {
+                    // If the intersection has no neighbor we have to deal with the virtual outer parallel dof
+                    const auto& boundaryFacetCenter = intersection.geometry().center();
+                    const auto distance = boundaryFacetCenter - elementCenter;
+                    const auto virtualFirstParallelFaceDofPos = intersection_.geometry().center() + distance;
+
+                    pairData_[numPairParallelIdx].virtualFirstParallelFaceDofPos = std::move(virtualFirstParallelFaceDofPos);
+                }
+                numPairParallelIdx++;
+            }
+        }
+    }
+
+    /*!
+     * \brief Fills the pair data with the parallel dofs and distances.
+     *        This function builds and extended stencil and is therefore only called when higher order upwinding methods are prescribed.
+     */
+    void fillParallelPairData_(std::true_type)
+    {
+        // set basic global positions and stencil size definitions
+        const auto numParallelFaces = pairData_[0].parallelDistances.size();
+        const auto& elementCenter = element_.geometry().center();
+
         // get the parallel Dofs
-        const int parallelLocalIdx = intersection_.indexInInside();
-        int numPairParallelIdx = 0;
+        const auto parallelLocalIdx = intersection_.indexInInside();
+        SmallLocalIndexType numPairParallelIdx = 0;
         std::stack<Element> parallelElementStack;
         for(const auto& intersection : intersections(gridView_, element_))
         {
@@ -407,9 +499,10 @@ private:
                     {
                         if(parallelElementStack.size() > 1)
                         {
-                            this->pairData_[numPairParallelIdx].parallelDofs[parallelElementStack.size()-2] = gridView_.indexSet().subIndex(parallelElementStack.top(), parallelLocalIdx, codimIntersection);
+                            pairData_[numPairParallelIdx].hasParallelNeighbor.set(parallelElementStack.size()-2, true);
+                            pairData_[numPairParallelIdx].parallelDofs[parallelElementStack.size()-2] = gridView_.indexSet().subIndex(parallelElementStack.top(), parallelLocalIdx, codimIntersection);
                         }
-                        this->pairData_[numPairParallelIdx].parallelDistances[parallelElementStack.size()-1] = setParallelPairDistances_(parallelElementStack.top(), parallelAxisIdx);
+                        pairData_[numPairParallelIdx].parallelDistances[parallelElementStack.size()-1] = setParallelPairDistances_(parallelElementStack.top(), parallelAxisIdx);
                         parallelElementStack.pop();
                     }
 
@@ -417,17 +510,16 @@ private:
                 else
                 {
                     // If the intersection has no neighbor we have to deal with the virtual outer parallel dof
-                    const auto& elementCenter = this->element_.geometry().center();
                     const auto& boundaryFacetCenter = intersection.geometry().center();
 
                     const auto distance = boundaryFacetCenter - elementCenter;
-                    const auto virtualFirstParallelFaceDofPos = this->intersection_.geometry().center() + distance;
+                    const auto virtualFirstParallelFaceDofPos = intersection_.geometry().center() + distance;
 
-                    this->pairData_[numPairParallelIdx].virtualFirstParallelFaceDofPos = std::move(virtualFirstParallelFaceDofPos);
+                    pairData_[numPairParallelIdx].virtualFirstParallelFaceDofPos = std::move(virtualFirstParallelFaceDofPos);
 
                     // The distance is saved doubled because with scvf.cellCenteredSelfToFirstParallelDistance
                     // an average between parallelDistances[0] and parallelDistances[1] will be computed
-                    this->pairData_[numPairParallelIdx].parallelDistances[0] = std::move(distance.two_norm() * 2);
+                    pairData_[numPairParallelIdx].parallelDistances[0] = std::move(distance.two_norm() * 2);
                 }
                 numPairParallelIdx++;
             }
@@ -464,26 +556,25 @@ private:
     void setNormalPairFirstInfo_(const int isIdx, const Element& element, const int numPairsIdx)
     {
         // store the inner normal dofIdx
-        auto& dofIdx = pairData_[numPairsIdx].normalPair.first;
-        dofIdx = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
+        pairData_[numPairsIdx].normalPair.first = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
 
         // store the local normal facet index
-        this->pairData_[numPairsIdx].localNormalFaceIdx = isIdx;
+        pairData_[numPairsIdx].localNormalFaceIdx = isIdx;
     }
 
     //! Sets the information about the normal faces (in the neighbor element)
     void setNormalPairSecondInfo_(const int isIdx, const Element& element, const int numPairsIdx)
     {
         // store the dofIdx
-        auto& dofIdx = pairData_[numPairsIdx].normalPair.second;
-        dofIdx = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
+        pairData_[numPairsIdx].hasNormalNeighbor = true;
+        pairData_[numPairsIdx].normalPair.second = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
 
         // store the element distance
         const auto& outerNormalFacet = getFacet_(isIdx, element);
         const auto outerNormalFacetPos = outerNormalFacet.geometry().center();
         const auto& innerNormalFacet = getFacet_(isIdx, element_);
         const auto innerNormalFacetPos = innerNormalFacet.geometry().center();
-        this->pairData_[numPairsIdx].normalDistance = (innerNormalFacetPos - outerNormalFacetPos).two_norm();
+        pairData_[numPairsIdx].normalDistance = (innerNormalFacetPos - outerNormalFacetPos).two_norm();
     }
 
     //! Sets the information about the parallel distances
@@ -526,9 +617,8 @@ private:
     const Element element_; //!< The respective element
     const typename Element::Geometry elementGeometry_; //!< Reference to the element geometry
     const GridView gridView_; //!< The grid view
-    AxisData<Scalar, geometryOrder> axisData_; //!< Data related to forward and backward faces
-    std::array<PairData<Scalar, GlobalPosition, geometryOrder>, numPairs> pairData_; //!< Collection of pair information related to normal and parallel faces
-    std::vector<GlobalPosition> innerNormalFacePos_;
+    AxisData axisData_; //!< Data related to forward and backward faces
+    std::array<PairData, numPairs> pairData_; //!< Collection of pair information related to normal and parallel faces
 };
 
 } // end namespace Dumux
diff --git a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
index 0fa6efc60735a797a487304caea533c7781e9032..9f163725c141730cb9a1906fa099adbddbb668d5 100644
--- a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
+++ b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
@@ -36,29 +36,52 @@
 
 namespace Dumux {
 
+/*!
+ * \ingroup StaggeredDiscretization
+ * \brief Default traits class to be used for the sub-control volume faces
+ *        for the free-flow staggered finite volume scheme
+ * \tparam GridView the type of the grid view
+ * \tparam upwindSchemeOrder the order of the upwind scheme
+ */
+template<class GridView, int upwindSchemeOrder>
+struct FreeFlowStaggeredDefaultScvfGeometryTraits
+{
+    using Geometry = typename GridView::template Codim<1>::Geometry;
+    using GridIndexType = typename IndexTraits<GridView>::GridIndex;
+    using LocalIndexType = typename IndexTraits<GridView>::LocalIndex;
+    using Scalar = typename GridView::ctype;
+    using GlobalPosition = Dune::FieldVector<Scalar, GridView::dimensionworld>;
+    using PairData = typename FreeFlowStaggeredGeometryHelper<GridView, upwindSchemeOrder>::PairData;
+    using AxisData = typename FreeFlowStaggeredGeometryHelper<GridView, upwindSchemeOrder>::AxisData;
+};
+
 /*!
  * \ingroup StaggeredDiscretization
  * \brief Class for a sub control volume face in the staggered method, i.e a part of the boundary
  *        of a sub control volume we compute fluxes on. This is a specialization for free flow models.
  */
 template<class GV,
-         class T = StaggeredDefaultScvfGeometryTraits<GV> >
+         int upwindSchemeOrder,
+         class T = FreeFlowStaggeredDefaultScvfGeometryTraits<GV, upwindSchemeOrder>>
 class FreeFlowStaggeredSubControlVolumeFace
-: public SubControlVolumeFaceBase<FreeFlowStaggeredSubControlVolumeFace<GV, T>, T>
+: public SubControlVolumeFaceBase<FreeFlowStaggeredSubControlVolumeFace<GV, upwindSchemeOrder, T>, T>
 {
-    using ThisType = FreeFlowStaggeredSubControlVolumeFace<GV, T>;
+    using ThisType = FreeFlowStaggeredSubControlVolumeFace<GV, upwindSchemeOrder, T>;
     using ParentType = SubControlVolumeFaceBase<ThisType, T>;
     using Geometry = typename T::Geometry;
     using GridIndexType = typename IndexTraits<GV>::GridIndex;
     using LocalIndexType = typename IndexTraits<GV>::LocalIndex;
-    using GeometryHelper = FreeFlowStaggeredGeometryHelper<GV>;
+
+    using PairData = typename T::PairData;
+    using AxisData = typename T::AxisData;
 
     using Scalar = typename T::Scalar;
     static const int dim = Geometry::mydimension;
     static const int dimworld = Geometry::coorddimension;
 
     static constexpr int numPairs = 2 * (dimworld - 1);
-    static constexpr int geometryOrder = GeometryHelper::geometryOrder;
+
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
 
 public:
     using GlobalPosition = typename T::GlobalPosition;
@@ -86,7 +109,7 @@ public:
       boundary_(is.boundary()),
 
       axisData_(geometryHelper.axisData()),
-      pairData_(geometryHelper.pairData()),
+      pairData_(std::move(geometryHelper.pairData())),
       localFaceIdx_(geometryHelper.localFaceIndex()),
       dirIdx_(geometryHelper.directionIndex()),
       outerNormalSign_(sign(unitOuterNormal_[directionIndex()])),
@@ -206,19 +229,19 @@ public:
     }
 
     //! Returns the data for one sub face
-    const PairData<Scalar, GlobalPosition, geometryOrder>& pairData(const int idx) const
+    const PairData& pairData(const int idx) const
     {
         return pairData_[idx];
     }
 
     //! Return an array of all pair data
-    const auto& pairData() const
+    const std::array<PairData, numPairs>& pairData() const
     {
         return pairData_;
     }
 
     //! Return an array of all pair data
-    const auto& axisData() const
+    const AxisData& axisData() const
     {
         return axisData_;
     }
@@ -237,7 +260,7 @@ public:
     */
     bool hasParallelNeighbor(const int localSubFaceIdx, const int parallelDegreeIdx) const
     {
-        return !(pairData(localSubFaceIdx).parallelDofs[parallelDegreeIdx] < 0);
+        return pairData(localSubFaceIdx).hasParallelNeighbor[parallelDegreeIdx];
     }
 
    /*!
@@ -247,7 +270,7 @@ public:
     */
     bool hasOuterNormal(const int localSubFaceIdx) const
     {
-        return !(pairData_[localSubFaceIdx].normalPair.second < 0);
+        return pairData(localSubFaceIdx).hasNormalNeighbor;
     }
 
    /*!
@@ -255,9 +278,10 @@ public:
     *
     * \param backwardIdx The index describing how many faces backward this dof is from the opposite face
     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
     bool hasBackwardNeighbor(const int backwardIdx) const
     {
-        return !(axisData().inAxisBackwardDofs[backwardIdx] < 0);
+        return axisData().hasBackwardNeighbor[backwardIdx];
     }
 
    /*!
@@ -265,9 +289,10 @@ public:
     *
     * \param forwardIdx The index describing how many faces forward this dof is of the self face
     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
     bool hasForwardNeighbor(const int forwardIdx) const
     {
-        return !(axisData().inAxisForwardDofs[forwardIdx] < 0);
+        return axisData().hasForwardNeighbor[forwardIdx];
     }
 
     //! Returns the dof of the face
@@ -325,8 +350,8 @@ private:
 
     int dofIdx_;
     Scalar selfToOppositeDistance_;
-    AxisData<Scalar, geometryOrder> axisData_;
-    std::array<PairData<Scalar, GlobalPosition, geometryOrder>, numPairs> pairData_;
+    AxisData axisData_;
+    std::array<PairData, numPairs> pairData_;
 
     int localFaceIdx_;
     unsigned int dirIdx_;
diff --git a/dumux/discretization/staggered/fvgridgeometry.hh b/dumux/discretization/staggered/fvgridgeometry.hh
index 2b7e9b22f68f18f07a9b1d2ebc413ddc95f69d7b..ccffc03b5d279c3a6fec46d2287146346796b3bb 100644
--- a/dumux/discretization/staggered/fvgridgeometry.hh
+++ b/dumux/discretization/staggered/fvgridgeometry.hh
@@ -157,8 +157,6 @@ public:
     { return this->fvGridGeometry_->numFaceDofs(); }
 };
 
-
-
 /*!
  * \ingroup StaggeredDiscretization
  * \brief Base class for the finite volume geometry vector for staggered models
@@ -193,6 +191,8 @@ class StaggeredFVGridGeometry<GV, true, Traits>
 public:
     //! export discretization method
     static constexpr DiscretizationMethod discMethod = DiscretizationMethod::staggered;
+    static constexpr int upwindSchemeOrder = Traits::upwindSchemeOrder;
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
 
     //! export the type of the fv element geometry (the local view type)
     using LocalView = typename Traits::template LocalView<ThisType, true>;
@@ -213,6 +213,10 @@ public:
     static constexpr auto faceIdx()
     { return typename DofTypeIndices::FaceIdx{}; }
 
+    //! The order of the stencil built
+    static constexpr int upwindStencilOrder()
+    {   return upwindSchemeOrder; }
+
     using CellCenterFVGridGeometryType = CellCenterFVGridGeometry<ThisType>;
     using FaceFVGridGeometryType = FaceFVGridGeometry<ThisType>;
 
@@ -227,10 +231,6 @@ public:
         if (!CheckOverlapSize<DiscretizationMethod::staggered>::isValid(gridView))
             DUNE_THROW(Dune::InvalidStateException, "The staggered discretization method needs at least an overlap of 1 for parallel computations. "
                                                      << " Set the parameter \"Grid.Overlap\" in the input file.");
-        if (hasParamInGroup(paramGroup, "Discretization.TvdApproach"))
-            stencilOrder_ = 2;
-        else
-            stencilOrder_ = 1;
     }
 
     //! The total number of sub control volumes
@@ -251,11 +251,6 @@ public:
         return numBoundaryScvf_;
     }
 
-    //! The order of the stencil built
-    std::size_t order() const
-    {
-        return stencilOrder_;
-    }
 
     //! The total number of intersections
     std::size_t numIntersections() const
@@ -351,7 +346,6 @@ public:
         }
 
         // build the connectivity map for an effecient assembly
-        connectivityMap_.setStencilOrder(stencilOrder_);
         connectivityMap_.update(*this);
     }
 
@@ -423,7 +417,6 @@ private:
     // mappers
     ConnectivityMap connectivityMap_;
     IntersectionMapper intersectionMapper_;
-    int stencilOrder_;
 
     std::vector<SubControlVolume> scvs_;
     std::vector<SubControlVolumeFace> scvfs_;
@@ -455,6 +448,8 @@ class StaggeredFVGridGeometry<GV, false, Traits>
 public:
     //! export discretization method
     static constexpr DiscretizationMethod discMethod = DiscretizationMethod::staggered;
+    static constexpr int upwindSchemeOrder = Traits::upwindSchemeOrder;
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
 
     using GeometryHelper = typename Traits::GeometryHelper;
 
@@ -477,6 +472,10 @@ public:
     static constexpr auto faceIdx()
     { return typename DofTypeIndices::FaceIdx{}; }
 
+    //! The order of the stencil built
+    static constexpr int upwindStencilOrder()
+    {   return upwindSchemeOrder; }
+
     using CellCenterFVGridGeometryType = CellCenterFVGridGeometry<ThisType>;
     using FaceFVGridGeometryType = FaceFVGridGeometry<ThisType>;
 
@@ -491,10 +490,6 @@ public:
         if (!CheckOverlapSize<DiscretizationMethod::staggered>::isValid(gridView))
             DUNE_THROW(Dune::InvalidStateException, "The staggered discretization method needs at least an overlap of 1 for parallel computations. "
                                                      << " Set the parameter \"Grid.Overlap\" in the input file.");
-        if (hasParamInGroup(paramGroup, "Discretization.TvdApproach"))
-            stencilOrder_ = 2;
-        else
-            stencilOrder_ = 1;
     }
 
     //! update all fvElementGeometries (do this again after grid adaption)
@@ -547,7 +542,6 @@ public:
         }
 
         // build the connectivity map for an effecient assembly
-        connectivityMap_.setStencilOrder(stencilOrder_);
         connectivityMap_.update(*this);
     }
 
@@ -563,12 +557,6 @@ public:
         return numScvf_;
     }
 
-    //! The order of the stencil built
-    std::size_t order() const
-    {
-        return stencilOrder_;
-    }
-
     //! The total number of boundary sub control volume faces
     std::size_t numBoundaryScvf() const
     {
@@ -652,7 +640,6 @@ private:
     // mappers
     ConnectivityMap connectivityMap_;
     IntersectionMapper intersectionMapper_;
-    int stencilOrder_;
 
     //! vectors that store the global data
     std::vector<std::vector<GridIndexType>> scvfIndicesOfScv_;
diff --git a/dumux/discretization/staggered/gridfluxvariablescache.hh b/dumux/discretization/staggered/gridfluxvariablescache.hh
index 3c87372776d2c95922574fc8730da2ef0249097d..0865bf6e21d2bb9ce611e198c6b34fc5b9df6942 100644
--- a/dumux/discretization/staggered/gridfluxvariablescache.hh
+++ b/dumux/discretization/staggered/gridfluxvariablescache.hh
@@ -28,7 +28,7 @@
 #include <dumux/discretization/localview.hh>
 #include <dumux/discretization/staggered/elementfluxvariablescache.hh>
 
-#include <dumux/freeflow/higherorderapproximation.hh>
+#include <dumux/freeflow/staggeredupwindmethods.hh>
 
 namespace Dumux {
 
@@ -38,14 +38,16 @@ namespace Dumux {
  * \tparam P The problem type
  * \tparam FVC The flux variables cache type
  */
-template<class P, class FVC, class FVCF>
+template<class P, class FVC, class FVCF, int upwOrder>
 struct StaggeredDefaultGridFluxVariablesCacheTraits
 {
     using Problem = P;
     using FluxVariablesCache = FVC;
     using FluxVariablesCacheFiller = FVCF;
+
     template<class GridFluxVariablesCache, bool cachingEnabled>
     using LocalView = StaggeredElementFluxVariablesCache<GridFluxVariablesCache, cachingEnabled>;
+    static constexpr int upwindSchemeOrder = upwOrder;
 };
 
 /*!
@@ -56,7 +58,8 @@ template<class Problem,
          class FluxVariablesCache,
          class FluxVariablesCacheFiller,
          bool EnableGridFluxVariablesCache = false,
-         class Traits = StaggeredDefaultGridFluxVariablesCacheTraits<Problem, FluxVariablesCache, FluxVariablesCacheFiller> >
+         int upwindSchemeOrder = 1,
+         class Traits = StaggeredDefaultGridFluxVariablesCacheTraits<Problem, FluxVariablesCache, FluxVariablesCacheFiller, upwindSchemeOrder>>
 class StaggeredGridFluxVariablesCache;
 
 /*!
@@ -64,17 +67,18 @@ class StaggeredGridFluxVariablesCache;
  * \brief Flux variables cache class for staggered models.
           Specialization in case of storing the flux cache.
  */
-template<class P, class FVC, class FVCF, class Traits>
-class StaggeredGridFluxVariablesCache<P, FVC, FVCF, true, Traits>
+template<class P, class FVC, class FVCF, int upwindSchemeOrder, class Traits>
+class StaggeredGridFluxVariablesCache<P, FVC, FVCF, true, upwindSchemeOrder, Traits>
 {
     using Problem = typename Traits::Problem;
-    using ThisType = StaggeredGridFluxVariablesCache<P, FVC, FVCF, true, Traits>;
+    using ThisType = StaggeredGridFluxVariablesCache<P, FVC, FVCF, true, upwindSchemeOrder, Traits>;
 
 public:
     //! export the flux variable cache type
     using FluxVariablesCache = typename Traits::FluxVariablesCache;
     using Scalar = typename FluxVariablesCache::Scalar;
 
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
     //! export the flux variable cache filler type
     using FluxVariablesCacheFiller = typename Traits::FluxVariablesCacheFiller;
 
@@ -86,7 +90,7 @@ public:
 
     StaggeredGridFluxVariablesCache(const Problem& problem, const std::string& paramGroup = "")
     : problemPtr_(&problem)
-    , higherOrderApproximation_(paramGroup)
+    , staggeredUpwindMethods_(paramGroup)
       {}
 
     // When global caching is enabled, precompute transmissibilities and stencils for all the scv faces
@@ -123,10 +127,10 @@ public:
         }
     }
 
-    //! Return the HigherOrderApproximation
-    const HigherOrderApproximation<Scalar>& higherOrderApproximation() const
+    //! Return the StaggeredUpwindMethods
+    const StaggeredUpwindMethods<Scalar, upwindSchemeOrder>& staggeredUpwindMethods() const
     {
-        return higherOrderApproximation_;
+        return staggeredUpwindMethods_;
     }
 
     const Problem& problem() const
@@ -141,7 +145,7 @@ public:
 
 private:
     const Problem* problemPtr_;
-    HigherOrderApproximation<Scalar> higherOrderApproximation_;
+    StaggeredUpwindMethods<Scalar, upwindSchemeOrder> staggeredUpwindMethods_;
 
     std::vector<FluxVariablesCache> fluxVarsCache_;
     std::vector<std::size_t> globalScvfIndices_;
@@ -152,11 +156,11 @@ private:
  * \brief Flux variables cache class for staggered models.
           Specialization in case of not storing the flux cache.
  */
-template<class P, class FVC, class FVCF, class Traits>
-class StaggeredGridFluxVariablesCache<P, FVC, FVCF, false, Traits>
+template<class P, class FVC, class FVCF, int upwindSchemeOrder, class Traits>
+class StaggeredGridFluxVariablesCache<P, FVC, FVCF, false, upwindSchemeOrder, Traits>
 {
     using Problem = typename Traits::Problem;
-    using ThisType = StaggeredGridFluxVariablesCache<P, FVC, FVCF, false, Traits>;
+    using ThisType = StaggeredGridFluxVariablesCache<P, FVC, FVCF, false, upwindSchemeOrder, Traits>;
 
 public:
     //! export the flux variable cache type
@@ -174,7 +178,7 @@ public:
 
     StaggeredGridFluxVariablesCache(const Problem& problem, const std::string& paramGroup = "")
     : problemPtr_(&problem)
-    , higherOrderApproximation_(paramGroup)
+    , staggeredUpwindMethods_(paramGroup)
       {}
 
     // When global caching is enabled, precompute transmissibilities and stencils for all the scv faces
@@ -187,15 +191,15 @@ public:
     const Problem& problem() const
     { return *problemPtr_; }
 
-    //! Return the HigherOrderApproximation
-    const HigherOrderApproximation<Scalar>& higherOrderApproximation() const
+    //! Return the UpwindingMethods
+    const StaggeredUpwindMethods<Scalar, upwindSchemeOrder>& staggeredUpwindMethods() const
     {
-        return higherOrderApproximation_;
+        return staggeredUpwindMethods_;
     }
 
 private:
     const Problem* problemPtr_;
-    HigherOrderApproximation<Scalar> higherOrderApproximation_;
+    StaggeredUpwindMethods<Scalar, upwindSchemeOrder> staggeredUpwindMethods_;
 
 };
 
diff --git a/dumux/freeflow/CMakeLists.txt b/dumux/freeflow/CMakeLists.txt
index 5f16de206e056526ae1a904c3c0261c80257a51b..e427be44131d694075407e521615b6a64ff1907d 100644
--- a/dumux/freeflow/CMakeLists.txt
+++ b/dumux/freeflow/CMakeLists.txt
@@ -7,5 +7,5 @@ install(FILES
 properties.hh
 turbulenceproperties.hh
 volumevariables.hh
-higherorderapproximation.hh
+staggeredupwindmethods.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/freeflow)
diff --git a/dumux/freeflow/navierstokes/staggered/CMakeLists.txt b/dumux/freeflow/navierstokes/staggered/CMakeLists.txt
index 5d59b30262acbbf8b4c049b917e591cdc1a74ba7..1f1987ff9f454cac7d604da5aa0205d1822f1077 100644
--- a/dumux/freeflow/navierstokes/staggered/CMakeLists.txt
+++ b/dumux/freeflow/navierstokes/staggered/CMakeLists.txt
@@ -2,4 +2,5 @@ install(FILES
 fluxoversurface.hh
 fluxvariables.hh
 localresidual.hh
+staggeredupwindfluxvariables.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/freeflow/navierstokes/staggered)
diff --git a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
index a39cd4824f2866c0e120b8c90e98ab13e3781b03..b177c16e251ad13b21fac33e0b95cff24244bbd5 100644
--- a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
+++ b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
@@ -31,11 +31,11 @@
 #include <dumux/common/parameters.hh>
 #include <dumux/common/properties.hh>
 
-#include <dumux/freeflow/higherorderapproximation.hh>
-
 #include <dumux/flux/fluxvariablesbase.hh>
 #include <dumux/discretization/method.hh>
 
+#include "staggeredupwindfluxvariables.hh"
+
 namespace Dumux {
 
 // forward declaration
@@ -86,6 +86,9 @@ class NavierStokesFluxVariablesImpl<TypeTag, DiscretizationMethod::staggered>
     static constexpr auto cellCenterIdx = FVGridGeometry::cellCenterIdx();
     static constexpr auto faceIdx = FVGridGeometry::faceIdx();
 
+    static constexpr int upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
+
 public:
 
     using HeatConductionType = GetPropType<TypeTag, Properties::HeatConductionType>;
@@ -200,11 +203,6 @@ public:
         // The velocities of the dof at interest and the one of the opposite scvf.
         const Scalar velocitySelf = elemFaceVars[scvf].velocitySelf();
         const Scalar velocityOpposite = elemFaceVars[scvf].velocityOpposite();
-        static const Scalar upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
-
-        // The volume variables within the current element. We only require those (and none of neighboring elements)
-        // because the fluxes are calculated over the staggered face at the center of the element.
-        const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
 
         // Advective flux.
         if (problem.enableInertiaTerms())
@@ -212,45 +210,14 @@ public:
             // Get the average velocity at the center of the element (i.e. the location of the staggered face).
             const Scalar transportingVelocity = (velocitySelf + velocityOpposite) * 0.5;
 
-            // Check if the the velocity of the dof at interest lies up- or downstream w.r.t. to the transporting velocity.
-            const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity);
-
-            Scalar momentum = 0.0;
-
-            // Variables that will store the velocities of interest:
-            // velocities[0]: downstream velocity
-            // velocities[1]: upstream velocity
-            // velocities[2]: upstream-upstream velocity
-            std::array<Scalar, 3> velocities{0.0, 0.0, 0.0};
-
-            // Variables that will store the distances between the dofs of interest:
-            // distances[0]: upstream to downstream distance
-            // distances[1]: upstream-upstream to upstream distance
-            // distances[2]: downstream staggered cell size
-            std::array<Scalar, 3> distances{0.0, 0.0, 0.0};
-
-            const auto& highOrder = gridFluxVarsCache.higherOrderApproximation();
-
-            // If a Tvd approach has been specified and I am not too near to the boundary I can use a second order
-            // approximation for the velocity. In this frontal flux I use for the density always the value that I have on the scvf.
-            if (highOrder.tvdApproach() != TvdApproach::none)
-            {
-                if (canFrontalSecondOrder_(scvf, selfIsUpstream, velocities, distances, elemFaceVars[scvf]))
-                {
-                    momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], selfIsUpstream, insideVolVars.density(), highOrder.tvdApproach());
-                }
-                else
-                    momentum = highOrder.upwind(velocities[0], velocities[1], insideVolVars.density(), upwindWeight);
-            }
-            else
-                momentum = selfIsUpstream ? highOrder.upwind(velocityOpposite, velocitySelf, insideVolVars.density(), upwindWeight)
-                                          : highOrder.upwind(velocitySelf, velocityOpposite, insideVolVars.density(), upwindWeight);
-
-            // Account for the orientation of the staggered face's normal outer normal vector
-            // (pointing in opposite direction of the scvf's one).
-            frontalFlux += transportingVelocity * momentum * -1.0 * scvf.directionSign();
+            frontalFlux += StaggeredUpwindFluxVariables<TypeTag, upwindSchemeOrder>::computeUpwindedFrontalMomentum(scvf, elemFaceVars, elemVolVars, gridFluxVarsCache, transportingVelocity)
+                           * transportingVelocity * -1.0 * scvf.directionSign();
         }
 
+        // The volume variables within the current element. We only require those (and none of neighboring elements)
+        // because the fluxes are calculated over the staggered face at the center of the element.
+        const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
+
         // Diffusive flux.
         // The velocity gradient already accounts for the orientation
         // of the staggered face's outer normal vector.
@@ -310,7 +277,7 @@ public:
                                                     const GridFluxVariablesCache& gridFluxVarsCache)
     {
         FacePrimaryVariables normalFlux(0.0);
-        auto& faceVars = elemFaceVars[scvf];
+        const auto& faceVars = elemFaceVars[scvf];
         const int numSubFaces = scvf.pairData().size();
 
         // Account for all sub faces.
@@ -367,9 +334,15 @@ public:
 
             // If there is no symmetry or Neumann boundary condition for the given sub face, proceed to calculate the tangential momentum flux.
             if (problem.enableInertiaTerms())
-                normalFlux += computeAdvectivePartOfLateralMomentumFlux_(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars, gridFluxVarsCache, localSubFaceIdx, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
-
-            normalFlux += computeDiffusivePartOfLateralMomentumFlux_(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars, localSubFaceIdx, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
+                normalFlux += computeAdvectivePartOfLateralMomentumFlux_(problem, fvGeometry, element,
+                                                                         scvf, normalFace, elemVolVars, faceVars,
+                                                                         gridFluxVarsCache, localSubFaceIdx,
+                                                                         lateralFaceHasDirichletPressure, lateralFaceHasBJS);
+
+            normalFlux += computeDiffusivePartOfLateralMomentumFlux_(problem, fvGeometry, element,
+                                                                     scvf, normalFace, elemVolVars, faceVars,
+                                                                     localSubFaceIdx,
+                                                                     lateralFaceHasDirichletPressure, lateralFaceHasBJS);
         }
         return normalFlux;
     }
@@ -413,54 +386,10 @@ private:
         // of interest is located.
         const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx);
 
-        static const Scalar upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight", 1.0);
-
-        // Check whether the own or the neighboring element is upstream.
-        const bool selfIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) );
-
-        // Get the volume variables of the own and the neighboring element
-        const auto& insideVolVars = elemVolVars[normalFace.insideScvIdx()];
-        const auto& outsideVolVars = elemVolVars[normalFace.outsideScvIdx()];
-
-        Scalar momentum = 0.0;
-
-        // Variables that will store the velocities of interest:
-        // velocities[0]: downstream velocity
-        // velocities[1]: upsteram velocity
-        // velocities[2]: upstream-upstream velocity
-        std::array<Scalar, 3> velocities{0.0, 0.0, 0.0};
-
-        // Variables that will store the distances between the dofs of interest:
-        // distances[0]: upstream to downstream distance
-        // distances[1]: upstream-upstream to upstream distance
-        // distances[2]: downstream staggered cell size
-        std::array<Scalar, 3> distances{0.0, 0.0, 0.0};
-
-        const auto& highOrder = gridFluxVarsCache.higherOrderApproximation();
-
-        // If a Tvd approach has been specified and I am not too near to the boundary I can use a second order approximation.
-        if (highOrder.tvdApproach() != TvdApproach::none)
-        {
-            if (canLateralSecondOrder_(scvf, fvGeometry, selfIsUpstream, localSubFaceIdx, velocities, distances, problem, element, faceVars, lateralFaceHasDirichletPressure, lateralFaceHasBJS))
-            {
-                momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], selfIsUpstream, selfIsUpstream ? insideVolVars.density() : outsideVolVars.density(), highOrder.tvdApproach());
-            }
-            else
-                momentum = highOrder.upwind(velocities[0], velocities[1], selfIsUpstream ? insideVolVars.density() : outsideVolVars.density(), upwindWeight);
-        }
-        else
-        {
-            const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx, 0)
-                                               ? faceVars.velocityParallel(localSubFaceIdx, 0)
-                                               : getParallelVelocityFromBoundary_(problem, scvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
-
-            momentum = selfIsUpstream ? highOrder.upwind(velocityFirstParallel, faceVars.velocitySelf(), insideVolVars.density(), upwindWeight)
-                                      : highOrder.upwind(faceVars.velocitySelf(), velocityFirstParallel, outsideVolVars.density(), upwindWeight);
-        }
-
-        // Account for the orientation of the staggered normal face's outer normal vector
-        // and its area (0.5 of the coinciding scfv).
-        return transportingVelocity * momentum * normalFace.directionSign() * normalFace.area() * 0.5 * extrusionFactor_(elemVolVars, normalFace);
+        return StaggeredUpwindFluxVariables<TypeTag, upwindSchemeOrder>::computeUpwindedLateralMomentum(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars,
+                                                                     gridFluxVarsCache, localSubFaceIdx, lateralFaceHasDirichletPressure,
+                                                                     lateralFaceHasBJS)
+               * transportingVelocity * normalFace.directionSign() * normalFace.area() * 0.5 * extrusionFactor_(elemVolVars, normalFace);
     }
 
     /*!
@@ -547,7 +476,9 @@ private:
 
             const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx,0)
                                                ? faceVars.velocityParallel(localSubFaceIdx,0)
-                                               : getParallelVelocityFromBoundary_(problem, scvf, normalFace, innerParallelVelocity, localSubFaceIdx, element, false, lateralFaceHasBJS);
+                                               : getParallelVelocityFromBoundary_(problem, scvf, normalFace,
+                                                                                  innerParallelVelocity, localSubFaceIdx,
+                                                                                  element, false, lateralFaceHasBJS);
 
             // The velocity gradient already accounts for the orientation
             // of the staggered face's outer normal vector.
@@ -613,7 +544,8 @@ private:
     //! helper function to conveniently create a ghost face used to retrieve boundary values from the problem
     SubControlVolumeFace makeGhostFace_(const SubControlVolumeFace& ownScvf, const GlobalPosition& pos) const
     {
-        return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()}, ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index());
+        return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()},
+                                    ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index());
     };
 
     //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest
@@ -657,151 +589,50 @@ private:
     }
 
     /*!
-     * \brief Check if a second order approximation for the frontal part of the advective term can be used
-     *
-     * This helper function checks if the scvf of interest is not too near to the
-     * boundary so that a dof upstream with respect to the upstream dof is available.
+     * \brief Return a velocity value from a boundary for which the boundary conditions have to be checked.
      *
-     * \param ownScvf The SubControlVolumeFace we are considering
-     * \param selfIsUpstream @c true if the velocity ownScvf is upstream wrt the transporting velocity
-     * \param velocities Variable that will store the velocities of interest
-     * \param distances Variable that will store the distances of interest
-     * \param faceVars The face variables related to ownScvf
+     * \param problem The problem
+     * \param scvf The SubControlVolumeFace that is normal to the boundary
+     * \param localIdx The local index of the face that is on the boundary
+     * \param boundaryElement The element that is on the boundary
+     * \param parallelVelocity The velocity over scvf
      */
-    bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf,
-                                const bool selfIsUpstream,
-                                std::array<Scalar, 3>& velocities,
-                                std::array<Scalar, 3>& distances,
-                                const FaceVariables& faceVars) const
+    Scalar getParallelVelocityFromOtherBoundary_(const Problem& problem,
+                                                 const FVElementGeometry& fvGeometry,
+                                                 const SubControlVolumeFace& scvf,
+                                                 const int localIdx,
+                                                 const Element& boundaryElement,
+                                                 const Scalar parallelVelocity) const
     {
-        // Depending on selfIsUpstream I can assign the downstream and the upstream velocities,
-        // then I have to check if I have a forward or a backward neighbor to retrieve
-        // an "upstream-upstream velocity" and be able to use a second order scheme.
-        if (selfIsUpstream)
-        {
-            velocities[0] = faceVars.velocityOpposite();
-            velocities[1] = faceVars.velocitySelf();
+        // A ghost subface at the boundary is created, featuring the location of the sub face's center
+        const SubControlVolumeFace& boundaryNormalFace = fvGeometry.scvf(scvf.insideScvIdx(), scvf.pairData(localIdx).localNormalFaceIdx);
+        GlobalPosition boundarySubFaceCenter = scvf.pairData(localIdx).virtualFirstParallelFaceDofPos + boundaryNormalFace.center();
+        boundarySubFaceCenter *= 0.5;
+        const SubControlVolumeFace boundarySubFace = makeGhostFace_(boundaryNormalFace, boundarySubFaceCenter);
 
-            if (ownScvf.hasForwardNeighbor(0))
-            {
-                velocities[2] = faceVars.velocityForward(0);
-                distances[0] = ownScvf.selfToOppositeDistance();
-                distances[1] = ownScvf.axisData().inAxisForwardDistances[0];
-                distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisBackwardDistances[0]);
-                return true;
-            }
-            else
-                return false;
-        }
-        else
-        {
-            velocities[0] = faceVars.velocitySelf();
-            velocities[1] = faceVars.velocityOpposite();
+        // The boundary condition is checked, in case of symmetry or Dirichlet for the pressure
+        // a gradient of zero is assumed in the direction normal to the bounadry, while if there is
+        // Dirichlet of BJS for the velocity the related values are exploited.
+        const auto bcTypes = problem.boundaryTypes(boundaryElement, boundarySubFace);
 
-            if (ownScvf.hasBackwardNeighbor(0))
-            {
-                velocities[2] = faceVars.velocityBackward(0);
-                distances[0] = ownScvf.selfToOppositeDistance();
-                distances[1] = ownScvf.axisData().inAxisBackwardDistances[0];
-                distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisForwardDistances[0]);
-                return true;
-            }
-            else
-                return false;
+        if (bcTypes.isDirichlet(Indices::velocity(scvf.directionIndex())))
+        {
+            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
+            return problem.dirichlet(boundaryElement, ghostFace)[Indices::velocity(scvf.directionIndex())];
         }
-    }
-
-    /*!
-     * \brief Check if a second order approximation for the lateral part of the advective term can be used
-     *
-     * This helper function checks if the scvf of interest is not too near to the
-     * boundary so that a dof upstream with respect to the upstream dof is available.
-     *
-     * \param ownScvf The SubControlVolumeFace we are considering
-     * \param selfIsUpstream @c true if the velocity ownScvf is upstream wrt the transporting velocity
-     * \param localSubFaceIdx The local subface index
-     * \param velocities Variable that will store the velocities of interest
-     * \param distances Variable that will store the distances of interest
-     */
-    bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf,
-                                const FVElementGeometry& fvGeometry,
-                                const bool selfIsUpstream,
-                                const int localSubFaceIdx,
-                                std::array<Scalar, 3>& velocities,
-                                std::array<Scalar, 3>& distances,
-                                const Problem& problem,
-                                const Element& element,
-                                const FaceVariables& faceVars,
-                                const bool lateralFaceHasDirichletPressure,
-                                const bool lateralFaceHasBJS) const
-    {
-         const SubControlVolumeFace& normalFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx);
-
-        // The local index of the faces that is opposite to localSubFaceIdx
-        const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1;
-
-        if (selfIsUpstream)
+        else if (bcTypes.isSymmetry() || bcTypes.isDirichlet(Indices::pressureIdx))
+            return parallelVelocity;
+        else if (bcTypes.isBJS(Indices::velocity(scvf.directionIndex())))
         {
-            // I can assign the upstream velocity. The downstream velocity can be assigned or retrieved
-            // from the boundary if there is no parallel neighbor.
-            velocities[1] = faceVars.velocitySelf();
-
-            if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0))
-            {
-                velocities[0] = faceVars.velocityParallel(localSubFaceIdx, 0);
-                distances[2] = ownScvf.pairData(localSubFaceIdx).parallelDistances[1];
-            }
-            else
-            {
-                velocities[0] = getParallelVelocityFromBoundary_(problem, ownScvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
-                distances[2] = ownScvf.area() / 2.0;
-            }
-
-            // The "upstream-upsteram" velocity is retrieved from the other parallel neighbor
-            // or from the boundary.
-            if (ownScvf.hasParallelNeighbor(oppositeSubFaceIdx, 0))
-                velocities[2] = faceVars.velocityParallel(oppositeSubFaceIdx, 0);
-            else
-                velocities[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, ownScvf, oppositeSubFaceIdx, element, velocities[1]);
-
-            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
-            distances[1] = ownScvf.cellCenteredParallelDistance(oppositeSubFaceIdx, 0);
-
-            return true;
+            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
+            return problem.bjsVelocity(boundaryElement, scvf, boundaryNormalFace, localIdx, parallelVelocity);
         }
         else
         {
-            // The self velocity is downstream, then if there is no parallel neighbor I can not use
-            // a second order approximation beacuse I have only two velocities.
-            velocities[0] = faceVars.velocitySelf();
-
-            if (!ownScvf.hasParallelNeighbor(localSubFaceIdx, 0))
-            {
-                velocities[1] = getParallelVelocityFromBoundary_(problem, ownScvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
-                return false;
-            }
-
-            velocities[1] = faceVars.velocityParallel(localSubFaceIdx, 0);
-
-            // If there is another parallel neighbor I can assign the "upstream-upstream"
-            // velocity, otherwise I retrieve it from the boundary.
-            if (ownScvf.hasParallelNeighbor(localSubFaceIdx, 1))
-                velocities[2] = faceVars.velocityParallel(localSubFaceIdx, 1);
-            else
-            {
-                const Element& elementParallel = fvGeometry.fvGridGeometry().element(fvGeometry.scv(normalFace.outsideScvIdx()));
-                const SubControlVolumeFace& firstParallelScvf = fvGeometry.scvf(normalFace.outsideScvIdx(), ownScvf.localFaceIdx());
-                velocities[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, firstParallelScvf, localSubFaceIdx, elementParallel, velocities[1]);
-            }
-
-            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
-            distances[1] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 1);
-            distances[2] = ownScvf.area();
-
-            return true;
+            // Neumann conditions are not well implemented
+            DUNE_THROW(Dune::InvalidStateException, "Something went wrong with the boundary conditions for the momentum equations at global position " << boundarySubFaceCenter);
         }
     }
-
     /*!
      * \brief Return the outer parallel velocity for normal faces that are on the boundary and therefore have no neighbor.
      *
@@ -836,51 +667,6 @@ private:
         return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())];
     }
 
-    /*!
-     * \brief Return a velocity value from a boundary for which the boundary conditions have to be checked.
-     *
-     * \param problem The problem
-     * \param scvf The SubControlVolumeFace that is normal to the boundary
-     * \param localIdx The local index of the face that is on the boundary
-     * \param boundaryElement The element that is on the boundary
-     * \param parallelVelocity The velocity over scvf
-     */
-    Scalar getParallelVelocityFromOtherBoundary_(const Problem& problem,
-                                                 const FVElementGeometry& fvGeometry,
-                                                 const SubControlVolumeFace& scvf,
-                                                 const int localIdx,
-                                                 const Element& boundaryElement,
-                                                 const Scalar parallelVelocity) const
-    {
-        // A ghost subface at the boundary is created, featuring the location of the sub face's center
-        const SubControlVolumeFace& boundaryNormalFace = fvGeometry.scvf(scvf.insideScvIdx(), scvf.pairData(localIdx).localNormalFaceIdx);
-        GlobalPosition boundarySubFaceCenter = scvf.pairData(localIdx).virtualFirstParallelFaceDofPos + boundaryNormalFace.center();
-        boundarySubFaceCenter *= 0.5;
-        const SubControlVolumeFace boundarySubFace = makeGhostFace_(boundaryNormalFace, boundarySubFaceCenter);
-
-        // The boundary condition is checked, in case of symmetry or Dirichlet for the pressure
-        // a gradient of zero is assumed in the direction normal to the bounadry, while if there is
-        // Dirichlet of BJS for the velocity the related values are exploited.
-        const auto bcTypes = problem.boundaryTypes(boundaryElement, boundarySubFace);
-
-        if (bcTypes.isDirichlet(Indices::velocity(scvf.directionIndex())))
-        {
-            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
-            return problem.dirichlet(boundaryElement, ghostFace)[Indices::velocity(scvf.directionIndex())];
-        }
-        else if (bcTypes.isSymmetry() || bcTypes.isDirichlet(Indices::pressureIdx))
-            return parallelVelocity;
-        else if (bcTypes.isBJS(Indices::velocity(scvf.directionIndex())))
-        {
-            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
-            return problem.bjsVelocity(boundaryElement, scvf, boundaryNormalFace, localIdx, parallelVelocity);
-        }
-        else
-        {
-            // Neumann conditions are not well implemented
-            DUNE_THROW(Dune::InvalidStateException, "Something went wrong with the boundary conditions for the momentum equations at global position " << boundarySubFaceCenter);
-        }
-    }
 };
 
 } // end namespace Dumux
diff --git a/dumux/freeflow/navierstokes/staggered/staggeredupwindfluxvariables.hh b/dumux/freeflow/navierstokes/staggered/staggeredupwindfluxvariables.hh
new file mode 100644
index 0000000000000000000000000000000000000000..39ae0b74f5c856bf8352fcd5d316e51b0499a879
--- /dev/null
+++ b/dumux/freeflow/navierstokes/staggered/staggeredupwindfluxvariables.hh
@@ -0,0 +1,664 @@
+// -*- 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 3 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 NavierStokesModel
+ * \copydoc Dumux::NavierStokesUpwindVariables
+ */
+#ifndef DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH
+#define DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH
+
+#include <array>
+
+#include <dumux/common/math.hh>
+#include <dumux/common/exceptions.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/common/properties.hh>
+
+#include <dumux/discretization/method.hh>
+#include <dumux/freeflow/staggeredupwindmethods.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief The upwinding variables class for the Navier-Stokes model using the staggered grid discretization.
+ */
+template<class TypeTag, int upwindSchemeOrder>
+class StaggeredUpwindFluxVariables
+{
+    using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
+
+    using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
+    using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
+    using VolumeVariables = typename GridVolumeVariables::VolumeVariables;
+
+    using GridFluxVariablesCache = typename GridVariables::GridFluxVariablesCache;
+    using FluxVariablesCache = typename GridFluxVariablesCache::FluxVariablesCache;
+
+    using GridFaceVariables = typename GridVariables::GridFaceVariables;
+    using ElementFaceVariables = typename GridFaceVariables::LocalView;
+    using FaceVariables = typename GridFaceVariables::FaceVariables;
+
+    using Problem = GetPropType<TypeTag, Properties::Problem>;
+    using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>;
+    using FVElementGeometry = typename FVGridGeometry::LocalView;
+    using GridView = typename FVGridGeometry::GridView;
+    using Element = typename GridView::template Codim<0>::Entity;
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>;
+    using Indices = typename ModelTraits::Indices;
+    using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace;
+    using CellCenterPrimaryVariables = GetPropType<TypeTag, Properties::CellCenterPrimaryVariables>;
+    using FacePrimaryVariables = GetPropType<TypeTag, Properties::FacePrimaryVariables>;
+    using BoundaryTypes = GetPropType<TypeTag, Properties::BoundaryTypes>;
+
+    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
+    static constexpr bool useHigherOrder = upwindSchemeOrder > 1;
+
+    static constexpr auto cellCenterIdx = FVGridGeometry::cellCenterIdx();
+    static constexpr auto faceIdx = FVGridGeometry::faceIdx();
+
+public:
+    /*!
+     * \brief Returns the momentum in the frontal directon.
+     *
+     *        Checks if the model has higher order methods enabled and if the scvf in
+     *        question is far enough from the boundary such that higher order methods can be employed.
+     *        Then the corresponding set of momenta are collected and the prescribed
+     *        upwinding method is used to calculate the momentum.
+     */
+    static FacePrimaryVariables computeUpwindedFrontalMomentum(const SubControlVolumeFace& scvf,
+                                                               const ElementFaceVariables& elemFaceVars,
+                                                               const ElementVolumeVariables& elemVolVars,
+                                                               const GridFluxVariablesCache& gridFluxVarsCache,
+                                                               const Scalar transportingVelocity)
+    {
+        Scalar momentum(0.0);
+        const bool canHigherOrder = canFrontalSecondOrder_(scvf, transportingVelocity, std::integral_constant<bool, useHigherOrder>{});
+
+        if (canHigherOrder)
+        {
+            const auto upwindingMomenta = getFrontalUpwindingMomenta_(scvf, elemFaceVars, elemVolVars[scvf.insideScvIdx()].density(),
+                                                                      transportingVelocity, std::integral_constant<bool, useHigherOrder>{});
+            momentum = doFrontalMomentumUpwinding_(scvf, upwindingMomenta, transportingVelocity, gridFluxVarsCache);
+        }
+        else
+        {
+            const auto upwindingMomenta = getFrontalUpwindingMomenta_(scvf, elemFaceVars, elemVolVars[scvf.insideScvIdx()].density(),
+                                                                      transportingVelocity, std::integral_constant<bool, false>{});
+            momentum = doFrontalMomentumUpwinding_(scvf, upwindingMomenta, transportingVelocity, gridFluxVarsCache);
+        }
+
+        return momentum;
+    }
+
+    /*!
+     * \brief Returns the momentum in the lateral directon.
+     *
+     *        Evaluates which face is upstream.
+     *        Checks if the model has higher order methods enabled and if the scvf in
+     *        question is far enough from the boundary such that higher order methods can be employed.
+     *        Then the corresponding set of momenta are collected and the prescribed
+     *        upwinding method is used to calculate the momentum.
+     */
+    static FacePrimaryVariables computeUpwindedLateralMomentum(const Problem& problem,
+                                                               const FVElementGeometry& fvGeometry,
+                                                               const Element& element,
+                                                               const SubControlVolumeFace& scvf,
+                                                               const SubControlVolumeFace& normalFace,
+                                                               const ElementVolumeVariables& elemVolVars,
+                                                               const FaceVariables& faceVars,
+                                                               const GridFluxVariablesCache& gridFluxVarsCache,
+                                                               const int localSubFaceIdx,
+                                                               const bool lateralFaceHasDirichletPressure,
+                                                               const bool lateralFaceHasBJS)
+    {
+        // Get the transporting velocity, located at the scvf perpendicular to the current scvf where the dof
+        // of interest is located.
+        const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx);
+
+        // Check whether the own or the neighboring element is upstream.
+        const bool selfIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) );
+
+        Scalar momentum(0.0);
+        const bool canHigherOrder = canLateralSecondOrder_(scvf, selfIsUpstream, localSubFaceIdx, std::integral_constant<bool, useHigherOrder>{});
+
+        if (canHigherOrder)
+        {
+            const auto parallelUpwindingMomenta = getLateralUpwindingMomenta_(problem, fvGeometry, element, scvf, elemVolVars, faceVars,
+                                                                              transportingVelocity, localSubFaceIdx,
+                                                                              lateralFaceHasDirichletPressure, lateralFaceHasBJS,
+                                                                              std::integral_constant<bool, useHigherOrder>{});
+            momentum = doLateralMomentumUpwinding_(scvf, normalFace, parallelUpwindingMomenta, transportingVelocity, localSubFaceIdx, gridFluxVarsCache);
+        }
+        else
+        {
+            const auto parallelUpwindingMomenta = getLateralUpwindingMomenta_(problem, fvGeometry, element, scvf, elemVolVars, faceVars,
+                                                                              transportingVelocity, localSubFaceIdx,
+                                                                              lateralFaceHasDirichletPressure, lateralFaceHasBJS,
+                                                                              std::integral_constant<bool, false>{});
+            momentum = doLateralMomentumUpwinding_(scvf, normalFace, parallelUpwindingMomenta, transportingVelocity, localSubFaceIdx, gridFluxVarsCache);
+        }
+
+        return momentum;
+    }
+
+private:
+
+  /*!
+     * \brief Returns false as higher order methods are not enabled.
+     *
+     *        If higher order methods are not prescribed, this function will be called and false is returned..
+     *
+     * \param ownScvf the sub control volume face
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param false_type False if higher order methods are not enabled.
+     */
+    static bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf,
+                                       const Scalar transportingVelocity,
+                                       std::false_type)
+    {
+        return false;
+    }
+
+    /*!
+     * \brief Returns whether or not the face in question is far enough from the wall to handle higher order methods.
+     *
+     *        Evaluates which face is upstream.
+     *        If the face is upstream, and the scvf has a forward neighbor, higher order methods are possible.
+     *        If the face is downstream, and the scvf has a backwards neighbor, higher order methods are possible.
+     *        Otherwise, higher order methods are not possible.
+     *        If higher order methods are not prescribed, this function will not be called.
+     *
+     * \param ownScvf the sub control volume face
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param true_type True if higher order methods are enabled.
+     */
+    static bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf,
+                                       const Scalar transportingVelocity,
+                                       std::true_type)
+    {
+        // Depending on selfIsUpstream I have to check if I have a forward or a backward neighbor to retrieve
+        const bool selfIsUpstream = ownScvf.directionSign() != sign(transportingVelocity);
+
+        if (selfIsUpstream)
+        {
+            if (ownScvf.hasForwardNeighbor(0))
+                return true;
+            else
+                return false;
+        }
+        else
+        {
+            if (ownScvf.hasBackwardNeighbor(0))
+                return true;
+            else
+                return false;
+        }
+    }
+
+    /*!
+     * \brief Returns an array of the two momenta needed for basic upwinding methods.
+     *
+     * \param scvf The sub control volume face
+     * \param elemFaceVars The element face variables
+     * \param density The given density \f$\mathrm{[kg/m^3]}\f$
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param false_type False if higher order methods are not enabled.
+     */
+    static std::array<Scalar, 2> getFrontalUpwindingMomenta_(const SubControlVolumeFace& scvf,
+                                                             const ElementFaceVariables& elemFaceVars,
+                                                             const Scalar density,
+                                                             const Scalar transportingVelocity,
+                                                             std::false_type)
+    {
+        const Scalar momentumSelf = elemFaceVars[scvf].velocitySelf() * density;
+        const Scalar momentumOpposite = elemFaceVars[scvf].velocityOpposite() * density;
+        const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity);
+
+        return selfIsUpstream ? std::array<Scalar, 2>{momentumOpposite, momentumSelf}
+                              : std::array<Scalar, 2>{momentumSelf, momentumOpposite};
+    }
+
+    /*!
+     * \brief Returns an array of the three momenta needed for higher order upwinding methods.
+     *
+     * \param scvf The sub control volume face
+     * \param elemFaceVars The element face variables
+     * \param density The given density \f$\mathrm{[kg/m^3]}\f$
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param true_type True if higher order methods are enabled.
+     */
+    static std::array<Scalar, 3> getFrontalUpwindingMomenta_(const SubControlVolumeFace& scvf,
+                                                             const ElementFaceVariables& elemFaceVars,
+                                                             const Scalar density,
+                                                             const Scalar transportingVelocity,
+                                                             std::true_type)
+    {
+        const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity);
+        std::array<Scalar, 3> momenta{0.0, 0.0, 0.0};
+
+        if (selfIsUpstream)
+        {
+            momenta[0] = elemFaceVars[scvf].velocityOpposite() * density;
+            momenta[1] = elemFaceVars[scvf].velocitySelf() * density;
+            momenta[2] = elemFaceVars[scvf].velocityForward(0) * density;
+        }
+        else
+        {
+            momenta[0] = elemFaceVars[scvf].velocitySelf() * density;
+            momenta[1] = elemFaceVars[scvf].velocityOpposite() * density;
+            momenta[2] = elemFaceVars[scvf].velocityBackward(0) * density;
+        }
+
+        return momenta;
+    }
+
+    /*!
+     * \brief Returns the upwinded momentum for basic upwind schemes
+     *
+     * \param scvf The sub control volume face
+     * \param momenta The momenta to be upwinded
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param gridFluxVarsCache The grid flux variables cache
+     */
+    static Scalar doFrontalMomentumUpwinding_(const SubControlVolumeFace& scvf,
+                                              const std::array<Scalar, 2>& momenta,
+                                              const Scalar transportingVelocity,
+                                              const GridFluxVariablesCache& gridFluxVarsCache)
+    {
+        const auto& upwindScheme = gridFluxVarsCache.staggeredUpwindMethods();
+        return upwindScheme.upwind(momenta[0], momenta[1]);
+    }
+
+    /*!
+     * \brief Returns the upwinded momentum for higher order upwind schemes
+     *
+     * \param scvf The sub control volume face
+     * \param momenta The momenta to be upwinded
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param gridFluxVarsCache The grid flux variables cache
+     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
+    static Scalar doFrontalMomentumUpwinding_(const SubControlVolumeFace& scvf,
+                                              const std::array<Scalar, 3>& momenta,
+                                              const Scalar transportingVelocity,
+                                              const GridFluxVariablesCache& gridFluxVarsCache)
+    {
+        const auto& upwindScheme = gridFluxVarsCache.staggeredUpwindMethods();
+        const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity);
+        std::array<Scalar,3> distances = getFrontalDistances_(scvf, selfIsUpstream);
+        return upwindScheme.tvd(momenta, distances, selfIsUpstream, upwindScheme.tvdApproach());
+    }
+
+    /*!
+     * \brief Returns an array of distances needed for non-uniform higher order upwind schemes
+     *
+     * \param ownScvf The sub control volume face
+     * \param selfIsUpstream bool describing upstream face.
+     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
+    static std::array<Scalar, 3> getFrontalDistances_(const SubControlVolumeFace& ownScvf,
+                                                      const bool selfIsUpstream)
+    {
+        // Depending on selfIsUpstream the downstream and the (up)upstream distances are saved.
+        // distances {upstream to downstream distance, up-upstream to upstream distance, downstream staggered cell size}
+        std::array<Scalar, 3> distances{0.0, 0.0, 0.0};
+
+        if (selfIsUpstream)
+        {
+            distances[0] = ownScvf.selfToOppositeDistance();
+            distances[1] = ownScvf.axisData().inAxisForwardDistances[0];
+            distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisBackwardDistances[0]);
+        }
+        else
+        {
+            distances[0] = ownScvf.selfToOppositeDistance();
+            distances[1] = ownScvf.axisData().inAxisBackwardDistances[0];
+            distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisForwardDistances[0]);
+        }
+        return distances;
+    }
+
+    /*!
+     * \brief Check if a second order approximation for the lateral part of the advective term can be used
+     *
+     * This helper function checks if the scvf of interest is not too near to the
+     * boundary so that a dof upstream with respect to the upstream dof is available.
+     *
+     * \param ownScvf The SubControlVolumeFace we are considering
+     * \param selfIsUpstream @c true if the velocity at ownScvf is upstream wrt the transporting velocity
+     * \param localSubFaceIdx The local subface index
+     * \param true_type True when second order is enabled.
+     */
+    static bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf,
+                                       const bool selfIsUpstream,
+                                       const int localSubFaceIdx,
+                                       std::true_type)
+    {
+        if (selfIsUpstream)
+        {
+            // The self velocity is upstream. The downstream velocity can be assigned or retrieved
+            // from the boundary, even if there is no parallel neighbor.
+            return true;
+        }
+        else
+        {
+            // The self velocity is downstream. If there is no parallel neighbor I cannot use a second order approximation.
+            if (!ownScvf.hasParallelNeighbor(localSubFaceIdx, 0))
+                return false;
+            else
+                return true;
+        }
+    }
+
+    /*!
+     * \brief Check if a second order approximation for the lateral part of the advective term can be used
+     *        If higher order methods are not prescribed, this is called and false is returned.
+     */
+    static bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf,
+                                       const bool selfIsUpstream,
+                                       const int localSubFaceIdx,
+                                       std::false_type)
+    {   return false; }
+
+
+    /*!
+     * \brief Returns an array of the three momenta needed for higher order upwinding methods.
+     *
+     *  Only called if higher order methods are enabled and the scvf can use higher order methods.
+     */
+    static std::array<Scalar, 3> getLateralUpwindingMomenta_(const Problem& problem,
+                                                             const FVElementGeometry& fvGeometry,
+                                                             const Element& element,
+                                                             const SubControlVolumeFace& ownScvf,
+                                                             const ElementVolumeVariables& elemVolVars,
+                                                             const FaceVariables& faceVars,
+                                                             const Scalar transportingVelocity,
+                                                             const int localSubFaceIdx,
+                                                             bool lateralFaceHasDirichletPressure,
+                                                             bool lateralFaceHasBJS,
+                                                             std::true_type)
+    {
+        std::array<Scalar, 3> momenta{0.0, 0.0, 0.0};
+        const SubControlVolumeFace& lateralFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx);
+
+        // Check whether the own or the neighboring element is upstream.
+        const bool selfIsUpstream = lateralFace.directionSign() == sign(transportingVelocity);
+
+        // Get the volume variables of the own and the neighboring element
+        const auto& insideVolVars = elemVolVars[lateralFace.insideScvIdx()];
+        const auto& outsideVolVars = elemVolVars[lateralFace.outsideScvIdx()];
+
+        // The local index of the faces that is opposite to localSubFaceIdx
+        const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1;
+
+        if (selfIsUpstream)
+        {
+            momenta[1] = faceVars.velocitySelf() * insideVolVars.density();
+
+            if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0))
+                momenta[0] = faceVars.velocityParallel(localSubFaceIdx, 0) * insideVolVars.density();
+            else
+                momenta[0] = getParallelVelocityFromBoundary_(problem, ownScvf, lateralFace,
+                                                              faceVars.velocitySelf(), localSubFaceIdx,
+                                                              element, lateralFaceHasDirichletPressure,
+                                                              lateralFaceHasBJS) * insideVolVars.density();
+
+            // The "upstream-upstream" velocity is retrieved from the other parallel neighbor or from the boundary.
+            if (ownScvf.hasParallelNeighbor(oppositeSubFaceIdx, 0))
+                momenta[2] = faceVars.velocityParallel(oppositeSubFaceIdx, 0) * insideVolVars.density();
+            else
+                momenta[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, ownScvf,
+                                                                   oppositeSubFaceIdx, element,
+                                                                   (momenta[1]/insideVolVars.density())) * insideVolVars.density();
+        }
+        else
+        {
+            momenta[0] = faceVars.velocitySelf() * outsideVolVars.density();
+            momenta[1] = faceVars.velocityParallel(localSubFaceIdx, 0) * outsideVolVars.density();
+
+            // If there is another parallel neighbor I can assign the "upstream-upstream" velocity, otherwise I retrieve it from the boundary.
+            if (ownScvf.hasParallelNeighbor(localSubFaceIdx, 1))
+                momenta[2] = faceVars.velocityParallel(localSubFaceIdx, 1) * outsideVolVars.density();
+            else
+            {
+                const Element& elementParallel = fvGeometry.fvGridGeometry().element(fvGeometry.scv(lateralFace.outsideScvIdx()));
+                const SubControlVolumeFace& firstParallelScvf = fvGeometry.scvf(lateralFace.outsideScvIdx(), ownScvf.localFaceIdx());
+                momenta[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, firstParallelScvf,
+                                                                   localSubFaceIdx, elementParallel,
+                                                                   (momenta[1]/outsideVolVars.density())) * outsideVolVars.density();
+            }
+        }
+        return momenta;
+
+    }
+
+    /*!
+     * \brief Returns an array of the two momenta needed for basic upwinding methods.
+     *
+     *  Called if higher order methods are not enabled of if the scvf can not use higher order methods.
+     */
+    static std::array<Scalar, 2> getLateralUpwindingMomenta_(const Problem& problem,
+                                                             const FVElementGeometry& fvGeometry,
+                                                             const Element& element,
+                                                             const SubControlVolumeFace& ownScvf,
+                                                             const ElementVolumeVariables& elemVolVars,
+                                                             const FaceVariables& faceVars,
+                                                             const Scalar transportingVelocity,
+                                                             const int localSubFaceIdx,
+                                                             bool lateralFaceHasDirichletPressure,
+                                                             bool lateralFaceHasBJS,
+                                                             std::false_type)
+    {
+         // Check whether the own or the neighboring element is upstream.
+        const SubControlVolumeFace& lateralFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx);
+        const bool selfIsUpstream = lateralFace.directionSign() == sign(transportingVelocity);
+
+        // Get the volume variables of the own and the neighboring element
+        const auto& insideVolVars = elemVolVars[lateralFace.insideScvIdx()];
+        const auto& outsideVolVars = elemVolVars[lateralFace.outsideScvIdx()];
+
+        const Scalar momentumSelf = faceVars.velocitySelf() * insideVolVars.density();
+
+        const Scalar momentumParallel = ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)
+                                      ? faceVars.velocityParallel(localSubFaceIdx, 0) * outsideVolVars.density()
+                                      : (getParallelVelocityFromBoundary_(problem, ownScvf, lateralFace,
+                                                                         faceVars.velocitySelf(), localSubFaceIdx, element,
+                                                                         lateralFaceHasDirichletPressure, lateralFaceHasBJS)
+                                      * insideVolVars.density());
+
+        return selfIsUpstream ? std::array<Scalar, 2>{momentumParallel, momentumSelf}
+                              : std::array<Scalar, 2>{momentumSelf, momentumParallel};
+    }
+
+    /*!
+     * \brief Returns the upwinded momentum for basic upwind schemes
+     *
+     *        Fowards to Frontal Momentum Upwinding method
+     */
+    static Scalar doLateralMomentumUpwinding_(const SubControlVolumeFace& scvf,
+                                              const SubControlVolumeFace& normalScvf,
+                                              const std::array<Scalar, 2>& momenta,
+                                              const Scalar transportingVelocity,
+                                              const int localSubFaceIdx,
+                                              const GridFluxVariablesCache& gridFluxVarsCache)
+    {
+        return doFrontalMomentumUpwinding_(scvf, momenta, transportingVelocity, gridFluxVarsCache);
+    }
+
+    /*!
+     * \brief Returns the upwinded momentum for higher order upwind schemes
+     *
+     * \param scvf The sub control volume face
+     * \param normalScvf The normal sub control volume face
+     * \param momenta The momenta to be upwinded
+     * \param transportingVelocity The average of the self and opposite velocities.
+     * \param localSubFaceIdx  The local index of the subface
+     * \param gridFluxVarsCache The grid flux variables cache
+     */
+    static Scalar doLateralMomentumUpwinding_(const SubControlVolumeFace& scvf,
+                                              const SubControlVolumeFace& normalScvf,
+                                              const std::array<Scalar, 3>& momenta,
+                                              const Scalar transportingVelocity,
+                                              const int localSubFaceIdx,
+                                              const GridFluxVariablesCache& gridFluxVarsCache)
+    {
+        const bool selfIsUpstream = ( normalScvf.directionSign() == sign(transportingVelocity) );
+        const auto& upwindScheme = gridFluxVarsCache.staggeredUpwindMethods();
+        std::array<Scalar,3> distances = getLateralDistances_(scvf, localSubFaceIdx, selfIsUpstream);
+        return upwindScheme.tvd(momenta, distances, selfIsUpstream, upwindScheme.tvdApproach());
+    }
+
+    /*!
+     * \brief Returns an array of distances needed for non-uniform higher order upwind schemes
+     *
+     * \param ownScvf The sub control volume face
+     * \param localSubFaceIdx  The local index of the subface
+     * \param selfIsUpstream bool describing upstream face.
+     */
+    template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0>
+    static std::array<Scalar, 3> getLateralDistances_(const SubControlVolumeFace& ownScvf,
+                                                      const int localSubFaceIdx,
+                                                      const bool selfIsUpstream)
+    {
+        // distances {upstream to downstream distance, up-upstream to upstream distance, downstream staggered cell size}
+        std::array<Scalar, 3> distances{0.0, 0.0, 0.0};
+
+        // The local index of the faces that is opposite to localSubFaceIdx
+        const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1;
+
+        if (selfIsUpstream)
+        {
+            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
+            distances[1] = ownScvf.cellCenteredParallelDistance(oppositeSubFaceIdx, 0);
+            if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0))
+                distances[2] = ownScvf.pairData(localSubFaceIdx).parallelDistances[1];
+            else
+                distances[2] = ownScvf.area() / 2.0;
+        }
+        else
+        {
+            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
+            distances[1] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 1);
+            distances[2] = ownScvf.pairData(localSubFaceIdx).parallelDistances[1];
+        }
+
+        return distances;
+    }
+
+    /*!
+     * \brief Return the outer parallel velocity for normal faces that are on the boundary and therefore have no neighbor.
+     *
+     * Calls the problem to retrieve a fixed value set on the boundary.
+     *
+     * \param problem The problem
+     * \param scvf The SubControlVolumeFace that is normal to the boundary
+     * \param normalFace The face at the boundary
+     * \param velocitySelf the velocity at scvf
+     * \param localSubFaceIdx The local index of the face that is on the boundary
+     * \param element The element that is on the boundary
+     * \param lateralFaceHasDirichletPressure @c true if there is a dirichlet condition for the pressure on the boundary
+     * \param lateralFaceHasBJS @c true if there is a BJS condition fot the velocity on the boundary
+     */
+    static Scalar getParallelVelocityFromBoundary_(const Problem& problem,
+                                                   const SubControlVolumeFace& scvf,
+                                                   const SubControlVolumeFace& normalFace,
+                                                   const Scalar velocitySelf,
+                                                   const int localSubFaceIdx,
+                                                   const Element& element,
+                                                   const bool lateralFaceHasDirichletPressure,
+                                                   const bool lateralFaceHasBJS)
+    {
+        // If there is a Dirichlet condition for the pressure we assume zero gradient for the velocity,
+        // so the velocity at the boundary equal to that on the scvf.
+        if (lateralFaceHasDirichletPressure)
+            return velocitySelf;
+
+        const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx);
+        if (lateralFaceHasBJS)
+            return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, velocitySelf);
+        return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())];
+    }
+
+    /*!
+     * \brief Return a velocity value from a boundary for which the boundary conditions have to be checked.
+     *
+     * \param problem The problem
+     * \param scvf The SubControlVolumeFace that is normal to the boundary
+     * \param localIdx The local index of the face that is on the boundary
+     * \param boundaryElement The element that is on the boundary
+     * \param parallelVelocity The velocity over scvf
+     */
+    static Scalar getParallelVelocityFromOtherBoundary_(const Problem& problem,
+                                                        const FVElementGeometry& fvGeometry,
+                                                        const SubControlVolumeFace& scvf,
+                                                        const int localIdx,
+                                                        const Element& boundaryElement,
+                                                        const Scalar parallelVelocity)
+    {
+        // A ghost subface at the boundary is created, featuring the location of the sub face's center
+        const SubControlVolumeFace& boundaryNormalFace = fvGeometry.scvf(scvf.insideScvIdx(), scvf.pairData(localIdx).localNormalFaceIdx);
+        GlobalPosition boundarySubFaceCenter = scvf.pairData(localIdx).virtualFirstParallelFaceDofPos + boundaryNormalFace.center();
+        boundarySubFaceCenter *= 0.5;
+        const SubControlVolumeFace boundarySubFace = makeGhostFace_(boundaryNormalFace, boundarySubFaceCenter);
+
+        // The boundary condition is checked, in case of symmetry or Dirichlet for the pressure
+        // a gradient of zero is assumed in the direction normal to the bounadry, while if there is
+        // Dirichlet of BJS for the velocity the related values are exploited.
+        const auto bcTypes = problem.boundaryTypes(boundaryElement, boundarySubFace);
+
+        if (bcTypes.isDirichlet(Indices::velocity(scvf.directionIndex())))
+        {
+            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
+            return problem.dirichlet(boundaryElement, ghostFace)[Indices::velocity(scvf.directionIndex())];
+        }
+        else if (bcTypes.isSymmetry() || bcTypes.isDirichlet(Indices::pressureIdx))
+            return parallelVelocity;
+        else if (bcTypes.isBJS(Indices::velocity(scvf.directionIndex())))
+        {
+            const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx);
+            return problem.bjsVelocity(boundaryElement, scvf, boundaryNormalFace, localIdx, parallelVelocity);
+        }
+        else
+        {
+            // Neumann conditions are not well implemented
+            DUNE_THROW(Dune::InvalidStateException, "Something went wrong with the boundary conditions for the momentum equations at global position " << boundarySubFaceCenter);
+        }
+    }
+
+
+    //! helper function to conveniently create a ghost face used to retrieve boundary values from the problem
+    static SubControlVolumeFace makeGhostFace_(const SubControlVolumeFace& ownScvf, const GlobalPosition& pos)
+    {
+        return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()},
+                                    ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index());
+    };
+
+    //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest
+    static SubControlVolumeFace makeParallelGhostFace_(const SubControlVolumeFace& ownScvf, const int localSubFaceIdx)
+    {
+        return makeGhostFace_(ownScvf, ownScvf.pairData(localSubFaceIdx).virtualFirstParallelFaceDofPos);
+    };
+};
+
+} // end namespace Dumux
+
+#endif // DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH
diff --git a/dumux/freeflow/higherorderapproximation.hh b/dumux/freeflow/staggeredupwindmethods.hh
similarity index 64%
rename from dumux/freeflow/higherorderapproximation.hh
rename to dumux/freeflow/staggeredupwindmethods.hh
index 601bf115d219eec1d22c79af9b71e02e387feee5..26754bff295067f88a2bf60bf6df7fa8a623f6cb 100644
--- a/dumux/freeflow/higherorderapproximation.hh
+++ b/dumux/freeflow/staggeredupwindmethods.hh
@@ -20,8 +20,8 @@
   * \brief This file contains different higher order methods for approximating the velocity.
   */
 
-#ifndef DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH
-#define DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH
+#ifndef DUMUX_UPWINDING_METHODS_HH
+#define DUMUX_UPWINDING_METHODS_HH
 
 #include <cmath>
 #include <functional>
@@ -47,17 +47,19 @@ enum class DifferencingScheme
 /**
   * \brief This file contains different higher order methods for approximating the velocity.
   */
-template<class Scalar>
-class HigherOrderApproximation
+template<class Scalar, int upwindSchemeOrder>
+class StaggeredUpwindMethods
 {
 public:
-    HigherOrderApproximation(const std::string& paramGroup = "")
+    StaggeredUpwindMethods(const std::string& paramGroup = "")
     {
-        if (hasParamInGroup(paramGroup, "Discretization.TvdApproach"))
+        upwindWeight_ = getParamFromGroup<Scalar>(paramGroup, "Flux.UpwindWeight");
+
+        if (upwindSchemeOrder > 1)
         {
             // Read the runtime parameters
-            tvdApproach_ = tvdApproachFromString(getParamFromGroup<std::string>(paramGroup, "Discretization.TvdApproach"));
-            differencingScheme_ = differencingSchemeFromString(getParamFromGroup<std::string>(paramGroup, "Discretization.DifferencingScheme"));
+            tvdApproach_ = tvdApproachFromString(getParamFromGroup<std::string>(paramGroup, "Flux.TvdApproach", "Uniform"));
+            differencingScheme_ = differencingSchemeFromString(getParamFromGroup<std::string>(paramGroup, "Flux.DifferencingScheme", "Minmod"));
 
             // Assign the limiter_ depending on the differencing scheme
             switch (differencingScheme_)
@@ -107,6 +109,33 @@ public:
                     break;
                 }
             }
+
+            if (!hasParamInGroup(paramGroup, "Flux.TvdApproach"))
+            {
+                std::cout << "No TvdApproach specified. Defaulting to the Uniform method." << "\n";
+                std::cout << "Other available TVD approaches for uniform (and nonuniform) grids are as follows: \n"
+                          << "  " << tvdApproachToString(TvdApproach::uniform) << ": assumes a Uniform cell size distribution\n"
+                          << "  " << tvdApproachToString(TvdApproach::li) << ": Li's approach for nonuniform cell sizes\n"
+                          << "  " << tvdApproachToString(TvdApproach::hou) << ": Hou's approach for nonuniform cell sizes \n";
+                std::cout << "Each approach can be specified as written above in the Flux group under the title TvdApproach in your input file. \n";
+            }
+            if (!hasParamInGroup(paramGroup, "Flux.DifferencingScheme"))
+            {
+
+                std::cout << "No DifferencingScheme specified. Defaulting to the Minmod scheme." << "\n";
+                std::cout << "Other available Differencing Schemes are as follows: \n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::vanleer) << ": The Vanleer flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::vanalbada) << ": The Vanalbada flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::minmod) << ": The Minmod flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::superbee) << ": The Superbee flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::umist) << ": The Umist flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::mclimiter) << ": The Mclimiter flux limiter\n"
+                          << "  " << differencingSchemeToString(DifferencingScheme::wahyd) << ": The Wahyd flux limiter";
+                std::cout << "Each scheme can be specified as written above in the Flux group under the variable DifferencingScheme in your input file. \n";
+            }
+
+            std::cout << "Using the tvdApproach \"" << tvdApproachToString(tvdApproach_)
+                      << "\" and the differencing Scheme \" " << differencingSchemeToString(differencingScheme_) << "\" \n";
         }
         else
         {
@@ -127,9 +156,9 @@ public:
         if (tvd == "Hou") return TvdApproach::hou;
         DUNE_THROW(ParameterException, "\nThis tvd approach : \"" << tvd << "\" is not implemented.\n"
                                        << "The available TVD approaches for uniform (and nonuniform) grids are as follows: \n"
-                                       << tvdApproachToString(TvdApproach::uniform) << ": Assumes a uniform cell size distribution\n"
-                                       << tvdApproachToString(TvdApproach::li) << ": Li's approach for nonuniform cell sizes\n"
-                                       << tvdApproachToString(TvdApproach::hou) << ": Hou's approach for nonuniform cell sizes");
+                                       << tvdApproachToString(TvdApproach::uniform) << ": assumes a uniform cell size distribution\n"
+                                       << tvdApproachToString(TvdApproach::li) << ": li's approach for nonuniform cell sizes\n"
+                                       << tvdApproachToString(TvdApproach::hou) << ": hou's approach for nonuniform cell sizes");
     }
 
     /**
@@ -152,13 +181,13 @@ public:
      */
     DifferencingScheme differencingSchemeFromString(const std::string& differencingScheme)
     {
-        if (differencingScheme == "vanleer") return DifferencingScheme::vanleer;
-        if (differencingScheme == "vanalbada") return DifferencingScheme::vanalbada;
-        if (differencingScheme == "minmod") return DifferencingScheme::minmod;
-        if (differencingScheme == "superbee") return DifferencingScheme::superbee;
-        if (differencingScheme == "umist") return DifferencingScheme::umist;
-        if (differencingScheme == "mclimiter") return DifferencingScheme::mclimiter;
-        if (differencingScheme == "wahyd") return DifferencingScheme::wahyd;
+        if (differencingScheme == "Vanleer") return DifferencingScheme::vanleer;
+        if (differencingScheme == "Vanalbada") return DifferencingScheme::vanalbada;
+        if (differencingScheme == "Minmod") return DifferencingScheme::minmod;
+        if (differencingScheme == "Superbee") return DifferencingScheme::superbee;
+        if (differencingScheme == "Umist") return DifferencingScheme::umist;
+        if (differencingScheme == "Mclimiter") return DifferencingScheme::mclimiter;
+        if (differencingScheme == "Wahyd") return DifferencingScheme::wahyd;
         DUNE_THROW(ParameterException, "\nThis differencing scheme: \"" << differencingScheme << "\" is not implemented.\n"
                                        << "The available differencing schemes are as follows: \n"
                                        << differencingSchemeToString(DifferencingScheme::vanleer) << ": The Vanleer flux limiter\n"
@@ -177,13 +206,13 @@ public:
     {
         switch (differencingScheme)
         {
-            case DifferencingScheme::vanleer: return "vanleer";
-            case DifferencingScheme::vanalbada: return "vanalbada";
-            case DifferencingScheme::minmod: return "minmod";
-            case DifferencingScheme::superbee: return "superbee";
-            case DifferencingScheme::umist: return "umist";
-            case DifferencingScheme::mclimiter: return "mclimiter";
-            case DifferencingScheme::wahyd: return "wahyd";
+            case DifferencingScheme::vanleer: return "Vanleer";
+            case DifferencingScheme::vanalbada: return "Vanalbada";
+            case DifferencingScheme::minmod: return "Minmod";
+            case DifferencingScheme::superbee: return "Superbee";
+            case DifferencingScheme::umist: return "Umist";
+            case DifferencingScheme::mclimiter: return "Mclimiter";
+            case DifferencingScheme::wahyd: return "Wahyd";
             default: return "Invalid"; // should never be reached
         }
     }
@@ -191,68 +220,19 @@ public:
     /**
       * \brief Upwind Method
       */
-    Scalar upwind(const Scalar downstreamVelocity,
-                  const Scalar upstreamVelocity,
-                  const Scalar density,
-                  const Scalar upwindWeight) const
-    {
-        return (upwindWeight * upstreamVelocity + (1.0 - upwindWeight) * downstreamVelocity) * density;
-    }
-
-    /**
-      * \brief Central Differencing Method
-      */
-    Scalar centralDifference(const Scalar downstreamVelocity,
-                             const Scalar upstreamVelocity,
-                             const Scalar density) const
-    {
-        return (0.5 * (upstreamVelocity + downstreamVelocity)) * density;
-    }
-
-    /**
-      * \brief Linear Upwind Method
-      */
-    Scalar linearUpwind(const Scalar upstreamVelocity,
-                        const Scalar upUpstreamVelocity,
-                        const Scalar upstreamToDownstreamDistance,
-                        const Scalar upUpstreamToUpstreamDistance,
-                        const Scalar density) const
+    Scalar upwind(const Scalar downstreamMomentum,
+                  const Scalar upstreamMomentum) const
     {
-        Scalar zeroOrder = upstreamVelocity;
-        Scalar firstOrder = -1.0 * ((upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance) * ( upstreamToDownstreamDistance / -2.0);
-        return (zeroOrder + firstOrder) * density;
-    }
-
-    /**
-      * \brief QUICK upwinding Scheme: Quadratic Upstream Interpolation for Convective Kinematics
-      */
-    Scalar upwindQUICK(const Scalar downstreamVelocity,
-                       const Scalar upstreamVelocity,
-                       const Scalar upUpstreamVelocity,
-                       const Scalar upstreamToDownstreamDistance,
-                       const Scalar upUpstreamToUpstreamDistance,
-                       const Scalar density) const
-    {
-        Scalar normalDistance = (upUpstreamToUpstreamDistance + upstreamToDownstreamDistance) / 2.0;
-        Scalar zeroOrder = upstreamVelocity;
-        Scalar firstOrder = ((downstreamVelocity - upstreamVelocity) / 2.0);
-        Scalar secondOrder = -(((downstreamVelocity - upstreamVelocity) / upstreamToDownstreamDistance) - ((upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance))
-                           * upstreamToDownstreamDistance * upstreamToDownstreamDistance / (8.0 * normalDistance);
-        return (zeroOrder + firstOrder + secondOrder) * density;
+        return (upwindWeight_ * upstreamMomentum + (1.0 - upwindWeight_) * downstreamMomentum);
     }
 
     /**
      * \brief Tvd Scheme: Total Variation Diminishing
      *
      */
-    Scalar tvd(const Scalar downstreamVelocity,
-               const Scalar upstreamVelocity,
-               const Scalar upUpstreamVelocity,
-               const Scalar upstreamToDownstreamDistance,
-               const Scalar upUpstreamToUpstreamDistance,
-               const Scalar downstreamStaggeredCellSize,
+    Scalar tvd(const std::array<Scalar,3>& momenta,
+               const std::array<Scalar,3>& distances,
                const bool selfIsUpstream,
-               const Scalar density,
                const TvdApproach tvdApproach) const
     {
         Scalar momentum = 0.0;
@@ -260,17 +240,17 @@ public:
         {
             case TvdApproach::uniform :
             {
-                momentum += tvdUniform(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, density);
+                momentum += tvdUniform(momenta, distances, selfIsUpstream);
                 break;
             }
             case TvdApproach::li :
             {
-                momentum += tvdLi(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, upstreamToDownstreamDistance, upUpstreamToUpstreamDistance, selfIsUpstream, density);
+                momentum += tvdLi(momenta, distances, selfIsUpstream);
                 break;
             }
             case TvdApproach::hou :
             {
-                momentum += tvdHou(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, upstreamToDownstreamDistance, upUpstreamToUpstreamDistance, downstreamStaggeredCellSize, density);
+                momentum += tvdHou(momenta, distances, selfIsUpstream);
                 break;
             }
             default:
@@ -287,22 +267,24 @@ public:
      *
      * This function assumes the cell size distribution to be uniform.
      */
-    Scalar tvdUniform(const Scalar downstreamVelocity,
-                      const Scalar upstreamVelocity,
-                      const Scalar upUpstreamVelocity,
-                      const Scalar density) const
+    Scalar tvdUniform(const std::array<Scalar,3>& momenta,
+                      const std::array<Scalar,3>& distances,
+                      const bool selfIsUpstream) const
     {
         using std::isfinite;
-        const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity);
+        const Scalar downstreamMomentum = momenta[0];
+        const Scalar upstreamMomentum = momenta[1];
+        const Scalar upUpstreamMomentum = momenta[2];
+        const Scalar ratio = (upstreamMomentum - upUpstreamMomentum) / (downstreamMomentum - upstreamMomentum);
 
         // If the velocity field is uniform (like at the first newton step) we get a NaN
         if(ratio > 0.0 && isfinite(ratio))
         {
-            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity);
-            return density * (upstreamVelocity + secondOrderTerm);
+            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamMomentum - upstreamMomentum);
+            return (upstreamMomentum + secondOrderTerm);
         }
         else
-            return density * upstreamVelocity;
+            return upstreamMomentum;
     }
 
     /**
@@ -312,31 +294,33 @@ public:
       * It tries to reconstruct the value for the velocity at the upstream-upstream point
       * if the grid was uniform.
       */
-    Scalar tvdLi(const Scalar downstreamVelocity,
-                 const Scalar upstreamVelocity,
-                 const Scalar upUpstreamVelocity,
-                 const Scalar upstreamToDownstreamDistance,
-                 const Scalar upUpstreamToUpstreamDistance,
-                 const bool selfIsUpstream,
-                 const Scalar density) const
+    Scalar tvdLi(const std::array<Scalar,3>& momenta,
+                 const std::array<Scalar,3>& distances,
+                 const bool selfIsUpstream) const
     {
         using std::isfinite;
-        // I need the information of selfIsUpstream to get the correct sign because upUpstreamToUpstreamDistance is always positive
-        const Scalar upUpstreamGradient = (upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance * selfIsUpstream;
+        const Scalar downstreamMomentum = momenta[0];
+        const Scalar upstreamMomentum = momenta[1];
+        const Scalar upUpstreamMomentum = momenta[2];
+        const Scalar upstreamToDownstreamDistance = distances[0];
+        const Scalar upUpstreamToUpstreamDistance = distances[1];
+
+        // selfIsUpstream is required to get the correct sign because upUpstreamToUpstreamDistance is always positive
+        const Scalar upUpstreamGradient = (upstreamMomentum - upUpstreamMomentum) / upUpstreamToUpstreamDistance * selfIsUpstream;
 
         // Distance between the upUpstream node and the position where it should be if the grid were uniform.
         const Scalar correctionDistance = upUpstreamToUpstreamDistance - upstreamToDownstreamDistance;
-        const Scalar reconstrutedUpUpstreamVelocity = upUpstreamVelocity + upUpstreamGradient * correctionDistance;
-        const Scalar ratio = (upstreamVelocity - reconstrutedUpUpstreamVelocity) / (downstreamVelocity - upstreamVelocity);
+        const Scalar reconstrutedUpUpstreamVelocity = upUpstreamMomentum + upUpstreamGradient * correctionDistance;
+        const Scalar ratio = (upstreamMomentum - reconstrutedUpUpstreamVelocity) / (downstreamMomentum - upstreamMomentum);
 
         // If the velocity field is uniform (like at the first newton step) we get a NaN
         if(ratio > 0.0 && isfinite(ratio))
         {
-            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity);
-            return density * (upstreamVelocity + secondOrderTerm);
+            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamMomentum - upstreamMomentum);
+            return (upstreamMomentum + secondOrderTerm);
         }
         else
-            return density * upstreamVelocity;
+            return upstreamMomentum;
     }
 
     /**
@@ -345,16 +329,18 @@ public:
      * This function manages the non uniformities of the grid according to [Hou, Simons, Hinkelmann 2007].
      * It should behave better then the Li's version in very stretched grids.
      */
-    Scalar tvdHou(const Scalar downstreamVelocity,
-                  const Scalar upstreamVelocity,
-                  const Scalar upUpstreamVelocity,
-                  const Scalar upstreamToDownstreamDistance,
-                  const Scalar upUpstreamToUpstreamDistance,
-                  const Scalar downstreamStaggeredCellSize,
-                  const Scalar density) const
+    Scalar tvdHou(const std::array<Scalar,3>& momenta,
+                  const std::array<Scalar,3>& distances,
+                  const bool selfIsUpstream) const
     {
         using std::isfinite;
-        const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity)
+        const Scalar downstreamMomentum = momenta[0];
+        const Scalar upstreamMomentum = momenta[1];
+        const Scalar upUpstreamMomentum = momenta[2];
+        const Scalar upstreamToDownstreamDistance = distances[0];
+        const Scalar upUpstreamToUpstreamDistance = distances[1];
+        const Scalar downstreamStaggeredCellSize = distances[2];
+        const Scalar ratio = (upstreamMomentum - upUpstreamMomentum) / (downstreamMomentum - upstreamMomentum)
                            * upstreamToDownstreamDistance / upUpstreamToUpstreamDistance;
 
         // If the velocity field is uniform (like at the first newton step) we get a NaN
@@ -362,11 +348,11 @@ public:
         {
             const Scalar upstreamStaggeredCellSize = 0.5 * (upstreamToDownstreamDistance + upUpstreamToUpstreamDistance);
             const Scalar R = (upstreamStaggeredCellSize + downstreamStaggeredCellSize) / upstreamStaggeredCellSize;
-            const Scalar secondOrderTerm = limiter_(ratio, R) / R * (downstreamVelocity - upstreamVelocity);
-            return density * (upstreamVelocity + secondOrderTerm);
+            const Scalar secondOrderTerm = limiter_(ratio, R) / R * (downstreamMomentum - upstreamMomentum);
+            return (upstreamMomentum + secondOrderTerm);
         }
         else
-            return density * upstreamVelocity;
+            return upstreamMomentum;
     }
 
     /**
@@ -451,10 +437,11 @@ public:
 private:
     TvdApproach tvdApproach_;
     DifferencingScheme differencingScheme_;
+    Scalar upwindWeight_;
 
     std::function<Scalar(const Scalar, const Scalar)> limiter_;
 };
 
 } // end namespace Dumux
 
-#endif // DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH
+#endif // DUMUX_UPWINDING_METHODS_HH
diff --git a/test/discretization/staggered/test_staggered_free_flow_geometry.cc b/test/discretization/staggered/test_staggered_free_flow_geometry.cc
index 59c12bf843499cb381d89778a0fbda8a7dc3497a..d2f5387926f24149271b46fbf3d26bc45795860e 100644
--- a/test/discretization/staggered/test_staggered_free_flow_geometry.cc
+++ b/test/discretization/staggered/test_staggered_free_flow_geometry.cc
@@ -53,13 +53,14 @@ public:
 } // end namespace Detail
 
 //! the fv grid geometry traits for this test
-template<class GridView>
+template<class GridView, int upwOrder>
 struct TestFVGGTraits : public DefaultMapperTraits<GridView>
 {
     using SubControlVolume = CCSubControlVolume<GridView>;
-    using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView>;
+    using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView, upwOrder>;
     using IntersectionMapper = ConformingGridIntersectionMapper<GridView>;
-    using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView>;
+    using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView, upwOrder>;
+    static constexpr int upwindSchemeOrder = upwOrder;
 
     struct DofTypeIndices
     {
@@ -70,8 +71,8 @@ struct TestFVGGTraits : public DefaultMapperTraits<GridView>
     template<class FVGridGeometry>
     using ConnectivityMap = StaggeredFreeFlowConnectivityMap<FVGridGeometry>;
 
-    template<class FVGridGeometry, bool enableCache>
-    using LocalView = StaggeredFVElementGeometry<FVGridGeometry, enableCache>;
+    template<class FVGridGeometry, bool cachingEnabled>
+    using LocalView = StaggeredFVElementGeometry<FVGridGeometry, cachingEnabled>;
 };
 
 } // end namespace Dumux
@@ -92,8 +93,10 @@ int main (int argc, char *argv[]) try
 
     constexpr int dim = Grid::dimension;
 
+    static constexpr int upwindSchemeOrder = 2;
+
     using FVGridGeometry = StaggeredFVGridGeometry<typename Grid::LeafGridView, /*enable caching=*/ true,
-                                                   TestFVGGTraits<typename Grid::LeafGridView> >;
+                                                   TestFVGGTraits<typename Grid::LeafGridView, upwindSchemeOrder> >;
     using FVElementGeometry = typename FVGridGeometry::LocalView;
     using SubControlVolume = typename FVElementGeometry::SubControlVolume;
     using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace;
@@ -159,11 +162,11 @@ int main (int argc, char *argv[]) try
                 std::cout <<  std::fixed << std::left << std::setprecision(2);
 
                 std::cout << " On Axis Dof Index: \n";
-                if(fvGridGeometry.order() > 1)
+                if(fvGridGeometry.upwindStencilOrder() > 1)
                 {std::cout << " | Forward dofIdx : " << std::setw(3) << scvf.axisData().inAxisForwardDofs[0] << "\n";}
                 std::cout << " | Self dofIdx : " << std::setw(3) << scvf.dofIndex() << "\n";
                 std::cout << " | Opposite dofIdx : " << std::setw(3) << scvf.dofIndexOpposingFace() << "\n";
-                if(fvGridGeometry.order() > 1)
+                if(fvGridGeometry.upwindStencilOrder() > 1)
                 {std::cout << " | Backward dofIdx : " << std::setw(3) << scvf.axisData().inAxisBackwardDofs[0] << "\n";}
 
                 std::cout << " Normal Dof Index: \n";
@@ -176,22 +179,22 @@ int main (int argc, char *argv[]) try
                 std::cout << " Parallel Dof Index: \n";
                 for(int i = 0; i < scvf.pairData().size(); i++)
                 {
-                    for(int j = 0; j < fvGridGeometry.order(); j++)
+                    for(int j = 0; j < fvGridGeometry.upwindStencilOrder(); j++)
                     {
                         std::cout << " | Parallel Dof "<< j << " on axis " << i << ": "<<  std::setw(3) << scvf.pairData(i).parallelDofs[j] << "\n";
                     }
                 }
 
                 std::cout << " Distances: \n";
-                if(fvGridGeometry.order() > 1)
+                if(fvGridGeometry.upwindStencilOrder() > 1)
                 {std::cout << " | Opposite To Backwards Face Dist : " << std::setw(3) << scvf.axisData().inAxisBackwardDistances[0] << "\n";}
                 std::cout << " | self To Opposite Dist : " << std::setw(3) << scvf.selfToOppositeDistance() << "\n";
-                if(fvGridGeometry.order() > 1)
-                {std::cout << " | Opposite To Backwards Face Dist : " << std::setw(3) << scvf.axisData().inAxisBackwardDistances[0] << "\n";}
+                if(fvGridGeometry.upwindStencilOrder() > 1)
+                {std::cout << " | self To Forwards Face Dist : " << std::setw(3) << scvf.axisData().inAxisForwardDistances[0] << "\n";}
 
                 for(int i = 0; i < scvf.pairData().size(); i++)
                 {
-                    for(int j = 0; j < fvGridGeometry.order(); j++)
+                    for(int j = 0; j < fvGridGeometry.upwindStencilOrder(); j++)
                     {
                         std::cout << " | Parallel Distance "<< j << " on axis " << i << ": "<< std::setw(3) << scvf.pairData(i).parallelDistances[j] << "\n";
                     }
diff --git a/test/freeflow/navierstokes/kovasznay/CMakeLists.txt b/test/freeflow/navierstokes/kovasznay/CMakeLists.txt
index f549843b312c1e46c5a53c91d6af8746889a7d06..3b2f353413d0744c1d8f05116eb4fbadeee4c1e0 100644
--- a/test/freeflow/navierstokes/kovasznay/CMakeLists.txt
+++ b/test/freeflow/navierstokes/kovasznay/CMakeLists.txt
@@ -2,6 +2,7 @@ dune_add_test(NAME test_ff_navierstokes_kovasznay
               LABELS freeflow navierstokes
               SOURCES main.cc
               CMAKE_GUARD HAVE_UMFPACK
+              COMPILE_DEFINITIONS UPWINDSCHEMEORDER=1
               COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py
               CMD_ARGS       --script fuzzy
                              --files ${CMAKE_SOURCE_DIR}/test/references/test_ff_navierstokes_kovasznay-reference.vtu
@@ -10,14 +11,17 @@ dune_add_test(NAME test_ff_navierstokes_kovasznay
                              -Problem.Name test_ff_navierstokes_kovasznay")
 
 dune_add_test(NAME test_ff_navierstokes_kovasznay_higherorder
-              TARGET test_ff_navierstokes_kovasznay
-              LABELS freeflow navierstokes
+              SOURCES main.cc
+              LABELS freeflow
+              CMAKE_GUARD HAVE_UMFPACK
+              COMPILE_DEFINITIONS UPWINDSCHEMEORDER=2
               COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py
               CMD_ARGS       --script fuzzy
                              --files ${CMAKE_SOURCE_DIR}/test/references/test_ff_navierstokes_kovasznay_higherorder-reference.vtu
                                      ${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay_higherorder-00001.vtu
-                             --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay params.input
+                             --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay_higherorder params.input
                              -Problem.Name test_ff_navierstokes_kovasznay_higherorder
-                             -Discretization.TvdApproach Hou
-                             -Discretization.DifferencingScheme vanleer")
+                             -Flux.TvdApproach Hou
+                             -Flux.DifferencingScheme Vanleer")
+
 dune_symlink_to_source_files(FILES "params.input")
diff --git a/test/freeflow/navierstokes/kovasznay/problem.hh b/test/freeflow/navierstokes/kovasznay/problem.hh
index bbbb7ab1ec1dab8c282c93977486df2f1cbda0d9..1c5374412fe7afaf30be5a6806a74c5d4296f55a 100644
--- a/test/freeflow/navierstokes/kovasznay/problem.hh
+++ b/test/freeflow/navierstokes/kovasznay/problem.hh
@@ -25,6 +25,10 @@
 #ifndef DUMUX_KOVASZNAY_TEST_PROBLEM_HH
 #define DUMUX_KOVASZNAY_TEST_PROBLEM_HH
 
+#ifndef UPWINDSCHEMEORDER
+#define UPWINDSCHEMEORDER 0
+#endif
+
 #include <dune/grid/yaspgrid.hh>
 
 #include <dumux/material/fluidsystems/1pliquid.hh>
@@ -68,6 +72,9 @@ template<class TypeTag>
 struct EnableGridFluxVariablesCache<TypeTag, TTag::KovasznayTest> { static constexpr bool value = true; };
 template<class TypeTag>
 struct EnableGridVolumeVariablesCache<TypeTag, TTag::KovasznayTest> { static constexpr bool value = true; };
+
+template<class TypeTag>
+struct UpwindSchemeOrder<TypeTag, TTag::KovasznayTest> { static constexpr int value = UPWINDSCHEMEORDER; };
 } // end namespace Properties
 
 /*!
@@ -99,12 +106,14 @@ class KovasznayTestProblem : public NavierStokesProblem<TypeTag>
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
     using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
+    static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
+
 public:
     KovasznayTestProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry)
     : ParentType(fvGridGeometry), eps_(1e-6)
     {
         printL2Error_ = getParam<bool>("Problem.PrintL2Error");
-
+        std::cout<< "upwindSchemeOrder is: " << FVGridGeometry::upwindStencilOrder() << "\n";
         kinematicViscosity_ = getParam<Scalar>("Component.LiquidKinematicViscosity", 1.0);
         Scalar reynoldsNumber = 1.0 / kinematicViscosity_;
         lambda_ = 0.5 * reynoldsNumber