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 "TransientBase.h"
11 :
12 : // MOOSE includes
13 : #include "Factory.h"
14 : #include "SubProblem.h"
15 : #include "TimeStepper.h"
16 : #include "MooseApp.h"
17 : #include "Conversion.h"
18 : #include "FEProblem.h"
19 : #include "NonlinearSystem.h"
20 : #include "Control.h"
21 : #include "TimePeriod.h"
22 : #include "MooseMesh.h"
23 : #include "TimeIntegrator.h"
24 : #include "Console.h"
25 : #include "AuxiliarySystem.h"
26 : #include "Convergence.h"
27 : #include "ConvergenceIterationTypes.h"
28 :
29 : #include "libmesh/implicit_system.h"
30 : #include "libmesh/nonlinear_implicit_system.h"
31 : #include "libmesh/transient_system.h"
32 : #include "libmesh/numeric_vector.h"
33 :
34 : // C++ Includes
35 : #include <iomanip>
36 : #include <iostream>
37 : #include <fstream>
38 : #include <sstream>
39 : #include <iomanip>
40 :
41 : InputParameters
42 168260 : TransientBase::defaultSteadyStateConvergenceParams()
43 : {
44 168260 : InputParameters params = emptyInputParameters();
45 :
46 504780 : params.addParam<Real>("steady_state_tolerance",
47 336520 : 1.0e-08,
48 : "Whenever the relative residual changes by less "
49 : "than this the solution will be considered to be "
50 : "at steady state.");
51 504780 : params.addParam<bool>("check_aux",
52 336520 : false,
53 : "Whether to check the auxiliary system for convergence to steady-state. If "
54 : "false, then the solution vector from the solver system is used.");
55 504780 : params.addParam<bool>(
56 : "normalize_solution_diff_norm_by_dt",
57 336520 : true,
58 : "Whether to divide the solution difference norm by dt. If taking 'small' "
59 : "time steps you probably want this to be true. If taking very 'large' timesteps in an "
60 : "attempt to *reach* a steady-state, you probably want this parameter to be false.");
61 :
62 168260 : params.addParamNamesToGroup("steady_state_tolerance check_aux normalize_solution_diff_norm_by_dt",
63 : "Steady State Detection");
64 :
65 168260 : return params;
66 0 : }
67 :
68 : InputParameters
69 89868 : TransientBase::validParams()
70 : {
71 89868 : InputParameters params = Executioner::validParams();
72 89868 : params += TransientBase::defaultSteadyStateConvergenceParams();
73 :
74 89868 : params.addClassDescription("Executioner for time varying simulations.");
75 :
76 89868 : std::vector<Real> sync_times(1);
77 89868 : sync_times[0] = -std::numeric_limits<Real>::max();
78 :
79 : /**
80 : * For backwards compatibility we'll allow users to set the TimeIntegration scheme inside of the
81 : * executioner block
82 : * as long as the TimeIntegrator does not have any additional parameters.
83 : */
84 : MooseEnum schemes("implicit-euler explicit-euler crank-nicolson bdf2 explicit-midpoint dirk "
85 : "explicit-tvd-rk-2 newmark-beta",
86 89868 : "implicit-euler");
87 :
88 89868 : params.addParam<Real>("start_time", 0.0, "The start time of the simulation");
89 89868 : params.addParam<Real>("end_time", 1.0e30, "The end time of the simulation");
90 89868 : params.addParam<Real>("dt", 1., "The timestep size between solves");
91 89868 : params.addParam<Real>("dtmin", 1.0e-12, "The minimum timestep size in an adaptive run");
92 89868 : params.addParam<Real>("dtmax", 1.0e30, "The maximum timestep size in an adaptive run");
93 269604 : params.addParam<bool>(
94 179736 : "reset_dt", false, "Use when restarting a calculation to force a change in dt.");
95 269604 : params.addParam<unsigned int>("num_steps",
96 179736 : std::numeric_limits<unsigned int>::max(),
97 : "The number of timesteps in a transient run");
98 89868 : params.addParam<int>("n_startup_steps", 0, "The number of timesteps during startup");
99 :
100 269604 : params.addParam<bool>(
101 179736 : "steady_state_detection", false, "Whether or not to check for steady state conditions");
102 89868 : params.addParam<ConvergenceName>(
103 : "steady_state_convergence",
104 : "Name of the Convergence object to use to assess whether the solution has reached a steady "
105 : "state. If not provided, a default Convergence will be constructed internally from the "
106 : "executioner parameters.");
107 269604 : params.addParam<Real>(
108 : "steady_state_start_time",
109 179736 : 0.0,
110 : "Minimum amount of time to run before checking for steady state conditions.");
111 :
112 89868 : params.addParam<std::vector<std::string>>("time_periods", "The names of periods");
113 89868 : params.addParam<std::vector<Real>>("time_period_starts", "The start times of time periods");
114 89868 : params.addParam<std::vector<Real>>("time_period_ends", "The end times of time periods");
115 269604 : params.addParam<bool>(
116 179736 : "abort_on_solve_fail", false, "abort if solve not converged rather than cut timestep");
117 269604 : params.addParam<bool>(
118 : "error_on_dtmin",
119 179736 : true,
120 : "Throw error when timestep is less than dtmin instead of just aborting solve.");
121 89868 : params.addParam<MooseEnum>("scheme", schemes, "Time integration scheme used.");
122 269604 : params.addParam<Real>("timestep_tolerance",
123 179736 : 1.0e-12,
124 : "the tolerance setting for final timestep size and sync times");
125 :
126 269604 : params.addParam<bool>("use_multiapp_dt",
127 179736 : false,
128 : "If true then the dt for the simulation will be "
129 : "chosen by the MultiApps. If false (the "
130 : "default) then the minimum over the master dt "
131 : "and the MultiApps is used");
132 :
133 89868 : params.addParamNamesToGroup(
134 : "steady_state_detection steady_state_convergence steady_state_start_time",
135 : "Steady State Detection");
136 :
137 89868 : params.addParamNamesToGroup("start_time dtmin dtmax n_startup_steps "
138 : "abort_on_solve_fail timestep_tolerance use_multiapp_dt",
139 : "Advanced");
140 :
141 89868 : params.addParamNamesToGroup("time_periods time_period_starts time_period_ends", "Time Periods");
142 :
143 : // This Executioner supports --test-restep
144 89868 : params.set<bool>("_supports_test_restep") = true;
145 :
146 179736 : return params;
147 89868 : }
148 :
149 30620 : TransientBase::TransientBase(const InputParameters & parameters)
150 : : Executioner(parameters),
151 30620 : _problem(_fe_problem),
152 61240 : _aux(_fe_problem.getAuxiliarySystem()),
153 30620 : _time_scheme(getParam<MooseEnum>("scheme").getEnum<Moose::TimeIntegratorType>()),
154 30620 : _time_stepper(nullptr),
155 30620 : _t_step(_problem.timeStep()),
156 30620 : _time(_problem.time()),
157 30620 : _time_old(_problem.timeOld()),
158 30620 : _dt(_problem.dt()),
159 30620 : _dt_old(_problem.dtOld()),
160 30620 : _unconstrained_dt(declareRecoverableData<Real>("unconstrained_dt", -1)),
161 30620 : _at_sync_point(declareRecoverableData<bool>("at_sync_point", false)),
162 30620 : _last_solve_converged(declareRecoverableData<bool>("last_solve_converged", true)),
163 30620 : _xfem_repeat_step(false),
164 30620 : _end_time(getParam<Real>("end_time")),
165 30620 : _dtmin(getParam<Real>("dtmin")),
166 30620 : _dtmax(getParam<Real>("dtmax")),
167 30620 : _num_steps(getParam<unsigned int>("num_steps")),
168 30620 : _n_startup_steps(getParam<int>("n_startup_steps")),
169 30620 : _steady_state_detection(getParam<bool>("steady_state_detection")),
170 30620 : _steady_state_start_time(getParam<Real>("steady_state_start_time")),
171 30620 : _sync_times(_app.getOutputWarehouse().getSyncTimes()),
172 30620 : _abort(getParam<bool>("abort_on_solve_fail")),
173 30620 : _error_on_dtmin(getParam<bool>("error_on_dtmin")),
174 30620 : _time_interval(declareRecoverableData<bool>("time_interval", false)),
175 30620 : _start_time(getParam<Real>("start_time")),
176 30620 : _timestep_tolerance(getParam<Real>("timestep_tolerance")),
177 30620 : _target_time(declareRecoverableData<Real>("target_time", -std::numeric_limits<Real>::max())),
178 30620 : _use_multiapp_dt(getParam<bool>("use_multiapp_dt")),
179 61240 : _testing_restep(false)
180 : {
181 30620 : _t_step = 0;
182 30620 : _dt = 0;
183 30620 : _next_interval_output_time = 0.0;
184 :
185 : // Either a start_time has been forced on us, or we want to tell the App about what our start time
186 : // is (in case anyone else is interested.
187 30620 : if (_app.hasStartTime())
188 0 : _start_time = _app.getStartTime();
189 30620 : else if (parameters.isParamSetByUser("start_time"))
190 3949 : _app.setStartTime(_start_time);
191 :
192 30620 : _time = _time_old = _start_time;
193 30620 : _problem.transient(true);
194 :
195 30620 : setupTimeIntegrator();
196 :
197 : // Cut timesteps and end_time in half
198 30616 : if (_app.testCheckpointHalfTransient())
199 : {
200 : mooseAssert(!_app.testReStep(), "Cannot use with restep");
201 2039 : _end_time = (_start_time + _end_time) / 2.0;
202 2039 : _num_steps /= 2.0;
203 :
204 2039 : if (_num_steps == 0) // Always do one step in the first half
205 748 : _num_steps = 1;
206 : }
207 : // Retest a timestep (see options below for which timestep)
208 28577 : else if (_app.testReStep())
209 : {
210 1394 : if (_problem.shouldSolve())
211 : {
212 : // If num_steps is defined, we'll use that to determine restep timestep
213 1127 : if (!parameters.isParamSetByAddParam("num_steps"))
214 865 : _test_restep_step = _t_step + (_num_steps + 1) / 2;
215 : // If end_time is defined, we'll use the half time to determine when to restep
216 1127 : if (!parameters.isParamSetByAddParam("end_time"))
217 300 : _test_restep_time = (_start_time + _end_time) / 2;
218 : // If neither was set or we are doing pseudo-transient, pick the second timestep
219 1127 : if ((!_test_restep_step && !_test_restep_time) || _steady_state_detection)
220 30 : _test_restep_step = 2;
221 :
222 1127 : std::stringstream msg;
223 1127 : if (_test_restep_step && _test_restep_time)
224 57 : msg << "Timestep " << *_test_restep_step << " or time " << *_test_restep_time
225 57 : << " (whichever happens first)";
226 1070 : else if (_test_restep_step)
227 827 : msg << "Timestep " << *_test_restep_step;
228 243 : else if (_test_restep_time)
229 243 : msg << "Time " << *_test_restep_time;
230 1127 : mooseInfo(msg.str(), " will be forcefully retried due to --test-restep.");
231 1127 : }
232 : else
233 267 : mooseInfo(
234 : "A timestep is not being retried with --test-restep because Problem/solve=false.\n\nTo "
235 : "avoid this test being ran, you could set `restep = false` in the test specification.");
236 : }
237 :
238 30616 : if (isParamValid("steady_state_convergence"))
239 38 : _problem.setSteadyStateConvergenceName(getParam<ConvergenceName>("steady_state_convergence"));
240 : else
241 : // Note that we create a steady-state Convergence object even if steady_state_detection ==
242 : // false. This could possibly be changed in the future, but TransientMultiApp would need to be
243 : // able to signal for the Convergence object to be created in case it uses steady-state
244 : // detection for sub-stepping.
245 30578 : _problem.setNeedToAddDefaultSteadyStateConvergence();
246 30616 : }
247 :
248 : void
249 29845 : TransientBase::init()
250 : {
251 29845 : _problem.execute(EXEC_PRE_MULTIAPP_SETUP);
252 29845 : _problem.initialSetup();
253 29656 : _fixed_point_solve->initialSetup();
254 :
255 : mooseAssert(getTimeStepper(), "No time stepper was set");
256 :
257 29656 : _time_stepper->init();
258 :
259 29633 : auto & conv = _problem.getConvergence(_problem.getSteadyStateConvergenceName());
260 29633 : conv.checkIterationType(ConvergenceIterationTypes::STEADY_STATE);
261 :
262 29633 : if (_app.isRecovering()) // Recover case
263 : {
264 2138 : if (_t_step == 0)
265 0 : mooseError(
266 : "Internal error in TransientBase executioner: _t_step is equal to 0 while recovering "
267 : "in init().");
268 :
269 2138 : _dt_old = _dt;
270 : }
271 29633 : }
272 :
273 : void
274 30013 : TransientBase::preExecute()
275 : {
276 30013 : _time_stepper->preExecute();
277 :
278 30013 : if (!_app.isRecovering())
279 : {
280 27984 : _t_step = 0;
281 27984 : _dt = 0;
282 27984 : _next_interval_output_time = 0.0;
283 27984 : if (!_app.isRestarting())
284 27589 : _time = _time_old = _start_time;
285 :
286 27984 : _problem.outputStep(EXEC_INITIAL);
287 :
288 27984 : computeDT();
289 27980 : _dt = getDT();
290 27980 : if (_dt == 0)
291 4 : mooseError("Time stepper computed zero time step size on initial which is not allowed.\n"
292 : "1. If you are using an existing time stepper, double check the values in your "
293 : "input file or report an error.\n"
294 : "2. If you are developing a new time stepper, make sure that initial time step "
295 : "size in your code is computed correctly.");
296 27976 : const auto & tis = getTimeIntegrators();
297 56029 : for (auto & ti : tis)
298 28057 : ti->init();
299 27972 : }
300 30001 : }
301 :
302 : void
303 184013 : TransientBase::preStep()
304 : {
305 184013 : _time_stepper->preStep();
306 184013 : }
307 :
308 : void
309 209299 : TransientBase::postStep()
310 : {
311 209299 : _time_stepper->postStep();
312 209299 : }
313 :
314 : void
315 21571 : TransientBase::execute()
316 : {
317 21571 : preExecute();
318 :
319 : // Start time loop...
320 199048 : while (keepGoing())
321 : {
322 177760 : incrementStepOrReject();
323 177756 : preStep();
324 177756 : computeDT();
325 177635 : takeStep();
326 177489 : endStep();
327 177489 : postStep();
328 : }
329 :
330 21284 : if (lastSolveConverged())
331 : {
332 21242 : _t_step++;
333 :
334 : /*
335 : * Call the multi-app executioners endStep and
336 : * postStep methods when doing Picard or when not automatically advancing sub-applications for
337 : * some other reason. We do not perform these calls for loose-coupling/auto-advancement
338 : * problems because TransientBase::endStep and TransientBase::postStep get called from
339 : * TransientBaseMultiApp::solveStep in that case.
340 : */
341 21242 : if (!_fixed_point_solve->autoAdvance())
342 : {
343 1082 : _problem.finishMultiAppStep(EXEC_MULTIAPP_FIXED_POINT_BEGIN,
344 : /*recurse_through_multiapp_levels=*/true);
345 1082 : _problem.finishMultiAppStep(EXEC_TIMESTEP_BEGIN, /*recurse_through_multiapp_levels=*/true);
346 1082 : _problem.finishMultiAppStep(EXEC_TIMESTEP_END, /*recurse_through_multiapp_levels=*/true);
347 1082 : _problem.finishMultiAppStep(EXEC_MULTIAPP_FIXED_POINT_END,
348 : /*recurse_through_multiapp_levels=*/true);
349 : }
350 : }
351 :
352 21284 : if (!_app.testCheckpointHalfTransient())
353 : {
354 19894 : TIME_SECTION("final", 1, "Executing Final Objects");
355 19894 : _problem.execMultiApps(EXEC_FINAL);
356 19894 : _problem.finalizeMultiApps();
357 19894 : _problem.execute(EXEC_FINAL);
358 19890 : _problem.outputStep(EXEC_FINAL);
359 19890 : }
360 :
361 : // This method is to finalize anything else we want to do on the problem side.
362 21280 : _problem.postExecute();
363 :
364 : // This method can be overridden for user defined activities in the Executioner.
365 21280 : postExecute();
366 :
367 21280 : if (_test_restep_step || _test_restep_time)
368 4 : mooseError((_test_restep_step ? "Timestep " : "Time "),
369 4 : (_test_restep_step ? *_test_restep_step : *_test_restep_time),
370 : " was never retried because the simulation did not get to this timestep.\n\nTo "
371 : "support restep testing, specify `num_steps` in the input.\nOtherwise, set "
372 : "`restep = false` in this test specification.");
373 21276 : }
374 :
375 : void
376 236497 : TransientBase::computeDT()
377 : {
378 : // We're repeating the solve of the previous timestep,
379 : // so we use the same dt
380 236497 : if (_testing_restep)
381 : {
382 : mooseAssert(!_test_restep_step && !_test_restep_time, "Should not be set");
383 1123 : return;
384 : }
385 :
386 235374 : _time_stepper->computeStep(); // This is actually when DT gets computed
387 : }
388 :
389 : void
390 202006 : TransientBase::incrementStepOrReject()
391 : {
392 202006 : if (lastSolveConverged())
393 : {
394 196778 : if (!_xfem_repeat_step)
395 : {
396 : #ifdef LIBMESH_ENABLE_AMR
397 196778 : if (_t_step != 0)
398 176696 : _problem.adaptMesh();
399 : #endif
400 :
401 196778 : _time_old = _time;
402 196778 : _t_step++;
403 :
404 196778 : bool advance_problem_state = true;
405 196778 : const auto & tis = getTimeIntegrators();
406 397801 : for (auto & ti : tis)
407 : // We do not want to advance the problem state here if a time integrator is already doing so
408 : // itself
409 201023 : if (ti->advancesProblemState())
410 : {
411 0 : advance_problem_state = false;
412 0 : break;
413 : }
414 :
415 196778 : if (advance_problem_state)
416 196778 : _problem.advanceState();
417 0 : else if (tis.size() > 1)
418 0 : mooseError("Either there must be a single time integrator which advances state or none of "
419 : "the time integrators should advance state.");
420 :
421 196778 : if (_t_step == 1)
422 20082 : return;
423 :
424 : /*
425 : * Call the multi-app executioners endStep and
426 : * postStep methods when doing Picard or when not automatically advancing sub-applications for
427 : * some other reason. We do not perform these calls for loose-coupling/auto-advancement
428 : * problems because TransientBase::endStep and TransientBase::postStep get called from
429 : * TransientBaseMultiApp::solveStep in that case.
430 : */
431 176696 : if (!_fixed_point_solve->autoAdvance())
432 : {
433 8316 : _problem.finishMultiAppStep(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
434 8316 : _problem.finishMultiAppStep(EXEC_TIMESTEP_BEGIN);
435 8316 : _problem.finishMultiAppStep(EXEC_TIMESTEP_END);
436 8316 : _problem.finishMultiAppStep(EXEC_MULTIAPP_FIXED_POINT_END);
437 : }
438 :
439 : /*
440 : * Ensure that we increment the sub-application time steps so that
441 : * when dt selection is made in the master application, we are using
442 : * the correct time step information
443 : */
444 176696 : _problem.incrementMultiAppTStep(EXEC_MULTIAPP_FIXED_POINT_BEGIN);
445 176696 : _problem.incrementMultiAppTStep(EXEC_TIMESTEP_BEGIN);
446 176696 : _problem.incrementMultiAppTStep(EXEC_TIMESTEP_END);
447 176696 : _problem.incrementMultiAppTStep(EXEC_MULTIAPP_FIXED_POINT_END);
448 196778 : }
449 : }
450 : else
451 : {
452 5228 : _problem.restoreMultiApps(EXEC_MULTIAPP_FIXED_POINT_BEGIN, true);
453 5228 : _problem.restoreMultiApps(EXEC_TIMESTEP_BEGIN, true);
454 5228 : _problem.restoreMultiApps(EXEC_TIMESTEP_END, true);
455 5228 : _problem.restoreMultiApps(EXEC_MULTIAPP_FIXED_POINT_END, true);
456 5228 : _time_stepper->rejectStep();
457 5224 : _time = _time_old;
458 : }
459 : }
460 :
461 : void
462 253427 : TransientBase::takeStep(Real input_dt)
463 : {
464 253427 : if (lastSolveConverged())
465 248319 : _dt_old = _dt;
466 :
467 : // We're repeating the solve of the previous timestep,
468 : // so we use the same dt
469 253427 : if (_testing_restep)
470 : {
471 : mooseAssert(!_test_restep_step && !_test_restep_time, "Should not be set");
472 1123 : _testing_restep = false;
473 : }
474 252304 : else if (input_dt == -1.0)
475 182769 : _dt = computeConstrainedDT();
476 : else
477 69535 : _dt = input_dt;
478 :
479 253423 : _time_stepper->preSolve();
480 :
481 : // Increment time
482 253423 : _time = _time_old + _dt;
483 :
484 253423 : _problem.timestepSetup();
485 :
486 253423 : _problem.onTimestepBegin();
487 :
488 253423 : _time_stepper->step();
489 253281 : _xfem_repeat_step = _fixed_point_solve->XFEMRepeatStep();
490 :
491 253281 : _last_solve_converged = _time_stepper->converged();
492 :
493 : // We're running with --test-restep and we have just solved
494 : // the timestep we are to repeat for the first time
495 508511 : if ((_test_restep_step && *_test_restep_step == _t_step) ||
496 255230 : (_test_restep_time && _time >= *_test_restep_time))
497 : {
498 : mooseAssert(!_testing_restep, "Should not be set");
499 :
500 1123 : mooseInfo("Aborting and retrying solve for timestep ", _t_step, " due to --test-restep");
501 1123 : _last_solve_converged = false;
502 1123 : _testing_restep = true;
503 1123 : _test_restep_step.reset();
504 1123 : _test_restep_time.reset();
505 :
506 1123 : return;
507 : }
508 :
509 252158 : if (!lastSolveConverged())
510 : {
511 4245 : _console << "Aborting as solve did not converge" << std::endl;
512 4245 : return;
513 : }
514 :
515 247913 : if (!(_problem.haveXFEM() && _fixed_point_solve->XFEMRepeatStep()))
516 : {
517 247913 : if (lastSolveConverged())
518 247913 : _time_stepper->acceptStep();
519 : else
520 0 : _time_stepper->rejectStep();
521 : }
522 :
523 247913 : _time = _time_old;
524 :
525 247913 : _time_stepper->postSolve();
526 :
527 247913 : return;
528 : }
529 :
530 : void
531 209254 : TransientBase::endStep(Real input_time)
532 : {
533 209254 : if (input_time == -1.0)
534 209229 : _time = _time_old + _dt;
535 : else
536 25 : _time = input_time;
537 :
538 209254 : if (lastSolveConverged())
539 : {
540 204003 : if (_xfem_repeat_step)
541 0 : _time = _time_old;
542 : else
543 : {
544 : // TODO: add linear system support
545 204003 : const auto & tis = getTimeIntegrators();
546 412251 : for (auto & ti : tis)
547 208248 : ti->postStep();
548 :
549 : // Compute the Error Indicators and Markers
550 204003 : _problem.computeIndicators();
551 204003 : _problem.computeMarkers();
552 :
553 : // Perform the output of the current time step
554 204003 : _problem.outputStep(EXEC_TIMESTEP_END);
555 :
556 : // output
557 204003 : if (_time_interval && (_time + _timestep_tolerance >= _next_interval_output_time))
558 0 : _next_interval_output_time += _time_interval_output_interval;
559 204003 : }
560 : }
561 209254 : }
562 :
563 : Real
564 182769 : TransientBase::computeConstrainedDT()
565 : {
566 : // // If start up steps are needed
567 : // if (_t_step == 1 && _n_startup_steps > 1)
568 : // _dt = _input_dt/(double)(_n_startup_steps);
569 : // else if (_t_step == 1+_n_startup_steps && _n_startup_steps > 1)
570 : // _dt = _input_dt;
571 :
572 182769 : Real dt_cur = _dt;
573 182769 : std::ostringstream diag;
574 :
575 : // After startup steps, compute new dt
576 182769 : if (_t_step > _n_startup_steps)
577 182665 : dt_cur = getDT();
578 :
579 : else
580 : {
581 : diag << "Timestep < n_startup_steps, using old dt: " << std::setw(9) << std::setprecision(6)
582 104 : << std::setfill('0') << std::showpoint << std::left << _dt << " tstep: " << _t_step
583 104 : << " n_startup_steps: " << _n_startup_steps << std::endl;
584 : }
585 182769 : _unconstrained_dt = dt_cur;
586 :
587 182769 : if (_verbose)
588 5684 : _console << diag.str();
589 :
590 182769 : diag.str("");
591 182769 : diag.clear();
592 :
593 : // Allow the time stepper to limit the time step
594 182769 : _at_sync_point = _time_stepper->constrainStep(dt_cur);
595 :
596 : // Don't let time go beyond next time interval output if specified
597 182765 : if ((_time_interval) && (_time + dt_cur + _timestep_tolerance >= _next_interval_output_time))
598 : {
599 0 : dt_cur = _next_interval_output_time - _time;
600 0 : _at_sync_point = true;
601 :
602 : diag << "Limiting dt for time interval output at time: " << std::setw(9) << std::setprecision(6)
603 0 : << std::setfill('0') << std::showpoint << std::left << _next_interval_output_time
604 0 : << " dt: " << std::setw(9) << std::setprecision(6) << std::setfill('0') << std::showpoint
605 0 : << std::left << dt_cur << std::endl;
606 : }
607 :
608 : // If a target time is set and the current dt would exceed it, limit dt to match the target
609 189022 : if (_target_time > -std::numeric_limits<Real>::max() + _timestep_tolerance &&
610 6257 : _time + dt_cur + _timestep_tolerance >= _target_time)
611 : {
612 1654 : dt_cur = _target_time - _time;
613 1654 : _at_sync_point = true;
614 :
615 : diag << "Limiting dt for target time: " << std::setw(9) << std::setprecision(6)
616 1654 : << std::setfill('0') << std::showpoint << std::left << _next_interval_output_time
617 1654 : << " dt: " << std::setw(9) << std::setprecision(6) << std::setfill('0') << std::showpoint
618 1654 : << std::left << dt_cur << std::endl;
619 : }
620 :
621 : // Constrain by what the multi apps are doing
622 182765 : constrainDTFromMultiApp(dt_cur, diag, EXEC_MULTIAPP_FIXED_POINT_BEGIN);
623 182765 : constrainDTFromMultiApp(dt_cur, diag, EXEC_TIMESTEP_BEGIN);
624 182765 : constrainDTFromMultiApp(dt_cur, diag, EXEC_TIMESTEP_END);
625 182765 : constrainDTFromMultiApp(dt_cur, diag, EXEC_MULTIAPP_FIXED_POINT_END);
626 :
627 182765 : if (_verbose)
628 5684 : _console << diag.str();
629 :
630 182765 : return dt_cur;
631 182765 : }
632 :
633 : void
634 731060 : TransientBase::constrainDTFromMultiApp(Real & dt_cur,
635 : std::ostringstream & diag,
636 : const ExecFlagType & execute_on) const
637 : {
638 731060 : Real multi_app_dt = _problem.computeMultiAppsDT(execute_on);
639 731060 : if (_use_multiapp_dt || multi_app_dt < dt_cur)
640 : {
641 1949 : dt_cur = multi_app_dt;
642 1949 : _at_sync_point = false;
643 1949 : diag << "Limiting dt for MultiApps on " << execute_on.name() << ": " << std::setw(9)
644 1949 : << std::setprecision(6) << std::setfill('0') << std::showpoint << std::left << dt_cur
645 1949 : << std::endl;
646 : }
647 731060 : }
648 :
649 : Real
650 236960 : TransientBase::getDT()
651 : {
652 236960 : return _time_stepper->getCurrentDT();
653 : }
654 :
655 : bool
656 199048 : TransientBase::keepGoing()
657 : {
658 199048 : bool keep_going = !_problem.isSolveTerminationRequested();
659 :
660 : // Check for stop condition based upon steady-state check flag:
661 199048 : if (lastSolveConverged())
662 : {
663 193804 : if (!_xfem_repeat_step)
664 : {
665 193804 : if (_steady_state_detection == true && _time > _steady_state_start_time)
666 : {
667 : // Check solution difference relative norm against steady-state tolerance
668 13814 : if (convergedToSteadyState())
669 : {
670 375 : _console << "Steady-State Solution Achieved at time: " << _time << std::endl;
671 : // Output last solve if not output previously by forcing it
672 375 : keep_going = false;
673 : }
674 : }
675 :
676 : // Check for stop condition based upon number of simulation steps and/or solution end time:
677 193800 : if (static_cast<unsigned int>(_t_step) >= _num_steps)
678 16840 : keep_going = false;
679 :
680 193800 : if ((_time >= _end_time) || (fabs(_time - _end_time) <= _timestep_tolerance))
681 4180 : keep_going = false;
682 : }
683 : }
684 5244 : else if (_abort)
685 : {
686 20 : _console << "Aborting as solve did not converge and input selected to abort" << std::endl;
687 20 : keep_going = false;
688 : }
689 5224 : else if (!_error_on_dtmin && _dt <= _dtmin)
690 : {
691 22 : _console << "Aborting as timestep already at or below dtmin" << std::endl;
692 22 : keep_going = false;
693 : }
694 :
695 199044 : return keep_going;
696 : }
697 :
698 : void
699 0 : TransientBase::estimateTimeError()
700 : {
701 0 : }
702 :
703 : bool
704 1493291 : TransientBase::lastSolveConverged() const
705 : {
706 1493291 : return _last_solve_converged;
707 : }
708 :
709 : void
710 28638 : TransientBase::postExecute()
711 : {
712 28638 : _time_stepper->postExecute();
713 28638 : }
714 :
715 : void
716 1734 : TransientBase::setTargetTime(Real target_time)
717 : {
718 1734 : _target_time = target_time;
719 1734 : }
720 :
721 : Real
722 19246 : TransientBase::computeSolutionChangeNorm(bool check_aux, bool normalize_by_dt) const
723 : {
724 19246 : return relativeSolutionDifferenceNorm(check_aux) / (normalize_by_dt ? _dt : Real(1));
725 : }
726 :
727 : void
728 30620 : TransientBase::setupTimeIntegrator()
729 : {
730 30620 : if (_pars.isParamSetByUser("scheme") && _problem.hasTimeIntegrator())
731 4 : mooseError("You cannot specify time_scheme in the Executioner and independently add a "
732 : "TimeIntegrator to the system at the same time");
733 :
734 30616 : if (!_problem.hasTimeIntegrator())
735 : {
736 : // backwards compatibility
737 29497 : std::string ti_str;
738 : using namespace Moose;
739 :
740 29497 : switch (_time_scheme)
741 : {
742 29165 : case TI_IMPLICIT_EULER:
743 29165 : ti_str = "ImplicitEuler";
744 29165 : break;
745 78 : case TI_EXPLICIT_EULER:
746 78 : ti_str = "ExplicitEuler";
747 78 : break;
748 49 : case TI_CRANK_NICOLSON:
749 49 : ti_str = "CrankNicolson";
750 49 : break;
751 166 : case TI_BDF2:
752 166 : ti_str = "BDF2";
753 166 : break;
754 0 : case TI_EXPLICIT_MIDPOINT:
755 0 : ti_str = "ExplicitMidpoint";
756 0 : break;
757 0 : case TI_LSTABLE_DIRK2:
758 0 : ti_str = "LStableDirk2";
759 0 : break;
760 0 : case TI_EXPLICIT_TVD_RK_2:
761 0 : ti_str = "ExplicitTVDRK2";
762 0 : break;
763 39 : case TI_NEWMARK_BETA:
764 39 : ti_str = "NewmarkBeta";
765 39 : break;
766 0 : default:
767 0 : mooseError("Unknown scheme: ", _time_scheme);
768 : break;
769 : }
770 :
771 58994 : InputParameters params = _app.getFactory().getValidParams(ti_str);
772 29497 : _problem.addTimeIntegrator(ti_str, ti_str, params);
773 29497 : }
774 30616 : }
775 :
776 : std::string
777 25236 : TransientBase::getTimeStepperName() const
778 : {
779 25236 : if (_time_stepper)
780 : {
781 25236 : TimeStepper & ts = *_time_stepper;
782 25236 : return demangle(typeid(ts).name());
783 : }
784 : else
785 0 : return std::string();
786 : }
787 :
788 : std::vector<std::string>
789 25236 : TransientBase::getTimeIntegratorNames() const
790 : {
791 25236 : const auto & tis = getTimeIntegrators();
792 25236 : if (tis.empty())
793 0 : mooseError("Time integrator has not been built yet so we can't retrieve its name");
794 :
795 25236 : std::vector<std::string> ret;
796 50556 : for (const auto & ti : tis)
797 : {
798 25320 : const auto & sys = ti->getCheckedPointerParam<SystemBase *>("_sys")->system();
799 25320 : const auto & uvars = ti->getParam<std::vector<VariableName>>("variables");
800 :
801 25320 : std::vector<VariableName> vars;
802 25436 : for (const auto & var : uvars)
803 116 : if (sys.has_variable(var))
804 116 : vars.push_back(var);
805 :
806 25320 : if (!uvars.empty() && vars.empty())
807 0 : continue;
808 :
809 25320 : if (tis.size() > 1)
810 : {
811 168 : const std::string sys_prefix = _problem.numSolverSystems() > 1 ? sys.name() : "";
812 168 : const std::string var_prefix = MooseUtils::join(vars, ", ");
813 168 : const bool both = !sys_prefix.empty() && !var_prefix.empty();
814 168 : ret.push_back("[" + sys_prefix + (both ? " (" : "") + var_prefix + (both ? ")" : "") + "]:");
815 168 : }
816 :
817 25320 : ret.push_back(ti->type());
818 25320 : }
819 50472 : return ret;
820 25236 : }
821 :
822 : void
823 30508 : TransientBase::setTimeStepper(TimeStepper & ts)
824 : {
825 : mooseAssert(!_time_stepper, "Already set");
826 30508 : _time_stepper = &ts;
827 30508 : }
828 :
829 : bool
830 20052 : TransientBase::convergedToSteadyState() const
831 : {
832 20052 : auto & convergence = _problem.getConvergence(_problem.getSteadyStateConvergenceName());
833 20052 : const auto status = convergence.checkConvergence(_t_step);
834 :
835 20052 : if (status == Convergence::MooseConvergenceStatus::DIVERGED)
836 8 : mooseError(
837 4 : "The steady-state Convergence object (", convergence.name(), ") reported divergence.");
838 20048 : else if (status == Convergence::MooseConvergenceStatus::CONVERGED)
839 743 : return true;
840 : else // status == Convergence::MooseConvergenceStatus::ITERATING
841 19305 : return false;
842 : }
843 :
844 : void
845 77 : TransientBase::parentOutputPositionChanged()
846 : {
847 77 : _fe_problem.parentOutputPositionChanged();
848 77 : }
|