diff --git a/dumux/discretization/staggered/freeflow/connectivitymap.hh b/dumux/discretization/staggered/freeflow/connectivitymap.hh
index 313874e84121d9bf907f3f820f4a566918899a0e..fc0b40e3558d4e9f7b8887dfebc0425dd2877d20 100644
--- a/dumux/discretization/staggered/freeflow/connectivitymap.hh
+++ b/dumux/discretization/staggered/freeflow/connectivitymap.hh
@@ -143,7 +143,7 @@ private:
                                          const FVElementGeometry& fvGeometry,
                                          const SubControlVolumeFace& scvf)
     {
-        stencil.push_back(scvf.dofIndex());
+        stencil.push_back(scvf.axisData().selfDof);
     }
 
     /*
@@ -163,8 +163,8 @@ private:
             auto& normalFace = fvGeometry.scvf(eIdx, data.localNormalFaceIdx);
             if(!normalFace.boundary())
             {
-                const auto outerParallelElementDofIdx = normalFace.outsideScvIdx();
-                stencil.push_back(outerParallelElementDofIdx);
+                const auto firstParallelElementDofIdx = normalFace.outsideScvIdx();
+                stencil.push_back(firstParallelElementDofIdx);
             }
         }
     }
@@ -177,21 +177,43 @@ private:
                                    const FVElementGeometry& fvGeometry,
                                    const SubControlVolumeFace& scvf)
     {
-        // the first entries are always the face dofIdx itself and the one of the opposing face
         if(stencil.empty())
         {
-            stencil.push_back(scvf.dofIndex());
-            stencil.push_back(scvf.dofIndexOpposingFace());
+            for(int i = 0; i < scvf.axisData().inAxisBackwardDofs.size(); 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 < scvf.axisData().inAxisForwardDofs.size(); i++)
+            {
+                if(scvf.hasForwardNeighbor(i))
+                {
+                    stencil.push_back(scvf.axisData().inAxisForwardDofs[i]);
+                }
+            }
         }
 
         for(const auto& data : scvf.pairData())
         {
+            // add normal dofs
             stencil.push_back(data.normalPair.first);
-            const auto outerParallelFaceDofIdx = data.outerParallelFaceDofIdx;
-            if(outerParallelFaceDofIdx >= 0)
-                stencil.push_back(outerParallelFaceDofIdx);
             if(!scvf.boundary())
                 stencil.push_back(data.normalPair.second);
+
+            // add parallel dofs
+            for (int i = 0; i < data.parallelDofs.size(); i++)
+            {
+                if(!(data.parallelDofs[i] < 0))
+                {
+                    stencil.push_back(data.parallelDofs[i]);
+                }
+            }
         }
     }
 
diff --git a/dumux/discretization/staggered/freeflow/facevariables.hh b/dumux/discretization/staggered/freeflow/facevariables.hh
index 6fd24dc689105446baddd6fb1b78c8d39db3f97c..a4edc1e42683e8f7b05d23f66d6746c89b38bed3 100644
--- a/dumux/discretization/staggered/freeflow/facevariables.hh
+++ b/dumux/discretization/staggered/freeflow/facevariables.hh
@@ -25,6 +25,7 @@
 #define DUMUX_DISCRETIZATION_STAGGERED_FREEFLOW_FACEVARIABLES_HH
 
 #include <array>
+#include <vector>
 
 namespace Dumux {
 
@@ -72,7 +73,30 @@ public:
         velocitySelf_ = faceSol[scvf.dofIndex()];
         velocityOpposite_ = 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_.clear();
+        for (int i = 0; i < scvf.axisData().inAxisForwardDofs.size(); i++)
+        {
+             if(!(scvf.axisData().inAxisForwardDofs[i] < 0))
+             {
+                 velocityForward_.push_back(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_.clear();
+        for (int i = 0; i < scvf.axisData().inAxisBackwardDofs.size(); i++)
+        {
+             if(!(scvf.axisData().inAxisBackwardDofs[i] < 0))
+             {
+                 velocityBackward_.push_back(faceSol[scvf.axisData().inAxisBackwardDofs[i]]);
+             }
+        }
+
         // handle all sub faces
+        std::vector<Scalar> a;
         for(int i = 0; i < scvf.pairData().size(); ++i)
         {
             const auto& subFaceData = scvf.pairData(i);
@@ -80,12 +104,19 @@ public:
             // treat the velocities normal to the face
             velocityNormalInside_[i] = faceSol[subFaceData.normalPair.first];
 
-            if(scvf.hasFrontalNeighbor(i))
+            if(scvf.hasOuterNormal(i))
                 velocityNormalOutside_[i] = faceSol[subFaceData.normalPair.second];
 
-            // treat the velocity parallel to the face
-            if(scvf.hasParallelNeighbor(i))
-                velocityParallel_[i] = faceSol[subFaceData.outerParallelFaceDofIdx];
+            a.clear();
+            // treat the velocities parallel to the self face
+            for(int j = 0; j < subFaceData.parallelDofs.size(); j++)
+            {
+                if(scvf.hasParallelNeighbor(i,j))
+                {
+                    a.push_back(faceSol[subFaceData.parallelDofs[j]]);
+                }
+            }
+            velocityParallel_[i] = a;
         }
     }
 
@@ -106,13 +137,34 @@ public:
     }
 
     /*!
-    * \brief Returns the velocity at the parallel face
+    * \brief Returns the velocity at a backward face
+    *
+    * \param backwardIdx The index describing how many faces backward this dof is from the opposite face
+    */
+    Scalar velocityBackward(const int backwardIdx) const
+    {
+        return velocityBackward_[backwardIdx];
+    }
+
+    /*!
+    * \brief Returns the velocity at a forward face
+    *
+    * \param forwardIdx The index describing how many faces forward this dof is of the self face
+    */
+    Scalar velocityForward(const int forwardIdx) const
+    {
+        return velocityForward_[forwardIdx];
+    }
+
+    /*!
+    * \brief Returns the velocity at a parallel face
     *
     * \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
+    Scalar velocityParallel(const int localSubFaceIdx, const int parallelDegreeIdx) const
     {
-        return velocityParallel_[localSubFaceIdx];
+        return velocityParallel_[localSubFaceIdx][parallelDegreeIdx];
     }
 
     /*!
@@ -136,12 +188,14 @@ public:
     }
 
 private:
-
     Scalar velocitySelf_;
     Scalar velocityOpposite_;
-    std::array<Scalar, numPairs> velocityParallel_;
-    std::array<Scalar, numPairs> velocityNormalInside_;
-    std::array<Scalar, numPairs> velocityNormalOutside_;
+    std::vector<Scalar> velocityForward_;
+    std::vector<Scalar> velocityBackward_;
+    std::array<std::vector<Scalar>, numPairs> velocityParallel_;
+    std::array<Scalar, numPairs>  velocityNormalInside_;
+    std::array<Scalar, numPairs>  velocityNormalOutside_;
+
 };
 
 } // end namespace Dumux
diff --git a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
index 7760f173a222308fc2689da2bac8229a316aa0e3..7d87aa882574026435e29e029e2ac2a5a994253d 100644
--- a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
+++ b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh
@@ -31,25 +31,41 @@
 #include <type_traits>
 #include <algorithm>
 
-namespace Dumux {
+namespace Dumux
+{
 
 /*!
  * \ingroup StaggeredDiscretization
- * \brief Data stored per sub face
+ * \brief Parallel Data stored per sub face
  */
 template<class Scalar, class GlobalPosition>
 struct PairData
 {
-    int outerParallelFaceDofIdx;
-    std::pair<int,int> normalPair;
+    std::vector<int> parallelDofs;
+    std::vector<Scalar> parallelDistances;
+    std::pair<signed int,signed int> normalPair;
     int localNormalFaceIdx;
-    int globalCommonEntIdx;
-    Scalar parallelDistance;
     Scalar normalDistance;
-    GlobalPosition virtualOuterParallelFaceDofPos;
+    GlobalPosition virtualFirstParallelFaceDofPos;
+};
+
+/*!
+ * \ingroup StaggeredDiscretization
+ * \brief In Axis Data stored per sub face
+ */
+template<class Scalar>
+struct AxisData
+{
+    int selfDof;
+    int oppositeDof;
+    std::vector<int> inAxisForwardDofs;
+    std::vector<int> inAxisBackwardDofs;
+    Scalar selfToOppositeDistance;
+    std::vector<Scalar> inAxisForwardDistances;
+    std::vector<Scalar> inAxisBackwardDistances;
 };
 
