mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: improve analytical eigendecompositions
- `tensor` and `tensor2D` returns complex eigenvalues/vectors
- `symmTensor` and `symmTensor2D` returns real eigenvalues/vectors
- adds new test routines for eigendecompositions
- improves numerical stability by:
- using new robust algorithms,
- reordering the conditional branches in root-type selection
This commit is contained in:
committed by
Andrew Heather
parent
6a53794e0a
commit
55e7da670c
@ -6,6 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2011-2017 OpenFOAM Foundation
|
||||
Copyright (C) 2020 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -73,7 +74,7 @@ const Foam::tensor Foam::tensor::I
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::vector Foam::eigenValues(const tensor& t)
|
||||
Foam::Vector<Foam::complex> Foam::eigenValues(const tensor& t)
|
||||
{
|
||||
// Coefficients of the characteristic cubic polynomial (a = 1)
|
||||
const scalar b =
|
||||
@ -86,117 +87,147 @@ Foam::vector Foam::eigenValues(const tensor& t)
|
||||
- t.xy()*t.yz()*t.zx() - t.xz()*t.zy()*t.yx()
|
||||
+ t.xx()*t.yz()*t.zy() + t.yy()*t.zx()*t.xz() + t.zz()*t.xy()*t.yx();
|
||||
|
||||
// Solve
|
||||
Roots<3> roots = cubicEqn(1, b, c, d).roots();
|
||||
// Determine the roots of the characteristic cubic polynomial
|
||||
const Roots<3> roots(cubicEqn(1, b, c, d).roots());
|
||||
|
||||
// Check the root types
|
||||
vector lambda = vector::zero;
|
||||
bool isComplex = false;
|
||||
forAll(roots, i)
|
||||
{
|
||||
switch (roots.type(i))
|
||||
{
|
||||
case roots::real:
|
||||
lambda[i] = roots[i];
|
||||
break;
|
||||
case roots::complex:
|
||||
WarningInFunction
|
||||
<< "Complex eigenvalues detected for tensor: " << t
|
||||
<< endl;
|
||||
lambda[i] = 0;
|
||||
isComplex = true;
|
||||
break;
|
||||
case roots::posInf:
|
||||
lambda[i] = VGREAT;
|
||||
break;
|
||||
case roots::negInf:
|
||||
lambda[i] = - VGREAT;
|
||||
break;
|
||||
case roots::nan:
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvalue calculation failed for tensor: " << t
|
||||
<< "Eigenvalue computation fails for tensor: " << t
|
||||
<< "due to the not-a-number root = " << roots[i]
|
||||
<< exit(FatalError);
|
||||
case roots::real:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the eigenvalues into ascending order
|
||||
if (lambda.x() > lambda.y())
|
||||
if (isComplex)
|
||||
{
|
||||
Swap(lambda.x(), lambda.y());
|
||||
}
|
||||
if (lambda.y() > lambda.z())
|
||||
{
|
||||
Swap(lambda.y(), lambda.z());
|
||||
}
|
||||
if (lambda.x() > lambda.y())
|
||||
{
|
||||
Swap(lambda.x(), lambda.y());
|
||||
return
|
||||
Vector<complex>
|
||||
(
|
||||
complex(roots[0], 0),
|
||||
complex(roots[1], roots[2]),
|
||||
complex(roots[1], -roots[2])
|
||||
);
|
||||
}
|
||||
|
||||
return lambda;
|
||||
return
|
||||
Vector<complex>
|
||||
(
|
||||
complex(roots[0], 0),
|
||||
complex(roots[1], 0),
|
||||
complex(roots[2], 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Foam::vector Foam::eigenVector
|
||||
Foam::Vector<Foam::complex> Foam::eigenVector
|
||||
(
|
||||
const tensor& T,
|
||||
const scalar lambda,
|
||||
const vector& direction1,
|
||||
const vector& direction2
|
||||
const complex& eVal,
|
||||
const Vector<complex>& standardBasis1,
|
||||
const Vector<complex>& standardBasis2
|
||||
)
|
||||
{
|
||||
// Construct the linear system for this eigenvalue
|
||||
tensor A(T - lambda*I);
|
||||
// Construct the characteristic equation system for this eigenvalue
|
||||
Tensor<complex> A(Zero);
|
||||
forAll(A, i)
|
||||
{
|
||||
A[i] = complex(T[i], 0);
|
||||
}
|
||||
A.xx() -= eVal;
|
||||
A.yy() -= eVal;
|
||||
A.zz() -= eVal;
|
||||
|
||||
// Determinants of the 2x2 sub-matrices used to find the eigenvectors
|
||||
scalar sd0, sd1, sd2;
|
||||
scalar magSd0, magSd1, magSd2;
|
||||
|
||||
// Sub-determinants for a unique eivenvalue
|
||||
sd0 = A.yy()*A.zz() - A.yz()*A.zy();
|
||||
sd1 = A.zz()*A.xx() - A.zx()*A.xz();
|
||||
sd2 = A.xx()*A.yy() - A.xy()*A.yx();
|
||||
magSd0 = mag(sd0);
|
||||
magSd1 = mag(sd1);
|
||||
magSd2 = mag(sd2);
|
||||
// Sub-determinants for a unique eigenvenvalue
|
||||
complex sd0 = A.yy()*A.zz() - A.yz()*A.zy();
|
||||
complex sd1 = A.zz()*A.xx() - A.zx()*A.xz();
|
||||
complex sd2 = A.xx()*A.yy() - A.xy()*A.yx();
|
||||
scalar magSd0 = mag(sd0);
|
||||
scalar magSd1 = mag(sd1);
|
||||
scalar magSd2 = mag(sd2);
|
||||
|
||||
// Evaluate the eigenvector using the largest sub-determinant
|
||||
if (magSd0 >= magSd1 && magSd0 >= magSd2 && magSd0 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
1,
|
||||
complex(1, 0),
|
||||
(A.yz()*A.zx() - A.zz()*A.yx())/sd0,
|
||||
(A.zy()*A.yx() - A.yy()*A.zx())/sd0
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
else if (magSd1 >= magSd2 && magSd1 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
(A.xz()*A.zy() - A.zz()*A.xy())/sd1,
|
||||
1,
|
||||
complex(1, 0),
|
||||
(A.zx()*A.xy() - A.xx()*A.zy())/sd1
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
else if (magSd2 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
(A.xy()*A.yz() - A.yy()*A.xz())/sd2,
|
||||
(A.yx()*A.xz() - A.xx()*A.yz())/sd2,
|
||||
1
|
||||
complex(1, 0)
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
|
||||
// Sub-determinants for a repeated eigenvalue
|
||||
sd0 = A.yy()*direction1.z() - A.yz()*direction1.y();
|
||||
sd1 = A.zz()*direction1.x() - A.zx()*direction1.z();
|
||||
sd2 = A.xx()*direction1.y() - A.xy()*direction1.x();
|
||||
sd0 = A.yy()*standardBasis1.z() - A.yz()*standardBasis1.y();
|
||||
sd1 = A.zz()*standardBasis1.x() - A.zx()*standardBasis1.z();
|
||||
sd2 = A.xx()*standardBasis1.y() - A.xy()*standardBasis1.x();
|
||||
magSd0 = mag(sd0);
|
||||
magSd1 = mag(sd1);
|
||||
magSd2 = mag(sd2);
|
||||
@ -204,90 +235,96 @@ Foam::vector Foam::eigenVector
|
||||
// Evaluate the eigenvector using the largest sub-determinant
|
||||
if (magSd0 >= magSd1 && magSd0 >= magSd2 && magSd0 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
1,
|
||||
(A.yz()*direction1.x() - direction1.z()*A.yx())/sd0,
|
||||
(direction1.y()*A.yx() - A.yy()*direction1.x())/sd0
|
||||
complex(1, 0),
|
||||
(A.yz()*standardBasis1.x() - standardBasis1.z()*A.yx())/sd0,
|
||||
(standardBasis1.y()*A.yx() - A.yy()*standardBasis1.x())/sd0
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
else if (magSd1 >= magSd2 && magSd1 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
(direction1.z()*A.zy() - A.zz()*direction1.y())/sd1,
|
||||
1,
|
||||
(A.zx()*direction1.y() - direction1.x()*A.zy())/sd1
|
||||
(standardBasis1.z()*A.zy() - A.zz()*standardBasis1.y())/sd1,
|
||||
complex(1, 0),
|
||||
(A.zx()*standardBasis1.y() - standardBasis1.x()*A.zy())/sd1
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
else if (magSd2 > SMALL)
|
||||
{
|
||||
vector ev
|
||||
const Vector<complex> eVec
|
||||
(
|
||||
(A.xy()*direction1.z() - direction1.y()*A.xz())/sd2,
|
||||
(direction1.x()*A.xz() - A.xx()*direction1.z())/sd2,
|
||||
1
|
||||
(A.xy()*standardBasis1.z() - standardBasis1.y()*A.xz())/sd2,
|
||||
(standardBasis1.x()*A.xz() - A.xx()*standardBasis1.z())/sd2,
|
||||
complex(1, 0)
|
||||
);
|
||||
|
||||
return ev/mag(ev);
|
||||
#ifdef FULLDEBUG
|
||||
if (mag(eVec) < SMALL)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Eigenvector magnitude should be non-zero:"
|
||||
<< "mag(eigenvector) = " << mag(eVec)
|
||||
<< abort(FatalError);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eVec/mag(eVec);
|
||||
}
|
||||
|
||||
// Triple eigenvalue
|
||||
return direction1^direction2;
|
||||
return standardBasis1^standardBasis2;
|
||||
}
|
||||
|
||||
|
||||
Foam::tensor Foam::eigenVectors(const tensor& T, const vector& lambdas)
|
||||
{
|
||||
vector Ux(1, 0, 0), Uy(0, 1, 0), Uz(0, 0, 1);
|
||||
|
||||
Ux = eigenVector(T, lambdas.x(), Uy, Uz);
|
||||
Uy = eigenVector(T, lambdas.y(), Uz, Ux);
|
||||
Uz = eigenVector(T, lambdas.z(), Ux, Uy);
|
||||
|
||||
return tensor(Ux, Uy, Uz);
|
||||
}
|
||||
|
||||
|
||||
Foam::tensor Foam::eigenVectors(const tensor& T)
|
||||
{
|
||||
const vector lambdas(eigenValues(T));
|
||||
|
||||
return eigenVectors(T, lambdas);
|
||||
}
|
||||
|
||||
|
||||
Foam::vector Foam::eigenValues(const symmTensor& T)
|
||||
{
|
||||
return eigenValues(tensor(T));
|
||||
}
|
||||
|
||||
|
||||
Foam::vector Foam::eigenVector
|
||||
Foam::Tensor<Foam::complex> Foam::eigenVectors
|
||||
(
|
||||
const symmTensor& T,
|
||||
const scalar lambda,
|
||||
const vector& direction1,
|
||||
const vector& direction2
|
||||
const tensor& T,
|
||||
const Vector<complex>& eVals
|
||||
)
|
||||
{
|
||||
return eigenVector(tensor(T), lambda, direction1, direction2);
|
||||
Vector<complex> Ux(complex(1, 0), Zero, Zero);
|
||||
Vector<complex> Uy(Zero, complex(1, 0), Zero);
|
||||
Vector<complex> Uz(Zero, Zero, complex(1, 0));
|
||||
|
||||
Ux = eigenVector(T, eVals.x(), Uy, Uz);
|
||||
Uy = eigenVector(T, eVals.y(), Uz, Ux);
|
||||
Uz = eigenVector(T, eVals.z(), Ux, Uy);
|
||||
|
||||
return Tensor<complex>(Ux, Uy, Uz);
|
||||
}
|
||||
|
||||
|
||||
Foam::tensor Foam::eigenVectors(const symmTensor& T, const vector& lambdas)
|
||||
Foam::Tensor<Foam::complex> Foam::eigenVectors(const tensor& T)
|
||||
{
|
||||
return eigenVectors(tensor(T), lambdas);
|
||||
}
|
||||
const Vector<complex> eVals(eigenValues(T));
|
||||
|
||||
|
||||
Foam::tensor Foam::eigenVectors(const symmTensor& T)
|
||||
{
|
||||
return eigenVectors(tensor(T));
|
||||
return eigenVectors(T, eVals);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2011-2014 OpenFOAM Foundation
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
Copyright (C) 2019-2020 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -28,7 +28,13 @@ Typedef
|
||||
Foam::tensor
|
||||
|
||||
Description
|
||||
Tensor of scalars.
|
||||
Tensor of scalars, i.e. Tensor<scalar>.
|
||||
|
||||
Analytical functions for the computation of complex eigenvalues and
|
||||
complex eigenvectors from a given tensor.
|
||||
|
||||
See also
|
||||
Test-Tensor.C
|
||||
|
||||
SourceFiles
|
||||
tensor.C
|
||||
@ -42,6 +48,7 @@ SourceFiles
|
||||
#include "vector.H"
|
||||
#include "sphericalTensor.H"
|
||||
#include "symmTensor.H"
|
||||
#include "complex.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -52,27 +59,51 @@ namespace Foam
|
||||
|
||||
typedef Tensor<scalar> tensor;
|
||||
|
||||
vector eigenValues(const tensor& T);
|
||||
vector eigenVector
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
//- Return complex eigenvalues of a given tensor
|
||||
// \param T tensor
|
||||
//
|
||||
// \return Vector<complex> eigenvalues
|
||||
Vector<complex> eigenValues(const tensor& T);
|
||||
|
||||
|
||||
//- Return a complex eigenvector corresponding to
|
||||
//- a given complex eigenvalue of a given tensor
|
||||
// \param T tensor
|
||||
// \param eVal complex eigenvalue
|
||||
// \param standardBasis1 tensor orthogonal component 1
|
||||
// \param standardBasis2 tensor orthogonal component 2
|
||||
//
|
||||
// \return Vector<complex> eigenvector
|
||||
Vector<complex> eigenVector
|
||||
(
|
||||
const tensor& T,
|
||||
const scalar lambda,
|
||||
const vector& direction1,
|
||||
const vector& direction2
|
||||
const complex& eVal,
|
||||
const Vector<complex>& standardBasis1,
|
||||
const Vector<complex>& standardBasis2
|
||||
);
|
||||
tensor eigenVectors(const tensor& T, const vector& lambdas);
|
||||
tensor eigenVectors(const tensor& T);
|
||||
|
||||
vector eigenValues(const symmTensor& T);
|
||||
vector eigenVector
|
||||
|
||||
//- Return complex eigenvectors corresponding to
|
||||
//- given complex eigenvalues of a given tensor
|
||||
// \param T tensor
|
||||
// \param eVals complex eigenvalues
|
||||
//
|
||||
// \return Tensor<complex> eigenvectors, each row is an eigenvector
|
||||
Tensor<complex> eigenVectors
|
||||
(
|
||||
const symmTensor& T,
|
||||
const scalar lambda,
|
||||
const vector& direction1,
|
||||
const vector& direction2
|
||||
const tensor& T,
|
||||
const Vector<complex>& eVals
|
||||
);
|
||||
tensor eigenVectors(const symmTensor& T, const vector& lambdas);
|
||||
tensor eigenVectors(const symmTensor& T);
|
||||
|
||||
|
||||
//- Return complex eigenvectors of a given tensor by computing
|
||||
//- the complex eigenvalues of the tensor in the background
|
||||
// \param T tensor
|
||||
//
|
||||
// \return Tensor<complex> complex eigenvectors, each row is an eigenvector
|
||||
Tensor<complex> eigenVectors(const tensor& T);
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
Reference in New Issue
Block a user