SPH
SymmetricTensor.cpp
Go to the documentation of this file.
2 
4 
5 
7  Svd svd = singularValueDecomposition(*this);
8  for (Size i = 0; i < 3; ++i) {
9  if (svd.S[i] < eps) {
10  svd.S[i] = 0._f;
11  } else {
12  svd.S[i] = 1._f / svd.S[i];
13  }
14  }
16  AffineMatrix result = svd.V * S * svd.U.transpose();
17  // result is ALMOST symmetric, but symmetric tensor assumes ABSOLUTELY symmetric input
18  /*SPH_ASSERT(almostEqual(result(0, 1), result(1, 0), 1.e-4f), result);
19  SPH_ASSERT(almostEqual(result(0, 2), result(2, 0), 1.e-4f), result);
20  SPH_ASSERT(almostEqual(result(1, 2), result(2, 1), 1.e-4f), result);*/
21  return SymmetricTensor(
22  Vector(result(0, 0), result(1, 1), result(2, 2)), Vector(result(0, 1), result(0, 2), result(1, 2)));
23 }
24 
25 constexpr int N = 3;
26 
27 INLINE static double hypot2(double x, double y) {
28  SPH_ASSERT(isReal(x) && isReal(y), x, y);
29  return sqrt(x * x + y * y);
30 }
31 
32 // Symmetric Householder reduction to tridiagonal form.
33 
34 static void tred2(double V[N][N], double d[N], double e[N]) {
35  // This is derived from the Algol procedures tred2 by
36  // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
37  // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
38  // Fortran subroutine in EISPACK.
39  for (int j = 0; j < N; j++) {
40  d[j] = V[N - 1][j];
41  }
42  // Householder reduction to tridiagonal form.
43  for (int i = N - 1; i > 0; i--) {
44  // Scale to avoid under/overflow.
45  double scale = 0.0;
46  double h = 0.0;
47  for (int k = 0; k < i; k++) {
48  scale = scale + fabs(d[k]);
49  }
50  if (scale == 0.0) {
51  e[i] = d[i - 1];
52  for (int j = 0; j < i; j++) {
53  d[j] = V[i - 1][j];
54  V[i][j] = 0.0;
55  V[j][i] = 0.0;
56  }
57  } else {
58  // Generate Householder vector.
59  for (int k = 0; k < i; k++) {
60  d[k] /= scale;
61  h += d[k] * d[k];
62  }
63  double f = d[i - 1];
64  SPH_ASSERT(h >= 0._f, h);
65  double g = sqrt(h);
66  if (f > 0) {
67  g = -g;
68  }
69  e[i] = scale * g;
70  h = h - f * g;
71  d[i - 1] = f - g;
72  for (int j = 0; j < i; j++) {
73  e[j] = 0.0;
74  }
75 
76  // Apply similarity transformation to remaining columns.
77  for (int j = 0; j < i; j++) {
78  f = d[j];
79  V[j][i] = f;
80  g = e[j] + V[j][j] * f;
81  for (int k = j + 1; k <= i - 1; k++) {
82  g += V[k][j] * d[k];
83  e[k] += V[k][j] * f;
84  }
85  e[j] = g;
86  }
87  f = 0.0;
88  SPH_ASSERT(h != 0._f);
89  for (int j = 0; j < i; j++) {
90  e[j] /= h;
91  f += e[j] * d[j];
92  }
93  double hh = f / (h + h);
94  for (int j = 0; j < i; j++) {
95  e[j] -= hh * d[j];
96  }
97  for (int j = 0; j < i; j++) {
98  f = d[j];
99  g = e[j];
100  for (int k = j; k <= i - 1; k++) {
101  V[k][j] -= (f * e[k] + g * d[k]);
102  }
103  d[j] = V[i - 1][j];
104  V[i][j] = 0.0;
105  }
106  }
107  d[i] = h;
108  }
109 
110  // Accumulate transformations.
111  for (int i = 0; i < N - 1; i++) {
112  V[N - 1][i] = V[i][i];
113  V[i][i] = 1.0;
114  double h = d[i + 1];
115  if (h != 0.0) {
116  for (int k = 0; k <= i; k++) {
117  d[k] = V[k][i + 1] / h;
118  }
119  for (int j = 0; j <= i; j++) {
120  double g = 0.0;
121  for (int k = 0; k <= i; k++) {
122  g += V[k][i + 1] * V[k][j];
123  }
124  for (int k = 0; k <= i; k++) {
125  V[k][j] -= g * d[k];
126  }
127  }
128  }
129  for (int k = 0; k <= i; k++) {
130  V[k][i + 1] = 0.0;
131  }
132  }
133  for (int j = 0; j < N; j++) {
134  d[j] = V[N - 1][j];
135  V[N - 1][j] = 0.0;
136  }
137  V[N - 1][N - 1] = 1.0;
138  e[0] = 0.0;
139 }
140 
141 // Symmetric tridiagonal QL algorithm.
142 static void tql2(double V[N][N], double d[N], double e[N]) {
143  // This is derived from the Algol procedures tql2, by
144  // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
145  // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
146  // Fortran subroutine in EISPACK.
147  for (int i = 1; i < N; i++) {
148  e[i - 1] = e[i];
149  }
150  e[N - 1] = 0.0;
151 
152  double f = 0.0;
153  double tst1 = 0.0;
154  double eps = pow(2.0, -52.0);
155  for (int l = 0; l < N; l++) {
156  // Find small subdiagonal element
157  tst1 = max(tst1, fabs(d[l]) + fabs(e[l]));
158  int m = l;
159  while (m < N) {
160  if (fabs(e[m]) <= eps * tst1) {
161  break;
162  }
163  m++;
164  }
165 
166  // If m == l, d[l] is an eigenvalue,
167  // otherwise, iterate.
168  if (m > l) {
169  int iter = 0;
170  do {
171  ++iter;
172  // Compute implicit shift
173  double g = d[l];
174  double p = (d[l + 1] - g) / (2.0 * e[l]);
175  SPH_ASSERT(isReal(p), p, e[l]);
176  double r = hypot2(p, 1.0);
177  if (p < 0) {
178  r = -r;
179  }
180  d[l] = e[l] / (p + r);
181  d[l + 1] = e[l] * (p + r);
182  double dl1 = d[l + 1];
183  double h = g - d[l];
184  for (int i = l + 2; i < N; i++) {
185  d[i] -= h;
186  }
187  f = f + h;
188 
189  // Implicit QL transformation.
190  p = d[m];
191  SPH_ASSERT(isReal(p), p);
192  double c = 1.0;
193  double c2 = c;
194  double c3 = c;
195  double el1 = e[l + 1];
196  double s = 0.0;
197  double s2 = 0.0;
198  for (int i = m - 1; i >= l; i--) {
199  c3 = c2;
200  c2 = c;
201  s2 = s;
202  g = c * e[i];
203  h = c * p;
204  r = hypot2(p, e[i]);
205  e[i + 1] = s * r;
206  s = e[i] / r;
207  c = p / r;
208  p = c * d[i] - s * g;
209  SPH_ASSERT(isReal(p), p);
210  d[i + 1] = h + s * (c * g + s * d[i]);
211 
212  // Accumulate transformation.
213  for (int k = 0; k < N; k++) {
214  h = V[k][i + 1];
215  V[k][i + 1] = s * V[k][i] + c * h;
216  V[k][i] = c * V[k][i] - s * h;
217  }
218  }
219  p = -s * s2 * c3 * el1 * e[l] / dl1;
220  e[l] = s * p;
221  d[l] = c * p;
222  } while (fabs(e[l]) > eps * tst1);
223  }
224  d[l] = d[l] + f;
225  e[l] = 0.0;
226  }
227  // Sort eigenvalues and corresponding vectors.
228  for (int i = 0; i < N - 1; i++) {
229  int k = i;
230  double p = d[i];
231  for (int j = i + 1; j < N; j++) {
232  if (d[j] < p) {
233  k = j;
234  p = d[j];
235  }
236  }
237  if (k != i) {
238  d[k] = d[i];
239  d[i] = p;
240  for (int j = 0; j < N; j++) {
241  p = V[j][i];
242  V[j][i] = V[j][k];
243  V[j][k] = p;
244  }
245  }
246  }
247 }
248 
250  SPH_ASSERT(isReal(t), t);
251  const Float scale = maxElement(abs(t));
252  if (scale < 1.e-20_f) {
253  // algorithm is unstable for very small values, just return the diagonal elements + identity matrix
254  return Eigen{ AffineMatrix::identity(), t.diagonal() };
255  }
256  SPH_ASSERT(isReal(scale));
257 
258  double e[N];
259  double d[N];
260  double V[N][N];
261  for (int i = 0; i < N; i++) {
262  for (int j = 0; j < N; j++) {
263  V[i][j] = t(i, j);
264  }
265  }
266  tred2(V, d, e);
267  tql2(V, d, e);
268  return { AffineMatrix(Vector(V[0][0], V[1][0], V[2][0]),
269  Vector(V[0][1], V[1][1], V[2][1]),
270  Vector(V[0][2], V[1][2], V[2][2])),
271  Vector(d[0], d[1], d[2]) };
272 }
273 
274 INLINE static double SIGN(double a, double b) {
275  return b >= 0.0 ? fabs(a) : -fabs(a);
276 }
277 
278 INLINE static double PYTHAG(double a, double b) {
279  double at = fabs(a), bt = fabs(b), ct, result;
280 
281  if (at > bt) {
282  ct = bt / at;
283  SPH_ASSERT(isReal(ct));
284  result = at * sqrt(1.0 + ct * ct);
285  } else if (bt > 0.0) {
286  ct = at / bt;
287  SPH_ASSERT(isReal(ct));
288  result = bt * sqrt(1.0 + ct * ct);
289  } else {
290  result = 0.0;
291  }
292  return result;
293 }
294 
295 
296 int dsvd(float (&a)[3][3], float* w, float (&v)[3][3]) {
297  const int m = 3, n = 3;
298  int flag, i, its, j, jj, k, l, nm;
299  double c, f, h, s, x, y, z;
300  double anorm = 0.0, g = 0.0, scale = 0.0;
301  double* rv1;
302 
303  SPH_ASSERT(m >= n, "#rows must be > #cols");
304 
305  rv1 = (double*)malloc((unsigned int)n * sizeof(double));
306 
307  /* Householder reduction to bidiagonal form */
308  for (i = 0; i < n; i++) {
309  /* left-hand reduction */
310  l = i + 1;
311  rv1[i] = scale * g;
312  g = s = scale = 0.0;
313  if (i < m) {
314  for (k = i; k < m; k++)
315  scale += fabs((double)a[k][i]);
316  if (scale) {
317  for (k = i; k < m; k++) {
318  a[k][i] = (float)((double)a[k][i] / scale);
319  s += ((double)a[k][i] * (double)a[k][i]);
320  }
321  SPH_ASSERT(s >= 0._f, s);
322  f = (double)a[i][i];
323  g = -SIGN(sqrt(s), f);
324  h = f * g - s;
325  a[i][i] = (float)(f - g);
326  if (i != n - 1) {
327  for (j = l; j < n; j++) {
328  for (s = 0.0, k = i; k < m; k++)
329  s += ((double)a[k][i] * (double)a[k][j]);
330  f = s / h;
331  for (k = i; k < m; k++)
332  a[k][j] += (float)(f * (double)a[k][i]);
333  }
334  }
335  for (k = i; k < m; k++)
336  a[k][i] = (float)((double)a[k][i] * scale);
337  }
338  }
339  w[i] = (float)(scale * g);
340 
341  /* right-hand reduction */
342  g = s = scale = 0.0;
343  if (i < m && i != n - 1) {
344  for (k = l; k < n; k++)
345  scale += fabs((double)a[i][k]);
346  if (scale) {
347  for (k = l; k < n; k++) {
348  a[i][k] = (float)((double)a[i][k] / scale);
349  s += ((double)a[i][k] * (double)a[i][k]);
350  }
351  SPH_ASSERT(s >= 0._f, s);
352  f = (double)a[i][l];
353  g = -SIGN(sqrt(s), f);
354  h = f * g - s;
355  a[i][l] = (float)(f - g);
356  for (k = l; k < n; k++)
357  rv1[k] = (double)a[i][k] / h;
358  if (i != m - 1) {
359  for (j = l; j < m; j++) {
360  for (s = 0.0, k = l; k < n; k++)
361  s += ((double)a[j][k] * (double)a[i][k]);
362  for (k = l; k < n; k++)
363  a[j][k] += (float)(s * rv1[k]);
364  }
365  }
366  for (k = l; k < n; k++)
367  a[i][k] = (float)((double)a[i][k] * scale);
368  }
369  }
370  anorm = max(anorm, (fabs((double)w[i]) + fabs(rv1[i])));
371  }
372 
373  /* accumulate the right-hand transformation */
374  for (i = n - 1; i >= 0; i--) {
375  if (i < n - 1) {
376  if (g) {
377  for (j = l; j < n; j++)
378  v[j][i] = (float)(((double)a[i][j] / (double)a[i][l]) / g);
379  /* double division to avoid underflow */
380  for (j = l; j < n; j++) {
381  for (s = 0.0, k = l; k < n; k++)
382  s += ((double)a[i][k] * (double)v[k][j]);
383  for (k = l; k < n; k++)
384  v[k][j] += (float)(s * (double)v[k][i]);
385  }
386  }
387  for (j = l; j < n; j++)
388  v[i][j] = v[j][i] = 0.0;
389  }
390  v[i][i] = 1.0;
391  g = rv1[i];
392  l = i;
393  }
394 
395  /* accumulate the left-hand transformation */
396  for (i = n - 1; i >= 0; i--) {
397  l = i + 1;
398  g = (double)w[i];
399  if (i < n - 1)
400  for (j = l; j < n; j++)
401  a[i][j] = 0.0;
402  if (g) {
403  g = 1.0 / g;
404  if (i != n - 1) {
405  for (j = l; j < n; j++) {
406  for (s = 0.0, k = l; k < m; k++)
407  s += ((double)a[k][i] * (double)a[k][j]);
408  f = (s / (double)a[i][i]) * g;
409  for (k = i; k < m; k++)
410  a[k][j] += (float)(f * (double)a[k][i]);
411  }
412  }
413  for (j = i; j < m; j++)
414  a[j][i] = (float)((double)a[j][i] * g);
415  } else {
416  for (j = i; j < m; j++)
417  a[j][i] = 0.0;
418  }
419  ++a[i][i];
420  }
421 
422  /* diagonalize the bidiagonal form */
423  for (k = n - 1; k >= 0; k--) { /* loop over singular values */
424  for (its = 0; its < 30; its++) { /* loop over allowed iterations */
425  flag = 1;
426  for (l = k; l >= 0; l--) { /* test for splitting */
427  nm = l - 1;
428  if (fabs(rv1[l]) + anorm == anorm) {
429  flag = 0;
430  break;
431  }
432  if (fabs((double)w[nm]) + anorm == anorm)
433  break;
434  }
435  if (flag) {
436  c = 0.0;
437  s = 1.0;
438  for (i = l; i <= k; i++) {
439  f = s * rv1[i];
440  if (fabs(f) + anorm != anorm) {
441  g = (double)w[i];
442  h = PYTHAG(f, g);
443  w[i] = (float)h;
444  h = 1.0 / h;
445  c = g * h;
446  s = (-f * h);
447  for (j = 0; j < m; j++) {
448  y = (double)a[j][nm];
449  z = (double)a[j][i];
450  a[j][nm] = (float)(y * c + z * s);
451  a[j][i] = (float)(z * c - y * s);
452  }
453  }
454  }
455  }
456  z = (double)w[k];
457  if (l == k) { /* convergence */
458  if (z < 0.0) { /* make singular value nonnegative */
459  w[k] = (float)(-z);
460  for (j = 0; j < n; j++)
461  v[j][k] = (-v[j][k]);
462  }
463  break;
464  }
465  SPH_ASSERT(its < 30, "No convergence after 30,000! iterations ");
466 
467 
468  /* shift from bottom 2 x 2 minor */
469  x = (double)w[l];
470  nm = k - 1;
471  y = (double)w[nm];
472  g = rv1[nm];
473  h = rv1[k];
474  f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
475  g = PYTHAG(f, 1.0);
476  f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
477 
478  /* next QR transformation */
479  c = s = 1.0;
480  for (j = l; j <= nm; j++) {
481  i = j + 1;
482  g = rv1[i];
483  y = (double)w[i];
484  h = s * g;
485  g = c * g;
486  z = PYTHAG(f, h);
487  rv1[j] = z;
488  c = f / z;
489  s = h / z;
490  f = x * c + g * s;
491  g = g * c - x * s;
492  h = y * s;
493  y = y * c;
494  for (jj = 0; jj < n; jj++) {
495  x = (double)v[jj][j];
496  z = (double)v[jj][i];
497  v[jj][j] = (float)(x * c + z * s);
498  v[jj][i] = (float)(z * c - x * s);
499  }
500  z = PYTHAG(f, h);
501  w[j] = (float)z;
502  if (z) {
503  z = 1.0 / z;
504  c = f * z;
505  s = h * z;
506  }
507  f = (c * g) + (s * y);
508  x = (c * y) - (s * g);
509  for (jj = 0; jj < m; jj++) {
510  y = (double)a[jj][j];
511  z = (double)a[jj][i];
512  a[jj][j] = (float)(y * c + z * s);
513  a[jj][i] = (float)(z * c - y * s);
514  }
515  }
516  rv1[l] = 0.0;
517  rv1[k] = f;
518  w[k] = (float)x;
519  }
520  }
521  free((void*)rv1);
522  return (1);
523 }
524 
525 
527  Vector diag = t.diagonal();
528  Vector off = t.offDiagonal();
529  float V[3][3];
530  float S[3];
531 
532  // clang-format off
533  float U[3][3] = {
534  { float(diag[0]), float(off[0]), float(off[1]) },
535  { float(off[0]), float(diag[1]), float(off[2]) },
536  { float(off[1]), float(off[2]), float(diag[2]) },
537  };
538 
539  dsvd(U, S, V);
540 
541  Svd result;
542  result.S = Vector(S[0], S[1], S[2]);
543  result.U = AffineMatrix(Vector(U[0][0], U[0][1], U[0][2]),
544  Vector(U[1][0], U[1][1], U[1][2]),
545  Vector(U[2][0], U[2][1], U[2][2]));
546  result.V = AffineMatrix(Vector(V[0][0], V[0][1], V[0][2]),
547  Vector(V[1][0], V[1][1], V[1][2]),
548  Vector(V[2][0], V[2][1], V[2][2]));
549  // clang-format on
550  return result;
551 }
552 
INLINE bool isReal(const AntisymmetricTensor &t)
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
INLINE Float maxElement(const T &value)
Returns maximum element, simply the value iself by default.
Definition: Generic.h:29
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
INLINE T sqrt(const T f)
Return a squared root of a value.
Definition: MathUtils.h:78
constexpr INLINE Float pow(const Float v)
Power for floats.
INLINE auto abs(const T &f)
Definition: MathUtils.h:276
#define INLINE
Macros for conditional compilation based on selected compiler.
Definition: Object.h:31
#define NAMESPACE_SPH_END
Definition: Object.h:12
Eigen eigenDecomposition(const SymmetricTensor &t)
Computes eigenvectors and corresponding eigenvalues of symmetric matrix.
int dsvd(float(&a)[3][3], float *w, float(&v)[3][3])
constexpr int N
Svd singularValueDecomposition(const SymmetricTensor &t)
Computes the singular value decomposition of symmetric matrix.
Basic algebra for symmetric 2nd order tensors.
BasicVector< Float > Vector
Definition: Vector.h:539
static AffineMatrix identity()
Definition: AffineMatrix.h:132
static AffineMatrix scale(const Vector &scaling)
Definition: AffineMatrix.h:136
INLINE AffineMatrix transpose() const
Returns the transposed matrix.
Definition: AffineMatrix.h:69
Symmetric tensor of 2nd order.
INLINE const Vector & offDiagonal() const
Returns the off-diagonal elements of the tensor.
SymmetricTensor pseudoInverse(const Float eps) const
SymmetricTensor()=default
INLINE const Vector & diagonal() const
Returns the diagonal part of the tensor.
AffineMatrix U
Vector S
AffineMatrix V