- /*!
+/*!
  * \ingroup StaggeredDiscretization
  * \brief Returns the dirction index of the facet (0 = x, 1 = y, 2 = z)
  */
@@ -65,11 +81,6 @@ inline static unsigned int directionIndex(Vector&& vector)
  * \ingroup StaggeredDiscretization
  * \brief Helper class constructing the dual grid finite volume geometries
  *        for the free flow staggered discretization method.
- *
- * A visualization of the variables that are in this document can be found in the following image:
- *
- * \image html staggeredgeometry.png
- *
  */
 template<class GridView>
 class FreeFlowStaggeredGeometryHelper
@@ -92,7 +103,9 @@ class FreeFlowStaggeredGeometryHelper
     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 int order_;
 
 public:
 
@@ -106,95 +119,247 @@ public:
         intersection_ = intersection;
         innerNormalFacePos_.clear();
         fillPairData_();
+        fillAxisData_();
     }
 
     /*!
-     * \brief Returns the global dofIdx of the intersection itself
+     * \brief Returns the local index of the face (i.e. the intersection)
      */
-    int dofIndex() const
+    int localFaceIndex() const
     {
-        //TODO: use proper intersection mapper!
-        const auto inIdx = intersection_.indexInInside();
-        return gridView_.indexSet().subIndex(intersection_.inside(), inIdx, codimIntersection);
+        return intersection_.indexInInside();
     }
 
     /*!
-     * \brief Returns the global dofIdx of the opposing intersection
+     * \brief Returns a copy of the axis data
      */
-    int dofIndexOpposingFace() const
+    auto axisData() const
     {
-        //TODO: use proper intersection mapper!
-        const auto inIdx = intersection_.indexInInside();
-        return gridView_.indexSet().subIndex(this->intersection_.inside(), localOppositeIdx_(inIdx), codimIntersection);
+        return axisData_;
     }
 
     /*!
-     * \brief Returns the local index of the face (i.e. the intersection)
+     * \brief Returns a copy of the pair data
      */
-    int localFaceIndex() const
+    auto pairData() const
     {
-        return intersection_.indexInInside();
+        return pairData_;
     }
 
-     /*!
-     * \brief Returns the distance between dofSelf and dofOpposite
+    /*!
+     * \brief Returns the dirction index of the primary facet (0 = x, 1 = y, 2 = z)
      */
-    Scalar selfToOppositeDistance() const
+    int directionIndex() const
     {
-        const auto inIdx = intersection_.indexInInside();
-        const auto self = getFacet_(inIdx, element_);
-        const auto opposite = getFacet_(localOppositeIdx_(inIdx), element_);
-        return (self.geometry().center() - opposite.geometry().center()).two_norm();
+        return Dumux::directionIndex(std::move(intersection_.centerUnitOuterNormal()));
     }
 
     /*!
-     * \brief Returns a copy of the pair data
+     * \brief Returns the dirction index of the facet passed as an argument (0 = x, 1 = y, 2 = z)
      */
-    auto pairData() const
+    int directionIndex(const Intersection& intersection) const
     {
-        return pairData_;
+        return Dumux::directionIndex(std::move(intersection.centerUnitOuterNormal()));
     }
 
-     /*!
-     * \brief Returns the dirction index of the facet (0 = x, 1 = y, 2 = z)
-     */
-    int directionIndex() const
+    //! \brief Returns the order needed by the scheme
+    static int order()
     {
-        return Dumux::directionIndex(std::move(intersection_.centerUnitOuterNormal()));
+        return order_;
+    }
+
+    //! \brief Set the order needed by the scheme
+    static void setOrder(const int order)
+    {
+        order_ = order;
     }
 
 private:
