From e4c9d81e6546a2ef858c6b0f9d0f05318ac3245c Mon Sep 17 00:00:00 2001
From: Kilian Weishaupt <kilian.weishaupt@iws.uni-stuttgart.de>
Date: Tue, 7 Sep 2021 13:09:59 +0200
Subject: [PATCH] [ff][staggered][navierstokes] Add helper classes for new
 staggered models

---
 .../navierstokes/momentum/fluxhelper.hh       |  29 ++-
 .../freeflow/navierstokes/scalarfluxhelper.hh | 183 ++++++++++++++++++
 .../navierstokes/scalarfluxvariables.hh       | 157 +++++++++++++++
 .../scalarfluxvariablescachefiller.hh         | 163 ++++++++++++++++
 .../navierstokes/scalarvolumevariables.hh     | 106 ++++++++++
 dumux/freeflow/navierstokes/velocityoutput.hh | 103 ++++++++++
 6 files changed, 723 insertions(+), 18 deletions(-)
 create mode 100644 dumux/freeflow/navierstokes/scalarfluxhelper.hh
 create mode 100644 dumux/freeflow/navierstokes/scalarfluxvariables.hh
 create mode 100644 dumux/freeflow/navierstokes/scalarfluxvariablescachefiller.hh
 create mode 100644 dumux/freeflow/navierstokes/scalarvolumevariables.hh
 create mode 100644 dumux/freeflow/navierstokes/velocityoutput.hh

diff --git a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
index 34fdb81355..46a95d283a 100644
--- a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
+++ b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
@@ -88,6 +88,10 @@ struct NavierStokesMomentumBoundaryFluxHelper
 
         if (scvf.isLateral())
         {
+            // if the lateral scvf is adjacent to a slip boundary, call the helper function for this special case
+            if (scv.boundary() && problem.onSlipBoundary(fvGeometry, fvGeometry.frontalScvfOnBoundary(scv)))
+                return slipVelocityMomentumFlux(problem, fvGeometry, scvf, elemVolVars, elemFluxVarsCache);
+
             // viscous terms
             const Scalar mu = problem.effectiveViscosity(element, fvGeometry, scvf);
 
@@ -101,7 +105,7 @@ struct NavierStokesMomentumBoundaryFluxHelper
                                               * scvf.directionSign();
 
             // advective terms
-            if (problem.enableInertiaTerms())
+            if (problem.enableInertiaTerms()) // TODO revise advection terms! Take care with upwinding!
             {
                 const auto transportingVelocity = [&]()
                 {
@@ -164,11 +168,9 @@ struct NavierStokesMomentumBoundaryFluxHelper
                 // lateral face coinciding with boundary
                 else if (scvf.boundary())
                 {
-                    assert(false);
-                    // this should not happen? TODO revise
-                    // const auto insideDensity = problem.density(element, fvGeometry.scv(scvf.insideScvIdx()));
-                    // const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
-                    // flux[scv.dofAxis()] += innerVelocity * transportingVelocity * insideDensity * scvf.directionSign();
+                    const auto insideDensity = problem.density(element, fvGeometry.scv(scvf.insideScvIdx()));
+                    const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
+                    flux[scv.dofAxis()] += innerVelocity * transportingVelocity * insideDensity * scvf.directionSign();
                 }
             }
         }
@@ -273,19 +275,10 @@ struct NavierStokesMomentumBoundaryFluxHelper
                      }
                 }();
 
-                const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
-                const auto outerVelocity = slipVelocity;
-
+                // Do not use upwinding here but directly take the slip velocity located on the boundary. Upwinding with a weight of 0.5
+                // would actually prevent second order grid convergence.
                 const auto rho = problem.getInsideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
-                const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity);
-
-                const auto insideMomentum = innerVelocity * rho.first;
-                const auto outsideMomentum = outerVelocity * rho.second;
-
-                static const auto upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
-
-                const auto transportedMomentum =  selfIsUpstream ? (upwindWeight * insideMomentum + (1.0 - upwindWeight) * outsideMomentum)
-                                                                    : (upwindWeight * outsideMomentum + (1.0 - upwindWeight) * insideMomentum);
+                const auto transportedMomentum = slipVelocity * rho.second;
 
                 flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
             }
