From a0f1e98d247419ee74af1969f22635da85b12940 Mon Sep 17 00:00:00 2001
From: Kutalmis Bercin <kutalmis.bercin@esi-group.com>
Date: Fri, 2 Dec 2022 15:49:20 +0000
Subject: [PATCH] ENH: finiteArea: improve robustness in code sections
 vulnerable to math errors

It has been observed that the finite-area framework is prone to numerical
issues when zero-valued edge lenghts, edge/face normals and face areas exist.

To improve exception handling at identified code sections to gracefully
overcome math errors, the problematic entities are lower-bounded by SMALL.
---
 .../liquidFilmFoam/capillaryCourantNo.H       |  2 +-
 .../faMesh/faMeshDemandDrivenData.C           | 81 +++++++++++++++++++
 .../faMesh/faPatches/faPatch/faPatch.C        | 14 +++-
 .../leastSquaresFaVectors.C                   | 14 ++++
 .../edgeInterpolation/edgeInterpolation.C     | 81 ++++++++++---------
 .../schemes/NVDscheme/faNVDscheme.C           | 12 +++
 6 files changed, 166 insertions(+), 38 deletions(-)

diff --git a/applications/solvers/finiteArea/liquidFilmFoam/capillaryCourantNo.H b/applications/solvers/finiteArea/liquidFilmFoam/capillaryCourantNo.H
index 8e66fd171f2..f4e87630e02 100644
--- a/applications/solvers/finiteArea/liquidFilmFoam/capillaryCourantNo.H
+++ b/applications/solvers/finiteArea/liquidFilmFoam/capillaryCourantNo.H
@@ -4,7 +4,7 @@
         sqrt
         (
             2*M_PI*sigma*sqr(aMesh.edgeInterpolation::deltaCoeffs())
-            *aMesh.edgeInterpolation::deltaCoeffs()
+            *mag(aMesh.edgeInterpolation::deltaCoeffs())
             /rhol
         )
     ).value()*runTime.deltaT().value();
diff --git a/src/finiteArea/faMesh/faMeshDemandDrivenData.C b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
index 1d98ea822d7..0a7620fc8c1 100644
--- a/src/finiteArea/faMesh/faMeshDemandDrivenData.C
+++ b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
@@ -594,6 +594,12 @@ Foam::tmp<Foam::vectorField> Foam::faMesh::calcRawEdgeNormals(int order) const
 
         edgeNormals[edgei].removeCollinear(edgeLine.unitVec());
         edgeNormals[edgei].normalise();
+
+        // Do not allow any mag(val) < SMALL
+        if (mag(edgeNormals[edgei]) < SMALL)
+        {
+            edgeNormals[edgei] = vector::uniform(SMALL);
+        }
     }
 
     return tedgeNormals;
@@ -658,6 +664,12 @@ void Foam::faMesh::calcLe() const
                 edges_[edgei].line(localPoints),
                 edgeNormals[edgei]
             );
+
+            // Do not allow any mag(val) < SMALL
+            if (mag(leVectors[edgei]) < SMALL)
+            {
+                leVectors[edgei] = vector::uniform(SMALL);
+            }
         }
 
         // Copy internal field
@@ -672,6 +684,15 @@ void Foam::faMesh::calcLe() const
         {
             const faPatch& fap = boundary()[patchi];
             bfld[patchi] = fap.patchRawSlice(leVectors);
+
+            for (auto& patchEdge : bfld[patchi])
+            {
+                // Do not allow any mag(val) < SMALL
+                if (mag(patchEdge) < SMALL)
+                {
+                    patchEdge = vector::uniform(SMALL);
+                }
+            }
         }
     }
     else
@@ -692,6 +713,12 @@ void Foam::faMesh::calcLe() const
                     edges_[edgei].line(localPoints),
                     edgeNormals[edgei]
                 );
+
+                // Do not allow any mag(val) < SMALL
+                if (mag(fld[edgei]) < SMALL)
+                {
+                    fld[edgei] = vector::uniform(SMALL);
+                }
             }
         }
 
@@ -715,6 +742,12 @@ void Foam::faMesh::calcLe() const
                     bndEdgeNormals[patchEdgei]
                 );
 
+                // Do not allow any mag(val) < SMALL
+                if (mag(pfld[patchEdgei]) < SMALL)
+                {
+                    pfld[patchEdgei] = vector::uniform(SMALL);
+                }
+
                 ++edgei;
             }
         }
