AbstractNonlinearAssembler.hpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
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 #ifndef _ABSTRACTNONLINEARASSEMBLER_HPP_
00029 #define _ABSTRACTNONLINEARASSEMBLER_HPP_
00030 
00036 #include <vector>
00037 
00038 #include "AbstractStaticAssembler.hpp"
00039 #include "BoundaryConditionsContainer.hpp"
00040 #include "AbstractNonlinearSolver.hpp"
00041 #include "AbstractNonlinearSolver.hpp"
00042 #include "SimplePetscNonlinearSolver.hpp"
00043 #include "RandomNumberGenerator.hpp"
00044 #include "PetscTools.hpp"
00045 
00046 
00047 /*
00048  * Since we need to pass function pointers to the PETSc SNES routines, we can't
00049  * make these functions below methods. This is a pain, since it also means we
00050  * need to pass round a pointer to our assembler object as the void *pContext,
00051  * and cast it within the function to access data members.
00052  *
00053  * All the functions are defined as stubs which call methods on *pContext.
00054  *
00055  * Note: these are global functions, hence the need for long names to avoid
00056  * potential conflicting names later
00057  *
00058  * [The implementations are at the bottom of this file]
00059  */
00060 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00061 PetscErrorCode AbstractNonlinearAssembler_AssembleResidual(SNES snes,
00062         Vec currentGuess,
00063         Vec residualVector,
00064         void *pContext);
00065 
00066 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00067 PetscErrorCode AbstractNonlinearAssembler_AssembleJacobian(SNES snes,
00068         Vec currentGuess,
00069         Mat *pGlobalJacobian,
00070         Mat *pPreconditioner,
00071         MatStructure *pMatStructure,
00072         void *pContext);
00073 
00074 
00078 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00079 class AbstractNonlinearAssembler : public AbstractStaticAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, true, CONCRETE>
00080 {
00081     typedef AbstractStaticAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, true, CONCRETE> BaseClassType; 
00083 private:
00084 
00086     Vec mInitialGuess;
00087 
00088 protected:
00089 
00091     AbstractNonlinearSolver *mpSolver;
00092 
00094     bool mWeAllocatedSolverMemory;
00095 
00097     bool mUseAnalyticalJacobian;
00098 
00105     void ApplyDirichletConditions(Vec currentGuess, bool applyToMatrix);
00106 
00107 public :
00108 
00119     PetscErrorCode AssembleResidual(const Vec currentGuess, Vec residualVector);
00120 
00133     PetscErrorCode AssembleJacobian(const Vec currentGuess, Mat* pGlobalJacobian);
00134 
00135 protected:
00136 
00146     PetscErrorCode AssembleJacobianNumerically(const Vec currentGuess, Mat* pJacobian);
00147 
00170     virtual void AssembleSystem(bool assembleVector, bool assembleMatrix,
00171                                 Vec currentGuess=NULL, double currentTime=0.0);
00172 
00176     bool ProblemIsNonlinear();
00177 
00184     void InitialiseForSolve(Vec initialGuess);
00185 
00196     Vec StaticSolve(Vec currentSolutionOrGuess=NULL,
00197                     double currentTime=0.0,
00198                     bool assembleMatrix=true);
00199 
00200 public:
00201 
00207     AbstractNonlinearAssembler(unsigned numQuadPoints = 2);
00208 
00212     ~AbstractNonlinearAssembler();
00213 
00225     void SetUseAnalyticalJacobian(bool useAnalyticalJacobian);
00226 
00235     virtual Vec Solve(Vec initialGuess, bool useAnalyticalJacobian=false);
00236 
00244     void SetNonlinearSolver(AbstractNonlinearSolver* pNonlinearSolver);
00245 
00251     Vec CreateConstantInitialGuess(double value);
00252 
00269     bool VerifyJacobian(double tol=1e-4, bool print=false);
00270 
00271 }; //end of class AbstractNonlinearAssembler
00272 
00273 
00275 // Implementation
00277 
00278 
00279 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00280 void AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::ApplyDirichletConditions(Vec currentGuess, bool applyToMatrix)
00281 {
00282     Vec& residual = this->mpLinearSystem->rGetRhsVector();
00283     Mat& jacobian = this->mpLinearSystem->rGetLhsMatrix();
00284     assert((jacobian && applyToMatrix) || (!jacobian && !applyToMatrix));
00285 
00286     if (residual)
00287     {
00288         this->mpBoundaryConditions->ApplyDirichletToNonlinearResidual(
00289             currentGuess, residual, *(this->mpMesh->GetDistributedVectorFactory()));
00290     }
00291     if (jacobian)
00292     {
00293         this->mpBoundaryConditions->ApplyDirichletToNonlinearJacobian(jacobian);
00294     }
00295 }
00296 
00297 
00298 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00299 PetscErrorCode AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::AssembleResidual(const Vec currentGuess, Vec residualVector)
00300 {
00301     // call assemble system with the current guess and the residual vector
00302     // to be assembled
00303     this->PrepareForSolve();
00304     delete this->mpLinearSystem;
00305     this->mpLinearSystem = new LinearSystem(residualVector, NULL);
00306     this->AssembleSystem(true, false, currentGuess, 0.0);
00307     return 0;
00308 }
00309 
00310 
00311 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00312 PetscErrorCode AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::AssembleJacobian(const Vec currentGuess, Mat *pGlobalJacobian)
00313 {
00314     if (mUseAnalyticalJacobian)
00315     {
00316         // call assemble system with the current guess and the jacobian to
00317         // be assembled
00318         delete this->mpLinearSystem;
00319         this->mpLinearSystem = new LinearSystem(NULL, *pGlobalJacobian);
00320         this->AssembleSystem(false, true, currentGuess, 0.0);
00321         return 0; // no error
00322     }
00323     else
00324     {
00325         return AssembleJacobianNumerically(currentGuess, pGlobalJacobian);
00326     }
00327 }
00328 
00329 
00330 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00331 PetscErrorCode AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::AssembleJacobianNumerically(const Vec currentGuess, Mat *pJacobian)
00332 {
00333     unsigned num_nodes = PROBLEM_DIM*this->mpMesh->GetNumNodes();
00334 
00335     // Set up working vectors
00336     Vec residual;
00337     Vec perturbed_residual;
00338     Vec result;
00339 
00340     VecCreate(PETSC_COMM_WORLD, &residual);
00341     VecCreate(PETSC_COMM_WORLD, &result);
00342     VecCreate(PETSC_COMM_WORLD, &perturbed_residual);
00343 
00344     VecSetSizes(residual,           PETSC_DECIDE, num_nodes);
00345     VecSetSizes(result,             PETSC_DECIDE, num_nodes);
00346     VecSetSizes(perturbed_residual, PETSC_DECIDE, num_nodes);
00347 
00348     VecSetFromOptions(residual);
00349     VecSetFromOptions(result);
00350     VecSetFromOptions(perturbed_residual);
00351 
00352     // Copy the currentGuess vector; we perturb the copy
00353     Vec current_guess_copy;
00354     PETSCEXCEPT( VecDuplicate(currentGuess, &current_guess_copy) );
00355     PETSCEXCEPT( VecCopy(currentGuess, current_guess_copy) );
00356 
00357     // Compute the current residual
00358     AssembleResidual(currentGuess, residual);
00359 
00360     // Amount to perturb each input element by
00361     double h = 0.00001;
00362     PetscScalar subtract = -1;
00363     PetscScalar one_over_h = 1.0/h;
00364 
00365     PetscInt ilo, ihi;
00366     VecGetOwnershipRange(current_guess_copy, &ilo, &ihi);
00367     unsigned lo = ilo;
00368     unsigned hi = ihi;
00369 
00370     // Iterate over entries in the input vector.
00371     for (unsigned global_index_outer = 0; global_index_outer < num_nodes; global_index_outer++)
00372     {
00373         //Only perturb if we own it
00374         if (lo<=global_index_outer && global_index_outer<hi)
00375         {
00376             PETSCEXCEPT( VecSetValue(current_guess_copy, global_index_outer,h, ADD_VALUES) );
00377         }
00378         AssembleResidual(current_guess_copy, perturbed_residual);
00379 
00380         // result = (perturbed_residual - residual) / h
00381 #if (PETSC_VERSION_MINOR == 2) //Old API
00382         PETSCEXCEPT( VecWAXPY(&subtract, residual, perturbed_residual, result) );
00383         PETSCEXCEPT( VecScale(&one_over_h, result) );
00384 #else
00385         PETSCEXCEPT( VecWAXPY(result, subtract, residual, perturbed_residual) );
00386         PETSCEXCEPT( VecScale(result, one_over_h) );
00387 #endif
00388 
00389         double *p_result;
00390         PETSCEXCEPT( VecGetArray(result, &p_result) );
00391         for (unsigned global_index=lo; global_index < hi; global_index++)
00392         {
00393             unsigned local_index = global_index - lo;
00394             PETSCEXCEPT( MatSetValue(*pJacobian, global_index, global_index_outer,
00395                                      p_result[local_index], INSERT_VALUES) );
00396         }
00397         PETSCEXCEPT( VecRestoreArray(result, &p_result) );
00398 
00399         if (lo<=global_index_outer && global_index_outer<hi)
00400         {
00401             PETSCEXCEPT( VecSetValue(current_guess_copy, global_index_outer, -h, ADD_VALUES) );
00402         }
00403     }
00404 
00405     VecDestroy(residual);
00406     VecDestroy(perturbed_residual);
00407     VecDestroy(result);
00408     VecDestroy(current_guess_copy);
00409 
00410     MatAssemblyBegin(*pJacobian, MAT_FINAL_ASSEMBLY);
00411     MatAssemblyEnd(*pJacobian, MAT_FINAL_ASSEMBLY);
00412 
00413     return 0; // No error
00414 }
00415 
00416 
00417 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00418 void AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::AssembleSystem(bool assembleVector, bool assembleMatrix,
00419                                 Vec currentGuess, double currentTime)
00420 {
00421     // If the problem is nonlinear the currentGuess MUST be specifed
00422     assert( currentGuess );
00423     BaseClassType::AssembleSystem(assembleVector, assembleMatrix, currentGuess, currentTime);
00424 }
00425 
00426 
00427 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00428 bool AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::ProblemIsNonlinear()
00429 {
00430     return true;
00431 }
00432 
00433 
00434 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00435 void AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::InitialiseForSolve(Vec initialGuess)
00436 {
00437     // Check size of initial guess is correct
00438     PetscInt size_of_init_guess;
00439     VecGetSize(initialGuess, &size_of_init_guess);
00440     PetscInt problem_size = PROBLEM_DIM * this->mpMesh->GetNumNodes();
00441     if (size_of_init_guess != problem_size)
00442     {
00443         std::stringstream error_message;
00444         error_message << "Size of initial guess vector, " << size_of_init_guess
00445                       << ", does not match size of problem, " << problem_size;
00446         EXCEPTION(error_message.str());
00447     }
00448 }
00449 
00450 
00451 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00452 Vec AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::StaticSolve(Vec currentSolutionOrGuess,
00453                     double currentTime,
00454                     bool assembleMatrix)
00455 {
00456     assert(this->mpBoundaryConditions!=NULL);
00457     assert(currentSolutionOrGuess!=NULL);
00458     assert(assembleMatrix); 
00459 
00460     // run the solver, telling it which global functions to call in order to assemble
00461     // the residual or jacobian
00462     Vec answer = this->mpSolver->Solve(&AbstractNonlinearAssembler_AssembleResidual<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>,
00463                                        &AbstractNonlinearAssembler_AssembleJacobian<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>,
00464                                        currentSolutionOrGuess,
00465                                        this);
00466     return answer;
00467 }
00468 
00469 
00470 
00471 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00472 AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::AbstractNonlinearAssembler(unsigned numQuadPoints)
00473     : BaseClassType(numQuadPoints),
00474       mInitialGuess(NULL),
00475       mWeAllocatedSolverMemory(true),
00476       mUseAnalyticalJacobian(false)
00477 {
00478     mpSolver = new SimplePetscNonlinearSolver;
00479 }
00480 
00481 
00482 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00483 AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::~AbstractNonlinearAssembler()
00484 {
00485     if (mWeAllocatedSolverMemory)
00486     {
00487         delete mpSolver;
00488     }
00489 }
00490 
00491 
00492 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00493 void AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::SetUseAnalyticalJacobian(bool useAnalyticalJacobian)
00494 {
00495     mUseAnalyticalJacobian = useAnalyticalJacobian;
00496 }
00497 
00498 
00499 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00500 Vec AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::Solve(Vec initialGuess, bool useAnalyticalJacobian)
00501 {
00502     assert(initialGuess!=NULL);
00503     SetUseAnalyticalJacobian(useAnalyticalJacobian);
00504 
00505     this->PrepareForSolve();
00506     InitialiseForSolve(initialGuess);
00507 
00508     return StaticSolve(initialGuess);
00509 }
00510 
00511 
00512 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00513 void AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::SetNonlinearSolver(AbstractNonlinearSolver* pNonlinearSolver)
00514 {
00515     if (mWeAllocatedSolverMemory)
00516     {
00517         delete mpSolver;
00518     }
00519     mpSolver = pNonlinearSolver;
00520     mWeAllocatedSolverMemory = false;
00521 }
00522 
00523 
00524 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00525 Vec AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::CreateConstantInitialGuess(double value)
00526 {
00527     assert(this->mpMesh!=NULL);
00528     unsigned size = PROBLEM_DIM * this->mpMesh->GetNumNodes();
00529     return PetscTools::CreateVec(size, value);
00530 }
00531 
00532 
00533 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00534 bool AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>::VerifyJacobian(double tol, bool print)
00535 {
00536     unsigned size = PROBLEM_DIM * this->mpMesh->GetNumNodes();
00537 
00538     Vec initial_guess;
00539     VecCreate(PETSC_COMM_WORLD, &initial_guess);
00540     VecSetSizes(initial_guess, PETSC_DECIDE, size);
00541     VecSetFromOptions(initial_guess);
00542 
00543     for (unsigned i=0; i<size; i++)
00544     {
00545         VecSetValue(initial_guess, i, 0.0, INSERT_VALUES);
00546     }
00547 
00548     VecAssemblyBegin(initial_guess);
00549     VecAssemblyEnd(initial_guess);
00550 
00551 
00552     Mat analytic_jacobian; //Jacobian Matrix
00553     Mat numerical_jacobian; //Jacobian Matrix
00554 
00555     PetscTools::SetupMat(analytic_jacobian, size, size);
00556     PetscTools::SetupMat(numerical_jacobian, size, size);
00557 
00558     mUseAnalyticalJacobian = true;
00559     AssembleJacobian(initial_guess, &analytic_jacobian);
00560 
00561     mUseAnalyticalJacobian = false;
00562     AssembleJacobian(initial_guess, &numerical_jacobian);
00563 
00564     bool all_less_than_tol = true;
00565 
00566     if (print)
00567     {
00568         std::cout << "Difference between numerical and analyical Jacobians:\n\n";
00569     }
00570     for (unsigned i=0; i<size; i++)
00571     {
00572         for (unsigned j=0; j<size; j++)
00573         {
00574             double val_a[1];
00575             double val_n[1];
00576             PetscInt row[1];
00577             PetscInt col[1];
00578             row[0] = i;
00579             col[0] = j;
00580 
00581             MatGetValues(numerical_jacobian,1,row,1,col,val_n);
00582             MatGetValues(analytic_jacobian,1,row,1,col,val_a);
00583 
00584             if (print)
00585             {
00586                 std::cout << val_n[0] - val_a[0]<< " ";
00587             }
00588 
00589             if (fabs(val_n[0]-val_a[0]) > tol)
00590             {
00591 #define COVERAGE_IGNORE // would have to write a bad concrete assembler class just to cover this line
00592                 all_less_than_tol = false;
00593 #undef COVERAGE_IGNORE
00594             }
00595         }
00596         if (print)
00597         {
00598             std::cout << "\n" <<std::flush;
00599         }
00600     }
00601     std::cout << std::flush;
00602 
00603     MatDestroy(numerical_jacobian);
00604     MatDestroy(analytic_jacobian);
00605     VecDestroy(initial_guess);
00606 
00607     return all_less_than_tol;
00608 }
00609 
00610 
00611 //========================================================================================//
00612 //========================================================================================//
00613 //                                                                                        //
00614 //                Global functions called by the Petsc nonlinear solver                   //
00615 //                                                                                        //
00616 //========================================================================================////========================================================================================//
00617 
00618 
00619 
00633 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00634 PetscErrorCode AbstractNonlinearAssembler_AssembleResidual(SNES snes, Vec currentGuess,
00635         Vec residualVector, void *pContext)
00636 {
00637     // Extract an assembler from the void*
00638     AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE> *pAssembler =
00639         (AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>*) pContext;
00640 
00641     PetscErrorCode ierr = pAssembler->AssembleResidual(currentGuess, residualVector);
00642 
00643     //double two_norm;
00644     //VecNorm(residualVector, NORM_2, &two_norm);
00645     //std::cout << "||residual|| = " << two_norm << "\n";
00646 
00647     return ierr;
00648 }
00649 
00650 
00651 
00667 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM, class CONCRETE>
00668 PetscErrorCode AbstractNonlinearAssembler_AssembleJacobian(SNES snes, Vec currentGuess,
00669         Mat *pGlobalJacobian, Mat *pPreconditioner,
00670         MatStructure *pMatStructure, void *pContext)
00671 {
00672     //std::cout << "begin assemble jacobian\n";
00673 
00674     // Extract an assembler from the void*
00675     AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE> *pAssembler =
00676         (AbstractNonlinearAssembler<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, CONCRETE>*) pContext;
00677 
00678     PetscErrorCode ierr = pAssembler->AssembleJacobian(currentGuess, pGlobalJacobian);
00679     //std::cout << "end assemble jacobian\n";
00680 
00681     return ierr;
00682 }
00683 
00684 
00685 
00686 #endif //_ABSTRACTNONLINEARASSEMBLER_HPP_

Generated on Tue Aug 4 16:10:24 2009 for Chaste by  doxygen 1.5.5