Added robust primitive cubic/quadratic/linear equation solutions.

Applied to eigen-value calculations. Fixed repeated-eigen-value issues
in eigen-vector generation.
This commit is contained in:
Will Bainbridge
2017-03-22 15:11:54 +00:00
parent 406a2c5d20
commit 0be6269add
19 changed files with 1477 additions and 216 deletions

View File

@ -0,0 +1,3 @@
Test-cubicEqn.C
EXE = $(FOAM_USER_APPBIN)/Test-cubicEqn

View File

@ -0,0 +1,3 @@
EXE_INC =
EXE_LIBS =

View File

@ -0,0 +1,101 @@
#include <ctime>
#include <random>
#include "cubicEqn.H"
#include "IOstreams.H"
#include "stringList.H"
using namespace Foam;
scalar randomScalar(const scalar min, const scalar max)
{
static_assert
(
sizeof(long) == sizeof(scalar),
"Scalar and long are not the same size"
);
static std::default_random_engine generator(std::time(0));
static std::uniform_int_distribution<long>
distribution
(
std::numeric_limits<long>::min(),
std::numeric_limits<long>::max()
);
scalar x;
do
{
long i = distribution(generator);
x = reinterpret_cast<scalar&>(i);
}
while (min > mag(x) || mag(x) > max || !std::isfinite(x));
return x;
};
template <class Type>
void test(const Type& polynomialEqn, const scalar tol)
{
Roots<Type::nComponents - 1> r = polynomialEqn.roots();
const scalar nan = std::numeric_limits<scalar>::quiet_NaN();
const scalar inf = std::numeric_limits<scalar>::infinity();
FixedList<label, Type::nComponents - 1> t;
FixedList<scalar, Type::nComponents - 1> v(nan);
FixedList<scalar, Type::nComponents - 1> e(nan);
bool ok = true;
forAll(r, i)
{
t[i] = r.type(i);
switch (t[i])
{
case roots::real:
v[i] = polynomialEqn.value(r[i]);
e[i] = polynomialEqn.error(r[i]);
ok = ok && mag(v[i]) < tol*mag(e[i]);
break;
case roots::posInf:
v[i] = + inf;
e[i] = nan;
break;
case roots::negInf:
v[i] = - inf;
e[i] = nan;
break;
default:
v[i] = e[i] = nan;
break;
}
}
if (!ok)
{
Info<< "Coeffs: " << polynomialEqn << endl
<< " Types: " << t << endl
<< " Roots: " << r << endl
<< "Values: " << v << endl
<< "Errors: " << e << endl << endl;
}
}
int main()
{
const int nTests = 1000000;
for (int t = 0; t < nTests; ++ t)
{
test
(
cubicEqn
(
randomScalar(1e-50, 1e+50),
randomScalar(1e-50, 1e+50),
randomScalar(1e-50, 1e+50),
randomScalar(1e-50, 1e+50)
),
100
);
}
Info << nTests << " cubics tested" << endl;
return 0;
}

View File

