https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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"
21#include "libmesh/petsc_vector.h"
22#include "libmesh/petsc_matrix.h"
23#include "libmesh/slepc_macro.h"
24#include "petscsnes.h"
25#include "slepceps.h"
26
27using namespace libMesh;
28
29namespace Moose
30{
31namespace SlepcSupport
32{
33
34const int subspace_factor = 2;
35
38{
39 MooseEnum solve_type("POWER ARNOLDI KRYLOVSCHUR JACOBI_DAVIDSON "
40 "NONLINEAR_POWER NEWTON PJFNK PJFNKMO JFNK",
41 "PJFNK");
42 params.set<MooseEnum>("solve_type") = solve_type;
43
44 params.setDocString("solve_type",
45 "POWER: Power / Inverse / RQI "
46 "ARNOLDI: Arnoldi "
47 "KRYLOVSCHUR: Krylov-Schur "
48 "JACOBI_DAVIDSON: Jacobi-Davidson "
49 "NONLINEAR_POWER: Nonlinear Power "
50 "NEWTON: Newton "
51 "PJFNK: Preconditioned Jacobian-free Newton-Kyrlov"
52 "JFNK: Jacobian-free Newton-Kyrlov"
53 "PJFNKMO: Preconditioned Jacobian-free Newton-Kyrlov with Matrix Only");
54
55 // When the eigenvalue problems is reformed as a coupled nonlinear system,
56 // we use part of Jacobian as the preconditioning matrix.
57 // Because the difference between the Jacobian and the preconditioning matrix is not small,
58 // the linear solver KSP can not reduce the residual much. After several tests,
59 // we find 1e-2 is a reasonable choice.
60 params.set<Real>("l_tol") = 1e-2;
61
62 return params;
63}
64
67{
69
70 // We are solving a Non-Hermitian eigenvalue problem by default
71 MooseEnum eigen_problem_type("HERMITIAN NON_HERMITIAN GEN_HERMITIAN GEN_NON_HERMITIAN "
72 "GEN_INDEFINITE POS_GEN_NON_HERMITIAN SLEPC_DEFAULT",
73 "GEN_NON_HERMITIAN");
74 params.addParam<MooseEnum>(
75 "eigen_problem_type",
76 eigen_problem_type,
77 "Type of the eigenvalue problem we are solving "
78 "HERMITIAN: Hermitian "
79 "NON_HERMITIAN: Non-Hermitian "
80 "GEN_HERMITIAN: Generalized Hermitian "
81 "GEN_NON_HERMITIAN: Generalized Non-Hermitian "
82 "GEN_INDEFINITE: Generalized indefinite Hermitian "
83 "POS_GEN_NON_HERMITIAN: Generalized Non-Hermitian with positive (semi-)definite B "
84 "SLEPC_DEFAULT: Use whatever SLEPC has by default ");
85
86 // Which eigenvalues are we interested in
87 MooseEnum which_eigen_pairs("LARGEST_MAGNITUDE SMALLEST_MAGNITUDE LARGEST_REAL SMALLEST_REAL "
88 "LARGEST_IMAGINARY SMALLEST_IMAGINARY TARGET_MAGNITUDE TARGET_REAL "
89 "TARGET_IMAGINARY ALL_EIGENVALUES SLEPC_DEFAULT");
90 params.addParam<MooseEnum>("which_eigen_pairs",
91 which_eigen_pairs,
92 "Which eigenvalue pairs to obtain from the solution "
93 "LARGEST_MAGNITUDE "
94 "SMALLEST_MAGNITUDE "
95 "LARGEST_REAL "
96 "SMALLEST_REAL "
97 "LARGEST_IMAGINARY "
98 "SMALLEST_IMAGINARY "
99 "TARGET_MAGNITUDE "
100 "TARGET_REAL "
101 "TARGET_IMAGINARY "
102 "ALL_EIGENVALUES "
103 "SLEPC_DEFAULT ");
104
105 params.addParam<unsigned int>("n_eigen_pairs", 1, "The number of eigen pairs");
106 params.addParam<unsigned int>("n_basis_vectors", 3, "The dimension of eigen subspaces");
107
108 params.addParam<Real>("eigen_tol", 1.0e-4, "Relative Tolerance for Eigen Solver");
109 params.addParam<unsigned int>("eigen_max_its", 10000, "Max Iterations for Eigen Solver");
110
111 params.addParam<Real>("l_abs_tol", 1e-50, "Absolute Tolerances for Linear Solver");
112
113 params.addParam<unsigned int>("free_power_iterations", 4, "The number of free power iterations");
114
115 params.addParam<unsigned int>(
116 "extra_power_iterations", 0, "The number of extra free power iterations");
117
119 "eigen_problem_type which_eigen_pairs n_eigen_pairs n_basis_vectors eigen_tol eigen_max_its "
120 "free_power_iterations extra_power_iterations",
121 "Eigen Solver");
122 params.addParamNamesToGroup("l_abs_tol", "Linear solver");
123
124 return params;
125}
126
127void
129 const SolverParams & solver_params,
130 const InputParameters & params)
131{
132 const auto & dont_add_these_options = eigen_problem.getPetscOptions().dont_add_these_options;
133 const auto prefix_with_dash = '-' + solver_params._prefix;
134
135 mooseAssert(solver_params._solver_sys_num != libMesh::invalid_uint,
136 "The solver system number must be initialized");
137
139 prefix_with_dash + "eps_tol",
140 stringify(params.get<Real>("eigen_tol")));
141
143 dont_add_these_options,
144 prefix_with_dash + "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 prefix_with_dash + "snes_max_it",
155 stringify(params.get<unsigned int>("nl_max_its")));
156
158 dont_add_these_options,
159 prefix_with_dash + "snes_max_funcs",
160 stringify(params.get<unsigned int>("nl_max_funcs")));
161
163 dont_add_these_options,
164 prefix_with_dash + "snes_atol",
165 stringify(params.get<Real>("nl_abs_tol")));
166
168 dont_add_these_options,
169 prefix_with_dash + "snes_rtol",
170 stringify(params.get<Real>("nl_rel_tol")));
171
173 dont_add_these_options,
174 prefix_with_dash + "snes_stol",
175 stringify(params.get<Real>("nl_rel_step_tol")));
176
177 // linear solver
179 dont_add_these_options,
180 prefix_with_dash + "ksp_max_it",
181 stringify(params.get<unsigned int>("l_max_its")));
182
184 prefix_with_dash + "ksp_rtol",
185 stringify(params.get<Real>("l_tol")));
186
188 dont_add_these_options,
189 prefix_with_dash + "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 prefix_with_dash + "st_ksp_max_it",
198 stringify(params.get<unsigned int>("l_max_its")));
199
201 prefix_with_dash + "st_ksp_rtol",
202 stringify(params.get<Real>("l_tol")));
203
205 dont_add_these_options,
206 prefix_with_dash + "st_ksp_atol",
207 stringify(params.get<Real>("l_abs_tol")));
208 }
209}
210
211void
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;
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
285void
286storeSolveType(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
297void
298setEigenProblemOptions(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
340void
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
403void
404setFreeNonlinearPowerIterations(unsigned int free_power_iterations)
405{
406 Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "0");
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
416}
417
418void
420{
421 Moose::PetscSupport::setSinglePetscOption("-eps_power_update", "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
430void
431setNewtonPetscOptions(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
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
473void
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
493void
494setEigenSolverOptions(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
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
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
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
549void
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);
569}
570
571// For matrices A and B
572PetscErrorCode
573mooseEPSFormMatrices(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
613namespace
614{
615void
616updateCurrentLocalSolution(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
643std::unique_ptr<NumericVector<Number>>
644createWrappedResidual(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
658void
659evaluateResidual(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
680void
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
711void
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
748PetscErrorCode
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
774PetscErrorCode
775mooseSlepcEigenFormJacobianA(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
860PetscErrorCode
861mooseSlepcEigenFormJacobianB(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
903void
904moosePetscSNESFormFunction(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
910PetscErrorCode
911mooseSlepcEigenFormFunctionA(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
942PetscErrorCode
943mooseSlepcEigenFormFunctionB(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
984PetscErrorCode
985mooseSlepcEigenFormFunctionAB(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
1052PetscErrorCode
1053mooseSlepcEigenFormNorm(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
1061void
1062attachCallbacksToMat(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
1125PetscErrorCode
1126mooseMatMult_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
1146PetscErrorCode
1147mooseMatMult_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
1164void
1165setOperationsForShellMat(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
1175PETSC_EXTERN PetscErrorCode
1177{
1179
1180 LibmeshPetscCallQ(PCRegister("moosepc", PCCreate_MoosePC));
1181
1182 PetscFunctionReturn(PETSC_SUCCESS);
1183}
1184
1185PETSC_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
1198PetscErrorCode
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
1207PetscErrorCode
1208PCView_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
1220PetscErrorCode
1221PCApply_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
1251PetscErrorCode
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
1282PetscErrorCode
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
1310PetscErrorCode
1311mooseSlepcEPSGetSNES(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
1331PetscErrorCode
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
1349PetscErrorCode
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
1368PetscErrorCode
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
1386PetscErrorCode
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
InputParameters emptyInputParameters()
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
unsigned int TagID
Definition MooseTypes.h:238
const ExecFlagType EXEC_LINEAR
Definition Moose.C:31
PetscFunctionBegin
Problem for solving eigenvalue problems.
bool negativeSignEigenKernel() const
A flag indicates if a negative sign is used in eigen kernels.
void computeMatricesTags(const NumericVector< Number > &soln, const std::vector< SparseMatrix< Number > * > &jacobians, const std::set< TagID > &tags)
Form several matrices simultaneously.
bool onLinearSolver() const
Whether or not we are in a linear solver iteration.
bool constantMatrices() const
Whether or not matrices are constant.
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.
virtual void execute(const ExecFlagType &exec_type) override
Convenience function for performing execution of MOOSE systems.
bool bxNormProvided() const
Whether a Bx norm postprocessor has been provided.
bool doFreePowerIteration() const
Whether or not we are doing free power iteration.
NonlinearEigenSystem & getCurrentNonlinearEigenSystem()
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.
bool isNonlinearEigenvalueSolver(unsigned int eigen_sys_num) const
bool outputInverseEigenvalue() const
Whether or not to output eigenvalue inverse.
Real formNorm()
Form the Bx norm.
bool wereMatricesFormed() const
Whether or not constant matrices were already formed.
void setNEigenPairsRequired(unsigned int n_eigen_pairs)
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.
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
virtual libMesh::EquationSystems & es() override
virtual std::size_t numNonlinearSystems() const override
void setCoupling(Moose::CouplingType type)
Set the coupling between variables TODO: allow user-defined coupling.
SolverParams & solverParams(unsigned int solver_sys_num=0)
Get the solver parameters.
Moose::PetscSupport::PetscOptions & getPetscOptions()
Retrieve a writable reference the PETSc options (used by PetscSupport)
bool constJacobian() const
Returns _const_jacobian (whether a MOOSE object has specified that the Jacobian is the same as the pr...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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...
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 setDocString(const std::string &name, const std::string &doc)
Set the doc string of a parameter.
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.
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MultiMooseEnum dont_add_these_options
Flags to explicitly not set, even if they are specified programmatically.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type.
Nonlinear eigenvalue system to be solved.
TagID eigenVectorTag() const
Vector tag ID of right hand side.
TagID nonEigenVectorTag() const
Vector tag ID of left hand side.
virtual SNES getSNES() override
Retrieve snes from slepc eigen solver.
libMesh::Preconditioner< Number > * preconditioner() const
TagID nonEigenMatrixTag() const
Matrix tag ID of left hand side.
TagID eigenMatrixTag() const
Matrix tag ID of right hand side.
libMesh::CondensedEigenSystem & sys()
virtual EPS getEPS()
Retrieve EPS (SLEPc eigen solver)
const ConsoleStream & console() const
Return console handle.
Definition Problem.h:48
unsigned int _solver_sys_num
Moose::WhichEigenPairs _which_eigen_pairs
Moose::EigenSolveType _eigen_solve_type
std::string _prefix
bool _customized_pc_for_eigen
Moose::EigenProblemType _eigen_problem_type
bool _eigen_matrix_free
bool _precond_matrix_free
bool _eigen_matrix_vector_mult
unsigned int number() const
Gets the number of this system.
void copy_super_to_sub(NumericVector< Number > &super, NumericVector< Number > &sub)
void copy_sub_to_super(const NumericVector< Number > &sub, NumericVector< Number > &super)
const SparseMatrix< Number > & get_matrix_B() const
const SparseMatrix< Number > & get_precond_matrix() const
const SparseMatrix< Number > & get_matrix_A() const
const Parallel::Communicator & comm() const
T & set(const std::string &)
virtual void swap(NumericVector< T > &v) override
virtual bool initialized() const
virtual void setup()
virtual void zero()=0
std::unique_ptr< NumericVector< Number > > current_local_solution
std::unique_ptr< NumericVector< Number > > solution
const DofMap & get_dof_map() const
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.
void petscSetDefaultKSPNormType(FEProblemBase &problem, KSP ksp)
Set norm type.
void addPetscOptionsFromCommandline(FEProblemBase *const problem=nullptr)
Insert command-line PETSc options into the active PETSc options database.
void petscSetDefaultPCSide(FEProblemBase &problem, KSP ksp)
Setup which side we want to apply preconditioner.
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't make sense for the current si...
void setSinglePetscOption(const std::string &name, const std::string &value="", FEProblemBase *const problem=nullptr)
A wrapper function for dealing with different versions of PetscOptionsSetValue.
PETSC_EXTERN PetscErrorCode registerPCToPETSc()
Let PETSc know there is a preconditioner.
void moosePetscSNESFormMatricesTags(SNES snes, Vec x, std::vector< Mat > &eigen_mats, std::vector< SparseMatrix< Number > * > &all_dofs_mats, void *ctx, const std::set< TagID > &tags)
Form multiple matrices for multiple tags.
void setFreeNonlinearPowerIterations(unsigned int free_power_iterations)
Set SLEPc/PETSc options to trigger free power iteration.
PetscErrorCode mooseSlepcEPSSNESKSPSetPCSide(FEProblemBase &problem, EPS eps)
Allow users to specify PC side.
PetscErrorCode PCSetUp_MoosePC(PC pc)
Setup preconditioner.
void slepcSetOptions(EigenProblem &eigen_problem, SolverParams &solver_params, const InputParameters &params)
Push all SLEPc/PETSc options into SLEPc/PETSc side.
PetscErrorCode mooseSlepcEigenFormFunctionMFFD(void *ctx, Vec x, Vec r)
Function call for MFFD.
PETSC_EXTERN PetscErrorCode PCCreate_MoosePC(PC pc)
Create a preconditioner from moose side.
PetscErrorCode mooseSlepcEigenFormNorm(SNES, Vec, PetscReal *norm, void *ctx)
void moosePetscSNESFormFunction(SNES, Vec x, Vec r, void *ctx, TagID tag)
PetscErrorCode mooseSlepcEPSGetSNES(EPS eps, SNES *snes)
Retrieve SNES from EPS.
PetscErrorCode mooseSlepcEPSSNESSetCustomizePC(EPS eps)
Attach a customized PC.
const int subspace_factor
PetscErrorCode mooseSlepcEigenFormFunctionB(SNES snes, Vec x, Vec r, void *ctx)
Form function residual Bx.
PetscErrorCode mooseSlepcEigenFormFunctionAB(SNES snes, Vec x, Vec Ax, Vec Bx, void *ctx)
Form function residual Ax-Bx.
void setOperationsForShellMat(EigenProblem &eigen_problem, Mat mat, bool eigen)
Set operations to shell mat.
void setWhichEigenPairsOptions(SolverParams &solver_params, const MultiMooseEnum &dont_add_these_options)
InputParameters getSlepcEigenProblemValidParams()
Retrieve valid params that allow users to specify eigen problem configuration.
PetscErrorCode mooseMatMult_NonEigen(Mat mat, Vec x, Vec y)
Implement MatMult via function evaluation for Ax.
void setNewtonPetscOptions(SolverParams &solver_params, const InputParameters &params)
void moosePetscSNESFormMatrixTag(SNES snes, Vec x, Mat eigen_mat, SparseMatrix< Number > &all_dofs_mat, void *ctx, TagID tag)
Form matrix according to tag.
InputParameters getSlepcValidParams(InputParameters &params)
void setSlepcEigenSolverTolerances(EigenProblem &eigen_problem, const SolverParams &solver_params, const InputParameters &params)
Control eigen solver tolerances via SLEPc options.
void setEigenProblemSolverParams(EigenProblem &eigen_problem, const InputParameters &params)
Retrieve eigen problem params from 'params', and then set these params into SolverParams.
PetscErrorCode mooseSlepcEigenFormJacobianA(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
Form Jacobian matrix A.
PetscErrorCode mooseSlepcEPSSNESSetUpOptionPrefix(EPS eps)
Get rid of prefix "-eps_power" for SNES, KSP, PC, etc.
PetscErrorCode mooseEPSFormMatrices(EigenProblem &eigen_problem, EPS eps, Vec x, void *ctx)
void storeSolveType(FEProblemBase &fe_problem, const InputParameters &params)
Set solve type into eigen problem (solverParams)
PetscErrorCode PCDestroy_MoosePC(PC pc)
Destroy preconditioner.
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.
PetscErrorCode mooseMatMult_Eigen(Mat mat, Vec x, Vec y)
Implement MatMult via function evaluation for Bx.
void setEigenSolverOptions(SolverParams &solver_params, const InputParameters &params)
void attachCallbacksToMat(EigenProblem &eigen_problem, Mat mat, bool eigen)
Attach call backs to mat.
PetscErrorCode mooseSlepcEigenFormFunctionA(SNES snes, Vec x, Vec r, void *ctx)
Form function residual Ax.
void setEigenProblemOptions(SolverParams &solver_params, const MultiMooseEnum &dont_add_these_options)
PetscErrorCode PCApply_MoosePC(PC pc, Vec x, Vec y)
Preconditioner application.
PetscErrorCode PCView_MoosePC(PC pc, PetscViewer viewer)
View preconditioner.
void setNonlinearPowerOptions(SolverParams &solver_params)
void clearFreeNonlinearPowerIterations(const InputParameters &params)
PetscErrorCode mooseSlepcStoppingTest(EPS eps, PetscInt its, PetscInt max_it, PetscInt nconv, PetscInt nev, EPSConvergedReason *reason, void *ctx)
A customized convergence checker.
PetscErrorCode mooseSlepcEigenFormJacobianB(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
Form Jacobian matrix B.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
std::string stringify(const T &t)
conversion to string
Definition Conversion.h:64
@ COUPLING_FULL
Definition MooseTypes.h:787
@ WEP_TARGET_MAGNITUDE
target magnitude
Definition MooseTypes.h:946
@ WEP_LARGEST_IMAGINARY
largest imaginary
Definition MooseTypes.h:944
@ WEP_TARGET_IMAGINARY
target imaginary
Definition MooseTypes.h:948
@ WEP_SMALLEST_REAL
smallest real
Definition MooseTypes.h:943
@ WEP_SLEPC_DEFAULT
use whatever we have in SLEPC
Definition MooseTypes.h:950
@ WEP_LARGEST_MAGNITUDE
largest magnitude
Definition MooseTypes.h:940
@ WEP_SMALLEST_MAGNITUDE
smallest magnitude
Definition MooseTypes.h:941
@ WEP_ALL_EIGENVALUES
all eigenvalues
Definition MooseTypes.h:949
@ WEP_SMALLEST_IMAGINARY
smallest imaginary
Definition MooseTypes.h:945
@ WEP_TARGET_REAL
target real
Definition MooseTypes.h:947
@ WEP_LARGEST_REAL
largest real
Definition MooseTypes.h:942
@ EST_PJFNKMO
The same as PJFNK except that matrix-vector multiplication is employed to replace residual evaluation...
Definition MooseTypes.h:917
@ EST_JACOBI_DAVIDSON
Jacobi-Davidson.
Definition MooseTypes.h:913
@ EST_KRYLOVSCHUR
Krylov-Schur.
Definition MooseTypes.h:912
@ EST_JFNK
Jacobian-free Newton Krylov.
Definition MooseTypes.h:918
@ EST_NEWTON
Newton-based eigensolver with an assembled Jacobian matrix (fully coupled by default)
Definition MooseTypes.h:915
@ EST_POWER
Power / Inverse / RQI.
Definition MooseTypes.h:910
@ EST_NONLINEAR_POWER
Nonlinear inverse power.
Definition MooseTypes.h:914
@ EST_ARNOLDI
Arnoldi.
Definition MooseTypes.h:911
@ EST_PJFNK
Preconditioned Jacobian-free Newton Krylov.
Definition MooseTypes.h:916
@ EPT_GEN_INDEFINITE
Generalized Hermitian indefinite.
Definition MooseTypes.h:929
@ EPT_NON_HERMITIAN
Non-Hermitian.
Definition MooseTypes.h:927
@ EPT_GEN_HERMITIAN
Generalized Hermitian.
Definition MooseTypes.h:928
@ EPT_HERMITIAN
Hermitian.
Definition MooseTypes.h:926
@ EPT_GEN_NON_HERMITIAN
Generalized Non-Hermitian.
Definition MooseTypes.h:930
@ EPT_POS_GEN_NON_HERMITIAN
Generalized Non-Hermitian with positive (semi-)definite B.
Definition MooseTypes.h:931
@ EPT_SLEPC_DEFAULT
use whatever SLPEC has by default
Definition MooseTypes.h:932
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
auto index_range(const T &sizable)
void * ctx
void libmesh_ignore(const Args &...)
const unsigned int invalid_uint
LibmeshPetscCallQ(DMShellGetContext(dm, &ctx))
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)