Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : // MOOSE includes
11 : #include "Eigenvalue.h"
12 : #include "EigenProblem.h"
13 : #include "Factory.h"
14 : #include "MooseApp.h"
15 : #include "NonlinearEigenSystem.h"
16 : #include "PetscSupport.h"
17 : #include "SlepcSupport.h"
18 : #include "UserObject.h"
19 :
20 : #include "libmesh/petsc_solver_exception.h"
21 :
22 : // Needed for LIBMESH_CHECK_ERR
23 : using libMesh::PetscSolverException;
24 :
25 : registerMooseObject("MooseApp", Eigenvalue);
26 :
27 : InputParameters
28 15385 : Eigenvalue::validParams()
29 : {
30 15385 : InputParameters params = Executioner::validParams();
31 :
32 15385 : params.addClassDescription(
33 : "Eigenvalue solves a standard/generalized linear or nonlinear eigenvalue problem");
34 :
35 15385 : params += FEProblemSolve::validParams();
36 15385 : params.addParam<Real>("time", 0.0, "System time");
37 :
38 : // matrix_free will be an invalid for griffin once the integration is done.
39 : // In this PR, we can not change it. It will still be a valid option when users
40 : // use non-Newton algorithms
41 46155 : params.addParam<bool>(
42 : "matrix_free",
43 30770 : false,
44 : "Whether or not to use a matrix free fashion to form operators. "
45 : "If true, shell matrices will be used and meanwhile a preconditioning matrix"
46 : "may be formed as well.");
47 :
48 46155 : params.addParam<bool>(
49 : "precond_matrix_free",
50 30770 : false,
51 : "Whether or not to use a matrix free fashion for forming the preconditioning matrix. "
52 : "If true, a shell matrix will be used for preconditioner.");
53 :
54 46155 : params.addParam<bool>("constant_matrices",
55 30770 : false,
56 : "Whether or not to use constant matrices so that we can use them to form "
57 : "residuals on both linear and "
58 : "nonlinear iterations");
59 :
60 46155 : params.addParam<bool>("precond_matrix_includes_eigen",
61 30770 : false,
62 : "Whether or not to include eigen kernels in the preconditioning matrix. "
63 : "If true, the preconditioning matrix will include eigen kernels.");
64 :
65 15385 : params.addPrivateParam<bool>("_use_eigen_value", true);
66 :
67 15385 : params.addParam<Real>("initial_eigenvalue", 1, "Initial eigenvalue");
68 15385 : params.addParam<PostprocessorName>(
69 : "normalization", "Postprocessor evaluating norm of eigenvector for normalization");
70 15385 : params.addParam<Real>("normal_factor",
71 : "Normalize eigenvector to make a defined norm equal to this factor");
72 :
73 46155 : params.addParam<bool>("auto_initialization",
74 30770 : true,
75 : "If true, we will set an initial eigen vector in moose, otherwise EPS "
76 : "solver will initial eigen vector");
77 :
78 15385 : params.addParamNamesToGroup("matrix_free precond_matrix_free constant_matrices "
79 : "precond_matrix_includes_eigen",
80 : "Matrix and Matrix-Free");
81 15385 : params.addParamNamesToGroup("initial_eigenvalue auto_initialization",
82 : "Eigenvector and eigenvalue initialization");
83 15385 : params.addParamNamesToGroup("normalization normal_factor", "Solution normalization");
84 :
85 : // If Newton and Inverse Power is combined in SLEPc side
86 15385 : params.addPrivateParam<bool>("_newton_inverse_power", false);
87 :
88 : // Add slepc options and eigen problems
89 : #ifdef LIBMESH_HAVE_SLEPC
90 15385 : Moose::SlepcSupport::getSlepcValidParams(params);
91 :
92 15385 : params += Moose::SlepcSupport::getSlepcEigenProblemValidParams();
93 : #endif
94 15385 : return params;
95 0 : }
96 :
97 562 : Eigenvalue::Eigenvalue(const InputParameters & parameters)
98 : : Executioner(parameters),
99 562 : _eigen_problem(*getCheckedPointerParam<EigenProblem *>(
100 : "_eigen_problem", "This might happen if you don't have a mesh")),
101 562 : _feproblem_solve(*this),
102 562 : _normalization(isParamValid("normalization") ? &getPostprocessorValue("normalization")
103 : : nullptr),
104 562 : _system_time(getParam<Real>("time")),
105 562 : _time_step(_eigen_problem.timeStep()),
106 562 : _time(_eigen_problem.time()),
107 1124 : _final_timer(registerTimedSection("final", 1))
108 : {
109 : // Extract and store SLEPc options
110 : #ifdef LIBMESH_HAVE_SLEPC
111 : mooseAssert(_fe_problem.numSolverSystems() == 1,
112 : "The Eigenvalue executioner only currently supports a single solver system.");
113 :
114 562 : Moose::SlepcSupport::storeSolveType(_eigen_problem, parameters);
115 :
116 562 : Moose::SlepcSupport::setEigenProblemSolverParams(_eigen_problem, parameters);
117 1116 : _eigen_problem.setEigenproblemType(
118 558 : _eigen_problem.solverParams(/*solver_sys_num=*/0)._eigen_problem_type);
119 :
120 : // pass two control parameters to eigen problem
121 1116 : _eigen_problem.solverParams(/*solver_sys_num=*/0)._free_power_iterations =
122 558 : getParam<unsigned int>("free_power_iterations");
123 1116 : _eigen_problem.solverParams(/*solver_sys_num=*/0)._extra_power_iterations =
124 558 : getParam<unsigned int>("extra_power_iterations");
125 :
126 558 : if (!isParamValid("normalization") && isParamValid("normal_factor"))
127 0 : paramError("normal_factor",
128 : "Cannot set scaling factor without defining normalization postprocessor.");
129 :
130 558 : _fixed_point_solve->setInnerSolve(_feproblem_solve);
131 558 : _time = _system_time;
132 :
133 558 : if (isParamValid("normalization"))
134 : {
135 10 : const auto & normpp = getParam<PostprocessorName>("normalization");
136 10 : if (isParamValid("normal_factor"))
137 10 : _eigen_problem.setNormalization(normpp, getParam<Real>("normal_factor"));
138 : else
139 0 : _eigen_problem.setNormalization(normpp);
140 : }
141 :
142 558 : _eigen_problem.setInitialEigenvalue(getParam<Real>("initial_eigenvalue"));
143 :
144 : // Set a flag to nonlinear eigen system
145 558 : _eigen_problem.getNonlinearEigenSystem(/*nl_sys_num=*/0)
146 558 : .precondMatrixIncludesEigenKernels(getParam<bool>("precond_matrix_includes_eigen"));
147 : #else
148 : mooseError("SLEPc is required to use Eigenvalue executioner, please use '--download-slepc in "
149 : "PETSc configuration'");
150 : #endif
151 : // SLEPc older than 3.13.0 can not take initial guess from moose
152 : // It may generate converge issues
153 : #if PETSC_RELEASE_LESS_THAN(3, 13, 0)
154 : mooseDeprecated(
155 : "Please use SLEPc-3.13.0 or higher. Old versions of SLEPc likely produce bad convergence");
156 : #endif
157 :
158 : // To avoid petsc unused option warnings, ensure we do not set irrelevant options.
159 558 : Moose::PetscSupport::dontAddLinearConvergedReason(_fe_problem);
160 558 : Moose::PetscSupport::dontAddNonlinearConvergedReason(_fe_problem);
161 558 : Moose::PetscSupport::dontAddPetscFlag("-mat_mffd_type", _fe_problem.getPetscOptions());
162 558 : Moose::PetscSupport::dontAddPetscFlag("-st_ksp_atol", _fe_problem.getPetscOptions());
163 558 : Moose::PetscSupport::dontAddPetscFlag("-st_ksp_max_it", _fe_problem.getPetscOptions());
164 558 : Moose::PetscSupport::dontAddPetscFlag("-st_ksp_rtol", _fe_problem.getPetscOptions());
165 558 : if (!_eigen_problem.solverParams()._eigen_matrix_free)
166 84 : Moose::PetscSupport::dontAddPetscFlag("-snes_mf_operator", _fe_problem.getPetscOptions());
167 558 : }
168 :
169 : #ifdef LIBMESH_HAVE_SLEPC
170 : void
171 542 : Eigenvalue::init()
172 : {
173 542 : if (isParamValid("normalization"))
174 : {
175 10 : const auto & normpp = getParam<PostprocessorName>("normalization");
176 10 : const auto & exec = _eigen_problem.getUserObject<UserObject>(normpp).getExecuteOnEnum();
177 10 : if (!exec.isValueSet(EXEC_LINEAR))
178 0 : mooseError("Normalization postprocessor ", normpp, " requires execute_on = 'linear'");
179 : }
180 :
181 : // Does not allow time kernels
182 542 : checkIntegrity();
183 :
184 : // Provide vector of ones to solver
185 : // "auto_initialization" is on by default and we init the vector values associated
186 : // with eigen-variables as ones. If "auto_initialization" is turned off by users,
187 : // it is up to users to provide an initial guess. If "auto_initialization" is off
188 : // and users does not provide an initial guess, slepc will automatically generate
189 : // a random vector as the initial guess. The motivation to offer this option is
190 : // that we have to initialize ONLY eigen variables in multiphysics simulation.
191 : // auto_initialization can be overriden by initial conditions.
192 542 : if (getParam<bool>("auto_initialization") && !_app.isRestarting())
193 532 : _eigen_problem.initEigenvector(1.0);
194 :
195 : // Some setup
196 542 : _eigen_problem.execute(EXEC_PRE_MULTIAPP_SETUP);
197 542 : _eigen_problem.initialSetup();
198 :
199 : // Make sure all PETSc options are setup correctly
200 542 : prepareSolverOptions();
201 542 : }
202 :
203 : void
204 542 : Eigenvalue::prepareSolverOptions()
205 : {
206 : #if PETSC_RELEASE_LESS_THAN(3, 12, 0)
207 : // Make sure the SLEPc options are setup for this app
208 : Moose::SlepcSupport::slepcSetOptions(
209 : _eigen_problem, _eigen_problem.solverParams(/*eigen_sys_num=*/0), _pars);
210 : #else
211 : // Options need to be setup once only
212 542 : if (!_eigen_problem.petscOptionsInserted())
213 : {
214 : // Parent application has the default data base
215 532 : if (!_app.isUltimateMaster())
216 24 : LibmeshPetscCall(PetscOptionsPush(_eigen_problem.petscOptionsDatabase()));
217 532 : Moose::SlepcSupport::slepcSetOptions(
218 532 : _eigen_problem, _eigen_problem.solverParams(/*eigen_sys_num=*/0), _pars);
219 532 : if (!_app.isUltimateMaster())
220 24 : LibmeshPetscCall(PetscOptionsPop());
221 532 : _eigen_problem.petscOptionsInserted() = true;
222 : }
223 : #endif
224 542 : }
225 :
226 : void
227 542 : Eigenvalue::checkIntegrity()
228 : {
229 : // check to make sure that we don't have any time kernels in eigenvalue simulation
230 542 : if (_eigen_problem.getNonlinearSystemBase(/*nl_sys=*/0).containsTimeKernel())
231 0 : mooseError("You have specified time kernels in your eigenvalue simulation");
232 542 : }
233 :
234 : #endif
235 :
236 : void
237 650 : Eigenvalue::execute()
238 : {
239 : #ifdef LIBMESH_HAVE_SLEPC
240 : // Recovering makes sense for only transient simulations since the solution from
241 : // the previous time steps is required.
242 650 : if (_app.isRecovering())
243 : {
244 24 : _console << "\nCannot recover eigenvalue solves!\nExiting...\n" << std::endl;
245 24 : _last_solve_converged = true;
246 24 : return;
247 : }
248 :
249 : // Outputs initial conditions set by users
250 : // It is consistent with Steady
251 626 : _time_step = 0;
252 626 : _time = _time_step;
253 626 : _eigen_problem.outputStep(EXEC_INITIAL);
254 626 : _time = _system_time;
255 :
256 626 : preExecute();
257 :
258 : // The following code of this function is copied from "Steady"
259 : // "Eigenvalue" implementation can be considered a one-time-step simulation to
260 : // have the code compatible with the rest moose world.
261 626 : _eigen_problem.advanceState();
262 :
263 : // First step in any eigenvalue state solve is always 1 (preserving backwards compatibility)
264 626 : _time_step = 1;
265 :
266 : #ifdef LIBMESH_ENABLE_AMR
267 :
268 : // Define the refinement loop
269 626 : auto steps = _eigen_problem.adaptivity().getSteps();
270 1242 : for (const auto r_step : make_range(steps + 1))
271 : {
272 : #endif // LIBMESH_ENABLE_AMR
273 626 : _eigen_problem.timestepSetup();
274 :
275 626 : _last_solve_converged = _fixed_point_solve->solve();
276 626 : if (!lastSolveConverged())
277 : {
278 10 : _console << "Aborting as solve did not converge" << std::endl;
279 10 : break;
280 : }
281 :
282 : // Compute markers and indicators only when we do have at least one adaptivity step
283 616 : if (steps)
284 : {
285 0 : _eigen_problem.computeIndicators();
286 0 : _eigen_problem.computeMarkers();
287 : }
288 : // need to keep _time in sync with _time_step to get correct output
289 616 : _time = _time_step;
290 616 : _eigen_problem.outputStep(EXEC_TIMESTEP_END);
291 616 : _time = _system_time;
292 :
293 : #ifdef LIBMESH_ENABLE_AMR
294 616 : if (r_step < steps)
295 : {
296 0 : _eigen_problem.adaptMesh();
297 : }
298 :
299 616 : _time_step++;
300 : }
301 : #endif
302 :
303 : {
304 626 : TIME_SECTION(_final_timer)
305 626 : _eigen_problem.execMultiApps(EXEC_FINAL);
306 626 : _eigen_problem.finalizeMultiApps();
307 626 : _eigen_problem.postExecute();
308 626 : _eigen_problem.execute(EXEC_FINAL);
309 626 : _time = _time_step;
310 626 : _eigen_problem.outputStep(EXEC_FINAL);
311 626 : _time = _system_time;
312 626 : }
313 :
314 626 : postExecute();
315 :
316 : #else
317 : mooseError("SLEPc is required for eigenvalue executioner, please use --download-slepc when "
318 : "configuring PETSc ");
319 : #endif
320 : }
|