Chaste Release::3.1
PetscMatTools.cpp
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 }