diff --git a/test/freeflow/navierstokes/angeli/CMakeLists.txt b/test/freeflow/navierstokes/angeli/CMakeLists.txt
index cb411f6e9a0d0f26db0f7422b50e5a280dddcba9..ff3f34615c093302af3a102bccb5fcc3b91c02ea 100644
--- a/test/freeflow/navierstokes/angeli/CMakeLists.txt
+++ b/test/freeflow/navierstokes/angeli/CMakeLists.txt
@@ -20,7 +20,6 @@ dumux_add_test(NAME test_ff_navierstokes_angeli_averaged
                              --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_angeli params.input
                              -TimeLoop.TEnd 1e-6 -TimeLoop.DtInitial 1e-6
                              -Problem.Name test_ff_navierstokes_angeli_averaged
-                             -Problem.UseVelocityAveragingForInitial true
-                             -Problem.UseVelocityAveragingForDirichlet true")
+                             -Problem.InterpolateExactVelocity true")
 
 dune_symlink_to_source_files(FILES "params.input")
diff --git a/test/freeflow/navierstokes/angeli/main.cc b/test/freeflow/navierstokes/angeli/main.cc
index 7da33e3f6e4921118e1009fd798146ed734e0a43..1911467ee65194bfaafc25d33a8063bfec24b1f9 100644
--- a/test/freeflow/navierstokes/angeli/main.cc
+++ b/test/freeflow/navierstokes/angeli/main.cc
@@ -32,18 +32,20 @@
 
 #include <dune/common/parallel/mpihelper.hh>
 #include <dune/common/timer.hh>
-#include <dune/grid/io/file/vtk.hh>
-
-#include <dumux/assembly/staggeredfvassembler.hh>
-#include <dumux/assembly/diffmethod.hh>
 #include <dumux/common/dumuxmessage.hh>
 #include <dumux/common/parameters.hh>
 #include <dumux/common/properties.hh>
-#include <dumux/io/grid/gridmanager.hh>
-#include <dumux/io/staggeredvtkoutputmodule.hh>
+
+#include <dumux/io/vtkoutputmodule.hh>
+#include <dumux/io/grid/gridmanager_yasp.hh>
+
 #include <dumux/linear/seqsolverbackend.hh>
-#include <dumux/nonlinear/newtonsolver.hh>
 
+#include <dumux/multidomain/fvassembler.hh>
+#include <dumux/multidomain/traits.hh>
+#include <dumux/multidomain/newtonsolver.hh>
+
+#include <dumux/freeflow/navierstokes/velocityoutput.hh>
 #include <test/freeflow/navierstokes/analyticalsolutionvectors.hh>
 #include <test/freeflow/navierstokes/errors.hh>
 
@@ -53,8 +55,10 @@ int main(int argc, char** argv)
 {
     using namespace Dumux;
 
-    // define the type tag for this problem
-    using TypeTag = Properties::TTag::AngeliTest;
+     // define the type tag for this problem
+    using MomentumTypeTag = Properties::TTag::AngeliTestMomentum;
+    using MassTypeTag = Properties::TTag::AngeliTestMass;
+
 
     // initialize MPI, finalize is done automatically on exit
     const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv);
@@ -67,7 +71,7 @@ int main(int argc, char** argv)
     Parameters::init(argc, argv);
 
     // try to create a grid (from the given grid file or the input file)
-    GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager;
+    GridManager<GetPropType<MomentumTypeTag, Properties::Grid>> gridManager;
     gridManager.init();
 
     ////////////////////////////////////////////////////////////
@@ -78,11 +82,27 @@ int main(int argc, char** argv)
     const auto& leafGridView = gridManager.grid().leafGridView();
 
     // create the finite volume grid geometry
-    using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
-    auto gridGeometry = std::make_shared<GridGeometry>(leafGridView);
+    using MomentumGridGeometry = GetPropType<MomentumTypeTag, Properties::GridGeometry>;
+    auto momentumGridGeometry = std::make_shared<MomentumGridGeometry>(leafGridView);
+
+    using MassGridGeometry = GetPropType<MassTypeTag, Properties::GridGeometry>;
+    auto massGridGeometry = std::make_shared<MassGridGeometry>(leafGridView);
+
+    // the coupling manager
+    using Traits = MultiDomainTraits<MomentumTypeTag, MassTypeTag>;
+    using CouplingManager = StaggeredFreeFlowCouplingManager<Traits>;
+
+    auto couplingManager = std::make_shared<CouplingManager>();
+
+    // the problem (boundary conditions)
+    using MomentumProblem = GetPropType<MomentumTypeTag, Properties::Problem>;
+    auto momentumProblem = std::make_shared<MomentumProblem>(momentumGridGeometry, couplingManager);
+
+    using MassProblem = GetPropType<MassTypeTag, Properties::Problem>;
+    auto massProblem = std::make_shared<MassProblem>(massGridGeometry, couplingManager);
 
     // get some time loop parameters
-    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
+    using Scalar = typename Traits::Scalar;
     const auto tStart = getParam<Scalar>("TimeLoop.TStart", 0.0);
     const auto tEnd = getParam<Scalar>("TimeLoop.TEnd");
     const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize");
@@ -92,62 +112,72 @@ int main(int argc, char** argv)
     auto timeLoop = std::make_shared<TimeLoop<Scalar>>(tStart, dt, tEnd);
     timeLoop->setMaxTimeStepSize(maxDt);
 
-    // the problem (initial and boundary conditions)
-    using Problem = GetPropType<TypeTag, Properties::Problem>;
-    auto problem = std::make_shared<Problem>(gridGeometry);
-    problem->setTime(timeLoop->time());
-
     // the solution vector
-    using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
+    constexpr auto momentumIdx = CouplingManager::freeFlowMomentumIndex;
+    constexpr auto massIdx = CouplingManager::freeFlowMassIndex;
+    using SolutionVector = typename Traits::SolutionVector;
     SolutionVector x;
-    x[GridGeometry::cellCenterIdx()].resize(gridGeometry->numCellCenterDofs());
-    x[GridGeometry::faceIdx()].resize(gridGeometry->numFaceDofs());
-    problem->applyInitialSolution(x);
+    momentumProblem->applyInitialSolution(x[momentumIdx]);
+    massProblem->applyInitialSolution(x[massIdx]);
     auto xOld = x;
 
     // the grid variables
-    using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
-    auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry);
-    gridVariables->init(x);
+    using MomentumGridVariables = GetPropType<MomentumTypeTag, Properties::GridVariables>;
+    auto momentumGridVariables = std::make_shared<MomentumGridVariables>(momentumProblem, momentumGridGeometry);
+
+    using MassGridVariables = GetPropType<MassTypeTag, Properties::GridVariables>;
+    auto massGridVariables = std::make_shared<MassGridVariables>(massProblem, massGridGeometry);
+
+    couplingManager->init(momentumProblem, massProblem, std::make_tuple(momentumGridVariables, massGridVariables), x, xOld);
 