@ -125,6 +125,9 @@ $(spatialVectorAlgebra)/SpatialVector/spatialVector/spatialVector.C
$(spatialVectorAlgebra)/SpatialTensor/spatialTensor/spatialTensor.C
$(spatialVectorAlgebra)/CompactSpatialTensor/compactSpatialTensor/compactSpatialTensor.C
primitives/polynomialEqns/cubicEqn/cubicEqn.C
primitives/polynomialEqns/quadraticEqn/quadraticEqn.C
containers/HashTables/HashTable/HashTableCore.C
containers/HashTables/StaticHashTable/StaticHashTableCore.C
containers/Lists/SortableList/ParSortableListName.C

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -24,6 +24,7 @@ License
\*---------------------------------------------------------------------------*/
#include "tensor.H"
#include "cubicEqn.H"
#include "mathematicalConstants.H"
using namespace Foam::constant::mathematical;
@ -72,137 +73,76 @@ const Foam::tensor Foam::tensor::I
Foam::vector Foam::eigenValues(const tensor& t)
{
// The eigenvalues
scalar i, ii, iii;
// Coefficients of the characteristic cubic polynomial (a = 1)
const scalar b =
- t.xx() - t.yy() - t.zz();
const scalar c =
t.xx()*t.yy() + t.xx()*t.zz() + t.yy()*t.zz()
- t.xy()*t.yx() - t.yz()*t.zy() - t.zx()*t.xz();
const scalar d =
- t.xx()*t.yy()*t.zz()
- 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();
// diagonal matrix
if
(
(
mag(t.xy()) + mag(t.xz()) + mag(t.yx())
+ mag(t.yz()) + mag(t.zx()) + mag(t.zy())
)
< SMALL
)
// Solve
Roots<3> roots = cubicEqn(1, b, c, d).roots();
// Check the root types
vector lambda = vector::zero;
forAll(roots, i)
{
i = t.xx();
ii = t.yy();
iii = t.zz();
}
// non-diagonal matrix
else
{
// Coefficients of the characteristic polynmial
// x^3 + a*x^2 + b*x + c = 0
scalar a =
- t.xx() - t.yy() - t.zz();
scalar b =
t.xx()*t.yy() + t.xx()*t.zz() + t.yy()*t.zz()
- t.xy()*t.yx() - t.yz()*t.zy() - t.zx()*t.xz();
scalar c =
- t.xx()*t.yy()*t.zz()
- 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();
// Auxillary variables
scalar aBy3 = a/3;
scalar P = (a*a - 3*b)/9; // == -p_wikipedia/3
scalar PPP = P*P*P;
scalar Q = (2*a*a*a - 9*a*b + 27*c)/54; // == q_wikipedia/2
scalar QQ = Q*Q;
// Three identical roots
if (mag(P) < SMALL && mag(Q) < SMALL)
switch (roots.type(i))
{
return vector(- aBy3, - aBy3, - aBy3);
}
// Two identical roots and one distinct root
else if (mag(QQ) > SMALL && mag(PPP/QQ - 1) < SMALL)
{
scalar sqrtP = sqrt(P);
scalar signQ = sign(Q);
i = ii = signQ*sqrtP - aBy3;
iii = - 2*signQ*sqrtP - aBy3;
}
// Three distinct roots
else if (PPP > QQ)
{
scalar sqrtP = sqrt(P);
scalar value = cos(acos(Q/sqrt(PPP))/3);
scalar delta = sqrt(3 - 3*value*value);
i = - 2*sqrtP*value - aBy3;
ii = sqrtP*(value + delta) - aBy3;
iii = sqrtP*(value - delta) - aBy3;
}
// One real root, two imaginary roots
// based on the above logic, PPP must be less than QQ
else
{
WarningInFunction
<< "complex eigenvalues detected for tensor: " << t
<< endl;
if (mag(P) < SMALL)
{
i = cbrt(QQ/2);
}
else
{
scalar w = cbrt(- Q - sqrt(QQ - PPP));
i = w + P/w - aBy3;
}
return vector(-VGREAT, i, VGREAT);
case roots::real:
lambda[i] = roots[i];
break;
case roots::complex:
WarningInFunction
<< "Complex eigenvalues detected for tensor: " << t
<< endl;
lambda[i] = 0;
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
<< exit(FatalError);
}
}
// Sort the eigenvalues into ascending order
if (i > ii)
if (lambda.x() > lambda.y())
{
Swap(i, ii);
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());
}
if (ii > iii)
{
Swap(ii, iii);
}
if (i > ii)
{
Swap(i, ii);
}
return vector(i, ii, iii);
return lambda;
}
Foam::vector Foam::eigenVector
(
const tensor& t,
const scalar lambda
const tensor& T,
const scalar lambda,
const vector& direction1,
const vector& direction2
)
{
// Constantly rotating direction ensures different eigenvectors are
// generated when called sequentially with a multiple eigenvalue
static vector direction(1,0,0);
vector oldDirection(direction);
scalar temp = direction[2];
direction[2] = direction[1];
direction[1] = direction[0];
direction[0] = temp;
// Construct the linear system for this eigenvalue
tensor A(t - lambda*I);
tensor A(T - lambda*I);
// Determinants of the 2x2 sub-matrices used to find the eigenvectors
scalar sd0, sd1, sd2;
@ -252,9 +192,9 @@ Foam::vector Foam::eigenVector
}
// Sub-determinants for a repeated eigenvalue
sd0 = A.yy()*direction.z() - A.yz()*direction.y();
sd1 = A.zz()*direction.x() - A.zx()*direction.z();
sd2 = A.xx()*direction.y() - A.xy()*direction.x();
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();
magSd0 = mag(sd0);
magSd1 = mag(sd1);
magSd2 = mag(sd2);
@ -265,8 +205,8 @@ Foam::vector Foam::eigenVector
vector ev
(
1,
(A.yz()*direction.x() - direction.z()*A.yx())/sd0,
(direction.y()*A.yx() - A.yy()*direction.x())/sd0
(A.yz()*direction1.x() - direction1.z()*A.yx())/sd0,
(direction1.y()*A.yx() - A.yy()*direction1.x())/sd0
);
return ev/mag(ev);
@ -275,9 +215,9 @@ Foam::vector Foam::eigenVector
{
vector ev
(
(direction.z()*A.zy() - A.zz()*direction.y())/sd1,
(direction1.z()*A.zy() - A.zz()*direction1.y())/sd1,
1,
(A.zx()*direction.y() - direction.x()*A.zy())/sd1
(A.zx()*direction1.y() - direction1.x()*A.zy())/sd1
);
return ev/mag(ev);
@ -286,8 +226,8 @@ Foam::vector Foam::eigenVector
{
vector ev
(
(A.xy()*direction.z() - direction.y()*A.xz())/sd2,
(direction.x()*A.xz() - A.xx()*direction.z())/sd2,
(A.xy()*direction1.z() - direction1.y()*A.xz())/sd2,
(direction1.x()*A.xz() - A.xx()*direction1.z())/sd2,
1
);
@ -295,40 +235,57 @@ Foam::vector Foam::eigenVector
}
// Triple eigenvalue
return oldDirection;
return direction1^direction2;
}
Foam::tensor Foam::eigenVectors(const tensor& t)
Foam::tensor Foam::eigenVectors(const tensor& T, const vector& lambdas)
{
vector evals(eigenValues(t));
vector Ux(1, 0, 0), Uy(0, 1, 0), Uz(0, 0, 1);
tensor evs
(
eigenVector(t, evals.x()),
eigenVector(t, evals.y()),
eigenVector(t, evals.z())
);
Ux = eigenVector(T, lambdas.x(), Uy, Uz);
Uy = eigenVector(T, lambdas.y(), Uz, Ux);
Uz = eigenVector(T, lambdas.z(), Ux, Uy);
return evs;
return tensor(Ux, Uy, Uz);
}
Foam::vector Foam::eigenValues(const symmTensor& t)
Foam::tensor Foam::eigenVectors(const tensor& T)
{
return eigenValues(tensor(t));
const vector lambdas(eigenValues(T));
return eigenVectors(T, lambdas);
}
Foam::vector Foam::eigenVector(const symmTensor& t, const scalar lambda)
Foam::vector Foam::eigenValues(const symmTensor& T)
{
return eigenVector(tensor(t), lambda);
return eigenValues(tensor(T));
}
Foam::tensor Foam::eigenVectors(const symmTensor& t)
Foam::vector Foam::eigenVector
(
const symmTensor& T,
const scalar lambda,
const vector& direction1,
const vector& direction2
)
{
return eigenVectors(tensor(t));
return eigenVector(tensor(T), lambda, direction1, direction2);
}
Foam::tensor Foam::eigenVectors(const symmTensor& T, const vector& lambdas)
{
return eigenVectors(tensor(T), lambdas);
}
Foam::tensor Foam::eigenVectors(const symmTensor& T)
{
return eigenVectors(tensor(T));
}

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -50,13 +50,27 @@ namespace Foam
typedef Tensor<scalar> tensor;
vector eigenValues(const tensor&);
vector eigenVector(const tensor&, const scalar lambda);
tensor eigenVectors(const tensor&);
vector eigenValues(const tensor& T);
vector eigenVector
(
const tensor& T,
const scalar lambda,
const vector& direction1,
const vector& direction2
);
tensor eigenVectors(const tensor& T, const vector& lambdas);
tensor eigenVectors(const tensor& T);
vector eigenValues(const symmTensor&);
vector eigenVector(const symmTensor&, const scalar lambda);
tensor eigenVectors(const symmTensor&);
vector eigenValues(const symmTensor& T);
vector eigenVector
(
const symmTensor& T,
const scalar lambda,
const vector& direction1,
const vector& direction2
);
tensor eigenVectors(const symmTensor& T, const vector& lambdas);
tensor eigenVectors(const symmTensor& T);
//- Data associated with tensor type are contiguous
template<>

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -24,6 +24,7 @@ License
\*---------------------------------------------------------------------------*/
#include "tensor2D.H"
#include "quadraticEqn.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -85,95 +86,96 @@ const Foam::tensor2D Foam::tensor2D::I
Foam::vector2D Foam::eigenValues(const tensor2D& t)
{
scalar i = 0;
scalar ii = 0;
// Coefficients of the characteristic quadratic polynomial (a = 1)
const scalar b = - t.xx() - t.yy();
const scalar c = t.xx()*t.yy() - t.xy()*t.yx();
if (mag(t.xy()) < SMALL && mag(t.yx()) < SMALL)
// Solve
Roots<2> roots = quadraticEqn(1, b, c).roots();
// Check the root types
vector2D lambda = vector2D::zero;
forAll(roots, i)
{
i = t.xx();
ii = t.yy();
}
else
{
scalar mb = t.xx() + t.yy();
scalar c = t.xx()*t.yy() - t.xy()*t.yx();
// If there is a zero root
if (mag(c) < SMALL)
switch (roots.type(i))
{
i = 0;
ii = mb;
}
else
{
scalar disc = sqr(mb) - 4*c;
if (disc > 0)
{
scalar q = sqrt(disc);
i = 0.5*(mb - q);
ii = 0.5*(mb + q);
}
else
{
case roots::real:
lambda[i] = roots[i];
break;
case roots::complex:
WarningInFunction
<< "Complex eigenvalues detected for tensor: " << t
<< endl;
lambda[i] = 0;
break;
case roots::posInf:
lambda[i] = VGREAT;
break;
case roots::negInf:
lambda[i] = - VGREAT;
break;
case roots::nan:
FatalErrorInFunction
<< "zero and complex eigenvalues in tensor2D: " << t
<< abort(FatalError);
}
<< "Eigenvalue calculation failed for tensor: " << t
<< exit(FatalError);
}
}
// Sort the eigenvalues into ascending order
if (i > ii)
if (lambda.x() > lambda.y())
{
Swap(i, ii);
Swap(lambda.x(), lambda.y());
}
return vector2D(i, ii);
return lambda;
}
Foam::vector2D Foam::eigenVector(const tensor2D& t, const scalar lambda)
Foam::vector2D Foam::eigenVector
(
const tensor2D& T,
const scalar lambda,
const vector2D& direction1
)
{
if (lambda < SMALL)
// Construct the linear system for this eigenvalue
tensor2D A(T - lambda*tensor2D::I);
// Evaluate the eigenvector using the largest divisor
if (mag(A.yy()) > mag(A.xx()) && mag(A.yy()) > SMALL)
{
return vector2D::zero;
vector2D ev(1, - A.yx()/A.yy());
return ev/mag(ev);
}
else if (mag(A.xx()) > SMALL)
{
vector2D ev(- A.xy()/A.xx(), 1);
return ev/mag(ev);
}
if (mag(t.xy()) < SMALL && mag(t.yx()) < SMALL)
{
if (lambda > min(t.xx(), t.yy()))
{
return vector2D(1, 0);
}
else
{
return vector2D(0, 1);
}
}
else if (mag(t.xy()) < SMALL)
{
return vector2D(lambda - t.yy(), t.yx());
}
else
{
return vector2D(t.xy(), lambda - t.yy());
}
// Repeated eigenvalue
return vector2D(- direction1.y(), direction1.x());
}
Foam::tensor2D Foam::eigenVectors(const tensor2D& t)
Foam::tensor2D Foam::eigenVectors(const tensor2D& T, const vector2D& lambdas)
{
vector2D evals(eigenValues(t));
vector2D Ux(1, 0), Uy(0, 1);
tensor2D evs
(
eigenVector(t, evals.x()),
eigenVector(t, evals.y())
);
Ux = eigenVector(T, lambdas.x(), Uy);
Uy = eigenVector(T, lambdas.y(), Ux);
return evs;
return tensor2D(Ux, Uy);
}
Foam::tensor2D Foam::eigenVectors(const tensor2D& T)
{
const vector2D lambdas(eigenValues(T));
return eigenVectors(T, lambdas);
}

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -49,7 +49,13 @@ namespace Foam
typedef Tensor2D<scalar> tensor2D;
vector2D eigenValues(const tensor2D& t);
vector2D eigenVector(const tensor2D& t, const scalar lambda);
vector2D eigenVector
(
const tensor2D& t,
const scalar lambda,
const vector2D& direction1
);
tensor2D eigenVectors(const tensor2D& t, const vector2D& lambdas);
tensor2D eigenVectors(const tensor2D& t);
//- Data associated with tensor2D type are contiguous

