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