LCOV - code coverage report
Current view: top level - src/solvers - petsc_nonlinear_solver.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4624 (b6265e) with base 1fb77f Lines: 367 512 71.7 %
Date: 2026-11-03 16:49:57 Functions: 27 34 79.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : #include "libmesh/libmesh_common.h"
      21             : 
      22             : #ifdef LIBMESH_HAVE_PETSC
      23             : 
      24             : // Local Includes
      25             : #include "libmesh/libmesh_logging.h"
      26             : #include "libmesh/nonlinear_implicit_system.h"
      27             : #include "libmesh/petsc_nonlinear_solver.h"
      28             : #include "libmesh/petsc_linear_solver.h"
      29             : #include "libmesh/petsc_vector.h"
      30             : #include "libmesh/petsc_mffd_matrix.h"
      31             : #include "libmesh/dof_map.h"
      32             : #include "libmesh/constraint_elimination_dof_map.h"
      33             : #include "libmesh/preconditioner.h"
      34             : #include "libmesh/solver_configuration.h"
      35             : #include "libmesh/petscdmlibmesh.h"
      36             : #include "libmesh/petsc_preconditioner.h"
      37             : 
      38             : #if defined(LIBMESH_HAVE_PETSC_HYPRE) && PETSC_VERSION_LESS_THAN(3, 23, 0) &&                      \
      39             :     !PETSC_VERSION_LESS_THAN(3, 12, 0) && defined(PETSC_HAVE_HYPRE_DEVICE)
      40             : #include <HYPRE_utilities.h>
      41             : #endif
      42             : 
      43             : namespace libMesh
      44             : {
      45             : class ResidualContext
      46             : {
      47             : public:
      48        5784 :   ResidualContext(PetscNonlinearSolver<Number> * solver_in, NonlinearImplicitSystem & sys_in) :
      49             :       solver(solver_in),
      50        5784 :       sys(sys_in)
      51        5784 :     {}
      52             : 
      53             :   PetscNonlinearSolver<Number> * solver;
      54             :   NonlinearImplicitSystem & sys;
      55             : };
      56             : 
      57             : /**
      58             :  * Finishes a residual evaluation into \p R, which the user callbacks assembled at the iterate
      59             :  * that PETSc's \p x represents. When eliminating constraints \p R is the system's full \p rhs,
      60             :  * restricted here into PETSc's reduced \p r. Otherwise \p R wraps \p r, and the constraint rows
      61             :  * are enforced in it
      62             :  */
      63       19898 : void libmesh_petsc_snes_finish_residual(ResidualContext & rc,
      64             :                                         Vec x,
      65             :                                         Vec r,
      66             :                                         NumericVector<Number> & R)
      67             : {
      68       19898 :   R.close();
      69             : 
      70       25274 :   if (rc.solver->eliminating_constraints())
      71             :     {
      72         252 :       rc.solver->restrict_vector(R, r);
      73         252 :       return;
      74             :     }
      75             : 
      76             :   // Synchronize PETSc x to local solution since the local solution may be changed due to the constraints
      77       19646 :   PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(rc.sys.solution.get());
      78       30638 :   PetscVector<Number> X_global(x, rc.sys.comm());
      79             : 
      80       19646 :   X_global.swap(X_sys);
      81       19646 :   rc.sys.update();
      82       19646 :   X_global.swap(X_sys);
      83             : 
      84       19646 :   if (rc.solver->_exact_constraint_enforcement)
      85             :     {
      86       24938 :       rc.sys.get_dof_map().enforce_constraints_on_residual(rc.sys, &R, rc.sys.current_local_solution.get(), /*homogeneous=*/false);
      87       19646 :       R.close();
      88             :     }
      89        8654 : }
      90             : 
      91             : ResidualContext
      92        8738 : libmesh_petsc_snes_residual_helper (SNES snes, Vec x, void * ctx)
      93             : {
      94        5784 :   LOG_SCOPE("residual()", "PetscNonlinearSolver");
      95             : 
      96        5784 :   libmesh_assert(x);
      97        5784 :   libmesh_assert(ctx);
      98             : 
      99             :   // No way to safety-check this cast, since we got a void *...
     100        5784 :   PetscNonlinearSolver<Number> * solver =
     101             :     static_cast<PetscNonlinearSolver<Number> *> (ctx);
     102             : 
     103        5784 :   libmesh_parallel_only(solver->comm());
     104             : 
     105             :   // Get the current iteration number from the snes object,
     106             :   // store it in the PetscNonlinearSolver object for possible use
     107             :   // by the user's residual function.
     108             :   {
     109       19898 :     PetscInt n_iterations = 0;
     110       19898 :     LibmeshPetscCall2(solver->comm(), SNESGetIterationNumber(snes, &n_iterations));
     111       19898 :     solver->_current_nonlinear_iteration_number = cast_int<unsigned>(n_iterations);
     112             :   }
     113             : 
     114       11160 :   NonlinearImplicitSystem & sys = solver->system();
     115             : 
     116       19898 :   solver->localize_solution(x);
     117             : 
     118       25682 :   return ResidualContext(solver, sys);
     119             : }
     120             : 
     121             : //--------------------------------------------------------------------
     122             : // Functions with C linkage to pass to PETSc.  PETSc will call these
     123             : // methods as needed.
     124             : //
     125             : // Since they must have C linkage they have no knowledge of a namespace.
     126             : // Give them an obscure name to avoid namespace pollution.
     127             : extern "C"
     128             : {
     129             :   // -----------------------------------------------------------------
     130             :   // this function monitors the nonlinear solve and checks to see
     131             :   // if we want to recalculate the preconditioner.  It only gets
     132             :   // added to the SNES instance if we're reusing the preconditioner
     133             :   PetscErrorCode
     134           0 :   libmesh_petsc_recalculate_monitor(SNES snes, PetscInt, PetscReal, void* ctx)
     135             :   {
     136             :     PetscFunctionBegin;
     137             : 
     138             :     // No way to safety-check this cast, since we got a void *...
     139           0 :     PetscNonlinearSolver<Number> * solver =
     140             :       static_cast<PetscNonlinearSolver<Number> *> (ctx);
     141             : 
     142             :     KSP ksp;
     143           0 :     LibmeshPetscCall2(solver->comm(), SNESGetKSP(snes, &ksp));
     144             : 
     145             :     PetscInt niter;
     146           0 :     LibmeshPetscCall2(solver->comm(), KSPGetIterationNumber(ksp, &niter));
     147             : 
     148           0 :     if (niter > cast_int<PetscInt>(solver->reuse_preconditioner_max_linear_its()))
     149             :     {
     150             :       // -2 is a magic number for "recalculate next time you need it
     151             :       // and then not again"
     152           0 :       LibmeshPetscCall2(solver->comm(), SNESSetLagPreconditioner(snes, -2));
     153             :     }
     154           0 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     155             :   }
     156             : 
     157             :   //-------------------------------------------------------------------
     158             :   // this function is called by PETSc at the end of each nonlinear step
     159             :   PetscErrorCode
     160       10184 :   libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *)
     161             :   {
     162             :     PetscFunctionBegin;
     163        3004 :     libMesh::out << "  NL step "
     164        3004 :                  << std::setw(2) << its
     165        3004 :                  << std::scientific
     166        3004 :                  << ", |residual|_2 = " << fnorm
     167        3004 :                  << std::endl;
     168       10184 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     169             :   }
     170             : 
     171             :   //---------------------------------------------------------------
     172             :   // this function is called by PETSc to evaluate the residual at X
     173             :   PetscErrorCode
     174       13508 :   libmesh_petsc_snes_residual (SNES snes, Vec x, Vec r, void * ctx)
     175             :   {
     176             :     PetscFunctionBegin;
     177             : 
     178       13508 :     ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
     179             : 
     180        3386 :     libmesh_parallel_only(rc.sys.comm());
     181             : 
     182        3386 :     libmesh_assert(r);
     183       16894 :     PetscVector<Number> R_petsc(r, rc.sys.comm());
     184             :     NumericVector<Number> & R =
     185       16894 :       rc.solver->eliminating_constraints() ? *rc.sys.rhs : static_cast<NumericVector<Number> &>(R_petsc);
     186             : 
     187       13508 :     if (rc.solver->_zero_out_residual)
     188       13508 :       R.zero();
     189             : 
     190             :     //-----------------------------------------------------------------------------
     191             :     // if the user has provided both function pointers and objects only the pointer
     192             :     // will be used, so catch that as an error
     193       13508 :     libmesh_error_msg_if(rc.solver->residual && rc.solver->residual_object,
     194             :                          "ERROR: cannot specify both a function and object to compute the Residual!");
     195             : 
     196       13508 :     libmesh_error_msg_if(rc.solver->matvec && rc.solver->residual_and_jacobian_object,
     197             :                          "ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
     198             : 
     199       13508 :     if (rc.solver->residual != nullptr)
     200          32 :       rc.solver->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     201             : 
     202       13484 :     else if (rc.solver->residual_object != nullptr)
     203         392 :       rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     204             : 
     205       13190 :     else if (rc.solver->matvec != nullptr)
     206           0 :       rc.solver->matvec (*rc.sys.current_local_solution.get(), &R, nullptr, rc.sys);
     207             : 
     208       13190 :     else if (rc.solver->residual_and_jacobian_object != nullptr)
     209             :     {
     210       13190 :       auto & jac = rc.sys.get_system_matrix();
     211             : 
     212       13190 :       if (rc.solver->_zero_out_jacobian)
     213       13190 :         jac.zero();
     214             : 
     215       13190 :       rc.solver->residual_and_jacobian_object->residual_and_jacobian(
     216       13190 :           *rc.sys.current_local_solution.get(), &R, &jac, rc.sys);
     217             : 
     218       13190 :       jac.close();
     219       13190 :       if (rc.solver->_exact_constraint_enforcement && !rc.solver->eliminating_constraints())
     220             :         {
     221       16134 :           rc.sys.get_dof_map().enforce_constraints_on_jacobian(rc.sys, &jac);
     222       12938 :           jac.close();
     223             :         }
     224             :     }
     225             : 
     226             :     else
     227           0 :       libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
     228             : 
     229             : 
     230       13508 :     libmesh_petsc_snes_finish_residual(rc, x, r, R);
     231             : 
     232       16894 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     233        6736 :   }
     234             : 
     235             :   //-----------------------------------------------------------------------------------------
     236             :   // this function is called by PETSc to approximate the Jacobian at X via finite differences
     237             :   PetscErrorCode
     238           0 :   libmesh_petsc_snes_fd_residual (SNES snes, Vec x, Vec r, void * ctx)
     239             :   {
     240             :     PetscFunctionBegin;
     241             : 
     242           0 :     ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
     243             : 
     244           0 :     libmesh_parallel_only(rc.sys.comm());
     245             : 
     246           0 :     libmesh_assert(r);
     247           0 :     PetscVector<Number> R_petsc(r, rc.sys.comm());
     248             :     NumericVector<Number> & R =
     249           0 :       rc.solver->eliminating_constraints() ? *rc.sys.rhs : static_cast<NumericVector<Number> &>(R_petsc);
     250             : 
     251           0 :     if (rc.solver->_zero_out_residual)
     252           0 :       R.zero();
     253             : 
     254           0 :     if (rc.solver->fd_residual_object != nullptr)
     255           0 :       rc.solver->fd_residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     256             : 
     257           0 :     else if (rc.solver->residual_object != nullptr)
     258           0 :       rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     259             : 
     260             :     else
     261           0 :       libmesh_error_msg("Error! Unable to compute residual for forming finite difference Jacobian!");
     262             : 
     263           0 :     libmesh_petsc_snes_finish_residual(rc, x, r, R);
     264             : 
     265           0 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     266           0 :   }
     267             : 
     268             :   //----------------------------------------------------------------
     269             :   // this function is called by PETSc to approximate Jacobian-vector
     270             :   // products at X via finite differences
     271             :   PetscErrorCode
     272        6390 :   libmesh_petsc_snes_mffd_residual (SNES snes, Vec x, Vec r, void * ctx)
     273             :   {
     274             :     PetscFunctionBegin;
     275             : 
     276        6390 :     ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
     277             : 
     278        2398 :     libmesh_parallel_only(rc.sys.comm());
     279             : 
     280        2398 :     libmesh_assert(r);
     281        8380 :     PetscVector<Number> R_petsc(r, rc.sys.comm());
     282             :     NumericVector<Number> & R =
     283        8380 :       rc.solver->eliminating_constraints() ? *rc.sys.rhs : static_cast<NumericVector<Number> &>(R_petsc);
     284             : 
     285        6390 :     if (rc.solver->_zero_out_residual)
     286        6390 :       R.zero();
     287             : 
     288        6390 :     if (rc.solver->mffd_residual_object != nullptr)
     289        8380 :       rc.solver->mffd_residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     290             : 
     291           0 :     else if (rc.solver->residual_object != nullptr)
     292           0 :       rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
     293             : 
     294             :     else
     295           0 :       libmesh_error_msg("Error! Unable to compute residual for forming finite differenced"
     296             :                         "Jacobian-vector products!");
     297             : 
     298        6390 :     libmesh_petsc_snes_finish_residual(rc, x, r, R);
     299             : 
     300        8788 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     301        2002 :   }
     302             : 
     303             :   //----------------------------------------------------------
     304             :   // this function serves an interface between the petsc layer
     305             :   // and the actual mffd residual computing routine
     306             :   PetscErrorCode
     307        6390 :   libmesh_petsc_snes_mffd_interface (void * ctx, Vec x, Vec r)
     308             :   {
     309             :     PetscFunctionBegin;
     310             : 
     311             :     // No way to safety-check this cast, since we got a void *...
     312        2398 :     PetscNonlinearSolver<Number> * solver =
     313             :       static_cast<PetscNonlinearSolver<Number> *> (ctx);
     314             : 
     315        6390 :     LibmeshPetscCall2(solver->comm(), libmesh_petsc_snes_mffd_residual(solver->snes(), x, r, ctx));
     316             : 
     317             : #if !PETSC_VERSION_LESS_THAN(3,8,4)
     318             : #ifndef NDEBUG
     319             : 
     320             :     // When the user requested to reuse the nonlinear residual as the base for doing matrix-free
     321             :     // approximation of the Jacobian, we'll do a sanity check to make sure that that was safe to do.
     322             :     // The check requires an exact match, so skip it for MPI-parallel or threaded runs, or when the
     323             :     // application reports that its residual is not reproducible
     324        7194 :     if (solver->snes_mf_reuse_base() && solver->reproducible_residual() &&
     325        7194 :         (solver->comm().size() == 1) && (libMesh::n_threads() == 1))
     326             :     {
     327           0 :       SNES snes = solver->snes();
     328             : 
     329             :       KSP ksp;
     330           0 :       LibmeshPetscCall2(solver->comm(), SNESGetKSP(snes, &ksp));
     331             : 
     332             :       PetscInt ksp_it;
     333           0 :       LibmeshPetscCall2(solver->comm(), KSPGetIterationNumber(ksp, &ksp_it));
     334             : 
     335             :       SNESType snes_type;
     336           0 :       LibmeshPetscCall2(solver->comm(), SNESGetType(snes, &snes_type));
     337             : 
     338           0 :       libmesh_assert_msg(snes_type, "We're being called from SNES; snes_type should be non-null");
     339             : 
     340             :       Mat J;
     341           0 :       LibmeshPetscCall2(solver->comm(), SNESGetJacobian(snes, &J, NULL, NULL, NULL));
     342           0 :       libmesh_assert_msg(J, "We're being called from SNES; J should be non-null");
     343             : 
     344             :       MatType mat_type;
     345           0 :       LibmeshPetscCall2(solver->comm(), MatGetType(J, &mat_type));
     346           0 :       libmesh_assert_msg(mat_type, "We're being called from SNES; mat_type should be non-null");
     347             : 
     348           0 :       bool is_operator_mffd = strcmp(mat_type, MATMFFD) == 0;
     349             : 
     350           0 :       if ((ksp_it == PetscInt(0)) && is_operator_mffd)
     351             :       {
     352           0 :         bool computing_base_vector = solver->computing_base_vector();
     353             : 
     354           0 :         if (computing_base_vector)
     355             :         {
     356             :           Vec nonlinear_residual;
     357             : 
     358           0 :           LibmeshPetscCall2(solver->comm(), SNESGetFunction(snes, &nonlinear_residual, NULL, NULL));
     359             : 
     360             :           PetscBool vecs_equal;
     361           0 :           LibmeshPetscCall2(solver->comm(), VecEqual(r, nonlinear_residual, &vecs_equal));
     362             : 
     363           0 :           libmesh_error_msg_if(!(vecs_equal == PETSC_TRUE),
     364             :                                "You requested to reuse the nonlinear residual vector as the base vector for "
     365             :                                "computing the action of the matrix-free Jacobian, but the vectors are not "
     366             :                                "the same. Your physics must have states; either remove the states "
     367             :                                "from your code or make sure that you set_mf_reuse_base(false)");
     368             :         }
     369             : 
     370             :         // There are always exactly two function evaluations for the zeroth ksp iteration when doing
     371             :         // matrix-free approximation of the Jacobian action: one corresponding to the evaluation of
     372             :         // the base vector, and the other corresponding to evaluation of the perturbed vector. So we
     373             :         // toggle back and forth between states
     374           0 :         solver->set_computing_base_vector(!computing_base_vector);
     375             :       }
     376             :     }
     377             : #endif
     378             : #endif
     379             : 
     380        6390 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     381             :   }
     382             : 
     383             :   //---------------------------------------------------------------
     384             :   // this function is called by PETSc to evaluate the Jacobian at X
     385             :   PetscErrorCode
     386        7628 :   libmesh_petsc_snes_jacobian(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
     387             :   {
     388             :     PetscFunctionBegin;
     389             : 
     390        6456 :     LOG_SCOPE("jacobian()", "PetscNonlinearSolver");
     391             : 
     392        2152 :     libmesh_assert(ctx);
     393             : 
     394             :     // No way to safety-check this cast, since we got a void *...
     395        2152 :     PetscNonlinearSolver<Number> * solver =
     396             :       static_cast<PetscNonlinearSolver<Number> *> (ctx);
     397             : 
     398        2152 :     libmesh_parallel_only(solver->comm());
     399             : 
     400             :     // Get the current iteration number from the snes object,
     401             :     // store it in the PetscNonlinearSolver object for possible use
     402             :     // by the user's Jacobian function.
     403             :     {
     404        7628 :       PetscInt n_iterations = 0;
     405        7628 :       LibmeshPetscCall2(solver->comm(), SNESGetIterationNumber(snes, &n_iterations));
     406        7628 :       solver->_current_nonlinear_iteration_number = cast_int<unsigned>(n_iterations);
     407             :     }
     408             : 
     409             :     //-----------------------------------------------------------------------------
     410             :     // if the user has provided both function pointers and objects only the pointer
     411             :     // will be used, so catch that as an error
     412        7628 :     libmesh_error_msg_if(solver->jacobian && solver->jacobian_object,
     413             :                          "ERROR: cannot specify both a function and object to compute the Jacobian!");
     414             : 
     415        7628 :     libmesh_error_msg_if(solver->matvec && solver->residual_and_jacobian_object,
     416             :                          "ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
     417             : 
     418        4304 :     NonlinearImplicitSystem & sys = solver->system();
     419             : 
     420        7628 :     PetscMatrixBase<Number> * const PC = pc ? PetscMatrixBase<Number>::get_context(pc, sys.comm()) : nullptr;
     421        7628 :     PetscMatrixBase<Number> * Jac = jac ? PetscMatrixBase<Number>::get_context(jac, sys.comm()) : nullptr;
     422             : 
     423        6456 :     PetscMFFDMatrix<Number> mffd_jac(sys.comm());
     424        7628 :     PetscBool p_is_shell = PETSC_FALSE;
     425        7628 :     PetscBool j_is_mffd = PETSC_FALSE;
     426        7628 :     PetscBool j_is_shell = PETSC_FALSE;
     427        7628 :     if (pc)
     428        7628 :       LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &p_is_shell));
     429        2152 :     libmesh_assert(jac);
     430        7628 :     LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &j_is_mffd));
     431        7628 :     LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &j_is_shell));
     432        7628 :     if (j_is_mffd == PETSC_TRUE)
     433             :       {
     434         408 :         libmesh_assert(!Jac);
     435         408 :         Jac = &mffd_jac;
     436             :         // mffd_jac is function-local, so don't attach a context to jac here -- it would
     437             :         // dangle once mffd_jac is destroyed at the end of this call.
     438        1226 :         mffd_jac.assign(jac, /*set_context=*/false);
     439             :       }
     440             : 
     441             :     // We already computed the Jacobian during the residual evaluation
     442        7628 :     if (solver->residual_and_jacobian_object)
     443             :     {
     444             :       // We could be doing matrix-free in which case we cannot rely on closing of explicit matrices
     445             :       // that occurs during the PETSc residual callback
     446        7376 :       if ((j_is_shell == PETSC_TRUE) || (j_is_mffd == PETSC_TRUE))
     447        1268 :         Jac->close();
     448             : 
     449        7376 :       if (pc && (p_is_shell == PETSC_TRUE))
     450          42 :         PC->close();
     451             : 
     452        7376 :       PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     453             :     }
     454             : 
     455             :     // Set the dof maps
     456         336 :     PC->attach_dof_map(sys.get_dof_map());
     457         336 :     Jac->attach_dof_map(sys.get_dof_map());
     458             : 
     459         252 :     solver->localize_solution(x);
     460             : 
     461         252 :     if (solver->_zero_out_jacobian)
     462         252 :       PC->zero();
     463             : 
     464             : 
     465         252 :     if (solver->jacobian != nullptr)
     466          18 :       solver->jacobian(*sys.current_local_solution.get(), *PC, sys);
     467             : 
     468         234 :     else if (solver->jacobian_object != nullptr)
     469         312 :       solver->jacobian_object->jacobian(*sys.current_local_solution.get(), *PC, sys);
     470             : 
     471           0 :     else if (solver->matvec != nullptr)
     472           0 :       solver->matvec(*sys.current_local_solution.get(), nullptr, PC, sys);
     473             : 
     474             :     else
     475           0 :       libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
     476             : 
     477         252 :     PC->close();
     478             :     // A reduced Jacobian has no constraint rows to enforce
     479         252 :     if (solver->_exact_constraint_enforcement && !solver->eliminating_constraints())
     480             :       {
     481         252 :         sys.get_dof_map().enforce_constraints_on_jacobian(sys, PC);
     482         252 :         PC->close();
     483             :       }
     484             : 
     485         252 :     if (Jac != PC)
     486             :       {
     487             :         // Assume that shells know what they're doing
     488           0 :         libmesh_assert(!solver->_exact_constraint_enforcement || solver->eliminating_constraints() ||
     489             :                        (j_is_mffd == PETSC_TRUE) || (j_is_shell == PETSC_TRUE));
     490           0 :         Jac->close();
     491             :       }
     492             : 
     493          84 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     494             :   }
     495             : 
     496             :   // This function gets called by PETSc in place of the standard Petsc line searches
     497             :   // if a linesearch object is supplied to the PetscNonlinearSolver class. It wraps
     498             :   // the linesearch algorithm implemented on the linesearch object.
     499             :   // * "linesearch" is an object that can be used to access the non-linear and linear solution
     500             :   // vectors as well as the residual and SNES object
     501             :   // * "ctx" is the PetscNonlinearSolver context
     502           0 :   PetscErrorCode libmesh_petsc_linesearch_shellfunc (SNESLineSearch linesearch, void * ctx)
     503             :   {
     504             :     PetscFunctionBegin;
     505             : 
     506             :     // No way to safety-check this cast, since we got a void *...
     507           0 :     PetscNonlinearSolver<Number> * solver =
     508             :       static_cast<PetscNonlinearSolver<Number> *> (ctx);
     509             : 
     510           0 :     libmesh_parallel_only(solver->comm());
     511             : 
     512           0 :     solver->linesearch_object->linesearch(linesearch);
     513           0 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     514             :   }
     515             : 
     516             :   // This function gets called by PETSc after the SNES linesearch is
     517             :   // complete.  We use it to exactly enforce any constraints on the
     518             :   // solution which may have drifted during the linear solve.  In the
     519             :   // PETSc nomenclature:
     520             :   // * "x" is the old solution vector,
     521             :   // * "y" is the search direction (Newton step) vector,
     522             :   // * "w" is the candidate solution vector, and
     523             :   // the user is responsible for setting changed_y and changed_w
     524             :   // appropriately, depending on whether or not the search
     525             :   // direction or solution vector was changed, respectively.
     526          18 :   PetscErrorCode libmesh_petsc_snes_postcheck(SNESLineSearch, Vec x, Vec y, Vec w, PetscBool * changed_y, PetscBool * changed_w, void * context)
     527             :   {
     528             :     PetscFunctionBegin;
     529             : 
     530          18 :     LOG_SCOPE("postcheck()", "PetscNonlinearSolver");
     531             : 
     532             :     // PETSc almost certainly initializes these to false already, but
     533             :     // it doesn't hurt to be explicit.
     534          18 :     *changed_w = PETSC_FALSE;
     535          18 :     *changed_y = PETSC_FALSE;
     536             : 
     537           6 :     libmesh_assert(context);
     538             : 
     539             :     // Cast the context to a NonlinearSolver object.
     540          18 :     PetscNonlinearSolver<Number> * solver =
     541             :       static_cast<PetscNonlinearSolver<Number> *> (context);
     542             : 
     543           6 :     libmesh_parallel_only(solver->comm());
     544             : 
     545             :     // If the user has provided both postcheck function pointer and
     546             :     // object, this is ambiguous, so throw an error.
     547          18 :     libmesh_error_msg_if(solver->postcheck && solver->postcheck_object,
     548             :                          "ERROR: cannot specify both a function and object for performing the solve postcheck!");
     549             : 
     550             :     // It's also possible that we don't need to do anything at all, in
     551             :     // that case return early...
     552          12 :     NonlinearImplicitSystem & sys = solver->system();
     553             : 
     554          18 :     if (!solver->postcheck && !solver->postcheck_object)
     555           0 :       PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     556             : 
     557             :     // The user sets these flags in his/her postcheck function to
     558             :     // indicate whether they changed something.
     559             :     bool
     560          18 :       changed_search_direction = false,
     561          18 :       changed_new_soln = false;
     562             : 
     563           6 :     auto call_postcheck = [&](const NumericVector<Number> & old_soln,
     564             :                               NumericVector<Number> & search_direction,
     565          12 :                               NumericVector<Number> & new_soln) {
     566          18 :       if (solver->postcheck)
     567           0 :         solver->postcheck(old_soln,
     568             :                           search_direction,
     569             :                           new_soln,
     570          18 :                           changed_search_direction,
     571          18 :                           changed_new_soln,
     572           0 :                           sys);
     573             :       else
     574          42 :         solver->postcheck_object->postcheck(old_soln,
     575             :                                             search_direction,
     576             :                                             new_soln,
     577             :                                             changed_search_direction,
     578             :                                             changed_new_soln,
     579          12 :                                             sys);
     580          24 :     };
     581             : 
     582          18 :     if (solver->eliminating_constraints())
     583             :       {
     584             :         // The user sees full iterates C x + h and C w + h and the full step C y. Restricting the
     585             :         // user's changes back to the reduced space keeps each change to an unconstrained entry
     586             :         // and projects the iterate back onto the constraints
     587           0 :         auto old_soln = sys.solution->zero_clone();
     588           0 :         auto search_direction = sys.solution->zero_clone();
     589           0 :         auto new_soln = sys.solution->zero_clone();
     590           0 :         solver->prolong(x, *old_soln, /*homogeneous=*/false);
     591           0 :         solver->prolong(y, *search_direction, /*homogeneous=*/true);
     592           0 :         solver->prolong(w, *new_soln, /*homogeneous=*/false);
     593             : 
     594           0 :         call_postcheck(*old_soln, *search_direction, *new_soln);
     595             : 
     596           0 :         if (changed_search_direction)
     597           0 :           solver->restrict_vector(*search_direction, y);
     598           0 :         if (changed_new_soln)
     599           0 :           solver->restrict_vector(*new_soln, w);
     600           0 :       }
     601             :     else
     602             :       {
     603          30 :         PetscVector<Number> petsc_x(x, sys.comm());
     604          30 :         PetscVector<Number> petsc_y(y, sys.comm());
     605          30 :         PetscVector<Number> petsc_w(w, sys.comm());
     606          18 :         call_postcheck(petsc_x, petsc_y, petsc_w);
     607           6 :       }
     608             : 
     609             :     // Record whether the user changed the solution or the search direction.
     610          18 :     if (changed_search_direction)
     611           0 :       *changed_y = PETSC_TRUE;
     612             : 
     613          18 :     if (changed_new_soln)
     614           0 :       *changed_w = PETSC_TRUE;
     615             : 
     616           6 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     617             :   }
     618             : 
     619           0 :   PetscErrorCode libmesh_petsc_snes_precheck(SNESLineSearch, Vec X, Vec Y, PetscBool * changed, void * context)
     620             :   {
     621             :     PetscFunctionBegin;
     622             : 
     623           0 :     LOG_SCOPE("precheck()", "PetscNonlinearSolver");
     624             : 
     625             :     // PETSc almost certainly initializes these to false already, but
     626             :     // it doesn't hurt to be explicit.
     627           0 :     *changed = PETSC_FALSE;
     628             : 
     629           0 :     libmesh_assert(context);
     630             : 
     631             :     // Cast the context to a NonlinearSolver object.
     632           0 :     PetscNonlinearSolver<Number> * solver =
     633             :       static_cast<PetscNonlinearSolver<Number> *> (context);
     634             : 
     635           0 :     libmesh_parallel_only(solver->comm());
     636             : 
     637             :     // It's possible that we don't need to do anything at all, in
     638             :     // that case return early...
     639           0 :     if (!solver->precheck_object)
     640           0 :       PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     641             : 
     642             :     // The user sets these flags in his/her postcheck function to
     643             :     // indicate whether they changed something.
     644             :     bool
     645           0 :       petsc_changed = false;
     646             : 
     647           0 :     auto & sys = solver->system();
     648             : 
     649             :     // The precheck solution is the system's current_local_solution, which holds the full iterate
     650           0 :     solver->localize_solution(X);
     651           0 :     libmesh_assert(sys.current_local_solution.get());
     652           0 :     auto & local_soln = *sys.current_local_solution.get();
     653             : 
     654           0 :     if (solver->eliminating_constraints())
     655             :       {
     656             :         // The user sees the full search direction C Y, and a change to it is restricted back
     657           0 :         auto search_direction = sys.solution->zero_clone();
     658           0 :         solver->prolong(Y, *search_direction, /*homogeneous=*/true);
     659           0 :         solver->precheck_object->precheck(local_soln, *search_direction, petsc_changed, sys);
     660           0 :         if (petsc_changed)
     661           0 :           solver->restrict_vector(*search_direction, Y);
     662           0 :       }
     663             :     else
     664             :       {
     665           0 :         PetscVector<Number> petsc_y(Y, sys.comm());
     666           0 :         solver->precheck_object->precheck(local_soln, petsc_y, petsc_changed, sys);
     667           0 :       }
     668             : 
     669             :     // Record whether the user changed the solution or the search direction.
     670           0 :     if (petsc_changed)
     671           0 :       *changed = PETSC_TRUE;
     672             : 
     673           0 :     PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
     674             :   }
     675             : 
     676             : } // end extern "C"
     677             : 
     678             : 
     679             : 
     680             : //---------------------------------------------------------------------
     681             : // PetscNonlinearSolver<> methods
     682             : template <typename T>
     683         198 : PetscNonlinearSolver<T>::PetscNonlinearSolver (sys_type & system_in) :
     684             :   NonlinearSolver<T>(system_in),
     685             :   linesearch_object(nullptr),
     686          66 :   _reason(SNES_CONVERGED_ITERATING/*==0*/), // Arbitrary initial value...
     687          66 :   _n_linear_iterations(0),
     688          66 :   _current_nonlinear_iteration_number(0),
     689          66 :   _zero_out_residual(true),
     690          66 :   _zero_out_jacobian(true),
     691          66 :   _default_monitor(true),
     692          66 :   _snesmf_reuse_base(true),
     693          66 :   _reproducible_residual(true),
     694          66 :   _computing_base_vector(true),
     695         198 :   _setup_reuse(false)
     696             : {
     697         198 : }
     698             : 
     699             : 
     700             : 
     701             : template <typename T>
     702         132 : PetscNonlinearSolver<T>::~PetscNonlinearSolver () = default;
     703             : 
     704             : 
     705             : 
     706             : template <typename T>
     707        2592 : void PetscNonlinearSolver<T>::clear ()
     708             : {
     709        2592 :   if (this->initialized())
     710             :     {
     711        2568 :       this->_is_initialized = false;
     712             : 
     713             :       // If we don't need the preconditioner next time
     714             :       // retain the original behavior of clearing the data
     715             :       // between solves.
     716        2568 :       if (!(reuse_preconditioner()))
     717             :         {
     718             :         // SNESReset really ought to work but replacing destroy() with
     719             :         // SNESReset causes a very slight change in behavior that
     720             :         // manifests as two failed MOOSE tests...
     721        2568 :         _snes.destroy();
     722             :         }
     723             : 
     724             :       // Reset the nonlinear iteration counter.  This information is only relevant
     725             :       // *during* the solve().  After the solve is completed it should return to
     726             :       // the default value of 0.
     727        2568 :       _current_nonlinear_iteration_number = 0;
     728             :     }
     729        2592 : }
     730             : 
     731             : template <typename T>
     732       11532 : void PetscNonlinearSolver<T>::init (const char * name)
     733             : {
     734        4112 :   parallel_object_only();
     735             : 
     736             :   // Initialize the data structures if not done so already.
     737       11532 :   if (!this->initialized())
     738             :     {
     739        2574 :       this->_is_initialized = true;
     740             : 
     741             :       // Make only if we don't already have a retained snes
     742             :       // hanging around from the last solve
     743        2574 :       if (!_snes)
     744        2574 :         LibmeshPetscCall(SNESCreate(this->comm().get(), _snes.get()));
     745             : 
     746             :       // I believe all of the following can be safely repeated
     747             :       // even on an old snes instance from the last solve
     748             : 
     749        2574 :       if (name)
     750             :         {
     751           4 :           libmesh_assert(std::string(name).front() != '-');
     752           4 :           libmesh_assert(std::string(name).back() == '_');
     753          12 :           LibmeshPetscCall(SNESSetOptionsPrefix(_snes, name));
     754             :         }
     755             : 
     756             :       // Attaching a DM to SNES.
     757             : #if defined(LIBMESH_ENABLE_AMR) && defined(LIBMESH_HAVE_METAPHYSICL)
     758        4294 :       bool use_petsc_dm = libMesh::on_command_line(
     759         858 :           "--" + (name ? std::string(name) : std::string("")) + "use_petsc_dm");
     760             : 
     761             :       // This needs to be called before SNESSetFromOptions
     762        2574 :       if (use_petsc_dm)
     763           0 :         this->_dm_wrapper.init_and_attach_petscdm(this->system(), _snes);
     764             :       else
     765             : #endif
     766             :       {
     767        1716 :         WrappedPetsc<DM> dm;
     768        2574 :         LibmeshPetscCall(DMCreate(this->comm().get(), dm.get()));
     769        2574 :         LibmeshPetscCall(DMSetType(dm, DMLIBMESH));
     770        2574 :         LibmeshPetscCall(DMlibMeshSetSystem(dm, this->system()));
     771             : 
     772        2574 :         if (name)
     773          12 :           LibmeshPetscCall(DMSetOptionsPrefix(dm, name));
     774             : 
     775        2574 :         LibmeshPetscCall(DMSetFromOptions(dm));
     776        2574 :         LibmeshPetscCall(DMSetUp(dm));
     777        2574 :         LibmeshPetscCall(SNESSetDM(_snes, dm));
     778             :         // SNES now owns the reference to dm.
     779             :       }
     780             : 
     781        2574 :       setup_default_monitor();
     782             : 
     783             :       // If the SolverConfiguration object is provided, use it to set
     784             :       // options during solver initialization.
     785        2574 :       if (this->_solver_configuration)
     786             :         {
     787           0 :           this->_solver_configuration->set_options_during_init();
     788             :         }
     789             : 
     790        2574 :       if (this->_preconditioner)
     791             :         {
     792             :           KSP ksp;
     793          30 :           LibmeshPetscCall(SNESGetKSP (_snes, &ksp));
     794             :           PC pc;
     795          30 :           LibmeshPetscCall(KSPGetPC(ksp,&pc));
     796             : 
     797          30 :           this->_preconditioner->init();
     798             : 
     799          30 :           LibmeshPetscCall(PCSetType(pc, PCSHELL));
     800          30 :           LibmeshPetscCall(PCShellSetContext(pc,(void *)this->_preconditioner));
     801             : 
     802             :           //Re-Use the shell functions from petsc_linear_solver
     803          30 :           LibmeshPetscCall(PCShellSetSetUp(pc,libmesh_petsc_preconditioner_setup));
     804          30 :           LibmeshPetscCall(PCShellSetApply(pc,libmesh_petsc_preconditioner_apply));
     805             :         }
     806             :     }
     807             : 
     808             : 
     809             :   // Tell PETSc about our linesearch "post-check" function, but only
     810             :   // if the user has provided one.  There seem to be extra,
     811             :   // unnecessary residual calculations if a postcheck function is
     812             :   // attached for no reason.
     813       11532 :   if (this->postcheck || this->postcheck_object)
     814             :     {
     815             :       SNESLineSearch linesearch;
     816          12 :       LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
     817             : 
     818          12 :       LibmeshPetscCall(SNESLineSearchSetPostCheck(linesearch, libmesh_petsc_snes_postcheck, this));
     819             :     }
     820             : 
     821       11532 :   if (this->precheck_object)
     822             :     {
     823             :       SNESLineSearch linesearch;
     824           0 :       LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
     825             : 
     826           0 :       LibmeshPetscCall(SNESLineSearchSetPreCheck(linesearch, libmesh_petsc_snes_precheck, this));
     827             :     }
     828       11532 : }
     829             : 
     830             : 
     831             : template <typename T>
     832        4394 : SNES PetscNonlinearSolver<T>::snes(const char * name)
     833             : {
     834        6396 :   this->init(name);
     835        4394 :   return _snes;
     836             : }
     837             : 
     838             : 
     839             : 
     840             : template <typename T>
     841             : void
     842           6 : PetscNonlinearSolver<T>::build_mat_null_space(NonlinearImplicitSystem::ComputeVectorSubspace * computeSubspaceObject,
     843             :                                               void (*computeSubspace)(std::vector<NumericVector<Number> *> &, sys_type &),
     844             :                                               MatNullSpace * msp)
     845             : {
     846           2 :   parallel_object_only();
     847             : 
     848           4 :   std::vector<NumericVector<Number> *> sp;
     849           6 :   if (computeSubspaceObject)
     850           6 :     (*computeSubspaceObject)(sp, this->system());
     851             :   else
     852           0 :     (*computeSubspace)(sp, this->system());
     853             : 
     854           6 :   *msp = LIBMESH_PETSC_NULLPTR;
     855           8 :   if (sp.size())
     856             :     {
     857           2 :       PetscInt nmodes = cast_int<PetscInt>(sp.size());
     858             : 
     859           8 :       std::vector<Vec> modes(nmodes);
     860           8 :       std::vector<PetscScalar> dots(nmodes);
     861             : 
     862          12 :       for (PetscInt i=0; i<nmodes; ++i)
     863             :         {
     864           8 :           auto pv = cast_ptr<PetscVector<T> *>(sp[i]);
     865             : 
     866           6 :           if (this->eliminating_constraints())
     867             :             {
     868             :               // A full mode v = C y of the constrained operator gives the reduced mode y, the
     869             :               // unconstrained entries of v
     870           4 :               const auto & reduced_map = this->system().get_dof_map().get_constraint_elimination();
     871           8 :               LibmeshPetscCall(VecCreate(this->comm().get(), &modes[i]));
     872          10 :               LibmeshPetscCall(VecSetSizes(modes[i],
     873             :                                            cast_int<PetscInt>(reduced_map.n_local_dofs()),
     874             :                                            cast_int<PetscInt>(reduced_map.n_dofs())));
     875             :               VecType vec_type;
     876           6 :               LibmeshPetscCall(VecGetType(pv->vec(), &vec_type));
     877           8 :               LibmeshPetscCall(VecSetType(modes[i], vec_type));
     878           8 :               LibmeshPetscCall(VecSetUp(modes[i]));
     879           8 :               this->restrict_vector(*pv, modes[i]);
     880             :             }
     881             :           else
     882             :             {
     883           0 :               LibmeshPetscCall(VecDuplicate(pv->vec(), &modes[i]));
     884             : 
     885           0 :               LibmeshPetscCall(VecCopy(pv->vec(), modes[i]));
     886             :             }
     887             :         }
     888             : 
     889             :       // Normalize.
     890           6 :       LibmeshPetscCall(VecNormalize(modes[0], LIBMESH_PETSC_NULLPTR));
     891             : 
     892           6 :       for (PetscInt i=1; i<nmodes; i++)
     893             :         {
     894             :           // Orthonormalize vec[i] against vec[0:i-1]
     895           0 :           LibmeshPetscCall(VecMDot(modes[i], i, modes.data(), dots.data()));
     896             : 
     897           0 :           for (PetscInt j=0; j<i; j++)
     898           0 :             dots[j] *= -1.;
     899             : 
     900           0 :           LibmeshPetscCall(VecMAXPY(modes[i], i, dots.data(), modes.data()));
     901             : 
     902           0 :           LibmeshPetscCall(VecNormalize(modes[i], LIBMESH_PETSC_NULLPTR));
     903             :         }
     904             : 
     905           6 :       LibmeshPetscCall(MatNullSpaceCreate(this->comm().get(), PETSC_FALSE, nmodes, modes.data(), msp));
     906             : 
     907          12 :       for (PetscInt i=0; i<nmodes; ++i)
     908           8 :         LibmeshPetscCall(VecDestroy(&modes[i]));
     909             :     }
     910           6 : }
     911             : 
     912             : template <typename T>
     913             : std::pair<unsigned int, Real>
     914        2568 : PetscNonlinearSolver<T>::solve (SparseMatrix<T> &  pre_in,  // System Preconditioning Matrix
     915             :                                 NumericVector<T> & x_in,    // Solution vector
     916             :                                 NumericVector<T> & r_in,    // Residual vector
     917             :                                 const double        tol,     // Stopping tolerance
     918             :                                 const unsigned int  m_its)
     919             : {
     920        2568 :   return this->solve(pre_in, pre_in, x_in, r_in, tol, m_its);
     921             : }
     922             : 
     923             : template <typename T>
     924             : std::pair<unsigned int, Real>
     925        2568 : PetscNonlinearSolver<T>::solve (SparseMatrix<T> &  jac_in,  // Jacobian operator matrix (Amat)
     926             :                                 SparseMatrix<T> &  pre_in,  // Preconditioning matrix (Pmat)
     927             :                                 NumericVector<T> & x_in,    // Solution vector
     928             :                                 NumericVector<T> & r_in,    // Residual vector
     929             :                                 const double,              // Stopping tolerance
     930             :                                 const unsigned int)
     931             : {
     932         856 :   parallel_object_only();
     933             : 
     934        2568 :   LOG_SCOPE("solve()", "PetscNonlinearSolver");
     935        2568 :   this->init ();
     936             : 
     937             :   // Make sure the data passed in are really of Petsc types
     938         856 :   PetscMatrixBase<T> * jac = cast_ptr<PetscMatrixBase<T> *>(&jac_in);
     939         856 :   PetscMatrixBase<T> * pre = cast_ptr<PetscMatrixBase<T> *>(&pre_in);
     940         856 :   PetscVector<T> * x   = cast_ptr<PetscVector<T> *>(&x_in);
     941         856 :   PetscVector<T> * r   = cast_ptr<PetscVector<T> *>(&r_in);
     942             : 
     943             :   // When eliminating constraints, SNES iterates on the reduced vector y, from which the full
     944             :   // solution x = C y + h is formed for every evaluation, and its residual is the reduced one.
     945             :   // The full x_in and r_in remain the system's assembly vectors
     946        1712 :   WrappedPetsc<Vec> reduced_x, reduced_r;
     947        2568 :   this->setup_constraint_elimination();
     948        2568 :   if (this->eliminating_constraints())
     949             :     {
     950          20 :       const auto & reduced_map = this->system().get_dof_map().get_constraint_elimination();
     951          20 :       const auto n = cast_int<PetscInt>(reduced_map.n_dofs());
     952          10 :       const auto n_local = cast_int<PetscInt>(reduced_map.n_local_dofs());
     953          30 :       LibmeshPetscCall(VecCreate(this->comm().get(), reduced_x.get()));
     954          30 :       LibmeshPetscCall(VecSetSizes(reduced_x, n_local, n));
     955             :       VecType vec_type;
     956          30 :       LibmeshPetscCall(VecGetType(x->vec(), &vec_type));
     957          30 :       LibmeshPetscCall(VecSetType(reduced_x, vec_type));
     958          30 :       LibmeshPetscCall(VecSetUp(reduced_x));
     959          30 :       LibmeshPetscCall(VecDuplicate(reduced_x, reduced_r.get()));
     960             : 
     961             :       // The initial guess is the unconstrained part of the current solution
     962          30 :       x->close();
     963          30 :       this->restrict_vector(*x, reduced_x);
     964             :     }
     965        2568 :   const Vec snes_x = this->eliminating_constraints() ? static_cast<Vec>(reduced_x) : x->vec();
     966        2568 :   const Vec snes_r = this->eliminating_constraints() ? static_cast<Vec>(reduced_r) : r->vec();
     967             : 
     968        2568 :   PetscInt n_iterations =0;
     969             :   // Should actually be a PetscReal, but I don't know which version of PETSc first introduced PetscReal
     970        2568 :   Real final_residual_norm=0.;
     971             : 
     972             :   // We don't want to do this twice because it resets
     973             :   // SNESSetLagPreconditioner
     974        2568 :   if ((reuse_preconditioner()) && (!_setup_reuse))
     975             :     {
     976           0 :       _setup_reuse = true;
     977           0 :       LibmeshPetscCall(SNESSetLagPreconditionerPersists(_snes, PETSC_TRUE));
     978             :       // According to the PETSC 3.16.5 docs -2 is a magic number which
     979             :       // means "recalculate the next time you need it and then not again"
     980           0 :       LibmeshPetscCall(SNESSetLagPreconditioner(_snes, -2));
     981             :       // Add in our callback which will trigger recalculating
     982             :       // the preconditioner when we hit reuse_preconditioner_max_linear_its
     983           0 :       LibmeshPetscCall(SNESMonitorSet(_snes, &libmesh_petsc_recalculate_monitor,
     984             :                                       this,
     985             :                                       NULL));
     986             :     }
     987        2568 :   else if (!(reuse_preconditioner()))
     988             :     // This covers the case where it was enabled but was then disabled
     989             :     {
     990        2568 :       LibmeshPetscCall(SNESSetLagPreconditionerPersists(_snes, PETSC_FALSE));
     991        2568 :       if (_setup_reuse)
     992             :         {
     993           0 :           _setup_reuse = false;
     994           0 :           LibmeshPetscCall(SNESMonitorCancel(_snes));
     995             :           // Readd default monitor
     996           0 :           setup_default_monitor();
     997             :         }
     998             :     }
     999             : 
    1000        2568 :   LibmeshPetscCall(SNESSetFunction (_snes, snes_r, libmesh_petsc_snes_residual, this));
    1001             : 
    1002             :   // Only set the jacobian function if we've been provided with something to call.
    1003             :   // This allows a user to set their own jacobian function if they want to
    1004        2568 :   if (this->jacobian || this->jacobian_object || this->residual_and_jacobian_object)
    1005        2568 :     LibmeshPetscCall(SNESSetJacobian (_snes, jac->mat(), pre->mat(), libmesh_petsc_snes_jacobian, this));
    1006             : 
    1007             :   // Have the Krylov subspace method use our good initial guess rather than 0
    1008             :   KSP ksp;
    1009        2568 :   LibmeshPetscCall(SNESGetKSP (_snes, &ksp));
    1010             : 
    1011             :   // Set the tolerances for the iterative solver.  Use the user-supplied
    1012             :   // tolerance for the relative residual & leave the others at default values
    1013        2568 :   LibmeshPetscCall(KSPSetTolerances (ksp, this->initial_linear_tolerance, PETSC_DEFAULT,
    1014             :                                      PETSC_DEFAULT, this->max_linear_iterations));
    1015             : 
    1016             :   // Set the tolerances for the non-linear solver.
    1017        2568 :   LibmeshPetscCall(SNESSetTolerances(_snes,
    1018             :                                      this->absolute_residual_tolerance,
    1019             :                                      this->relative_residual_tolerance,
    1020             :                                      this->relative_step_tolerance,
    1021             :                                      this->max_nonlinear_iterations,
    1022             :                                      this->max_function_evaluations));
    1023             : 
    1024             :   // Not supported by PETSc
    1025         856 :   if (this->absolute_step_tolerance != 0) // 0 is default value, both in MOOSE and libMesh
    1026             :     libmesh_warning("Setting the absolute step tolerance is not supported with the PETSc nonlinear solver.");
    1027             : 
    1028             :   // Set the divergence tolerance for the non-linear solver
    1029             : #if !PETSC_VERSION_LESS_THAN(3,8,0)
    1030        2568 :   LibmeshPetscCall(SNESSetDivergenceTolerance(_snes, this->divergence_tolerance));
    1031             : #endif
    1032             : 
    1033             :   //Pull in command-line options
    1034             : #if PETSC_VERSION_LESS_THAN(3,7,0)
    1035             :   LibmeshPetscCall(KSPSetFromOptions(ksp));
    1036             : #endif
    1037        2568 :   LibmeshPetscCall(SNESSetFromOptions(_snes));
    1038             : 
    1039             :   PC pc;
    1040        2568 :   LibmeshPetscCall(KSPGetPC(ksp, &pc));
    1041        2568 :   PetscPreconditioner<T>::set_petsc_aux_data(pc, this->system());
    1042             : 
    1043             : #if defined(LIBMESH_HAVE_PETSC_HYPRE) && PETSC_VERSION_LESS_THAN(3, 23, 0) &&                      \
    1044             :     !PETSC_VERSION_LESS_THAN(3, 12, 0) && defined(PETSC_HAVE_HYPRE_DEVICE)
    1045             :   {
    1046             :     // Make sure hypre has been initialized
    1047             :     LibmeshPetscCallExternal(HYPRE_Initialize);
    1048             :     PetscScalar * dummyarray;
    1049             :     PetscMemType mtype;
    1050             :     LibmeshPetscCall(VecGetArrayAndMemType(x->vec(), &dummyarray, &mtype));
    1051             :     LibmeshPetscCall(VecRestoreArrayAndMemType(x->vec(), &dummyarray));
    1052             :     if (PetscMemTypeHost(mtype))
    1053             :       LibmeshPetscCallExternal(HYPRE_SetMemoryLocation, HYPRE_MEMORY_HOST);
    1054             :   }
    1055             : #endif
    1056             : 
    1057        2568 :   if (this->user_presolve)
    1058           0 :     this->user_presolve(this->system());
    1059             : 
    1060             :   //Set the preconditioning matrix
    1061        2568 :   if (this->_preconditioner)
    1062             :     {
    1063          10 :       this->_preconditioner->set_matrix(pre_in);
    1064          30 :       this->_preconditioner->init();
    1065             :     }
    1066             : 
    1067             :   // If the SolverConfiguration object is provided, use it to override
    1068             :   // solver options.
    1069        2568 :   if (this->_solver_configuration)
    1070           0 :     this->_solver_configuration->configure_solver();
    1071             : 
    1072             :   // In PETSc versions before 3.5.0, it is not possible to call
    1073             :   // SNESSetUp() before the solution and rhs vectors are initialized, as
    1074             :   // this triggers the
    1075             :   //
    1076             :   // "Solution vector cannot be right hand side vector!"
    1077             :   //
    1078             :   // error message. It is also not possible to call SNESSetSolution()
    1079             :   // in those versions of PETSc to work around the problem, since that
    1080             :   // API was removed in 3.0.0 and only restored in 3.6.0. The
    1081             :   // overzealous check was moved out of SNESSetUp in PETSc 3.5.0
    1082             :   // (petsc/petsc@154060b), so this code block should be safe to use
    1083             :   // in 3.5.0 and later.
    1084             : #if !PETSC_VERSION_LESS_THAN(3,6,0)
    1085        2568 :   LibmeshPetscCall(SNESSetSolution(_snes, snes_x));
    1086             : #endif
    1087        2568 :   LibmeshPetscCall(SNESSetUp(_snes));
    1088             : 
    1089             :   Mat J, P;
    1090        2568 :   LibmeshPetscCall(SNESGetJacobian(_snes, &J, &P,
    1091             :                                    LIBMESH_PETSC_NULLPTR,
    1092             :                                    LIBMESH_PETSC_NULLPTR));
    1093        2568 :   LibmeshPetscCall(MatMFFDSetFunction(J, libmesh_petsc_snes_mffd_interface, this));
    1094             : #if !PETSC_VERSION_LESS_THAN(3,8,4)
    1095             : #ifndef NDEBUG
    1096             :   // If we're in debug mode, do not reuse the nonlinear function evaluation as the base for doing
    1097             :   // matrix-free approximations of the Jacobian action. Instead if the user requested that we reuse
    1098             :   // the base, we'll check the base function evaluation and compare it to the nonlinear residual
    1099             :   // evaluation. If they are different, then we'll error and inform the user that it's unsafe to
    1100             :   // reuse the base
    1101         856 :   LibmeshPetscCall(MatSNESMFSetReuseBase(J, PETSC_FALSE));
    1102             : #else
    1103             :   // Resue the residual vector from SNES
    1104        1712 :   LibmeshPetscCall(MatSNESMFSetReuseBase(J, static_cast<PetscBool>(_snesmf_reuse_base)));
    1105             : #endif
    1106             : #endif
    1107             : 
    1108             :   // Only set the nullspace if we have a way of computing it and the result is non-empty.
    1109        2568 :   if (this->nullspace || this->nullspace_object)
    1110             :     {
    1111           4 :       WrappedPetsc<MatNullSpace> msp;
    1112           6 :       this->build_mat_null_space(this->nullspace_object, this->nullspace, msp.get());
    1113           6 :       if (msp)
    1114             :         {
    1115           6 :           LibmeshPetscCall(MatSetNullSpace(J, msp));
    1116           6 :           if (P != J)
    1117           0 :             LibmeshPetscCall(MatSetNullSpace(P, msp));
    1118             :         }
    1119             :     }
    1120             : 
    1121             :   // Only set the transpose nullspace if we have a way of computing it and the result is non-empty.
    1122        2568 :   if (this->transpose_nullspace || this->transpose_nullspace_object)
    1123             :     {
    1124             : #if PETSC_VERSION_LESS_THAN(3,6,0)
    1125             :       libmesh_warning("MatSetTransposeNullSpace is only supported for PETSc >= 3.6, transpose nullspace will be ignored.");
    1126             : #else
    1127           0 :       WrappedPetsc<MatNullSpace> msp;
    1128           0 :       this->build_mat_null_space(this->transpose_nullspace_object, this->transpose_nullspace, msp.get());
    1129           0 :       if (msp)
    1130             :         {
    1131           0 :           LibmeshPetscCall(MatSetTransposeNullSpace(J, msp));
    1132           0 :           if (P != J)
    1133           0 :             LibmeshPetscCall(MatSetTransposeNullSpace(P, msp));
    1134             :         }
    1135             : #endif
    1136             :     }
    1137             : 
    1138             :   // Only set the nearnullspace if we have a way of computing it and the result is non-empty.
    1139        2568 :   if (this->nearnullspace || this->nearnullspace_object)
    1140             :     {
    1141           0 :       WrappedPetsc<MatNullSpace> msp;
    1142           0 :       this->build_mat_null_space(this->nearnullspace_object, this->nearnullspace, msp.get());
    1143             : 
    1144           0 :       if (msp)
    1145             :         {
    1146           0 :           LibmeshPetscCall(MatSetNearNullSpace(J, msp));
    1147           0 :           if (P != J)
    1148           0 :             LibmeshPetscCall(MatSetNearNullSpace(P, msp));
    1149             :         }
    1150             :     }
    1151             : 
    1152             :   SNESLineSearch linesearch;
    1153        2568 :   if (linesearch_object)
    1154             :   {
    1155           0 :     LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
    1156           0 :     LibmeshPetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHSHELL));
    1157             : #if PETSC_RELEASE_GREATER_EQUALS(3, 21, 0)
    1158             :     LibmeshPetscCall(SNESLineSearchShellSetApply(linesearch, libmesh_petsc_linesearch_shellfunc, this));
    1159             : #else
    1160           0 :     LibmeshPetscCall(SNESLineSearchShellSetUserFunc(linesearch, libmesh_petsc_linesearch_shellfunc, this));
    1161             : #endif
    1162             :   }
    1163             : 
    1164        2568 :   LibmeshPetscCall(SNESSolve (_snes, LIBMESH_PETSC_NULLPTR, snes_x));
    1165             : 
    1166        2568 :   if (this->eliminating_constraints())
    1167          30 :     this->prolong(snes_x, *x, /*homogeneous=*/false);
    1168             : 
    1169        2568 :   LibmeshPetscCall(SNESGetIterationNumber(_snes, &n_iterations));
    1170             : 
    1171        2568 :   LibmeshPetscCall(SNESGetLinearSolveIterations(_snes, &_n_linear_iterations));
    1172             : 
    1173             :   // SNESGetFunction has been around forever and should work on all
    1174             :   // versions of PETSc.  This is also now the recommended approach
    1175             :   // according to the documentation for the PETSc 3.5.1 release:
    1176             :   // http://www.mcs.anl.gov/petsc/documentation/changes/35.html
    1177             :   Vec f;
    1178        2568 :   LibmeshPetscCall(SNESGetFunction(_snes, &f, 0, 0));
    1179        2568 :   LibmeshPetscCall(VecNorm(f, NORM_2, pPR(&final_residual_norm)));
    1180             : 
    1181             :   // Get and store the reason for convergence
    1182        2568 :   LibmeshPetscCall(SNESGetConvergedReason(_snes, &_reason));
    1183             : 
    1184             :   //Based on Petsc 2.3.3 documentation all diverged reasons are negative
    1185        2568 :   this->converged = (_reason >= 0);
    1186             : 
    1187             :   // Reset data structure
    1188        2568 :   this->clear();
    1189             : 
    1190             :   // return the # of its. and the final residual norm.
    1191        5992 :   return std::make_pair(n_iterations, final_residual_norm);
    1192             : }
    1193             : 
    1194             : 
    1195             : 
    1196             : template <typename T>
    1197          24 : void PetscNonlinearSolver<T>::print_converged_reason()
    1198             : {
    1199             : 
    1200           8 :   libMesh::out << "Nonlinear solver convergence/divergence reason: "
    1201          24 :                << SNESConvergedReasons[this->get_converged_reason()] << std::endl;
    1202          24 : }
    1203             : 
    1204             : 
    1205             : 
    1206             : template <typename T>
    1207          24 : SNESConvergedReason PetscNonlinearSolver<T>::get_converged_reason()
    1208             : {
    1209          24 :   if (this->initialized())
    1210           0 :     LibmeshPetscCall(SNESGetConvergedReason(_snes, &_reason));
    1211             : 
    1212          24 :   return _reason;
    1213             : }
    1214             : 
    1215             : template <typename T>
    1216           0 : int PetscNonlinearSolver<T>::get_total_linear_iterations()
    1217             : {
    1218           0 :   return _n_linear_iterations;
    1219             : }
    1220             : 
    1221             : template <typename T>
    1222        2574 : void PetscNonlinearSolver<T>::setup_default_monitor()
    1223             : {
    1224        2574 :   if (_default_monitor)
    1225        2574 :     LibmeshPetscCall(
    1226             :       SNESMonitorSet(_snes, libmesh_petsc_snes_monitor, this, LIBMESH_PETSC_NULLPTR));
    1227        2574 : }
    1228             : 
    1229             : template <typename T>
    1230        7704 : bool PetscNonlinearSolver<T>::reuse_preconditioner() const
    1231             : {
    1232        7704 :   return this->_reuse_preconditioner;
    1233             : }
    1234             : 
    1235             : template <typename T>
    1236           0 : unsigned int PetscNonlinearSolver<T>::reuse_preconditioner_max_linear_its() const
    1237             : {
    1238           0 :   return this->_reuse_preconditioner_max_linear_its;
    1239             : }
    1240             : 
    1241             : template <typename T>
    1242           0 : void PetscNonlinearSolver<T>::force_new_preconditioner()
    1243             : {
    1244             :   // Easiest way is just to clear everything out
    1245           0 :   this->_is_initialized = false;
    1246           0 :   _snes.destroy();
    1247           0 :   _setup_reuse = false;
    1248           0 : }
    1249             : 
    1250             : template <typename T>
    1251        2568 : void PetscNonlinearSolver<T>::setup_constraint_elimination()
    1252             : {
    1253        2568 :   _retained_is.destroy();
    1254             : 
    1255        1712 :   const DofMap & dof_map = this->system().get_dof_map();
    1256        2568 :   if (!dof_map.has_constraint_elimination())
    1257        2538 :     return;
    1258             : 
    1259          10 :   const auto & retained = dof_map.get_constraint_elimination().local_retained_dofs();
    1260          40 :   std::vector<PetscInt> indices(retained.begin(), retained.end());
    1261          40 :   LibmeshPetscCall(ISCreateGeneral(this->comm().get(),
    1262             :                                    cast_int<PetscInt>(indices.size()),
    1263             :                                    indices.data(),
    1264             :                                    PETSC_COPY_VALUES,
    1265             :                                    _retained_is.get()));
    1266             : }
    1267             : 
    1268             : template <typename T>
    1269       20150 : void PetscNonlinearSolver<T>::localize_solution(Vec x)
    1270             : {
    1271       11328 :   sys_type & sys = this->system();
    1272             : 
    1273       20150 :   if (this->eliminating_constraints())
    1274             :     {
    1275             :       // x is the reduced iterate y; the system solution becomes C y + h
    1276         252 :       this->prolong(x, *sys.solution, /*homogeneous=*/false);
    1277         252 :       sys.update();
    1278         252 :       return;
    1279             :     }
    1280             : 
    1281        5784 :   PetscVector<T> & X_sys = *cast_ptr<PetscVector<T> *>(sys.solution.get());
    1282       31058 :   PetscVector<T> X_global(x, sys.comm());
    1283             : 
    1284             :   // Use the system's update() to get a good local version of the
    1285             :   // parallel solution.  This operation does not modify the incoming
    1286             :   // "x" vector, it only localizes information from "x" into
    1287             :   // sys.current_local_solution.
    1288       19898 :   X_global.swap(X_sys);
    1289       19898 :   sys.update();
    1290       19898 :   X_global.swap(X_sys);
    1291             : 
    1292             :   // Enforce constraints (if any) exactly on the
    1293             :   // current_local_solution.  This is the solution vector that is
    1294             :   // actually used in residual and Jacobian evaluations, and is
    1295             :   // not locked by debug-enabled PETSc the way that "x" is.
    1296       19898 :   if (this->_exact_constraint_enforcement)
    1297       19898 :     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
    1298        8738 : }
    1299             : 
    1300             : template <typename T>
    1301         282 : void PetscNonlinearSolver<T>::prolong(Vec y, NumericVector<T> & x, const bool homogeneous) const
    1302             : {
    1303          94 :   libmesh_assert(this->eliminating_constraints());
    1304          94 :   auto & petsc_x = cast_ref<PetscVector<T> &>(x);
    1305          94 :   libmesh_assert_equal_to(petsc_x.type(), PARALLEL);
    1306             : 
    1307             :   // The constrained entries of x are overwritten below, so their prior values do not matter
    1308         282 :   LibmeshPetscCall(VecISCopy(petsc_x.vec(), _retained_is, SCATTER_FORWARD, y));
    1309         282 :   x.close();
    1310         376 :   this->system().get_dof_map().enforce_constraints_exactly(this->system(), &x, homogeneous);
    1311         282 : }
    1312             : 
    1313             : template <typename T>
    1314         300 : void PetscNonlinearSolver<T>::restrict_vector(const NumericVector<T> & x, Vec y) const
    1315             : {
    1316         100 :   libmesh_assert(this->eliminating_constraints());
    1317         100 :   const auto & petsc_x = cast_ref<const PetscVector<T> &>(x);
    1318         300 :   LibmeshPetscCall(VecISCopy(petsc_x.vec(), _retained_is, SCATTER_REVERSE, y));
    1319         300 : }
    1320             : 
    1321             : //------------------------------------------------------------------
    1322             : // Explicit instantiations
    1323             : template class LIBMESH_EXPORT PetscNonlinearSolver<Number>;
    1324             : 
    1325             : } // namespace libMesh
    1326             : 
    1327             : 
    1328             : 
    1329             : #endif // #ifdef LIBMESH_HAVE_PETSC

Generated by: LCOV version 1.14