Chaste  Release::3.4
PetscMatTools.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "PetscMatTools.hpp"
37 #include <algorithm>
38 #include <cassert>
39 
40 
42 // Implementation
44 
45 void PetscMatTools::SetElement(Mat matrix, PetscInt row, PetscInt col, double value)
46 {
47  PetscInt lo, hi;
48  GetOwnershipRange(matrix, lo, hi);
49 
50  if (row >= lo && row < hi)
51  {
52  MatSetValue(matrix, row, col, value, INSERT_VALUES);
53  }
54 }
55 
56 void PetscMatTools::AddToElement(Mat matrix, PetscInt row, PetscInt col, double value)
57 {
58  PetscInt lo, hi;
59  GetOwnershipRange(matrix, lo, hi);
60 
61  if (row >= lo && row < hi)
62  {
63  MatSetValue(matrix, row, col, value, ADD_VALUES);
64  }
65 }
66 
68 {
69  MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY);
70  MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY);
71 }
72 
74 {
75  MatAssemblyBegin(matrix, MAT_FLUSH_ASSEMBLY);
76  MatAssemblyEnd(matrix, MAT_FLUSH_ASSEMBLY);
77 }
78 
80 {
81  //Give full precision, scientific notation
82  PetscViewerSetFormat(PETSC_VIEWER_STDOUT_WORLD, PETSC_VIEWER_ASCII_MATLAB);
83  MatView(matrix,PETSC_VIEWER_STDOUT_WORLD);
84 }
85 
86 void PetscMatTools::SetRow(Mat matrix, PetscInt row, double value)
87 {
88  PetscInt lo, hi;
89  GetOwnershipRange(matrix, lo, hi);
90 
91  if (row >= lo && row < hi)
92  {
93  PetscInt rows, cols;
94  MatGetSize(matrix, &rows, &cols);
95  for (PetscInt i=0; i<cols; i++)
96  {
97  SetElement(matrix, row, i, value);
98  }
99  }
100 }
101 
102 void PetscMatTools::ZeroRowsWithValueOnDiagonal(Mat matrix, std::vector<unsigned>& rRows, double diagonalValue)
103 {
104  Finalise(matrix);
105 
106  /*
107  * Important! PETSc by default will destroy the sparsity structure for this row and deallocate memory
108  * when the row is zeroed, and if there is a next timestep, the memory will have to reallocated when
109  * assembly to done again. This can kill performance. The following makes sure the zeroed rows are kept.
110  *
111  * Note: if the following lines are called, then once MatZeroRows() is called below, there will be an
112  * error if some rows have no entries at all. Hence for problems with dummy variables, Stokes flow for
113  * example, the identity block needs to be added before dirichlet BCs.
114  */
115 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 1) //PETSc 3.1 or later
116  MatSetOption(matrix, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE);
117 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR == 0) //PETSc 3.0
118  MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS, PETSC_TRUE);
119 #else //PETSc 2.x.x
120  MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS);
121 #endif
122 
123  PetscInt* rows = new PetscInt[rRows.size()];
124  for (unsigned i=0; i<rRows.size(); i++)
125  {
126  rows[i] = rRows[i];
127  }
128 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
129  IS is;
130  ISCreateGeneral(PETSC_COMM_WORLD, rRows.size(), rows, &is);
131  MatZeroRows(matrix, is, &diagonalValue);
132  ISDestroy(PETSC_DESTROY_PARAM(is));
133  /*
134  *
135 [2]PETSC ERROR: MatMissingDiagonal_SeqAIJ() line 1011 in src/mat/impls/aij/seq/aij.c
136 [2]PETSC ERROR: PETSc has generated inconsistent data!
137 [2]PETSC ERROR: Matrix is missing diagonal number 15!
138 [2]PETSC ERROR: MatILUFactorSymbolic_SeqAIJ() line 906 in src/mat/impls/aij/seq/aijfact.c
139  *
140  *
141  // There appears to be a problem with MatZeroRows not setting diagonals correctly
142  // While we are supporting PETSc 2.2, we have to do this the slow way
143 
144  AssembleFinal(matrix);
145  PetscInt lo, hi;
146  GetOwnershipRange(matrix, lo, hi);
147  PetscInt size=GetSize(matrix);
149  for (unsigned index=0; index<rRows.size(); index++)
150  {
151  PetscInt row = rRows[index];
152  if (row >= lo && row < hi)
153  {
154  std::vector<unsigned> non_zero_cols;
155  // This row is local, so zero it.
156  for (PetscInt column = 0; column < size; column++)
157  {
158  if (GetElement(matrix, row, column) != 0.0)
159  {
160  non_zero_cols.push_back(column);
161  }
162  }
163  // Separate "gets" from "sets" so that we don't have to keep going into "assembled" mode
164  for (unsigned i=0; i<non_zero_cols.size();i++)
165  {
166  SetElement(matrix, row, non_zero_cols[i], 0.0);
167  }
168  // Set the diagonal
169  SetElement(matrix, row, row, diagonalValue);
170  }
171  // Everyone communicate after row is finished
172  AssembleFinal(matrix);
173  }
174  */
175 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
176  MatZeroRows(matrix, rRows.size(), rows, diagonalValue , NULL, NULL);
177 #else
178  MatZeroRows(matrix, rRows.size(), rows, diagonalValue);
179 #endif
180  delete [] rows;
181 }
182 
183 
184 void PetscMatTools::ZeroRowsAndColumnsWithValueOnDiagonal(Mat matrix, std::vector<unsigned> rowColIndices, double diagonalValue)
185 {
186  Finalise(matrix);
187 
188  // sort the vector as we will be repeatedly searching for entries in it
189  std::sort(rowColIndices.begin(), rowColIndices.end());
190 
191  PetscInt lo, hi;
192  GetOwnershipRange(matrix, lo, hi);
193  unsigned size = hi-lo;
194 
195  std::vector<unsigned>* cols_to_zero_per_row = new std::vector<unsigned>[size];
196 
197  // collect the columns to be zeroed, for each row. We don't zero yet as we would
198  // then have to repeatedly call Finalise before each MatGetRow
199  for (PetscInt row = lo; row < hi; row++)
200  {
201  // get all the non-zero cols for this row
202  PetscInt num_cols;
203  const PetscInt* cols;
204  MatGetRow(matrix, row, &num_cols, &cols, PETSC_NULL);
205 
206  // see which of these cols are in the list of cols to be zeroed
207  for(PetscInt i=0; i<num_cols; i++)
208  {
209  if(std::binary_search(rowColIndices.begin(), rowColIndices.end(), cols[i]))
210  {
211  cols_to_zero_per_row[row-lo].push_back(cols[i]);
212  }
213  }
214 
215  // this must be called for each MatGetRow
216  MatRestoreRow(matrix, row, &num_cols, &cols, PETSC_NULL);
217  }
218 
219  // Now zero columns of each row
220  for (PetscInt row = lo; row < hi; row++)
221  {
222  unsigned num_cols_to_zero_this_row = cols_to_zero_per_row[row-lo].size();
223 
224  if(num_cols_to_zero_this_row>0)
225  {
226  PetscInt* cols_to_zero = new PetscInt[num_cols_to_zero_this_row];
227  double* zeros = new double[num_cols_to_zero_this_row];
228  for(unsigned i=0; i<num_cols_to_zero_this_row; i++)
229  {
230  cols_to_zero[i] = cols_to_zero_per_row[row-lo][i];
231  zeros[i] = 0.0;
232  }
233 
234  PetscInt rows[1];
235  rows[0] = row;
236  MatSetValues(matrix, 1, rows, num_cols_to_zero_this_row, cols_to_zero, zeros, INSERT_VALUES);
237  delete [] cols_to_zero;
238  delete [] zeros;
239  }
240  }
241 
242  delete [] cols_to_zero_per_row;
243 
244  // Now zero the rows and add the diagonal entries
245  ZeroRowsWithValueOnDiagonal(matrix, rowColIndices, diagonalValue);
246 }
247 
249 {
250  Finalise(matrix);
251 
252  PetscInt lo, hi;
253  GetOwnershipRange(matrix, lo, hi);
254 
255  // Determine which rows in this column are non-zero (and therefore need to be zeroed)
256  std::vector<unsigned> nonzero_rows;
257  for (PetscInt row = lo; row < hi; row++)
258  {
259  if (GetElement(matrix, row, col) != 0.0)
260  {
261  nonzero_rows.push_back(row);
262  }
263  }
264 
265  // Set those rows to be zero by calling MatSetValues
266  unsigned size = nonzero_rows.size();
267  PetscInt* rows = new PetscInt[size];
268  PetscInt cols[1];
269  double* zeros = new double[size];
270 
271  cols[0] = col;
272 
273  for (unsigned i=0; i<size; i++)
274  {
275  rows[i] = nonzero_rows[i];
276  zeros[i] = 0.0;
277  }
278 
279  MatSetValues(matrix, size, rows, 1, cols, zeros, INSERT_VALUES);
280  delete [] rows;
281  delete [] zeros;
282 }
283 
285 {
286  MatZeroEntries(matrix);
287 }
288 
289 unsigned PetscMatTools::GetSize(Mat matrix)
290 {
291  PetscInt rows, cols;
292 
293  MatGetSize(matrix, &rows, &cols);
294  assert(rows == cols);
295  return (unsigned) rows;
296 }
297 
299 {
300  MatGetOwnershipRange(matrix, &lo, &hi);
301 }
302 
304 {
305  PetscInt lo, hi;
306  GetOwnershipRange(matrix, lo, hi);
307 
308  assert(lo <= row && row < hi);
309  PetscInt row_as_array[1];
310  row_as_array[0] = row;
311  PetscInt col_as_array[1];
312  col_as_array[0] = col;
313 
314  double ret_array[1];
315 
316  MatGetValues(matrix, 1, row_as_array, 1, col_as_array, ret_array);
317 
318  return ret_array[0];
319 }
320 
321 void PetscMatTools::SetOption(Mat matrix, MatOption option)
322 {
323 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
324  MatSetOption(matrix, option, PETSC_TRUE);
325 #else
326  MatSetOption(matrix, option);
327 #endif
328 }
329 
331 {
332  /*
333  * We need to make sure that lhs_ith_row doesn't ignore off processor entries when assembling,
334  * otherwise the VecSetValues call a few lines below will not work as expected.
335  */
336 
337  PetscInt lo, hi;
338  PetscMatTools::GetOwnershipRange(matrix, lo, hi);
339  unsigned size = PetscMatTools::GetSize(matrix);
340 
341  Vec mat_ith_row = PetscTools::CreateVec(size, hi-lo, false);
342 
343  PetscInt num_entries;
344  const PetscInt* column_indices;
345  const PetscScalar* values;
346 
347  bool am_row_owner = (PetscInt)rowIndex >= lo && (PetscInt)rowIndex < hi;
348 
349  /*
350  * Am I the owner of the row? If so get the non-zero entries and add them lhs_ith_row.
351  * In parallel, VecAssembly{Begin,End} will send values to the rest of processors.
352  */
353  if (am_row_owner)
354  {
355  MatGetRow(matrix, rowIndex, &num_entries, &column_indices, &values);
356  VecSetValues(mat_ith_row, num_entries, column_indices, values, INSERT_VALUES);
357  }
358 
359  VecAssemblyBegin(mat_ith_row);
360  VecAssemblyEnd(mat_ith_row);
361 
362  if (am_row_owner)
363  {
364  MatRestoreRow(matrix, rowIndex, &num_entries, &column_indices, &values);
365  }
366 
367  return mat_ith_row;
368 }
369 
370 
371 bool PetscMatTools::CheckEquality(const Mat mat1, const Mat mat2, double tol)
372 {
373  Mat y;
374  MatDuplicate(mat2, MAT_COPY_VALUES, &y);
375 
376  double minus_one = -1.0;
377 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
378  // MatAYPX(*a, X, Y) does Y = X + a*Y.
379  MatAYPX(&minus_one, mat1, y);
380 #elif (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR == 1) //PETSc 2.3.1
381  // MatAYPX( Y, a, X) does Y = a*Y + X.
382  MatAYPX(y, minus_one, mat1);
383 #else
384  // MatAYPX( Y, a, X, structure) does Y = a*Y + X.
385  MatAYPX(y, minus_one, mat1, DIFFERENT_NONZERO_PATTERN);
386 #endif
387  PetscReal norm;
388  MatNorm(y, NORM_INFINITY, &norm);
390 
391  return (norm < tol);
392 }
393 
394 bool PetscMatTools::CheckSymmetry(const Mat matrix, double tol)
395 {
396  Mat trans;
397 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
398  MatTranspose(matrix, MAT_INITIAL_MATRIX, &trans);
399 #else
400  MatTranspose(matrix, &trans);
401 #endif
402  bool is_symmetric = PetscMatTools::CheckEquality(matrix, trans, tol);
403  PetscTools::Destroy(trans);
404  return is_symmetric;
405 }
406 
408 {
409 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 3) //PETSc 3.3 or later
410  MatSetOption(matrix, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
411 #endif
412 }
413 
static void TurnOffVariableAllocationError(Mat matrix)
#define PETSC_DESTROY_PARAM(x)
Definition: PetscTools.hpp:69
static Vec CreateVec(int size, int localSize=PETSC_DECIDE, bool ignoreOffProcEntries=true)
Definition: PetscTools.cpp:214
static void GetOwnershipRange(Mat matrix, PetscInt &lo, PetscInt &hi)
static void AddToElement(Mat matrix, PetscInt row, PetscInt col, double value)
static Vec GetMatrixRowDistributed(Mat matrix, unsigned rowIndex)
static void Zero(Mat matrix)
static void Display(Mat matrix)
static double GetElement(Mat matrix, PetscInt row, PetscInt col)
static bool CheckEquality(const Mat mat1, const Mat mat2, double tol=1e-10)
static void SetElement(Mat matrix, PetscInt row, PetscInt col, double value)
static void ZeroRowsWithValueOnDiagonal(Mat matrix, std::vector< unsigned > &rRows, double diagonalValue)
static void ZeroRowsAndColumnsWithValueOnDiagonal(Mat matrix, std::vector< unsigned > rowColIndices, double diagonalValue)
static void SwitchWriteMode(Mat matrix)
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:351
static bool CheckSymmetry(const Mat matrix, double tol=1e-10)
static void SetOption(Mat matrix, MatOption option)
static void Finalise(Mat matrix)
static unsigned GetSize(Mat matrix)
static void SetRow(Mat matrix, PetscInt row, double value)
static void ZeroColumn(Mat matrix, PetscInt col)