@@ -759,6 +792,13 @@ void Foam::faMesh::calcMagLe() const
         for (const edge& e : internalEdges())
         {
             *iter = e.mag(localPoints);
+
+            // Do not allow any mag(val) < SMALL
+            if (mag(*iter) < SMALL)
+            {
+                *iter = SMALL;
+            }
+
             ++iter;
         }
     }
@@ -774,6 +814,13 @@ void Foam::faMesh::calcMagLe() const
             for (const edge& e : boundary()[patchi].patchSlice(edges_))
             {
                 *iter = e.mag(localPoints);
+
+                // Do not allow any mag(val) < SMALL
+                if (mag(*iter) < SMALL)
+                {
+                    *iter = SMALL;
+                }
+
                 ++iter;
             }
         }
@@ -955,6 +1002,12 @@ void Foam::faMesh::calcS() const
         forAll(fld, facei)
         {
             fld[facei] = Foam::mag(meshFaceAreas[facei]);
+
+            // Do not allow any mag(val) < SMALL
+            if (mag(fld[facei]) < SMALL)
+            {
+                fld[facei] = SMALL;
+            }
         }
     }
     else
@@ -968,6 +1021,13 @@ void Foam::faMesh::calcS() const
         for (const face& f : faces())
         {
             *iter = f.mag(localPoints);
+
+            // Do not allow any mag(val) < SMALL
+            if (mag(*iter) < SMALL)
+            {
+                *iter = SMALL;
+            }
+
             ++iter;
         }
     }
@@ -1028,6 +1088,15 @@ void Foam::faMesh::calcFaceAreaNormals() const
 
         // Make unit normals
         fld.normalise();
+
+        for (auto& f : fld)
+        {
+            // Do not allow any mag(val) < SMALL
+            if (mag(f) < SMALL)
+            {
+                f = vector::uniform(SMALL);
+            }
+        }
     }
 
 
@@ -1122,6 +1191,12 @@ void Foam::faMesh::calcEdgeAreaNormals() const
 
             fld[edgei].removeCollinear(edgeLine.unitVec());
             fld[edgei].normalise();
+
+            // Do not allow any mag(val) < SMALL
+            if (mag(fld[edgei]) < SMALL)
+            {
+                fld[edgei] = vector::uniform(SMALL);
+            }
         }
     }
 
@@ -1148,6 +1223,12 @@ void Foam::faMesh::calcEdgeAreaNormals() const
 
                 pfld[patchEdgei].removeCollinear(edgeLine.unitVec());
                 pfld[patchEdgei].normalise();
+
+                // Do not allow any mag(val) < SMALL
+                if (mag(pfld[patchEdgei]) < SMALL)
+                {
+                    pfld[patchEdgei] = vector::uniform(SMALL);
+                }
             }
         }
     }
diff --git a/src/finiteArea/faMesh/faPatches/faPatch/faPatch.C b/src/finiteArea/faMesh/faPatches/faPatch/faPatch.C
index a6848fc4944..595ffb0702d 100644
--- a/src/finiteArea/faMesh/faPatches/faPatch/faPatch.C
+++ b/src/finiteArea/faMesh/faPatches/faPatch/faPatch.C
@@ -478,7 +478,19 @@ Foam::tmp<Foam::vectorField> Foam::faPatch::delta() const
 {
     // Use patch-normal delta for all non-coupled BCs
     const vectorField nHat(edgeNormals());
-    return nHat*(nHat & (edgeCentres() - edgeFaceCentres()));
+
+    vectorField edgePN(edgeCentres() - edgeFaceCentres());
+
+    // Do not allow any mag(val) < SMALL
+    for (vector& edgei : edgePN)
+    {
+        if (mag(edgei) < SMALL)
+        {
+            edgei = vector::uniform(SMALL);
+        }
+    }
+
+    return nHat*(nHat & edgePN);
 }
 
 
diff --git a/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C b/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
index 153d22fbedc..88e35b3d511 100644
--- a/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
+++ b/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
@@ -115,6 +115,13 @@ void Foam::leastSquaresFaVectors::makeLeastSquaresVectors() const
         label nei = neighbour[facei];
 
         vector d = C[nei] - C[own];
+
+        // Do not allow any mag(val) < SMALL
+        if (mag(d) < SMALL)
+        {
+            d = vector::uniform(SMALL);
+        }
+
         symmTensor wdd = (1.0/magSqr(d))*sqr(d);
 
         dd[own] += wdd;
