diff --git a/dumux/flux/forchheimervelocity.hh b/dumux/flux/forchheimervelocity.hh
new file mode 100644
index 0000000000000000000000000000000000000000..bcf1d19cc8bd4d92198eb4a50a014b48419f69a5
--- /dev/null
+++ b/dumux/flux/forchheimervelocity.hh
@@ -0,0 +1,371 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 3 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup Flux
+ * \brief Forchheimer's law
+ *        This file contains the calculation of the Forchheimer velocity
+ *        for a given Darcy velocity
+ */
+#ifndef DUMUX_FLUX_FORCHHEIMER_VELOCITY_HH
+#define DUMUX_FLUX_FORCHHEIMER_VELOCITY_HH
+
+#include <dune/common/fvector.hh>
+#include <dune/common/fmatrix.hh>
+
+#include <dumux/common/math.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/common/properties.hh>
+#include <dumux/common/typetraits/typetraits.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup Flux
+ * \brief Forchheimer's law
+ *        This file contains the calculation of the Forchheimer velocity
+ *        for a given Darcy velocity
+ */
+template<class Scalar, class GridGeometry, class FluxVariables>
+class ForchheimerVelocity
+{
+    using FVElementGeometry = typename GridGeometry::LocalView;
+    using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+    using GridView = typename GridGeometry::GridView;
+    using Element = typename GridView::template Codim<0>::Entity;
+
+    static constexpr int dim = GridView::dimension;
+    static constexpr int dimWorld = GridView::dimensionworld;
+    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
+  public:
+    using UpwindScheme = typename FluxVariables::UpwindScheme;
+    using DimWorldVector = Dune::FieldVector<Scalar, dimWorld>;
+    using DimWorldMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
+
+    /*! \brief Compute the Forchheimer velocity of a phase at
+    *          the given sub-control volume face using the Forchheimer equation.
+    *
+    *
+    * see e.g. Nield & Bejan: Convection in Porous Media \cite nield2006
+    *
+    * The relative passability \f$ \eta_r\f$ is the "Forchheimer-equivalent" to the relative
+    * permeability \f$ k_r\f$.
+    * We use the same function as for \f$ k_r \f$ (VG, BC, linear) other authors use a simple
+    * power law e.g.: \f$\eta_{rw} = S_w^3\f$
+    *
+    * Some rearrangements have been made to the original Forchheimer relation:
+    *
+    * \f[ \mathbf{v_\alpha} + c_F \sqrt{\mathbf{K}} \frac{\rho_\alpha}{\mu_\alpha }
+    *     |\mathbf{v_\alpha}| \mathbf{v_\alpha}
+    *     + \frac{k_{r \alpha}}{\mu_\alpha} \mathbf{K} \nabla \left(p_\alpha
+    *     + \rho_\alpha g z \right)=  0
+    * \f]
+    *
+    * This already includes the assumption \f$ k_r(S_w) = \eta_r(S_w)\f$:
+    * - \f$\eta_{rw} = S_w^x\f$ looks very similar to e.g. Van Genuchten relative permeabilities
+    * - Fichot, et al. (2006), Nuclear Engineering and Design, state that several authors claim
+    *   that \f$ k_r(S_w), \eta_r(S_w)\f$ can be chosen equal
+    * - It leads to the equation not degenerating for the case of \f$S_w=1\f$, because I do not
+    *   need to multiply with two different functions, and therefore there are terms not being
+    *   zero.
+    * - If this assumption is not to be made: Some regularization needs to be introduced ensuring
+    *   that not all terms become zero for \f$S_w=1\f$.
+    *
+    * This non-linear equations is solved for \f$\mathbf{v_\alpha}\f$ using Newton's method
+    * and an analytical derivative w.r.t. \f$\mathbf{v_\alpha}\f$.
+    *
+    * The gradient of the Forchheimer relations looks as follows (mind that \f$\sqrt{\mathbf{K}}\f$
+    * is a tensor):
+    *
+    * \f[  f\left(\mathbf{v_\alpha}\right) =
+    * \left(
+    * \begin{array}{ccc}
+    * 1 & 0 &0 \\
+    * 0 & 1 &0 \\
+    * 0 & 0 &1 \\
+    * \end{array}
+    * \right)
+    * +
+    * c_F \frac{\rho_\alpha}{\mu_\alpha} |\mathbf{v}_\alpha| \sqrt{\mathbf{K}}
+    * +
+    * c_F \frac{\rho_\alpha}{\mu_\alpha}\frac{1}{|\mathbf{v}_\alpha|} \sqrt{\mathbf{K}}
+    * \left(
+    * \begin{array}{ccc}
+    * v_x^2 & v_xv_y & v_xv_z \\
+    * v_yv_x & v_{y}^2 & v_yv_z \\
+    * v_zv_x & v_zv_y &v_{z}^2 \\
+    * \end{array}
+    * \right)
+    *  \f]
+    *
+    * \note We restrict the use of Forchheimer's law to diagonal permeability tensors so far. This might be changed to
+    * general tensors using eigenvalue decomposition to get \f$\sqrt{\mathbf{K}}\f$
+    */
+    template<class ElementVolumeVariables>
+    static DimWorldVector velocity(const FVElementGeometry& fvGeometry,
+                                   const ElementVolumeVariables& elemVolVars,
+                                   const SubControlVolumeFace& scvf,
+                                   const int phaseIdx,
+                                   const DimWorldMatrix sqrtK,
+                                   const DimWorldVector darcyVelocity)
+    {
+        const auto& problem = elemVolVars.gridVolVars().problem();
+
+        // Obtain the Forchheimer coefficient from the spatial parameters
+        const Scalar forchCoeff = problem.spatialParams().forchCoeff(scvf);
+
+        // Initial guess of velocity: Darcy relation
+        DimWorldVector velocity = darcyVelocity;
+
+        DimWorldVector deltaV(0.0);           // the change in velocity between Newton iterations
+        DimWorldVector residual(10e10);       // the residual (function value that is to be minimized)
+        DimWorldMatrix gradF(0.0);            // slope of equation that is to be solved
+
+        // Search by means of the Newton method for a root of Forchheimer equation
+        static const Scalar epsilon = getParamFromGroup<Scalar>(problem.paramGroup(), "Forchheimer.NewtonTolerance", 1e-12);
+        static const std::size_t maxNumIter = getParamFromGroup<std::size_t>(problem.paramGroup(), "Forchheimer.MaxIterations", 30);
+        for (int k = 0; residual.two_norm() > epsilon ; ++k)
+        {
+            if (k >= maxNumIter)
+                DUNE_THROW(NumericalProblem, "could not determine forchheimer velocity within "
+                                             << k << " iterations");
+
+            // calculate current value of Forchheimer equation
+            forchheimerResidual_(residual,
+                                 forchCoeff,
+                                 sqrtK,
+                                 darcyVelocity,
+                                 velocity,
+                                 elemVolVars,
+                                 scvf,
+                                 phaseIdx);
+
+            // newton's method: slope (gradF) and current value (residual) of function is known,
+            forchheimerDerivative_(gradF,
+                                   forchCoeff,
+                                   sqrtK,
+                                   velocity,
+                                   elemVolVars,
+                                   scvf,
+                                   phaseIdx);
+
+            // solve for change in velocity ("x-Axis intercept")
+            gradF.solve(deltaV, residual);
+            velocity -= deltaV;
+        }
+
+        // The fluxvariables expect a value on which an upwinding of the mobility is performed.
+        // We therefore divide by the mobility here.
+        auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.mobility(phaseIdx); };
+        const Scalar forchheimerUpwindMobility = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
+
+        // Do not divide by zero. If the mobility is zero, the resulting flux with upwinding will be zero anyway.
+        if (forchheimerUpwindMobility > 0.0)
+            velocity /= forchheimerUpwindMobility;
+
+        return velocity;
+    }
+
+    /*! \brief Returns the harmonic mean of \f$\sqrt{K_0}\f$ and \f$\sqrt{K_1}\f$.
+     *
+     * This is a specialization for scalar-valued permeabilities which returns a tensor with identical diagonal entries.
+     */
+    template<class Problem, class ElementVolumeVariables,
+             bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
+             std::enable_if_t<scalarPerm, int> = 0>
+    static DimWorldMatrix calculateHarmonicMeanSqrtPermeability(const Problem& problem,
+                                                                const ElementVolumeVariables& elemVolVars,
+                                                                const SubControlVolumeFace& scvf)
+    {
+        using std::sqrt;
+        Scalar harmonicMeanSqrtK(0.0);
+
+        const auto insideScvIdx = scvf.insideScvIdx();
+        const auto& insideVolVars = elemVolVars[insideScvIdx];
+        const Scalar Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
+        const Scalar sqrtKi = sqrt(Ki);
+
+        if (!scvf.boundary())
+        {
+            const auto outsideScvIdx = scvf.outsideScvIdx();
+            const auto& outsideVolVars = elemVolVars[outsideScvIdx];
+            const Scalar Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
+            const Scalar sqrtKj = sqrt(Kj);
+            harmonicMeanSqrtK = problem.spatialParams().harmonicMean(sqrtKi, sqrtKj, scvf.unitOuterNormal());
+        }
+        else
+            harmonicMeanSqrtK = sqrtKi;
+
+        // Convert to tensor
+        DimWorldMatrix result(0.0);
+        for (int i = 0; i < dimWorld; ++i)
+            result[i][i] = harmonicMeanSqrtK;
+
+        return result;
+    }
+
+    /*! \brief Returns the harmonic mean of \f$\sqrt{\mathbf{K_0}}\f$ and \f$\sqrt{\mathbf{K_1}}\f$.
+     *
+     * This is a specialization for tensor-valued permeabilities.
+     */
+    template<class Problem, class ElementVolumeVariables,
+             bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
+             std::enable_if_t<!scalarPerm, int> = 0>
+    static DimWorldMatrix calculateHarmonicMeanSqrtPermeability(const Problem& problem,
+                                                                const ElementVolumeVariables& elemVolVars,
+                                                                const SubControlVolumeFace& scvf)
+    {
+        using std::sqrt;
+        DimWorldMatrix sqrtKi(0.0);
+        DimWorldMatrix sqrtKj(0.0);
+        DimWorldMatrix harmonicMeanSqrtK(0.0);
+
+        const auto insideScvIdx = scvf.insideScvIdx();
+        const auto& insideVolVars = elemVolVars[insideScvIdx];
+        const auto& Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
+        // Make sure the permeability matrix does not have off-diagonal entries
+        // TODO implement method using eigenvalues and eigenvectors for general tensors
+        if (!isDiagonal_(Ki))
+            DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
+
+        for (int i = 0; i < dim; ++i)
+            sqrtKi[i][i] = sqrt(Ki[i][i]);
+
+        if (!scvf.boundary())
+        {
+            const auto outsideScvIdx = scvf.outsideScvIdx();
+            const auto& outsideVolVars = elemVolVars[outsideScvIdx];
+            const auto& Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
+            // Make sure the permeability matrix does not have off-diagonal entries
+            if (!isDiagonal_(Kj))
+                DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
+
+            for (int i = 0; i < dim; ++i)
+                sqrtKj[i][i] = sqrt(Kj[i][i]);
+
+            harmonicMeanSqrtK = problem.spatialParams().harmonicMean(sqrtKi, sqrtKj, scvf.unitOuterNormal());
+        }
+        else
+            harmonicMeanSqrtK = sqrtKi;
+
+        return harmonicMeanSqrtK;
+    }
+
+private:
+
+    //! calculate the residual of the Forchheimer equation
+    template<class ElementVolumeVariables>
+    static void forchheimerResidual_(DimWorldVector& residual,
+                                     const Scalar forchCoeff,
+                                     const DimWorldMatrix& sqrtK,
+                                     const DimWorldVector& darcyVelocity,
+                                     const DimWorldVector& oldVelocity,
+                                     const ElementVolumeVariables& elemVolVars,
+                                     const SubControlVolumeFace& scvf,
+                                     const int phaseIdx)
+    {
+        residual = oldVelocity;
+        // residual += k_r/mu  K grad p
+        residual -= darcyVelocity;
+        // residual += c_F rho / mu abs(v) sqrt(K) v
+        auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
+        const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, oldVelocity * scvf.unitOuterNormal(), phaseIdx);
+        sqrtK.usmv(forchCoeff * rhoOverMu * oldVelocity.two_norm(), oldVelocity, residual);
+    }
+
+     //! The analytical derivative of the the Forcheimer equation's residual
+    template<class ElementVolumeVariables>
+    static void forchheimerDerivative_(DimWorldMatrix& derivative,
+                                       const Scalar forchCoeff,
+                                       const DimWorldMatrix& sqrtK,
+                                       const DimWorldVector& velocity,
+                                       const ElementVolumeVariables& elemVolVars,
+                                       const SubControlVolumeFace& scvf,
+                                       const int phaseIdx)
+    {
+        // Initialize because for low velocities, we add and do not set the entries.
+        derivative = 0.0;
+
+        auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
+
+        const Scalar absV = velocity.two_norm();
+        // This part of the derivative is only calculated if the velocity is sufficiently small:
+        // do not divide by zero!
+        // This should not be very bad because the derivative is only intended to give an
+        // approximation of the gradient of the
+        // function that goes into the Newton scheme.
+        // This is important in the case of a one-phase region in two-phase flow. The non-existing
+        // phase has a velocity of zero (kr=0).
+        // f = sqrtK * vTimesV (this is  a matrix)
+        // f *= forchCoeff density / |velocity| / viscosity (multiply all entries with scalars)
+        const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
+        if (absV > 1e-20)
+        {
+            for (int i = 0; i < dim; i++)
+            {
+                for (int k = 0; k <= i; k++)
+                {
+                    derivative[i][k] = sqrtK[i][i] * velocity[i] * velocity[k] * forchCoeff
+                    / absV  * rhoOverMu;
+
+                    if (k < i)
+                        derivative[k][i] = derivative[i][k];
+                }
+            }
+        }
+
+        // add on the main diagonal:
+        // 1 + sqrtK_i forchCoeff density |v| / viscosity
+        for (int i = 0; i < dim; i++)
+            derivative[i][i] += (1.0 + (sqrtK[i][i]*forchCoeff * absV * rhoOverMu));
+    }
+
+    /*!
+     * \brief Check whether all off-diagonal entries of a tensor are zero.
+     *
+     * \param K the tensor that is to be checked.
+     *
+     * \return True if all off-diagonals are zero.
+     *
+    */
+    static bool isDiagonal_(const DimWorldMatrix & K)
+    {
+        using std::abs;
+        for (int i = 0; i < dim; i++)
+            for (int k = 0; k < dim; k++)
+                if ((i != k) && (abs(K[i][k]) >= 1e-25))
+                    return false;
+        return true;
+    }
+
+    template<class Problem, class VolumeVariables>
+    static decltype(auto) getPermeability_(const Problem& problem,
+                                           const VolumeVariables& volVars,
+                                           const GlobalPosition& scvfIpGlobal)
+    {
+        if constexpr (Problem::SpatialParams::evaluatePermeabilityAtScvfIP())
+            return problem.spatialParams().permeabilityAtPos(scvfIpGlobal);
+        else
+            return volVars.permeability();
+    }
+};
+
+} // end namespace Dumux
+
+#endif