https://mooseframework.inl.gov
SlepcSupport.C
Go to the documentation of this file.
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 
39 {
40  MooseEnum solve_type("POWER ARNOLDI KRYLOVSCHUR JACOBI_DAVIDSON "
41  "NONLINEAR_POWER NEWTON PJFNK PJFNKMO JFNK",
42  "PJFNK");
43  params.set<MooseEnum>("solve_type") = solve_type;
44 
45  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  params.set<Real>("l_tol") = 1e-2;
62 
63  return params;
64 }
65 
68 {
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  "GEN_NON_HERMITIAN");
75  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  "TARGET_IMAGINARY ALL_EIGENVALUES SLEPC_DEFAULT");
91  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  params.addParam<unsigned int>("n_eigen_pairs", 1, "The number of eigen pairs");
107  params.addParam<unsigned int>("n_basis_vectors", 3, "The dimension of eigen subspaces");
108 
109  params.addParam<Real>("eigen_tol", 1.0e-4, "Relative Tolerance for Eigen Solver");
110  params.addParam<unsigned int>("eigen_max_its", 10000, "Max Iterations for Eigen Solver");
111 
112  params.addParam<Real>("l_abs_tol", 1e-50, "Absolute Tolerances for Linear Solver");
113 
114  params.addParam<unsigned int>("free_power_iterations", 4, "The number of free power iterations");
115 
116  params.addParam<unsigned int>(
117  "extra_power_iterations", 0, "The number of extra free power iterations");
118 
119  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  params.addParamNamesToGroup("l_abs_tol", "Linear solver");
124 
125  return params;
126 }
127 
128 void
130  const SolverParams & solver_params,
131  const InputParameters & params)
132 {
133  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 
139  solver_params._prefix + "eps_tol",
140  stringify(params.get<Real>("eigen_tol")));
141 
143  dont_add_these_options,
144  solver_params._prefix + "eps_max_it",
145  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  if (eigen_problem.isNonlinearEigenvalueSolver(solver_params._solver_sys_num))
150  {
151  // nonlinear solver tolerances
153  dont_add_these_options,
154  solver_params._prefix + "snes_max_it",
155  stringify(params.get<unsigned int>("nl_max_its")));
156 
158  dont_add_these_options,
159  solver_params._prefix + "snes_max_funcs",
160  stringify(params.get<unsigned int>("nl_max_funcs")));
161 
163  dont_add_these_options,
164  solver_params._prefix + "snes_atol",
165  stringify(params.get<Real>("nl_abs_tol")));
166 
168  dont_add_these_options,
169  solver_params._prefix + "snes_rtol",
170  stringify(params.get<Real>("nl_rel_tol")));
171 
173  dont_add_these_options,
174  solver_params._prefix + "snes_stol",
175  stringify(params.get<Real>("nl_rel_step_tol")));
176 
177  // linear solver
179  dont_add_these_options,
180  solver_params._prefix + "ksp_max_it",
181  stringify(params.get<unsigned int>("l_max_its")));
182 
184  solver_params._prefix + "ksp_rtol",
185  stringify(params.get<Real>("l_tol")));
186 
188  dont_add_these_options,
189  solver_params._prefix + "ksp_atol",
190  stringify(params.get<Real>("l_abs_tol")));
191  }
192  else
193  { // linear eigenvalue problem
194  // linear solver
196  dont_add_these_options,
197  solver_params._prefix + "st_ksp_max_it",
198  stringify(params.get<unsigned int>("l_max_its")));
199 
201  solver_params._prefix + "st_ksp_rtol",
202  stringify(params.get<Real>("l_tol")));
203 
205  dont_add_these_options,
206  solver_params._prefix + "st_ksp_atol",
207  stringify(params.get<Real>("l_abs_tol")));
208  }
209 }
210 
211 void
213 {
214  for (const auto i : make_range(eigen_problem.numNonlinearSystems()))
215  {
216  const std::string & eigen_problem_type = params.get<MooseEnum>("eigen_problem_type");
217  if (!eigen_problem_type.empty())
218  eigen_problem.solverParams(i)._eigen_problem_type =
219  Moose::stringToEnum<Moose::EigenProblemType>(eigen_problem_type);
220  else
221  mooseError("Have to specify a valid eigen problem type");
222 
223  const std::string & which_eigen_pairs = params.get<MooseEnum>("which_eigen_pairs");
224  if (!which_eigen_pairs.empty())
225  eigen_problem.solverParams(i)._which_eigen_pairs =
226  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  unsigned int n_eigen_pairs = params.get<unsigned int>("n_eigen_pairs");
233  unsigned int n_basis_vectors = params.get<unsigned int>("n_basis_vectors");
234 
235  eigen_problem.setNEigenPairsRequired(n_eigen_pairs);
236 
237  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  if (subspace_factor * n_eigen_pairs > n_basis_vectors)
241  {
242  n_basis_vectors = subspace_factor * n_eigen_pairs;
243  mooseWarning(
244  "Number of subspaces in Eigensolver is changed by moose because the value you set "
245  "is too small");
246  }
247 
248  eigen_problem.es().parameters.set<unsigned int>("basis vectors") = n_basis_vectors;
249 
250  // Operators A and B are formed as shell matrices
251  eigen_problem.solverParams(i)._eigen_matrix_free = params.get<bool>("matrix_free");
252 
253  // Preconditioning is formed as a shell matrix
254  eigen_problem.solverParams(i)._precond_matrix_free = params.get<bool>("precond_matrix_free");
255 
256  if (params.get<MooseEnum>("solve_type") == "PJFNK")
257  {
258  eigen_problem.solverParams(i)._eigen_matrix_free = true;
259  }
260  if (params.get<MooseEnum>("solve_type") == "JFNK")
261  {
262  eigen_problem.solverParams(i)._eigen_matrix_free = true;
263  eigen_problem.solverParams(i)._precond_matrix_free = true;
264  }
265  // We need matrices so that we can implement residual evaluations
266  if (params.get<MooseEnum>("solve_type") == "PJFNKMO")
267  {
268  eigen_problem.solverParams(i)._eigen_matrix_free = true;
269  eigen_problem.solverParams(i)._precond_matrix_free = false;
270  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  eigen_problem.setCoupling(Moose::COUPLING_FULL);
274  }
275  }
276 
277  eigen_problem.constantMatrices(params.get<bool>("constant_matrices"));
278 
279  if (eigen_problem.constantMatrices() && params.get<MooseEnum>("solve_type") != "PJFNKMO")
280  {
281  mooseError("constant_matrices flag is only valid for solve type: PJFNKMO");
282  }
283 }
284 
285 void
286 storeSolveType(FEProblemBase & fe_problem, const InputParameters & params)
287 {
288  if (!(dynamic_cast<EigenProblem *>(&fe_problem)))
289  return;
290 
291  if (params.isParamValid("solve_type"))
292  for (const auto i : make_range(fe_problem.numNonlinearSystems()))
293  fe_problem.solverParams(i)._eigen_solve_type =
294  Moose::stringToEnum<Moose::EigenSolveType>(params.get<MooseEnum>("solve_type"));
295 }
296 
297 void
298 setEigenProblemOptions(SolverParams & solver_params, const MultiMooseEnum & dont_add_these_options)
299 {
300  switch (solver_params._eigen_problem_type)
301  {
304  "-eps_hermitian");
305  break;
306 
309  "-eps_non_hermitian");
310  break;
311 
314  "-eps_gen_hermitian");
315  break;
316 
319  "-eps_gen_indefinite");
320  break;
321 
324  "-eps_gen_non_hermitian");
325  break;
326 
329  "-eps_pos_gen_non_hermitian");
330  break;
331 
333  break;
334 
335  default:
336  mooseError("Unknown eigen solver type \n");
337  }
338 }
339 
340 void
342  const MultiMooseEnum & dont_add_these_options)
343 {
344  switch (solver_params._which_eigen_pairs)
345  {
348  "-eps_largest_magnitude");
349  break;
350 
353  "-eps_smallest_magnitude");
354  break;
355 
358  "-eps_largest_real");
359  break;
360 
363  "-eps_smallest_real");
364  break;
365 
368  "-eps_largest_imaginary");
369  break;
370 
373  "-eps_smallest_imaginary");
374  break;
375 
378  "-eps_target_magnitude");
379  break;
380 
383  "-eps_target_real");
384  break;
385 
388  "-eps_target_imaginary");
389  break;
390 
392  Moose::PetscSupport::setSinglePetscOptionIfAppropriate(dont_add_these_options, "-eps_all");
393  break;
394 
396  break;
397 
398  default:
399  mooseError("Unknown type of WhichEigenPairs \n");
400  }
401 }
402 
403 void
404 setFreeNonlinearPowerIterations(unsigned int free_power_iterations)
405 {
406  Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "0");
407  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  Moose::PetscSupport::setSinglePetscOption("-snes_rtol", "0.99999999999");
412  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  Moose::PetscSupport::setSinglePetscOption("-eps_tol", "1e-50");
416 }
417 
418 void
420 {
421  Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "1");
422  Moose::PetscSupport::setSinglePetscOption("-eps_max_it", "1");
424  stringify(params.get<unsigned int>("nl_max_its")));
426  stringify(params.get<Real>("nl_rel_tol")));
427  Moose::PetscSupport::setSinglePetscOption("-eps_tol", stringify(params.get<Real>("eigen_tol")));
428 }
429 
430 void
431 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  bool initial_power = params.get<bool>("_newton_inverse_power");
436 
437  Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
438  Moose::PetscSupport::setSinglePetscOption("-eps_power_nonlinear", "1");
439  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  Moose::PetscSupport::setSinglePetscOption("-eps_max_it", "1");
443  if (initial_power)
444  {
445  Moose::PetscSupport::setSinglePetscOption("-init_eps_power_snes_max_it", "1");
446  Moose::PetscSupport::setSinglePetscOption("-init_eps_power_ksp_rtol", "1e-2");
448  "-init_eps_max_it", stringify(params.get<unsigned int>("free_power_iterations")));
449  }
450  Moose::PetscSupport::setSinglePetscOption("-eps_target_magnitude", "");
451  if (solver_params._eigen_matrix_free)
452  {
453  Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "1");
454  if (initial_power)
455  Moose::PetscSupport::setSinglePetscOption("-init_eps_power_snes_mf_operator", "1");
456  }
457  else
458  {
459  Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "0");
460  if (initial_power)
461  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 }
472 
473 void
475 {
476 #if !SLEPC_VERSION_LESS_THAN(3, 8, 0) || !PETSC_VERSION_RELEASE
477  Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
478  Moose::PetscSupport::setSinglePetscOption("-eps_power_nonlinear", "1");
479  Moose::PetscSupport::setSinglePetscOption("-eps_target_magnitude", "");
480  if (solver_params._eigen_matrix_free)
481  Moose::PetscSupport::setSinglePetscOption("-snes_mf_operator", "1");
482  else
483  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 }
492 
493 void
494 setEigenSolverOptions(SolverParams & solver_params, const InputParameters & params)
495 {
496  // Avoid unused variable warnings when you have SLEPc but not PETSc-dev.
497  libmesh_ignore(params);
498 
499  switch (solver_params._eigen_solve_type)
500  {
501  case Moose::EST_POWER:
502  Moose::PetscSupport::setSinglePetscOption("-eps_type", "power");
503  break;
504 
505  case Moose::EST_ARNOLDI:
506  Moose::PetscSupport::setSinglePetscOption("-eps_type", "arnoldi");
507  break;
508 
510  Moose::PetscSupport::setSinglePetscOption("-eps_type", "krylovschur");
511  break;
512 
515  break;
516 
518  setNonlinearPowerOptions(solver_params);
519  break;
520 
521  case Moose::EST_NEWTON:
522  setNewtonPetscOptions(solver_params, params);
523  break;
524 
525  case Moose::EST_PJFNK:
526  solver_params._eigen_matrix_free = true;
527  solver_params._customized_pc_for_eigen = false;
528  setNewtonPetscOptions(solver_params, params);
529  break;
530 
531  case Moose::EST_JFNK:
532  solver_params._eigen_matrix_free = true;
533  solver_params._customized_pc_for_eigen = true;
534  setNewtonPetscOptions(solver_params, params);
535  break;
536 
537  case Moose::EST_PJFNKMO:
538  solver_params._eigen_matrix_free = true;
539  solver_params._customized_pc_for_eigen = false;
540  solver_params._eigen_matrix_vector_mult = true;
541  setNewtonPetscOptions(solver_params, params);
542  break;
543 
544  default:
545  mooseError("Unknown eigen solver type \n");
546  }
547 }
548 
549 void
551  SolverParams & solver_params,
552  const InputParameters & params)
553 {
554  const auto & dont_add_these_options = eigen_problem.getPetscOptions().dont_add_these_options;
555 
557  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  setSlepcEigenSolverTolerances(eigen_problem, solver_params, params);
561  setEigenSolverOptions(solver_params, params);
562  // when Bx norm postprocessor is provided, we switch off the sign normalization
563  if (eigen_problem.bxNormProvided())
565  dont_add_these_options, "-eps_power_sign_normalization", "0", &eigen_problem);
566  setEigenProblemOptions(solver_params, eigen_problem.getPetscOptions().dont_add_these_options);
567  setWhichEigenPairsOptions(solver_params, eigen_problem.getPetscOptions().dont_add_these_options);
569 }
570 
571 // For matrices A and B
572 PetscErrorCode
573 mooseEPSFormMatrices(EigenProblem & eigen_problem, EPS eps, Vec x, void * ctx)
574 {
575  ST st;
576  Mat A, B;
577  PetscBool aisshell, bisshell;
579 
580  if (eigen_problem.constantMatrices() && eigen_problem.wereMatricesFormed())
581  PetscFunctionReturn(PETSC_SUCCESS);
582 
583  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  PetscFunctionReturn(PETSC_SUCCESS);
588 
589  NonlinearEigenSystem & eigen_nl = eigen_problem.getCurrentNonlinearEigenSystem();
590  auto & sys = eigen_nl.sys();
591  SNES snes = eigen_nl.getSNES();
592  // Rest ST state so that we can retrieve matrices
593  LibmeshPetscCallQ(EPSGetST(eps, &st));
594  LibmeshPetscCallQ(STResetMatrixState(st));
595  LibmeshPetscCallQ(EPSGetOperators(eps, &A, &B));
596  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)A, MATSHELL, &aisshell));
597  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)B, MATSHELL, &bisshell));
598  if (aisshell || bisshell)
599  {
600  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  std::vector<Mat> mats = {A, B};
606  std::vector<SparseMatrix<Number> *> libmesh_mats = {&sys.get_matrix_A(), &sys.get_matrix_B()};
608  snes, x, mats, libmesh_mats, ctx, {eigen_nl.nonEigenMatrixTag(), eigen_nl.eigenMatrixTag()});
609  eigen_problem.wereMatricesFormed(true);
610  PetscFunctionReturn(PETSC_SUCCESS);
611 }
612 
613 namespace
614 {
615 void
616 updateCurrentLocalSolution(CondensedEigenSystem & sys, Vec x)
617 {
618  auto & dof_map = sys.get_dof_map();
619 
620  PetscVector<Number> X_global(x, sys.comm());
621 
622  if (dof_map.n_constrained_dofs())
623  {
624  sys.copy_sub_to_super(X_global, *sys.solution);
625  // Set the constrained dof values
626  dof_map.enforce_constraints_exactly(sys);
627  sys.update();
628  }
629  else
630  {
631  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  X_global.swap(X_sys);
638  sys.update();
639  X_global.swap(X_sys);
640  }
641 }
642 
643 std::unique_ptr<NumericVector<Number>>
644 createWrappedResidual(CondensedEigenSystem & sys, Vec r)
645 {
646  auto & dof_map = sys.get_dof_map();
647 
648  if (dof_map.n_constrained_dofs())
649  return sys.solution->zero_clone();
650  else
651  {
652  auto R = std::make_unique<PetscVector<Number>>(r, sys.comm());
653  R->zero();
654  return R;
655  }
656 }
657 
658 void
659 evaluateResidual(EigenProblem & eigen_problem, Vec x, Vec r, TagID tag)
660 {
661  auto & nl = eigen_problem.getCurrentNonlinearEigenSystem();
662  auto & sys = nl.sys();
663  auto & dof_map = sys.get_dof_map();
664 
665  updateCurrentLocalSolution(sys, x);
666  auto R = createWrappedResidual(sys, r);
667 
668  eigen_problem.computeResidualTag(*sys.current_local_solution.get(), *R, tag);
669 
670  R->close();
671 
672  if (dof_map.n_constrained_dofs())
673  {
674  PetscVector<Number> sub_r(r, sys.comm());
675  sys.copy_super_to_sub(*R, sub_r);
676  }
677 }
678 }
679 
680 void
682  SNES /*snes*/, Vec x, Mat eigen_mat, SparseMatrix<Number> & all_dofs_mat, void * ctx, TagID tag)
683 {
684  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
685  auto & nl = eigen_problem->getCurrentNonlinearEigenSystem();
686  auto & sys = nl.sys();
687  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  updateCurrentLocalSolution(sys, x);
698 
699  if (!eigen_problem->constJacobian())
700  all_dofs_mat.zero();
701 
702  eigen_problem->computeJacobianTag(*sys.current_local_solution.get(), all_dofs_mat, tag);
703 
704  if (dof_map.n_constrained_dofs())
705  {
706  PetscMatrix<Number> wrapped_eigen_mat(eigen_mat, sys.comm());
707  sys.copy_super_to_sub(all_dofs_mat, wrapped_eigen_mat);
708  }
709 }
710 
711 void
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  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
720  auto & nl = eigen_problem->getCurrentNonlinearEigenSystem();
721  auto & sys = nl.sys();
722  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  updateCurrentLocalSolution(sys, x);
733 
734  for (auto * const all_dofs_mat : all_dofs_mats)
735  if (!eigen_problem->constJacobian())
736  all_dofs_mat->zero();
737 
738  eigen_problem->computeMatricesTags(*sys.current_local_solution.get(), all_dofs_mats, tags);
739 
740  if (dof_map.n_constrained_dofs())
741  for (const auto i : index_range(eigen_mats))
742  {
743  PetscMatrix<Number> wrapped_eigen_mat(eigen_mats[i], sys.comm());
744  sys.copy_super_to_sub(*all_dofs_mats[i], wrapped_eigen_mat);
745  }
746 }
747 
748 PetscErrorCode
750 {
751  PetscErrorCode (*func)(SNES, Vec, Vec, void *);
752  void * fctx;
754 
755  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
756  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
757  SNES snes = eigen_nl.getSNES();
758 
759  eigen_problem->onLinearSolver(true);
760 
761  LibmeshPetscCallQ(SNESGetFunction(snes, NULL, &func, &fctx));
762  if (fctx != ctx)
763  {
764  SETERRQ(
765  PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_INCOMP, "Contexts are not consistent \n");
766  }
767  LibmeshPetscCallQ((*func)(snes, x, r, ctx));
768 
769  eigen_problem->onLinearSolver(false);
770 
771  PetscFunctionReturn(PETSC_SUCCESS);
772 }
773 
774 PetscErrorCode
775 mooseSlepcEigenFormJacobianA(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
776 {
777  PetscBool jisshell, pisshell;
778  PetscBool jismffd;
779 
781 
782  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
783  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
784  auto & sys = eigen_nl.sys();
785 
786  // If both jacobian and preconditioning are shell matrices,
787  // and then assemble them and return
788  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &jisshell));
789  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &jismffd));
790 
791  if (jismffd && eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult)
792  {
795 
796  EPS eps = eigen_nl.getEPS();
797 
798  LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
799 
800  if (pc != jac)
801  {
802  LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
803  LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
804  }
805  PetscFunctionReturn(PETSC_SUCCESS);
806  }
807 
808  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &pisshell));
809  if ((jisshell || jismffd) && pisshell)
810  {
811  // Just assemble matrices and return
812  LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
813  LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
814  LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
815  LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
816 
817  PetscFunctionReturn(PETSC_SUCCESS);
818  }
819 
820  // Jacobian and precond matrix are the same
821  if (jac == pc)
822  {
823  if (!pisshell)
825  snes, x, pc, sys.get_matrix_A(), ctx, eigen_nl.precondMatrixTag());
826 
827  PetscFunctionReturn(PETSC_SUCCESS);
828  }
829  else
830  {
831  if (!jisshell && !jismffd && !pisshell) // We need to form both Jacobian and precond matrix
832  {
833  std::vector<Mat> mats = {jac, pc};
834  std::vector<SparseMatrix<Number> *> libmesh_mats = {&sys.get_matrix_A(),
835  &sys.get_precond_matrix()};
836  std::set<TagID> tags = {eigen_nl.nonEigenMatrixTag(), eigen_nl.precondMatrixTag()};
837  moosePetscSNESFormMatricesTags(snes, x, mats, libmesh_mats, ctx, tags);
838  PetscFunctionReturn(PETSC_SUCCESS);
839  }
840  if (!pisshell) // We need to form only precond matrix
841  {
843  snes, x, pc, sys.get_precond_matrix(), ctx, eigen_nl.precondMatrixTag());
844  LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
845  LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
846  PetscFunctionReturn(PETSC_SUCCESS);
847  }
848  if (!jisshell && !jismffd) // We need to form only Jacobian matrix
849  {
851  snes, x, jac, sys.get_matrix_A(), ctx, eigen_nl.nonEigenMatrixTag());
852  LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
853  LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
854  PetscFunctionReturn(PETSC_SUCCESS);
855  }
856  }
857  PetscFunctionReturn(PETSC_SUCCESS);
858 }
859 
860 PetscErrorCode
861 mooseSlepcEigenFormJacobianB(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
862 {
863  PetscBool jshell, pshell;
864  PetscBool jismffd;
865 
867 
868  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
869  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
870  auto & sys = eigen_nl.sys();
871 
872  // If both jacobian and preconditioning are shell matrices,
873  // and then assemble them and return
874  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &jshell));
875  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &jismffd));
876  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &pshell));
877  if ((jshell || jismffd) && pshell)
878  {
879  // Just assemble matrices and return
880  LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
881  LibmeshPetscCallQ(MatAssemblyBegin(pc, MAT_FINAL_ASSEMBLY));
882  LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
883  LibmeshPetscCallQ(MatAssemblyEnd(pc, MAT_FINAL_ASSEMBLY));
884 
885  PetscFunctionReturn(PETSC_SUCCESS);
886  }
887 
888  if (jac != pc && (!jshell && !jshell))
889  SETERRQ(PetscObjectComm((PetscObject)snes),
890  PETSC_ERR_ARG_INCOMP,
891  "Jacobian and precond matrices should be the same for eigen kernels \n");
892 
893  moosePetscSNESFormMatrixTag(snes, x, pc, sys.get_matrix_B(), ctx, eigen_nl.eigenMatrixTag());
894 
895  if (eigen_problem->negativeSignEigenKernel())
896  {
897  LibmeshPetscCallQ(MatScale(pc, -1.));
898  }
899 
900  PetscFunctionReturn(PETSC_SUCCESS);
901 }
902 
903 void
904 moosePetscSNESFormFunction(SNES /*snes*/, Vec x, Vec r, void * ctx, TagID tag)
905 {
906  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
907  evaluateResidual(*eigen_problem, x, r, tag);
908 }
909 
910 PetscErrorCode
911 mooseSlepcEigenFormFunctionA(SNES snes, Vec x, Vec r, void * ctx)
912 {
914 
915  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
916  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
917 
918  if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
919  (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
920  {
921  EPS eps = eigen_nl.getEPS();
922  Mat A;
923  ST st;
924 
925  LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
926 
927  // Rest ST state so that we can restrieve matrices
928  LibmeshPetscCallQ(EPSGetST(eps, &st));
929  LibmeshPetscCallQ(STResetMatrixState(st));
930  LibmeshPetscCallQ(EPSGetOperators(eps, &A, NULL));
931 
932  LibmeshPetscCallQ(MatMult(A, x, r));
933 
934  PetscFunctionReturn(PETSC_SUCCESS);
935  }
936 
937  moosePetscSNESFormFunction(snes, x, r, ctx, eigen_nl.nonEigenVectorTag());
938 
939  PetscFunctionReturn(PETSC_SUCCESS);
940 }
941 
942 PetscErrorCode
943 mooseSlepcEigenFormFunctionB(SNES snes, Vec x, Vec r, void * ctx)
944 {
946 
947  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
948  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
949 
950  if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
951  (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
952  {
953  EPS eps = eigen_nl.getEPS();
954  ST st;
955  Mat B;
956 
957  LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
958 
959  // Rest ST state so that we can restrieve matrices
960  LibmeshPetscCallQ(EPSGetST(eps, &st));
961  LibmeshPetscCallQ(STResetMatrixState(st));
962  LibmeshPetscCallQ(EPSGetOperators(eps, NULL, &B));
963 
964  LibmeshPetscCallQ(MatMult(B, x, r));
965 
966  if (eigen_problem->bxNormProvided())
967  {
968  // User has provided a postprocessor. We need it updated
969  updateCurrentLocalSolution(eigen_nl.sys(), x);
970  eigen_problem->execute(EXEC_LINEAR);
971  }
972  }
973  else
974  moosePetscSNESFormFunction(snes, x, r, ctx, eigen_nl.eigenVectorTag());
975 
976  if (eigen_problem->negativeSignEigenKernel())
977  {
978  LibmeshPetscCallQ(VecScale(r, -1.));
979  }
980 
981  PetscFunctionReturn(PETSC_SUCCESS);
982 }
983 
984 PetscErrorCode
985 mooseSlepcEigenFormFunctionAB(SNES /*snes*/, Vec x, Vec Ax, Vec Bx, void * ctx)
986 {
988 
989  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
990  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
991  auto & sys = eigen_nl.sys();
992  auto & dof_map = sys.get_dof_map();
993 
994  if (eigen_problem->solverParams(eigen_nl.number())._eigen_matrix_vector_mult &&
995  (eigen_problem->onLinearSolver() || eigen_problem->constantMatrices()))
996  {
997  EPS eps = eigen_nl.getEPS();
998  ST st;
999  Mat A, B;
1000 
1001  LibmeshPetscCallQ(mooseEPSFormMatrices(*eigen_problem, eps, x, ctx));
1002 
1003  // Rest ST state so that we can restrieve matrices
1004  LibmeshPetscCallQ(EPSGetST(eps, &st));
1005  LibmeshPetscCallQ(STResetMatrixState(st));
1006 
1007  LibmeshPetscCallQ(EPSGetOperators(eps, &A, &B));
1008 
1009  LibmeshPetscCallQ(MatMult(A, x, Ax));
1010  LibmeshPetscCallQ(MatMult(B, x, Bx));
1011 
1012  if (eigen_problem->negativeSignEigenKernel())
1013  LibmeshPetscCallQ(VecScale(Bx, -1.));
1014 
1015  if (eigen_problem->bxNormProvided())
1016  {
1017  // User has provided a postprocessor. We need it updated
1018  updateCurrentLocalSolution(sys, x);
1019  eigen_problem->execute(EXEC_LINEAR);
1020  }
1021 
1022  PetscFunctionReturn(PETSC_SUCCESS);
1023  }
1024 
1025  updateCurrentLocalSolution(sys, x);
1026  auto AX = createWrappedResidual(sys, Ax);
1027  auto BX = createWrappedResidual(sys, Bx);
1028 
1029  eigen_problem->computeResidualAB(*sys.current_local_solution.get(),
1030  *AX,
1031  *BX,
1032  eigen_nl.nonEigenVectorTag(),
1033  eigen_nl.eigenVectorTag());
1034 
1035  AX->close();
1036  BX->close();
1037 
1038  if (dof_map.n_constrained_dofs())
1039  {
1040  PetscVector<Number> sub_Ax(Ax, sys.comm());
1041  sys.copy_super_to_sub(*AX, sub_Ax);
1042  PetscVector<Number> sub_Bx(Bx, sys.comm());
1043  sys.copy_super_to_sub(*BX, sub_Bx);
1044  }
1045 
1046  if (eigen_problem->negativeSignEigenKernel())
1047  LibmeshPetscCallQ(VecScale(Bx, -1.));
1048 
1049  PetscFunctionReturn(PETSC_SUCCESS);
1050 }
1051 
1052 PetscErrorCode
1053 mooseSlepcEigenFormNorm(SNES /*snes*/, Vec /*Bx*/, PetscReal * norm, void * ctx)
1054 {
1056  auto * const eigen_problem = static_cast<EigenProblem *>(ctx);
1057  *norm = eigen_problem->formNorm();
1058  PetscFunctionReturn(PETSC_SUCCESS);
1059 }
1060 
1061 void
1062 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  LibmeshPetscCallA(
1076  eigen_problem.comm().get(),
1077  PetscObjectComposeFunction((PetscObject)mat,
1078  "formJacobian",
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  LibmeshPetscCallA(
1086  eigen_problem.comm().get(),
1087  PetscObjectComposeFunction((PetscObject)mat,
1088  "formFunction",
1091 
1092  // It's also beneficial to be able to evaluate both A and B residuals at once
1093  LibmeshPetscCallA(eigen_problem.comm().get(),
1094  PetscObjectComposeFunction((PetscObject)mat,
1095  "formFunctionAB",
1097 
1098  // Users may choose to provide a custom measure of the norm of B (Bx for a linear system)
1099  if (eigen_problem.bxNormProvided())
1100  LibmeshPetscCallA(eigen_problem.comm().get(),
1101  PetscObjectComposeFunction((PetscObject)mat,
1102  "formNorm",
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  LibmeshPetscCallA(eigen_problem.comm().get(),
1109  PetscContainerCreate(eigen_problem.comm().get(), &container));
1110  LibmeshPetscCallA(eigen_problem.comm().get(),
1111  PetscContainerSetPointer(container, &eigen_problem));
1112  LibmeshPetscCallA(
1113  eigen_problem.comm().get(),
1114  PetscObjectCompose((PetscObject)mat, "formJacobianCtx", (PetscObject)container));
1115  LibmeshPetscCallA(
1116  eigen_problem.comm().get(),
1117  PetscObjectCompose((PetscObject)mat, "formFunctionCtx", (PetscObject)container));
1118  if (eigen_problem.bxNormProvided())
1119  LibmeshPetscCallA(eigen_problem.comm().get(),
1120  PetscObjectCompose((PetscObject)mat, "formNormCtx", (PetscObject)container));
1121 
1122  LibmeshPetscCallA(eigen_problem.comm().get(), PetscContainerDestroy(&container));
1123 }
1124 
1125 PetscErrorCode
1126 mooseMatMult_Eigen(Mat mat, Vec x, Vec r)
1127 {
1129  void * ctx = nullptr;
1130  LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
1131 
1132  if (!ctx)
1133  mooseError("No context is set for shell matrix ");
1134 
1135  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1136  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
1137 
1138  evaluateResidual(*eigen_problem, x, r, eigen_nl.eigenVectorTag());
1139 
1140  if (eigen_problem->negativeSignEigenKernel())
1141  LibmeshPetscCallQ(VecScale(r, -1.));
1142 
1143  PetscFunctionReturn(PETSC_SUCCESS);
1144 }
1145 
1146 PetscErrorCode
1147 mooseMatMult_NonEigen(Mat mat, Vec x, Vec r)
1148 {
1150  void * ctx = nullptr;
1151  LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
1152 
1153  if (!ctx)
1154  mooseError("No context is set for shell matrix ");
1155 
1156  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1157  NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
1158 
1159  evaluateResidual(*eigen_problem, x, r, eigen_nl.nonEigenVectorTag());
1160 
1161  PetscFunctionReturn(PETSC_SUCCESS);
1162 }
1163 
1164 void
1165 setOperationsForShellMat(EigenProblem & eigen_problem, Mat mat, bool eigen)
1166 {
1167  LibmeshPetscCallA(eigen_problem.comm().get(), MatShellSetContext(mat, &eigen_problem));
1168  LibmeshPetscCallA(eigen_problem.comm().get(),
1169  MatShellSetOperation(mat,
1170  MATOP_MULT,
1171  eigen ? (void (*)(void))mooseMatMult_Eigen
1172  : (void (*)(void))mooseMatMult_NonEigen));
1173 }
1174 
1175 PETSC_EXTERN PetscErrorCode
1177 {
1179 
1180  LibmeshPetscCallQ(PCRegister("moosepc", PCCreate_MoosePC));
1181 
1182  PetscFunctionReturn(PETSC_SUCCESS);
1183 }
1184 
1185 PETSC_EXTERN PetscErrorCode
1187 {
1189 
1190  pc->ops->view = PCView_MoosePC;
1191  pc->ops->destroy = PCDestroy_MoosePC;
1192  pc->ops->setup = PCSetUp_MoosePC;
1193  pc->ops->apply = PCApply_MoosePC;
1194 
1195  PetscFunctionReturn(PETSC_SUCCESS);
1196 }
1197 
1198 PetscErrorCode
1200 {
1202  /* We do not need to do anything right now, but later we may have some data we need to free here
1203  */
1204  PetscFunctionReturn(PETSC_SUCCESS);
1205 }
1206 
1207 PetscErrorCode
1208 PCView_MoosePC(PC /*pc*/, PetscViewer viewer)
1209 {
1210  PetscBool iascii;
1211 
1213  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1214  if (iascii)
1215  LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, " %s\n", "moosepc"));
1216 
1217  PetscFunctionReturn(PETSC_SUCCESS);
1218 }
1219 
1220 PetscErrorCode
1221 PCApply_MoosePC(PC pc, Vec x, Vec y)
1222 {
1223  void * ctx;
1224  Mat Amat, Pmat;
1225  PetscContainer container;
1226 
1228  LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
1230  PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
1231  if (container)
1232  LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
1233  else
1234  mooseError(" Can not find a context \n");
1235 
1236  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1237  NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
1238  auto preconditioner = nl_eigen.preconditioner();
1239 
1240  if (!preconditioner)
1241  mooseError("There is no moose preconditioner in nonlinear eigen system \n");
1242 
1243  PetscVector<Number> x_vec(x, preconditioner->comm());
1244  PetscVector<Number> y_vec(y, preconditioner->comm());
1245 
1246  preconditioner->apply(x_vec, y_vec);
1247 
1248  PetscFunctionReturn(PETSC_SUCCESS);
1249 }
1250 
1251 PetscErrorCode
1253 {
1254  void * ctx;
1255  Mat Amat, Pmat;
1256  PetscContainer container;
1257 
1259  LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
1261  PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
1262  if (container)
1263  LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
1264  else
1265  mooseError(" Can not find a context \n");
1266 
1267  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1268  NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
1269  Preconditioner<Number> * preconditioner = nl_eigen.preconditioner();
1270 
1271  if (!preconditioner)
1272  mooseError("There is no moose preconditioner in nonlinear eigen system \n");
1273 
1274  if (!preconditioner->initialized())
1275  preconditioner->init();
1276 
1277  preconditioner->setup();
1278 
1279  PetscFunctionReturn(PETSC_SUCCESS);
1280 }
1281 
1282 PetscErrorCode
1284  PetscInt its,
1285  PetscInt max_it,
1286  PetscInt nconv,
1287  PetscInt nev,
1288  EPSConvergedReason * reason,
1289  void * ctx)
1290 {
1291  EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1292 
1294  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  if (eigen_problem->doFreePowerIteration() && its == max_it && *reason <= 0)
1303  {
1304  *reason = EPS_CONVERGED_USER;
1305  eps->nconv = 1;
1306  }
1307  PetscFunctionReturn(PETSC_SUCCESS);
1308 }
1309 
1310 PetscErrorCode
1311 mooseSlepcEPSGetSNES(EPS eps, SNES * snes)
1312 {
1313  PetscBool same, nonlinear;
1314 
1316  LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)eps, EPSPOWER, &same));
1317 
1318  if (!same)
1319  mooseError("It is not eps power, and there is no snes");
1320 
1321  LibmeshPetscCallQ(EPSPowerGetNonlinear(eps, &nonlinear));
1322 
1323  if (!nonlinear)
1324  mooseError("It is not a nonlinear eigen solver");
1325 
1326  LibmeshPetscCallQ(EPSPowerGetSNES(eps, snes));
1327 
1328  PetscFunctionReturn(PETSC_SUCCESS);
1329 }
1330 
1331 PetscErrorCode
1333 {
1334  SNES snes;
1335  const char * prefix = nullptr;
1336 
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  LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)eps, &prefix));
1343  // Set option prefix to SNES
1344  LibmeshPetscCallQ(SNESSetOptionsPrefix(snes, prefix));
1345 
1346  PetscFunctionReturn(PETSC_SUCCESS);
1347 }
1348 
1349 PetscErrorCode
1351 {
1352  SNES snes;
1353  KSP ksp;
1354  PC pc;
1355 
1357  // Get SNES from EPS
1359  // Get KSP from SNES
1360  LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
1361  // Get PC from KSP
1362  LibmeshPetscCallQ(KSPGetPC(ksp, &pc));
1363  // Set PC type
1364  LibmeshPetscCallQ(PCSetType(pc, "moosepc"));
1365  PetscFunctionReturn(PETSC_SUCCESS);
1366 }
1367 
1368 PetscErrorCode
1370 {
1371  SNES snes;
1372  KSP ksp;
1373 
1375  // Get SNES from EPS
1377  // Get KSP from SNES
1378  LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
1379 
1381 
1383  PetscFunctionReturn(PETSC_SUCCESS);
1384 }
1385 
1386 PetscErrorCode
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 
1400  EigenProblem * eigen_problem = static_cast<EigenProblem *>(mctx);
1401  auto & console = eigen_problem->console();
1402 
1403  auto inverse = eigen_problem->outputInverseEigenvalue();
1404  LibmeshPetscCallQ(EPSGetST(eps, &st));
1405  eigenr = eigr[0];
1406  eigeni = eigi[0];
1407  // Make the eigenvalue consistent with shift type
1408  LibmeshPetscCallQ(STBackTransform(st, 1, &eigenr, &eigeni));
1409 
1410  auto eigenvalue = inverse ? 1.0 / eigenr : eigenr;
1411 
1412  // The term "k-eigenvalue" is adopted from the neutronics community.
1413  console << " Iteration " << its << std::setprecision(10) << std::fixed
1414  << (inverse ? " k-eigenvalue = " : " eigenvalue = ") << eigenvalue << std::endl;
1415 
1416  PetscFunctionReturn(PETSC_SUCCESS);
1417 }
1418 
1419 } // namespace SlepcSupport
1420 } // namespace moose
1421 
1422 #endif // LIBMESH_HAVE_SLEPC
void setNewtonPetscOptions(SolverParams &solver_params, const InputParameters &params)
Definition: SlepcSupport.C:431
Nonlinear eigenvalue system to be solved.
bool constantMatrices() const
Whether or not matrices are constant.
Definition: EigenProblem.h:207
bool wereMatricesFormed() const
Whether or not constant matrices were already formed.
Definition: EigenProblem.h:217
Generalized Non-Hermitian.
Definition: MooseTypes.h:876
virtual void computeResidualTag(const NumericVector< Number > &soln, NumericVector< Number > &residual, TagID tag) override
Form a vector for all kernels and BCs with a given tag.
Definition: EigenProblem.C:295
unsigned int _solver_sys_num
Definition: SolverParams.h:36
Moose::PetscSupport::PetscOptions & getPetscOptions()
Retrieve a writable reference the PETSc options (used by PetscSupport)
const SparseMatrix< Number > & get_precond_matrix() const
int eps(unsigned int i, unsigned int j)
2D version
Generalized Hermitian indefinite.
Definition: MooseTypes.h:875
void addPetscOptionsFromCommandline()
Definition: PetscSupport.C:233
bool _eigen_matrix_free
Definition: SolverParams.h:27
Newton-based eigensolver with an assembled Jacobian matrix (fully coupled by default) ...
Definition: MooseTypes.h:861
Moose::EigenSolveType _eigen_solve_type
Definition: SolverParams.h:24
smallest magnitude
Definition: MooseTypes.h:887
const unsigned int invalid_uint
PetscErrorCode mooseSlepcEigenFormFunctionMFFD(void *ctx, Vec x, Vec r)
Function call for MFFD.
Definition: SlepcSupport.C:749
std::string _prefix
Definition: SolverParams.h:35
unsigned int TagID
Definition: MooseTypes.h:210
virtual std::size_t numNonlinearSystems() const override
bool outputInverseEigenvalue() const
Whether or not to output eigenvalue inverse.
Definition: EigenProblem.h:187
const SparseMatrix< Number > & get_matrix_A() const
void setDocString(const std::string &name, const std::string &doc)
Set the doc string of a parameter.
void moosePetscSNESFormMatricesTags(SNES, Vec x, std::vector< Mat > &eigen_mats, std::vector< SparseMatrix< Number > *> &all_dofs_mats, void *ctx, const std::set< TagID > &tags)
Definition: SlepcSupport.C:712
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
PetscErrorCode mooseSlepcEPSSNESSetCustomizePC(EPS eps)
Attach a customized PC.
PetscErrorCode mooseSlepcStoppingTest(EPS eps, PetscInt its, PetscInt max_it, PetscInt nconv, PetscInt nev, EPSConvergedReason *reason, void *ctx)
A customized convergence checker.
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
TagID nonEigenVectorTag() const
Vector tag ID of left hand side.
void setCoupling(Moose::CouplingType type)
Set the coupling between variables TODO: allow user-defined coupling.
const SparseMatrix< Number > & get_matrix_B() const
void setEigenSolverOptions(SolverParams &solver_params, const InputParameters &params)
Definition: SlepcSupport.C:494
PetscErrorCode mooseMatMult_Eigen(Mat mat, Vec x, Vec y)
Implement MatMult via function evaluation for Bx.
The same as PJFNK except that matrix-vector multiplication is employed to replace residual evaluation...
Definition: MooseTypes.h:863
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:336
TagID eigenVectorTag() const
Vector tag ID of right hand side.
PetscErrorCode mooseSlepcEigenFormJacobianB(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
Form Jacobian matrix B.
Definition: SlepcSupport.C:861
void clearFreeNonlinearPowerIterations(const InputParameters &params)
Definition: SlepcSupport.C:419
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
PetscFunctionBegin
virtual void execute(const ExecFlagType &exec_type) override
Convenience function for performing execution of MOOSE systems.
Definition: EigenProblem.C:165
PetscErrorCode mooseSlepcEigenFormFunctionAB(SNES snes, Vec x, Vec Ax, Vec Bx, void *ctx)
Form function residual Ax-Bx.
Definition: SlepcSupport.C:985
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
bool negativeSignEigenKernel() const
A flag indicates if a negative sign is used in eigen kernels.
Definition: EigenProblem.h:57
const Parallel::Communicator & comm() const
void petscSetDefaultKSPNormType(FEProblemBase &problem, KSP ksp)
Set norm type.
Definition: PetscSupport.C:442
PetscErrorCode PCApply_MoosePC(PC pc, Vec x, Vec y)
Preconditioner application.
use whatever SLPEC has by default
Definition: MooseTypes.h:878
bool _customized_pc_for_eigen
Definition: SolverParams.h:29
target magnitude
Definition: MooseTypes.h:892
void inverse(const std::vector< std::vector< Real >> &m, std::vector< std::vector< Real >> &m_inv)
Inverse the dense square matrix m using LAPACK routines.
Definition: MatrixTools.C:23
PetscErrorCode PCDestroy_MoosePC(PC pc)
Destroy preconditioner.
PetscErrorCode mooseSlepcEigenFormFunctionA(SNES snes, Vec x, Vec r, void *ctx)
Form function residual Ax.
Definition: SlepcSupport.C:911
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
void copy_super_to_sub(NumericVector< Number > &super, NumericVector< Number > &sub)
void setNEigenPairsRequired(unsigned int n_eigen_pairs)
Definition: EigenProblem.h:38
virtual bool initialized() const
Generalized Non-Hermitian with positive (semi-)definite B.
Definition: MooseTypes.h:877
void setEigenProblemOptions(SolverParams &solver_params, const MultiMooseEnum &dont_add_these_options)
Definition: SlepcSupport.C:298
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
PetscErrorCode mooseMatMult_NonEigen(Mat mat, Vec x, Vec y)
Implement MatMult via function evaluation for Ax.
Power / Inverse / RQI.
Definition: MooseTypes.h:856
bool isNonlinearEigenvalueSolver(unsigned int eigen_sys_num) const
Definition: EigenProblem.C:676
InputParameters emptyInputParameters()
Krylov-Schur.
Definition: MooseTypes.h:858
void setSlepcEigenSolverTolerances(EigenProblem &eigen_problem, const SolverParams &solver_params, const InputParameters &params)
Control eigen solver tolerances via SLEPc options.
Definition: SlepcSupport.C:129
Real formNorm()
Form the Bx norm.
Definition: EigenProblem.C:693
virtual void swap(NumericVector< T > &v) override
InputParameters getSlepcValidParams(InputParameters &params)
Definition: SlepcSupport.C:38
virtual EPS getEPS()
Retrieve EPS (SLEPc eigen solver)
void libmesh_ignore(const Args &...)
bool _eigen_matrix_vector_mult
Definition: SolverParams.h:28
Preconditioned Jacobian-free Newton Krylov.
Definition: MooseTypes.h:862
PetscErrorCode PCSetUp_MoosePC(PC pc)
Setup preconditioner.
Moose::EigenProblemType _eigen_problem_type
Definition: SolverParams.h:25
virtual void zero()=0
void attachCallbacksToMat(EigenProblem &eigen_problem, Mat mat, bool eigen)
Attach call backs to mat.
void copy_sub_to_super(const NumericVector< Number > &sub, NumericVector< Number > &super)
void slepcSetOptions(EigenProblem &eigen_problem, SolverParams &solver_params, const InputParameters &params)
Push all SLEPc/PETSc options into SLEPc/PETSc side.
Definition: SlepcSupport.C:550
PETSC_EXTERN PetscErrorCode PCCreate_MoosePC(PC pc)
Create a preconditioner from moose side.
bool constJacobian() const
Returns _const_jacobian (whether a MOOSE object has specified that the Jacobian is the same as the pr...
PetscErrorCode mooseSlepcEigenFormNorm(SNES, Vec, PetscReal *norm, void *ctx)
virtual libMesh::EquationSystems & es() override
void computeResidualAB(const NumericVector< Number > &soln, NumericVector< Number > &residualA, NumericVector< Number > &residualB, TagID tagA, TagID tagB)
Form two vetors, where each is associated with one tag, through one element-loop. ...
Definition: EigenProblem.C:327
std::unique_ptr< NumericVector< Number > > solution
target imaginary
Definition: MooseTypes.h:894
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
PetscErrorCode PCView_MoosePC(PC pc, PetscViewer viewer)
View preconditioner.
Moose::WhichEigenPairs _which_eigen_pairs
Definition: SolverParams.h:26
unsigned int number() const
Gets the number of this system.
Definition: SystemBase.C:1159
auto norm(const T &a) -> decltype(std::abs(a))
const ExecFlagType EXEC_LINEAR
Definition: Moose.C:29
void setNonlinearPowerOptions(SolverParams &solver_params)
Definition: SlepcSupport.C:474
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
void computeMatricesTags(const NumericVector< Number > &soln, const std::vector< SparseMatrix< Number > *> &jacobians, const std::set< TagID > &tags)
Form several matrices simultaneously.
Definition: EigenProblem.C:207
Nonlinear inverse power.
Definition: MooseTypes.h:860
void petscSetOptions(const PetscOptions &po, const SolverParams &solver_params, FEProblemBase *const problem=nullptr)
A function for setting the PETSc options in PETSc from the options supplied to MOOSE.
Definition: PetscSupport.C:272
NonlinearEigenSystem & getCurrentNonlinearEigenSystem()
Definition: EigenProblem.h:307
PetscErrorCode mooseSlepcEigenFormJacobianA(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
Form Jacobian matrix A.
Definition: SlepcSupport.C:775
PetscErrorCode mooseSlepcEigenFormFunctionB(SNES snes, Vec x, Vec r, void *ctx)
Form function residual Bx.
Definition: SlepcSupport.C:943
PetscErrorCode mooseSlepcEPSMonitor(EPS eps, PetscInt its, PetscInt nconv, PetscScalar *eigr, PetscScalar *eigi, PetscReal *errest, PetscInt nest, void *mctx)
A customized solver monitor to print out eigenvalue.
Jacobi-Davidson.
Definition: MooseTypes.h:859
bool _precond_matrix_free
Definition: SolverParams.h:30
void moosePetscSNESFormFunction(SNES, Vec x, Vec r, void *ctx, TagID tag)
Definition: SlepcSupport.C:904
LibmeshPetscCallQ(DMShellGetContext(dm, &ctx))
const ConsoleStream & console() const
Return console handle.
Definition: Problem.h:48
void setSinglePetscOption(const std::string &name, const std::string &value="", FEProblemBase *const problem=nullptr)
A wrapper function for dealing with different versions of PetscOptionsSetValue.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
PetscErrorCode mooseSlepcEPSGetSNES(EPS eps, SNES *snes)
Retrieve SNES from EPS.
bool onLinearSolver() const
Whether or not we are in a linear solver iteration.
Definition: EigenProblem.h:197
smallest imaginary
Definition: MooseTypes.h:891
void setFreeNonlinearPowerIterations(unsigned int free_power_iterations)
Set SLEPc/PETSc options to trigger free power iteration.
Definition: SlepcSupport.C:404
T & set(const std::string &)
libMesh::CondensedEigenSystem & sys()
Jacobian-free Newton Krylov.
Definition: MooseTypes.h:864
virtual SNES getSNES() override
Retrieve snes from slepc eigen solver.
TagID eigenMatrixTag() const
Matrix tag ID of right hand side.
largest imaginary
Definition: MooseTypes.h:890
IntRange< T > make_range(T beg, T end)
Non-Hermitian.
Definition: MooseTypes.h:873
PetscErrorCode mooseSlepcEPSSNESKSPSetPCSide(FEProblemBase &problem, EPS eps)
Allow users to specify PC side.
SolverParams & solverParams(unsigned int solver_sys_num=0)
Get the solver parameters.
std::unique_ptr< NumericVector< Number > > current_local_solution
void moosePetscSNESFormMatrixTag(SNES, Vec x, Mat eigen_mat, SparseMatrix< Number > &all_dofs_mat, void *ctx, TagID tag)
Definition: SlepcSupport.C:681
PetscErrorCode mooseSlepcEPSSNESSetUpOptionPrefix(EPS eps)
Get rid of prefix "-eps_power" for SNES, KSP, PC, etc.
all eigenvalues
Definition: MooseTypes.h:895
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
void setSinglePetscOptionIfAppropriate(const MultiMooseEnum &dont_add_these_options, const std::string &name, const std::string &value="", FEProblemBase *const problem=nullptr)
Same as setSinglePetscOption, but does not set the option if it doesn&#39;t make sense for the current si...
void * ctx
void setOperationsForShellMat(EigenProblem &eigen_problem, Mat mat, bool eigen)
Set operations to shell mat.
void setEigenProblemSolverParams(EigenProblem &eigen_problem, const InputParameters &params)
Retrieve eigen problem params from &#39;params&#39;, and then set these params into SolverParams.
Definition: SlepcSupport.C:212
TagID nonEigenMatrixTag() const
Matrix tag ID of left hand side.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type...
use whatever we have in SLEPC
Definition: MooseTypes.h:896
const int subspace_factor
Definition: SlepcSupport.C:35
PetscErrorCode mooseEPSFormMatrices(EigenProblem &eigen_problem, EPS eps, Vec x, void *ctx)
Definition: SlepcSupport.C:573
Problem for solving eigenvalue problems.
Definition: EigenProblem.h:21
void storeSolveType(FEProblemBase &fe_problem, const InputParameters &params)
Set solve type into eigen problem (solverParams)
Definition: SlepcSupport.C:286
largest magnitude
Definition: MooseTypes.h:886
MultiMooseEnum dont_add_these_options
Flags to explicitly not set, even if they are specified programmatically.
Definition: PetscSupport.h:54
PETSC_EXTERN PetscErrorCode registerPCToPETSc()
Let PETSc know there is a preconditioner.
bool bxNormProvided() const
Whether a Bx norm postprocessor has been provided.
Definition: EigenProblem.h:232
void setWhichEigenPairsOptions(SolverParams &solver_params, const MultiMooseEnum &dont_add_these_options)
Definition: SlepcSupport.C:341
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
virtual void setup()
const DofMap & get_dof_map() const
bool doFreePowerIteration() const
Whether or not we are doing free power iteration.
Definition: EigenProblem.h:79
libMesh::Preconditioner< Number > * preconditioner() const
auto index_range(const T &sizable)
Generalized Hermitian.
Definition: MooseTypes.h:874
void petscSetDefaultPCSide(FEProblemBase &problem, KSP ksp)
Setup which side we want to apply preconditioner.
Definition: PetscSupport.C:453
virtual void computeJacobianTag(const NumericVector< Number > &soln, SparseMatrix< Number > &jacobian, TagID tag) override
Form a Jacobian matrix for all kernels and BCs with a given tag.
Definition: EigenProblem.C:176
InputParameters getSlepcEigenProblemValidParams()
Retrieve valid params that allow users to specify eigen problem configuration.
Definition: SlepcSupport.C:67
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.