CppNoddy  0.92
Loading...
Searching...
No Matches
SparseLinearEigenSystem.cpp
Go to the documentation of this file.
1/// \file SparseLinearEigenSystem.cpp
2/// Implementation for the SparseLinearEigenSystem class
3/// This class links to SLEPc to perform the solver phase.
4
5#include <vector>
6#include <set>
7#include <algorithm>
8#include <string>
9
11#include <Exceptions.h>
12#include <Types.h>
13#include <Timer.h>
14
15#ifdef SLEPC
16
17namespace CppNoddy {
18
19 template <typename _Type>
20 SparseLinearEigenSystem<_Type>::SparseLinearEigenSystem(SparseMatrix<_Type >* Aptr, SparseMatrix<_Type >* Bptr) :
21 LinearEigenSystem_base() {
22 m_region_defined = false;
23 m_guess_defined = false;
24 //
25 m_pA = Aptr;
26 m_pB = Bptr;
27 m_nev = 8;
28 m_nconv = 0;
29 m_order = (EPSWhich)7; // target magnitude is the default
30 // base class
31 m_calc_eigenvectors = true; // SLEPc methods *always* obtain eigenvecs.
32 }
33
34 template <typename _Type>
35 SparseLinearEigenSystem<_Type>::~SparseLinearEigenSystem() {
36 }
37
38 template <typename _Type>
39 unsigned SparseLinearEigenSystem<_Type>::get_nconv() const {
40 return m_nconv;
41 }
42
43 template <typename _Type>
44 void SparseLinearEigenSystem<_Type>::set_nev(unsigned n) {
45 m_nev = n;
46 }
47
48 template <typename _Type>
49 void SparseLinearEigenSystem<_Type>::set_target(std::complex<double> target) {
50 // defaults to (0,0)
51 m_shift = target;
52 }
53
54 template <typename _Type>
55 void SparseLinearEigenSystem<_Type>::set_order(std::string order_string) {
56 int flag(0);
57 if(order_string == "EPS_TARGET_MAGNITUDE") {
58 m_order=(EPSWhich)7;
59 flag=1;
60 }
61 if(order_string == "EPS_TARGET_REAL") {
62 m_order=(EPSWhich)8;
63 flag=1;
64 }
65 if(order_string == "EPS_TARGET_IMAGINARY") {
66 m_order=(EPSWhich)9;
67 flag=1;
68 }
69 //typedef enum { EPS_LARGEST_MAGNITUDE=1,
70 //EPS_SMALLEST_MAGNITUDE,
71 //EPS_LARGEST_REAL,
72 //EPS_SMALLEST_REAL,
73 //EPS_LARGEST_IMAGINARY,
74 //EPS_SMALLEST_IMAGINARY,
75 //EPS_TARGET_MAGNITUDE,
76 //EPS_TARGET_REAL,
77 //EPS_TARGET_IMAGINARY, -- only if COMPLEX
78 //EPS_ALL,
79 //EPS_WHICH_USER } EPSWhich;
80 if(flag==0) {
81 std::string problem;
82 problem = "The SparseLinearEigenSystem::set_order method has been called\n";
83 problem += "with an ordering_string that is not recognised.\n";
84 throw ExceptionExternal(problem);
85 }
86 }
87
88 template <typename _Type>
89 bool& SparseLinearEigenSystem<_Type>::region_defined() {
90 return m_region_defined;
91 }
92
93 template <typename _Type>
94 void SparseLinearEigenSystem<_Type>::set_region(const double& a, const double& b, const double& c, const double& d) {
95 m_region_defined = true;
96 m_real_left = a;
97 m_real_right = b;
98 m_imag_bottom = c;
99 m_imag_top = d;
100 }
101
102 template <typename _Type>
103 bool& SparseLinearEigenSystem<_Type>::guess_defined() {
104 return m_guess_defined;
105 }
106
107 template <typename _Type>
108 void SparseLinearEigenSystem<_Type>::set_initial_guess(const DenseVector<_Type>& guess) {
109 m_guess_defined = true;
110 m_initial_guess = guess;
111 }
112
113 template <typename _Type>
114 void SparseLinearEigenSystem<_Type >::eigensolve() {
115 // only one method available: SLEPc's generalized shift-invert solver
116 eigensolve_slepc();
117 }
118
119
120 template <typename _Type>
121 void SparseLinearEigenSystem<_Type>::eigensolve_slepc() {
122#ifndef SLEPC
123 std::string problem;
124 problem = "The SparseLinearEigenSystem::eigensolve method has been called\n";
125 problem += "but the compiler option -DSLEPC was not provided when\n";
126 problem += "the library was built.";
127 throw ExceptionExternal(problem);
128#else
129 // create the SLEPc and PETSc data types needed
130 Mat petsc_A,petsc_B;
131 PetscInt n;
132#ifdef PETSC_Z
133 // for complex PETSC we only need one (complex) vector to store eigenvec.
134 Vec petsc_x;
135#endif
136#ifdef PETSC_D
137 // for double PETSC we need separate real and imag parts for eigenvec.
138 Vec petsc_xr,petsc_xi;
139#endif
140 ST petsc_st;
141 EPS petsc_eps;
142
143 // assuming A & B are square
144 n = m_pA -> nrows();
145
146 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147 Define the matrices that define the eigensystem, Ax=lambdaBx
148 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
149
150 // we need to convert from the native sparse format to that required by SLEPc/PETSc
151
152 // define A using PETSc structures
153 MatCreate(PETSC_COMM_WORLD,&petsc_A);
154 MatSetSizes(petsc_A,PETSC_DECIDE,PETSC_DECIDE,n,n);
155 MatSetFromOptions(petsc_A);
156
157 // we ABSOLUTELY MUST pre-allocate, otherwise the performance really is AWFUL!
158 // get the number of non-zero elts in each row as a vector
159 PetscInt* all_rows_nnz = new PetscInt[ n ];
160 m_pA -> nelts_all_rows(all_rows_nnz);
161
162 // pre-allocate memory using the number of non-zero elts
163 // in each row (the 0 is ignored here)
164 MatSeqAIJSetPreallocation(petsc_A, 0, all_rows_nnz);
165 // finish the A definition
166 MatSetUp(petsc_A);
167
168
169 for(PetscInt i = 0; i<n; ++i) {
170 // move the matrix data into PETSc format 1 row at a time
171 std::size_t nelts_in_row = all_rows_nnz[i];
172 // row i has all_rows_nnz[i] elements that are non-zero, so we store their columns
173 PetscInt* cols = new PetscInt[all_rows_nnz[i]];
174 // store the non-zero elts in this row
175 PetscScalar* storage = new PetscScalar[all_rows_nnz[i]];
176 // get the data from the CppNoddy sparse matrix structure
177 m_pA -> get_row_petsc(i, storage, cols);
178 MatSetValues(petsc_A,1,&i,nelts_in_row,cols,storage,INSERT_VALUES);
179 // delete temp storage made in the conversion
180 delete[] cols;
181 delete[] storage;
182 }
183 // delete the temp storage
184 delete[] all_rows_nnz;
185
186 // MatSetValue inserted values are generally cached
187 // so we need to explicitly do final assembly
188 MatAssemblyBegin(petsc_A,MAT_FINAL_ASSEMBLY);
189 MatAssemblyEnd(petsc_A,MAT_FINAL_ASSEMBLY);
190
191
192 // configure the B matrix
193 MatCreate(PETSC_COMM_WORLD,&petsc_B);
194 // set B to be an nxn matrix
195 MatSetSizes(petsc_B,PETSC_DECIDE,PETSC_DECIDE,n,n);
196 // add any command line options
197 MatSetFromOptions(petsc_B);
198
199 // we ABSOLUTELY MUST pre-allocate, otherwise the performance really is AWFUL!
200 // get the number of non-zero elts in each row as a vector
201 all_rows_nnz = new PetscInt[ n ];
202 m_pB -> nelts_all_rows(all_rows_nnz);
203
204 // allocate memory using the number of non-zero elts in each row (the 0 is ignored here)
205 MatSeqAIJSetPreallocation(petsc_B, 0, all_rows_nnz);
206 // finish the B definition
207 MatSetUp(petsc_B);
208
209 // fill the petsc_B matrix from the CppNoddy::SparseMatrix object
210 for(PetscInt i = 0; i<n; ++i) {
211 // move the matrix data into PETSc format 1 row at a time
212 std::size_t nelts_in_row = all_rows_nnz[i];
213 // row i has all_rows_nnz[i] elements that are non-zero, so we store their columns
214 PetscInt* cols = new PetscInt[all_rows_nnz[i]];
215 // store the non-zero elts in this row
216 PetscScalar* storage = new PetscScalar[all_rows_nnz[i]];
217 // get the data from the CppNoddy sparse matrix structure
218 m_pB -> get_row_petsc(i, storage, cols);
219 MatSetValues(petsc_B,1,&i,nelts_in_row,cols,storage,INSERT_VALUES);
220 // delete temp storage made in the conversion
221 delete[] cols;
222 delete[] storage;
223 }
224 // delete the temp storage
225 delete[] all_rows_nnz;
226
227 // MatSetValue inserted values are generally cached
228 // so we need to explicitly do final assembly
229 MatAssemblyBegin(petsc_B,MAT_FINAL_ASSEMBLY);
230 MatAssemblyEnd(petsc_B,MAT_FINAL_ASSEMBLY);
231
232 // PETSc storage for the eigenvector, using A to define the size
233#ifdef PETSC_D
234 MatCreateVecs(petsc_A,NULL,&petsc_xr);
235 MatCreateVecs(petsc_A,NULL,&petsc_xi);
236#endif
237#ifdef PETSC_Z
238 MatCreateVecs(petsc_A,NULL,&petsc_x);
239#endif
240 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
241 Create the eigensolver and set various options
242 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
243
244 // create the eigensolver environment
245 EPSCreate(PETSC_COMM_WORLD,&petsc_eps);
246 // define a generalised problem with a B
247 EPSSetOperators(petsc_eps,petsc_A,petsc_B);
248 // Default is generalizzed non-Hermitian
249 EPSSetProblemType(petsc_eps,EPS_GNHEP);
250 // Method is Krylov Schur
251 EPSSetType(petsc_eps, EPSKRYLOVSCHUR);
252 // add any command line options
253 EPSSetFromOptions(petsc_eps);
254
255 // target spectrum shift - defaults to (0,0)
256#ifdef PETSC_D
257 EPSSetTarget(petsc_eps, m_shift.real());
258#endif
259#ifdef PETSC_Z
260 EPSSetTarget(petsc_eps, m_shift);
261#endif
262 // set the order of the returned ev's, as set by the get_order method.
263 EPSSetWhichEigenpairs(petsc_eps, m_order);
264 // set the number of requested ev's. Not sure if this is relevant if REGION_DEFINED
265 //EPSSetDimensions(eps,NEV,NEV+1,PETSC_DEFAULT);
266 if ( m_nev < 5 )
267 {
268 EPSSetDimensions(petsc_eps,m_nev,5,PETSC_DEFAULT);
269 }
270 else
271 {
272 EPSSetDimensions(petsc_eps,m_nev,2*m_nev,PETSC_DEFAULT);
273 }
274
275 // set tolerance and max number of iterations
276 //EPSSetTolerances(petsc_eps, 1.e-6, 20);
277 // EPSSetTrueResidual(eps, PETSC_TRUE );
278 // EPSSetConvergenceTest(eps, EPS_CONV_ABS);
279
280 // define a monitor function to view convergence (function set above)
281 // not needed: use -eps_monitor
282 // EPSMonitorSet(petsc_eps,&monitor_function, NULL, NULL);
283
284 //Vec x;
285// VecCreate(PETSC_COMM_WORLD,&petsc_x);
286// VecSetSizes(petsc_x,PETSC_DECIDE,n);
287// VecSetFromOptions(petsc_x);
288// if ( m_guess_defined ) {
289// for ( PetscInt i = 0; i < n; ++i ) {
290// #ifdef PETSC_Z
291// VecSetValue(petsc_x,i,m_initial_guess[i],INSERT_VALUES);
292// #endif
293// #ifdef PETSC_D
294// VecSetValue(petsc_x,i,m_initial_guess[i].real(),INSERT_VALUES);
295// #endif
296// }
297// #ifdef DEBUG
298// std::cout << "[DEBUG] setting initial space/guess\n";
299// #endif
300// EPSSetInitialSpace(petsc_eps,1,&petsc_x);
301// }
302
303 /*
304 Define the region containing the eigenvalues of interest
305 */
306 if(m_region_defined) {
307 RG petsc_rg;
308 EPSGetRG(petsc_eps, &petsc_rg);
309 RGSetType(petsc_rg, RGINTERVAL);
310 RGIntervalSetEndpoints(petsc_rg,m_real_left,m_real_right,
311 m_imag_bottom,m_imag_top);
312 }
313
314 // get access to the spectral transformation
315 EPSGetST(petsc_eps, &petsc_st);
316 // we have to use "STSINVERT" instead of the default, because B is
317 // typically singular for all problems I'm interested in.
318 STSetType(petsc_st, STSINVERT);
319
320 // KSP is the linear solver object of the PETSc library
321 KSP petsc_ksp;
322 STGetKSP(petsc_st, &petsc_ksp);
323
324 KSPSetOperators(petsc_ksp,petsc_A,petsc_A);
325 // set to precondition only
326 KSPSetType(petsc_ksp, KSPPREONLY);
327 // get a preconditioner object
328 PC petsc_pc;
329 KSPGetPC(petsc_ksp,&petsc_pc);
330 // set it to LU factorization is precondition
331 PCSetType(petsc_pc,PCLU);
332 PCFactorSetMatSolverType(petsc_pc,MATSOLVERMUMPS);
333 PCFactorSetUpMatSolverType(petsc_pc);
334
335 // create m_petsc_F
336 Mat petsc_F;
337 PCFactorGetMatrix(petsc_pc,&petsc_F);
338
339 KSPSetUp(petsc_ksp);
340
341 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
342 Solve the eigensystem
343 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
344
345 EPSSolve(petsc_eps);
346 // EPSGetDimensions(eps,&nev,NULL,NULL);
347 // update the NEV private data with the number of returned eigenvalues
348 // NEV = (unsigned)nev; // is this always the same as the input nev?
349
350 // Optional: Get some information from the solver and display it
351 #ifdef DEBUG
352 PetscInt its, lits, maxit;
353 EPSType petsc_eps_type;
354 PetscReal tol;
355 //
356 std::cout << "[DEBUG] Target location for eigenvalue = " << m_shift << "\n";
357 std::cout << "[DEBUG] Target ordering of returned eigenvalues (see EPSWhich enum) = " << m_order << "\n";
358 #endif
359 //
360 #ifdef DEBUG
361 EPSGetIterationNumber(petsc_eps,&its);
362 PetscPrintf(PETSC_COMM_WORLD,"[DEBUG] Number of iterations of the method: %D\n",its);
363 EPSGetST(petsc_eps,&petsc_st);
364 STGetKSP(petsc_st,&petsc_ksp);
365 KSPGetTotalIterations(petsc_ksp,&lits);
366 PetscPrintf(PETSC_COMM_WORLD,"[DEBUG] Number of linear iterations of the method: %D\n",lits);
367 EPSGetType(petsc_eps,&petsc_eps_type);
368 PetscPrintf(PETSC_COMM_WORLD,"[DEBUG] Solution method: %s\n\n",petsc_eps_type);
369 PetscPrintf(PETSC_COMM_WORLD,"[DEBUG] Number of requested eigenvalues: %D\n",m_nev);
370 EPSGetTolerances(petsc_eps,&tol,&maxit);
371 PetscPrintf(PETSC_COMM_WORLD,"[DEBUG] Stopping condition: tol=%.4g, maxit=%D\n",(double)tol,maxit);
372 #endif
373 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
374 Display solution and clean up
375 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
376
377 PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD,PETSC_VIEWER_ASCII_INFO_DETAIL);
378 //EPSReasonView(petsc_eps,PETSC_VIEWER_STDOUT_WORLD); // deprecated in 3.14
379 EPSConvergedReasonView(petsc_eps,PETSC_VIEWER_STDOUT_WORLD); // replaces EPSReasonView
380 EPSErrorView(petsc_eps,EPS_ERROR_ABSOLUTE,PETSC_VIEWER_STDOUT_WORLD);
381 PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD);
382 //#endif
383
384 /*
385 Save eigenvectors, if requested
386 */
387 PetscInt nconv;
388 EPSGetConverged(petsc_eps,&nconv);
389 // store it in the class
390 m_nconv = (unsigned)nconv;
391 // create a complex eigenvalue vector
392 m_all_eigenvalues = DenseVector<D_complex>(m_nconv, 0.0);
393 // complex eigenvector matrix
394 m_all_eigenvectors = DenseMatrix<D_complex>(m_nconv, n, 0.0);
395 //
396 for(unsigned i=0; i<m_nconv; i++) {
397#ifdef PETSC_Z
398 EPSGetEigenvalue(petsc_eps,i,&m_all_eigenvalues[i],NULL);
399 std::cout << m_all_eigenvalues[i] << "\n";
400#endif
401#ifdef PETSC_D
402 double lambda_r,lambda_i;
403 EPSGetEigenvalue(petsc_eps,i,&lambda_r,&lambda_i);
404 m_all_eigenvalues[i] = D_complex(lambda_r, lambda_i);
405#endif
406
407 if(m_calc_eigenvectors) {
408#ifdef PETSC_D
409 // get the i-th eigenvector from SLEPc
410 EPSGetEigenvector(petsc_eps,i,petsc_xr,petsc_xi);
411 // convert to a more accessible data structure
412 PetscScalar* arrayr;
413 VecGetArray1d(petsc_xr, n, 0, &arrayr);
414 PetscScalar* arrayi;
415 VecGetArray1d(petsc_xi, n, 0, &arrayi);
416 for(int j=0; j<n; ++j) {
417 m_all_eigenvectors[i][j]=D_complex(arrayr[j], arrayi[j]);
418 }
419 // documentation says to "restore", though it might not matter as we're done with it now
420 VecRestoreArray1d(petsc_xr, n, 0, &arrayr);
421 VecRestoreArray1d(petsc_xi, n, 0, &arrayi);
422#endif
423#ifdef PETSC_Z
424 // get the i-th eigenvector from SLEPc
425 EPSGetEigenvector(petsc_eps,i,petsc_x,NULL);
426 // convert to a more accessible data structure
427 PetscScalar* array;
428 VecGetArray1d(petsc_x, n, 0, &array);
429 for(int j=0; j<n; ++j) {
430 m_all_eigenvectors[i][j]=array[j];
431 }
432 // documentation says to "restore", though it might not matter as we're done with it now
433 VecRestoreArray1d(petsc_x, n, 0, &array);
434#endif
435 }
436 }
437
438 /*
439 Free work space
440 */
441
442 EPSDestroy(&petsc_eps);
443 MatDestroy(&petsc_A);
444 MatDestroy(&petsc_B);
445#ifdef PETSC_D
446 VecDestroy(&petsc_xr);
447 VecDestroy(&petsc_xi);
448#endif
449#ifdef PETSC_Z
450 VecDestroy(&petsc_x);
451#endif
452
453#endif
454 }
455
456
457#ifdef PETSC_Z
458 template class SparseLinearEigenSystem<D_complex>
459 ;
460#endif
461#ifdef PETSC_D
462 template class SparseLinearEigenSystem<double>
463 ;
464#endif
465
466
467} // end namespace
468
469#endif
The collection of CppNoddy exceptions.
Specification of the sparse linear eigensystem class.
A spec for the CppNoddy Timer object.
A collection of OO numerical routines aimed at simple (typical) applied problems in continuum mechani...
std::complex< double > D_complex
A complex double precision number using std::complex.
Definition: Types.h:98

© 2012

R.E. Hewitt