LCOV - code coverage report
Current view: top level - src/utils - PetscSupport.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 481 599 80.3 %
Date: 2026-05-29 20:35:17 Functions: 52 58 89.7 %
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 "AuxiliarySystem.h"
      19             : #include "DisplacedProblem.h"
      20             : #include "PenetrationLocator.h"
      21             : #include "NearestNodeLocator.h"
      22             : #include "MooseTypes.h"
      23             : #include "MooseUtils.h"
      24             : #include "CommandLine.h"
      25             : #include "Console.h"
      26             : #include "MultiMooseEnum.h"
      27             : #include "Conversion.h"
      28             : #include "Executioner.h"
      29             : #include "MooseMesh.h"
      30             : #include "ComputeLineSearchObjectWrapper.h"
      31             : #include "Convergence.h"
      32             : #include "ParallelParamObject.h"
      33             : 
      34             : #include "libmesh/equation_systems.h"
      35             : #include "libmesh/linear_implicit_system.h"
      36             : #include "libmesh/nonlinear_implicit_system.h"
      37             : #include "libmesh/petsc_linear_solver.h"
      38             : #include "libmesh/petsc_matrix.h"
      39             : #include "libmesh/petsc_nonlinear_solver.h"
      40             : #include "libmesh/petsc_preconditioner.h"
      41             : #include "libmesh/petsc_vector.h"
      42             : #include "libmesh/sparse_matrix.h"
      43             : #include "libmesh/petsc_solver_exception.h"
      44             : #include "libmesh/simple_range.h"
      45             : 
      46             : // PETSc includes
      47             : #include <petsc.h>
      48             : #include <petscsnes.h>
      49             : #include <petscksp.h>
      50             : #include <petscmat.h>
      51             : #include <petscis.h>
      52             : #include <petscdm.h>
      53             : #include <petscoptions.h>
      54             : 
      55             : // PetscDMMoose include
      56             : #include "PetscDMMoose.h"
      57             : 
      58             : // Standard includes
      59             : #include <ostream>
      60             : #include <cctype>
      61             : #include <fstream>
      62             : #include <optional>
      63             : #include <string>
      64             : 
      65             : using namespace libMesh;
      66             : 
      67             : void
      68           0 : MooseVecView(NumericVector<Number> & vector)
      69             : {
      70           0 :   PetscVector<Number> & petsc_vec = static_cast<PetscVector<Number> &>(vector);
      71           0 :   LibmeshPetscCallA(vector.comm().get(), VecView(petsc_vec.vec(), 0));
      72           0 : }
      73             : 
      74             : void
      75           0 : MooseMatView(SparseMatrix<Number> & mat)
      76             : {
      77           0 :   PetscMatrixBase<Number> & petsc_mat = static_cast<PetscMatrix<Number> &>(mat);
      78           0 :   LibmeshPetscCallA(mat.comm().get(), MatView(petsc_mat.mat(), 0));
      79           0 : }
      80             : 
      81             : void
      82           0 : MooseVecView(const NumericVector<Number> & vector)
      83             : {
      84           0 :   PetscVector<Number> & petsc_vec =
      85             :       static_cast<PetscVector<Number> &>(const_cast<NumericVector<Number> &>(vector));
      86           0 :   LibmeshPetscCallA(vector.comm().get(), VecView(petsc_vec.vec(), 0));
      87           0 : }
      88             : 
      89             : void
      90           0 : MooseMatView(const SparseMatrix<Number> & mat)
      91             : {
      92           0 :   PetscMatrixBase<Number> & petsc_mat =
      93             :       static_cast<PetscMatrix<Number> &>(const_cast<SparseMatrix<Number> &>(mat));
      94           0 :   LibmeshPetscCallA(mat.comm().get(), MatView(petsc_mat.mat(), 0));
      95           0 : }
      96             : 
      97             : namespace Moose
      98             : {
      99             : namespace PetscSupport
     100             : {
     101             : 
     102             : namespace
     103             : {
     104             : 
     105             : void
     106          64 : applySystemVectorTypeOptions(FEProblemBase & problem, libMesh::System & lm_sys)
     107             : {
     108         228 :   for (auto & [_, vec] : as_range(lm_sys.vectors_begin(), lm_sys.vectors_end()))
     109             :   {
     110         164 :     auto * const petsc_vec = cast_ptr<PetscVector<Number> *>(vec.get());
     111         164 :     LibmeshPetscCallA(problem.comm().get(), VecSetFromOptions(petsc_vec->vec()));
     112             :   }
     113             : 
     114             :   // The solution vectors aren't included in the system vectors storage.
     115          64 :   auto * petsc_vec = cast_ptr<PetscVector<Number> *>(lm_sys.solution.get());
     116          64 :   LibmeshPetscCallA(problem.comm().get(), VecSetFromOptions(petsc_vec->vec()));
     117          64 :   petsc_vec = cast_ptr<PetscVector<Number> *>(lm_sys.current_local_solution.get());
     118          64 :   LibmeshPetscCallA(problem.comm().get(), VecSetFromOptions(petsc_vec->vec()));
     119          64 : }
     120             : 
     121             : void
     122          32 : applyVectorTypeOptions(FEProblemBase & problem)
     123             : {
     124          64 :   for (const auto sys_index : make_range(problem.numSolverSystems()))
     125          32 :     applySystemVectorTypeOptions(problem, problem.getSolverSystem(sys_index).system());
     126             : 
     127          32 :   applySystemVectorTypeOptions(problem, problem.getAuxiliarySystem().system());
     128          32 : }
     129             : 
     130             : bool
     131      340443 : petscOptionsHasName(::PetscOptions options,
     132             :                     const std::string & name,
     133             :                     const std::string & prefix = "")
     134             : {
     135      340443 :   PetscBool found = PETSC_FALSE;
     136      340443 :   const char * const prefix_ptr = prefix.empty() ? nullptr : prefix.c_str();
     137      340443 :   LibmeshPetscCallA(PETSC_COMM_WORLD,
     138             :                     PetscOptionsHasName(options, prefix_ptr, name.c_str(), &found));
     139      340443 :   return found;
     140             : }
     141             : 
     142             : bool
     143           3 : hasMatrixFreeSolveType(const FEProblemBase & problem)
     144             : {
     145           6 :   for (const auto sys_index : make_range(problem.numSolverSystems()))
     146           3 :     if (const auto solve_type = problem.solverParams(sys_index)._type;
     147           3 :         solve_type == Moose::ST_JFNK || solve_type == Moose::ST_PJFNK)
     148           0 :       return true;
     149             : 
     150           3 :   return false;
     151             : }
     152             : 
     153             : bool
     154      127862 : mightBeMatTypeOption(const std::string & name)
     155             : {
     156             :   static constexpr std::string_view mat_type_suffix = "mat_type";
     157      255724 :   return name.size() >= mat_type_suffix.size() &&
     158      127862 :          std::equal(mat_type_suffix.rbegin(),
     159      127862 :                     mat_type_suffix.rend(),
     160      127862 :                     name.rbegin(),
     161      353658 :                     [](const char left, const char right)
     162             :                     // tolower requires representability by unsigned char
     163             :                     {
     164      353658 :                       return static_cast<int>(left) ==
     165      353658 :                              std::tolower(libMesh::cast_int<unsigned char>(right));
     166      127862 :                     });
     167             : }
     168             : 
     169             : void
     170       40465 : errorOnUnprefixedMatTypeOption(::PetscOptions options, FEProblemBase & problem)
     171             : {
     172      161860 :   if (!petscOptionsHasName(options, "-mat_type"))
     173       40462 :     return;
     174             : 
     175             :   std::string error_string =
     176             :       "Setting option '-mat_type' is not supported without a solver-system prefix. Use an option "
     177           3 :       "such as '-" +
     178           3 :       problem.getSolverSystem(0).name() + "_mat_type' for assembled libMesh matrices.";
     179           3 :   if (hasMatrixFreeSolveType(problem))
     180             :     error_string +=
     181             :         " Attempting to change the matrix "
     182             :         "type for the MFFD matrix type used to represent the Jacobian for (P)JFNK solve "
     183           0 :         "types is not supported.";
     184           3 :   mooseError(error_string);
     185           0 : }
     186             : 
     187             : // Allow iterating over all systems or allowing caller to specify a specific system for which to
     188             : // apply matrix type options
     189             : void
     190      363384 : applyMatrixTypeOptions(FEProblemBase & problem,
     191             :                        const std::optional<std::size_t> system_index = std::nullopt)
     192             : {
     193      363384 :   const auto begin = system_index.value_or(0);
     194      363384 :   const auto end = system_index ? begin + 1 : problem.numSolverSystems();
     195             : 
     196      739308 :   for (const auto sys_index : make_range(begin, end))
     197             :   {
     198      375924 :     auto & solver_system = problem.getSolverSystem(sys_index);
     199      375924 :     auto & lm_sys = solver_system.system();
     200             : 
     201             :     // Even in matrix-free modes the libMesh matrix wrappers can exist before the PETSc Mat does.
     202      375924 :     if (problem.solverParams(sys_index)._type == Moose::ST_JFNK)
     203         149 :       continue;
     204             : 
     205      752804 :     for (auto & [_, mat] : as_range(lm_sys.matrices_begin(), lm_sys.matrices_end()))
     206      377029 :       if (auto * const petsc_mat = dynamic_cast<PetscMatrixBase<Number> *>(mat.get()); petsc_mat)
     207             :       {
     208      377029 :         LibmeshPetscCallA(
     209             :             problem.comm().get(),
     210             :             MatSetOptionsPrefix(petsc_mat->mat(), (solver_system.name() + "_").c_str()));
     211      377029 :         LibmeshPetscCallA(problem.comm().get(), MatSetFromOptions(petsc_mat->mat()));
     212             :       }
     213             :   }
     214      363384 : }
     215             : 
     216             : } // namespace
     217             : 
     218             : std::string
     219        2490 : stringify(const LineSearchType & t)
     220             : {
     221        2490 :   switch (t)
     222             :   {
     223        2407 :     case LS_BASIC:
     224        4814 :       return "basic";
     225           0 :     case LS_DEFAULT:
     226           0 :       return "default";
     227           0 :     case LS_NONE:
     228           0 :       return "none";
     229           0 :     case LS_SHELL:
     230           0 :       return "shell";
     231           0 :     case LS_L2:
     232           0 :       return "l2";
     233          21 :     case LS_BT:
     234          42 :       return "bt";
     235          62 :     case LS_CP:
     236         124 :       return "cp";
     237           0 :     case LS_CONTACT:
     238           0 :       return "contact";
     239           0 :     case LS_PROJECT:
     240           0 :       return "project";
     241           0 :     case LS_INVALID:
     242           0 :       mooseError("Invalid LineSearchType");
     243             :   }
     244           0 :   return "";
     245             : }
     246             : 
     247             : std::string
     248       28074 : stringify(const MffdType & t)
     249             : {
     250       28074 :   switch (t)
     251             :   {
     252       28065 :     case MFFD_WP:
     253       56130 :       return "wp";
     254           9 :     case MFFD_DS:
     255          18 :       return "ds";
     256           0 :     case MFFD_INVALID:
     257           0 :       mooseError("Invalid MffdType");
     258             :   }
     259           0 :   return "";
     260             : }
     261             : 
     262             : void
     263       40651 : setSolverOptions(const SolverParams & solver_params, const MultiMooseEnum & dont_add_these_options)
     264             : {
     265       40651 :   const auto prefix_with_dash = '-' + solver_params._prefix;
     266             :   // set PETSc options implied by a solve type
     267       40651 :   switch (solver_params._type)
     268             :   {
     269       27958 :     case Moose::ST_PJFNK:
     270       55916 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     271       55916 :                                         prefix_with_dash + "snes_mf_operator");
     272       27958 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     273       55916 :                                         prefix_with_dash + "mat_mffd_type",
     274       55916 :                                         stringify(solver_params._mffd_type));
     275       27958 :       break;
     276             : 
     277         116 :     case Moose::ST_JFNK:
     278         116 :       setSinglePetscOptionIfAppropriate(dont_add_these_options, prefix_with_dash + "snes_mf");
     279         116 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     280         232 :                                         prefix_with_dash + "mat_mffd_type",
     281         232 :                                         stringify(solver_params._mffd_type));
     282         116 :       break;
     283             : 
     284       10539 :     case Moose::ST_NEWTON:
     285       10539 :       break;
     286             : 
     287           0 :     case Moose::ST_FD:
     288           0 :       setSinglePetscOptionIfAppropriate(dont_add_these_options, prefix_with_dash + "snes_fd");
     289           0 :       break;
     290             : 
     291        2038 :     case Moose::ST_LINEAR:
     292        6114 :       setSinglePetscOptionIfAppropriate(
     293        4076 :           dont_add_these_options, prefix_with_dash + "snes_type", "ksponly");
     294        4076 :       setSinglePetscOptionIfAppropriate(dont_add_these_options,
     295        4076 :                                         prefix_with_dash + "snes_monitor_cancel");
     296        2038 :       break;
     297             :   }
     298             : 
     299       40651 :   Moose::LineSearchType ls_type = solver_params._line_search;
     300       40651 :   if (ls_type == Moose::LS_NONE)
     301        2341 :     ls_type = Moose::LS_BASIC;
     302             : 
     303       40651 :   if (ls_type != Moose::LS_DEFAULT && ls_type != Moose::LS_CONTACT && ls_type != Moose::LS_PROJECT)
     304        2490 :     setSinglePetscOptionIfAppropriate(
     305        4980 :         dont_add_these_options, prefix_with_dash + "snes_linesearch_type", stringify(ls_type));
     306       40651 : }
     307             : 
     308             : void
     309       40983 : addPetscOptionsFromCommandline(FEProblemBase * const problem)
     310             : {
     311             :   // commandline options always win
     312             :   // the options from a user commandline will overwrite the existing ones if any conflicts
     313             :   int argc;
     314             :   char ** args;
     315             : 
     316       40983 :   LibmeshPetscCallA(PETSC_COMM_WORLD, PetscGetArgs(&argc, &args));
     317       40983 :   std::vector<const char *> cl_args(args + 1, args + argc);
     318       40983 :   const auto cl_argc = libMesh::cast_int<int>(cl_args.size());
     319             : 
     320             :   ::PetscOptions command_line_options;
     321       40983 :   LibmeshPetscCallA(PETSC_COMM_WORLD, PetscOptionsCreate(&command_line_options));
     322       40983 :   LibmeshPetscCallA(PETSC_COMM_WORLD,
     323             :                     PetscOptionsInsertArgs(command_line_options, cl_argc, cl_args.data()));
     324       40983 :   LibmeshPetscCallA(PETSC_COMM_WORLD,
     325             :                     PetscOptionsInsertArgs(LIBMESH_PETSC_NULLPTR, cl_argc, cl_args.data()));
     326             : 
     327       40983 :   if (!problem)
     328             :   {
     329         541 :     LibmeshPetscCallA(PETSC_COMM_WORLD, PetscOptionsDestroy(&command_line_options));
     330         541 :     return;
     331             :   }
     332             : 
     333       40442 :   errorOnUnprefixedMatTypeOption(command_line_options, *problem);
     334             : 
     335             :   // Some vector/matrix-type options may have been consumed before the PETSc database rebuild.
     336             :   // Replay only the command-line-controlled applications so input-file options handled through
     337             :   // setSinglePetscOption() do not pay the cost twice.
     338      121317 :   const bool have_vec_type = petscOptionsHasName(command_line_options, "-vec_type");
     339       40439 :   bool have_mat_type = false;
     340             : 
     341       81088 :   for (const auto sys_index : make_range(problem->numSolverSystems()))
     342             :   {
     343       40658 :     have_mat_type = petscOptionsHasName(
     344       81316 :         command_line_options, "-mat_type", problem->getSolverSystem(sys_index).name() + "_");
     345       40658 :     if (have_mat_type)
     346           9 :       break;
     347             :   }
     348             : 
     349       40439 :   if (have_vec_type)
     350           9 :     applyVectorTypeOptions(*problem);
     351       40439 :   if (have_mat_type)
     352           9 :     applyMatrixTypeOptions(*problem);
     353             : 
     354       40439 :   LibmeshPetscCallA(PETSC_COMM_WORLD, PetscOptionsDestroy(&command_line_options));
     355       40980 : }
     356             : 
     357             : void
     358       40442 : petscSetOptionsHelper(const PetscOptions & po, FEProblemBase * const problem)
     359             : {
     360             :   // Add any additional options specified in the input file
     361       42684 :   for (const auto & flag : po.flags)
     362             :     // Need to use name method here to pass a str instead of an EnumItem because
     363             :     // we don't care if the id attributes match
     364        2242 :     if (!po.dont_add_these_options.contains(flag.name()) ||
     365           0 :         po.user_set_options.contains(flag.name()))
     366        8968 :       setSinglePetscOption(flag.rawName().c_str());
     367             : 
     368             :   // Add option pairs
     369      169464 :   for (auto & option : po.pairs)
     370      130198 :     if (!po.dont_add_these_options.contains(option.first) ||
     371        1176 :         po.user_set_options.contains(option.first))
     372      127855 :       setSinglePetscOption(option.first, option.second, problem);
     373             : 
     374       40442 :   addPetscOptionsFromCommandline(problem);
     375       40439 : }
     376             : 
     377             : void
     378        1483 : petscSetOptions(const PetscOptions & po,
     379             :                 const SolverParams & solver_params,
     380             :                 FEProblemBase * const problem)
     381             : {
     382        1483 :   PetscCallAbort(PETSC_COMM_WORLD, PetscOptionsClear(LIBMESH_PETSC_NULLPTR));
     383        1483 :   setSolverOptions(solver_params, po.dont_add_these_options);
     384        1483 :   petscSetOptionsHelper(po, problem);
     385        1483 : }
     386             : 
     387             : void
     388       38959 : petscSetOptions(const PetscOptions & po,
     389             :                 const std::vector<SolverParams> & solver_params_vec,
     390             :                 FEProblemBase * const problem)
     391             : {
     392       38959 :   PetscCallAbort(PETSC_COMM_WORLD, PetscOptionsClear(LIBMESH_PETSC_NULLPTR));
     393       78127 :   for (const auto & solver_params : solver_params_vec)
     394       39168 :     setSolverOptions(solver_params, po.dont_add_these_options);
     395       38959 :   petscSetOptionsHelper(po, problem);
     396       38956 : }
     397             : 
     398             : PetscErrorCode
     399       47663 : petscSetupOutput(CommandLine * cmd_line)
     400             : {
     401             :   PetscFunctionBegin;
     402       47663 :   char code[10] = {45, 45, 109, 111, 111, 115, 101};
     403       47663 :   const std::vector<std::string> argv = cmd_line->getArguments();
     404      563064 :   for (const auto & arg : argv)
     405             :   {
     406      515401 :     if (arg.compare(code) == 0)
     407             :     {
     408           0 :       Console::petscSetupOutput();
     409           0 :       break;
     410             :     }
     411             :   }
     412       47663 :   PetscFunctionReturn(PETSC_SUCCESS);
     413       47663 : }
     414             : 
     415             : PetscErrorCode
     416      747622 : petscNonlinearConverged(SNES /*snes*/,
     417             :                         PetscInt it,
     418             :                         PetscReal /*xnorm*/,
     419             :                         PetscReal /*snorm*/,
     420             :                         PetscReal /*fnorm*/,
     421             :                         SNESConvergedReason * reason,
     422             :                         void * ctx)
     423             : {
     424             :   PetscFunctionBegin;
     425      747622 :   FEProblemBase & problem = *static_cast<FEProblemBase *>(ctx);
     426             : 
     427             :   // execute objects that may be used in convergence check
     428      747622 :   problem.execute(EXEC_NONLINEAR_CONVERGENCE);
     429             : 
     430             :   // perform the convergence check
     431             :   Convergence::MooseConvergenceStatus status;
     432      747622 :   if (problem.getFailNextNonlinearConvergenceCheck())
     433             :   {
     434         221 :     status = Convergence::MooseConvergenceStatus::DIVERGED;
     435         221 :     problem.resetFailNextNonlinearConvergenceCheck();
     436             :   }
     437             :   else
     438             :   {
     439      747401 :     auto & convergence = problem.getConvergence(
     440      747401 :         problem.getNonlinearConvergenceNames()[problem.currentNonlinearSystem().number()]);
     441      747401 :     status = convergence.checkConvergence(it);
     442             :   }
     443             : 
     444             :   // convert convergence status to PETSc converged reason
     445      747619 :   switch (status)
     446             :   {
     447      462783 :     case Convergence::MooseConvergenceStatus::ITERATING:
     448      462783 :       *reason = SNES_CONVERGED_ITERATING;
     449      462783 :       break;
     450             : 
     451      284568 :     case Convergence::MooseConvergenceStatus::CONVERGED:
     452      284568 :       *reason = SNES_CONVERGED_FNORM_ABS;
     453      284568 :       break;
     454             : 
     455         268 :     case Convergence::MooseConvergenceStatus::DIVERGED:
     456         268 :       *reason = SNES_DIVERGED_DTOL;
     457         268 :       break;
     458             :   }
     459             : 
     460      747619 :   PetscFunctionReturn(PETSC_SUCCESS);
     461             : }
     462             : 
     463             : PetscErrorCode
     464        4635 : petscLinearConverged(
     465             :     KSP /*ksp*/, PetscInt it, PetscReal /*norm*/, KSPConvergedReason * reason, void * ctx)
     466             : {
     467             :   PetscFunctionBegin;
     468        4635 :   FEProblemBase & problem = *static_cast<FEProblemBase *>(ctx);
     469             : 
     470             :   // execute objects that may be used in convergence check
     471             :   // Right now, setting objects to execute on this flag would be ignored except in the
     472             :   // linear-system-only use case.
     473        4635 :   problem.execute(EXEC_LINEAR_CONVERGENCE);
     474             : 
     475             :   // perform the convergence check
     476             :   Convergence::MooseConvergenceStatus status;
     477        4635 :   if (problem.getFailNextSystemConvergenceCheck())
     478             :   {
     479           0 :     status = Convergence::MooseConvergenceStatus::DIVERGED;
     480           0 :     problem.resetFailNextSystemConvergenceCheck();
     481             :   }
     482             :   else
     483             :   {
     484        4635 :     auto & convergence = problem.getConvergence(
     485        4635 :         problem.getLinearConvergenceNames()[problem.currentLinearSystem().number()]);
     486        4635 :     status = convergence.checkConvergence(it);
     487             :   }
     488             : 
     489             :   // convert convergence status to PETSc converged reason
     490        4635 :   switch (status)
     491             :   {
     492        4113 :     case Convergence::MooseConvergenceStatus::ITERATING:
     493        4113 :       *reason = KSP_CONVERGED_ITERATING;
     494        4113 :       break;
     495             : 
     496             :       // TODO: find a KSP code that works better for this case
     497         522 :     case Convergence::MooseConvergenceStatus::CONVERGED:
     498             : #if PETSC_VERSION_LESS_THAN(3, 24, 0)
     499             :       *reason = KSP_CONVERGED_RTOL_NORMAL;
     500             : #else
     501         522 :       *reason = KSP_CONVERGED_RTOL_NORMAL_EQUATIONS;
     502             : #endif
     503         522 :       break;
     504             : 
     505           0 :     case Convergence::MooseConvergenceStatus::DIVERGED:
     506           0 :       *reason = KSP_DIVERGED_DTOL;
     507           0 :       break;
     508             :   }
     509             : 
     510        4635 :   PetscFunctionReturn(PETSC_SUCCESS);
     511             : }
     512             : 
     513             : PCSide
     514           0 : getPetscPCSide(Moose::PCSideType pcs)
     515             : {
     516           0 :   switch (pcs)
     517             :   {
     518           0 :     case Moose::PCS_LEFT:
     519           0 :       return PC_LEFT;
     520           0 :     case Moose::PCS_RIGHT:
     521           0 :       return PC_RIGHT;
     522           0 :     case Moose::PCS_SYMMETRIC:
     523           0 :       return PC_SYMMETRIC;
     524           0 :     default:
     525           0 :       mooseError("Unknown PC side requested.");
     526             :       break;
     527             :   }
     528             : }
     529             : 
     530             : KSPNormType
     531      375285 : getPetscKSPNormType(Moose::MooseKSPNormType kspnorm)
     532             : {
     533      375285 :   switch (kspnorm)
     534             :   {
     535           0 :     case Moose::KSPN_NONE:
     536           0 :       return KSP_NORM_NONE;
     537           0 :     case Moose::KSPN_PRECONDITIONED:
     538           0 :       return KSP_NORM_PRECONDITIONED;
     539      375285 :     case Moose::KSPN_UNPRECONDITIONED:
     540      375285 :       return KSP_NORM_UNPRECONDITIONED;
     541           0 :     case Moose::KSPN_NATURAL:
     542           0 :       return KSP_NORM_NATURAL;
     543           0 :     case Moose::KSPN_DEFAULT:
     544           0 :       return KSP_NORM_DEFAULT;
     545           0 :     default:
     546           0 :       mooseError("Unknown KSP norm type requested.");
     547             :       break;
     548             :   }
     549             : }
     550             : 
     551             : void
     552      350937 : petscSetDefaultKSPNormType(FEProblemBase & problem, KSP ksp)
     553             : {
     554      726222 :   for (const auto i : make_range(problem.numSolverSystems()))
     555             :   {
     556      375285 :     SolverSystem & sys = problem.getSolverSystem(i);
     557      375285 :     LibmeshPetscCallA(problem.comm().get(),
     558             :                       KSPSetNormType(ksp, getPetscKSPNormType(sys.getMooseKSPNormType())));
     559             :   }
     560      350937 : }
     561             : 
     562             : void
     563      350937 : petscSetDefaultPCSide(FEProblemBase & problem, KSP ksp)
     564             : {
     565      726222 :   for (const auto i : make_range(problem.numSolverSystems()))
     566             :   {
     567      375285 :     SolverSystem & sys = problem.getSolverSystem(i);
     568             : 
     569             :     // PETSc 3.2.x+
     570      375285 :     if (sys.getPCSide() != Moose::PCS_DEFAULT)
     571           0 :       LibmeshPetscCallA(problem.comm().get(), KSPSetPCSide(ksp, getPetscPCSide(sys.getPCSide())));
     572             :   }
     573      350937 : }
     574             : 
     575             : void
     576      349748 : petscSetKSPDefaults(FEProblemBase & problem, KSP ksp)
     577             : {
     578      349748 :   auto & es = problem.es();
     579             : 
     580      349748 :   PetscReal rtol = es.parameters.get<Real>("linear solver tolerance");
     581      349748 :   PetscReal atol = es.parameters.get<Real>("linear solver absolute tolerance");
     582             : 
     583             :   // MOOSE defaults this to -1 for some dumb reason
     584      349748 :   if (atol < 0)
     585           0 :     atol = 1e-50;
     586             : 
     587      349748 :   PetscReal maxits = es.parameters.get<unsigned int>("linear solver maximum iterations");
     588             : 
     589             :   // 1e100 is because we don't use divtol currently
     590      349748 :   LibmeshPetscCallA(problem.comm().get(), KSPSetTolerances(ksp, rtol, atol, 1e100, maxits));
     591             : 
     592      349748 :   petscSetDefaultPCSide(problem, ksp);
     593             : 
     594      349748 :   petscSetDefaultKSPNormType(problem, ksp);
     595      349748 : }
     596             : 
     597             : void
     598      363352 : petscSetDefaults(FEProblemBase & problem)
     599             : {
     600             :   // Apply matrix-type options once the per-system matrix prefixes are known. This is different
     601             :   // from vectors: libMesh/PETSc vector construction already sees a global '-vec_type' option,
     602             :   // but prefixed matrix options such as '-nl0_mat_type' cannot match anything until we set the
     603             :   // matrix prefix here. Without this, a matrix may be constructed with the default type and keep
     604             :   // that type for the rest of the solve, unless we not only set the options prefix but also apply
     605             :   // the options to the matrix in this function call.
     606      363352 :   applyMatrixTypeOptions(problem);
     607             : 
     608      712792 :   for (const auto nl_index : make_range(problem.numNonlinearSystems()))
     609             :   {
     610      349440 :     NonlinearSystemBase & nl = problem.getNonlinearSystemBase(nl_index);
     611             : 
     612             :     // dig out PETSc solver
     613      349440 :     auto * const petsc_solver = cast_ptr<PetscNonlinearSolver<Number> *>(nl.nonlinearSolver());
     614             : 
     615             :     // Ensure we properly prefix SNES which in turn prefixes its KSP
     616      349440 :     const char * snes_prefix = nullptr;
     617      349440 :     std::string snes_prefix_str;
     618      349440 :     if (nl.system().prefix_with_name())
     619             :     {
     620       24348 :       snes_prefix_str = nl.system().prefix();
     621       24348 :       snes_prefix = snes_prefix_str.c_str();
     622             :     }
     623      349440 :     SNES snes = petsc_solver->snes(snes_prefix);
     624             :     KSP ksp;
     625      349440 :     LibmeshPetscCallA(nl.comm().get(), SNESGetKSP(snes, &ksp));
     626      349440 :     LibmeshPetscCallA(nl.comm().get(), SNESSetMaxLinearSolveFailures(snes, 1000000));
     627      349440 :     LibmeshPetscCallA(nl.comm().get(), SNESSetCheckJacobianDomainError(snes, PETSC_TRUE));
     628      349440 :     LibmeshPetscCallA(
     629             :         nl.comm().get(),
     630             :         SNESSetConvergenceTest(snes, petscNonlinearConverged, &problem, LIBMESH_PETSC_NULLPTR));
     631             : 
     632      349440 :     petscSetKSPDefaults(problem, ksp);
     633      349440 :   }
     634             : 
     635      389804 :   for (auto sys_index : make_range(problem.numLinearSystems()))
     636             :   {
     637             :     // dig out PETSc solver
     638       26452 :     LinearSystem & lin_sys = problem.getLinearSystem(sys_index);
     639       26452 :     auto & lm_lin_sys = lin_sys.linearImplicitSystem();
     640             :     auto * const petsc_solver =
     641       26452 :         dynamic_cast<PetscLinearSolver<Number> *>(lm_lin_sys.get_linear_solver());
     642             :     // Ensure we properly prefix KSP
     643       26452 :     if (lm_lin_sys.prefix_with_name())
     644         732 :       petsc_solver->init(lm_lin_sys.prefix().c_str());
     645             :     else
     646       25720 :       petsc_solver->init();
     647             :     // The KSP call here would initialize without a prefix if we hadn't "manually" performed
     648             :     // initialization above
     649       26452 :     KSP ksp = petsc_solver->ksp();
     650             : 
     651       26452 :     if (problem.hasLinearConvergenceObjects())
     652         531 :       LibmeshPetscCallA(
     653             :           lin_sys.comm().get(),
     654             :           KSPSetConvergenceTest(ksp, petscLinearConverged, &problem, LIBMESH_PETSC_NULLPTR));
     655             : 
     656             :     // We dont set the KSP defaults here because they seem to clash with the linear solve parameters
     657             :     // set in FEProblemBase::solveLinearSystem
     658             :   }
     659      363352 : }
     660             : 
     661             : void
     662       73278 : processSingletonMooseWrappedOptions(FEProblemBase & fe_problem, const InputParameters & params)
     663             : {
     664       73278 :   setSolveTypeFromParams(fe_problem, params);
     665       73278 :   setLineSearchFromParams(fe_problem, params);
     666       73278 :   setMFFDTypeFromParams(fe_problem, params);
     667       73278 : }
     668             : 
     669             : #define checkPrefix(prefix)                                                                        \
     670             :   mooseAssert(prefix[0] == '-',                                                                    \
     671             :               "Leading prefix character must be a '-'. Current prefix is '" << prefix << "'");     \
     672             :   mooseAssert((prefix.size() == 1) || (prefix.back() == '_'),                                      \
     673             :               "Terminating prefix character must be a '_'. Current prefix is '" << prefix << "'"); \
     674             :   mooseAssert(MooseUtils::isAllLowercase(prefix), "PETSc prefixes should be all lower-case")
     675             : 
     676             : void
     677       73278 : storePetscOptions(FEProblemBase & fe_problem,
     678             :                   const std::string & prefix,
     679             :                   const ParallelParamObject & param_object)
     680             : {
     681       73278 :   const auto & params = param_object.parameters();
     682       73278 :   processSingletonMooseWrappedOptions(fe_problem, params);
     683             : 
     684             :   // The parameters contained in the Action
     685       73278 :   const auto & petsc_options = params.get<MultiMooseEnum>("petsc_options");
     686             :   const auto & petsc_pair_options =
     687      219834 :       params.get<MooseEnumItem, std::string>("petsc_options_iname", "petsc_options_value");
     688             : 
     689             :   // A reference to the PetscOptions object that contains the settings that will be used in the
     690             :   // solve
     691       73278 :   auto & po = fe_problem.getPetscOptions();
     692             : 
     693             :   // First process the single petsc options/flags
     694       73278 :   addPetscFlagsToPetscOptions(petsc_options, prefix, param_object, po);
     695             : 
     696             :   // Then process the option-value pairs
     697      146556 :   addPetscPairsToPetscOptions(
     698       73278 :       petsc_pair_options, fe_problem.mesh().dimension(), prefix, param_object, po);
     699       73278 : }
     700             : 
     701             : void
     702       86419 : setSolveTypeFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     703             : {
     704             :   // Note: Options set in the Preconditioner block will override those set in the Executioner block
     705      330163 :   if (params.isParamValid("solve_type") && !params.isParamValid("_use_eigen_value"))
     706             :   {
     707             :     // Extract the solve type
     708       34888 :     const std::string & solve_type = params.get<MooseEnum>("solve_type");
     709       70174 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     710       35286 :       fe_problem.solverParams(i)._type = Moose::stringToEnum<Moose::SolveType>(solve_type);
     711       34888 :   }
     712       86419 : }
     713             : 
     714             : void
     715       73278 : setLineSearchFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     716             : {
     717             :   // Note: Options set in the Preconditioner block will override those set in the Executioner block
     718      146556 :   if (params.isParamValid("line_search"))
     719             :   {
     720       59977 :     const auto & line_search = params.get<MooseEnum>("line_search");
     721      119395 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     722       59418 :       if (fe_problem.solverParams(i)._line_search == Moose::LS_INVALID || line_search != "default")
     723             :       {
     724             :         Moose::LineSearchType enum_line_search =
     725       59065 :             Moose::stringToEnum<Moose::LineSearchType>(line_search);
     726       59065 :         fe_problem.solverParams(i)._line_search = enum_line_search;
     727       59065 :         if (enum_line_search == LS_CONTACT || enum_line_search == LS_PROJECT)
     728             :         {
     729           0 :           NonlinearImplicitSystem * nl_system = dynamic_cast<NonlinearImplicitSystem *>(
     730           0 :               &fe_problem.getNonlinearSystemBase(i).system());
     731           0 :           if (!nl_system)
     732           0 :             mooseError("You've requested a line search but you must be solving an EigenProblem. "
     733             :                        "These two things are not consistent.");
     734             :           PetscNonlinearSolver<Real> * petsc_nonlinear_solver =
     735           0 :               dynamic_cast<PetscNonlinearSolver<Real> *>(nl_system->nonlinear_solver.get());
     736           0 :           if (!petsc_nonlinear_solver)
     737           0 :             mooseError("Currently the MOOSE line searches all use Petsc, so you "
     738             :                        "must use Petsc as your non-linear solver.");
     739             :           petsc_nonlinear_solver->linesearch_object =
     740           0 :               std::make_unique<ComputeLineSearchObjectWrapper>(fe_problem);
     741             :         }
     742             :       }
     743             :   }
     744       73278 : }
     745             : 
     746             : void
     747       73278 : setMFFDTypeFromParams(FEProblemBase & fe_problem, const InputParameters & params)
     748             : {
     749      146556 :   if (params.isParamValid("mffd_type"))
     750             :   {
     751       72770 :     const auto & mffd_type = params.get<MooseEnum>("mffd_type");
     752      145227 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     753       72457 :       fe_problem.solverParams(i)._mffd_type = Moose::stringToEnum<Moose::MffdType>(mffd_type);
     754             :   }
     755       73278 : }
     756             : 
     757             : template <typename T>
     758             : void
     759       54683 : checkUserProvidedPetscOption(const T & option, const ParallelParamObject & param_object)
     760             : {
     761       54683 :   const auto & string_option = static_cast<const std::string &>(option);
     762       54683 :   if (string_option[0] != '-')
     763           0 :     param_object.mooseError("PETSc option '", string_option, "' does not begin with '-'");
     764       54683 : }
     765             : 
     766             : void
     767       73278 : addPetscFlagsToPetscOptions(const MultiMooseEnum & petsc_flags,
     768             :                             std::string prefix,
     769             :                             const ParallelParamObject & param_object,
     770             :                             PetscOptions & po)
     771             : {
     772       73278 :   prefix.insert(prefix.begin(), '-');
     773             :   checkPrefix(prefix);
     774             : 
     775             :   // Update the PETSc single flags
     776       75623 :   for (const auto & option : petsc_flags)
     777             :   {
     778        2345 :     checkUserProvidedPetscOption(option, param_object);
     779             : 
     780        2345 :     const std::string & string_option = option.name();
     781             : 
     782             :     /**
     783             :      * "-log_summary" cannot be used in the input file. This option needs to be set when PETSc is
     784             :      * initialized
     785             :      * which happens before the parser is even created.  We'll throw an error if somebody attempts
     786             :      * to add this option later.
     787             :      */
     788        2345 :     if (option == "-log_summary" || option == "-log_view")
     789           0 :       mooseError("The PETSc option \"-log_summary\" or \"-log_view\" can only be used on the "
     790             :                  "command line.  Please "
     791             :                  "remove it from the input file");
     792             : 
     793             :     // Update the stored items, but do not create duplicates
     794        2345 :     const std::string prefixed_option = prefix + string_option.substr(1);
     795        2345 :     if (!po.flags.isValueSet(prefixed_option))
     796             :     {
     797        2345 :       po.flags.setAdditionalValue(prefixed_option);
     798        2345 :       po.user_set_options.setAdditionalValue(prefixed_option);
     799             :     }
     800        2345 :   }
     801       73278 : }
     802             : 
     803             : void
     804       59977 : setConvergedReasonFlags(FEProblemBase & fe_problem, std::string prefix)
     805             : {
     806       59977 :   prefix.insert(prefix.begin(), '-');
     807             :   checkPrefix(prefix);
     808       59977 :   libmesh_ignore(fe_problem); // avoid unused warnings for old PETSc
     809             : 
     810             : #if !PETSC_VERSION_LESS_THAN(3, 14, 0)
     811             :   // the boolean in these pairs denote whether the user has specified any of the reason flags in the
     812             :   // input file
     813      119954 :   std::array<std::string, 2> reason_flags = {{"snes_converged_reason", "ksp_converged_reason"}};
     814             : 
     815       59977 :   auto & po = fe_problem.getPetscOptions();
     816             : 
     817      179931 :   for (const auto & reason_flag : reason_flags)
     818             :   {
     819      119954 :     const auto full_flag = prefix + reason_flag;
     820      237509 :     if (!po.flags.isValueSet(full_flag) && !po.dont_add_these_options.contains(full_flag) &&
     821      117555 :         (std::find_if(po.pairs.begin(),
     822             :                       po.pairs.end(),
     823      160139 :                       [&full_flag](auto & pair)
     824      515203 :                       { return pair.first == (full_flag); }) == po.pairs.end()))
     825      117555 :       po.pairs.emplace_back(full_flag, "::failed");
     826      119954 :   }
     827             : #endif
     828       59977 : }
     829             : 
     830             : void
     831       73499 : addPetscPairsToPetscOptions(
     832             :     const std::vector<std::pair<MooseEnumItem, std::string>> & petsc_pair_options,
     833             :     const unsigned int mesh_dimension,
     834             :     std::string prefix,
     835             :     const ParallelParamObject & param_object,
     836             :     PetscOptions & po)
     837             : {
     838       73499 :   prefix.insert(prefix.begin(), '-');
     839             :   checkPrefix(prefix);
     840             : 
     841             :   // Setup the name value pairs
     842       73499 :   bool boomeramg_found = false;
     843       73499 :   bool strong_threshold_found = false;
     844             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     845       73499 :   bool superlu_dist_found = false;
     846       73499 :   bool fact_pattern_found = false;
     847       73499 :   bool tiny_pivot_found = false;
     848             : #endif
     849       73499 :   std::string pc_description = "";
     850             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     851             :   // If users use HMG, we would like to set
     852       73499 :   bool hmg_found = false;
     853       73499 :   bool matptap_found = false;
     854       73499 :   bool hmg_strong_threshold_found = false;
     855             : #endif
     856       73499 :   std::vector<std::pair<std::string, std::string>> new_options;
     857             : 
     858      125837 :   for (const auto & [option_name, option_value] : petsc_pair_options)
     859             :   {
     860       52338 :     checkUserProvidedPetscOption(option_name, param_object);
     861             : 
     862       52338 :     new_options.clear();
     863             :     const std::string prefixed_option_name =
     864       52338 :         prefix + static_cast<const std::string &>(option_name).substr(1);
     865             : 
     866             :     // Do not add duplicate settings
     867       52338 :     if (auto it =
     868       52338 :             MooseUtils::findPair(po.pairs, po.pairs.begin(), prefixed_option_name, MooseUtils::Any);
     869       52338 :         it == po.pairs.end())
     870             :     {
     871             : #if !PETSC_VERSION_LESS_THAN(3, 9, 0)
     872       52230 :       if (option_name == "-pc_factor_mat_solver_package")
     873         142 :         new_options.emplace_back(prefix + "pc_factor_mat_solver_type", option_value);
     874             : #else
     875             :       if (option_name == "-pc_factor_mat_solver_type")
     876             :         new_options.push_back(prefix + "pc_factor_mat_solver_package", option_value);
     877             : #endif
     878             : 
     879             :       // Look for a pc description
     880       79083 :       if (option_name == "-pc_type" || option_name == "-sub_pc_type" ||
     881       26853 :           option_name == "-pc_hypre_type")
     882       44804 :         pc_description += option_value + ' ';
     883             : 
     884             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     885       52230 :       if (option_name == "-pc_type" && option_value == "hmg")
     886          36 :         hmg_found = true;
     887             : 
     888             :       // MPIAIJ for PETSc 3.12.0: -matptap_via
     889             :       // MAIJ for PETSc 3.12.0: -matmaijptap_via
     890             :       // MPIAIJ for PETSc 3.13 to 3.16: -matptap_via, -matproduct_ptap_via
     891             :       // MAIJ for PETSc 3.13 to 3.16: -matproduct_ptap_via
     892             :       // MPIAIJ for PETSc 3.17 and higher: -matptap_via, -mat_product_algorithm
     893             :       // MAIJ for PETSc 3.17 and higher: -mat_product_algorithm
     894             : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
     895       52338 :       if (hmg_found && (option_name == "-matptap_via" || option_name == "-matmaijptap_via" ||
     896         108 :                         option_name == "-matproduct_ptap_via"))
     897           0 :         new_options.emplace_back(prefix + "mat_product_algorithm", option_value);
     898             : #elif !PETSC_VERSION_LESS_THAN(3, 13, 0)
     899             :       if (hmg_found && (option_name == "-matptap_via" || option_name == "-matmaijptap_via"))
     900             :         new_options.emplace_back(prefix + "matproduct_ptap_via", option_value);
     901             : #else
     902             :       if (hmg_found && (option_name == "-matproduct_ptap_via"))
     903             :       {
     904             :         new_options.emplace_back(prefix + "matptap_via", option_value);
     905             :         new_options.emplace_back(prefix + "matmaijptap_via", option_value);
     906             :       }
     907             : #endif
     908             : 
     909      104460 :       if (option_name == "-matptap_via" || option_name == "-matmaijptap_via" ||
     910      104460 :           option_name == "-matproduct_ptap_via" || option_name == "-mat_product_algorithm")
     911           0 :         matptap_found = true;
     912             : 
     913             :       // For 3D problems, we need to set this 0.7
     914       52230 :       if (option_name == "-hmg_inner_pc_hypre_boomeramg_strong_threshold")
     915           0 :         hmg_strong_threshold_found = true;
     916             : #endif
     917             :       // This special case is common enough that we'd like to handle it for the user.
     918       52230 :       if (option_name == "-pc_hypre_type" && option_value == "boomeramg")
     919       19331 :         boomeramg_found = true;
     920       52230 :       if (option_name == "-pc_hypre_boomeramg_strong_threshold")
     921           0 :         strong_threshold_found = true;
     922             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     923       52230 :       if ((option_name == "-pc_factor_mat_solver_package" ||
     924       53382 :            option_name == "-pc_factor_mat_solver_type") &&
     925        1152 :           option_value == "superlu_dist")
     926         285 :         superlu_dist_found = true;
     927       52230 :       if (option_name == "-mat_superlu_dist_fact")
     928           0 :         fact_pattern_found = true;
     929       52230 :       if (option_name == "-mat_superlu_dist_replacetinypivot")
     930           0 :         tiny_pivot_found = true;
     931             : #endif
     932             : 
     933       52230 :       if (!new_options.empty())
     934             :       {
     935         142 :         std::copy(new_options.begin(), new_options.end(), std::back_inserter(po.pairs));
     936         284 :         for (const auto & option : new_options)
     937         142 :           po.user_set_options.setAdditionalValue(option.first);
     938             :       }
     939             :       else
     940             :       {
     941       52088 :         po.pairs.push_back(std::make_pair(prefixed_option_name, option_value));
     942       52088 :         po.user_set_options.setAdditionalValue(prefixed_option_name);
     943             :       }
     944             :     }
     945             :     else
     946             :     {
     947             :       do
     948             :       {
     949         108 :         it->second = option_value;
     950         108 :         it = MooseUtils::findPair(po.pairs, std::next(it), prefixed_option_name, MooseUtils::Any);
     951         108 :       } while (it != po.pairs.end());
     952             :     }
     953       52338 :   }
     954             : 
     955             :   // When running a 3D mesh with boomeramg, it is almost always best to supply a strong threshold
     956             :   // value. We will provide that for the user here if they haven't supplied it themselves.
     957       73499 :   if (boomeramg_found && !strong_threshold_found && mesh_dimension == 3)
     958             :   {
     959         961 :     po.pairs.emplace_back(prefix + "pc_hypre_boomeramg_strong_threshold", "0.7");
     960         961 :     pc_description += "strong_threshold: 0.7 (auto)";
     961             :   }
     962             : 
     963             : #if !PETSC_VERSION_LESS_THAN(3, 12, 0)
     964       73499 :   if (hmg_found && !hmg_strong_threshold_found && mesh_dimension == 3)
     965             :   {
     966          24 :     po.pairs.emplace_back(prefix + "hmg_inner_pc_hypre_boomeramg_strong_threshold", "0.7");
     967          24 :     pc_description += "strong_threshold: 0.7 (auto)";
     968             :   }
     969             : 
     970             :   // Default PETSc PtAP takes too much memory, and it is not quite useful
     971             :   // Let us switch to use new algorithm
     972       73499 :   if (hmg_found && !matptap_found)
     973             :   {
     974             : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
     975          36 :     po.pairs.emplace_back(prefix + "mat_product_algorithm", "allatonce");
     976             : #elif !PETSC_VERSION_LESS_THAN(3, 13, 0)
     977             :     po.pairs.emplace_back(prefix + "matproduct_ptap_via", "allatonce");
     978             : #else
     979             :     po.pairs.emplace_back(prefix + "matptap_via", "allatonce");
     980             :     po.pairs.emplace_back(prefix + "matmaijptap_via", "allatonce");
     981             : #endif
     982             :   }
     983             : #endif
     984             : 
     985             : #if !PETSC_VERSION_LESS_THAN(3, 7, 0)
     986             :   // In PETSc-3.7.{0--4}, there is a bug when using superlu_dist, and we have to use
     987             :   // SamePattern_SameRowPerm, otherwise we use whatever we have in PETSc
     988       73499 :   if (superlu_dist_found && !fact_pattern_found)
     989             :   {
     990         285 :     po.pairs.emplace_back(prefix + "mat_superlu_dist_fact",
     991             : #if PETSC_VERSION_LESS_THAN(3, 7, 5)
     992             :                           "SamePattern_SameRowPerm");
     993             :     pc_description += "mat_superlu_dist_fact: SamePattern_SameRowPerm ";
     994             : #else
     995             :                           "SamePattern");
     996         285 :     pc_description += "mat_superlu_dist_fact: SamePattern ";
     997             : #endif
     998             :   }
     999             : 
    1000             :   // restore this superlu  option
    1001       73499 :   if (superlu_dist_found && !tiny_pivot_found)
    1002             :   {
    1003         285 :     po.pairs.emplace_back(prefix + "mat_superlu_dist_replacetinypivot", "1");
    1004         285 :     pc_description += " mat_superlu_dist_replacetinypivot: true ";
    1005             :   }
    1006             : #endif
    1007             :   // Set Preconditioner description
    1008       73499 :   if (!pc_description.empty() && prefix.size() > 1)
    1009         729 :     po.pc_description += "[" + prefix.substr(1, prefix.size() - 2) + "]: ";
    1010       73499 :   po.pc_description += pc_description;
    1011       73499 : }
    1012             : 
    1013             : std::set<std::string>
    1014      156854 : getPetscValidLineSearches()
    1015             : {
    1016      156854 :   return {"default", "shell", "none", "basic", "l2", "bt", "cp"};
    1017             : }
    1018             : 
    1019             : InputParameters
    1020      204584 : getPetscValidParams()
    1021             : {
    1022      204584 :   InputParameters params = emptyInputParameters();
    1023             : 
    1024      818336 :   MooseEnum solve_type("PJFNK JFNK NEWTON FD LINEAR");
    1025      818336 :   params.addParam<MooseEnum>("solve_type",
    1026             :                              solve_type,
    1027             :                              "PJFNK: Preconditioned Jacobian-Free Newton Krylov "
    1028             :                              "JFNK: Jacobian-Free Newton Krylov "
    1029             :                              "NEWTON: Full Newton Solve "
    1030             :                              "FD: Use finite differences to compute Jacobian "
    1031             :                              "LINEAR: Solving a linear problem");
    1032             : 
    1033      818336 :   MooseEnum mffd_type("wp ds", "wp");
    1034      818336 :   params.addParam<MooseEnum>("mffd_type",
    1035             :                              mffd_type,
    1036             :                              "Specifies the finite differencing type for "
    1037             :                              "Jacobian-free solve types. Note that the "
    1038             :                              "default is wp (for Walker and Pernice).");
    1039             : 
    1040      613752 :   params.addParam<MultiMooseEnum>(
    1041      409168 :       "petsc_options", getCommonPetscFlags(), "Singleton PETSc options");
    1042      613752 :   params.addParam<MultiMooseEnum>(
    1043      409168 :       "petsc_options_iname", getCommonPetscKeys(), "Names of PETSc name/value pairs");
    1044      818336 :   params.addParam<std::vector<std::string>>(
    1045             :       "petsc_options_value",
    1046             :       "Values of PETSc name/value pairs (must correspond with \"petsc_options_iname\"");
    1047      613752 :   params.addParamNamesToGroup("solve_type petsc_options petsc_options_iname petsc_options_value "
    1048             :                               "mffd_type",
    1049             :                               "PETSc");
    1050             : 
    1051      409168 :   return params;
    1052      204584 : }
    1053             : 
    1054             : MultiMooseEnum
    1055      210670 : getCommonSNESFlags()
    1056             : {
    1057             :   return MultiMooseEnum(
    1058             :       "-ksp_monitor_snes_lg -snes_ksp_ew -snes_converged_reason "
    1059             :       "-snes_ksp -snes_linesearch_monitor -snes_mf -snes_mf_operator -snes_monitor "
    1060             :       "-snes_test_display -snes_view -snes_monitor_cancel",
    1061             :       "",
    1062      421340 :       true);
    1063             : }
    1064             : 
    1065             : MultiMooseEnum
    1066      209638 : getCommonKSPFlags()
    1067             : {
    1068             :   return MultiMooseEnum(
    1069      419276 :       "-ksp_converged_reason -ksp_gmres_modifiedgramschmidt -ksp_monitor", "", true);
    1070             : }
    1071             : 
    1072             : MultiMooseEnum
    1073      208974 : getCommonPetscFlags()
    1074             : {
    1075      208974 :   auto options = MultiMooseEnum("-dm_moose_print_embedding -dm_view", "", true);
    1076      208974 :   options.addValidName(getCommonKSPFlags());
    1077      208974 :   options.addValidName(getCommonSNESFlags());
    1078      208974 :   return options;
    1079           0 : }
    1080             : 
    1081             : MultiMooseEnum
    1082      210670 : getCommonSNESKeys()
    1083             : {
    1084             :   return MultiMooseEnum("-snes_atol -snes_linesearch_type -snes_ls -snes_max_it -snes_rtol "
    1085             :                         "-snes_divergence_tolerance -snes_type",
    1086             :                         "",
    1087      421340 :                         true);
    1088             : }
    1089             : 
    1090             : MultiMooseEnum
    1091      209638 : getCommonKSPKeys()
    1092             : {
    1093             :   return MultiMooseEnum("-ksp_atol -ksp_gmres_restart -ksp_max_it -ksp_pc_side -ksp_rtol "
    1094             :                         "-ksp_type -sub_ksp_type",
    1095             :                         "",
    1096      419276 :                         true);
    1097             : }
    1098             : MultiMooseEnum
    1099      208974 : getCommonPetscKeys()
    1100             : {
    1101             :   auto options = MultiMooseEnum("-mat_fd_coloring_err -mat_fd_type -mat_mffd_type "
    1102             :                                 "-pc_asm_overlap -pc_factor_levels "
    1103             :                                 "-pc_factor_mat_ordering_type -pc_hypre_boomeramg_grid_sweeps_all "
    1104             :                                 "-pc_hypre_boomeramg_max_iter "
    1105             :                                 "-pc_hypre_boomeramg_strong_threshold -pc_hypre_type -pc_type "
    1106             :                                 "-sub_pc_type",
    1107             :                                 "",
    1108      208974 :                                 true);
    1109      208974 :   options.addValidName(getCommonKSPKeys());
    1110      208974 :   options.addValidName(getCommonSNESKeys());
    1111      208974 :   return options;
    1112           0 : }
    1113             : 
    1114             : bool
    1115         560 : isSNESVI(FEProblemBase & fe_problem)
    1116             : {
    1117         560 :   const PetscOptions & petsc = fe_problem.getPetscOptions();
    1118             : 
    1119             :   int argc;
    1120             :   char ** args;
    1121         560 :   LibmeshPetscCallA(fe_problem.comm().get(), PetscGetArgs(&argc, &args));
    1122             : 
    1123         560 :   std::vector<std::string> cml_arg;
    1124        6822 :   for (int i = 0; i < argc; i++)
    1125       12524 :     cml_arg.push_back(args[i]);
    1126             : 
    1127         560 :   if (MooseUtils::findPair(petsc.pairs, petsc.pairs.begin(), MooseUtils::Any, "vinewtonssls") ==
    1128        1120 :           petsc.pairs.end() &&
    1129         560 :       MooseUtils::findPair(petsc.pairs, petsc.pairs.begin(), MooseUtils::Any, "vinewtonrsls") ==
    1130         600 :           petsc.pairs.end() &&
    1131        1160 :       std::find(cml_arg.begin(), cml_arg.end(), "vinewtonssls") == cml_arg.end() &&
    1132         600 :       std::find(cml_arg.begin(), cml_arg.end(), "vinewtonrsls") == cml_arg.end())
    1133          40 :     return false;
    1134             : 
    1135         520 :   return true;
    1136         560 : }
    1137             : 
    1138             : void
    1139      218858 : setSinglePetscOption(const std::string & name,
    1140             :                      const std::string & value /*=""*/,
    1141             :                      FEProblemBase * const problem /*=nullptr*/)
    1142             : {
    1143      218858 :   static const TIMPI::Communicator comm_world(PETSC_COMM_WORLD);
    1144      218858 :   const TIMPI::Communicator & comm = problem ? problem->comm() : comm_world;
    1145      218858 :   LibmeshPetscCallA(comm.get(),
    1146             :                     PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR,
    1147             :                                          name.c_str(),
    1148             :                                          value == "" ? LIBMESH_PETSC_NULLPTR : value.c_str()));
    1149             :   // Create a single option data base so that we can use PETSC's internal option checking which
    1150             :   // is case insensitive. This is better than re-implementing case-insensitive checks here in this
    1151             :   // TU
    1152             :   ::PetscOptions single_option;
    1153      218858 :   LibmeshPetscCallA(comm.get(), PetscOptionsCreate(&single_option));
    1154      218858 :   LibmeshPetscCallA(comm.get(),
    1155             :                     PetscOptionsSetValue(single_option,
    1156             :                                          name.c_str(),
    1157             :                                          value == "" ? LIBMESH_PETSC_NULLPTR : value.c_str()));
    1158          23 :   auto check_problem = [problem, &name]()
    1159             :   {
    1160          23 :     if (!problem)
    1161           0 :       mooseError(
    1162             :           "Setting the option '",
    1163             :           name,
    1164             :           "' requires passing a 'problem' parameter. Contact a developer of your application "
    1165             :           "to have them update their code. If in doubt, reach out to the MOOSE team on Github "
    1166             :           "discussions");
    1167      218858 :   };
    1168             : 
    1169             :   // Select vector type from user-passed PETSc options
    1170      875432 :   if (petscOptionsHasName(single_option, "-vec_type"))
    1171             :   {
    1172          23 :     check_problem();
    1173          23 :     applyVectorTypeOptions(*problem);
    1174             :   }
    1175             :   // First do a cheap suffix check so unrelated PETSc options do not pay for looping over every
    1176             :   // solver system. Once we know the name looks like a matrix-type option, rely on PETSc's
    1177             :   // option lookup for the actual case-insensitive and prefix-aware matching.
    1178      218835 :   else if (problem && mightBeMatTypeOption(name))
    1179             :   {
    1180          23 :     errorOnUnprefixedMatTypeOption(single_option, *problem);
    1181             : 
    1182          23 :     for (const auto i : index_range(problem->_solver_systems))
    1183             :     {
    1184          23 :       const auto & solver_sys_name = problem->_solver_sys_names[i];
    1185          69 :       if (!petscOptionsHasName(single_option, "-mat_type", solver_sys_name + "_"))
    1186           0 :         continue;
    1187             : 
    1188          23 :       if (problem->solverParams(i)._type == Moose::ST_JFNK)
    1189           0 :         mooseError("Setting option '", name, "' is incompatible with a JFNK 'solve_type'");
    1190             : 
    1191          23 :       applyMatrixTypeOptions(*problem, i);
    1192          23 :       break;
    1193             :     }
    1194             :   }
    1195             : 
    1196      218858 :   LibmeshPetscCallA(comm.get(), PetscOptionsDestroy(&single_option));
    1197      218858 : }
    1198             : 
    1199             : void
    1200       68455 : setSinglePetscOptionIfAppropriate(const MultiMooseEnum & dont_add_these_options,
    1201             :                                   const std::string & name,
    1202             :                                   const std::string & value /*=""*/,
    1203             :                                   FEProblemBase * const problem /*=nullptr*/)
    1204             : {
    1205       68455 :   if (!dont_add_these_options.contains(name))
    1206       64106 :     setSinglePetscOption(name, value, problem);
    1207       68455 : }
    1208             : 
    1209             : void
    1210           0 : colorAdjacencyMatrix(PetscScalar * adjacency_matrix,
    1211             :                      unsigned int size,
    1212             :                      unsigned int colors,
    1213             :                      std::vector<unsigned int> & vertex_colors,
    1214             :                      const char * coloring_algorithm)
    1215             : {
    1216             :   // Mat A will be a dense matrix from the incoming data structure
    1217             :   Mat A;
    1218           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatCreate(PETSC_COMM_SELF, &A));
    1219           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSetSizes(A, size, size, size, size));
    1220           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSetType(A, MATSEQDENSE));
    1221             :   // PETSc requires a non-const data array to populate the matrix
    1222           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatSeqDenseSetPreallocation(A, adjacency_matrix));
    1223           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
    1224           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
    1225             : 
    1226             :   // Convert A to a sparse matrix
    1227             : #if PETSC_VERSION_LESS_THAN(3, 7, 0)
    1228             :   LibmeshPetscCallA(PETSC_COMM_SELF, MatConvert(A, MATAIJ, MAT_REUSE_MATRIX, &A));
    1229             : #else
    1230           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatConvert(A, MATAIJ, MAT_INPLACE_MATRIX, &A));
    1231             : #endif
    1232             : 
    1233             :   ISColoring iscoloring;
    1234             :   MatColoring mc;
    1235           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringCreate(A, &mc));
    1236           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetType(mc, coloring_algorithm));
    1237           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetMaxColors(mc, static_cast<PetscInt>(colors)));
    1238             : 
    1239             :   // Petsc normally colors by distance two (neighbors of neighbors), we just want one
    1240           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetDistance(mc, 1));
    1241           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringSetFromOptions(mc));
    1242           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringApply(mc, &iscoloring));
    1243             : 
    1244             :   PetscInt nn;
    1245             :   IS * is;
    1246             : #if PETSC_RELEASE_LESS_THAN(3, 12, 0)
    1247             :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringGetIS(iscoloring, &nn, &is));
    1248             : #else
    1249           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringGetIS(iscoloring, PETSC_USE_POINTER, &nn, &is));
    1250             : #endif
    1251             : 
    1252           0 :   if (nn > static_cast<PetscInt>(colors))
    1253           0 :     throw std::runtime_error("Not able to color with designated number of colors");
    1254             : 
    1255           0 :   for (int i = 0; i < nn; i++)
    1256             :   {
    1257             :     PetscInt isize;
    1258             :     const PetscInt * indices;
    1259           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISGetLocalSize(is[i], &isize));
    1260           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISGetIndices(is[i], &indices));
    1261           0 :     for (int j = 0; j < isize; j++)
    1262             :     {
    1263             :       mooseAssert(indices[j] < static_cast<PetscInt>(vertex_colors.size()), "Index out of bounds");
    1264           0 :       vertex_colors[indices[j]] = i;
    1265             :     }
    1266           0 :     LibmeshPetscCallA(PETSC_COMM_SELF, ISRestoreIndices(is[i], &indices));
    1267             :   }
    1268             : 
    1269           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatDestroy(&A));
    1270           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, MatColoringDestroy(&mc));
    1271           0 :   LibmeshPetscCallA(PETSC_COMM_SELF, ISColoringDestroy(&iscoloring));
    1272           0 : }
    1273             : 
    1274             : void
    1275       40756 : dontAddPetscFlag(const std::string & flag, PetscOptions & petsc_options)
    1276             : {
    1277       40756 :   if (!petsc_options.dont_add_these_options.contains(flag))
    1278       31460 :     petsc_options.dont_add_these_options.setAdditionalValue(flag);
    1279       40756 : }
    1280             : 
    1281             : void
    1282         625 : dontAddNonlinearConvergedReason(FEProblemBase & fe_problem)
    1283             : {
    1284        1250 :   dontAddPetscFlag("-snes_converged_reason", fe_problem.getPetscOptions());
    1285         625 : }
    1286             : 
    1287             : void
    1288         634 : dontAddLinearConvergedReason(FEProblemBase & fe_problem)
    1289             : {
    1290        1268 :   dontAddPetscFlag("-ksp_converged_reason", fe_problem.getPetscOptions());
    1291         634 : }
    1292             : 
    1293             : void
    1294         664 : dontAddCommonKSPOptions(FEProblemBase & fe_problem)
    1295             : {
    1296         664 :   auto & petsc_options = fe_problem.getPetscOptions();
    1297        2656 :   for (const auto & flag : getCommonKSPFlags().getNames())
    1298        2656 :     dontAddPetscFlag(flag, petsc_options);
    1299        5312 :   for (const auto & key : getCommonKSPKeys().getNames())
    1300        5312 :     dontAddPetscFlag(key, petsc_options);
    1301         664 : }
    1302             : 
    1303             : void
    1304         664 : dontAddCommonSNESOptions(FEProblemBase & fe_problem)
    1305             : {
    1306         664 :   dontAddCommonSNESOptions(fe_problem, "");
    1307         664 : }
    1308             : 
    1309             : void
    1310        1696 : dontAddCommonSNESOptions(FEProblemBase & fe_problem, const std::string & prefix)
    1311             : {
    1312        1696 :   auto & petsc_options = fe_problem.getPetscOptions();
    1313       20352 :   for (const auto & flag : getCommonSNESFlags().getNames())
    1314       20352 :     dontAddPetscFlag("-" + prefix + flag.substr(1), petsc_options);
    1315       13568 :   for (const auto & key : getCommonSNESKeys().getNames())
    1316       13568 :     dontAddPetscFlag("-" + prefix + key.substr(1), petsc_options);
    1317        1696 : }
    1318             : 
    1319             : std::unique_ptr<PetscMatrix<Number>>
    1320          40 : createMatrixFromFile(const libMesh::Parallel::Communicator & comm,
    1321             :                      Mat & mat,
    1322             :                      const std::string & binary_mat_file,
    1323             :                      const unsigned int mat_number_to_load)
    1324             : {
    1325          40 :   LibmeshPetscCallA(comm.get(), MatCreate(comm.get(), &mat));
    1326             :   PetscViewer matviewer;
    1327          40 :   LibmeshPetscCallA(
    1328             :       comm.get(),
    1329             :       PetscViewerBinaryOpen(comm.get(), binary_mat_file.c_str(), FILE_MODE_READ, &matviewer));
    1330          80 :   for (unsigned int i = 0; i < mat_number_to_load; ++i)
    1331          40 :     LibmeshPetscCallA(comm.get(), MatLoad(mat, matviewer));
    1332          40 :   LibmeshPetscCallA(comm.get(), PetscViewerDestroy(&matviewer));
    1333             : 
    1334          80 :   return std::make_unique<PetscMatrix<Number>>(mat, comm);
    1335             : }
    1336             : 
    1337             : } // Namespace PetscSupport
    1338             : } // Namespace MOOSE

Generated by: LCOV version 1.14