From d457709abd60fefa9ab9aadd1638485770b2799d Mon Sep 17 00:00:00 2001
From: Kilian Weishaupt <kilian.weishaupt@iws.uni-stuttgart.de>
Date: Sun, 2 Jun 2019 09:28:18 +0200
Subject: [PATCH] [staggered]fluxVars] Improve handling of Neumann BCs for the
 momentum eq

* correctly account for the case when there is a Neumann bc for both
  the normal and tangential part of the stress.
---
 .../navierstokes/staggered/fluxvariables.hh   | 116 +++++++++++-------
 1 file changed, 75 insertions(+), 41 deletions(-)

diff --git a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
index 70b8c08024..cb1ffcb090 100644
--- a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
+++ b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh
@@ -282,6 +282,12 @@ public:
         const auto& faceVars = elemFaceVars[scvf];
         const int numSubFaces = scvf.pairData().size();
 
+        // If the current scvf is on a boundary, check if there is a Neumann BC for the stress in tangential direction.
+        // Create a boundaryTypes object (will be empty if not at a boundary).
+        Dune::Std::optional<BoundaryTypes> currentScvfBoundaryTypes;
+        if (scvf.boundary())
+            currentScvfBoundaryTypes.emplace(problem.boundaryTypes(element, scvf));
+
         // Account for all sub faces.
         for (int localSubFaceIdx = 0; localSubFaceIdx < numSubFaces; ++localSubFaceIdx)
         {
@@ -289,31 +295,39 @@ public:
             // Get the face normal to the face the dof lives on. The staggered sub face conincides with half of this lateral face.
             const auto& lateralScvf = fvGeometry.scvf(eIdx, scvf.pairData(localSubFaceIdx).localLateralFaceIdx);
 
-            // create a boundaryTypes object (will be empty if not at a boundary)
+            // If the current scvf is on a bounary and if there is a Neumann BC for the stress in tangential direction,
+            // assign this value for the lateral momentum flux. No further calculations are required. We assume that all lateral faces
+            // have the same type of BC (Neumann), but we sample the value at their actual positions.
+            if (currentScvfBoundaryTypes && currentScvfBoundaryTypes->isNeumann(Indices::velocity(lateralScvf.directionIndex())))
+            {
+                // Get the location of the lateral staggered face's center.
+                const auto& lateralStaggeredFaceCenter = lateralStaggeredFaceCenter_(scvf, localSubFaceIdx);
+                lateralFlux += problem.neumann(element, fvGeometry, elemVolVars, elemFaceVars,
+                                               scvf.makeBoundaryFace(lateralStaggeredFaceCenter))[Indices::velocity(lateralScvf.directionIndex())]
+                                              * elemVolVars[scvf.insideScvIdx()].extrusionFactor() * lateralScvf.area() * 0.5
+                                              * lateralScvf.directionSign();
+                continue;
+            }
+
+            // Create a boundaryTypes object (will be empty if not at a boundary).
             Dune::Std::optional<BoundaryTypes> lateralFaceBoundaryTypes;
 
             // Check if there is face/element parallel to our face of interest where the dof lives on. If there is no parallel neighbor,
             // we are on a boundary where we have to check for boundary conditions.
             if (lateralScvf.boundary())
             {
-                // Construct a temporary scvf which corresponds to the staggered sub face, featuring the location
-                // the sub faces's center.
-                auto lateralBoundaryFaceCenter = scvf.pairData(localSubFaceIdx).lateralStaggeredFaceCenter + lateralScvf.center();
-                lateralBoundaryFaceCenter *= 0.5;
-
-                //    ________________
-                //    --------####o###                 || frontal face of staggered half-control-volume
-                //    |      ||      | current scvf    #  lateralBoundaryFace of interest, lies on lateral boundary
-                //    |      ||      |                 x  dof position
-                //    |      ||      x~~~~> vel.Self   -- element boundaries
-                //    |      ||      |                 __ domain boundary
-                //    |      ||      |                 o  position at which the boundary conditions will be evaluated
-                //    ----------------                    (lateralBoundaryFaceCenter)
-
-                const auto lateralBoundaryFace = lateralScvf.makeBoundaryFace(lateralBoundaryFaceCenter);
-
-                // Retrieve the boundary types that correspond to the sub face.
-                lateralFaceBoundaryTypes.emplace(problem.boundaryTypes(element, lateralBoundaryFace));
+                // Retrieve the boundary types that correspond to the center of the lateral scvf. As a convention, we always query
+                // the type of BCs at the center of the element's "actual" lateral scvf (not the face of the staggered control volume).
+                // The value of the BC will be evaluated at the center of the staggered face.
+                //     --------T######V                 || frontal face of staggered half-control-volume
+                //     |      ||      | current scvf    #  lateral staggered face of interest (may lie on a boundary)
+                //     |      ||      |                 x  dof position
+                //     |      ||      x~~~~> vel.Self   -- element boundaries
+                //     |      ||      |                 T  position at which the type of boundary conditions will be evaluated
+                //     |      ||      |                    (center of lateral scvf)
+                //     ----------------                 V  position at which the value of the boundary conditions will be evaluated
+                //                                         (center of the staggered lateral face)
+                lateralFaceBoundaryTypes.emplace(problem.boundaryTypes(element, lateralScvf));
 
                 // Check if we have a symmetry boundary condition. If yes, the tangential part of the momentum flux can be neglected
                 // and we may skip any further calculations for the given sub face.
@@ -323,13 +337,17 @@ public:
                 // Handle Neumann boundary conditions. No further calculations are then required for the given sub face.
                 if (lateralFaceBoundaryTypes->isNeumann(Indices::velocity(scvf.directionIndex())))
                 {
+                    // Construct a temporary scvf which corresponds to the staggered sub face, featuring the location
+                    // the staggered faces's center.
+                    const auto& lateralStaggeredFaceCenter = lateralStaggeredFaceCenter_(scvf, localSubFaceIdx);
+                    const auto lateralBoundaryFace = lateralScvf.makeBoundaryFace(lateralStaggeredFaceCenter);
                     lateralFlux += problem.neumann(element, fvGeometry, elemVolVars, elemFaceVars, lateralBoundaryFace)[Indices::velocity(scvf.directionIndex())]
                                                   * elemVolVars[lateralScvf.insideScvIdx()].extrusionFactor() * lateralScvf.area() * 0.5;
                     continue;
                 }
 
                 // Handle wall-function fluxes (only required for RANS models)
-                if (incorporateWallFunction_(lateralFlux, problem, element, fvGeometry, scvf, lateralBoundaryFace, elemVolVars, elemFaceVars))
+                if (incorporateWallFunction_(lateralFlux, problem, element, fvGeometry, scvf, elemVolVars, elemFaceVars, localSubFaceIdx))
                     continue;
 
                 // Check the consistency of the boundary conditions, only one of the following must be set
@@ -340,7 +358,7 @@ public:
                 if (admittableBcTypes.count() != 1)
                 {
                     DUNE_THROW(Dune::InvalidStateException,  "Something went wrong with the boundary conditions "
-                    "for the momentum equations at global position " << lateralBoundaryFaceCenter);
+                    "for the momentum equations at global position " << lateralStaggeredFaceCenter_(scvf, localSubFaceIdx));
                 }
             }
 