View File

@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
Class
Foam::Roots
Description
Templated storage for the roots of polynomial equations, plus flags to
indicate the nature of the roots.
SourceFiles
RootsI.H
Roots.C
\*---------------------------------------------------------------------------*/
#ifndef Roots_H
#define Roots_H
#include "VectorSpace.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace roots
{
//- Types of root
enum type
{
real = 0,
complex,
posInf,
negInf,
nan
};
}
/*---------------------------------------------------------------------------*\
Class Roots Declaration
\*---------------------------------------------------------------------------*/
template<direction N>
class Roots
:
public VectorSpace<Roots<N>, scalar, N>
{
// Private data
//- Root types, encoded into a single integer
label types_;
public:
// Constructors
//- Construct null
inline Roots();
//- Construct with a uniform value
inline Roots(const roots::type t, const scalar x);
//- Construct by concatenation
inline Roots
(
const roots::type t,
const scalar x,
const Roots<N - 1>& xs
);
//- Construct by concatenation
inline Roots
(
const Roots<N - 1>& xs,
const roots::type t,
const scalar x
);
//- Construct by concatenation
template <direction M>
inline Roots(const Roots<M>& xs, const Roots<N - M>& ys);
// Member Functions
//- Set the type of the i-th root
inline void type(const direction i, const roots::type t);
//- Return the type of the i-th root
inline roots::type type(const direction i) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "RootsI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template <Foam::direction N>
inline Foam::Roots<N>::Roots()
:
types_(0)
{
forAll(*this, i)
{
type(i, roots::nan);
}
}
template <Foam::direction N>
inline Foam::Roots<N>::Roots(const roots::type t, const scalar x)
:
types_(0)
{
forAll(*this, i)
{
this->v_[i] = x;
type(i, t);
}
}
template <Foam::direction N>
inline Foam::Roots<N>::Roots
(
const roots::type t,
const scalar x,
const Roots<N - 1>& xs
)
:
types_(0)
{
this->v_[0] = x;
type(0, t);
forAll(xs, i)
{
this->v_[i+1] = xs[i];
type(i + 1, xs.type(i));
}
}
template <Foam::direction N>
inline Foam::Roots<N>::Roots
(
const Roots<N - 1>& xs,
const roots::type t,
const scalar x
)
:
types_(0)
{
forAll(xs, i)
{
this->v_[i] = xs[i];
type(i, xs.type(i));
}
this->v_[N-1] = x;
type(N - 1, t);
}
template <Foam::direction N>
template <Foam::direction M>
inline Foam::Roots<N>::Roots
(
const Roots<M>& xs,
const Roots<N - M>& ys
)
:
types_(0)
{
forAll(xs, i)
{
this->v_[i] = xs[i];
type(i, xs.type(i));
}
forAll(ys, i)
{
this->v_[i + M] = ys[i];
type(i + M, ys.type(i));
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template <Foam::direction N>
inline void Foam::Roots<N>::type
(
const direction i,
const roots::type t
)
{
types_ += (t - type(i)) << 3*i;
}
template <Foam::direction N>
inline Foam::roots::type Foam::Roots<N>::type(const direction i) const
{
return static_cast<roots::type>((types_ >> 3*i) & 7);
}
// ************************************************************************* //

View File

@ -0,0 +1,164 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "linearEqn.H"
#include "quadraticEqn.H"
#include "cubicEqn.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::Roots<3> Foam::cubicEqn::roots() const
{
/*
This function solves a cubic equation of the following form:
a*x^3 + b*x^2 + c*x + d = 0
x^3 + B*x^2 + C*x + D = 0
The following two substitutions are used:
x = t - B/3
t = w - P/3/w
This reduces the problem to a quadratic in w^3.
w^6 + Q*w^3 - P = 0
Where Q and P are given in the code below.
The properties of the cubic can be related to the properties of this
quadratic in w^3. If it has a repeated root a zero, the cubic has a tripl
root. If it has a repeated root not at zero, the cubic has two real roots,
one repeated and one not. If it has two complex roots, the cubic has three
real roots. If it has two real roots, then the cubic has one real root and
two complex roots.
This is solved for the most numerically accurate value of w^3. See the
quadratic function for details on how to pick a value. This single value of
w^3 can yield up to three cube roots for w, which relate to the three
solutions for x.
Only a single root, or pair of conjugate roots, is directly evaluated; the
one, or ones with the lowest relative numerical error. Root identities are
then used to recover the remaining roots, possibly utilising a quadratic
and/or linear solution. This seems to be a good way of maintaining the
accuracy of roots at very different magnitudes.
*/
const scalar a = this->a();
const scalar b = this->b();
const scalar c = this->c();
const scalar d = this->d();
if (a == 0)
{
return Roots<3>(quadraticEqn(b, c, d).roots(), roots::nan, 0);
}
// This is assumed not to over- or under-flow. If it does, all bets are off.
const scalar p = c*a - b*b/3;
const scalar q = b*b*b*(2.0/27.0) - b*c*a/3 + d*a*a;
const scalar disc = p*p*p/27 + q*q/4;
// How many roots of what types are available?
const bool oneReal = disc == 0 && p == 0;
const bool twoReal = disc == 0 && p != 0;
const bool threeReal = disc < 0;
//const bool oneRealTwoComplex = disc > 0;
static const scalar sqrt3 = sqrt(3.0);
scalar x;
if (oneReal)
{
const Roots<1> r = linearEqn(- a, b/3).roots();
return Roots<3>(r.type(0), r[0]);
}
else if (twoReal)
{
if (q*b > 0)
{
x = - 2*cbrt(q/2) - b/3;
}
else
{
x = cbrt(q/2) - b/3;
const Roots<1> r = linearEqn(- a, x).roots();
return Roots<3>(Roots<2>(r, r), linearEqn(x*x, a*d).roots());
}
}
else if (threeReal)
{
const scalar wCbRe = - q/2, wCbIm = sqrt(- disc);
const scalar wAbs = cbrt(hypot(wCbRe, wCbIm));
const scalar wArg = atan2(wCbIm, wCbRe)/3;
const scalar wRe = wAbs*cos(wArg), wIm = wAbs*sin(wArg);
if (b > 0)
{
x = - wRe - mag(wIm)*sqrt3 - b/3;
}
else
{
x = 2*wRe - b/3;
}
}
else // if (oneRealTwoComplex)
{
const scalar wCb = - q/2 - sign(q)*sqrt(disc);
const scalar w = cbrt(wCb);
const scalar t = w - p/(3*w);
if (p + t*b < 0)
{
x = t - b/3;
}
else
{
const scalar xRe = - t/2 - b/3, xIm = sqrt3/2*(w + p/3/w);
x = - a*a*d/(xRe*xRe + xIm*xIm);
// This form of solving for the remaining roots seems more stable
// for this case. This has been determined by trial and error.
return
Roots<3>
(
linearEqn(- a, x).roots(),
quadraticEqn(a*x, x*x + b*x, - a*d).roots()
);
}
}
return
Roots<3>
(
linearEqn(- a, x).roots(),
quadraticEqn(- x*x, c*x + a*d, d*x).roots()
);
}
// ************************************************************************* //

View File

@ -0,0 +1,115 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
Class
Foam::cubicEqn
Description
Cubic equation of the form a*x^3 + b*x^2 + c*x + d = 0
SourceFiles
cubicEqnI.H
cubicEqn.C
\*---------------------------------------------------------------------------*/
#ifndef cubicEqn_H
#define cubicEqn_H
#include "Roots.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class cubicEqn Declaration
\*---------------------------------------------------------------------------*/
class cubicEqn
:
public VectorSpace<cubicEqn, scalar, 4>
{
public:
//- Component labeling enumeration
enum components { A, B, C, D };
// Constructors
//- Construct null
inline cubicEqn();
//- Construct initialized to zero
inline cubicEqn(const Foam::zero);
//- Construct from components
inline cubicEqn
(
const scalar a,
const scalar b,
const scalar c,
const scalar d
);
// Member Functions
// Access
inline scalar a() const;
inline scalar b() const;
inline scalar c() const;
inline scalar d() const;
inline scalar& a();
inline scalar& b();
inline scalar& c();
inline scalar& d();
//- Evaluate at x
inline scalar value(const scalar x) const;
//- Estimate the error of evaluation at x
inline scalar error(const scalar x) const;
//- Get the roots
Roots<3> roots() const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "cubicEqnI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,115 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::cubicEqn::cubicEqn()
{}
inline Foam::cubicEqn::cubicEqn(const Foam::zero)
:
VectorSpace<cubicEqn, scalar, 4>(Foam::zero())
{}
inline Foam::cubicEqn::cubicEqn
(
const scalar a,
const scalar b,
const scalar c,
const scalar d
)
{
this->v_[A] = a;
this->v_[B] = b;
this->v_[C] = c;
this->v_[D] = d;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline Foam::scalar Foam::cubicEqn::a() const
{
return this->v_[A];
}
inline Foam::scalar Foam::cubicEqn::b() const
{
return this->v_[B];
}
inline Foam::scalar Foam::cubicEqn::c() const
{
return this->v_[C];
}
inline Foam::scalar Foam::cubicEqn::d() const
{
return this->v_[D];
}
inline Foam::scalar& Foam::cubicEqn::a()
{
return this->v_[A];
}
inline Foam::scalar& Foam::cubicEqn::b()
{
return this->v_[B];
}
inline Foam::scalar& Foam::cubicEqn::c()
{
return this->v_[C];
}
inline Foam::scalar& Foam::cubicEqn::d()
{
return this->v_[D];
}
inline Foam::scalar Foam::cubicEqn::value(const scalar x) const
{
return x*(x*(x*a() + b()) + c()) + d();
}
inline Foam::scalar Foam::cubicEqn::error(const scalar x) const
{
return mag(SMALL*x*(x*(x*3*a() + 2*b()) + c()));
}
// ************************************************************************* //

View File

@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
Class
Foam::linearEqn
Description
Linear equation of the form a*x + b = 0
SourceFiles
linearEqnI.H
\*---------------------------------------------------------------------------*/
#ifndef linearEqn_H
#define linearEqn_H
#include "Roots.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class linearEqn Declaration
\*---------------------------------------------------------------------------*/
class linearEqn
:
public VectorSpace<linearEqn, scalar, 2>
{
public:
//- Component labeling enumeration
enum components { A, B };
// Constructors
//- Construct null
inline linearEqn();
//- Construct initialized to zero
inline linearEqn(const Foam::zero);
//- Construct from components
inline linearEqn(const scalar a, const scalar b);
// Member Functions
// Access
inline scalar a() const;
inline scalar b() const;
inline scalar& a();
inline scalar& b();
//- Evaluate at x
inline scalar value(const scalar x) const;
//- Estimate the error of evaluation at x
inline scalar error(const scalar x) const;
//- Get the roots
inline Roots<1> roots() const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "linearEqnI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,111 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::linearEqn::linearEqn()
{}
inline Foam::linearEqn::linearEqn(const Foam::zero)
:
VectorSpace<linearEqn, scalar, 2>(Foam::zero())
{}
inline Foam::linearEqn::linearEqn(const scalar a, const scalar b)
{
this->v_[A] = a;
this->v_[B] = b;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline Foam::scalar Foam::linearEqn::a() const
{
return this->v_[A];
}
inline Foam::scalar Foam::linearEqn::b() const
{
return this->v_[B];
}
inline Foam::scalar& Foam::linearEqn::a()
{
return this->v_[A];
}
inline Foam::scalar& Foam::linearEqn::b()
{
return this->v_[B];
}
inline Foam::scalar Foam::linearEqn::value(const scalar x) const
{
return x*a() + b();
}
inline Foam::scalar Foam::linearEqn::error(const scalar x) const
{
return mag(SMALL*x*a());
}
inline Foam::Roots<1> Foam::linearEqn::roots() const
{
/*
This function solves a linear equation of the following form:
a*x + b = 0
x + B = 0
*/
const scalar a = this->a();
const scalar b = this->b();
if (a == 0)
{
return Roots<1>(roots::nan, 0);
}
if (mag(b/VGREAT) >= mag(a))
{
return Roots<1>(sign(a) == sign(b) ? roots::negInf : roots::posInf, 0);
}
return Roots<1>(roots::real, - b/a);
}
// ************************************************************************* //

View File

@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "linearEqn.H"
#include "quadraticEqn.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::Roots<2> Foam::quadraticEqn::roots() const
{
/*
This function solves a quadraticEqn equation of the following form:
a*x^2 + b*x + c = 0
x^2 + B*x + C = 0
The quadraticEqn formula is as follows:
x = - B/2 +- sqrt(B*B - 4*C)/2
If the sqrt generates a complex number, this provides the result. If not
then the real root with the smallest floating point error is calculated.
x0 = - B/2 - sign(B)*sqrt(B*B - 4*C)/2
The other root is the obtained using an identity.
x1 = C/x0
*/
const scalar a = this->a();
const scalar b = this->b();
const scalar c = this->c();
if (a == 0)
{
return Roots<2>(linearEqn(b, c).roots(), roots::nan, 0);
}
// This is assumed not to over- or under-flow. If it does, all bets are off.
const scalar disc = b*b/4 - a*c;
// How many roots of what types are available?
const bool oneReal = disc == 0;
const bool twoReal = disc > 0;
//const bool twoComplex = disc < 0;
if (oneReal)
{
const Roots<1> r = linearEqn(- a, b/2).roots();
return Roots<2>(r, r);
}
else if (twoReal)
{
const scalar x = - b/2 - sign(b)*sqrt(disc);
return Roots<2>(linearEqn(- a, x).roots(), linearEqn(- x, c).roots());
}
else // if (twoComplex)
{
return Roots<2>(roots::complex, 0);
}
}
// ************************************************************************* //

View File

@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
Class
Foam::quadraticEqn
Description
Quadratic equation of the form a*x^2 + b*x + c = 0
SourceFiles
quadraticEqnI.H
quadraticEqn.C
\*---------------------------------------------------------------------------*/
#ifndef quadraticEqn_H
#define quadraticEqn_H
#include "Roots.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class quadraticEqn Declaration
\*---------------------------------------------------------------------------*/
class quadraticEqn
:
public VectorSpace<quadraticEqn, scalar, 3>
{
public:
//- Component labeling enumeration
enum components { A, B, C };
// Constructors
//- Construct null
inline quadraticEqn();
//- Construct initialized to zero
inline quadraticEqn(const Foam::zero);
//- Construct from components
inline quadraticEqn(const scalar a, const scalar b, const scalar c);
// Member Functions
// Access
inline scalar a() const;
inline scalar b() const;
inline scalar c() const;
inline scalar& a();
inline scalar& b();
inline scalar& c();
//- Evaluate at x
inline scalar value(const scalar x) const;
//- Estimate the error of evaluation at x
inline scalar error(const scalar x) const;
//- Get the roots
Roots<2> roots() const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "quadraticEqnI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::quadraticEqn::quadraticEqn()
{}
inline Foam::quadraticEqn::quadraticEqn(const Foam::zero)
:
VectorSpace<quadraticEqn, scalar, 3>(Foam::zero())
{}
inline Foam::quadraticEqn::quadraticEqn
(
const scalar a,
const scalar b,
const scalar c
)
{
this->v_[A] = a;
this->v_[B] = b;
this->v_[C] = c;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline Foam::scalar Foam::quadraticEqn::a() const
{
return this->v_[A];
}
inline Foam::scalar Foam::quadraticEqn::b() const
{
return this->v_[B];
}
inline Foam::scalar Foam::quadraticEqn::c() const
{
return this->v_[C];
}
inline Foam::scalar& Foam::quadraticEqn::a()
{
return this->v_[A];
}
inline Foam::scalar& Foam::quadraticEqn::b()
{
return this->v_[B];
}
inline Foam::scalar& Foam::quadraticEqn::c()
{
return this->v_[C];
}
inline Foam::scalar Foam::quadraticEqn::value(const scalar x) const
{
return x*(x*a() + b()) + c();
}
inline Foam::scalar Foam::quadraticEqn::error(const scalar x) const
{
return mag(SMALL*x*(x*2*a() + b()));
}
// ************************************************************************* //

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -458,7 +458,7 @@ void Foam::edgeCollapser::faceCollapseAxisAndAspectRatio
// normal, as it has the greatest value. The minimum eigenvalue
// is the dominant collapse axis for high aspect ratio faces.
collapseAxis = eigenVector(J, eVals.x());
collapseAxis = eigenVectors(J, eVals).x();
// The inertia calculation describes the mass distribution as a
// function of distance squared to the axis, so the square root of