AbstractNonlinearElasticityAssembler.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 
00029 #ifndef ABSTRACTNONLINEARELASTICITYASSEMBLER_HPP_
00030 #define ABSTRACTNONLINEARELASTICITYASSEMBLER_HPP_
00031 
00032 #include <vector>
00033 #include <cmath>
00034 #include "LinearSystem.hpp"
00035 #include "AbstractIncompressibleMaterialLaw.hpp"
00036 #include "OutputFileHandler.hpp"
00037 #include "LogFile.hpp"
00038 #include "PetscTools.hpp"
00039 #include "MechanicsEventHandler.hpp"
00040 
00044 //#define ___USE_DEALII_LINEAR_SYSTEM___ 
00045 
00046 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00047   #include "DealiiLinearSystem.hpp"
00048 #endif  
00049 
00050 
00051 //#include "Timer.hpp" // in the dealii folder
00052 
00053 template<unsigned DIM>
00054 class AbstractNonlinearElasticityAssembler
00055 {
00056 protected:
00058     static const double MAX_NEWTON_ABS_TOL = 1e-8;
00060     static const double MIN_NEWTON_ABS_TOL = 1e-12;
00062     static const double NEWTON_REL_TOL = 1e-4;
00063 
00068     unsigned mNumDofs;
00069 
00075     std::vector<AbstractIncompressibleMaterialLaw<DIM>*> mMaterialLaws;
00081 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00082     DealiiLinearSystem* mpLinearSystem;
00083 #else
00084     LinearSystem* mpLinearSystem;
00085 #endif
00086 
00104     LinearSystem* mpPreconditionMatrixLinearSystem;
00105 
00107     c_vector<double,DIM> mBodyForce;
00109     double mDensity;
00110 
00112     std::string mOutputDirectory;
00114     std::vector<unsigned> mFixedNodes;
00116     std::vector<c_vector<double,DIM> > mFixedNodeDisplacements;
00117 
00119     bool mWriteOutput;
00120     
00126     std::vector<double> mCurrentSolution;
00127     
00132     FourthOrderTensor2<DIM> dTdE;
00133     
00135     unsigned mNumNewtonIterations;
00136     
00137     
00139     std::vector<c_vector<double,DIM> > mDeformedPosition;
00140 
00145     std::vector<double> mPressures;
00150     std::vector<c_vector<double,DIM> > mSurfaceTractions;
00151 
00153     c_vector<double,DIM> (*mpBodyForceFunction)(c_vector<double,DIM>&);
00157     c_vector<double,DIM> (*mpTractionBoundaryConditionFunction)(c_vector<double,DIM>&);
00159     bool mUsingBodyForceFunction;
00161     bool mUsingTractionBoundaryConditionFunction;
00162 
00163 
00164 
00165     virtual void FormInitialGuess()=0;
00166     virtual void AssembleSystem(bool assembleResidual, bool assembleJacobian)=0;
00167     virtual std::vector<c_vector<double,DIM> >& rGetDeformedPosition()=0;
00168 
00172     void ApplyBoundaryConditions(bool applyToMatrix)
00173     {
00174         assert(mFixedNodeDisplacements.size()==mFixedNodes.size());
00175         
00176         // The boundary conditions on the NONLINEAR SYSTEM are x=boundary_values
00177         // on the boundary nodes. However:
00178         // The boundary conditions on the LINEAR SYSTEM  Ju=f, where J is the
00179         // u the negative update vector and f is the residual is
00180         // u=current_soln-boundary_values on the boundary nodes
00181         for(unsigned i=0; i<mFixedNodes.size(); i++)
00182         {
00183             unsigned node_index = mFixedNodes[i];
00184             for(unsigned j=0; j<DIM; j++)
00185             {
00186                 unsigned dof_index = DIM*node_index+j;
00187                 double value = mCurrentSolution[dof_index] - mFixedNodeDisplacements[i](j);
00188                 if (applyToMatrix)
00189                 {
00190                     mpLinearSystem->ZeroMatrixRow(dof_index);
00191                     mpLinearSystem->SetMatrixElement(dof_index,dof_index,1);
00192 
00193                     // apply same bcs to preconditioner matrix
00194                     mpPreconditionMatrixLinearSystem->ZeroMatrixRow(dof_index);
00195                     mpPreconditionMatrixLinearSystem->SetMatrixElement(dof_index,dof_index,1);
00196                 }
00197                 mpLinearSystem->SetRhsVectorElement(dof_index, value);
00198             }
00199         }
00200     }
00201 
00203     double CalculateResidualNorm()
00204     {
00205         double norm;
00206 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00207         norm = mpLinearSystem->GetRhsVectorNorm();
00208 #else
00209         VecNorm(mpLinearSystem->rGetRhsVector(), NORM_2, &norm);
00210 #endif
00211         return norm/mNumDofs;
00212     }
00213 
00221     double TakeNewtonStep()
00222     {
00223         //Timer::Reset();
00224 
00226         // Assemble Jacobian (and preconditioner)
00228         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ASSEMBLE);
00229         AssembleSystem(true, true);
00230         MechanicsEventHandler::EndEvent(MechanicsEventHandler::ASSEMBLE);
00231         //Timer::PrintAndReset("AssembleSystem");
00232 
00233 
00235         // Solve the linear system using Petsc GMRES and an LU 
00236         // factorisation of the preconditioner. Note we
00237         // don't call Solve on the linear_system as we want to
00238         // set Petsc options..
00240         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::SOLVE);
00241 
00242 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00243         // solve using an umfpack (in dealii) direct solve..
00244         mpLinearSystem->Solve();
00245         Vector<double>& update = mpLinearSystem->rGetLhsVector(); 
00246         //Timer::PrintAndReset("Direct Solve");
00247 #else
00248         KSP solver;
00249         Vec solution;
00250         VecDuplicate(mpLinearSystem->rGetRhsVector(),&solution);
00251 
00252         Mat& r_jac = mpLinearSystem->rGetLhsMatrix();
00253         Mat& r_precond_jac = mpPreconditionMatrixLinearSystem->rGetLhsMatrix();
00254 
00255         KSPCreate(MPI_COMM_SELF,&solver);
00256 
00257         KSPSetOperators(solver, r_jac, r_precond_jac, SAME_NONZERO_PATTERN /*in precond between successive sovles*/);
00258 
00259         // set max iterations
00260         KSPSetTolerances(solver, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 10000); //hopefully with the preconditioner this max is way too high
00261         KSPSetType(solver,KSPGMRES);
00262         KSPGMRESSetRestart(solver,100); // gmres num restarts
00263 
00264         KSPSetFromOptions(solver);
00265         KSPSetUp(solver);
00266         
00267         PC pc;
00268         KSPGetPC(solver, &pc);
00269         PCSetType(pc, PCLU);         // Note: ILU factorisation doesn't have much effect, but LU works well.
00270        
00271         KSPSetFromOptions(solver);
00272         KSPSolve(solver,mpLinearSystem->rGetRhsVector(),solution);
00273 
00274         //Timer::PrintAndReset("KSP Solve");
00275 
00276         ReplicatableVector update(solution);
00277 #endif
00278         MechanicsEventHandler::EndEvent(MechanicsEventHandler::SOLVE);
00279 
00281         // Update the solution
00282         //  Newton method:       sol = sol - update, where update=Jac^{-1}*residual
00283         //  Newton with damping: sol = sol - s*update, where s is chosen
00284         //   such that |residual(sol)| is minimised. Damping is important to 
00285         //   avoid initial divergence.
00286         //
00287         // Normally, finding the best s from say 0.05,0.1,0.2,..,1.0 is cheap,
00288         // but this is not the case in cardiac electromechanics calculations.
00289         // Therefore, we initially check s=1 (expected to be the best most of the
00290         // time, then s=0.9. If the norm of the residual increases, we assume
00291         // s=1 is the best. Otherwise, check s=0.8 to see if s=0.9 is a local min. 
00293         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::UPDATE);
00294         std::vector<double> old_solution(mNumDofs);
00295         for(unsigned i=0; i<mNumDofs; i++)
00296         {
00297             old_solution[i] = mCurrentSolution[i];
00298         }
00299         
00300         std::vector<double> damping_values; // = {1.0, 0.9, .., 0.2, 0.1, 0.05} ie size 11
00301         for (unsigned i=10; i>=1; i--)
00302         {
00303             damping_values.push_back((double)i/10.0);
00304         }
00305         damping_values.push_back(0.05);
00306         assert(damping_values.size()==11);
00307 
00308         double initial_norm_resid = CalculateResidualNorm();
00309         unsigned index = 0;
00310         for(unsigned j=0; j<mNumDofs; j++)
00311         {
00312 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00313             mCurrentSolution[j] = old_solution[j] - damping_values[index]*update(j);
00314 #else
00315             mCurrentSolution[j] = old_solution[j] - damping_values[index]*update[j];
00316 #endif
00317         }
00318  
00319         // compute residual
00320         AssembleSystem(true, false);
00321         double norm_resid = CalculateResidualNorm();
00322         std::cout << "\tTesting s = " << damping_values[index] << ", |f| = " << norm_resid << "\n" << std::flush;
00323                     
00324         double next_norm_resid = -DBL_MAX;
00325         index = 1;
00326 
00327         // exit loop when next norm of the residual first increases
00328         while(next_norm_resid < norm_resid  && index<damping_values.size())
00329         {
00330             if(index!=1)
00331             {
00332                 norm_resid = next_norm_resid;
00333             }
00334             
00335             for(unsigned j=0; j<mNumDofs; j++)
00336             {
00337 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00338                 mCurrentSolution[j] = old_solution[j] - damping_values[index]*update(j);
00339 #else
00340                 mCurrentSolution[j] = old_solution[j] - damping_values[index]*update[j];
00341 #endif
00342             }
00343 
00344             // compute residual
00345             AssembleSystem(true, false);
00346             next_norm_resid = CalculateResidualNorm();
00347             std::cout << "\tTesting s = " << damping_values[index] << ", |f| = " << next_norm_resid << "\n" << std::flush;
00348             index++;
00349         }
00350 
00351         if (initial_norm_resid < norm_resid)
00352         {
00353             #define COVERAGE_IGNORE
00354             assert(0); // assert here as sometimes the following causes a seg fault - don't know why
00355             EXCEPTION("Residual does not appear to decrease in newton direction, quitting");
00356             #undef COVERAGE_IGNORE
00357         }
00358         else
00359         {
00360             index-=2;
00361             std::cout << "\tBest s = " << damping_values[index] << "\n"  << std::flush;
00362             for(unsigned j=0; j<mNumDofs; j++)
00363             {
00364 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00365                 mCurrentSolution[j] = old_solution[j] - damping_values[index]*update(j);
00366 #else
00367                 mCurrentSolution[j] = old_solution[j] - damping_values[index]*update[j];
00368 #endif
00369             }
00370         }        
00371 
00372 
00373 //        double best_norm_resid = DBL_MAX;
00374 //        double best_damping_value = 0.0;
00375 //
00376 //        std::vector<double> damping_values;
00377 //        damping_values.reserve(12);
00378 //        damping_values.push_back(0.0);
00379 //        damping_values.push_back(0.05);
00380 //        for (unsigned i=1; i<=10; i++)
00381 //        {
00382 //            damping_values.push_back((double)i/10.0);
00383 //        }
00384 //
00385 //        for (unsigned i=0; i<damping_values.size(); i++)
00386 //        {
00387 //            for(unsigned j=0; j<mNumDofs; j++)
00388 //            {
00389 //#ifdef ___USE_DEALII_LINEAR_SYSTEM___
00390 //                mCurrentSolution[j] = old_solution[j] - damping_values[i]*update(j);
00391 //#else
00392 //                mCurrentSolution[j] = old_solution[j] - damping_values[i]*update[j];
00393 //#endif
00394 //            }
00395 //
00396 //            // compute residual
00397 //            AssembleSystem(true, false);
00398 //            double norm_resid = CalculateResidualNorm();
00399 //
00400 //            std::cout << "\tTesting s = " << damping_values[i] << ", |f| = " << norm_resid << "\n" << std::flush;
00401 //            if (norm_resid < best_norm_resid)
00402 //            {
00403 //                best_norm_resid = norm_resid;
00404 //                best_damping_value = damping_values[i];
00405 //            }
00406 //        }
00407 //
00408 //        if (best_damping_value == 0.0)
00409 //        {
00410 //            #define COVERAGE_IGNORE
00411 //            assert(0);
00412 //            EXCEPTION("Residual does not decrease in newton direction, quitting");
00413 //            #undef COVERAGE_IGNORE
00414 //        }
00415 //        else
00416 //        {
00417 //            std::cout << "\tBest s = " << best_damping_value << "\n"  << std::flush;
00418 //        }
00419 //        //Timer::PrintAndReset("Find best damping");
00420 //
00421 //        // implement best update and recalculate residual
00422 //        for(unsigned j=0; j<mNumDofs; j++)
00423 //        {
00424 //#ifdef ___USE_DEALII_LINEAR_SYSTEM___
00425 //            mCurrentSolution[j] = old_solution[j] - best_damping_value*update(j);
00426 //#else
00427 //            mCurrentSolution[j] = old_solution[j] - best_damping_value*update[j];
00428 //#endif
00429 //        }
00430         MechanicsEventHandler::EndEvent(MechanicsEventHandler::UPDATE);
00431 
00432 
00433 #ifndef ___USE_DEALII_LINEAR_SYSTEM___
00434         VecDestroy(solution);
00435         KSPDestroy(solver);
00436 #endif
00437         return norm_resid;
00438     }
00439 
00440 
00445     virtual void PostNewtonStep(unsigned counter, double normResidual)
00446     {
00447     }
00448     
00449 public:
00450     AbstractNonlinearElasticityAssembler(unsigned numDofs,
00451                                          AbstractIncompressibleMaterialLaw<DIM>* pMaterialLaw,
00452                                          c_vector<double,DIM> bodyForce,
00453                                          double density,
00454                                          std::string outputDirectory,
00455                                          std::vector<unsigned>& fixedNodes)
00456         : mNumDofs(numDofs),
00457           mBodyForce(bodyForce),
00458           mDensity(density),
00459           mOutputDirectory(outputDirectory),
00460           mFixedNodes(fixedNodes),
00461           mUsingBodyForceFunction(false),
00462           mUsingTractionBoundaryConditionFunction(false)
00463     {
00464         assert(pMaterialLaw != NULL);
00465         mMaterialLaws.push_back(pMaterialLaw);
00466 
00467         assert(DIM==2 || DIM==3);
00468         assert(density > 0);
00469         assert(fixedNodes.size()>0);
00470         mWriteOutput = (mOutputDirectory != "");
00471         
00472 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00474         //mpLinearSystem = new DealiiLinearSystem( mesh );
00475 #else
00476         mpLinearSystem = new LinearSystem(mNumDofs);
00477 #endif
00478         mpPreconditionMatrixLinearSystem = new LinearSystem(mNumDofs, (MatType)MATAIJ);
00479     }
00480 
00481 
00482     AbstractNonlinearElasticityAssembler(unsigned numDofs,
00483                                          std::vector<AbstractIncompressibleMaterialLaw<DIM>*>& rMaterialLaws,
00484                                          c_vector<double,DIM> bodyForce,
00485                                          double density,
00486                                          std::string outputDirectory,
00487                                          std::vector<unsigned>& fixedNodes)
00488         : mNumDofs(numDofs),
00489           mBodyForce(bodyForce),
00490           mDensity(density),
00491           mOutputDirectory(outputDirectory),
00492           mFixedNodes(fixedNodes),
00493           mUsingBodyForceFunction(false),
00494           mUsingTractionBoundaryConditionFunction(false)
00495     {
00496         mMaterialLaws.resize(rMaterialLaws.size(), NULL);
00497         for(unsigned i=0; i<mMaterialLaws.size(); i++)
00498         {
00499             assert(rMaterialLaws[i] != NULL);
00500             mMaterialLaws[i] = rMaterialLaws[i];
00501         }
00502 
00503         assert(DIM==2 || DIM==3);
00504         assert(density > 0);
00505         assert(fixedNodes.size()>0);
00506         mWriteOutput = (mOutputDirectory != "");
00507         
00508 #ifdef ___USE_DEALII_LINEAR_SYSTEM___
00510         //mpLinearSystem = new DealiiLinearSystem( mesh );
00511 #else
00512         mpLinearSystem = new LinearSystem(mNumDofs);
00513 #endif
00514         mpPreconditionMatrixLinearSystem = new LinearSystem(mNumDofs, (MatType)MATAIJ);
00515     }
00516 
00517 
00518     virtual ~AbstractNonlinearElasticityAssembler()
00519     {
00520         delete mpLinearSystem;
00521         delete mpPreconditionMatrixLinearSystem;
00522     }
00523     
00524 
00528     void Solve(double tol = -1.0, 
00529                unsigned offset=0, 
00530                unsigned maxNumNewtonIterations=INT_MAX,
00531                bool quitIfNoConvergence=true)
00532     {
00533         if(mWriteOutput)
00534         {
00535             WriteOutput(0+offset);
00536         }
00537     
00538         // compute residual
00539         AssembleSystem(true, false);
00540         double norm_resid = this->CalculateResidualNorm();
00541         std::cout << "\nNorm of residual is " << norm_resid << "\n";
00542     
00543         mNumNewtonIterations = 0;
00544         unsigned counter = 1;
00545     
00546         if(tol<0) // ie if wasn't passed in as a parameter
00547         {        
00548             tol = NEWTON_REL_TOL*norm_resid;
00549     
00550             #define COVERAGE_IGNORE // not going to have tests in cts for everything
00551             if(tol > MAX_NEWTON_ABS_TOL)
00552             {
00553                 tol = MAX_NEWTON_ABS_TOL;
00554             }
00555             if(tol < MIN_NEWTON_ABS_TOL)
00556             {
00557                 tol = MIN_NEWTON_ABS_TOL;
00558             }
00559             #undef COVERAGE_IGNORE
00560         }
00561 
00562         std::cout << "Solving with tolerance " << tol << "\n";
00563     
00564         while (norm_resid > tol && counter<=maxNumNewtonIterations)
00565         {
00566             std::cout <<  "\n-------------------\n"
00567                       <<   "Newton iteration " << counter
00568                       << ":\n-------------------\n";
00569     
00570             // take newton step (and get returned residual)
00571             norm_resid = TakeNewtonStep();
00572     
00573             std::cout << "Norm of residual is " << norm_resid << "\n";    
00574             if(mWriteOutput)
00575             {
00576                 WriteOutput(counter+offset);
00577             }
00578     
00579             mNumNewtonIterations = counter;
00580     
00581             PostNewtonStep(counter,norm_resid);
00582     
00583             counter++;
00584             if (counter==20)
00585             {
00586                 #define COVERAGE_IGNORE
00587                 EXCEPTION("Not converged after 20 newton iterations, quitting");
00588                 #undef COVERAGE_IGNORE
00589             }
00590         }
00591 
00592         if ((norm_resid > tol) && quitIfNoConvergence)
00593         {
00594             #define COVERAGE_IGNORE
00595             EXCEPTION("Failed to converge");
00596             #undef COVERAGE_IGNORE
00597         }
00598     
00599         // we have solved for a deformation so note this
00600         //mADeformedHasBeenSolved = true;
00601     }
00602     
00603 
00604 
00608     void WriteOutput(unsigned counter)
00609     {
00610         // only write output if the flag mWriteOutput has been set
00611         if (!mWriteOutput)
00612         {
00613             return;
00614         }
00615 
00616         OutputFileHandler output_file_handler(mOutputDirectory, (counter==0));
00617         std::stringstream file_name;
00618         file_name << "/solution_" << counter << ".nodes";
00619 
00620         out_stream p_file = output_file_handler.OpenOutputFile(file_name.str());
00621 
00622         std::vector<c_vector<double,DIM> >& r_deformed_position = rGetDeformedPosition();
00623         for(unsigned i=0; i<r_deformed_position.size(); i++)
00624         {
00625             for(unsigned j=0; j<DIM; j++)
00626             {
00627                 *p_file << r_deformed_position[i](j) << " ";
00628             } 
00629             *p_file << "\n";
00630         }
00631         p_file->close();
00632     }
00633     
00634     unsigned GetNumNewtonIterations()
00635     {
00636         return mNumNewtonIterations;
00637     }
00638 
00643     void SetFunctionalBodyForce(c_vector<double,DIM> (*pFunction)(c_vector<double,DIM>&))
00644     {
00645         mUsingBodyForceFunction = true;
00646         mpBodyForceFunction = pFunction;
00647     }
00648     
00649     void SetWriteOutput(bool writeOutput = true)
00650     {
00651         if(writeOutput && (mOutputDirectory==""))
00652         {
00653             EXCEPTION("Can't write output if no output directory was given in constructor");
00654         }
00655         mWriteOutput = writeOutput;
00656     }
00657 };
00658 
00659 #endif /*ABSTRACTNONLINEARELASTICITYASSEMBLER_HPP_*/

Generated on Wed Mar 18 12:51:56 2009 for Chaste by  doxygen 1.5.5