Anasazi  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ModeLaplace1DQ2.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 //**************************************************************************
11 //
12 // NOTICE
13 //
14 // This software is a result of the research described in the report
15 //
16 // " A comparison of algorithms for modal analysis in the absence
17 // of a sparse direct method", P. ArbenZ, R. Lehoucq, and U. Hetmaniuk,
18 // Sandia National Laboratories, Technical report SAND2003-1028J.
19 //
20 // It is based on the Epetra, AztecOO, and ML packages defined in the Trilinos
21 // framework ( http://software.sandia.gov/trilinos/ ).
22 //
23 // The distribution of this software follows also the rules defined in Trilinos.
24 // This notice shall be marked on anY reproduction of this software, in whole or
25 // in part.
26 //
27 // This program is distributed in the hope that it will be useful, but
28 // WITHOUT ANY WARRANTY; without even the implied warranty of
29 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
30 //
31 // Code Authors: U. Hetmaniuk (ulhetma@sandia.gov), R. Lehoucq (rblehou@sandia.gov)
32 //
33 //**************************************************************************
34 
35 #include "ModeLaplace1DQ2.h"
36 #include "Teuchos_Assert.hpp"
37 
38 
39 const int ModeLaplace1DQ2::dofEle = 3;
40 const int ModeLaplace1DQ2::maxConnect = 5;
41 #ifndef M_PI
42 const double ModeLaplace1DQ2::M_PI = 3.14159265358979323846;
43 #endif
44 
45 
46 ModeLaplace1DQ2::ModeLaplace1DQ2(const Epetra_Comm &_Comm, double _Lx, int _nX)
47  : myVerify(_Comm),
48  MyComm(_Comm),
49  mySort(),
50  Map(0),
51  K(0),
52  M(0),
53  Lx(_Lx),
54  nX(_nX),
55  x(0)
56  {
57 
58  preProcess();
59 
60 }
61 
62 
63 ModeLaplace1DQ2::~ModeLaplace1DQ2() {
64 
65  if (Map)
66  delete Map;
67  Map = 0;
68 
69  if (K)
70  delete K;
71  K = 0;
72 
73  if (M)
74  delete M;
75  M = 0;
76 
77  if (x)
78  delete[] x;
79  x = 0;
80 
81 }
82 
83 
84 void ModeLaplace1DQ2::preProcess() {
85 
86  // Create the distribution of equations across processors
87  makeMap();
88 
89  // Count the number of elements touched by this processor
90  bool *isTouched = new bool[nX];
91  int i;
92  for (i=0; i<nX; ++i)
93  isTouched[i] = false;
94 
95  int numEle = countElements(isTouched);
96 
97  // Create the mesh
98  int *elemTopo = new int[dofEle*numEle];
99  makeMyElementsTopology(elemTopo, isTouched);
100 
101  delete[] isTouched;
102 
103  // Get the number of nonZeros per row
104  int localSize = Map->NumMyElements();
105  int *numNz = new int[localSize];
106  int *connectivity = new int[localSize*maxConnect];
107  makeMyConnectivity(elemTopo, numEle, connectivity, numNz);
108 
109  // Make the stiffness matrix
110  makeStiffness(elemTopo, numEle, connectivity, numNz);
111 
112  // Assemble the mass matrix
113  makeMass(elemTopo, numEle, connectivity, numNz);
114 
115  // Free some memory
116  delete[] elemTopo;
117  delete[] numNz;
118  delete[] connectivity;
119 
120  // Get the geometrical coordinates of the managed nodes
121  double hx = Lx/nX;
122 
123  x = new double[localSize];
124 
125  for (i=0; i<2*nX-1; ++i) {
126  int node = i;
127  if (Map->LID(node) > -1) {
128  x[Map->LID(node)] = (i+1)*hx*0.5;
129  }
130  }
131 
132 }
133 
134 
135 void ModeLaplace1DQ2::makeMap() {
136 
137  int globalSize = (2*nX - 1);
138  assert(globalSize > MyComm.NumProc());
139 
140  // Create a uniform distribution of the unknowns across processors
141  Map = new Epetra_Map(globalSize, 0, MyComm);
142 
143 }
144 
145 
146 int ModeLaplace1DQ2::countElements(bool *isTouched) {
147 
148  // This routine counts and flags the elements that contain the nodes
149  // on this processor.
150 
151  int i;
152  int numEle = 0;
153 
154  for (i=0; i<nX; ++i) {
155  isTouched[i] = false;
156  int node;
157  node = (i==0) ? -1 : 2*i-1;
158  if ((node > -1) && (Map->LID(node) > -1)) {
159  isTouched[i] = true;
160  numEle += 1;
161  continue;
162  }
163  node = (i==nX-1) ? -1 : 2*i+1;
164  if ((node > -1) && (Map->LID(node) > -1)) {
165  isTouched[i] = true;
166  numEle += 1;
167  continue;
168  }
169  node = 2*i;
170  if ((node > -1) && (Map->LID(node) > -1)) {
171  isTouched[i] = true;
172  numEle += 1;
173  continue;
174  }
175  }
176 
177  return numEle;
178 
179 }
180 
181 
182 void ModeLaplace1DQ2::makeMyElementsTopology(int *elemTopo, bool *isTouched) {
183 
184  // Create the element topology for the elements containing nodes for this processor
185  // Note: Put the flag -1 when the node has a Dirichlet boundary condition
186 
187  int i;
188  int numEle = 0;
189 
190  for (i=0; i<nX; ++i) {
191  if (isTouched[i] == false)
192  continue;
193  elemTopo[dofEle*numEle] = (i==0) ? -1 : 2*i-1;
194  elemTopo[dofEle*numEle+1] = (i==nX-1) ? -1 : 2*i+1;
195  elemTopo[dofEle*numEle+2] = 2*i;
196  numEle += 1;
197  }
198 
199 }
200 
201 
202 void ModeLaplace1DQ2::makeMyConnectivity(int *elemTopo, int numEle, int *connectivity,
203  int *numNz) {
204 
205  // This routine creates the connectivity of each managed node
206  // from the element topology.
207 
208  int i, j;
209  int localSize = Map->NumMyElements();
210 
211  for (i=0; i<localSize; ++i) {
212  numNz[i] = 0;
213  for (j=0; j<maxConnect; ++j) {
214  connectivity[i*maxConnect + j] = -1;
215  }
216  }
217 
218  for (i=0; i<numEle; ++i) {
219  for (j=0; j<dofEle; ++j) {
220  if (elemTopo[dofEle*i+j] == -1)
221  continue;
222  int node = Map->LID(elemTopo[dofEle*i+j]);
223  if (node > -1) {
224  int k;
225  for (k=0; k<dofEle; ++k) {
226  int neighbor = elemTopo[dofEle*i+k];
227  if (neighbor == -1)
228  continue;
229  // Check if this neighbor is already stored
230  int l;
231  for (l=0; l<maxConnect; ++l) {
232  if (neighbor == connectivity[node*maxConnect + l])
233  break;
234  if (connectivity[node*maxConnect + l] == -1) {
235  connectivity[node*maxConnect + l] = neighbor;
236  numNz[node] += 1;
237  break;
238  }
239  } // for (l = 0; l < maxConnect; ++l)
240  } // for (k = 0; k < dofEle; ++k)
241  } // if (node > -1)
242  } // for (j = 0; j < dofEle; ++j)
243  } // for (i = 0; i < numEle; ++i)
244 
245 }
246 
247 
248 void ModeLaplace1DQ2::makeStiffness(int *elemTopo, int numEle, int *connectivity,
249  int *numNz) {
250 
251  // Create Epetra_Matrix for stiffness
252  K = new Epetra_CrsMatrix(Copy, *Map, numNz);
253 
254  int i;
255  int localSize = Map->NumMyElements();
256 
257  double *values = new double[maxConnect];
258  for (i=0; i<maxConnect; ++i)
259  values[i] = 0.0;
260 
261  for (i=0; i<localSize; ++i) {
262  int info = K->InsertGlobalValues(Map->GID(i), numNz[i], values, connectivity+maxConnect*i);
263  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
264  "ModeLaplace1DQ2::makeStiffness(): InsertGlobalValues() returned error code " << info);
265  }
266 
267  // Define the elementary matrix
268  double *kel = new double[dofEle*dofEle];
269  makeElementaryStiffness(kel);
270 
271  // Assemble the matrix
272  int *indices = new int[dofEle];
273  int numEntries;
274  int j;
275  for (i=0; i<numEle; ++i) {
276  for (j=0; j<dofEle; ++j) {
277  if (elemTopo[dofEle*i + j] == -1)
278  continue;
279  if (Map->LID(elemTopo[dofEle*i+j]) == -1)
280  continue;
281  numEntries = 0;
282  int k;
283  for (k=0; k<dofEle; ++k) {
284  if (elemTopo[dofEle*i+k] == -1)
285  continue;
286  indices[numEntries] = elemTopo[dofEle*i+k];
287  values[numEntries] = kel[dofEle*j + k];
288  numEntries += 1;
289  }
290  int info = K->SumIntoGlobalValues(elemTopo[dofEle*i+j], numEntries, values, indices);
291  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
292  "ModeLaplace1DQ2::makeStiffness(): SumIntoGlobalValues() returned error code " << info);
293  }
294  }
295 
296  delete[] kel;
297  delete[] values;
298  delete[] indices;
299 
300  int info = K->FillComplete();
301  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
302  "ModeLaplace1DQ2::makeStiffness(): FillComplete() returned error code " << info);
303  info = K->OptimizeStorage();
304  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
305  "ModeLaplace1DQ2::makeStiffness(): OptimizeStorage() returned error code " << info);
306 
307 }
308 
309 
310 void ModeLaplace1DQ2::makeElementaryStiffness(double *kel) const {
311 
312  double hx = Lx/nX;
313 
314  kel[0] = 7.0/(3.0*hx); kel[1] = 1.0/(3.0*hx); kel[2] = - 8.0/(3.0*hx);
315  kel[3] = 1.0/(3.0*hx); kel[4] = 7.0/(3.0*hx); kel[5] = - 8.0/(3.0*hx);
316  kel[6] = - 8.0/(3.0*hx); kel[7] = - 8.0/(3.0*hx); kel[8] = 16.0/(3.0*hx);
317 
318 }
319 
320 
321 void ModeLaplace1DQ2::makeMass(int *elemTopo, int numEle, int *connectivity,
322  int *numNz) {
323 
324  // Create Epetra_Matrix for mass
325  M = new Epetra_CrsMatrix(Copy, *Map, numNz);
326 
327  int i;
328  int localSize = Map->NumMyElements();
329 
330  double *values = new double[maxConnect];
331  for (i=0; i<maxConnect; ++i)
332  values[i] = 0.0;
333  for (i=0; i<localSize; ++i) {
334  int info = M->InsertGlobalValues(Map->GID(i), numNz[i], values, connectivity + maxConnect*i);
335  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
336  "ModeLaplace1DQ2::makeMass(): InsertGlobalValues() returned error code " << info);
337  }
338 
339  // Define the elementary matrix
340  double *mel = new double[dofEle*dofEle];
341  makeElementaryMass(mel);
342 
343  // Assemble the matrix
344  int *indices = new int[dofEle];
345  int numEntries;
346  int j;
347  for (i=0; i<numEle; ++i) {
348  for (j=0; j<dofEle; ++j) {
349  if (elemTopo[dofEle*i + j] == -1)
350  continue;
351  if (Map->LID(elemTopo[dofEle*i+j]) == -1)
352  continue;
353  numEntries = 0;
354  int k;
355  for (k=0; k<dofEle; ++k) {
356  if (elemTopo[dofEle*i+k] == -1)
357  continue;
358  indices[numEntries] = elemTopo[dofEle*i+k];
359  values[numEntries] = mel[dofEle*j + k];
360  numEntries += 1;
361  }
362  int info = M->SumIntoGlobalValues(elemTopo[dofEle*i+j], numEntries, values, indices);
363  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
364  "ModeLaplace1DQ2::makeMass(): SumIntoGlobalValues() returned error code " << info);
365  }
366  }
367 
368  delete[] mel;
369  delete[] values;
370  delete[] indices;
371 
372  int info = M->FillComplete();
373  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
374  "ModeLaplace1DQ2::makeMass(): FillComplete() returned error code " << info);
375  info = M->OptimizeStorage();
376  TEUCHOS_TEST_FOR_EXCEPTION(info != 0, std::runtime_error,
377  "ModeLaplace1DQ2::makeMass(): OptimizeStorage() returned error code " << info);
378 }
379 
380 
381 void ModeLaplace1DQ2::makeElementaryMass(double *mel) const {
382 
383  double hx = Lx/nX;
384 
385  mel[0] = 2.0*hx/15.0; mel[1] = -hx/30.0; mel[2] = hx/15.0;
386  mel[3] = -hx/30.0; mel[4] = 2.0*hx/15.0; mel[5] = hx/15.0;
387  mel[6] = hx/15.0; mel[7] = hx/15.0; mel[8] = 8.0*hx/15.0;
388 
389 }
390 
391 
392 double ModeLaplace1DQ2::getFirstMassEigenValue() const {
393 
394  double hx = Lx/nX;
395 
396  // Compute the coefficient alphax
397  double cosi = cos(M_PI*hx/2/Lx);
398  double a = 2.0*cosi;
399  double b = 4.0 + cos(M_PI*hx/Lx);
400  double c = -2.0*cosi;
401  double delta = sqrt(b*b - 4*a*c);
402  double alphax = (-b - delta)*0.5/a;
403 
404  double discrete = hx/15.0*(8.0+2*alphax*cosi);
405 
406  return discrete;
407 
408 }
409 
410 
411 int ModeLaplace1DQ2::eigenCheck(const Epetra_MultiVector &Q, double *lambda,
412  double *normWeight) const {
413 
414  int info = 0;
415  int qc = Q.NumVectors();
416  int myPid = MyComm.MyPID();
417 
418  cout.precision(2);
419  cout.setf(ios::scientific, ios::floatfield);
420 
421  // Check orthonormality of eigenvectors
422  double tmp = myVerify.errorOrthonormality(&Q, M);
423  if (myPid == 0)
424  cout << " Maximum coefficient in matrix Q^T M Q - I = " << tmp << endl;
425 
426  // Print out norm of residuals
427  myVerify.errorEigenResiduals(Q, lambda, K, M, normWeight);
428 
429  // Check the eigenvalues
430  int numX = (int) ceil(sqrt(Lx*Lx*lambda[qc-1]/M_PI/M_PI));
431  numX = (numX > 2*nX) ? 2*nX : numX;
432  int newSize = (numX-1);
433  double *discrete = new (nothrow) double[2*newSize];
434  if (discrete == 0) {
435  return -1;
436  }
437  double *continuous = discrete + newSize;
438 
439  double hx = Lx/nX;
440 
441  int i;
442  for (i = 1; i < numX; ++i) {
443  continuous[i-1] = (M_PI/Lx)*(M_PI/Lx)*i*i;
444  // Compute the coefficient alphaX
445  double cosi = cos(i*M_PI*hx/2.0/Lx);
446  double a = cosi*(92.0 - 12.0*cos(i*M_PI*hx/Lx));
447  double b = 48.0 + 32.0*cos(i*M_PI*hx/Lx);
448  double c = -160.0*cosi;
449  double delta = sqrt(b*b - 4*a*c);
450  double alphaX = (-b - delta)*0.5/a;
451  alphaX = (alphaX < 0.0) ? (-b + delta)*0.5/a : alphaX;
452  // Compute the discrete eigenvalue
453  discrete[i-1] = 240.0*(1.0 - alphaX*cosi)/( (8.0 + 2*alphaX*cosi)*(3.0*hx*hx) );
454  }
455 
456  // Sort the eigenvalues in ascending order
457  mySort.sortScalars(newSize, continuous);
458 
459  int *used = new (nothrow) int[newSize];
460  if (used == 0) {
461  delete[] discrete;
462  return -1;
463  }
464 
465  mySort.sortScalars(newSize, discrete, used);
466 
467  int *index = new (nothrow) int[newSize];
468  if (index == 0) {
469  delete[] discrete;
470  delete[] used;
471  return -1;
472  }
473 
474  for (i=0; i<newSize; ++i) {
475  index[used[i]] = i;
476  }
477  delete[] used;
478 
479  int nMax = myVerify.errorLambda(continuous, discrete, newSize, lambda, qc);
480 
481  // Define the exact discrete eigenvectors
482  int localSize = Map->NumMyElements();
483  double *vQ = new (nothrow) double[(nMax+1)*localSize + nMax];
484  if (vQ == 0) {
485  delete[] discrete;
486  delete[] index;
487  info = -1;
488  return info;
489  }
490 
491  double *normL2 = vQ + (nMax+1)*localSize;
492  Epetra_MultiVector Qex(View, *Map, vQ, localSize, nMax);
493 
494  if ((myPid == 0) && (nMax > 0)) {
495  cout << endl;
496  cout << " --- Relative discretization errors for exact eigenvectors ---" << endl;
497  cout << endl;
498  cout << " Cont. Values Disc. Values Error H^1 norm L^2 norm\n";
499  }
500 
501  for (i=1; i < numX; ++i) {
502  if (index[i-1] < nMax) {
503  // Compute the coefficient alphaX
504  double cosi = cos(i*M_PI*hx/2/Lx);
505  double a = cosi*(92.0 - 12.0*cos(i*M_PI*hx/Lx));
506  double b = 48.0 + 32.0*cos(i*M_PI*hx/Lx);
507  double c = -160.0*cosi;
508  double delta = sqrt(b*b - 4*a*c);
509  double alphaX = (-b - delta)*0.5/a;
510  alphaX = (alphaX < 0.0) ? (-b + delta)*0.5/a : alphaX;
511  int ii;
512  for (ii=0; ii<localSize; ++ii) {
513  if (fabs(x[ii] - floor(x[ii]/hx+0.5)*hx) < 0.25*hx) {
514  Qex.ReplaceMyValue(ii, index[i-1], alphaX*sin(i*(M_PI/Lx)*x[ii]));
515  }
516  else {
517  Qex.ReplaceMyValue(ii, index[i-1], sin(i*(M_PI/Lx)*x[ii]));
518  }
519  }
520  // Normalize Qex against the mass matrix
521  Epetra_MultiVector MQex(View, *Map, vQ + nMax*localSize, localSize, 1);
522  Epetra_MultiVector Qi(View, Qex, index[i-1], 1);
523  M->Apply(Qi, MQex);
524  double mnorm = 0.0;
525  Qi.Dot(MQex, &mnorm);
526  Qi.Scale(1.0/sqrt(mnorm));
527  // Compute the L2 norm
528  Epetra_MultiVector shapeInt(View, *Map, vQ + nMax*localSize, localSize, 1);
529  for (ii=0; ii<localSize; ++ii) {
530  double iX;
531  if (fabs(x[ii] - floor(x[ii]/hx+0.5)*hx) < 0.25*hx)
532  iX = 2.0*sin(i*(M_PI/Lx)*x[ii])/(hx*hx*i*(M_PI/Lx)*i*(M_PI/Lx)*i*(M_PI/Lx))*
533  sqrt(2.0/Lx)*( 3*hx*i*(M_PI/Lx) - 4*sin(i*(M_PI/Lx)*hx) +
534  cos(i*(M_PI/Lx)*hx)*hx*i*(M_PI/Lx) );
535  else
536  iX = 8.0*sin(i*(M_PI/Lx)*x[ii])/(hx*hx*i*(M_PI/Lx)*i*(M_PI/Lx)*i*(M_PI/Lx))*
537  sqrt(2.0/Lx)*( 2*sin(i*(M_PI/Lx)*0.5*hx) -
538  cos(i*(M_PI/Lx)*0.5*hx)*hx*i*(M_PI/Lx));
539  shapeInt.ReplaceMyValue(ii, 0, iX);
540  }
541  Qi.Dot(shapeInt, normL2+index[i-1]);
542  } // if index[i-1] < nMax)
543  } // for (i=1; i < numX; ++i)
544 
545  if (myPid == 0) {
546  for (i = 0; i < nMax; ++i) {
547  double normH1 = continuous[i]*(1.0 - 2.0*normL2[i]) + discrete[i];
548  normL2[i] = 2.0 - 2.0*normL2[i];
549  normH1+= normL2[i];
550  // Print out the result
551  if (myPid == 0) {
552  cout << " ";
553  cout.width(4);
554  cout << i+1 << ". ";
555  cout.setf(ios::scientific, ios::floatfield);
556  cout.precision(8);
557  cout << continuous[i] << " " << discrete[i] << " ";
558  cout.precision(3);
559  cout << fabs(discrete[i] - continuous[i])/continuous[i] << " ";
560  cout << sqrt(fabs(normH1)/(continuous[i]+1.0)) << " ";
561  cout << sqrt(fabs(normL2[i])) << endl;
562  }
563  } // for (i = 0; i < nMax; ++i)
564  } // if (myPid == 0)
565 
566  delete[] discrete;
567  delete[] index;
568 
569  // Check the angles between exact discrete eigenvectors and computed
570 
571  myVerify.errorSubspaces(Q, Qex, M);
572 
573  delete[] vQ;
574 
575  return info;
576 
577 }
578 
579 
580 void ModeLaplace1DQ2::memoryInfo() const {
581 
582  int myPid = MyComm.MyPID();
583 
584  Epetra_RowMatrix *Mat = dynamic_cast<Epetra_RowMatrix *>(M);
585  if ((myPid == 0) && (Mat)) {
586  cout << " Total number of nonzero entries in mass matrix = ";
587  cout.width(15);
588  cout << Mat->NumGlobalNonzeros() << endl;
589  double memSize = Mat->NumGlobalNonzeros()*(sizeof(double) + sizeof(int));
590  memSize += 2*Mat->NumGlobalRows()*sizeof(int);
591  cout << " Memory requested for mass matrix per processor = (EST) ";
592  cout.precision(2);
593  cout.width(6);
594  cout.setf(ios::fixed, ios::floatfield);
595  cout << memSize/1024.0/1024.0/MyComm.NumProc() << " MB " << endl;
596  cout << endl;
597  }
598 
599  Mat = dynamic_cast<Epetra_RowMatrix *>(K);
600  if ((myPid == 0) && (Mat)) {
601  cout << " Total number of nonzero entries in stiffness matrix = ";
602  cout.width(15);
603  cout << Mat->NumGlobalNonzeros() << endl;
604  double memSize = Mat->NumGlobalNonzeros()*(sizeof(double) + sizeof(int));
605  memSize += 2*Mat->NumGlobalRows()*sizeof(int);
606  cout << " Memory requested for stiffness matrix per processor = (EST) ";
607  cout.precision(2);
608  cout.width(6);
609  cout.setf(ios::fixed, ios::floatfield);
610  cout << memSize/1024.0/1024.0/MyComm.NumProc() << " MB " << endl;
611  cout << endl;
612  }
613 
614 }
615 
616 
617 void ModeLaplace1DQ2::problemInfo() const {
618 
619  int myPid = MyComm.MyPID();
620 
621  if (myPid == 0) {
622  cout.precision(2);
623  cout.setf(ios::fixed, ios::floatfield);
624  cout << " --- Problem definition ---\n\n";
625  cout << " >> Laplace equation in 1D with homogeneous Dirichlet condition\n";
626  cout << " >> Domain = [0, " << Lx << "]\n";
627  cout << " >> Orthogonal mesh uniform per direction with Q2 elements (3 nodes)\n";
628  cout << endl;
629  cout << " Global size = " << Map->NumGlobalElements() << endl;
630  cout << endl;
631  cout << " Number of elements in [0, " << Lx << "] (X-direction): " << nX << endl;
632  cout << endl;
633  cout << " Number of interior nodes in the X-direction: " << 2*nX-1 << endl;
634  cout << endl;
635  }
636 
637 }
638 
639 
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
virtual int NumGlobalNonzeros() const =0
virtual int NumGlobalRows() const =0