From 902a81689ec546730eac3d3a6be4dc5909bc88da Mon Sep 17 00:00:00 2001
From: Timo Koch <timo.koch@iws.uni-stuttgart.de>
Date: Wed, 10 Jun 2020 18:57:18 +0200
Subject: [PATCH] [ff][test] Add rotational-symmetric pipe flow test

---
 .../navierstokes/channel/CMakeLists.txt       |   1 +
 .../navierstokes/channel/pipe/CMakeLists.txt  |   8 +
 .../channel/pipe/convergencetest.py           |  81 +++++++++
 .../navierstokes/channel/pipe/main.cc         | 159 ++++++++++++++++++
 .../navierstokes/channel/pipe/params.input    |  22 +++
 .../navierstokes/channel/pipe/problem.hh      | 150 +++++++++++++++++
 .../navierstokes/channel/pipe/properties.hh   |  83 +++++++++
 test/freeflow/navierstokes/l2error.hh         |  12 +-
 8 files changed, 512 insertions(+), 4 deletions(-)
 create mode 100644 test/freeflow/navierstokes/channel/pipe/CMakeLists.txt
 create mode 100755 test/freeflow/navierstokes/channel/pipe/convergencetest.py
 create mode 100644 test/freeflow/navierstokes/channel/pipe/main.cc
 create mode 100644 test/freeflow/navierstokes/channel/pipe/params.input
 create mode 100644 test/freeflow/navierstokes/channel/pipe/problem.hh
 create mode 100644 test/freeflow/navierstokes/channel/pipe/properties.hh

diff --git a/test/freeflow/navierstokes/channel/CMakeLists.txt b/test/freeflow/navierstokes/channel/CMakeLists.txt
index 642e957dc2..31ded5a8b0 100644
--- a/test/freeflow/navierstokes/channel/CMakeLists.txt
+++ b/test/freeflow/navierstokes/channel/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(1d)
 add_subdirectory(2d)
 add_subdirectory(3d)
