1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
27 #include "mathematicalConstants.H"
29 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 const char* const tensor::typeName = "tensor";
40 const char* tensor::componentNames[] =
48 const tensor tensor::zero
56 const tensor tensor::one
64 const tensor tensor::max
66 VGREAT, VGREAT, VGREAT,
67 VGREAT, VGREAT, VGREAT,
68 VGREAT, VGREAT, VGREAT
72 const tensor tensor::min
74 -VGREAT, -VGREAT, -VGREAT,
75 -VGREAT, -VGREAT, -VGREAT,
76 -VGREAT, -VGREAT, -VGREAT
80 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
82 // Return eigenvalues in ascending order of absolute values
83 vector eigenValues(const tensor& t)
92 mag(t.xy()) + mag(t.xz()) + mag(t.yx())
93 + mag(t.yz()) + mag(t.zx()) + mag(t.zy())
105 scalar a = -t.xx() - t.yy() - t.zz();
107 scalar b = t.xx()*t.yy() + t.xx()*t.zz() + t.yy()*t.zz()
108 - t.xy()*t.yx() - t.xz()*t.zx() - t.yz()*t.zy();
110 scalar c = - t.xx()*t.yy()*t.zz() - t.xy()*t.yz()*t.zx()
111 - t.xz()*t.yx()*t.zy() + t.xz()*t.yy()*t.zx()
112 + t.xy()*t.yx()*t.zz() + t.xx()*t.yz()*t.zy();
114 // If there is a zero root
115 if (mag(c) < 1.0e-100)
117 scalar disc = sqr(a) - 4*b;
121 scalar q = -0.5*sqrt(max(scalar(0), disc));
129 FatalErrorIn("eigenValues(const tensor&)")
130 << "zero and complex eigenvalues in tensor: " << t
131 << abort(FatalError);
136 scalar Q = (a*a - 3*b)/9;
137 scalar R = (2*a*a*a - 9*a*b + 27*c)/54;
142 // Three different real roots
145 scalar sqrtQ = sqrt(Q);
146 scalar theta = acos(R/(Q*sqrtQ));
148 scalar m2SqrtQ = -2*sqrtQ;
151 i = m2SqrtQ*cos(theta/3) - aBy3;
152 ii = m2SqrtQ*cos((theta + mathematicalConstant::twoPi)/3)
154 iii = m2SqrtQ*cos((theta - mathematicalConstant::twoPi)/3)
159 scalar A = cbrt(R + sqrt(R2 - Q3));
161 // Three equal real roots
165 return vector(root, root, root);
170 WarningIn("eigenValues(const tensor&)")
171 << "complex eigenvalues detected for tensor: " << t
181 // Sort the eigenvalues into ascending order
182 if (mag(i) > mag(ii))
187 if (mag(ii) > mag(iii))
192 if (mag(i) > mag(ii))
197 return vector(i, ii, iii);
201 vector eigenVector(const tensor& t, const scalar lambda)
203 // Construct the matrix for the eigenvector problem
204 tensor A(t - lambda*I);
206 // Calculate the sub-determinants of the 3 components
207 scalar sd0 = A.yy()*A.zz() - A.yz()*A.zy();
208 scalar sd1 = A.xx()*A.zz() - A.xz()*A.zx();
209 scalar sd2 = A.xx()*A.yy() - A.xy()*A.yx();
211 scalar magSd0 = mag(sd0);
212 scalar magSd1 = mag(sd1);
213 scalar magSd2 = mag(sd2);
215 // Evaluate the eigenvector using the largest sub-determinant
216 if (magSd0 > magSd1 && magSd0 > magSd2 && magSd0 > SMALL)
221 (A.yz()*A.zx() - A.zz()*A.yx())/sd0,
222 (A.zy()*A.yx() - A.yy()*A.zx())/sd0
228 else if (magSd1 > magSd2 && magSd1 > SMALL)
232 (A.xz()*A.zy() - A.zz()*A.xy())/sd1,
234 (A.zx()*A.xy() - A.xx()*A.zy())/sd1
240 else if (magSd2 > SMALL)
244 (A.xy()*A.yz() - A.yy()*A.xz())/sd2,
245 (A.yx()*A.xz() - A.xx()*A.yz())/sd2,
254 // Double identical eigen-value
255 if (mag(A.xx()) > SMALL)
257 return vector(0, 1, 0);
259 else if (mag(A.yy()) > SMALL)
261 return vector(0, 0, 1);
263 else if (mag(A.zz()) > SMALL)
265 return vector(1, 0, 0);
269 // Everything is zero. Return arbitrary vector
270 return vector(1, 0, 0);
276 tensor eigenVectors(const tensor& t)
278 vector evals(eigenValues(t));
280 // Modification for strict-aliasing compliance.
281 // Sandeep Menon, 01/Dec/2010
283 // Test for null eigen values to return a not null eigen vector.
284 // Jovani Favero, 18/Nov/2009. Adjusted by HJ, to correct for multiple zero
285 // entries in eigenvalues
287 // Start with largest eigen-value: if this is zero, all are zero
288 // and xyz tensor is returned
289 if (mag(evals.z()) < SMALL)
294 // One valid eigen-vector. Manufacture second and third direction
295 // as orthogonal vectors onto the first one with arbitrary orientation
296 if (mag(evals.y()) < SMALL)
298 // Take the z eigenvector and find a non-zero component
299 vector zVec = eigenVector(t, evals.z());
303 if (mag(zVec.z()) > 0)
306 yVec = vector(zVec.x(), -zVec.z(), zVec.y());
308 else if (mag(zVec.y()) > 0)
311 yVec = vector(-zVec.y(), zVec.x(), zVec.z());
316 yVec = vector(zVec.z(), zVec.y(), -zVec.x());
319 vector xVec = yVec ^ zVec;
321 return tensor(xVec, yVec, zVec);
324 if (mag(evals.x()) < SMALL)
326 vector xVec = eigenVector(t, evals.x());
327 vector yVec = eigenVector(t, evals.y());
328 vector zVec = eigenVector(t, evals.z());
330 // If second and third eigen value is the same, the two vectors
331 // will be identical. Fix this
332 if (mag(evals.z() - evals.y()) < SMALL)
337 return tensor(xVec, yVec, zVec);
342 eigenVector(t, evals.x()),
343 eigenVector(t, evals.y()),
344 eigenVector(t, evals.z())
349 // Return eigenvalues in ascending order of absolute values
350 vector eigenValues(const symmTensor& t)
359 mag(t.xy()) + mag(t.xz()) + mag(t.xy())
360 + mag(t.yz()) + mag(t.xz()) + mag(t.yz())
372 scalar a = -t.xx() - t.yy() - t.zz();
374 scalar b = t.xx()*t.yy() + t.xx()*t.zz() + t.yy()*t.zz()
375 - t.xy()*t.xy() - t.xz()*t.xz() - t.yz()*t.yz();
377 scalar c = - t.xx()*t.yy()*t.zz() - t.xy()*t.yz()*t.xz()
378 - t.xz()*t.xy()*t.yz() + t.xz()*t.yy()*t.xz()
379 + t.xy()*t.xy()*t.zz() + t.xx()*t.yz()*t.yz();
381 // If there is a zero root
384 const scalar disc = Foam::max(sqr(a) - 4*b, scalar(0));
386 scalar q = -0.5*sqrt(max(scalar(0), disc));
394 scalar Q = (a*a - 3*b)/9;
395 scalar R = (2*a*a*a - 9*a*b + 27*c)/54;
400 // Three different real roots
403 scalar sqrtQ = sqrt(Q);
404 scalar theta = acos(R/(Q*sqrtQ));
406 scalar m2SqrtQ = -2*sqrtQ;
409 i = m2SqrtQ*cos(theta/3) - aBy3;
410 ii = m2SqrtQ*cos((theta + mathematicalConstant::twoPi)/3)
412 iii = m2SqrtQ*cos((theta - mathematicalConstant::twoPi)/3)
417 scalar A = cbrt(R + sqrt(R2 - Q3));
419 // Three equal real roots
423 return vector(root, root, root);
428 WarningIn("eigenValues(const symmTensor&)")
429 << "complex eigenvalues detected for symmTensor: " << t
439 // Sort the eigenvalues into ascending order
440 if (mag(i) > mag(ii))
445 if (mag(ii) > mag(iii))
450 if (mag(i) > mag(ii))
455 return vector(i, ii, iii);
459 vector eigenVector(const symmTensor& t, const scalar lambda)
461 // Construct the matrix for the eigenvector problem
462 symmTensor A(t - lambda*I);
464 // Calculate the sub-determinants of the 3 components
465 scalar sd0 = A.yy()*A.zz() - A.yz()*A.yz();
466 scalar sd1 = A.xx()*A.zz() - A.xz()*A.xz();
467 scalar sd2 = A.xx()*A.yy() - A.xy()*A.xy();
469 scalar magSd0 = mag(sd0);
470 scalar magSd1 = mag(sd1);
471 scalar magSd2 = mag(sd2);
473 // Evaluate the eigenvector using the largest sub-determinant
474 if (magSd0 > magSd1 && magSd0 > magSd2 && magSd0 > SMALL)
479 (A.yz()*A.xz() - A.zz()*A.xy())/sd0,
480 (A.yz()*A.xy() - A.yy()*A.xz())/sd0
486 else if (magSd1 > magSd2 && magSd1 > SMALL)
490 (A.xz()*A.yz() - A.zz()*A.xy())/sd1,
492 (A.xz()*A.xy() - A.xx()*A.yz())/sd1
498 else if (magSd2 > SMALL)
502 (A.xy()*A.yz() - A.yy()*A.xz())/sd2,
503 (A.xy()*A.xz() - A.xx()*A.yz())/sd2,
512 // Double identical eigen-value
513 if (mag(A.xx()) > SMALL)
515 return vector(0, 1, 0);
517 else if (mag(A.yy()) > SMALL)
519 return vector(0, 0, 1);
521 else if (mag(A.zz()) > SMALL)
523 return vector(1, 0, 0);
527 // Everything is zero. Return arbitrary vector
528 return vector(1, 0, 0);
534 tensor eigenVectors(const symmTensor& t)
536 vector evals(eigenValues(t));
538 // Modification for strict-aliasing compliance.
539 // Sandeep Menon, 01/Dec/2010
541 // Test for null eigen values to return a not null eigen vector
542 // Jovani Favero, 18/Nov/2009
544 // Start with largest eigen-value: if this is zero, all are zero
545 // and original tensor is returned
546 if (mag(evals.z()) < SMALL)
551 // One valid eigen-vector. Manufacture second and third direction
552 // as orthogonal vectors onto the first one with arbitrary orientation
553 if (mag(evals.y()) < SMALL)
555 // Take the z eigenvector and find a non-zero component
556 vector zVec = eigenVector(t, evals.z());
560 if (mag(zVec.z()) > 0)
563 yVec = vector(zVec.x(), -zVec.z(), zVec.y());
565 else if (mag(zVec.y()) > 0)
568 yVec = vector(-zVec.y(), zVec.x(), zVec.z());
573 yVec = vector(zVec.z(), zVec.y(), -zVec.x());
576 vector xVec = yVec ^ zVec;
578 // Note different return
579 return tensor(xVec, yVec, zVec);
582 if (mag(evals.x()) < SMALL)
584 vector xVec = eigenVector(t, evals.x());
585 vector yVec = eigenVector(t, evals.y());
586 vector zVec = eigenVector(t, evals.z());
588 // If second and third eigen value is the same, the two vectors
589 // will be identical. Fix this
590 if (mag(evals.z() - evals.y()) < SMALL)
595 return tensor(xVec, yVec, zVec);
600 eigenVector(t, evals.x()),
601 eigenVector(t, evals.y()),
602 eigenVector(t, evals.z())
607 // Matrix inversion with singular value decomposition
608 tensor hinv(const tensor& t)
610 static const scalar large = 1e10;
611 static const scalar small = 1e-10;
619 vector eig = eigenValues(t);
620 tensor eigVecs = eigenVectors(t);
622 tensor zeroInv = tensor::zero;
624 // Test if all eigen values are zero.
625 // If this happens then eig.z() = SMALL, and hinv(t)
626 // returns a zero tensor.
627 // Jovani Favero, 18/Nov/2009
628 // Further bug fix: replace > with == and add SMALL to zeroInv
629 // Dominik Christ, 7/Aug/2012
630 if (mag(eig.z()) == large*mag(eig.z()))
632 // Three zero eigen values (z is largest in magnitude).
633 // Return zero inverse
637 // Compare smaller eigen values and if out of range of large
638 // consider them singular
640 if (mag(eig.z()) > large*mag(eig.x()))
642 // Make a tensor out of symmTensor sqr. HJ, 24/Oct/2009
643 zeroInv += tensor(sqr(eigVecs.x()));
646 if (mag(eig.z()) > large*mag(eig.y()))
648 // Make a tensor out of symmTensor sqr. HJ, 24/Oct/2009
649 zeroInv += tensor(sqr(eigVecs.y()));
652 return inv(t + zeroInv) - zeroInv;
657 symmTensor hinv(const symmTensor& t)
659 static const scalar large = 1e10;
660 static const scalar small = 1e-10;
668 vector eig = eigenValues(t);
669 tensor eigVecs = eigenVectors(t);
671 symmTensor zeroInv = symmTensor::zero;
673 // Test if all eigen values are zero,
674 // If this happens then eig.z() = SMALL
675 // and hinv(t) return a zero tensor.
676 // Jovani Favero, 18/Nov/2009
677 // Further bug fix: replace > with == and add SMALL to zeroInv
678 // Dominik Christ, 7/Aug/2012
679 if (mag(eig.z()) == large*mag(eig.z()))
681 // Three zero eigen values (z is largest in magnitude).
682 // Return zero inverse
686 // Compare smaller eigen values and if out of range of large
687 // consider them singular
689 if (mag(eig.z()) > large*mag(eig.x()))
691 zeroInv += sqr(eigVecs.x());
694 if (mag(eig.z()) > large*mag(eig.y()))
696 zeroInv += sqr(eigVecs.y());
699 return inv(t + zeroInv) - zeroInv;
704 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
706 } // End namespace Foam
708 // ************************************************************************* //