diff --git a/dumux/freeflow/navierstokes/scalarfluxhelper.hh b/dumux/freeflow/navierstokes/scalarfluxhelper.hh
new file mode 100644
index 0000000000..9e35c8b5f5
--- /dev/null
+++ b/dumux/freeflow/navierstokes/scalarfluxhelper.hh
@@ -0,0 +1,183 @@
+// -*- 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/>.   *
+ *****************************************************************************/
+
+#ifndef DUMUX_NAVIERSTOKES_SCALAR_BOUNDARY_FLUXHELPER_HH
+#define DUMUX_NAVIERSTOKES_SCALAR_BOUNDARY_FLUXHELPER_HH
+
+#include <dune/common/float_cmp.hh>
+#include <dune/common/std/type_traits.hh>
+#include <dumux/common/math.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/discretization/elementsolution.hh>
+
+namespace Dumux {
+
+#ifndef DOXYGEN
+namespace Detail {
+// helper structs and functions detecting if the VolumeVariables belong to a non-isothermal model
+template <class Indices>
+using NonisothermalDetector = decltype(std::declval<Indices>().energyEqIdx);
+
+template<class Indices>
+static constexpr bool isNonIsothermal()
+{ return Dune::Std::is_detected<NonisothermalDetector, Indices>::value; }
+
+} // end namespace Detail
+#endif
+
+template<class AdvectiveFlux>
+struct NavierStokesScalarBoundaryFluxHelper
+{
+    /*!
+     * \brief Return the area-specific, weighted advective flux of a scalar quantity.
+     */
+    template<class VolumeVariables, class SubControlVolumeFace, class Scalar, class UpwindTerm>
+    static Scalar advectiveScalarUpwindFlux(const VolumeVariables& insideVolVars,
+                                            const VolumeVariables& outsideVolVars,
+                                            const SubControlVolumeFace& scvf,
+                                            const Scalar volumeFlux,
+                                            const Scalar upwindWeight,
+                                            UpwindTerm upwindTerm)
+    {
+        using std::signbit;
+        const bool insideIsUpstream = !signbit(volumeFlux);
+
+        const auto& upstreamVolVars = insideIsUpstream ? insideVolVars : outsideVolVars;
+        const auto& downstreamVolVars = insideIsUpstream ? outsideVolVars : insideVolVars;
+
+        return (upwindWeight * upwindTerm(upstreamVolVars) +
+               (1.0 - upwindWeight) * upwindTerm(downstreamVolVars))
+               * volumeFlux;
+    }
+
+    template<class Indices, class NumEqVector, class UpwindFunction>
+    static void addModelSpecificAdvectiveFlux(NumEqVector& flux,
+                                              const UpwindFunction& upwind)
+    {
+        // add advective fluxes based on physical type of model
+        AdvectiveFlux::addAdvectiveFlux(flux, upwind);
+
+        // for non-isothermal models, add the energy flux
+        if constexpr (Detail::isNonIsothermal<Indices>())
+        {
+            auto upwindTerm = [](const auto& volVars) { return volVars.density()*volVars.enthalpy(); };
+            flux[Indices::energyEqIdx] = upwind(upwindTerm);
+        }
+    }
+
+    /*!
+     * \brief Return the area-specific outflow fluxes for all scalar balance equations.
+     *        The values specified in outsideBoundaryPriVars are used in case of flow reversal.
+     */
+    template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables>
+    static auto scalarOutflowFlux(const Problem& problem,
+                                  const Element& element,
+                                  const FVElementGeometry& fvGeometry,
+                                  const typename FVElementGeometry::SubControlVolumeFace& scvf,
+                                  const ElementVolumeVariables& elemVolVars,
+                                  typename ElementVolumeVariables::VolumeVariables::PrimaryVariables&& outsideBoundaryPriVars,
+                                  const typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type upwindWeight = 1.0)
+    {
+        using VolumeVariables = typename ElementVolumeVariables::VolumeVariables;
+        using PrimaryVariables = typename VolumeVariables::PrimaryVariables;
+        using NumEqVector = PrimaryVariables;
+        NumEqVector flux;
+        const auto velocity = problem.faceVelocity(element,fvGeometry, scvf);
+        const auto volumeFlux = velocity * scvf.unitOuterNormal();
+        using std::signbit;
+        const bool insideIsUpstream = !signbit(volumeFlux);
+        const VolumeVariables& insideVolVars = elemVolVars[scvf.insideScvIdx()];
+        const VolumeVariables& outsideVolVars = [&]()
+        {
+            // only use the inside volVars for "true" outflow conditions (avoid constructing the outside volVars)
+            if (insideIsUpstream && Dune::FloatCmp::eq(upwindWeight, 1.0, 1e-6))
+                return insideVolVars;
+            else
+            {
+                // construct outside volVars from the given priVars for situations of flow reversal
+                VolumeVariables boundaryVolVars;
+                boundaryVolVars.update(elementSolution<FVElementGeometry>(std::forward<PrimaryVariables>(outsideBoundaryPriVars)),
+                                       problem,
+                                       element,
+                                       fvGeometry.scv(scvf.insideScvIdx()));
+                return boundaryVolVars;
+            }
+        }();
+
+        auto upwindFuntion = [&](const auto& upwindTerm)
+        {
+            return advectiveScalarUpwindFlux(insideVolVars, outsideVolVars, scvf, volumeFlux, upwindWeight, upwindTerm);
+        };
+
+        addModelSpecificAdvectiveFlux<typename VolumeVariables::Indices>(flux, upwindFuntion);
+
+        return flux;
+    }
+
+    /*!
+     * \brief Return the area-specific outflow fluxes for all scalar balance equations.
+     *        This should only be used of flow reversal does never occur.
+     *        A (deactivable) warning is emitted otherwise.
+     */
+    template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables>
+    static auto scalarOutflowFlux(const Problem& problem,
+                                  const Element& element,
+                                  const FVElementGeometry& fvGeometry,
+                                  const typename FVElementGeometry::SubControlVolumeFace& scvf,
+                                  const ElementVolumeVariables& elemVolVars)
+    {
+        using VolumeVariables = typename ElementVolumeVariables::VolumeVariables;
+        using NumEqVector = typename VolumeVariables::PrimaryVariables;
+        NumEqVector flux;
+        const auto velocity = problem.faceVelocity(element,fvGeometry, scvf);
+        const auto volumeFlux = velocity * scvf.unitOuterNormal();
+        using std::signbit;
+        const bool insideIsUpstream = !signbit(volumeFlux);
+        const VolumeVariables& insideVolVars = elemVolVars[scvf.insideScvIdx()];
+
+        if constexpr (VolumeVariables::FluidSystem::isCompressible(0/*phaseIdx*/) /*TODO viscosityIsConstant*/ || NumEqVector::size() > 1)
+        {
+            static const bool verbose = getParamFromGroup<bool>(problem.paramGroup(), "Flux.EnableOutflowReversalWarning", true);
+            using std::abs;
+            if (verbose && !insideIsUpstream && abs(volumeFlux) > 1e-10)
+            {
+                std::cout << "velo " << velocity << ", flux " << volumeFlux << std::endl;
+                std::cout << "\n ********** WARNING ********** \n\n"
+                "Outflow condition set at " << scvf.center() << " might be invalid due to flow reversal. "
+                "Consider using \n"
+                "outflowFlux(problem, element, fvGeometry, scvf, elemVolVars, outsideBoundaryPriVars, upwindWeight) \n"
+                "instead where you can specify primary variables for inflow situations.\n"
+                "\n ***************************** \n" << std::endl;
+            }
+        }
+
+        auto upwindFuntion = [&](const auto& upwindTerm)
+        {
+            return advectiveScalarUpwindFlux(insideVolVars, insideVolVars, scvf, volumeFlux, 1.0 /*upwindWeight*/, upwindTerm);
+        };
+
+        addModelSpecificAdvectiveFlux<typename VolumeVariables::Indices>(flux, upwindFuntion);
+
+        return flux;
+    }
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/freeflow/navierstokes/scalarfluxvariables.hh b/dumux/freeflow/navierstokes/scalarfluxvariables.hh
new file mode 100644
index 0000000000..3a07b5e9bb
--- /dev/null
+++ b/dumux/freeflow/navierstokes/scalarfluxvariables.hh
@@ -0,0 +1,157 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 3 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup NavierStokesModel
+ * \copydoc Dumux::NavierStokesFluxVariablesImpl
+ */
+#ifndef DUMUX_NAVIERSTOKES_SCALAR_CONSERVATION_MODEL_FLUXVARIABLES_HH
+#define DUMUX_NAVIERSTOKES_SCALAR_CONSERVATION_MODEL_FLUXVARIABLES_HH
+
+#include <dumux/common/math.hh>
+#include <dumux/common/typetraits/problem.hh>
+#include <dumux/flux/fluxvariablesbase.hh>
+#include <dumux/discretization/extrusion.hh>
+#include <dumux/discretization/method.hh>
+#include <dumux/flux/upwindscheme.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief The flux variables base class for scalar quantities balanced in the Navier-Stokes model.
+ */
+template<class Problem,
+         class ModelTraits,
+         class FluxTypes,
+         class ElementVolumeVariables,
+         class ElementFluxVariablesCache,
+         class UpwindScheme = UpwindScheme<typename ProblemTraits<Problem>::GridGeometry>>
+class NavierStokesScalarConservationModelFluxVariables
+: public FluxVariablesBase<Problem,
+                           typename ProblemTraits<Problem>::GridGeometry::LocalView,
+                           ElementVolumeVariables,
+                           ElementFluxVariablesCache>
+{
+    using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type;
+    using GridGeometry = typename ProblemTraits<Problem>::GridGeometry;
+    using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+    using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
+    using FVElementGeometry = typename GridGeometry::LocalView;
+    using Extrusion = Extrusion_t<typename ProblemTraits<Problem>::GridGeometry>;
+
+public:
+
+    struct AdvectionType
+    {
+        static Scalar flux(const Problem& problem,
+                           const Element& element,
+                           const FVElementGeometry& fvGeometry,
+                           const ElementVolumeVariables& elemVolVars,
+                           const SubControlVolumeFace& scvf,
+                           const int phaseIdx,
+                           const ElementFluxVariablesCache& elemFluxVarsCache)
+        {
+            const auto velocity = problem.faceVelocity(element, fvGeometry, scvf);
+            const Scalar volumeFlux = velocity*scvf.unitOuterNormal()*Extrusion::area(scvf)*extrusionFactor_(elemVolVars, scvf);
+            return volumeFlux;
+        }
+    };
+
+    /*!
+     * \brief Returns the advective flux computed by the respective law.
+     */
+    template<typename FunctionType>
+    Scalar getAdvectiveFlux(const FunctionType& upwindTerm) const
+    {
+        if constexpr (ModelTraits::enableAdvection())
+        {
+            const auto& scvf = this->scvFace();
+            const auto velocity = this->problem().faceVelocity(this->element(), this->fvGeometry(), scvf);
+            const Scalar volumeFlux = velocity*scvf.unitOuterNormal()*Extrusion::area(scvf)*extrusionFactor_(this->elemVolVars(), scvf);
+            return UpwindScheme::apply(*this, upwindTerm, volumeFlux, 0/*phaseIdx*/);
+        }
+        else
+            return 0.0;
+    }
+
+    /*!
+     * \brief Returns the conductive energy flux computed by the respective law.
+     */
+    Scalar heatConductionFlux() const
+    {
+        if constexpr (ModelTraits::enableEnergyBalance())
+        {
+            using HeatConductionType = typename FluxTypes::HeatConductionType;
+            return HeatConductionType::flux(this->problem(),
+                                            this->element(),
+                                            this->fvGeometry(),
+                                            this->elemVolVars(),
+                                            this->scvFace(),
+                                            this->elemFluxVarsCache());
+        }
+        else
+            return 0.0;
+    }
+
+    /*!
+     * \brief Returns the advective energy flux.
+     */
+    Scalar heatAdvectionFlux() const
+    {
+        if constexpr (ModelTraits::enableEnergyBalance())
+        {
+            const auto upwindTerm = [](const auto& volVars) { return volVars.density() * volVars.enthalpy(); };
+            return getAdvectiveFlux(upwindTerm);
+        }
+        else
+            return 0.0;
+    }
+
+    /*!
+     * \brief Returns the total energy flux.
+     */
+    Scalar heatFlux() const
+    {
+        return heatConductionFlux() + heatAdvectionFlux();
+    }
+
+    /*!
+     * \brief Adds the energy flux to a given flux vector.
+     */
+    template<class NumEqVector>
+    void addHeatFlux(NumEqVector& flux) const
+    {
+        if constexpr (ModelTraits::enableEnergyBalance())
+            flux[ModelTraits::Indices::energyEqIdx] = heatFlux();
+    }
+
+private:
+
+    static Scalar extrusionFactor_(const ElementVolumeVariables& elemVolVars, const SubControlVolumeFace& scvf)
+    {
+        const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
+        const auto& outsideVolVars = elemVolVars[scvf.outsideScvIdx()];
+        return harmonicMean(insideVolVars.extrusionFactor(), outsideVolVars.extrusionFactor());
+    }
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/freeflow/navierstokes/scalarfluxvariablescachefiller.hh b/dumux/freeflow/navierstokes/scalarfluxvariablescachefiller.hh
new file mode 100644
index 0000000000..d764a3d139
--- /dev/null
+++ b/dumux/freeflow/navierstokes/scalarfluxvariablescachefiller.hh
@@ -0,0 +1,163 @@
+// -*- 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 FreeflowModels
+ * \brief A helper class to fill the flux variables cache
+ */
+#ifndef DUMUX_FREEFLOW_SCALAR_FLUXVARIABLESCACHE_FILLER_HH
+#define DUMUX_FREEFLOW_SCALAR_FLUXVARIABLESCACHE_FILLER_HH
+
+#include <dumux/common/properties.hh>
+#include <dumux/common/parameters.hh>
+
+#include <dumux/discretization/method.hh>
+#include <dumux/discretization/extrusion.hh>
+#include <dumux/flux/referencesystemformulation.hh>
+#include <dumux/common/typetraits/problem.hh>
+
+namespace Dumux {
+
+// forward declaration
+template<class Problem, class ModelTraits, bool diffusionIsSolDependent, bool heatConductionIsSolDependent, DiscretizationMethod discMethod>
+class FreeFlowScalarFluxVariablesCacheFillerImplementation;
+
+/*!
+ * \ingroup PorousmediumflowModels
+ * \brief The flux variables cache filler class for free flow
+ *
+ * Helps filling the flux variables cache depending several policies
+ */
+template<class Problem, class ModelTraits, bool diffusionIsSolDependent, bool heatConductionIsSolDependent>
+using FreeFlowScalarFluxVariablesCacheFiller = FreeFlowScalarFluxVariablesCacheFillerImplementation<Problem, ModelTraits, diffusionIsSolDependent, heatConductionIsSolDependent, ProblemTraits<Problem>::GridGeometry::discMethod>;
+
+//! Specialization of the flux variables cache filler for the cell centered tpfa method
+template<class Problem, class ModelTraits, bool diffusionIsSolDependent, bool heatConductionIsSolDependent>
+class FreeFlowScalarFluxVariablesCacheFillerImplementation<Problem, ModelTraits, diffusionIsSolDependent, heatConductionIsSolDependent, DiscretizationMethod::cctpfa>
+{
+    using GridGeometry = typename ProblemTraits<Problem>::GridGeometry;
+    using GridView = typename GridGeometry::GridView;
+    using FVElementGeometry = typename GridGeometry::LocalView;
+    using SubControlVolume = typename GridGeometry::SubControlVolume;
+    using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+
+    using Element = typename GridView::template Codim<0>::Entity;
+
+    static constexpr bool diffusionEnabled = ModelTraits::enableMolecularDiffusion();
+    static constexpr bool heatConductionEnabled = ModelTraits::enableEnergyBalance();
+
+public:
+    static constexpr bool isSolDependent = (diffusionEnabled && diffusionIsSolDependent) ||
+                                           (heatConductionEnabled && heatConductionIsSolDependent);
+
+    //! The constructor. Sets the problem pointer
+    FreeFlowScalarFluxVariablesCacheFillerImplementation(const Problem& problem)
+    : problemPtr_(&problem) {}
+
+    /*!
+     * \brief function to fill the flux variables caches
+     *
+     * \param fluxVarsCacheContainer Either the element or global flux variables cache
+     * \param scvfFluxVarsCache The flux var cache to be updated corresponding to the given scvf
+     * \param element The finite element
+     * \param fvGeometry The finite volume geometry
+     * \param elemVolVars The element volume variables
+     * \param scvf The corresponding sub-control volume face
+     * \param forceUpdateAll if true, forces all caches to be updated (even the solution-independent ones)
+     */
+    template<class FluxVariablesCacheContainer, class FluxVariablesCache, class ElementVolumeVariables>
+    void fill(FluxVariablesCacheContainer& fluxVarsCacheContainer,
+              FluxVariablesCache& scvfFluxVarsCache,
+              const Element& element,
+              const FVElementGeometry& fvGeometry,
+              const ElementVolumeVariables& elemVolVars,
+              const SubControlVolumeFace& scvf,
+              const bool forceUpdateAll = false)
+    {
+        // fill the physics-related quantities of the caches
+        if (forceUpdateAll)
+        {
+            if constexpr (diffusionEnabled)
+                fillDiffusion_(scvfFluxVarsCache, element, fvGeometry, elemVolVars, scvf);
+            if constexpr (heatConductionEnabled)
+                fillHeatConduction_(scvfFluxVarsCache, element, fvGeometry, elemVolVars, scvf);
+        }
+        else
+        {
+            if constexpr (diffusionEnabled && diffusionIsSolDependent)
+                fillDiffusion_(scvfFluxVarsCache, element, fvGeometry, elemVolVars, scvf);
+            if constexpr (heatConductionEnabled && heatConductionIsSolDependent)
+                fillHeatConduction_(scvfFluxVarsCache, element, fvGeometry, elemVolVars, scvf);
+        }
+    }
+
+private:
+
+    const Problem& problem() const
+    { return *problemPtr_; }
+
+
+    //! method to fill the diffusive quantities
+    template<class FluxVariablesCache, class ElementVolumeVariables>
+    void fillDiffusion_(FluxVariablesCache& scvfFluxVarsCache,
+                        const Element& element,
+                        const FVElementGeometry& fvGeometry,
+                        const ElementVolumeVariables& elemVolVars,
+                        const SubControlVolumeFace& scvf)
+    {
+        using DiffusionType = typename ElementVolumeVariables::VolumeVariables::MolecularDiffusionType;
+        using DiffusionFiller = typename DiffusionType::Cache::Filler;
+        using FluidSystem = typename ElementVolumeVariables::VolumeVariables::FluidSystem;
+
+        static constexpr int numPhases = ModelTraits::numFluidPhases();
+        static constexpr int numComponents = ModelTraits::numFluidComponents();
+
+        // forward to the filler of the diffusive quantities
+        if constexpr (FluidSystem::isTracerFluidSystem())
+            for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                for (unsigned int compIdx = 0; compIdx < numComponents; ++compIdx)
+                    DiffusionFiller::fill(scvfFluxVarsCache, phaseIdx, compIdx, problem(), element, fvGeometry, elemVolVars, scvf, *this);
+        else
+            for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                for (unsigned int compIdx = 0; compIdx < numComponents; ++compIdx)
+                    if (compIdx != FluidSystem::getMainComponent(phaseIdx))
+                        DiffusionFiller::fill(scvfFluxVarsCache, phaseIdx, compIdx, problem(), element, fvGeometry, elemVolVars, scvf, *this);
+    }
+
+    //! method to fill the quantities related to heat conduction
+    template<class FluxVariablesCache, class ElementVolumeVariables>
+    void fillHeatConduction_(FluxVariablesCache& scvfFluxVarsCache,
+                             const Element& element,
+                             const FVElementGeometry& fvGeometry,
+                             const ElementVolumeVariables& elemVolVars,
+                             const SubControlVolumeFace& scvf)
+    {
+        using HeatConductionType = typename ElementVolumeVariables::VolumeVariables::HeatConductionType;
+        using HeatConductionFiller = typename HeatConductionType::Cache::Filler;
+
+        // forward to the filler of the diffusive quantities
+        HeatConductionFiller::fill(scvfFluxVarsCache, problem(), element, fvGeometry, elemVolVars, scvf, *this);
+    }
+
+    const Problem* problemPtr_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/freeflow/navierstokes/scalarvolumevariables.hh b/dumux/freeflow/navierstokes/scalarvolumevariables.hh
new file mode 100644
index 0000000000..ebbaa231f9
--- /dev/null
+++ b/dumux/freeflow/navierstokes/scalarvolumevariables.hh
@@ -0,0 +1,106 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 3 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup NavierStokesModel
+ * \copydoc Dumux::NavierStokesVolumeVariables
+ */
+#ifndef DUMUX_NAVIERSTOKES_SCALAR_CONSERVATION_MODEL_VOLUME_VARIABLES_HH
+#define DUMUX_NAVIERSTOKES_SCALAR_CONSERVATION_MODEL_VOLUME_VARIABLES_HH
+
+
+namespace Dumux {
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Volume variables for the single-phase Navier-Stokes model.
+ */
+template <class Traits>
+class NavierStokesScalarConservationModelVolumeVariables
+{
+    using Scalar = typename Traits::PrimaryVariables::value_type;
+
+public:
+    //! export the type used for the primary variables
+    using PrimaryVariables = typename Traits::PrimaryVariables;
+    //! export the indices type
+    using Indices = typename Traits::ModelTraits::Indices;
+    //! Export the underlying fluid system
+    using FluidSystem = typename Traits::FluidSystem;
+    //! Export the fluid state type
+    using FluidState = typename Traits::FluidState;
+
+    //! Return number of phases considered by the model
+    static constexpr int numFluidPhases() { return Traits::ModelTraits::numFluidPhases(); }
+    //! Return number of components considered by the model
+    static constexpr int numFluidComponents() { return Traits::ModelTraits::numFluidComponents(); }
+
+     /*!
+     * \brief Update all quantities for a given control volume
+     *
+     * \param elemSol A vector containing all primary variables connected to the element
+     * \param problem The object specifying the problem which ought to
+     *                be simulated
+     * \param element An element which contains part of the control volume
+     * \param scv The sub-control volume
+     */
+    template<class ElementSolution, class Problem, class Element, class SubControlVolume>
+    void update(const ElementSolution& elemSol,
+                const Problem& problem,
+                const Element& element,
+                const SubControlVolume& scv)
+    {
+        priVars_ = elemSol[scv.indexInElement()];
+        extrusionFactor_ = problem.extrusionFactor(element, scv, elemSol);
+    }
+
+    /*!
+     * \brief Return how much the sub-control volume is extruded.
+     *
+     * This means the factor by which a lower-dimensional (1D or 2D)
+     * entity needs to be expanded to get a full dimensional cell. The
+     * default is 1.0 which means that 1D problems are actually
+     * thought as pipes with a cross section of 1 m^2 and 2D problems
+     * are assumed to extend 1 m to the back.
+     */
+    Scalar extrusionFactor() const
+    { return extrusionFactor_; }
+
+    /*!
+     * \brief Return a component of primary variable vector
+     *
+     * \param pvIdx The index of the primary variable of interest
+     */
+    Scalar priVar(const int pvIdx) const
+    { return priVars_[pvIdx]; }
+
+    /*!
+     * \brief Return the primary variable vector
+     */
+    const PrimaryVariables& priVars() const
+    { return priVars_; }
+
+protected:
+    PrimaryVariables priVars_;
+    Scalar extrusionFactor_;
+};
+
+} // end namespace Dumux
+
+#endif // DUMUX_NAVIERSTOKES_MOMENTUM_VOLUME_VARIABLES_HH
diff --git a/dumux/freeflow/navierstokes/velocityoutput.hh b/dumux/freeflow/navierstokes/velocityoutput.hh
new file mode 100644
index 0000000000..eb504effe3
--- /dev/null
+++ b/dumux/freeflow/navierstokes/velocityoutput.hh
@@ -0,0 +1,103 @@
+// -*- 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 StaggeredDiscretization
+ * \copydoc Dumux::StaggeredFreeFlowVelocityOutput
+ */
+#ifndef DUMUX_FREEFLOW_NAVIERSTOKES_VELOCITYOUTPUT_HH
+#define DUMUX_FREEFLOW_NAVIERSTOKES_VELOCITYOUTPUT_HH
+
+#include <dumux/io/velocityoutput.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/discretization/method.hh>
+#include <dumux/freeflow/navierstokes/momentum/velocityreconstruction.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup StaggeredDiscretization
+ * \brief Velocity output for staggered free-flow models
+ */
+template<class GridVariables>
+class NavierStokesVelocityOutput : public VelocityOutput<GridVariables>
+{
+    using ParentType = VelocityOutput<GridVariables>;
+    using GridGeometry = typename GridVariables::GridGeometry;
+    using FVElementGeometry = typename GridGeometry::LocalView;
+    using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
+    using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
+    using ElementFluxVarsCache = typename GridVariables::GridFluxVariablesCache::LocalView;
+    using VolumeVariables = typename GridVariables::VolumeVariables;
+    using FluidSystem = typename VolumeVariables::FluidSystem;
+    using GridView = typename GridGeometry::GridView;
+    using Element = typename GridView::template Codim<0>::Entity;
+
+public:
+    using VelocityVector = typename ParentType::VelocityVector;
+
+    NavierStokesVelocityOutput(const std::string& paramGroup = "")
+    {
+        enableOutput_ = getParamFromGroup<bool>(paramGroup, "Vtk.AddVelocity", true);
+    }
+
+    //! Returns whether to enable the velocity output or not
+    bool enableOutput() const override { return enableOutput_; }
+
+    //! returns the phase name of a given phase index
+    std::string phaseName(int phaseIdx) const override { return FluidSystem::phaseName(phaseIdx); }
+
+    //! returns the number of phases
+    int numFluidPhases() const override { return VolumeVariables::numFluidPhases(); }
+
+    //! Calculate the velocities for the scvs in the element
+    //! We assume the local containers to be bound to the complete stencil
+    void calculateVelocity(VelocityVector& velocity,
+                           const Element& element,
+                           const FVElementGeometry& fvGeometry,
+                           const ElementVolumeVariables& elemVolVars,
+                           const ElementFluxVarsCache& elemFluxVarsCache,
+                           int phaseIdx) const override
+    {
+        using Problem = std::decay_t<decltype(elemVolVars.gridVolVars().problem())>;
+        if constexpr (Problem::momentumDiscretizationMethod == DiscretizationMethod::fcstaggered)
+            calculateVelocityForStaggeredGrid_(velocity, element, fvGeometry, elemVolVars);
+    }
+
+private:
+    void calculateVelocityForStaggeredGrid_(VelocityVector& velocity,
+                                            const Element& element,
+                                            const FVElementGeometry& fvGeometry,
+                                            const ElementVolumeVariables& elemVolVars) const
+    {
+        const auto eIdx = fvGeometry.gridGeometry().elementMapper().index(element);
+        const auto getFaceVelocity = [&](const FVElementGeometry& fvG, const auto& scvf)
+        {
+            return elemVolVars.gridVolVars().problem().faceVelocity(element, fvGeometry, scvf);
+        };
+
+        velocity[eIdx] = StaggeredVelocityReconstruction::cellCenterVelocity(getFaceVelocity, fvGeometry);
+    }
+
+bool enableOutput_;
+};
+
+} // end namespace Dumux
+
+#endif
-- 
GitLab