PetscMatTools.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 #include "PetscMatTools.hpp"
00030 #include <cassert>
00031 
00033 // Implementation
00035 
00036 void PetscMatTools::SetElement(Mat matrix, PetscInt row, PetscInt col, double value)
00037 {
00038     PetscInt lo, hi;
00039     GetOwnershipRange(matrix, lo, hi);
00040 
00041     if (row >= lo && row < hi)
00042     {
00043         MatSetValue(matrix, row, col, value, INSERT_VALUES);
00044     }
00045 }
00046 
00047 void PetscMatTools::AddToElement(Mat matrix, PetscInt row, PetscInt col, double value)
00048 {
00049     PetscInt lo, hi;
00050     GetOwnershipRange(matrix, lo, hi);
00051 
00052     if (row >= lo && row < hi)
00053     {
00054         MatSetValue(matrix, row, col, value, ADD_VALUES);
00055     }
00056 }
00057 
00058 void PetscMatTools::Finalise(Mat matrix)
00059 {
00060     MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY);
00061     MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY);
00062 }
00063 
00064 void PetscMatTools::SwitchWriteMode(Mat matrix)
00065 {
00066     MatAssemblyBegin(matrix, MAT_FLUSH_ASSEMBLY);
00067     MatAssemblyEnd(matrix, MAT_FLUSH_ASSEMBLY);
00068 }
00069 
00070 void PetscMatTools::Display(Mat matrix)
00071 {
00072     MatView(matrix,PETSC_VIEWER_STDOUT_WORLD);
00073 }
00074 
00075 void PetscMatTools::SetRow(Mat matrix, PetscInt row, double value)
00076 {
00077     PetscInt lo, hi;
00078     GetOwnershipRange(matrix, lo, hi);
00079 
00080     if (row >= lo && row < hi)
00081     {
00082         PetscInt rows, cols;
00083         MatGetSize(matrix, &rows, &cols);
00084         for (PetscInt i=0; i<cols; i++)
00085         {
00086             SetElement(matrix, row, i, value);
00087         }
00088     }
00089 }
00090 
00091 void PetscMatTools::ZeroRowsWithValueOnDiagonal(Mat matrix, std::vector<unsigned>& rRows, double diagonalValue)
00092 {
00093     Finalise(matrix);
00094 
00095     /*
00096      * Important! PETSc by default will destroy the sparsity structure for this row and deallocate memory
00097      * when the row is zeroed, and if there is a next timestep, the memory will have to reallocated when
00098      * assembly to done again. This can kill performance. The following makes sure the zeroed rows are kept.
00099      */
00100 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00101  #if (PETSC_VERSION_MINOR == 0)
00102     MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS, PETSC_TRUE);
00103  #else
00104     MatSetOption(matrix, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE);
00105  #endif
00106 #else
00107     MatSetOption(matrix, MAT_KEEP_ZEROED_ROWS);
00108 #endif
00109 
00110     PetscInt* rows = new PetscInt[rRows.size()];
00111     for (unsigned i=0; i<rRows.size(); i++)
00112     {
00113         rows[i] = rRows[i];
00114     }
00115 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00116     IS is;
00117     ISCreateGeneral(PETSC_COMM_WORLD, rRows.size(), rows, &is);
00118     MatZeroRows(matrix, is, &diagonalValue);
00119     ISDestroy(is);
00120     /*
00121      *
00122 [2]PETSC ERROR: MatMissingDiagonal_SeqAIJ() line 1011 in src/mat/impls/aij/seq/aij.c
00123 [2]PETSC ERROR: PETSc has generated inconsistent data!
00124 [2]PETSC ERROR: Matrix is missing diagonal number 15!
00125 [2]PETSC ERROR: MatILUFactorSymbolic_SeqAIJ() line 906 in src/mat/impls/aij/seq/aijfact.c
00126      *
00127      *
00128     // There appears to be a problem with MatZeroRows not setting diagonals correctly
00129     // While we are supporting PETSc 2.2, we have to do this the slow way
00130 
00131     AssembleFinal(matrix);
00132     PetscInt lo, hi;
00133     GetOwnershipRange(matrix, lo, hi);
00134     PetscInt size=GetSize(matrix);
00136     for (unsigned index=0; index<rRows.size(); index++)
00137     {
00138         PetscInt row = rRows[index];
00139         if (row >= lo && row < hi)
00140         {
00141             std::vector<unsigned> non_zero_cols;
00142             // This row is local, so zero it.
00143             for (PetscInt column = 0; column < size; column++)
00144             {
00145                 if (GetElement(matrix, row, column) != 0.0)
00146                 {
00147                     non_zero_cols.push_back(column);
00148                 }
00149             }
00150             // Separate "gets" from "sets" so that we don't have to keep going into "assembled" mode
00151             for (unsigned i=0; i<non_zero_cols.size();i++)
00152             {
00153                 SetElement(matrix, row, non_zero_cols[i], 0.0);
00154             }
00155             // Set the diagonal
00156             SetElement(matrix, row, row, diagonalValue);
00157         }
00158         // Everyone communicate after row is finished
00159         AssembleFinal(matrix);
00160     }
00161     */
00162 #else
00163     MatZeroRows(matrix, rRows.size(), rows, diagonalValue);
00164 #endif
00165     delete [] rows;
00166 }
00167 
00168 
00169 void PetscMatTools::ZeroRowsAndColumnsWithValueOnDiagonal(Mat matrix, std::vector<unsigned>& rRowColIndices, double diagonalValue)
00170 {
00171     Finalise(matrix);
00172 
00173     PetscInt lo, hi;
00174     GetOwnershipRange(matrix, lo, hi);
00175     const unsigned num_cols = rRowColIndices.size();
00176     std::vector<unsigned>* p_nonzero_rows_per_column = new std::vector<unsigned>[num_cols];
00177 
00178     /*
00179      * For each column: collect all the row indices corresponding to a non-zero entry.
00180      * We do all the columns at once, before doing the zeroing, as otherwise a
00181      * MatAssemblyBegin() & MatAssemblyEnd() would have to be called after every
00182      * MatSetValues and before the below MatGetValues.
00183      *
00184      * Note that looping over rows first and getting the column values in one hit is
00185      * *much* more efficient than looping over columns first and calling GetElement
00186      * for each entry!
00187      */
00188     PetscReal* col_values = new PetscReal[num_cols];
00189     PetscInt* col_indices = new PetscInt[num_cols];
00190     for (unsigned col=0; col<num_cols; col++)
00191     {
00192         col_indices[col] = rRowColIndices[col];
00193     }
00194     for (PetscInt row = lo; row < hi; row++)
00195     {
00196         MatGetValues(matrix, 1, &row, num_cols, col_indices, col_values);
00197 
00198         for (unsigned index=0; index<num_cols; index++)
00199         {
00200             if (col_values[index] != 0)
00201             {
00202                 p_nonzero_rows_per_column[index].push_back(row);
00203             }
00204         }
00205     }
00206     delete[] col_values;
00207     delete[] col_indices;
00208 
00209     // Now zero each column in turn
00210     for (unsigned index=0; index<num_cols; index++)
00211     {
00212         // set those rows to be zero by calling MatSetValues
00213         unsigned size = p_nonzero_rows_per_column[index].size();
00214         PetscInt* rows = new PetscInt[size];
00215         PetscInt cols[1];
00216         double* zeros = new double[size];
00217 
00218         cols[0] = rRowColIndices[index];
00219 
00220         for (unsigned i=0; i<size; i++)
00221         {
00222             rows[i] = p_nonzero_rows_per_column[index][i];
00223             zeros[i] = 0.0;
00224         }
00225 
00226         MatSetValues(matrix, size, rows, 1, cols, zeros, INSERT_VALUES);
00227         delete [] rows;
00228         delete [] zeros;
00229     }
00230     delete[] p_nonzero_rows_per_column;
00231 
00232     // Now zero the rows and add the diagonal entries
00233     ZeroRowsWithValueOnDiagonal(matrix, rRowColIndices, diagonalValue);
00234 }
00235 
00236 void PetscMatTools::ZeroColumn(Mat matrix, PetscInt col)
00237 {
00238     Finalise(matrix);
00239 
00240     PetscInt lo, hi;
00241     GetOwnershipRange(matrix, lo, hi);
00242 
00243     // Determine which rows in this column are non-zero (and therefore need to be zeroed)
00244     std::vector<unsigned> nonzero_rows;
00245     for (PetscInt row = lo; row < hi; row++)
00246     {
00247         if (GetElement(matrix, row, col) != 0.0)
00248         {
00249             nonzero_rows.push_back(row);
00250         }
00251     }
00252 
00253     // Set those rows to be zero by calling MatSetValues
00254     unsigned size = nonzero_rows.size();
00255     PetscInt* rows = new PetscInt[size];
00256     PetscInt cols[1];
00257     double* zeros = new double[size];
00258 
00259     cols[0] = col;
00260 
00261     for (unsigned i=0; i<size; i++)
00262     {
00263         rows[i] = nonzero_rows[i];
00264         zeros[i] = 0.0;
00265     }
00266 
00267     MatSetValues(matrix, size, rows, 1, cols, zeros, INSERT_VALUES);
00268     delete [] rows;
00269     delete [] zeros;
00270 }
00271 
00272 void PetscMatTools::Zero(Mat matrix)
00273 {
00274     MatZeroEntries(matrix);
00275 }
00276 
00277 unsigned PetscMatTools::GetSize(Mat matrix)
00278 {
00279     PetscInt rows, cols;
00280 
00281     MatGetSize(matrix, &rows, &cols);
00282     assert(rows == cols);
00283     return (unsigned) rows;
00284 }
00285 
00286 void PetscMatTools::GetOwnershipRange(Mat matrix, PetscInt& lo, PetscInt& hi)
00287 {
00288     MatGetOwnershipRange(matrix, &lo, &hi);
00289 }
00290 
00291 double PetscMatTools::GetElement(Mat matrix, PetscInt row, PetscInt col)
00292 {
00293     PetscInt lo, hi;
00294     GetOwnershipRange(matrix, lo, hi);
00295 
00296     assert(lo <= row && row < hi);
00297     PetscInt row_as_array[1];
00298     row_as_array[0] = row;
00299     PetscInt col_as_array[1];
00300     col_as_array[0] = col;
00301 
00302     double ret_array[1];
00303 
00304     MatGetValues(matrix, 1, row_as_array, 1, col_as_array, ret_array);
00305 
00306     return ret_array[0];
00307 }
00308 
00309 void PetscMatTools::SetOption(Mat matrix, MatOption option)
00310 {
00311 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00312     MatSetOption(matrix, option, PETSC_TRUE);
00313 #else
00314     MatSetOption(matrix, option);
00315 #endif
00316 }
00317 
00318 Vec PetscMatTools::GetMatrixRowDistributed(Mat matrix, unsigned rowIndex)
00319 {
00320     /*
00321      * We need to make sure that lhs_ith_row doesn't ignore off processor entries when assembling,
00322      * otherwise the VecSetValues call a few lines below will not work as expected.
00323      */
00324 
00325     PetscInt lo, hi;
00326     PetscMatTools::GetOwnershipRange(matrix, lo, hi);
00327     unsigned size = PetscMatTools::GetSize(matrix);
00328 
00329     Vec mat_ith_row = PetscTools::CreateVec(size, hi-lo, false);
00330 
00331     PetscInt num_entries;
00332     const PetscInt* column_indices;
00333     const PetscScalar* values;
00334 
00335     bool am_row_owner = (PetscInt)rowIndex >= lo && (PetscInt)rowIndex < hi;
00336 
00337     /*
00338      * Am I the owner of the row? If so get the non-zero entries and add them lhs_ith_row.
00339      * In parallel, VecAssembly{Begin,End} will send values to the rest of processors.
00340      */
00341     if (am_row_owner)
00342     {
00343         MatGetRow(matrix, rowIndex, &num_entries, &column_indices, &values);
00344         VecSetValues(mat_ith_row, num_entries, column_indices, values, INSERT_VALUES);
00345     }
00346 
00347     VecAssemblyBegin(mat_ith_row);
00348     VecAssemblyEnd(mat_ith_row);
00349 
00350     if (am_row_owner)
00351     {
00352         MatRestoreRow(matrix, rowIndex, &num_entries, &column_indices, &values);
00353     }
00354 
00355     return mat_ith_row;
00356 }
00357 
00358 
00359 bool PetscMatTools::CheckEquality(const Mat mat1, const Mat mat2, double tol)
00360 {
00361     Mat y;
00362     MatDuplicate(mat2, MAT_COPY_VALUES, &y);
00363 
00364     double minus_one = -1.0;
00365 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00366     // MatAYPX(*a, X, Y) does  Y = X + a*Y.
00367     MatAYPX(&minus_one, mat1, y);
00368 #elif (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR == 1) //PETSc 2.3.1
00369     // MatAYPX( Y, a, X) does Y = a*Y + X.
00370     MatAYPX(y, minus_one, mat1);
00371 #else
00372     // MatAYPX( Y, a, X, structure) does Y = a*Y + X.
00373     MatAYPX(y, minus_one, mat1, DIFFERENT_NONZERO_PATTERN);
00374 #endif
00375     PetscReal norm;
00376     MatNorm(y, NORM_INFINITY, &norm);
00377     MatDestroy(y);
00378 
00379     return (norm < tol);
00380 }
00381 
00382 bool PetscMatTools::CheckSymmetry(const Mat matrix, double tol)
00383 {
00384     Mat trans;
00385 #if PETSC_VERSION_MAJOR==2
00386     MatTranspose(matrix, &trans);
00387 #else
00388     MatTranspose(matrix, MAT_INITIAL_MATRIX, &trans);
00389 #endif
00390     bool is_symmetric = PetscMatTools::CheckEquality(matrix, trans, tol);
00391     MatDestroy(trans);
00392     return is_symmetric;
00393 }
Generated on Thu Dec 22 13:00:07 2011 for Chaste by  doxygen 1.6.3