Chaste Release::3.1
|
00001 /* 00002 00003 Copyright (c) 2005-2012, University of Oxford. 00004 All rights reserved. 00005 00006 University of Oxford means the Chancellor, Masters and Scholars of the 00007 University of Oxford, having an administrative office at Wellington 00008 Square, Oxford OX1 2JD, UK. 00009 00010 This file is part of Chaste. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 * Redistributions of source code must retain the above copyright notice, 00015 this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright notice, 00017 this list of conditions and the following disclaimer in the documentation 00018 and/or other materials provided with the distribution. 00019 * Neither the name of the University of Oxford nor the names of its 00020 contributors may be used to endorse or promote products derived from this 00021 software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 */ 00035 00036 #include "PetscMatTools.hpp" 00037 #include <algorithm> 00038 #include <cassert> 00039 00040 00042 // Implementation 00044 00045 void PetscMatTools::SetElement(Mat matrix, PetscInt row, PetscInt col, double value) 00046 { 00047 PetscInt lo, hi; 00048 GetOwnershipRange(matrix, lo, hi); 00049 00050 if (row >= lo && row < hi) 00051 { 00052 MatSetValue(matrix, row, col, value, INSERT_VALUES); 00053 } 00054 } 00055 00056 void PetscMatTools::AddToElement(Mat matrix, PetscInt row, PetscInt col, double value) 00057 { 00058 PetscInt lo, hi; 00059 GetOwnershipRange(matrix, lo, hi); 00060 00061 if (row >= lo && row < hi) 00062 { 00063 MatSetValue(matrix, row, col, value, ADD_VALUES); 00064 } 00065 } 00066 00067 void PetscMatTools::Finalise(Mat matrix) 00068 { 00069 MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY); 00070 MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY); 00071 } 00072 00073 void PetscMatTools::SwitchWriteMode(Mat matrix) 00074 { 00075 MatAssemblyBegin(matrix, MAT_FLUSH_ASSEMBLY); 00076 MatAssemblyEnd(matrix, MAT_FLUSH_ASSEMBLY); 00077 } 00078 00079 void PetscMatTools::Display(Mat matrix) 00080 { 00081 MatView(matrix,PETSC_VIEWER_STDOUT_WORLD); 00082 } 00083 00084 void PetscMatTools::SetRow(Mat matrix, PetscInt row, double value) 00085 { 00086 PetscInt lo, hi; 00087 GetOwnershipRange(matrix, lo, hi); 00088 00089 if (row >= lo && row < hi) 00090 { 00091 PetscInt rows, cols; 00092 MatGetSize(matrix, &rows, &cols); 00093 for (PetscInt i=0; i<cols; i++) 00094 { 00095 SetElement(matrix, row, i, value); 00096 } 00097 } 00098 } 00099 00100 void PetscMatTools::ZeroRowsWithValueOnDiagonal(Mat matrix, std::vector<unsigned>& rRows, double diagonalValue) 00101 { 00102 Finalise(matrix); 00103 00104 /* 00105 * Important! PETSc by default will destroy the sparsity structure for this row and deallocate memory 00106 * when the row is zeroed, and if there is a next timestep, the memory will have to reallocated when 00107 * assembly to done again. This can kill performance. The following makes sure the zeroed rows are kept. 00108 * 00109 * Note: if the following lines are called, then once MatZeroRows() is called below, there will be an 00110 * error if some rows have no entries at all. Hence for problems with dummy variables, Stokes flow for 00111 * example, the identity block needs to be added before dirichlet BCs. 00112 */ 00113 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 1) //PETSc 3.1 or later 00114 MatSetOption(matrix, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE); 00115 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR == 0) //PETSc 3.0 00116 MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS, PETSC_TRUE); 00117 #else //PETSc 2.x.x 00118 MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS); 00119 #endif 00120 00121 PetscInt* rows = new PetscInt[rRows.size()]; 00122 for (unsigned i=0; i<rRows.size(); i++) 00123 { 00124 rows[i] = rRows[i]; 00125 } 00126 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2 00127 IS is; 00128 ISCreateGeneral(PETSC_COMM_WORLD, rRows.size(), rows, &is); 00129 MatZeroRows(matrix, is, &diagonalValue); 00130 ISDestroy(PETSC_DESTROY_PARAM(is)); 00131 /* 00132 * 00133 [2]PETSC ERROR: MatMissingDiagonal_SeqAIJ() line 1011 in src/mat/impls/aij/seq/aij.c 00134 [2]PETSC ERROR: PETSc has generated inconsistent data! 00135 [2]PETSC ERROR: Matrix is missing diagonal number 15! 00136 [2]PETSC ERROR: MatILUFactorSymbolic_SeqAIJ() line 906 in src/mat/impls/aij/seq/aijfact.c 00137 * 00138 * 00139 // There appears to be a problem with MatZeroRows not setting diagonals correctly 00140 // While we are supporting PETSc 2.2, we have to do this the slow way 00141 00142 AssembleFinal(matrix); 00143 PetscInt lo, hi; 00144 GetOwnershipRange(matrix, lo, hi); 00145 PetscInt size=GetSize(matrix); 00147 for (unsigned index=0; index<rRows.size(); index++) 00148 { 00149 PetscInt row = rRows[index]; 00150 if (row >= lo && row < hi) 00151 { 00152 std::vector<unsigned> non_zero_cols; 00153 // This row is local, so zero it. 00154 for (PetscInt column = 0; column < size; column++) 00155 { 00156 if (GetElement(matrix, row, column) != 0.0) 00157 { 00158 non_zero_cols.push_back(column); 00159 } 00160 } 00161 // Separate "gets" from "sets" so that we don't have to keep going into "assembled" mode 00162 for (unsigned i=0; i<non_zero_cols.size();i++) 00163 { 00164 SetElement(matrix, row, non_zero_cols[i], 0.0); 00165 } 00166 // Set the diagonal 00167 SetElement(matrix, row, row, diagonalValue); 00168 } 00169 // Everyone communicate after row is finished 00170 AssembleFinal(matrix); 00171 } 00172 */ 00173 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later 00174 MatZeroRows(matrix, rRows.size(), rows, diagonalValue , NULL, NULL); 00175 #else 00176 MatZeroRows(matrix, rRows.size(), rows, diagonalValue); 00177 #endif 00178 delete [] rows; 00179 } 00180 00181 00182 void PetscMatTools::ZeroRowsAndColumnsWithValueOnDiagonal(Mat matrix, std::vector<unsigned> rowColIndices, double diagonalValue) 00183 { 00184 Finalise(matrix); 00185 00186 // sort the vector as we will be repeatedly searching for entries in it 00187 std::sort(rowColIndices.begin(), rowColIndices.end()); 00188 00189 PetscInt lo, hi; 00190 GetOwnershipRange(matrix, lo, hi); 00191 unsigned size = hi-lo; 00192 00193 std::vector<unsigned>* cols_to_zero_per_row = new std::vector<unsigned>[size]; 00194 00195 // collect the columns to be zeroed, for each row. We don't zero yet as we would 00196 // then have to repeatedly call Finalise before each MatGetRow 00197 for (PetscInt row = lo; row < hi; row++) 00198 { 00199 // get all the non-zero cols for this row 00200 PetscInt num_cols; 00201 const PetscInt* cols; 00202 MatGetRow(matrix, row, &num_cols, &cols, PETSC_NULL); 00203 00204 // see which of these cols are in the list of cols to be zeroed 00205 for(PetscInt i=0; i<num_cols; i++) 00206 { 00207 if(std::binary_search(rowColIndices.begin(), rowColIndices.end(), cols[i])) 00208 { 00209 cols_to_zero_per_row[row-lo].push_back(cols[i]); 00210 } 00211 } 00212 00213 // this must be called for each MatGetRow 00214 MatRestoreRow(matrix, row, &num_cols, &cols, PETSC_NULL); 00215 } 00216 00217 // Now zero columns of each row 00218 for (PetscInt row = lo; row < hi; row++) 00219 { 00220 unsigned num_cols_to_zero_this_row = cols_to_zero_per_row[row-lo].size(); 00221 00222 if(num_cols_to_zero_this_row>0) 00223 { 00224 PetscInt* cols_to_zero = new PetscInt[num_cols_to_zero_this_row]; 00225 double* zeros = new double[num_cols_to_zero_this_row]; 00226 for(unsigned i=0; i<num_cols_to_zero_this_row; i++) 00227 { 00228 cols_to_zero[i] = cols_to_zero_per_row[row-lo][i]; 00229 zeros[i] = 0.0; 00230 } 00231 00232 PetscInt rows[1]; 00233 rows[0] = row; 00234 MatSetValues(matrix, 1, rows, num_cols_to_zero_this_row, cols_to_zero, zeros, INSERT_VALUES); 00235 delete [] cols_to_zero; 00236 delete [] zeros; 00237 } 00238 } 00239 00240 delete [] cols_to_zero_per_row; 00241 00242 // Now zero the rows and add the diagonal entries 00243 ZeroRowsWithValueOnDiagonal(matrix, rowColIndices, diagonalValue); 00244 } 00245 00246 void PetscMatTools::ZeroColumn(Mat matrix, PetscInt col) 00247 { 00248 Finalise(matrix); 00249 00250 PetscInt lo, hi; 00251 GetOwnershipRange(matrix, lo, hi); 00252 00253 // Determine which rows in this column are non-zero (and therefore need to be zeroed) 00254 std::vector<unsigned> nonzero_rows; 00255 for (PetscInt row = lo; row < hi; row++) 00256 { 00257 if (GetElement(matrix, row, col) != 0.0) 00258 { 00259 nonzero_rows.push_back(row); 00260 } 00261 } 00262 00263 // Set those rows to be zero by calling MatSetValues 00264 unsigned size = nonzero_rows.size(); 00265 PetscInt* rows = new PetscInt[size]; 00266 PetscInt cols[1]; 00267 double* zeros = new double[size]; 00268 00269 cols[0] = col; 00270 00271 for (unsigned i=0; i<size; i++) 00272 { 00273 rows[i] = nonzero_rows[i]; 00274 zeros[i] = 0.0; 00275 } 00276 00277 MatSetValues(matrix, size, rows, 1, cols, zeros, INSERT_VALUES); 00278 delete [] rows; 00279 delete [] zeros; 00280 } 00281 00282 void PetscMatTools::Zero(Mat matrix) 00283 { 00284 MatZeroEntries(matrix); 00285 } 00286 00287 unsigned PetscMatTools::GetSize(Mat matrix) 00288 { 00289 PetscInt rows, cols; 00290 00291 MatGetSize(matrix, &rows, &cols); 00292 assert(rows == cols); 00293 return (unsigned) rows; 00294 } 00295 00296 void PetscMatTools::GetOwnershipRange(Mat matrix, PetscInt& lo, PetscInt& hi) 00297 { 00298 MatGetOwnershipRange(matrix, &lo, &hi); 00299 } 00300 00301 double PetscMatTools::GetElement(Mat matrix, PetscInt row, PetscInt col) 00302 { 00303 PetscInt lo, hi; 00304 GetOwnershipRange(matrix, lo, hi); 00305 00306 assert(lo <= row && row < hi); 00307 PetscInt row_as_array[1]; 00308 row_as_array[0] = row; 00309 PetscInt col_as_array[1]; 00310 col_as_array[0] = col; 00311 00312 double ret_array[1]; 00313 00314 MatGetValues(matrix, 1, row_as_array, 1, col_as_array, ret_array); 00315 00316 return ret_array[0]; 00317 } 00318 00319 void PetscMatTools::SetOption(Mat matrix, MatOption option) 00320 { 00321 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x 00322 MatSetOption(matrix, option, PETSC_TRUE); 00323 #else 00324 MatSetOption(matrix, option); 00325 #endif 00326 } 00327 00328 Vec PetscMatTools::GetMatrixRowDistributed(Mat matrix, unsigned rowIndex) 00329 { 00330 /* 00331 * We need to make sure that lhs_ith_row doesn't ignore off processor entries when assembling, 00332 * otherwise the VecSetValues call a few lines below will not work as expected. 00333 */ 00334 00335 PetscInt lo, hi; 00336 PetscMatTools::GetOwnershipRange(matrix, lo, hi); 00337 unsigned size = PetscMatTools::GetSize(matrix); 00338 00339 Vec mat_ith_row = PetscTools::CreateVec(size, hi-lo, false); 00340 00341 PetscInt num_entries; 00342 const PetscInt* column_indices; 00343 const PetscScalar* values; 00344 00345 bool am_row_owner = (PetscInt)rowIndex >= lo && (PetscInt)rowIndex < hi; 00346 00347 /* 00348 * Am I the owner of the row? If so get the non-zero entries and add them lhs_ith_row. 00349 * In parallel, VecAssembly{Begin,End} will send values to the rest of processors. 00350 */ 00351 if (am_row_owner) 00352 { 00353 MatGetRow(matrix, rowIndex, &num_entries, &column_indices, &values); 00354 VecSetValues(mat_ith_row, num_entries, column_indices, values, INSERT_VALUES); 00355 } 00356 00357 VecAssemblyBegin(mat_ith_row); 00358 VecAssemblyEnd(mat_ith_row); 00359 00360 if (am_row_owner) 00361 { 00362 MatRestoreRow(matrix, rowIndex, &num_entries, &column_indices, &values); 00363 } 00364 00365 return mat_ith_row; 00366 } 00367 00368 00369 bool PetscMatTools::CheckEquality(const Mat mat1, const Mat mat2, double tol) 00370 { 00371 Mat y; 00372 MatDuplicate(mat2, MAT_COPY_VALUES, &y); 00373 00374 double minus_one = -1.0; 00375 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2 00376 // MatAYPX(*a, X, Y) does Y = X + a*Y. 00377 MatAYPX(&minus_one, mat1, y); 00378 #elif (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR == 1) //PETSc 2.3.1 00379 // MatAYPX( Y, a, X) does Y = a*Y + X. 00380 MatAYPX(y, minus_one, mat1); 00381 #else 00382 // MatAYPX( Y, a, X, structure) does Y = a*Y + X. 00383 MatAYPX(y, minus_one, mat1, DIFFERENT_NONZERO_PATTERN); 00384 #endif 00385 PetscReal norm; 00386 MatNorm(y, NORM_INFINITY, &norm); 00387 PetscTools::Destroy(y); 00388 00389 return (norm < tol); 00390 } 00391 00392 bool PetscMatTools::CheckSymmetry(const Mat matrix, double tol) 00393 { 00394 Mat trans; 00395 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x 00396 MatTranspose(matrix, MAT_INITIAL_MATRIX, &trans); 00397 #else 00398 MatTranspose(matrix, &trans); 00399 #endif 00400 bool is_symmetric = PetscMatTools::CheckEquality(matrix, trans, tol); 00401 PetscTools::Destroy(trans); 00402 return is_symmetric; 00403 }