LinearSystem.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2010
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 "LinearSystem.hpp"
00030 #include "PetscException.hpp"
00031 #include <iostream>
00032 #include "OutputFileHandler.hpp"
00033 #include "PetscTools.hpp"
00034 #include <cassert>
00035 #include "HeartEventHandler.hpp"
00036 #include "Timer.hpp"
00037 
00038 
00040 // Implementation
00042 
00043 LinearSystem::LinearSystem(PetscInt lhsVectorSize, MatType matType)
00044    :mSize(lhsVectorSize),
00045     mMatNullSpace(NULL),
00046     mDestroyMatAndVec(true),
00047     mKspIsSetup(false),
00048     mNonZerosUsed(0.0),
00049     mMatrixIsConstant(false),
00050     mTolerance(1e-6),
00051     mUseAbsoluteTolerance(false),
00052     mDirichletBoundaryConditionsVector(NULL),
00053     mpBlockDiagonalPC(NULL),
00054     mpLDUFactorisationPC(NULL)
00055 {
00056     SetupVectorAndMatrix(matType);
00057 
00060     mKspType = "gmres";
00061     mPcType = "jacobi";
00062 
00063 #ifdef TRACE_KSP
00064     mNumSolves = 0;
00065     mTotalNumIterations = 0;
00066     mMaxNumIterations = 0;
00067 #endif
00068 }
00069 
00070 LinearSystem::LinearSystem(PetscInt lhsVectorSize, Mat lhsMatrix, Vec rhsVector, MatType matType)
00071    :mSize(lhsVectorSize),
00072     mMatNullSpace(NULL),
00073     mDestroyMatAndVec(true),
00074     mKspIsSetup(false),
00075     mNonZerosUsed(0.0),
00076     mMatrixIsConstant(false),
00077     mTolerance(1e-6),
00078     mUseAbsoluteTolerance(false),
00079     mDirichletBoundaryConditionsVector(NULL),
00080     mpBlockDiagonalPC(NULL),
00081     mpLDUFactorisationPC(NULL)
00082 {
00083     // Conveniently, PETSc Mats and Vecs are actually pointers
00084     mLhsMatrix = lhsMatrix;
00085     mRhsVector = rhsVector;
00086 
00087     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00088 
00089 #ifdef TRACE_KSP
00090     mNumSolves = 0;
00091     mTotalNumIterations = 0;
00092     mMaxNumIterations = 0;
00093 #endif
00094 }
00095 
00096 LinearSystem::LinearSystem(Vec templateVector)
00097    :mMatNullSpace(NULL),
00098     mDestroyMatAndVec(true),
00099     mKspIsSetup(false),
00100     mMatrixIsConstant(false),
00101     mTolerance(1e-6),
00102     mUseAbsoluteTolerance(false),
00103     mDirichletBoundaryConditionsVector(NULL),
00104     mpBlockDiagonalPC(NULL),
00105     mpLDUFactorisationPC(NULL)
00106 {
00107     VecDuplicate(templateVector, &mRhsVector);
00108     VecGetSize(mRhsVector, &mSize);
00109     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00110     PetscInt local_size = mOwnershipRangeHi - mOwnershipRangeLo;
00111 
00112     PetscTools::SetupMat(mLhsMatrix, mSize, mSize, (MatType) MATMPIAIJ, local_size, local_size);
00113 
00116     mKspType = "gmres";
00117     mPcType = "jacobi";
00118 
00119 #ifdef TRACE_KSP
00120     mNumSolves = 0;
00121     mTotalNumIterations = 0;
00122     mMaxNumIterations = 0;
00123 #endif
00124 }
00125 
00126 LinearSystem::LinearSystem(Vec residualVector, Mat jacobianMatrix)
00127     :mMatNullSpace(NULL),
00128     mDestroyMatAndVec(false),
00129     mKspIsSetup(false),
00130     mMatrixIsConstant(false),
00131     mTolerance(1e-6),
00132     mUseAbsoluteTolerance(false),
00133     mDirichletBoundaryConditionsVector(NULL),
00134     mpBlockDiagonalPC(NULL),
00135     mpLDUFactorisationPC(NULL)
00136 {
00137     assert(residualVector || jacobianMatrix);
00138     mRhsVector = residualVector;
00139     mLhsMatrix = jacobianMatrix;
00140 
00141     PetscInt mat_size=0, vec_size=0;
00142     if (mRhsVector)
00143     {
00144         VecGetSize(mRhsVector, &vec_size);
00145         mSize = (unsigned)vec_size;
00146         VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00147     }
00148     if (mLhsMatrix)
00149     {
00150         PetscInt mat_cols;
00151         MatGetSize(mLhsMatrix, &mat_size, &mat_cols);
00152         assert(mat_size == mat_cols);
00153         mSize = (unsigned)mat_size;
00154         MatGetOwnershipRange(mLhsMatrix, &mOwnershipRangeLo, &mOwnershipRangeHi);
00155     }
00156     assert(!mRhsVector || !mLhsMatrix || vec_size == mat_size);
00157 
00160     mKspType = "gmres";
00161     mPcType = "jacobi";
00162 
00163 #ifdef TRACE_KSP
00164     mNumSolves = 0;
00165     mTotalNumIterations = 0;
00166     mMaxNumIterations = 0;
00167 #endif
00168 }
00169 
00170 LinearSystem::~LinearSystem()
00171 {
00172     if (mpBlockDiagonalPC)
00173     {
00174         delete mpBlockDiagonalPC;
00175     }
00176 
00177     if (mpLDUFactorisationPC)
00178     {
00179         delete mpLDUFactorisationPC;
00180     }
00181 
00182     if (mDestroyMatAndVec)
00183     {
00184         VecDestroy(mRhsVector);
00185         MatDestroy(mLhsMatrix);
00186     }
00187 
00188     if (mMatNullSpace)
00189     {
00190         MatNullSpaceDestroy(mMatNullSpace);
00191     }
00192 
00193     if (mKspIsSetup)
00194     {
00195         KSPDestroy(mKspSolver);
00196     }
00197 
00198     if (mDirichletBoundaryConditionsVector)
00199     {
00201         VecDestroy(mDirichletBoundaryConditionsVector);
00202     }
00203 
00204 #ifdef TRACE_KSP
00205     if (mNumSolves > 0)
00206     {
00207         double ave_num_iterations = mTotalNumIterations/(double)mNumSolves;
00208 
00209         std::cout << std::endl << "KSP iterations report:" << std::endl;
00210         std::cout << "mNumSolves" << "\t" << "mTotalNumIterations" << "\t" << "mMaxNumIterations" << "\t" << "mAveNumIterations" << std::endl;
00211         std::cout << mNumSolves << "\t" << mTotalNumIterations << "\t" << mMaxNumIterations << "\t" << ave_num_iterations << std::endl;
00212     }
00213 #endif
00214 
00215 }
00216 
00217 void LinearSystem::SetupVectorAndMatrix(MatType matType)
00218 {
00219     VecCreate(PETSC_COMM_WORLD, &mRhsVector);
00220     VecSetSizes(mRhsVector, PETSC_DECIDE, mSize);
00221     VecSetFromOptions(mRhsVector);
00222 
00223     PetscTools::SetupMat(mLhsMatrix, mSize, mSize, matType);
00224 
00225     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00226 }
00227 
00228 void LinearSystem::SetMatrixElement(PetscInt row, PetscInt col, double value)
00229 {
00230     if (row >= mOwnershipRangeLo && row < mOwnershipRangeHi)
00231     {
00232         MatSetValue(mLhsMatrix, row, col, value, INSERT_VALUES);
00233     }
00234 }
00235 
00236 void LinearSystem::AddToMatrixElement(PetscInt row, PetscInt col, double value)
00237 {
00238     if (row >= mOwnershipRangeLo && row < mOwnershipRangeHi)
00239     {
00240         MatSetValue(mLhsMatrix, row, col, value, ADD_VALUES);
00241     }
00242 }
00243 
00244 void LinearSystem::AssembleFinalLinearSystem()
00245 {
00246     AssembleFinalLhsMatrix();
00247     AssembleRhsVector();
00248 }
00249 
00250 void LinearSystem::AssembleIntermediateLinearSystem()
00251 {
00252     AssembleIntermediateLhsMatrix();
00253     AssembleRhsVector();
00254 }
00255 
00256 void LinearSystem::AssembleFinalLhsMatrix()
00257 {
00258     MatAssemblyBegin(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00259     MatAssemblyEnd(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00260 }
00261 
00262 void LinearSystem::AssembleIntermediateLhsMatrix()
00263 {
00264     MatAssemblyBegin(mLhsMatrix, MAT_FLUSH_ASSEMBLY);
00265     MatAssemblyEnd(mLhsMatrix, MAT_FLUSH_ASSEMBLY);
00266 }
00267 
00268 void LinearSystem::AssembleRhsVector()
00269 {
00270     VecAssemblyBegin(mRhsVector);
00271     VecAssemblyEnd(mRhsVector);
00272 }
00273 
00274 void LinearSystem::SetRhsVectorElement(PetscInt row, double value)
00275 {
00276     if (row >= mOwnershipRangeLo && row < mOwnershipRangeHi)
00277     {
00278         VecSetValues(mRhsVector, 1, &row, &value, INSERT_VALUES);
00279     }
00280 }
00281 
00282 void LinearSystem::AddToRhsVectorElement(PetscInt row, double value)
00283 {
00284     if (row >= mOwnershipRangeLo && row < mOwnershipRangeHi)
00285     {
00286         VecSetValues(mRhsVector, 1, &row, &value, ADD_VALUES);
00287     }
00288 }
00289 
00290 void LinearSystem::DisplayMatrix()
00291 {
00292     MatView(mLhsMatrix,PETSC_VIEWER_STDOUT_WORLD);
00293 }
00294 
00295 void LinearSystem::DisplayRhs()
00296 {
00297     VecView(mRhsVector,PETSC_VIEWER_STDOUT_WORLD);
00298 }
00299 
00300 void LinearSystem::SetMatrixRow(PetscInt row, double value)
00301 {
00302     if (row >= mOwnershipRangeLo && row < mOwnershipRangeHi)
00303     {
00304         PetscInt rows, cols;
00305         MatGetSize(mLhsMatrix, &rows, &cols);
00306         for (PetscInt i=0; i<cols; i++)
00307         {
00308             this->SetMatrixElement(row, i, value);
00309         }
00310     }
00311 }
00312 
00313 Vec LinearSystem::GetMatrixRowDistributed(unsigned row_index)
00314 {
00315     Vec lhs_ith_row;
00316     VecDuplicate(mRhsVector, &lhs_ith_row); // Allocate same parallel layout
00317 
00318     PetscInt num_entries;
00319     const PetscInt *column_indices;
00320     const PetscScalar *values;
00321 
00322     bool am_row_owner = (PetscInt)row_index >= mOwnershipRangeLo && (PetscInt)row_index < mOwnershipRangeHi;
00323 
00324     // Am I the owner of the row? If so get the non-zero entries and add them lhs_ith_row.
00325     // In parallel, VecAssembly{Begin,End} will send values to the rest of processors.
00326     if (am_row_owner)
00327     {
00328         MatGetRow(mLhsMatrix, row_index, &num_entries, &column_indices, &values);
00329         VecSetValues(lhs_ith_row, num_entries, column_indices, values, INSERT_VALUES);
00330     }
00331 
00332     VecAssemblyBegin(lhs_ith_row);
00333     VecAssemblyEnd(lhs_ith_row);
00334 
00335     if (am_row_owner)
00336     {
00337         MatRestoreRow(mLhsMatrix, row_index, &num_entries, &column_indices, &values);
00338     }
00339 
00340     return lhs_ith_row;
00341 }
00342 
00343 void LinearSystem::ZeroMatrixRowsWithValueOnDiagonal(std::vector<unsigned>& rRows, double diagonalValue)
00344 {
00345     MatAssemblyBegin(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00346     MatAssemblyEnd(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00347 
00348     // Important! Petsc by default will destroy the sparsity structure for this row and deallocate memory
00349     // when the row is zeroed, and if there is a next timestep, the memory will have to reallocated
00350     // when assembly to done again. This can kill performance. The following makes sure the zeroed rows
00351     // are kept.
00352 #if PETSC_VERSION_MAJOR == 3
00353     MatSetOption(mLhsMatrix, MAT_KEEP_ZEROED_ROWS, PETSC_TRUE);
00354 #else
00355     MatSetOption(mLhsMatrix, MAT_KEEP_ZEROED_ROWS);
00356 #endif
00357 
00358     PetscInt* rows = new PetscInt[rRows.size()];
00359     for(unsigned i=0; i<rRows.size(); i++)
00360     {
00361         rows[i] = rRows[i];
00362     }
00363 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00364     IS is;
00365     ISCreateGeneral(PETSC_COMM_WORLD, rRows.size(), rows, &is);
00366     MatZeroRows(mLhsMatrix, is, &diagonalValue);
00367     ISDestroy(is);
00368 #else
00369     MatZeroRows(mLhsMatrix, rRows.size(), rows, diagonalValue);
00370 #endif
00371     delete [] rows;
00372 }
00373 
00374 
00375 void LinearSystem::ZeroMatrixRowsAndColumnsWithValueOnDiagonal(std::vector<unsigned>& rRowColIndices, double diagonalValue)
00376 {
00377     MatAssemblyBegin(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00378     MatAssemblyEnd(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00379 
00380     std::vector<unsigned> nonzero_rows_per_column[rRowColIndices.size()];
00381 
00382     // for each column: collect all the row indices corresponding to a non-zero entry
00383     // We do all the columns at once, before doing the zeroing, as otherwise
00384     // a MatAssemblyBegin() & MatAssemblyEnd() would have to be called
00385     // after every MatSetValues and before the below GetMatrixElement()
00386     for(unsigned index=0; index<rRowColIndices.size(); index++)
00387     {
00388         unsigned column = rRowColIndices[index];
00389 
00390         // determine which rows in this column are non-zero (and
00391         // therefore need to be zeroed)
00392         for (PetscInt row = mOwnershipRangeLo; row < mOwnershipRangeHi; row++)
00393         {
00394             if (GetMatrixElement(row, column) != 0.0)
00395             {
00396                 nonzero_rows_per_column[index].push_back(row);
00397             }
00398         }
00399     }
00400 
00401     // Now zero each column in turn
00402     for(unsigned index=0; index<rRowColIndices.size(); index++)
00403     {
00404         // set those rows to be zero by calling MatSetValues
00405         unsigned size = nonzero_rows_per_column[index].size();
00406         PetscInt* rows = new PetscInt[size];
00407         PetscInt cols[1];
00408         double* zeros = new double[size];
00409 
00410         cols[0] = rRowColIndices[index];
00411 
00412         for (unsigned i=0; i<size; i++)
00413         {
00414             rows[i] = nonzero_rows_per_column[index][i];
00415             zeros[i] = 0.0;
00416         }
00417 
00418         MatSetValues(mLhsMatrix, size, rows, 1, cols, zeros, INSERT_VALUES);
00419         delete [] rows;
00420         delete [] zeros;
00421     }
00422 
00423     // now zero the rows and add the diagonal entries
00424     ZeroMatrixRowsWithValueOnDiagonal(rRowColIndices, diagonalValue);
00425 }
00426 
00427 
00428 
00429 
00430 void LinearSystem::ZeroMatrixColumn(PetscInt col)
00431 {
00432     MatAssemblyBegin(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00433     MatAssemblyEnd(mLhsMatrix, MAT_FINAL_ASSEMBLY);
00434 
00435     // determine which rows in this column are non-zero (and
00436     // therefore need to be zeroed)
00437     std::vector<unsigned> nonzero_rows;
00438     for (PetscInt row = mOwnershipRangeLo; row < mOwnershipRangeHi; row++)
00439     {
00440         if (GetMatrixElement(row, col) != 0.0)
00441         {
00442             nonzero_rows.push_back(row);
00443         }
00444     }
00445 
00446     // set those rows to be zero by calling MatSetValues
00447     unsigned size = nonzero_rows.size();
00448     PetscInt* rows = new PetscInt[size];
00449     PetscInt cols[1];
00450     double* zeros = new double[size];
00451 
00452     cols[0] = col;
00453 
00454     for (unsigned i=0; i<size; i++)
00455     {
00456         rows[i] = nonzero_rows[i];
00457         zeros[i] = 0.0;
00458     }
00459 
00460     MatSetValues(mLhsMatrix, size, rows, 1, cols, zeros, INSERT_VALUES);
00461     delete [] rows;
00462     delete [] zeros;
00463 }
00464 
00465 void LinearSystem::ZeroRhsVector()
00466 {
00467     double* p_rhs_vector_array;
00468     VecGetArray(mRhsVector, &p_rhs_vector_array);
00469     for (PetscInt local_index=0; local_index<mOwnershipRangeHi - mOwnershipRangeLo; local_index++)
00470     {
00471         p_rhs_vector_array[local_index]=0.0;
00472     }
00473     VecRestoreArray(mRhsVector, &p_rhs_vector_array);
00474 }
00475 
00476 void LinearSystem::ZeroLhsMatrix()
00477 {
00478     MatZeroEntries(mLhsMatrix);
00479 }
00480 
00481 void LinearSystem::ZeroLinearSystem()
00482 {
00483     ZeroRhsVector();
00484     ZeroLhsMatrix();
00485 }
00486 
00487 unsigned LinearSystem::GetSize() const
00488 {
00489     return (unsigned) mSize;
00490 }
00491 
00492 void LinearSystem::SetNullBasis(Vec nullBasis[], unsigned numberOfBases)
00493 {
00494 #ifndef NDEBUG
00495     // Check all the vectors of the base are normal
00496     for (unsigned vec_index=0; vec_index<numberOfBases; vec_index++)
00497     {
00498         PetscReal l2_norm;
00499         VecNorm(nullBasis[vec_index], NORM_2, &l2_norm);
00500         if (fabs(l2_norm-1.0) > 1e-08)
00501         {
00502             EXCEPTION("One of the vectors in the null space is not normalised");
00503         }
00504     }
00505 
00506     // Check all the vectors of the base are orthogonal
00507     for (unsigned vec_index=1; vec_index<numberOfBases; vec_index++)
00508     {
00509         // The strategy is to check the (i-1)-th vector against vectors from i to n with VecMDot. This should be the most efficient way of doing it.
00510         unsigned num_vectors_ahead = numberOfBases-vec_index;
00511         PetscScalar dot_products[num_vectors_ahead];
00512 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00513         VecMDot(num_vectors_ahead, nullBasis[vec_index-1], &nullBasis[vec_index], dot_products);
00514 #else
00515         VecMDot(nullBasis[vec_index-1], num_vectors_ahead, &nullBasis[vec_index], dot_products);
00516 #endif
00517         for (unsigned index=0; index<num_vectors_ahead; index++)
00518         {
00519             if (fabs(dot_products[index]) > 1e-08 )
00520             {
00521                 EXCEPTION("The null space is not orthogonal.");
00522             }
00523         }
00524 
00525     }
00526 
00527 #endif
00528 
00529     PETSCEXCEPT( MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_FALSE, numberOfBases, nullBasis, &mMatNullSpace) );
00530 }
00531 
00532 void LinearSystem::RemoveNullSpace()
00533 {
00534     // Only remove if previously set
00535     if (mMatNullSpace)
00536     {
00537         PETSCEXCEPT( MatNullSpaceDestroy(mMatNullSpace) );
00538         PETSCEXCEPT( MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_FALSE, 0, NULL, &mMatNullSpace) );
00539         if (mKspIsSetup)
00540         {
00541             PETSCEXCEPT( KSPSetNullSpace(mKspSolver, mMatNullSpace) );
00542         }
00543         //else: it will be set next time Solve() is called
00544     }
00545 }
00546 
00547 
00548 void LinearSystem::GetOwnershipRange(PetscInt& lo, PetscInt& hi)
00549 {
00550     lo = mOwnershipRangeLo;
00551     hi = mOwnershipRangeHi;
00552 }
00553 
00554 double LinearSystem::GetMatrixElement(PetscInt row, PetscInt col)
00555 {
00556     assert(mOwnershipRangeLo <= row && row < mOwnershipRangeHi);
00557     PetscInt row_as_array[1];
00558     row_as_array[0] = row;
00559     PetscInt col_as_array[1];
00560     col_as_array[0] = col;
00561 
00562     double ret_array[1];
00563 
00564     MatGetValues(mLhsMatrix, 1, row_as_array, 1, col_as_array, ret_array);
00565 
00566     return ret_array[0];
00567 }
00568 
00569 double LinearSystem::GetRhsVectorElement(PetscInt row)
00570 {
00571     assert(mOwnershipRangeLo <= row && row < mOwnershipRangeHi);
00572 
00573     double* p_rhs_vector;
00574     PetscInt local_index=row-mOwnershipRangeLo;
00575     VecGetArray(mRhsVector, &p_rhs_vector);
00576     double answer=p_rhs_vector[local_index];
00577     VecRestoreArray(mRhsVector, &p_rhs_vector);
00578 
00579     return answer;
00580 }
00581 
00582 unsigned LinearSystem::GetNumIterations() const
00583 {
00584     assert(this->mKspIsSetup);
00585 
00586     PetscInt num_its;
00587     KSPGetIterationNumber(this->mKspSolver, &num_its);
00588 
00589     return (unsigned) num_its;
00590 }
00591 
00592 
00593 Vec& LinearSystem::rGetRhsVector()
00594 {
00595     return mRhsVector;
00596 }
00597 
00598 Vec LinearSystem::GetRhsVector() const
00599 {
00600     return mRhsVector;
00601 }
00602 
00603 Mat& LinearSystem::rGetLhsMatrix()
00604 {
00605     return mLhsMatrix;
00606 }
00607 
00608 Mat LinearSystem::GetLhsMatrix() const
00609 {
00610     return mLhsMatrix;
00611 }
00612 
00613 Vec& LinearSystem::rGetDirichletBoundaryConditionsVector()
00614 {
00615     return mDirichletBoundaryConditionsVector;
00616 }
00617 
00618 void LinearSystem::SetMatrixIsSymmetric(bool isSymmetric)
00619 {
00621 
00622     if (isSymmetric)
00623     {
00624 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00625         MatSetOption(mLhsMatrix, MAT_SYMMETRIC, PETSC_TRUE);
00626         MatSetOption(mLhsMatrix, MAT_SYMMETRY_ETERNAL, PETSC_TRUE);
00627 #else
00628         MatSetOption(mLhsMatrix, MAT_SYMMETRIC);
00629         MatSetOption(mLhsMatrix, MAT_SYMMETRY_ETERNAL);
00630 #endif
00631     }
00632     else
00633     {
00634 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00635         MatSetOption(mLhsMatrix, MAT_SYMMETRIC, PETSC_FALSE);
00636         MatSetOption(mLhsMatrix, MAT_STRUCTURALLY_SYMMETRIC, PETSC_FALSE);
00637         MatSetOption(mLhsMatrix, MAT_SYMMETRY_ETERNAL, PETSC_FALSE);
00638 #else
00639         MatSetOption(mLhsMatrix, MAT_NOT_SYMMETRIC);
00640         MatSetOption(mLhsMatrix, MAT_NOT_STRUCTURALLY_SYMMETRIC);
00641         MatSetOption(mLhsMatrix, MAT_NOT_SYMMETRY_ETERNAL);
00642 #endif
00643     }
00644 }
00645 
00646 bool LinearSystem::IsMatrixSymmetric()
00647 {
00648     PetscTruth symmetry_flag_is_set;
00649     PetscTruth symmetry_flag;
00650 
00651     MatIsSymmetricKnown(mLhsMatrix, &symmetry_flag_is_set, &symmetry_flag);
00652 
00653     // If the flag is not set we assume is a non-symmetric matrix
00654     return symmetry_flag_is_set && symmetry_flag;
00655 }
00656 
00657 void LinearSystem::SetMatrixIsConstant(bool matrixIsConstant)
00658 {
00659     mMatrixIsConstant=matrixIsConstant;
00660 }
00661 
00662 void LinearSystem::SetRelativeTolerance(double relativeTolerance)
00663 {
00664     mTolerance=relativeTolerance;
00665     mUseAbsoluteTolerance=false;
00666     if (mKspIsSetup)
00667     {
00668         KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
00669     }
00670 }
00671 
00672 void LinearSystem::SetAbsoluteTolerance(double absoluteTolerance)
00673 {
00674     mTolerance=absoluteTolerance;
00675     mUseAbsoluteTolerance=true;
00676     if (mKspIsSetup)
00677     {
00678         KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
00679     }
00680 }
00681 
00682 void LinearSystem::SetKspType(const char *kspType)
00683 {
00684     mKspType = kspType;
00685     if (mKspIsSetup)
00686     {
00687         KSPSetType(mKspSolver, kspType);
00688         KSPSetFromOptions(mKspSolver);
00689     }
00690 }
00691 
00692 void LinearSystem::SetPcType(const char* pcType)
00693 {
00694     mPcType=pcType;
00695     if (mKspIsSetup)
00696     {
00697         if (mPcType == "blockdiagonal")
00698         {
00699             if (mpBlockDiagonalPC)
00700             {
00701                 // If the preconditioner has been set to "blockdiagonal" before, we need to free the pointer.
00702                 delete mpBlockDiagonalPC;
00703             }
00704             mpBlockDiagonalPC = new PCBlockDiagonal(mKspSolver);
00705         }
00706         else if (mPcType == "ldufactorisation")
00707         {
00708             if (mpLDUFactorisationPC)
00709             {
00710                 // If this preconditioner has been set before, we need to free the pointer.
00711                 delete mpLDUFactorisationPC;
00712             }
00713             mpLDUFactorisationPC = new PCLDUFactorisation(mKspSolver);
00714         }
00715         else
00716         {
00717             PC prec;
00718             KSPGetPC(mKspSolver, &prec);
00719             PCSetType(prec, pcType);
00720         }
00721         KSPSetFromOptions(mKspSolver);
00722     }
00723 }
00724 
00725 Vec LinearSystem::Solve(Vec lhsGuess)
00726 {
00727     /* The following lines are very useful for debugging
00728      *    MatView(mLhsMatrix,    PETSC_VIEWER_STDOUT_WORLD);
00729      *    VecView(mRhsVector,    PETSC_VIEWER_STDOUT_WORLD);
00730      */
00731     //Double check that the non-zero pattern hasn't changed
00732     MatInfo mat_info;
00733     MatGetInfo(mLhsMatrix, MAT_GLOBAL_SUM, &mat_info);
00734 
00735     if (!mKspIsSetup)
00736     {
00737         HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00738         mNonZerosUsed=mat_info.nz_used;
00739         //MatNorm(mLhsMatrix, NORM_FROBENIUS, &mMatrixNorm);
00740         PC prec; //Type of pre-conditioner
00741 
00742         KSPCreate(PETSC_COMM_WORLD, &mKspSolver);
00743         //See
00744         //http://www-unix.mcs.anl.gov/petsc/petsc-2/snapshots/petsc-current/docs/manualpages/KSP/KSPSetOperators.html
00745         //The preconditioner flag (last argument) in the following calls says
00746         //how to reuse the preconditioner on subsequent iterations
00747         if (mMatrixIsConstant)
00748         {
00749             KSPSetOperators(mKspSolver, mLhsMatrix, mLhsMatrix, SAME_PRECONDITIONER);
00750         }
00751         else
00752         {
00753             KSPSetOperators(mKspSolver, mLhsMatrix, mLhsMatrix, SAME_NONZERO_PATTERN);
00754         }
00755 
00756         // Set either absolute or relative tolerance of the KSP solver.
00757         // The default is to use relative tolerance (1e-6)
00758         if (mUseAbsoluteTolerance)
00759         {
00760             KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
00761         }
00762         else
00763         {
00764             KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
00765         }
00766 
00767         // set ksp and pc types
00768         KSPSetType(mKspSolver, mKspType.c_str());
00769         KSPGetPC(mKspSolver, &prec);
00770 
00771 
00772         // Turn off pre-conditioning if the system size is very small
00773         if (mSize <= 4)
00774         {
00775             PCSetType(prec, PCNONE);
00776         }
00777         else
00778         {
00779 #ifdef TRACE_KSP
00780             Timer::Reset();
00781 #endif
00782             if (mPcType == "blockdiagonal")
00783             {
00784                 mpBlockDiagonalPC = new PCBlockDiagonal(mKspSolver);
00785 #ifdef TRACE_KSP
00786                 Timer::Print("Purpose-build preconditioner creation");
00787 #endif
00788 
00789             }
00790             else if (mPcType == "ldufactorisation")
00791             {
00792                 mpLDUFactorisationPC = new PCLDUFactorisation(mKspSolver);
00793 #ifdef TRACE_KSP
00794                 Timer::Print("Purpose-build preconditioner creation");
00795 #endif
00796             }
00797             else
00798             {
00799                 PCSetType(prec, mPcType.c_str());
00800             }
00801         }
00802 
00803         if (mMatNullSpace)
00804         {
00806             PETSCEXCEPT( KSPSetNullSpace(mKspSolver, mMatNullSpace) );
00807         }
00808 
00809         if (lhsGuess)
00810         {
00811             //Assume that the user of this method will always be kind enough
00812             //to give us a reasonable guess.
00813             KSPSetInitialGuessNonzero(mKspSolver,PETSC_TRUE);
00814         }
00815 
00816         KSPSetFromOptions(mKspSolver);
00817 
00818 #ifdef TRACE_KSP
00819         Timer::Reset();
00820 #endif
00821         KSPSetUp(mKspSolver);
00822 #ifdef TRACE_KSP
00823         Timer::Print("KSPSetUP (contains preconditioner creation for PETSc preconditioners)");
00824 #endif
00825 
00826         mKspIsSetup = true;
00827 
00828         HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00829     }
00830     else
00831     {
00832         #define COVERAGE_IGNORE
00833         if (mNonZerosUsed!=mat_info.nz_used)
00834         {
00835             EXCEPTION("LinearSystem doesn't allow the non-zero pattern of a matrix to change. (I think you changed it).");
00836         }
00837 //        PetscScalar norm;
00838 //        MatNorm(mLhsMatrix, NORM_FROBENIUS, &norm);
00839 //        if (fabs(norm - mMatrixNorm) > 0)
00840 //        {
00841 //            EXCEPTION("LinearSystem doesn't allow the matrix norm to change");
00842 //        }
00843         #undef COVERAGE_IGNORE
00844     }
00845 
00846     // Create solution vector
00848     HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00849     Vec lhs_vector;
00850     VecDuplicate(mRhsVector, &lhs_vector);//Sets the same size (doesn't copy)
00851     if (lhsGuess)
00852     {
00853         VecCopy(lhsGuess, lhs_vector);
00854     }
00855     HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00856 //    //Double check that the mRhsVector contains sensible values
00857 //    double* p_rhs,* p_guess;
00858 //    VecGetArray(mRhsVector, &p_rhs);
00859 //    VecGetArray(lhsGuess, &p_guess);
00860 //    for (int global_index=mOwnershipRangeLo; global_index<mOwnershipRangeHi; global_index++)
00861 //    {
00862 //        int local_index = global_index - mOwnershipRangeLo;
00863 //        assert(!std::isnan(p_rhs[local_index]));
00864 //        assert(!std::isnan(p_guess[local_index]));
00865 //        if (p_rhs[local_index] != p_rhs[local_index])
00866 //        {
00867 //            std::cout << "********* PETSc nan\n";
00868 //            assert(0);
00869 //        }
00870 //    }
00871 //    std::cout << "b[0] = " << p_rhs[0] << ", guess[0] = " << p_guess[0] << "\n";
00872 //    VecRestoreArray(mRhsVector, &p_rhs);
00873 //    VecRestoreArray(lhsGuess, &p_guess);
00874 //    // Test A*guess
00875 //    Vec temp;
00876 //    VecDuplicate(mRhsVector, &temp);
00877 //    MatMult(mLhsMatrix, lhs_vector, temp);
00878 //    double* p_temp;
00879 //    VecGetArray(temp, &p_temp);
00880 //    std::cout << "temp[0] = " << p_temp[0] << "\n";
00881 //    VecRestoreArray(temp, &p_temp);
00882 //    VecDestroy(temp);
00883 //    // Dump the matrix to file
00884 //    PetscViewer viewer;
00885 //    PetscViewerASCIIOpen(PETSC_COMM_WORLD,"mat.output",&viewer);
00886 //    MatView(mLhsMatrix, viewer);
00887 //    PetscViewerFlush(viewer);
00888 //    PetscViewerDestroy(viewer);
00889 //    // Dump the rhs vector to file
00890 //    PetscViewerASCIIOpen(PETSC_COMM_WORLD,"vec.output",&viewer);
00891 //    PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
00892 //    VecView(mRhsVector, viewer);
00893 //    PetscViewerFlush(viewer);
00894 //    PetscViewerDestroy(viewer);
00895 
00896     try
00897     {
00898         HeartEventHandler::BeginEvent(HeartEventHandler::SOLVE_LINEAR_SYSTEM);
00899 
00900 #ifdef TRACE_KSP
00901         Timer::Reset();
00902 #endif
00903 
00904         PETSCEXCEPT(KSPSolve(mKspSolver, mRhsVector, lhs_vector));
00905         HeartEventHandler::EndEvent(HeartEventHandler::SOLVE_LINEAR_SYSTEM);
00906 
00907 #ifdef TRACE_KSP
00908         PetscInt num_it;
00909         KSPGetIterationNumber(mKspSolver, &num_it);
00910         std::cout << "++ Solve: " << mNumSolves << " NumIterations: " << num_it << " "; // don't add std::endl so we get Timer::Print output in the same line (better for grep-ing)
00911         Timer::Print("Solve");
00912 
00913         mNumSolves++;
00914         mTotalNumIterations += num_it;
00915         if ((unsigned) num_it > mMaxNumIterations)
00916         {
00917             mMaxNumIterations = num_it;
00918         }
00919 #endif
00920 
00921 
00922         // Check that solver converged and throw if not
00923         KSPConvergedReason reason;
00924         KSPGetConvergedReason(mKspSolver, &reason);
00925         KSPEXCEPT(reason);
00926 
00927 
00928     }
00929     catch (const Exception& e)
00930     {
00931         // Destroy solution vector on error to avoid memory leaks
00932         VecDestroy(lhs_vector);
00933         throw e;
00934     }
00935 
00936     return lhs_vector;
00937 }
00938 
00939 
00940 // Serialization for Boost >= 1.36
00941 #include "SerializationExportWrapperForCpp.hpp"
00942 CHASTE_CLASS_EXPORT(LinearSystem);

Generated by  doxygen 1.6.2