PetscMatTools.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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     //Give full precision, scientific notation
00082     PetscViewerSetFormat(PETSC_VIEWER_STDOUT_WORLD, PETSC_VIEWER_ASCII_MATLAB);
00083     MatView(matrix,PETSC_VIEWER_STDOUT_WORLD);
00084 }
00085 
00086 void PetscMatTools::SetRow(Mat matrix, PetscInt row, double value)
00087 {
00088     PetscInt lo, hi;
00089     GetOwnershipRange(matrix, lo, hi);
00090 
00091     if (row >= lo && row < hi)
00092     {
00093         PetscInt rows, cols;
00094         MatGetSize(matrix, &rows, &cols);
00095         for (PetscInt i=0; i<cols; i++)
00096         {
00097             SetElement(matrix, row, i, value);
00098         }
00099     }
00100 }
00101 
00102 void PetscMatTools::ZeroRowsWithValueOnDiagonal(Mat matrix, std::vector<unsigned>& rRows, double diagonalValue)
00103 {
00104     Finalise(matrix);
00105 
00106     /*
00107      * Important! PETSc by default will destroy the sparsity structure for this row and deallocate memory
00108      * when the row is zeroed, and if there is a next timestep, the memory will have to reallocated when
00109      * assembly to done again. This can kill performance. The following makes sure the zeroed rows are kept.
00110      *
00111      * Note: if the following lines are called, then once MatZeroRows() is called below, there will be an
00112      * error if some rows have no entries at all. Hence for problems with dummy variables, Stokes flow for
00113      * example, the identity block needs to be added before dirichlet BCs.
00114      */
00115 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 1) //PETSc 3.1 or later
00116     MatSetOption(matrix, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE);
00117 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR == 0) //PETSc 3.0
00118     MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS, PETSC_TRUE);
00119 #else //PETSc 2.x.x
00120     MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS);
00121 #endif
00122 
00123     PetscInt* rows = new PetscInt[rRows.size()];
00124     for (unsigned i=0; i<rRows.size(); i++)
00125     {
00126         rows[i] = rRows[i];
00127     }
00128 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00129     IS is;
00130     ISCreateGeneral(PETSC_COMM_WORLD, rRows.size(), rows, &is);
00131     MatZeroRows(matrix, is, &diagonalValue);
00132     ISDestroy(PETSC_DESTROY_PARAM(is));
00133     /*
00134      *
00135 [2]PETSC ERROR: MatMissingDiagonal_SeqAIJ() line 1011 in src/mat/impls/aij/seq/aij.c
00136 [2]PETSC ERROR: PETSc has generated inconsistent data!
00137 [2]PETSC ERROR: Matrix is missing diagonal number 15!
00138 [2]PETSC ERROR: MatILUFactorSymbolic_SeqAIJ() line 906 in src/mat/impls/aij/seq/aijfact.c
00139      *
00140      *
00141     // There appears to be a problem with MatZeroRows not setting diagonals correctly
00142     // While we are supporting PETSc 2.2, we have to do this the slow way
00143 
00144     AssembleFinal(matrix);
00145     PetscInt lo, hi;
00146     GetOwnershipRange(matrix, lo, hi);
00147     PetscInt size=GetSize(matrix);
00149     for (unsigned index=0; index<rRows.size(); index++)
00150     {
00151         PetscInt row = rRows[index];
00152         if (row >= lo && row < hi)
00153         {
00154             std::vector<unsigned> non_zero_cols;
00155             // This row is local, so zero it.
00156             for (PetscInt column = 0; column < size; column++)
00157             {
00158                 if (GetElement(matrix, row, column) != 0.0)
00159                 {
00160                     non_zero_cols.push_back(column);
00161                 }
00162             }
00163             // Separate "gets" from "sets" so that we don't have to keep going into "assembled" mode
00164             for (unsigned i=0; i<non_zero_cols.size();i++)
00165             {
00166                 SetElement(matrix, row, non_zero_cols[i], 0.0);
00167             }
00168             // Set the diagonal
00169             SetElement(matrix, row, row, diagonalValue);
00170         }
00171         // Everyone communicate after row is finished
00172         AssembleFinal(matrix);
00173     }
00174     */
00175 #elif (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00176     MatZeroRows(matrix, rRows.size(), rows, diagonalValue , NULL, NULL);
00177 #else
00178     MatZeroRows(matrix, rRows.size(), rows, diagonalValue);
00179 #endif
00180     delete [] rows;
00181 }
00182 
00183 
00184 void PetscMatTools::ZeroRowsAndColumnsWithValueOnDiagonal(Mat matrix, std::vector<unsigned> rowColIndices, double diagonalValue)
00185 {
00186     Finalise(matrix);
00187 
00188     // sort the vector as we will be repeatedly searching for entries in it
00189     std::sort(rowColIndices.begin(), rowColIndices.end());
00190 
00191     PetscInt lo, hi;
00192     GetOwnershipRange(matrix, lo, hi);
00193     unsigned size = hi-lo;
00194 
00195     std::vector<unsigned>* cols_to_zero_per_row = new std::vector<unsigned>[size];
00196 
00197     // collect the columns to be zeroed, for each row. We don't zero yet as we would
00198     // then have to repeatedly call Finalise before each MatGetRow
00199     for (PetscInt row = lo; row < hi; row++)
00200     {
00201         // get all the non-zero cols for this row
00202         PetscInt num_cols;
00203         const PetscInt* cols;
00204         MatGetRow(matrix, row, &num_cols, &cols, PETSC_NULL);
00205 
00206         // see which of these cols are in the list of cols to be zeroed
00207         for(PetscInt i=0; i<num_cols; i++)
00208         {
00209             if(std::binary_search(rowColIndices.begin(), rowColIndices.end(), cols[i]))
00210             {
00211                 cols_to_zero_per_row[row-lo].push_back(cols[i]);
00212             }
00213         }
00214 
00215         // this must be called for each MatGetRow
00216         MatRestoreRow(matrix, row, &num_cols, &cols, PETSC_NULL);
00217     }
00218 
00219     // Now zero columns of each row
00220     for (PetscInt row = lo; row < hi; row++)
00221     {
00222         unsigned num_cols_to_zero_this_row = cols_to_zero_per_row[row-lo].size();
00223 
00224         if(num_cols_to_zero_this_row>0)
00225         {
00226             PetscInt* cols_to_zero = new PetscInt[num_cols_to_zero_this_row];
00227             double* zeros = new double[num_cols_to_zero_this_row];
00228             for(unsigned i=0; i<num_cols_to_zero_this_row; i++)
00229             {
00230                 cols_to_zero[i] = cols_to_zero_per_row[row-lo][i];
00231                 zeros[i] = 0.0;
00232             }
00233 
00234             PetscInt rows[1];
00235             rows[0] = row;
00236             MatSetValues(matrix, 1, rows, num_cols_to_zero_this_row, cols_to_zero, zeros, INSERT_VALUES);
00237             delete [] cols_to_zero;
00238             delete [] zeros;
00239         }
00240     }
00241 
00242     delete [] cols_to_zero_per_row;
00243 
00244     // Now zero the rows and add the diagonal entries
00245     ZeroRowsWithValueOnDiagonal(matrix, rowColIndices, diagonalValue);
00246 }
00247 
00248 void PetscMatTools::ZeroColumn(Mat matrix, PetscInt col)
00249 {
00250     Finalise(matrix);
00251 
00252     PetscInt lo, hi;
00253     GetOwnershipRange(matrix, lo, hi);
00254 
00255     // Determine which rows in this column are non-zero (and therefore need to be zeroed)
00256     std::vector<unsigned> nonzero_rows;
00257     for (PetscInt row = lo; row < hi; row++)
00258     {
00259         if (GetElement(matrix, row, col) != 0.0)
00260         {
00261             nonzero_rows.push_back(row);
00262         }
00263     }
00264 
00265     // Set those rows to be zero by calling MatSetValues
00266     unsigned size = nonzero_rows.size();
00267     PetscInt* rows = new PetscInt[size];
00268     PetscInt cols[1];
00269     double* zeros = new double[size];
00270 
00271     cols[0] = col;
00272 
00273     for (unsigned i=0; i<size; i++)
00274     {
00275         rows[i] = nonzero_rows[i];
00276         zeros[i] = 0.0;
00277     }
00278 
00279     MatSetValues(matrix, size, rows, 1, cols, zeros, INSERT_VALUES);
00280     delete [] rows;
00281     delete [] zeros;
00282 }
00283 
00284 void PetscMatTools::Zero(Mat matrix)
00285 {
00286     MatZeroEntries(matrix);
00287 }
00288 
00289 unsigned PetscMatTools::GetSize(Mat matrix)
00290 {
00291     PetscInt rows, cols;
00292 
00293     MatGetSize(matrix, &rows, &cols);
00294     assert(rows == cols);
00295     return (unsigned) rows;
00296 }
00297 
00298 void PetscMatTools::GetOwnershipRange(Mat matrix, PetscInt& lo, PetscInt& hi)
00299 {
00300     MatGetOwnershipRange(matrix, &lo, &hi);
00301 }
00302 
00303 double PetscMatTools::GetElement(Mat matrix, PetscInt row, PetscInt col)
00304 {
00305     PetscInt lo, hi;
00306     GetOwnershipRange(matrix, lo, hi);
00307 
00308     assert(lo <= row && row < hi);
00309     PetscInt row_as_array[1];
00310     row_as_array[0] = row;
00311     PetscInt col_as_array[1];
00312     col_as_array[0] = col;
00313 
00314     double ret_array[1];
00315 
00316     MatGetValues(matrix, 1, row_as_array, 1, col_as_array, ret_array);
00317 
00318     return ret_array[0];
00319 }
00320 
00321 void PetscMatTools::SetOption(Mat matrix, MatOption option)
00322 {
00323 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00324     MatSetOption(matrix, option, PETSC_TRUE);
00325 #else
00326     MatSetOption(matrix, option);
00327 #endif
00328 }
00329 
00330 Vec PetscMatTools::GetMatrixRowDistributed(Mat matrix, unsigned rowIndex)
00331 {
00332     /*
00333      * We need to make sure that lhs_ith_row doesn't ignore off processor entries when assembling,
00334      * otherwise the VecSetValues call a few lines below will not work as expected.
00335      */
00336 
00337     PetscInt lo, hi;
00338     PetscMatTools::GetOwnershipRange(matrix, lo, hi);
00339     unsigned size = PetscMatTools::GetSize(matrix);
00340 
00341     Vec mat_ith_row = PetscTools::CreateVec(size, hi-lo, false);
00342 
00343     PetscInt num_entries;
00344     const PetscInt* column_indices;
00345     const PetscScalar* values;
00346 
00347     bool am_row_owner = (PetscInt)rowIndex >= lo && (PetscInt)rowIndex < hi;
00348 
00349     /*
00350      * Am I the owner of the row? If so get the non-zero entries and add them lhs_ith_row.
00351      * In parallel, VecAssembly{Begin,End} will send values to the rest of processors.
00352      */
00353     if (am_row_owner)
00354     {
00355         MatGetRow(matrix, rowIndex, &num_entries, &column_indices, &values);
00356         VecSetValues(mat_ith_row, num_entries, column_indices, values, INSERT_VALUES);
00357     }
00358 
00359     VecAssemblyBegin(mat_ith_row);
00360     VecAssemblyEnd(mat_ith_row);
00361 
00362     if (am_row_owner)
00363     {
00364         MatRestoreRow(matrix, rowIndex, &num_entries, &column_indices, &values);
00365     }
00366 
00367     return mat_ith_row;
00368 }
00369 
00370 
00371 bool PetscMatTools::CheckEquality(const Mat mat1, const Mat mat2, double tol)
00372 {
00373     Mat y;
00374     MatDuplicate(mat2, MAT_COPY_VALUES, &y);
00375 
00376     double minus_one = -1.0;
00377 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00378     // MatAYPX(*a, X, Y) does  Y = X + a*Y.
00379     MatAYPX(&minus_one, mat1, y);
00380 #elif (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR == 1) //PETSc 2.3.1
00381     // MatAYPX( Y, a, X) does Y = a*Y + X.
00382     MatAYPX(y, minus_one, mat1);
00383 #else
00384     // MatAYPX( Y, a, X, structure) does Y = a*Y + X.
00385     MatAYPX(y, minus_one, mat1, DIFFERENT_NONZERO_PATTERN);
00386 #endif
00387     PetscReal norm;
00388     MatNorm(y, NORM_INFINITY, &norm);
00389     PetscTools::Destroy(y);
00390 
00391     return (norm < tol);
00392 }
00393 
00394 bool PetscMatTools::CheckSymmetry(const Mat matrix, double tol)
00395 {
00396     Mat trans;
00397 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00398     MatTranspose(matrix, MAT_INITIAL_MATRIX, &trans);
00399 #else
00400     MatTranspose(matrix, &trans);
00401 #endif
00402     bool is_symmetric = PetscMatTools::CheckEquality(matrix, trans, tol);
00403     PetscTools::Destroy(trans);
00404     return is_symmetric;
00405 }
00406 
00407 void PetscMatTools::TurnOffVariableAllocationError(Mat matrix)
00408 {
00409 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 3) //PETSc 3.3 or later
00410     MatSetOption(matrix, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
00411 #endif
00412 }
00413 

Generated by  doxygen 1.6.2