diff --git a/applications/test/invTensor/Make/files b/applications/test/invTensor/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..c99507752a8dfbb9a22d31479d899f68d3d40dd7 --- /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 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88 --- /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 0000000000000000000000000000000000000000..b16b7d36662a76393f36367183f91cba96f11291 --- /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 <http://www.gnu.org/licenses/>. + +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<class TensorType> +void test_invert(const TensorType& tt) +{ + Info<< pTraits<TensorType>::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 2d7cbf60163a9f4231feb24adf117a92f9f37bab..a43534155124d42092a3c9e1be91a5f95618eb52 100644 --- a/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H +++ b/src/OpenFOAM/dimensionedTypes/dimensionedSymmTensor/dimensionedSymmTensor.H @@ -50,7 +50,7 @@ namespace Foam typedef dimensioned<symmTensor> 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 8de0683efe3a05a224c92a483113818910865e83..a66aea3c07f71026e9068eee4a90f7fc737a24bf 100644 --- a/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H +++ b/src/OpenFOAM/dimensionedTypes/dimensionedTensor/dimensionedTensor.H @@ -51,8 +51,7 @@ namespace Foam typedef dimensioned<tensor> 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 1be5183d4c650e0a929e00a8f445c02ce5abbfcd..afdf86c186e6f2d6641cef97b2f55b855ded80e6 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<symmTensor>& result, const UList<symmTensor>& tf1) +void inv(Field<symmTensor>& result, const UList<symmTensor>& 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<symmTensorField> inv(const UList<symmTensor>& tf) diff --git a/src/OpenFOAM/fields/Fields/tensorField/tensorField.C b/src/OpenFOAM/fields/Fields/tensorField/tensorField.C index c4f76fc9788b76a95aa3918a325c7167cf8876c4..9b343fa5a0389597f15439b1f715ef369f58d283 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<tensor>& result, const UList<tensor>& tf1) +void inv(Field<tensor>& result, const UList<tensor>& 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<tensorField> inv(const UList<tensor>& tf) diff --git a/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H b/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H index e3650810231eea727c5477625309d3d1b1e5b50e..2f38d3e481aed30348dc7c40ece150976ed451fb 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<Cmpt> adjunct() const; + + //- Return cofactor matrix (transpose of adjunct matrix) inline SymmTensor<Cmpt> cof() const; - //- Return 2D cofactor matrix by excluding given direction - inline SymmTensor<Cmpt> cof2D(const direction excludeCmpt) const; + //- Return 2D adjunct matrix by excluding given direction + inline SymmTensor<Cmpt> adjunct2D(const direction excludeCmpt) const; + + //- Return inverse + inline SymmTensor<Cmpt> inv() const; - //- Return inverse, with optional (ad hoc) failsafe handling - //- of 2D tensors - inline SymmTensor<Cmpt> inv(const bool failsafe=false) const; + //- Return inverse, with (ad hoc) failsafe handling of 2D tensors + inline SymmTensor<Cmpt> safeInv() const; //- Return inverse of 2D tensor (by excluding given direction) inline SymmTensor<Cmpt> inv2D(const direction excludeCmpt) const; diff --git a/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H b/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H index 47c10fa13b8fe17c7d1b336f2f2a6238e6a0b3d2..5c28c6c2bc6ff3f1fd3cc6279f57780f9453fe7f 100644 --- a/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H +++ b/src/OpenFOAM/primitives/SymmTensor/SymmTensorI.H @@ -272,24 +272,28 @@ inline Cmpt Foam::SymmTensor<Cmpt>::det2D(const direction excludeCmpt) const template<class Cmpt> -inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::cof() const +inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::adjunct() const { + // symmetric: cof() == adjunct() return SymmTensor<Cmpt> ( - yy()*zz() - yz()*yz(), - xz()*yz() - xy()*zz(), - xy()*yz() - xz()*yy(), + yy()*zz() - yz()*yz(), xz()*yz() - xy()*zz(), xy()*yz() - xz()*yy(), + xx()*zz() - xz()*xz(), xy()*xz() - xx()*yz(), + xx()*yy() - xy()*xy() + ); +} - xx()*zz() - xz()*xz(), - xy()*xz() - xx()*yz(), - xx()*yy() - xy()*xy() - ); +template<class Cmpt> +inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::cof() const +{ + // symmetric: cof() == adjunct() + return this->adjunct(); } template<class Cmpt> -inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::cof2D +inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::adjunct2D ( const direction excludeCmpt ) const @@ -335,7 +339,84 @@ inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::inv2D { const Cmpt detval = this->det2D(excludeCmpt); - return this->cof2D(excludeCmpt).T()/detval; + return this->adjunct2D(excludeCmpt)/detval; +} + + +// Invert without much error handling +template<class Cmpt> +inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::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<class Cmpt> +inline Foam::SymmTensor<Cmpt> Foam::SymmTensor<Cmpt>::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<Cmpt> work(*this); + + if (small_xx) { work.xx() += pTraits<Cmpt>::one; } + if (small_yy) { work.yy() += pTraits<Cmpt>::one; } + if (small_zz) { work.zz() += pTraits<Cmpt>::one; } + + const Cmpt detval = work.det(); + + if (mag(detval) < ROOTVSMALL) + { + // Appears to be nearly zero - leave untouched? + return SymmTensor<Cmpt>(Zero); + } + + work = work.adjunct()/detval; + + if (small_xx) { work.xx() -= pTraits<Cmpt>::one; } + if (small_yy) { work.yy() -= pTraits<Cmpt>::one; } + if (small_zz) { work.zz() -= pTraits<Cmpt>::one; } + + return work; + } + } + + const Cmpt detval = this->det(); + + if (mag(detval) < ROOTVSMALL) + { + // Appears to be nearly zero - leave untouched? + return SymmTensor<Cmpt>(Zero); + } + + return this->adjunct()/detval; } @@ -438,7 +519,7 @@ inline SymmTensor<Cmpt> inv(const SymmTensor<Cmpt>& st, const Cmpt detval) } #endif - return st.cof().T()/detval; + return st.adjunct()/detval; } @@ -446,62 +527,7 @@ inline SymmTensor<Cmpt> inv(const SymmTensor<Cmpt>& st, const Cmpt detval) template<class Cmpt> inline SymmTensor<Cmpt> inv(const SymmTensor<Cmpt>& st) { - return inv(st, st.det()); -} - - -template<class Cmpt> -inline SymmTensor<Cmpt> SymmTensor<Cmpt>::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<Cmpt> work(*this); - - if (small_xx) { work.xx() += pTraits<Cmpt>::one; } - if (small_yy) { work.yy() += pTraits<Cmpt>::one; } - if (small_zz) { work.zz() += pTraits<Cmpt>::one; } - - work = Foam::inv(work, work.det()); - - if (small_xx) { work.xx() -= pTraits<Cmpt>::one; } - if (small_yy) { work.yy() -= pTraits<Cmpt>::one; } - if (small_zz) { work.zz() -= pTraits<Cmpt>::one; } - - return work; - } - else if (mag(detval) < VSMALL) - { - // Really appears to be zero - leave untouched? - return SymmTensor<Cmpt>(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 8869e9fb56a4b0a55e0bf974beabfb65335ccc3a..fb41001b1f8009a25c3a12572172d697bd4b794c 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 1308b015e9d851e74a768f2dac237c0680d988b9..e70a951c9b9c1b2419fb5dd67e33d8051162e8a9 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<Cmpt> adjunct() const; + + //- Return cofactor matrix (transpose of adjunct matrix) inline SymmTensor2D<Cmpt> cof() const; //- Return inverse diff --git a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H index b583b669c5ef7a3c37fe289c9c0ec07f70c0f81f..4eba6e6cc3bcc5fddee58091313f7137fa209867 100644 --- a/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H +++ b/src/OpenFOAM/primitives/SymmTensor2D/SymmTensor2DI.H @@ -109,16 +109,45 @@ inline Cmpt Foam::SymmTensor2D<Cmpt>::det() const template<class Cmpt> -inline Foam::SymmTensor2D<Cmpt> Foam::SymmTensor2D<Cmpt>::cof() const +inline Foam::SymmTensor2D<Cmpt> Foam::SymmTensor2D<Cmpt>::adjunct() const { + // symmetric: cof() == adjunct() return SymmTensor2D<Cmpt> ( - yy(), -yx(), + yy(), -xy(), xx() ); } +template<class Cmpt> +inline Foam::SymmTensor2D<Cmpt> Foam::SymmTensor2D<Cmpt>::cof() const +{ + // symmetric: cof() == adjunct() + return this->adjunct(); +} + + +// Invert without much error handling +template<class Cmpt> +inline Foam::SymmTensor2D<Cmpt> Foam::SymmTensor2D<Cmpt>::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<class Cmpt> @@ -220,7 +249,7 @@ inline SymmTensor2D<Cmpt> inv(const SymmTensor2D<Cmpt>& st, const Cmpt detval) } #endif - return st.cof().T()/detval; + return st.adjunct()/detval; } @@ -228,15 +257,7 @@ inline SymmTensor2D<Cmpt> inv(const SymmTensor2D<Cmpt>& st, const Cmpt detval) template<class Cmpt> inline SymmTensor2D<Cmpt> inv(const SymmTensor2D<Cmpt>& st) { - return inv(st, st.det()); -} - - -// The inverse of this SymmTensor2D -template<class Cmpt> -inline SymmTensor2D<Cmpt> SymmTensor2D<Cmpt>::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 7391d06865bd8d02d4a58f5dc19058adbd549e7b..601e00441674f65891255b40e3bd2bc542d2bcaa 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<Cmpt> adjunct() const; + + //- Return cofactor matrix (transpose of adjunct matrix) inline Tensor<Cmpt> cof() const; - //- Return 2D cofactor matrix by excluding given direction - inline Tensor<Cmpt> cof2D(const direction excludeCmpt) const; + //- Return 2D adjunct matrix by excluding given direction + inline Tensor<Cmpt> adjunct2D(const direction excludeCmpt) const; + + //- Return inverse + inline Tensor<Cmpt> inv() const; - //- Return inverse, with optional (ad hoc) failsafe handling - //- of 2D tensors - inline Tensor<Cmpt> inv(const bool failsafe=false) const; + //- Return inverse, with (ad hoc) failsafe handling of 2D tensors + inline Tensor<Cmpt> safeInv() const; //- Return inverse of 2D tensor (by excluding given direction) inline Tensor<Cmpt> inv2D(const direction excludeCmpt) const; diff --git a/src/OpenFOAM/primitives/Tensor/TensorI.H b/src/OpenFOAM/primitives/Tensor/TensorI.H index 6e24c0f506cb408dacf6a927fb7d868c485cce0f..b389f12ee13596c278cec9ad40f6c3b6d795ae72 100644 --- a/src/OpenFOAM/primitives/Tensor/TensorI.H +++ b/src/OpenFOAM/primitives/Tensor/TensorI.H @@ -468,27 +468,26 @@ inline Cmpt Foam::Tensor<Cmpt>::det2D(const direction excludeCmpt) const template<class Cmpt> -inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof() const +inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::adjunct() const { return Tensor<Cmpt> ( - yy()*zz() - zy()*yz(), - zx()*yz() - yx()*zz(), - yx()*zy() - yy()*zx(), + 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() + ); +} - xz()*zy() - xy()*zz(), - xx()*zz() - xz()*zx(), - xy()*zx() - xx()*zy(), - xy()*yz() - xz()*yy(), - yx()*xz() - xx()*yz(), - xx()*yy() - yx()*xy() - ); +template<class Cmpt> +inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof() const +{ + return this->adjunct().T(); } template<class Cmpt> -inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof2D +inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::adjunct2D ( const direction excludeCmpt ) const @@ -500,8 +499,8 @@ inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof2D return Tensor<Cmpt> ( Zero, Zero, Zero, - Zero, zz(), -zy(), - Zero, -yz(), yy() + Zero, zz(), -yz(), + Zero, -zy(), yy() ); } @@ -509,9 +508,9 @@ inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof2D { return Tensor<Cmpt> ( - zz(), Zero, -zx(), + zz(), Zero, -xz(), Zero, Zero, Zero, - -xz(), Zero, xx() + -zx(), Zero, xx() ); } } @@ -519,8 +518,8 @@ inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::cof2D // Fall-through: Eliminate z return Tensor<Cmpt> ( - yy(), -yx(), Zero, - -xy(), xx(), Zero, + yy(), -xy(), Zero, + -yx(), xx(), Zero, Zero, Zero, Zero ); } @@ -534,7 +533,7 @@ inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::inv2D { const Cmpt detval = this->det2D(excludeCmpt); - return this->cof2D(excludeCmpt).T()/detval; + return this->adjunct2D(excludeCmpt)/detval; } @@ -576,6 +575,84 @@ Foam::Tensor<Cmpt>::schur(const Tensor<Cmpt>& t2) const } +// Invert without much error handling +template<class Cmpt> +inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::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<class Cmpt> +inline Foam::Tensor<Cmpt> Foam::Tensor<Cmpt>::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<Cmpt> work(*this); + + if (small_xx) { work.xx() += pTraits<Cmpt>::one; } + if (small_yy) { work.yy() += pTraits<Cmpt>::one; } + if (small_zz) { work.zz() += pTraits<Cmpt>::one; } + + const Cmpt detval = work.det(); + + if (mag(detval) < ROOTVSMALL) + { + // Appears to be nearly zero - leave untouched? + return Tensor<Cmpt>(Zero); + } + + work = work.adjunct()/detval; + + if (small_xx) { work.xx() -= pTraits<Cmpt>::one; } + if (small_yy) { work.yy() -= pTraits<Cmpt>::one; } + if (small_zz) { work.zz() -= pTraits<Cmpt>::one; } + + return work; + } + } + + const Cmpt detval = this->det(); + + if (mag(detval) < ROOTVSMALL) + { + // Appears to be nearly zero - leave untouched? + return Tensor<Cmpt>(Zero); + } + + return this->adjunct()/detval; +} + + // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // template<class Cmpt> @@ -750,7 +827,7 @@ inline Tensor<Cmpt> inv(const Tensor<Cmpt>& t, const Cmpt detval) } #endif - return t.cof().T()/detval; + return t.adjunct()/detval; } @@ -758,63 +835,7 @@ inline Tensor<Cmpt> inv(const Tensor<Cmpt>& t, const Cmpt detval) template<class Cmpt> inline Tensor<Cmpt> inv(const Tensor<Cmpt>& t) { - return inv(t, t.det()); -} - - -template<class Cmpt> -inline Tensor<Cmpt> Tensor<Cmpt>::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<Cmpt> work(*this); - - if (small_xx) { work.xx() += pTraits<Cmpt>::one; } - if (small_yy) { work.yy() += pTraits<Cmpt>::one; } - if (small_zz) { work.zz() += pTraits<Cmpt>::one; } - - work = Foam::inv(work, work.det()); - - if (small_xx) { work.xx() -= pTraits<Cmpt>::one; } - if (small_yy) { work.yy() -= pTraits<Cmpt>::one; } - if (small_zz) { work.zz() -= pTraits<Cmpt>::one; } - - return work; - } - else if (mag(detval) < VSMALL) - { - // Really appears to be zero - leave untouched? - return Tensor<Cmpt>(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 dfe5851bd0aea0797ebb56448d492795934ac975..d26aad40a4147af6c05f97316a986534f555d6cf 100644 --- a/src/OpenFOAM/primitives/Tensor/floats/tensor.C +++ b/src/OpenFOAM/primitives/Tensor/floats/tensor.C @@ -304,9 +304,9 @@ Foam::Tensor<Foam::complex> 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 f12b68104f9ff89b632d28e31fdc25fe655918f1..69f8efd09111b440d4cd91bc300602a88540690f 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<Cmpt> adjunct() const; + + //- Return cofactor matrix (transpose of adjunct matrix) inline Tensor2D<Cmpt> cof() const; //- Return inverse diff --git a/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H b/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H index 9367f4c82bf07a11945b9edc244fa3ce02beb22c..1acbb876a8c78aab927ad61f5d34ff329a6d0578 100644 --- a/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H +++ b/src/OpenFOAM/primitives/Tensor2D/Tensor2DI.H @@ -318,16 +318,23 @@ inline Cmpt Foam::Tensor2D<Cmpt>::det() const template<class Cmpt> -inline Foam::Tensor2D<Cmpt> Foam::Tensor2D<Cmpt>::cof() const +inline Foam::Tensor2D<Cmpt> Foam::Tensor2D<Cmpt>::adjunct() const { return Tensor2D<Cmpt> ( - yy(), -yx(), - -xy(), xx() + yy(), -xy(), + -yx(), xx() ); } +template<class Cmpt> +inline Foam::Tensor2D<Cmpt> Foam::Tensor2D<Cmpt>::cof() const +{ + return this->adjunct().T(); +} + + template<class Cmpt> inline Foam::Tensor2D<Cmpt> Foam::Tensor2D<Cmpt>::inner(const Tensor2D<Cmpt>& t2) const @@ -359,6 +366,26 @@ Foam::Tensor2D<Cmpt>::schur(const Tensor2D<Cmpt>& t2) const } +// Invert without much error handling +template<class Cmpt> +inline Foam::Tensor2D<Cmpt> Foam::Tensor2D<Cmpt>::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<class Cmpt> @@ -463,7 +490,7 @@ inline Cmpt det(const Tensor2D<Cmpt>& t) } -//- Return the cofactor Tensor2D of a Tensor2D +//- Return the cofactor of a Tensor2D template<class Cmpt> inline Tensor2D<Cmpt> cof(const Tensor2D<Cmpt>& t) { @@ -485,7 +512,7 @@ inline Tensor2D<Cmpt> inv(const Tensor2D<Cmpt>& t, const Cmpt detval) } #endif - return t.cof().T()/detval; + return t.adjunct()/detval; } @@ -493,15 +520,7 @@ inline Tensor2D<Cmpt> inv(const Tensor2D<Cmpt>& t, const Cmpt detval) template<class Cmpt> inline Tensor2D<Cmpt> inv(const Tensor2D<Cmpt>& t) { - return inv(t, t.det()); -} - - -// Return the inverse of this Tensor2D -template<class Cmpt> -inline Tensor2D<Cmpt> Tensor2D<Cmpt>::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 f2bd352bbd066ac922be82dccedd009fc0029b81..e40618b0a079ab0ce61629b10497677c3a601a07 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 5bfe9a515fa72299078fa47e07e6b4b34f3d79e2..584968db7d3fcbda94907c84a69e3f2a384eb850 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]; - - patchLsP[patchFacei] = (invDd[faceCells[patchFacei]] & d)/magSqr(d); + const scalar magSqrDist = d.magSqr(); + + 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 40dfc42445da7ead4c948bdf416681c703e0f8c9..c8f5b010511ed8372bc579ace2b020645988d932 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 20c8bf54060c120581d5477e44e10a44f4f5990f..c19c1ba86b29734578404c0cbe4e4bd0336acbc8 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));