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 "TransientMultiApp.h"
12 :
13 : #include "AllLocalDofIndicesThread.h"
14 : #include "AuxiliarySystem.h"
15 : #include "Console.h"
16 : #include "LayeredSideDiffusiveFluxAverage.h"
17 : #include "MooseMesh.h"
18 : #include "Output.h"
19 : #include "TimeStepper.h"
20 : #include "TransientBase.h"
21 : #include "NonlinearSystem.h"
22 :
23 : #include "libmesh/mesh_tools.h"
24 : #include "libmesh/numeric_vector.h"
25 :
26 : registerMooseObject("MooseApp", TransientMultiApp);
27 :
28 : InputParameters
29 19982 : TransientMultiApp::validParams()
30 : {
31 19982 : InputParameters params = MultiApp::validParams();
32 19982 : params += TransientInterface::validParams();
33 39964 : params.addClassDescription("MultiApp for performing coupled simulations with the parent and "
34 : "sub-application both progressing in time.");
35 :
36 59946 : params.addParam<bool>("sub_cycling",
37 39964 : false,
38 : "Set to true to allow this MultiApp to take smaller "
39 : "timesteps than the rest of the simulation. More "
40 : "than one timestep will be performed for each "
41 : "parent application timestep");
42 :
43 59946 : params.addParam<bool>("interpolate_transfers",
44 39964 : false,
45 : "Only valid when sub_cycling. This allows "
46 : "transferred values to be interpolated "
47 : "over the time frame the MultiApp is "
48 : "executing over when sub_cycling");
49 :
50 59946 : params.addParam<bool>("detect_steady_state",
51 39964 : false,
52 : "If true, then if/while sub-cycling ('sub_cycling = true'), a steady-state "
53 : "check will be performed for each child app, allowing them to skip to the "
54 : "end of the parent time step if steady conditions are detected.");
55 :
56 79928 : params.addParam<bool>("output_sub_cycles", false, "If true then every sub-cycle will be output.");
57 59946 : params.addParam<bool>(
58 39964 : "print_sub_cycles", true, "Toggle the display of sub-cycles on the screen.");
59 :
60 59946 : params.addParam<unsigned int>(
61 39964 : "max_failures", 0, "Maximum number of solve failures tolerated while sub_cycling.");
62 :
63 79928 : params.addParamNamesToGroup("sub_cycling interpolate_transfers detect_steady_state "
64 : "output_sub_cycles print_sub_cycles max_failures",
65 : "Sub cycling");
66 :
67 59946 : params.addParam<bool>("tolerate_failure",
68 39964 : false,
69 : "If true this MultiApp won't participate in dt "
70 : "decisions and will always be fast-forwarded to "
71 : "the current time.");
72 :
73 59946 : params.addParam<bool>(
74 : "catch_up",
75 39964 : false,
76 : "If true this will allow failed solves to attempt to 'catch up' using smaller timesteps.");
77 :
78 59946 : params.addParam<Real>("max_catch_up_steps",
79 39964 : 2,
80 : "Maximum number of steps to allow an app to take "
81 : "when trying to catch back up after a failed "
82 : "solve.");
83 :
84 79928 : params.addParamNamesToGroup("catch_up max_catch_up_steps", "Recovering failed solutions");
85 59946 : params.addParamNamesToGroup("tolerate_failure", "Accepting failed solutions");
86 :
87 19982 : return params;
88 0 : }
89 :
90 5398 : TransientMultiApp::TransientMultiApp(const InputParameters & parameters)
91 : : MultiApp(parameters),
92 10784 : _sub_cycling(getParam<bool>("sub_cycling")),
93 10784 : _interpolate_transfers(getParam<bool>("interpolate_transfers")),
94 10784 : _detect_steady_state(getParam<bool>("detect_steady_state")),
95 10784 : _output_sub_cycles(getParam<bool>("output_sub_cycles")),
96 10784 : _max_failures(getParam<unsigned int>("max_failures")),
97 10784 : _tolerate_failure(getParam<bool>("tolerate_failure")),
98 5392 : _failures(0),
99 10784 : _catch_up(getParam<bool>("catch_up")),
100 10784 : _max_catch_up_steps(getParam<Real>("max_catch_up_steps")),
101 10784 : _first(declareRecoverableData<bool>("first", true)),
102 5392 : _auto_advance(false),
103 16182 : _print_sub_cycles(getParam<bool>("print_sub_cycles"))
104 : {
105 : // Transfer interpolation only makes sense for sub-cycling solves
106 5392 : if (_interpolate_transfers && !_sub_cycling)
107 0 : paramError("interpolate_transfers",
108 : "MultiApp ",
109 0 : name(),
110 : " is set to interpolate_transfers but is not sub_cycling! That is not valid!");
111 :
112 : // Subcycling overrides catch up, we don't want to confuse users by allowing them to set both.
113 5392 : if (_sub_cycling && _catch_up)
114 6 : paramError("catch_up",
115 : "MultiApp ",
116 3 : name(),
117 : " \"sub_cycling\" and \"catch_up\" cannot both be set to true simultaneously.");
118 :
119 5389 : if (_sub_cycling && _keep_solution_during_restore)
120 0 : paramError("keep_solution_during_restore",
121 : "In MultiApp ",
122 0 : name(),
123 : " it doesn't make any sense to keep a solution during restore when doing "
124 : "sub_cycling. Consider trying \"catch_up\" steps instead");
125 :
126 5389 : if (!_catch_up && _keep_solution_during_restore)
127 0 : paramError("keep_solution_during_restore",
128 : "In MultiApp ",
129 0 : name(),
130 : " \"keep_solution_during_restore\" requires \"catch_up = true\". Either disable "
131 : "\"keep_solution_during_restart\" or set \"catch_up = true\"");
132 :
133 5389 : if (_sub_cycling && _tolerate_failure)
134 0 : paramInfo("tolerate_failure",
135 : "In MultiApp ",
136 0 : name(),
137 : " both \"sub_cycling\" and \"tolerate_failure\" are set to true. \"tolerate_failure\""
138 : " will be ignored.");
139 5389 : }
140 :
141 : NumericVector<Number> &
142 1021 : TransientMultiApp::appTransferVector(unsigned int app, std::string var_name)
143 : {
144 1021 : if (std::find(_transferred_vars.begin(), _transferred_vars.end(), var_name) ==
145 2042 : _transferred_vars.end())
146 872 : _transferred_vars.push_back(var_name);
147 :
148 1021 : if (_interpolate_transfers)
149 197 : return appProblemBase(app).getAuxiliarySystem().system().get_vector("transfer");
150 :
151 824 : return appProblemBase(app).getAuxiliarySystem().solution();
152 : }
153 :
154 : void
155 5314 : TransientMultiApp::initialSetup()
156 : {
157 5314 : MultiApp::initialSetup();
158 :
159 5314 : if (!_has_an_app)
160 52 : return;
161 :
162 5262 : Moose::ScopedCommSwapper swapper(_my_comm);
163 :
164 5262 : if (_has_an_app)
165 : {
166 5262 : _transient_executioners.resize(_my_num_apps);
167 : // Grab Transient Executioners from each app
168 13041 : for (unsigned int i = 0; i < _my_num_apps; i++)
169 7782 : setupApp(i);
170 : }
171 5259 : }
172 :
173 : bool
174 60513 : TransientMultiApp::solveStep(Real dt, Real target_time, bool auto_advance)
175 : {
176 60513 : if (!_has_an_app)
177 79 : return true;
178 :
179 60434 : TIME_SECTION(_solve_step_timer);
180 :
181 60434 : _auto_advance = auto_advance;
182 :
183 60434 : if (_fe_problem.verboseMultiApps())
184 1886 : _console << COLOR_CYAN << "Solving MultiApp '" << name() << "' with target time " << target_time
185 1886 : << " and dt " << dt << " with auto-advance " << (auto_advance ? "on" : "off")
186 1886 : << COLOR_DEFAULT << std::endl;
187 :
188 : // "target_time" must always be in global time
189 60434 : target_time += _app.getGlobalTimeOffset();
190 :
191 60434 : Moose::ScopedCommSwapper swapper(_my_comm);
192 60434 : bool return_value = true;
193 :
194 : // Make sure we swap back the communicator regardless of how this routine is exited
195 : try
196 : {
197 : int rank;
198 : int ierr;
199 60434 : ierr = MPI_Comm_rank(_communicator.get(), &rank);
200 60434 : mooseCheckMPIErr(ierr);
201 :
202 127209 : for (unsigned int i = 0; i < _my_num_apps; i++)
203 : {
204 66849 : FEProblemBase & problem = appProblemBase(_first_local_app + i);
205 :
206 66849 : TransientBase * ex = _transient_executioners[i];
207 :
208 : // The App might have a different local time from the rest of the problem
209 66849 : Real app_time_offset = _apps[i]->getGlobalTimeOffset();
210 :
211 : // Maybe this MultiApp was already solved
212 133389 : if ((ex->getTime() + app_time_offset + ex->timestepTol() >= target_time) ||
213 66540 : (ex->getTime() >= ex->endTime()))
214 329 : continue;
215 :
216 : // Examine global time synchronization
217 66520 : if (!_sub_cycling && !_reset_happened.size())
218 : {
219 : // The multi-app general offset is substracted to go into local time.
220 64325 : if (std::abs(target_time - _app.getGlobalTimeOffset() - ex->getTime() - dt) >
221 64325 : ex->timestepTol())
222 70 : mooseDoOnce(mooseWarning(
223 : "The target time (time a multiapp must reach at the end of the time step) "
224 : "is desynchronized between this app and subapp ",
225 : i,
226 : ".\n If this is desired: use the 'global_time_offset' multiapp parameter to "
227 : "declare a constant offset\n"
228 : "If the apps should (eventually) be synchronized in time, please either: \n"
229 : " - match the 'start_time' in the main app and the multiapp, in the Executioner "
230 : "block\n"
231 : " - set 'sub_cycling' to true in the multiapp parameters\n"
232 : "This message will only print once for all apps and all time steps."));
233 : }
234 :
235 66517 : if (_sub_cycling)
236 : {
237 1600 : Real time_old = ex->getTime() + app_time_offset;
238 :
239 1600 : if (_interpolate_transfers)
240 : {
241 161 : AuxiliarySystem & aux_system = problem.getAuxiliarySystem();
242 161 : System & libmesh_aux_system = aux_system.system();
243 :
244 161 : NumericVector<Number> & solution = *libmesh_aux_system.solution;
245 161 : NumericVector<Number> & transfer_old = libmesh_aux_system.get_vector("transfer_old");
246 :
247 161 : solution.close();
248 :
249 : // Save off the current auxiliary solution
250 161 : transfer_old = solution;
251 :
252 161 : transfer_old.close();
253 :
254 : // Snag all of the local dof indices for all of these variables
255 161 : AllLocalDofIndicesThread aldit(problem, _transferred_vars);
256 161 : ConstElemRange & elem_range = *problem.mesh().getActiveLocalElementRange();
257 161 : Threads::parallel_reduce(elem_range, aldit);
258 :
259 161 : _transferred_dofs = aldit.getDofIndices();
260 161 : }
261 :
262 : // Disable/enable output for sub cycling
263 1600 : problem.allowOutput(_output_sub_cycles); // disables all outputs, including console
264 1600 : problem.allowOutput<Console>(_print_sub_cycles); // re-enables Console to print, if desired
265 :
266 1600 : ex->setTargetTime(target_time - app_time_offset);
267 :
268 : // unsigned int failures = 0;
269 :
270 1600 : bool at_steady = false;
271 :
272 : // ADL: During restart, there is already an FEProblemBase::advanceState that occurs at the
273 : // end of TransientMultiApp::setupApp. advanceState, along with copying the solutions
274 : // backwards in time/state, also *moves* (note it doesn't copy!) stateful material
275 : // properties backwards (through swapping). So if restarting from a full-solve steady
276 : // multi-app for example, then after one advance state, we will have good information in old
277 : // and no information in current. But then if we advance again we no longer have good data
278 : // in the old material properties, so don't advance here if we're restarting
279 1600 : if (_first && !_app.isRecovering() && !_app.isRestarting())
280 178 : problem.advanceState();
281 :
282 1600 : bool local_first = _first;
283 :
284 : // Now do all of the solves we need
285 9053 : while ((!at_steady && ex->getTime() + app_time_offset + ex->timestepTol() < target_time) ||
286 1583 : !ex->lastSolveConverged())
287 : {
288 5887 : if (local_first != true)
289 5708 : ex->incrementStepOrReject();
290 :
291 5887 : local_first = false;
292 :
293 5887 : ex->preStep();
294 5887 : ex->computeDT();
295 :
296 5887 : if (_interpolate_transfers)
297 : {
298 : // See what time this executioner is going to go to.
299 1498 : Real future_time = ex->getTime() + app_time_offset + ex->getDT();
300 :
301 : // How far along we are towards the target time:
302 1498 : Real step_percent = (future_time - time_old) / (target_time - time_old);
303 :
304 1498 : Real one_minus_step_percent = 1.0 - step_percent;
305 :
306 : // Do the interpolation for each variable that was transferred to
307 1498 : FEProblemBase & problem = appProblemBase(_first_local_app + i);
308 1498 : AuxiliarySystem & aux_system = problem.getAuxiliarySystem();
309 1498 : System & libmesh_aux_system = aux_system.system();
310 :
311 1498 : NumericVector<Number> & solution = *libmesh_aux_system.solution;
312 1498 : NumericVector<Number> & transfer = libmesh_aux_system.get_vector("transfer");
313 1498 : NumericVector<Number> & transfer_old = libmesh_aux_system.get_vector("transfer_old");
314 :
315 1498 : solution.close(); // Just to be sure
316 1498 : transfer.close();
317 1498 : transfer_old.close();
318 :
319 444818 : for (const auto & dof : _transferred_dofs)
320 : {
321 886640 : solution.set(dof,
322 443320 : (transfer_old(dof) * one_minus_step_percent) +
323 443320 : (transfer(dof) * step_percent));
324 : // solution.set(dof, transfer_old(dof));
325 : // solution.set(dof, transfer(dof));
326 : // solution.set(dof, 1);
327 : }
328 :
329 1498 : solution.close();
330 : }
331 :
332 5887 : ex->takeStep();
333 :
334 5887 : bool converged = ex->lastSolveConverged();
335 :
336 5887 : if (!converged)
337 : {
338 17 : mooseWarning(
339 17 : "While sub_cycling ", name(), _first_local_app + i, " failed to converge!\n");
340 :
341 17 : _failures++;
342 :
343 17 : if (_failures > _max_failures)
344 : {
345 17 : std::stringstream oss;
346 17 : oss << "While sub_cycling " << name() << _first_local_app << i << " REALLY failed!";
347 17 : throw MultiAppSolveFailure(oss.str());
348 17 : }
349 : }
350 5870 : if (_detect_steady_state)
351 241 : at_steady = ex->convergedToSteadyState();
352 :
353 5870 : if (converged && _detect_steady_state && at_steady)
354 : {
355 23 : if (_fe_problem.verboseMultiApps())
356 0 : _console << "Detected Steady State! Fast-forwarding to " << target_time << std::endl;
357 :
358 : // Indicate that the next output call (occurs in ex->endStep()) should output,
359 : // regardless of intervals etc...
360 23 : problem.forceOutput();
361 :
362 : // Clean up the end
363 23 : ex->endStep(target_time - app_time_offset);
364 23 : ex->postStep();
365 : }
366 : else
367 : {
368 5847 : ex->endStep();
369 5847 : ex->postStep();
370 : }
371 : }
372 :
373 : // If we were looking for a steady state, but didn't reach one, we still need to output one
374 : // more time, regardless of interval
375 : // Note: if we turn off the output for all time steps for sub-cycling, we still need to
376 : // have one output at the end.
377 1583 : if ((!at_steady && _detect_steady_state) || !_output_sub_cycles)
378 1526 : problem.outputStep(EXEC_FORCED);
379 :
380 : } // end of sub_cycling
381 64917 : else if (_tolerate_failure)
382 : {
383 0 : ex->takeStep(dt);
384 0 : ex->endStep(target_time - app_time_offset);
385 0 : ex->postStep();
386 : }
387 : // matched time steps (no subcycling)
388 : else
389 : {
390 : // ADL: During restart, there is already an FEProblemBase::advanceState that occurs at the
391 : // end of TransientMultiApp::setupApp. advanceState, along with copying the solutions
392 : // backwards in time/state, also *moves* (note it doesn't copy!) stateful material
393 : // properties backwards (through swapping). So if restarting from a full-solve steady
394 : // multi-app for example, then after one advance state, we will have good information in old
395 : // and no information in current. But then if we advance again we no longer have good data
396 : // in the old material properties, so don't advance here if we're restarting
397 64917 : if (_first && !_app.isRecovering() && !_app.isRestarting())
398 6542 : problem.advanceState();
399 :
400 64917 : if (auto_advance)
401 15329 : problem.allowOutput(true);
402 :
403 64917 : ex->takeStep(dt);
404 :
405 64917 : if (auto_advance)
406 : {
407 15329 : ex->endStep();
408 15329 : ex->postStep();
409 :
410 15329 : if (!ex->lastSolveConverged())
411 : {
412 7 : mooseWarning(name(), _first_local_app + i, " failed to converge!\n");
413 :
414 7 : if (_catch_up)
415 : {
416 7 : if (_fe_problem.verboseMultiApps())
417 0 : _console << "Starting time step catch up!" << std::endl;
418 :
419 7 : bool caught_up = false;
420 :
421 7 : unsigned int catch_up_step = 0;
422 :
423 : // Cut the timestep in half to first try two half-step solves
424 7 : Real catch_up_dt = dt / 2;
425 7 : Real catch_up_time = 0;
426 :
427 21 : while (!caught_up && catch_up_step < _max_catch_up_steps)
428 : {
429 14 : if (_fe_problem.verboseMultiApps())
430 0 : _console << "Solving " << name() << " catch up step " << catch_up_step
431 0 : << std::endl;
432 14 : ex->incrementStepOrReject();
433 :
434 : // Avoid numerical precision errors on target time
435 14 : if (catch_up_time + catch_up_dt > dt)
436 0 : catch_up_dt = dt - catch_up_time;
437 :
438 14 : ex->computeDT();
439 14 : ex->takeStep(catch_up_dt);
440 14 : ex->endStep();
441 :
442 14 : if (ex->lastSolveConverged())
443 : {
444 14 : catch_up_time += catch_up_dt;
445 14 : if (std::abs(catch_up_time - dt) <
446 14 : (1 + std::abs(ex->getTime())) * ex->timestepTol())
447 : {
448 7 : problem.outputStep(EXEC_FORCED);
449 7 : caught_up = true;
450 : }
451 : }
452 : else
453 : // Keep cutting time step in half until it converges
454 0 : catch_up_dt /= 2.0;
455 :
456 14 : ex->postStep();
457 :
458 14 : catch_up_step++;
459 : }
460 :
461 7 : if (!caught_up)
462 0 : throw MultiAppSolveFailure(name() + " Failed to catch up!\n");
463 : }
464 : }
465 : }
466 : else // auto_advance == false
467 : {
468 49588 : if (!ex->lastSolveConverged())
469 : {
470 : // Even if we don't allow auto_advance - we can still catch up to the current time if
471 : // possible
472 68 : if (_catch_up)
473 : {
474 14 : if (_fe_problem.verboseMultiApps())
475 0 : _console << "Starting Catch Up!" << std::endl;
476 :
477 14 : bool caught_up = false;
478 :
479 14 : unsigned int catch_up_step = 0;
480 :
481 14 : Real catch_up_dt = dt / 2;
482 :
483 : // Note: this loop will _break_ if target_time is satisfied
484 28 : while (catch_up_step < _max_catch_up_steps)
485 : {
486 28 : if (_fe_problem.verboseMultiApps())
487 0 : _console << "Solving " << name() << " catch up step " << catch_up_step
488 0 : << std::endl;
489 28 : ex->incrementStepOrReject();
490 :
491 28 : ex->computeDT();
492 28 : ex->takeStep(catch_up_dt); // Cut the timestep in half to try two half-step solves
493 :
494 : // This is required because we can't call endStep() yet
495 : // (which normally increments time)
496 28 : Real current_time = ex->getTime() + ex->getDT();
497 :
498 28 : if (ex->lastSolveConverged())
499 : {
500 56 : if (current_time + app_time_offset +
501 28 : (ex->timestepTol() * std::abs(current_time)) >=
502 : target_time)
503 : {
504 14 : caught_up = true;
505 14 : break; // break here so that we don't run endStep() or postStep() since this
506 : // MultiApp should NOT be auto_advanced
507 : }
508 : }
509 : else
510 0 : catch_up_dt /= 2.0;
511 :
512 14 : ex->endStep();
513 14 : ex->postStep();
514 :
515 14 : catch_up_step++;
516 : }
517 :
518 14 : if (!caught_up)
519 0 : throw MultiAppSolveFailure(name() + " Failed to catch up!\n");
520 : }
521 : else
522 54 : throw MultiAppSolveFailure(name() + " failed to converge");
523 : }
524 : }
525 : }
526 :
527 : // Re-enable all output (it may of been disabled by sub-cycling)
528 66446 : problem.allowOutput(true);
529 : }
530 :
531 60360 : _first = false;
532 :
533 60360 : if (_fe_problem.verboseMultiApps())
534 1886 : _console << "Successfully Solved MultiApp " << name() << "." << std::endl;
535 : }
536 71 : catch (MultiAppSolveFailure & e)
537 : {
538 71 : mooseWarning(e.what());
539 71 : _console << "Failed to Solve MultiApp " << name() << ", attempting to recover." << std::endl;
540 71 : return_value = false;
541 71 : }
542 :
543 60431 : _transferred_vars.clear();
544 :
545 60431 : return return_value;
546 60431 : }
547 :
548 : void
549 13157 : TransientMultiApp::incrementTStep(Real target_time)
550 : {
551 13157 : if (!_sub_cycling)
552 : {
553 29207 : for (unsigned int i = 0; i < _my_num_apps; i++)
554 : {
555 16623 : TransientBase * ex = _transient_executioners[i];
556 :
557 : // The App might have a different local time from the rest of the problem
558 16623 : Real app_time_offset = _apps[i]->getGlobalTimeOffset();
559 :
560 : // Only increment the step if we are after (target_time) the
561 : // start_time (added to app_time_offset) of this sub_app.
562 16623 : if (_apps[i]->getStartTime() + app_time_offset < target_time)
563 16567 : ex->incrementStepOrReject();
564 : }
565 : }
566 13157 : }
567 :
568 : void
569 8590 : TransientMultiApp::finishStep(bool recurse_through_multiapp_levels)
570 : {
571 8590 : if (!_sub_cycling)
572 : {
573 16698 : for (unsigned int i = 0; i < _my_num_apps; i++)
574 : {
575 8355 : TransientBase * ex = _transient_executioners[i];
576 8355 : ex->endStep();
577 8355 : ex->postStep();
578 8355 : if (recurse_through_multiapp_levels)
579 : {
580 1031 : ex->feProblem().finishMultiAppStep(EXEC_TIMESTEP_BEGIN,
581 : /*recurse_through_multiapp_levels=*/true);
582 1031 : ex->feProblem().finishMultiAppStep(EXEC_TIMESTEP_END,
583 : /*recurse_through_multiapp_levels=*/true);
584 : }
585 : }
586 : }
587 8590 : }
588 :
589 : Real
590 17385 : TransientMultiApp::computeDT()
591 : {
592 17385 : if (_sub_cycling) // Bow out of the timestep selection dance
593 668 : return std::numeric_limits<Real>::max();
594 :
595 16717 : Real smallest_dt = std::numeric_limits<Real>::max();
596 :
597 16717 : if (_has_an_app)
598 : {
599 16675 : Moose::ScopedCommSwapper swapper(_my_comm);
600 :
601 39368 : for (unsigned int i = 0; i < _my_num_apps; i++)
602 : {
603 22693 : TransientBase * ex = _transient_executioners[i];
604 22693 : ex->computeDT();
605 22693 : Real dt = ex->getDT();
606 :
607 22693 : smallest_dt = std::min(dt, smallest_dt);
608 : }
609 16675 : }
610 :
611 16717 : if (_tolerate_failure) // Bow out of the timestep selection dance, we do this down here because we
612 : // need to call computeConstrainedDT at least once for these
613 : // executioners...
614 0 : return std::numeric_limits<Real>::max();
615 :
616 16717 : _communicator.min(smallest_dt);
617 16717 : return smallest_dt;
618 : }
619 :
620 : void
621 69 : TransientMultiApp::resetApp(
622 : unsigned int global_app,
623 : Real /*time*/) // FIXME: Note that we are passing in time but also grabbing it below
624 : {
625 69 : if (hasLocalApp(global_app))
626 : {
627 69 : unsigned int local_app = globalAppToLocal(global_app);
628 :
629 : // Grab the current time the App is at so we can start the new one at the same place
630 : Real time =
631 69 : _transient_executioners[local_app]->getTime() + _apps[local_app]->getGlobalTimeOffset();
632 :
633 : // Reset the Multiapp
634 69 : MultiApp::resetApp(global_app, time);
635 :
636 69 : Moose::ScopedCommSwapper swapper(_my_comm);
637 :
638 : // Setup the app, disable the output so that the initial condition does not output
639 : // When an app is reset the initial condition was effectively already output before reset
640 69 : FEProblemBase & problem = appProblemBase(local_app);
641 69 : problem.allowOutput(false);
642 69 : setupApp(local_app, time);
643 69 : problem.allowOutput(true);
644 69 : }
645 69 : }
646 :
647 : void
648 7851 : TransientMultiApp::setupApp(unsigned int i, Real /*time*/) // FIXME: Should we be passing time?
649 : {
650 7851 : auto & app = _apps[i];
651 7851 : TransientBase * ex = dynamic_cast<TransientBase *>(app->getExecutioner());
652 7851 : if (!ex)
653 0 : mooseError("MultiApp ", name(), " is not using a Transient Executioner!");
654 :
655 : // Get the FEProblemBase for the current MultiApp
656 7851 : FEProblemBase & problem = appProblemBase(_first_local_app + i);
657 :
658 : // Update the file numbers for the outputs from the parent application
659 7851 : app->getOutputWarehouse().setFileNumbers(_app.getOutputFileNumbers());
660 :
661 : // Add these vectors before we call init on the executioner because that will try to restore these
662 : // vectors in a restart context
663 7851 : if (_interpolate_transfers)
664 : {
665 24 : AuxiliarySystem & aux_system = problem.getAuxiliarySystem();
666 24 : System & libmesh_aux_system = aux_system.system();
667 :
668 : // We'll store a copy of the auxiliary system's solution at the old time in here
669 24 : libmesh_aux_system.add_vector("transfer_old", false);
670 :
671 : // This will be where we'll transfer the value to for the "target" time
672 24 : libmesh_aux_system.add_vector("transfer", false);
673 : }
674 :
675 : // Call initialization method of Executioner (Note, this performs the output of the initial time
676 : // step, if desired)
677 7851 : ex->init();
678 :
679 7848 : ex->preExecute();
680 7848 : if (!_app.isRecovering())
681 : {
682 7154 : problem.timeStep()++;
683 7154 : problem.advanceState();
684 : }
685 7848 : _transient_executioners[i] = ex;
686 7848 : }
|