-     /*!
+    /*!
+     * \brief Fills all entries of the in axis data
+     */
+    void fillAxisData_()
+    {
+        unsigned int numForwardBackwardAxisDofs = order_ - 1;
+        const auto inIdx = intersection_.indexInInside();
+        const auto oppIdx = localOppositeIdx_(inIdx);
+
+        // Clear the containers before filling them
+        this->axisData_.inAxisForwardDofs.clear();
+        this->axisData_.inAxisBackwardDofs.clear();
+        this->axisData_.inAxisForwardDistances.clear();
+        this->axisData_.inAxisBackwardDistances.clear();
+
+        // initialize values that could remain unitialized if the intersection lies on a boundary
+        for(int i = 0; i < numForwardBackwardAxisDofs; i++)
+        {
+            this->axisData_.inAxisForwardDofs.push_back(-1);
+            this->axisData_.inAxisBackwardDofs.push_back(-1);
+            this->axisData_.inAxisForwardDistances.push_back(0);
+            this->axisData_.inAxisBackwardDistances.push_back(0);
+        }
+
+        // Set the self Dof
+        this->axisData_.selfDof = gridView_.indexSet().subIndex(this->intersection_.inside(), inIdx, codimIntersection);
+        const auto self = getFacet_(inIdx, element_);
+
+        // Set the opposite Dof
+        this->axisData_.oppositeDof = gridView_.indexSet().subIndex(this->intersection_.inside(), oppIdx, codimIntersection);
+        const auto opposite = getFacet_(oppIdx, element_);
+
+        // Set the Self to Opposite Distance
+        this->axisData_.selfToOppositeDistance = (self.geometry().center() - opposite.geometry().center()).two_norm();
+
+        // Set the Forward Dofs
+        std::stack<Element> inAxisForwardElementStack;
+        auto selfFace = getFacet_(inIdx, element_);
+        if(intersection_.neighbor())
+        {
+            if (order_ > 1)
+                inAxisForwardElementStack.push(intersection_.outside());
+            bool keepStackingForward = (inAxisForwardElementStack.size() < order_-1);
+            while(keepStackingForward)
+            {
+                auto e = inAxisForwardElementStack.top();
+                for(const auto& intersection : intersections(gridView_,e))
+                {
+                    if( (intersection.indexInInside() == inIdx ) )
+                    {
+                        if( intersection.neighbor())
+                        {
+                            inAxisForwardElementStack.push(intersection.outside());
+                            keepStackingForward = (inAxisForwardElementStack.size() < order_-1);
+                        }
+                        else
+                        {
+                            keepStackingForward = false;
+                        }
+                    }
+                }
+            }
+        }
+        std::vector<GlobalPosition> forwardFaceCoordinates(inAxisForwardElementStack.size(), selfFace.geometry().center());
+        while(!inAxisForwardElementStack.empty())
+        {
+            int forwardIdx = inAxisForwardElementStack.size()-1;
+            this->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++)
+        {
+            if(i == 0)
+            {
+                this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i] - self.geometry().center()).two_norm();
+            }
+            else
+            {
+                this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i]- forwardFaceCoordinates[i-1]).two_norm();
+            }
+        }
+
+        // Set the Backward Dofs
+        std::stack<Element> inAxisBackwardElementStack;
+        auto oppFace = getFacet_(oppIdx, element_);
+        for(const auto& intersection : intersections(gridView_, element_))
+        {
+            if(intersection.indexInInside() == oppIdx)
+            {
+                if(intersection.neighbor())
+                {
+                    if (order_ > 1)
+                        inAxisBackwardElementStack.push(intersection.outside());
+                    bool keepStackingBackward = (inAxisBackwardElementStack.size() < order_-1);
+                    while(keepStackingBackward)
+                    {
+                        auto e = inAxisBackwardElementStack.top();
+                        for(const auto& intersectionOut : intersections(gridView_,e))
+                        {
+
+                            if( (intersectionOut.indexInInside() == oppIdx ) )
+                            {
+                                if( intersectionOut.neighbor())
+                                {
+                                    inAxisBackwardElementStack.push(intersectionOut.outside());
+                                    keepStackingBackward = (inAxisBackwardElementStack.size() < order_-1);
+                                }
+                                else
+                                {
+                                    keepStackingBackward = false;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        std::vector<GlobalPosition> backwardFaceCoordinates(inAxisBackwardElementStack.size(), oppFace.geometry().center());
+        while(!inAxisBackwardElementStack.empty())
+        {
+            int backwardIdx = inAxisBackwardElementStack.size()-1;
+            this->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++)
+        {
+            if(i == 0)
+            {
+                this->axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - opposite.geometry().center()).two_norm();
+            }
+            else
+            {
+                this->axisData_.inAxisBackwardDistances[i] = (backwardFaceCoordinates[i] - backwardFaceCoordinates[i-1]).two_norm();
+            }
+        }
+    }
+
+    /*!
      * \brief Fills all entries of the pair data
      */
     void fillPairData_()
     {
-        const int localFacetIdx = intersection_.indexInInside();
-
         // initialize values that could remain unitialized if the intersection lies on a boundary
         for(auto& data : pairData_)
         {
-            data.outerParallelFaceDofIdx = -1;
-            data.normalPair.second = -1;
-            data.normalDistance = -1;
-            data.parallelDistance = -1;
-        }
+            int numParallelDofs = order_;
+
+            // parallel Dofs
+            data.parallelDofs.clear();
+            data.parallelDofs.resize(numParallelDofs, -1);
 
-        // set the inner parts of the normal pairs
-        const auto localIndices = getLocalIndices_(localFacetIdx);
-        setInnerIndices_(localIndices);
+            // parallel Distances
+            data.parallelDistances.clear();
+            data.parallelDistances.resize(numParallelDofs + 1, 0.0);
 
-        // get the positions of the faces normal to the intersection within the element
-        innerNormalFacePos_.reserve(numPairs);
+            // outer normals
+            data.normalPair.second = -1;
+        }
 
+        // set basic global positions
+        const auto& elementCenter = this->element_.geometry().center();
+        const auto& FacetCenter = intersection_.geometry().center();
 
-        for(int i = 0; i < numPairs; ++i)
+        // get the inner normal Dof Index
+        int numPairInnerNormalIdx = 0;
+        for(const auto& innerElementIntersection : intersections(gridView_, element_))
         {
-           const auto& innerNormalFacet = getFacet_(localIndices.normalLocalDofIdx[i], element_);
-           innerNormalFacePos_.emplace_back(innerNormalFacet.geometry().center());
+            if(facetIsNormal_(innerElementIntersection.indexInInside(), intersection_.indexInInside()))
+            {
+                const int innerElementIntersectionIdx = innerElementIntersection.indexInInside();
+                setNormalPairFirstInfo_(innerElementIntersectionIdx, element_, numPairInnerNormalIdx);
+                numPairInnerNormalIdx++;
+
+                innerNormalFacePos_.reserve(numPairs);
+                const auto& innerNormalFacet = getFacet_(innerElementIntersection.indexInInside(), element_);
+                innerNormalFacePos_.emplace_back(innerNormalFacet.geometry().center());
+            }
         }
 
-        // go into the direct neighbor element
+        // get the outer normal Dof Index
+        int numPairOuterNormalIdx = 0;
         if(intersection_.neighbor())
         {
             // the direct neighbor element and the respective intersection index
@@ -202,60 +367,92 @@ private:
 
             for(const auto& directNeighborElementIntersection : intersections(gridView_, directNeighborElement))
             {
-
-                const int directNeighborElemIsIdx = directNeighborElementIntersection.indexInInside();
                 // skip the directly neighboring face itself and its opposing one
-                if(facetIsNormal_(directNeighborElemIsIdx, intersection_.indexInOutside()))
+                if(facetIsNormal_(directNeighborElementIntersection.indexInInside(), intersection_.indexInOutside()))
                 {
-                    setPairInfo_(directNeighborElemIsIdx, directNeighborElement, false);
-
-                    // go into the adjacent neighbor element to get parallel dof info
-                    if(directNeighborElementIntersection.neighbor())
-                    {
-                        const auto& diagonalNeighborElement = directNeighborElementIntersection.outside();
-                        for(const auto& dIs : intersections(gridView_, diagonalNeighborElement))
-                        {
-                            if(facetIsNormal_(dIs.indexInInside(), directNeighborElementIntersection.indexInOutside()))
-                                setPairInfo_(dIs.indexInInside(), diagonalNeighborElement, true);
-                        }
-                    }
+                    const int directNeighborElemIsIdx = directNeighborElementIntersection.indexInInside();
+                    setNormalPairSecondInfo_(directNeighborElemIsIdx, directNeighborElement, numPairOuterNormalIdx);
+                    numPairOuterNormalIdx++;
                 }
             }
         }
         else // intersection is on boundary
         {
-            // find an intersection normal to the face
-            for(const auto& normalIntersection : intersections(gridView_, element_))
+            // fill the normal pair entries
+            for(int pairIdx = 0; pairIdx < numPairs; ++pairIdx)
             {
-                if(facetIsNormal_(normalIntersection.indexInInside(), intersection_.indexInInside()) && normalIntersection.neighbor())
-                {
-                    const auto& neighborElement = normalIntersection.outside();
+                assert(pairData_[pairIdx].normalPair.second == -1);
+                const auto distance = FacetCenter - elementCenter;
+                this->pairData_[pairIdx].normalDistance = std::move(distance.two_norm());
+                numPairOuterNormalIdx++;
+            }
+        }
+
 
-                    for(const auto& neighborElementIs : intersections(gridView_, neighborElement))
+        // get the parallel Dofs
+        const int parallelLocalIdx = intersection_.indexInInside();
+        int numPairParallelIdx = 0;
+        std::stack<Element> parallelElementStack;
+        for(const auto& intersection : intersections(gridView_, element_))
+        {
+            if( facetIsNormal_(intersection.indexInInside(), parallelLocalIdx) )
+            {
+                if( intersection.neighbor() )
+                {
+                    auto parallelAxisIdx = directionIndex(intersection);
+                    auto localNormalIntersectionIndex = intersection.indexInInside();
+                    parallelElementStack.push(element_);
+                    bool keepStacking =  (parallelElementStack.size() < order_+1);
+                    while(keepStacking)
                     {
-                        // iterate over facets sub-entities
-                        if(facetIsNormal_(normalIntersection.indexInInside(), neighborElementIs.indexInInside()))
-                            setPairInfo_(neighborElementIs.indexInInside(), neighborElement, true);
+                        auto e = parallelElementStack.top();
+                        for(const auto& normalIntersection : intersections(gridView_, e))
+                        {
+                            if( normalIntersection.indexInInside() == localNormalIntersectionIndex )
+                            {
+                                if( normalIntersection.neighbor() )
+                                {
+                                    parallelElementStack.push(normalIntersection.outside());
+                                    keepStacking = (parallelElementStack.size() < order_+1);
+                                }
+                                else
+                                {
+                                keepStacking = false;
+                                }
+                            }
+                        }
+                    }
+                    while(!parallelElementStack.empty())
+                    {
+                        if(parallelElementStack.size() > 1)
+                        {
+                            this->pairData_[numPairParallelIdx].parallelDofs[parallelElementStack.size()-2] = gridView_.indexSet().subIndex(parallelElementStack.top(), parallelLocalIdx, codimIntersection);
+                        }
+                        this->pairData_[numPairParallelIdx].parallelDistances[parallelElementStack.size()-1] = setParallelPairDistances_(parallelElementStack.top(), parallelAxisIdx);
+                        parallelElementStack.pop();
                     }
                 }
-            }
+                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();
 
-            // fill the normal pair entries
-            for(int pairIdx = 0; pairIdx < numPairs; ++pairIdx)
-            {
-                assert(pairData_[pairIdx].normalPair.second == -1);
-                const auto& elementCenter = this->element_.geometry().center();
-                const auto& boundaryFacetCenter = intersection_.geometry().center();
-                const auto distance = boundaryFacetCenter - elementCenter;
+                    const auto distance = boundaryFacetCenter - elementCenter;
+                    const auto virtualFirstParallelFaceDofPos = this->intersection_.geometry().center() + distance;
+
+                    this->pairData_[numPairParallelIdx].virtualFirstParallelFaceDofPos = std::move(virtualFirstParallelFaceDofPos);
 
-                pairData_[pairIdx].normalDistance = std::move(distance.two_norm());
+                    // 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);
+                }
+                numPairParallelIdx++;
             }
         }
-
-        treatVirtualOuterParallelFaceDofs_();
     }
 
-     /*!
+    /*!
      * \brief Returns the local opposing intersection index
      *
      * \param idx The local index of the intersection itself
@@ -265,7 +462,7 @@ private:
         return (idx % 2) ? (idx - 1) : (idx + 1);
     }
 
-     /*!
+    /*!
      * \brief Returns true if the intersection lies normal to another given intersection
      *
      * \param selfIdx The local index of the intersection itself
@@ -276,201 +473,85 @@ private:
         return !(selfIdx == otherIdx || localOppositeIdx_(selfIdx) == otherIdx);
     };
 
-     /*!
-     * \brief Returns the global index of the common entity
-     *
-     * \param localIdx The local index of the common entity
-     * \param element The element
-     */
-    int localToGlobalCommonEntityIdx_(const int localIdx, const Element& element) const
-    {
-        return this->gridView_.indexSet().subIndex(element, localIdx, codimCommonEntity);
-    };
-
     auto getFacet_(const int localFacetIdx, const Element& element) const
     {
         return element.template subEntity <1> (localFacetIdx);
     };
 
-    void setPairInfo_(const int isIdx, const Element& element, const bool isParallel)
+    //! Sets the information about the normal faces (within the element)
+    void setNormalPairFirstInfo_(const int isIdx, const Element& element, const int numPairsIdx)
     {
-        const auto referenceElement = ReferenceElements::general(element_.geometry().type());
+        // store the inner normal dofIdx
+        auto& dofIdx = pairData_[numPairsIdx].normalPair.first;
+        dofIdx = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
 
-        // iterate over facets sub-entities
-        for(int i = 0; i < numFacetSubEntities; ++i)
-        {
-            int localCommonEntIdx = referenceElement.subEntity(isIdx, 1, i, codimCommonEntity);
-            int globalCommonEntIdx = localToGlobalCommonEntityIdx_(localCommonEntIdx, element);
-
-            // fill the normal pair entries
-            for(int pairIdx = 0; pairIdx < numPairs; ++pairIdx)
-            {
-                    if(globalCommonEntIdx == pairData_[pairIdx].globalCommonEntIdx)
-                    {
-                            auto& dofIdx = isParallel ? pairData_[pairIdx].outerParallelFaceDofIdx : pairData_[pairIdx].normalPair.second;
-                            dofIdx = gridView_.indexSet().subIndex(element, isIdx, codimIntersection);
-                            if(isParallel)
-                            {
-                                    const auto& selfFacet = getFacet_(intersection_.indexInInside(), element_);
-                                    const auto& parallelFacet = getFacet_(isIdx, element);
-                                    pairData_[pairIdx].parallelDistance = (selfFacet.geometry().center() - parallelFacet.geometry().center()).two_norm();
-                            }
-                            else
-                            {
-                                    const auto& outerNormalFacet = getFacet_(isIdx, element);
-                                    const auto outerNormalFacetPos = outerNormalFacet.geometry().center();
-                                    pairData_[pairIdx].normalDistance = (innerNormalFacePos_[pairIdx] - outerNormalFacetPos).two_norm();
-                            }
-                    }
-            }
-        }
+        // store the local normal facet index
+        this->pairData_[numPairsIdx].localNormalFaceIdx = isIdx;
     }
 
-    template<class Indices>
-    void setInnerIndices_(const Indices& indices)
+    //! Sets the information about the normal faces (in the neighbor element)
+    void setNormalPairSecondInfo_(const int isIdx, const Element& element, const int numPairsIdx)
     {
-        for(int i = 0; i < numPairs; ++i)
-        {
-            this->pairData_[i].normalPair.first = this->gridView_.indexSet().subIndex(this->intersection_.inside(), indices.normalLocalDofIdx[i], codimIntersection);
-            this->pairData_[i].globalCommonEntIdx = this->gridView_.indexSet().subIndex(this->intersection_.inside(), indices.localCommonEntIdx[i], codimCommonEntity);
-            this->pairData_[i].localNormalFaceIdx = indices.normalLocalDofIdx[i];
-        }
+        // store the dofIdx
+        auto& dofIdx = pairData_[numPairsIdx].normalPair.second;
+        dofIdx = 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();
     }
 
-    /*!
-    * \brief Sets the position of "virtual" dofs on facets which are perpendicular to facets on the domain boundary.
-    *        This is required to account e.g. for wall friction.
-    */
-    void treatVirtualOuterParallelFaceDofs_()
+    //! Sets the information about the parallel distances
+    Scalar setParallelPairDistances_(const Element& element, const int parallelAxisIdx) const
     {
-        // get the local index of the facet we are dealing with in this class
-        const int localIntersectionIdx = this->intersection_.indexInInside();
-
-        // iterate over all intersections of the element, this facet is part of
-        for(const auto& is : intersections(this->gridView_, this->element_))
+        Scalar distance = 0;
+        std::vector<GlobalPosition> faces;
+        faces.reserve(numfacets);
+        for (const auto& intElement : intersections(gridView_, element))
         {
-            const int otherIsIdx = is.indexInInside();
-            // check if any of these intersection, which are normal to our facet, lie on the domain boundary
-            if(( !is.neighbor() ) &&  this->facetIsNormal_(localIntersectionIdx, otherIsIdx) )
-            {
-                const auto& elementCenter = this->element_.geometry().center();
-                const auto& boundaryFacetCenter = is.geometry().center();
-
-                const auto distance = boundaryFacetCenter - elementCenter;
-                const auto virtualOuterParallelFaceDofPos = this->intersection_.geometry().center() + distance;
-
-                auto foundCorrectIdx = [otherIsIdx](const auto& x) { return x.localNormalFaceIdx == otherIsIdx; };
-                const int index = std::find_if(this->pairData_.begin(), this->pairData_.end(), foundCorrectIdx) - this->pairData_.begin();
-
-                this->pairData_[index].virtualOuterParallelFaceDofPos = std::move(virtualOuterParallelFaceDofPos);
-                this->pairData_[index].parallelDistance = std::move(distance.two_norm());
-            }
+            faces.push_back(intElement.geometry().center());
         }
-    }
-
-    auto getLocalIndices_(const int localFacetIdx) const
-    {
-        struct Indices
-        {
-            std::array<int, numPairs> normalLocalDofIdx;
-            std::array<int, numPairs> localCommonEntIdx;
-        };
-
-        Indices indices;
-        if (dim == 1)
-            return indices;
-
-        switch(localFacetIdx)
+        switch(parallelAxisIdx)
         {
             case 0:
-                indices.normalLocalDofIdx[0] = 3;
-                indices.normalLocalDofIdx[1] = 2;
-                indices.localCommonEntIdx[0] = 2;
-                indices.localCommonEntIdx[1] = 0;
-                if(dim == 3)
-                {
-                    indices.normalLocalDofIdx[2] = 4;
-                    indices.normalLocalDofIdx[3] = 5;
-                    indices.localCommonEntIdx[2] = 4;
-                    indices.localCommonEntIdx[3] = 8;
-                }
-                break;
+            {
+                distance = (faces[1] - faces[0]).two_norm();
+            break;
+            }
             case 1:
-                indices.normalLocalDofIdx[0] = 2;
-                indices.normalLocalDofIdx[1] = 3;
-                indices.localCommonEntIdx[0] = 1;
-                indices.localCommonEntIdx[1] = 3;
-                if(dim == 3)
-                {
-                    indices.normalLocalDofIdx[2] = 4;
-                    indices.normalLocalDofIdx[3] = 5;
-                    indices.localCommonEntIdx[2] = 5;
-                    indices.localCommonEntIdx[3] = 9;
-                }
-                break;
+            {
+                distance = (faces[3] - faces[2]).two_norm();
+            break;
+            }
             case 2:
-                indices.normalLocalDofIdx[0] = 0;
-                indices.normalLocalDofIdx[1] = 1;
-                indices.localCommonEntIdx[0] = 0;
-                indices.localCommonEntIdx[1] = 1;
-                if(dim == 3)
-                {
-                    indices.normalLocalDofIdx[2] = 4;
-                    indices.normalLocalDofIdx[3] = 5;
-                    indices.localCommonEntIdx[2] = 6;
-                    indices.localCommonEntIdx[3] = 10;
-                }
-                break;
-            case 3:
-                indices.normalLocalDofIdx[0] = 1;
-                indices.normalLocalDofIdx[1] = 0;
-                indices.localCommonEntIdx[0] = 3;
-                indices.localCommonEntIdx[1] = 2;
-                if(dim == 3)
-                {
-                    indices.normalLocalDofIdx[2] = 4;
-                    indices.normalLocalDofIdx[3] = 5;
-                    indices.localCommonEntIdx[2] = 7;
-                    indices.localCommonEntIdx[3] = 11;
-                }
-                break;
-            case 4:
-                assert(dim == 3);
-                indices.normalLocalDofIdx[0] = 0;
-                indices.normalLocalDofIdx[1] = 1;
-                indices.normalLocalDofIdx[2] = 3;
-                indices.normalLocalDofIdx[3] = 2;
-                indices.localCommonEntIdx[0] = 4;
-                indices.localCommonEntIdx[1] = 5;
-                indices.localCommonEntIdx[2] = 7;
-                indices.localCommonEntIdx[3] = 6;
-                break;
-            case 5:
+            {
                 assert(dim == 3);
-                indices.normalLocalDofIdx[0] = 0;
-                indices.normalLocalDofIdx[1] = 1;
-                indices.normalLocalDofIdx[2] = 2;
-                indices.normalLocalDofIdx[3] = 3;
-                indices.localCommonEntIdx[0] = 8;
-                indices.localCommonEntIdx[1] = 9;
-                indices.localCommonEntIdx[2] = 10;
-                indices.localCommonEntIdx[3] = 11;
-                break;
+                distance = (faces[5] - faces[4]).two_norm();
+            break;
+            }
             default:
+            {
                 DUNE_THROW(Dune::InvalidStateException, "Something went terribly wrong");
+            }
         }
-        return indices;
+        return distance;
     }
 
-    // TODO: check whether to use references here or not
     Intersection intersection_; //!< The intersection of interest
     const Element element_; //!< The respective element
     const typename Element::Geometry elementGeometry_; //!< Reference to the element geometry
-    const GridView gridView_;
-    std::array<PairData<Scalar, GlobalPosition>, numPairs> pairData_; //!< collection of pair information
+    const GridView gridView_; //!< The grid view
+    AxisData<Scalar> axisData_; //!< Data related to forward and backward faces
+    std::array<PairData<Scalar, GlobalPosition>, numPairs> pairData_; //!< Collection of pair information related to normal and parallel faces
     std::vector<GlobalPosition> innerNormalFacePos_;
 };
 
+template<class GridView>
+int FreeFlowStaggeredGeometryHelper<GridView>::order_ = 1;
+
 } // end namespace Dumux
 
 #endif
diff --git a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
index 924c9b897e6e495dbc25a8a13d4de9a88de0e69c..d9f06de4a8887ef2fe77313ff7269ddcbc680734 100644
--- a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
+++ b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh
@@ -73,8 +73,7 @@ public:
                                           const typename Intersection::Geometry& isGeometry,
                                           GridIndexType scvfIndex,
                                           const std::vector<GridIndexType>& scvIndices,
-                                          const GeometryHelper& geometryHelper
-                           )
+                                          const GeometryHelper& geometryHelper)
     : ParentType(),
       geomType_(isGeometry.type()),
       area_(isGeometry.volume()),
