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