@@ -169,6 +176,13 @@ void Foam::leastSquaresFaVectors::makeLeastSquaresVectors() const
         label nei = neighbour[facei];
 
         vector d = C[nei] - C[own];
+
+        // Do not allow any mag(val) < SMALL
+        if (mag(d) < SMALL)
+        {
+            d = vector::uniform(SMALL);
+        }
+
         scalar magSfByMagSqrd = 1.0/magSqr(d);
 
         lsP[facei] = magSfByMagSqrd*(invDd[own] & d);
diff --git a/src/finiteArea/interpolation/edgeInterpolation/edgeInterpolation.C b/src/finiteArea/interpolation/edgeInterpolation/edgeInterpolation.C
index ad65518493a..b57f0e75f23 100644
--- a/src/finiteArea/interpolation/edgeInterpolation/edgeInterpolation.C
+++ b/src/finiteArea/interpolation/edgeInterpolation/edgeInterpolation.C
@@ -248,6 +248,12 @@ void Foam::edgeInterpolation::makeLPN() const
             );
 
         lPNIn[edgeI] = (lPE + lEN);
+
+        // Do not allow any mag(val) < SMALL
+        if (mag(lPNIn[edgeI]) < SMALL)
+        {
+            lPNIn[edgeI] = SMALL;
+        }
     }
 
 
@@ -287,7 +293,7 @@ void Foam::edgeInterpolation::makeWeights() const
             false
         ),
         mesh(),
-        dimless
+        dimensionedScalar(dimless, 1)
     );
     edgeScalarField& weightingFactors = *weightingFactors_;
 
@@ -323,15 +329,12 @@ void Foam::edgeInterpolation::makeWeights() const
               + skewCorrEdge
             );
 
-        weightingFactorsIn[edgeI] =
-            lEN
-            /(
-                lPE
-#               ifdef BAD_MESH_STABILISATION
-              + VSMALL
-#               endif
-              + lEN
-            );
+        // weight = (0,1]
+        const scalar lPN = lPE + lEN;
+        if (mag(lPN) > SMALL)
+        {
+            weightingFactorsIn[edgeI] = lEN/lPN;
+        }
     }
 
     forAll(mesh().boundary(), patchI)
@@ -370,7 +373,7 @@ void Foam::edgeInterpolation::makeDeltaCoeffs() const
             false
         ),
         mesh(),
-        dimless/dimLength
+        dimensionedScalar(dimless/dimLength, SMALL)
     );
     edgeScalarField& DeltaCoeffs = *differenceFactors_;
     scalarField& dc = DeltaCoeffs.primitiveFieldRef();
@@ -427,11 +430,12 @@ void Foam::edgeInterpolation::makeDeltaCoeffs() const
         // Edge normal - area tangent
         edgeNormal = normalised(lengths[edgeI]);
 
-        // Delta coefficient as per definition
-//         dc[edgeI] = 1.0/(lPN*(unitDelta & edgeNormal));
-
-        // Stabilised form for bad meshes.  HJ, 23/Jul/2009
-        dc[edgeI] = 1.0/max((lPN*(unitDelta & edgeNormal)), 0.05*lPN);
+        // Do not allow any mag(val) < SMALL
+        const scalar alpha = lPN*(unitDelta & edgeNormal);
+        if (mag(alpha) > SMALL)
+        {
+            dc[edgeI] = scalar(1)/max(alpha, 0.05*lPN);
+        }
     }
 
 
@@ -478,7 +482,7 @@ void Foam::edgeInterpolation::makeCorrectionVectors() const
     const edgeList& edges = mesh().edges();
     const pointField& points = mesh().points();
 
-    scalarField deltaCoeffs(owner.size());
+    scalarField deltaCoeffs(owner.size(), SMALL);
 
     vectorField& CorrVecsIn = CorrVecs.primitiveFieldRef();
 
@@ -499,8 +503,12 @@ void Foam::edgeInterpolation::makeCorrectionVectors() const
         // Edge normal - area tangent
         edgeNormal = normalised(lengths[edgeI]);
 
-        // Delta coeffs
-        deltaCoeffs[edgeI] = 1.0/(unitDelta & edgeNormal);
+        // Do not allow any mag(val) < SMALL
+        const scalar alpha = unitDelta & edgeNormal;
+        if (mag(alpha) > SMALL)
+        {
+            deltaCoeffs[edgeI] = scalar(1)/alpha;
+        }
 
         // Edge correction vector
         CorrVecsIn[edgeI] =