@@ -83,9 +82,8 @@ public:
       scvfIndex_(scvfIndex),
       scvIndices_(scvIndices),
       boundary_(is.boundary()),
-      dofIdx_(geometryHelper.dofIndex()),
-      dofIdxOpposingFace_(geometryHelper.dofIndexOpposingFace()),
-      selfToOppositeDistance_(geometryHelper.selfToOppositeDistance()),
+
+      axisData_(geometryHelper.axisData()),
       pairData_(geometryHelper.pairData()),
       localFaceIdx_(geometryHelper.localFaceIndex()),
       dirIdx_(geometryHelper.directionIndex()),
@@ -181,18 +179,6 @@ public:
         return Geometry(geomType_, corners_);
     }
 
-    //! The global index of the dof living on this face
-    GridIndexType dofIndex() const
-    {
-        return dofIdx_;
-    }
-
-    //! The global index of the dof living on the opposing face
-    GridIndexType dofIndexOpposingFace() const
-    {
-        return dofIdxOpposingFace_;
-    }
-
     //! The local index of this sub control volume face
     LocalIndexType localFaceIdx() const
     {
@@ -205,12 +191,6 @@ public:
         return dirIdx_;
     }
 
-    //! The distance between the position of the dof itself and the one of the oppsing dof
-    Scalar selfToOppositeDistance() const
-    {
-        return selfToOppositeDistance_;
-    }
-
     //! Returns whether the unitNormal of the face points in positive coordinate direction
     bool normalInPosCoordDir() const
     {
@@ -235,21 +215,102 @@ public:
         return pairData_;
     }
 
