From 131e84ecf392ca1f4c75c2af266e6b176e0b7cff Mon Sep 17 00:00:00 2001
From: Melanie Lipp <melanie.lipp@iws.uni-stuttgart.de>
Date: Wed, 25 Aug 2021 12:53:10 +0200
Subject: [PATCH] [test][freeflow] Move createAnalyticalSolution to file and
 reuse for several tests. Change signature of analyticalSolution to include
 time for angeli test.

---
 .../navierstokes/analyticalsolutionvectors.hh | 158 ++++++++++++++++++
 test/freeflow/navierstokes/angeli/main.cc     |  74 ++------
 test/freeflow/navierstokes/angeli/problem.hh  |  29 ++--
 test/freeflow/navierstokes/channel/1d/main.cc |  17 +-
 .../navierstokes/channel/1d/problem.hh        |  67 --------
 test/freeflow/navierstokes/channel/2d/main.cc |  17 +-
 .../navierstokes/channel/2d/problem.hh        |  69 --------
 test/freeflow/navierstokes/donea/main.cc      |  17 +-
 test/freeflow/navierstokes/donea/problem.hh   |  67 --------
 test/freeflow/navierstokes/kovasznay/main.cc  |  17 +-
 .../navierstokes/kovasznay/problem.hh         |  71 --------
 test/freeflow/navierstokes/sincos/main.cc     |  79 +++------
 test/freeflow/navierstokes/sincos/problem.hh  |   2 -
 13 files changed, 267 insertions(+), 417 deletions(-)
 create mode 100644 test/freeflow/navierstokes/analyticalsolutionvectors.hh

diff --git a/test/freeflow/navierstokes/analyticalsolutionvectors.hh b/test/freeflow/navierstokes/analyticalsolutionvectors.hh
new file mode 100644
index 0000000000..fa41298e19
--- /dev/null
+++ b/test/freeflow/navierstokes/analyticalsolutionvectors.hh
@@ -0,0 +1,158 @@
+// -*- 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 NavierStokesTests
+ * \copydoc Dumux::NavierStokesAnalyticalSolutionVectors
+ */
+#ifndef DUMUX_TEST_ANALYTICALSOLVECTORS_HH
+#define DUMUX_TEST_ANALYTICALSOLVECTORS_HH
+
+namespace Dumux {
+/*!
+ * \ingroup NavierStokesTests
+ * \brief Creates and provides the analytical solution in a form that can be written into the vtk output files
+ */
+template<class Scalar, class FaceSolutionVector, class CellCenterSolutionVector, class GridGeometry, class Problem, class Indices>
+class NavierStokesAnalyticalSolutionVectors
+{
+    using GridView = typename GridGeometry::GridView;
+    using FVElementGeometry = typename GridGeometry::LocalView;
+    using SubControlVolume = typename FVElementGeometry::SubControlVolume;
+    using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace;
+    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
+    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
+
+public:
+    NavierStokesAnalyticalSolutionVectors(std::shared_ptr<const Problem> problem)
+    : problem_(problem)
+    { }
+
+    /*!
+     * \brief Creates the instationary analytical solution in a form that can be written into the vtk output files
+     */
+    void update(Scalar time)
+    {
+        auto instationaryAnalyticalPressureSolution = [&](const SubControlVolume& scv)
+        {
+            return problem_->analyticalSolution(scv.dofPosition(), time)[Indices::pressureIdx];
+        };
+
+        auto instationaryAnalyticalVelocitySolution = [&](const SubControlVolumeFace& scvf)
+        {
+            return problem_->analyticalSolution(scvf.center(), time)[Indices::velocity(scvf.directionIndex())];
+        };
+
+        auto instationaryAnalyticalVelocitySolutionAtScvCenter = [&](const SubControlVolume& scv)
+        {
+            return problem_->analyticalSolution(scv.center(), time);
+        };
+
+        createAnalyticalSolution_(instationaryAnalyticalPressureSolution, instationaryAnalyticalVelocitySolution, instationaryAnalyticalVelocitySolutionAtScvCenter);
+    }
+
+    /*!
+     * \brief Creates the stationary analytical solution in a form that can be written into the vtk output files
+     */
+    void update()
+    {
+        auto analyticalPressureSolution = [&](const SubControlVolume& scv)
+        {
+            return problem_->analyticalSolution(scv.dofPosition())[Indices::pressureIdx];
+        };
+
+        auto analyticalVelocitySolution = [&](const SubControlVolumeFace& scvf)
+        {
+            return problem_->analyticalSolution(scvf.center())[Indices::velocity(scvf.directionIndex())];
+        };
+
+        auto analyticalVelocitySolutionAtScvCenter = [&](const SubControlVolume& scv)
+        {
+            return problem_->analyticalSolution(scv.center());
+        };
+
+        createAnalyticalSolution_(analyticalPressureSolution, analyticalVelocitySolution, analyticalVelocitySolutionAtScvCenter);
+    }
+
+    /*!
+     * \brief Returns the analytical solution for the pressure
+     */
+    auto& getAnalyticalPressureSolution() const
+    {
+        return analyticalPressure_;
+    }
+
+   /*!
+     * \brief Returns the analytical solution for the velocity
+     */
+    auto& getAnalyticalVelocitySolution() const
+    {
+        return analyticalVelocity_;
+    }
+
+   /*!
+     * \brief Returns the analytical solution for the velocity at the faces
+     */
+    auto& getAnalyticalVelocitySolutionOnFace() const
+    {
+        return analyticalVelocityOnFace_;
+    }
+
+private:
+    /*!
+     * \brief Creates the analytical solution in a form that can be written into the vtk output files
+     */
+    template <class LambdaA, class LambdaB, class LambdaC>
+    void createAnalyticalSolution_(const LambdaA& analyticalPressureSolution,
+                                  const LambdaB& analyticalVelocitySolution,
+                                  const LambdaC& analyticalVelocitySolutionAtScvCenter)
+    {
+        analyticalPressure_.resize((problem_->gridGeometry()).numCellCenterDofs());
+        analyticalVelocity_.resize((problem_->gridGeometry()).numCellCenterDofs());
+        analyticalVelocityOnFace_.resize((problem_->gridGeometry()).numFaceDofs());
+
+        for (const auto& element : elements((problem_->gridGeometry()).gridView()))
+        {
+            auto fvGeometry = localView((problem_->gridGeometry()));
+            fvGeometry.bindElement(element);
+            for (auto&& scv : scvs(fvGeometry))
+            {
+                // velocities on faces
+                for (auto&& scvf : scvfs(fvGeometry))
+                {
+                    analyticalVelocityOnFace_[scvf.dofIndex()][scvf.directionIndex()] = analyticalVelocitySolution(scvf);
+                }
+
+                analyticalPressure_[scv.dofIndex()] = analyticalPressureSolution(scv);
+
+                for(int dirIdx = 0; dirIdx < dimWorld; ++dirIdx)
+                    analyticalVelocity_[scv.dofIndex()][dirIdx] = analyticalVelocitySolutionAtScvCenter(scv)[Indices::velocity(dirIdx)];
+            }
+        }
+     }
+
+    std::shared_ptr<const Problem> problem_;
+
+    CellCenterSolutionVector analyticalPressure_;
+    std::vector<VelocityVector> analyticalVelocity_;
+    std::vector<VelocityVector> analyticalVelocityOnFace_;
+};
+} // end namespace Dumux
+
+#endif
diff --git a/test/freeflow/navierstokes/angeli/main.cc b/test/freeflow/navierstokes/angeli/main.cc
index 6233bb7531..d1769df4af 100644
--- a/test/freeflow/navierstokes/angeli/main.cc
+++ b/test/freeflow/navierstokes/angeli/main.cc
@@ -46,60 +46,7 @@
 
 #include "properties.hh"
 