-    // initialize the vtk output module
-    StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
-    using IOFields = GetPropType<TypeTag, Properties::IOFields>;
+    massGridVariables->init(x[massIdx]);
+    momentumGridVariables->init(x[momentumIdx]);
+
+    // intialize the vtk output module
+    using IOFields = GetPropType<MassTypeTag, Properties::IOFields>;
+    VtkOutputModule vtkWriter(*massGridVariables, x[massIdx], massProblem->name());
     IOFields::initOutputModule(vtkWriter); // Add model specific output fields
-    NavierStokesAnalyticalSolutionVectors analyticalSolVectors(problem, tStart);
-    vtkWriter.addField(analyticalSolVectors.getAnalyticalPressureSolution(), "pressureExact");
-    vtkWriter.addField(analyticalSolVectors.getAnalyticalVelocitySolution(), "velocityExact");
-    vtkWriter.addFaceField(analyticalSolVectors.getAnalyticalVelocitySolutionOnFace(), "faceVelocityExact");
+
+    vtkWriter.addVelocityOutput(std::make_shared<NavierStokesVelocityOutput<MassGridVariables>>());
+    NavierStokesTest::AnalyticalSolutionVectors analyticalSolVectors(momentumProblem, massProblem);
+    vtkWriter.addField(analyticalSolVectors.analyticalPressureSolution(), "pressureExact");
+    vtkWriter.addField(analyticalSolVectors.analyticalVelocitySolution(), "velocityExact");
     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, timeLoop, xOld);
