From 0be6269add80fd3037e64de38bcd9d5a64e6d1cf Mon Sep 17 00:00:00 2001 From: Will Bainbridge <http://cfd.direct> Date: Wed, 22 Mar 2017 15:11:54 +0000 Subject: [PATCH] Added robust primitive cubic/quadratic/linear equation solutions. Applied to eigen-value calculations. Fixed repeated-eigen-value issues in eigen-vector generation. --- applications/test/cubicEqn/Make/files | 3 + applications/test/cubicEqn/Make/options | 3 + applications/test/cubicEqn/Test-cubicEqn.C | 101 ++++++++ src/OpenFOAM/Make/files | 3 + .../primitives/Tensor/tensor/tensor.C | 229 +++++++----------- .../primitives/Tensor/tensor/tensor.H | 30 ++- .../primitives/Tensor2D/tensor2D/tensor2D.C | 130 +++++----- .../primitives/Tensor2D/tensor2D/tensor2D.H | 10 +- .../primitives/polynomialEqns/Roots.H | 131 ++++++++++ .../primitives/polynomialEqns/RootsI.H | 136 +++++++++++ .../polynomialEqns/cubicEqn/cubicEqn.C | 164 +++++++++++++ .../polynomialEqns/cubicEqn/cubicEqn.H | 115 +++++++++ .../polynomialEqns/cubicEqn/cubicEqnI.H | 115 +++++++++ .../polynomialEqns/linearEqn/linearEqn.H | 104 ++++++++ .../polynomialEqns/linearEqn/linearEqnI.H | 111 +++++++++ .../quadraticEqn/quadraticEqn.C | 88 +++++++ .../quadraticEqn/quadraticEqn.H | 107 ++++++++ .../quadraticEqn/quadraticEqnI.H | 101 ++++++++ .../polyTopoChange/edgeCollapser.C | 4 +- 19 files changed, 1473 insertions(+), 212 deletions(-) create mode 100644 applications/test/cubicEqn/Make/files create mode 100644 applications/test/cubicEqn/Make/options create mode 100644 applications/test/cubicEqn/Test-cubicEqn.C create mode 100644 src/OpenFOAM/primitives/polynomialEqns/Roots.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/RootsI.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.C create mode 100644 src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqnI.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqn.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqnI.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.C create mode 100644 src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.H create mode 100644 src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqnI.H diff --git a/applications/test/cubicEqn/Make/files b/applications/test/cubicEqn/Make/files new file mode 100644 index 00000000000..91b9e84dcc8 --- /dev/null +++ b/applications/test/cubicEqn/Make/files @@ -0,0 +1,3 @@ +Test-cubicEqn.C + +EXE = $(FOAM_USER_APPBIN)/Test-cubicEqn diff --git a/applications/test/cubicEqn/Make/options b/applications/test/cubicEqn/Make/options new file mode 100644 index 00000000000..4c3dd783cb4 --- /dev/null +++ b/applications/test/cubicEqn/Make/options @@ -0,0 +1,3 @@ +EXE_INC = + +EXE_LIBS = diff --git a/applications/test/cubicEqn/Test-cubicEqn.C b/applications/test/cubicEqn/Test-cubicEqn.C new file mode 100644 index 00000000000..d0ab6066662 --- /dev/null +++ b/applications/test/cubicEqn/Test-cubicEqn.C @@ -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; +} diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files index 84f83567d01..44ef405e419 100644 --- a/src/OpenFOAM/Make/files +++ b/src/OpenFOAM/Make/files @@ -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 diff --git a/src/OpenFOAM/primitives/Tensor/tensor/tensor.C b/src/OpenFOAM/primitives/Tensor/tensor/tensor.C index 2d8da5e71ca..0914c279a4d 100644 --- a/src/OpenFOAM/primitives/Tensor/tensor/tensor.C +++ b/src/OpenFOAM/primitives/Tensor/tensor/tensor.C @@ -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; - - // diagonal matrix - if - ( - ( - mag(t.xy()) + mag(t.xz()) + mag(t.yx()) - + mag(t.yz()) + mag(t.zx()) + mag(t.zy()) - ) - < SMALL - ) - { - i = t.xx(); - ii = t.yy(); - iii = t.zz(); - } - - // non-diagonal matrix - else + // 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(); + + // Solve + Roots<3> roots = cubicEqn(1, b, c, d).roots(); + + // Check the root types + vector lambda = vector::zero; + forAll(roots, i) { - // 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 (ii > iii) + if (lambda.y() > lambda.z()) { - Swap(ii, iii); + Swap(lambda.y(), lambda.z()); } - - if (i > ii) + if (lambda.x() > lambda.y()) { - Swap(i, ii); + Swap(lambda.x(), lambda.y()); } - 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, 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) +Foam::tensor Foam::eigenVectors(const tensor& T) { - vector evals(eigenValues(t)); + const vector lambdas(eigenValues(T)); + + return eigenVectors(T, lambdas); +} - tensor evs - ( - eigenVector(t, evals.x()), - eigenVector(t, evals.y()), - eigenVector(t, evals.z()) - ); - return evs; +Foam::vector Foam::eigenValues(const symmTensor& T) +{ + return eigenValues(tensor(T)); } -Foam::vector Foam::eigenValues(const symmTensor& t) +Foam::vector Foam::eigenVector +( + const symmTensor& T, + const scalar lambda, + const vector& direction1, + const vector& direction2 +) { - return eigenValues(tensor(t)); + return eigenVector(tensor(T), lambda, direction1, direction2); } -Foam::vector Foam::eigenVector(const symmTensor& t, const scalar lambda) +Foam::tensor Foam::eigenVectors(const symmTensor& T, const vector& lambdas) { - return eigenVector(tensor(t), lambda); + return eigenVectors(tensor(T), lambdas); } -Foam::tensor Foam::eigenVectors(const symmTensor& t) +Foam::tensor Foam::eigenVectors(const symmTensor& T) { - return eigenVectors(tensor(t)); + return eigenVectors(tensor(T)); } diff --git a/src/OpenFOAM/primitives/Tensor/tensor/tensor.H b/src/OpenFOAM/primitives/Tensor/tensor/tensor.H index b92cc8e64a1..89966a9f72a 100644 --- a/src/OpenFOAM/primitives/Tensor/tensor/tensor.H +++ b/src/OpenFOAM/primitives/Tensor/tensor/tensor.H @@ -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 symmTensor&); -vector eigenVector(const symmTensor&, const scalar lambda); -tensor eigenVectors(const symmTensor&); +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& 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<> diff --git a/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.C b/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.C index 36f97252558..bf5a12aa7f5 100644 --- a/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.C +++ b/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.C @@ -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) - { - i = t.xx(); - ii = t.yy(); - } - else - { - scalar mb = t.xx() + t.yy(); - scalar c = t.xx()*t.yy() - t.xy()*t.yx(); + // Solve + Roots<2> roots = quadraticEqn(1, b, c).roots(); - // If there is a zero root - if (mag(c) < SMALL) - { - i = 0; - ii = mb; - } - else + // Check the root types + vector2D lambda = vector2D::zero; + forAll(roots, i) + { + switch (roots.type(i)) { - 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) - { - return vector2D::zero; - } + // Construct the linear system for this eigenvalue + tensor2D A(T - lambda*tensor2D::I); - if (mag(t.xy()) < SMALL && mag(t.yx()) < SMALL) + // Evaluate the eigenvector using the largest divisor + if (mag(A.yy()) > mag(A.xx()) && mag(A.yy()) > 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()); + vector2D ev(1, - A.yx()/A.yy()); + + return ev/mag(ev); } - else + else if (mag(A.xx()) > SMALL) { - return vector2D(t.xy(), lambda - t.yy()); + vector2D ev(- A.xy()/A.xx(), 1); + + return ev/mag(ev); } + + // 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); + + Ux = eigenVector(T, lambdas.x(), Uy); + Uy = eigenVector(T, lambdas.y(), Ux); + + return tensor2D(Ux, Uy); +} + - tensor2D evs - ( - eigenVector(t, evals.x()), - eigenVector(t, evals.y()) - ); +Foam::tensor2D Foam::eigenVectors(const tensor2D& T) +{ + const vector2D lambdas(eigenValues(T)); - return evs; + return eigenVectors(T, lambdas); } diff --git a/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.H b/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.H index 1b7cf2f6007..feb7f0e2756 100644 --- a/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.H +++ b/src/OpenFOAM/primitives/Tensor2D/tensor2D/tensor2D.H @@ -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 diff --git a/src/OpenFOAM/primitives/polynomialEqns/Roots.H b/src/OpenFOAM/primitives/polynomialEqns/Roots.H new file mode 100644 index 00000000000..27829eee46b --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/Roots.H @@ -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 + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/RootsI.H b/src/OpenFOAM/primitives/polynomialEqns/RootsI.H new file mode 100644 index 00000000000..dd1052f0afd --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/RootsI.H @@ -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); +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.C b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.C new file mode 100644 index 00000000000..c46b794c38e --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.C @@ -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() + ); +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.H b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.H new file mode 100644 index 00000000000..db923761452 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqn.H @@ -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 + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqnI.H b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqnI.H new file mode 100644 index 00000000000..eb6a3283e45 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/cubicEqn/cubicEqnI.H @@ -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())); +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqn.H b/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqn.H new file mode 100644 index 00000000000..debc52cc01d --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqn.H @@ -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 + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqnI.H b/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqnI.H new file mode 100644 index 00000000000..e86ccbdd0a6 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/linearEqn/linearEqnI.H @@ -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); +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.C b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.C new file mode 100644 index 00000000000..d84c2928c25 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.C @@ -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); + } +} + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.H b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.H new file mode 100644 index 00000000000..1327dc15c94 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqn.H @@ -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 + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqnI.H b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqnI.H new file mode 100644 index 00000000000..4b4aacae614 --- /dev/null +++ b/src/OpenFOAM/primitives/polynomialEqns/quadraticEqn/quadraticEqnI.H @@ -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())); +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/edgeCollapser.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/edgeCollapser.C index c0eb30678cb..9933c780d48 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/edgeCollapser.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/edgeCollapser.C @@ -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 -- GitLab