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 : #include "FixedPointSolve.h"
11 :
12 : #include "FEProblem.h"
13 : #include "Executioner.h"
14 : #include "MooseMesh.h"
15 : #include "NonlinearSystem.h"
16 : #include "AllLocalDofIndicesThread.h"
17 : #include "Console.h"
18 : #include "EigenExecutionerBase.h"
19 : #include "Convergence.h"
20 :
21 : InputParameters
22 468482 : FixedPointSolve::fixedPointDefaultConvergenceParams()
23 : {
24 468482 : InputParameters params = emptyInputParameters();
25 :
26 1405446 : params.addParam<unsigned int>(
27 936964 : "fixed_point_min_its", 1, "Specifies the minimum number of fixed point iterations.");
28 1405446 : params.addParam<unsigned int>(
29 936964 : "fixed_point_max_its", 1, "Specifies the maximum number of fixed point iterations.");
30 1405446 : params.addParam<bool>("disable_fixed_point_residual_norm_check",
31 936964 : false,
32 : "Disable the residual norm evaluation thus the three parameters "
33 : "fixed_point_rel_tol, fixed_point_abs_tol and fixed_point_force_norms.");
34 1405446 : params.addParam<bool>(
35 : "fixed_point_force_norms",
36 936964 : false,
37 : "Force the evaluation of both the TIMESTEP_BEGIN and TIMESTEP_END norms regardless of the "
38 : "existence of active MultiApps with those execute_on flags, default: false.");
39 1405446 : params.addParam<bool>(
40 : "accept_on_max_fixed_point_iteration",
41 936964 : false,
42 : "True to treat reaching the maximum number of fixed point iterations as converged.");
43 1405446 : params.addRangeCheckedParam<Real>("fixed_point_rel_tol",
44 936964 : 1e-8,
45 : "fixed_point_rel_tol>0",
46 : "The relative nonlinear residual drop to shoot for "
47 : "during fixed point iterations. This check is "
48 : "performed based on the main app's nonlinear "
49 : "residual.");
50 1405446 : params.addRangeCheckedParam<Real>("fixed_point_abs_tol",
51 936964 : 1e-50,
52 : "fixed_point_abs_tol>0",
53 : "The absolute nonlinear residual to shoot for "
54 : "during fixed point iterations. This check is "
55 : "performed based on the main app's nonlinear "
56 : "residual.");
57 :
58 468482 : params.addParam<PostprocessorName>("custom_pp",
59 : "Postprocessor for custom fixed point convergence check.");
60 1405446 : params.addParam<bool>("direct_pp_value",
61 936964 : false,
62 : "True to use direct postprocessor value "
63 : "(scaled by value on first iteration). "
64 : "False (default) to use difference in postprocessor "
65 : "value between fixed point iterations.");
66 1405446 : params.addRangeCheckedParam<Real>("custom_rel_tol",
67 936964 : 1e-8,
68 : "custom_rel_tol>0",
69 : "The relative nonlinear residual drop to shoot for "
70 : "during fixed point iterations. This check is "
71 : "performed based on the postprocessor defined by "
72 : "custom_pp residual.");
73 1405446 : params.addRangeCheckedParam<Real>("custom_abs_tol",
74 936964 : 1e-50,
75 : "custom_abs_tol>0",
76 : "The absolute nonlinear residual to shoot for "
77 : "during fixed point iterations. This check is "
78 : "performed based on postprocessor defined by "
79 : "the custom_pp residual.");
80 :
81 468482 : params.addParamNamesToGroup(
82 : "fixed_point_min_its fixed_point_max_its disable_fixed_point_residual_norm_check "
83 : "accept_on_max_fixed_point_iteration fixed_point_rel_tol fixed_point_abs_tol "
84 : "fixed_point_force_norms custom_pp direct_pp_value custom_abs_tol custom_rel_tol",
85 : "Fixed point iterations");
86 :
87 468482 : return params;
88 0 : }
89 :
90 : InputParameters
91 333199 : FixedPointSolve::validParams()
92 : {
93 333199 : InputParameters params = emptyInputParameters();
94 333199 : params += FixedPointSolve::fixedPointDefaultConvergenceParams();
95 :
96 333199 : params.addParam<ConvergenceName>(
97 : "multiapp_fixed_point_convergence",
98 : "Name of the Convergence object to use to assess convergence of the "
99 : "MultiApp fixed point solve. If not provided, a default Convergence "
100 : "will be constructed internally from the executioner parameters.");
101 :
102 : // Parameters for relaxing the fixed point process
103 999597 : params.addRangeCheckedParam<Real>("relaxation_factor",
104 666398 : 1.0,
105 : "relaxation_factor>0 & relaxation_factor<2",
106 : "Fraction of newly computed value to keep."
107 : "Set between 0 and 2.");
108 999597 : params.addParam<std::vector<std::string>>(
109 : "transformed_variables",
110 666398 : std::vector<std::string>(),
111 : "List of main app variables to transform during fixed point iterations");
112 999597 : params.addParam<std::vector<PostprocessorName>>(
113 : "transformed_postprocessors",
114 666398 : std::vector<PostprocessorName>(),
115 : "List of main app postprocessors to transform during fixed point iterations");
116 999597 : params.addDeprecatedParam<std::vector<std::string>>(
117 : "relaxed_variables",
118 666398 : std::vector<std::string>(),
119 : "List of main app variables to relax during fixed point iterations",
120 : "Relaxed variables is deprecated, use transformed_variables instead.");
121 :
122 333199 : params.addParam<bool>("auto_advance",
123 : "Whether to automatically advance sub-applications regardless of whether "
124 : "their solve converges, for transient executioners only.");
125 :
126 333199 : params.addParamNamesToGroup(
127 : "multiapp_fixed_point_convergence "
128 : "relaxation_factor transformed_variables transformed_postprocessors auto_advance",
129 : "Fixed point iterations");
130 :
131 999597 : params.addParam<unsigned int>(
132 : "max_xfem_update",
133 666398 : std::numeric_limits<unsigned int>::max(),
134 : "Maximum number of times to update XFEM crack topology in a step due to evolving cracks");
135 999597 : params.addParam<bool>("update_xfem_at_timestep_begin",
136 666398 : false,
137 : "Should XFEM update the mesh at the beginning of the timestep");
138 :
139 333199 : params.addParamNamesToGroup("max_xfem_update update_xfem_at_timestep_begin",
140 : "XFEM fixed point iterations");
141 :
142 333199 : return params;
143 0 : }
144 :
145 57629 : FixedPointSolve::FixedPointSolve(Executioner & ex)
146 : : SolveObject(ex),
147 171435 : _has_fixed_point_its(getParam<unsigned int>("fixed_point_max_its") > 1 ||
148 113806 : isParamSetByUser("multiapp_fixed_point_convergence")),
149 57629 : _relax_factor(getParam<Real>("relaxation_factor")),
150 57629 : _transformed_vars(getParam<std::vector<std::string>>("transformed_variables")),
151 57629 : _transformed_pps(getParam<std::vector<PostprocessorName>>("transformed_postprocessors")),
152 : // this value will be set by MultiApp
153 57629 : _secondary_relaxation_factor(1.0),
154 57629 : _fixed_point_it(0),
155 57629 : _fixed_point_status(MooseFixedPointConvergenceReason::UNSOLVED),
156 57629 : _max_xfem_update(getParam<unsigned int>("max_xfem_update")),
157 57629 : _update_xfem_at_timestep_begin(getParam<bool>("update_xfem_at_timestep_begin")),
158 57629 : _xfem_update_count(0),
159 57629 : _xfem_repeat_step(false),
160 57629 : _old_entering_time(_problem.time() - 1),
161 57629 : _fail_step(false),
162 57629 : _auto_advance_set_by_user(isParamValid("auto_advance")),
163 172887 : _auto_advance_user_value(_auto_advance_set_by_user ? getParam<bool>("auto_advance") : true)
164 : {
165 : // Handle deprecated parameters
166 57629 : if (!parameters().isParamSetByAddParam("relaxed_variables"))
167 0 : _transformed_vars = getParam<std::vector<std::string>>("relaxed_variables");
168 :
169 57629 : if (_transformed_vars.size() > 0 && _transformed_pps.size() > 0)
170 0 : mooseWarning(
171 : "Both variable and postprocessor transformation are active. If the two share dofs, the "
172 : "transformation will not be correct.");
173 :
174 57629 : if (!_app.isUltimateMaster())
175 : {
176 11385 : _secondary_relaxation_factor = _app.fixedPointConfig().sub_relaxation_factor;
177 11385 : _secondary_transformed_variables = _app.fixedPointConfig().sub_transformed_vars;
178 11385 : _secondary_transformed_pps = _app.fixedPointConfig().sub_transformed_pps;
179 : }
180 :
181 57629 : if (isParamValid("multiapp_fixed_point_convergence"))
182 52 : _problem.setMultiAppFixedPointConvergenceName(
183 : getParam<ConvergenceName>("multiapp_fixed_point_convergence"));
184 : else
185 57577 : _problem.setNeedToAddDefaultMultiAppFixedPointConvergence();
186 57629 : }
187 :
188 : bool
189 258852 : FixedPointSolve::solve()
190 : {
191 258852 : TIME_SECTION("PicardSolve", 1);
192 :
193 258852 : Real current_dt = _problem.dt();
194 :
195 258852 : bool converged = true;
196 :
197 : // need to back up multi-apps even when not doing fixed point iteration for recovering from failed
198 : // multiapp solve
199 258852 : _problem.backupMultiApps(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
200 258852 : _problem.backupMultiApps(EXEC_TIMESTEP_BEGIN);
201 258852 : _problem.backupMultiApps(EXEC_TIMESTEP_END);
202 258852 : _problem.backupMultiApps(EXEC_MULTIAPP_FIXED_POINT_END);
203 :
204 : // Prepare to relax variables as a main app
205 258852 : std::set<dof_id_type> transformed_dofs;
206 258852 : if ((_relax_factor != 1.0 || !dynamic_cast<PicardSolve *>(this)) && _transformed_vars.size() > 0)
207 : {
208 : // Snag all of the local dof indices for all of these variables
209 1749 : AllLocalDofIndicesThread aldit(_problem, _transformed_vars);
210 1749 : libMesh::ConstElemRange & elem_range = *_problem.mesh().getActiveLocalElementRange();
211 1749 : Threads::parallel_reduce(elem_range, aldit);
212 :
213 1749 : transformed_dofs = aldit.getDofIndices();
214 1749 : }
215 :
216 : // Prepare to relax variables as a subapp
217 258852 : std::set<dof_id_type> secondary_transformed_dofs;
218 258852 : if (_secondary_relaxation_factor != 1.0 || !dynamic_cast<PicardSolve *>(this))
219 : {
220 33629 : if (_secondary_transformed_variables.size() > 0)
221 : {
222 : // Snag all of the local dof indices for all of these variables
223 5281 : AllLocalDofIndicesThread aldit(_problem, _secondary_transformed_variables);
224 5281 : libMesh::ConstElemRange & elem_range = *_problem.mesh().getActiveLocalElementRange();
225 5281 : Threads::parallel_reduce(elem_range, aldit);
226 :
227 5281 : secondary_transformed_dofs = aldit.getDofIndices();
228 5281 : }
229 :
230 : // To detect a new time step
231 33629 : if (_old_entering_time == _problem.time())
232 : {
233 : // Keep track of the iteration number of the main app
234 21043 : _main_fixed_point_it++;
235 :
236 : // Save variable values before the solve. Solving will provide new values
237 21043 : saveVariableValues(false);
238 : }
239 : else
240 12586 : _main_fixed_point_it = 0;
241 : }
242 :
243 258852 : if (_has_fixed_point_its)
244 : {
245 11297 : auto & convergence = _problem.getConvergence(_problem.getMultiAppFixedPointConvergenceName());
246 11297 : convergence.initialize();
247 : }
248 :
249 258852 : _fixed_point_it = 0;
250 : while (true)
251 : {
252 303771 : if (_has_fixed_point_its)
253 : {
254 56216 : if (_fixed_point_it != 0)
255 : {
256 : // For every iteration other than the first, we need to restore the state of the MultiApps
257 44919 : _problem.restoreMultiApps(EXEC_TIMESTEP_BEGIN);
258 44919 : _problem.restoreMultiApps(EXEC_TIMESTEP_END);
259 : }
260 :
261 56212 : _console << COLOR_MAGENTA << "Beginning fixed point iteration " << _fixed_point_it
262 56212 : << COLOR_DEFAULT << std::endl
263 56212 : << std::endl;
264 : }
265 :
266 : // Solve a single application for one time step
267 303767 : const bool solve_converged = solveStep(transformed_dofs);
268 :
269 303425 : if (solve_converged)
270 : {
271 299348 : if (_has_fixed_point_its)
272 : {
273 : // Examine convergence metrics & properties and set the convergence reason
274 56191 : bool break_out = examineFixedPointConvergence(converged);
275 :
276 56191 : if (break_out)
277 : {
278 : // Except DefaultMultiAppFixedPointConvergence, convergence objects will not
279 : // update _fixed_point_status, so we give those cases generic values:
280 11272 : if (_fixed_point_status == MooseFixedPointConvergenceReason::CONVERGED_NONLINEAR)
281 : {
282 22 : if (converged)
283 22 : _fixed_point_status = MooseFixedPointConvergenceReason::CONVERGED_OBJECT;
284 : else
285 0 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_OBJECT;
286 : }
287 :
288 11272 : break;
289 : }
290 : }
291 : }
292 : else
293 : {
294 : // If the last solve didn't converge then we need to exit this step completely (even in the
295 : // case of coupling). So we can retry...
296 4077 : converged = false;
297 4077 : break;
298 : }
299 :
300 288076 : _problem.dt() =
301 : current_dt; // _dt might be smaller than this at this point for multistep methods
302 :
303 288076 : _fixed_point_it++;
304 :
305 288076 : if (!_has_fixed_point_its)
306 243157 : break;
307 44919 : }
308 :
309 258506 : if (converged)
310 : {
311 : // Fixed point iteration loop ends right above
312 254042 : _problem.execute(EXEC_MULTIAPP_FIXED_POINT_END);
313 254042 : _problem.execTransfers(EXEC_MULTIAPP_FIXED_POINT_END);
314 254042 : if (!_problem.execMultiApps(EXEC_MULTIAPP_FIXED_POINT_END, autoAdvance()))
315 : {
316 0 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_FAILED_MULTIAPP;
317 0 : return false;
318 : }
319 254042 : _problem.outputStep(EXEC_MULTIAPP_FIXED_POINT_END);
320 : }
321 :
322 : // Save postprocessors after the solve and their potential timestep_end execution
323 : // The postprocessors could be overwritten at timestep_begin, which is why they are saved
324 : // after the solve. They could also be saved right after the transfers.
325 258506 : if (_old_entering_time == _problem.time())
326 42002 : savePostprocessorValues(false);
327 :
328 258506 : if (converged)
329 : {
330 : // Update the subapp using the fixed point algorithm
331 254042 : if (_secondary_transformed_variables.size() > 0 &&
332 254042 : useFixedPointAlgorithmUpdateInsteadOfPicard(false) && _old_entering_time == _problem.time())
333 2475 : transformVariables(secondary_transformed_dofs, false);
334 :
335 : // Update the entering time, used to detect failed solves
336 254042 : _old_entering_time = _problem.time();
337 : }
338 :
339 258506 : if (_has_fixed_point_its)
340 11293 : printFixedPointConvergenceReason();
341 :
342 258506 : return converged;
343 258506 : }
344 :
345 : void
346 303667 : FixedPointSolve::saveAllValues(const bool primary)
347 : {
348 303667 : saveVariableValues(primary);
349 303667 : savePostprocessorValues(primary);
350 303667 : }
351 :
352 : bool
353 303767 : FixedPointSolve::solveStep(const std::set<dof_id_type> & transformed_dofs)
354 : {
355 303767 : bool auto_advance = autoAdvance();
356 :
357 303767 : _executioner.preSolve();
358 303767 : _problem.execTransfers(EXEC_TIMESTEP_BEGIN);
359 :
360 303767 : if (_fixed_point_it == 0)
361 : {
362 258852 : _problem.execute(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
363 258852 : _problem.execTransfers(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
364 258852 : if (!_problem.execMultiApps(EXEC_MULTIAPP_FIXED_POINT_BEGIN, autoAdvance()))
365 : {
366 0 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_FAILED_MULTIAPP;
367 0 : return false;
368 : }
369 258852 : _problem.outputStep(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
370 : }
371 :
372 303767 : if (!_problem.execMultiApps(EXEC_TIMESTEP_BEGIN, auto_advance))
373 : {
374 21 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_FAILED_MULTIAPP;
375 21 : return false;
376 : }
377 :
378 303675 : if (_problem.haveXFEM() && _update_xfem_at_timestep_begin)
379 0 : _problem.updateMeshXFEM();
380 :
381 303675 : _problem.execute(EXEC_TIMESTEP_BEGIN);
382 :
383 : // Transform the fixed point postprocessors before solving, but after the timestep_begin transfers
384 : // have been received
385 303671 : if (_transformed_pps.size() > 0 && useFixedPointAlgorithmUpdateInsteadOfPicard(true))
386 11682 : transformPostprocessors(true);
387 306531 : if (_secondary_transformed_pps.size() > 0 && useFixedPointAlgorithmUpdateInsteadOfPicard(false) &&
388 2860 : _problem.time() == _old_entering_time)
389 2860 : transformPostprocessors(false);
390 :
391 303671 : if (_has_fixed_point_its)
392 : {
393 56191 : auto & convergence = _problem.getConvergence(_problem.getMultiAppFixedPointConvergenceName());
394 56191 : convergence.preExecute();
395 : }
396 :
397 : // Perform output for timestep begin
398 303671 : _problem.outputStep(EXEC_TIMESTEP_BEGIN);
399 :
400 : // Update warehouse active objects
401 303667 : _problem.updateActiveObjects();
402 :
403 : // Save the current values of variables and postprocessors, before the solve
404 303667 : saveAllValues(true);
405 :
406 303667 : if (_has_fixed_point_its)
407 56191 : _console << COLOR_MAGENTA << "\nMain app solve:" << COLOR_DEFAULT << std::endl;
408 303667 : if (!_inner_solve->solve())
409 : {
410 3876 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_NONLINEAR;
411 :
412 : // Perform the output of the current, failed time step (this only occurs if desired)
413 3876 : _problem.outputStep(EXEC_FAILED);
414 3876 : return false;
415 : }
416 : else
417 299709 : _fixed_point_status = MooseFixedPointConvergenceReason::CONVERGED_NONLINEAR;
418 :
419 : // Use the fixed point algorithm if the conditions (availability of values, etc) are met
420 299709 : if (_transformed_vars.size() > 0 && useFixedPointAlgorithmUpdateInsteadOfPicard(true))
421 5603 : transformVariables(transformed_dofs, true);
422 :
423 299709 : if (_problem.haveXFEM() && (_xfem_update_count < _max_xfem_update) && _problem.updateMeshXFEM())
424 : {
425 0 : _console << "\nXFEM modified mesh, repeating step" << std::endl;
426 0 : _xfem_repeat_step = true;
427 0 : ++_xfem_update_count;
428 : }
429 : else
430 : {
431 299709 : if (_problem.haveXFEM())
432 : {
433 0 : _xfem_repeat_step = false;
434 0 : _xfem_update_count = 0;
435 0 : _console << "\nXFEM did not modify mesh, continuing" << std::endl;
436 : }
437 :
438 299709 : _problem.onTimestepEnd();
439 299709 : _problem.execute(EXEC_TIMESTEP_END);
440 :
441 299588 : _problem.execTransfers(EXEC_TIMESTEP_END);
442 299588 : if (!_problem.execMultiApps(EXEC_TIMESTEP_END, auto_advance))
443 : {
444 84 : _fixed_point_status = MooseFixedPointConvergenceReason::DIVERGED_FAILED_MULTIAPP;
445 84 : return false;
446 : }
447 : }
448 :
449 299444 : if (_fail_step)
450 : {
451 96 : _fail_step = false;
452 96 : return false;
453 : }
454 :
455 299348 : _executioner.postSolve();
456 :
457 299348 : return true;
458 : }
459 :
460 : bool
461 56191 : FixedPointSolve::examineFixedPointConvergence(bool & converged)
462 : {
463 56191 : _problem.execute(EXEC_MULTIAPP_FIXED_POINT_CONVERGENCE);
464 :
465 56191 : auto & convergence = _problem.getConvergence(_problem.getMultiAppFixedPointConvergenceName());
466 56191 : const auto status = convergence.checkConvergence(_fixed_point_it);
467 56191 : switch (status)
468 : {
469 10885 : case Convergence::MooseConvergenceStatus::CONVERGED:
470 10885 : converged = true;
471 10885 : return true;
472 387 : case Convergence::MooseConvergenceStatus::DIVERGED:
473 387 : converged = false;
474 387 : return true;
475 44919 : case Convergence::MooseConvergenceStatus::ITERATING:
476 44919 : converged = false;
477 44919 : return false;
478 0 : default:
479 0 : mooseError("Should not reach here");
480 : }
481 : }
482 :
483 : void
484 11293 : FixedPointSolve::printFixedPointConvergenceReason()
485 : {
486 11293 : _console << "Fixed point convergence reason: ";
487 11293 : switch (_fixed_point_status)
488 : {
489 3336 : case MooseFixedPointConvergenceReason::CONVERGED_ABS:
490 3336 : _console << "CONVERGED_ABS";
491 3336 : break;
492 7411 : case MooseFixedPointConvergenceReason::CONVERGED_RELATIVE:
493 7411 : _console << "CONVERGED_RELATIVE";
494 7411 : break;
495 41 : case MooseFixedPointConvergenceReason::CONVERGED_PP:
496 41 : _console << "CONVERGED_PP";
497 41 : break;
498 75 : case MooseFixedPointConvergenceReason::REACH_MAX_ITS:
499 75 : _console << "REACH_MAX_ITS";
500 75 : break;
501 22 : case MooseFixedPointConvergenceReason::CONVERGED_OBJECT:
502 22 : _console << "CONVERGED_OBJECT (see Convergence object)";
503 22 : break;
504 387 : case MooseFixedPointConvergenceReason::DIVERGED_MAX_ITS:
505 387 : _console << "DIVERGED_MAX_ITS";
506 387 : break;
507 0 : case MooseFixedPointConvergenceReason::DIVERGED_NONLINEAR:
508 0 : _console << "DIVERGED_NONLINEAR";
509 0 : break;
510 21 : case MooseFixedPointConvergenceReason::DIVERGED_FAILED_MULTIAPP:
511 21 : _console << "DIVERGED_FAILED_MULTIAPP";
512 21 : break;
513 0 : case MooseFixedPointConvergenceReason::DIVERGED_OBJECT:
514 0 : _console << "DIVERGED_OBJECT (see Convergence object)";
515 0 : break;
516 0 : default:
517 : // UNSOLVED and CONVERGED_NONLINEAR should not be hit when coupling
518 : // iteration is not on here
519 0 : mooseError("Internal error: wrong fixed point status!");
520 : break;
521 : }
522 11293 : _console << std::endl;
523 11293 : }
524 :
525 : bool
526 998567 : FixedPointSolve::autoAdvance() const
527 : {
528 998567 : bool auto_advance = !(_has_fixed_point_its && _problem.isTransient());
529 :
530 998567 : if (dynamic_cast<EigenExecutionerBase *>(&_executioner) && _has_fixed_point_its)
531 0 : auto_advance = true;
532 :
533 998567 : if (_auto_advance_set_by_user)
534 194 : auto_advance = _auto_advance_user_value;
535 :
536 998567 : return auto_advance;
537 : }
|