+    //! Return an array of all pair data
+    const auto& axisData() const
+    {
+        return axisData_;
+    }
+
+    //! Returns @c true if the face is a ghost face
     bool isGhostFace() const
     {
         return isGhostFace_;
     }
 
-    bool hasParallelNeighbor(const int localFaceIdx) const
+   /*!
+    * \brief Check if the face has a parallel neighbor
+    *
+    * \param localSubFaceIdx The local index of the subface
+    * \param parallelDegreeIdx The index describing how many faces away from the self face
+    */
+    bool hasParallelNeighbor(const int localSubFaceIdx, const int parallelDegreeIdx) const
+    {
+        return !(pairData(localSubFaceIdx).parallelDofs[parallelDegreeIdx] < 0);
+    }
+
+   /*!
+    * \brief Check if the face has an outer normal neighbor
+    *
+    * \param localSubFaceIdx The local index of the subface
+    */
+    bool hasOuterNormal(const int localSubFaceIdx) const
+    {
+        return !(pairData_[localSubFaceIdx].normalPair.second < 0);
+    }
+
+   /*!
+    * \brief Check if the face has a backward neighbor
+    *
+    * \param backwardIdx The index describing how many faces backward this dof is from the opposite face
+    */
+    bool hasBackwardNeighbor(const int backwardIdx) const
     {
-        return pairData_[localFaceIdx].outerParallelFaceDofIdx >= 0;
+        return !(axisData().inAxisBackwardDofs[backwardIdx] < 0);
     }
 
-    bool hasFrontalNeighbor(const int localFaceIdx) const
+   /*!
+    * \brief Check if the face has a forward neighbor
+    *
+    * \param forwardIdx The index describing how many faces forward this dof is of the self face
+    */
+    bool hasForwardNeighbor(const int forwardIdx) const
     {
-        return pairData_[localFaceIdx].normalPair.second >= 0;
+        return !(axisData().inAxisForwardDofs[forwardIdx] < 0);
     }
 
+    //! Returns the dof of the face
+    GridIndexType dofIndex() const
+    {
+        return axisData().selfDof;
+    }
+
+    //! Returns the dof of the opposing face
+    GridIndexType dofIndexOpposingFace() const
+    {
+        return axisData().oppositeDof;
+    }
+
+    //! Returns the dof the first forward face
+    GridIndexType dofIndexForwardFace() const
+    {
+        return axisData().inAxisForwardDofs[0];
+    }
+
+    //! Returns the dof of the first backward face
+    GridIndexType dofIndexBackwardFace() const
+    {
+        return axisData().inAxisBackwardDofs[0];
+    }
+
+    //! Returns the distance between the face and the opposite one
+    Scalar selfToOppositeDistance() const
+    {
+        return axisData().selfToOppositeDistance;
+    }
+
+    /*!
+    * \brief Returns the distance between the parallel dofs
+    *
+    * \param localSubFaceIdx The local index of the subface
+    * \param parallelDegreeIdx The index describing how many faces away from the self
+    */
+    Scalar cellCenteredParallelDistance(const int localSubFaceIdx, const int parallelDegreeIdx) const
+    {
+        return (pairData(localSubFaceIdx).parallelDistances[parallelDegreeIdx] +
+                pairData(localSubFaceIdx).parallelDistances[parallelDegreeIdx+1]) * 0.5;
+    }
+
+
 private:
     Dune::GeometryType geomType_;
     std::vector<GlobalPosition> corners_;
@@ -261,10 +322,11 @@ private:
     bool boundary_;
 
     int dofIdx_;
-    int dofIdxOpposingFace_;
     Scalar selfToOppositeDistance_;
+    AxisData<Scalar> axisData_;
     std::array<PairData<Scalar, GlobalPosition>, numPairs> pairData_;
-    LocalIndexType localFaceIdx_;
+
+    int localFaceIdx_;
     unsigned int dirIdx_;
     int outerNormalSign_;
     bool isGhostFace_;
@@ -272,4 +334,4 @@ private:
 
 } // end namespace Dumux
 
-#endif
+#endif // DUMUX_DISCRETIZATION_STAGGERED_FREE_FLOW_SUBCONTROLVOLUMEFACE_HH
diff --git a/dumux/discretization/staggered/fvgridgeometry.hh b/dumux/discretization/staggered/fvgridgeometry.hh
index bdf0c3fd28814c6e195c80555903d28eb61027b4..51b9c01e4474b9ee380eab996ead9ebf9503c3ca 100644
--- a/dumux/discretization/staggered/fvgridgeometry.hh
+++ b/dumux/discretization/staggered/fvgridgeometry.hh
@@ -227,6 +227,8 @@ 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("Discretization", "TvdApproach"))
+            GeometryHelper::setOrder(2);
     }
 
     //! The total number of sub control volumes