+    using Assembler = MultiDomainFVAssembler<Traits, CouplingManager, DiffMethod::numeric>;
+    auto assembler = std::make_shared<Assembler>(std::make_tuple(momentumProblem, massProblem),
+                                                 std::make_tuple(momentumGridGeometry, massGridGeometry),
+                                                 std::make_tuple(momentumGridVariables, massGridVariables),
+                                                 couplingManager, timeLoop, xOld);
 
     // 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);
+    // the non-linear solver
+    using NewtonSolver = Dumux::MultiDomainNewtonSolver<Assembler, LinearSolver, CouplingManager>;
+    NewtonSolver nonLinearSolver(assembler, linearSolver, couplingManager);
 
     // the discrete L2 and Linfity errors
     const bool printErrors = getParam<bool>("Problem.PrintErrors", false);
-    NavierStokesErrors errors(problem, x, 0.0);
-    NavierStokesErrorCSVWriter errorCSVWriter(problem);
+    NavierStokesTest::Errors errors(momentumProblem, massProblem, x);
+    NavierStokesTest::ErrorCSVWriter errorCSVWriter(momentumProblem, massProblem);
 
     // time loop
     timeLoop->start(); do
     {
-        problem->setTime(timeLoop->time() + timeLoop->timeStepSize());
+        massProblem->updateTime(timeLoop->time() + timeLoop->timeStepSize());
+        momentumProblem->updateTime(timeLoop->time() + timeLoop->timeStepSize());
 
         // solve the non-linear system with time step control
         nonLinearSolver.solve(x, *timeLoop);
 
         // make the new solution the old solution
         xOld = x;
-        gridVariables->advanceTimeStep();
+        momentumGridVariables->advanceTimeStep();
+        massGridVariables->advanceTimeStep();
 
         // print discrete L2 and Linfity errors
         if (printErrors)
@@ -161,6 +191,7 @@ int main(int argc, char** argv)
 
         // advance to the time loop to the next step
         timeLoop->advanceTimeStep();
+        analyticalSolVectors.update(timeLoop->time());
 
         // write vtk output
         vtkWriter.write(timeLoop->time());
@@ -175,6 +206,7 @@ int main(int argc, char** argv)
 
     timeLoop->finalize(leafGridView.comm());
 
+
     ////////////////////////////////////////////////////////////
     // finalize, print dumux message to say goodbye
     ////////////////////////////////////////////////////////////
diff --git a/test/freeflow/navierstokes/angeli/params.input b/test/freeflow/navierstokes/angeli/params.input
index f6d7188543b2c1bdc414bfe99dfd741f58570503..d5b8163b7957832dfb816942d7f98ed23a3839e1 100644
--- a/test/freeflow/navierstokes/angeli/params.input
+++ b/test/freeflow/navierstokes/angeli/params.input
@@ -12,8 +12,6 @@ Name = test_angeli # name passed to the output routines
 EnableGravity = false
 PrintErrors = false
 EnableInertiaTerms = true
-UseVelocityAveragingForInitial = false
-UseVelocityAveragingForDirichlet = false
 
 [Component]
 LiquidDensity = 1
diff --git a/test/freeflow/navierstokes/angeli/problem.hh b/test/freeflow/navierstokes/angeli/problem.hh
index fd37c83dec48e7badb868bfb260e275d94e683b9..546a89f2b04e13a3f7c338d0e9ed25100f7a8721 100644
--- a/test/freeflow/navierstokes/angeli/problem.hh
+++ b/test/freeflow/navierstokes/angeli/problem.hh
@@ -51,30 +51,36 @@ class AngeliTestProblem : public NavierStokesProblem<TypeTag>
 {
     using ParentType = NavierStokesProblem<TypeTag>;
 
-    using BoundaryTypes = Dumux::NavierStokesBoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>;
+    using BoundaryTypes = typename ParentType::BoundaryTypes;
     using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
     using GridView = typename GridGeometry::GridView;
     using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>;
-    using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>;
-    using NumEqVector = Dumux::NumEqVector<PrimaryVariables>;
+    using NumEqVector = typename ParentType::NumEqVector;
+    using PrimaryVariables = typename ParentType::PrimaryVariables;
     using Scalar = GetPropType<TypeTag, Properties::Scalar>;
     using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
-
-    using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
     using SubControlVolume = typename GridGeometry::SubControlVolume;
     using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+    using FVElementGeometry = typename GridGeometry::LocalView;
+
+    static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld;
+    using Element = typename FVElementGeometry::Element;
+
     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
+    using VelocityVector = Dune::FieldVector<Scalar, dimWorld>;
+
+    using CouplingManager = GetPropType<TypeTag, Properties::CouplingManager>;
+
 
 public:
     using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
 
-    AngeliTestProblem(std::shared_ptr<const GridGeometry> gridGeometry)
-    : ParentType(gridGeometry)
+    AngeliTestProblem(std::shared_ptr<const GridGeometry> gridGeometry, std::shared_ptr<CouplingManager> couplingManager)
+    : ParentType(gridGeometry, couplingManager)
     {
         kinematicViscosity_ = getParam<Scalar>("Component.LiquidKinematicViscosity", 1.0);
-        rho_ = getParam<Scalar>("Component.LiquidDensity", 1.0);
-        useVelocityAveragingForDirichlet_ = getParam<bool>("Problem.UseVelocityAveragingForDirichlet", false);
-        useVelocityAveragingForInitial_ = getParam<bool>("Problem.UseVelocityAveragingForInitial", false);
+        useNeumann_ = getParam<bool>("Problem.UseNeumann", false);
+        interpolateExactVelocity_ = getParam<bool>("Problem.InterpolateExactVelocity", false);
     }
 
     /*!
@@ -99,34 +105,63 @@ public:
     {
         BoundaryTypes values;
 
-        // set Dirichlet values for the velocity everywhere
-        values.setDirichlet(Indices::velocityXIdx);
-        values.setDirichlet(Indices::velocityYIdx);
+        if constexpr (ParentType::isMomentumProblem())
+        {
+            static constexpr Scalar eps = 1e-8;
+            if (useNeumann_ && (globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps ||
+                                globalPos[1] > this->gridGeometry().bBoxMax()[1] - eps))
+                values.setAllNeumann();
+            else
+                values.setAllDirichlet();
+        }
+        else
+            values.setAllNeumann();
 
         return values;
+
     }
 
+    //! Enable internal Dirichlet constraints
+    static constexpr bool enableInternalDirichletConstraints()
+    { return !ParentType::isMomentumProblem(); }
+
+
     /*!
-     * \brief Returns whether a fixed Dirichlet value shall be used at a given cell.
+     * \brief Tag a degree of freedom to carry internal Dirichlet constraints.
+     *        If true is returned for a dof, the equation for this dof is replaced
+     *        by the constraint that its primary variable values must match the
+     *        user-defined values obtained from the function internalDirichlet(),
+     *        which must be defined in the problem.
      *
      * \param element The finite element
-     * \param fvGeometry The finite-volume geometry
-     * \param scv The sub control volume
-     * \param pvIdx The primary variable index in the solution vector
+     * \param scv The sub-control volume
      */
-    bool isDirichletCell(const Element& element,
-                         const typename GridGeometry::LocalView& fvGeometry,
-                         const typename GridGeometry::SubControlVolume& scv,
-                         int pvIdx) const
+    std::bitset<PrimaryVariables::dimension> hasInternalDirichletConstraint(const Element& element, const SubControlVolume& scv) const
     {
-        // set a fixed pressure in all cells at the boundary
-        for (const auto& scvf : scvfs(fvGeometry))
-            if (scvf.boundary())
-                return true;
+        std::bitset<PrimaryVariables::dimension> values;
+
+        // We don't need internal Dirichlet conditions if a Neumann BC is set for the momentum balance (which accounts for the pressure).
+        // If only Dirichlet BCs are set for the momentum balance, fix the pressure at some cells such that the solution is fully defined.
+        if (!useNeumann_)
+        {
+            const auto fvGeometry = localView(this->gridGeometry()).bindElement(element);
+            if (fvGeometry.hasBoundaryScvf())
+                values.set(Indices::pressureIdx);
+        }
 
-        return false;
+        return values;
     }
 
+    /*!
+     * \brief Define the values of internal Dirichlet constraints for a degree of freedom.
+     * \param element The finite element
+     * \param scv The sub-control volume
+     */
+    PrimaryVariables internalDirichlet(const Element& element, const SubControlVolume& scv) const
+    { return analyticalSolution(scv.center(), time_); }
+
+
+
     /*!
      * \brief Evaluate the boundary conditions for a dirichlet
      *        control volume face (velocities)
@@ -138,26 +173,58 @@ public:
      */
     PrimaryVariables dirichlet(const Element& element, const SubControlVolumeFace& scvf) const
     {
-        if (useVelocityAveragingForDirichlet_)
-            return averagedVelocity_(scvf, time_);
+        if (ParentType::isMomentumProblem() && interpolateExactVelocity_)
+            return velocityDirichlet_(scvf);
         else
             return analyticalSolution(scvf.center(), time_);
     }
 
     /*!
-     * \brief Evaluate the boundary conditions for a dirichlet
-     *        control volume face (pressure)
+     * \brief Evaluates the boundary conditions for a Neumann control volume.
      *
-     * \param element The finite element
-     * \param scv the sub control volume
+     * \param element The element for which the Neumann boundary condition is set
+     * \param fvGeometry The fvGeometry
+     * \param elemVolVars The element volume variables
+     * \param elemFaceVars The element face variables
+     * \param scvf The boundary sub control volume face
      */
-    PrimaryVariables dirichlet(const Element& element, const SubControlVolume& scv) const
+    template<class ElementVolumeVariables, class ElementFluxVariablesCache>
+    NumEqVector neumann(const Element& element,
+                        const FVElementGeometry& fvGeometry,
+                        const ElementVolumeVariables& elemVolVars,
+                        const ElementFluxVariablesCache& elemFluxVarsCache,
+                        const SubControlVolumeFace& scvf) const
     {
-        PrimaryVariables priVars(0.0);
-        priVars[Indices::pressureIdx] = analyticalSolution(scv.center(), time_)[Indices::pressureIdx];
-        return priVars;
+        NumEqVector values(0.0);
+
+        if constexpr (!ParentType::isMomentumProblem())
+        {
+            const auto insideDensity = elemVolVars[scvf.insideScvIdx()].density();
+            values[Indices::conti0EqIdx] = this->faceVelocity(element, fvGeometry, scvf) * insideDensity * scvf.unitOuterNormal();
+        }
+        else
+        {
+            using std::sin;
+            using std::cos;
+            Dune::FieldMatrix<Scalar, 2, 2> momentumFlux(0.0);
+            const auto x = scvf.ipGlobal()[0];
+            const auto y = scvf.ipGlobal()[1];
+            const Scalar mu = kinematicViscosity_;
+            const Scalar t = time_;
+            momentumFlux[0][0] = M_PI*M_PI *(-4.0*mu*exp(10.0*M_PI*M_PI*mu*t)*sin(M_PI*x)*sin(2*M_PI*y) + (4.0*sin(2*M_PI*y)*sin(2*M_PI*y)*cos(M_PI*x)*cos(M_PI*x) - 1.0*cos(2*M_PI*x) - 0.25*cos(4*M_PI*y))*exp(5.0*M_PI*M_PI*mu*t))*exp(-15.0*M_PI*M_PI*mu*t);
+            momentumFlux[0][1] = M_PI*M_PI *( 3.0*mu*exp(10.0*M_PI*M_PI*mu*t) - 2.0*exp(5.0*M_PI*M_PI*mu*t)*sin(M_PI*x)*sin(2*M_PI*y))*exp(-15.0*M_PI*M_PI*mu*t)*cos(M_PI*x)*cos(2*M_PI*y);
+            momentumFlux[1][0] = momentumFlux[0][1];
+            momentumFlux[1][1] = M_PI*M_PI *( 4.0*mu*exp(10.0*M_PI*M_PI*mu*t)*sin(M_PI*x)*sin(2*M_PI*y) + (sin(M_PI*x)*sin(M_PI*x)*cos(2*M_PI*y)*cos(2*M_PI*y) - 1.0*cos(2*M_PI*x) - 0.25*cos(4*M_PI*y))*exp(5.0*M_PI*M_PI*mu*t))*exp(-15.0*M_PI*M_PI*mu*t);
+
+            const auto normal = scvf.unitOuterNormal();
+            momentumFlux.mv(normal, values);
+        }
+
+        return values;
     }
 
+
+
     /*!
      * \brief Returns the analytical solution of the problem at a given time and position.
      * \param globalPos The global position
@@ -169,10 +236,17 @@ public:
         const Scalar y = globalPos[1];
 
         PrimaryVariables values;
+        using std::exp;
+        using std::sin;
+        using std::cos;
 
-        values[Indices::pressureIdx] = - 0.25 * std::exp(-10.0 * kinematicViscosity_ * M_PI * M_PI * time) * M_PI * M_PI * (4.0 * std::cos(2.0 * M_PI * x) + std::cos(4.0 * M_PI * y))*rho_;
-        values[Indices::velocityXIdx] = - 2.0 * M_PI * std::exp(- 5.0 * kinematicViscosity_ * M_PI * M_PI * time) * std::cos(M_PI * x) * std::sin(2.0 * M_PI * y);
-        values[Indices::velocityYIdx] = M_PI * std::exp(- 5.0 * kinematicViscosity_ * M_PI * M_PI * time) * std::sin(M_PI * x) * std::cos(2.0 * M_PI * y);
+        if constexpr (ParentType::isMomentumProblem())
+        {
+            values[Indices::velocityXIdx] = - 2.0 * M_PI * exp(- 5.0 * kinematicViscosity_ * M_PI * M_PI * time) * cos(M_PI * x) * sin(2.0 * M_PI * y);
+            values[Indices::velocityYIdx] = M_PI * exp(- 5.0 * kinematicViscosity_ * M_PI * M_PI * time) * sin(M_PI * x) * cos(2.0 * M_PI * y);
+        }
+        else
+            values[Indices::pressureIdx] = - 0.25 * exp(-10.0 * kinematicViscosity_ * M_PI * M_PI * time) * M_PI * M_PI * (4.0 * cos(2.0 * M_PI * x) + cos(4.0 * M_PI * y));
 
         return values;
     }
@@ -185,31 +259,42 @@ public:
     // \{
 
     /*!
-     * \brief Evaluates the initial value for a control volume (pressure)
+     * \brief Evaluates the initial value for a control volume (velocity)
      */
     PrimaryVariables initial(const SubControlVolume& scv) const
     {
-        PrimaryVariables priVars(0.0);
-        priVars[Indices::pressureIdx] = analyticalSolution(scv.center(), time_)[Indices::pressureIdx];
-        return priVars;
+        // We can either evaluate the analytical solution point-wise at the
+        // momentum balance integration points or use an averaged velocity.
+
+        // Not using the quadrature to integrate and average the velocity
+        // will yield a spurious pressure solution in the first time step
+        // because the discrete mass balance equation is not fulfilled when just taking
+        // point-wise values of the analytical solution.
+
+        if (!interpolateExactVelocity_)
+            return analyticalSolution(scv.dofPosition(), 0.0);
+
+        // Get the element intersection/facet corresponding corresponding to the dual grid scv
+        // (where the velocity DOFs are located) and use a quadrature to get the average velocity
+        // on that facet.
+        const auto& element = this->gridGeometry().element(scv.elementIndex());
+
+        for (const auto& intersection : intersections(this->gridGeometry().gridView(), element))
+        {
+            if (intersection.indexInInside() == scv.indexInElement())
+                return velocityDirichlet_(intersection);
+
+        }
+        DUNE_THROW(Dune::InvalidStateException, "No intersection found");
     }
 
+
     /*!
-     * \brief Evaluates the initial value for a sub control volume face (velocities)
-     *
-     * Simply assigning the value of the analytical solution at the face center
-     * gives a discrete solution that is not divergence-free. For small initial
-     * time steps, this has a negative impact on the pressure solution
-     * after the first time step. The flag UseVelocityAveragingForInitial triggers the
-     * function averagedVelocity_ which uses a higher order quadrature formula to
-     * bring the discrete solution sufficiently close to being divergence-free.
+     * \brief Evaluates the initial value for an element (pressure)
      */
-    PrimaryVariables initial(const SubControlVolumeFace& scvf) const
+    PrimaryVariables initial(const Element& element) const
     {
-        if (useVelocityAveragingForInitial_)
-            return averagedVelocity_(scvf, time_);
-        else
-            return analyticalSolution(scvf.center(), time_);
+        return analyticalSolution(element.geometry().center(), 0.0);
     }
 
     // \}
@@ -217,34 +302,33 @@ public:
     /*!
      * \brief Updates the time information
      */
-    void setTime(Scalar t)
+    void updateTime(Scalar t)
     {
         time_ = t;
     }
 
 private:
-    PrimaryVariables averagedVelocity_(const SubControlVolumeFace& scvf, Scalar t) const
+    template<class Entity>
+    PrimaryVariables velocityDirichlet_(const Entity& entity) const
     {
         PrimaryVariables priVars(0.0);
-        const auto geo = scvf.geometry();
-        const auto& quad = Dune::QuadratureRules<Scalar, GridView::dimension-1>::rule(geo.type(), 3);
+        const auto geo = entity.geometry();
+        const auto& quad = Dune::QuadratureRules<Scalar, decltype(geo)::mydimension>::rule(geo.type(), 3);
         for (auto&& qp : quad)
         {
             const auto w = qp.weight()*geo.integrationElement(qp.position());
             const auto globalPos = geo.global(qp.position());
-            const auto sol = analyticalSolution(globalPos, t);
-            priVars[Indices::velocityXIdx] += sol[Indices::velocityXIdx]*w;
-            priVars[Indices::velocityYIdx] += sol[Indices::velocityYIdx]*w;
+            const auto sol = analyticalSolution(globalPos, time_);
+            priVars += sol*w;
         }
-        priVars[Indices::velocityXIdx] /= scvf.area();
-        priVars[Indices::velocityYIdx] /= scvf.area();
+        priVars /= geo.volume();
         return priVars;
     }
 
-    Scalar kinematicViscosity_, rho_;
-    Scalar time_ = 0;
-    bool useVelocityAveragingForDirichlet_;
-    bool useVelocityAveragingForInitial_;
+    Scalar kinematicViscosity_;
+    Scalar time_ = 0.0;
+    bool useNeumann_;
+    bool interpolateExactVelocity_;
 };
 } // end namespace Dumux
 
