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