LCOV - code coverage report
Current view: top level - src/utils - PetscSupport.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 430 556 77.3 %
Date: 2025-08-08 20:01:16 Functions: 43 50 86.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "PetscSupport.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "MooseApp.h"
      14             : #include "FEProblem.h"
      15             : #include "DisplacedProblem.h"
      16             : #include "NonlinearSystem.h"
      17             : #include "LinearSystem.h"
      18             : #include "DisplacedProblem.h"
      19             : #include "PenetrationLocator.h"
      20             : #include "NearestNodeLocator.h"
      21             : #include "MooseTypes.h"
      22             : #include "MooseUtils.h"
      23             : #include "CommandLine.h"
      24             : #include "Console.h"
      25             : #include "MultiMooseEnum.h"
      26             : #include "Conversion.h"
      27             : #include "Executioner.h"
      28             : #include "MooseMesh.h"
      29             : #include "ComputeLineSearchObjectWrapper.h"
      30             : #include "Convergence.h"
      31             : #include "ParallelParamObject.h"
      32             : 
      33             : #include "libmesh/equation_systems.h"
      34             : #include "libmesh/linear_implicit_system.h"
      35             : #include "libmesh/nonlinear_implicit_system.h"
      36             : #include "libmesh/petsc_linear_solver.h"
      37             : #include "libmesh/petsc_matrix.h"
      38             : #include "libmesh/petsc_nonlinear_solver.h"
      39             : #include "libmesh/petsc_preconditioner.h"
      40             : #include "libmesh/petsc_vector.h"
      41             : #include "libmesh/sparse_matrix.h"
      42             : #include "libmesh/petsc_solver_exception.h"
      43             : #include "libmesh/simple_range.h"
      44             : 
      45             : // PETSc includes
      46             : #include <petsc.h>
      47             : #include <petscsnes.h>
      48             : #include <petscksp.h>
      49             : 
      50             : // For graph coloring
      51             : #include <petscmat.h>
      52             : #include <petscis.h>
      53             : #include <petscdm.h>
      54             : 
      55             : // PetscDMMoose include
      56             : #include "PetscDMMoose.h"
      57             : 
      58             : // Standard includes
      59             : #include <ostream>
      60             : #include <fstream>
      61             : #include <string>
      62             : 
      63             : using namespace libMesh;
      64             : 
      65             : void
      66           0 : MooseVecView(NumericVector<Number> & vector)
      67             : {
      68           0 :   PetscVector<Number> & petsc_vec = static_cast<PetscVector<Number> &>(vector);
      69           0 :   LibmeshPetscCallA(vector.comm().get(), VecView(petsc_vec.vec(), 0));
      70           0 : }
      71             : 
      72             : void
      73           0 : MooseMatView(SparseMatrix<Number> & mat)
      74             : {
      75           0 :   PetscMatrixBase<Number> & petsc_mat = static_cast<PetscMatrix<Number> &>(mat);
      76           0 :   LibmeshPetscCallA(mat.comm().get(), MatView(petsc_mat.mat(), 0));
      77           0 : }
      78             : 
      79             : void
      80           0 : MooseVecView(const NumericVector<Number> & vector)
      81             : {
      82           0 :   PetscVector<Number> & petsc_vec =
      83             :       static_cast<PetscVector<Number> &>(const_cast<NumericVector<Number> &>(vector));
      84           0 :   LibmeshPetscCallA(vector.comm().get(), VecView(petsc_vec.vec(), 0));
      85           0 : }
      86             : 
      87             : void
      88           0 : MooseMatView(const SparseMatrix<Number> & mat)
      89             : {
      90           0 :   PetscMatrixBase<Number> & petsc_mat =
      91             :       static_cast<PetscMatrix<Number> &>(const_cast<SparseMatrix<Number> &>(mat));
      92           0 :   LibmeshPetscCallA(mat.comm().get(), MatView(petsc_mat.mat(), 0));
      93           0 : }
      94             : 
      95             : namespace Moose
      96             : {
      97             : namespace PetscSupport
      98             : {
      99             : 
     100             : std::string
     101        3001 : stringify(const LineSearchType & t)
     102             : {
     103        3001 :   switch (t)
     104             :   {
     105        2906 :     case LS_BASIC:
     106        2906 :       return "basic";
     107           0 :     case LS_DEFAULT:
     108           0 :       return "default";
     109           0 :     case LS_NONE:
     110           0 :       return "none";
     111           0 :     case LS_SHELL:
     112           0 :       return "shell";
     113           0 :     case LS_L2:
     114           0 :       return "l2";
     115          23 :     case LS_BT:
     116          23 :       return "bt";
     117          72 :     case LS_CP:
     118          72 :       return "cp";
     119           0 :     case LS_CONTACT:
     120           0 :       return "contact";
     121           0 :     case LS_PROJECT:
     122           0 :       return "project";
     123           0 :     case LS_INVALID:
     124           0 :       mooseError("Invalid LineSearchType");
     125             :   }
     126           0 :   return "";
     127             : }
     128             : 
     129             : std::string
     130       29067 : stringify(const MffdType & t)
     131             : {
     132       29067 :   switch (t)
     133             :   {
     134       29057 :     case MFFD_WP:
     135       29057 :       return "wp";
     136          10 :     case MFFD_DS:
     137          10 :       return "ds";
     138           0 :     case MFFD_INVALID:
     139           0 :       mooseError("Invalid MffdType");
     140             :   }
     141           0 :   return "";
     142             : }
     143             : 
     144             : void
     145       42863 : setSolverOptions(const SolverParams & solver_params, const MultiMooseEnum & dont_add_these_options)
     146             : {
     147             :   // set PETSc options implied by a solve type
     148       42863 :   switch (solver_params._type)
     149             :   {
     150       28945 :     case Moose::ST_PJFNK:
     151       86835 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     152       57890 :                                         solver_params._prefix + "snes_mf_operator");
     153       28945 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     154       57890 :                                         solver_params._prefix + "mat_mffd_type",
     155       57890 :                                         stringify(solver_params._mffd_type));
     156       28945 :       break;
     157             : 
     158         122 :     case Moose::ST_JFNK:
     159         122 :       setSinglePetscOptionIfAppropriate(dont_add_these_options, solver_params._prefix + "snes_mf");
     160         122 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     161         244 :                                         solver_params._prefix + "mat_mffd_type",
     162         244 :                                         stringify(solver_params._mffd_type));
     163         122 :       break;
     164             : 
     165       11504 :     case Moose::ST_NEWTON:
     166       11504 :       break;
     167             : 
     168           0 :     case Moose::ST_FD:
     169           0 :       setSinglePetscOptionIfAppropriate(dont_add_these_options, solver_params._prefix + "snes_fd");
     170           0 :       break;
     171             : 
     172        2292 :     case Moose::ST_LINEAR:
     173        6876 :       setSinglePetscOptionIfAppropriate(
     174        4584 :           dont_add_these_options, solver_params._prefix + "snes_type", "ksponly");
     175        6876 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     176        4584 :                                         solver_params._prefix + "snes_monitor_cancel");
     177        2292 :       break;
     178             :   }
     179             : 
     180       42863 :   Moose::LineSearchType ls_type = solver_params._line_search;
     181       42863 :   if (ls_type == Moose::LS_NONE)
     182        2752 :     ls_type = Moose::LS_BASIC;
     183             : 
     184       42863 :   if (ls_type != Moose::LS_DEFAULT && ls_type != Moose::LS_CONTACT && ls_type != Moose::LS_PROJECT)
     185        3001 :     setSinglePetscOptionIfAppropriate(
     186        6002 :         dont_add_these_options, solver_params._prefix + "snes_linesearch_type", stringify(ls_type));
     187       42863 : }
     188             : 
     189             : void
     190         105 : petscSetupDM(NonlinearSystemBase & nl, const std::string & dm_name)
     191             : {
     192             :   PetscBool ismoose;
     193         105 :   DM dm = LIBMESH_PETSC_NULLPTR;
     194             : 
     195             :   // Initialize the part of the DM package that's packaged with Moose; in the PETSc source tree this
     196             :   // call would be in DMInitializePackage()
     197         105 :   LibmeshPetscCallA(nl.comm().get(), DMMooseRegisterAll());
     198             :   // Create and set up the DM that will consume the split options and deal with block matrices.
     199             :   PetscNonlinearSolver<Number> * petsc_solver =
     200         105 :       dynamic_cast<PetscNonlinearSolver<Number> *>(nl.nonlinearSolver());
     201         105 :   const char * snes_prefix = nullptr;
     202         105 :   std::string snes_prefix_str;
     203         105 :   if (nl.system().prefix_with_name())
     204             :   {
     205           0 :     snes_prefix_str = nl.system().prefix();
     206           0 :     snes_prefix = snes_prefix_str.c_str();
     207             :   }
     208         105 :   SNES snes = petsc_solver->snes(snes_prefix);
     209             :   // if there exists a DMMoose object, not to recreate a new one
     210         105 :   LibmeshPetscCallA(nl.comm().get(), SNESGetDM(snes, &dm));
     211         105 :   if (dm)
     212             :   {
     213         105 :     LibmeshPetscCallA(nl.comm().get(), PetscObjectTypeCompare((PetscObject)dm, DMMOOSE, &ismoose));
     214         105 :     if (ismoose)
     215           0 :       return;
     216             :   }
     217         105 :   LibmeshPetscCallA(nl.comm().get(), DMCreateMoose(nl.comm().get(), nl, dm_name, &dm));
     218         105 :   LibmeshPetscCallA(nl.comm().get(), DMSetFromOptions(dm));
     219         105 :   LibmeshPetscCallA(nl.comm().get(), DMSetUp(dm));
     220         105 :   LibmeshPetscCallA(nl.comm().get(), SNESSetDM(snes, dm));
     221         105 :   LibmeshPetscCallA(nl.comm().get(), DMDestroy(&dm));
     222             :   // We temporarily comment out this updating function because
     223             :   // we lack an approach to check if the problem
     224             :   // structure has been changed from the last iteration.
     225             :   // The indices will be rebuilt for every timestep.
     226             :   // TODO: figure out a way to check the structure changes of the
     227             :   // matrix
     228             :   // ierr = SNESSetUpdate(snes,SNESUpdateDMMoose);
     229             :   // CHKERRABORT(nl.comm().get(),ierr);
     230         105 : }
     231             : 
     232             : void
     233       43211 : addPetscOptionsFromCommandline()
     234             : {
     235             :   // commandline options always win
     236             :   // the options from a user commandline will overwrite the existing ones if any conflicts
     237             :   { // Get any options specified on the command-line
     238             :     int argc;
     239             :     char ** args;
     240             : 
     241       43211 :     LibmeshPetscCallA(PETSC_COMM_WORLD, PetscGetArgs(&argc, &args));
     242             : #if PETSC_VERSION_LESS_THAN(3, 7, 0)
     243             :     LibmeshPetscCallA(PETSC_COMM_WORLD, PetscOptionsInsert(&argc, &args, NULL));
     244             : #else
     245       43211 :     LibmeshPetscCallA(PETSC_COMM_WORLD,
     246             :                       PetscOptionsInsert(LIBMESH_PETSC_NULLPTR, &argc, &args, NULL));
     247             : #endif
     248             :   }
     249       43211 : }
     250             : 
     251             : void
     252       42653 : petscSetOptionsHelper(const PetscOptions & po, FEProblemBase * const problem)
     253             : {
     254             :   // Add any additional options specified in the input file
     255       45251 :   for (const auto & flag : po.flags)
     256             :     // Need to use name method here to pass a str instead of an EnumItem because
     257             :     // we don't care if the id attributes match
     258        2598 :     if (!po.dont_add_these_options.contains(flag.name()) ||
     259           0 :         po.user_set_options.contains(flag.name()))
     260        2598 :       setSinglePetscOption(flag.rawName().c_str());
     261             : 
     262             :   // Add option pairs
     263      180554 :   for (auto & option : po.pairs)
     264      139424 :     if (!po.dont_add_these_options.contains(option.first) ||
     265        1523 :         po.user_set_options.contains(option.first))
     266      136388 :       setSinglePetscOption(option.first, option.second, problem);
     267             : 
     268       42653 :   addPetscOptionsFromCommandline();
     269       42653 : }
     270             : 
     271             : void
     272        1815 : petscSetOptions(const PetscOptions & po,
     273             :                 const SolverParams & solver_params,
     274             :                 FEProblemBase * const problem)
     275             : {
     276        1815 :   PetscCallAbort(PETSC_COMM_WORLD, PetscOptionsClear(LIBMESH_PETSC_NULLPTR));
     277        1815 :   setSolverOptions(solver_params, po.dont_add_these_options);
     278        1815 :   petscSetOptionsHelper(po, problem);
     279        1815 : }
     280             : 
     281             : void
     282       40838 : petscSetOptions(const PetscOptions & po,
     283             :                 const std::vector<SolverParams> & solver_params_vec,
     284             :                 FEProblemBase * const problem)
     285             : {
     286       40838 :   PetscCallAbort(PETSC_COMM_WORLD, PetscOptionsClear(LIBMESH_PETSC_NULLPTR));
     287       81886 :   for (const auto & solver_params : solver_params_vec)
     288       41048 :     setSolverOptions(solver_params, po.dont_add_these_options);
     289       40838 :   petscSetOptionsHelper(po, problem);
     290       40838 : }
     291             : 
     292             : PetscErrorCode
     293       48118 : petscSetupOutput(CommandLine * cmd_line)
     294             : {
     295             :   PetscFunctionBegin;
     296       48118 :   char code[10] = {45, 45, 109, 111, 111, 115, 101};
     297       48118 :   const std::vector<std::string> argv = cmd_line->getArguments();
     298      506842 :   for (const auto & arg : argv)
     299             :   {
     300      458724 :     if (arg.compare(code) == 0)
     301             :     {
     302           0 :       Console::petscSetupOutput();
     303           0 :       break;
     304             :     }
     305             :   }
     306       48118 :   PetscFunctionReturn(PETSC_SUCCESS);
     307       48118 : }
     308             : 
     309             : PetscErrorCode
     310      812780 : petscNonlinearConverged(SNES /*snes*/,
     311             :                         PetscInt it,
     312             :                         PetscReal /*xnorm*/,
     313             :                         PetscReal /*snorm*/,
     314             :                         PetscReal /*fnorm*/,
     315             :                         SNESConvergedReason * reason,
     316             :                         void * ctx)
     317             : {
     318             :   PetscFunctionBegin;
     319      812780 :   FEProblemBase & problem = *static_cast<FEProblemBase *>(ctx);
     320             : 
     321             :   // execute objects that may be used in convergence check
     322      812780 :   problem.execute(EXEC_NONLINEAR_CONVERGENCE);
     323             : 
     324             :   // perform the convergence check
     325             :   Convergence::MooseConvergenceStatus status;
     326      812780 :   if (problem.getFailNextNonlinearConvergenceCheck())
     327             :   {
     328         212 :     status = Convergence::MooseConvergenceStatus::DIVERGED;
     329         212 :     problem.resetFailNextNonlinearConvergenceCheck();
     330             :   }
     331             :   else
     332             :   {
     333      812568 :     auto & convergence = problem.getConvergence(
     334      812568 :         problem.getNonlinearConvergenceNames()[problem.currentNonlinearSystem().number()]);
     335      812568 :     status = convergence.checkConvergence(it);
     336             :   }
     337             : 
     338             :   // convert convergence status to PETSc converged reason
     339      812776 :   switch (status)
     340             :   {
     341      506175 :     case Convergence::MooseConvergenceStatus::ITERATING:
     342      506175 :       *reason = SNES_CONVERGED_ITERATING;
     343      506175 :       break;
     344             : 
     345      306337 :     case Convergence::MooseConvergenceStatus::CONVERGED:
     346      306337 :       *reason = SNES_CONVERGED_FNORM_ABS;
     347      306337 :       break;
     348             : 
     349         264 :     case Convergence::MooseConvergenceStatus::DIVERGED:
     350         264 :       *reason = SNES_DIVERGED_DTOL;
     351         264 :       break;
     352             :   }
     353             : 
     354      812776 :   PetscFunctionReturn(PETSC_SUCCESS);
     355             : }
     356             : 
     357             : PetscErrorCode
     358        2634 : petscLinearConverged(
     359             :     KSP /*ksp*/, PetscInt it, PetscReal /*norm*/, KSPConvergedReason * reason, void * ctx)
     360             : {
     361             :   PetscFunctionBegin;
     362        2634 :   FEProblemBase & problem = *static_cast<FEProblemBase *>(ctx);
     363             : 
     364             :   // execute objects that may be used in convergence check
     365             :   // Right now, setting objects to execute on this flag would be ignored except in the
     366             :   // linear-system-only use case.
     367        2634 :   problem.execute(EXEC_LINEAR_CONVERGENCE);
     368             : 
     369             :   // perform the convergence check
     370             :   Convergence::MooseConvergenceStatus status;
     371        2634 :   if (problem.getFailNextSystemConvergenceCheck())
     372             :   {
     373           0 :     status = Convergence::MooseConvergenceStatus::DIVERGED;
     374           0 :     problem.resetFailNextSystemConvergenceCheck();
     375             :   }
     376             :   else
     377             :   {
     378        2634 :     auto & convergence = problem.getConvergence(
     379        2634 :         problem.getLinearConvergenceNames()[problem.currentLinearSystem().number()]);
     380        2634 :     status = convergence.checkConvergence(it);
     381             :   }
     382             : 
     383             :   // convert convergence status to PETSc converged reason
     384        2634 :   switch (status)
     385             :   {
     386        2201 :     case Convergence::MooseConvergenceStatus::ITERATING:
     387        2201 :       *reason = KSP_CONVERGED_ITERATING;
     388        2201 :       break;
     389             : 
     390             :       // TODO: find a KSP code that works better for this case
     391         433 :     case Convergence::MooseConvergenceStatus::CONVERGED:
     392         433 :       *reason = KSP_CONVERGED_RTOL_NORMAL;
     393         433 :       break;
     394             : 
     395           0 :     case Convergence::MooseConvergenceStatus::DIVERGED:
     396           0 :       *reason = KSP_DIVERGED_DTOL;
     397           0 :       break;
     398             :   }
     399             : 
     400        2634 :   PetscFunctionReturn(PETSC_SUCCESS);
     401             : }
     402             : 
     403             : PCSide
     404           0 : getPetscPCSide(Moose::PCSideType pcs)
     405             : {
     406           0 :   switch (pcs)
     407             :   {
     408           0 :     case Moose::PCS_LEFT:
     409           0 :       return PC_LEFT;
     410           0 :     case Moose::PCS_RIGHT:
     411           0 :       return PC_RIGHT;
     412           0 :     case Moose::PCS_SYMMETRIC:
     413           0 :       return PC_SYMMETRIC;
     414           0 :     default:
     415           0 :       mooseError("Unknown PC side requested.");
     416             :       break;
     417             :   }
     418             : }
     419             : 
     420             : KSPNormType
     421      403395 : getPetscKSPNormType(Moose::MooseKSPNormType kspnorm)
     422             : {
     423      403395 :   switch (kspnorm)
     424             :   {
     425           0 :     case Moose::KSPN_NONE:
     426           0 :       return KSP_NORM_NONE;
     427           0 :     case Moose::KSPN_PRECONDITIONED:
     428           0 :       return KSP_NORM_PRECONDITIONED;
     429      403395 :     case Moose::KSPN_UNPRECONDITIONED:
     430      403395 :       return KSP_NORM_UNPRECONDITIONED;
     431           0 :     case Moose::KSPN_NATURAL:
     432           0 :       return KSP_NORM_NATURAL;
     433           0 :     case Moose::KSPN_DEFAULT:
     434           0 :       return KSP_NORM_DEFAULT;
     435           0 :     default:
     436           0 :       mooseError("Unknown KSP norm type requested.");
     437             :       break;
     438             :   }
     439             : }
     440             : 
     441             : void
     442      377409 : petscSetDefaultKSPNormType(FEProblemBase & problem, KSP ksp)
     443             : {
     444      780804 :   for (const auto i : make_range(problem.numSolverSystems()))
     445             :   {
     446      403395 :     SolverSystem & sys = problem.getSolverSystem(i);
     447      403395 :     LibmeshPetscCallA(problem.comm().get(),
     448             :                       KSPSetNormType(ksp, getPetscKSPNormType(sys.getMooseKSPNormType())));
     449             :   }
     450      377409 : }
     451             : 
     452             : void
     453      377409 : petscSetDefaultPCSide(FEProblemBase & problem, KSP ksp)
     454             : {
     455      780804 :   for (const auto i : make_range(problem.numSolverSystems()))
     456             :   {
     457      403395 :     SolverSystem & sys = problem.getSolverSystem(i);
     458             : 
     459             :     // PETSc 3.2.x+
     460      403395 :     if (sys.getPCSide() != Moose::PCS_DEFAULT)
     461           0 :       LibmeshPetscCallA(problem.comm().get(), KSPSetPCSide(ksp, getPetscPCSide(sys.getPCSide())));
     462             :   }
     463      377409 : }
     464             : 
     465             : void
     466      376163 : petscSetKSPDefaults(FEProblemBase & problem, KSP ksp)
     467             : {
     468      376163 :   auto & es = problem.es();
     469             : 
     470      376163 :   PetscReal rtol = es.parameters.get<Real>("linear solver tolerance");
     471      376163 :   PetscReal atol = es.parameters.get<Real>("linear solver absolute tolerance");
     472             : 
     473             :   // MOOSE defaults this to -1 for some dumb reason
     474      376163 :   if (atol < 0)
     475           0 :     atol = 1e-50;
     476             : 
     477      376163 :   PetscReal maxits = es.parameters.get<unsigned int>("linear solver maximum iterations");
     478             : 
     479             :   // 1e100 is because we don't use divtol currently
     480      376163 :   LibmeshPetscCallA(problem.comm().get(), KSPSetTolerances(ksp, rtol, atol, 1e100, maxits));
     481             : 
     482      376163 :   petscSetDefaultPCSide(problem, ksp);
     483             : 
     484      376163 :   petscSetDefaultKSPNormType(problem, ksp);
     485      376163 : }
     486             : 
     487             : void
     488      368162 : petscSetDefaults(FEProblemBase & problem)
     489             : {
     490             :   // We care about both nonlinear and linear systems when setting the SNES prefix because
     491             :   // SNESSetOptionsPrefix will also set its KSP prefix which could compete with linear system KSPs
     492      744005 :   for (const auto nl_index : make_range(problem.numNonlinearSystems()))
     493             :   {
     494      375843 :     NonlinearSystemBase & nl = problem.getNonlinearSystemBase(nl_index);
     495             : 
     496             :     //
     497             :     // prefix system matrices
     498             :     //
     499             : 
     500      375843 :     auto & lm_sys = nl.system();
     501             :     // This check is necessary because we still have at least the system matrix lying around even
     502             :     // when doing matrix-free, but critically even though the libmesh object(s) exist, the wrapped
     503             :     // PETSc Mat(s) do not
     504      375843 :     if (problem.solverParams(nl_index)._type != Moose::ST_JFNK)
     505      752416 :       for (auto & [mat_name, mat] : as_range(lm_sys.matrices_begin(), lm_sys.matrices_end()))
     506             :       {
     507      376706 :         libmesh_ignore(mat_name);
     508      376706 :         if (auto * const petsc_mat = dynamic_cast<PetscMatrixBase<Number> *>(mat.get()); petsc_mat)
     509             :         {
     510      376706 :           LibmeshPetscCallA(nl.comm().get(),
     511             :                             MatSetOptionsPrefix(petsc_mat->mat(), (nl.name() + "_").c_str()));
     512             :           // We should call this here to ensure that options from the command line are properly
     513             :           // applied
     514      376706 :           LibmeshPetscCallA(nl.comm().get(), MatSetFromOptions(petsc_mat->mat()));
     515             :         }
     516             :       }
     517             : 
     518             :     //
     519             :     // prefix SNES/KSP
     520             :     //
     521             : 
     522             :     // dig out PETSc solver
     523      375843 :     auto * const petsc_solver = cast_ptr<PetscNonlinearSolver<Number> *>(nl.nonlinearSolver());
     524      375843 :     const char * snes_prefix = nullptr;
     525      375843 :     std::string snes_prefix_str;
     526      375843 :     if (nl.system().prefix_with_name())
     527             :     {
     528       25986 :       snes_prefix_str = nl.system().prefix();
     529       25986 :       snes_prefix = snes_prefix_str.c_str();
     530             :     }
     531      375843 :     SNES snes = petsc_solver->snes(snes_prefix);
     532             :     KSP ksp;
     533      375843 :     LibmeshPetscCallA(nl.comm().get(), SNESGetKSP(snes, &ksp));
     534             : 
     535      375843 :     LibmeshPetscCallA(nl.comm().get(), SNESSetMaxLinearSolveFailures(snes, 1000000));
     536             : 
     537      375843 :     LibmeshPetscCallA(nl.comm().get(), SNESSetCheckJacobianDomainError(snes, PETSC_TRUE));
     538             : 
     539             :     // In 3.0.0, the context pointer must actually be used, and the
     540             :     // final argument to KSPSetConvergenceTest() is a pointer to a
     541             :     // routine for destroying said private data context.  In this case,
     542             :     // we use the default context provided by PETSc in addition to
     543             :     // a few other tests.
     544             :     {
     545      375843 :       LibmeshPetscCallA(
     546             :           nl.comm().get(),
     547             :           SNESSetConvergenceTest(snes, petscNonlinearConverged, &problem, LIBMESH_PETSC_NULLPTR));
     548             :     }
     549             : 
     550      375843 :     petscSetKSPDefaults(problem, ksp);
     551      375843 :   }
     552             : 
     553      373587 :   for (auto sys_index : make_range(problem.numLinearSystems()))
     554             :   {
     555             :     // dig out PETSc solver
     556        5425 :     LinearSystem & lin_sys = problem.getLinearSystem(sys_index);
     557        5425 :     PetscLinearSolver<Number> * petsc_solver = dynamic_cast<PetscLinearSolver<Number> *>(
     558        5425 :         lin_sys.linearImplicitSystem().get_linear_solver());
     559        5425 :     KSP ksp = petsc_solver->ksp();
     560             : 
     561        5425 :     if (problem.hasLinearConvergenceObjects())
     562         447 :       LibmeshPetscCallA(
     563             :           lin_sys.comm().get(),
     564             :           KSPSetConvergenceTest(ksp, petscLinearConverged, &problem, LIBMESH_PETSC_NULLPTR));
     565             : 
     566             :     // We dont set the KSP defaults here because they seem to clash with the linear solve parameters
     567             :     // set in FEProblemBase::solveLinearSystem
     568             :   }
     569      368162 : }
     570             : 
     571             : void
     572       76672 : processSingletonMooseWrappedOptions(FEProblemBase & fe_problem, const InputParameters & params)
     573             : {
     574       76672 :   setSolveTypeFromParams(fe_problem, params);
     575       76672 :   setLineSearchFromParams(fe_problem, params);
     576       76672 :   setMFFDTypeFromParams(fe_problem, params);
     577       76672 : }
     578             : 
     579             : #define checkPrefix(prefix)                                                                        \
     580             :   mooseAssert(prefix[0] == '-',                                                                    \
     581             :               "Leading prefix character must be a '-'. Current prefix is '" << prefix << "'");     \
     582             :   mooseAssert((prefix.size() == 1) || (prefix.back() == '_'),                                      \
     583             :               "Terminating prefix character must be a '_'. Current prefix is '" << prefix << "'"); \
     584             :   mooseAssert(MooseUtils::isAllLowercase(prefix), "PETSc prefixes should be all lower-case")
     585             : 
     586             : void
     587       76672 : storePetscOptions(FEProblemBase & fe_problem,
     588             :                   const std::string & prefix,
     589             :                   const ParallelParamObject & param_object)
     590             : {
     591       76672 :   const auto & params = param_object.parameters();
     592       76672 :   processSingletonMooseWrappedOptions(fe_problem, params);
     593             : 
     594             :   // The parameters contained in the Action
     595       76672 :   const auto & petsc_options = params.get<MultiMooseEnum>("petsc_options");
     596             :   const auto & petsc_pair_options =
     597       76672 :       params.get<MooseEnumItem, std::string>("petsc_options_iname", "petsc_options_value");
     598             : 
     599             :   // A reference to the PetscOptions object that contains the settings that will be used in the
     600             :   // solve
     601       76672 :   auto & po = fe_problem.getPetscOptions();
     602             : 
     603             :   // First process the single petsc options/flags
     604       76672 :   addPetscFlagsToPetscOptions(petsc_options, prefix, param_object, po);
     605             : 
     606             :   // Then process the option-value pairs
     607       76672 :   addPetscPairsToPetscOptions(
     608       76672 :       petsc_pair_options, fe_problem.mesh().dimension(), prefix, param_object, po);
     609       76672 : }
     610             : 
     611             : void
     612       91098 : setSolveTypeFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     613             : {
     614             :   // Note: Options set in the Preconditioner block will override those set in the Executioner block
     615       91098 :   if (params.isParamValid("solve_type") && !params.isParamValid("_use_eigen_value"))
     616             :   {
     617             :     // Extract the solve type
     618       37492 :     const std::string & solve_type = params.get<MooseEnum>("solve_type");
     619       75430 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     620       37938 :       fe_problem.solverParams(i)._type = Moose::stringToEnum<Moose::SolveType>(solve_type);
     621       37492 :   }
     622       91098 : }
     623             : 
     624             : void
     625       76672 : setLineSearchFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     626             : {
     627             :   // Note: Options set in the Preconditioner block will override those set in the Executioner block
     628       76672 :   if (params.isParamValid("line_search"))
     629             :   {
     630       62186 :     const auto & line_search = params.get<MooseEnum>("line_search");
     631      123554 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     632       61368 :       if (fe_problem.solverParams(i)._line_search == Moose::LS_INVALID || line_search != "default")
     633             :       {
     634             :         Moose::LineSearchType enum_line_search =
     635       61008 :             Moose::stringToEnum<Moose::LineSearchType>(line_search);
     636       61008 :         fe_problem.solverParams(i)._line_search = enum_line_search;
     637       61008 :         if (enum_line_search == LS_CONTACT || enum_line_search == LS_PROJECT)
     638             :         {
     639           0 :           NonlinearImplicitSystem * nl_system = dynamic_cast<NonlinearImplicitSystem *>(
     640           0 :               &fe_problem.getNonlinearSystemBase(i).system());
     641           0 :           if (!nl_system)
     642           0 :             mooseError("You've requested a line search but you must be solving an EigenProblem. "
     643             :                        "These two things are not consistent.");
     644             :           PetscNonlinearSolver<Real> * petsc_nonlinear_solver =
     645           0 :               dynamic_cast<PetscNonlinearSolver<Real> *>(nl_system->nonlinear_solver.get());
     646           0 :           if (!petsc_nonlinear_solver)
     647           0 :             mooseError("Currently the MOOSE line searches all use Petsc, so you "
     648             :                        "must use Petsc as your non-linear solver.");
     649             :           petsc_nonlinear_solver->linesearch_object =
     650           0 :               std::make_unique<ComputeLineSearchObjectWrapper>(fe_problem);
     651             :         }
     652             :       }
     653             :   }
     654       76672 : }
     655             : 
     656             : void
     657       76672 : setMFFDTypeFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     658             : {
     659       76672 :   if (params.isParamValid("mffd_type"))
     660             :   {
     661       76209 :     const auto & mffd_type = params.get<MooseEnum>("mffd_type");
     662      151868 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     663       75659 :       fe_problem.solverParams(i)._mffd_type = Moose::stringToEnum<Moose::MffdType>(mffd_type);
     664             :   }
     665       76672 : }
     666             : 
     667             : template <typename T>
     668             : void
     669       59079 : checkUserProvidedPetscOption(const T & option, const ParallelParamObject & param_object)
     670             : {
     671       59079 :   const auto & string_option = static_cast<const std::string &>(option);
     672       59079 :   if (string_option[0] != '-')
     673           0 :     param_object.mooseError("PETSc option '", string_option, "' does not begin with '-'");
     674       59079 : }
     675             : 
     676             : void
     677       76672 : addPetscFlagsToPetscOptions(const MultiMooseEnum & petsc_flags,
     678             :                             const std::string & prefix,
     679             :                             const ParallelParamObject & param_object,
     680             :                             PetscOptions & po)
     681             : {
     682             :   checkPrefix(prefix);
     683             : 
     684             :   // Update the PETSc single flags
     685       79386 :   for (const auto & option : petsc_flags)
     686             :   {
     687        2714 :     checkUserProvidedPetscOption(option, param_object);
     688             : 
     689        2714 :     const std::string & string_option = option.name();
     690             : 
     691             :     /**
     692             :      * "-log_summary" cannot be used in the input file. This option needs to be set when PETSc is
     693             :      * initialized
     694             :      * which happens before the parser is even created.  We'll throw an error if somebody attempts
     695             :      * to add this option later.
     696             :      */
     697        2714 :     if (option == "-log_summary" || option == "-log_view")
     698           0 :       mooseError("The PETSc option \"-log_summary\" or \"-log_view\" can only be used on the "
     699             :                  "command line.  Please "
     700             :                  "remove it from the input file");
     701             : 
     702             :     // Update the stored items, but do not create duplicates
     703        2714 :     const std::string prefixed_option = prefix + string_option.substr(1);
     704        2714 :     if (!po.flags.isValueSet(prefixed_option))
     705             :     {
     706        2714 :       po.flags.setAdditionalValue(prefixed_option);
     707        2714 :       po.user_set_options.setAdditionalValue(prefixed_option);
     708             :     }
     709        2714 :   }
     710       76672 : }
     711             : 
     712             : void
     713       62186 : setConvergedReasonFlags(FEProblemBase & fe_problem, const std::string & prefix)
     714             : {
     715             :   checkPrefix(prefix);
     716       62186 :   libmesh_ignore(fe_problem); // avoid unused warnings for old PETSc
     717             : 
     718             : #if !PETSC_VERSION_LESS_THAN(3, 14, 0)
     719             :   // the boolean in these pairs denote whether the user has specified any of the reason flags in the
     720             :   // input file
     721       62186 :   std::array<std::string, 2> reason_flags = {{"snes_converged_reason", "ksp_converged_reason"}};
     722             : 
     723       62186 :   auto & po = fe_problem.getPetscOptions();
     724             : 
     725      186558 :   for (const auto & reason_flag : reason_flags)
     726      372259 :     if (!po.flags.isValueSet(prefix + reason_flag) &&
     727      123515 :         (std::find_if(po.pairs.begin(),
     728             :                       po.pairs.end(),
     729      175105 :                       [&reason_flag, &prefix](auto & pair)
     730      546507 :                       { return pair.first == (prefix + reason_flag); }) == po.pairs.end()))
     731      123515 :       po.pairs.emplace_back(prefix + reason_flag, "::failed");
     732             : #endif
     733       62186 : }
     734             : 
     735             : void
     736       76910 : addPetscPairsToPetscOptions(
     737             :     const std::vector<std::pair<MooseEnumItem, std::string>> & petsc_pair_options,
     738             :     const unsigned int mesh_dimension,
     739             :     const std::string & prefix,
     740             :     const ParallelParamObject & param_object,
     741             :     PetscOptions & po)
     742             : {
     743             :   checkPrefix(prefix);
     744             : 
     745             :   // Setup the name value pairs
     746       76910 :   bool boomeramg_found = false;
     747       76910 :   bool strong_threshold_found = false;
     748             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     749       76910 :   bool superlu_dist_found = false;
     750       76910 :   bool fact_pattern_found = false;
     751       76910 :   bool tiny_pivot_found = false;
     752             : #endif
     753       76910 :   std::string pc_description = "";
     754             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     755             :   // If users use HMG, we would like to set
     756       76910 :   bool hmg_found = false;
     757       76910 :   bool matptap_found = false;
     758       76910 :   bool hmg_strong_threshold_found = false;
     759             : #endif
     760       76910 :   std::vector<std::pair<std::string, std::string>> new_options;
     761             : 
     762      133275 :   for (const auto & [option_name, option_value] : petsc_pair_options)
     763             :   {
     764       56365 :     checkUserProvidedPetscOption(option_name, param_object);
     765             : 
     766       56365 :     new_options.clear();
     767             :     const std::string prefixed_option_name =
     768       56365 :         prefix + static_cast<const std::string &>(option_name).substr(1);
     769             : 
     770             :     // Do not add duplicate settings
     771       56365 :     if (auto it =
     772       56365 :             MooseUtils::findPair(po.pairs, po.pairs.begin(), prefixed_option_name, MooseUtils::Any);
     773       56365 :         it == po.pairs.end())
     774             :     {
     775             : #if !PETSC_VERSION_LESS_THAN(3, 9, 0)
     776       56245 :       if (option_name == "-pc_factor_mat_solver_package")
     777         156 :         new_options.emplace_back(prefix + "pc_factor_mat_solver_type", option_value);
     778             : #else
     779             :       if (option_name == "-pc_factor_mat_solver_type")
     780             :         new_options.push_back(prefix + "pc_factor_mat_solver_package", option_value);
     781             : #endif
     782             : 
     783             :       // Look for a pc description
     784       84649 :       if (option_name == "-pc_type" || option_name == "-sub_pc_type" ||
     785       28404 :           option_name == "-pc_hypre_type")
     786       49568 :         pc_description += option_value + ' ';
     787             : 
     788             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     789       56245 :       if (option_name == "-pc_type" && option_value == "hmg")
     790          42 :         hmg_found = true;
     791             : 
     792             :         // MPIAIJ for PETSc 3.12.0: -matptap_via
     793             :         // MAIJ for PETSc 3.12.0: -matmaijptap_via
     794             :         // MPIAIJ for PETSc 3.13 to 3.16: -matptap_via, -matproduct_ptap_via
     795             :         // MAIJ for PETSc 3.13 to 3.16: -matproduct_ptap_via
     796             :         // MPIAIJ for PETSc 3.17 and higher: -matptap_via, -mat_product_algorithm
     797             :         // MAIJ for PETSc 3.17 and higher: -mat_product_algorithm
     798             : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
     799       56371 :       if (hmg_found && (option_name == "-matptap_via" || option_name == "-matmaijptap_via" ||
     800         126 :                         option_name == "-matproduct_ptap_via"))
     801           0 :         new_options.emplace_back(prefix + "mat_product_algorithm", option_value);
     802             : #elif !PETSC_VERSION_LESS_THAN(3, 13, 0)
     803             :       if (hmg_found && (option_name == "-matptap_via" || option_name == "-matmaijptap_via"))
     804             :         new_options.emplace_back(prefix + "matproduct_ptap_via", option_value);
     805             : #else
     806             :       if (hmg_found && (option_name == "-matproduct_ptap_via"))
     807             :       {
     808             :         new_options.emplace_back(prefix + "matptap_via", option_value);
     809             :         new_options.emplace_back(prefix + "matmaijptap_via", option_value);
     810             :       }
     811             : #endif
     812             : 
     813      112490 :       if (option_name == "-matptap_via" || option_name == "-matmaijptap_via" ||
     814      112490 :           option_name == "-matproduct_ptap_via" || option_name == "-mat_product_algorithm")
     815           0 :         matptap_found = true;
     816             : 
     817             :       // For 3D problems, we need to set this 0.7
     818       56245 :       if (option_name == "-hmg_inner_pc_hypre_boomeramg_strong_threshold")
     819           0 :         hmg_strong_threshold_found = true;
     820             : #endif
     821             :       // This special case is common enough that we'd like to handle it for the user.
     822       56245 :       if (option_name == "-pc_hypre_type" && option_value == "boomeramg")
     823       21727 :         boomeramg_found = true;
     824       56245 :       if (option_name == "-pc_hypre_boomeramg_strong_threshold")
     825           0 :         strong_threshold_found = true;
     826             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     827       56245 :       if ((option_name == "-pc_factor_mat_solver_package" ||
     828       57206 :            option_name == "-pc_factor_mat_solver_type") &&
     829         961 :           option_value == "superlu_dist")
     830         311 :         superlu_dist_found = true;
     831       56245 :       if (option_name == "-mat_superlu_dist_fact")
     832           0 :         fact_pattern_found = true;
     833       56245 :       if (option_name == "-mat_superlu_dist_replacetinypivot")
     834           0 :         tiny_pivot_found = true;
     835             : #endif
     836             : 
     837       56245 :       if (!new_options.empty())
     838             :       {
     839         156 :         std::copy(new_options.begin(), new_options.end(), std::back_inserter(po.pairs));
     840         312 :         for (const auto & option : new_options)
     841         156 :           po.user_set_options.setAdditionalValue(option.first);
     842             :       }
     843             :       else
     844             :       {
     845       56089 :         po.pairs.push_back(std::make_pair(prefixed_option_name, option_value));
     846       56089 :         po.user_set_options.setAdditionalValue(prefixed_option_name);
     847             :       }
     848             :     }
     849             :     else
     850             :     {
     851             :       do
     852             :       {
     853         120 :         it->second = option_value;
     854         120 :         it = MooseUtils::findPair(po.pairs, std::next(it), prefixed_option_name, MooseUtils::Any);
     855         120 :       } while (it != po.pairs.end());
     856             :     }
     857       56365 :   }
     858             : 
     859             :   // When running a 3D mesh with boomeramg, it is almost always best to supply a strong threshold
     860             :   // value. We will provide that for the user here if they haven't supplied it themselves.
     861       76910 :   if (boomeramg_found && !strong_threshold_found && mesh_dimension == 3)
     862             :   {
     863         984 :     po.pairs.emplace_back(prefix + "pc_hypre_boomeramg_strong_threshold", "0.7");
     864         984 :     pc_description += "strong_threshold: 0.7 (auto)";
     865             :   }
     866             : 
     867             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     868       76910 :   if (hmg_found && !hmg_strong_threshold_found && mesh_dimension == 3)
     869             :   {
     870          28 :     po.pairs.emplace_back(prefix + "hmg_inner_pc_hypre_boomeramg_strong_threshold", "0.7");
     871          28 :     pc_description += "strong_threshold: 0.7 (auto)";
     872             :   }
     873             : 
     874             :   // Default PETSc PtAP takes too much memory, and it is not quite useful
     875             :   // Let us switch to use new algorithm
     876       76910 :   if (hmg_found && !matptap_found)
     877             :   {
     878             : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
     879          42 :     po.pairs.emplace_back(prefix + "mat_product_algorithm", "allatonce");
     880             : #elif !PETSC_VERSION_LESS_THAN(3, 13, 0)
     881             :     po.pairs.emplace_back(prefix + "matproduct_ptap_via", "allatonce");
     882             : #else
     883             :     po.pairs.emplace_back(prefix + "matptap_via", "allatonce");
     884             :     po.pairs.emplace_back(prefix + "matmaijptap_via", "allatonce");
     885             : #endif
     886             :   }
     887             : #endif
     888             : 
     889             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     890             :   // In PETSc-3.7.{0--4}, there is a bug when using superlu_dist, and we have to use
     891             :   // SamePattern_SameRowPerm, otherwise we use whatever we have in PETSc
     892       76910 :   if (superlu_dist_found && !fact_pattern_found)
     893             :   {
     894         311 :     po.pairs.emplace_back(prefix + "mat_superlu_dist_fact",
     895             : #if PETSC_VERSION_LESS_THAN(3, 7, 5)
     896             :                           "SamePattern_SameRowPerm");
     897             :     pc_description += "mat_superlu_dist_fact: SamePattern_SameRowPerm ";
     898             : #else
     899             :                           "SamePattern");
     900         311 :     pc_description += "mat_superlu_dist_fact: SamePattern ";
     901             : #endif
     902             :   }
     903             : 
     904             :   // restore this superlu  option
     905       76910 :   if (superlu_dist_found && !tiny_pivot_found)
     906             :   {
     907         311 :     po.pairs.emplace_back(prefix + "mat_superlu_dist_replacetinypivot", "1");
     908         311 :     pc_description += " mat_superlu_dist_replacetinypivot: true ";
     909             :   }
     910             : #endif
     911             :   // Set Preconditioner description
     912       76910 :   if (!pc_description.empty() && prefix.size() > 1)
     913         706 :     po.pc_description += "[" + prefix.substr(1, prefix.size() - 2) + "]: ";
     914       76910 :   po.pc_description += pc_description;
     915       76910 : }
     916             : 
     917             : std::set<std::string>
     918      295827 : getPetscValidLineSearches()
     919             : {
     920     2662443 :   return {"default", "shell", "none", "basic", "l2", "bt", "cp"};
     921      591654 : }
     922             : 
     923             : InputParameters
     924      410305 : getPetscValidParams()
     925             : {
     926      410305 :   InputParameters params = emptyInputParameters();
     927             : 
     928      410305 :   MooseEnum solve_type("PJFNK JFNK NEWTON FD LINEAR");
     929      410305 :   params.addParam<MooseEnum>("solve_type",
     930             :                              solve_type,
     931             :                              "PJFNK: Preconditioned Jacobian-Free Newton Krylov "
     932             :                              "JFNK: Jacobian-Free Newton Krylov "
     933             :                              "NEWTON: Full Newton Solve "
     934             :                              "FD: Use finite differences to compute Jacobian "
     935             :                              "LINEAR: Solving a linear problem");
     936             : 
     937      410305 :   MooseEnum mffd_type("wp ds", "wp");
     938      410305 :   params.addParam<MooseEnum>("mffd_type",
     939             :                              mffd_type,
     940             :                              "Specifies the finite differencing type for "
     941             :                              "Jacobian-free solve types. Note that the "
     942             :                              "default is wp (for Walker and Pernice).");
     943             : 
     944     1230915 :   params.addParam<MultiMooseEnum>(
     945      820610 :       "petsc_options", getCommonPetscFlags(), "Singleton PETSc options");
     946     1230915 :   params.addParam<MultiMooseEnum>(
     947      820610 :       "petsc_options_iname", getCommonPetscKeys(), "Names of PETSc name/value pairs");
     948      410305 :   params.addParam<std::vector<std::string>>(
     949             :       "petsc_options_value",
     950             :       "Values of PETSc name/value pairs (must correspond with \"petsc_options_iname\"");
     951      410305 :   params.addParamNamesToGroup("solve_type petsc_options petsc_options_iname petsc_options_value "
     952             :                               "mffd_type",
     953             :                               "PETSc");
     954             : 
     955      820610 :   return params;
     956      410305 : }
     957             : 
     958             : MultiMooseEnum
     959      426658 : getCommonSNESFlags()
     960             : {
     961             :   return MultiMooseEnum(
     962             :       "-ksp_monitor_snes_lg -snes_ksp_ew -ksp_snes_ew -snes_converged_reason "
     963             :       "-snes_ksp -snes_linesearch_monitor -snes_mf -snes_mf_operator -snes_monitor "
     964             :       "-snes_test_display -snes_view -snes_monitor_cancel",
     965             :       "",
     966      426658 :       true);
     967             : }
     968             : 
     969             : MultiMooseEnum
     970      425994 : getCommonKSPFlags()
     971             : {
     972             :   return MultiMooseEnum(
     973      425994 :       "-ksp_converged_reason -ksp_gmres_modifiedgramschmidt -ksp_monitor", "", true);
     974             : }
     975             : 
     976             : MultiMooseEnum
     977      425994 : getCommonPetscFlags()
     978             : {
     979      425994 :   auto options = MultiMooseEnum("-dm_moose_print_embedding -dm_view", "", true);
     980      425994 :   options.addValidName(getCommonKSPFlags());
     981      425994 :   options.addValidName(getCommonSNESFlags());
     982      425994 :   return options;
     983           0 : }
     984             : 
     985             : MultiMooseEnum
     986      426658 : getCommonSNESKeys()
     987             : {
     988             :   return MultiMooseEnum("-snes_atol -snes_linesearch_type -snes_ls -snes_max_it -snes_rtol "
     989             :                         "-snes_divergence_tolerance -snes_type",
     990             :                         "",
     991      426658 :                         true);
     992             : }
     993             : 
     994             : MultiMooseEnum
     995      425994 : getCommonKSPKeys()
     996             : {
     997             :   return MultiMooseEnum("-ksp_atol -ksp_gmres_restart -ksp_max_it -ksp_pc_side -ksp_rtol "
     998             :                         "-ksp_type -sub_ksp_type",
     999             :                         "",
    1000      425994 :                         true);
    1001             : }
    1002             : MultiMooseEnum
    1003      425994 : getCommonPetscKeys()
    1004             : {
    1005             :   auto options = MultiMooseEnum("-mat_fd_coloring_err -mat_fd_type -mat_mffd_type "
    1006             :                                 "-pc_asm_overlap -pc_factor_levels "
    1007             :                                 "-pc_factor_mat_ordering_type -pc_hypre_boomeramg_grid_sweeps_all "
    1008             :                                 "-pc_hypre_boomeramg_max_iter "
    1009             :                                 "-pc_hypre_boomeramg_strong_threshold -pc_hypre_type -pc_type "
    1010             :                                 "-sub_pc_type",
    1011             :                                 "",
    1012      425994 :                                 true);
    1013      425994 :   options.addValidName(getCommonKSPKeys());
    1014      425994 :   options.addValidName(getCommonSNESKeys());
    1015      425994 :   return options;
    1016           0 : }
    1017             : 
    1018             : bool
    1019         604 : isSNESVI(FEProblemBase & fe_problem)
    1020             : {
    1021         604 :   const PetscOptions & petsc = fe_problem.getPetscOptions();
    1022             : 
    1023             :   int argc;
    1024             :   char ** args;
    1025         604 :   LibmeshPetscCallA(fe_problem.comm().get(), PetscGetArgs(&argc, &args));
    1026             : 
    1027         604 :   std::vector<std::string> cml_arg;
    1028        6724 :   for (int i = 0; i < argc; i++)
    1029        6120 :     cml_arg.push_back(args[i]);
    1030             : 
    1031         604 :   if (MooseUtils::findPair(petsc.pairs, petsc.pairs.begin(), MooseUtils::Any, "vinewtonssls") ==
    1032        1208 :           petsc.pairs.end() &&
    1033         604 :       MooseUtils::findPair(petsc.pairs, petsc.pairs.begin(), MooseUtils::Any, "vinewtonrsls") ==
    1034         648 :           petsc.pairs.end() &&
    1035        1252 :       std::find(cml_arg.begin(), cml_arg.end(), "vinewtonssls") == cml_arg.end() &&
    1036         648 :       std::find(cml_arg.begin(), cml_arg.end(), "vinewtonrsls") == cml_arg.end())
    1037          44 :     return false;
    1038             : 
    1039         560 :   return true;
    1040         604 : }
    1041             : 
    1042             : void
    1043      233124 : setSinglePetscOption(const std::string & name,
    1044             :                      const std::string & value /*=""*/,
    1045             :                      FEProblemBase * const problem /*=nullptr*/)
    1046             : {
    1047      233124 :   static const TIMPI::Communicator comm_world(PETSC_COMM_WORLD);
    1048      233124 :   const TIMPI::Communicator & comm = problem ? problem->comm() : comm_world;
    1049      233124 :   LibmeshPetscCallA(comm.get(),
    1050             :                     PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR,
    1051             :                                          name.c_str(),
    1052             :                                          value == "" ? LIBMESH_PETSC_NULLPTR : value.c_str()));
    1053      233124 :   const auto lower_case_name = MooseUtils::toLower(name);
    1054          20 :   auto check_problem = [problem, &lower_case_name]()
    1055             :   {
    1056          20 :     if (!problem)
    1057           0 :       mooseError(
    1058             :           "Setting the option '",
    1059             :           lower_case_name,
    1060             :           "' requires passing a 'problem' parameter. Contact a developer of your application "
    1061             :           "to have them update their code. If in doubt, reach out to the MOOSE team on Github "
    1062             :           "discussions");
    1063      233144 :   };
    1064             : 
    1065             :   // Select vector type from user-passed PETSc options
    1066      233124 :   if (lower_case_name.find("-vec_type") != std::string::npos)
    1067             :   {
    1068          10 :     check_problem();
    1069          20 :     for (auto & solver_system : problem->_solver_systems)
    1070             :     {
    1071          10 :       auto & lm_sys = solver_system->system();
    1072          30 :       for (auto & [vec_name, vec] : as_range(lm_sys.vectors_begin(), lm_sys.vectors_end()))
    1073             :       {
    1074          20 :         libmesh_ignore(vec_name);
    1075          20 :         auto * const petsc_vec = cast_ptr<PetscVector<Number> *>(vec.get());
    1076          20 :         LibmeshPetscCallA(comm.get(), VecSetFromOptions(petsc_vec->vec()));
    1077             :       }
    1078             :       // The solution vectors aren't included in the system vectors storage
    1079          10 :       auto * petsc_vec = cast_ptr<PetscVector<Number> *>(lm_sys.solution.get());
    1080          10 :       LibmeshPetscCallA(comm.get(), VecSetFromOptions(petsc_vec->vec()));
    1081          10 :       petsc_vec = cast_ptr<PetscVector<Number> *>(lm_sys.current_local_solution.get());
    1082          10 :       LibmeshPetscCallA(comm.get(), VecSetFromOptions(petsc_vec->vec()));
    1083             :     }
    1084             :   }
    1085             :   // Select matrix type from user-passed PETSc options
    1086      233114 :   else if (lower_case_name.find("mat_type") != std::string::npos)
    1087             :   {
    1088          10 :     check_problem();
    1089             : 
    1090          10 :     bool found_matching_prefix = false;
    1091          10 :     for (const auto i : index_range(problem->_solver_systems))
    1092             :     {
    1093          10 :       const auto & solver_sys_name = problem->_solver_sys_names[i];
    1094          10 :       if (lower_case_name.find("-" + MooseUtils::toLower(solver_sys_name) + "_mat_type") ==
    1095             :           std::string::npos)
    1096           0 :         continue;
    1097             : 
    1098          10 :       if (problem->solverParams(i)._type == Moose::ST_JFNK)
    1099           0 :         mooseError(
    1100             :             "Setting option '", lower_case_name, "' is incompatible with a JFNK 'solve_type'");
    1101             : 
    1102          10 :       auto & lm_sys = problem->_solver_systems[i]->system();
    1103          20 :       for (auto & [mat_name, mat] : as_range(lm_sys.matrices_begin(), lm_sys.matrices_end()))
    1104             :       {
    1105          10 :         libmesh_ignore(mat_name);
    1106          10 :         if (auto * const petsc_mat = dynamic_cast<PetscMatrixBase<Number> *>(mat.get()); petsc_mat)
    1107             :         {
    1108             : #ifdef DEBUG
    1109             :           const char * mat_prefix;
    1110             :           LibmeshPetscCallA(comm.get(), MatGetOptionsPrefix(petsc_mat->mat(), &mat_prefix));
    1111             :           mooseAssert(strcmp(mat_prefix, (solver_sys_name + "_").c_str()) == 0,
    1112             :                       "We should have prefixed these matrices previously");
    1113             : #endif
    1114          10 :           LibmeshPetscCallA(comm.get(), MatSetFromOptions(petsc_mat->mat()));
    1115             :         }
    1116             :       }
    1117          10 :       found_matching_prefix = true;
    1118          10 :       break;
    1119             :     }
    1120             : 
    1121          10 :     if (!found_matching_prefix)
    1122           0 :       mooseError("We did not find a matching solver system name for the petsc option '",
    1123             :                  lower_case_name,
    1124             :                  "'");
    1125             :   }
    1126      233124 : }
    1127             : 
    1128             : void
    1129       71630 : setSinglePetscOptionIfAppropriate(const MultiMooseEnum & dont_add_these_options,
    1130             :                                   const std::string & name,
    1131             :                                   const std::string & value /*=""*/,
    1132             :                                   FEProblemBase * const problem /*=nullptr*/)
    1133             : {
    1134       71630 :   if (!dont_add_these_options.contains(name))
    1135       70193 :     setSinglePetscOption(name, value, problem);
    1136       71630 : }
    1137             : 
    1138             : void
    1139           0 : colorAdjacencyMatrix(PetscScalar * adjacency_matrix,
    1140             :                      unsigned int size,
    1141             :                      unsigned int colors,
    1142             :                      std::vector<unsigned int> & vertex_colors,
    1143             :                      const char * coloring_algorithm)
    1144             : {
    1145             :   // Mat A will be a dense matrix from the incoming data structure
    1146             :   Mat A;
    1147           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatCreate(PETSC_COMM_SELF, &A));
    1148           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSetSizes(A, size, size, size, size));
    1149           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSetType(A, MATSEQDENSE));
    1150             :   // PETSc requires a non-const data array to populate the matrix
    1151           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSeqDenseSetPreallocation(A, adjacency_matrix));
    1152           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
    1153           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
    1154             : 
    1155             :   // Convert A to a sparse matrix
    1156             : #if PETSC_VERSION_LESS_THAN(3, 7, 0)
    1157             :   LibmeshPetscCallA(PETSC_COMM_SELF, MatConvert(A, MATAIJ, MAT_REUSE_MATRIX, &A));
    1158             : #else
    1159           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatConvert(A, MATAIJ, MAT_INPLACE_MATRIX, &A));
    1160             : #endif
    1161             : 
    1162             :   ISColoring iscoloring;
    1163             :   MatColoring mc;
    1164           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringCreate(A, &mc));
    1165           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetType(mc, coloring_algorithm));
    1166           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetMaxColors(mc, static_cast<PetscInt>(colors)));
    1167             : 
    1168             :   // Petsc normally colors by distance two (neighbors of neighbors), we just want one
    1169           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetDistance(mc, 1));
    1170           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetFromOptions(mc));
    1171           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringApply(mc, &iscoloring));
    1172             : 
    1173             :   PetscInt nn;
    1174             :   IS * is;
    1175             : #if PETSC_RELEASE_LESS_THAN(3, 12, 0)
    1176             :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringGetIS(iscoloring, &nn, &is));
    1177             : #else
    1178           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringGetIS(iscoloring, PETSC_USE_POINTER, &nn, &is));
    1179             : #endif
    1180             : 
    1181           0 :   if (nn > static_cast<PetscInt>(colors))
    1182           0 :     throw std::runtime_error("Not able to color with designated number of colors");
    1183             : 
    1184           0 :   for (int i = 0; i < nn; i++)
    1185             :   {
    1186             :     PetscInt isize;
    1187             :     const PetscInt * indices;
    1188           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISGetLocalSize(is[i], &isize));
    1189           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISGetIndices(is[i], &indices));
    1190           0 :     for (int j = 0; j < isize; j++)
    1191             :     {
    1192             :       mooseAssert(indices[j] < static_cast<PetscInt>(vertex_colors.size()), "Index out of bounds");
    1193           0 :       vertex_colors[indices[j]] = i;
    1194             :     }
    1195           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISRestoreIndices(is[i], &indices));
    1196             :   }
    1197             : 
    1198           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatDestroy(&A));
    1199           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringDestroy(&mc));
    1200           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringDestroy(&iscoloring));
    1201           0 : }
    1202             : 
    1203             : void
    1204        3748 : dontAddPetscFlag(const std::string & flag, PetscOptions & petsc_options)
    1205             : {
    1206        3748 :   if (!petsc_options.dont_add_these_options.contains(flag))
    1207        3748 :     petsc_options.dont_add_these_options.setAdditionalValue(flag);
    1208        3748 : }
    1209             : 
    1210             : void
    1211         656 : dontAddNonlinearConvergedReason(FEProblemBase & fe_problem)
    1212             : {
    1213         656 :   dontAddPetscFlag("-snes_converged_reason", fe_problem.getPetscOptions());
    1214         656 : }
    1215             : 
    1216             : void
    1217         666 : dontAddLinearConvergedReason(FEProblemBase & fe_problem)
    1218             : {
    1219         666 :   dontAddPetscFlag("-ksp_converged_reason", fe_problem.getPetscOptions());
    1220         666 : }
    1221             : 
    1222             : void
    1223           0 : dontAddCommonKSPOptions(FEProblemBase & fe_problem)
    1224             : {
    1225           0 :   auto & petsc_options = fe_problem.getPetscOptions();
    1226           0 :   for (const auto & flag : getCommonKSPFlags().getNames())
    1227           0 :     dontAddPetscFlag(flag, petsc_options);
    1228           0 :   for (const auto & key : getCommonKSPKeys().getNames())
    1229           0 :     dontAddPetscFlag(key, petsc_options);
    1230           0 : }
    1231             : 
    1232             : void
    1233         664 : dontAddCommonSNESOptions(FEProblemBase & fe_problem)
    1234             : {
    1235         664 :   auto & petsc_options = fe_problem.getPetscOptions();
    1236        8632 :   for (const auto & flag : getCommonSNESFlags().getNames())
    1237        7968 :     if (!petsc_options.dont_add_these_options.contains(flag))
    1238        4648 :       petsc_options.dont_add_these_options.setAdditionalValue(flag);
    1239        5312 :   for (const auto & key : getCommonSNESKeys().getNames())
    1240        4648 :     if (!petsc_options.dont_add_these_options.contains(key))
    1241        2988 :       petsc_options.dont_add_these_options.setAdditionalValue(key);
    1242         664 : }
    1243             : 
    1244             : std::unique_ptr<PetscMatrix<Number>>
    1245          48 : createMatrixFromFile(const libMesh::Parallel::Communicator & comm,
    1246             :                      Mat & mat,
    1247             :                      const std::string & binary_mat_file,
    1248             :                      const unsigned int mat_number_to_load)
    1249             : {
    1250          48 :   LibmeshPetscCallA(comm.get(), MatCreate(comm.get(), &mat));
    1251             :   PetscViewer matviewer;
    1252          48 :   LibmeshPetscCallA(
    1253             :       comm.get(),
    1254             :       PetscViewerBinaryOpen(comm.get(), binary_mat_file.c_str(), FILE_MODE_READ, &matviewer));
    1255          96 :   for (unsigned int i = 0; i < mat_number_to_load; ++i)
    1256          48 :     LibmeshPetscCallA(comm.get(), MatLoad(mat, matviewer));
    1257          48 :   LibmeshPetscCallA(comm.get(), PetscViewerDestroy(&matviewer));
    1258             : 
    1259          96 :   return std::make_unique<PetscMatrix<Number>>(mat, comm);
    1260             : }
    1261             : 
    1262             : } // Namespace PetscSupport
    1263             : } // Namespace MOOSE

Generated by: LCOV version 1.14