AbstractNonlinearElasticitySolver.hpp

00001 /*
00002 
00003 Copyright (c) 2005-2014, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #ifndef ABSTRACTNONLINEARELASTICITYSOLVER_HPP_
00037 #define ABSTRACTNONLINEARELASTICITYSOLVER_HPP_
00038 
00039 #include <vector>
00040 #include <cmath>
00041 #include "AbstractContinuumMechanicsSolver.hpp"
00042 #include "LinearSystem.hpp"
00043 #include "LogFile.hpp"
00044 #include "MechanicsEventHandler.hpp"
00045 #include "ReplicatableVector.hpp"
00046 #include "FourthOrderTensor.hpp"
00047 #include "CmguiDeformedSolutionsWriter.hpp"
00048 #include "AbstractMaterialLaw.hpp"
00049 #include "QuadraticBasisFunction.hpp"
00050 #include "SolidMechanicsProblemDefinition.hpp"
00051 #include "Timer.hpp"
00052 #include "AbstractPerElementWriter.hpp"
00053 #include "petscsnes.h"
00054 
00055 
00056 
00057 //#define MECH_USE_HYPRE    // uses HYPRE to solve linear systems, requires PETSc to be installed with HYPRE
00058 
00059 
00064 typedef enum StrainType_
00065 {
00066     DEFORMATION_GRADIENT_F = 0,
00067     DEFORMATION_TENSOR_C,
00068     LAGRANGE_STRAIN_E
00069 } StrainType;
00070 
00071 
00072 
00073 // Bizarrely PETSc 2.2 has this, but doesn't put it in the petscksp.h header...
00074 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00075 extern PetscErrorCode KSPInitialResidual(KSP,Vec,Vec,Vec,Vec,Vec);
00076 #endif
00077 
00078 
00080 //  Globals functions used by the SNES solver
00082 
00092 template<unsigned DIM>
00093 PetscErrorCode AbstractNonlinearElasticitySolver_ComputeResidual(SNES snes,
00094                                                                  Vec currentGuess,
00095                                                                  Vec residualVector,
00096                                                                  void* pContext);
00097 
00108 template<unsigned DIM>
00109 PetscErrorCode AbstractNonlinearElasticitySolver_ComputeJacobian(SNES snes,
00110                                                                  Vec currentGuess,
00111                                                                  Mat* pGlobalJacobian,
00112                                                                  Mat* pPreconditioner,
00113                                                                  MatStructure* pMatStructure,
00114                                                                  void* pContext);
00115 template <unsigned DIM>
00116 class  AbstractNonlinearElasticitySolver; //Forward declaration
00117 
00124 template<unsigned DIM>
00125 class StressPerElementWriter : public AbstractPerElementWriter<DIM, DIM, DIM*DIM>
00126 {
00127 private:
00128     AbstractNonlinearElasticitySolver<DIM>* mpSolver; 
00129 public:    
00130     
00135     StressPerElementWriter(AbstractTetrahedralMesh<DIM, DIM>* pMesh,
00136                            AbstractNonlinearElasticitySolver<DIM>* pSolver)
00137      : AbstractPerElementWriter<DIM, DIM, DIM*DIM>(pMesh),
00138        mpSolver(pSolver)
00139     {
00140     }
00141 
00149     void Visit(Element<DIM, DIM>* pElement, unsigned localIndex, c_vector<double, DIM*DIM>& rData)
00150     {
00154         assert(localIndex == pElement->GetIndex()); //Will fail when we move to DistributedQuadraticMesh
00155         //Flatten the matrix
00156         c_matrix<double, DIM, DIM> data = mpSolver->GetAverageStressPerElement(localIndex);
00157         for (unsigned i=0; i<DIM; i++)
00158         {
00159             for (unsigned j=0; j<DIM; j++)
00160             {
00161                 rData[i*DIM+j] = data(i,j);
00162             }
00163         }
00164     }
00165 };
00166 
00178 template<unsigned DIM>
00179 class AbstractNonlinearElasticitySolver : public AbstractContinuumMechanicsSolver<DIM>
00180 {
00181     friend class StressRecoveror<DIM>;
00182     friend class VtkNonlinearElasticitySolutionWriter<DIM>;
00183 
00184 
00185 protected:
00186 
00188     static const size_t NUM_VERTICES_PER_ELEMENT = DIM+1;
00189 
00191     static const size_t NUM_NODES_PER_ELEMENT = (DIM+1)*(DIM+2)/2;      // assuming quadratic
00192 
00194     static const size_t NUM_NODES_PER_BOUNDARY_ELEMENT = DIM*(DIM+1)/2; // assuming quadratic
00195 
00200     static const size_t BOUNDARY_STENCIL_SIZE = DIM*NUM_NODES_PER_BOUNDARY_ELEMENT;
00201 
00209     static double MAX_NEWTON_ABS_TOL;
00210 
00212     static double MIN_NEWTON_ABS_TOL;
00213 
00215     static double NEWTON_REL_TOL;
00216 
00221     SolidMechanicsProblemDefinition<DIM>& mrProblemDefinition;
00222 
00227     Mat& mrJacobianMatrix;
00228 
00235     c_matrix<double,DIM,DIM> mChangeOfBasisMatrix;
00236 
00242     double mKspAbsoluteTol;
00243 
00249     bool mWriteOutputEachNewtonIteration;
00250 
00254     FourthOrderTensor<DIM,DIM,DIM,DIM> dTdE;
00255 
00257     unsigned mNumNewtonIterations;
00258 
00264     double mCurrentTime;
00265 
00266 
00270     bool mCheckedOutwardNormals;
00271 
00276     bool mUseSnesSolver;
00277 
00283     double mLastDampingValue;
00284 
00288     bool mFirstStep;
00289     
00294     bool mTakeFullFirstNewtonStep;
00295     
00299     bool mPetscDirectSolve;
00300 
00308     bool mIncludeActiveTension;
00309 
00314     bool mSetComputeAverageStressPerElement;
00315 
00323     std::vector<c_vector<double,DIM*(DIM+1)/2> > mAverageStressesPerElement;
00324 
00332     void AddStressToAverageStressPerElement(c_matrix<double,DIM,DIM>& rT, unsigned elementIndex);
00333 
00342     virtual void SetKspSolverAndPcType(KSP solver);
00343 
00344 
00357     virtual void AssembleSystem(bool assembleResidual, bool assembleLinearSystem)=0;
00358 
00359 
00360 
00368     virtual void FinishAssembleSystem(bool assembleResidual, bool assembleLinearSystem);
00369 
00370 
00372     //
00373     // Element level methods
00374     //
00376 
00384     void GetElementCentroidStrain(StrainType strainType,
00385                                   Element<DIM,DIM>& rElement,
00386                                   c_matrix<double,DIM,DIM>& rDeformationGradient);
00387 
00406     virtual void AddActiveStressAndStressDerivative(c_matrix<double,DIM,DIM>& rC,
00407                                                     unsigned elementIndex,
00408                                                     unsigned currentQuadPointGlobalIndex,
00409                                                     c_matrix<double,DIM,DIM>& rT,
00410                                                     FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00411                                                     bool addToDTdE)
00412     {
00413         // does nothing - subclass needs to overload this if there are active stresses
00414     }
00415 
00423     virtual void SetupChangeOfBasisMatrix(unsigned elementIndex, unsigned currentQuadPointGlobalIndex)
00424     {
00425     }
00426 
00427 
00428 
00447     void AssembleOnBoundaryElement(BoundaryElement<DIM-1, DIM>& rBoundaryElement,
00448                                    c_matrix<double, BOUNDARY_STENCIL_SIZE, BOUNDARY_STENCIL_SIZE>& rAelem,
00449                                    c_vector<double, BOUNDARY_STENCIL_SIZE>& rBelem,
00450                                    bool assembleResidual,
00451                                    bool assembleJacobian,
00452                                    unsigned boundaryConditionIndex);
00453 
00454 
00469     bool ShouldAssembleMatrixTermForPressureOnDeformedBc();
00470 
00490     void AssembleOnBoundaryElementForPressureOnDeformedBc(BoundaryElement<DIM-1,DIM>& rBoundaryElement,
00491                                                           c_matrix<double,BOUNDARY_STENCIL_SIZE,BOUNDARY_STENCIL_SIZE>& rAelem,
00492                                                           c_vector<double,BOUNDARY_STENCIL_SIZE>& rBelem,
00493                                                           bool assembleResidual,
00494                                                           bool assembleJacobian,
00495                                                           unsigned boundaryConditionIndex);
00496 
00498     //
00499     //    These methods form the non-SNES nonlinear solver
00500     //
00502 
00514     double ComputeResidualAndGetNorm(bool allowException);
00515 
00517     double CalculateResidualNorm();
00518 
00528     void VectorSum(std::vector<double>& rX, ReplicatableVector& rY, double a, std::vector<double>& rZ);
00529 
00537     void PrintLineSearchResult(double s, double residNorm);
00538 
00546     double TakeNewtonStep();
00547 
00557     double UpdateSolutionUsingLineSearch(Vec solution);
00558 
00566     virtual void PostNewtonStep(unsigned counter, double normResidual);
00567 
00573     void SolveNonSnes(double tol=-1.0);
00574 
00575 
00577     //
00578     //    These methods form the SNES nonlinear solver
00579     //
00581 public: // need to be public as are called by global functions
00588     void ComputeResidual(Vec currentGuess, Vec residualVector);
00589 
00597     void ComputeJacobian(Vec currentGuess, Mat* pJacobian, Mat* pPreconditioner);
00598 
00599 private:
00604     void SolveSnes();
00605 
00606 public:
00607 
00618     AbstractNonlinearElasticitySolver(AbstractTetrahedralMesh<DIM,DIM>& rQuadMesh,
00619                                       SolidMechanicsProblemDefinition<DIM>& rProblemDefinition,
00620                                       std::string outputDirectory,
00621                                       CompressibilityType compressibilityType);
00622 
00626     virtual ~AbstractNonlinearElasticitySolver();
00627 
00633     void Solve(double tol=-1.0);
00634 
00644     void SetIncludeActiveTension(bool includeActiveTension = true)
00645     {
00646         mIncludeActiveTension = includeActiveTension;
00647     }
00648 
00652     unsigned GetNumNewtonIterations();
00653 
00654 
00662     void SetWriteOutputEachNewtonIteration(bool writeOutputEachNewtonIteration=true)
00663     {
00664         mWriteOutputEachNewtonIteration = writeOutputEachNewtonIteration;
00665     }
00666 
00673     void SetKspAbsoluteTolerance(double kspAbsoluteTolerance)
00674     {
00675         assert(kspAbsoluteTolerance > 0);
00676         mKspAbsoluteTol = kspAbsoluteTolerance;
00677     }
00678 
00692     void SetTakeFullFirstNewtonStep(bool takeFullFirstStep = true)
00693     {
00694         mTakeFullFirstNewtonStep = takeFullFirstStep;
00695     }
00696 
00710     void SetUsePetscDirectSolve(bool usePetscDirectSolve = true)
00711     {
00712         mPetscDirectSolve = usePetscDirectSolve;
00713     }
00714 
00715 
00722     void SetCurrentTime(double time)
00723     {
00724         mCurrentTime = time;
00725     }
00726 
00731     void CreateCmguiOutput();
00732 
00733 
00747     void WriteCurrentStrains(StrainType strainType, std::string fileName, int counterToAppend = -1);
00748 
00755     void SetComputeAverageStressPerElementDuringSolve(bool setComputeAverageStressPerElement = true);
00756 
00770     void WriteCurrentAverageElementStresses(std::string fileName, int counterToAppend = -1);
00771 
00776     std::vector<c_vector<double,DIM> >& rGetSpatialSolution();
00777 
00782     std::vector<c_vector<double,DIM> >& rGetDeformedPosition();
00783 
00792     c_matrix<double,DIM,DIM> GetAverageStressPerElement(unsigned elementIndex);
00793 };
00794 
00796 // Implementation: first, the non-nonlinear-solve methods
00798 
00799 template<unsigned DIM>
00800 AbstractNonlinearElasticitySolver<DIM>::AbstractNonlinearElasticitySolver(AbstractTetrahedralMesh<DIM,DIM>& rQuadMesh,
00801                                                                           SolidMechanicsProblemDefinition<DIM>& rProblemDefinition,
00802                                                                           std::string outputDirectory,
00803                                                                           CompressibilityType compressibilityType)
00804     : AbstractContinuumMechanicsSolver<DIM>(rQuadMesh, rProblemDefinition, outputDirectory, compressibilityType),
00805       mrProblemDefinition(rProblemDefinition),
00806       mrJacobianMatrix(this->mSystemLhsMatrix),
00807       mKspAbsoluteTol(-1),
00808       mWriteOutputEachNewtonIteration(false),
00809       mNumNewtonIterations(0),
00810       mCurrentTime(0.0),
00811       mCheckedOutwardNormals(false),
00812       mLastDampingValue(0.0),
00813       mIncludeActiveTension(true),
00814       mSetComputeAverageStressPerElement(false)
00815 {
00816     mUseSnesSolver = (mrProblemDefinition.GetSolveUsingSnes() ||
00817                       CommandLineArguments::Instance()->OptionExists("-mech_use_snes") );
00818 
00819     mChangeOfBasisMatrix = identity_matrix<double>(DIM,DIM);
00820 
00821     mTakeFullFirstNewtonStep = CommandLineArguments::Instance()->OptionExists("-mech_full_first_newton_step");
00822     mPetscDirectSolve = CommandLineArguments::Instance()->OptionExists("-mech_petsc_direct_solve");
00823 }
00824 
00825 template<unsigned DIM>
00826 AbstractNonlinearElasticitySolver<DIM>::~AbstractNonlinearElasticitySolver()
00827 {
00828 }
00829 
00830 
00831 template<unsigned DIM>
00832 void AbstractNonlinearElasticitySolver<DIM>::FinishAssembleSystem(bool assembleResidual, bool assembleJacobian)
00833 {
00834     PetscVecTools::Finalise(this->mResidualVector);
00835 
00836     if (assembleJacobian)
00837     {
00838         PetscMatTools::SwitchWriteMode(mrJacobianMatrix);
00839         PetscMatTools::SwitchWriteMode(this->mPreconditionMatrix);
00840 
00841         VecCopy(this->mResidualVector, this->mLinearSystemRhsVector);
00842     }
00843 
00844     // Apply Dirichlet boundary conditions
00845     if(assembleJacobian)
00846     {
00847         this->ApplyDirichletBoundaryConditions(NONLINEAR_PROBLEM_APPLY_TO_EVERYTHING, this->mCompressibilityType==COMPRESSIBLE);
00848     }
00849     else if (assembleResidual)
00850     {
00851         this->ApplyDirichletBoundaryConditions(NONLINEAR_PROBLEM_APPLY_TO_RESIDUAL_ONLY, this->mCompressibilityType==COMPRESSIBLE);
00852     }
00853 
00854     if (assembleResidual)
00855     {
00856         PetscVecTools::Finalise(this->mResidualVector);
00857     }
00858     if (assembleJacobian)
00859     {
00860         PetscMatTools::Finalise(mrJacobianMatrix);
00861         PetscMatTools::Finalise(this->mPreconditionMatrix);
00862         PetscVecTools::Finalise(this->mLinearSystemRhsVector);
00863     }
00864 }
00865 
00866 
00867 template<unsigned DIM>
00868 std::vector<c_vector<double,DIM> >& AbstractNonlinearElasticitySolver<DIM>::rGetSpatialSolution()
00869 {
00870     this->mSpatialSolution.clear();
00871     this->mSpatialSolution.resize(this->mrQuadMesh.GetNumNodes(), zero_vector<double>(DIM));
00872     for (unsigned i=0; i<this->mrQuadMesh.GetNumNodes(); i++)
00873     {
00874         for (unsigned j=0; j<DIM; j++)
00875         {
00876             this->mSpatialSolution[i](j) = this->mrQuadMesh.GetNode(i)->rGetLocation()[j] + this->mCurrentSolution[this->mProblemDimension*i+j];
00877         }
00878     }
00879     return this->mSpatialSolution;
00880 }
00881 
00882 template<unsigned DIM>
00883 std::vector<c_vector<double,DIM> >& AbstractNonlinearElasticitySolver<DIM>::rGetDeformedPosition()
00884 {
00885     return rGetSpatialSolution();
00886 }
00887 
00888 
00889 template<unsigned DIM>
00890 void AbstractNonlinearElasticitySolver<DIM>::WriteCurrentStrains(StrainType strainType, std::string fileName, int counterToAppend)
00891 {
00892     if (!this->mWriteOutput)
00893     {
00894         return;
00895     }
00896 
00897     std::stringstream file_name;
00898     file_name << fileName;
00899     if (counterToAppend >= 0)
00900     {
00901         file_name << "_" << counterToAppend;
00902     }
00903     file_name << ".strain";
00904 
00905     out_stream p_file = this->mpOutputFileHandler->OpenOutputFile(file_name.str());
00906 
00907     c_matrix<double,DIM,DIM> strain;
00908 
00909     for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->mrQuadMesh.GetElementIteratorBegin();
00910          iter != this->mrQuadMesh.GetElementIteratorEnd();
00911          ++iter)
00912     {
00913         GetElementCentroidStrain(strainType, *iter, strain);
00914         for(unsigned i=0; i<DIM; i++)
00915         {
00916             for(unsigned j=0; j<DIM; j++)
00917             {
00918                 *p_file << strain(i,j) << " ";
00919             }
00920         }
00921         *p_file << "\n";
00922     }
00923     p_file->close();
00924 }
00925 
00926 
00927 
00928 template<unsigned DIM>
00929 void AbstractNonlinearElasticitySolver<DIM>::WriteCurrentAverageElementStresses(std::string fileName, int counterToAppend)
00930 {
00931     if (!this->mWriteOutput)
00932     {
00933         return;
00934     }
00935 
00936     if(!mSetComputeAverageStressPerElement)
00937     {
00938         EXCEPTION("Call SetComputeAverageStressPerElementDuringSolve() before solve if calling WriteCurrentAverageElementStresses()");
00939     }
00940 
00941     std::stringstream file_name;
00942     file_name << fileName;
00943     if (counterToAppend >= 0)
00944     {
00945         file_name << "_" << counterToAppend;
00946     }
00947     file_name << ".stress";
00948     assert(mAverageStressesPerElement.size()==this->mrQuadMesh.GetNumElements());
00949 
00950     StressPerElementWriter<DIM> stress_writer(&(this->mrQuadMesh),this);
00951     stress_writer.WriteData(*(this->mpOutputFileHandler), file_name.str());
00952 }
00953 
00954 
00955 template<unsigned DIM>
00956 void AbstractNonlinearElasticitySolver<DIM>::CreateCmguiOutput()
00957 {
00958     if (this->mOutputDirectory=="")
00959     {
00960         EXCEPTION("No output directory was given so no output was written, cannot convert to cmgui format");
00961     }
00962 
00963     CmguiDeformedSolutionsWriter<DIM> writer(this->mOutputDirectory + "/cmgui",
00964                                              "solution",
00965                                              this->mrQuadMesh,
00966                                              WRITE_QUADRATIC_MESH);
00967 
00968     std::vector<c_vector<double,DIM> >& r_deformed_positions = this->rGetDeformedPosition();
00969     writer.WriteInitialMesh(); // this writes solution_0.exnode and .exelem
00970     writer.WriteDeformationPositions(r_deformed_positions, 1); // this writes the final solution as solution_1.exnode
00971     writer.WriteCmguiScript(); // writes LoadSolutions.com
00972 }
00973 
00974 template<unsigned DIM>
00975 void AbstractNonlinearElasticitySolver<DIM>::SetComputeAverageStressPerElementDuringSolve(bool setComputeAverageStressPerElement)
00976 {
00977     mSetComputeAverageStressPerElement = setComputeAverageStressPerElement;
00978     if(setComputeAverageStressPerElement && mAverageStressesPerElement.size()==0)
00979     {
00980         mAverageStressesPerElement.resize(this->mrQuadMesh.GetNumElements(), zero_vector<double>(DIM*(DIM+1)/2));
00981     }
00982 }
00983 
00984 template<unsigned DIM>
00985 void AbstractNonlinearElasticitySolver<DIM>::AddStressToAverageStressPerElement(c_matrix<double,DIM,DIM>& rT, unsigned elemIndex)
00986 {
00987     assert(mSetComputeAverageStressPerElement);
00988     assert(elemIndex<this->mrQuadMesh.GetNumElements());
00989 
00990     // In 2d the matrix is
00991     // [T00 T01]
00992     // [T10 T11]
00993     // where T01 = T10. We store this as a vector
00994     // [T00 T01 T11]
00995     //
00996     // Similarly, for 3d we store
00997     // [T00 T01 T02 T11 T12 T22]
00998     for(unsigned i=0; i<DIM*(DIM+1)/2; i++)
00999     {
01000         unsigned row;
01001         unsigned col;
01002         if(DIM==2)
01003         {
01004             row = i<=1 ? 0 : 1;
01005             col = i==0 ? 0 : 1;
01006         }
01007         else // DIM==3
01008         {
01009             row = i<=2 ? 0 : (i<=4? 1 : 2);
01010             col = i==0 ? 0 : (i==1 || i==3? 1 : 2);
01011         }
01012 
01013         this->mAverageStressesPerElement[elemIndex](i) += rT(row,col);
01014     }
01015 }
01016 
01017 
01018 template<unsigned DIM>
01019 c_matrix<double,DIM,DIM> AbstractNonlinearElasticitySolver<DIM>::GetAverageStressPerElement(unsigned elementIndex)
01020 {
01021     if(!mSetComputeAverageStressPerElement)
01022     {
01023         EXCEPTION("Call SetComputeAverageStressPerElementDuringSolve() before solve if calling GetAverageStressesPerElement()");
01024     }
01025     assert(elementIndex<this->mrQuadMesh.GetNumElements());
01026 
01027     c_matrix<double,DIM,DIM> stress;
01028 
01029     // In 2d the matrix is
01030     // [T00 T01]
01031     // [T10 T11]
01032     // where T01 = T10, and was stored as
01033     // [T00 T01 T11]
01034     //
01035     // Similarly, for 3d the matrix was stored as
01036     // [T00 T01 T02 T11 T12 T22]
01037     if(DIM==2)
01038     {
01039         stress(0,0) = mAverageStressesPerElement[elementIndex](0);
01040         stress(1,0) = stress(0,1) = mAverageStressesPerElement[elementIndex](1);
01041         stress(1,1) = mAverageStressesPerElement[elementIndex](2);
01042     }
01043     else
01044     {
01045         stress(0,0) = mAverageStressesPerElement[elementIndex](0);
01046         stress(1,0) = stress(0,1) = mAverageStressesPerElement[elementIndex](1);
01047         stress(2,0) = stress(0,2) = mAverageStressesPerElement[elementIndex](2);
01048         stress(1,1) = mAverageStressesPerElement[elementIndex](3);
01049         stress(2,1) = stress(1,2) = mAverageStressesPerElement[elementIndex](4);
01050         stress(2,2) = mAverageStressesPerElement[elementIndex](5);
01051     }
01052 
01053     return stress;
01054 }
01055 
01056 
01058 // Methods at the 'element level'.
01060 
01061 template<unsigned DIM>
01062 void AbstractNonlinearElasticitySolver<DIM>::GetElementCentroidStrain(StrainType strainType,
01063                                                                       Element<DIM,DIM>& rElement,
01064                                                                       c_matrix<double,DIM,DIM>& rStrain)
01065 {
01066     static c_matrix<double,DIM,DIM> jacobian;
01067     static c_matrix<double,DIM,DIM> inverse_jacobian;
01068     double jacobian_determinant;
01069 
01070     this->mrQuadMesh.GetInverseJacobianForElement(rElement.GetIndex(), jacobian, jacobian_determinant, inverse_jacobian);
01071 
01072     // Get the current displacement at the nodes
01073     static c_matrix<double,DIM,NUM_NODES_PER_ELEMENT> element_current_displacements;
01074     for (unsigned II=0; II<NUM_NODES_PER_ELEMENT; II++)
01075     {
01076         for (unsigned JJ=0; JJ<DIM; JJ++)
01077         {
01078             element_current_displacements(JJ,II) = this->mCurrentSolution[this->mProblemDimension*rElement.GetNodeGlobalIndex(II) + JJ];
01079         }
01080     }
01081 
01082     // Allocate memory for the basis functions values and derivative values
01083     static c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> grad_quad_phi;
01084     static c_matrix<double,DIM,DIM> grad_u; // grad_u = (du_i/dX_M)
01085 
01086     // we need the point in the canonical element which corresponds to the centroid of the
01087     // version of the element in physical space. This point can be shown to be (1/3,1/3).
01088     ChastePoint<DIM> quadrature_point;
01089     if(DIM==2)
01090     {
01091         quadrature_point.rGetLocation()(0) = 1.0/3.0;
01092         quadrature_point.rGetLocation()(1) = 1.0/3.0;
01093     }
01094     else
01095     {
01096         assert(DIM==3);
01097         quadrature_point.rGetLocation()(0) = 1.0/4.0;
01098         quadrature_point.rGetLocation()(1) = 1.0/4.0;
01099         quadrature_point.rGetLocation()(2) = 1.0/4.0;
01100     }
01101 
01102     QuadraticBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_quad_phi);
01103 
01104     // Interpolate grad_u
01105     grad_u = zero_matrix<double>(DIM,DIM);
01106     for (unsigned node_index=0; node_index<NUM_NODES_PER_ELEMENT; node_index++)
01107     {
01108         for (unsigned i=0; i<DIM; i++)
01109         {
01110             for (unsigned M=0; M<DIM; M++)
01111             {
01112                 grad_u(i,M) += grad_quad_phi(M,node_index)*element_current_displacements(i,node_index);
01113             }
01114         }
01115     }
01116 
01117     c_matrix<double,DIM,DIM> deformation_gradient;
01118 
01119     for (unsigned i=0; i<DIM; i++)
01120     {
01121         for (unsigned M=0; M<DIM; M++)
01122         {
01123             deformation_gradient(i,M) = (i==M?1:0) + grad_u(i,M);
01124         }
01125     }
01126 
01127     switch(strainType)
01128     {
01129         case DEFORMATION_GRADIENT_F:
01130         {
01131             rStrain = deformation_gradient;
01132             break;
01133         }
01134         case DEFORMATION_TENSOR_C:
01135         {
01136             rStrain = prod(trans(deformation_gradient),deformation_gradient);
01137             break;
01138         }
01139         case LAGRANGE_STRAIN_E:
01140         {
01141             c_matrix<double,DIM,DIM> C = prod(trans(deformation_gradient),deformation_gradient);
01142             for (unsigned M=0; M<DIM; M++)
01143             {
01144                 for (unsigned N=0; N<DIM; N++)
01145                 {
01146                     rStrain(M,N) = 0.5* ( C(M,N)-(M==N?1:0) );
01147                 }
01148             }
01149             break;
01150         }
01151         default:
01152         {
01153             NEVER_REACHED;
01154             break;
01155         }
01156     }
01157 }
01158 
01159 
01160 
01161 template<unsigned DIM>
01162 void AbstractNonlinearElasticitySolver<DIM>::AssembleOnBoundaryElement(
01163             BoundaryElement<DIM-1,DIM>& rBoundaryElement,
01164             c_matrix<double,BOUNDARY_STENCIL_SIZE,BOUNDARY_STENCIL_SIZE>& rAelem,
01165             c_vector<double,BOUNDARY_STENCIL_SIZE>& rBelem,
01166             bool assembleResidual,
01167             bool assembleJacobian,
01168             unsigned boundaryConditionIndex)
01169 {
01170     if(   this->mrProblemDefinition.GetTractionBoundaryConditionType() == PRESSURE_ON_DEFORMED
01171        || this->mrProblemDefinition.GetTractionBoundaryConditionType() == FUNCTIONAL_PRESSURE_ON_DEFORMED)
01172     {
01173         AssembleOnBoundaryElementForPressureOnDeformedBc(rBoundaryElement, rAelem, rBelem,
01174                                                          assembleResidual, assembleJacobian, boundaryConditionIndex);
01175         return;
01176     }
01177 
01178     rAelem.clear();
01179     rBelem.clear();
01180 
01181     if (assembleJacobian && !assembleResidual)
01182     {
01183         // Nothing to do
01184         return;
01185     }
01186 
01187     c_vector<double, DIM> weighted_direction;
01188     double jacobian_determinant;
01189     this->mrQuadMesh.GetWeightedDirectionForBoundaryElement(rBoundaryElement.GetIndex(), weighted_direction, jacobian_determinant);
01190 
01191     c_vector<double,NUM_NODES_PER_BOUNDARY_ELEMENT> phi;
01192 
01193     for (unsigned quad_index=0; quad_index<this->mpBoundaryQuadratureRule->GetNumQuadPoints(); quad_index++)
01194     {
01195         double wJ = jacobian_determinant * this->mpBoundaryQuadratureRule->GetWeight(quad_index);
01196 
01197         const ChastePoint<DIM-1>& quad_point = this->mpBoundaryQuadratureRule->rGetQuadPoint(quad_index);
01198 
01199         QuadraticBasisFunction<DIM-1>::ComputeBasisFunctions(quad_point, phi);
01200 
01201         // Get the required traction, interpolating X (slightly inefficiently,
01202         // as interpolating using quad bases) if necessary
01203         c_vector<double,DIM> traction = zero_vector<double>(DIM);
01204         switch (this->mrProblemDefinition.GetTractionBoundaryConditionType())
01205         {
01206             case FUNCTIONAL_TRACTION:
01207             {
01208                 c_vector<double,DIM> X = zero_vector<double>(DIM);
01209                 for (unsigned node_index=0; node_index<NUM_NODES_PER_BOUNDARY_ELEMENT; node_index++)
01210                 {
01211                     X += phi(node_index)*this->mrQuadMesh.GetNode( rBoundaryElement.GetNodeGlobalIndex(node_index) )->rGetLocation();
01212                 }
01213                 traction = this->mrProblemDefinition.EvaluateTractionFunction(X, this->mCurrentTime);
01214                 break;
01215             }
01216             case ELEMENTWISE_TRACTION:
01217             {
01218                 traction = this->mrProblemDefinition.rGetElementwiseTractions()[boundaryConditionIndex];
01219                 break;
01220             }
01221             default:
01222                 NEVER_REACHED;
01223         }
01224 
01225 
01226         for (unsigned index=0; index<NUM_NODES_PER_BOUNDARY_ELEMENT*DIM; index++)
01227         {
01228             unsigned spatial_dim = index%DIM;
01229             unsigned node_index = (index-spatial_dim)/DIM;
01230 
01231             assert(node_index < NUM_NODES_PER_BOUNDARY_ELEMENT);
01232 
01233             rBelem(index) -=   traction(spatial_dim)
01234                              * phi(node_index)
01235                              * wJ;
01236         }
01237     }
01238 }
01239 
01240 template<unsigned DIM>
01241 bool AbstractNonlinearElasticitySolver<DIM>::ShouldAssembleMatrixTermForPressureOnDeformedBc()
01242 {
01243     if(mUseSnesSolver)
01244     {
01245         // although not using this in the first few steps might be useful when the deformation
01246         // is large, the snes solver is more robust, so we have this on all the time. (Also because
01247         // for cardiac problems and in timesteps after the initial large deformation, we want this on
01248         // in the first step
01249         return true;
01250 
01251         // could do something like this, if make the snes a member variable
01252         //PetscInt iteration_number;
01253         //SNESGetIterationNumber(mSnes,&iteration_number);
01254         //return (iteration_number >= 3);
01255     }
01256     else
01257     {
01258         return (mLastDampingValue >= 0.5);
01259     }
01260 
01261 }
01262 
01263 template<unsigned DIM>
01264 void AbstractNonlinearElasticitySolver<DIM>::AssembleOnBoundaryElementForPressureOnDeformedBc(
01265             BoundaryElement<DIM-1,DIM>& rBoundaryElement,
01266             c_matrix<double,BOUNDARY_STENCIL_SIZE,BOUNDARY_STENCIL_SIZE>& rAelem,
01267             c_vector<double,BOUNDARY_STENCIL_SIZE>& rBelem,
01268             bool assembleResidual,
01269             bool assembleJacobian,
01270             unsigned boundaryConditionIndex)
01271 {
01272     assert(   this->mrProblemDefinition.GetTractionBoundaryConditionType()==PRESSURE_ON_DEFORMED
01273            || this->mrProblemDefinition.GetTractionBoundaryConditionType()==FUNCTIONAL_PRESSURE_ON_DEFORMED);
01274 
01275     rAelem.clear();
01276     rBelem.clear();
01277 
01278     c_vector<double, DIM> weighted_direction;
01279     double jacobian_determinant;
01280     // note: jacobian determinant may be over-written below
01281     this->mrQuadMesh.GetWeightedDirectionForBoundaryElement(rBoundaryElement.GetIndex(), weighted_direction, jacobian_determinant);
01282 
01283 
01285     // Find the volume element of the mesh which
01286     // contains this boundary element
01288 
01289     Element<DIM,DIM>* p_containing_vol_element = NULL;
01290 
01291     std::set<unsigned> potential_elements = rBoundaryElement.GetNode(0)->rGetContainingElementIndices();
01292     for(std::set<unsigned>::iterator iter = potential_elements.begin();
01293         iter != potential_elements.end();
01294         iter++)
01295     {
01296         p_containing_vol_element = this->mrQuadMesh.GetElement(*iter);
01297 
01298         bool this_vol_ele_contains_surf_ele = true;
01299         // loop over the nodes of boundary element and see if they are in the volume element
01300         for(unsigned i=1; i<NUM_NODES_PER_BOUNDARY_ELEMENT; i++) // don't need to start at 0, given looping over contain elems of node 0
01301         {
01302             unsigned surf_element_node_index = rBoundaryElement.GetNodeGlobalIndex(i);
01303             bool found_this_node = false;
01304             for(unsigned j=0; j<p_containing_vol_element->GetNumNodes(); j++)
01305             {
01306                 unsigned vol_element_node_index = p_containing_vol_element->GetNodeGlobalIndex(j);
01307                 if(surf_element_node_index == vol_element_node_index)
01308                 {
01309                     found_this_node = true;
01310                     break;
01311                 }
01312             }
01313             if(!found_this_node)
01314             {
01315                 this_vol_ele_contains_surf_ele = false;
01316                 break;
01317             }
01318         }
01319         if(this_vol_ele_contains_surf_ele)
01320         {
01321             break;
01322         }
01323     }
01324 
01325     // Find the local node index in the volume element for each node in the boundary element
01326     std::vector<unsigned> surf_to_vol_map(NUM_NODES_PER_BOUNDARY_ELEMENT);
01327     for(unsigned i=0; i<NUM_NODES_PER_BOUNDARY_ELEMENT; i++)
01328     {
01329         unsigned index = rBoundaryElement.GetNodeGlobalIndex(i);
01330         for(unsigned j=0; j<NUM_NODES_PER_ELEMENT; j++)
01331         {
01332             if(p_containing_vol_element->GetNodeGlobalIndex(j)==index)
01333             {
01334                 surf_to_vol_map[i] = j;
01335                 break;
01336             }
01337         }
01338     }
01339 
01340 
01341     // We require the volume element to compute F, which requires grad_phi on the volume element. For this we will
01342     // need the inverse jacobian for the volume element
01343     static c_matrix<double,DIM,DIM> jacobian_vol_element;
01344     static c_matrix<double,DIM,DIM> inverse_jacobian_vol_element;
01345     double jacobian_determinant_vol_element;
01346     this->mrQuadMesh.GetInverseJacobianForElement(p_containing_vol_element->GetIndex(), jacobian_vol_element, jacobian_determinant_vol_element, inverse_jacobian_vol_element);
01347 
01348     // Get the current displacements at each node of the volume element, to be used in computing F
01349     static c_matrix<double,DIM,NUM_NODES_PER_ELEMENT> element_current_displacements;
01350     for (unsigned II=0; II<NUM_NODES_PER_ELEMENT; II++)
01351     {
01352         for (unsigned JJ=0; JJ<DIM; JJ++)
01353         {
01354             element_current_displacements(JJ,II) = this->mCurrentSolution[this->mProblemDimension*p_containing_vol_element->GetNodeGlobalIndex(II) + JJ];
01355         }
01356     }
01357 
01358 
01359     // We will need both {grad phi_i} for the quadratic bases of the volume element, for computing F..
01360     static c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> grad_quad_phi_vol_element;
01361     // ..the phi_i for each of the quadratic bases of the surface element, for the standard FE assembly part.
01362     c_vector<double,NUM_NODES_PER_BOUNDARY_ELEMENT> quad_phi_surf_element;
01363     // We need this too, which is obtained by taking a subset of grad_quad_phi_vol_element
01364     static c_matrix<double, DIM, NUM_NODES_PER_BOUNDARY_ELEMENT> grad_quad_phi_surf_element;
01365 
01366     c_matrix<double,DIM,DIM> F;
01367     c_matrix<double,DIM,DIM> invF;
01368 
01369     c_vector<double,DIM> normal = rBoundaryElement.CalculateNormal();
01370     c_matrix<double,1,DIM> normal_as_mat;
01371     for(unsigned i=0; i<DIM; i++)
01372     {
01373         normal_as_mat(0,i) = normal(i);
01374     }
01375 
01376     double normal_pressure;
01377     switch (this->mrProblemDefinition.GetTractionBoundaryConditionType())
01378     {
01379         case PRESSURE_ON_DEFORMED:
01380             normal_pressure = this->mrProblemDefinition.GetNormalPressure();
01381             break;
01382         case FUNCTIONAL_PRESSURE_ON_DEFORMED:
01383             normal_pressure = this->mrProblemDefinition.EvaluateNormalPressureFunction(this->mCurrentTime);
01384             break;
01385         default:
01386             NEVER_REACHED;
01387     }
01388 
01389 
01390 
01391     for (unsigned quad_index=0; quad_index<this->mpBoundaryQuadratureRule->GetNumQuadPoints(); quad_index++)
01392     {
01393         double wJ = jacobian_determinant * this->mpBoundaryQuadratureRule->GetWeight(quad_index);
01394 
01395         // Get the quadrature point on this surface element (in canonical space) - so eg, for a 2D problem,
01396         // the quad point is in 1D space
01397         const ChastePoint<DIM-1>& quadrature_point = this->mpBoundaryQuadratureRule->rGetQuadPoint(quad_index);
01398         QuadraticBasisFunction<DIM-1>::ComputeBasisFunctions(quadrature_point, quad_phi_surf_element);
01399 
01400         // We will need the xi coordinates of this quad point in the volume element. We could do this by figuring
01401         // out how the nodes of the surface element are ordered in the list of nodes in the volume element,
01402         // however it is less fiddly to compute directly. Firstly, compute the corresponding physical location
01403         // of the quad point, by interpolating
01404         c_vector<double,DIM> X = zero_vector<double>(DIM);
01405         for (unsigned node_index=0; node_index<NUM_NODES_PER_BOUNDARY_ELEMENT; node_index++)
01406         {
01407             X += quad_phi_surf_element(node_index)*rBoundaryElement.GetNode(node_index)->rGetLocation();
01408         }
01409 
01410 
01411         // Now compute the xi coordinates of the quad point in the volume element
01412         c_vector<double,DIM+1> weight = p_containing_vol_element->CalculateInterpolationWeights(X);
01413         c_vector<double,DIM> xi;
01414         for(unsigned i=0; i<DIM; i++)
01415         {
01416             xi(i) = weight(i+1); // Note, in 2d say, weights = [1-xi(0)-xi(1), xi(0), xi(1)]
01417         }
01418 
01419         // check one of the weights was zero, as the quad point is on the boundary of the volume element
01420         if(DIM==2)
01421         {
01422             assert( DIM!=2 || (fabs(weight(0))<1e-6) || (fabs(weight(1))<1e-6) || (fabs(weight(2))<1e-6) );
01423         }
01424         else
01425         {
01426             #define COVERAGE_IGNORE
01427             assert( DIM!=3 || (fabs(weight(0))<1e-6) || (fabs(weight(1))<1e-6) || (fabs(weight(2))<1e-6)  || (fabs(weight(3))<1e-6) );
01428             #undef COVERAGE_IGNORE
01429         }
01430 
01431         // Now we can compute the grad_phi and then interpolate F
01432         QuadraticBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(xi, inverse_jacobian_vol_element, grad_quad_phi_vol_element);
01433 
01434         F = identity_matrix<double>(DIM,DIM);
01435         for (unsigned node_index=0; node_index<NUM_NODES_PER_ELEMENT; node_index++)
01436         {
01437             for (unsigned i=0; i<DIM; i++)
01438             {
01439                 for (unsigned M=0; M<DIM; M++)
01440                 {
01441                     F(i,M) += grad_quad_phi_vol_element(M,node_index)*element_current_displacements(i,node_index);
01442                 }
01443             }
01444         }
01445 
01446         double detF = Determinant(F);
01447         invF = Inverse(F);
01448 
01449         if(assembleResidual)
01450         {
01451             c_vector<double,DIM> traction = detF*normal_pressure*prod(trans(invF),normal);
01452 
01453             // assemble
01454             for (unsigned index=0; index<NUM_NODES_PER_BOUNDARY_ELEMENT*DIM; index++)
01455             {
01456                 unsigned spatial_dim = index%DIM;
01457                 unsigned node_index = (index-spatial_dim)/DIM;
01458 
01459                 assert(node_index < NUM_NODES_PER_BOUNDARY_ELEMENT);
01460 
01461                 rBelem(index) -=   traction(spatial_dim)
01462                                  * quad_phi_surf_element(node_index)
01463                                  * wJ;
01464             }
01465         }
01466 
01467 
01468         // Sometimes we don't include the analytic jacobian for this integral
01469         // in the jacobian that is assembled - the ShouldAssembleMatrixTermForPressureOnDeformedBc()
01470         // bit below - see the documentation for this method to see why.
01471         if(assembleJacobian && ShouldAssembleMatrixTermForPressureOnDeformedBc())
01472         {
01473             for(unsigned II=0; II<NUM_NODES_PER_BOUNDARY_ELEMENT; II++)
01474             {
01475                 for(unsigned N=0; N<DIM; N++)
01476                 {
01477                     grad_quad_phi_surf_element(N,II) = grad_quad_phi_vol_element(N,surf_to_vol_map[II]);
01478                 }
01479             }
01480 
01481             static FourthOrderTensor<DIM,DIM,DIM,DIM> tensor1;
01482             for(unsigned N=0; N<DIM; N++)
01483             {
01484                 for(unsigned e=0; e<DIM; e++)
01485                 {
01486                     for(unsigned M=0; M<DIM; M++)
01487                     {
01488                         for(unsigned d=0; d<DIM; d++)
01489                         {
01490                             tensor1(N,e,M,d) = invF(N,e)*invF(M,d) - invF(M,e)*invF(N,d);
01491                         }
01492                     }
01493                 }
01494             }
01495 
01496             // tensor2(II,e,M,d) = tensor1(N,e,M,d)*grad_quad_phi_surf_element(N,II)
01497             static FourthOrderTensor<NUM_NODES_PER_BOUNDARY_ELEMENT,DIM,DIM,DIM> tensor2;
01498             tensor2.template SetAsContractionOnFirstDimension<DIM>( trans(grad_quad_phi_surf_element), tensor1);
01499 
01500             // tensor3 is really a third-order tensor
01501             // tensor3(II,e,0,d) = tensor2(II,e,M,d)*normal(M)
01502             static FourthOrderTensor<NUM_NODES_PER_BOUNDARY_ELEMENT,DIM,1,DIM> tensor3;
01503             tensor3.template SetAsContractionOnThirdDimension<DIM>( normal_as_mat, tensor2);
01504 
01505             for (unsigned index1=0; index1<NUM_NODES_PER_BOUNDARY_ELEMENT*DIM; index1++)
01506             {
01507                 unsigned spatial_dim1 = index1%DIM;
01508                 unsigned node_index1 = (index1-spatial_dim1)/DIM;
01509 
01510                 for (unsigned index2=0; index2<NUM_NODES_PER_BOUNDARY_ELEMENT*DIM; index2++)
01511                 {
01512                     unsigned spatial_dim2 = index2%DIM;
01513                     unsigned node_index2 = (index2-spatial_dim2)/DIM;
01514 
01515                     rAelem(index1,index2) -=    normal_pressure
01516                                               * detF
01517                                               * tensor3(node_index2,spatial_dim2,0,spatial_dim1)
01518                                               * quad_phi_surf_element(node_index1)
01519                                               * wJ;
01520                 }
01521             }
01522         }
01523     }
01524 }
01525 
01526 
01527 
01528 
01529 template<unsigned DIM>
01530 void AbstractNonlinearElasticitySolver<DIM>::Solve(double tol)
01531 {
01532 
01533     // check the problem definition is set up correctly (and fully).
01534     mrProblemDefinition.Validate();
01535 
01536     // If the problem includes specified pressures on deformed surfaces (as opposed
01537     // to specified tractions), the code needs to compute normals, and they need
01538     // to be consistently all facing outward (or all facing inward). Check the undeformed
01539     // mesh boundary elements has nodes that are ordered so that all normals are
01540     // outward-facing
01541     if(mrProblemDefinition.GetTractionBoundaryConditionType()==PRESSURE_ON_DEFORMED && mCheckedOutwardNormals==false)
01542     {
01543         this->mrQuadMesh.CheckOutwardNormals();
01544         mCheckedOutwardNormals = true;
01545     }
01546 
01547     // Write the initial solution
01548     this->WriteCurrentSpatialSolution("initial", "nodes");
01549 
01550     if(mUseSnesSolver)
01551     {
01552         SolveSnes();
01553     }
01554     else
01555     {
01556         SolveNonSnes(tol);
01557     }
01558 
01559     // Remove pressure dummy values (P=0 at internal nodes, which should have been
01560     // been the result of the solve above), by linear interpolating from vertices of
01561     // edges to the internal node of the edge
01562     if(this->mCompressibilityType==INCOMPRESSIBLE)
01563     {
01564         this->RemovePressureDummyValuesThroughLinearInterpolation();
01565     }
01566 
01567     // Write the final solution
01568     this->WriteCurrentSpatialSolution("solution", "nodes");
01569 
01570 }
01571 
01572 
01576 template<unsigned DIM>
01577 void AbstractNonlinearElasticitySolver<DIM>::SetKspSolverAndPcType(KSP solver)
01578 {
01579     // Four alternatives
01580     //   (a) Petsc direct solve
01581     //   Otherwise iterative solve with:
01582     //   (b) Incompressible: GMRES with ILU preconditioner (or bjacobi=ILU on each process) [default]. Very poor on large problems.
01583     //   (c) Incompressible: GMRES with AMG preconditioner. Uncomment #define MECH_USE_HYPRE above. Requires Petsc3 with HYPRE installed.
01584     //   (d) Compressible: CG with ICC
01585 
01586     PC pc;
01587     KSPGetPC(solver, &pc);
01588 
01589 
01590     if (mPetscDirectSolve)
01591     {
01592         if(this->mCompressibilityType==COMPRESSIBLE)
01593         {
01594             KSPSetType(solver,KSPPREONLY);
01595 
01596         }
01597         PCSetType(pc, PCLU);
01598 
01599         // See #2057
01600         // PCFactorSetMatSolverPackage(pc,"mumps");
01601     }
01602     else
01603     {
01604         if (this->mCompressibilityType==COMPRESSIBLE)
01605         {
01606             KSPSetType(solver,KSPCG);
01607             if(PetscTools::IsSequential())
01608             {
01609                 PCSetType(pc, PCICC);
01610                 //Note that PetscOptionsSetValue is dangerous because we can't easily do
01611                 //regression testing.  If a name changes, then the behaviour of the code changes
01612                 //because it won't recognise the old name.  However, it won't fail to compile/run.
01613                 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 1) //PETSc 3.1 or later
01614                     PetscOptionsSetValue("-pc_factor_shift_type", "positive_definite");
01615                 #else
01616                     PetscOptionsSetValue("-pc_factor_shift_positive_definite", "");
01617                 #endif
01618             }
01619             else
01620             {
01621                 PCSetType(pc, PCBJACOBI);
01622             }
01623         }
01624         else
01625         {
01626             unsigned num_restarts = 100;
01627             KSPSetType(solver,KSPGMRES);
01628             KSPGMRESSetRestart(solver,num_restarts);
01629 
01630             #ifndef MECH_USE_HYPRE
01631                 PCSetType(pc, PCBJACOBI); // BJACOBI = ILU on each block (block = part of matrix on each process)
01632             #else
01634                 // Speed up linear solve time massively for larger simulations (in fact GMRES may stagnate without
01635                 // this for larger problems), by using a AMG preconditioner -- needs HYPRE installed
01637                 PetscOptionsSetValue("-pc_hypre_type", "boomeramg");
01638                 // PetscOptionsSetValue("-pc_hypre_boomeramg_max_iter", "1");
01639                 // PetscOptionsSetValue("-pc_hypre_boomeramg_strong_threshold", "0.0");
01640 
01641                 PCSetType(pc, PCHYPRE);
01642                 KSPSetPreconditionerSide(solver, PC_RIGHT);
01643 
01644                 // other possible preconditioners..
01645                 //PCBlockDiagonalMechanics* p_custom_pc = new PCBlockDiagonalMechanics(solver, this->mPreconditionMatrix, mBlock1Size, mBlock2Size);
01646                 //PCLDUFactorisationMechanics* p_custom_pc = new PCLDUFactorisationMechanics(solver, this->mPreconditionMatrix, mBlock1Size, mBlock2Size);
01647                 //remember to delete memory..
01648                 //KSPSetPreconditionerSide(solver, PC_RIGHT);
01649             #endif
01650         }
01651     }
01652 }
01653 
01654 
01655 
01657 //  The code for the non-SNES solver - maybe remove all this
01658 //  as SNES solver appears better
01660 
01661 template<unsigned DIM>
01662 double AbstractNonlinearElasticitySolver<DIM>::ComputeResidualAndGetNorm(bool allowException)
01663 {
01664     if (!allowException)
01665     {
01666         // Assemble the residual
01667         AssembleSystem(true, false);
01668     }
01669     else
01670     {
01671         try
01672         {
01673             // Try to assemble the residual using this current solution
01674             AssembleSystem(true, false);
01675         }
01676         catch(Exception&)
01677         {
01678             // If fail (because e.g. ODEs fail to solve, or strains are too large for material law), return infinity
01679             return DBL_MAX;
01680         }
01681     }
01682 
01683     // Return the scaled norm of the residual
01684     return CalculateResidualNorm();
01685 }
01686 
01687 template<unsigned DIM>
01688 double AbstractNonlinearElasticitySolver<DIM>::CalculateResidualNorm()
01689 {
01690     double norm;
01691 
01692     //\todo Change to NORM_1 and remove the division by mNumDofs...
01693     VecNorm(this->mResidualVector, NORM_2, &norm);
01694     return norm/this->mNumDofs;
01695 }
01696 
01697 template<unsigned DIM>
01698 void AbstractNonlinearElasticitySolver<DIM>::VectorSum(std::vector<double>& rX,
01699                                                        ReplicatableVector& rY,
01700                                                        double a,
01701                                                        std::vector<double>& rZ)
01702 {
01703     assert(rX.size()==rY.GetSize());
01704     assert(rY.GetSize()==rZ.size());
01705     for (unsigned i=0; i<rX.size(); i++)
01706     {
01707         rZ[i] = rX[i] + a*rY[i];
01708     }
01709 }
01710 
01711 
01712 template<unsigned DIM>
01713 double AbstractNonlinearElasticitySolver<DIM>::TakeNewtonStep()
01714 {
01715     if(this->mVerbose)
01716     {
01717         Timer::Reset();
01718     }
01719 
01721     // Assemble Jacobian (and preconditioner)
01723     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ASSEMBLE);
01724     AssembleSystem(true, true);
01725     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ASSEMBLE);
01726     if(this->mVerbose)
01727     {
01728         Timer::PrintAndReset("AssembleSystem");
01729     }
01730 
01732     // Solve the linear system.
01734     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::SOLVE);
01735 
01736     Vec solution;
01737     VecDuplicate(this->mResidualVector,&solution);
01738 
01739     KSP solver;
01740     KSPCreate(PETSC_COMM_WORLD,&solver);
01741 
01742     KSPSetOperators(solver, mrJacobianMatrix, this->mPreconditionMatrix, DIFFERENT_NONZERO_PATTERN /*in precond between successive solves*/);
01743 
01744     // Set the type of KSP solver (CG, GMRES etc) and preconditioner (ILU, HYPRE, etc)
01745     SetKspSolverAndPcType(solver);
01746 
01747     //PetscOptionsSetValue("-ksp_monitor","");
01748     //PetscOptionsSetValue("-ksp_norm_type","natural");
01749 
01750     KSPSetFromOptions(solver);
01751     KSPSetUp(solver);
01752 
01753 
01754     // Set the linear system absolute tolerance.
01755     // This is either the user provided value, or set to
01756     // max {1e-6 * initial_residual, 1e-12}
01757     if (mKspAbsoluteTol < 0)
01758     {
01759         Vec temp;
01760         VecDuplicate(this->mResidualVector, &temp);
01761         Vec temp2;
01762         VecDuplicate(this->mResidualVector, &temp2);
01763         Vec linsys_residual;
01764         VecDuplicate(this->mResidualVector, &linsys_residual);
01765 
01766         KSPInitialResidual(solver, solution, temp, temp2, linsys_residual, this->mLinearSystemRhsVector);
01767         double initial_resid_norm;
01768         VecNorm(linsys_residual, NORM_2, &initial_resid_norm);
01769 
01770         PetscTools::Destroy(temp);
01771         PetscTools::Destroy(temp2);
01772         PetscTools::Destroy(linsys_residual);
01773 
01774         double ksp_rel_tol = 1e-6;
01775         double absolute_tol = ksp_rel_tol * initial_resid_norm;
01776         if(absolute_tol < 1e-12)
01777         {
01778             absolute_tol = 1e-12;
01779         }
01780         KSPSetTolerances(solver, 1e-16, absolute_tol, PETSC_DEFAULT, 1000 /* max iters */); // Note: some machines - max iters seems to be 1000 whatever we give here
01781     }
01782     else
01783     {
01784         KSPSetTolerances(solver, 1e-16, mKspAbsoluteTol, PETSC_DEFAULT, 1000 /* max iters */); // Note: some machines - max iters seems to be 1000 whatever we give here
01785     }
01786 
01787     if(this->mVerbose)
01788     {
01789         Timer::PrintAndReset("KSP Setup");
01790     }
01791 
01792     KSPSolve(solver,this->mLinearSystemRhsVector,solution);
01793 
01794 //    ///// For printing matrix when debugging
01795 //    OutputFileHandler handler("TEMP",false);
01796 //    std::stringstream ss;
01797 //    static unsigned counter = 0;
01798 //    ss << "all_" << counter++ << ".txt";
01799 //    out_stream p_file = handler.OpenOutputFile(ss.str());
01800 //    *p_file << std::setprecision(10);
01801 //    for(unsigned i=0; i<this->mNumDofs; i++)
01802 //    {
01803 //        for(unsigned j=0; j<this->mNumDofs; j++)
01804 //        {
01805 //            *p_file << PetscMatTools::GetElement(mrJacobianMatrix, i, j) << " ";
01806 //        }
01807 //        *p_file << PetscVecTools::GetElement(this->mLinearSystemRhsVector, i) << " ";
01808 //        *p_file << PetscVecTools::GetElement(solution, i) << "\n";
01809 //    }
01810 //    p_file->close();
01811 
01812 
01814     // Error checking for linear solve
01816 
01817     // warn if ksp reports failure
01818     KSPConvergedReason reason;
01819     KSPGetConvergedReason(solver,&reason);
01820 
01821     if(reason != KSP_DIVERGED_ITS)
01822     {
01823         // Throw an exception if the solver failed for any reason other than DIVERGED_ITS.
01824         // This is not covered as would be difficult to cover - requires a bad matrix to
01825         // assembled, for example.
01826         #define COVERAGE_IGNORE
01827         KSPEXCEPT(reason);
01828         #undef COVERAGE_IGNORE
01829     }
01830     else
01831     {
01832         // DIVERGED_ITS just means it didn't converge in the given maximum number of iterations,
01833         // which is potentially not a problem, as the nonlinear solver may (and often will) still converge.
01834         // Just warn once.
01835         // (Very difficult to cover in normal tests, requires relative and absolute ksp tols to be very small, there
01836         // is no interface for setting both of these. Could be covered by setting up a problem the solver
01837         // finds difficult to solve, but this would be overkill.)
01838         #define COVERAGE_IGNORE
01839         WARN_ONCE_ONLY("Linear solve (within a mechanics solve) didn't converge, but this may not stop nonlinear solve converging")
01840         #undef COVERAGE_IGNORE
01841     }
01842 
01843     // quit if no ksp iterations were done
01844     int num_iters;
01845     KSPGetIterationNumber(solver, &num_iters);
01846     if (num_iters==0)
01847     {
01848         PetscTools::Destroy(solution);
01849         KSPDestroy(PETSC_DESTROY_PARAM(solver));
01850         EXCEPTION("KSP Absolute tolerance was too high, linear system wasn't solved - there will be no decrease in Newton residual. Decrease KspAbsoluteTolerance");
01851     }
01852 
01853 
01854     if(this->mVerbose)
01855     {
01856         Timer::PrintAndReset("KSP Solve");
01857         std::cout << "[" << PetscTools::GetMyRank() << "]: Num iterations = " << num_iters << "\n" << std::flush;
01858     }
01859 
01860     MechanicsEventHandler::EndEvent(MechanicsEventHandler::SOLVE);
01861 
01863     // Update the solution
01864     //  Newton method:       sol = sol - update, where update=Jac^{-1}*residual
01865     //  Newton with damping: sol = sol - s*update, where s is chosen
01866     //   such that |residual(sol)| is minimised. Damping is important to
01867     //   avoid initial divergence.
01868     //
01869     // Normally, finding the best s from say 0.05,0.1,0.2,..,1.0 is cheap,
01870     // but this is not the case in cardiac electromechanics calculations.
01871     // Therefore, we initially check s=1 (expected to be the best most of the
01872     // time, then s=0.9. If the norm of the residual increases, we assume
01873     // s=1 is the best. Otherwise, check s=0.8 to see if s=0.9 is a local min.
01875     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::UPDATE);
01876     double new_norm_resid = UpdateSolutionUsingLineSearch(solution);
01877     MechanicsEventHandler::EndEvent(MechanicsEventHandler::UPDATE);
01878 
01879     PetscTools::Destroy(solution);
01880     KSPDestroy(PETSC_DESTROY_PARAM(solver));
01881 
01882     return new_norm_resid;
01883 }
01884 
01885 
01886 template<unsigned DIM>
01887 void AbstractNonlinearElasticitySolver<DIM>::PrintLineSearchResult(double s, double residNorm)
01888 {
01889     if(this->mVerbose)
01890     {
01891         std::cout << "\tTesting s = " << s << ", |f| = " << residNorm << "\n" << std::flush;
01892     }
01893 }
01894 
01895 template<unsigned DIM>
01896 double AbstractNonlinearElasticitySolver<DIM>::UpdateSolutionUsingLineSearch(Vec solution)
01897 {
01898     double initial_norm_resid = CalculateResidualNorm();
01899     if(this->mVerbose)
01900     {
01901         std::cout << "\tInitial |f| [corresponding to s=0] is " << initial_norm_resid << "\n"  << std::flush;
01902     }
01903 
01904     ReplicatableVector update(solution);
01905 
01906     std::vector<double> old_solution = this->mCurrentSolution;
01907 
01908     std::vector<double> damping_values; // = {1.0, 0.9, .., 0.2, 0.1, 0.05} ie size 11
01909     for (unsigned i=10; i>=1; i--)
01910     {
01911         damping_values.push_back((double)i/10.0);
01912     }
01913     damping_values.push_back(0.05);
01914     assert(damping_values.size()==11);
01915 
01917     // let mCurrentSolution = old_solution - damping_val[0]*update; and compute residual
01918     unsigned index = 0;
01919     VectorSum(old_solution, update, -damping_values[index], this->mCurrentSolution);
01920     double current_resid_norm = ComputeResidualAndGetNorm(true);
01921     PrintLineSearchResult(damping_values[index], current_resid_norm);
01922 
01924     // let mCurrentSolution = old_solution - damping_val[1]*update; and compute residual
01925     index = 1;
01926     VectorSum(old_solution, update, -damping_values[index], this->mCurrentSolution);
01927     double next_resid_norm = ComputeResidualAndGetNorm(true);
01928     PrintLineSearchResult(damping_values[index], next_resid_norm);
01929 
01930     index = 2;
01931     // While f(s_next) < f(s_current), [f = residnorm], keep trying new damping values,
01932     // ie exit thus loop when next norm of the residual first increases
01933     while (    (next_resid_norm==DBL_MAX) // the residual is returned as infinity if the deformation is so large to cause exceptions in the material law/EM contraction model
01934             || ( (next_resid_norm < current_resid_norm) && (index<damping_values.size()) ) )
01935     {
01936         current_resid_norm = next_resid_norm;
01937 
01938         // let mCurrentSolution = old_solution - damping_val*update; and compute residual
01939         VectorSum(old_solution, update, -damping_values[index], this->mCurrentSolution);
01940         next_resid_norm = ComputeResidualAndGetNorm(true);
01941         PrintLineSearchResult(damping_values[index], next_resid_norm);
01942 
01943         index++;
01944     }
01945 
01946     unsigned best_index;
01947 
01948     if (index==damping_values.size() && (next_resid_norm < current_resid_norm))
01949     {
01950         // Difficult to come up with large forces/tractions such that it had to
01951         // test right down to s=0.05, but overall doesn't fail.
01952         // The possible damping values have been manually temporarily altered to
01953         // get this code to be called, it appears to work correctly. Even if it
01954         // didn't tests wouldn't fail, they would just be v. slightly less efficient.
01955         #define COVERAGE_IGNORE
01956         // if we exited because we got to the end of the possible damping values, the
01957         // best one was the last one (excl the final index++ at the end)
01958         current_resid_norm = next_resid_norm;
01959         best_index = index-1;
01960         #undef COVERAGE_IGNORE
01961     }
01962     else
01963     {
01964         // else the best one must have been the second last one (excl the final index++ at the end)
01965         // (as we would have exited when the resid norm first increased)
01966         best_index = index-2;
01967     }
01968 
01969     // See documentation for SetTakeFullFirstNewtonStep()
01970     bool full_first_step = mTakeFullFirstNewtonStep && mFirstStep;
01971 
01972 
01973     // Check out best was better than the original residual-norm
01974     if (initial_norm_resid < current_resid_norm && !full_first_step)
01975     {
01976         #define COVERAGE_IGNORE
01977         EXCEPTION("Residual does not appear to decrease in newton direction, quitting");
01978         #undef COVERAGE_IGNORE
01979     }
01980 
01981     // See documentation for SetTakeFullFirstNewtonStep()
01982     if(full_first_step)
01983     {
01984         if(this->mVerbose)
01985         {
01986             std::cout << "\tTaking full first Newton step...\n";
01987         }
01988         best_index = 0;
01989     }
01990 
01991     if(this->mVerbose)
01992     {
01993         std::cout << "\tChoosing s = " << damping_values[best_index] << "\n"  << std::flush;
01994     }
01995 
01996 
01998 //
01999 //    double l_inf_disp = 0.0;
02000 //    double l_inf_pressure = 0.0;
02001 //
02002 //    if( this->mCompressibilityType==INCOMPRESSIBLE )
02003 //    {
02004 //        for(unsigned i=0; i<this->mrQuadMesh.GetNumNodes(); i++)
02005 //        {
02006 //            for(unsigned j=0; j<DIM; j++)
02007 //            {
02008 //                double value = update[(DIM+1)*i + j]*damping_values[best_index];
02009 //                l_inf_disp = std::max(l_inf_disp, fabs(value));
02010 //            }
02011 //            l_inf_pressure = std::max(l_inf_pressure, fabs(update[(DIM+1)*i + DIM]*damping_values[best_index]));
02012 //        }
02013 //        std::cout << "l_inf_disp, l_inf_pressure = " << l_inf_disp << " " << l_inf_pressure << "\n";
02014 //    }
02015 //    else
02016 //    {
02017 //        for(unsigned i=0; i<this->mrQuadMesh.GetNumNodes(); i++)
02018 //        {
02019 //            for(unsigned j=0; j<DIM; j++)
02020 //            {
02021 //                double value = update[DIM*i + j]*damping_values[best_index];
02022 //                l_inf_disp = std::max(l_inf_disp, fabs(value));
02023 //            }
02024 //        }
02025 //        std::cout << "l_inf_disp = " << l_inf_disp << "\n";
02026 //    }
02027 
02028 
02029 
02030     VectorSum(old_solution, update, -damping_values[best_index], this->mCurrentSolution);
02031 
02032     mLastDampingValue = damping_values[best_index];
02033     return current_resid_norm;
02034 }
02035 
02036 template<unsigned DIM>
02037 void AbstractNonlinearElasticitySolver<DIM>::PostNewtonStep(unsigned counter, double normResidual)
02038 {
02039 }
02040 
02041 
02042 template<unsigned DIM>
02043 void AbstractNonlinearElasticitySolver<DIM>::SolveNonSnes(double tol)
02044 {
02045     mLastDampingValue = 0;
02046 
02047     if (mWriteOutputEachNewtonIteration)
02048     {
02049         this->WriteCurrentSpatialSolution("newton_iteration", "nodes", 0);
02050     }
02051 
02052     // Compute residual
02053     double norm_resid = ComputeResidualAndGetNorm(false);
02054     if(this->mVerbose)
02055     {
02056         std::cout << "\nNorm of residual is " << norm_resid << "\n";
02057     }
02058 
02059     mNumNewtonIterations = 0;
02060     unsigned iteration_number = 1;
02061 
02062     if (tol < 0) // i.e. if wasn't passed in as a parameter
02063     {
02064         tol = NEWTON_REL_TOL*norm_resid;
02065 
02066         #define COVERAGE_IGNORE // not going to have tests in cts for everything
02067         if (tol > MAX_NEWTON_ABS_TOL)
02068         {
02069             tol = MAX_NEWTON_ABS_TOL;
02070         }
02071         if (tol < MIN_NEWTON_ABS_TOL)
02072         {
02073             tol = MIN_NEWTON_ABS_TOL;
02074         }
02075         #undef COVERAGE_IGNORE
02076     }
02077 
02078     if(this->mVerbose)
02079     {
02080         std::cout << "Solving with tolerance " << tol << "\n";
02081     }
02082 
02083     while (norm_resid > tol)
02084     {
02085         if(this->mVerbose)
02086         {
02087             std::cout <<  "\n-------------------\n"
02088                       <<   "Newton iteration " << iteration_number
02089                       << ":\n-------------------\n";
02090         }
02091 
02092         // take newton step (and get returned residual)
02093         mFirstStep = (iteration_number==1);
02094         norm_resid = TakeNewtonStep();
02095 
02096         if(this->mVerbose)
02097         {
02098             std::cout << "Norm of residual is " << norm_resid << "\n";
02099         }
02100 
02101         if (mWriteOutputEachNewtonIteration)
02102         {
02103             this->WriteCurrentSpatialSolution("newton_iteration", "nodes", iteration_number);
02104         }
02105 
02106         mNumNewtonIterations = iteration_number;
02107 
02108         PostNewtonStep(iteration_number,norm_resid);
02109 
02110         iteration_number++;
02111         if (iteration_number==20)
02112         {
02113             #define COVERAGE_IGNORE
02114             EXCEPTION("Not converged after 20 newton iterations, quitting");
02115             #undef COVERAGE_IGNORE
02116         }
02117     }
02118 
02119     if (norm_resid > tol)
02120     {
02121         #define COVERAGE_IGNORE
02122         EXCEPTION("Failed to converge");
02123         #undef COVERAGE_IGNORE
02124     }
02125 }
02126 
02127 
02128 
02129 template<unsigned DIM>
02130 unsigned AbstractNonlinearElasticitySolver<DIM>::GetNumNewtonIterations()
02131 {
02132     return mNumNewtonIterations;
02133 }
02134 
02135 
02136 
02138 //  SNES version of the nonlinear solver
02140 
02141 
02142 template<unsigned DIM>
02143 void AbstractNonlinearElasticitySolver<DIM>::SolveSnes()
02144 {
02145     // Set up solution guess for residuals
02146     Vec initial_guess;
02147     VecDuplicate(this->mResidualVector, &initial_guess);
02148     double* p_initial_guess;
02149     VecGetArray(initial_guess, &p_initial_guess);
02150     int lo, hi;
02151     VecGetOwnershipRange(initial_guess, &lo, &hi);
02152     for (int global_index=lo; global_index<hi; global_index++)
02153     {
02154         int local_index = global_index - lo;
02155         p_initial_guess[local_index] = this->mCurrentSolution[global_index];
02156     }
02157     VecRestoreArray(initial_guess, &p_initial_guess);
02158     PetscVecTools::Finalise(initial_guess);
02159 
02160     Vec snes_residual_vec;
02161     VecDuplicate(this->mResidualVector, &snes_residual_vec);
02162 
02163     SNES snes;
02164 
02165     SNESCreate(PETSC_COMM_WORLD, &snes);
02166     SNESSetFunction(snes, snes_residual_vec, &AbstractNonlinearElasticitySolver_ComputeResidual<DIM>, this);
02167     SNESSetJacobian(snes, mrJacobianMatrix, this->mPreconditionMatrix, &AbstractNonlinearElasticitySolver_ComputeJacobian<DIM>, this);
02168 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 4) //PETSc 3.4 or later
02169     SNESSetType(snes, SNESNEWTONLS);
02170 #else
02171     SNESSetType(snes, SNESLS);
02172 #endif
02173     SNESSetTolerances(snes,1e-5,1e-5,1e-5,PETSC_DEFAULT,PETSC_DEFAULT);
02174     SNESSetMaxLinearSolveFailures(snes,100);
02175 
02176     KSP ksp;
02177     SNESGetKSP(snes, &ksp);
02178 
02179     KSPSetTolerances(ksp, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1000 /* max iters */); // Note: some machines - max iters seems to be 1000 whatever we give here
02180 
02181     // Set the type of KSP solver (CG, GMRES etc) and preconditioner (ILU, HYPRE, etc)
02182     SetKspSolverAndPcType(ksp);
02183 
02184     if(this->mVerbose)
02185     {
02186         PetscOptionsSetValue("-snes_monitor","");
02187     }
02188     SNESSetFromOptions(snes);
02189 
02190 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
02191     PetscErrorCode err = SNESSolve(snes, initial_guess);
02192 #else
02193     PetscErrorCode err = SNESSolve(snes, PETSC_NULL, initial_guess);
02194 #endif
02195 
02196     SNESConvergedReason reason;
02197     SNESGetConvergedReason(snes,&reason);
02198 
02199 #define COVERAGE_IGNORE
02200     if (err != 0)
02201     {
02202         std::stringstream err_stream;
02203         err_stream << err;
02204         PetscTools::Destroy(initial_guess);
02205         PetscTools::Destroy(snes_residual_vec);
02206         SNESDestroy(PETSC_DESTROY_PARAM(snes));
02207         EXCEPTION("Nonlinear Solver failed. PETSc error code: "+err_stream.str()+" .");
02208     }
02209 
02210     if (reason < 0)
02211     {
02212         std::stringstream reason_stream;
02213         reason_stream << reason;
02214         PetscTools::Destroy(initial_guess);
02215         PetscTools::Destroy(snes_residual_vec);
02216         SNESDestroy(PETSC_DESTROY_PARAM(snes));
02217         EXCEPTION("Nonlinear Solver did not converge. PETSc reason code: "+reason_stream.str()+" .");
02218     }
02219 #undef COVERAGE_IGNORE
02220 
02221     PetscInt num_iters;
02222     SNESGetIterationNumber(snes,&num_iters);
02223     mNumNewtonIterations = num_iters;
02224 
02225     PetscTools::Destroy(initial_guess);
02226     PetscTools::Destroy(snes_residual_vec);
02227     SNESDestroy(PETSC_DESTROY_PARAM(snes));
02228 }
02229 
02230 
02231 template<unsigned DIM>
02232 void AbstractNonlinearElasticitySolver<DIM>::ComputeResidual(Vec currentGuess, Vec residualVector)
02233 {
02234     // Note: AssembleSystem() assumes the current solution is in this->mCurrentSolution and assembles
02235     // this->mResiduaVector and/or this->mrJacobianMatrix. Since PETSc wants us to use the input
02236     // currentGuess, and write the output to residualVector, we have to copy do some copies below.
02237 
02238     ReplicatableVector guess_repl(currentGuess);
02239     for(unsigned i=0; i<guess_repl.GetSize(); i++)
02240     {
02241         this->mCurrentSolution[i] = guess_repl[i];
02242     }
02243     AssembleSystem(true,false);
02244     VecCopy(this->mResidualVector, residualVector);
02245 }
02246 
02247 template<unsigned DIM>
02248 void AbstractNonlinearElasticitySolver<DIM>::ComputeJacobian(Vec currentGuess, Mat* pJacobian, Mat* pPreconditioner)
02249 {
02250     // Note: AssembleSystem() assumes the current solution is in this->mCurrentSolution and assembles
02251     // this->mResiduaVector and/or this->mrJacobianMatrix.
02252     // We need to copy the input currentGuess into the local mCurrentGuess.
02253     // We don't have to copy mrJacobianMatrix to pJacobian, which would be expensive, as they will
02254     // point to the same memory.
02255 
02256     // check Petsc data corresponds to internal Mats
02257     assert(mrJacobianMatrix==*pJacobian);
02258     assert(this->mPreconditionMatrix==*pPreconditioner);
02259 
02260     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ASSEMBLE);
02261     ReplicatableVector guess_repl(currentGuess);
02262     for(unsigned i=0; i<guess_repl.GetSize(); i++)
02263     {
02264         this->mCurrentSolution[i] = guess_repl[i];
02265     }
02266 
02267     AssembleSystem(false,true);
02268     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ASSEMBLE);
02269 }
02270 
02271 
02272 
02273 template<unsigned DIM>
02274 PetscErrorCode AbstractNonlinearElasticitySolver_ComputeResidual(SNES snes,
02275                                                                  Vec currentGuess,
02276                                                                  Vec residualVector,
02277                                                                  void* pContext)
02278 {
02279     // Extract the solver from the void*
02280     AbstractNonlinearElasticitySolver<DIM>* p_solver = (AbstractNonlinearElasticitySolver<DIM>*)pContext;
02281     p_solver->ComputeResidual(currentGuess, residualVector);
02282     return 0;
02283 }
02284 
02285 template<unsigned DIM>
02286 PetscErrorCode AbstractNonlinearElasticitySolver_ComputeJacobian(SNES snes,
02287                                                                  Vec currentGuess,
02288                                                                  Mat* pGlobalJacobian,
02289                                                                  Mat* pPreconditioner,
02290                                                                  MatStructure* pMatStructure,
02291                                                                  void* pContext)
02292 {
02293     // Extract the solver from the void*
02294     AbstractNonlinearElasticitySolver<DIM>* p_solver = (AbstractNonlinearElasticitySolver<DIM>*) pContext;
02295     p_solver->ComputeJacobian(currentGuess, pGlobalJacobian, pPreconditioner);
02296     return 0;
02297 }
02298 
02299 
02300 // Constant setting definitions
02301 
02302 template<unsigned DIM>
02303 double AbstractNonlinearElasticitySolver<DIM>::MAX_NEWTON_ABS_TOL = 1e-7;
02304 
02305 template<unsigned DIM>
02306 double AbstractNonlinearElasticitySolver<DIM>::MIN_NEWTON_ABS_TOL = 1e-10;
02307 
02308 template<unsigned DIM>
02309 double AbstractNonlinearElasticitySolver<DIM>::NEWTON_REL_TOL = 1e-4;
02310 
02311 #endif /*ABSTRACTNONLINEARELASTICITYSOLVER_HPP_*/

Generated by  doxygen 1.6.2