HermesCommon  2.0
petsc_solver.cpp
Go to the documentation of this file.
1 // This file is part of HermesCommon
2 //
3 // Copyright (c) 2009 hp-FEM group at the University of Nevada, Reno (UNR).
4 // Email: hpfem-group@unr.edu, home page: http://hpfem.org/.
5 //
6 // Hermes2D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published
8 // by the Free Software Foundation; either version 2 of the License,
9 // or (at your option) any later version.
10 //
11 // Hermes2D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with Hermes2D; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "config.h"
23 #ifdef WITH_PETSC
24 #include "petsc_solver.h"
25 #include "callstack.h"
26 
28 
29 namespace Hermes
30 {
31  namespace Algebra
32  {
33  static int num_petsc_objects = 0;
34 
35  inline void vec_get_value(Vec x, PetscInt ni, const PetscInt ix[], std::complex<double> y[])
36  {
37  VecGetValues(x, ni, ix, y);
38  }
39 
40  void vec_get_value(Vec x, PetscInt ni, const PetscInt ix[], double y[])
41  {
42  PetscScalar *py = new PetscScalar[ni];
43  VecGetValues(x, ni, ix, py);
44  for (int i = 0;i<ni;i++)y[i] = py[i].real();
45  delete [] py;
46  }
47 
48  int remove_petsc_object()
49  {
50  PetscTruth petsc_initialized, petsc_finalized;
51  int ierr = PetscFinalized(&petsc_finalized); CHKERRQ(ierr);
52  ierr = PetscInitialized(&petsc_initialized); CHKERRQ(ierr);
53  if(petsc_finalized == PETSC_TRUE || petsc_initialized == PETSC_FALSE)
54  // This should never happen here.
55  return -1;
56 
57  if(--num_petsc_objects == 0)
58  {
59  int ierr = PetscFinalize();
60  CHKERRQ(ierr);
61  this->info("PETSc finalized. No more PETSc usage allowed until application restart.");
62  }
63  }
64 
65  int add_petsc_object()
66  {
67  int ierr;
68  PetscTruth petsc_initialized, petsc_finalized;
69  ierr = PetscFinalized(&petsc_finalized); CHKERRQ(ierr);
70 
71  if(petsc_finalized == PETSC_TRUE)
72  throw Hermes::Exceptions::Exception("PETSc cannot be used once it has been finalized. You must restart the application.");
73 
74  ierr = PetscInitialized(&petsc_initialized); CHKERRQ(ierr);
75 
76  if(petsc_initialized != PETSC_TRUE)
77  {
78  ierr = PetscInitializeNoArguments();
79  CHKERRQ(ierr);
80  }
81 
82  num_petsc_objects++;
83  }
84 
85  template<typename Scalar>
86  PetscMatrix<Scalar>::PetscMatrix()
87  {
88  inited = false;
89  add_petsc_object();
90  }
91 
92  template<typename Scalar>
93  PetscMatrix<Scalar>::~PetscMatrix()
94  {
95  free();
96  remove_petsc_object();
97  }
98 
99  template<typename Scalar>
100  void PetscMatrix<Scalar>::alloc()
101  {
102  assert(this->pages != NULL);
103 
104  // calc nnz
105  int *nnz_array = new int[this->size];
106 
107  // fill in nnz_array
108  int aisize = this->get_num_indices();
109  int *ai = new int[aisize];
110 
111  // sort the indices and remove duplicities, insert into ai
112  int pos = 0;
113  for (unsigned int i = 0; i < this->size; i++)
114  {
115  nnz_array[i] = sort_and_store_indices(this->pages[i], ai + pos, ai + aisize);
116  pos += nnz_array[i];
117  }
118  // stote the number of nonzeros
119  nnz = pos;
120  delete [] this->pages; this->pages = NULL;
121  delete [] ai;
122 
123  //
124  MatCreateSeqAIJ(PETSC_COMM_SELF, this->size, this->size, 0, nnz_array, &matrix);
125  // MatSetOption(matrix, MAT_ROW_ORIENTED);
126  // MatSetOption(matrix, MAT_ROWS_SORTED);
127 
128  delete [] nnz_array;
129 
130  inited = true;
131  }
132 
133  template<typename Scalar>
134  void PetscMatrix<Scalar>::free()
135  {
136  if(inited) MatDestroy(matrix);
137  inited = false;
138  }
139 
140  template<typename Scalar>
141  void PetscMatrix<Scalar>::finish()
142  {
143  MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY);
144  MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY);
145  }
146 
147  template<>
148  double PetscMatrix<double>::get(unsigned int m, unsigned int n)
149  {
150  double v = 0.0;
151  PetscScalar pv;
152  MatGetValues(matrix, 1, (PetscInt*) &m, 1, (PetscInt*) &n, &pv);
153  v = pv.real();
154  return v;
155  }
156 
157  template<>
158  std::complex<double> PetscMatrix<std::complex<double> >::get(unsigned int m, unsigned int n)
159  {
160  std::complex<double> v = 0.0;
161  MatGetValues(matrix, 1, (PetscInt*) &m, 1, (PetscInt*) &n, &v);
162  return v;
163  }
164 
165  template<typename Scalar>
166  void PetscMatrix<Scalar>::zero()
167  {
168  MatZeroEntries(matrix);
169  }
170 
171  inline PetscScalar to_petsc(double x)
172  {
173  return std::complex<double>(x, 0);
174  }
175 
176  inline PetscScalar to_petsc(std::complex<double> x)
177  {
178  return x;
179  }
180 
181  template<typename Scalar>
182  void PetscMatrix<Scalar>::add(unsigned int m, unsigned int n, Scalar v)
183  {
184  if(v != 0.0)
185  { // ignore zero values.
186  #pragma omp critical (PetscMatrix_add)
187  MatSetValue(matrix, (PetscInt) m, (PetscInt) n, to_petsc(v), ADD_VALUES);
188  }
189  }
190 
192 
193  template<typename Scalar>
194  void PetscMatrix<Scalar>::add_to_diagonal(Scalar v)
195  {
196  for (unsigned int i = 0; i<this->size; i++)
197  {
198  add(i, i, v);
199  }
200  };
201 
202  template<typename Scalar>
203  void PetscMatrix<Scalar>::add(unsigned int m, unsigned int n, Scalar **mat, int *rows, int *cols)
204  {
206  // row and cols for -1)
207  for (unsigned int i = 0; i < m; i++) // rows
208  for (unsigned int j = 0; j < n; j++) // cols
209  if(rows[i] >= 0 && cols[j] >= 0) // not Dir. dofs.
210  add(rows[i], cols[j], mat[i][j]);
211  }
212 
213  template<typename Scalar>
214  bool PetscMatrix<Scalar>::dump(FILE *file, const char *var_name, EMatrixDumpFormat fmt, char* number_format)
215  {
216  switch (fmt)
217  {
218  case DF_MATLAB_SPARSE: //only to stdout
219  PetscViewer viewer = PETSC_VIEWER_STDOUT_SELF;
220  PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
221  MatView(matrix, viewer);
222  return true;
223  }
224  return false;
225  }
226 
227  template<typename Scalar>
228  unsigned int PetscMatrix<Scalar>::get_matrix_size() const
229  {
230  return this->size;
231  }
232 
233  template<typename Scalar>
234  unsigned int PetscMatrix<Scalar>::get_nnz() const
235  {
236  return nnz;
237  }
238 
239  template<typename Scalar>
240  double PetscMatrix<Scalar>::get_fill_in() const
241  {
242  return (double) nnz / ((double)this->size*this->size);
243  }
244 
245  template<typename Scalar>
246  void PetscMatrix<Scalar>::multiply_with_vector(Scalar* vector_in, Scalar* vector_out)
247  {
248  for (unsigned int i = 0;i<this->size;i++)
249  {
250  vector_out[i] = 0;
251  for (unsigned int j = 0; j<this->size; j++)
252  {
253  vector_out[i] +=vector_in[j]*get(i, j);
254  }
255  }
256  }
257 
258  template<typename Scalar>
259  void PetscMatrix<Scalar>::add_matrix(PetscMatrix<Scalar>* mat)
260  {
261  MatAXPY(matrix, 1, mat->matrix, DIFFERENT_NONZERO_PATTERN); //matrix = 1*mat + matrix (matrix and mat have different nonzero structure)
262  }
263 
264  template<typename Scalar>
265  void PetscMatrix<Scalar>::add_to_diagonal_blocks(int num_stages, PetscMatrix<Scalar>* mat)
266  {
267  int ndof = mat->get_size();
268  if(this->get_size() != (unsigned int) num_stages * ndof)
269  throw Hermes::Exceptions::Exception("Incompatible matrix sizes in PetscMatrix<Scalar>::add_to_diagonal_blocks()");
270 
271  for (int i = 0; i < num_stages; i++)
272  {
273  this->add_as_block(ndof*i, ndof*i, mat);
274  }
275  }
276 
277  template<typename Scalar>
278  void PetscMatrix<Scalar>::add_sparse_to_diagonal_blocks(int num_stages, SparseMatrix<Scalar>* mat)
279  {
280  add_to_diagonal_blocks(num_stages, static_cast<PetscMatrix<Scalar>*>(mat));
281  }
282 
283  template<typename Scalar>
284  void PetscMatrix<Scalar>::add_as_block(unsigned int i, unsigned int j, PetscMatrix<Scalar>* mat)
285  {
286  if((this->get_size() < i + mat->get_size() )||(this->get_size() < j + mat->get_size() ))
287  throw Hermes::Exceptions::Exception("Incompatible matrix sizes in PetscMatrix<Scalar>::add_as_block()");
288  unsigned int block_size = mat->get_size();
289  for (unsigned int r = 0;r<block_size;r++)
290  {
291  for (unsigned int c = 0;c<block_size;c++)
292  {
293  this->add(i + r, j + c, mat->get(r, c));
294  }
295  }
296  }
297 
298  // Multiplies matrix with a Scalar.
299 
300  template<typename Scalar>
301  void PetscMatrix<Scalar>::multiply_with_Scalar(Scalar value)
302  {
303  MatScale(matrix, to_petsc(value));
304  }
305  // Creates matrix in PETSC format using size, nnz, and the three arrays.
306 
307  template<typename Scalar>
308  void PetscMatrix<Scalar>::create(unsigned int size, unsigned int nnz, int* ap, int* ai, Scalar* ax)
309  {
310  this->size = size;
311  this->nnz = nnz;
312  PetscScalar* pax = new PetscScalar[nnz];
313  for (unsigned i = 0;i<nnz;i++)
314  pax[i] = to_petsc(ax[i]);
315  MatCreateSeqAIJWithArrays(PETSC_COMM_SELF, size, size, ap, ai, pax, &matrix);
316  delete pax;
317  }
318 
319  template<typename Scalar>
320  PetscMatrix<Scalar>* PetscMatrix<Scalar>::duplicate()
321  {
322  PetscMatrix<Scalar>*ptscmatrix = new PetscMatrix<Scalar>();
323  MatDuplicate(matrix, MAT_COPY_VALUES, &(ptscmatrix->matrix));
324  ptscmatrix->size = this->size;
325  ptscmatrix->nnz = nnz;
326  return ptscmatrix;
327  };
328 
329  template<typename Scalar>
330  PetscVector<Scalar>::PetscVector()
331  {
332  inited = false;
333  add_petsc_object();
334  }
335 
336  template<typename Scalar>
337  PetscVector<Scalar>::~PetscVector()
338  {
339  free();
340  remove_petsc_object();
341  }
342 
343  template<typename Scalar>
344  void PetscVector<Scalar>::alloc(unsigned int n)
345  {
346  free();
347  this->size = n;
348  VecCreateSeq(PETSC_COMM_SELF, this->size, &vec);
349  inited = true;
350  }
351 
352  template<typename Scalar>
353  void PetscVector<Scalar>::free()
354  {
355  if(inited) VecDestroy(vec);
356  inited = false;
357  }
358 
359  template<typename Scalar>
360  void PetscVector<Scalar>::finish()
361  {
362  VecAssemblyBegin(vec);
363  VecAssemblyEnd(vec);
364  }
365 
366  template<>
367  double PetscVector<double>::get(unsigned int idx)
368  {
369  double y = 0;
370  PetscScalar py;
371  VecGetValues(vec, 1, (PetscInt*) &idx, &py);
372  y = py.real();
373  return y;
374  }
375 
376  template<>
377  std::complex<double> PetscVector<std::complex<double> >::get(unsigned int idx)
378  {
379  std::complex<double> y = 0;
380  VecGetValues(vec, 1, (PetscInt*) &idx, &y);
381  return y;
382  }
383 
384  template<typename Scalar>
385  void PetscVector<Scalar>::extract(Scalar *v) const
386  {
387  int *idx = new int[this->size];
388  for (unsigned int i = 0; i < this->size; i++) idx[i] = i;
389  vec_get_value(vec, this->size, idx, v);
390  delete [] idx;
391  }
392 
393  template<typename Scalar>
394  void PetscVector<Scalar>::zero()
395  {
396  VecZeroEntries(vec);
397  }
398 
399  template<typename Scalar>
400  void PetscVector<Scalar>::change_sign()
401  {
402  PetscScalar* y = new PetscScalar[this->size];
403  int *idx = new int[this->size];
404  for (unsigned int i = 0; i < this->size; i++) idx[i] = i;
405  VecGetValues(vec, this->size, idx, y);
406  for (unsigned int i = 0; i < this->size; i++) y[i] *= -1.;
407  VecSetValues(vec, this->size, idx, y, INSERT_VALUES);
408  delete [] y;
409  delete [] idx;
410  }
411 
412  template<typename Scalar>
413  void PetscVector<Scalar>::set(unsigned int idx, Scalar y)
414  {
415  VecSetValue(vec, idx, to_petsc(y), INSERT_VALUES);
416  }
417 
418  template<typename Scalar>
419  void PetscVector<Scalar>::add(unsigned int idx, Scalar y)
420  {
421 #pragma omp critical (PetscVector_add)
422  VecSetValue(vec, idx, to_petsc(y), ADD_VALUES);
423  }
424 
425  template<typename Scalar>
426  void PetscVector<Scalar>::add(unsigned int n, unsigned int *idx, Scalar *y)
427  {
428  PetscScalar py;
429  for (unsigned int i = 0; i < n; i++)
430  {
431  VecSetValue(vec, idx[i], to_petsc(y[i]), ADD_VALUES);
432  }
433  }
434 
435  template<typename Scalar>
436  void PetscVector<Scalar>::add_vector(Vector<Scalar>* vec)
437  {
438  assert(this->length() == vec->length());
439  for (unsigned int i = 0; i < this->length(); i++) this->add(i, vec->get(i));
440  }
441 
442  template<typename Scalar>
443  void PetscVector<Scalar>::add_vector(Scalar* vec)
444  {
445  for (unsigned int i = 0; i < this->length(); i++) this->add(i, vec[i]);
446  }
447 
448  template<typename Scalar>
449  bool PetscVector<Scalar>::dump(FILE *file, const char *var_name, EMatrixDumpFormat fmt, char* number_format)
450  {
451  switch (fmt)
452  {
453  case DF_MATLAB_SPARSE: //only to stdout
454  PetscViewer viewer = PETSC_VIEWER_STDOUT_SELF;
455  PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
456  VecView(vec, viewer);
457  return true;
458  }
459  return false;
460  }
461 
462  template class HERMES_API PetscMatrix<double>;
463  template class HERMES_API PetscMatrix<std::complex<double> >;
464  template class HERMES_API PetscVector<double>;
465  template class HERMES_API PetscVector<std::complex<double> >;
466  }
467  namespace Solvers
468  {
469  template<typename Scalar>
470  PetscLinearMatrixSolver<Scalar>::PetscLinearMatrixSolver(PetscMatrix<Scalar> *mat, PetscVector<Scalar> *rhs)
471  : DirectSolver<Scalar>(), m(mat), rhs(rhs)
472  {
473  add_petsc_object();
474  }
475 
476  template<typename Scalar>
477  PetscLinearMatrixSolver<Scalar>::~PetscLinearMatrixSolver()
478  {
479  remove_petsc_object();
480  }
481 
482  template<typename Scalar>
483  bool PetscLinearMatrixSolver<Scalar>::get_matrix_size()
484  {
485  return m->size();
486  }
487 
488  template<typename Scalar>
489  bool PetscLinearMatrixSolver<Scalar>::solve()
490  {
491  assert(m != NULL);
492  assert(rhs != NULL);
493 
494  PetscErrorCode ec;
495  KSP ksp;
496  Vec x;
497 
498  this->tick();
499 
500  KSPCreate(PETSC_COMM_WORLD, &ksp);
501 
502  KSPSetOperators(ksp, m->matrix, m->matrix, DIFFERENT_NONZERO_PATTERN);
503  KSPSetFromOptions(ksp);
504  VecDuplicate(rhs->vec, &x);
505 
506  ec = KSPSolve(ksp, rhs->vec, x);
507  if(ec) return false;
508 
509  this->tick();
510  this->time = this->accumulated();
511 
512  // allocate memory for solution vector
513  delete [] this->sln;
514  this->sln = new Scalar[m->size];
515  memset(this->sln, 0, m->size * sizeof(Scalar));
516 
517  // index map vector (basic serial code uses the map sln[i] = x[i] for all dofs.
518  int *idx = new int[m->size];
519  for (unsigned int i = 0; i < m->size; i++) idx[i] = i;
520 
521  // copy solution to the output solution vector
522  vec_get_value(x, m->size, idx, this->sln);
523  delete [] idx;
524 
525  KSPDestroy(ksp);
526  VecDestroy(x);
527 
528  return true;
529  }
530 
531  template class HERMES_API PetscLinearMatrixSolver<double>;
532  template class HERMES_API PetscLinearMatrixSolver<std::complex<double> >;
533  }
534 }
535 #endif