$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 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 #include "libmesh/diff_system.h" 00020 #include "libmesh/dof_map.h" 00021 #include "libmesh/libmesh_logging.h" 00022 #include "libmesh/petsc_diff_solver.h" 00023 #include "libmesh/petsc_matrix.h" 00024 #include "libmesh/petsc_vector.h" 00025 #include "libmesh/petsc_auto_fieldsplit.h" 00026 00027 #ifdef LIBMESH_HAVE_PETSC 00028 00029 namespace libMesh 00030 { 00031 00032 //-------------------------------------------------------------------- 00033 // Functions with C linkage to pass to PETSc. PETSc will call these 00034 // methods as needed. 00035 // 00036 // Since they must have C linkage they have no knowledge of a namespace. 00037 // Give them an obscure name to avoid namespace pollution. 00038 extern "C" 00039 { 00040 // Older versions of PETSc do not have the different int typedefs. 00041 // On 64-bit machines, PetscInt may actually be a long long int. 00042 // This change occurred in Petsc-2.2.1. 00043 #if PETSC_VERSION_LESS_THAN(2,2,1) 00044 typedef int PetscErrorCode; 00045 typedef int PetscInt; 00046 #endif 00047 00048 // Function to hand to PETSc's SNES, 00049 // which monitors convergence at X 00050 PetscErrorCode 00051 __libmesh_petsc_diff_solver_monitor (SNES snes, PetscInt its, 00052 PetscReal fnorm, void *ctx) 00053 { 00054 PetscDiffSolver& solver = 00055 *(static_cast<PetscDiffSolver*> (ctx)); 00056 00057 if (solver.verbose) 00058 libMesh::out << " PetscDiffSolver step " << its 00059 << ", |residual|_2 = " << fnorm << std::endl; 00060 if (solver.linear_solution_monitor.get()) { 00061 int ierr = 0; 00062 00063 Vec petsc_delta_u; 00064 ierr = SNESGetSolutionUpdate(snes, &petsc_delta_u); 00065 CHKERRABORT(solver.comm().get(), ierr); 00066 PetscVector<Number> delta_u(petsc_delta_u, solver.comm()); 00067 delta_u.close(); 00068 00069 Vec petsc_u; 00070 ierr = SNESGetSolution(snes, &petsc_u); 00071 CHKERRABORT(solver.comm().get(), ierr); 00072 PetscVector<Number> u(petsc_u, solver.comm()); 00073 u.close(); 00074 00075 Vec petsc_res; 00076 ierr = SNESGetFunction(snes, &petsc_res, NULL, NULL); 00077 CHKERRABORT(solver.comm().get(), ierr); 00078 PetscVector<Number> res(petsc_res, solver.comm()); 00079 res.close(); 00080 00081 (*solver.linear_solution_monitor)( 00082 delta_u, delta_u.l2_norm(), 00083 u, u.l2_norm(), 00084 res, res.l2_norm(), its); 00085 } 00086 return 0; 00087 } 00088 00089 // Functions to hand to PETSc's SNES, 00090 // which compute the residual or jacobian at X 00091 PetscErrorCode 00092 __libmesh_petsc_diff_solver_residual (SNES, Vec x, Vec r, void *ctx) 00093 { 00094 libmesh_assert(x); 00095 libmesh_assert(r); 00096 libmesh_assert(ctx); 00097 00098 PetscDiffSolver& solver = 00099 *(static_cast<PetscDiffSolver*> (ctx)); 00100 ImplicitSystem &sys = solver.system(); 00101 00102 if (solver.verbose) 00103 libMesh::out << "Assembling the residual" << std::endl; 00104 00105 PetscVector<Number>& X_system = 00106 *cast_ptr<PetscVector<Number>*>(sys.solution.get()); 00107 PetscVector<Number>& R_system = 00108 *cast_ptr<PetscVector<Number>*>(sys.rhs); 00109 PetscVector<Number> X_input(x, sys.comm()), R_input(r, sys.comm()); 00110 00111 // DiffSystem assembles from the solution and into the rhs, so swap 00112 // those with our input vectors before assembling. They'll probably 00113 // already be references to the same vectors, but PETSc might do 00114 // something tricky. 00115 X_input.swap(X_system); 00116 R_input.swap(R_system); 00117 00118 // We may need to correct a non-conforming solution 00119 sys.get_dof_map().enforce_constraints_exactly(sys); 00120 00121 // We may need to localize a parallel solution 00122 sys.update(); 00123 00124 // Do DiffSystem assembly 00125 sys.assembly(true, false); 00126 R_system.close(); 00127 00128 // Swap back 00129 X_input.swap(X_system); 00130 R_input.swap(R_system); 00131 00132 // No errors, we hope 00133 return 0; 00134 } 00135 00136 00137 #if PETSC_RELEASE_LESS_THAN(3,5,0) 00138 PetscErrorCode 00139 __libmesh_petsc_diff_solver_jacobian (SNES, Vec x, Mat *libmesh_dbg_var(j), Mat *pc, 00140 MatStructure *msflag, void *ctx) 00141 #else 00142 PetscErrorCode 00143 __libmesh_petsc_diff_solver_jacobian (SNES, Vec x, Mat libmesh_dbg_var(j), Mat pc, 00144 void *ctx) 00145 #endif 00146 { 00147 libmesh_assert(x); 00148 libmesh_assert(j); 00149 // libmesh_assert_equal_to (pc, j); // We don't use separate preconditioners yet 00150 libmesh_assert(ctx); 00151 00152 PetscDiffSolver& solver = 00153 *(static_cast<PetscDiffSolver*> (ctx)); 00154 ImplicitSystem &sys = solver.system(); 00155 00156 if (solver.verbose) 00157 libMesh::out << "Assembling the Jacobian" << std::endl; 00158 00159 PetscVector<Number>& X_system = 00160 *cast_ptr<PetscVector<Number>*>(sys.solution.get()); 00161 PetscVector<Number> X_input(x, sys.comm()); 00162 00163 #if PETSC_RELEASE_LESS_THAN(3,5,0) 00164 PetscMatrix<Number> J_input(*pc, sys.comm()); 00165 #else 00166 PetscMatrix<Number> J_input(pc, sys.comm()); 00167 #endif 00168 PetscMatrix<Number>& J_system = 00169 *cast_ptr<PetscMatrix<Number>*>(sys.matrix); 00170 00171 // DiffSystem assembles from the solution and into the jacobian, so 00172 // swap those with our input vectors before assembling. They'll 00173 // probably already be references to the same vectors, but PETSc 00174 // might do something tricky. 00175 X_input.swap(X_system); 00176 J_input.swap(J_system); 00177 00178 // We may need to correct a non-conforming solution 00179 sys.get_dof_map().enforce_constraints_exactly(sys); 00180 00181 // We may need to localize a parallel solution 00182 sys.update(); 00183 00184 // Do DiffSystem assembly 00185 sys.assembly(false, true); 00186 J_system.close(); 00187 00188 // Swap back 00189 X_input.swap(X_system); 00190 J_input.swap(J_system); 00191 00192 #if PETSC_RELEASE_LESS_THAN(3,5,0) 00193 *msflag = SAME_NONZERO_PATTERN; 00194 #endif 00195 // No errors, we hope 00196 return 0; 00197 } 00198 00199 } // extern "C" 00200 00201 00202 PetscDiffSolver::PetscDiffSolver (sys_type& s) 00203 : Parent(s) 00204 { 00205 } 00206 00207 00208 void PetscDiffSolver::init () 00209 { 00210 START_LOG("init()", "PetscDiffSolver"); 00211 00212 Parent::init(); 00213 00214 int ierr=0; 00215 00216 #if PETSC_VERSION_LESS_THAN(2,1,2) 00217 // At least until Petsc 2.1.1, the SNESCreate had a different 00218 // calling syntax. The second argument was of type SNESProblemType, 00219 // and could have a value of either SNES_NONLINEAR_EQUATIONS or 00220 // SNES_UNCONSTRAINED_MINIMIZATION. 00221 ierr = SNESCreate(this->comm().get(), SNES_NONLINEAR_EQUATIONS, &_snes); 00222 LIBMESH_CHKERRABORT(ierr); 00223 #else 00224 ierr = SNESCreate(this->comm().get(),&_snes); 00225 LIBMESH_CHKERRABORT(ierr); 00226 #endif 00227 00228 #if PETSC_VERSION_LESS_THAN(2,3,3) 00229 ierr = SNESSetMonitor (_snes, __libmesh_petsc_diff_solver_monitor, 00230 this, PETSC_NULL); 00231 #else 00232 // API name change in PETSc 2.3.3 00233 ierr = SNESMonitorSet (_snes, __libmesh_petsc_diff_solver_monitor, 00234 this, PETSC_NULL); 00235 #endif 00236 LIBMESH_CHKERRABORT(ierr); 00237 00238 if (libMesh::on_command_line("--solver_system_names")) 00239 { 00240 ierr = SNESSetOptionsPrefix(_snes, (_system.name()+"_").c_str()); 00241 LIBMESH_CHKERRABORT(ierr); 00242 } 00243 00244 ierr = SNESSetFromOptions(_snes); 00245 LIBMESH_CHKERRABORT(ierr); 00246 00247 KSP my_ksp; 00248 ierr = SNESGetKSP(_snes, &my_ksp); 00249 LIBMESH_CHKERRABORT(ierr); 00250 00251 PC my_pc; 00252 ierr = KSPGetPC(my_ksp, &my_pc); 00253 LIBMESH_CHKERRABORT(ierr); 00254 00255 petsc_auto_fieldsplit(my_pc, _system); 00256 00257 STOP_LOG("init()", "PetscDiffSolver"); 00258 } 00259 00260 00261 00262 PetscDiffSolver::~PetscDiffSolver () 00263 { 00264 } 00265 00266 00267 00268 void PetscDiffSolver::clear() 00269 { 00270 START_LOG("clear()", "PetscDiffSolver"); 00271 00272 int ierr = LibMeshSNESDestroy(&_snes); 00273 LIBMESH_CHKERRABORT(ierr); 00274 00275 STOP_LOG("clear()", "PetscDiffSolver"); 00276 } 00277 00278 00279 00280 void PetscDiffSolver::reinit() 00281 { 00282 Parent::reinit(); 00283 00284 KSP my_ksp; 00285 int ierr = SNESGetKSP(_snes, &my_ksp); 00286 LIBMESH_CHKERRABORT(ierr); 00287 00288 PC my_pc; 00289 ierr = KSPGetPC(my_ksp, &my_pc); 00290 LIBMESH_CHKERRABORT(ierr); 00291 00292 petsc_auto_fieldsplit(my_pc, _system); 00293 } 00294 00295 00296 00297 DiffSolver::SolveResult convert_solve_result(SNESConvergedReason r) 00298 { 00299 switch (r) 00300 { 00301 case SNES_CONVERGED_FNORM_ABS: 00302 return DiffSolver::CONVERGED_ABSOLUTE_RESIDUAL; 00303 case SNES_CONVERGED_FNORM_RELATIVE: 00304 return DiffSolver::CONVERGED_RELATIVE_RESIDUAL; 00305 #if PETSC_VERSION_LESS_THAN(3,2,1) 00306 case SNES_CONVERGED_PNORM_RELATIVE: 00307 #else 00308 case SNES_CONVERGED_SNORM_RELATIVE: 00309 #endif 00310 return DiffSolver::CONVERGED_RELATIVE_STEP; 00311 #if !PETSC_VERSION_LESS_THAN(2,3,3) 00312 case SNES_CONVERGED_ITS: 00313 #endif 00314 case SNES_CONVERGED_TR_DELTA: 00315 return DiffSolver::CONVERGED_NO_REASON; 00316 case SNES_DIVERGED_FUNCTION_DOMAIN: 00317 case SNES_DIVERGED_FUNCTION_COUNT: 00318 case SNES_DIVERGED_FNORM_NAN: 00319 #if !PETSC_VERSION_LESS_THAN(3,3,0) 00320 case SNES_DIVERGED_INNER: 00321 #endif 00322 #if !PETSC_VERSION_LESS_THAN(2,3,2) 00323 case SNES_DIVERGED_LINEAR_SOLVE: 00324 #endif 00325 case SNES_DIVERGED_LOCAL_MIN: 00326 return DiffSolver::DIVERGED_NO_REASON; 00327 case SNES_DIVERGED_MAX_IT: 00328 return DiffSolver::DIVERGED_MAX_NONLINEAR_ITERATIONS; 00329 #if PETSC_VERSION_LESS_THAN(3,2,0) 00330 case SNES_DIVERGED_LS_FAILURE: 00331 #else 00332 case SNES_DIVERGED_LINE_SEARCH: 00333 #endif 00334 return DiffSolver::DIVERGED_BACKTRACKING_FAILURE; 00335 // In PETSc, SNES_CONVERGED_ITERATING means 00336 // the solve is still iterating, but by the 00337 // time we get here, we must have either 00338 // converged or diverged, so 00339 // SNES_CONVERGED_ITERATING is invalid. 00340 case SNES_CONVERGED_ITERATING: 00341 return DiffSolver::INVALID_SOLVE_RESULT; 00342 default: 00343 break; 00344 } 00345 return DiffSolver::INVALID_SOLVE_RESULT; 00346 } 00347 00348 00349 00350 unsigned int PetscDiffSolver::solve() 00351 { 00352 this->init(); 00353 00354 START_LOG("solve()", "PetscDiffSolver"); 00355 00356 PetscVector<Number> &x = 00357 *(cast_ptr<PetscVector<Number>*>(_system.solution.get())); 00358 PetscMatrix<Number> &jac = 00359 *(cast_ptr<PetscMatrix<Number>*>(_system.matrix)); 00360 PetscVector<Number> &r = 00361 *(cast_ptr<PetscVector<Number>*>(_system.rhs)); 00362 00363 #ifdef LIBMESH_ENABLE_CONSTRAINTS 00364 _system.get_dof_map().enforce_constraints_exactly(_system); 00365 #endif 00366 00367 int ierr = 0; 00368 00369 ierr = SNESSetFunction (_snes, r.vec(), 00370 __libmesh_petsc_diff_solver_residual, this); 00371 LIBMESH_CHKERRABORT(ierr); 00372 00373 ierr = SNESSetJacobian (_snes, jac.mat(), jac.mat(), 00374 __libmesh_petsc_diff_solver_jacobian, this); 00375 LIBMESH_CHKERRABORT(ierr); 00376 00377 # if PETSC_VERSION_LESS_THAN(2,2,0) 00378 00379 ierr = SNESSolve (_snes, x.vec(), &_outer_iterations); 00380 LIBMESH_CHKERRABORT(ierr); 00381 00382 // 2.2.x style 00383 #elif PETSC_VERSION_LESS_THAN(2,3,0) 00384 00385 ierr = SNESSolve (_snes, x.vec()); 00386 LIBMESH_CHKERRABORT(ierr); 00387 00388 // 2.3.x & newer style 00389 #else 00390 00391 ierr = SNESSolve (_snes, PETSC_NULL, x.vec()); 00392 LIBMESH_CHKERRABORT(ierr); 00393 00394 #endif 00395 00396 STOP_LOG("solve()", "PetscDiffSolver"); 00397 00398 SNESConvergedReason reason; 00399 SNESGetConvergedReason(_snes, &reason); 00400 00401 this->clear(); 00402 00403 return convert_solve_result(reason); 00404 } 00405 00406 00407 } // namespace libMesh 00408 00409 #endif // LIBMESH_HAVE_PETSC