Flow123d  last_with_con_2.0.0-4-g42e6930
schur.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 schur.cc
15  * @ingroup la
16  * @brief Assembly explicit Schur complement for the given linear system.
17  * Provides method for resolution of the full original vector of unknowns.
18  *
19  * Aim: Explicit schur should be faster then implicit, i.e.
20  *
21  * @todo
22  * - vyresit navaznost na lin sys - solve a export seq vektoru, redukce ... ?
23  * - inv_a - predava se pri konstrukci, ale neumoznuje jeji reuse - aktualizaci assemblace
24  * resp. nutno si na ni drzet ukazatel venku ... reseni ?
25  * - ? remove old_4_new - just for LSView
26  * - automatic preallocation
27  * - eliminated block given by IS
28  * - in place Schur
29  * - ? nemodifikovat puvodni system, leda skrze jeho metody
30  */
31 
32 #include <petscvec.h>
33 #include <algorithm>
34 #include <limits>
35 #include <petscmat.h>
36 #include <armadillo>
37 #include <petscis.h>
38 
39 #include "system/sys_profiler.hh"
40 #include "la/distribution.hh"
42 #include "system/system.hh"
43 #include "la/linsys.hh"
44 #include "la/linsys_BDDC.hh"
45 #include "la/schur.hh"
46 
47 /**
48  * Create Schur complement system.
49  * @param[in] orig : original system
50  * @param[in] inv_a : inversion of the A block
51  * @param[in] ia : index set of the A block,
52  * default continuous given by inv_a:
53  * proc 1 2 3
54  *
55  * Orig: ****** ****** ****
56  * IA : *** ** ***
57  *
58  *
59  */
60 
62 : LinSys_PETSC(ds), IsA(ia), state(created)
63 {
64  // check index set
65  OLD_ASSERT(IsA != NULL, "Index set IsA is not defined.\n" );
66 
67  // initialize variables
68  Compl = NULL;
69  IA = NULL;
70  B = NULL;
71  Bt = NULL;
72  xA = NULL;
73  IAB = NULL;
74  IsB = NULL;
75  RHS1 = NULL;
76  RHS2 = NULL;
77  Sol1 = NULL;
78  Sol2 = NULL;
79 
80  // create A block index set
81  ISGetLocalSize(IsA, &loc_size_A);
82 
83  // create B block index set
85  ISCreateStride(PETSC_COMM_WORLD,loc_size_B,rows_ds_->begin()+loc_size_A,1,&IsB);
86 }
87 
88 
90 : LinSys_PETSC(other),
91  loc_size_A(other.loc_size_A), loc_size_B(other.loc_size_B), state(other.state),
92  Compl(other.Compl), ds_(other.ds_)
93 {
94  MatCopy(other.IA, IA, DIFFERENT_NONZERO_PATTERN);
95  MatCopy(other.IAB, IAB, DIFFERENT_NONZERO_PATTERN);
96  ISCopy(other.IsA, IsA);
97  ISCopy(other.IsB, IsB);
98  VecCopy(other.RHS1, RHS1);
99  VecCopy(other.RHS2, RHS2);
100  VecCopy(other.Sol1, Sol1);
101  VecCopy(other.Sol2, Sol2);
102 
103  B = NULL;
104  Bt = NULL;
105  xA = NULL;
106 }
107 
108 
109 /**
110  * COMPUTE A SCHUR COMPLEMENT OF A PETSC MATRIX
111  *
112  * given symmetric original matrix Orig has form
113  * A B x_1 RHS_1
114  * B' C * x_2 = RHS_2
115  * where the first block is given by index set IsA, and the second block by IsB
116  * user has to provide inverse IA of the A-block
117  * we suppose that original matrix have non-zero pattern for the schur complement
118  *
119  * we return: Shur - schur complement, ShurRHS - RHS of the complemented system:
120  * (B' * IA * B - C) * x_2 = (B' * IA * RHS_1 - RHS_2)
121  * IAB - a matrix to compute eliminated part of the solution:
122  * x_1 = IA * RHS_1 - IAB * x_2
123  *
124  * Actually as B' is stored separetly, the routine can be used also for nonsymetric original
125  * system
126  *
127  */
128 
130 {
131  START_TIMER("form schur complement");
132 
133  PetscErrorCode ierr = 0;
134  MatReuse mat_reuse; // reuse structures after first computation of schur
135  PetscScalar *rhs_array, *sol_array;
136 
137  mat_reuse=MAT_REUSE_MATRIX;
138  if (state==created) {
139  mat_reuse=MAT_INITIAL_MATRIX; // indicate first construction
140 
141  // create complement system
142  // TODO: introduce LS as true object, clarify its internal states
143  // create RHS sub vecs RHS1, RHS2
144  VecGetArray(rhs_, &rhs_array);
145  VecCreateMPIWithArray(PETSC_COMM_WORLD,1,loc_size_A,PETSC_DETERMINE,rhs_array,&(RHS1));
146 
147  // create Solution sub vecs Sol1, Compl->solution
148  VecGetArray(solution_, &sol_array);
149  VecCreateMPIWithArray(PETSC_COMM_WORLD,1,loc_size_A,PETSC_DETERMINE,sol_array,&(Sol1));
150 
151  VecCreateMPIWithArray(PETSC_COMM_WORLD,1,loc_size_B,PETSC_DETERMINE,rhs_array+loc_size_A,&(RHS2));
152  VecCreateMPIWithArray(PETSC_COMM_WORLD,1,loc_size_B,PETSC_DETERMINE,sol_array+loc_size_A,&(Sol2));
153 
154  VecRestoreArray(rhs_, &rhs_array);
155  VecRestoreArray(solution_, &sol_array);
156 
157  VecGetArray( Sol2, &sol_array );
158  Compl->set_solution( sol_array );
159 
160  VecRestoreArray( Sol2, &sol_array );
161 
162  }
163 
164  // compose Schur complement
165  // Petsc need some fill estimate for results of multiplication in form nnz(A*B)/(nnz(A)+nnz(B))
166  // for the first Schur compl: IA*B is bounded by ( d*(d+1) )/( d*d+2*d ) <= 5/6 for d<=4
167  // B'*IA*B bounded by ( (d+1)*(d+1) )/ ( d*(d+1) + d ) ~ 1
168  // for the second Schur : IA*B have fill ratio ~ 1.
169  // B'*IA*B ... ( N/2 *(2*N-1) )/( 2 + 2*N ) <= 1.4
170  // nevertheless Petsc does not allows fill ratio below 1. so we use 1.1 for the first
171  // and 1.5 for the second multiplication
172 
173  if (matrix_changed_) {
175 
176  // compute IAB=IA*B, loc_size_B removed
177  ierr+=MatGetSubMatrix(matrix_, IsA, IsB, mat_reuse, &B);
178  ierr+=MatMatMult(IA, B, mat_reuse, 1.0 ,&(IAB)); // 6/7 - fill estimate
179  // compute xA=Bt* IAB = Bt * IA * B, locSizeA removed
180  ierr+=MatGetSubMatrix(matrix_, IsB, IsA, mat_reuse, &(Bt));
181  ierr+=MatMatMult(Bt, IAB, mat_reuse, 1.9 ,&(xA)); // 1.1 - fill estimate (PETSC report values over 1.8)
182 
183  // get C block, loc_size_B removed
184  ierr+=MatGetSubMatrix( matrix_, IsB, IsB, mat_reuse, const_cast<Mat *>( Compl->get_matrix() ) );
185  // compute complement = (-1)cA+xA = Bt*IA*B - C
186  if ( is_negative_definite() ) {
187  ierr+=MatAXPY(*( Compl->get_matrix() ), -1, xA, SUBSET_NONZERO_PATTERN);
188  } else {
189  ierr+=MatScale(*( Compl->get_matrix() ),-1.0);
190  ierr+=MatAXPY(*( Compl->get_matrix() ), 1, xA, SUBSET_NONZERO_PATTERN);
191  }
193 
194  OLD_ASSERT( ierr == 0, "PETSC Error during calculation of Schur complement.\n");
195 
196  }
197 
198  form_rhs();
199 
200  matrix_changed_ = false;
201 
202  state=formed;
203 }
204 
206 {
207  START_TIMER("form rhs");
208  if (rhs_changed_ || matrix_changed_) {
209  MatMultTranspose(IAB, RHS1, *( Compl->get_rhs() ));
210  VecAXPY(*( Compl->get_rhs() ), -1, RHS2);
211  if ( is_negative_definite() ) {
212  VecScale(*( Compl->get_rhs() ), -1.0);
213  }
215  rhs_changed_ = false;
216  }
217 
218  state=formed;
219 }
220 
221 
222 
224 {
225  OLD_ASSERT(ls != nullptr, "NULL complement ls.\n");
226  Compl = ls;
227  if (!in_rec_.is_empty()) {
229  }
230 }
231 
232 
234 {
235  ds_ = new Distribution(loc_size_B, PETSC_COMM_WORLD);
236  return ds_;
237 }
238 
240 {
241  START_TIMER("create inversion matrix");
242  PetscInt ncols, pos_start, pos_start_IA;
243 
244  MatReuse mat_reuse=MAT_REUSE_MATRIX;
245  if (state==created) mat_reuse=MAT_INITIAL_MATRIX; // indicate first construction
246 
247  MatGetSubMatrix(matrix_, IsA, IsA, mat_reuse, &IA);
248  MatGetOwnershipRange(matrix_,&pos_start,PETSC_NULL);
249  MatGetOwnershipRange(IA,&pos_start_IA,PETSC_NULL);
250 
251  std::vector<PetscInt> submat_rows;
252  const PetscInt *cols;
253  const PetscScalar *vals;
254 
255  std::vector<unsigned int> processed_rows(loc_size_A,0);
256 
257  unsigned int mat_block=1; //actual processed block of matrix
258  for(unsigned int loc_row=0; loc_row < processed_rows.size(); loc_row++) {
259  if (processed_rows[loc_row] != 0) continue;
260 
261  PetscInt min=std::numeric_limits<int>::max(), max=-1, size_submat;
262  PetscInt b_vals = 0; // count of values stored in B-block of Orig system
263  submat_rows.clear();
264  MatGetRow(matrix_, loc_row + pos_start, &ncols, &cols, PETSC_NULL);
265  for (PetscInt i=0; i<ncols; i++) {
266  if (cols[i] < pos_start || cols[i] >= pos_start+loc_size_A) {
267  b_vals++;
268  } else {
269  if (cols[i] < min) {
270  min=cols[i];
271  }
272  if (cols[i] > max) {
273  max=cols[i];
274  }
275  }
276  }
277  size_submat = max - min + 1;
278  OLD_ASSERT(ncols-b_vals == size_submat, "Submatrix cannot contains empty values.\n");
279 
280  MatRestoreRow(matrix_, loc_row + pos_start, &ncols, &cols, PETSC_NULL);
281  arma::mat submat2(size_submat, size_submat);
282  submat2.zeros();
283  for (PetscInt i=0; i<size_submat; i++) {
284  processed_rows[ loc_row + i ] = mat_block;
285  submat_rows.push_back( i + loc_row + pos_start_IA );
286  MatGetRow(matrix_, i + loc_row + pos_start, &ncols, &cols, &vals);
287  for (PetscInt j=0; j<ncols; j++) {
288  if (cols[j] >= pos_start && cols[j] < pos_start+loc_size_A) {
289  submat2( i, cols[j] - loc_row - pos_start ) = vals[j];
290  }
291  }
292  MatRestoreRow(matrix_, i + loc_row + pos_start, &ncols, &cols, &vals);
293  }
294  // get inversion matrix
295  arma::mat invmat = submat2.i();
296  // stored to inversion IA matrix
297  const PetscInt* rows = &submat_rows[0];
298  MatSetValues(IA, submat_rows.size(), rows, submat_rows.size(), rows, invmat.memptr(), INSERT_VALUES);
299 
300  mat_block++;
301  }
302 
303  MatAssemblyBegin(IA, MAT_FINAL_ASSEMBLY);
304  MatAssemblyEnd(IA, MAT_FINAL_ASSEMBLY);
305 }
306 
307 
309 {
310  if (Compl != NULL) {
311  return Compl->get_solution_precision();
312  }
313  return std::numeric_limits<double>::infinity();
314 }
315 
316 
318  START_TIMER("SchurComplement::solve");
319  this->form_schur();
320  int converged_reason = Compl->solve();
321 
322  // TODO: Resolve step is not necessary inside of nonlinear solver. Can optimize.
323  this->resolve();
324  return converged_reason;
325 }
326 
327 
328 /**
329  * COMPUTE ELIMINATED PART OF THE ORIG. SYS. & RESTORE RHS and SOLUTION VECTORS
330  * x_1 = IA * RHS_1 - IAB * x_2
331  */
332 
334 {
335  this->form_schur();
336 
337  START_TIMER("SchurComplemet::resolve without form schur");
338 
339  MatMult(IAB,Compl->get_solution(),Sol1);
340  VecScale(Sol1,-1);
341  MatMultAdd(IA,RHS1,Sol1,Sol1);
342 }
343 
344 
346 {
347  resolve();
349 }
350 
351 
352 
353 /**
354  * SCHUR COMPLEMENT destructor
355  */
357 
358  if ( B != NULL ) MatDestroy(&B);
359  if ( Bt != NULL ) MatDestroy(&Bt);
360  if ( xA != NULL ) MatDestroy(&xA);
361  if ( IA != NULL ) MatDestroy(&IA);
362  if ( IAB != NULL ) MatDestroy(&IAB);
363  if ( IsA != NULL ) ISDestroy(&IsA);
364  if ( IsB != NULL ) ISDestroy(&IsB);
365  if ( RHS1 != NULL ) VecDestroy(&RHS1);
366  if ( RHS2 != NULL ) VecDestroy(&RHS2);
367  if ( Sol1 != NULL ) VecDestroy(&Sol1);
368  if ( Sol2 != NULL ) VecDestroy(&Sol2);
369  if ( IA != NULL ) MatDestroy(&IA);
370 
371  if (Compl != NULL) delete Compl;
372 
373 }
const Vec * get_rhs()
Definition: linsys_PETSC.hh:62
bool is_negative_definite()
Definition: linsys.hh:513
void set_complement(LinSys_PETSC *ls)
Set complement LinSys object.
Definition: schur.cc:223
void resolve()
Definition: schur.cc:333
bool matrix_changed_
true if the matrix was changed since the last solve
Definition: linsys.hh:595
Wrappers for linear systems based on MPIAIJ and MATIS format.
const Mat * get_matrix()
Definition: linsys_PETSC.hh:57
Definition: schur.hh:55
void set_rhs_changed()
Definition: linsys.hh:193
Vec rhs_
PETSc vector constructed with vx array.
LinSys_PETSC * Compl
Definition: schur.hh:147
void set_from_input(const Input::Record in_rec)
int loc_size_B
Definition: schur.hh:139
Input::Record in_rec_
Definition: linsys.hh:608
bool rhs_changed_
true if the right hand side was changed since the last solve
Definition: linsys.hh:596
Distribution * ds_
Definition: schur.hh:149
~SchurComplement()
Definition: schur.cc:356
void form_rhs()
Definition: schur.cc:205
Assembly explicit Schur complement for the given linear system. Provides method for resolution of the...
SchurState state
Definition: schur.hh:144
#define OLD_ASSERT(...)
Definition: global_defs.h:131
const Vec & get_solution()
Definition: linsys.hh:257
unsigned int begin(int proc) const
get starting local index
#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 compute_residual() override
Definition: schur.cc:345
void form_schur()
Definition: schur.cc:129
int loc_size_A
Definition: schur.hh:139
bool is_empty() const
Definition: accessors.hh:351
int solve() override
Definition: schur.cc:317
void set_solution(double *sol_array)
Definition: linsys.hh:265
double compute_residual() override
SchurComplement(IS ia, Distribution *ds)
Definition: schur.cc:61
void create_inversion_matrix()
create IA matrix
Definition: schur.cc:239
Support classes for parallel programing.
double get_solution_precision() override
get precision of solving
Definition: schur.cc:308
Distribution * make_complement_distribution()
get distribution of complement object if complement is defined
Definition: schur.cc:233
Definition: schur.hh:56
double get_solution_precision()
void set_matrix_changed()
Definition: linsys.hh:187
Mat matrix_
Petsc matrix of the problem.
Vec solution_
PETSc vector constructed with vb array.
Definition: linsys.hh:598
Solver based on Multilevel BDDC - using corresponding class of OpenFTL package.
unsigned int lsize(int proc) const
get local size