symmTensor.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2020-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "symmTensor.H"
30 #include "cubicEqn.H"
31 #include "mathematicalConstants.H"
32 #include "SVD.H"
33 
34 using namespace Foam::constant::mathematical;
35 
36 
37 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
38 
39 template<>
40 const char* const Foam::symmTensor::vsType::typeName = "symmTensor";
41 
42 template<>
43 const char* const Foam::symmTensor::vsType::componentNames[] =
44 {
45  "xx", "xy", "xz",
46  "yy", "yz",
47  "zz"
48 };
49 
50 template<>
52 (
54 );
55 
56 template<>
58 (
60 );
61 
62 template<>
64 (
65  symmTensor::uniform(VGREAT)
66 );
67 
68 template<>
70 (
71  symmTensor::uniform(-VGREAT)
72 );
73 
74 template<>
76 (
77  symmTensor::uniform(ROOTVGREAT)
78 );
79 
80 template<>
82 (
83  symmTensor::uniform(-ROOTVGREAT)
84 );
85 
86 template<>
88 (
89  1, 0, 0,
90  1, 0,
91  1
92 );
93 
94 
95 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
96 
98 {
99  // Return ascending diagonal if T is effectively diagonal tensor
100  if ((sqr(T.xy()) + sqr(T.xz()) + sqr(T.yz())) < ROOTSMALL)
101  {
102  vector eVals(T.diag());
103 
104  std::sort(eVals.begin(), eVals.end());
105 
106  return eVals;
107  }
108 
109  // Coefficients of the characteristic cubic polynomial (a = 1)
110  const scalar b = - T.xx() - T.yy() - T.zz();
111  const scalar c =
112  T.xx()*T.yy() + T.xx()*T.zz() + T.yy()*T.zz()
113  - T.xy()*T.yx() - T.yz()*T.zy() - T.zx()*T.xz();
114  const scalar d =
115  - T.xx()*T.yy()*T.zz()
116  - T.xy()*T.yz()*T.zx() - T.xz()*T.zy()*T.yx()
117  + T.xx()*T.yz()*T.zy() + T.yy()*T.zx()*T.xz() + T.zz()*T.xy()*T.yx();
118 
119  // Determine the roots of the characteristic cubic polynomial
120  Roots<3> roots(cubicEqn(1, b, c, d).roots());
121 
122  vector eVals(Zero);
123 
124  // Check the root types
125  forAll(roots, i)
126  {
127  switch (roots.type(i))
128  {
129  case roots::real:
130  eVals[i] = roots[i];
131  break;
132  case roots::complex:
133  case roots::posInf:
134  case roots::negInf:
135  case roots::nan:
137  << "Eigenvalue computation fails for symmTensor: " << T
138  << "due to the non-real root = " << roots[i]
139  << endl;
140  eVals[i] = roots[i];
141  break;
142  }
143  }
144 
145  // Sort the eigenvalues into ascending order
146  std::sort(eVals.begin(), eVals.end());
147 
148  return eVals;
149 }
150 
151 
153 (
154  const symmTensor& T,
155  const scalar eVal,
156  const vector& standardBasis1,
157  const vector& standardBasis2
158 )
159 {
160  // Construct the characteristic equation system for this eigenvalue
161  const tensor A(T - eVal*I);
162 
163  {
164  // Determinants of the 2x2 sub-matrices used to find the eigenvectors
165  // Sub-determinants for a unique eigenvenvalue
166  const scalar sd0 = A.yy()*A.zz() - A.yz()*A.zy();
167  const scalar sd1 = A.zz()*A.xx() - A.zx()*A.xz();
168  const scalar sd2 = A.xx()*A.yy() - A.xy()*A.yx();
169  const scalar magSd0 = mag(sd0);
170  const scalar magSd1 = mag(sd1);
171  const scalar magSd2 = mag(sd2);
172 
173  // Evaluate the eigenvector using the largest sub-determinant
174  if (magSd0 >= magSd1 && magSd0 >= magSd2 && magSd0 > SMALL)
175  {
176  const vector eVec
177  (
178  1,
179  (A.yz()*A.zx() - A.zz()*A.yx())/sd0,
180  (A.zy()*A.yx() - A.yy()*A.zx())/sd0
181  );
182 
183  #ifdef FULLDEBUG
184  if (mag(eVec) < SMALL)
185  {
187  << "Eigenvector magnitude should be non-zero:"
188  << "mag(eigenvector) = " << mag(eVec)
189  << abort(FatalError);
190  }
191  #endif
192 
193  return eVec/mag(eVec);
194  }
195  else if (magSd1 >= magSd2 && magSd1 > SMALL)
196  {
197  const vector eVec
198  (
199  (A.xz()*A.zy() - A.zz()*A.xy())/sd1,
200  1,
201  (A.zx()*A.xy() - A.xx()*A.zy())/sd1
202  );
203 
204  #ifdef FULLDEBUG
205  if (mag(eVec) < SMALL)
206  {
208  << "Eigenvector magnitude should be non-zero:"
209  << "mag(eigenvector) = " << mag(eVec)
210  << abort(FatalError);
211  }
212  #endif
213 
214  return eVec/mag(eVec);
215  }
216  else if (magSd2 > SMALL)
217  {
218  const vector eVec
219  (
220  (A.xy()*A.yz() - A.yy()*A.xz())/sd2,
221  (A.yx()*A.xz() - A.xx()*A.yz())/sd2,
222  1
223  );
224 
225  #ifdef FULLDEBUG
226  if (mag(eVec) < SMALL)
227  {
229  << "Eigenvector magnitude should be non-zero:"
230  << "mag(eigenvector) = " << mag(eVec)
231  << abort(FatalError);
232  }
233  #endif
234 
235  return eVec/mag(eVec);
236  }
237  }
238 
239  // Sub-determinants for a repeated eigenvalue
240  const scalar sd0 = A.yy()*standardBasis1.z() - A.yz()*standardBasis1.y();
241  const scalar sd1 = A.zz()*standardBasis1.x() - A.zx()*standardBasis1.z();
242  const scalar sd2 = A.xx()*standardBasis1.y() - A.xy()*standardBasis1.x();
243  const scalar magSd0 = mag(sd0);
244  const scalar magSd1 = mag(sd1);
245  const scalar magSd2 = mag(sd2);
246 
247  // Evaluate the eigenvector using the largest sub-determinant
248  if (magSd0 >= magSd1 && magSd0 >= magSd2 && magSd0 > SMALL)
249  {
250  const vector eVec
251  (
252  1,
253  (A.yz()*standardBasis1.x() - standardBasis1.z()*A.yx())/sd0,
254  (standardBasis1.y()*A.yx() - A.yy()*standardBasis1.x())/sd0
255  );
256 
257  #ifdef FULLDEBUG
258  if (mag(eVec) < SMALL)
259  {
261  << "Eigenvector magnitude should be non-zero:"
262  << "mag(eigenvector) = " << mag(eVec)
263  << abort(FatalError);
264  }
265  #endif
266 
267  return eVec/mag(eVec);
268  }
269  else if (magSd1 >= magSd2 && magSd1 > SMALL)
270  {
271  const vector eVec
272  (
273  (standardBasis1.z()*A.zy() - A.zz()*standardBasis1.y())/sd1,
274  1,
275  (A.zx()*standardBasis1.y() - standardBasis1.x()*A.zy())/sd1
276  );
277 
278  #ifdef FULLDEBUG
279  if (mag(eVec) < SMALL)
280  {
282  << "Eigenvector magnitude should be non-zero:"
283  << "mag(eigenvector) = " << mag(eVec)
284  << abort(FatalError);
285  }
286  #endif
287 
288  return eVec/mag(eVec);
289  }
290  else if (magSd2 > SMALL)
291  {
292  const vector eVec
293  (
294  (A.xy()*standardBasis1.z() - standardBasis1.y()*A.xz())/sd2,
295  (standardBasis1.x()*A.xz() - A.xx()*standardBasis1.z())/sd2,
296  1
297  );
298 
299  #ifdef FULLDEBUG
300  if (mag(eVec) < SMALL)
301  {
303  << "Eigenvector magnitude should be non-zero:"
304  << "mag(eigenvector) = " << mag(eVec)
305  << abort(FatalError);
306  }
307  #endif
308 
309  return eVec/mag(eVec);
310  }
312  // Triple eigenvalue
313  return standardBasis1^standardBasis2;
314 }
315 
316 
318 (
319  const symmTensor& T,
320  const vector& eVals
321 )
322 {
323  vector Ux(1, 0, 0), Uy(0, 1, 0), Uz(0, 0, 1);
324 
325  Ux = eigenVector(T, eVals.x(), Uy, Uz);
326  Uy = eigenVector(T, eVals.y(), Uz, Ux);
327  Uz = eigenVector(T, eVals.z(), Ux, Uy);
328 
329  return tensor(Ux, Uy, Uz);
330 }
331 
332 
334 {
335  const vector eVals(eigenValues(T));
336 
337  return eigenVectors(T, eVals);
338 }
339 
340 
342 {
343  const scalar detval = st.det();
344 
345  if (detval < ROOTVSMALL)
346  {
347  // Fall back to pseudo inverse
348  scalarRectangularMatrix mat(3, 3);
349 
350  mat(0,0) = st.xx(); mat(0,1) = st.xy(); mat(0,2) = st.xz();
351  mat(1,0) = st.yx(); mat(1,1) = st.yy(); mat(1,2) = st.yz();
352  mat(2,0) = st.zx(); mat(2,1) = st.zy(); mat(2,2) = st.zz();
353 
354  mat = SVDinv(mat);
355 
356  return symmTensor
357  (
358  mat(0,0), mat(0,1), mat(0,2),
359  mat(1,1), mat(1,2),
360  mat(2,2)
361  );
362  }
363 
364  return Foam::inv(st, detval);
365 }
366 
367 
368 // ************************************************************************* //
vector eigenVector(const symmTensor &T, const scalar eVal, const vector &standardBasis1, const vector &standardBasis2)
Return a real eigenvector corresponding to a given real eigenvalue of a given symmTensor.
Definition: symmTensor.C:146
static const char *const typeName
Definition: VectorSpace.H:121
scalarRectangularMatrix SVDinv(const scalarRectangularMatrix &A, scalar minCondition=0)
Return the inverse of matrix A using SVD.
static const Form max
Definition: VectorSpace.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
dimensionedSymmTensor sqr(const dimensionedVector &dv)
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
static const char *const componentNames[]
Definition: VectorSpace.H:122
static const Form rootMin
Definition: VectorSpace.H:128
Tensor< scalar > tensor
Definition: symmTensor.H:57
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static const Form min
Definition: VectorSpace.H:126
Cmpt det() const
The determinate.
Definition: SymmTensorI.H:235
dimensionedTensor eigenVectors(const dimensionedSymmTensor &dt)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
static const SymmTensor I
Definition: SymmTensor.H:74
static const Identity< scalar > I
Definition: Identity.H:100
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
SymmTensor< scalar > symmTensor
SymmTensor of scalars, i.e. SymmTensor<scalar>.
Definition: symmTensor.H:55
Mathematical constants.
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
symmTensor pinv(const symmTensor &st)
Return inverse of a given symmTensor, and fall back to pseudo-inverse if the symmTensor is singular...
Definition: symmTensor.C:334
Vector< scalar > vector
Definition: vector.H:57
errorManip< error > abort(error &err)
Definition: errorManip.H:139
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
static const Form rootMax
Definition: VectorSpace.H:127
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionedScalar c
Speed of light in a vacuum.
static const Form one
Definition: VectorSpace.H:124
dimensionedVector eigenValues(const dimensionedSymmTensor &dt)
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:157
static const Foam::dimensionedScalar A("", Foam::dimPressure, 611.21)
Tensor of scalars, i.e. Tensor<scalar>.
static const Form zero
Definition: VectorSpace.H:123
RectangularMatrix< scalar > scalarRectangularMatrix
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127