diff --git a/dumux/freeflow/CMakeLists.txt b/dumux/freeflow/CMakeLists.txt
index 318f762168f1509c66e1f3c7e1b5f8cac466943e..5f16de206e056526ae1a904c3c0261c80257a51b 100644
--- a/dumux/freeflow/CMakeLists.txt
+++ b/dumux/freeflow/CMakeLists.txt
@@ -7,4 +7,5 @@ install(FILES
 properties.hh
 turbulenceproperties.hh
 volumevariables.hh
+higherorderapproximation.hh
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/freeflow)
diff --git a/dumux/freeflow/higherorderapproximation.hh b/dumux/freeflow/higherorderapproximation.hh
new file mode 100644
index 0000000000000000000000000000000000000000..0d6da3d71592bb247470bd343902b335da873cca
--- /dev/null
+++ b/dumux/freeflow/higherorderapproximation.hh
@@ -0,0 +1,353 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 2 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/** \file
+  * \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
+
+#include <cmath>
+#include <functional>
+#include <iostream>
+#include <string>
+
+#include <dumux/common/exceptions.hh>
+
+namespace Dumux {
+
+//! \brief Available Tvd approaches
+enum class TvdApproach
+{
+    none    = 0,
+    uniform = 1,
+    li      = 2,
+    hou     = 3
+};
+
+//! \biraf Available differencing schemes
+enum class DifferencingScheme
+{
+    none      = 0,
+    vanleer   = 1,
+    vanalbada = 2,
+    minmod    = 3,
+    superbee  = 4,
+    umist     = 5,
+    mclimiter = 6,
+    wahyd     = 7
+};
+
+/**
+  * \brief This file contains different higher order methods for approximating the velocity.
+  */
+template<class Scalar>
+class HigherOrderApproximation
+{
+public:
+    HigherOrderApproximation(const std::string& paramGroup = "")
+    {
+        if (hasParamInGroup(paramGroup, "Discretization.TvdApproach"))
+        {
+            // Read the runtime parameters
+            tvdApproach_ = static_cast<TvdApproach>(getParamFromGroup<int>(paramGroup, "Discretization.TvdApproach"));
+            if(tvdApproach_ == TvdApproach::none)
+            {
+                DUNE_THROW(ParameterException, "\nTvd approach 0 is not implemented.\n" <<
+                        static_cast<int>(TvdApproach::uniform) << ": Uniform Tvd\n" <<
+                        static_cast<int>(TvdApproach::li) << ": Li's approach\n" <<
+                        static_cast<int>(TvdApproach::hou) << ": Hou's approach");
+            }
+            differencingScheme_ = static_cast<DifferencingScheme>(getParamFromGroup<int>(paramGroup, "Discretization.DifferencingScheme"));
+
+            // Assign the limiter_ depending on the differencing scheme
+            switch (differencingScheme_)
+            {
+                case DifferencingScheme::vanleer :
+                {
+                    limiter_ = this->vanleer;
+                    break;
+                }
+                case DifferencingScheme::vanalbada :
+                {
+                    if (tvdApproach_ == TvdApproach::hou)
+                        DUNE_THROW(ParameterException, "\nDifferencing scheme " << static_cast<int>(DifferencingScheme::vanalbada) <<
+                            " (Van Albada) is not implemented for the Tvd approach " << static_cast<int>(TvdApproach::hou) <<" (Hou).");
+                    else
+                        limiter_ = this->vanalbada;
+                    break;
+                }
+                case DifferencingScheme::minmod :
+                {
+                    limiter_ = this->minmod;
+                    break;
+                }
+                case DifferencingScheme::superbee :
+                {
+                    limiter_ = this->superbee;
+                    break;
+                }
+                case DifferencingScheme::umist :
+                {
+                    limiter_ = this->umist;
+                    break;
+                }
+                case DifferencingScheme::mclimiter :
+                {
+                    limiter_ = this->mclimiter;
+                    break;
+                }
+                case DifferencingScheme::wahyd :
+                {
+                    limiter_ = this->wahyd;
+                    break;
+                }
+                default:
+                {
+                    DUNE_THROW(ParameterException, "\nDifferencing scheme " << static_cast<int>(differencingScheme_) <<
+                        " is not implemented.\n");
+                    break;
+                }
+            }
+        }
+        else
+        {
+            // If the runtime parameters are not specified we will use upwind
+            tvdApproach_ = TvdApproach::none;
+            differencingScheme_ = DifferencingScheme::none;
+        }
+    }
+
+    /**
+      * \brief Upwind Method
+      */
+    Scalar upwind(const Scalar downstreamVelocity,
+                  const Scalar upstreamVelocity,
+                  const Scalar density) const
+    {
+        return upstreamVelocity * 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 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;
+    }
+
+    /**
+      * \brief Tvd Scheme: Total Variation Diminuishing
+      */
+    Scalar tvd(const Scalar downstreamVelocity,
+               const Scalar upstreamVelocity,
+               const Scalar upUpstreamVelocity,
+               const Scalar density) const
+    {
+        const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity);
+
+        // If the velocity field is uniform (like at the first newton step) we get a NaN
+        if(ratio > 0.0 && std::isfinite(ratio))
+        {
+            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity);
+            return density * (upstreamVelocity + secondOrderTerm);
+        }
+        else
+            return density * upstreamVelocity;
+    }
+
+    /**
+      * \brief Tvd Scheme: Total Variation Diminuishing
+      *
+      * This functions manages the non uniformities of the grid according to [Li, Liao 2007].
+      * It tries to reconstruct the value for the velocity at the upstream-upstream point
+      * if the grid was uniform.
+      */
+    Scalar tvd(const Scalar downstreamVelocity,
+               const Scalar upstreamVelocity,
+               const Scalar upUpstreamVelocity,
+               const Scalar upstreamToDownstreamDistance,
+               const Scalar upUpstreamToUpstreamDistance,
+               const bool selfIsUpstream,
+               const Scalar density) const
+    {
+        // I need the information of selfIsUpstream to get the correct sign because upUpstreamToUpstreamDistance is always positive
+        const Scalar upUpstreamGradient = (upstreamVelocity - upUpstreamVelocity) / 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);
+
+        // If the velocity field is uniform (like at the first newton step) we get a NaN
+        if(ratio > 0.0 && std::isfinite(ratio))
+        {
+            const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity);
+            return density * (upstreamVelocity + secondOrderTerm);
+        }
+        else
+            return density * upstreamVelocity;
+    }
+
+    /**
+     * \brief Tvd Scheme: Total Variation Diminuishing
+     *
+     * This functions 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 tvd(const Scalar downstreamVelocity,
+               const Scalar upstreamVelocity,
+               const Scalar upUpstreamVelocity,
+               const Scalar upstreamToDownstreamDistance,
+               const Scalar upUpstreamToUpstreamDistance,
+               const Scalar downstreamStaggeredCellSize,
+               const Scalar density) const
+    {
+        const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity)
+                           * upstreamToDownstreamDistance / upUpstreamToUpstreamDistance;
+
+        // If the velocity field is uniform (like at the first newton step) we get a NaN
+        if(ratio > 0.0 && std::isfinite(ratio))
+        {
+            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);
+        }
+        else
+            return density * upstreamVelocity;
+    }
+
+    /**
+      * \brief Van Leer flux limiter function [Van Leer 1974]
+      *
+      * With R != 2 is the modified Van Leer flux limiter function [Hou, Simons, Hinkelmann 2007]
+      */
+    static Scalar vanleer(const Scalar r, const Scalar R)
+    {
+        return R * r / (R - 1.0 + r);
+    }
+
+    /**
+      * \brief Van Albada flux limiter function [Van Albada et al. 1982]
+      */
+    static Scalar vanalbada(const Scalar r, const Scalar R)
+    {
+        return r * (r + 1.0) / (1.0 + r * r);
+    }
+
+    /**
+      * \brief MinMod flux limiter function [Roe 1985]
+      */
+    static Scalar minmod(const Scalar r, const Scalar R)
+    {
+        return std::min(r, 1.0);
+    }
+
+    /**
+      * \brief SUPERBEE flux limiter function [Roe 1985]
+      *
+      * With R != 2 is the modified SUPERBEE flux limiter function [Hou, Simons, Hinkelmann 2007]
+      */
+    static Scalar superbee(const Scalar r, const Scalar R)
+    {
+        return std::max(std::min(R * r, 1.0), std::min(r, R));
+    }
+
+    /**
+      * \brief UMIST flux limiter function [Lien and Leschziner 1993]
+      */
+    static Scalar umist(const Scalar r, const Scalar R)
+    {
+        return std::min({R * r, (r * (5.0 - R) + R - 1.0) / 4.0, (r * (R - 1.0) + 5.0 - R) / 4.0, R});
+    }
+
+    /*
+     * \brief Monotonized-Central limiter [Van Leer 1977]
+     */
+    static Scalar mclimiter(const Scalar r, const Scalar R)
+    {
+        return std::min({R * r, (r + 1.0) / 2.0, R});
+    }
+
+    /**
+      * \brief WAHYD Scheme [Hou, Simons, Hinkelmann 2007];
+      */
+    static Scalar wahyd(const Scalar r, const Scalar R)
+    {
+        return r > 1 ? std::min((r + R * r * r) / (R + r * r), R)
+                     : vanleer(r, R);
+    }
+
+    //! Returns the Tvd approach
+    const TvdApproach& tvdApproach() const
+    {
+        return tvdApproach_;
+    }
+
+    //! Returns the differencing scheme
+    const DifferencingScheme& differencingScheme() const
+    {
+        return differencingScheme_;
+    }
+
+private:
+    TvdApproach tvdApproach_;
+    DifferencingScheme differencingScheme_;
+
+    std::function<Scalar(const Scalar, const Scalar)> limiter_;
+};
+
+} // end namespace Dumux
+
+#endif // DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH
diff --git a/dumux/freeflow/navierstokes/problem.hh b/dumux/freeflow/navierstokes/problem.hh
index 58922e3fc8b2cece2feeb7f85a5858cfb7c0832e..d988a03955a7e0471131bbcd694974c0bf31d05a 100644
--- a/dumux/freeflow/navierstokes/problem.hh
+++ b/dumux/freeflow/navierstokes/problem.hh
@@ -28,6 +28,9 @@
 #include <dumux/common/properties.hh>
 #include <dumux/common/staggeredfvproblem.hh>
 #include <dumux/discretization/method.hh>
