Flow123d  last_with_con_2.0.0-4-g42e6930
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  " ```norm( res_n ) < max( norm( res_0 ) * r_tol, a_tol )```\n"
34  "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.\n"
35  "If the initial guess of the solution is provided (usually only for transient equations) the residual of this estimate is used,\n"
36  "otherwise the norm of preconditioned RHS is used.\n"
37  "The default norm is L2 norm of preconditioned residual: (($ P^{-1}(Ax-b)$)), usage of other norm may be prescribed using the 'option' key.\n"
38  "See also PETSc documentation for KSPSetNormType.")
40  .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."),
41  "Relative residual tolerance, (to initial error).")
42  .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."),
43  "Absolute residual tolerance.")
44  .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."),
45  "Maximum number of outer iterations of the linear solver.")
46  .declare_key("options", it::String(), it::Default("\"\""), "Options passed to PETSC before creating KSP instead of default setting.")
47  .close();
48 }
49 
50 
52 
53 
55  : LinSys( rows_ds ),
56  init_guess_nonzero(false),
57  matrix_(0)
58 {
59  // create PETSC vectors:
60  PetscErrorCode ierr;
61  // rhs
62  v_rhs_= new double[ rows_ds_->lsize() + 1 ];
63  ierr = VecCreateMPIWithArray( comm_, 1, rows_ds_->lsize(), PETSC_DECIDE, v_rhs_, &rhs_ ); CHKERRV( ierr );
64  ierr = VecZeroEntries( rhs_ ); CHKERRV( ierr );
65  VecDuplicate(rhs_, &residual_);
66 
67  params_ = "";
68  matrix_ = NULL;
69  solution_precision_ = std::numeric_limits<double>::infinity();
70  matrix_changed_ = true;
71  rhs_changed_ = true;
72 }
73 
75  : LinSys(other), params_(other.params_), v_rhs_(NULL), solution_precision_(other.solution_precision_)
76 {
77  MatCopy(other.matrix_, matrix_, DIFFERENT_NONZERO_PATTERN);
78  VecCopy(other.rhs_, rhs_);
79  VecCopy(other.on_vec_, on_vec_);
80  VecCopy(other.off_vec_, off_vec_);
81 }
82 
83 void LinSys_PETSC::set_tolerances(double r_tol, double a_tol, unsigned int max_it)
84 {
85  if (! in_rec_.is_empty()) {
86  // input record is set
87  r_tol_ = in_rec_.val<double>("r_tol", r_tol);
88  a_tol_ = in_rec_.val<double>("a_tol", a_tol);
89  max_it_ = in_rec_.val<unsigned int>("max_it", max_it);
90  }
91 }
92 
93 
95 {
96  PetscErrorCode ierr;
97 
98  ierr = VecCreateMPI( comm_, rows_ds_->lsize(), PETSC_DECIDE, &(on_vec_) ); CHKERRV( ierr );
99  ierr = VecDuplicate( on_vec_, &(off_vec_) ); CHKERRV( ierr );
100  status_ = ALLOCATE;
101 }
102 
104 {
105  switch ( status_ ) {
106  case ALLOCATE:
107  this->preallocate_matrix( );
108  break;
109  case INSERT:
110  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
111  break;
112  case ADD:
113  case DONE:
114  break;
115  default:
116  OLD_ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
117  }
118  status_ = ADD;
119 }
120 
122 {
123  switch ( status_ ) {
124  case ALLOCATE:
125  this->preallocate_matrix();
126  break;
127  case ADD:
128  this->finish_assembly( MAT_FLUSH_ASSEMBLY );
129  break;
130  case INSERT:
131  case DONE:
132  break;
133  default:
134  OLD_ASSERT( false, "Can not set values. Matrix is not preallocated.\n");
135  }
136  status_ = INSERT;
137 }
138 
139 void LinSys_PETSC::mat_set_values( int nrow, int *rows, int ncol, int *cols, double *vals )
140 {
141  PetscErrorCode ierr;
142 
143  // here vals would need to be converted from double to PetscScalar if it was ever something else than double :-)
144  switch (status_) {
145  case INSERT:
146  case ADD:
147  ierr = MatSetValues(matrix_,nrow,rows,ncol,cols,vals,(InsertMode)status_); CHKERRV( ierr );
148  break;
149  case ALLOCATE:
150  this->preallocate_values(nrow,rows,ncol,cols);
151  break;
152  default: DebugOut() << "LS SetValues with non allowed insert mode.\n";
153  }
154 
155  matrix_changed_ = true;
156 }
157 
158 void LinSys_PETSC::rhs_set_values( int nrow, int *rows, double *vals )
159 {
160  PetscErrorCode ierr;
161 
162  switch (status_) {
163  case INSERT:
164  case ADD:
165  ierr = VecSetValues(rhs_,nrow,rows,vals,(InsertMode)status_); CHKERRV( ierr );
166  break;
167  case ALLOCATE:
168  break;
169  default: OLD_ASSERT(false, "LinSys's status disallow set values.\n");
170  }
171 
172  rhs_changed_ = true;
173 }
174 
175 void LinSys_PETSC::preallocate_values(int nrow,int *rows,int ncol,int *cols)
176 {
177  int i,j;
178  int col;
179  PetscInt row;
180 
181  for (i=0; i<nrow; i++) {
182  row=rows[i];
183  for(j=0; j<ncol; j++) {
184  col = cols[j];
185  if (rows_ds_->get_proc(row) == rows_ds_->get_proc(col))
186  VecSetValue(on_vec_,row,1.0,ADD_VALUES);
187  else
188  VecSetValue(off_vec_,row,1.0,ADD_VALUES);
189  }
190  }
191 }
192 
194 {
195  OLD_ASSERT(status_ == ALLOCATE, "Linear system has to be in ALLOCATE status.");
196 
197  PetscErrorCode ierr;
198  PetscInt *on_nz, *off_nz;
199  PetscScalar *on_array, *off_array;
200 
201  // assembly and get values from counting vectors, destroy them
202  VecAssemblyBegin(on_vec_);
203  VecAssemblyBegin(off_vec_);
204 
205  on_nz = new PetscInt[ rows_ds_->lsize() ];
206  off_nz = new PetscInt[ rows_ds_->lsize() ];
207 
208  VecAssemblyEnd(on_vec_);
209  VecAssemblyEnd(off_vec_);
210 
211  VecGetArray( on_vec_, &on_array );
212  VecGetArray( off_vec_, &off_array );
213 
214  for ( unsigned int i=0; i<rows_ds_->lsize(); i++ ) {
215  on_nz[i] = static_cast<PetscInt>( on_array[i]+0.1 ); // small fraction to ensure correct rounding
216  off_nz[i] = static_cast<PetscInt>( off_array[i]+0.1 );
217  }
218 
219  VecRestoreArray(on_vec_,&on_array);
220  VecRestoreArray(off_vec_,&off_array);
221  VecDestroy(&on_vec_);
222  VecDestroy(&off_vec_);
223 
224  // create PETSC matrix with preallocation
225  if (matrix_ != NULL)
226  {
227  ierr = MatDestroy(&matrix_); CHKERRV( ierr );
228  }
229  ierr = MatCreateAIJ(PETSC_COMM_WORLD, rows_ds_->lsize(), rows_ds_->lsize(), PETSC_DETERMINE, PETSC_DETERMINE,
230  0, on_nz, 0, off_nz, &matrix_); CHKERRV( ierr );
231 
232  if (symmetric_) MatSetOption(matrix_, MAT_SYMMETRIC, PETSC_TRUE);
233  MatSetOption(matrix_, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
234 
235  delete[] on_nz;
236  delete[] off_nz;
237 }
238 
240 {
241  MatAssemblyType assemblyType = MAT_FINAL_ASSEMBLY;
242  this->finish_assembly( assemblyType );
243 }
244 
245 void LinSys_PETSC::finish_assembly( MatAssemblyType assembly_type )
246 {
247  PetscErrorCode ierr;
248 
249  if (status_ == ALLOCATE) {
250  WarningOut() << "Finalizing linear system without setting values.\n";
251  this->preallocate_matrix();
252  }
253  ierr = MatAssemblyBegin(matrix_, assembly_type); CHKERRV( ierr );
254  ierr = VecAssemblyBegin(rhs_); CHKERRV( ierr );
255  ierr = MatAssemblyEnd(matrix_, assembly_type); CHKERRV( ierr );
256  ierr = VecAssemblyEnd(rhs_); CHKERRV( ierr );
257 
258  if (assembly_type == MAT_FINAL_ASSEMBLY) status_ = DONE;
259 
260  matrix_changed_ = true;
261  rhs_changed_ = true;
262 }
263 
264 void LinSys_PETSC::apply_constrains( double scalar )
265 {
266  PetscErrorCode ierr;
267 
268  // check that system matrix is assembled
269  OLD_ASSERT ( status_ == DONE, "System matrix and right-hand side are not assembled when applying constraints." );
270 
271  // number of constraints
272  PetscInt numConstraints = static_cast<PetscInt>( constraints_.size() );
273 
274  // Additional multiplier for numerical reasons (criterion to be established)
275  const PetscScalar diagScalar = static_cast<PetscScalar>( scalar );
276 
277  std::vector<PetscInt> globalDofs;
279 
280  // Constraint iterators
281  ConstraintVec_::const_iterator cIter = constraints_.begin( );
282  ConstraintVec_::const_iterator cEnd = constraints_.end( );
283  // collect global dof indices and the correpsonding values
284  for ( ; cIter != cEnd; ++cIter ) {
285  globalDofs.push_back( static_cast<PetscInt>( cIter -> first ) );
286  values.push_back( static_cast<PetscScalar>( cIter -> second ) * diagScalar );
287  }
288 
289  // prepare pointers to be passed to PETSc
290  PetscInt * globalDofPtr = this->makePetscPointer_( globalDofs );
291  PetscScalar * valuePtr = this->makePetscPointer_( values );
292 
293  // set matrix rows to zero
294  ierr = MatZeroRows( matrix_, numConstraints, globalDofPtr, diagScalar, PETSC_NULL, PETSC_NULL ); CHKERRV( ierr );
295  matrix_changed_ = true;
296 
297  // set RHS entries to values (crashes if called with NULL pointers)
298  if ( numConstraints ) {
299  ierr = VecSetValues( rhs_, numConstraints, globalDofPtr, valuePtr, INSERT_VALUES ); CHKERRV( ierr );
300  rhs_changed_ = true;
301  }
302 
303  // perform communication in the rhs vector
304  ierr = VecAssemblyBegin( rhs_ ); CHKERRV( ierr );
305  ierr = VecAssemblyEnd( rhs_ ); CHKERRV( ierr );
306 }
307 
308 
310 {
311  init_guess_nonzero = set_nonzero;
312 }
313 
314 
316 {
317 
318  const char *petsc_dflt_opt;
319  int nits;
320 
321  // -mat_no_inode ... inodes are usefull only for
322  // vector problems e.g. MH without Schur complement reduction
323  if (rows_ds_->np() > 1) {
324  // parallel setting
325  if (this->is_positive_definite())
326  //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";
327  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";
328  else
329  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";
330 
331  }
332  else {
333  // serial setting
334  if (this->is_positive_definite())
335  //petsc_dflt_opt="-ksp_type cg -pc_type icc -pc_factor_levels 3 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
336  petsc_dflt_opt="-ksp_type bcgs -pc_type ilu -pc_factor_levels 5 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
337  else
338  petsc_dflt_opt="-ksp_type bcgs -pc_type ilu -pc_factor_levels 5 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
339  }
340 
341  if (params_ == "") params_ = petsc_dflt_opt;
342  LogOut().fmt("inserting petsc options: {}\n",params_.c_str());
343  PetscOptionsInsertString(params_.c_str()); // overwrites previous options values
344 
345  MatSetOption( matrix_, MAT_USE_INODES, PETSC_FALSE );
346 
347  chkerr(KSPCreate( comm_, &system ));
348  chkerr(KSPSetOperators(system, matrix_, matrix_));
349 
350 
351  // TODO take care of tolerances - shall we support both input file and command line petsc setting
352  chkerr(KSPSetTolerances(system, r_tol_, a_tol_, PETSC_DEFAULT,PETSC_DEFAULT));
353  KSPSetFromOptions(system);
354  // We set the KSP flag set_initial_guess_nonzero
355  // unless KSP type is preonly.
356  // In such case PETSc fails (version 3.4.1)
357  if (init_guess_nonzero)
358  {
359  KSPType type;
360  KSPGetType(system, &type);
361  if (strcmp(type, KSPPREONLY) != 0)
362  KSPSetInitialGuessNonzero(system, PETSC_TRUE);
363  }
364 
365  {
366  START_TIMER("PETSC linear solver");
367  START_TIMER("PETSC linear iteration");
368  chkerr(KSPSolve(system, rhs_, solution_ ));
369  KSPGetConvergedReason(system,&reason);
370  KSPGetIterationNumber(system,&nits);
371  ADD_CALLS(nits);
372  }
373  // substitute by PETSc call for residual
374  VecNorm(rhs_, NORM_2, &residual_norm_);
375 
376  LogOut().fmt("convergence reason {}, number of iterations is {}\n", reason, nits);
377 
378  // get residual norm
379  KSPGetResidualNorm(system, &solution_precision_);
380 
381  // TODO: I do not understand this
382  //Profiler::instance()->set_timer_subframes("SOLVING MH SYSTEM", nits);
383 
384  KSPDestroy(&system);
385 
386  return static_cast<int>(reason);
387 
388 }
389 
391 {
392  std::string matFileName = "flow123d_matrix.m";
393  std::string rhsFileName = "flow123d_rhs.m";
394  std::string solFileName = "flow123d_sol.m";
395 
396  PetscViewer myViewer;
397 
398  PetscViewerASCIIOpen(comm_,matFileName.c_str(),&myViewer);
399  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
400  MatView( matrix_, myViewer );
401  PetscViewerDestroy(&myViewer);
402 
403  PetscViewerASCIIOpen(comm_,rhsFileName.c_str(),&myViewer);
404  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
405  VecView( rhs_, myViewer );
406  PetscViewerDestroy(&myViewer);
407 
408  if ( solution_ != NULL ) {
409  PetscViewerASCIIOpen(comm_,solFileName.c_str(),&myViewer);
410  PetscViewerSetFormat(myViewer,PETSC_VIEWER_ASCII_MATLAB);
411  VecView( solution_, myViewer );
412  PetscViewerDestroy(&myViewer);
413  }
414 }
415 
417 {
418  PetscErrorCode ierr;
419 
420  if (matrix_ != NULL) { ierr = MatDestroy(&matrix_); CHKERRV( ierr ); }
421  ierr = VecDestroy(&rhs_); CHKERRV( ierr );
422 
423  if (v_rhs_ != NULL) delete[] v_rhs_;
424 }
425 
426 
427 
429 {
430  LinSys::set_from_input( in_rec );
431 
432  // PETSC specific parameters
433  params_ = in_rec.val<string>("options");
434 }
435 
436 
438 {
439  return solution_precision_;
440 }
441 
442 
444 {
445  MatMult(matrix_, solution_, residual_);
446  VecAXPY(residual_,-1.0, rhs_);
447  double residual_norm;
448  VecNorm(residual_, NORM_2, &residual_norm);
449  return residual_norm;
450 }
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 start_insert_assembly()
void apply_constrains(double scalar=1.)
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:582
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:50
LinSys_PETSC(const Distribution *rows_ds)
Definition: linsys_PETSC.cc:54
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 set_from_input(const Input::Record in_rec)
Class for declaration of the integral input data.
Definition: type_base.hh:465
void preallocate_values(int nrow, int *rows, int ncol, int *cols)
void rhs_set_values(int nrow, int *rows, double *vals)
Input::Record in_rec_
Definition: linsys.hh:608
#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:286
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:195
#define OLD_ASSERT(...)
Definition: global_defs.h:131
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:518
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:277
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:468
void finish_assembly()
bool is_empty() const
Definition: accessors.hh:351
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()
void start_allocation()
Definition: linsys_PETSC.cc:94
double compute_residual() override
std::string params_
command-line-like options for the PETSc solver
void mat_set_values(int nrow, int *rows, int ncol, int *cols, double *vals)
void start_add_assembly()
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:86
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
double get_solution_precision()
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:83
Mat matrix_
Petsc matrix of the problem.
static const int registrar
Registrar of class to factory.
Record type proxy class.
Definition: type_record.hh:171
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:568
Vec solution_
PETSc vector constructed with vb array.
Definition: linsys.hh:598
double solution_precision_
unsigned int lsize(int proc) const
get local size