-/*!
-* \brief Creates analytical solution.
-* Returns a tuple of the analytical solution for the pressure, the velocity and the velocity at the faces
-* \param problem the problem for which to evaluate the analytical solution
-*/
-template<class Problem, class Scalar = double>
-auto createAnalyticalSolution(const Problem& problem)
-{
-    const auto& gridGeometry = problem.gridGeometry();
-    using GridView = typename std::decay_t<decltype(gridGeometry)>::GridView;
-
-    static constexpr auto dim = GridView::dimension;
-    static constexpr auto dimWorld = GridView::dimensionworld;
-
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
-
-    std::vector<Scalar> analyticalPressure;
-    std::vector<VelocityVector> analyticalVelocity;
-    std::vector<VelocityVector> analyticalVelocityOnFace;
-
-    analyticalPressure.resize(gridGeometry.numCellCenterDofs());
-    analyticalVelocity.resize(gridGeometry.numCellCenterDofs());
-    analyticalVelocityOnFace.resize(gridGeometry.numFaceDofs());
-
-    using Indices = typename Problem::Indices;
-    auto fvGeometry = localView(gridGeometry);
-    for (const auto& element : elements(gridGeometry.gridView()))
-    {
-        fvGeometry.bindElement(element);
-        for (auto&& scv : scvs(fvGeometry))
-        {
-            auto ccDofIdx = scv.dofIndex();
-            auto ccDofPosition = scv.dofPosition();
-            auto analyticalSolutionAtCc = problem.analyticalSolution(ccDofPosition);
-
-            // velocities on faces
-            for (auto&& scvf : scvfs(fvGeometry))
-            {
-                const auto faceDofIdx = scvf.dofIndex();
-                const auto faceDofPosition = scvf.center();
-                const auto dirIdx = scvf.directionIndex();
-                const auto analyticalSolutionAtFace = problem.analyticalSolution(faceDofPosition);
-                analyticalVelocityOnFace[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-            }
-
-            analyticalPressure[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-            for(int dirIdx = 0; dirIdx < dim; ++dirIdx)
-                analyticalVelocity[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-        }
-    }
-
-    return std::make_tuple(analyticalPressure, analyticalVelocity, analyticalVelocityOnFace);
-}
+#include "../analyticalsolutionvectors.hh"
 
 int main(int argc, char** argv)
 {
@@ -162,15 +109,22 @@ int main(int argc, char** argv)
     auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
     gridVariables->init(x);
 
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
+    Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+    analyticalSolVectors.update(tStart);
+
     // initialize the vtk output module
     StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
     using IOFields = GetPropType<TypeTag, Properties::IOFields>;
     IOFields::initOutputModule(vtkWriter); // Add model specific output fields
-
-    auto analyticalSolution = createAnalyticalSolution(*problem);
-    vtkWriter.addField(std::get<0>(analyticalSolution), "pressureExact");
-    vtkWriter.addField(std::get<1>(analyticalSolution), "velocityExact");
-    vtkWriter.addFaceField(std::get<2>(analyticalSolution), "faceVelocityExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
     vtkWriter.write(0.0);
 
     // the assembler with time loop for instationary problem
@@ -218,7 +172,7 @@ int main(int argc, char** argv)
         }
 
         // update the analytical solution
-        analyticalSolution = createAnalyticalSolution(*problem);
+        analyticalSolVectors.update(timeLoop->time() + timeLoop->timeStepSize());
 
         // advance to the time loop to the next step
         timeLoop->advanceTimeStep();
diff --git a/test/freeflow/navierstokes/angeli/problem.hh b/test/freeflow/navierstokes/angeli/problem.hh
index 2ae0f471cf..b8117d40c4 100644
--- a/test/freeflow/navierstokes/angeli/problem.hh
+++ b/test/freeflow/navierstokes/angeli/problem.hh
@@ -62,12 +62,10 @@ class AngeliTestProblem : public NavierStokesProblem<TypeTag>
     using Scalar = GetPropType<TypeTag, Properties::Scalar>;
     using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
 
-    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
     using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using SubControlVolume = typename GridGeometry::SubControlVolume;
     using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
 public:
     using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
@@ -79,6 +77,7 @@ public:
         rho_ = getParam<Scalar>("Component.LiquidDensity", 1.0);
         useVelocityAveragingForDirichlet_ = getParam<bool>("Problem.UseVelocityAveragingForDirichlet", false);
         useVelocityAveragingForInitial_ = getParam<bool>("Problem.UseVelocityAveragingForInitial", false);
+        tStart_ = getParam<Scalar>("TimeLoop.TStart", 0.0);
     }
 
     /*!
@@ -143,9 +142,9 @@ public:
     PrimaryVariables dirichlet(const Element& element, const SubControlVolumeFace& scvf) const
     {
         if (useVelocityAveragingForDirichlet_)
-            return averagedVelocity_(scvf);
+            return averagedVelocity_(scvf, time_);
         else
-            return analyticalSolution(scvf.center());
+            return analyticalSolution(scvf.center(), time_);
     }
 
     /*!
@@ -158,7 +157,7 @@ public:
     PrimaryVariables dirichlet(const Element& element, const SubControlVolume& scv) const
     {
         PrimaryVariables priVars(0.0);
-        priVars[Indices::pressureIdx] = analyticalSolution(scv.center())[Indices::pressureIdx];
+        priVars[Indices::pressureIdx] = analyticalSolution(scv.center(), time_)[Indices::pressureIdx];
         return priVars;
     }
 
@@ -166,11 +165,10 @@ public:
      * \brief Returns the analytical solution of the problem at a given time and position.
      * \param globalPos The global position
      */
-    PrimaryVariables analyticalSolution(const GlobalPosition& globalPos) const
+    PrimaryVariables analyticalSolution(const GlobalPosition& globalPos, Scalar t) const
     {
         const Scalar x = globalPos[0];
         const Scalar y = globalPos[1];
-        const Scalar t = time_;
 
         PrimaryVariables values;
 
@@ -181,6 +179,12 @@ public:
         return values;
     }
 
+    //TODO remove as soon as error calculation is able to deal with time-dependent analytical solution
+    PrimaryVariables analyticalSolution(const GlobalPosition& globalPos) const
+    {
+        return analyticalSolution(globalPos, time_);
+    }
+
     // \}
 
     /*!
@@ -194,7 +198,7 @@ public:
     PrimaryVariables initial(const SubControlVolume& scv) const
     {
         PrimaryVariables priVars(0.0);
-        priVars[Indices::pressureIdx] = analyticalSolution(scv.center())[Indices::pressureIdx];
+        priVars[Indices::pressureIdx] = analyticalSolution(scv.center(), tStart_)[Indices::pressureIdx];
         return priVars;
     }
 
@@ -211,9 +215,9 @@ public:
     PrimaryVariables initial(const SubControlVolumeFace& scvf) const
     {
         if (useVelocityAveragingForInitial_)
-            return averagedVelocity_(scvf);
+            return averagedVelocity_(scvf, tStart_);
         else
-            return analyticalSolution(scvf.center());
+            return analyticalSolution(scvf.center(), tStart_);
     }
 
     // \}
@@ -227,7 +231,7 @@ public:
     }
 
 private:
-    PrimaryVariables averagedVelocity_(const SubControlVolumeFace& scvf) const
+    PrimaryVariables averagedVelocity_(const SubControlVolumeFace& scvf, Scalar t) const
     {
         PrimaryVariables priVars(0.0);
         const auto geo = scvf.geometry();
@@ -236,7 +240,7 @@ private:
         {
             const auto w = qp.weight()*geo.integrationElement(qp.position());
             const auto globalPos = geo.global(qp.position());
-            const auto sol = analyticalSolution(globalPos);
+            const auto sol = analyticalSolution(globalPos, t);
             priVars[Indices::velocityXIdx] += sol[Indices::velocityXIdx]*w;
             priVars[Indices::velocityYIdx] += sol[Indices::velocityYIdx]*w;
         }
@@ -249,6 +253,7 @@ private:
     Scalar time_ = 0;
     bool useVelocityAveragingForDirichlet_;
     bool useVelocityAveragingForInitial_;
+    Scalar tStart_;
 };
 } // end namespace Dumux
 
diff --git a/test/freeflow/navierstokes/channel/1d/main.cc b/test/freeflow/navierstokes/channel/1d/main.cc
index f1aa15c446..7e8521fac5 100644
--- a/test/freeflow/navierstokes/channel/1d/main.cc
+++ b/test/freeflow/navierstokes/channel/1d/main.cc
@@ -44,6 +44,8 @@
 
 #include "properties.hh"
 
+#include "../../analyticalsolutionvectors.hh"
+
 int main(int argc, char** argv)
 {
     using namespace Dumux;
@@ -91,13 +93,22 @@ int main(int argc, char** argv)
     auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
     gridVariables->init(x);
 
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
+    Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+    analyticalSolVectors.update();
+
     // intialize the vtk output module
     using IOFields = GetPropType<TypeTag, Properties::IOFields>;
     StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
     IOFields::initOutputModule(vtkWriter); // Add model specific output fields
-    vtkWriter.addField(problem->getAnalyticalPressureSolution(), "pressureExact");
-    vtkWriter.addField(problem->getAnalyticalVelocitySolution(), "velocityExact");
-    vtkWriter.addFaceField(problem->getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
     vtkWriter.write(0.0);
 
     // the assembler with time loop for instationary problem
diff --git a/test/freeflow/navierstokes/channel/1d/problem.hh b/test/freeflow/navierstokes/channel/1d/problem.hh
index 19dc7edbf1..66b9823b1e 100644
--- a/test/freeflow/navierstokes/channel/1d/problem.hh
+++ b/test/freeflow/navierstokes/channel/1d/problem.hh
@@ -73,7 +73,6 @@ public:
         printL2Error_ = getParam<bool>("Problem.PrintL2Error");
         density_ = getParam<Scalar>("Component.LiquidDensity");
         kinematicViscosity_ = getParam<Scalar>("Component.LiquidKinematicViscosity");
-        createAnalyticalSolution_();
     }
 
    /*!
@@ -285,76 +284,10 @@ public:
         return analyticalSolution(globalPos);
     }
 
-   /*!
-     * \brief Returns the analytical solution for the pressure
-     */
-    auto& getAnalyticalPressureSolution() const
-    {
-        return analyticalPressure_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity.
-     */
-    auto& getAnalyticalVelocitySolution() const
-    {
-        return analyticalVelocity_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity at the faces.
-     */
-    auto& getAnalyticalVelocitySolutionOnFace() const
-    {
-        return analyticalVelocityOnFace_;
-    }
-
 private:
-
-   /*!
-     * \brief Adds additional VTK output data to the VTKWriter.
-     *
-     * Function is called by the output module on every write.
-     */
-    void createAnalyticalSolution_()
-    {
-        analyticalPressure_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocity_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocityOnFace_.resize(this->gridGeometry().numFaceDofs());
-        auto fvGeometry = localView(this->gridGeometry());
-        for (const auto& element : elements(this->gridGeometry().gridView()))
-        {
-            fvGeometry.bindElement(element);
-            for (auto&& scv : scvs(fvGeometry))
-            {
-                const auto ccDofIdx = scv.dofIndex();
-                const auto ccDofPosition = scv.dofPosition();
-                const auto analyticalSolutionAtCc = analyticalSolution(ccDofPosition);
-
-                // velocities on faces
-                for (auto&& scvf : scvfs(fvGeometry))
-                {
-                    const auto faceDofIdx = scvf.dofIndex();
-                    const auto faceDofPosition = scvf.center();
-                    const auto dirIdx = scvf.directionIndex();
-                    const auto analyticalSolutionAtFace = analyticalSolution(faceDofPosition);
-                    analyticalVelocityOnFace_[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-                }
-
-                analyticalPressure_[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-                for(int dirIdx = 0; dirIdx < ModelTraits::dim(); ++dirIdx)
-                    analyticalVelocity_[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-            }
-        }
-     }
-
     bool printL2Error_;
     Scalar density_;
     Scalar kinematicViscosity_;
-    std::vector<Scalar> analyticalPressure_;
-    std::vector<GlobalPosition> analyticalVelocity_;
-    std::vector<GlobalPosition> analyticalVelocityOnFace_;
 };
 } // end namespace Dumux
 
diff --git a/test/freeflow/navierstokes/channel/2d/main.cc b/test/freeflow/navierstokes/channel/2d/main.cc
index 2c180790aa..f0e9352d90 100644
--- a/test/freeflow/navierstokes/channel/2d/main.cc
+++ b/test/freeflow/navierstokes/channel/2d/main.cc
@@ -45,6 +45,8 @@
 
 #include "properties.hh"
 
+#include "../../analyticalsolutionvectors.hh"
+
 int main(int argc, char** argv)
 {
     using namespace Dumux;
@@ -108,6 +110,12 @@ int main(int argc, char** argv)
     auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
     gridVariables->init(x);
 
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
     // initialize the vtk output module
     using IOFields = GetPropType<TypeTag, Properties::IOFields>;
     StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
@@ -117,9 +125,12 @@ int main(int argc, char** argv)
 
     if (problem->hasAnalyticalSolution())
     {
-        vtkWriter.addField(problem->getAnalyticalPressureSolution(), "pressureExact");
-        vtkWriter.addField(problem->getAnalyticalVelocitySolution(), "velocityExact");
-        vtkWriter.addFaceField(problem->getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+        Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+        analyticalSolVectors.update();
+
+        vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+        vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+        vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
     }
 
     vtkWriter.write(restartTime);
diff --git a/test/freeflow/navierstokes/channel/2d/problem.hh b/test/freeflow/navierstokes/channel/2d/problem.hh
index 96122cf361..a39a7b46eb 100644
--- a/test/freeflow/navierstokes/channel/2d/problem.hh
+++ b/test/freeflow/navierstokes/channel/2d/problem.hh
@@ -66,8 +66,6 @@ class ChannelTestProblem : public NavierStokesProblem<TypeTag>
 
     using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
-    static constexpr auto dimWorld = GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimensionworld;
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
     using TimeLoopPtr = std::shared_ptr<CheckPointTimeLoop<Scalar>>;
 
@@ -102,8 +100,6 @@ public:
         const bool isStationary = getParam<bool>("Problem.IsStationary", false);
 
         hasAnalyticalSolution_ = isStationary && useVelocityProfile_ && (outletCondition_ != OutletCondition::doNothing);
-        if (hasAnalyticalSolution_)
-            createAnalyticalSolution_();
     }
 
    /*!
@@ -338,73 +334,12 @@ public:
         return timeLoop_->time();
     }
 
-/*!
-     * \brief Returns the analytical solution for the pressure
-     */
-    auto& getAnalyticalPressureSolution() const
-    {
-        return analyticalPressure_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity
-     */
-    auto& getAnalyticalVelocitySolution() const
-    {
-        return analyticalVelocity_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity at the faces
-     */
-    auto& getAnalyticalVelocitySolutionOnFace() const
-    {
-        return analyticalVelocityOnFace_;
-    }
-
     bool hasAnalyticalSolution()
     {
         return hasAnalyticalSolution_;
     }
 
 private:
-
-   /*!
-     * \brief Adds additional VTK output data to the VTKWriter. Function is called by the output module on every write.
-     */
-    void createAnalyticalSolution_()
-    {
-        analyticalPressure_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocity_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocityOnFace_.resize(this->gridGeometry().numFaceDofs());
-        auto fvGeometry = localView(this->gridGeometry());
-        for (const auto& element : elements(this->gridGeometry().gridView()))
-        {
-            fvGeometry.bindElement(element);
-            for (auto&& scv : scvs(fvGeometry))
-            {
-                auto ccDofIdx = scv.dofIndex();
-                auto ccDofPosition = scv.dofPosition();
-                auto analyticalSolutionAtCc = analyticalSolution(ccDofPosition);
-
-                // velocities on faces
-                for (auto&& scvf : scvfs(fvGeometry))
-                {
-                    const auto faceDofIdx = scvf.dofIndex();
-                    const auto faceDofPosition = scvf.center();
-                    const auto dirIdx = scvf.directionIndex();
-                    const auto analyticalSolutionAtFace = analyticalSolution(faceDofPosition);
-                    analyticalVelocityOnFace_[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-                }
-
-                analyticalPressure_[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-                for(int dirIdx = 0; dirIdx < ModelTraits::dim(); ++dirIdx)
-                    analyticalVelocity_[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-            }
-        }
-     }
-
     bool isInlet_(const GlobalPosition& globalPos) const
     {
         return globalPos[0] < eps_;
@@ -424,10 +359,6 @@ private:
     bool useVelocityProfile_;
     bool hasAnalyticalSolution_;
     TimeLoopPtr timeLoop_;
-
-    std::vector<Scalar> analyticalPressure_;
-    std::vector<VelocityVector> analyticalVelocity_;
-    std::vector<VelocityVector> analyticalVelocityOnFace_;
 };
 } // end namespace Dumux
 
diff --git a/test/freeflow/navierstokes/donea/main.cc b/test/freeflow/navierstokes/donea/main.cc
index 177bb4f434..73bddb0732 100644
--- a/test/freeflow/navierstokes/donea/main.cc
+++ b/test/freeflow/navierstokes/donea/main.cc
@@ -45,6 +45,8 @@
 
 #include "properties.hh"
 
+#include "../analyticalsolutionvectors.hh"
+
 int main(int argc, char** argv)
 {
     using namespace Dumux;
@@ -93,13 +95,22 @@ int main(int argc, char** argv)
     auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
     gridVariables->init(x);
 
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
+    Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+    analyticalSolVectors.update();
+
     // intialize the vtk output module
     using IOFields = GetPropType<TypeTag, Properties::IOFields>;
     StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
     IOFields::initOutputModule(vtkWriter); // Add model specific output fields
-    vtkWriter.addField(problem->getAnalyticalPressureSolution(), "pressureExact");
-    vtkWriter.addField(problem->getAnalyticalVelocitySolution(), "velocityExact");
-    vtkWriter.addFaceField(problem->getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
     vtkWriter.write(0.0);
 
     // use the staggered FV assembler
diff --git a/test/freeflow/navierstokes/donea/problem.hh b/test/freeflow/navierstokes/donea/problem.hh
index e61ac2b385..7d8fb0ce0e 100644
--- a/test/freeflow/navierstokes/donea/problem.hh
+++ b/test/freeflow/navierstokes/donea/problem.hh
@@ -57,17 +57,14 @@ class DoneaTestProblem : public NavierStokesProblem<TypeTag>
     using Scalar = GetPropType<TypeTag, Properties::Scalar>;
     using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
 
-    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
     using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
 public:
     DoneaTestProblem(std::shared_ptr<const GridGeometry> gridGeometry)
     : ParentType(gridGeometry)
     {
         printL2Error_ = getParam<bool>("Problem.PrintL2Error");
-        createAnalyticalSolution_();
         mu_ = getParam<Scalar>("Component.LiquidKinematicViscosity", 1.0);
     }
 
@@ -210,30 +207,6 @@ public:
         return values;
     }
 
-   /*!
-     * \brief Returns the analytical solution for the pressure
-     */
-    auto& getAnalyticalPressureSolution() const
-    {
-        return analyticalPressure_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity
-     */
-    auto& getAnalyticalVelocitySolution() const
-    {
-        return analyticalVelocity_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity at the faces
-     */
-    auto& getAnalyticalVelocitySolutionOnFace() const
-    {
-        return analyticalVelocityOnFace_;
-    }
-
 private:
     Scalar f1_(Scalar x) const
     { return x*(1.0-x); /*x - x^2*/ }
@@ -283,47 +256,7 @@ private:
     Scalar dxxV_ (Scalar x, Scalar y) const
     { return -f2_(y)*dddf2_(x); }
 
-   /*!
-     * \brief Adds additional VTK output data to the VTKWriter. Function is called by the output module on every write.
-     */
-    void createAnalyticalSolution_()
-    {
-        analyticalPressure_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocity_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocityOnFace_.resize(this->gridGeometry().numFaceDofs());
-
-        auto fvGeometry = localView(this->gridGeometry());
-        for (const auto& element : elements(this->gridGeometry().gridView()))
-        {
-            fvGeometry.bindElement(element);
-            for (auto&& scv : scvs(fvGeometry))
-            {
-                const auto ccDofIdx = scv.dofIndex();
-                const auto ccDofPosition = scv.dofPosition();
-                const auto analyticalSolutionAtCc = analyticalSolution(ccDofPosition);
-
-                // velocities on faces
-                for (auto&& scvf : scvfs(fvGeometry))
-                {
-                    const auto faceDofIdx = scvf.dofIndex();
-                    const auto faceDofPosition = scvf.center();
-                    const auto dirIdx = scvf.directionIndex();
-                    const auto analyticalSolutionAtFace = analyticalSolution(faceDofPosition);
-                    analyticalVelocityOnFace_[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-                }
-
-                analyticalPressure_[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-                for(int dirIdx = 0; dirIdx < ModelTraits::dim(); ++dirIdx)
-                    analyticalVelocity_[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-            }
-        }
-     }
-
     bool printL2Error_;
-    std::vector<Scalar> analyticalPressure_;
-    std::vector<VelocityVector> analyticalVelocity_;
-    std::vector<VelocityVector> analyticalVelocityOnFace_;
     Scalar mu_;
 };
 } // end namespace Dumux
diff --git a/test/freeflow/navierstokes/kovasznay/main.cc b/test/freeflow/navierstokes/kovasznay/main.cc
index c9af40ea99..0b3f316cb1 100644
--- a/test/freeflow/navierstokes/kovasznay/main.cc
+++ b/test/freeflow/navierstokes/kovasznay/main.cc
@@ -46,6 +46,8 @@
 
 #include "properties.hh"
 
+#include "../analyticalsolutionvectors.hh"
+
 int main(int argc, char** argv)
 {
     using namespace Dumux;
@@ -121,13 +123,22 @@ int main(int argc, char** argv)
     auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
     gridVariables->init(x);
 
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
+    Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+    analyticalSolVectors.update();
+
     // intialize the vtk output module
     using IOFields = GetPropType<TypeTag, Properties::IOFields>;
     StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
     IOFields::initOutputModule(vtkWriter); // Add model specific output fields
-    vtkWriter.addField(problem->getAnalyticalPressureSolution(), "pressureExact");
-    vtkWriter.addField(problem->getAnalyticalVelocitySolution(), "velocityExact");
-    vtkWriter.addFaceField(problem->getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
     vtkWriter.write(0.0);
 
     // the assembler with time loop for instationary problem
diff --git a/test/freeflow/navierstokes/kovasznay/problem.hh b/test/freeflow/navierstokes/kovasznay/problem.hh
index 748d6b4654..a8bc9b170c 100644
--- a/test/freeflow/navierstokes/kovasznay/problem.hh
+++ b/test/freeflow/navierstokes/kovasznay/problem.hh
@@ -60,10 +60,8 @@ class KovasznayTestProblem : public NavierStokesProblem<TypeTag>
     using Scalar = GetPropType<TypeTag, Properties::Scalar>;
     using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
 
-    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
     using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
     static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
 
@@ -78,8 +76,6 @@ public:
         Scalar reynoldsNumber = 1.0 / kinematicViscosity_;
         lambda_ = 0.5 * reynoldsNumber
                         - std::sqrt(reynoldsNumber * reynoldsNumber * 0.25 + 4.0 * M_PI * M_PI);
-
-        createAnalyticalSolution_();
     }
 
    /*!
@@ -214,80 +210,13 @@ public:
         return values;
     }
 
-   /*!
-     * \brief Returns the analytical solution for the pressure.
-     */
-    auto& getAnalyticalPressureSolution() const
-    {
-        return analyticalPressure_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity
-     */
-    auto& getAnalyticalVelocitySolution() const
-    {
-        return analyticalVelocity_;
-    }
-
-   /*!
-     * \brief Returns the analytical solution for the velocity at the faces.
-     */
-    auto& getAnalyticalVelocitySolutionOnFace() const
-    {
-        return analyticalVelocityOnFace_;
-    }
-
 private:
-
-   /*!
-     * \brief Adds additional VTK output data to the VTKWriter.
-     *
-     * Function is called by the output module on every write.
-     */
-    void createAnalyticalSolution_()
-    {
-        analyticalPressure_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocity_.resize(this->gridGeometry().numCellCenterDofs());
-        analyticalVelocityOnFace_.resize(this->gridGeometry().numFaceDofs());
-
-        auto fvGeometry = localView(this->gridGeometry());
-        for (const auto& element : elements(this->gridGeometry().gridView()))
-        {
-            fvGeometry.bindElement(element);
-            for (auto&& scv : scvs(fvGeometry))
-            {
-                const auto ccDofIdx = scv.dofIndex();
-                const auto ccDofPosition = scv.dofPosition();
-                const auto analyticalSolutionAtCc = analyticalSolution(ccDofPosition);
-
-                // velocities on faces
-                for (auto&& scvf : scvfs(fvGeometry))
-                {
-                    const auto faceDofIdx = scvf.dofIndex();
-                    const auto faceDofPosition = scvf.center();
-                    const auto dirIdx = scvf.directionIndex();
-                    const auto analyticalSolutionAtFace = analyticalSolution(faceDofPosition);
-                    analyticalVelocityOnFace_[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-                }
-
-                analyticalPressure_[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-                for(int dirIdx = 0; dirIdx < ModelTraits::dim(); ++dirIdx)
-                    analyticalVelocity_[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-            }
-        }
-    }
-
     static constexpr Scalar eps_=1e-6;
 
     Scalar rho_;
     Scalar kinematicViscosity_;
     Scalar lambda_;
     bool printL2Error_;
-    std::vector<Scalar> analyticalPressure_;
-    std::vector<VelocityVector> analyticalVelocity_;
-    std::vector<VelocityVector> analyticalVelocityOnFace_;
 };
 } // end namespace Dumux
 
diff --git a/test/freeflow/navierstokes/sincos/main.cc b/test/freeflow/navierstokes/sincos/main.cc
index 90b37841fc..c965dc6c25 100644
--- a/test/freeflow/navierstokes/sincos/main.cc
+++ b/test/freeflow/navierstokes/sincos/main.cc
@@ -48,62 +48,14 @@
 
 #include "properties.hh"
 
+#include "../analyticalsolutionvectors.hh"
+
 /*!
 * \brief Creates analytical solution.
 * Returns a tuple of the analytical solution for the pressure, the velocity and the velocity at the faces
 * \param time the time at which to evaluate the analytical solution
 * \param problem the problem for which to evaluate the analytical solution
 */
-template<class Scalar, class Problem>
-auto createAnalyticalSolution(const Scalar time, const Problem& problem)
-{
-    const auto& gridGeometry = problem.gridGeometry();
-    using GridView = typename std::decay_t<decltype(gridGeometry)>::GridView;
-
-    static constexpr auto dim = GridView::dimension;
-    static constexpr auto dimWorld = GridView::dimensionworld;
-
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
-
-    std::vector<Scalar> analyticalPressure;
-    std::vector<VelocityVector> analyticalVelocity;
-    std::vector<VelocityVector> analyticalVelocityOnFace;
-
-    analyticalPressure.resize(gridGeometry.numCellCenterDofs());
-    analyticalVelocity.resize(gridGeometry.numCellCenterDofs());
-    analyticalVelocityOnFace.resize(gridGeometry.numFaceDofs());
-
-    using Indices = typename Problem::Indices;
-    auto fvGeometry = localView(gridGeometry);
-    for (const auto& element : elements(gridGeometry.gridView()))
-    {
-        fvGeometry.bindElement(element);
-        for (auto&& scv : scvs(fvGeometry))
-        {
-            const auto ccDofIdx = scv.dofIndex();
-            const auto ccDofPosition = scv.dofPosition();
-            const auto analyticalSolutionAtCc = problem.analyticalSolution(ccDofPosition, time);
-
-            // velocities on faces
-            for (auto&& scvf : scvfs(fvGeometry))
-            {
-                const auto faceDofIdx = scvf.dofIndex();
-                const auto faceDofPosition = scvf.center();
-                const auto dirIdx = scvf.directionIndex();
-                const auto analyticalSolutionAtFace = problem.analyticalSolution(faceDofPosition, time);
-                analyticalVelocityOnFace[faceDofIdx][dirIdx] = analyticalSolutionAtFace[Indices::velocity(dirIdx)];
-            }
-
-            analyticalPressure[ccDofIdx] = analyticalSolutionAtCc[Indices::pressureIdx];
-
-            for(int dirIdx = 0; dirIdx < dim; ++dirIdx)
-                analyticalVelocity[ccDofIdx][dirIdx] = analyticalSolutionAtCc[Indices::velocity(dirIdx)];
-        }
-    }
-
-    return std::make_tuple(analyticalPressure, analyticalVelocity, analyticalVelocityOnFace);
-}
-
 template<class Problem>
 auto createSource(const Problem& problem)
 {
@@ -237,13 +189,26 @@ int main(int argc, char** argv)
     vtkWriter.addField(sourceX, "sourceX");
     vtkWriter.addField(sourceY, "sourceY");
 
-    auto analyticalSolution = createAnalyticalSolution(timeLoop->time(), *problem);
-    vtkWriter.addField(std::get<0>(analyticalSolution), "pressureExact");
-    vtkWriter.addField(std::get<1>(analyticalSolution), "velocityExact");
-    vtkWriter.addFaceField(std::get<2>(analyticalSolution), "faceVelocityExact");
+    const bool isStationary = getParam<bool>("Problem.IsStationary");
+
+    // create analytical solution vectors
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using FaceSolutionVector = GetPropType<TypeTag, Properties::FaceSolutionVector>;
+    using CellCenterSolutionVector = GetPropType<TypeTag, Properties::CellCenterSolutionVector>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+
+    Dumux::NavierStokesAnalyticalSolutionVectors<Scalar, FaceSolutionVector, CellCenterSolutionVector, GridGeometry, Problem, Indices> analyticalSolVectors(problem);
+    if (isStationary)
+        analyticalSolVectors.update();
+    else
+        analyticalSolVectors.update(0.0);
+
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
+    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+
     vtkWriter.write(0.0);
 
-    const bool isStationary = getParam<bool>("Problem.IsStationary");
 
     // the assembler with time loop for instationary problem
     using Assembler = StaggeredFVAssembler<TypeTag, DiffMethod::numeric>;
@@ -272,7 +237,7 @@ int main(int argc, char** argv)
         }
 
         // write vtk output
-        analyticalSolution = createAnalyticalSolution(timeLoop->time(), *problem);
+        analyticalSolVectors.update();
         vtkWriter.write(1.0);
 
         timer.stop();
@@ -302,7 +267,7 @@ int main(int argc, char** argv)
             // advance to the time loop to the next step
             timeLoop->advanceTimeStep();
             problem->updateTime(timeLoop->time());
-            analyticalSolution = createAnalyticalSolution(timeLoop->time(), *problem);
+            analyticalSolVectors.update(timeLoop->time());
 
             // write vtk output
             vtkWriter.write(timeLoop->time());
diff --git a/test/freeflow/navierstokes/sincos/problem.hh b/test/freeflow/navierstokes/sincos/problem.hh
index d197933e7d..3c43fbea8f 100644
--- a/test/freeflow/navierstokes/sincos/problem.hh
+++ b/test/freeflow/navierstokes/sincos/problem.hh
@@ -58,10 +58,8 @@ class SincosTestProblem : public NavierStokesProblem<TypeTag>
     using SubControlVolume = typename GridGeometry::SubControlVolume;
     using FVElementGeometry = typename GridGeometry::LocalView;
 
-    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
     using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
-    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
 
 public:
     using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>;
-- 
GitLab