diff --git a/applications/test/invTensor/Make/files b/applications/test/invTensor/Make/files
new file mode 100644
index 0000000000..c99507752a
--- /dev/null
+++ b/applications/test/invTensor/Make/files
@@ -0,0 +1,3 @@
+Test-invTensor.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-invTensor
diff --git a/applications/test/invTensor/Make/options b/applications/test/invTensor/Make/options
new file mode 100644
index 0000000000..18e6fe47af
--- /dev/null
+++ b/applications/test/invTensor/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = */
+/* EXE_LIBS = */
diff --git a/applications/test/invTensor/Test-invTensor.C b/applications/test/invTensor/Test-invTensor.C
new file mode 100644
index 0000000000..b16b7d3666
--- /dev/null
+++ b/applications/test/invTensor/Test-invTensor.C
@@ -0,0 +1,96 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM 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.
+
+ OpenFOAM 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 OpenFOAM. If not, see .
+
+Application
+ Test-invTensor
+
+Description
+ Tests for regular and corner cases of tensor inversion.
+
+\*---------------------------------------------------------------------------*/
+
+#include "tensor.H"
+#include "symmTensor.H"
+#include "transform.H"
+#include "unitConversion.H"
+#include "Random.H"
+#include "scalar.H"
+#include "complex.H"
+#include "sigFpe.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template
+void test_invert(const TensorType& tt)
+{
+ Info<< pTraits::typeName << nl
+ << "ten : " << tt << nl;
+
+ // Non-failsafe. try/catch does not work here
+ if (tt.det() < ROOTVSMALL)
+ {
+ Info<< "inv : " << "nan/inf" << nl;
+ }
+ else
+ {
+ Info<< "inv : " << tt.inv() << nl;
+ }
+
+ // Failsafe
+ Info<< "inv : " << tt.safeInv() << nl;
+
+ Info<< nl;
+}
+
+
+// * * * * * * * * * * * * * * * Main Program * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+ // Run with FOAM_SIGFPE=true to ensure we see divide by zero
+ Foam::sigFpe::set(true);
+
+ {
+ symmTensor st(1e-3, 0, 0, 1e-3, 0, 1e-6);
+ test_invert(st);
+ }
+
+ {
+ symmTensor st(1e-3, 0, 0, 1e-3, 0, 1e-30);
+ test_invert(st);
+ }
+
+ {
+ symmTensor st(1e-3, 0, 0, 1e-3, 0, 0);
+ test_invert(st);
+ }
+
+ return 0;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H b/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H
index 2d7cbf6016..a435341551 100644
--- a/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H
+++ b/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H
@@ -50,7 +50,7 @@ namespace Foam
typedef dimensioned dimensionedSymmTensor;
-// global functions
+// Global Functions
dimensionedSymmTensor sqr(const dimensionedVector&);
dimensionedSymmTensor innerSqr(const dimensionedSymmTensor&);
@@ -65,7 +65,7 @@ dimensionedSymmTensor cof(const dimensionedSymmTensor&);
dimensionedSymmTensor inv(const dimensionedSymmTensor&);
-// global operators
+// Global Operators
//- Hodge Dual operator (tensor -> vector)
dimensionedVector operator*(const dimensionedSymmTensor&);
diff --git a/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H b/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H
index 8de0683efe..a66aea3c07 100644
--- a/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H
+++ b/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H
@@ -51,8 +51,7 @@ namespace Foam
typedef dimensioned dimensionedTensor;
-
-// global functions
+// Global Functions
dimensionedScalar tr(const dimensionedTensor&);
dimensionedTensor dev(const dimensionedTensor&);
@@ -68,7 +67,7 @@ dimensionedVector eigenValues(const dimensionedSymmTensor&);
dimensionedTensor eigenVectors(const dimensionedSymmTensor&);
-// global operators
+// Global Operators
//- Hodge Dual operator (tensor -> vector)
dimensionedVector operator*(const dimensionedTensor&);
diff --git a/src/OpenFOAM/fields/Fields/symmTensorField/symmTensorField.C b/src/OpenFOAM/fields/Fields/symmTensorField/symmTensorField.C
index 1be5183d4c..afdf86c186 100644
--- a/src/OpenFOAM/fields/Fields/symmTensorField/symmTensorField.C
+++ b/src/OpenFOAM/fields/Fields/symmTensorField/symmTensorField.C
@@ -51,55 +51,10 @@ UNARY_FUNCTION(symmTensor, symmTensor, dev2)
UNARY_FUNCTION(scalar, symmTensor, det)
UNARY_FUNCTION(symmTensor, symmTensor, cof)
-void inv(Field& result, const UList& tf1)
+void inv(Field& result, const UList& f1)
{
- if (result.empty() || tf1.empty())
- {
- return;
- }
-
- // Attempting to identify 2-D cases
- const scalar minThreshold = SMALL * magSqr(tf1[0]);
-
- const bool small_xx = (magSqr(tf1[0].xx()) < minThreshold);
- const bool small_yy = (magSqr(tf1[0].yy()) < minThreshold);
- const bool small_zz = (magSqr(tf1[0].zz()) < minThreshold);
-
- if (small_xx || small_yy || small_zz)
- {
- const vector adjust
- (
- (small_xx ? 1 : 0),
- (small_yy ? 1 : 0),
- (small_zz ? 1 : 0)
- );
-
- // Cannot use TFOR_ALL_F_OP_FUNC_F (additional operations)
-
- const label loopLen = (result).size();
-
- /* pragmas... */
- for (label i = 0; i < loopLen; ++i)
- {
- symmTensor work(tf1[i]);
- work.addDiag(adjust);
-
- result[i] = Foam::inv(work);
- result[i].subtractDiag(adjust);
- }
- }
- else
- {
- // Same as TFOR_ALL_F_OP_FUNC_F
-
- const label loopLen = (result).size();
-
- /* pragmas... */
- for (label i = 0; i < loopLen; ++i)
- {
- result[i] = Foam::inv(tf1[i]);
- }
- }
+ // With 'failsafe' invert
+ TFOR_ALL_F_OP_F_FUNC(symmTensor, result, =, symmTensor, f1, safeInv)
}
tmp inv(const UList& tf)
diff --git a/src/OpenFOAM/fields/Fields/tensorField/tensorField.C b/src/OpenFOAM/fields/Fields/tensorField/tensorField.C
index c4f76fc978..9b343fa5a0 100644
--- a/src/OpenFOAM/fields/Fields/tensorField/tensorField.C
+++ b/src/OpenFOAM/fields/Fields/tensorField/tensorField.C
@@ -50,55 +50,10 @@ UNARY_FUNCTION(tensor, tensor, dev2)
UNARY_FUNCTION(scalar, tensor, det)
UNARY_FUNCTION(tensor, tensor, cof)
-void inv(Field& result, const UList& tf1)
+void inv(Field& result, const UList& f1)
{
- if (result.empty() || tf1.empty())
- {
- return;
- }
-
- // Attempting to identify 2-D cases
- const scalar minThreshold = SMALL * magSqr(tf1[0]);
-
- const bool small_xx = (magSqr(tf1[0].xx()) < minThreshold);
- const bool small_yy = (magSqr(tf1[0].yy()) < minThreshold);
- const bool small_zz = (magSqr(tf1[0].zz()) < minThreshold);
-
- if (small_xx || small_yy || small_zz)
- {
- const vector adjust
- (
- (small_xx ? 1 : 0),
- (small_yy ? 1 : 0),
- (small_zz ? 1 : 0)
- );
-
- // Cannot use TFOR_ALL_F_OP_FUNC_F (additional operations)
-
- const label loopLen = (result).size();
-
- /* pragmas... */
- for (label i = 0; i < loopLen; ++i)
- {
- tensor work(tf1[i]);
- work.addDiag(adjust);
-
- result[i] = Foam::inv(work);
- result[i].subtractDiag(adjust);
- }
- }
- else
- {
- // Same as TFOR_ALL_F_OP_FUNC_F
-
- const label loopLen = (result).size();
-
- /* pragmas... */
- for (label i = 0; i < loopLen; ++i)
- {
- result[i] = Foam::inv(tf1[i]);
- }
- }
+ // With 'failsafe' invert
+ TFOR_ALL_F_OP_F_FUNC(tensor, result, =, tensor, f1, safeInv)
}
tmp inv(const UList& tf)
diff --git a/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H b/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H
index e365081023..2f38d3e481 100644
--- a/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H
+++ b/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H
@@ -245,15 +245,20 @@ public:
//- The 2D determinant by excluding given direction
inline Cmpt det2D(const direction excludeCmpt) const;
- //- Return cofactor matrix
+ //- Return adjunct matrix (transpose of cofactor matrix)
+ inline SymmTensor adjunct() const;
+
+ //- Return cofactor matrix (transpose of adjunct matrix)
inline SymmTensor cof() const;
- //- Return 2D cofactor matrix by excluding given direction
- inline SymmTensor cof2D(const direction excludeCmpt) const;
+ //- Return 2D adjunct matrix by excluding given direction
+ inline SymmTensor adjunct2D(const direction excludeCmpt) const;
- //- Return inverse, with optional (ad hoc) failsafe handling
- //- of 2D tensors
- inline SymmTensor inv(const bool failsafe=false) const;
+ //- Return inverse
+ inline SymmTensor inv() const;
+
+ //- Return inverse, with (ad hoc) failsafe handling of 2D tensors
+ inline SymmTensor safeInv() const;
//- Return inverse of 2D tensor (by excluding given direction)
inline SymmTensor inv2D(const direction excludeCmpt) const;
diff --git a/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H b/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H
index 47c10fa13b..5c28c6c2bc 100644
--- a/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H
+++ b/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H
@@ -272,24 +272,28 @@ inline Cmpt Foam::SymmTensor::det2D(const direction excludeCmpt) const
template
-inline Foam::SymmTensor Foam::SymmTensor::cof() const
+inline Foam::SymmTensor Foam::SymmTensor::adjunct() const
{
+ // symmetric: cof() == adjunct()
return SymmTensor
(
- yy()*zz() - yz()*yz(),
- xz()*yz() - xy()*zz(),
- xy()*yz() - xz()*yy(),
-
- xx()*zz() - xz()*xz(),
- xy()*xz() - xx()*yz(),
-
- xx()*yy() - xy()*xy()
+ yy()*zz() - yz()*yz(), xz()*yz() - xy()*zz(), xy()*yz() - xz()*yy(),
+ xx()*zz() - xz()*xz(), xy()*xz() - xx()*yz(),
+ xx()*yy() - xy()*xy()
);
}
template
-inline Foam::SymmTensor Foam::SymmTensor::cof2D
+inline Foam::SymmTensor Foam::SymmTensor::cof() const
+{
+ // symmetric: cof() == adjunct()
+ return this->adjunct();
+}
+
+
+template
+inline Foam::SymmTensor Foam::SymmTensor::adjunct2D
(
const direction excludeCmpt
) const
@@ -335,7 +339,84 @@ inline Foam::SymmTensor Foam::SymmTensor::inv2D
{
const Cmpt detval = this->det2D(excludeCmpt);
- return this->cof2D(excludeCmpt).T()/detval;
+ return this->adjunct2D(excludeCmpt)/detval;
+}
+
+
+// Invert without much error handling
+template
+inline Foam::SymmTensor Foam::SymmTensor::inv() const
+{
+ const Cmpt detval = this->det();
+
+ #ifdef FULLDEBUG
+ if (mag(detval) < VSMALL)
+ {
+ FatalErrorInFunction
+ << "Tensor not properly invertible, determinant:"
+ << detval << " tensor:" << *this << nl
+ << abort(FatalError);
+ }
+ #endif
+
+ return this->adjunct()/detval;
+}
+
+
+// Invert with some error handling
+template
+inline Foam::SymmTensor Foam::SymmTensor::safeInv() const
+{
+ {
+ // Attempt to identify and handle 2-D cases.
+ // - use diagSqr instead of magSqr for fewer operations
+ const scalar magSqr_xx = Foam::magSqr(xx());
+ const scalar magSqr_yy = Foam::magSqr(yy());
+ const scalar magSqr_zz = Foam::magSqr(zz());
+
+ // SMALL: 1e-15 (double), 1e-6 (float), but 1e-6 may be adequate
+
+ const scalar threshold = SMALL * (magSqr_xx + magSqr_yy + magSqr_zz);
+
+ const bool small_xx = (magSqr_xx < threshold);
+ const bool small_yy = (magSqr_yy < threshold);
+ const bool small_zz = (magSqr_zz < threshold);
+
+ if (small_xx || small_yy || small_zz)
+ {
+ SymmTensor work(*this);
+
+ if (small_xx) { work.xx() += pTraits::one; }
+ if (small_yy) { work.yy() += pTraits::one; }
+ if (small_zz) { work.zz() += pTraits::one; }
+
+ const Cmpt detval = work.det();
+
+ if (mag(detval) < ROOTVSMALL)
+ {
+ // Appears to be nearly zero - leave untouched?
+ return SymmTensor(Zero);
+ }
+
+ work = work.adjunct()/detval;
+
+ if (small_xx) { work.xx() -= pTraits::one; }
+ if (small_yy) { work.yy() -= pTraits::one; }
+ if (small_zz) { work.zz() -= pTraits::one; }
+
+ return work;
+ }
+ }
+
+ const Cmpt detval = this->det();
+
+ if (mag(detval) < ROOTVSMALL)
+ {
+ // Appears to be nearly zero - leave untouched?
+ return SymmTensor(Zero);
+ }
+
+ return this->adjunct()/detval;
}
@@ -438,7 +519,7 @@ inline SymmTensor inv(const SymmTensor& st, const Cmpt detval)
}
#endif
- return st.cof().T()/detval;
+ return st.adjunct()/detval;
}
@@ -446,62 +527,7 @@ inline SymmTensor inv(const SymmTensor& st, const Cmpt detval)
template
inline SymmTensor inv(const SymmTensor& st)
{
- return inv(st, st.det());
-}
-
-
-template
-inline SymmTensor SymmTensor::inv(const bool failsafe) const
-{
- const Cmpt detval = this->det();
-
- if (failsafe)
- {
- // Attempt to identify and handle 2-D cases.
- // - use diagSqr instead of magSqr for fewer operations
- const scalar magSqr_xx = Foam::magSqr(xx());
- const scalar magSqr_yy = Foam::magSqr(yy());
- const scalar magSqr_zz = Foam::magSqr(zz());
-
- const scalar threshold = SMALL * (magSqr_xx + magSqr_yy + magSqr_zz);
-
- const bool small_xx = (magSqr_xx < threshold);
- const bool small_yy = (magSqr_yy < threshold);
- const bool small_zz = (magSqr_zz < threshold);
-
- if (small_xx || small_yy || small_zz)
- {
- SymmTensor work(*this);
-
- if (small_xx) { work.xx() += pTraits::one; }
- if (small_yy) { work.yy() += pTraits::one; }
- if (small_zz) { work.zz() += pTraits::one; }
-
- work = Foam::inv(work, work.det());
-
- if (small_xx) { work.xx() -= pTraits::one; }
- if (small_yy) { work.yy() -= pTraits::one; }
- if (small_zz) { work.zz() -= pTraits::one; }
-
- return work;
- }
- else if (mag(detval) < VSMALL)
- {
- // Really appears to be zero - leave untouched?
- return SymmTensor(Zero);
- }
- }
- #ifdef FULLDEBUG
- else if (mag(detval) < VSMALL)
- {
- FatalErrorInFunction
- << "Tensor not properly invertible, determinant:"
- << detval << " tensor:" << *this << nl
- << abort(FatalError);
- }
- #endif
-
- return this->cof().T()/detval;
+ return st.inv();
}
diff --git a/src/OpenFOAM/primitives/SymmTensor/symmTensor/symmTensor.C b/src/OpenFOAM/primitives/SymmTensor/symmTensor/symmTensor.C
index 8869e9fb56..fb41001b1f 100644
--- a/src/OpenFOAM/primitives/SymmTensor/symmTensor/symmTensor.C
+++ b/src/OpenFOAM/primitives/SymmTensor/symmTensor/symmTensor.C
@@ -340,9 +340,9 @@ Foam::tensor Foam::eigenVectors(const symmTensor& T)
Foam::symmTensor Foam::pinv(const symmTensor& st)
{
- const scalar dt = det(st);
+ const scalar detval = st.det();
- if (dt < ROOTVSMALL)
+ if (detval < ROOTVSMALL)
{
// Fall back to pseudo inverse
scalarRectangularMatrix mat(3, 3);
@@ -361,7 +361,7 @@ Foam::symmTensor Foam::pinv(const symmTensor& st)
);
}
- return inv(st, dt);
+ return Foam::inv(st, detval);
}
diff --git a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2D.H b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2D.H
index 1308b015e9..e70a951c9b 100644
--- a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2D.H
+++ b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2D.H
@@ -152,7 +152,10 @@ public:
//- The determinate
inline Cmpt det() const;
- //- Return cofactor matrix
+ //- Return adjunct matrix (transpose of cofactor matrix)
+ inline SymmTensor2D adjunct() const;
+
+ //- Return cofactor matrix (transpose of adjunct matrix)
inline SymmTensor2D cof() const;
//- Return inverse
diff --git a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H
index b583b669c5..4eba6e6cc3 100644
--- a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H
+++ b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H
@@ -109,16 +109,45 @@ inline Cmpt Foam::SymmTensor2D::det() const
template
-inline Foam::SymmTensor2D Foam::SymmTensor2D::cof() const
+inline Foam::SymmTensor2D Foam::SymmTensor2D::adjunct() const
{
+ // symmetric: cof() == adjunct()
return SymmTensor2D
(
- yy(), -yx(),
+ yy(), -xy(),
xx()
);
}
+template
+inline Foam::SymmTensor2D Foam::SymmTensor2D::cof() const
+{
+ // symmetric: cof() == adjunct()
+ return this->adjunct();
+}
+
+
+// Invert without much error handling
+template
+inline Foam::SymmTensor2D Foam::SymmTensor2D::inv() const
+{
+ const Cmpt detval = this->det();
+
+ #ifdef FULLDEBUG
+ if (mag(detval) < SMALL)
+ {
+ FatalErrorInFunction
+ << "SymmTensor2D not properly invertible, determinant:"
+ << detval << " tensor:" << *this << nl
+ << abort(FatalError);
+ }
+ #endif
+
+ return this->adjunct()/detval;
+}
+
+
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template
@@ -220,7 +249,7 @@ inline SymmTensor2D inv(const SymmTensor2D& st, const Cmpt detval)
}
#endif
- return st.cof().T()/detval;
+ return st.adjunct()/detval;
}
@@ -228,15 +257,7 @@ inline SymmTensor2D inv(const SymmTensor2D& st, const Cmpt detval)
template
inline SymmTensor2D inv(const SymmTensor2D& st)
{
- return inv(st, st.det());
-}
-
-
-// The inverse of this SymmTensor2D
-template
-inline SymmTensor2D SymmTensor2D::inv() const
-{
- return Foam::inv(*this, this->det());
+ return st.inv();
}
diff --git a/src/OpenFOAM/primitives/Tensor/Tensor.H b/src/OpenFOAM/primitives/Tensor/Tensor.H
index 7391d06865..601e004416 100644
--- a/src/OpenFOAM/primitives/Tensor/Tensor.H
+++ b/src/OpenFOAM/primitives/Tensor/Tensor.H
@@ -287,15 +287,20 @@ public:
//- The 2D determinant by excluding given direction
inline Cmpt det2D(const direction excludeCmpt) const;
- //- Return cofactor matrix
+ //- Return adjunct matrix (transpose of cofactor matrix)
+ inline Tensor adjunct() const;
+
+ //- Return cofactor matrix (transpose of adjunct matrix)
inline Tensor cof() const;
- //- Return 2D cofactor matrix by excluding given direction
- inline Tensor cof2D(const direction excludeCmpt) const;
+ //- Return 2D adjunct matrix by excluding given direction
+ inline Tensor adjunct2D(const direction excludeCmpt) const;
- //- Return inverse, with optional (ad hoc) failsafe handling
- //- of 2D tensors
- inline Tensor inv(const bool failsafe=false) const;
+ //- Return inverse
+ inline Tensor inv() const;
+
+ //- Return inverse, with (ad hoc) failsafe handling of 2D tensors
+ inline Tensor safeInv() const;
//- Return inverse of 2D tensor (by excluding given direction)
inline Tensor inv2D(const direction excludeCmpt) const;
diff --git a/src/OpenFOAM/primitives/Tensor/TensorI.H b/src/OpenFOAM/primitives/Tensor/TensorI.H
index 6e24c0f506..b389f12ee1 100644
--- a/src/OpenFOAM/primitives/Tensor/TensorI.H
+++ b/src/OpenFOAM/primitives/Tensor/TensorI.H
@@ -468,27 +468,26 @@ inline Cmpt Foam::Tensor::det2D(const direction excludeCmpt) const
template
-inline Foam::Tensor Foam::Tensor::cof() const
+inline Foam::Tensor Foam::Tensor::adjunct() const
{
return Tensor
(
- yy()*zz() - zy()*yz(),
- zx()*yz() - yx()*zz(),
- yx()*zy() - yy()*zx(),
-
- xz()*zy() - xy()*zz(),
- xx()*zz() - xz()*zx(),
- xy()*zx() - xx()*zy(),
-
- xy()*yz() - xz()*yy(),
- yx()*xz() - xx()*yz(),
- xx()*yy() - yx()*xy()
+ yy()*zz() - zy()*yz(), xz()*zy() - xy()*zz(), xy()*yz() - xz()*yy(),
+ zx()*yz() - yx()*zz(), xx()*zz() - xz()*zx(), yx()*xz() - xx()*yz(),
+ yx()*zy() - yy()*zx(), xy()*zx() - xx()*zy(), xx()*yy() - yx()*xy()
);
}
template
-inline Foam::Tensor Foam::Tensor::cof2D
+inline Foam::Tensor Foam::Tensor::cof() const
+{
+ return this->adjunct().T();
+}
+
+
+template
+inline Foam::Tensor Foam::Tensor::adjunct2D
(
const direction excludeCmpt
) const
@@ -500,8 +499,8 @@ inline Foam::Tensor Foam::Tensor::cof2D
return Tensor
(
Zero, Zero, Zero,
- Zero, zz(), -zy(),
- Zero, -yz(), yy()
+ Zero, zz(), -yz(),
+ Zero, -zy(), yy()
);
}
@@ -509,9 +508,9 @@ inline Foam::Tensor Foam::Tensor::cof2D
{
return Tensor
(
- zz(), Zero, -zx(),
+ zz(), Zero, -xz(),
Zero, Zero, Zero,
- -xz(), Zero, xx()
+ -zx(), Zero, xx()
);
}
}
@@ -519,8 +518,8 @@ inline Foam::Tensor Foam::Tensor::cof2D
// Fall-through: Eliminate z
return Tensor
(
- yy(), -yx(), Zero,
- -xy(), xx(), Zero,
+ yy(), -xy(), Zero,
+ -yx(), xx(), Zero,
Zero, Zero, Zero
);
}
@@ -534,7 +533,7 @@ inline Foam::Tensor Foam::Tensor::inv2D
{
const Cmpt detval = this->det2D(excludeCmpt);
- return this->cof2D(excludeCmpt).T()/detval;
+ return this->adjunct2D(excludeCmpt)/detval;
}
@@ -576,6 +575,84 @@ Foam::Tensor::schur(const Tensor& t2) const
}
+// Invert without much error handling
+template
+inline Foam::Tensor Foam::Tensor::inv() const
+{
+ const Cmpt detval = this->det();
+
+ #ifdef FULLDEBUG
+ if (mag(detval) < VSMALL)
+ {
+ FatalErrorInFunction
+ << "Tensor not properly invertible, determinant:"
+ << detval << " tensor:" << *this << nl
+ << abort(FatalError);
+ }
+ #endif
+
+ return this->adjunct()/detval;
+}
+
+
+// Invert with some error handling
+template
+inline Foam::Tensor Foam::Tensor::safeInv() const
+{
+ {
+ // Attempt to identify and handle 2-D cases
+ // - use diagSqr instead of magSqr for fewer operations
+
+ const scalar magSqr_xx = Foam::magSqr(xx());
+ const scalar magSqr_yy = Foam::magSqr(yy());
+ const scalar magSqr_zz = Foam::magSqr(zz());
+
+ // SMALL: 1e-15 (double), 1e-6 (float), but 1e-6 may be adequate
+
+ const scalar threshold = SMALL * (magSqr_xx + magSqr_yy + magSqr_zz);
+
+ const bool small_xx = (magSqr_xx < threshold);
+ const bool small_yy = (magSqr_yy < threshold);
+ const bool small_zz = (magSqr_zz < threshold);
+
+ if (small_xx || small_yy || small_zz)
+ {
+ Tensor work(*this);
+
+ if (small_xx) { work.xx() += pTraits::one; }
+ if (small_yy) { work.yy() += pTraits::one; }
+ if (small_zz) { work.zz() += pTraits::one; }
+
+ const Cmpt detval = work.det();
+
+ if (mag(detval) < ROOTVSMALL)
+ {
+ // Appears to be nearly zero - leave untouched?
+ return Tensor(Zero);
+ }
+
+ work = work.adjunct()/detval;
+
+ if (small_xx) { work.xx() -= pTraits::one; }
+ if (small_yy) { work.yy() -= pTraits::one; }
+ if (small_zz) { work.zz() -= pTraits::one; }
+
+ return work;
+ }
+ }
+
+ const Cmpt detval = this->det();
+
+ if (mag(detval) < ROOTVSMALL)
+ {
+ // Appears to be nearly zero - leave untouched?
+ return Tensor(Zero);
+ }
+
+ return this->adjunct()/detval;
+}
+
+
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template
@@ -750,7 +827,7 @@ inline Tensor inv(const Tensor& t, const Cmpt detval)
}
#endif
- return t.cof().T()/detval;
+ return t.adjunct()/detval;
}
@@ -758,63 +835,7 @@ inline Tensor inv(const Tensor& t, const Cmpt detval)
template
inline Tensor inv(const Tensor& t)
{
- return inv(t, t.det());
-}
-
-
-template
-inline Tensor Tensor::inv(const bool failsafe) const
-{
- const Cmpt detval = this->det();
-
- if (failsafe)
- {
- // Attempt to identify and handle 2-D cases
- // - use diagSqr instead of magSqr for fewer operations
-
- const scalar magSqr_xx = Foam::magSqr(xx());
- const scalar magSqr_yy = Foam::magSqr(yy());
- const scalar magSqr_zz = Foam::magSqr(zz());
-
- const scalar threshold = SMALL * (magSqr_xx + magSqr_yy + magSqr_zz);
-
- const bool small_xx = (magSqr_xx < threshold);
- const bool small_yy = (magSqr_yy < threshold);
- const bool small_zz = (magSqr_zz < threshold);
-
- if (small_xx || small_yy || small_zz)
- {
- Tensor work(*this);
-
- if (small_xx) { work.xx() += pTraits::one; }
- if (small_yy) { work.yy() += pTraits::one; }
- if (small_zz) { work.zz() += pTraits::one; }
-
- work = Foam::inv(work, work.det());
-
- if (small_xx) { work.xx() -= pTraits::one; }
- if (small_yy) { work.yy() -= pTraits::one; }
- if (small_zz) { work.zz() -= pTraits::one; }
-
- return work;
- }
- else if (mag(detval) < VSMALL)
- {
- // Really appears to be zero - leave untouched?
- return Tensor(Zero);
- }
- }
- #ifdef FULLDEBUG
- else if (mag(detval) < VSMALL)
- {
- FatalErrorInFunction
- << "Tensor not properly invertible, determinant:"
- << detval << " tensor:" << *this << nl
- << abort(FatalError);
- }
- #endif
-
- return this->cof().T()/detval;
+ return t.inv();
}
diff --git a/src/OpenFOAM/primitives/Tensor/floats/tensor.C b/src/OpenFOAM/primitives/Tensor/floats/tensor.C
index dfe5851bd0..d26aad40a4 100644
--- a/src/OpenFOAM/primitives/Tensor/floats/tensor.C
+++ b/src/OpenFOAM/primitives/Tensor/floats/tensor.C
@@ -304,9 +304,9 @@ Foam::Tensor Foam::eigenVectors(const tensor& T)
Foam::tensor Foam::pinv(const tensor& t)
{
- const scalar dt = det(t);
+ const scalar detval = t.det();
- if (dt < ROOTVSMALL)
+ if (detval < ROOTVSMALL)
{
// Fall back to pseudo inverse
scalarRectangularMatrix mat(3, 3);
@@ -325,7 +325,7 @@ Foam::tensor Foam::pinv(const tensor& t)
);
}
- return inv(t, dt);
+ return Foam::inv(t, detval);
}
diff --git a/src/OpenFOAM/primitives/Tensor2D/Tensor2D.H b/src/OpenFOAM/primitives/Tensor2D/Tensor2D.H
index f12b68104f..69f8efd091 100644
--- a/src/OpenFOAM/primitives/Tensor2D/Tensor2D.H
+++ b/src/OpenFOAM/primitives/Tensor2D/Tensor2D.H
@@ -212,7 +212,10 @@ public:
//- The determinate
inline Cmpt det() const;
- //- Return cofactor matrix
+ //- Return adjunct matrix (transpose of cofactor matrix)
+ inline Tensor2D adjunct() const;
+
+ //- Return cofactor matrix (transpose of adjunct matrix)
inline Tensor2D cof() const;
//- Return inverse
diff --git a/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H b/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H
index 9367f4c82b..1acbb876a8 100644
--- a/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H
+++ b/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H
@@ -318,16 +318,23 @@ inline Cmpt Foam::Tensor2D::det() const
template
-inline Foam::Tensor2D Foam::Tensor2D::cof() const
+inline Foam::Tensor2D Foam::Tensor2D::adjunct() const
{
return Tensor2D
(
- yy(), -yx(),
- -xy(), xx()
+ yy(), -xy(),
+ -yx(), xx()
);
}
+template
+inline Foam::Tensor2D Foam::Tensor2D::cof() const
+{
+ return this->adjunct().T();
+}
+
+
template
inline Foam::Tensor2D
Foam::Tensor2D::inner(const Tensor2D& t2) const
@@ -359,6 +366,26 @@ Foam::Tensor2D::schur(const Tensor2D& t2) const
}
+// Invert without much error handling
+template
+inline Foam::Tensor2D Foam::Tensor2D::inv() const
+{
+ const Cmpt detval = this->det();
+
+ #ifdef FULLDEBUG
+ if (mag(detval) < SMALL)
+ {
+ FatalErrorInFunction
+ << "SymmTensor2D not properly invertible, determinant:"
+ << detval << " tensor:" << *this << nl
+ << abort(FatalError);
+ }
+ #endif
+
+ return this->adjunct()/detval;
+}
+
+
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template
@@ -463,7 +490,7 @@ inline Cmpt det(const Tensor2D& t)
}
-//- Return the cofactor Tensor2D of a Tensor2D
+//- Return the cofactor of a Tensor2D
template
inline Tensor2D cof(const Tensor2D& t)
{
@@ -485,7 +512,7 @@ inline Tensor2D inv(const Tensor2D& t, const Cmpt detval)
}
#endif
- return t.cof().T()/detval;
+ return t.adjunct()/detval;
}
@@ -493,15 +520,7 @@ inline Tensor2D inv(const Tensor2D& t, const Cmpt detval)
template
inline Tensor2D inv(const Tensor2D& t)
{
- return inv(t, t.det());
-}
-
-
-// Return the inverse of this Tensor2D
-template
-inline Tensor2D Tensor2D::inv() const
-{
- return Foam::inv(*this, this->det());
+ return t.inv();
}
diff --git a/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C b/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
index f2bd352bbd..e40618b0a0 100644
--- a/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
+++ b/src/finiteArea/finiteArea/gradSchemes/leastSquaresFaGrad/leastSquaresFaVectors.C
@@ -148,26 +148,8 @@ void Foam::leastSquaresFaVectors::makeLeastSquaresVectors() const
}
- // Invert the dd tensor.
-
- // Cannot rely on the usual field inv() since that only uses the
- // first element to guess if the remaining tensors are 2D (or
- // singular). We, however, can have a mixture (eg, skipping over
- // zero-length edges can yield a zero). Instead use the
- // 'failsafe' inv() that checks each tensor (#2724)
-
- // Fragile: const symmTensorField invDd(inv(dd));
-
- symmTensorField invDd(dd.size());
- {
- const label loopLen = dd.size();
-
- /* pragmas... */
- for (label i = 0; i < loopLen; ++i)
- {
- invDd[i] = dd[i].inv(true); // With 'failsafe' handling
- }
- }
+ // Invert the dd tensors - including failsafe checks
+ const symmTensorField invDd(inv(dd));
// Revisit all faces and calculate the lsP and lsN vectors
diff --git a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/invDistLeastSquaresVectors.C b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/invDistLeastSquaresVectors.C
index 5bfe9a515f..584968db7d 100644
--- a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/invDistLeastSquaresVectors.C
+++ b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/invDistLeastSquaresVectors.C
@@ -104,14 +104,17 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
const label nei = neighbour[facei];
const vector d(C[nei] - C[own]);
- const symmTensor wdd(sqr(d)/magSqr(d));
- dd[own] += wdd;
- dd[nei] += wdd;
+ const scalar magSqrDist = d.magSqr();
+
+ if (magSqrDist > ROOTVSMALL)
+ {
+ const symmTensor wdd(sqr(d)/magSqrDist);
+ dd[own] += wdd;
+ dd[nei] += wdd;
+ }
}
-
- surfaceVectorField::Boundary& blsP =
- pVectors_.boundaryField();
+ auto& blsP = pVectors_.boundaryField();
forAll(blsP, patchi)
{
@@ -126,13 +129,17 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
forAll(pd, patchFacei)
{
const vector& d = pd[patchFacei];
+ const scalar magSqrDist = d.magSqr();
- dd[faceCells[patchFacei]] += sqr(d)/magSqr(d);
+ if (magSqrDist > ROOTVSMALL)
+ {
+ dd[faceCells[patchFacei]] += sqr(d)/magSqrDist;
+ }
}
}
- // Invert the dd tensor
+ // Invert the dd tensors - including failsafe checks
const symmTensorField invDd(inv(dd));
@@ -143,9 +150,18 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
const label nei = neighbour[facei];
const vector d(C[nei] - C[own]);
+ const scalar magSqrDist = d.magSqr();
- pVectors_[facei] = (invDd[own] & d)/magSqr(d);
- nVectors_[facei] = -(invDd[nei] & d)/magSqr(d);
+ if (magSqrDist > ROOTVSMALL)
+ {
+ pVectors_[facei] = (invDd[own] & d)/magSqrDist;
+ nVectors_[facei] = -(invDd[nei] & d)/magSqrDist;
+ }
+ else
+ {
+ pVectors_[facei] = Zero;
+ nVectors_[facei] = Zero;
+ }
}
forAll(blsP, patchi)
@@ -161,8 +177,17 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
forAll(pd, patchFacei)
{
const vector& d = pd[patchFacei];
+ const scalar magSqrDist = d.magSqr();
- patchLsP[patchFacei] = (invDd[faceCells[patchFacei]] & d)/magSqr(d);
+ if (magSqrDist > ROOTVSMALL)
+ {
+ patchLsP[patchFacei] =
+ (invDd[faceCells[patchFacei]] & d)/magSqrDist;
+ }
+ else
+ {
+ patchLsP[patchFacei] = Zero;
+ }
}
}
diff --git a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/leastSquaresVectors.C b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/leastSquaresVectors.C
index 40dfc42445..c8f5b01051 100644
--- a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/leastSquaresVectors.C
+++ b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/leastSquaresVectors.C
@@ -151,7 +151,7 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
}
- // Invert the dd tensor
+ // Invert the dd tensors - including failsafe checks
const symmTensorField invDd(inv(dd));
diff --git a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/unweightedLeastSquaresVectors.C b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/unweightedLeastSquaresVectors.C
index 20c8bf5406..c19c1ba86b 100644
--- a/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/unweightedLeastSquaresVectors.C
+++ b/src/finiteVolume/finiteVolume/gradSchemes/leastSquaresGrad/unweightedLeastSquaresVectors.C
@@ -129,7 +129,7 @@ void Foam::leastSquaresVectors::calcLeastSquaresVectors()
}
- // Invert the dd tensor
+ // Invert the dd tensors - including failsafe checks
const symmTensorField invDd(inv(dd));