$extrastylesheet
petsc_diff_solver.C
Go to the documentation of this file.
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