LCOV - code coverage report
Current view: top level - src/utils - SlepcSupport.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 517 637 81.2 %
Date: 2025-07-17 01:28:37 Functions: 40 43 93.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 "libmesh/libmesh_config.h"
      11             : 
      12             : #ifdef LIBMESH_HAVE_SLEPC
      13             : 
      14             : #include "SlepcSupport.h"
      15             : // MOOSE includes
      16             : #include "InputParameters.h"
      17             : #include "Conversion.h"
      18             : #include "EigenProblem.h"
      19             : #include "FEProblemBase.h"
      20             : #include "NonlinearEigenSystem.h"
      21             : #include "libmesh/petsc_vector.h"
      22             : #include "libmesh/petsc_matrix.h"
      23             : #include "libmesh/slepc_macro.h"
      24             : #include "libmesh/auto_ptr.h"
      25             : #include "petscsnes.h"
      26             : #include "slepceps.h"
      27             : 
      28             : using namespace libMesh;
      29             : 
      30             : namespace Moose
      31             : {
      32             : namespace SlepcSupport
      33             : {
      34             : 
      35             : const int subspace_factor = 2;
      36             : 
      37             : InputParameters
      38       15385 : getSlepcValidParams(InputParameters & params)
      39             : {
      40             :   MooseEnum solve_type("POWER ARNOLDI KRYLOVSCHUR JACOBI_DAVIDSON "
      41             :                        "NONLINEAR_POWER NEWTON PJFNK PJFNKMO JFNK",
      42       15385 :                        "PJFNK");
      43       15385 :   params.set<MooseEnum>("solve_type") = solve_type;
      44             : 
      45       15385 :   params.setDocString("solve_type",
      46             :                       "POWER: Power / Inverse / RQI "
      47             :                       "ARNOLDI: Arnoldi "
      48             :                       "KRYLOVSCHUR: Krylov-Schur "
      49             :                       "JACOBI_DAVIDSON: Jacobi-Davidson "
      50             :                       "NONLINEAR_POWER: Nonlinear Power "
      51             :                       "NEWTON: Newton "
      52             :                       "PJFNK: Preconditioned Jacobian-free Newton-Kyrlov"
      53             :                       "JFNK: Jacobian-free Newton-Kyrlov"
      54             :                       "PJFNKMO: Preconditioned Jacobian-free Newton-Kyrlov with Matrix Only");
      55             : 
      56             :   // When the eigenvalue problems is reformed as a coupled nonlinear system,
      57             :   // we use part of Jacobian as the preconditioning matrix.
      58             :   // Because the difference between the Jacobian and the preconditioning matrix is not small,
      59             :   // the linear solver KSP can not reduce the residual much. After several tests,
      60             :   // we find 1e-2 is a reasonable choice.
      61       15385 :   params.set<Real>("l_tol") = 1e-2;
      62             : 
      63       30770 :   return params;
      64       15385 : }
      65             : 
      66             : InputParameters
      67       15385 : getSlepcEigenProblemValidParams()
      68             : {
      69       15385 :   InputParameters params = emptyInputParameters();
      70             : 
      71             :   // We are solving a Non-Hermitian eigenvalue problem by default
      72             :   MooseEnum eigen_problem_type("HERMITIAN NON_HERMITIAN GEN_HERMITIAN GEN_NON_HERMITIAN "
      73             :                                "GEN_INDEFINITE POS_GEN_NON_HERMITIAN SLEPC_DEFAULT",
      74       15385 :                                "GEN_NON_HERMITIAN");
      75       15385 :   params.addParam<MooseEnum>(
      76             :       "eigen_problem_type",
      77             :       eigen_problem_type,
      78             :       "Type of the eigenvalue problem we are solving "
      79             :       "HERMITIAN: Hermitian "
      80             :       "NON_HERMITIAN: Non-Hermitian "
      81             :       "GEN_HERMITIAN: Generalized Hermitian "
      82             :       "GEN_NON_HERMITIAN: Generalized Non-Hermitian "
      83             :       "GEN_INDEFINITE: Generalized indefinite Hermitian "
      84             :       "POS_GEN_NON_HERMITIAN: Generalized Non-Hermitian with positive (semi-)definite B "
      85             :       "SLEPC_DEFAULT: Use whatever SLEPC has by default ");
      86             : 
      87             :   // Which eigenvalues are we interested in
      88             :   MooseEnum which_eigen_pairs("LARGEST_MAGNITUDE SMALLEST_MAGNITUDE LARGEST_REAL SMALLEST_REAL "
      89             :                               "LARGEST_IMAGINARY SMALLEST_IMAGINARY TARGET_MAGNITUDE TARGET_REAL "
      90       15385 :                               "TARGET_IMAGINARY ALL_EIGENVALUES SLEPC_DEFAULT");
      91       15385 :   params.addParam<MooseEnum>("which_eigen_pairs",
      92             :                              which_eigen_pairs,
      93             :                              "Which eigenvalue pairs to obtain from the solution "
      94             :                              "LARGEST_MAGNITUDE "
      95             :                              "SMALLEST_MAGNITUDE "
      96             :                              "LARGEST_REAL "
      97             :                              "SMALLEST_REAL "
      98             :                              "LARGEST_IMAGINARY "
      99             :                              "SMALLEST_IMAGINARY "
     100             :                              "TARGET_MAGNITUDE "
     101             :                              "TARGET_REAL "
     102             :                              "TARGET_IMAGINARY "
     103             :                              "ALL_EIGENVALUES "
     104             :                              "SLEPC_DEFAULT ");
     105             : 
     106       15385 :   params.addParam<unsigned int>("n_eigen_pairs", 1, "The number of eigen pairs");
     107       15385 :   params.addParam<unsigned int>("n_basis_vectors", 3, "The dimension of eigen subspaces");
     108             : 
     109       15385 :   params.addParam<Real>("eigen_tol", 1.0e-4, "Relative Tolerance for Eigen Solver");
     110       15385 :   params.addParam<unsigned int>("eigen_max_its", 10000, "Max Iterations for Eigen Solver");
     111             : 
     112       15385 :   params.addParam<Real>("l_abs_tol", 1e-50, "Absolute Tolerances for Linear Solver");
     113             : 
     114       15385 :   params.addParam<unsigned int>("free_power_iterations", 4, "The number of free power iterations");
     115             : 
     116       46155 :   params.addParam<unsigned int>(
     117       30770 :       "extra_power_iterations", 0, "The number of extra free power iterations");
     118             : 
     119       15385 :   params.addParamNamesToGroup(
     120             :       "eigen_problem_type which_eigen_pairs n_eigen_pairs n_basis_vectors eigen_tol eigen_max_its "
     121             :       "free_power_iterations extra_power_iterations",
     122             :       "Eigen Solver");
     123       15385 :   params.addParamNamesToGroup("l_abs_tol", "Linear solver");
     124             : 
     125       30770 :   return params;
     126       15385 : }
     127             : 
     128             : void
     129         532 : setSlepcEigenSolverTolerances(EigenProblem & eigen_problem,
     130             :                               const SolverParams & solver_params,
     131             :                               const InputParameters & params)
     132             : {
     133         532 :   const auto & dont_add_these_options = eigen_problem.getPetscOptions().dont_add_these_options;
     134             : 
     135             :   mooseAssert(solver_params._solver_sys_num != libMesh::invalid_uint,
     136             :               "The solver system number must be initialized");
     137             : 
     138         532 :   Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     139        1064 :                                                          solver_params._prefix + "eps_tol",
     140        1064 :                                                          stringify(params.get<Real>("eigen_tol")));
     141             : 
     142         532 :   Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     143             :       dont_add_these_options,
     144        1064 :       solver_params._prefix + "eps_max_it",
     145        1064 :       stringify(params.get<unsigned int>("eigen_max_its")));
     146             : 
     147             :   // if it is a nonlinear eigenvalue solver, we need to set tolerances for nonlinear solver and
     148             :   // linear solver
     149         532 :   if (eigen_problem.isNonlinearEigenvalueSolver(solver_params._solver_sys_num))
     150             :   {
     151             :     // nonlinear solver tolerances
     152         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     153             :         dont_add_these_options,
     154         944 :         solver_params._prefix + "snes_max_it",
     155         944 :         stringify(params.get<unsigned int>("nl_max_its")));
     156             : 
     157         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     158             :         dont_add_these_options,
     159         944 :         solver_params._prefix + "snes_max_funcs",
     160         944 :         stringify(params.get<unsigned int>("nl_max_funcs")));
     161             : 
     162         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     163             :         dont_add_these_options,
     164         944 :         solver_params._prefix + "snes_atol",
     165         944 :         stringify(params.get<Real>("nl_abs_tol")));
     166             : 
     167         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     168             :         dont_add_these_options,
     169         944 :         solver_params._prefix + "snes_rtol",
     170         944 :         stringify(params.get<Real>("nl_rel_tol")));
     171             : 
     172         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     173             :         dont_add_these_options,
     174         944 :         solver_params._prefix + "snes_stol",
     175         944 :         stringify(params.get<Real>("nl_rel_step_tol")));
     176             : 
     177             :     // linear solver
     178         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     179             :         dont_add_these_options,
     180         944 :         solver_params._prefix + "ksp_max_it",
     181         944 :         stringify(params.get<unsigned int>("l_max_its")));
     182             : 
     183         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     184         944 :                                                            solver_params._prefix + "ksp_rtol",
     185         944 :                                                            stringify(params.get<Real>("l_tol")));
     186             : 
     187         472 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     188             :         dont_add_these_options,
     189         944 :         solver_params._prefix + "ksp_atol",
     190         944 :         stringify(params.get<Real>("l_abs_tol")));
     191             :   }
     192             :   else
     193             :   { // linear eigenvalue problem
     194             :     // linear solver
     195          60 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     196             :         dont_add_these_options,
     197         120 :         solver_params._prefix + "st_ksp_max_it",
     198         120 :         stringify(params.get<unsigned int>("l_max_its")));
     199             : 
     200          60 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     201         120 :                                                            solver_params._prefix + "st_ksp_rtol",
     202         120 :                                                            stringify(params.get<Real>("l_tol")));
     203             : 
     204          60 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     205             :         dont_add_these_options,
     206         120 :         solver_params._prefix + "st_ksp_atol",
     207         120 :         stringify(params.get<Real>("l_abs_tol")));
     208             :   }
     209         532 : }
     210             : 
     211             : void
     212         562 : setEigenProblemSolverParams(EigenProblem & eigen_problem, const InputParameters & params)
     213             : {
     214        1124 :   for (const auto i : make_range(eigen_problem.numNonlinearSystems()))
     215             :   {
     216         562 :     const std::string & eigen_problem_type = params.get<MooseEnum>("eigen_problem_type");
     217         562 :     if (!eigen_problem_type.empty())
     218         562 :       eigen_problem.solverParams(i)._eigen_problem_type =
     219         562 :           Moose::stringToEnum<Moose::EigenProblemType>(eigen_problem_type);
     220             :     else
     221           0 :       mooseError("Have to specify a valid eigen problem type");
     222             : 
     223         562 :     const std::string & which_eigen_pairs = params.get<MooseEnum>("which_eigen_pairs");
     224         562 :     if (!which_eigen_pairs.empty())
     225          72 :       eigen_problem.solverParams(i)._which_eigen_pairs =
     226          72 :           Moose::stringToEnum<Moose::WhichEigenPairs>(which_eigen_pairs);
     227             : 
     228             :     // Set necessary parameters used in EigenSystem::solve(),
     229             :     // i.e. the number of requested eigenpairs nev and the number
     230             :     // of basis vectors ncv used in the solution algorithm. Note that
     231             :     // ncv >= nev must hold and ncv >= 2*nev is recommended
     232         562 :     unsigned int n_eigen_pairs = params.get<unsigned int>("n_eigen_pairs");
     233         562 :     unsigned int n_basis_vectors = params.get<unsigned int>("n_basis_vectors");
     234             : 
     235         562 :     eigen_problem.setNEigenPairsRequired(n_eigen_pairs);
     236             : 
     237         562 :     eigen_problem.es().parameters.set<unsigned int>("eigenpairs") = n_eigen_pairs;
     238             : 
     239             :     // If the subspace dimension is too small, we increase it automatically
     240         562 :     if (subspace_factor * n_eigen_pairs > n_basis_vectors)
     241             :     {
     242           0 :       n_basis_vectors = subspace_factor * n_eigen_pairs;
     243           0 :       mooseWarning(
     244             :           "Number of subspaces in Eigensolver is changed by moose because the value you set "
     245             :           "is too small");
     246             :     }
     247             : 
     248         562 :     eigen_problem.es().parameters.set<unsigned int>("basis vectors") = n_basis_vectors;
     249             : 
     250             :     // Operators A and B are formed as shell matrices
     251         562 :     eigen_problem.solverParams(i)._eigen_matrix_free = params.get<bool>("matrix_free");
     252             : 
     253             :     // Preconditioning is formed as a shell matrix
     254         562 :     eigen_problem.solverParams(i)._precond_matrix_free = params.get<bool>("precond_matrix_free");
     255             : 
     256         562 :     if (params.get<MooseEnum>("solve_type") == "PJFNK")
     257             :     {
     258         308 :       eigen_problem.solverParams(i)._eigen_matrix_free = true;
     259             :     }
     260         562 :     if (params.get<MooseEnum>("solve_type") == "JFNK")
     261             :     {
     262          40 :       eigen_problem.solverParams(i)._eigen_matrix_free = true;
     263          40 :       eigen_problem.solverParams(i)._precond_matrix_free = true;
     264             :     }
     265             :     // We need matrices so that we can implement residual evaluations
     266         562 :     if (params.get<MooseEnum>("solve_type") == "PJFNKMO")
     267             :     {
     268          96 :       eigen_problem.solverParams(i)._eigen_matrix_free = true;
     269          96 :       eigen_problem.solverParams(i)._precond_matrix_free = false;
     270          96 :       eigen_problem.solverParams(i)._eigen_matrix_vector_mult = true;
     271             :       // By default, we need to form full matrices, otherwise residual
     272             :       // evaluations will not be accurate
     273          96 :       eigen_problem.setCoupling(Moose::COUPLING_FULL);
     274             :     }
     275         562 :   }
     276             : 
     277         562 :   eigen_problem.constantMatrices(params.get<bool>("constant_matrices"));
     278             : 
     279         562 :   if (eigen_problem.constantMatrices() && params.get<MooseEnum>("solve_type") != "PJFNKMO")
     280             :   {
     281           4 :     mooseError("constant_matrices flag is only valid for solve type: PJFNKMO");
     282             :   }
     283         558 : }
     284             : 
     285             : void
     286         562 : storeSolveType(FEProblemBase & fe_problem, const InputParameters & params)
     287             : {
     288         562 :   if (!(dynamic_cast<EigenProblem *>(&fe_problem)))
     289           0 :     return;
     290             : 
     291         562 :   if (params.isParamValid("solve_type"))
     292        1124 :     for (const auto i : make_range(fe_problem.numNonlinearSystems()))
     293        1124 :       fe_problem.solverParams(i)._eigen_solve_type =
     294         562 :           Moose::stringToEnum<Moose::EigenSolveType>(params.get<MooseEnum>("solve_type"));
     295             : }
     296             : 
     297             : void
     298         532 : setEigenProblemOptions(SolverParams & solver_params, const MultiMooseEnum & dont_add_these_options)
     299             : {
     300         532 :   switch (solver_params._eigen_problem_type)
     301             :   {
     302          24 :     case Moose::EPT_HERMITIAN:
     303          24 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     304             :                                                              "-eps_hermitian");
     305          24 :       break;
     306             : 
     307          12 :     case Moose::EPT_NON_HERMITIAN:
     308          12 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     309             :                                                              "-eps_non_hermitian");
     310          12 :       break;
     311             : 
     312           0 :     case Moose::EPT_GEN_HERMITIAN:
     313           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     314             :                                                              "-eps_gen_hermitian");
     315           0 :       break;
     316             : 
     317           0 :     case Moose::EPT_GEN_INDEFINITE:
     318           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     319             :                                                              "-eps_gen_indefinite");
     320           0 :       break;
     321             : 
     322         496 :     case Moose::EPT_GEN_NON_HERMITIAN:
     323         496 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     324             :                                                              "-eps_gen_non_hermitian");
     325         496 :       break;
     326             : 
     327           0 :     case Moose::EPT_POS_GEN_NON_HERMITIAN:
     328           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     329             :                                                              "-eps_pos_gen_non_hermitian");
     330           0 :       break;
     331             : 
     332           0 :     case Moose::EPT_SLEPC_DEFAULT:
     333           0 :       break;
     334             : 
     335           0 :     default:
     336           0 :       mooseError("Unknown eigen solver type \n");
     337             :   }
     338         532 : }
     339             : 
     340             : void
     341         532 : setWhichEigenPairsOptions(SolverParams & solver_params,
     342             :                           const MultiMooseEnum & dont_add_these_options)
     343             : {
     344         532 :   switch (solver_params._which_eigen_pairs)
     345             :   {
     346          36 :     case Moose::WEP_LARGEST_MAGNITUDE:
     347          36 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     348             :                                                              "-eps_largest_magnitude");
     349          36 :       break;
     350             : 
     351          24 :     case Moose::WEP_SMALLEST_MAGNITUDE:
     352          24 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     353             :                                                              "-eps_smallest_magnitude");
     354          24 :       break;
     355             : 
     356           0 :     case Moose::WEP_LARGEST_REAL:
     357           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     358             :                                                              "-eps_largest_real");
     359           0 :       break;
     360             : 
     361           0 :     case Moose::WEP_SMALLEST_REAL:
     362           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     363             :                                                              "-eps_smallest_real");
     364           0 :       break;
     365             : 
     366           0 :     case Moose::WEP_LARGEST_IMAGINARY:
     367           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     368             :                                                              "-eps_largest_imaginary");
     369           0 :       break;
     370             : 
     371           0 :     case Moose::WEP_SMALLEST_IMAGINARY:
     372           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     373             :                                                              "-eps_smallest_imaginary");
     374           0 :       break;
     375             : 
     376           0 :     case Moose::WEP_TARGET_MAGNITUDE:
     377           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     378             :                                                              "-eps_target_magnitude");
     379           0 :       break;
     380             : 
     381           0 :     case Moose::WEP_TARGET_REAL:
     382           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     383             :                                                              "-eps_target_real");
     384           0 :       break;
     385             : 
     386           0 :     case Moose::WEP_TARGET_IMAGINARY:
     387           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options,
     388             :                                                              "-eps_target_imaginary");
     389           0 :       break;
     390             : 
     391           0 :     case Moose::WEP_ALL_EIGENVALUES:
     392           0 :       Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options, "-eps_all");
     393           0 :       break;
     394             : 
     395         472 :     case Moose::WEP_SLEPC_DEFAULT:
     396         472 :       break;
     397             : 
     398           0 :     default:
     399           0 :       mooseError("Unknown type of WhichEigenPairs \n");
     400             :   }
     401         532 : }
     402             : 
     403             : void
     404         529 : setFreeNonlinearPowerIterations(unsigned int free_power_iterations)
     405             : {
     406         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "0");
     407         529 :   Moose::PetscSupport::setSinglePetscOption("-snes_max_it", "2");
     408             :   // During each power iteration, we want solver converged unless linear solver does not
     409             :   // work. We here use a really loose tolerance for this purpose.
     410             :   // -snes_no_convergence_test is a perfect option, but it was removed from PETSc
     411         529 :   Moose::PetscSupport::setSinglePetscOption("-snes_rtol", "0.99999999999");
     412         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_max_it", stringify(free_power_iterations));
     413             :   // We always want the number of free power iterations respected so we don't want to stop early if
     414             :   // we've satisfied a convergence criterion. Consequently we make this tolerance very tight
     415         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_tol", "1e-50");
     416         529 : }
     417             : 
     418             : void
     419         529 : clearFreeNonlinearPowerIterations(const InputParameters & params)
     420             : {
     421         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "1");
     422         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_max_it", "1");
     423         529 :   Moose::PetscSupport::setSinglePetscOption("-snes_max_it",
     424        1058 :                                             stringify(params.get<unsigned int>("nl_max_its")));
     425         529 :   Moose::PetscSupport::setSinglePetscOption("-snes_rtol",
     426        1058 :                                             stringify(params.get<Real>("nl_rel_tol")));
     427         529 :   Moose::PetscSupport::setSinglePetscOption("-eps_tol", stringify(params.get<Real>("eigen_tol")));
     428         529 : }
     429             : 
     430             : void
     431         460 : setNewtonPetscOptions(SolverParams & solver_params, const InputParameters & params)
     432             : {
     433             : #if !SLEPC_VERSION_LESS_THAN(3, 8, 0) || !PETSC_VERSION_RELEASE
     434             :   // Whether or not we need to involve an initial inverse power
     435         460 :   bool initial_power = params.get<bool>("_newton_inverse_power");
     436             : 
     437         460 :   Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
     438         460 :   Moose::PetscSupport::setSinglePetscOption("-eps_power_nonlinear", "1");
     439         460 :   Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "1");
     440             :   // Only one outer iteration in EPS is allowed when Newton/PJFNK/JFNK
     441             :   // is used as the eigen solver
     442         460 :   Moose::PetscSupport::setSinglePetscOption("-eps_max_it", "1");
     443         460 :   if (initial_power)
     444             :   {
     445           0 :     Moose::PetscSupport::setSinglePetscOption("-init_eps_power_snes_max_it", "1");
     446           0 :     Moose::PetscSupport::setSinglePetscOption("-init_eps_power_ksp_rtol", "1e-2");
     447           0 :     Moose::PetscSupport::setSinglePetscOption(
     448           0 :         "-init_eps_max_it", stringify(params.get<unsigned int>("free_power_iterations")));
     449             :   }
     450         460 :   Moose::PetscSupport::setSinglePetscOption("-eps_target_magnitude", "");
     451         460 :   if (solver_params._eigen_matrix_free)
     452             :   {
     453         448 :     Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "1");
     454         448 :     if (initial_power)
     455           0 :       Moose::PetscSupport::setSinglePetscOption("-init_eps_power_snes_mf_operator", "1");
     456             :   }
     457             :   else
     458             :   {
     459          12 :     Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "0");
     460          12 :     if (initial_power)
     461           0 :       Moose::PetscSupport::setSinglePetscOption("-init_eps_power_snes_mf_operator", "0");
     462             :   }
     463             : #if PETSC_RELEASE_LESS_THAN(3, 13, 0)
     464             :   Moose::PetscSupport::setSinglePetscOption("-st_type", "sinvert");
     465             :   if (initial_power)
     466             :     Moose::PetscSupport::setSinglePetscOption("-init_st_type", "sinvert");
     467             : #endif
     468             : #else
     469             :   mooseError("Newton-based eigenvalue solver requires SLEPc 3.7.3 or higher");
     470             : #endif
     471         460 : }
     472             : 
     473             : void
     474          12 : setNonlinearPowerOptions(SolverParams & solver_params)
     475             : {
     476             : #if !SLEPC_VERSION_LESS_THAN(3, 8, 0) || !PETSC_VERSION_RELEASE
     477          12 :   Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
     478          12 :   Moose::PetscSupport::setSinglePetscOption("-eps_power_nonlinear", "1");
     479          12 :   Moose::PetscSupport::setSinglePetscOption("-eps_target_magnitude", "");
     480          12 :   if (solver_params._eigen_matrix_free)
     481          12 :     Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "1");
     482             :   else
     483           0 :     Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "0");
     484             : 
     485             : #if PETSC_RELEASE_LESS_THAN(3, 13, 0)
     486             :   Moose::PetscSupport::setSinglePetscOption("-st_type", "sinvert");
     487             : #endif
     488             : #else
     489             :   mooseError("Nonlinear Inverse Power requires SLEPc 3.7.3 or higher");
     490             : #endif
     491          12 : }
     492             : 
     493             : void
     494         532 : setEigenSolverOptions(SolverParams & solver_params, const InputParameters & params)
     495             : {
     496             :   // Avoid unused variable warnings when you have SLEPc but not PETSc-dev.
     497         532 :   libmesh_ignore(params);
     498             : 
     499         532 :   switch (solver_params._eigen_solve_type)
     500             :   {
     501           0 :     case Moose::EST_POWER:
     502           0 :       Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
     503           0 :       break;
     504             : 
     505           0 :     case Moose::EST_ARNOLDI:
     506           0 :       Moose::PetscSupport::setSinglePetscOption("-eps_type", "arnoldi");
     507           0 :       break;
     508             : 
     509          36 :     case Moose::EST_KRYLOVSCHUR:
     510          36 :       Moose::PetscSupport::setSinglePetscOption("-eps_type", "krylovschur");
     511          36 :       break;
     512             : 
     513          24 :     case Moose::EST_JACOBI_DAVIDSON:
     514          24 :       Moose::PetscSupport::setSinglePetscOption("-eps_type", "jd");
     515          24 :       break;
     516             : 
     517          12 :     case Moose::EST_NONLINEAR_POWER:
     518          12 :       setNonlinearPowerOptions(solver_params);
     519          12 :       break;
     520             : 
     521          34 :     case Moose::EST_NEWTON:
     522          34 :       setNewtonPetscOptions(solver_params, params);
     523          34 :       break;
     524             : 
     525         294 :     case Moose::EST_PJFNK:
     526         294 :       solver_params._eigen_matrix_free = true;
     527         294 :       solver_params._customized_pc_for_eigen = false;
     528         294 :       setNewtonPetscOptions(solver_params, params);
     529         294 :       break;
     530             : 
     531          40 :     case Moose::EST_JFNK:
     532          40 :       solver_params._eigen_matrix_free = true;
     533          40 :       solver_params._customized_pc_for_eigen = true;
     534          40 :       setNewtonPetscOptions(solver_params, params);
     535          40 :       break;
     536             : 
     537          92 :     case Moose::EST_PJFNKMO:
     538          92 :       solver_params._eigen_matrix_free = true;
     539          92 :       solver_params._customized_pc_for_eigen = false;
     540          92 :       solver_params._eigen_matrix_vector_mult = true;
     541          92 :       setNewtonPetscOptions(solver_params, params);
     542          92 :       break;
     543             : 
     544           0 :     default:
     545           0 :       mooseError("Unknown eigen solver type \n");
     546             :   }
     547         532 : }
     548             : 
     549             : void
     550         532 : slepcSetOptions(EigenProblem & eigen_problem,
     551             :                 SolverParams & solver_params,
     552             :                 const InputParameters & params)
     553             : {
     554         532 :   const auto & dont_add_these_options = eigen_problem.getPetscOptions().dont_add_these_options;
     555             : 
     556         532 :   Moose::PetscSupport::petscSetOptions(
     557         532 :       eigen_problem.getPetscOptions(), solver_params, &eigen_problem);
     558             :   // Call "SolverTolerances" first, so some solver specific tolerance such as "eps_max_it"
     559             :   // can be overriden
     560         532 :   setSlepcEigenSolverTolerances(eigen_problem, solver_params, params);
     561         532 :   setEigenSolverOptions(solver_params, params);
     562             :   // when Bx norm postprocessor is provided, we switch off the sign normalization
     563         532 :   if (eigen_problem.bxNormProvided())
     564          32 :     Moose::PetscSupport::setSinglePetscOptionIfAppropriate(
     565             :         dont_add_these_options, "-eps_power_sign_normalization", "0", &eigen_problem);
     566         532 :   setEigenProblemOptions(solver_params, eigen_problem.getPetscOptions().dont_add_these_options);
     567         532 :   setWhichEigenPairsOptions(solver_params, eigen_problem.getPetscOptions().dont_add_these_options);
     568         532 :   Moose::PetscSupport::addPetscOptionsFromCommandline();
     569         532 : }
     570             : 
     571             : // For matrices A and B
     572             : PetscErrorCode
     573        5825 : mooseEPSFormMatrices(EigenProblem & eigen_problem, EPS eps, Vec x, void * ctx)
     574             : {
     575             :   ST st;
     576             :   Mat A, B;
     577             :   PetscBool aisshell, bisshell;
     578             :   PetscFunctionBegin;
     579             : 
     580        5825 :   if (eigen_problem.constantMatrices() && eigen_problem.wereMatricesFormed())
     581        3222 :     PetscFunctionReturn(PETSC_SUCCESS);
     582             : 
     583        2603 :   if (eigen_problem.onLinearSolver())
     584             :     // We reach here during linear iteration when solve type is PJFNKMO.
     585             :     // We will use the matrices assembled at the beginning of this Newton
     586             :     // iteration for the following residual evaluation.
     587        2253 :     PetscFunctionReturn(PETSC_SUCCESS);
     588             : 
     589         350 :   NonlinearEigenSystem & eigen_nl = eigen_problem.getCurrentNonlinearEigenSystem();
     590         350 :   auto & sys = eigen_nl.sys();
     591         350 :   SNES snes = eigen_nl.getSNES();
     592             :   // Rest ST state so that we can retrieve matrices
     593         350 :   LibmeshPetscCallQ(EPSGetST(eps, &st));
     594         350 :   LibmeshPetscCallQ(STResetMatrixState(st));
     595         350 :   LibmeshPetscCallQ(EPSGetOperators(eps, &A, &B));
     596         350 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)A, MATSHELL, &aisshell));
     597         350 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)B, MATSHELL, &bisshell));
     598         350 :   if (aisshell || bisshell)
     599             :   {
     600           0 :     SETERRQ(PetscObjectComm((PetscObject)eps),
     601             :             PETSC_ERR_ARG_INCOMP,
     602             :             "A and B matrices can not be shell matrices when using PJFNKMO \n");
     603             :   }
     604             :   // Form A and B
     605         350 :   std::vector<Mat> mats = {A, B};
     606         350 :   std::vector<SparseMatrix<Number> *> libmesh_mats = {&sys.get_matrix_A(), &sys.get_matrix_B()};
     607         350 :   moosePetscSNESFormMatricesTags(
     608         350 :       snes, x, mats, libmesh_mats, ctx, {eigen_nl.nonEigenMatrixTag(), eigen_nl.eigenMatrixTag()});
     609         350 :   eigen_problem.wereMatricesFormed(true);
     610         350 :   PetscFunctionReturn(PETSC_SUCCESS);
     611         350 : }
     612             : 
     613             : namespace
     614             : {
     615             : void
     616       33564 : updateCurrentLocalSolution(CondensedEigenSystem & sys, Vec x)
     617             : {
     618       33564 :   auto & dof_map = sys.get_dof_map();
     619             : 
     620       33564 :   PetscVector<Number> X_global(x, sys.comm());
     621             : 
     622       33564 :   if (dof_map.n_constrained_dofs())
     623             :   {
     624         911 :     sys.copy_sub_to_super(X_global, *sys.solution);
     625             :     // Set the constrained dof values
     626         911 :     dof_map.enforce_constraints_exactly(sys);
     627         911 :     sys.update();
     628             :   }
     629             :   else
     630             :   {
     631       32653 :     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
     632             : 
     633             :     // Use the system's update() to get a good local version of the
     634             :     // parallel solution.  This operation does not modify the incoming
     635             :     // "x" vector, it only localizes information from "x" into
     636             :     // sys.current_local_solution.
     637       32653 :     X_global.swap(X_sys);
     638       32653 :     sys.update();
     639       32653 :     X_global.swap(X_sys);
     640             :   }
     641       33564 : }
     642             : 
     643             : std::unique_ptr<NumericVector<Number>>
     644       42287 : createWrappedResidual(CondensedEigenSystem & sys, Vec r)
     645             : {
     646       42287 :   auto & dof_map = sys.get_dof_map();
     647             : 
     648       42287 :   if (dof_map.n_constrained_dofs())
     649        1201 :     return sys.solution->zero_clone();
     650             :   else
     651             :   {
     652       41086 :     auto R = std::make_unique<PetscVector<Number>>(r, sys.comm());
     653       41086 :     R->zero();
     654       41086 :     return R;
     655       41086 :   }
     656             : }
     657             : 
     658             : void
     659       16773 : evaluateResidual(EigenProblem & eigen_problem, Vec x, Vec r, TagID tag)
     660             : {
     661       16773 :   auto & nl = eigen_problem.getCurrentNonlinearEigenSystem();
     662       16773 :   auto & sys = nl.sys();
     663       16773 :   auto & dof_map = sys.get_dof_map();
     664             : 
     665       16773 :   updateCurrentLocalSolution(sys, x);
     666       16773 :   auto R = createWrappedResidual(sys, r);
     667             : 
     668       16773 :   eigen_problem.computeResidualTag(*sys.current_local_solution.get(), *R, tag);
     669             : 
     670       16773 :   R->close();
     671             : 
     672       16773 :   if (dof_map.n_constrained_dofs())
     673             :   {
     674         423 :     PetscVector<Number> sub_r(r, sys.comm());
     675         423 :     sys.copy_super_to_sub(*R, sub_r);
     676         423 :   }
     677       16773 : }
     678             : }
     679             : 
     680             : void
     681        3438 : moosePetscSNESFormMatrixTag(
     682             :     SNES /*snes*/, Vec x, Mat eigen_mat, SparseMatrix<Number> & all_dofs_mat, void * ctx, TagID tag)
     683             : {
     684        3438 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     685        3438 :   auto & nl = eigen_problem->getCurrentNonlinearEigenSystem();
     686        3438 :   auto & sys = nl.sys();
     687        3438 :   auto & dof_map = sys.get_dof_map();
     688             : 
     689             : #ifndef NDEBUG
     690             :   auto & petsc_all_dofs_mat = cast_ref<PetscMatrix<Number> &>(all_dofs_mat);
     691             :   mooseAssert(
     692             :       !dof_map.n_constrained_dofs() == (eigen_mat == petsc_all_dofs_mat.mat()),
     693             :       "If we do not have constrained dofs, then eigen_mat and all_dofs_mat should be the same. "
     694             :       "Conversely, if we do have constrained dofs, they must be different");
     695             : #endif
     696             : 
     697        3438 :   updateCurrentLocalSolution(sys, x);
     698             : 
     699        3438 :   if (!eigen_problem->constJacobian())
     700        3438 :     all_dofs_mat.zero();
     701             : 
     702        3438 :   eigen_problem->computeJacobianTag(*sys.current_local_solution.get(), all_dofs_mat, tag);
     703             : 
     704        3438 :   if (dof_map.n_constrained_dofs())
     705             :   {
     706          88 :     PetscMatrix<Number> wrapped_eigen_mat(eigen_mat, sys.comm());
     707          88 :     sys.copy_super_to_sub(all_dofs_mat, wrapped_eigen_mat);
     708          88 :   }
     709        3438 : }
     710             : 
     711             : void
     712         350 : moosePetscSNESFormMatricesTags(SNES /*snes*/,
     713             :                                Vec x,
     714             :                                std::vector<Mat> & eigen_mats,
     715             :                                std::vector<SparseMatrix<Number> *> & all_dofs_mats,
     716             :                                void * ctx,
     717             :                                const std::set<TagID> & tags)
     718             : {
     719         350 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     720         350 :   auto & nl = eigen_problem->getCurrentNonlinearEigenSystem();
     721         350 :   auto & sys = nl.sys();
     722         350 :   auto & dof_map = sys.get_dof_map();
     723             : 
     724             : #ifndef NDEBUG
     725             :   for (const auto i : index_range(eigen_mats))
     726             :     mooseAssert(!dof_map.n_constrained_dofs() ==
     727             :                     (eigen_mats[i] == cast_ptr<PetscMatrix<Number> *>(all_dofs_mats[i])->mat()),
     728             :                 "If we do not have constrained dofs, then mat and libmesh_mat should be the same. "
     729             :                 "Conversely, if we do have constrained dofs, they must be different");
     730             : #endif
     731             : 
     732         350 :   updateCurrentLocalSolution(sys, x);
     733             : 
     734        1050 :   for (auto * const all_dofs_mat : all_dofs_mats)
     735         700 :     if (!eigen_problem->constJacobian())
     736         700 :       all_dofs_mat->zero();
     737             : 
     738         350 :   eigen_problem->computeMatricesTags(*sys.current_local_solution.get(), all_dofs_mats, tags);
     739             : 
     740         350 :   if (dof_map.n_constrained_dofs())
     741          33 :     for (const auto i : index_range(eigen_mats))
     742             :     {
     743          22 :       PetscMatrix<Number> wrapped_eigen_mat(eigen_mats[i], sys.comm());
     744          22 :       sys.copy_super_to_sub(*all_dofs_mats[i], wrapped_eigen_mat);
     745          22 :     }
     746         350 : }
     747             : 
     748             : PetscErrorCode
     749        4383 : mooseSlepcEigenFormFunctionMFFD(void * ctx, Vec x, Vec r)
     750             : {
     751             :   PetscErrorCode (*func)(SNES, Vec, Vec, void *);
     752             :   void * fctx;
     753             :   PetscFunctionBegin;
     754             : 
     755        4383 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     756        4383 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     757        4383 :   SNES snes = eigen_nl.getSNES();
     758             : 
     759        4383 :   eigen_problem->onLinearSolver(true);
     760             : 
     761        4383 :   LibmeshPetscCallQ(SNESGetFunction(snes, NULL, &func, &fctx));
     762        4383 :   if (fctx != ctx)
     763             :   {
     764           0 :     SETERRQ(
     765             :         PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_INCOMP, "Contexts are not consistent \n");
     766             :   }
     767        4383 :   LibmeshPetscCallQ((*func)(snes, x, r, ctx));
     768             : 
     769        4383 :   eigen_problem->onLinearSolver(false);
     770             : 
     771        4383 :   PetscFunctionReturn(PETSC_SUCCESS);
     772             : }
     773             : 
     774             : PetscErrorCode
     775        4372 : mooseSlepcEigenFormJacobianA(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
     776             : {
     777             :   PetscBool jisshell, pisshell;
     778             :   PetscBool jismffd;
     779             : 
     780             :   PetscFunctionBegin;
     781             : 
     782        4372 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     783        4372 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     784        4372 :   auto & sys = eigen_nl.sys();
     785             : 
     786             :   // If both jacobian and preconditioning are shell matrices,
     787             :   // and then assemble them and return
     788        4372 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &jisshell));
     789        4372 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &jismffd));
     790             : 
     791        4372 :   if (jismffd && eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult)
     792             :   {
     793         644 :     LibmeshPetscCallQ(
     794             :         MatMFFDSetFunction(jac, Moose::SlepcSupport::mooseSlepcEigenFormFunctionMFFD, ctx));
     795             : 
     796         644 :     EPS eps = eigen_nl.getEPS();
     797             : 
     798         644 :     LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
     799             : 
     800         644 :     if (pc != jac)
     801             :     {
     802         644 :       LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
     803         644 :       LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
     804             :     }
     805         644 :     PetscFunctionReturn(PETSC_SUCCESS);
     806             :   }
     807             : 
     808        3728 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &pisshell));
     809        3728 :   if ((jisshell || jismffd) && pisshell)
     810             :   {
     811             :     // Just assemble matrices and return
     812         290 :     LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
     813         290 :     LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
     814         290 :     LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
     815         290 :     LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
     816             : 
     817         290 :     PetscFunctionReturn(PETSC_SUCCESS);
     818             :   }
     819             : 
     820             :   // Jacobian and precond matrix are the same
     821        3438 :   if (jac == pc)
     822             :   {
     823         221 :     if (!pisshell)
     824         221 :       moosePetscSNESFormMatrixTag(
     825             :           snes, x, pc, sys.get_matrix_A(), ctx, eigen_nl.precondMatrixTag());
     826             : 
     827         221 :     PetscFunctionReturn(PETSC_SUCCESS);
     828             :   }
     829             :   else
     830             :   {
     831        3217 :     if (!jisshell && !jismffd && !pisshell) // We need to form both Jacobian and precond matrix
     832             :     {
     833           0 :       std::vector<Mat> mats = {jac, pc};
     834           0 :       std::vector<SparseMatrix<Number> *> libmesh_mats = {&sys.get_matrix_A(),
     835           0 :                                                           &sys.get_precond_matrix()};
     836           0 :       std::set<TagID> tags = {eigen_nl.nonEigenMatrixTag(), eigen_nl.precondMatrixTag()};
     837           0 :       moosePetscSNESFormMatricesTags(snes, x, mats, libmesh_mats, ctx, tags);
     838           0 :       PetscFunctionReturn(PETSC_SUCCESS);
     839           0 :     }
     840        3217 :     if (!pisshell) // We need to form only precond matrix
     841             :     {
     842        3217 :       moosePetscSNESFormMatrixTag(
     843             :           snes, x, pc, sys.get_precond_matrix(), ctx, eigen_nl.precondMatrixTag());
     844        3217 :       LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
     845        3217 :       LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
     846        3217 :       PetscFunctionReturn(PETSC_SUCCESS);
     847             :     }
     848           0 :     if (!jisshell && !jismffd) // We need to form only Jacobian matrix
     849             :     {
     850           0 :       moosePetscSNESFormMatrixTag(
     851             :           snes, x, jac, sys.get_matrix_A(), ctx, eigen_nl.nonEigenMatrixTag());
     852           0 :       LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
     853           0 :       LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
     854           0 :       PetscFunctionReturn(PETSC_SUCCESS);
     855             :     }
     856             :   }
     857           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     858             : }
     859             : 
     860             : PetscErrorCode
     861           0 : mooseSlepcEigenFormJacobianB(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
     862             : {
     863             :   PetscBool jshell, pshell;
     864             :   PetscBool jismffd;
     865             : 
     866             :   PetscFunctionBegin;
     867             : 
     868           0 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     869           0 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     870           0 :   auto & sys = eigen_nl.sys();
     871             : 
     872             :   // If both jacobian and preconditioning are shell matrices,
     873             :   // and then assemble them and return
     874           0 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &jshell));
     875           0 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &jismffd));
     876           0 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &pshell));
     877           0 :   if ((jshell || jismffd) && pshell)
     878             :   {
     879             :     // Just assemble matrices and return
     880           0 :     LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
     881           0 :     LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
     882           0 :     LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
     883           0 :     LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
     884             : 
     885           0 :     PetscFunctionReturn(PETSC_SUCCESS);
     886             :   }
     887             : 
     888           0 :   if (jac != pc && (!jshell && !jshell))
     889           0 :     SETERRQ(PetscObjectComm((PetscObject)snes),
     890             :             PETSC_ERR_ARG_INCOMP,
     891             :             "Jacobian and precond matrices should be the same for eigen kernels \n");
     892             : 
     893           0 :   moosePetscSNESFormMatrixTag(snes, x, pc, sys.get_matrix_B(), ctx, eigen_nl.eigenMatrixTag());
     894             : 
     895           0 :   if (eigen_problem->negativeSignEigenKernel())
     896             :   {
     897           0 :     LibmeshPetscCallQ(MatScale(pc, -1.));
     898             :   }
     899             : 
     900           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     901             : }
     902             : 
     903             : void
     904       16773 : moosePetscSNESFormFunction(SNES /*snes*/, Vec x, Vec r, void * ctx, TagID tag)
     905             : {
     906       16773 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     907       16773 :   evaluateResidual(*eigen_problem, x, r, tag);
     908       16773 : }
     909             : 
     910             : PetscErrorCode
     911       16318 : mooseSlepcEigenFormFunctionA(SNES snes, Vec x, Vec r, void * ctx)
     912             : {
     913             :   PetscFunctionBegin;
     914             : 
     915       16318 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     916       16318 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     917             : 
     918       20015 :   if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
     919        3697 :       (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
     920             :   {
     921        2657 :     EPS eps = eigen_nl.getEPS();
     922             :     Mat A;
     923             :     ST st;
     924             : 
     925        2657 :     LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
     926             : 
     927             :     // Rest ST state so that we can restrieve matrices
     928        2657 :     LibmeshPetscCallQ(EPSGetST(eps, &st));
     929        2657 :     LibmeshPetscCallQ(STResetMatrixState(st));
     930        2657 :     LibmeshPetscCallQ(EPSGetOperators(eps, &A, NULL));
     931             : 
     932        2657 :     LibmeshPetscCallQ(MatMult(A, x, r));
     933             : 
     934        2657 :     PetscFunctionReturn(PETSC_SUCCESS);
     935             :   }
     936             : 
     937       13661 :   moosePetscSNESFormFunction(snes, x, r, ctx, eigen_nl.nonEigenVectorTag());
     938             : 
     939       13661 :   PetscFunctionReturn(PETSC_SUCCESS);
     940             : }
     941             : 
     942             : PetscErrorCode
     943        3364 : mooseSlepcEigenFormFunctionB(SNES snes, Vec x, Vec r, void * ctx)
     944             : {
     945             :   PetscFunctionBegin;
     946             : 
     947        3364 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     948        3364 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     949             : 
     950        4396 :   if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
     951        1032 :       (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
     952             :   {
     953         252 :     EPS eps = eigen_nl.getEPS();
     954             :     ST st;
     955             :     Mat B;
     956             : 
     957         252 :     LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
     958             : 
     959             :     // Rest ST state so that we can restrieve matrices
     960         252 :     LibmeshPetscCallQ(EPSGetST(eps, &st));
     961         252 :     LibmeshPetscCallQ(STResetMatrixState(st));
     962         252 :     LibmeshPetscCallQ(EPSGetOperators(eps, NULL, &B));
     963             : 
     964         252 :     LibmeshPetscCallQ(MatMult(B, x, r));
     965             : 
     966         252 :     if (eigen_problem->bxNormProvided())
     967             :     {
     968             :       // User has provided a postprocessor. We need it updated
     969           0 :       updateCurrentLocalSolution(eigen_nl.sys(), x);
     970           0 :       eigen_problem->execute(EXEC_LINEAR);
     971             :     }
     972             :   }
     973             :   else
     974        3112 :     moosePetscSNESFormFunction(snes, x, r, ctx, eigen_nl.eigenVectorTag());
     975             : 
     976        3364 :   if (eigen_problem->negativeSignEigenKernel())
     977             :   {
     978        3364 :     LibmeshPetscCallQ(VecScale(r, -1.));
     979             :   }
     980             : 
     981        3364 :   PetscFunctionReturn(PETSC_SUCCESS);
     982             : }
     983             : 
     984             : PetscErrorCode
     985       15029 : mooseSlepcEigenFormFunctionAB(SNES /*snes*/, Vec x, Vec Ax, Vec Bx, void * ctx)
     986             : {
     987             :   PetscFunctionBegin;
     988             : 
     989       15029 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
     990       15029 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
     991       15029 :   auto & sys = eigen_nl.sys();
     992       15029 :   auto & dof_map = sys.get_dof_map();
     993             : 
     994       17863 :   if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
     995        2834 :       (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
     996             :   {
     997        2272 :     EPS eps = eigen_nl.getEPS();
     998             :     ST st;
     999             :     Mat A, B;
    1000             : 
    1001        2272 :     LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
    1002             : 
    1003             :     // Rest ST state so that we can restrieve matrices
    1004        2272 :     LibmeshPetscCallQ(EPSGetST(eps, &st));
    1005        2272 :     LibmeshPetscCallQ(STResetMatrixState(st));
    1006             : 
    1007        2272 :     LibmeshPetscCallQ(EPSGetOperators(eps, &A, &B));
    1008             : 
    1009        2272 :     LibmeshPetscCallQ(MatMult(A, x, Ax));
    1010        2272 :     LibmeshPetscCallQ(MatMult(B, x, Bx));
    1011             : 
    1012        2272 :     if (eigen_problem->negativeSignEigenKernel())
    1013        2272 :       LibmeshPetscCallQ(VecScale(Bx, -1.));
    1014             : 
    1015        2272 :     if (eigen_problem->bxNormProvided())
    1016             :     {
    1017             :       // User has provided a postprocessor. We need it updated
    1018         246 :       updateCurrentLocalSolution(sys, x);
    1019         246 :       eigen_problem->execute(EXEC_LINEAR);
    1020             :     }
    1021             : 
    1022        2272 :     PetscFunctionReturn(PETSC_SUCCESS);
    1023             :   }
    1024             : 
    1025       12757 :   updateCurrentLocalSolution(sys, x);
    1026       12757 :   auto AX = createWrappedResidual(sys, Ax);
    1027       12757 :   auto BX = createWrappedResidual(sys, Bx);
    1028             : 
    1029       25514 :   eigen_problem->computeResidualAB(*sys.current_local_solution.get(),
    1030       12757 :                                    *AX,
    1031       12757 :                                    *BX,
    1032             :                                    eigen_nl.nonEigenVectorTag(),
    1033             :                                    eigen_nl.eigenVectorTag());
    1034             : 
    1035       12757 :   AX->close();
    1036       12757 :   BX->close();
    1037             : 
    1038       12757 :   if (dof_map.n_constrained_dofs())
    1039             :   {
    1040         389 :     PetscVector<Number> sub_Ax(Ax, sys.comm());
    1041         389 :     sys.copy_super_to_sub(*AX, sub_Ax);
    1042         389 :     PetscVector<Number> sub_Bx(Bx, sys.comm());
    1043         389 :     sys.copy_super_to_sub(*BX, sub_Bx);
    1044         389 :   }
    1045             : 
    1046       12757 :   if (eigen_problem->negativeSignEigenKernel())
    1047       12757 :     LibmeshPetscCallQ(VecScale(Bx, -1.));
    1048             : 
    1049       12757 :   PetscFunctionReturn(PETSC_SUCCESS);
    1050       12757 : }
    1051             : 
    1052             : PetscErrorCode
    1053        2314 : mooseSlepcEigenFormNorm(SNES /*snes*/, Vec /*Bx*/, PetscReal * norm, void * ctx)
    1054             : {
    1055             :   PetscFunctionBegin;
    1056        2314 :   auto * const eigen_problem = static_cast<EigenProblem *>(ctx);
    1057        2314 :   *norm = eigen_problem->formNorm();
    1058        2314 :   PetscFunctionReturn(PETSC_SUCCESS);
    1059             : }
    1060             : 
    1061             : void
    1062        1927 : attachCallbacksToMat(EigenProblem & eigen_problem, Mat mat, bool eigen)
    1063             : {
    1064             :   // Recall that we are solving the potentially nonlinear problem:
    1065             :   // F(x) = A(x) - \lambda B(x) = 0
    1066             :   //
    1067             :   // To solve this, we can use Newton's method: J \Delta x = -F
    1068             :   // Generally we will approximate J using matrix free methods. However, in order to solve the
    1069             :   // linearized system efficiently, we typically will need preconditioning. Typically we will build
    1070             :   // the preconditioner only from A, but we also have the option to include information from B
    1071             : 
    1072             :   // Attach the Jacobian computation function. If \p mat is the "eigen" matrix corresponding to B,
    1073             :   // then attach our JacobianB computation routine, else the matrix corresponds to A, and we attach
    1074             :   // the JacobianA computation routine
    1075        1927 :   LibmeshPetscCallA(
    1076             :       eigen_problem.comm().get(),
    1077             :       PetscObjectComposeFunction((PetscObject)mat,
    1078             :                                  "formJacobian",
    1079             :                                  eigen ? Moose::SlepcSupport::mooseSlepcEigenFormJacobianB
    1080             :                                        : Moose::SlepcSupport::mooseSlepcEigenFormJacobianA));
    1081             : 
    1082             :   // Attach the residual computation function. If \p mat is the "eigen" matrix corresponding to B,
    1083             :   // then attach our FunctionB computation routine, else the matrix corresponds to A, and we attach
    1084             :   // the FunctionA computation routine
    1085        1927 :   LibmeshPetscCallA(
    1086             :       eigen_problem.comm().get(),
    1087             :       PetscObjectComposeFunction((PetscObject)mat,
    1088             :                                  "formFunction",
    1089             :                                  eigen ? Moose::SlepcSupport::mooseSlepcEigenFormFunctionB
    1090             :                                        : Moose::SlepcSupport::mooseSlepcEigenFormFunctionA));
    1091             : 
    1092             :   // It's also beneficial to be able to evaluate both A and B residuals at once
    1093        1927 :   LibmeshPetscCallA(eigen_problem.comm().get(),
    1094             :                     PetscObjectComposeFunction((PetscObject)mat,
    1095             :                                                "formFunctionAB",
    1096             :                                                Moose::SlepcSupport::mooseSlepcEigenFormFunctionAB));
    1097             : 
    1098             :   // Users may choose to provide a custom measure of the norm of B (Bx for a linear system)
    1099        1927 :   if (eigen_problem.bxNormProvided())
    1100          82 :     LibmeshPetscCallA(eigen_problem.comm().get(),
    1101             :                       PetscObjectComposeFunction((PetscObject)mat,
    1102             :                                                  "formNorm",
    1103             :                                                  Moose::SlepcSupport::mooseSlepcEigenFormNorm));
    1104             : 
    1105             :   // Finally we need to attach the "context" object, which is our EigenProblem, to the matrices so
    1106             :   // that eventually when we get callbacks from SLEPc we can call methods on the EigenProblem
    1107             :   PetscContainer container;
    1108        1927 :   LibmeshPetscCallA(eigen_problem.comm().get(),
    1109             :                     PetscContainerCreate(eigen_problem.comm().get(), &container));
    1110        1927 :   LibmeshPetscCallA(eigen_problem.comm().get(),
    1111             :                     PetscContainerSetPointer(container, &eigen_problem));
    1112        1927 :   LibmeshPetscCallA(
    1113             :       eigen_problem.comm().get(),
    1114             :       PetscObjectCompose((PetscObject)mat, "formJacobianCtx", (PetscObject)container));
    1115        1927 :   LibmeshPetscCallA(
    1116             :       eigen_problem.comm().get(),
    1117             :       PetscObjectCompose((PetscObject)mat, "formFunctionCtx", (PetscObject)container));
    1118        1927 :   if (eigen_problem.bxNormProvided())
    1119          82 :     LibmeshPetscCallA(eigen_problem.comm().get(),
    1120             :                       PetscObjectCompose((PetscObject)mat, "formNormCtx", (PetscObject)container));
    1121             : 
    1122        1927 :   LibmeshPetscCallA(eigen_problem.comm().get(), PetscContainerDestroy(&container));
    1123        1927 : }
    1124             : 
    1125             : PetscErrorCode
    1126           0 : mooseMatMult_Eigen(Mat mat, Vec x, Vec r)
    1127             : {
    1128             :   PetscFunctionBegin;
    1129           0 :   void * ctx = nullptr;
    1130           0 :   LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
    1131             : 
    1132           0 :   if (!ctx)
    1133           0 :     mooseError("No context is set for shell matrix ");
    1134             : 
    1135           0 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
    1136           0 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
    1137             : 
    1138           0 :   evaluateResidual(*eigen_problem, x, r, eigen_nl.eigenVectorTag());
    1139             : 
    1140           0 :   if (eigen_problem->negativeSignEigenKernel())
    1141           0 :     LibmeshPetscCallQ(VecScale(r, -1.));
    1142             : 
    1143           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1144             : }
    1145             : 
    1146             : PetscErrorCode
    1147           0 : mooseMatMult_NonEigen(Mat mat, Vec x, Vec r)
    1148             : {
    1149             :   PetscFunctionBegin;
    1150           0 :   void * ctx = nullptr;
    1151           0 :   LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
    1152             : 
    1153           0 :   if (!ctx)
    1154           0 :     mooseError("No context is set for shell matrix ");
    1155             : 
    1156           0 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
    1157           0 :   NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
    1158             : 
    1159           0 :   evaluateResidual(*eigen_problem, x, r, eigen_nl.nonEigenVectorTag());
    1160             : 
    1161           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1162             : }
    1163             : 
    1164             : void
    1165        1104 : setOperationsForShellMat(EigenProblem & eigen_problem, Mat mat, bool eigen)
    1166             : {
    1167        1104 :   LibmeshPetscCallA(eigen_problem.comm().get(), MatShellSetContext(mat, &eigen_problem));
    1168        1104 :   LibmeshPetscCallA(eigen_problem.comm().get(),
    1169             :                     MatShellSetOperation(mat,
    1170             :                                          MATOP_MULT,
    1171             :                                          eigen ? (void (*)(void))mooseMatMult_Eigen
    1172             :                                                : (void (*)(void))mooseMatMult_NonEigen));
    1173        1104 : }
    1174             : 
    1175             : PETSC_EXTERN PetscErrorCode
    1176          50 : registerPCToPETSc()
    1177             : {
    1178             :   PetscFunctionBegin;
    1179             : 
    1180          50 :   LibmeshPetscCallQ(PCRegister("moosepc", PCCreate_MoosePC));
    1181             : 
    1182          50 :   PetscFunctionReturn(PETSC_SUCCESS);
    1183             : }
    1184             : 
    1185             : PETSC_EXTERN PetscErrorCode
    1186         100 : PCCreate_MoosePC(PC pc)
    1187             : {
    1188             :   PetscFunctionBegin;
    1189             : 
    1190         100 :   pc->ops->view = PCView_MoosePC;
    1191         100 :   pc->ops->destroy = PCDestroy_MoosePC;
    1192         100 :   pc->ops->setup = PCSetUp_MoosePC;
    1193         100 :   pc->ops->apply = PCApply_MoosePC;
    1194             : 
    1195         100 :   PetscFunctionReturn(PETSC_SUCCESS);
    1196             : }
    1197             : 
    1198             : PetscErrorCode
    1199         100 : PCDestroy_MoosePC(PC /*pc*/)
    1200             : {
    1201             :   PetscFunctionBegin;
    1202             :   /* We do not need to do anything right now, but later we may have some data we need to free here
    1203             :    */
    1204         100 :   PetscFunctionReturn(PETSC_SUCCESS);
    1205             : }
    1206             : 
    1207             : PetscErrorCode
    1208          80 : PCView_MoosePC(PC /*pc*/, PetscViewer viewer)
    1209             : {
    1210             :   PetscBool iascii;
    1211             : 
    1212             :   PetscFunctionBegin;
    1213          80 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
    1214          80 :   if (iascii)
    1215          80 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "  %s\n", "moosepc"));
    1216             : 
    1217          80 :   PetscFunctionReturn(PETSC_SUCCESS);
    1218             : }
    1219             : 
    1220             : PetscErrorCode
    1221        1230 : PCApply_MoosePC(PC pc, Vec x, Vec y)
    1222             : {
    1223             :   void * ctx;
    1224             :   Mat Amat, Pmat;
    1225             :   PetscContainer container;
    1226             : 
    1227             :   PetscFunctionBegin;
    1228        1230 :   LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
    1229        1230 :   LibmeshPetscCallQ(
    1230             :       PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
    1231        1230 :   if (container)
    1232        1230 :     LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
    1233             :   else
    1234           0 :     mooseError(" Can not find a context \n");
    1235             : 
    1236        1230 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
    1237        1230 :   NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
    1238        1230 :   auto preconditioner = nl_eigen.preconditioner();
    1239             : 
    1240        1230 :   if (!preconditioner)
    1241           0 :     mooseError("There is no moose preconditioner in nonlinear eigen system \n");
    1242             : 
    1243        1230 :   PetscVector<Number> x_vec(x, preconditioner->comm());
    1244        1230 :   PetscVector<Number> y_vec(y, preconditioner->comm());
    1245             : 
    1246        1230 :   preconditioner->apply(x_vec, y_vec);
    1247             : 
    1248        1230 :   PetscFunctionReturn(PETSC_SUCCESS);
    1249        1230 : }
    1250             : 
    1251             : PetscErrorCode
    1252         360 : PCSetUp_MoosePC(PC pc)
    1253             : {
    1254             :   void * ctx;
    1255             :   Mat Amat, Pmat;
    1256             :   PetscContainer container;
    1257             : 
    1258             :   PetscFunctionBegin;
    1259         360 :   LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
    1260         360 :   LibmeshPetscCallQ(
    1261             :       PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
    1262         360 :   if (container)
    1263         360 :     LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
    1264             :   else
    1265           0 :     mooseError(" Can not find a context \n");
    1266             : 
    1267         360 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
    1268         360 :   NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
    1269         360 :   Preconditioner<Number> * preconditioner = nl_eigen.preconditioner();
    1270             : 
    1271         360 :   if (!preconditioner)
    1272           0 :     mooseError("There is no moose preconditioner in nonlinear eigen system \n");
    1273             : 
    1274         360 :   if (!preconditioner->initialized())
    1275          50 :     preconditioner->init();
    1276             : 
    1277         360 :   preconditioner->setup();
    1278             : 
    1279         360 :   PetscFunctionReturn(PETSC_SUCCESS);
    1280             : }
    1281             : 
    1282             : PetscErrorCode
    1283        2824 : mooseSlepcStoppingTest(EPS eps,
    1284             :                        PetscInt its,
    1285             :                        PetscInt max_it,
    1286             :                        PetscInt nconv,
    1287             :                        PetscInt nev,
    1288             :                        EPSConvergedReason * reason,
    1289             :                        void * ctx)
    1290             : {
    1291        2824 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
    1292             : 
    1293             :   PetscFunctionBegin;
    1294        2824 :   LibmeshPetscCallQ(EPSStoppingBasic(eps, its, max_it, nconv, nev, reason, NULL));
    1295             : 
    1296             :   // If we do free power iteration, we need to mark the solver as converged.
    1297             :   // It is because SLEPc does not offer a way to copy unconverged solution.
    1298             :   // If the solver is not marked as "converged", we have no way to get solution
    1299             :   // from slepc. Note marking as "converged" has no side-effects at all for us.
    1300             :   // If free power iteration is used as a stand-alone solver, we won't trigger
    1301             :   // as "doFreePowerIteration()" is false.
    1302        2824 :   if (eigen_problem->doFreePowerIteration() && its == max_it && *reason <= 0)
    1303             :   {
    1304         507 :     *reason = EPS_CONVERGED_USER;
    1305         507 :     eps->nconv = 1;
    1306             :   }
    1307        2824 :   PetscFunctionReturn(PETSC_SUCCESS);
    1308             : }
    1309             : 
    1310             : PetscErrorCode
    1311        8367 : mooseSlepcEPSGetSNES(EPS eps, SNES * snes)
    1312             : {
    1313             :   PetscBool same, nonlinear;
    1314             : 
    1315             :   PetscFunctionBegin;
    1316        8367 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)eps, EPSPOWER, &same));
    1317             : 
    1318        8367 :   if (!same)
    1319           0 :     mooseError("It is not eps power, and there is no snes");
    1320             : 
    1321        8367 :   LibmeshPetscCallQ(EPSPowerGetNonlinear(eps, &nonlinear));
    1322             : 
    1323        8367 :   if (!nonlinear)
    1324           0 :     mooseError("It is not a nonlinear eigen solver");
    1325             : 
    1326        8367 :   LibmeshPetscCallQ(EPSPowerGetSNES(eps, snes));
    1327             : 
    1328        8367 :   PetscFunctionReturn(PETSC_SUCCESS);
    1329             : }
    1330             : 
    1331             : PetscErrorCode
    1332        1178 : mooseSlepcEPSSNESSetUpOptionPrefix(EPS eps)
    1333             : {
    1334             :   SNES snes;
    1335        1178 :   const char * prefix = nullptr;
    1336             : 
    1337             :   PetscFunctionBegin;
    1338        1178 :   LibmeshPetscCallQ(mooseSlepcEPSGetSNES(eps, &snes));
    1339             :   // There is an extra "eps_power" in snes that users do not like it.
    1340             :   // Let us remove that from snes.
    1341             :   // Retrieve option prefix from EPS
    1342        1178 :   LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)eps, &prefix));
    1343             :   // Set option prefix to SNES
    1344        1178 :   LibmeshPetscCallQ(SNESSetOptionsPrefix(snes, prefix));
    1345             : 
    1346        1178 :   PetscFunctionReturn(PETSC_SUCCESS);
    1347             : }
    1348             : 
    1349             : PetscErrorCode
    1350         100 : mooseSlepcEPSSNESSetCustomizePC(EPS eps)
    1351             : {
    1352             :   SNES snes;
    1353             :   KSP ksp;
    1354             :   PC pc;
    1355             : 
    1356             :   PetscFunctionBegin;
    1357             :   // Get SNES from EPS
    1358         100 :   LibmeshPetscCallQ(mooseSlepcEPSGetSNES(eps, &snes));
    1359             :   // Get KSP from SNES
    1360         100 :   LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
    1361             :   // Get PC from KSP
    1362         100 :   LibmeshPetscCallQ(KSPGetPC(ksp, &pc));
    1363             :   // Set PC type
    1364         100 :   LibmeshPetscCallQ(PCSetType(pc, "moosepc"));
    1365         100 :   PetscFunctionReturn(PETSC_SUCCESS);
    1366             : }
    1367             : 
    1368             : PetscErrorCode
    1369        1178 : mooseSlepcEPSSNESKSPSetPCSide(FEProblemBase & problem, EPS eps)
    1370             : {
    1371             :   SNES snes;
    1372             :   KSP ksp;
    1373             : 
    1374             :   PetscFunctionBegin;
    1375             :   // Get SNES from EPS
    1376        1178 :   LibmeshPetscCallQ(mooseSlepcEPSGetSNES(eps, &snes));
    1377             :   // Get KSP from SNES
    1378        1178 :   LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
    1379             : 
    1380        1178 :   Moose::PetscSupport::petscSetDefaultPCSide(problem, ksp);
    1381             : 
    1382        1178 :   Moose::PetscSupport::petscSetDefaultKSPNormType(problem, ksp);
    1383        1178 :   PetscFunctionReturn(PETSC_SUCCESS);
    1384             : }
    1385             : 
    1386             : PetscErrorCode
    1387        4834 : mooseSlepcEPSMonitor(EPS eps,
    1388             :                      PetscInt its,
    1389             :                      PetscInt /*nconv*/,
    1390             :                      PetscScalar * eigr,
    1391             :                      PetscScalar * eigi,
    1392             :                      PetscReal * /*errest*/,
    1393             :                      PetscInt /*nest*/,
    1394             :                      void * mctx)
    1395             : {
    1396             :   ST st;
    1397             :   PetscScalar eigenr, eigeni;
    1398             : 
    1399             :   PetscFunctionBegin;
    1400        4834 :   EigenProblem * eigen_problem = static_cast<EigenProblem *>(mctx);
    1401        4834 :   auto & console = eigen_problem->console();
    1402             : 
    1403        4834 :   auto inverse = eigen_problem->outputInverseEigenvalue();
    1404        4834 :   LibmeshPetscCallQ(EPSGetST(eps, &st));
    1405        4834 :   eigenr = eigr[0];
    1406        4834 :   eigeni = eigi[0];
    1407             :   // Make the eigenvalue consistent with shift type
    1408        4834 :   LibmeshPetscCallQ(STBackTransform(st, 1, &eigenr, &eigeni));
    1409             : 
    1410        4834 :   auto eigenvalue = inverse ? 1.0 / eigenr : eigenr;
    1411             : 
    1412             :   // The term "k-eigenvalue" is adopted from the neutronics community.
    1413        4834 :   console << " Iteration " << its << std::setprecision(10) << std::fixed
    1414        4834 :           << (inverse ? " k-eigenvalue = " : " eigenvalue = ") << eigenvalue << std::endl;
    1415             : 
    1416        4834 :   PetscFunctionReturn(PETSC_SUCCESS);
    1417             : }
    1418             : 
    1419             : } // namespace SlepcSupport
    1420             : } // namespace moose
    1421             : 
    1422             : #endif // LIBMESH_HAVE_SLEPC

Generated by: LCOV version 1.14