LCOV - code coverage report
Current view: top level - src/multiapps - TransientMultiApp.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 291 318 91.5 %
Date: 2025-07-17 01:28:37 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14