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

Generated on Mon Nov 1 12:35:17 2010 for Chaste by  doxygen 1.5.5