+add_subdirectory(pipe)
diff --git a/test/freeflow/navierstokes/channel/pipe/CMakeLists.txt b/test/freeflow/navierstokes/channel/pipe/CMakeLists.txt
new file mode 100644
index 0000000000..b6df8b1811
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/CMakeLists.txt
@@ -0,0 +1,8 @@
+dune_symlink_to_source_files(FILES params.input convergencetest.py)
+dumux_add_test(NAME test_ff_stokes_channel_pipe
+               LABELS freeflow navierstokes
+               SOURCES main.cc
+               LABELS freeflow
+               CMAKE_GUARD HAVE_UMFPACK
+               COMMAND ./convergencetest.py
+               CMD_ARGS test_ff_stokes_channel_pipe)
diff --git a/test/freeflow/navierstokes/channel/pipe/convergencetest.py b/test/freeflow/navierstokes/channel/pipe/convergencetest.py
new file mode 100755
index 0000000000..24750a8962
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/convergencetest.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+
+from math import *
+import subprocess
+import sys
+
+if len(sys.argv) < 2:
+    sys.stderr.write('Please provide a single argument <testname> to the script\n')
+    sys.exit(1)
+
+testname = str(sys.argv[1])
+testargs = [str(i) for i in sys.argv][2:]
+
+# remove the old log file
+subprocess.call(['rm', testname + '.log'])
+print("Removed old log file ({})!".format(testname + '.log'))
+
+# do the runs with different refinement
+for i in [0, 1, 2]:
+    subprocess.call(['./' + testname] + testargs + ['-Grid.Refinement', str(i),
+                                      '-Problem.Name', testname])
+
+# check the rates and append them to the log file
+logfile = open(testname + '.log', "r+")
+
+errorP = []
+errorVx = []
+errorVy = []
+for line in logfile:
+    line = line.strip("\n")
+    line = line.strip("\[ConvergenceTest\]")
+    line = line.split()
+    errorP.append(float(line[2]))
+    errorVx.append(float(line[5]))
+    errorVy.append(float(line[8]))
+
+resultsP = []
+resultsVx = []
+resultsVy = []
+logfile.truncate(0)
+logfile.write("n\terrorP\t\trateP\t\terrorVx\t\trateVx\t\terrorVy\t\trateVy\n")
+logfile.write("-"*50 + "\n")
+for i in range(len(errorP)-1):
+    if isnan(errorP[i]) or isinf(errorP[i]):
+        continue
+    if not ((errorP[i] < 1e-12 or errorP[i+1] < 1e-12) and (errorVx[i] < 1e-12 or errorVx[i+1] < 1e-12) and (errorVy[i] < 1e-12 or errorVy[i+1] < 1e-12)):
+        rateP = (log(errorP[i])-log(errorP[i+1]))/log(2)
+        rateVx = (log(errorVx[i])-log(errorVx[i+1]))/log(2)
+        rateVy = (log(errorVy[i])-log(errorVy[i+1]))/log(2)
+        message = "{}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\n".format(i, errorP[i], rateP,  errorVx[i], rateVx, errorVy[i], rateVy)
+        logfile.write(message)
+        resultsP.append(rateP)
+        resultsVx.append(rateVx)
+        resultsVy.append(rateVy)
+    else:
+        logfile.write("error: exact solution!?")
+i = len(errorP)-1
+message = "{}\t{:0.4e}\t\t{}\t{:0.4e}\t\t{}\t{:0.4e}\t\t{}\n".format(i, errorP[i], "",  errorVx[i], "", errorVy[i], "")
+logfile.write(message)
+
+logfile.close()
+print("\nComputed the following convergence rates for {}:\n".format(testname))
+subprocess.call(['cat', testname + '.log'])
+
+def mean(numbers):
+    return float(sum(numbers)) / len(numbers)
+
+# check the rates, we expect rates around 2
+if mean(resultsP) < 2.1 and mean(resultsP) < 1.8:
+    sys.stderr.write("*"*70 + "\n" + "The convergence rates for pressure were not close enough to 2! Test failed.\n" + "*"*70 + "\n")
+    sys.exit(1)
+
+if mean(resultsVx) < 2.05 and mean(resultsVx) < 1.9:
+    sys.stderr.write("*"*70 + "\n" + "The convergence rates for x-velocity were not close enough to 2! Test failed.\n" + "*"*70 + "\n")
+    sys.exit(1)
+
+if mean(resultsVy) < 2.05 and mean(resultsVy) < 1.9:
+    sys.stderr.write("*"*70 + "\n" + "The convergence rates for y-velocity were not close enough to 2! Test failed.\n" + "*"*70 + "\n")
+    sys.exit(1)
+
+sys.exit(0)
diff --git a/test/freeflow/navierstokes/channel/pipe/main.cc b/test/freeflow/navierstokes/channel/pipe/main.cc
new file mode 100644
index 0000000000..4ca8e828d6
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/main.cc
@@ -0,0 +1,159 @@
+// -*- 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/>.   *
+ *****************************************************************************/
+
+#include <config.h>
+
+#include <iostream>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dumux/common/properties.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/common/partial.hh>
+#include <dumux/linear/seqsolverbackend.hh>
+#include <dumux/assembly/diffmethod.hh>
+#include <dumux/io/vtkoutputmodule.hh>
+#include <dumux/io/staggeredvtkoutputmodule.hh>
+#include <dumux/io/grid/gridmanager_yasp.hh>
+
+#include <dumux/assembly/staggeredfvassembler.hh>
+#include <dumux/nonlinear/newtonsolver.hh>
+
+#include "properties.hh"
+#include "../../l2error.hh"
+
+template<class Problem, class SolutionVector, class GridGeometry>
+void printL2Error(const Problem& problem, const SolutionVector& x, const GridGeometry& gridGeometry)
+{
+    using namespace Dumux;
+    using Scalar = double;
+    using TypeTag = Properties::TTag::PipeFlow;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+    using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>;
+    using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>;
+
+    using L2Error = NavierStokesTestL2Error<Scalar, ModelTraits, PrimaryVariables>;
+    const auto [l2errorAbs, l2errorRel] = L2Error::calculateL2Error(*problem, x);
+    const int numCellCenterDofs = gridGeometry->numCellCenterDofs();
+    const int numFaceDofs = gridGeometry->numFaceDofs();
+    std::cout << std::setprecision(8) << "** L2 error (abs/rel) for "
+                << std::setw(6) << numCellCenterDofs << " cc dofs and " << numFaceDofs << " face dofs (total: " << numCellCenterDofs + numFaceDofs << "): "
+                << std::scientific
+                << "L2(p) = " << l2errorAbs[Indices::pressureIdx] << " / " << l2errorRel[Indices::pressureIdx]
+                << " , L2(vx) = " << l2errorAbs[Indices::velocityXIdx] << " / " << l2errorRel[Indices::velocityXIdx]
+                << " , L2(vy) = " << l2errorAbs[Indices::velocityYIdx] << " / " << l2errorRel[Indices::velocityYIdx]
+                << std::endl;
+
+    // write the norm into a log file
+    std::ofstream logFile;
+    logFile.open(problem->name() + ".log", std::ios::app);
+    logFile << "[ConvergenceTest] L2(p) = " << l2errorAbs[Indices::pressureIdx] << " L2(vx) = " << l2errorAbs[Indices::velocityXIdx] << " L2(vy) = " << l2errorAbs[Indices::velocityYIdx] << std::endl;
+    logFile.close();
+}
+
+int main(int argc, char** argv) try
+{
+    using namespace Dumux;
+
+    // initialize MPI, finalize is done automatically on exit
+    const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv);
+
+    // parse command line arguments and input file
+    Parameters::init(argc, argv);
+
+    // Define the sub problem type tags
+    using TypeTag = Properties::TTag::PipeFlow;
+
+    // try to create a grid (from the given grid file or the input file)
+    Dumux::GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager;
+    gridManager.init();
+
+    // we compute on the leaf grid view
+    const auto& gridView = gridManager.grid().leafGridView();
+
+    // create the finite volume grid geometry
+    using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
+    auto gridGeometry = std::make_shared<GridGeometry>(gridView);
+    gridGeometry->update();
+
+    // the problem (initial and boundary conditions)
+    using Problem = GetPropType<TypeTag, Properties::Problem>;
+    auto problem = std::make_shared<Problem>(gridGeometry);
+
+    // the solution vector
+    using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
+    SolutionVector sol;
+    sol[GridGeometry::cellCenterIdx()].resize(gridGeometry->numCellCenterDofs());
+    sol[GridGeometry::faceIdx()].resize(gridGeometry->numFaceDofs());
+    problem->applyInitialSolution(sol);
+
+    // the grid variables
+    using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
+    auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
+    gridVariables->init(sol);
+
+    // intialize the vtk output module
+    using IOFields = GetPropType<TypeTag, Properties::IOFields>;
+    StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, sol, problem->name());
+    IOFields::initOutputModule(vtkWriter); // Add model specific output fields
+    vtkWriter.write(0.0);
+
+    // the assembler with time loop for instationary problem
+    using Assembler = StaggeredFVAssembler<TypeTag, DiffMethod::numeric>;
+    auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables);
+
+    // the linear solver
+    using LinearSolver = Dumux::UMFPackBackend;
+    auto linearSolver = std::make_shared<LinearSolver>();
+
+    // the non-linear solver
+    using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>;
+    NewtonSolver nonLinearSolver(assembler, linearSolver);
+
+    nonLinearSolver.solve(sol);
+    vtkWriter.write(1.0);
+
+    printL2Error(problem, sol, gridGeometry);
+
+    // print dumux end message
+    if (mpiHelper.rank() == 0)
+        Parameters::print();
+
+    return 0;
+
+} // end main
+catch (const Dumux::ParameterException &e)
+{
+    std::cerr << std::endl << e << " ---> Abort!" << std::endl;
+    return 1;
+}
+catch (const Dune::DGFException & e)
+{
+    std::cerr << "DGF exception thrown (" << e <<
+                 "). Most likely, the DGF file name is wrong "
+                 "or the DGF file is corrupted, "
+                 "e.g. missing hash at end of file or wrong number (dimensions) of entries."
+                 << " ---> Abort!" << std::endl;
+    return 2;
+}
+catch (const Dune::Exception &e)
+{
+    std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl;
+    return 3;
+}
diff --git a/test/freeflow/navierstokes/channel/pipe/params.input b/test/freeflow/navierstokes/channel/pipe/params.input
new file mode 100644
index 0000000000..268e5b9bf9
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/params.input
@@ -0,0 +1,22 @@
+[Grid]
+Positions0 = 0 0.1e-3 # [m]
+Positions1 = 0 1.0e-3 # [m]
+Cells0 = 10 # [-]
+Cells1 = 100 # [-]
+Grading0 = 1
+Grading1 = 1
+
+[Problem]
+Name = test_ff_stokes_channel_pipe
+MeanInletVelocity = 0.1 # [m/s]
+
+[Problem]
+EnableGravity = false
+EnableInertiaTerms = false
+
+[Component]
+LiquidKinematicViscosity = 1e-6
+LiquidDensity = 1000
+
+[Vtk]
+AddVelocity = 1
diff --git a/test/freeflow/navierstokes/channel/pipe/problem.hh b/test/freeflow/navierstokes/channel/pipe/problem.hh
new file mode 100644
index 0000000000..cf5cc443b9
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/problem.hh
@@ -0,0 +1,150 @@
+// -*- 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_TEST_FREEFLOW_PIPE_PROBLEM_HH
+#define DUMUX_TEST_FREEFLOW_PIPE_PROBLEM_HH
+
+#include <dumux/common/properties.hh>
+#include <dumux/common/parameters.hh>
+#include <dumux/freeflow/navierstokes/problem.hh>
+#include <dumux/freeflow/navierstokes/boundarytypes.hh>
+
+namespace Dumux {
+
+/*!
+ * \brief Freeflow problem for pipe flow
+ * Simulation of a radially-symmetric pipe flow with circular cross-section
+ */
+template <class TypeTag>
+class FreeFlowPipeProblem : public NavierStokesProblem<TypeTag>
+{
+    using ParentType = NavierStokesProblem<TypeTag>;
+    using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
+    using GridView = typename GridGeometry::GridView;
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
+    using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+    using Element = typename GridView::template Codim<0>::Entity;
+    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
+    using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>;
+    using BoundaryTypes = NavierStokesBoundaryTypes<PrimaryVariables::size()>;
+
+public:
+    FreeFlowPipeProblem(std::shared_ptr<const GridGeometry> gridGeometry)
+    : ParentType(gridGeometry)
+
+    {
+        name_ = getParamFromGroup<std::string>(this->paramGroup(), "Problem.Name");
+        meanInletVelocity_ = getParamFromGroup<Scalar>(this->paramGroup(), "Problem.MeanInletVelocity");
+        mu_ = getParam<Scalar>("Component.LiquidKinematicViscosity")*getParam<Scalar>("Component.LiquidDensity");
+
+        pipeRadius_ = this->gridGeometry().bBoxMax()[0] - this->gridGeometry().bBoxMin()[0];
+        pipeLength_ = this->gridGeometry().bBoxMax()[1] - this->gridGeometry().bBoxMin()[1];
+        eps_ = 1e-7*pipeRadius_;
+
+        std::cout << "-- Reynolds number: " << 2*pipeRadius_*meanInletVelocity_/getParam<Scalar>("Component.LiquidKinematicViscosity") << std::endl;
+    }
+
+    const std::string& name() const
+    {
+        return name_;
+    }
+
+    Scalar temperature() const
+    { return 293.15; }
+
+    /*!
+     * \brief Specifies which kind of boundary condition should be
+     *        used for which equation on a given boundary segment.
+     */
+    BoundaryTypes boundaryTypes(const Element& element,
+                                const SubControlVolumeFace& scvf) const
+    {
+        BoundaryTypes values;
+        const auto& globalPos = scvf.dofPosition();
+
+        // inlet
+        if (onLowerBoundary_(globalPos))
+        {
+            values.setDirichlet(Indices::velocityXIdx);
+            values.setDirichlet(Indices::velocityYIdx);
+        }
+        //  outlet
+        else if (onUpperBoundary_(globalPos))
+        {
+            values.setDirichlet(Indices::pressureIdx);
+        }
+        // pipe centerline
+        else if (onInnerBoundary_(globalPos))
+        {
+            values.setAllSymmetry();
+        }
+        // pipe wall
+        else if (onOuterBoundary_(globalPos))
+        {
+            values.setDirichlet(Indices::velocityXIdx);
+            values.setDirichlet(Indices::velocityYIdx);
+        }
+
+        return values;
+    }
+
+    PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const
+    { return analyticalSolution(globalPos); }
+
+    PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const
+    { return analyticalSolution(globalPos); }
+
+    PrimaryVariables analyticalSolution(const GlobalPosition& globalPos) const
+    {
+        PrimaryVariables values(0.0);
+
+        // paraboloid velocity profile
+        const auto r = globalPos[0] - this->gridGeometry().bBoxMin()[0];
+        const auto y = globalPos[1] - this->gridGeometry().bBoxMin()[1];
+        values[Indices::velocityXIdx] = 0.0;
+        values[Indices::velocityYIdx] = 2.0*meanInletVelocity_*(1.0 - r*r/(pipeRadius_*pipeRadius_));
+        values[Indices::pressureIdx] = 1e5 + (pipeLength_-y)*meanInletVelocity_*8.0*mu_/(pipeRadius_*pipeRadius_);
+
+        return values;
+    }
+
+private:
+    bool onInnerBoundary_(const GlobalPosition &globalPos) const
+    { return globalPos[0] < this->gridGeometry().bBoxMin()[0] + eps_; }
+
+    bool onOuterBoundary_(const GlobalPosition &globalPos) const
+    { return globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; }
+
+    bool onLowerBoundary_(const GlobalPosition &globalPos) const
+    { return globalPos[1] < this->gridGeometry().bBoxMin()[1] + eps_; }
+
+    bool onUpperBoundary_(const GlobalPosition &globalPos) const
+    { return globalPos[1] > this->gridGeometry().bBoxMax()[1] - eps_; }
+
+    std::string name_;
+    Scalar meanInletVelocity_;
+    Scalar mu_;
+    Scalar pipeRadius_, pipeLength_;
+    Scalar eps_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/test/freeflow/navierstokes/channel/pipe/properties.hh b/test/freeflow/navierstokes/channel/pipe/properties.hh
new file mode 100644
index 0000000000..9de9fd669a
--- /dev/null
+++ b/test/freeflow/navierstokes/channel/pipe/properties.hh
@@ -0,0 +1,83 @@
+// -*- 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_TEST_FREEFLOW_PIPE_PROPERTIES_HH
+#define DUMUX_TEST_FREEFLOW_PIPE_PROPERTIES_HH
+
+#include <dune/grid/yaspgrid.hh>
+
+#include <dumux/common/properties.hh>
+
+#include <dumux/discretization/staggered/freeflow/properties.hh>
+#include <dumux/discretization/extrusion.hh>
+#include <dumux/freeflow/navierstokes/model.hh>
+#include <dumux/material/fluidsystems/1pliquid.hh>
+#include <dumux/material/components/constant.hh>
+
+#include "problem.hh"
+
+namespace Dumux::Properties {
+
+// Create new type tags
+namespace TTag {
+struct PipeFlow { using InheritsFrom = std::tuple<NavierStokes, StaggeredFreeFlowModel>; };
+} // end namespace TTag
+
+// the fluid system
+template<class TypeTag>
+struct FluidSystem<TypeTag, TTag::PipeFlow>
+{
+    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using type = FluidSystems::OnePLiquid<Scalar, Dumux::Components::Constant<1, Scalar> > ;
+};
+
+// Set the grid type
+template<class TypeTag>
+struct Grid<TypeTag, TTag::PipeFlow>
+{ using type = Dune::YaspGrid<2, Dune::TensorProductCoordinates<GetPropType<TypeTag, Properties::Scalar>, 2> >; };
+
+// Set the problem property
+template<class TypeTag>
+struct Problem<TypeTag, TTag::PipeFlow>
+{ using type = FreeFlowPipeProblem<TypeTag> ; };
+
+template<class TypeTag>
+struct EnableGridGeometryCache<TypeTag, TTag::PipeFlow> { static constexpr bool value = true; };
+template<class TypeTag>
+struct EnableGridFluxVariablesCache<TypeTag, TTag::PipeFlow> { static constexpr bool value = true; };
+template<class TypeTag>
+struct EnableGridVolumeVariablesCache<TypeTag, TTag::PipeFlow> { static constexpr bool value = true; };
+
+// rotation-symmetric grid geometry forming a cylinder channel
+template<class TypeTag>
+struct GridGeometry<TypeTag, TTag::PipeFlow>
+{
+    static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>();
+    static constexpr bool enableCache = getPropValue<TypeTag, Properties::EnableGridGeometryCache>();
+    using GridView = typename GetPropType<TypeTag, Properties::Grid>::LeafGridView;
+
+    struct GGTraits : public StaggeredFreeFlowDefaultFVGridGeometryTraits<GridView, upwindSchemeOrder>
+    { using Extrusion = RotationalExtrusion<0>; };
+
+    using type = StaggeredFVGridGeometry<GridView, enableCache, GGTraits>;
+};
+
+} // end namespace Dumux::Properties
+
+#endif
diff --git a/test/freeflow/navierstokes/l2error.hh b/test/freeflow/navierstokes/l2error.hh
index 8c001783b3..ec418b0ab8 100644
--- a/test/freeflow/navierstokes/l2error.hh
+++ b/test/freeflow/navierstokes/l2error.hh
@@ -26,6 +26,7 @@
 
 #include <vector>
 #include <cmath>
+#include <dumux/discretization/extrusion.hh>
 
 namespace Dumux {
 
@@ -50,6 +51,7 @@ public:
     static auto calculateL2Error(const Problem& problem, const SolutionVector& curSol)
     {
         using GridGeometry = std::decay_t<decltype(problem.gridGeometry())>;
+        using Extrusion = Extrusion_t<GridGeometry>;
         PrimaryVariables sumError(0.0), sumReference(0.0), l2NormAbs(0.0), l2NormRel(0.0);
 
         const int numFaceDofs = problem.gridGeometry().numFaceDofs();
@@ -73,9 +75,9 @@ public:
                 const auto& posCellCenter = scv.dofPosition();
                 const auto analyticalSolutionCellCenter = problem.analyticalSolution(posCellCenter)[Indices::pressureIdx];
                 const auto numericalSolutionCellCenter = curSol[GridGeometry::cellCenterIdx()][dofIdxCellCenter][Indices::pressureIdx - ModelTraits::dim()];
-                sumError[Indices::pressureIdx] += squaredDiff_(analyticalSolutionCellCenter, numericalSolutionCellCenter) * scv.volume();
-                sumReference[Indices::pressureIdx] += analyticalSolutionCellCenter * analyticalSolutionCellCenter * scv.volume();
-                totalVolume += scv.volume();
+                sumError[Indices::pressureIdx] += squaredDiff_(analyticalSolutionCellCenter, numericalSolutionCellCenter) * Extrusion::volume(scv);
+                sumReference[Indices::pressureIdx] += analyticalSolutionCellCenter * analyticalSolutionCellCenter * Extrusion::volume(scv);
+                totalVolume += Extrusion::volume(scv);
 
                 // treat face dofs
                 for (auto&& scvf : scvfs(fvGeometry))
@@ -87,7 +89,9 @@ public:
                     directionIndex[dofIdxFace] = dirIdx;
                     errorVelocity[dofIdxFace] = squaredDiff_(analyticalSolutionFace, numericalSolutionFace);
                     velocityReference[dofIdxFace] = squaredDiff_(analyticalSolutionFace, 0.0);
-                    const Scalar staggeredHalfVolume = 0.5 * scv.volume();
+                    auto faceScvCenter = scv.center() + scvf.center(); faceScvCenter *= 0.5;
+                    typename GridGeometry::Traits::FaceSubControlVolume faceScv(faceScvCenter, 0.5*scv.volume());
+                    const Scalar staggeredHalfVolume = Extrusion::volume(faceScv);
                     staggeredVolume[dofIdxFace] = staggeredVolume[dofIdxFace] + staggeredHalfVolume;
                 }
             }
-- 
GitLab