Mmg
Simplicial remeshers (mesh adaptation, isovalue discretization, lagrangian movement)
eigenv.c
Go to the documentation of this file.
1/* =============================================================================
2** This file is part of the mmg software package for the tetrahedral
3** mesh modification.
4** Copyright (c) Bx INP/CNRS/Inria/UBordeaux/UPMC, 2004-
5**
6** mmg is free software: you can redistribute it and/or modify it
7** under the terms of the GNU Lesser General Public License as published
8** by the Free Software Foundation, either version 3 of the License, or
9** (at your option) any later version.
10**
11** mmg is distributed in the hope that it will be useful, but WITHOUT
12** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13** FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14** License for more details.
15**
16** You should have received a copy of the GNU Lesser General Public
17** License and of the GNU General Public License along with mmg (in
18** files COPYING.LESSER and COPYING). If not, see
19** <http://www.gnu.org/licenses/>. Please read their terms carefully and
20** use this copy of the mmg distribution only if you accept them.
21** =============================================================================
22*/
23
36#include <stdio.h>
37#include <string.h>
38#include <math.h>
39#include <assert.h>
40#include <stdlib.h>
41#include <stdint.h>
42#include "eigenv_private.h"
43#include "mmgcommon_private.h"
44
45/* seeking at least 1.e-05 accuracy, more if not sufficient */
46#define MG_EIGENV_EPS27 1.e-27
47#define MG_EIGENV_EPS13 1.e-13
48#define MG_EIGENV_EPS10 1.e-10
49#define MG_EIGENV_EPS5e6 5.e-06
50#define MG_EIGENV_EPS6 1.e-06
51#define MG_EIGENV_EPS2e6 2.e-06
52#define MG_EIGENV_EPS5 1.e-05
53#define MAXTOU 50
54
59#define egal(x,y) ( \
60 ( ((x) == 0.0f) ? (fabs(y) < MG_EIGENV_EPS6) : \
61 ( ((y) == 0.0f) ? (fabs(x) < MG_EIGENV_EPS6) : \
62 (fabs((x)-(y)) / (fabs(x) + fabs(y)) < MG_EIGENV_EPS2e6) ) ) )
63
67static double Id[3][3] = {
68 {1.0, 0.0, 0.0},
69 {0.0, 1.0, 0.0},
70 {0.0, 0.0, 1.0} };
71
72
86static int newton3(double p[4],double x[3]) {
87 double b,c,d,da,db,dc,epsd;
88 double delta,fx,dfx,dxx;
89 double fdx0,fdx1,dx0,dx1,x1,x2,tmp,epsA,epsB;
90 int it,it2,n;
91 static int8_t mmgWarn=0;
92
93 /* coeffs polynomial, a=1 */
94 if ( p[3] != 1. ) {
95 if ( !mmgWarn ) {
96 fprintf(stderr,"\n ## Warning: %s: bad use of newton3 function, polynomial"
97 " must be of type P(x) = x^3+bx^2+cx+d.\n",
98 __func__);
99 mmgWarn = 1;
100 }
101 return 0;
102 }
103
104 b = p[2];
105 c = p[1];
106 d = p[0];
107
108 /* 1st derivative of f */
109 da = 3.0;
110 db = 2.0*b;
111
112 /* solve 2nd order eqn */
113 delta = db*db - 4.0*da*c;
114 epsd = db*db*MG_EIGENV_EPS10;
115
116 /* inflexion (f'(x)=0, x=-b/2a) */
117 x1 = -db / 6.0f;
118
119 n = 1;
120 if ( delta > epsd ) {
121 delta = sqrt(delta);
122 dx0 = (-db + delta) / 6.0;
123 dx1 = (-db - delta) / 6.0;
124 /* Horner */
125 fdx0 = d + dx0*(c+dx0*(b+dx0));
126 fdx1 = d + dx1*(c+dx1*(b+dx1));
127
128
129 x[2] = -b - 2.0*dx0;
130 tmp = -b - 2.0*dx1;
131 if ( fabs(fdx0) < MG_EIGENV_EPS27 ||
132 ( fabs(fdx0) < MG_EIGENV_EPS13 && (dx0 > 0. && x[2] > 0.) ) ) {
133 /* dx0: double root, compute single root */
134 n = 2;
135 x[0] = dx0;
136 x[1] = dx0;
137 /* check if P(x) = 0 */
138 fx = d + x[2]*(c+x[2]*(b+x[2]));
139 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
140#ifdef DEBUG
141 fprintf(stderr,"\n ## Error: %s: ERR 9100, newton3: fx= %E.\n",
142 __func__,fx);
143#endif
144 return 0;
145 }
146 return n;
147 }
148 else if ( fabs(fdx1) < MG_EIGENV_EPS27 ||
149 ( fabs(fdx1) < MG_EIGENV_EPS13 && (dx1 > 0. && tmp > 0.) ) ) {
150 /* dx1: double root, compute single root */
151 n = 2;
152 x[0] = dx1;
153 x[1] = dx1;
154 x[2] = tmp;
155 /* check if P(x) = 0 */
156 fx = d + x[2]*(c+x[2]*(b+x[2]));
157 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
158#ifdef DEBUG
159 fprintf(stderr,"\n ## Error: %s: ERR 9100, newton3: fx= %E.\n",
160 __func__,fx);
161#endif
162 return 0;
163 }
164 return n;
165 }
166 }
167
168 else if ( fabs(delta) < db*db * 1.e-20 ||
169 ( fabs(delta) < epsd && x1 > 0. ) ) {
170 /* triple root */
171 n = 3;
172 x[0] = x1;
173 x[1] = x1;
174 x[2] = x1;
175 /* check if P(x) = 0 */
176 fx = d + x[0]*(c+x[0]*(b+x[0]));
177 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
178#ifdef DEBUG
179 fprintf(stderr,"\n ## Error: %s: ERR 9100, newton3: fx= %E.\n",
180 __func__,fx);
181#endif
182 return 0;
183 }
184 return n;
185 }
186
187 else {
188#ifdef DEBUG
189 fprintf(stderr,"\n ## Error: %s: ERR 9101, newton3: no real roots.\n",
190 __func__);
191#endif
192 return 0;
193 }
194
195 /* Newton method: find one root (middle)
196 starting point: P"(x)=0 */
197 x1 = -b / 3.0;
198 dfx = c + b*x1;
199 fx = d + x1*(c -2.0*x1*x1);
200
201 epsA = MG_EIGENV_EPS13;
202 epsB = MG_EIGENV_EPS10;
203
204 it2 = 0;
205newton :
206
207 it = 0;
208 do {
209 x2 = x1 - fx / dfx;
210 fx = d + x2*(c+x2*(b+x2));
211 if ( fabs(fx) < epsA && x2 > 0. ) {
212 x[0] = x2;
213 break;
214 }
215 dfx = c + x2*(db + da*x2);
216
217 /* check for break-off condition */
218 dxx = fabs((x2-x1) / x2);
219 if ( dxx < epsB && x2 > 0. ) {
220 x[0] = x2;
221
222 /* Check accuracy for 1e-5 precision only (we don't want to fail for smaller
223 * precision) */
224 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
225 fprintf(stderr,"\n ## Error: %s: ERR 9102, newton3, no root found"
226 " (fx %E).\n",
227 __func__,fx);
228 return 0;
229 }
230 break;
231 }
232 else
233 x1 = x2;
234 }
235 while ( ++it < MAXTOU );
236
237 if ( it == MAXTOU ) {
238 x[0] = x1;
239 fx = d + x1*(c+(x1*(b+x1)));
240 /* Check accuracy for 1e-5 precision only (we don't want to fail for smaller
241 * precision) */
242 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
243 fprintf(stderr,"\n ## Error: %s: ERR 9102, newton3, no root found"
244 " (fx %E).\n",
245 __func__,fx);
246 return 0;
247 }
248 }
249
250 /* solve 2nd order equation
251 P(x) = (x-sol(1))* (x^2+bb*x+cc) */
252 db = b + x[0];
253 dc = c + x[0]*db;
254 delta = db*db - 4.0*dc;
255
256 if ( delta <= 0.0 ) {
257 fprintf(stderr,"\n ## Error: %s: ERR 9103, newton3, det = 0.\n",__func__);
258 return 0;
259 }
260
261 delta = sqrt(delta);
262 x[1] = 0.5 * (-db+delta);
263 x[2] = 0.5 * (-db-delta);
264
265 while ( ++it2 < 5 && ( ( x[0] <= 0 && fabs(x[0]) <= MG_EIGENV_EPS5 ) ||
266 ( x[1] <= 0 && fabs(x[1]) <= MG_EIGENV_EPS5 ) ||
267 ( x[2] <= 0 && fabs(x[2]) <= MG_EIGENV_EPS5 ) ) ) {
268 /* Get back to the newton method with increased accuracy */
269 epsA /= 10;
270 epsB /= 10;
271 goto newton;
272 }
273
274#ifdef DEBUG
275 /* check for root accuracy */
276 fx = d + x[1]*(c+x[1]*(b+x[1]));
277 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
278 fprintf(stderr,"\n ## Error: %s: ERR 9104, newton3: fx= %E x= %E.\n",
279 __func__,fx,x[1]);
280 return 0;
281 }
282 fx = d + x[2]*(c+x[2]*(b+x[2]));
283 if ( fabs(fx) > MG_EIGENV_EPS10 ) {
284 fprintf(stderr,"\n ## Error: %s: ERR 9104, newton3: fx= %E x= %E.\n",
285 __func__,fx,x[2]);
286 return 0;
287 }
288#endif
289
290 return n;
291}
292
310static
311int MMG5_check_accuracy(double mat[6],double lambda[3], double v[3][3],
312 double w1[3], double w2[3], double w3[3],
313 double maxm, int order, int symmat) {
314 double err,tmpx,tmpy,tmpz;
315 double m[6];
316 int i,j,k;
317
318 if ( !symmat ) return 1;
319
320 k = 0;
321 for (i=0; i<3; i++) {
322 for (j=i; j<3; j++) {
323 m[k++] = lambda[0]*v[0][i]*v[0][j]
324 + lambda[1]*v[1][i]*v[1][j]
325 + lambda[2]*v[2][i]*v[2][j];
326 }
327 }
328 err = fabs(mat[0]-m[0]);
329 for (i=1; i<6; i++)
330 if ( fabs(m[i]-mat[i]) > err ) err = fabs(m[i]-mat[i]);
331
332 if ( err > 1.e03*maxm ) {
333 fprintf(stderr,"\n ## Error: %s:\nProbleme eigenv3: err= %f\n",__func__,err*maxm);
334 fprintf(stderr,"\n ## Error: %s:mat depart :\n",__func__);
335 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,mat[0],mat[1],mat[2]);
336 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,mat[1],mat[3],mat[4]);
337 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,mat[2],mat[4],mat[5]);
338 fprintf(stderr,"\n ## Error: %s:mat finale :\n",__func__);
339 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,m[0],m[1],m[2]);
340 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,m[1],m[3],m[4]);
341 fprintf(stderr,"\n ## Error: %s:%13.6f %13.6f %13.6f\n",__func__,m[2],m[4],m[5]);
342 fprintf(stderr,"\n ## Error: %s:lambda : %f %f %f\n",__func__,lambda[0],lambda[1],lambda[2]);
343 fprintf(stderr,"\n ## Error: %s: ordre %d\n",__func__,order);
344 fprintf(stderr,"\n ## Error: %s:\nOrtho:\n",__func__);
345 fprintf(stderr,"\n ## Error: %s:v1.v2 = %.14f\n",__func__,
346 v[0][0]*v[1][0]+v[0][1]*v[1][1]+ v[0][2]*v[1][2]);
347 fprintf(stderr,"\n ## Error: %s:v1.v3 = %.14f\n",__func__,
348 v[0][0]*v[2][0]+v[0][1]*v[2][1]+ v[0][2]*v[2][2]);
349 fprintf(stderr,"\n ## Error: %s:v2.v3 = %.14f\n",__func__,
350 v[1][0]*v[2][0]+v[1][1]*v[2][1]+ v[1][2]*v[2][2]);
351
352 fprintf(stderr,"\n ## Error: %s:Consistency\n",__func__);
353 for (i=0; i<3; i++) {
354 tmpx = v[0][i]*m[0] + v[1][i]*m[1]
355 + v[2][i]*m[2] - lambda[i]*v[0][i];
356 tmpy = v[0][i]*m[1] + v[1][i]*m[3]
357 + v[2][i]*m[4] - lambda[i]*v[1][i];
358 tmpz = v[0][i]*m[2] + v[1][i]*m[4]
359 + v[2][i]*m[5] - lambda[i]*v[2][i];
360 fprintf(stderr,"\n ## Error: %s: Av %d - lambda %d *v %d = %f %f %f\n",
361 __func__,i,i,i,tmpx,tmpy,tmpz);
362
363 fprintf(stderr,"\n ## Error: %s:w1 %f %f %f\n",__func__,w1[0],w1[1],w1[2]);
364 fprintf(stderr,"\n ## Error: %s:w2 %f %f %f\n",__func__,w2[0],w2[1],w2[2]);
365 fprintf(stderr,"\n ## Error: %s:w3 %f %f %f\n",__func__,w3[0],w3[1],w3[2]);
366 }
367 return 0;
368 }
369
370 return 1;
371}
372
385int MMG5_eigenv3d(int symmat,double *mat,double lambda[3],double v[3][3]) {
386 double a11,a12,a13,a21,a22,a23,a31,a32,a33;
387 double aa,bb,cc,dd,ee,ii,vx1[3],vx2[3],vx3[3],dd1,dd2,dd3;
388 double maxd,maxm,valm,p[4],w1[3],w2[3],w3[3],epsd;
389 int k,n;
390
391 epsd = MG_EIGENV_EPS13;
392
393 w1[0] = w1[1] = w1[2] = 0;
394 w2[0] = w2[1] = w2[2] = 0;
395 w3[0] = w3[1] = w3[2] = 0;
396 /* default */
397 memcpy(v,Id,9*sizeof(double));
398 if ( symmat ) {
399 lambda[0] = (double)mat[0];
400 lambda[1] = (double)mat[3];
401 lambda[2] = (double)mat[5];
402
403 maxm = fabs(mat[0]);
404 for (k=1; k<6; k++) {
405 valm = fabs(mat[k]);
406 if ( valm > maxm ) maxm = valm;
407 }
408 /* single float accuracy if sufficient, else double float accuracy */
409 if ( maxm < MG_EIGENV_EPS5e6 ) {
410 if ( lambda[0]>0. && lambda[1] > 0. && lambda[2] > 0. ) {
411 return 1;
412 }
413 else if ( maxm < MG_EIGENV_EPS13 ) {
414 return 0;
415 }
416 epsd = MG_EIGENV_EPS27;
417 }
418
419 /* normalize matrix */
420 dd = 1.0 / maxm;
421 a11 = mat[0] * dd;
422 a12 = mat[1] * dd;
423 a13 = mat[2] * dd;
424 a22 = mat[3] * dd;
425 a23 = mat[4] * dd;
426 a33 = mat[5] * dd;
427
428 /* diagonal matrix */
429 maxd = fabs(a12);
430 valm = fabs(a13);
431 if ( valm > maxd ) maxd = valm;
432 valm = fabs(a23);
433 if ( valm > maxd ) maxd = valm;
434 if ( maxd < epsd ) return 1;
435
436 a21 = a12;
437 a31 = a13;
438 a32 = a23;
439
440 /* build characteristic polynomial
441 P(X) = X^3 - trace X^2 + (somme des mineurs)X - det = 0 */
442 aa = a11*a22;
443 bb = a23*a32;
444 cc = a12*a21;
445 dd = a13*a31;
446 p[0] = a11*bb + a33*(cc-aa) + a22*dd -2.0*a12*a13*a23;
447 p[1] = a11*(a22 + a33) + a22*a33 - bb - cc - dd;
448 p[2] = -a11 - a22 - a33;
449 p[3] = 1.0;
450 }
451 else {
452 lambda[0] = (double)mat[0];
453 lambda[1] = (double)mat[4];
454 lambda[2] = (double)mat[8];
455
456 maxm = fabs(mat[0]);
457 for (k=1; k<9; k++) {
458 valm = fabs(mat[k]);
459 if ( valm > maxm ) maxm = valm;
460 }
461
462 /* single float accuracy if sufficient, else double float accuracy */
463 if ( maxm < MG_EIGENV_EPS5e6 ) {
464 if ( lambda[0]>0. && lambda[1] > 0. && lambda[2] > 0. ) {
465 return 1;
466 }
467 else if ( maxm < MG_EIGENV_EPS13 ) {
468 return 0;
469 }
470 epsd = MG_EIGENV_EPS27;
471 }
472
473 /* normalize matrix */
474 dd = 1.0 / maxm;
475 a11 = mat[0] * dd;
476 a12 = mat[1] * dd;
477 a13 = mat[2] * dd;
478 a21 = mat[3] * dd;
479 a22 = mat[4] * dd;
480 a23 = mat[5] * dd;
481 a31 = mat[6] * dd;
482 a32 = mat[7] * dd;
483 a33 = mat[8] * dd;
484
485 /* diagonal matrix */
486 maxd = fabs(a12);
487 valm = fabs(a13);
488 if ( valm > maxd ) maxd = valm;
489 valm = fabs(a23);
490 if ( valm > maxd ) maxd = valm;
491 valm = fabs(a21);
492 if ( valm > maxd ) maxd = valm;
493 valm = fabs(a31);
494 if ( valm > maxd ) maxd = valm;
495 valm = fabs(a32);
496 if ( valm > maxd ) maxd = valm;
497 if ( maxd < epsd ) return 1;
498
499 /* build characteristic polynomial
500 P(X) = X^3 - trace X^2 + (somme des mineurs)X - det = 0 */
501 aa = a22*a33 - a23*a32;
502 bb = a23*a31 - a21*a33;
503 cc = a21*a32 - a31*a22;
504 ee = a11*a33 - a13*a31;
505 ii = a11*a22 - a12*a21;
506
507 p[0] = -a11*aa - a12*bb - a13*cc;
508 p[1] = aa + ee + ii;
509 p[2] = -a11 - a22 - a33;
510 p[3] = 1.0;
511 }
512
513 /* solve polynomial (find roots using newton) */
514 n = newton3(p,lambda);
515 if ( n <= 0 ) return 0;
516
517 /* compute eigenvectors:
518 an eigenvalue belong to orthogonal of Im(A-lambda*Id) */
519 v[0][0] = 1.0; v[0][1] = v[0][2] = 0.0;
520 v[1][1] = 1.0; v[1][0] = v[1][2] = 0.0;
521 v[2][2] = 1.0; v[2][0] = v[2][1] = 0.0;
522
523 w1[1] = a12; w1[2] = a13;
524 w2[0] = a21; w2[2] = a23;
525 w3[0] = a31; w3[1] = a32;
526
527 if ( n == 1 ) {
528 /* vk = crsprd(wi,wj) */
529 for (k=0; k<3; k++) {
530 w1[0] = a11 - lambda[k];
531 w2[1] = a22 - lambda[k];
532 w3[2] = a33 - lambda[k];
533
534 /* cross product vectors in (Im(A-lambda(i) Id) ortho */
535 MMG5_crossprod3d(w1,w3,vx1);
536 MMG5_dotprod(3,vx1,vx1,&dd1);
537
538 MMG5_crossprod3d(w1,w2,vx2);
539 MMG5_dotprod(3,vx2,vx2,&dd2);
540
541 MMG5_crossprod3d(w2,w3,vx3);
542 MMG5_dotprod(3,vx3,vx3,&dd3);
543
544 /* find vector of max norm */
545 if ( dd1 > dd2 ) {
546 if ( dd1 > dd3 ) {
547 dd1 = 1.0 / sqrt(dd1);
548 v[k][0] = vx1[0] * dd1;
549 v[k][1] = vx1[1] * dd1;
550 v[k][2] = vx1[2] * dd1;
551 }
552 else {
553 dd3 = 1.0 / sqrt(dd3);
554 v[k][0] = vx3[0] * dd3;
555 v[k][1] = vx3[1] * dd3;
556 v[k][2] = vx3[2] * dd3;
557 }
558 }
559 else {
560 if ( dd2 > dd3 ) {
561 dd2 = 1.0 / sqrt(dd2);
562 v[k][0] = vx2[0] * dd2;
563 v[k][1] = vx2[1] * dd2;
564 v[k][2] = vx2[2] * dd2;
565 }
566 else {
567 dd3 = 1.0 / sqrt(dd3);
568 v[k][0] = vx3[0] * dd3;
569 v[k][1] = vx3[1] * dd3;
570 v[k][2] = vx3[2] * dd3;
571 }
572 }
573 }
574 }
575
576 /* (vp1,vp2) double, vp3 simple root */
577 else if ( n == 2 ) {
578 /* basis vectors of Im(tA-lambda[2]*I) */
579 double z1[3],z2[3];
580
582 w1[0] = a11 - lambda[2];
583 w2[1] = a22 - lambda[2];
584 w3[2] = a33 - lambda[2];
585
586 /* ker(A-lambda[2]*I) has dimension 1 and it is orthogonal to
587 * Im(tA-lambda[2]*I), which has dimension 2.
588 * So the eigenvector vp[2] can be computed as the cross product of the two
589 * linearly independent rows of (A-lambda[2]*I).
590 *
591 * Compute all pairwise cross products of the rows of (A-lambda[2]*I), and
592 * pick the one with maximum norm (the other two will have zero norm, but
593 * this is tricky to detect numerically due to cancellation errors). */
594 MMG5_crossprod3d(w1,w3,vx1);
595 MMG5_dotprod(3,vx1,vx1,&dd1);
596
597 MMG5_crossprod3d(w1,w2,vx2);
598 MMG5_dotprod(3,vx2,vx2,&dd2);
599
600 MMG5_crossprod3d(w2,w3,vx3);
601 MMG5_dotprod(3,vx3,vx3,&dd3);
602
603 /* find vector of max norm to pick the two linearly independent rows */
604 if ( dd1 > dd2 ) {
605 if ( dd1 > dd3 ) {
606 dd1 = 1.0 / sqrt(dd1);
607 v[2][0] = vx1[0] * dd1;
608 v[2][1] = vx1[1] * dd1;
609 v[2][2] = vx1[2] * dd1;
610 memcpy(z1,w1,3*sizeof(double));
611 memcpy(z2,w3,3*sizeof(double));
612 }
613 else {
614 dd3 = 1.0 / sqrt(dd3);
615 v[2][0] = vx3[0] * dd3;
616 v[2][1] = vx3[1] * dd3;
617 v[2][2] = vx3[2] * dd3;
618 memcpy(z1,w2,3*sizeof(double));
619 memcpy(z2,w3,3*sizeof(double));
620 }
621 }
622 else {
623 if ( dd2 > dd3 ) {
624 dd2 = 1.0 / sqrt(dd2);
625 v[2][0] = vx2[0] * dd2;
626 v[2][1] = vx2[1] * dd2;
627 v[2][2] = vx2[2] * dd2;
628 memcpy(z1,w1,3*sizeof(double));
629 memcpy(z2,w2,3*sizeof(double));
630 }
631 else {
632 dd3 = 1.0 / sqrt(dd3);
633 v[2][0] = vx3[0] * dd3;
634 v[2][1] = vx3[1] * dd3;
635 v[2][2] = vx3[2] * dd3;
636 memcpy(z1,w2,3*sizeof(double));
637 memcpy(z2,w3,3*sizeof(double));
638 }
639 }
640 /* The two linearly independent rows provide a basis for Im(tA-lambda[2]*I).
641 * Normalize them to reduce roundoff errors. */
642 MMG5_dotprod(3,z1,z1,&dd1);
643 dd1 = 1.0 / sqrt(dd1);
644 z1[0] *= dd1;
645 z1[1] *= dd1;
646 z1[2] *= dd1;
647 MMG5_dotprod(3,z2,z2,&dd2);
648 dd2 = 1.0 / sqrt(dd2);
649 z2[0] *= dd2;
650 z2[1] *= dd2;
651 z2[2] *= dd2;
652
653
655 w1[0] = a11 - lambda[0];
656 w2[1] = a22 - lambda[0];
657 w3[2] = a33 - lambda[0];
658
659 /* ker(A-lambda[0]*I) has dimension 2 and it is orthogonal to
660 * Im(tA-lambda[0]*I), which has dimension 1.
661 * Eigenvectors vp[0],vp[1] belong to ker(A-lambda[0]*I) and can't belong to
662 * ker(A-lambda[2]*I) since eigenvalue lambda[2] is distinct. Thus, by
663 * orthogonality, the vectors belonging to Im(tA-lambda[2]*I) can't belong
664 * to Im(tA-lambda[0]*I).
665 * Denoting as c20 and c21 the two basis vectors for Im(tA-lambda[2]*I), and
666 * as c0 the only basis vector for Im(tA-lambda[0]*I), two _distinct_
667 * eigenvectors vp[0] and vp[0] in ker(A-lambda[0]*I) can thus be computed
668 * as:
669 * vp[0] = c0 x c20
670 * vp[1] = c0 x c21
671 * (Stated differently, vp[0] and vp[1] would be colinear only if
672 * Im(tA-lambda[0]*I) belonged to Im(tA-lambda[2]), which would imply that
673 * ker(A-lambda[2]*I) belong to ker(A-lambda[0]*I), that is not possible).
674 *
675 * Find the basis of Im(tA-lambda[0]*I) as the row with maximum projection
676 * on vp[2] (this helps in selecting a row that does not belong to
677 * Im(tA-lambda[2]*I) in case lambda[2] is numerically not well separated
678 * from lambda[0]=lambda[1]).
679 */
680 MMG5_dotprod(3,w1,v[2],&dd1);
681 MMG5_dotprod(3,w2,v[2],&dd2);
682 MMG5_dotprod(3,w3,v[2],&dd3);
683 dd1 = fabs(dd1);
684 dd2 = fabs(dd2);
685 dd3 = fabs(dd3);
686
687 /* find vector with max projection to pick the linearly independent row */
688 if( dd1 > dd2 ) {
689 if( dd1 > dd3 ) {
690 MMG5_dotprod(3,w1,w1,&dd1);
691 dd1 = 1.0 / sqrt(dd1);
692 vx1[0] = w1[0]*dd1;
693 vx1[1] = w1[1]*dd1;
694 vx1[2] = w1[2]*dd1;
695 } else {
696 MMG5_dotprod(3,w3,w3,&dd3);
697 dd3 = 1.0 / sqrt(dd3);
698 vx1[0] = w3[0]*dd3;
699 vx1[1] = w3[1]*dd3;
700 vx1[2] = w3[2]*dd3;
701 }
702 } else {
703 if( dd2 > dd3 ) {
704 MMG5_dotprod(3,w2,w2,&dd2);
705 dd2 = 1.0 / sqrt(dd2);
706 vx1[0] = w2[0]*dd2;
707 vx1[1] = w2[1]*dd2;
708 vx1[2] = w2[2]*dd2;
709 } else {
710 MMG5_dotprod(3,w3,w3,&dd3);
711 dd3 = 1.0 / sqrt(dd3);
712 vx1[0] = w3[0]*dd3;
713 vx1[1] = w3[1]*dd3;
714 vx1[2] = w3[2]*dd3;
715 }
716 }
717 /* cross product of the first basis vector of Im(tA-lambda[2]*I) with the
718 * basis vector of Im(tA-lambda[0]) */
719 MMG5_crossprod3d(z1,vx1,v[0]);
720 MMG5_dotprod(3,v[0],v[0],&dd1);
721 assert( dd1 > MG_EIGENV_EPS27 );
722 dd1 = 1.0 / sqrt(dd1);
723 v[0][0] *= dd1;
724 v[0][1] *= dd1;
725 v[0][2] *= dd1;
726
727 /* 3rd vector as the cross product of the second basis vector of
728 * Im(tA-lambda[2]*I) with the basis vector of Im(tA-lambda[0]) */
729 MMG5_crossprod3d(vx1,z2,v[1]);
730 MMG5_dotprod(3,v[1],v[1],&dd2);
731 assert( dd2 > MG_EIGENV_EPS27 );
732 dd2 = 1.0 / sqrt(dd2);
733 v[1][0] *= dd2;
734 v[1][1] *= dd2;
735 v[1][2] *= dd2;
736
737 /* enforce orthogonality in the symmetric case (can't prove that c20 and
738 * c21 are orthogonal in a general symmetric case), the result will still
739 * belong to ker(A-lambda[0]*I) */
740 if( symmat ) {
741 MMG5_dotprod(3,v[1],v[0],&dd1);
742 v[1][0] -= dd1*v[0][0];
743 v[1][1] -= dd1*v[0][1];
744 v[1][2] -= dd1*v[0][2];
745 /* normalize again */
746 MMG5_dotprod(3,v[1],v[1],&dd2);
747 assert( dd2 > MG_EIGENV_EPS27 );
748 dd2 = 1.0 / sqrt(dd2);
749 v[1][0] *= dd2;
750 v[1][1] *= dd2;
751 v[1][2] *= dd2;
752 }
753 }
754
755 lambda[0] *= maxm;
756 lambda[1] *= maxm;
757 lambda[2] *= maxm;
758
759 /* check accuracy */
760 if ( getenv("MMG_EIGENV_DDEBUG") && symmat ) {
761 if ( !MMG5_check_accuracy ( mat, lambda, v, w1, w2, w3, maxm, n, symmat ) )
762 return 0;
763 }
764
765 return n;
766}
767
780int MMG5_eigenv2d(int symmat,double *mat,double lambda[2],double vp[2][2]) {
781 double dd,sqDelta,trmat,vnorm;
782 static int8_t mmgWarn0=0;
783
784 /* wrapper function if symmetric matrix */
785 if( symmat )
786 return MMG5_eigensym(mat,lambda,vp);
787
788
789 dd = mat[0] - mat[3];
790 sqDelta = sqrt(fabs(dd*dd + 4.0*mat[1]*mat[2]));
791 trmat = mat[0] + mat[3];
792
793 lambda[0] = 0.5 * (trmat - sqDelta);
794 if ( lambda[0] < 0.0 ) {
795 if ( !mmgWarn0 ) {
796 mmgWarn0 = 1;
797 fprintf(stderr,"\n ## Warning: %s: at least 1 metric with a "
798 "negative eigenvalue: %f \n",__func__,lambda[0]);
799 }
800 return 0;
801 }
802
803 /* First case : matrices m and n are homothetic: n = lambda0*m */
804 if ( sqDelta < MMG5_EPS ) {
805
806 /* only one eigenvalue with degree 2 */
807 return 2;
808
809 }
810 /* Second case: both eigenvalues of mat are distinct ; theory says qf associated to m and n
811 are diagonalizable in basis (vp[0], vp[1]) - the coreduction basis */
812 else {
813 lambda[1] = 0.5 * (trmat + sqDelta);
814 assert(lambda[1] >= 0.0);
815
816 vp[0][0] = mat[1];
817 vp[0][1] = (lambda[0] - mat[0]);
818 vnorm = sqrt(vp[0][0]*vp[0][0] + vp[0][1]*vp[0][1]);
819
820 if ( vnorm < MMG5_EPS ) {
821 vp[0][0] = (lambda[0] - mat[3]);
822 vp[0][1] = mat[2];
823 vnorm = sqrt(vp[0][0]*vp[0][0] + vp[0][1]*vp[0][1]);
824 }
825
826 vnorm = 1.0 / vnorm;
827 vp[0][0] *= vnorm;
828 vp[0][1] *= vnorm;
829
830 vp[1][0] = mat[1];
831 vp[1][1] = (lambda[1] - mat[0]);
832 vnorm = sqrt(vp[1][0]*vp[1][0] + vp[1][1]*vp[1][1]);
833
834 if ( vnorm < MMG5_EPS ) {
835 vp[1][0] = (lambda[1] - mat[3]);
836 vp[1][1] = mat[2];
837 vnorm = sqrt(vp[1][0]*vp[1][0] + vp[1][1]*vp[1][1]);
838 }
839
840 vnorm = 1.0 / vnorm;
841 vp[1][0] *= vnorm;
842 vp[1][1] *= vnorm;
843
844 /* two distinct eigenvalues with degree 1 */
845 return 1;
846 }
847}
848
858int MMG5_eigen2(double *mm,double *lambda,double vp[2][2]) {
859 double m[3],dd,a1,xn,ddeltb,rr1,rr2,ux,uy;
860
861 /* normalize */
862 memcpy(m,mm,3*sizeof(double));
863 xn = fabs(m[0]);
864 if ( fabs(m[1]) > xn ) xn = fabs(m[1]);
865 if ( fabs(m[2]) > xn ) xn = fabs(m[2]);
866 if ( xn < MG_EIGENV_EPS10 ) {
867 lambda[0] = lambda[1] = 0.0;
868 vp[0][0] = 1.0;
869 vp[0][1] = 0.0;
870 vp[1][0] = 0.0;
871 vp[1][1] = 1.0;
872 return 1;
873 }
874 xn = 1.0 / xn;
875 m[0] *= xn;
876 m[1] *= xn;
877 m[2] *= xn;
878
879 if ( egal(m[1],0.0) ) {
880 rr1 = m[0];
881 rr2 = m[2];
882 goto vect;
883 }
884
885 /* eigenvalues of jacobian */
886 a1 = -(m[0] + m[2]);
887 ddeltb = a1*a1 - 4.0 * (m[0]*m[2] - m[1]*m[1]);
888
889 if ( ddeltb < 0.0 ) {
890 fprintf(stderr,"\n ## Error: %s: Delta: %f\n",__func__,ddeltb);
891 ddeltb = 0.0;
892 }
893 ddeltb = sqrt(ddeltb);
894
895 if ( fabs(a1) < MG_EIGENV_EPS6 ) {
896 rr1 = 0.5 * sqrt(ddeltb);
897 rr2 = -rr1;
898 }
899 else if ( a1 < 0.0 ) {
900 rr1 = 0.5 * (-a1 + ddeltb);
901 rr2 = (-m[1]*m[1] + m[0]*m[2]) / rr1;
902 }
903 else if ( a1 > 0.0 ) {
904 rr1 = 0.5 * (-a1 - ddeltb);
905 rr2 = (-m[1]*m[1] + m[0]*m[2]) / rr1;
906 }
907 else {
908 rr1 = 0.5 * ddeltb;
909 rr2 = -rr1;
910 }
911
912vect:
913 xn = 1.0 / xn;
914 lambda[0] = rr1 * xn;
915 lambda[1] = rr2 * xn;
916
917 /* eigenvectors */
918 a1 = m[0] - rr1;
919 if ( fabs(a1)+fabs(m[1]) < MG_EIGENV_EPS6 ) {
920 if (fabs(lambda[1]) < fabs(lambda[0]) ) {
921 ux = 1.0;
922 uy = 0.0;
923 }
924 else {
925 ux = 0.0;
926 uy = 1.0;
927 }
928 }
929 else if ( fabs(a1) < fabs(m[1]) ) {
930 ux = 1.0;
931 uy = -a1 / m[1];
932 }
933 else if ( fabs(a1) > fabs(m[1]) ) {
934 ux = -m[1] / a1;
935 uy = 1.0;
936 }
937 else if ( fabs(lambda[1]) > fabs(lambda[0]) ) {
938 ux = 0.0;
939 uy = 1.0;
940 }
941 else {
942 ux = 1.0;
943 uy = 0.0;
944 }
945
946 dd = sqrt(ux*ux + uy*uy);
947 dd = 1.0 / dd;
948 if ( fabs(lambda[0]) > fabs(lambda[1]) ) {
949 vp[0][0] = ux * dd;
950 vp[0][1] = uy * dd;
951 }
952 else {
953 vp[0][0] = uy * dd;
954 vp[0][1] = -ux * dd;
955 }
956
957 /* orthogonal vector */
958 vp[1][0] = -vp[0][1];
959 vp[1][1] = vp[0][0];
960
961 return 1;
962}
963
973inline int MMG5_eigensym(double m[3],double lambda[2],double vp[2][2]) {
974 double sqDelta,dd,trm,vnorm,maxm,a11,a12,a22;
975 static int ddebug = 0;
976
977 lambda[0] = lambda[1] = 0.;
978 vp[0][0] = vp[0][1] = vp[1][0] = vp[1][1] = 0.;
979
980 maxm = fabs(m[0]);
981 maxm = fabs( m[1] ) > maxm ? fabs ( m[1] ) : maxm;
982 maxm = fabs( m[2] ) > maxm ? fabs ( m[2] ) : maxm;
983
984 if ( maxm < MG_EIGENV_EPS13 ) {
985 if ( ddebug ) printf(" ## Warning:%s: Quasi-null matrix.",__func__);
986 maxm = 1.;
987 }
988
989 /* normalize matrix */
990 dd = 1.0 / maxm;
991 a11 = m[0] * dd;
992 a12 = m[1] * dd;
993 a22 = m[2] * dd;
994
995 dd = a11 - a22;
996 trm = a11 + a22;
997 sqDelta = sqrt(dd*dd + 4.0*a12*a12);
998 lambda[0] = 0.5*(trm - sqDelta);
999
1000 /* Case when m = lambda[0]*I */
1001 if ( sqDelta < MMG5_EPS ) {
1002 lambda[0] *= maxm;
1003 lambda[1] = lambda[0];
1004 vp[0][0] = 1.0;
1005 vp[0][1] = 0.0;
1006
1007 vp[1][0] = 0.0;
1008 vp[1][1] = 1.0;
1009 return 2;
1010 }
1011 /* Remark: the computation of an independent basis of eigenvectors fail if the
1012 * matrix is diagonal (we find twice the same eigenvector) */
1013 vp[0][0] = a12;
1014 vp[0][1] = (lambda[0] - a11);
1015 vnorm = sqrt(vp[0][0]*vp[0][0] + vp[0][1]*vp[0][1]);
1016
1017 if ( vnorm < MMG5_EPS ) {
1018 vp[0][0] = (lambda[0] - a22);
1019 vp[0][1] = a12;
1020 vnorm = sqrt(vp[0][0]*vp[0][0] + vp[0][1]*vp[0][1]);
1021 }
1022 assert(vnorm > MMG5_EPSD);
1023
1024 vnorm = 1.0/vnorm;
1025 vp[0][0] *= vnorm;
1026 vp[0][1] *= vnorm;
1027
1028 vp[1][0] = -vp[0][1];
1029 vp[1][1] = vp[0][0];
1030
1031 lambda[1] = a11*vp[1][0]*vp[1][0] + 2.0*a12*vp[1][0]*vp[1][1]
1032 + a22*vp[1][1]*vp[1][1];
1033
1034 lambda[0] *= maxm;
1035 lambda[1] *= maxm;
1036
1037 /* Check orthogonality of eigenvectors. If they are not, we probably miss the
1038 * dectection of a diagonal matrix. */
1039 assert ( fabs(vp[0][0]*vp[1][0] + vp[0][1]*vp[1][1]) <= MG_EIGENV_EPS6 );
1040
1041 return 1;
1042}
tmp[*strlen0]
int MMG5_eigensym(double m[3], double lambda[2], double vp[2][2])
Definition: eigenv.c:973
#define egal(x, y)
Definition: eigenv.c:59
#define MG_EIGENV_EPS13
Definition: eigenv.c:47
int MMG5_eigen2(double *mm, double *lambda, double vp[2][2])
Find eigenvalues and vectors of a 2x2 matrix.
Definition: eigenv.c:858
#define MG_EIGENV_EPS5e6
Definition: eigenv.c:49
#define MAXTOU
Definition: eigenv.c:53
int MMG5_eigenv3d(int symmat, double *mat, double lambda[3], double v[3][3])
Find eigenvalues and vectors of a 3x3 matrix.
Definition: eigenv.c:385
int MMG5_eigenv2d(int symmat, double *mat, double lambda[2], double vp[2][2])
Find eigenvalues and vectors of a 2x2 matrix.
Definition: eigenv.c:780
#define MG_EIGENV_EPS27
Definition: eigenv.c:46
#define MG_EIGENV_EPS5
Definition: eigenv.c:52
static double Id[3][3]
Identity matrix.
Definition: eigenv.c:67
static int newton3(double p[4], double x[3])
Find root(s) of a polynomial of degree 3.
Definition: eigenv.c:86
#define MG_EIGENV_EPS10
Definition: eigenv.c:48
static int MMG5_check_accuracy(double mat[6], double lambda[3], double v[3][3], double w1[3], double w2[3], double w3[3], double maxm, int order, int symmat)
Definition: eigenv.c:311
#define MG_EIGENV_EPS6
Definition: eigenv.c:50
#define MMG5_EPSD
#define MMG5_EPS
void MMG5_dotprod(int8_t dim, double *a, double *b, double *result)
Definition: tools.c:265
void MMG5_crossprod3d(double *a, double *b, double *result)
Definition: tools.c:300