petsc_nonlinear_solver.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 #include "libmesh/libmesh_common.h" 00021 00022 #ifdef LIBMESH_HAVE_PETSC 00023 00024 00025 // C++ includes 00026 00027 // Local Includes 00028 #include "libmesh/nonlinear_implicit_system.h" 00029 #include "libmesh/petsc_nonlinear_solver.h" 00030 #include "libmesh/petsc_linear_solver.h" 00031 #include "libmesh/petsc_vector.h" 00032 #include "libmesh/petsc_matrix.h" 00033 #include "libmesh/dof_map.h" 00034 #include "libmesh/preconditioner.h" 00035 00036 namespace libMesh 00037 { 00038 00039 //-------------------------------------------------------------------- 00040 // Functions with C linkage to pass to PETSc. PETSc will call these 00041 // methods as needed. 00042 // 00043 // Since they must have C linkage they have no knowledge of a namespace. 00044 // Give them an obscure name to avoid namespace pollution. 00045 extern "C" 00046 { 00047 // Older versions of PETSc do not have the different int typedefs. 00048 // On 64-bit machines, PetscInt may actually be a long long int. 00049 // This change occurred in Petsc-2.2.1. 00050 #if PETSC_VERSION_LESS_THAN(2,2,1) 00051 typedef int PetscErrorCode; 00052 typedef int PetscInt; 00053 #endif 00054 00055 //------------------------------------------------------------------- 00056 // this function is called by PETSc at the end of each nonlinear step 00057 PetscErrorCode 00058 __libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *) 00059 { 00060 //int ierr=0; 00061 00062 //if (its > 0) 00063 libMesh::out << " NL step " 00064 << std::setw(2) << its 00065 << std::scientific 00066 << ", |residual|_2 = " << fnorm 00067 << std::endl; 00068 00069 //return ierr; 00070 return 0; 00071 } 00072 00073 00074 00075 //--------------------------------------------------------------- 00076 // this function is called by PETSc to evaluate the residual at X 00077 PetscErrorCode 00078 __libmesh_petsc_snes_residual (SNES snes, Vec x, Vec r, void *ctx) 00079 { 00080 START_LOG("residual()", "PetscNonlinearSolver"); 00081 00082 int ierr=0; 00083 00084 libmesh_assert(x); 00085 libmesh_assert(r); 00086 libmesh_assert(ctx); 00087 00088 PetscNonlinearSolver<Number>* solver = 00089 static_cast<PetscNonlinearSolver<Number>*> (ctx); 00090 00091 // Get the current iteration number from the snes object, 00092 // store it in the PetscNonlinearSolver object for possible use 00093 // by the user's residual function. 00094 { 00095 int n_iterations = 0; 00096 ierr = SNESGetIterationNumber(snes, &n_iterations); 00097 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00098 solver->set_current_nonlinear_iteration_number( static_cast<unsigned>(n_iterations) ); 00099 } 00100 00101 NonlinearImplicitSystem &sys = solver->system(); 00102 00103 PetscVector<Number>& X_sys = *libmesh_cast_ptr<PetscVector<Number>*>(sys.solution.get()); 00104 PetscVector<Number>& R_sys = *libmesh_cast_ptr<PetscVector<Number>*>(sys.rhs); 00105 PetscVector<Number> X_global(x), R(r); 00106 00107 // Use the systems update() to get a good local version of the parallel solution 00108 X_global.swap(X_sys); 00109 R.swap(R_sys); 00110 00111 sys.get_dof_map().enforce_constraints_exactly(sys); 00112 00113 sys.update(); 00114 00115 //Swap back 00116 X_global.swap(X_sys); 00117 R.swap(R_sys); 00118 00119 R.zero(); 00120 00121 //----------------------------------------------------------------------------- 00122 // if the user has provided both function pointers and objects only the pointer 00123 // will be used, so catch that as an error 00124 if (solver->residual && solver->residual_object) 00125 { 00126 libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Residual!" << std::endl; 00127 libmesh_error(); 00128 } 00129 00130 if (solver->matvec && solver->residual_and_jacobian_object) 00131 { 00132 libMesh::err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & Jacobian!" << std::endl; 00133 libmesh_error(); 00134 } 00135 //----------------------------------------------------------------------------- 00136 00137 if (solver->residual != NULL) solver->residual (*sys.current_local_solution.get(), R, sys); 00138 else if (solver->residual_object != NULL) solver->residual_object->residual (*sys.current_local_solution.get(), R, sys); 00139 else if (solver->matvec != NULL) solver->matvec (*sys.current_local_solution.get(), &R, NULL, sys); 00140 else if (solver->residual_and_jacobian_object != NULL) solver->residual_and_jacobian_object->residual_and_jacobian (*sys.current_local_solution.get(), &R, NULL, sys); 00141 else libmesh_error(); 00142 00143 R.close(); 00144 X_global.close(); 00145 00146 STOP_LOG("residual()", "PetscNonlinearSolver"); 00147 00148 return ierr; 00149 } 00150 00151 00152 00153 //--------------------------------------------------------------- 00154 // this function is called by PETSc to evaluate the Jacobian at X 00155 PetscErrorCode 00156 __libmesh_petsc_snes_jacobian (SNES snes, Vec x, Mat *jac, Mat *pc, MatStructure *msflag, void *ctx) 00157 { 00158 START_LOG("jacobian()", "PetscNonlinearSolver"); 00159 00160 int ierr=0; 00161 00162 libmesh_assert(ctx); 00163 00164 PetscNonlinearSolver<Number>* solver = 00165 static_cast<PetscNonlinearSolver<Number>*> (ctx); 00166 00167 // Get the current iteration number from the snes object, 00168 // store it in the PetscNonlinearSolver object for possible use 00169 // by the user's Jacobian function. 00170 { 00171 int n_iterations = 0; 00172 ierr = SNESGetIterationNumber(snes, &n_iterations); 00173 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00174 solver->set_current_nonlinear_iteration_number( static_cast<unsigned>(n_iterations) ); 00175 } 00176 00177 NonlinearImplicitSystem &sys = solver->system(); 00178 00179 PetscMatrix<Number> PC(*pc); 00180 PetscMatrix<Number> Jac(*jac); 00181 PetscVector<Number>& X_sys = *libmesh_cast_ptr<PetscVector<Number>*>(sys.solution.get()); 00182 PetscMatrix<Number>& Jac_sys = *libmesh_cast_ptr<PetscMatrix<Number>*>(sys.matrix); 00183 PetscVector<Number> X_global(x); 00184 00185 // Set the dof maps 00186 PC.attach_dof_map(sys.get_dof_map()); 00187 Jac.attach_dof_map(sys.get_dof_map()); 00188 00189 // Use the systems update() to get a good local version of the parallel solution 00190 X_global.swap(X_sys); 00191 Jac.swap(Jac_sys); 00192 00193 sys.get_dof_map().enforce_constraints_exactly(sys); 00194 sys.update(); 00195 00196 X_global.swap(X_sys); 00197 Jac.swap(Jac_sys); 00198 00199 PC.zero(); 00200 00201 //----------------------------------------------------------------------------- 00202 // if the user has provided both function pointers and objects only the pointer 00203 // will be used, so catch that as an error 00204 if (solver->jacobian && solver->jacobian_object) 00205 { 00206 libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Jacobian!" << std::endl; 00207 libmesh_error(); 00208 } 00209 00210 if (solver->matvec && solver->residual_and_jacobian_object) 00211 { 00212 libMesh::err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & Jacobian!" << std::endl; 00213 libmesh_error(); 00214 } 00215 //----------------------------------------------------------------------------- 00216 00217 if (solver->jacobian != NULL) solver->jacobian (*sys.current_local_solution.get(), PC, sys); 00218 else if (solver->jacobian_object != NULL) solver->jacobian_object->jacobian (*sys.current_local_solution.get(), PC, sys); 00219 else if (solver->matvec != NULL) solver->matvec (*sys.current_local_solution.get(), NULL, &PC, sys); 00220 else if (solver->residual_and_jacobian_object != NULL) solver->residual_and_jacobian_object->residual_and_jacobian (*sys.current_local_solution.get(), NULL, &PC, sys); 00221 else libmesh_error(); 00222 00223 PC.close(); 00224 Jac.close(); 00225 X_global.close(); 00226 00227 *msflag = SAME_NONZERO_PATTERN; 00228 00229 STOP_LOG("jacobian()", "PetscNonlinearSolver"); 00230 00231 return ierr; 00232 } 00233 00234 } // end extern "C" 00235 //--------------------------------------------------------------------- 00236 00237 00238 00239 //--------------------------------------------------------------------- 00240 // PetscNonlinearSolver<> methods 00241 template <typename T> 00242 PetscNonlinearSolver<T>::PetscNonlinearSolver (sys_type& system_in) : 00243 NonlinearSolver<T>(system_in), 00244 _reason(SNES_CONVERGED_ITERATING/*==0*/), // Arbitrary initial value... 00245 _n_linear_iterations(0), 00246 _current_nonlinear_iteration_number(0) 00247 { 00248 } 00249 00250 00251 00252 template <typename T> 00253 PetscNonlinearSolver<T>::~PetscNonlinearSolver () 00254 { 00255 this->clear (); 00256 } 00257 00258 00259 00260 template <typename T> 00261 void PetscNonlinearSolver<T>::clear () 00262 { 00263 if (this->initialized()) 00264 { 00265 this->_is_initialized = false; 00266 00267 int ierr=0; 00268 00269 ierr = LibMeshSNESDestroy(&_snes); 00270 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00271 00272 // Reset the nonlinear iteration counter. This information is only relevant 00273 // *during* the solve(). After the solve is completed it should return to 00274 // the default value of 0. 00275 _current_nonlinear_iteration_number = 0; 00276 } 00277 } 00278 00279 00280 00281 template <typename T> 00282 void PetscNonlinearSolver<T>::init () 00283 { 00284 // Initialize the data structures if not done so already. 00285 if (!this->initialized()) 00286 { 00287 this->_is_initialized = true; 00288 00289 int ierr=0; 00290 00291 #if PETSC_VERSION_LESS_THAN(2,1,2) 00292 // At least until Petsc 2.1.1, the SNESCreate had a different calling syntax. 00293 // The second argument was of type SNESProblemType, and could have a value of 00294 // either SNES_NONLINEAR_EQUATIONS or SNES_UNCONSTRAINED_MINIMIZATION. 00295 ierr = SNESCreate(libMesh::COMM_WORLD, SNES_NONLINEAR_EQUATIONS, &_snes); 00296 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00297 00298 #else 00299 00300 ierr = SNESCreate(libMesh::COMM_WORLD,&_snes); 00301 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00302 00303 #endif 00304 00305 00306 #if PETSC_VERSION_LESS_THAN(2,3,3) 00307 ierr = SNESSetMonitor (_snes, __libmesh_petsc_snes_monitor, 00308 this, PETSC_NULL); 00309 #else 00310 // API name change in PETSc 2.3.3 00311 ierr = SNESMonitorSet (_snes, __libmesh_petsc_snes_monitor, 00312 this, PETSC_NULL); 00313 #endif 00314 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00315 00316 #if PETSC_VERSION_LESS_THAN(3,1,0) 00317 // Cannot call SNESSetOptions before SNESSetFunction when using 00318 // any matrix free options with PETSc 3.1.0+ 00319 ierr = SNESSetFromOptions(_snes); 00320 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00321 #endif 00322 00323 if(this->_preconditioner) 00324 { 00325 KSP ksp; 00326 ierr = SNESGetKSP (_snes, &ksp); 00327 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00328 PC pc; 00329 ierr = KSPGetPC(ksp,&pc); 00330 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00331 00332 this->_preconditioner->init(); 00333 00334 PCSetType(pc, PCSHELL); 00335 PCShellSetContext(pc,(void*)this->_preconditioner); 00336 00337 //Re-Use the shell functions from petsc_linear_solver 00338 PCShellSetSetUp(pc,__libmesh_petsc_preconditioner_setup); 00339 PCShellSetApply(pc,__libmesh_petsc_preconditioner_apply); 00340 } 00341 } 00342 } 00343 00344 #if !PETSC_VERSION_LESS_THAN(3,3,0) 00345 template <typename T> 00346 void 00347 PetscNonlinearSolver<T>::build_mat_null_space(NonlinearImplicitSystem::ComputeVectorSubspace* computeSubspaceObject, 00348 void (*computeSubspace)(std::vector<NumericVector<Number>*>&, sys_type&), 00349 MatNullSpace *msp) 00350 { 00351 PetscErrorCode ierr; 00352 std::vector<NumericVector<Number>* > sp; 00353 if (computeSubspaceObject) 00354 (*computeSubspaceObject)(sp, this->system()); 00355 else 00356 (*computeSubspace)(sp, this->system()); 00357 00358 *msp = PETSC_NULL; 00359 if (sp.size()) 00360 { 00361 Vec *modes; 00362 PetscScalar *dots; 00363 PetscInt nmodes = sp.size(); 00364 00365 ierr = PetscMalloc2(nmodes,Vec,&modes,nmodes,PetscScalar,&dots); 00366 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00367 00368 for (PetscInt i=0; i<nmodes; ++i) 00369 { 00370 PetscVector<T>* pv = libmesh_cast_ptr<PetscVector<T>*>(sp[i]); 00371 Vec v = pv->vec(); 00372 00373 ierr = VecDuplicate(v, modes+i); 00374 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00375 00376 ierr = VecCopy(v,modes[i]); 00377 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00378 } 00379 00380 // Normalize. 00381 ierr = VecNormalize(modes[0],PETSC_NULL); 00382 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00383 00384 for (PetscInt i=1; i<nmodes; i++) 00385 { 00386 // Orthonormalize vec[i] against vec[0:i-1] 00387 ierr = VecMDot(modes[i],i,modes,dots); 00388 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00389 00390 for (PetscInt j=0; j<i; j++) 00391 dots[j] *= -1.; 00392 00393 ierr = VecMAXPY(modes[i],i,dots,modes); 00394 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00395 00396 ierr = VecNormalize(modes[i],PETSC_NULL); 00397 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00398 } 00399 00400 ierr = MatNullSpaceCreate(libMesh::COMM_WORLD, PETSC_FALSE, nmodes, modes, msp); 00401 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00402 00403 for (PetscInt i=0; i<nmodes; ++i) 00404 { 00405 ierr = VecDestroy(modes+i); 00406 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00407 } 00408 00409 ierr = PetscFree2(modes,dots); 00410 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00411 } 00412 } 00413 #endif 00414 00415 template <typename T> 00416 std::pair<unsigned int, Real> 00417 PetscNonlinearSolver<T>::solve (SparseMatrix<T>& jac_in, // System Jacobian Matrix 00418 NumericVector<T>& x_in, // Solution vector 00419 NumericVector<T>& r_in, // Residual vector 00420 const double, // Stopping tolerance 00421 const unsigned int) 00422 { 00423 START_LOG("solve()", "PetscNonlinearSolver"); 00424 this->init (); 00425 00426 // Make sure the data passed in are really of Petsc types 00427 PetscMatrix<T>* jac = libmesh_cast_ptr<PetscMatrix<T>*>(&jac_in); 00428 PetscVector<T>* x = libmesh_cast_ptr<PetscVector<T>*>(&x_in); 00429 PetscVector<T>* r = libmesh_cast_ptr<PetscVector<T>*>(&r_in); 00430 00431 int ierr=0; 00432 int n_iterations =0; 00433 // Should actually be a PetscReal, but I don't know which version of PETSc first introduced PetscReal 00434 Real final_residual_norm=0.; 00435 00436 ierr = SNESSetFunction (_snes, r->vec(), __libmesh_petsc_snes_residual, this); 00437 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00438 00439 // Only set the jacobian function if we've been provided with something to call. 00440 // This allows a user to set their own jacobian function if they want to 00441 if (this->jacobian || this->jacobian_object || this->residual_and_jacobian_object) 00442 { 00443 ierr = SNESSetJacobian (_snes, jac->mat(), jac->mat(), __libmesh_petsc_snes_jacobian, this); 00444 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00445 } 00446 #if !PETSC_VERSION_LESS_THAN(3,3,0) 00447 // Only set the nullspace if we have a way of computing it and the result is non-empty. 00448 if (this->nullspace || this->nullspace_object) 00449 { 00450 MatNullSpace msp; 00451 this->build_mat_null_space(this->nullspace_object, this->nullspace, &msp); 00452 if (msp) 00453 { 00454 ierr = MatSetNullSpace(jac->mat(), msp); 00455 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00456 00457 ierr = MatNullSpaceDestroy(&msp); 00458 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00459 } 00460 } 00461 00462 // Only set the nearnullspace if we have a way of computing it and the result is non-empty. 00463 if (this->nearnullspace || this->nearnullspace_object) 00464 { 00465 MatNullSpace msp = PETSC_NULL; 00466 this->build_mat_null_space(this->nearnullspace_object, this->nearnullspace, &msp); 00467 00468 if(msp) { 00469 ierr = MatSetNearNullSpace(jac->mat(), msp); 00470 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00471 00472 ierr = MatNullSpaceDestroy(&msp); 00473 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00474 } 00475 } 00476 #endif 00477 // Have the Krylov subspace method use our good initial guess rather than 0 00478 KSP ksp; 00479 ierr = SNESGetKSP (_snes, &ksp); 00480 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00481 00482 // Set the tolerances for the iterative solver. Use the user-supplied 00483 // tolerance for the relative residual & leave the others at default values 00484 ierr = KSPSetTolerances (ksp, this->initial_linear_tolerance, PETSC_DEFAULT, 00485 PETSC_DEFAULT, this->max_linear_iterations); 00486 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00487 00488 // Set the tolerances for the non-linear solver. 00489 ierr = SNESSetTolerances(_snes, this->absolute_residual_tolerance, this->relative_residual_tolerance, 00490 this->relative_step_tolerance, this->max_nonlinear_iterations, this->max_function_evaluations); 00491 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00492 00493 //Pull in command-line options 00494 KSPSetFromOptions(ksp); 00495 SNESSetFromOptions(_snes); 00496 00497 if (this->user_presolve) 00498 this->user_presolve(this->system()); 00499 00500 //Set the preconditioning matrix 00501 if(this->_preconditioner) 00502 { 00503 this->_preconditioner->set_matrix(jac_in); 00504 this->_preconditioner->init(); 00505 } 00506 00507 // ierr = KSPSetInitialGuessNonzero (ksp, PETSC_TRUE); 00508 // CHKERRABORT(libMesh::COMM_WORLD,ierr); 00509 00510 // Older versions (at least up to 2.1.5) of SNESSolve took 3 arguments, 00511 // the last one being a pointer to an int to hold the number of iterations required. 00512 # if PETSC_VERSION_LESS_THAN(2,2,0) 00513 00514 ierr = SNESSolve (_snes, x->vec(), &n_iterations); 00515 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00516 00517 // 2.2.x style 00518 #elif PETSC_VERSION_LESS_THAN(2,3,0) 00519 00520 ierr = SNESSolve (_snes, x->vec()); 00521 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00522 00523 // 2.3.x & newer style 00524 #else 00525 00526 ierr = SNESSolve (_snes, PETSC_NULL, x->vec()); 00527 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00528 00529 ierr = SNESGetIterationNumber(_snes,&n_iterations); 00530 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00531 00532 ierr = SNESGetLinearSolveIterations(_snes, &_n_linear_iterations); 00533 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00534 00535 ierr = SNESGetFunctionNorm(_snes,&final_residual_norm); 00536 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00537 00538 #endif 00539 00540 // Get and store the reason for convergence 00541 SNESGetConvergedReason(_snes, &_reason); 00542 00543 //Based on Petsc 2.3.3 documentation all diverged reasons are negative 00544 this->converged = (_reason >= 0); 00545 00546 this->clear(); 00547 00548 STOP_LOG("solve()", "PetscNonlinearSolver"); 00549 00550 // return the # of its. and the final residual norm. 00551 return std::make_pair(n_iterations, final_residual_norm); 00552 } 00553 00554 00555 00556 template <typename T> 00557 void PetscNonlinearSolver<T>::print_converged_reason() 00558 { 00559 00560 libMesh::out << "Nonlinear solver convergence/divergence reason: " 00561 << SNESConvergedReasons[this->get_converged_reason()] << std::endl; 00562 } 00563 00564 00565 00566 template <typename T> 00567 SNESConvergedReason PetscNonlinearSolver<T>::get_converged_reason() 00568 { 00569 int ierr=0; 00570 00571 if (this->initialized()) 00572 { 00573 ierr = SNESGetConvergedReason(_snes, &_reason); 00574 CHKERRABORT(libMesh::COMM_WORLD,ierr); 00575 } 00576 00577 return _reason; 00578 } 00579 00580 template <typename T> 00581 int PetscNonlinearSolver<T>::get_total_linear_iterations() 00582 { 00583 return _n_linear_iterations; 00584 } 00585 00586 00587 //------------------------------------------------------------------ 00588 // Explicit instantiations 00589 template class PetscNonlinearSolver<Number>; 00590 00591 } // namespace libMesh 00592 00593 00594 00595 #endif // #ifdef LIBMESH_HAVE_PETSC
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:48 UTC
Hosted By: