LinearSystem.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 "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 #include "Warnings.hpp"
00038 #include <algorithm>
00039 
00041 // Implementation
00043 
00044 LinearSystem::LinearSystem(PetscInt lhsVectorSize, unsigned rowPreallocation)
00045    :mPrecondMatrix(NULL),
00046     mSize(lhsVectorSize),
00047     mMatNullSpace(NULL),
00048     mDestroyMatAndVec(true),
00049     mKspIsSetup(false),
00050     mNonZerosUsed(0.0),
00051     mMatrixIsConstant(false),
00052     mTolerance(1e-6),
00053     mUseAbsoluteTolerance(false),
00054     mDirichletBoundaryConditionsVector(NULL),
00055     mpBlockDiagonalPC(NULL),
00056     mpLDUFactorisationPC(NULL),
00057     mpTwoLevelsBlockDiagonalPC(NULL),
00058     mpBathNodes( boost::shared_ptr<std::vector<PetscInt> >() ),
00059     mPrecondMatrixIsNotLhs(false),
00060     mRowPreallocation(rowPreallocation),
00061     mUseFixedNumberIterations(false),
00062     mEvaluateNumItsEveryNSolves(UINT_MAX),
00063     mpConvergenceTestContext(NULL),
00064     mEigMin(DBL_MAX),
00065     mEigMax(DBL_MIN),
00066     mForceSpectrumReevaluation(false)
00067 {
00068     assert(lhsVectorSize > 0);
00069     if (mRowPreallocation == UINT_MAX)
00070     {
00071         // Automatic preallocation if it's a small matrix
00072         if (lhsVectorSize<15)
00073         {
00074             mRowPreallocation=lhsVectorSize;
00075         }
00076         else
00077         {
00078             EXCEPTION("You must provide a rowPreallocation argument for a large sparse system");
00079         }
00080     }
00081 
00082     mRhsVector = PetscTools::CreateVec(mSize);
00083     PetscTools::SetupMat(mLhsMatrix, mSize, mSize, mRowPreallocation, PETSC_DECIDE, PETSC_DECIDE);
00084 
00085     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00086 
00089     mKspType = "gmres";
00090     mPcType = "jacobi";
00091 
00092     mNumSolves = 0;
00093 #ifdef TRACE_KSP
00094     mTotalNumIterations = 0;
00095     mMaxNumIterations = 0;
00096 #endif
00097 }
00098 
00099 LinearSystem::LinearSystem(PetscInt lhsVectorSize, Mat lhsMatrix, Vec rhsVector)
00100    :mPrecondMatrix(NULL),
00101     mSize(lhsVectorSize),
00102     mMatNullSpace(NULL),
00103     mDestroyMatAndVec(true),
00104     mKspIsSetup(false),
00105     mNonZerosUsed(0.0),
00106     mMatrixIsConstant(false),
00107     mTolerance(1e-6),
00108     mUseAbsoluteTolerance(false),
00109     mDirichletBoundaryConditionsVector(NULL),
00110     mpBlockDiagonalPC(NULL),
00111     mpLDUFactorisationPC(NULL),
00112     mpTwoLevelsBlockDiagonalPC(NULL),
00113     mpBathNodes( boost::shared_ptr<std::vector<PetscInt> >() ),
00114     mPrecondMatrixIsNotLhs(false),
00115     mUseFixedNumberIterations(false),
00116     mEvaluateNumItsEveryNSolves(UINT_MAX),
00117     mpConvergenceTestContext(NULL),
00118     mEigMin(DBL_MAX),
00119     mEigMax(DBL_MIN),
00120     mForceSpectrumReevaluation(false)
00121 {
00122     assert(lhsVectorSize > 0);
00123     // Conveniently, PETSc Mats and Vecs are actually pointers
00124     mLhsMatrix = lhsMatrix;
00125     mRhsVector = rhsVector;
00126 
00127     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00128 
00129     mNumSolves = 0;
00130 #ifdef TRACE_KSP
00131     mTotalNumIterations = 0;
00132     mMaxNumIterations = 0;
00133 #endif
00134 }
00135 
00136 LinearSystem::LinearSystem(Vec templateVector, unsigned rowPreallocation)
00137    :mPrecondMatrix(NULL),
00138     mMatNullSpace(NULL),
00139     mDestroyMatAndVec(true),
00140     mKspIsSetup(false),
00141     mMatrixIsConstant(false),
00142     mTolerance(1e-6),
00143     mUseAbsoluteTolerance(false),
00144     mDirichletBoundaryConditionsVector(NULL),
00145     mpBlockDiagonalPC(NULL),
00146     mpLDUFactorisationPC(NULL),
00147     mpTwoLevelsBlockDiagonalPC(NULL),
00148     mpBathNodes( boost::shared_ptr<std::vector<PetscInt> >() ),
00149     mPrecondMatrixIsNotLhs(false),
00150     mRowPreallocation(rowPreallocation),
00151     mUseFixedNumberIterations(false),
00152     mEvaluateNumItsEveryNSolves(UINT_MAX),
00153     mpConvergenceTestContext(NULL),
00154     mEigMin(DBL_MAX),
00155     mEigMax(DBL_MIN),
00156     mForceSpectrumReevaluation(false)
00157 {
00158     VecDuplicate(templateVector, &mRhsVector);
00159     VecGetSize(mRhsVector, &mSize);
00160     VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00161     PetscInt local_size = mOwnershipRangeHi - mOwnershipRangeLo;
00162 
00163     PetscTools::SetupMat(mLhsMatrix, mSize, mSize, mRowPreallocation, local_size, local_size);
00164 
00167     mKspType = "gmres";
00168     mPcType = "jacobi";
00169 
00170     mNumSolves = 0;
00171 #ifdef TRACE_KSP
00172     mTotalNumIterations = 0;
00173     mMaxNumIterations = 0;
00174 #endif
00175 }
00176 
00177 LinearSystem::LinearSystem(Vec residualVector, Mat jacobianMatrix)
00178    :mPrecondMatrix(NULL),
00179     mMatNullSpace(NULL),
00180     mDestroyMatAndVec(false),
00181     mKspIsSetup(false),
00182     mMatrixIsConstant(false),
00183     mTolerance(1e-6),
00184     mUseAbsoluteTolerance(false),
00185     mDirichletBoundaryConditionsVector(NULL),
00186     mpBlockDiagonalPC(NULL),
00187     mpLDUFactorisationPC(NULL),
00188     mpTwoLevelsBlockDiagonalPC(NULL),
00189     mpBathNodes( boost::shared_ptr<std::vector<PetscInt> >() ),
00190     mPrecondMatrixIsNotLhs(false),
00191     mRowPreallocation(UINT_MAX),
00192     mUseFixedNumberIterations(false),
00193     mEvaluateNumItsEveryNSolves(UINT_MAX),
00194     mpConvergenceTestContext(NULL),
00195     mEigMin(DBL_MAX),
00196     mEigMax(DBL_MIN),
00197     mForceSpectrumReevaluation(false)
00198 {
00199     assert(residualVector || jacobianMatrix);
00200     mRhsVector = residualVector;
00201     mLhsMatrix = jacobianMatrix;
00202 
00203     PetscInt mat_size=0, vec_size=0;
00204     if (mRhsVector)
00205     {
00206         VecGetSize(mRhsVector, &vec_size);
00207         mSize = (unsigned)vec_size;
00208         VecGetOwnershipRange(mRhsVector, &mOwnershipRangeLo, &mOwnershipRangeHi);
00209     }
00210     if (mLhsMatrix)
00211     {
00212         PetscInt mat_cols;
00213         MatGetSize(mLhsMatrix, &mat_size, &mat_cols);
00214         assert(mat_size == mat_cols);
00215         mSize = (unsigned)mat_size;
00216         MatGetOwnershipRange(mLhsMatrix, &mOwnershipRangeLo, &mOwnershipRangeHi);
00217 
00218         MatInfo matrix_info;
00219         MatGetInfo(mLhsMatrix, MAT_GLOBAL_MAX, &matrix_info);
00220 
00221         /*
00222          * Assuming that mLhsMatrix was created with PetscTools::SetupMat, the value
00223          * below should be equivalent to what was used as preallocation in that call.
00224          */
00225         mRowPreallocation = (unsigned) matrix_info.nz_allocated / mSize;
00226     }
00227     assert(!mRhsVector || !mLhsMatrix || vec_size == mat_size);
00228 
00231     mKspType = "gmres";
00232     mPcType = "jacobi";
00233 
00234     mNumSolves = 0;
00235 #ifdef TRACE_KSP
00236     mTotalNumIterations = 0;
00237     mMaxNumIterations = 0;
00238 #endif
00239 }
00240 
00241 LinearSystem::~LinearSystem()
00242 {
00243     delete mpBlockDiagonalPC;
00244     delete mpLDUFactorisationPC;
00245     delete mpTwoLevelsBlockDiagonalPC;
00246 
00247     if (mDestroyMatAndVec)
00248     {
00249         VecDestroy(mRhsVector);
00250         MatDestroy(mLhsMatrix);
00251     }
00252 
00253     if (mPrecondMatrixIsNotLhs)
00254     {
00255         MatDestroy(mPrecondMatrix);
00256     }
00257 
00258     if (mMatNullSpace)
00259     {
00260         MatNullSpaceDestroy(mMatNullSpace);
00261     }
00262 
00263     if (mKspIsSetup)
00264     {
00265         KSPDestroy(mKspSolver);
00266     }
00267 
00268     if (mDirichletBoundaryConditionsVector)
00269     {
00271         VecDestroy(mDirichletBoundaryConditionsVector);
00272     }
00273 
00274 #if (PETSC_VERSION_MAJOR == 3)
00275     if (mpConvergenceTestContext)
00276     {
00277         KSPDefaultConvergedDestroy(mpConvergenceTestContext);
00278     }
00279 #endif
00280 
00281 #ifdef TRACE_KSP
00282     if (PetscTools::AmMaster())
00283     {
00284         if (mNumSolves > 0)
00285         {
00286             double ave_num_iterations = mTotalNumIterations/(double)mNumSolves;
00287 
00288             std::cout << std::endl << "KSP iterations report:" << std::endl;
00289             std::cout << "mNumSolves" << "\t" << "mTotalNumIterations" << "\t" << "mMaxNumIterations" << "\t" << "mAveNumIterations" << std::endl;
00290             std::cout << mNumSolves << "\t" << mTotalNumIterations << "\t" << mMaxNumIterations << "\t" << ave_num_iterations << std::endl;
00291         }
00292     }
00293 #endif
00294 }
00295 
00296 void LinearSystem::SetMatrixElement(PetscInt row, PetscInt col, double value)
00297 {
00298     PetscMatTools::SetElement(mLhsMatrix, row, col, value);
00299 }
00300 
00301 void LinearSystem::AddToMatrixElement(PetscInt row, PetscInt col, double value)
00302 {
00303     PetscMatTools::AddToElement(mLhsMatrix, row, col, value);
00304 }
00305 
00306 void LinearSystem::AssembleFinalLinearSystem()
00307 {
00308     FinaliseLhsMatrix();
00309     FinaliseRhsVector();
00310 }
00311 
00312 void LinearSystem::AssembleIntermediateLinearSystem()
00313 {
00314     SwitchWriteModeLhsMatrix();
00315     FinaliseRhsVector();
00316 }
00317 
00318 void LinearSystem::FinaliseLhsMatrix()
00319 {
00320     PetscMatTools::Finalise(mLhsMatrix);
00321 }
00322 
00323 void LinearSystem::SwitchWriteModeLhsMatrix()
00324 {
00325     PetscMatTools::SwitchWriteMode(mLhsMatrix);
00326 }
00327 
00328 void LinearSystem::FinalisePrecondMatrix()
00329 {
00330     PetscMatTools::Finalise(mPrecondMatrix);
00331 }
00332 
00333 void LinearSystem::FinaliseRhsVector()
00334 {
00335     PetscVecTools::Finalise(mRhsVector);
00336 }
00337 
00338 void LinearSystem::SetRhsVectorElement(PetscInt row, double value)
00339 {
00340     PetscVecTools::SetElement(mRhsVector, row, value);
00341 }
00342 
00343 void LinearSystem::AddToRhsVectorElement(PetscInt row, double value)
00344 {
00345     PetscVecTools::AddToElement(mRhsVector, row, value);
00346 }
00347 
00348 void LinearSystem::DisplayMatrix()
00349 {
00350     PetscMatTools::Display(mLhsMatrix);
00351 }
00352 
00353 void LinearSystem::DisplayRhs()
00354 {
00355     PetscVecTools::Display(mRhsVector);
00356 }
00357 
00358 void LinearSystem::SetMatrixRow(PetscInt row, double value)
00359 {
00360     PetscMatTools::SetRow(mLhsMatrix, row, value);
00361 }
00362 
00363 Vec LinearSystem::GetMatrixRowDistributed(unsigned rowIndex)
00364 {
00365     return PetscMatTools::GetMatrixRowDistributed(mLhsMatrix, rowIndex);
00366 }
00367 
00368 void LinearSystem::ZeroMatrixRowsWithValueOnDiagonal(std::vector<unsigned>& rRows, double diagonalValue)
00369 {
00370     PetscMatTools::ZeroRowsWithValueOnDiagonal(mLhsMatrix, rRows, diagonalValue);
00371 }
00372 
00373 void LinearSystem::ZeroMatrixRowsAndColumnsWithValueOnDiagonal(std::vector<unsigned>& rRowColIndices, double diagonalValue)
00374 {
00375     PetscMatTools::ZeroRowsAndColumnsWithValueOnDiagonal(mLhsMatrix, rRowColIndices, diagonalValue);
00376 }
00377 
00378 void LinearSystem::ZeroMatrixColumn(PetscInt col)
00379 {
00380     PetscMatTools::ZeroColumn(mLhsMatrix, col);
00381 }
00382 
00383 void LinearSystem::ZeroRhsVector()
00384 {
00385     PetscVecTools::Zero(mRhsVector);
00386 }
00387 
00388 void LinearSystem::ZeroLhsMatrix()
00389 {
00390     PetscMatTools::Zero(mLhsMatrix);
00391 }
00392 
00393 void LinearSystem::ZeroLinearSystem()
00394 {
00395     ZeroRhsVector();
00396     ZeroLhsMatrix();
00397 }
00398 
00399 unsigned LinearSystem::GetSize() const
00400 {
00401     return (unsigned) mSize;
00402 }
00403 
00404 void LinearSystem::SetNullBasis(Vec nullBasis[], unsigned numberOfBases)
00405 {
00406 #ifndef NDEBUG
00407     // Check all the vectors of the base are normal
00408     for (unsigned vec_index=0; vec_index<numberOfBases; vec_index++)
00409     {
00410         PetscReal l2_norm;
00411         VecNorm(nullBasis[vec_index], NORM_2, &l2_norm);
00412         if (fabs(l2_norm-1.0) > 1e-08)
00413         {
00414             EXCEPTION("One of the vectors in the null space is not normalised");
00415         }
00416     }
00417 
00418     // Check all the vectors of the base are orthogonal
00419     for (unsigned vec_index=1; vec_index<numberOfBases; vec_index++)
00420     {
00421         // 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.
00422         unsigned num_vectors_ahead = numberOfBases-vec_index;
00423         PetscScalar dot_products[num_vectors_ahead];
00424 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00425         VecMDot(num_vectors_ahead, nullBasis[vec_index-1], &nullBasis[vec_index], dot_products);
00426 #else
00427         VecMDot(nullBasis[vec_index-1], num_vectors_ahead, &nullBasis[vec_index], dot_products);
00428 #endif
00429         for (unsigned index=0; index<num_vectors_ahead; index++)
00430         {
00431             if (fabs(dot_products[index]) > 1e-08 )
00432             {
00433                 EXCEPTION("The null space is not orthogonal.");
00434             }
00435         }
00436     }
00437 
00438 #endif
00439 
00440     PETSCEXCEPT( MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_FALSE, numberOfBases, nullBasis, &mMatNullSpace) );
00441 }
00442 
00443 void LinearSystem::RemoveNullSpace()
00444 {
00445     // Only remove if previously set
00446     if (mMatNullSpace)
00447     {
00448         PETSCEXCEPT( MatNullSpaceDestroy(mMatNullSpace) );
00449         PETSCEXCEPT( MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_FALSE, 0, NULL, &mMatNullSpace) );
00450         if (mKspIsSetup)
00451         {
00452             PETSCEXCEPT( KSPSetNullSpace(mKspSolver, mMatNullSpace) );
00453         }
00454         //else: it will be set next time Solve() is called
00455     }
00456 }
00457 
00458 void LinearSystem::GetOwnershipRange(PetscInt& lo, PetscInt& hi)
00459 {
00460     lo = mOwnershipRangeLo;
00461     hi = mOwnershipRangeHi;
00462 }
00463 
00464 double LinearSystem::GetMatrixElement(PetscInt row, PetscInt col)
00465 {
00466     return PetscMatTools::GetElement(mLhsMatrix, row, col);
00467 }
00468 
00469 double LinearSystem::GetRhsVectorElement(PetscInt row)
00470 {
00471     return PetscVecTools::GetElement(mRhsVector, row);
00472 }
00473 
00474 unsigned LinearSystem::GetNumIterations() const
00475 {
00476     assert(this->mKspIsSetup);
00477 
00478     PetscInt num_its;
00479     KSPGetIterationNumber(this->mKspSolver, &num_its);
00480 
00481     return (unsigned) num_its;
00482 }
00483 
00484 Vec& LinearSystem::rGetRhsVector()
00485 {
00486     return mRhsVector;
00487 }
00488 
00489 Vec LinearSystem::GetRhsVector() const
00490 {
00491     return mRhsVector;
00492 }
00493 
00494 Mat& LinearSystem::rGetLhsMatrix()
00495 {
00496     return mLhsMatrix;
00497 }
00498 
00499 Mat LinearSystem::GetLhsMatrix() const
00500 {
00501     return mLhsMatrix;
00502 }
00503 
00504 Mat& LinearSystem::rGetPrecondMatrix()
00505 {
00506     if (!mPrecondMatrixIsNotLhs)
00507     {
00508         EXCEPTION("LHS matrix used for preconditioner construction");
00509     }
00510 
00511     return mPrecondMatrix;
00512 }
00513 
00514 Vec& LinearSystem::rGetDirichletBoundaryConditionsVector()
00515 {
00516     return mDirichletBoundaryConditionsVector;
00517 }
00518 
00519 void LinearSystem::SetMatrixIsSymmetric(bool isSymmetric)
00520 {
00522 
00523     if (isSymmetric)
00524     {
00525         PetscMatTools::SetOption(mLhsMatrix, MAT_SYMMETRIC);
00526         PetscMatTools::SetOption(mLhsMatrix, MAT_SYMMETRY_ETERNAL);
00527     }
00528     else
00529     {
00530 // don't have a PetscMatTools method for setting options to false
00531 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00532         MatSetOption(mLhsMatrix, MAT_SYMMETRIC, PETSC_FALSE);
00533         MatSetOption(mLhsMatrix, MAT_STRUCTURALLY_SYMMETRIC, PETSC_FALSE);
00534         MatSetOption(mLhsMatrix, MAT_SYMMETRY_ETERNAL, PETSC_FALSE);
00535 #else
00536         MatSetOption(mLhsMatrix, MAT_NOT_SYMMETRIC);
00537         MatSetOption(mLhsMatrix, MAT_NOT_STRUCTURALLY_SYMMETRIC);
00538         MatSetOption(mLhsMatrix, MAT_NOT_SYMMETRY_ETERNAL);
00539 #endif
00540     }
00541 }
00542 
00543 bool LinearSystem::IsMatrixSymmetric()
00544 {
00545     PetscTruth symmetry_flag_is_set;
00546     PetscTruth symmetry_flag;
00547 
00548     MatIsSymmetricKnown(mLhsMatrix, &symmetry_flag_is_set, &symmetry_flag);
00549 
00550     // If the flag is not set we assume is a non-symmetric matrix
00551     return symmetry_flag_is_set && symmetry_flag;
00552 }
00553 
00554 void LinearSystem::SetMatrixIsConstant(bool matrixIsConstant)
00555 {
00556     mMatrixIsConstant = matrixIsConstant;
00557 }
00558 
00559 void LinearSystem::SetRelativeTolerance(double relativeTolerance)
00560 {
00561     mTolerance = relativeTolerance;
00562     mUseAbsoluteTolerance = false;
00563     if (mKspIsSetup)
00564     {
00565         KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
00566     }
00567 }
00568 
00569 void LinearSystem::SetAbsoluteTolerance(double absoluteTolerance)
00570 {
00571     mTolerance = absoluteTolerance;
00572     mUseAbsoluteTolerance = true;
00573     if (mKspIsSetup)
00574     {
00575         KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
00576     }
00577 }
00578 
00579 void LinearSystem::SetKspType(const char *kspType)
00580 {
00581     mKspType = kspType;
00582     if (mKspIsSetup)
00583     {
00584         KSPSetType(mKspSolver, kspType);
00585         KSPSetFromOptions(mKspSolver);
00586     }
00587 }
00588 
00589 void LinearSystem::SetPcType(const char* pcType, boost::shared_ptr<std::vector<PetscInt> > pBathNodes)
00590 {
00591     mPcType = pcType;
00592     mpBathNodes = pBathNodes;
00593 
00594     if (mKspIsSetup)
00595     {
00596         if (mPcType == "blockdiagonal")
00597         {
00598             // If the previous preconditioner was purpose-built we need to free the appropriate pointer.
00600             delete mpBlockDiagonalPC;
00601             mpBlockDiagonalPC = NULL;
00602             delete mpLDUFactorisationPC;
00603             mpLDUFactorisationPC = NULL;
00604             delete mpTwoLevelsBlockDiagonalPC;
00605             mpTwoLevelsBlockDiagonalPC = NULL;
00606 
00607             mpBlockDiagonalPC = new PCBlockDiagonal(mKspSolver);
00608         }
00609         else if (mPcType == "ldufactorisation")
00610         {
00611             // If the previous preconditioner was purpose-built we need to free the appropriate pointer.
00613             delete mpBlockDiagonalPC;
00614             mpBlockDiagonalPC = NULL;
00615             delete mpLDUFactorisationPC;
00616             mpLDUFactorisationPC = NULL;
00617             delete mpTwoLevelsBlockDiagonalPC;
00618             mpTwoLevelsBlockDiagonalPC = NULL;
00619 
00620             mpLDUFactorisationPC = new PCLDUFactorisation(mKspSolver);
00621         }
00622         else if (mPcType == "twolevelsblockdiagonal")
00623         {
00624             // If the previous preconditioner was purpose-built we need to free the appropriate pointer.
00626             delete mpBlockDiagonalPC;
00627             mpBlockDiagonalPC = NULL;
00628             delete mpLDUFactorisationPC;
00629             mpLDUFactorisationPC = NULL;
00630             delete mpTwoLevelsBlockDiagonalPC;
00631             mpTwoLevelsBlockDiagonalPC = NULL;
00632 
00633             if (!mpBathNodes)
00634             {
00635                 TERMINATE("You must provide a list of bath nodes when using TwoLevelsBlockDiagonalPC");
00636             }
00637             mpTwoLevelsBlockDiagonalPC = new PCTwoLevelsBlockDiagonal(mKspSolver, *mpBathNodes);
00638         }
00639         else
00640         {
00641             PC prec;
00642             KSPGetPC(mKspSolver, &prec);
00643             PCSetType(prec, pcType);
00644         }
00645         KSPSetFromOptions(mKspSolver);
00646     }
00647 }
00648 
00649 Vec LinearSystem::Solve(Vec lhsGuess)
00650 {
00651     /*
00652      * The following lines are very useful for debugging:
00653      *    MatView(mLhsMatrix,    PETSC_VIEWER_STDOUT_WORLD);
00654      *    VecView(mRhsVector,    PETSC_VIEWER_STDOUT_WORLD);
00655      */
00656 
00657     // Double check that the non-zero pattern hasn't changed
00658     MatInfo mat_info;
00659     MatGetInfo(mLhsMatrix, MAT_GLOBAL_SUM, &mat_info);
00660 
00661     if (!mKspIsSetup)
00662     {
00663         // Create PETSc Vec that may be required if we use a Chebyshev solver
00664         Vec chebyshev_lhs_vector = NULL;
00665 
00666         HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00667         mNonZerosUsed=mat_info.nz_used;
00668         //MatNorm(mLhsMatrix, NORM_FROBENIUS, &mMatrixNorm);
00669         PC prec; //Type of pre-conditioner
00670 
00671         KSPCreate(PETSC_COMM_WORLD, &mKspSolver);
00672 
00673         /*
00674          * See
00675          *
00676          * http://www-unix.mcs.anl.gov/petsc/petsc-2/snapshots/petsc-current/docs/manualpages/KSP/KSPSetOperators.html
00677          *
00678          * The preconditioner flag (last argument) in the following calls says
00679          * how to reuse the preconditioner on subsequent iterations.
00680          */
00681 
00682         MatStructure preconditioner_over_successive_calls;
00683 
00684         if (mMatrixIsConstant)
00685         {
00686             preconditioner_over_successive_calls = SAME_PRECONDITIONER;
00687         }
00688         else
00689         {
00690             preconditioner_over_successive_calls = SAME_NONZERO_PATTERN;
00691         }
00692 
00693         if (mPrecondMatrixIsNotLhs)
00694         {
00695             KSPSetOperators(mKspSolver, mLhsMatrix, mPrecondMatrix, preconditioner_over_successive_calls);
00696         }
00697         else
00698         {
00699             KSPSetOperators(mKspSolver, mLhsMatrix, mLhsMatrix, preconditioner_over_successive_calls);
00700         }
00701 
00702         // Set either absolute or relative tolerance of the KSP solver.
00703         // The default is to use relative tolerance (1e-6)
00704         if (mUseAbsoluteTolerance)
00705         {
00706             KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
00707         }
00708         else
00709         {
00710             KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
00711         }
00712 
00713         // Set ksp and pc types
00714         KSPSetType(mKspSolver, mKspType.c_str());
00715         KSPGetPC(mKspSolver, &prec);
00716 
00717         // Turn off pre-conditioning if the system size is very small
00718         if (mSize <= 4)
00719         {
00720             PCSetType(prec, PCNONE);
00721         }
00722         else
00723         {
00724 #ifdef TRACE_KSP
00725             Timer::Reset();
00726 #endif
00727             if (mPcType == "blockdiagonal")
00728             {
00729                 mpBlockDiagonalPC = new PCBlockDiagonal(mKspSolver);
00730 #ifdef TRACE_KSP
00731                 if (PetscTools::AmMaster())
00732                 {
00733                     Timer::Print("Purpose-build preconditioner creation");
00734                 }
00735 #endif
00736             }
00737             else if (mPcType == "ldufactorisation")
00738             {
00739                 mpLDUFactorisationPC = new PCLDUFactorisation(mKspSolver);
00740 #ifdef TRACE_KSP
00741                 if (PetscTools::AmMaster())
00742                 {
00743                     Timer::Print("Purpose-build preconditioner creation");
00744                 }
00745 #endif
00746             }
00747             else if (mPcType == "twolevelsblockdiagonal")
00748             {
00749                 if (!mpBathNodes)
00750                 {
00751                     TERMINATE("You must provide a list of bath nodes when using TwoLevelsBlockDiagonalPC");
00752                 }
00753                 mpTwoLevelsBlockDiagonalPC = new PCTwoLevelsBlockDiagonal(mKspSolver, *mpBathNodes);
00754 #ifdef TRACE_KSP
00755                 if (PetscTools::AmMaster())
00756                 {
00757                     Timer::Print("Purpose-build preconditioner creation");
00758                 }
00759 #endif
00760 
00761             }
00762             else
00763             {
00764                 PCSetType(prec, mPcType.c_str());
00765             }
00766         }
00767 
00768         if (mMatNullSpace)
00769         {
00771             PETSCEXCEPT( KSPSetNullSpace(mKspSolver, mMatNullSpace) );
00772         }
00773 
00774         if (lhsGuess)
00775         {
00776             // Assume that the user of this method will always be kind enough to give us a reasonable guess.
00777             KSPSetInitialGuessNonzero(mKspSolver,PETSC_TRUE);
00778         }
00779 
00780         KSPSetFromOptions(mKspSolver);
00781 
00782         /*
00783          * Non-adaptive Chebyshev: the required spectrum approximation is computed just once
00784          * at the beginning of the simulation. This is done with two extra CG solves.
00785          */
00786         if (mKspType == "chebychev" && !mUseFixedNumberIterations)
00787         {
00788 #ifdef TRACE_KSP
00789             Timer::Reset();
00790 #endif
00791             // Preconditioned matrix spectrum is approximated with CG
00792             KSPSetType(mKspSolver,"cg");
00793             KSPSetComputeEigenvalues(mKspSolver, PETSC_TRUE);
00794             KSPSetUp(mKspSolver);
00795 
00796             VecDuplicate(mRhsVector, &chebyshev_lhs_vector);
00797             if (lhsGuess)
00798             {
00799                 VecCopy(lhsGuess, chebyshev_lhs_vector);
00800             }
00801 
00802             // Smallest eigenvalue is approximated to default tolerance
00803             KSPSolve(mKspSolver, mRhsVector, chebyshev_lhs_vector);
00804 
00805             PetscReal *r_eig = new PetscReal[mSize];
00806             PetscReal *c_eig = new PetscReal[mSize];
00807             PetscInt eigs_computed;
00808             KSPComputeEigenvalues(mKspSolver, mSize, r_eig, c_eig, &eigs_computed);
00809 
00810             mEigMin = r_eig[0];
00811 
00812             // Largest eigenvalue is approximated to machine precision
00813             KSPSetTolerances(mKspSolver, DBL_EPSILON, DBL_EPSILON, PETSC_DEFAULT, PETSC_DEFAULT);
00814             KSPSetUp(mKspSolver);
00815             if (lhsGuess)
00816             {
00817                 VecCopy(lhsGuess, chebyshev_lhs_vector);
00818             }
00819 
00820             KSPSolve(mKspSolver, mRhsVector, chebyshev_lhs_vector);
00821             KSPComputeEigenvalues(mKspSolver, mSize, r_eig, c_eig, &eigs_computed);
00822 
00823             mEigMax = r_eig[eigs_computed-1];
00824 
00825 #ifdef TRACE_KSP
00826             /*
00827              * Under certain circumstances (see Golub&Overton 1988), underestimating
00828              * the spectrum of the preconditioned operator improves convergence rate.
00829              * See publication for a discussion and for definition of alpha and sigma_one.
00830              */
00831             if (PetscTools::AmMaster())
00832             {
00833                 std::cout << "EIGS: ";
00834                 for (int index=0; index<eigs_computed; index++)
00835                 {
00836                     std::cout << r_eig[index] << ", ";
00837                 }
00838                 std::cout << std::endl;
00839             }
00840 
00841             if (PetscTools::AmMaster()) std::cout << "EIGS "<< mEigMax << " " << mEigMin <<std::endl;
00842             double alpha = 2/(mEigMax+mEigMin);
00843             double sigma_one = 1 - alpha*mEigMin;
00844             if (PetscTools::AmMaster()) std::cout << "sigma_1 = 1 - alpha*mEigMin = "<< sigma_one <<std::endl;
00845 #endif
00846 
00847             // Set Chebyshev solver and max/min eigenvalues
00848             assert(mKspType == "chebychev");
00849             KSPSetType(mKspSolver, mKspType.c_str());
00850             KSPChebychevSetEigenvalues(mKspSolver, mEigMax, mEigMin);
00851             KSPSetComputeEigenvalues(mKspSolver, PETSC_FALSE);
00852             if (mUseAbsoluteTolerance)
00853             {
00854                 KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
00855             }
00856             else
00857             {
00858                 KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
00859             }
00860 
00861             delete[] r_eig;
00862             delete[] c_eig;
00863 
00864 #ifdef TRACE_KSP
00865             if (PetscTools::AmMaster())
00866             {
00867                 Timer::Print("Computing extremal eigenvalues");
00868             }
00869 #endif
00870         }
00871 
00872 #ifdef TRACE_KSP
00873         Timer::Reset();
00874 #endif
00875 
00876         KSPSetUp(mKspSolver);
00877 
00878         if (chebyshev_lhs_vector)
00879         {
00880             VecDestroy(chebyshev_lhs_vector);
00881         }
00882 
00883 #ifdef TRACE_KSP
00884         if (PetscTools::AmMaster())
00885         {
00886             Timer::Print("KSPSetUP (contains preconditioner creation for PETSc preconditioners)");
00887         }
00888 #endif
00889 
00890         mKspIsSetup = true;
00891 
00892         HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00893     }
00894     else
00895     {
00896         #define COVERAGE_IGNORE
00897         if (mNonZerosUsed!=mat_info.nz_used)
00898         {
00899             EXCEPTION("LinearSystem doesn't allow the non-zero pattern of a matrix to change. (I think you changed it).");
00900         }
00901 //        PetscScalar norm;
00902 //        MatNorm(mLhsMatrix, NORM_FROBENIUS, &norm);
00903 //        if (fabs(norm - mMatrixNorm) > 0)
00904 //        {
00905 //            EXCEPTION("LinearSystem doesn't allow the matrix norm to change");
00906 //        }
00907         #undef COVERAGE_IGNORE
00908     }
00909 
00910     HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00911     // Create solution vector
00913     Vec lhs_vector;
00914     VecDuplicate(mRhsVector, &lhs_vector); // Sets the same size (doesn't copy)
00915     if (lhsGuess)
00916     {
00917         VecCopy(lhsGuess, lhs_vector);
00918     }
00919 
00920     // Check if the right hand side is small (but non-zero), PETSc can diverge immediately
00921     // with a non-zero initial guess. Here we check for this and alter the initial guess to zero.
00922     PetscReal rhs_norm;
00923     VecNorm(mRhsVector, NORM_1, &rhs_norm);
00924     double rhs_zero_tol = 1e-15;
00925 
00926     if (rhs_norm < rhs_zero_tol && rhs_norm > 0.0)
00927     {
00928         WARNING("Using zero initial guess due to small right hand side vector");
00929         PetscVecTools::Zero(lhs_vector);
00930     }
00931 
00932     HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00933 //    // Double check that the mRhsVector contains sensible values
00934 //    double* p_rhs,* p_guess;
00935 //    VecGetArray(mRhsVector, &p_rhs);
00936 //    VecGetArray(lhsGuess, &p_guess);
00937 //    for (int global_index=mOwnershipRangeLo; global_index<mOwnershipRangeHi; global_index++)
00938 //    {
00939 //        int local_index = global_index - mOwnershipRangeLo;
00940 //        assert(!std::isnan(p_rhs[local_index]));
00941 //        assert(!std::isnan(p_guess[local_index]));
00942 //        if (p_rhs[local_index] != p_rhs[local_index])
00943 //        {
00944 //            std::cout << "********* PETSc nan\n";
00945 //            assert(0);
00946 //        }
00947 //    }
00948 //    std::cout << "b[0] = " << p_rhs[0] << ", guess[0] = " << p_guess[0] << "\n";
00949 //    VecRestoreArray(mRhsVector, &p_rhs);
00950 //    VecRestoreArray(lhsGuess, &p_guess);
00951 //    // Test A*guess
00952 //    Vec temp;
00953 //    VecDuplicate(mRhsVector, &temp);
00954 //    MatMult(mLhsMatrix, lhs_vector, temp);
00955 //    double* p_temp;
00956 //    VecGetArray(temp, &p_temp);
00957 //    std::cout << "temp[0] = " << p_temp[0] << "\n";
00958 //    VecRestoreArray(temp, &p_temp);
00959 //    VecDestroy(temp);
00960 //    // Dump the matrix to file
00961 //    PetscViewer viewer;
00962 //    PetscViewerASCIIOpen(PETSC_COMM_WORLD,"mat.output",&viewer);
00963 //    MatView(mLhsMatrix, viewer);
00964 //    PetscViewerFlush(viewer);
00965 //    PetscViewerDestroy(viewer);
00966 //    // Dump the rhs vector to file
00967 //    PetscViewerASCIIOpen(PETSC_COMM_WORLD,"vec.output",&viewer);
00968 //    PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
00969 //    VecView(mRhsVector, viewer);
00970 //    PetscViewerFlush(viewer);
00971 //    PetscViewerDestroy(viewer);
00972 
00973     try
00974     {
00975         HeartEventHandler::BeginEvent(HeartEventHandler::SOLVE_LINEAR_SYSTEM);
00976 
00977 #ifdef TRACE_KSP
00978         Timer::Reset();
00979 #endif
00980 
00981         // Current solve has to be done with tolerance-based stop criteria in order to record iterations taken
00982         if (mUseFixedNumberIterations && (mNumSolves%mEvaluateNumItsEveryNSolves==0 || mForceSpectrumReevaluation))
00983         {
00984 #if ((PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) || (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR <= 2))
00985             KSPSetNormType(mKspSolver, KSP_PRECONDITIONED_NORM);
00986 #else
00987             KSPSetNormType(mKspSolver, KSP_NORM_PRECONDITIONED);
00988 #endif
00989 
00990 #if (PETSC_VERSION_MAJOR == 3)
00991             if (!mpConvergenceTestContext)
00992             {
00993                 KSPDefaultConvergedCreate(&mpConvergenceTestContext);
00994             }
00995             KSPSetConvergenceTest(mKspSolver, KSPDefaultConverged, &mpConvergenceTestContext, PETSC_NULL);
00996 #else
00997             KSPSetConvergenceTest(mKspSolver, KSPDefaultConverged, PETSC_NULL);
00998 #endif
00999 
01000             if (mUseAbsoluteTolerance)
01001             {
01002                 KSPSetTolerances(mKspSolver, DBL_EPSILON, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT);
01003             }
01004             else
01005             {
01006                 KSPSetTolerances(mKspSolver, mTolerance, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
01007             }
01008 
01010             std::stringstream num_it_str;
01011             num_it_str << 1000;
01012             PetscOptionsSetValue("-ksp_max_it", num_it_str.str().c_str());
01013 
01014             // Adaptive Chebyshev: reevaluate spectrum with cg
01015             if (mKspType == "chebychev")
01016             {
01017                 // You can estimate preconditioned matrix spectrum with CG
01018                 KSPSetType(mKspSolver,"cg");
01019                 KSPSetComputeEigenvalues(mKspSolver, PETSC_TRUE);
01020             }
01021 
01022             KSPSetFromOptions(mKspSolver);
01023             KSPSetUp(mKspSolver);
01024         }
01025 
01026         PETSCEXCEPT(KSPSolve(mKspSolver, mRhsVector, lhs_vector));
01027         HeartEventHandler::EndEvent(HeartEventHandler::SOLVE_LINEAR_SYSTEM);
01028 
01029 #ifdef TRACE_KSP
01030         PetscInt num_it;
01031         KSPGetIterationNumber(mKspSolver, &num_it);
01032         if (PetscTools::AmMaster())
01033         {
01034             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)
01035             Timer::Print("Solve");
01036         }
01037 
01038         mTotalNumIterations += num_it;
01039         if ((unsigned) num_it > mMaxNumIterations)
01040         {
01041             mMaxNumIterations = num_it;
01042         }
01043 #endif
01044 
01045         // Check that solver converged and throw if not
01046         KSPConvergedReason reason;
01047         KSPGetConvergedReason(mKspSolver, &reason);
01048 
01049         if (mUseFixedNumberIterations && PETSC_VERSION_MAJOR < 3)
01050         {
01051             WARNING("Not explicitly checking convergence reason when using fixed number of iterations and PETSc 2");
01052         }
01053         else
01054         {
01055             KSPEXCEPT(reason);
01056         }
01057 
01058         if (mUseFixedNumberIterations && (mNumSolves%mEvaluateNumItsEveryNSolves==0 || mForceSpectrumReevaluation))
01059         {
01060             // Adaptive Chebyshev: reevaluate spectrum with cg
01061             if (mKspType == "chebychev")
01062             {
01063                 PetscReal *r_eig = new PetscReal[mSize];
01064                 PetscReal *c_eig = new PetscReal[mSize];
01065                 PetscInt eigs_computed;
01066                 KSPComputeEigenvalues(mKspSolver, mSize, r_eig, c_eig, &eigs_computed);
01067 
01068                 mEigMin = r_eig[0];
01069                 /*
01070                  * Using max() covers a borderline case found in TestChasteBenchmarksForPreDiCT where there's a big
01071                  * gap in the spectrum between ~1.2 and ~2.5. Some reevaluations pick up 2.5 and others don't. If it
01072                  * is not picked up, Chebyshev will diverge after 10 solves or so.
01073                  */
01074                 mEigMax = std::max(mEigMax,r_eig[eigs_computed-1]);
01075 
01076                 delete[] r_eig;
01077                 delete[] c_eig;
01078 
01079                 assert(mKspType == "chebychev");
01080                 KSPSetType(mKspSolver, mKspType.c_str());
01081                 KSPChebychevSetEigenvalues(mKspSolver, mEigMax, mEigMin);
01082                 KSPSetComputeEigenvalues(mKspSolver, PETSC_FALSE);
01083             }
01084 
01085 #if ((PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) || (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 3 && PETSC_VERSION_SUBMINOR <= 2))
01086             if (mKspType == "chebychev")
01087             {
01088                 // See #1695 for more details.
01089                 EXCEPTION("Chebyshev with fixed number of iterations is known to be broken in PETSc <= 2.3.2");
01090             }
01091 
01092             KSPSetNormType(mKspSolver, KSP_NO_NORM);
01093 #else
01094             KSPSetNormType(mKspSolver, KSP_NORM_NO);
01095 #endif
01096 
01097 #if (PETSC_VERSION_MAJOR != 3)
01098             KSPSetConvergenceTest(mKspSolver, KSPSkipConverged, PETSC_NULL);
01099 #endif
01100 
01101             PetscInt num_it;
01102             KSPGetIterationNumber(mKspSolver, &num_it);
01103             std::stringstream num_it_str;
01104             num_it_str << num_it;
01105             PetscOptionsSetValue("-ksp_max_it", num_it_str.str().c_str());
01106 
01107             KSPSetFromOptions(mKspSolver);
01108             KSPSetUp(mKspSolver);
01109 
01110             mForceSpectrumReevaluation=false;
01111         }
01112 
01113         mNumSolves++;
01114 
01115     }
01116     catch (const Exception& e)
01117     {
01118         // Destroy solution vector on error to avoid memory leaks
01119         VecDestroy(lhs_vector);
01120         throw e;
01121     }
01122 
01123     return lhs_vector;
01124 }
01125 
01126 void LinearSystem::SetPrecondMatrixIsDifferentFromLhs(bool precondIsDifferent)
01127 {
01128     mPrecondMatrixIsNotLhs = precondIsDifferent;
01129 
01130     if (mPrecondMatrixIsNotLhs)
01131     {
01132         if (mRowPreallocation == UINT_MAX)
01133         {
01134             /*
01135              * At the time of writing, this line will be reached if the constructor
01136              * with signature LinearSystem(Vec residualVector, Mat jacobianMatrix) is
01137              * called with jacobianMatrix=NULL and preconditioning matrix different
01138              * from lhs is used.
01139              *
01140              * If this combination is ever required you will need to work out
01141              * matrix allocation (mRowPreallocation) here.
01142              */
01143             NEVER_REACHED;
01144         }
01145 
01146         PetscInt local_size = mOwnershipRangeHi - mOwnershipRangeLo;
01147         PetscTools::SetupMat(mPrecondMatrix, mSize, mSize, mRowPreallocation, local_size, local_size);
01148     }
01149 }
01150 
01151 void LinearSystem::SetUseFixedNumberIterations(bool useFixedNumberIterations, unsigned evaluateNumItsEveryNSolves)
01152 {
01153 
01154     mUseFixedNumberIterations = useFixedNumberIterations;
01155     mEvaluateNumItsEveryNSolves = evaluateNumItsEveryNSolves;
01156 }
01157 
01158 void LinearSystem::ResetKspSolver()
01159 {
01160     if (mKspIsSetup)
01161     {
01162         KSPDestroy(mKspSolver);
01163     }
01164 
01165     mKspIsSetup = false;
01166     mForceSpectrumReevaluation = true;
01167 
01168     /*
01169      * Reset max number of iterations. This option is stored in the configuration database and
01170      * explicitely read in with KSPSetFromOptions() everytime a KSP object is created. Therefore,
01171      * destroying the KSP object will not ensure that it is set back to default.
01172      */
01174     std::stringstream num_it_str;
01175     num_it_str << 1000;
01176     PetscOptionsSetValue("-ksp_max_it", num_it_str.str().c_str());
01177 }
01178 
01179 // Serialization for Boost >= 1.36
01180 #include "SerializationExportWrapperForCpp.hpp"
01181 CHASTE_CLASS_EXPORT(LinearSystem)
Generated on Thu Dec 22 13:00:07 2011 for Chaste by  doxygen 1.6.3