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));