Flow123d  release_2.1.0-87-gfbc1563
linsys_PETSC.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file linsys_PETSC.cc
15  * @brief Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construction
16  * @author Jakub Sistek
17  */
18 
19 // derived from base linsys
20 #include "la/linsys_PETSC.hh"
21 #include "petscvec.h"
22 #include "petscksp.h"
23 #include "petscmat.h"
24 #include "system/sys_profiler.hh"
25 
26 
27 //#include <boost/bind.hpp>
28 
29 namespace it = Input::Type;
30 
32  return it::Record("Petsc", "Interface to PETSc solvers. Convergence criteria is:\n"
33  "```\n"
34  "norm( res_n ) < max( norm( res_0 ) * r_tol, a_tol )\n"
35  "```\n"
36  "where res_i is the residuum vector after i-th iteration of the solver and res_0 is an estimate of the norm of initial residual. "
37  "If the initial guess of the solution is provided (usually only for transient equations) the residual of this estimate is used, "
38  "otherwise the norm of preconditioned RHS is used. "
39  "The default norm is L2 norm of preconditioned residual: (($ P^{-1}(Ax-b)$)), usage of other norm may be prescribed using the 'option' key. "
40  "See also PETSc documentation for KSPSetNormType.")
42  .declare_key("r_tol", it::Double(0.0, 1.0), it::Default::read_time("Defalut value set by nonlinear solver or equation. If not we use value 1.0e-7."),
43  "Relative residual tolerance, (to initial error).")
44  .declare_key("a_tol", it::Double(0.0), it::Default::read_time("Defalut value set by nonlinear solver or equation. If not we use value 1.0e-11."),
45  "Absolute residual tolerance.")
46  .declare_key("max_it", it::Integer(0), it::Default::read_time("Defalut value set by nonlinear solver or equation. If not we use value 1000."),
47  "Maximum number of outer iterations of the linear solver.")
48  .declare_key("options", it::String(), it::Default("\"\""), "Options passed to PETSC before creating KSP instead of default setting.")
49  .close();
50 }
51 
52 
54 
55 
57  : LinSys( rows_ds ),
58  init_guess_nonzero(false),
59  matrix_(0)
60 {
61  // create PETSC vectors:
62  PetscErrorCode ierr;
63  // rhs
64  v_rhs_= new double[ rows_ds_->lsize() + 1 ];
65  ierr = VecCreateMPIWithArray( comm_, 1, rows_ds_->lsize(), PETSC_DECIDE, v_rhs_, &rhs_ ); CHKERRV( ierr );
66  ierr = VecZeroEntries( rhs_ ); CHKERRV( ierr );
67  VecDuplicate(rhs_, &residual_);
68 
69  params_ = "";
70  matrix_ = NULL;
71  solution_precision_ = std::numeric_limits<double>::infinity();
72  matrix_changed_ = true;
73  rhs_changed_ = true;
74 }
75 
77  : LinSys(other), params_(other.params_), v_rhs_(NULL), solution_precision_(other.solution_precision_)
78 {
79  MatCopy(other.matrix_, matrix_, DIFFERENT_NONZERO_PATTERN);
80  VecCopy(other.rhs_, rhs_);
81  VecCopy(other.on_vec_, on_vec_);
82  VecCopy(other.off_vec_, off_vec_);
83 }
84 
85 void LinSys_PETSC::set_tolerances(double r_tol, double a_tol, unsigned int max_it)
86 {
87  if (! in_rec_.is_empty()) {
88  // input record is set
89  r_tol_ = in_rec_.val<double>("r_tol", r_tol);
90  a_tol_ = in_rec_.val<double>("a_tol", a_tol);
91  max_it_ = in_rec_.val<unsigned int>("max_it", max_it);
92  } else {
93  r_tol_ = r_tol;
94  a_tol_ = a_tol;
95  max_it_ = max_it;
96 
97  }
98 }
99 
100 
102 {
103  PetscErrorCode ierr;
104 
105  ierr = VecCreateMPI( comm_, rows_ds_->lsize(), PETSC_DECIDE, &(on_vec_) ); CHKERRV( ierr );
106  ierr = VecDuplicate( on_vec_, &(off_vec_) ); CHKERRV( ierr );
107  status_ = ALLOCATE;
108 }
109 
111 {
112  switch ( status_ ) {
113  case ALLOCATE:
114  this->preallocate_matrix( );
115  break;
116  case INSERT:
117  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
118  break;
119  case ADD:
120  case DONE:
121  break;
122  default:
123  OLD_ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
124  }
125  status_ = ADD;
126 }
127 
129 {
130  switch ( status_ ) {
131  case ALLOCATE:
132  this->preallocate_matrix();
133  break;
134  case ADD:
135  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
136  break;
137  case INSERT:
138  case DONE:
139  break;
140  default:
141  OLD_ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
142  }
143  status_ = INSERT;
144 }
145 
146 void LinSys_PETSC::mat_set_values( int nrow, int *rows, int ncol, int *cols, double *vals )
147 {
148  PetscErrorCode ierr;
149 
150  // here vals would need to be converted from double to PetscScalar if it was ever something else than double :-)
151  switch (status_) {
152  case INSERT:
153  case ADD:
154  ierr = MatSetValues(matrix_,nrow,rows,ncol,cols,vals,(InsertMode)status_); CHKERRV( ierr );
155  break;
156  case ALLOCATE:
157  this->preallocate_values(nrow,rows,ncol,cols);
158  break;
159  default: DebugOut() << "LS SetValues with non allowed insert mode.\n";
160  }
161 
162  matrix_changed_ = true;
163 }
164 
165 void LinSys_PETSC::rhs_set_values( int nrow, int *rows, double *vals )
166 {
167  PetscErrorCode ierr;
168 
169  switch (status_) {
170  case INSERT:
171  case ADD:
172  ierr = VecSetValues(rhs_,nrow,rows,vals,(InsertMode)status_); CHKERRV( ierr );
173  break;
174  case ALLOCATE:
175  break;
176  default: OLD_ASSERT(false, "LinSys's status disallow set values.\n");
177  }
178 
179  rhs_changed_ = true;
180 }
181 
182 void LinSys_PETSC::preallocate_values(int nrow,int *rows,int ncol,int *cols)
183 {
184  int i,j;
185  int col;
186  PetscInt row;
187 
188  for (i=0; i<nrow; i++) {
189  row=rows[i];
190  for(j=0; j<ncol; j++) {
191  col = cols[j];
192  if (rows_ds_->get_proc(row) == rows_ds_->get_proc(col))
193  VecSetValue(on_vec_,row,1.0,ADD_VALUES);
194  else
195  VecSetValue(off_vec_,row,1.0,ADD_VALUES);
196  }
197  }
198 }
199 
201 {
202  OLD_ASSERT(status_ == ALLOCATE, "Linear system has to be in ALLOCATE status.");
203 
204  PetscErrorCode ierr;
205  PetscInt *on_nz, *off_nz;
206  PetscScalar *on_array, *off_array;
207 
208  // assembly and get values from counting vectors, destroy them
209  VecAssemblyBegin(on_vec_);
210  VecAssemblyBegin(off_vec_);
211 
212  on_nz = new PetscInt[ rows_ds_->lsize() ];
213  off_nz = new PetscInt[ rows_ds_->lsize() ];
214 
215  VecAssemblyEnd(on_vec_);
216  VecAssemblyEnd(off_vec_);
217 
218  VecGetArray( on_vec_, &on_array );
219  VecGetArray( off_vec_, &off_array );
220 
221  for ( unsigned int i=0; i<rows_ds_->lsize(); i++ ) {
222  on_nz[i] = static_cast<PetscInt>( on_array[i]+0.1 ); // small fraction to ensure correct rounding
223  off_nz[i] = static_cast<PetscInt>( off_array[i]+0.1 );
224  }
225 
226  VecRestoreArray(on_vec_,&on_array);
227  VecRestoreArray(off_vec_,&off_array);
228  VecDestroy(&on_vec_);
229  VecDestroy(&off_vec_);
230 
231  // create PETSC matrix with preallocation
232  if (matrix_ != NULL)
233  {
234  ierr = MatDestroy(&matrix_); CHKERRV( ierr );
235  }
236  ierr = MatCreateAIJ(PETSC_COMM_WORLD, rows_ds_->lsize(), rows_ds_->lsize(), PETSC_DETERMINE, PETSC_DETERMINE,
237  0, on_nz, 0, off_nz, &matrix_); CHKERRV( ierr );
238 
239  if (symmetric_) MatSetOption(matrix_, MAT_SYMMETRIC, PETSC_TRUE);
240  MatSetOption(matrix_, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
241 
242  delete[] on_nz;
243  delete[] off_nz;
244 }
245 
247 {
248  MatAssemblyType assemblyType = MAT_FINAL_ASSEMBLY;
249  this->finish_assembly( assemblyType );
250 }
251 
252 void LinSys_PETSC::finish_assembly( MatAssemblyType assembly_type )
253 {
254  PetscErrorCode ierr;
255 
256  if (status_ == ALLOCATE) {
257  WarningOut() << "Finalizing linear system without setting values.\n";
258  this->preallocate_matrix();
259  }
260  ierr = MatAssemblyBegin(matrix_, assembly_type); CHKERRV( ierr );
261  ierr = VecAssemblyBegin(rhs_); CHKERRV( ierr );
262  ierr = MatAssemblyEnd(matrix_, assembly_type); CHKERRV( ierr );
263  ierr = VecAssemblyEnd(rhs_); CHKERRV( ierr );
264 
265  if (assembly_type == MAT_FINAL_ASSEMBLY) status_ = DONE;
266 
267  matrix_changed_ = true;
268  rhs_changed_ = true;
269 }
270 
271 void LinSys_PETSC::apply_constrains( double scalar )
272 {
273  PetscErrorCode ierr;
274 
275  // check that system matrix is assembled
276  OLD_ASSERT ( status_ == DONE, "System matrix and right-hand side are not assembled when applying constraints." );
277 
278  // number of constraints
279  PetscInt numConstraints = static_cast<PetscInt>( constraints_.size() );
280 
281  // Additional multiplier for numerical reasons (criterion to be established)
282  const PetscScalar diagScalar = static_cast<PetscScalar>( scalar );
283 
284  std::vector<PetscInt> globalDofs;
286 
287  // Constraint iterators
288  ConstraintVec_::const_iterator cIter = constraints_.begin( );
289  ConstraintVec_::const_iterator cEnd = constraints_.end( );
290  // collect global dof indices and the correpsonding values
291  for ( ; cIter != cEnd; ++cIter ) {
292  globalDofs.push_back( static_cast<PetscInt>( cIter -> first ) );
293  values.push_back( static_cast<PetscScalar>( cIter -> second ) * diagScalar );
294  }
295 
296  // prepare pointers to be passed to PETSc
297  PetscInt * globalDofPtr = this->makePetscPointer_( globalDofs );
298  PetscScalar * valuePtr = this->makePetscPointer_( values );
299 
300  // set matrix rows to zero
301  ierr = MatZeroRows( matrix_, numConstraints, globalDofPtr, diagScalar, PETSC_NULL, PETSC_NULL ); CHKERRV( ierr );
302  matrix_changed_ = true;
303 
304  // set RHS entries to values (crashes if called with NULL pointers)
305  if ( numConstraints ) {
306  ierr = VecSetValues( rhs_, numConstraints, globalDofPtr, valuePtr, INSERT_VALUES ); CHKERRV( ierr );
307  rhs_changed_ = true;
308  }
309 
310  // perform communication in the rhs vector
311  ierr = VecAssemblyBegin( rhs_ ); CHKERRV( ierr );
312  ierr = VecAssemblyEnd( rhs_ ); CHKERRV( ierr );
313 }
314 
315 
317 {
318  init_guess_nonzero = set_nonzero;
319 }
320 
321 
323 {
324 
325  const char *petsc_dflt_opt;
326  int nits;
327 
328  // -mat_no_inode ... inodes are usefull only for
329  // vector problems e.g. MH without Schur complement reduction
330  if (rows_ds_->np() > 1) {
331  // parallel setting
332  if (this->is_positive_definite())
333  //petsc_dflt_opt="-ksp_type cg -ksp_diagonal_scale_fix -pc_type asm -pc_asm_overlap 4 -sub_pc_type icc -sub_pc_factor_levels 3 -sub_pc_factor_fill 6.0";
334  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";
335  else
336  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";
337 
338  }
339  else {
340  // serial setting
341  if (this->is_positive_definite())
342  //petsc_dflt_opt="-ksp_type cg -pc_type icc -pc_factor_levels 3 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
343  petsc_dflt_opt="-ksp_type bcgs -pc_type ilu -pc_factor_levels 5 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
344  else
345  petsc_dflt_opt="-ksp_type bcgs -pc_type ilu -pc_factor_levels 5 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
346  }
347 
348  if (params_ == "") params_ = petsc_dflt_opt;
349  LogOut().fmt("inserting petsc options: {}\n",params_.c_str());
350  PetscOptionsInsertString(params_.c_str()); // overwrites previous options values
351 
352  MatSetOption( matrix_, MAT_USE_INODES, PETSC_FALSE );
353 
354  chkerr(KSPCreate( comm_, &system ));
355  chkerr(KSPSetOperators(system, matrix_, matrix_));
356 
357 
358  // TODO take care of tolerances - shall we support both input file and command line petsc setting
359  chkerr(KSPSetTolerances(system, r_tol_, a_tol_, PETSC_DEFAULT,PETSC_DEFAULT));
360  chkerr(KSPSetTolerances(system, r_tol_, a_tol_, PETSC_DEFAULT, max_it_));
361  KSPSetFromOptions(system);
362  // We set the KSP flag set_initial_guess_nonzero
363  // unless KSP type is preonly.
364  // In such case PETSc fails (version 3.4.1)
365  if (init_guess_nonzero)
366  {
367  KSPType type;
368  KSPGetType(system, &type);
369  if (strcmp(type, KSPPREONLY) != 0)
370  KSPSetInitialGuessNonzero(system, PETSC_TRUE);
371  }
372 
373  {
374  START_TIMER("PETSC linear solver");
375  START_TIMER("PETSC linear iteration");
376  chkerr(KSPSolve(system, rhs_, solution_ ));
377  KSPGetConvergedReason(system,&reason);
378  KSPGetIterationNumber(system,&nits);
379  ADD_CALLS(nits);
380  }
381  // substitute by PETSc call for residual
382  VecNorm(rhs_, NORM_2, &residual_norm_);
383 
384  LogOut().fmt("convergence reason {}, number of iterations is {}\n", reason, nits);
385 
386  // get residual norm
387  KSPGetResidualNorm(system, &solution_precision_);
388 
389  // TODO: I do not understand this
390  //Profiler::instance()->set_timer_subframes("SOLVING MH SYSTEM", nits);
391 
392  KSPDestroy(&system);
393 
394  return static_cast<int>(reason);
395 
396 }
397 
399 {
400  std::string matFileName = "flow123d_matrix.m";
401  std::string rhsFileName = "flow123d_rhs.m";
402  std::string solFileName = "flow123d_sol.m";
403 
404  PetscViewer myViewer;
405 
406  PetscViewerASCIIOpen(comm_,matFileName.c_str(),&myViewer);
407  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
408  MatView( matrix_, myViewer );
409  PetscViewerDestroy(&myViewer);
410 
411  PetscViewerASCIIOpen(comm_,rhsFileName.c_str(),&myViewer);
412  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
413  VecView( rhs_, myViewer );
414  PetscViewerDestroy(&myViewer);
415 
416  if ( solution_ != NULL ) {
417  PetscViewerASCIIOpen(comm_,solFileName.c_str(),&myViewer);
418  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
419  VecView( solution_, myViewer );
420  PetscViewerDestroy(&myViewer);
421  }
422 }
423 
425 {
426  PetscErrorCode ierr;
427 
428  if (matrix_ != NULL) { ierr = MatDestroy(&matrix_); CHKERRV( ierr ); }
429  ierr = VecDestroy(&rhs_); CHKERRV( ierr );
430 
431  if (v_rhs_ != NULL) delete[] v_rhs_;
432 }
433 
434 
435 
437 {
438  LinSys::set_from_input( in_rec );
439 
440  // PETSC specific parameters
441  params_ = in_rec.val<string>("options");
442 }
443 
444 
446 {
447  return solution_precision_;
448 }
449 
450 
452 {
453  MatMult(matrix_, solution_, residual_);
454  VecAXPY(residual_,-1.0, rhs_);
455  double residual_norm;
456  VecNorm(residual_, NORM_2, &residual_norm);
457  return residual_norm;
458 }
unsigned int get_proc(unsigned int idx) const
get processor of the given index
SetValuesMode status_
Set value status of the linear system.
Definition: linsys.hh:583
void apply_constrains(double scalar=1.) override
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:593
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
virtual void set_from_input(const Input::Record in_rec)
Definition: linsys.hh:554
bool matrix_changed_
true if the matrix was changed since the last solve
Definition: linsys.hh:595
double * v_rhs_
local RHS array pointing to Vec rhs_
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:56
void set_from_input(const Input::Record in_rec) override
LinSys_PETSC(const Distribution *rows_ds)
Definition: linsys_PETSC.cc:56
int solve() override
bool symmetric_
Definition: linsys.hh:590
ConstraintVec_ constraints_
Definition: linsys.hh:604
Vec rhs_
PETSc vector constructed with vx array.
void chkerr(unsigned int ierr)
Replacement of new/delete operator in the spirit of xmalloc.
Definition: system.hh:142
#define ADD_CALLS(n_calls)
Increase number of calls in actual timer.
void start_allocation() override
Class for declaration of the integral input data.
Definition: type_base.hh:483
void preallocate_values(int nrow, int *rows, int ncol, int *cols)
Input::Record in_rec_
Definition: linsys.hh:608
void start_add_assembly() override
#define LogOut()
Macro defining &#39;log&#39; record of log.
Definition: logger.hh:237
bool rhs_changed_
true if the right hand side was changed since the last solve
Definition: linsys.hh:596
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:301
double get_solution_precision() override
void start_insert_assembly() override
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:193
#define OLD_ASSERT(...)
Definition: global_defs.h:131
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:534
MPI_Comm comm_
Definition: linsys.hh:582
unsigned int max_it_
maximum number of iterations of linear solver
Definition: linsys.hh:580
T * makePetscPointer_(std::vector< T > &array)
Accessor to the data with type Type::Record.
Definition: accessors.hh:286
const Ret val(const string &key) const
#define START_TIMER(tag)
Starts a timer with specified tag.
const Distribution * rows_ds_
final distribution of rows of MH matrix
Definition: linsys.hh:588
double a_tol_
absolute tolerance of linear solver
Definition: linsys.hh:579
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:488
bool is_empty() const
Definition: accessors.hh:360
unsigned int np() const
get num of processors
double residual_norm_
local solution array pointing into Vec solution_
Definition: linsys.hh:602
Vec on_vec_
Vectors for counting non-zero entries in diagonal block.
void preallocate_matrix()
double compute_residual() override
std::string params_
command-line-like options for the PETSc solver
static Default read_time(const std::string &description)
The factory function to make an default value that will be specified at the time when a key will be r...
Definition: type_record.hh:92
KSPConvergedReason reason
Vec off_vec_
Vectors for counting non-zero entries in off-diagonal block.
double r_tol_
relative tolerance of linear solver
Definition: linsys.hh:578
void finish_assembly() override
void view() override
void rhs_set_values(int nrow, int *rows, double *vals) override
Abstract linear system class.
bool is_positive_definite()
Definition: linsys.hh:510
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:234
void set_initial_guess_nonzero(bool set_nonzero=true)
static const Input::Type::Record & get_input_type()
Definition: linsys_PETSC.cc:31
void set_tolerances(double r_tol, double a_tol, unsigned int max_it) override
Definition: linsys_PETSC.cc:85
Mat matrix_
Petsc matrix of the problem.
static const int registrar
Registrar of class to factory.
Record type proxy class.
Definition: type_record.hh:177
void mat_set_values(int nrow, int *rows, int ncol, int *cols, double *vals) override
static Input::Type::Abstract & get_input_type()
Definition: linsys.cc:29
bool init_guess_nonzero
flag for starting from nonzero guess
#define DebugOut()
Macro defining &#39;debug&#39; record of log.
Definition: logger.hh:240
Class for declaration of the input data that are in string format.
Definition: type_base.hh:582
Vec solution_
PETSc vector constructed with vb array.
Definition: linsys.hh:598
double solution_precision_
unsigned int lsize(int proc) const
get local size