@@ -570,7 +578,7 @@ void Foam::edgeInterpolation::makeSkewCorrectionVectors() const
             false
         ),
         mesh(),
-        dimless
+        dimensionedVector(dimless, Zero)
     );
     edgeVectorField& SkewCorrVecs = *skewCorrectionVectors_;
 
@@ -590,19 +598,22 @@ void Foam::edgeInterpolation::makeSkewCorrectionVectors() const
         const vector& P = C[owner[edgeI]];
         const vector& N = C[neighbour[edgeI]];
         const vector& S = points[edges[edgeI].start()];
-        vector e = edges[edgeI].vec(points);
 
-        const scalar beta = magSqr((N - P)^e);
+        // (T:Eq. 5.4)
+        const vector d(N - P);
+        const vector e(edges[edgeI].vec(points));
+        const vector de(d^e);
+        const scalar alpha = magSqr(de);
 
-        if (beta < ROOTVSMALL)
+        if (alpha < SMALL)
         {
             // Too small - skew correction remains zero
             continue;
         }
+        const scalar beta = -((d^(S - P)) & de)/alpha;
 
-        const scalar alpha = -(((N - P)^(S - P))&((N - P)^e))/beta;
-
-        vector E(S + alpha*e);
+        // (T:Eq. 5.3)
+        const vector E(S + beta*e);
 
         SkewCorrVecs[edgeI] = Ce[edgeI] - E;
     }
@@ -630,28 +641,26 @@ void Foam::edgeInterpolation::makeSkewCorrectionVectors() const
                 const vector& P = C[edgeFaces[edgeI]];
                 const vector& N = ngbC[edgeI];
                 const vector& S = points[patchEdges[edgeI].start()];
-                vector e = patchEdges[edgeI].vec(points);
 
-                const scalar beta = magSqr((N - P)^e);
+                // (T:Eq. 5.4)
+                const vector d(N - P);
+                const vector e(patchEdges[edgeI].vec(points));
+                const vector de(d^e);
+                const scalar alpha = magSqr(de);
 
-                if (beta < ROOTVSMALL)
+                if (alpha < SMALL)
                 {
                     // Too small - skew correction remains zero
                     continue;
                 }
+                const scalar beta = -((d^(S - P)) & de)/alpha;
 
-                const scalar alpha = -(((N - P)^(S - P))&((N - P)^e))/beta;
-
-                vector E(S + alpha*e);
+                const vector E(S + beta*e);
 
                 patchSkewCorrVecs[edgeI] =
                     Ce.boundaryField()[patchI][edgeI] - E;
             }
         }
-        else
-        {
-            patchSkewCorrVecs = vector::zero;
-        }
     }
 
     #ifdef FA_SKEW_CORRECTION
diff --git a/src/finiteArea/interpolation/edgeInterpolation/schemes/NVDscheme/faNVDscheme.C b/src/finiteArea/interpolation/edgeInterpolation/schemes/NVDscheme/faNVDscheme.C
index c659ad3f4f0..8b7c6850109 100644
--- a/src/finiteArea/interpolation/edgeInterpolation/schemes/NVDscheme/faNVDscheme.C
+++ b/src/finiteArea/interpolation/edgeInterpolation/schemes/NVDscheme/faNVDscheme.C
@@ -108,6 +108,12 @@ Foam::tmp<Foam::edgeScalarField> Foam::faNVDscheme<Type,NVDweight>::weights
         d.normalise();
         d *= mesh.edgeInterpolation::lPN().internalField()[edge];
 
+        // Do not allow any mag(val) < SMALL
+        if (mag(d) < SMALL)
+        {
+            d = vector::uniform(SMALL);
+        }
+
         weights[edge] =
             this->weight
             (
@@ -189,6 +195,12 @@ Foam::tmp<Foam::edgeScalarField> Foam::faNVDscheme<Type,NVDweight>::weights
                 d.normalise();
                 d *= pLPN[edgeI];
 
+                // Do not allow any mag(val) < SMALL
+                if (mag(d) < SMALL)
+                {
+                    d = vector::uniform(SMALL);
+                }
+
                 pWeights[edgeI] =
                     this->weight
                     (
-- 
GitLab