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 // SLEPc creates its work vectors, including the SNES solution and right-hand side, from the
1123 // operators. The vector type a matrix creates is chosen by its matrix type (VECSTANDARD for a
1124 // shell matrix, VECCUDA or VECHIP for MATHYPRE built with device support), so match the system
1125 // solution type, which honors '-vec_type' (e.g. device vectors required by GPU solvers).
1126 const auto & solution = cast_ref<const PetscVector<Number> &>(
1127 *eigen_problem.getCurrentNonlinearEigenSystem().sys().solution);
1128 VecType vec_type;
1129 LibmeshPetscCallA(eigen_problem.comm().get(), VecGetType(solution.vec(), &vec_type));
1130 LibmeshPetscCallA(eigen_problem.comm().get(), MatSetVecType(mat, vec_type));
1131}
1132
1133PetscErrorCode
1134mooseMatMult_Eigen(Mat mat, Vec x, Vec r)
1135{
1137 void * ctx = nullptr;
1138 LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
1139
1140 if (!ctx)
1141 mooseError("No context is set for shell matrix ");
1142
1143 EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1144 NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
1145
1146 evaluateResidual(*eigen_problem, x, r, eigen_nl.eigenVectorTag());
1147
1148 if (eigen_problem->negativeSignEigenKernel())
1149 LibmeshPetscCallQ(VecScale(r, -1.));
1150
1151 PetscFunctionReturn(PETSC_SUCCESS);
1152}
1153
1154PetscErrorCode
1155mooseMatMult_NonEigen(Mat mat, Vec x, Vec r)
1156{
1158 void * ctx = nullptr;
1159 LibmeshPetscCallQ(MatShellGetContext(mat, &ctx));
1160
1161 if (!ctx)
1162 mooseError("No context is set for shell matrix ");
1163
1164 EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1165 NonlinearEigenSystem & eigen_nl = eigen_problem->getCurrentNonlinearEigenSystem();
1166
1167 evaluateResidual(*eigen_problem, x, r, eigen_nl.nonEigenVectorTag());
1168
1169 PetscFunctionReturn(PETSC_SUCCESS);
1170}
1171
1172void
1173setOperationsForShellMat(EigenProblem & eigen_problem, Mat mat, bool eigen)
1174{
1175 LibmeshPetscCallA(eigen_problem.comm().get(), MatShellSetContext(mat, &eigen_problem));
1176 LibmeshPetscCallA(eigen_problem.comm().get(),
1177 MatShellSetOperation(mat,
1178 MATOP_MULT,
1179 eigen ? (void (*)(void))mooseMatMult_Eigen
1180 : (void (*)(void))mooseMatMult_NonEigen));
1181}
1182
1183PETSC_EXTERN PetscErrorCode
1185{
1187
1188 LibmeshPetscCallQ(PCRegister("moosepc", PCCreate_MoosePC));
1189
1190 PetscFunctionReturn(PETSC_SUCCESS);
1191}
1192
1193PETSC_EXTERN PetscErrorCode
1195{
1197
1198 pc->ops->view = PCView_MoosePC;
1199 pc->ops->destroy = PCDestroy_MoosePC;
1200 pc->ops->setup = PCSetUp_MoosePC;
1201 pc->ops->apply = PCApply_MoosePC;
1202
1203 PetscFunctionReturn(PETSC_SUCCESS);
1204}
1205
1206PetscErrorCode
1208{
1210 /* We do not need to do anything right now, but later we may have some data we need to free here
1211 */
1212 PetscFunctionReturn(PETSC_SUCCESS);
1213}
1214
1215PetscErrorCode
1216PCView_MoosePC(PC /*pc*/, PetscViewer viewer)
1217{
1218 PetscBool iascii;
1219
1221 LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1222 if (iascii)
1223 LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, " %s\n", "moosepc"));
1224
1225 PetscFunctionReturn(PETSC_SUCCESS);
1226}
1227
1228PetscErrorCode
1229PCApply_MoosePC(PC pc, Vec x, Vec y)
1230{
1231 void * ctx;
1232 Mat Amat, Pmat;
1233 PetscContainer container;
1234
1236 LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
1238 PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
1239 if (container)
1240 LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
1241 else
1242 mooseError(" Can not find a context \n");
1243
1244 EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1245 NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
1246 auto preconditioner = nl_eigen.preconditioner();
1247
1248 if (!preconditioner)
1249 mooseError("There is no moose preconditioner in nonlinear eigen system \n");
1250
1251 PetscVector<Number> x_vec(x, preconditioner->comm());
1252 PetscVector<Number> y_vec(y, preconditioner->comm());
1253
1254 preconditioner->apply(x_vec, y_vec);
1255
1256 PetscFunctionReturn(PETSC_SUCCESS);
1257}
1258
1259PetscErrorCode
1261{
1262 void * ctx;
1263 Mat Amat, Pmat;
1264 PetscContainer container;
1265
1267 LibmeshPetscCallQ(PCGetOperators(pc, &Amat, &Pmat));
1269 PetscObjectQuery((PetscObject)Pmat, "formFunctionCtx", (PetscObject *)&container));
1270 if (container)
1271 LibmeshPetscCallQ(PetscContainerGetPointer(container, &ctx));
1272 else
1273 mooseError(" Can not find a context \n");
1274
1275 EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1276 NonlinearEigenSystem & nl_eigen = eigen_problem->getCurrentNonlinearEigenSystem();
1277 Preconditioner<Number> * preconditioner = nl_eigen.preconditioner();
1278
1279 if (!preconditioner)
1280 mooseError("There is no moose preconditioner in nonlinear eigen system \n");
1281
1282 if (!preconditioner->initialized())
1283 preconditioner->init();
1284
1285 preconditioner->setup();
1286
1287 PetscFunctionReturn(PETSC_SUCCESS);
1288}
1289
1290PetscErrorCode
1292 PetscInt its,
1293 PetscInt max_it,
1294 PetscInt nconv,
1295 PetscInt nev,
1296 EPSConvergedReason * reason,
1297 void * ctx)
1298{
1299 EigenProblem * eigen_problem = static_cast<EigenProblem *>(ctx);
1300
1302 LibmeshPetscCallQ(EPSStoppingBasic(eps, its, max_it, nconv, nev, reason, NULL));
1303
1304 // If we do free power iteration, we need to mark the solver as converged.
1305 // It is because SLEPc does not offer a way to copy unconverged solution.
1306 // If the solver is not marked as "converged", we have no way to get solution
1307 // from slepc. Note marking as "converged" has no side-effects at all for us.
1308 // If free power iteration is used as a stand-alone solver, we won't trigger
1309 // as "doFreePowerIteration()" is false.
1310 if (eigen_problem->doFreePowerIteration() && its == max_it && *reason <= 0)
1311 {
1312 *reason = EPS_CONVERGED_USER;
1313 eps->nconv = 1;
1314 }
1315 PetscFunctionReturn(PETSC_SUCCESS);
1316}
1317
1318PetscErrorCode
1319mooseSlepcEPSGetSNES(EPS eps, SNES * snes)
1320{
1321 PetscBool same, nonlinear;
1322
1324 LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)eps, EPSPOWER, &same));
1325
1326 if (!same)
1327 mooseError("It is not eps power, and there is no snes");
1328
1329 LibmeshPetscCallQ(EPSPowerGetNonlinear(eps, &nonlinear));
1330
1331 if (!nonlinear)
1332 mooseError("It is not a nonlinear eigen solver");
1333
1334 LibmeshPetscCallQ(EPSPowerGetSNES(eps, snes));
1335
1336 PetscFunctionReturn(PETSC_SUCCESS);
1337}
1338
1339PetscErrorCode
1341{
1342 SNES snes;
1343 const char * prefix = nullptr;
1344
1347 // There is an extra "eps_power" in snes that users do not like it.
1348 // Let us remove that from snes.
1349 // Retrieve option prefix from EPS
1350 LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)eps, &prefix));
1351 // Set option prefix to SNES
1352 LibmeshPetscCallQ(SNESSetOptionsPrefix(snes, prefix));
1353
1354 PetscFunctionReturn(PETSC_SUCCESS);
1355}
1356
1357PetscErrorCode
1359{
1360 SNES snes;
1361 KSP ksp;
1362 PC pc;
1363
1365 // Get SNES from EPS
1367 // Get KSP from SNES
1368 LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
1369 // Get PC from KSP
1370 LibmeshPetscCallQ(KSPGetPC(ksp, &pc));
1371 // Set PC type
1372 LibmeshPetscCallQ(PCSetType(pc, "moosepc"));
1373 PetscFunctionReturn(PETSC_SUCCESS);
1374}
1375
1376PetscErrorCode
1378{
1379 SNES snes;
1380 KSP ksp;
1381
1383 // Get SNES from EPS
1385 // Get KSP from SNES
1386 LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
1387
1389
1391 PetscFunctionReturn(PETSC_SUCCESS);
1392}
1393
1394PetscErrorCode
1396 PetscInt its,
1397 PetscInt /*nconv*/,
1398 PetscScalar * eigr,
1399 PetscScalar * eigi,
1400 PetscReal * /*errest*/,
1401 PetscInt /*nest*/,
1402 void * mctx)
1403{
1404 ST st;
1405 PetscScalar eigenr, eigeni;
1406
1408 EigenProblem * eigen_problem = static_cast<EigenProblem *>(mctx);
1409 auto & console = eigen_problem->console();
1410
1411 auto inverse = eigen_problem->outputInverseEigenvalue();
1412 LibmeshPetscCallQ(EPSGetST(eps, &st));
1413 eigenr = eigr[0];
1414 eigeni = eigi[0];
1415 // Make the eigenvalue consistent with shift type
1416 LibmeshPetscCallQ(STBackTransform(st, 1, &eigenr, &eigeni));
1417
1418 auto eigenvalue = inverse ? 1.0 / eigenr : eigenr;
1419
1420 // The term "k-eigenvalue" is adopted from the neutronics community.
1421 console << " Iteration " << its << std::setprecision(10) << std::fixed
1422 << (inverse ? " k-eigenvalue = " : " eigenvalue = ") << eigenvalue << std::endl;
1423
1424 PetscFunctionReturn(PETSC_SUCCESS);
1425}
1426
1427} // namespace SlepcSupport
1428} // namespace moose
1429
1430#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:65
@ COUPLING_FULL
Definition MooseTypes.h:791
@ WEP_TARGET_MAGNITUDE
target magnitude
Definition MooseTypes.h:950
@ WEP_LARGEST_IMAGINARY
largest imaginary
Definition MooseTypes.h:948
@ WEP_TARGET_IMAGINARY
target imaginary
Definition MooseTypes.h:952
@ WEP_SMALLEST_REAL
smallest real
Definition MooseTypes.h:947
@ WEP_SLEPC_DEFAULT
use whatever we have in SLEPC
Definition MooseTypes.h:954
@ WEP_LARGEST_MAGNITUDE
largest magnitude
Definition MooseTypes.h:944
@ WEP_SMALLEST_MAGNITUDE
smallest magnitude
Definition MooseTypes.h:945
@ WEP_ALL_EIGENVALUES
all eigenvalues
Definition MooseTypes.h:953
@ WEP_SMALLEST_IMAGINARY
smallest imaginary
Definition MooseTypes.h:949
@ WEP_TARGET_REAL
target real
Definition MooseTypes.h:951
@ WEP_LARGEST_REAL
largest real
Definition MooseTypes.h:946
@ EST_PJFNKMO
The same as PJFNK except that matrix-vector multiplication is employed to replace residual evaluation...
Definition MooseTypes.h:921
@ EST_JACOBI_DAVIDSON
Jacobi-Davidson.
Definition MooseTypes.h:917
@ EST_KRYLOVSCHUR
Krylov-Schur.
Definition MooseTypes.h:916
@ EST_JFNK
Jacobian-free Newton Krylov.
Definition MooseTypes.h:922
@ EST_NEWTON
Newton-based eigensolver with an assembled Jacobian matrix (fully coupled by default)
Definition MooseTypes.h:919
@ EST_POWER
Power / Inverse / RQI.
Definition MooseTypes.h:914
@ EST_NONLINEAR_POWER
Nonlinear inverse power.
Definition MooseTypes.h:918
@ EST_ARNOLDI
Arnoldi.
Definition MooseTypes.h:915
@ EST_PJFNK
Preconditioned Jacobian-free Newton Krylov.
Definition MooseTypes.h:920
@ EPT_GEN_INDEFINITE
Generalized Hermitian indefinite.
Definition MooseTypes.h:933
@ EPT_NON_HERMITIAN
Non-Hermitian.
Definition MooseTypes.h:931
@ EPT_GEN_HERMITIAN
Generalized Hermitian.
Definition MooseTypes.h:932
@ EPT_HERMITIAN
Hermitian.
Definition MooseTypes.h:930
@ EPT_GEN_NON_HERMITIAN
Generalized Non-Hermitian.
Definition MooseTypes.h:934
@ EPT_POS_GEN_NON_HERMITIAN
Generalized Non-Hermitian with positive (semi-)definite B.
Definition MooseTypes.h:935
@ EPT_SLEPC_DEFAULT
use whatever SLPEC has by default
Definition MooseTypes.h:936
const unsigned int invalid_uint