diff --git a/test/freeflow/navierstokes/angeli/properties.hh b/test/freeflow/navierstokes/angeli/properties.hh
index 51217da6891b4103e0158cfea590a62d6e9e74db..976b49397d9776922d8f2dc4ad0b9fb5d9f28b5f 100644
--- a/test/freeflow/navierstokes/angeli/properties.hh
+++ b/test/freeflow/navierstokes/angeli/properties.hh
@@ -26,19 +26,27 @@
 #define DUMUX_ANGELI_TEST_PROPERTIES_HH
 
 #include <dune/grid/yaspgrid.hh>
-#include <dumux/discretization/staggered/freeflow/properties.hh>
 
-#include <dumux/freeflow/navierstokes/model.hh>
+#include <dumux/freeflow/navierstokes/momentum/model.hh>
+#include <dumux/freeflow/navierstokes/mass/1p/model.hh>
+
+#include <dumux/discretization/fcstaggered.hh>
+#include <dumux/discretization/cctpfa.hh>
+
 #include <dumux/material/components/constant.hh>
 #include <dumux/material/fluidsystems/1pliquid.hh>
 
+#include <dumux/multidomain/staggeredfreeflow/couplingmanager.hh>
+
 #include "problem.hh"
 
 namespace Dumux::Properties {
 
 // Create new type tags
 namespace TTag {
-struct AngeliTest { using InheritsFrom = std::tuple<NavierStokes, StaggeredFreeFlowModel>; };
+struct AngeliTest {};
+struct AngeliTestMomentum { using InheritsFrom = std::tuple<AngeliTest, NavierStokesMomentum, FaceCenteredStaggeredModel>; };
+struct AngeliTestMass { using InheritsFrom = std::tuple<AngeliTest, NavierStokesMassOneP, CCTpfaModel>; };
 } // end namespace TTag
 
 // the fluid system
@@ -66,6 +74,15 @@ struct EnableGridFluxVariablesCache<TypeTag, TTag::AngeliTest> { static constexp
 template<class TypeTag>
 struct EnableGridVolumeVariablesCache<TypeTag, TTag::AngeliTest> { static constexpr bool value = true; };
 
-} // end namespace Dumux::Properties
+// Set the problem property
+template<class TypeTag>
+struct CouplingManager<TypeTag, TTag::AngeliTest>
+{
+private:
+    using Traits = MultiDomainTraits<TTag::AngeliTestMomentum, TTag::AngeliTestMass>;
+public:
+    using type = StaggeredFreeFlowCouplingManager<Traits>;
+};
 
+} // end namespace Dumux::Properties
 #endif