+
+#include <dumux/freeflow/higherorderapproximation.hh>
+
 #include "model.hh"
 
 namespace Dumux {
@@ -92,8 +95,9 @@ public:
      * \param paramGroup The parameter group in which to look for runtime parameters first (default is "")
      */
     NavierStokesProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry, const std::string& paramGroup = "")
-    : ParentType(fvGridGeometry, paramGroup)
-    , gravity_(0.0)
+    : ParentType(fvGridGeometry, paramGroup),
+      gravity_(0.0),
+      higherOrderApproximation_(paramGroup)
     {
         if (getParamFromGroup<bool>(paramGroup, "Problem.EnableGravity"))
             gravity_[dim-1]  = -9.81;
@@ -215,7 +219,13 @@ public:
         using std::sqrt;
         const Scalar K = asImp_().permeability(element, normalFace);
         const Scalar alpha = asImp_().alphaBJ(normalFace);
-        return velocitySelf / (alpha / sqrt(K) * scvf.pairData(localSubFaceIdx).parallelDistance + 1.0);
+        return velocitySelf / (alpha / sqrt(K) * scvf.cellCenteredParallelDistance(localSubFaceIdx,0) + 1.0);
+    }
+
+    //! Return the HigherOrderApproximation
+    const HigherOrderApproximation<Scalar>& higherOrderApproximation() const
+    {
+        return higherOrderApproximation_;
     }
 
 private:
@@ -229,6 +239,7 @@ private:
     { return *static_cast<const Implementation *>(this); }
 
     GravityVector gravity_;
+    HigherOrderApproximation<Scalar> higherOrderApproximation_;
     bool enableInertiaTerms_;
 };
 
diff --git a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
index 5bf038741a1b122e14e45804fce996d0d40c228c..46ca4213af4d7a131b1d58cf93728c537641a75a 100644
--- a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
+++ b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
@@ -24,10 +24,15 @@
 #ifndef DUMUX_NAVIERSTOKES_STAGGERED_FLUXVARIABLES_HH
 #define DUMUX_NAVIERSTOKES_STAGGERED_FLUXVARIABLES_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/freeflow/higherorderapproximation.hh>
+
 #include <dumux/flux/fluxvariablesbase.hh>
 #include <dumux/discretization/method.hh>
 
@@ -207,17 +212,61 @@ public:
             // 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);
 
-            // Lamba function to evaluate the transported momentum, regarding an user-specified upwind weight.
-            auto computeMomentum = [&insideVolVars, &problem](const Scalar upstreamVelocity, const Scalar downstreamVelocity)
-            {
-                static const Scalar upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
-                return (upwindWeight * upstreamVelocity + (1.0 - upwindWeight) * downstreamVelocity) * insideVolVars.density();
-            };
+            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};
 
-            // Get the momentum that is advectively transported and account for the flow direction.
-            const Scalar momentum = selfIsUpstream ? computeMomentum(velocitySelf, velocityOpposite)
-                                                   : computeMomentum(velocityOpposite, velocitySelf);
+            const auto& highOrder = problem.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]))
+                {
+                    switch (highOrder.tvdApproach())
+                    {
+                        case TvdApproach::uniform :
+                        {
+                            momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], insideVolVars.density());
+                            break;
+                        }
+                        case TvdApproach::li :
+                        {
+                            momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], selfIsUpstream, insideVolVars.density());
+                            break;
+                        }
+                        case TvdApproach::hou :
+                        {
+                            momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], insideVolVars.density());
+                            break;
+                        }
+                        default:
+                        {
+                            DUNE_THROW(ParameterException, "\nTvd approach " << static_cast<int>(highOrder.tvdApproach()) << " is not implemented.\n" <<
+                                        static_cast<int>(TvdApproach::uniform) << ": Uniform Tvd\n" <<
+                                        static_cast<int>(TvdApproach::li) << ": Li's approach\n" <<
+                                        static_cast<int>(TvdApproach::hou) << ": Hou's approach");
+                            break;
+                        }
+                    }
+                }
+                else
+                    momentum = highOrder.upwind(velocities[0], velocities[1], insideVolVars.density());
+            }
+            else
+                momentum = selfIsUpstream ? highOrder.upwind(velocityOpposite, velocitySelf, insideVolVars.density())
+                                          : highOrder.upwind(velocitySelf, velocityOpposite, insideVolVars.density());
 
             // Account for the orientation of the staggered face's normal outer normal vector
             // (pointing in opposite direction of the scvf's one).
@@ -290,18 +339,18 @@ public:
         {
             const auto eIdx = scvf.insideScvIdx();
             // Get the face normal to the face the dof lives on. The staggered sub face conincides with half of this normal face.
-            const auto& normalFace = fvGeometry.scvf(eIdx, scvf.pairData()[localSubFaceIdx].localNormalFaceIdx);
+            const auto& normalFace = fvGeometry.scvf(eIdx, scvf.pairData(localSubFaceIdx).localNormalFaceIdx);
 
             bool lateralFaceHasDirichletPressure = false; // check for Dirichlet boundary condition for the pressure
             bool lateralFaceHasBJS = false; // check for Beavers-Joseph-Saffman boundary condition
 
             // Check if there is face/element parallel to our face of interest where the dof lives on. If there is no parallel neighbor,
             // we are on a boundary where we have to check for boundary conditions.
-            if(!scvf.hasParallelNeighbor(localSubFaceIdx))
+            if(!scvf.hasParallelNeighbor(localSubFaceIdx,0))
             {
                 // Construct a temporary scvf which corresponds to the staggered sub face, featuring the location
                 // the sub faces's center.
-                auto localSubFaceCenter = scvf.pairData(localSubFaceIdx).virtualOuterParallelFaceDofPos - normalFace.center();
+                auto localSubFaceCenter = scvf.pairData(localSubFaceIdx).virtualFirstParallelFaceDofPos - normalFace.center();
                 localSubFaceCenter *= 0.5;
                 localSubFaceCenter += normalFace.center();
                 const auto localSubFace = makeGhostFace_(normalFace, localSubFaceCenter);
@@ -384,54 +433,72 @@ private:
         const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx);
 
         // Check whether the own or the neighboring element is upstream.
-        const bool ownElementIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) );
-
-        // Get the velocities at the current (own) scvf and at the parallel one at the neighboring scvf.
-        const Scalar velocitySelf = faceVars.velocitySelf();
+        const bool selfIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) );
 