@@ -457,9 +475,8 @@ private:
         {
             // Create a boundaryTypes object (will be empty if not at a boundary).
             Dune::Std::optional<BoundaryTypes> bcTypes;
-            const auto& boundaryFace = scvf.makeBoundaryFace(scvf.pairData(localSubFaceIdx).lateralStaggeredFaceCenter);
 
-            // Get the boundary conditions if we are at a boundary.
+            // Get the boundary conditions if we are at a boundary. We sample the type of BC at the center of the current scvf.
             if (scvf.boundary())
                 bcTypes.emplace(problem.boundaryTypes(element, scvf));
 
@@ -493,7 +510,11 @@ private:
                     if (!scvf.boundary())
                         return faceVars.velocityLateralOutside(localSubFaceIdx);
                     else if (bcTypes->isDirichlet(Indices::velocity(lateralFace.directionIndex())))
-                        return problem.dirichlet(element, boundaryFace)[Indices::velocity(lateralFace.directionIndex())];
+                    {
+                        // Sample the value of the Dirichlet BC at the center of the lateral face intersecting with the boundary.
+                        const auto& lateralBoundaryFacePos = lateralStaggeredFaceCenter_(scvf, localSubFaceIdx);
+                        return problem.dirichlet(element, scvf.makeBoundaryFace(lateralBoundaryFacePos))[Indices::velocity(lateralFace.directionIndex())];
+                    }
                     else
                     {
                         // Compute the BJS slip velocity at the boundary. Note that the relevant velocity gradient is now
@@ -529,7 +550,11 @@ private:
                 if (!lateralFace.boundary())
                     return faceVars.velocityParallel(localSubFaceIdx, 0);
                 else if (lateralFaceBoundaryTypes->isDirichlet(Indices::velocity(scvf.directionIndex())))
-                    return problem.dirichlet(element, makeParallelGhostFace_(scvf, localSubFaceIdx))[Indices::velocity(scvf.directionIndex())];
+                {
+                    // Sample the value of the Dirichlet BC at the center of the staggered lateral face.
+                    const auto& lateralBoundaryFacePos = lateralStaggeredFaceCenter_(scvf, localSubFaceIdx);
+                    return problem.dirichlet(element, lateralFace.makeBoundaryFace(lateralBoundaryFacePos))[Indices::velocity(scvf.directionIndex())];
+                }
                 else
                 {
                     const auto& scv = fvGeometry.scv(scvf.insideScvIdx());
@@ -601,19 +626,23 @@ private:
 
 private:
 
-    //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest
-    SubControlVolumeFace makeParallelGhostFace_(const SubControlVolumeFace& ownScvf, const int localSubFaceIdx) const
+    /*!
+     * \brief Get the location of the lateral staggered face's center.
+     *        Only needed for boundary conditions if the current scvf or the lateral one is on a bounary.
+     *
+     * \verbatim
+     *      --------#######o                 || frontal face of staggered half-control-volume
+     *      |      ||      | current scvf    #  lateral staggered face of interest (may lie on a boundary)
+     *      |      ||      |                 x  dof position
+     *      |      ||      x~~~~> vel.Self   -- element boundaries, current scvf may lie on a boundary
+     *      |      ||      |                 o  position at which the boundary conditions will be evaluated
+     *      |      ||      |                    (lateralStaggeredFaceCenter)
+     *      ----------------
+     * \endverbatim
+     */
+    const GlobalPosition& lateralStaggeredFaceCenter_(const SubControlVolumeFace& scvf, const int localSubFaceIdx) const
     {
-        //    ________________
-        //    --------#######o                 || frontal face of staggered half-control-volume
-        //    |      ||      | current scvf    #  lateralBoundaryFace of interest, lies on lateral boundary
-        //    |      ||      |                 x  dof position
-        //    |      ||      x~~~~> vel.Self   -- element boundaries
-        //    |      ||      |                 __ domain boundary
-        //    |      ||      |                 o  position at which the boundary conditions will be evaluated
-        //    ----------------
-
-        return ownScvf.makeBoundaryFace(ownScvf.pairData(localSubFaceIdx).lateralStaggeredFaceCenter);
+        return scvf.pairData(localSubFaceIdx).lateralStaggeredFaceCenter;
     };
 
     //! helper function to get the averaged extrusion factor for a face
@@ -635,14 +664,19 @@ private:
                                   const Element& element,
                                   const FVElementGeometry& fvGeometry,
                                   const SubControlVolumeFace& scvf,
-                                  const SubControlVolumeFace& lateralBoundaryFace,
                                   const ElementVolumeVariables& elemVolVars,
-                                  const ElementFaceVariables& elemFaceVars) const
+                                  const ElementFaceVariables& elemFaceVars,
+                                  const std::size_t localSubFaceIdx) const
     {
-        if (problem.useWallFunction(element, lateralBoundaryFace, Indices::velocity(scvf.directionIndex())))
+        const auto eIdx = scvf.insideScvIdx();
+        const auto& lateralScvf = fvGeometry.scvf(eIdx, scvf.pairData(localSubFaceIdx).localLateralFaceIdx);
+
+        if (problem.useWallFunction(element, lateralScvf, Indices::velocity(scvf.directionIndex())))
         {
+            const auto& lateralStaggeredFaceCenter = lateralStaggeredFaceCenter_(scvf, localSubFaceIdx);
+            const auto lateralBoundaryFace = lateralScvf.makeBoundaryFace(lateralStaggeredFaceCenter);
             lateralFlux += problem.wallFunction(element, fvGeometry, elemVolVars, elemFaceVars, scvf, lateralBoundaryFace)[Indices::velocity(scvf.directionIndex())]
-                                               * elemVolVars[lateralBoundaryFace.insideScvIdx()].extrusionFactor() * lateralBoundaryFace.area() * 0.5;
+                                               * extrusionFactor_(elemVolVars, lateralScvf) * lateralScvf.area() * 0.5;
             return true;
         }
         else
-- 
GitLab