Flow123d
linsys_PETSC.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2007 Technical University of Liberec. All rights reserved.
4  *
5  * Please make a following refer to Flow123d on your project site if you use the program for any purpose,
6  * especially for academic research:
7  * Flow123d, Research Centre: Advanced Remedial Technologies, Technical University of Liberec, Czech Republic
8  *
9  * This program is free software; you can redistribute it and/or modify it under the terms
10  * of the GNU General Public License version 3 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along with this program; if not,
17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
18  *
19  *
20  * $Id: la_linsys.hh 1299 2011-08-23 21:42:50Z jakub.sistek $
21  * $Revision: 1299 $
22  * $LastChangedBy: jakub.sistek $
23  * $LastChangedDate: 2011-08-23 23:42:50 +0200 (Tue, 23 Aug 2011) $
24  *
25  * @file
26  * @brief Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construction
27  * @author Jakub Sistek
28  *
29  *
30  */
31 
32 // derived from base linsys
33 #include "la/linsys_PETSC.hh"
34 #include "petscvec.h"
35 #include "petscksp.h"
36 #include "petscmat.h"
37 
38 #include <boost/bind.hpp>
39 
40 namespace it = Input::Type;
41 
42 it::Record LinSys_PETSC::input_type = it::Record("Petsc", "Solver setting.")
44  .declare_key("a_tol", it::Double(0.0), it::Default("1.0e-9"), "Absolute residual tolerance.")
45  .declare_key("options", it::String(), it::Default(""), "Options passed to PETSC before creating KSP instead of default setting.");
46 
47 
49  : LinSys( rows_ds ),
50  matrix_(0),
51  init_guess_nonzero(false)
52 {
53  // set type
54  //type = LinSys::PETSC;
55 
56  // create PETSC vectors:
57  PetscErrorCode ierr;
58  // rhs
59  v_rhs_= new double[ rows_ds_->lsize() + 1 ];
60  ierr = VecCreateMPIWithArray( comm_, 1, rows_ds_->lsize(), PETSC_DECIDE, v_rhs_, &rhs_ ); CHKERRV( ierr );
61  ierr = VecZeroEntries( rhs_ ); CHKERRV( ierr );
62 
63  params_ = "";
64  matrix_ = NULL;
65  solution_precision_ = std::numeric_limits<double>::infinity();
66  matrix_changed_ = true;
67  rhs_changed_ = true;
68 }
69 
71  : LinSys(other), params_(other.params_), v_rhs_(NULL), solution_precision_(solution_precision_)
72 {
73  MatCopy(other.matrix_, matrix_, DIFFERENT_NONZERO_PATTERN);
74  VecCopy(other.rhs_, rhs_);
75  VecCopy(other.on_vec_, on_vec_);
76  VecCopy(other.off_vec_, off_vec_);
77 }
78 
80 {
81  PetscErrorCode ierr;
82 
83  ierr = VecCreateMPI( comm_, rows_ds_->lsize(), PETSC_DECIDE, &(on_vec_) ); CHKERRV( ierr );
84  ierr = VecDuplicate( on_vec_, &(off_vec_) ); CHKERRV( ierr );
85  status_ = ALLOCATE;
86 }
87 
89 {
90  switch ( status_ ) {
91  case ALLOCATE:
92  this->preallocate_matrix( );
93  break;
94  case INSERT:
95  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
96  break;
97  case ADD:
98  case DONE:
99  break;
100  default:
101  ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
102  }
103  status_ = ADD;
104 }
105 
107 {
108  switch ( status_ ) {
109  case ALLOCATE:
110  this->preallocate_matrix();
111  break;
112  case ADD:
113  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
114  break;
115  case INSERT:
116  case DONE:
117  break;
118  default:
119  ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
120  }
121  status_ = INSERT;
122 }
123 
124 void LinSys_PETSC::mat_set_values( int nrow, int *rows, int ncol, int *cols, double *vals )
125 {
126  PetscErrorCode ierr;
127 
128  // here vals would need to be converted from double to PetscScalar if it was ever something else than double :-)
129  switch (status_) {
130  case INSERT:
131  case ADD:
132  ierr = MatSetValues(matrix_,nrow,rows,ncol,cols,vals,(InsertMode)status_); CHKERRV( ierr );
133  break;
134  case ALLOCATE:
135  this->preallocate_values(nrow,rows,ncol,cols);
136  break;
137  default: DBGMSG("LS SetValues with non allowed insert mode.\n");
138  }
139 
140  matrix_changed_ = true;
141 }
142 
143 void LinSys_PETSC::rhs_set_values( int nrow, int *rows, double *vals )
144 {
145  PetscErrorCode ierr;
146 
147  switch (status_) {
148  case INSERT:
149  case ADD:
150  ierr = VecSetValues(rhs_,nrow,rows,vals,(InsertMode)status_); CHKERRV( ierr );
151  break;
152  case ALLOCATE:
153  break;
154  default: ASSERT(false, "LinSys's status disallow set values.\n");
155  }
156 
157  rhs_changed_ = true;
158 }
159 
160 void LinSys_PETSC::preallocate_values(int nrow,int *rows,int ncol,int *cols)
161 {
162  int i,j;
163  int col;
164  PetscInt row;
165 
166  for (i=0; i<nrow; i++) {
167  row=rows[i];
168  for(j=0; j<ncol; j++) {
169  col = cols[j];
170  if (rows_ds_->get_proc(row) == rows_ds_->get_proc(col))
171  VecSetValue(on_vec_,row,1.0,ADD_VALUES);
172  else
173  VecSetValue(off_vec_,row,1.0,ADD_VALUES);
174  }
175  }
176 }
177 
179 {
180  ASSERT(status_ == ALLOCATE, "Linear system has to be in ALLOCATE status.");
181 
182  PetscErrorCode ierr;
183  PetscInt *on_nz, *off_nz;
184  PetscScalar *on_array, *off_array;
185 
186  // assembly and get values from counting vectors, destroy them
187  VecAssemblyBegin(on_vec_);
188  VecAssemblyBegin(off_vec_);
189 
190  on_nz = new PetscInt[ rows_ds_->lsize() ];
191  off_nz = new PetscInt[ rows_ds_->lsize() ];
192 
193  VecAssemblyEnd(on_vec_);
194  VecAssemblyEnd(off_vec_);
195 
196  VecGetArray( on_vec_, &on_array );
197  VecGetArray( off_vec_, &off_array );
198 
199  for ( int i=0; i<rows_ds_->lsize(); i++ ) {
200  on_nz[i] = static_cast<PetscInt>( on_array[i]+0.1 ); // small fraction to ensure correct rounding
201  off_nz[i] = static_cast<PetscInt>( off_array[i]+0.1 );
202  }
203 
204  VecRestoreArray(on_vec_,&on_array);
205  VecRestoreArray(off_vec_,&off_array);
206  VecDestroy(&on_vec_);
207  VecDestroy(&off_vec_);
208 
209  // create PETSC matrix with preallocation
210  if (matrix_ != NULL)
211  {
212  ierr = MatDestroy(&matrix_); CHKERRV( ierr );
213  }
214  ierr = MatCreateAIJ(PETSC_COMM_WORLD, rows_ds_->lsize(), rows_ds_->lsize(), PETSC_DETERMINE, PETSC_DETERMINE,
215  0, on_nz, 0, off_nz, &matrix_); CHKERRV( ierr );
216 
217  if (symmetric_) MatSetOption(matrix_, MAT_SYMMETRIC, PETSC_TRUE);
218  MatSetOption(matrix_, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
219 
220  delete[] on_nz;
221  delete[] off_nz;
222 }
223 
225 {
226  MatAssemblyType assemblyType = MAT_FINAL_ASSEMBLY;
227  this->finish_assembly( assemblyType );
228 }
229 
230 void LinSys_PETSC::finish_assembly( MatAssemblyType assembly_type )
231 {
232  PetscErrorCode ierr;
233 
234  if (status_ == ALLOCATE) {
235  xprintf(Warn, "Finalizing linear system without setting values.\n");
236  this->preallocate_matrix();
237  }
238  ierr = MatAssemblyBegin(matrix_, assembly_type); CHKERRV( ierr );
239  ierr = VecAssemblyBegin(rhs_); CHKERRV( ierr );
240  ierr = MatAssemblyEnd(matrix_, assembly_type); CHKERRV( ierr );
241  ierr = VecAssemblyEnd(rhs_); CHKERRV( ierr );
242 
243  if (assembly_type == MAT_FINAL_ASSEMBLY) status_ = DONE;
244 
245  matrix_changed_ = true;
246  rhs_changed_ = true;
247 }
248 
249 void LinSys_PETSC::apply_constrains( double scalar )
250 {
251  PetscErrorCode ierr;
252 
253  // check that system matrix is assembled
254  ASSERT ( status_ == DONE, "System matrix and right-hand side are not assembled when applying constraints." );
255 
256  // number of constraints
257  PetscInt numConstraints = static_cast<PetscInt>( constraints_.size() );
258 
259  // Additional multiplier for numerical reasons (criterion to be established)
260  const PetscScalar diagScalar = static_cast<PetscScalar>( scalar );
261 
262  std::vector<PetscInt> globalDofs;
264 
265  // Constraint iterators
266  ConstraintVec_::const_iterator cIter = constraints_.begin( );
267  ConstraintVec_::const_iterator cEnd = constraints_.end( );
268  // collect global dof indices and the correpsonding values
269  for ( ; cIter != cEnd; ++cIter ) {
270  globalDofs.push_back( static_cast<PetscInt>( cIter -> first ) );
271  values.push_back( static_cast<PetscScalar>( cIter -> second ) * diagScalar );
272  }
273 
274  // prepare pointers to be passed to PETSc
275  PetscInt * globalDofPtr = this->makePetscPointer_( globalDofs );
276  PetscScalar * valuePtr = this->makePetscPointer_( values );
277 
278  // set matrix rows to zero
279  ierr = MatZeroRows( matrix_, numConstraints, globalDofPtr, diagScalar, PETSC_NULL, PETSC_NULL ); CHKERRV( ierr );
280  matrix_changed_ = true;
281 
282  // set RHS entries to values (crashes if called with NULL pointers)
283  if ( numConstraints ) {
284  ierr = VecSetValues( rhs_, numConstraints, globalDofPtr, valuePtr, INSERT_VALUES ); CHKERRV( ierr );
285  rhs_changed_ = true;
286  }
287 
288  // perform communication in the rhs vector
289  ierr = VecAssemblyBegin( rhs_ ); CHKERRV( ierr );
290  ierr = VecAssemblyEnd( rhs_ ); CHKERRV( ierr );
291 }
292 
293 
295 {
296  init_guess_nonzero = set_nonzero;
297 }
298 
299 
301 {
302  PetscErrorCode ierr;
303 
304  KSP system;
305  KSPConvergedReason reason;
306 
307  const char *petsc_dflt_opt;
308  //const char *petsc_str;
309  int nits;
310 
311  // -mat_no_inode ... inodes are usefull only for
312  // vector problems e.g. MH without Schur complement reduction
313  if (rows_ds_->np() > 1) {
314  // parallel setting
315  if (this->is_positive_definite())
316  petsc_dflt_opt="-ksp_type bcgs -ksp_diagonal_scale_fix -pc_type asm -pc_asm_overlap 4 -sub_pc_type ilu -sub_pc_factor_levels 3 -sub_pc_factor_fill 6.0";
317  //petsc_dflt_opt="-ksp_type preonly -pc_type cholesky -pc_factor_mat_solver_package mumps -mat_mumps_sym 1";
318  // -ksp_type preonly -pc_type lu
319  else
320  petsc_dflt_opt="-ksp_type bcgs -ksp_diagonal_scale_fix -pc_type asm -pc_asm_overlap 4 -sub_pc_type ilu -sub_pc_factor_levels 3";
321 
322  }
323  else {
324  // serial setting
325  if (this->is_positive_definite())
326  petsc_dflt_opt="-ksp_type cg -pc_type ilu -pc_factor_levels 3 -ksp_diagonal_scale_fix -pc_factor_shift_positive_definite -pc_factor_fill 6.0";
327  else
328  petsc_dflt_opt="-ksp_type bcgs -pc_type ilu -pc_factor_levels 5 -ksp_diagonal_scale_fix -pc_factor_shift_positive_definite";
329  }
330 
331  if (params_ == "") params_ = petsc_dflt_opt;
332  //petsc_str = params.c_str();
333  xprintf(MsgLog,"inserting petsc options: %s\n",params_.c_str());
334  PetscOptionsInsertString(params_.c_str()); // overwrites previous options values
335  //xfree(petsc_str);
336 
337  ierr = MatSetOption( matrix_, MAT_USE_INODES, PETSC_FALSE );
338 
339  ierr = KSPCreate( comm_, &system );
340  ierr = KSPSetOperators(system, matrix_, matrix_, DIFFERENT_NONZERO_PATTERN);
341 
342  // TODO take care of tolerances - shall we support both input file and command line petsc setting
343  //double solver_accurany = OptGetDbl("Solver","Solver_accurancy","1.0e-7");
344  //double r_tol = OptGetDbl("Solver", "r_tol", "-1" );
345  //if (r_tol < 0) r_tol=solver_accuracy;
346  //double a_tol = OptGetDbl("Solver", "a_tol", "1.0e-9" );
347  ierr = KSPSetTolerances(system, r_tol_, a_tol_, PETSC_DEFAULT,PETSC_DEFAULT);
348  ierr = KSPSetFromOptions(system);
349  // We set the KSP flag set_initial_guess_nonzero
350  // unless KSP type is preonly.
351  // In such case PETSc fails (version 3.4.1)
352  if (init_guess_nonzero)
353  {
354  KSPType type;
355  KSPGetType(system, &type);
356  if (strcmp(type, KSPPREONLY) != 0)
357  ierr = KSPSetInitialGuessNonzero(system, PETSC_TRUE);
358  }
359 
360  ierr = KSPSolve(system, rhs_, solution_ );
361  ierr = KSPGetConvergedReason(system,&reason);
362  ierr = KSPGetIterationNumber(system,&nits);
363 
364  // substitute by PETSc call for residual
365  ierr = VecNorm(rhs_, NORM_2, &residual_norm_);
366 
367  xprintf(MsgLog,"convergence reason %d, number of iterations is %d\n", reason, nits);
368 
369  // get residual norm
370  ierr = KSPGetResidualNorm(system, &solution_precision_);
371 
372  // TODO: I do not understand this
373  //Profiler::instance()->set_timer_subframes("SOLVING MH SYSTEM", nits);
374 
375  ierr = KSPDestroy(&system);
376 
377  return static_cast<int>(reason);
378 
379 }
380 
382 {
383  this -> gatherSolution_( );
384  globalSolution.resize( globalSolution_.size( ) );
385  std::copy( globalSolution_.begin(), globalSolution_.end(), globalSolution.begin() );
386 }
387 
389 {
390  std::string matFileName = "flow123d_matrix.m";
391  std::string rhsFileName = "flow123d_rhs.m";
392  std::string solFileName = "flow123d_sol.m";
393 
394  PetscViewer myViewer;
395 
396  PetscViewerASCIIOpen(comm_,matFileName.c_str(),&myViewer);
397  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
398  MatView( matrix_, myViewer );
399  PetscViewerDestroy(&myViewer);
400 
401  PetscViewerASCIIOpen(comm_,rhsFileName.c_str(),&myViewer);
402  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
403  VecView( rhs_, myViewer );
404  PetscViewerDestroy(&myViewer);
405 
406  if ( solution_ != NULL ) {
407  PetscViewerASCIIOpen(comm_,solFileName.c_str(),&myViewer);
408  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
409  VecView( solution_, myViewer );
410  PetscViewerDestroy(&myViewer);
411  }
412 }
413 
415 {
416  PetscErrorCode ierr;
417 
418  if (matrix_ != NULL) { ierr = MatDestroy(&matrix_); CHKERRV( ierr ); }
419  ierr = VecDestroy(&rhs_); CHKERRV( ierr );
420 
421  if (v_rhs_ != NULL) delete[] v_rhs_;
422 }
423 
425 {
426  // unsigned
427  unsigned globalSize = rows_ds_->size( );
428 
429  // create a large local solution vector for gathering
430  PetscErrorCode ierr;
431  Vec solutionGathered; //!< large vector of global solution stored locally
432  //ierr = VecCreateSeq( PETSC_COMM_SELF, globalSize, &solutionGathered ); CHKERRV( ierr );
433 
434  // prepare gathering scatter
435  VecScatter VSdistToLarge; //!< scatter for gathering local parts into large vector
436  ierr = VecScatterCreateToAll( solution_, &VSdistToLarge, &solutionGathered ); CHKERRV( ierr );
437  //ierr = VecScatterCreate( solution_, PETSC_NULL, solutionGathered, PETSC_NULL, &VSdistToLarge ); CHKERRV( ierr );
438 
439  // get global solution vector at each process
440  ierr = VecScatterBegin( VSdistToLarge, solution_, solutionGathered, INSERT_VALUES, SCATTER_FORWARD ); CHKERRV( ierr );
441  ierr = VecScatterEnd( VSdistToLarge, solution_, solutionGathered, INSERT_VALUES, SCATTER_FORWARD ); CHKERRV( ierr );
442 
443  // extract array of global solution for copy
444  PetscScalar *solutionGatheredArray;
445  ierr = VecGetArray( solutionGathered, &solutionGatheredArray );
446 
447  // copy the result to calling routine
448  //std::transform( &(solutionGatheredArray[0]), &(solutionGatheredArray[ globalSize ]), sol_disordered.begin( ),
449  // LinSys_PETSC::PetscScalar2Double_( ) ) ;
450 
451  //reorder solution
452  globalSolution_.resize( globalSize );
453  for ( int i = 0; i < globalSize; i++ ) {
454  globalSolution_[i] = static_cast<double>( solutionGatheredArray[i] );
455  }
456 
457  // release array
458  ierr = VecRestoreArray( solutionGathered, &solutionGatheredArray );
459 
460  // destroy PETSc objects
461  ierr = VecDestroy( &solutionGathered ); CHKERRV( ierr );
462  ierr = VecScatterDestroy( &VSdistToLarge ); CHKERRV( ierr );
463 }
464 
465 
467 {
468  if (! in_rec.is_empty()) {
469  // common values
470  LinSys::set_from_input( in_rec );
471 
472  // PETSc specific setting
473  a_tol_ = in_rec.val<double>("a_tol");
474  params_ = in_rec.val<string>("options");
475  }
476 }
477 
478 
480 {
481  return solution_precision_;
482 }
483