-        // Lambda to conveniently get 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.
-        auto getParallelVelocityFromBoundary = [&]()
-        {
-            // 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;
-
-            // Check if we have a Beavers-Joseph-Saffman condition.
-            // If yes, the parallel velocity at the boundary is calculated accordingly.
-            if (lateralFaceHasBJS)
-                return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, velocitySelf);
+        // Get the volume variables of the own and the neighboring element
+        const auto& insideVolVars = elemVolVars[normalFace.insideScvIdx()];
+        const auto& outsideVolVars = elemVolVars[normalFace.outsideScvIdx()];
 
-            const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx);
+        Scalar momentum = 0.0;
 
-            return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())];
-        };
+        // 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};
 
-        const Scalar velocityParallel = scvf.hasParallelNeighbor(localSubFaceIdx)
-                                        ? faceVars.velocityParallel(localSubFaceIdx)
-                                        : getParallelVelocityFromBoundary();
+        // 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};
 
-        // Get the volume variables of the own and the neighboring element
-        const auto& insideVolVars = elemVolVars[normalFace.insideScvIdx()];
-        const auto& outsideVolVars = elemVolVars[normalFace.outsideScvIdx()];
+        const auto& highOrder = problem.higherOrderApproximation();
 
-        // Lamba function to evaluate the transported momentum, regarding an user-specified upwind weight.
-        auto computeMomentum = [&problem](const VolumeVariables& upstreamVolVars,
-                                  const VolumeVariables& downstreamVolVars,
-                                  const Scalar upstreamVelocity,
-                                  const Scalar downstreamVelocity)
+        // 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)
         {
-            static const Scalar upWindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
-            const Scalar density = upWindWeight * upstreamVolVars.density() + (1.0 - upWindWeight) * downstreamVolVars.density();
-            const Scalar transportedVelocity =  upWindWeight * upstreamVelocity + (1.0 - upWindWeight) * downstreamVelocity;
-            return transportedVelocity * density;
-        };
+            if (canLateralSecondOrder_(scvf, selfIsUpstream, localSubFaceIdx, velocities, distances, problem, element, faceVars, lateralFaceHasDirichletPressure, lateralFaceHasBJS))
+            {
+                switch (highOrder.tvdApproach())
+                {
+                    case TvdApproach::uniform :
+                    {
+                        momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], selfIsUpstream ? insideVolVars.density() : outsideVolVars.density());
+                        break;
+                    }
+                    case TvdApproach::li :
+                    {
+                        momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], selfIsUpstream, selfIsUpstream ? insideVolVars.density() : outsideVolVars.density());
+                        break;
+                    }
+                    case TvdApproach::hou :
+                    {
+                        momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], selfIsUpstream ? insideVolVars.density() : outsideVolVars.density());
+                        break;
+                    }
+                    default:
+                    {
+                        DUNE_THROW(ParameterException, "\nTvd approach " << static_cast<int>(highOrder.tvdApproach()) << " is not implemented.\n" <<
+                                    static_cast<int>(TvdApproach::uniform) << ": Uniform Tvd\n" <<
+                                    static_cast<int>(TvdApproach::li) << ": Li's approach\n" <<
+                                    static_cast<int>(TvdApproach::hou) << ": Hou's approach");
+                        break;
+                    }
+                }
+            }
+            else
+                momentum = highOrder.upwind(velocities[0], velocities[1], selfIsUpstream ? insideVolVars.density() : outsideVolVars.density());
+        }
+        else
+        {
+            const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx, 0)
+                                               ? faceVars.velocityParallel(localSubFaceIdx, 0)
+                                               : getParallelVelocityFromBoundary_(problem, scvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS);
 
-        // Get the momentum that is advectively transported and account for the flow direction.
-        const Scalar momentum = ownElementIsUpstream ?
-                                computeMomentum(insideVolVars, outsideVolVars, velocitySelf, velocityParallel)
-                              : computeMomentum(outsideVolVars, insideVolVars, velocityParallel, velocitySelf);
+            momentum = selfIsUpstream ? highOrder.upwind(velocityFirstParallel, faceVars.velocitySelf(), insideVolVars.density())
+                                      : highOrder.upwind(faceVars.velocitySelf(), velocityFirstParallel, outsideVolVars.density());
+        }
 
         // Account for the orientation of the staggered normal face's outer normal vector
         // and its area (0.5 of the coinciding scfv).
@@ -519,24 +586,14 @@ private:
             // and at the parallel one at the neighboring scvf.
             const Scalar innerParallelVelocity = faceVars.velocitySelf();
 
-            // Lambda to conveniently get 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.
-            auto getParallelVelocityFromBoundary = [&]()
-            {
-                const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx);
-                if (lateralFaceHasBJS)
-                    return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, innerParallelVelocity);
-                return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())];
-            };
-
-            const Scalar outerParallelVelocity = scvf.hasParallelNeighbor(localSubFaceIdx)
-                                                 ? faceVars.velocityParallel(localSubFaceIdx)
-                                                 : getParallelVelocityFromBoundary();
+            const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx,0)
+                                               ? faceVars.velocityParallel(localSubFaceIdx,0)
+                                               : 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.
-            const Scalar parallelGradient = (outerParallelVelocity - innerParallelVelocity)
-                                            / scvf.pairData(localSubFaceIdx).parallelDistance;
+            const Scalar parallelGradient = (velocityFirstParallel - innerParallelVelocity)
+                                          / scvf.cellCenteredParallelDistance(localSubFaceIdx,0);
 
             normalDiffusiveFlux -= muAvg * parallelGradient;
         }
@@ -603,7 +660,7 @@ private:
     //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest
     SubControlVolumeFace makeParallelGhostFace_(const SubControlVolumeFace& ownScvf, const int localSubFaceIdx) const
     {
-        return makeGhostFace_(ownScvf, ownScvf.pairData(localSubFaceIdx).virtualOuterParallelFaceDofPos);
+        return makeGhostFace_(ownScvf, ownScvf.pairData(localSubFaceIdx).virtualFirstParallelFaceDofPos);
     };
 
     //! helper function to get the averaged extrusion factor for a face
@@ -613,7 +670,6 @@ private:
         const auto& outsideVolVars = elemVolVars[scvf.outsideScvIdx()];
         return harmonicMean(insideVolVars.extrusionFactor(), outsideVolVars.extrusionFactor());
     }
-
     //! do nothing if no turbulence model is used
     template<class ...Args, bool turbulenceModel = ModelTraits::usesTurbulenceModel(), std::enable_if_t<!turbulenceModel, int> = 0>
     bool incorporateWallFunction_(Args&&... args) const
@@ -639,9 +695,233 @@ private:
         }
         else
             return false;
-    };
+    }
+
+    /*!
+     * \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.
+     *
+     * \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
+     */
+    bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf,
+                                const bool selfIsUpstream,
+                                std::array<Scalar, 3>& velocities,
+                                std::array<Scalar, 3>& distances,
+                                const FaceVariables& faceVars) 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();
+
+            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();
+
+            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;
+        }
+    }
+
+    /*!
+     * \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 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 = problem.fvGridGeometry().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)
+        {
+            // 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, ownScvf, oppositeSubFaceIdx, element, velocities[1]);
+
+            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
+            distances[1] = ownScvf.cellCenteredParallelDistance(oppositeSubFaceIdx, 0);
+
+            return true;
+        }
+        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 = problem.fvGridGeometry().element(problem.fvGridGeometry().scv(normalFace.outsideScvIdx()));
+                const SubControlVolumeFace& firstParallelScvf = problem.fvGridGeometry().scvf(normalFace.outsideScvIdx(), ownScvf.localFaceIdx());
+                velocities[2] = getParallelVelocityFromOtherBoundary_(problem, firstParallelScvf, localSubFaceIdx, elementParallel, velocities[1]);
+            }
+
+            distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0);
+            distances[1] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 1);
+            distances[2] = ownScvf.area();
+
+            return true;
+        }
+    }
+
+    /*!
+     * \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
+     */
+    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) const
+    {
+        // 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
+     */
+    Scalar getParallelVelocityFromOtherBoundary_(const Problem& problem,
+                                                 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 = problem.fvGridGeometry().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
+} // end namespace Dumux
 
-#endif
+#endif // DUMUX_NAVIERSTOKES_STAGGERED_FLUXVARIABLES_HH