Anasazi Version of the Day
Loading...
Searching...
No Matches
CheckingTools.cpp
1// @HEADER
2// *****************************************************************************
3// Anasazi: Block Eigensolvers Package
4//
5// Copyright 2004 NTESS and the Anasazi contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10// This software is a result of the research described in the report
11//
12// "A comparison of algorithms for modal analysis in the absence
13// of a sparse direct method", P. Arbenz, R. Lehoucq, and U. Hetmaniuk,
14// Sandia National Laboratories, Technical report SAND2003-1028J.
15//
16// It is based on the Epetra, AztecOO, and ML packages defined in the Trilinos
17// framework ( http://trilinos.org/ ).
18
19#include "CheckingTools.h"
20
21
22CheckingTools::CheckingTools(const Epetra_Comm &_Comm) :
23 MyComm(_Comm) {
24
25}
26
27
28double CheckingTools::errorOrthogonality(const Epetra_MultiVector *X,
29 const Epetra_MultiVector *R, const Epetra_Operator *M) const {
30
31 // Return the maximum value of R_i^T * M * X_j / || MR_i || || X_j ||
32 // When M is not specified, the identity is used.
33 double maxDot = 0.0;
34
35 int xc = (X) ? X->NumVectors() : 0;
36 int rc = (R) ? R->NumVectors() : 0;
37
38 if (xc*rc == 0)
39 return maxDot;
40
41 int i, j;
42 for (i = 0; i < rc; ++i) {
43 Epetra_Vector MRi(Copy, (*R), i);
44 if (M)
45 M->Apply(*((*R)(i)), MRi);
46 double normMR = 0.0;
47 MRi.Norm2(&normMR);
48 double dot = 0.0;
49 for (j = 0; j < xc; ++j) {
50 double normXj = 0.0;
51 (*X)(j)->Norm2(&normXj);
52 (*X)(j)->Dot(MRi, &dot);
53 dot = fabs(dot)/(normMR*normXj);
54 maxDot = (dot > maxDot) ? dot : maxDot;
55 }
56 }
57
58 return maxDot;
59
60}
61
62
63double CheckingTools::errorOrthonormality(const Epetra_MultiVector *X,
64 const Epetra_Operator *M) const {
65
66 // Return the maximum coefficient of the matrix X^T * M * X - I
67 // When M is not specified, the identity is used.
68 double maxDot = 0.0;
69
70 int xc = (X) ? X->NumVectors() : 0;
71 if (xc == 0)
72 return maxDot;
73
74 int i, j;
75 for (i = 0; i < xc; ++i) {
76 Epetra_Vector MXi(Copy, (*X), i);
77 if (M)
78 M->Apply(*((*X)(i)), MXi);
79 double dot = 0.0;
80 for (j = 0; j < xc; ++j) {
81 (*X)(j)->Dot(MXi, &dot);
82 dot = (i == j) ? fabs(dot - 1.0) : fabs(dot);
83 maxDot = (dot > maxDot) ? dot : maxDot;
84 }
85 }
86
87 return maxDot;
88
89}
90
91
92double CheckingTools::errorEquality(const Epetra_MultiVector *X,
93 const Epetra_MultiVector *MX, const Epetra_Operator *M) const {
94
95 // Return the maximum coefficient of the matrix M * X - MX
96 // scaled by the maximum coefficient of MX.
97 // When M is not specified, the identity is used.
98
99 double maxDiff = 0.0;
100
101 int xc = (X) ? X->NumVectors() : 0;
102 int mxc = (MX) ? MX->NumVectors() : 0;
103
104 if ((xc != mxc) || (xc*mxc == 0))
105 return maxDiff;
106
107 int i;
108 double maxCoeffX = 0.0;
109 for (i = 0; i < xc; ++i) {
110 double tmp = 0.0;
111 (*MX)(i)->NormInf(&tmp);
112 maxCoeffX = (tmp > maxCoeffX) ? tmp : maxCoeffX;
113 }
114
115 for (i = 0; i < xc; ++i) {
116 Epetra_Vector MtimesXi(Copy, (*X), i);
117 if (M)
118 M->Apply(*((*X)(i)), MtimesXi);
119 MtimesXi.Update(-1.0, *((*MX)(i)), 1.0);
120 double diff = 0.0;
121 MtimesXi.NormInf(&diff);
122 maxDiff = (diff > maxDiff) ? diff : maxDiff;
123 }
124
125 return (maxCoeffX == 0.0) ? maxDiff : maxDiff/maxCoeffX;
126
127}
128
129
130int CheckingTools::errorSubspaces(const Epetra_MultiVector &Q, const Epetra_MultiVector &Qex,
131 const Epetra_Operator *M) const {
132
133 int info = 0;
134 int myPid = MyComm.MyPID();
135
136 int qr = Q.MyLength();
137 int qc = Q.NumVectors();
138 int qexc = Qex.NumVectors();
139
140 double *mQ = new (nothrow) double[qr];
141 if (mQ == 0) {
142 info = -1;
143 return info;
144 }
145
146 double *z = new (nothrow) double[qexc*qc];
147 if (z == 0) {
148 delete[] mQ;
149 info = -1;
150 return info;
151 }
152
153 Epetra_LocalMap lMap(qexc, 0, MyComm);
154 Epetra_MultiVector QextMQ(View, lMap, z, qexc, qc);
155
156 int j;
157 for (j=0; j<qc; ++j) {
158 Epetra_MultiVector Qj(View, Q, j, 1);
159 Epetra_MultiVector MQ(View, Q.Map(), mQ, qr, 1);
160 if (M)
161 M->Apply(Qj, MQ);
162 else
163 memcpy(mQ, Qj.Values(), qr*sizeof(double));
164 Epetra_MultiVector colJ(View, QextMQ, j, 1);
165 colJ.Multiply('T', 'N', 1.0, Qex, MQ, 0.0);
166 }
167 delete[] mQ;
168
169 // Compute the SVD
170
171 int svSize = (qc > qexc) ? qexc : qc;
172 double *sv = new (nothrow) double[svSize];
173 if (sv == 0) {
174 delete[] z;
175 info = -1;
176 return info;
177 }
178
179 // lwork is larger than the value suggested by LAPACK
180 int lwork = (qexc > qc) ? qexc + 5*qc : qc + 5*qexc;
181 double *work = new (nothrow) double[lwork];
182 if (work == 0) {
183 delete[] z;
184 delete[] sv;
185 info = -1;
186 return info;
187 }
188
189 Epetra_LAPACK call;
190 call.GESVD('N','N',qexc,qc,QextMQ.Values(),qexc,sv,0,qc,0,qc,work,&lwork,&info);
191
192 delete[] work;
193 delete[] z;
194
195 // Treat error messages
196
197 if (info < 0) {
198 if (myPid == 0) {
199 cerr << endl;
200 cerr << " In DGESVD, argument " << -info << " has an illegal value\n";
201 cerr << endl;
202 }
203 delete[] sv;
204 return info;
205 }
206
207 if (info > 0) {
208 if (myPid == 0) {
209 cerr << endl;
210 cerr << " In DGESVD, DBSQR did not converge (" << info << ").\n";
211 cerr << endl;
212 }
213 delete[] sv;
214 return info;
215 }
216
217 // Output the result
218
219 if (myPid == 0) {
220 cout << endl;
221 cout << " --- Principal angles between eigenspaces ---\n";
222 cout << endl;
223 cout << " Reference space built with " << Qex.NumVectors() << " vectors." << endl;
224 cout << " Dimension of computed space: " << Q.NumVectors() << endl;
225 cout << endl;
226 cout.setf(ios::scientific, ios::floatfield);
227 cout.precision(4);
228 cout << " Smallest singular value = " << sv[0] << endl;
229 cout << " Largest singular value = " << sv[svSize-1] << endl;
230 cout << endl;
231 cout << " Smallest angle between subspaces (rad) = ";
232 cout << ((sv[0] > 1.0) ? 0.0 : asin(sqrt(1.0 - sv[0]*sv[0]))) << endl;
233 cout << " Largest angle between subspaces (rad) = ";
234 cout << ((sv[0] > 1.0) ? 0.0 : asin(sqrt(1.0 - sv[svSize-1]*sv[svSize-1]))) << endl;
235 cout << endl;
236 }
237
238 delete[] sv;
239
240 return info;
241
242}
243
244
245void CheckingTools::errorEigenResiduals(const Epetra_MultiVector &Q, double *lambda,
246 const Epetra_Operator *K, const Epetra_Operator *M,
247 double *normWeight) const {
248
249 if ((K == 0) || (lambda == 0))
250 return;
251
252 int myPid = MyComm.MyPID();
253
254 int qr = Q.MyLength();
255 int qc = Q.NumVectors();
256
257 double *work = new (nothrow) double[2*qr];
258 if (work == 0)
259 return;
260
261 if (myPid == 0) {
262 cout << endl;
263 cout << " --- Norms of residuals for computed eigenmodes ---\n";
264 cout << endl;
265 cout << " Eigenvalue";
266 if (normWeight)
267 cout << " User Norm Scaled User N.";
268 cout << " 2-Norm Scaled 2-Nor.\n";
269 }
270
271 Epetra_Vector KQ(View, Q.Map(), work);
272 Epetra_Vector MQ(View, Q.Map(), work + qr);
273 Epetra_Vector Qj(View, Q.Map(), Q.Values());
274
275 double maxUserNorm = 0.0;
276 double minUserNorm = 1.0e+100;
277 double maxL2Norm = 0.0;
278 double minL2Norm = 1.0e+100;
279
280 // Compute the residuals and norms
281 int j;
282 for (j=0; j<qc; ++j) {
283
284 Qj.ResetView(Q.Values() + j*qr);
285 if (M)
286 M->Apply(Qj, MQ);
287 else
288 memcpy(MQ.Values(), Qj.Values(), qr*sizeof(double));
289 K->Apply(Qj, KQ);
290 KQ.Update(-lambda[j], MQ, 1.0);
291
292 double residualL2 = 0.0;
293 KQ.Norm2(&residualL2);
294
295 double residualUser = 0.0;
296 if (normWeight) {
297 Epetra_Vector vectWeight(View, Q.Map(), normWeight);
298 KQ.NormWeighted(vectWeight, &residualUser);
299 }
300
301 if (myPid == 0) {
302 cout << " ";
303 cout.width(4);
304 cout.precision(8);
305 cout.setf(ios::scientific, ios::floatfield);
306 cout << j+1 << ". " << lambda[j] << " ";
307 if (normWeight) {
308 cout << residualUser << " ";
309 cout << ((lambda[j] == 0.0) ? 0.0 : residualUser/lambda[j]) << " ";
310 }
311 cout << residualL2 << " " << ((lambda[j] == 0.0) ? 0.0 : residualL2/lambda[j]) << " ";
312 cout << endl;
313 }
314
315 if (lambda[j] > 0.0) {
316 maxL2Norm = (residualL2/lambda[j] > maxL2Norm) ? residualL2/lambda[j] : maxL2Norm;
317 minL2Norm = (residualL2/lambda[j] < minL2Norm) ? residualL2/lambda[j] : minL2Norm;
318 if (normWeight) {
319 maxUserNorm = (residualUser/lambda[j] > maxUserNorm) ? residualUser/lambda[j]
320 : maxUserNorm;
321 minUserNorm = (residualUser/lambda[j] < minUserNorm) ? residualUser/lambda[j]
322 : minUserNorm;
323 }
324 }
325
326 } // for j=0; j<qc; ++j)
327
328 if (myPid == 0) {
329 cout << endl;
330 if (normWeight) {
331 cout << " >> Minimum scaled user-norm of residuals = " << minUserNorm << endl;
332 cout << " >> Maximum scaled user-norm of residuals = " << maxUserNorm << endl;
333 cout << endl;
334 }
335 cout << " >> Minimum scaled L2 norm of residuals = " << minL2Norm << endl;
336 cout << " >> Maximum scaled L2 norm of residuals = " << maxL2Norm << endl;
337 cout << endl;
338 }
339
340 if (work)
341 delete[] work;
342
343}
344
345
346void CheckingTools::errorEigenResiduals(const Epetra_MultiVector &Q, double *lambda,
347 const Epetra_Operator *K, const Epetra_Operator *M,
348 const Epetra_Operator *Msolver) const {
349
350 if ((K == 0) || (lambda == 0) || (Msolver == 0))
351 return;
352
353 int myPid = MyComm.MyPID();
354
355 int qr = Q.MyLength();
356 int qc = Q.NumVectors();
357
358 double *work = new (nothrow) double[2*qr];
359 if (work == 0)
360 return;
361
362 if (myPid == 0) {
363 cout << endl;
364 cout << " --- Norms of residuals for computed eigenmodes ---\n";
365 cout << endl;
366 cout << " Eigenvalue";
367 cout << " M^{-1} N. Sca. M^{-1} N.";
368 cout << " 2-Norm Scaled 2-Nor.\n";
369 }
370
371 Epetra_Vector KQ(View, Q.Map(), work);
372 Epetra_Vector MQ(View, Q.Map(), work + qr);
373 Epetra_Vector Qj(View, Q.Map(), Q.Values());
374
375 double maxMinvNorm = 0.0;
376 double minMinvNorm = 1.0e+100;
377 double maxL2Norm = 0.0;
378 double minL2Norm = 1.0e+100;
379
380 // Compute the residuals and norms
381 int j;
382 for (j=0; j<qc; ++j) {
383
384 Qj.ResetView(Q.Values() + j*qr);
385 if (M)
386 M->Apply(Qj, MQ);
387 else
388 memcpy(MQ.Values(), Qj.Values(), qr*sizeof(double));
389 K->Apply(Qj, KQ);
390 KQ.Update(-lambda[j], MQ, 1.0);
391
392 double residualL2 = 0.0;
393 KQ.Norm2(&residualL2);
394
395 double residualMinv = 0.0;
396 Msolver->ApplyInverse(KQ, MQ);
397 KQ.Dot(MQ, &residualMinv);
398 residualMinv = sqrt(fabs(residualMinv));
399
400 if (myPid == 0) {
401 cout << " ";
402 cout.width(4);
403 cout.precision(8);
404 cout.setf(ios::scientific, ios::floatfield);
405 cout << j+1 << ". " << lambda[j] << " ";
406 cout << residualMinv << " ";
407 cout << ((lambda[j] == 0.0) ? 0.0 : residualMinv/lambda[j]) << " ";
408 cout << residualL2 << " " << ((lambda[j] == 0.0) ? 0.0 : residualL2/lambda[j]) << " ";
409 cout << endl;
410 }
411
412 if (lambda[j] > 0.0) {
413 maxL2Norm = (residualL2/lambda[j] > maxL2Norm) ? residualL2/lambda[j] : maxL2Norm;
414 minL2Norm = (residualL2/lambda[j] < minL2Norm) ? residualL2/lambda[j] : minL2Norm;
415 maxMinvNorm = (residualMinv/lambda[j] > maxMinvNorm) ? residualMinv/lambda[j]
416 : maxMinvNorm;
417 minMinvNorm = (residualMinv/lambda[j] < minMinvNorm) ? residualMinv/lambda[j]
418 : minMinvNorm;
419 }
420
421 } // for j=0; j<qc; ++j)
422
423 if (myPid == 0) {
424 cout << endl;
425 cout << " >> Minimum scaled M^{-1}-norm of residuals = " << minMinvNorm << endl;
426 cout << " >> Maximum scaled M^{-1}-norm of residuals = " << maxMinvNorm << endl;
427 cout << endl;
428 cout << " >> Minimum scaled L2 norm of residuals = " << minL2Norm << endl;
429 cout << " >> Maximum scaled L2 norm of residuals = " << maxL2Norm << endl;
430 cout << endl;
431 }
432
433 if (work)
434 delete[] work;
435
436}
437
438
439int CheckingTools::errorLambda(double *continuous, double *discrete, int numDiscrete,
440 double *lambda, int nev) const {
441
442 int myPid = MyComm.MyPID();
443 int nMax = 0;
444 int i, j;
445
446 // Allocate working arrays
447
448 int *used = new (nothrow) int[numDiscrete + nev];
449 if (used == 0) {
450 return nMax;
451 }
452
453 int *bestMatch = used + numDiscrete;
454
455 // Find the best match for the eigenvalues
456 double eps = 0.0;
457 Epetra_LAPACK call;
458 call.LAMCH('E', eps);
459
460 double gap = Epetra_MaxDouble;
461 for (i=0; i<numDiscrete; ++i) {
462 used[i] = -1;
463 for (j = i; j < numDiscrete; ++j) {
464 if (discrete[j] > (1.0 + 10.0*eps)*discrete[i]) {
465 double tmp = (discrete[j] - discrete[i])/discrete[i];
466 gap = (tmp < gap) ? tmp : gap;
467 break;
468 }
469 }
470 }
471
472 for (i=0; i<nev; ++i) {
473 bestMatch[i] = -1;
474 }
475
476 for (i=0; i<nev; ++i) {
477 if (lambda[i] < continuous[0]) {
478 continue;
479 }
480 bestMatch[i] = (i == 0) ? 0 : bestMatch[i-1] + 1;
481 int jStart = bestMatch[i];
482 for (j = jStart; j < numDiscrete; ++j) {
483 double diff = fabs(lambda[i]-discrete[j]);
484 if (diff < 0.5*gap*lambda[i]) {
485 bestMatch[i] = j;
486 break;
487 }
488 }
489 used[bestMatch[i]] = i;
490 }
491
492 // Print the results for the eigenvalues
493 if (myPid == 0) {
494 cout << endl;
495 cout << " --- Relative errors on eigenvalues ---\n";
496 cout << endl;
497 cout << " Exact Cont. Exact Disc. Computed ";
498 cout << " Alg. Err. Mesh Err.\n";
499 }
500
501 int iCount = 0;
502 for (i=0; i<nev; ++i) {
503 if (bestMatch[i] == -1) {
504 if (myPid == 0) {
505 cout << " ************** ************** ";
506 cout.precision(8);
507 cout.setf(ios::scientific, ios::floatfield);
508 cout << lambda[i];
509 cout << " ********* *********" << endl;
510 }
511 iCount += 1;
512 }
513 }
514
515 double lastDiscrete = 0.0;
516 for (i=0; i<numDiscrete; ++i) {
517 if ((iCount == nev) && (discrete[i] > lastDiscrete)) {
518 break;
519 }
520 if (used[i] < 0) {
521 nMax += 1;
522 lastDiscrete = discrete[i];
523 if (myPid == 0) {
524 cout << " ";
525 cout.width(4);
526 cout << i+1 << ". ";
527 cout.precision(8);
528 cout.setf(ios::scientific, ios::floatfield);
529 cout << continuous[i] << " " << discrete[i] << " ";
530 cout << "************** ********* ";
531 cout.precision(3);
532 cout << fabs(continuous[i]-discrete[i])/continuous[i] << endl;
533 }
534 }
535 else {
536 nMax += 1;
537 lastDiscrete = discrete[i];
538 if (myPid == 0) {
539 cout << " ";
540 cout.width(4);
541 cout << i+1 << ". ";
542 cout.precision(8);
543 cout.setf(ios::scientific, ios::floatfield);
544 cout << continuous[i] << " " << discrete[i] << " " << lambda[used[i]] << " ";
545 cout.precision(3);
546 cout << fabs(lambda[used[i]]-discrete[i])/discrete[i] << " ";
547 cout << fabs(continuous[i]-discrete[i])/continuous[i] << endl;
548 }
549 iCount += 1;
550 }
551 }
552
553 delete[] used;
554
555 return nMax;
556
557}
558
559
560int CheckingTools::inputArguments(const int &numEigen, const Epetra_Operator *K,
561 const Epetra_Operator *M, const Epetra_Operator *P,
562 const Epetra_MultiVector &Q, const int &minSize) const {
563
564 // Routine to check some arguments
565 //
566 // info = - 1 >> The stiffness matrix K has not been specified.
567 // info = - 2 >> The maps for the matrix K and the matrix M differ.
568 // info = - 3 >> The maps for the matrix K and the preconditioner P differ.
569 // info = - 4 >> The maps for the vectors and the matrix K differ.
570 // info = - 5 >> Q is too small for the number of eigenvalues requested.
571 // info = - 6 >> Q is too small for the computation parameters.
572 //
573
574 int myPid = MyComm.MyPID();
575
576 if (K == 0) {
577 if (myPid == 0)
578 cerr << "\n !!! The matrix K to analyze has not been specified !!! \n\n";
579 return -1;
580 }
581
582 if (M) {
583 int mGlobal = (M->OperatorDomainMap()).NumGlobalElements();
584 int mLocal = (M->OperatorDomainMap()).NumMyElements();
585 int kGlobal = (K->OperatorDomainMap()).NumGlobalElements();
586 int kLocal = (K->OperatorDomainMap()).NumMyElements();
587 if ((mGlobal != kGlobal) || (mLocal != kLocal)) {
588 if (myPid == 0) {
589 cerr << endl;
590 cerr << " !!! The maps for the matrice K and the mass M are different !!!\n";
591 cerr << endl;
592 }
593 return -2;
594 }
595 }
596
597 if (P) {
598 int pGlobal = (P->OperatorDomainMap()).NumGlobalElements();
599 int pLocal = (P->OperatorDomainMap()).NumMyElements();
600 int kGlobal = (K->OperatorDomainMap()).NumGlobalElements();
601 int kLocal = (K->OperatorDomainMap()).NumMyElements();
602 if ((pGlobal != kGlobal) || (pLocal != kLocal)) {
603 if (myPid == 0) {
604 cerr << endl;
605 cerr << " !!! The maps for the matrice K and the preconditioner P are different !!!\n";
606 cerr << endl;
607 }
608 return -3;
609 }
610 }
611
612 if ((Q.MyLength() != (K->OperatorDomainMap()).NumMyElements()) ||
613 (Q.GlobalLength() != (K->OperatorDomainMap()).NumGlobalElements())) {
614 if (myPid == 0) {
615 cerr << "\n !!! The maps for the vectors and the matrix are different !!! \n\n";
616 }
617 return -4;
618 }
619
620 if (Q.NumVectors() < numEigen) {
621 if (myPid == 0) {
622 cerr << endl;
623 cerr << " !!! The number of eigenvalues is too large for the space allocated !!! \n\n";
624 cerr << " The recommended size for " << numEigen << " eigenvalues is ";
625 cerr << minSize << endl;
626 cerr << endl;
627 }
628 return -5;
629 }
630
631 if (Q.NumVectors() < minSize) {
632 if (myPid == 0) {
633 cerr << endl;
634 cerr << " !!! The space allocated is too small for the number of eigenvalues";
635 cerr << " and the size of blocks specified !!! \n";
636 cerr << " The recommended size for " << numEigen << " eigenvalues is ";
637 cerr << minSize << endl;
638 cerr << endl;
639 }
640 return -6;
641 }
642
643 return 0;
644
645}
646
647