LCOV - code coverage report
Current view: top level - src/multiapps - MultiApp.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 9a5f1f Lines: 694 769 90.2 %
Date: 2026-06-21 21:23:42 Functions: 41 44 93.2 %
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 "MultiApp.h"
      12             : 
      13             : #include "AppFactory.h"
      14             : #include "AuxiliarySystem.h"
      15             : #include "DisplacedProblem.h"
      16             : #include "Console.h"
      17             : #include "Executioner.h"
      18             : #include "FEProblem.h"
      19             : #include "MooseMesh.h"
      20             : #include "MooseUtils.h"
      21             : #include "OutputWarehouse.h"
      22             : #include "SetupInterface.h"
      23             : #include "UserObject.h"
      24             : #include "CommandLine.h"
      25             : #include "Conversion.h"
      26             : #include "NonlinearSystemBase.h"
      27             : #include "DelimitedFileReader.h"
      28             : #include "MooseAppCoordTransform.h"
      29             : #include "MultiAppTransfer.h"
      30             : #include "Positions.h"
      31             : #include "Transient.h"
      32             : #include "Backup.h"
      33             : #include "Parser.h"
      34             : 
      35             : #include "libmesh/mesh_tools.h"
      36             : #include "libmesh/numeric_vector.h"
      37             : 
      38             : // C++ includes
      39             : #include <algorithm>
      40             : #include <fstream>
      41             : #include <iomanip>
      42             : #include <iterator>
      43             : #include <set>
      44             : 
      45             : // Call to "uname"
      46             : #ifdef LIBMESH_HAVE_SYS_UTSNAME_H
      47             : #include <sys/utsname.h>
      48             : #endif
      49             : 
      50             : InputParameters
      51       31568 : MultiApp::validParams()
      52             : {
      53       31568 :   InputParameters params = MooseObject::validParams();
      54       31568 :   params += SetupInterface::validParams();
      55             : 
      56       63136 :   params.addParam<bool>("use_displaced_mesh",
      57       63136 :                         false,
      58             :                         "Whether or not this object should use the "
      59             :                         "displaced mesh for computation.  Note that "
      60             :                         "in the case this is true but no "
      61             :                         "displacements are provided in the Mesh block "
      62             :                         "the undisplaced mesh will still be used.");
      63             : 
      64       31568 :   std::ostringstream app_types_strings;
      65       63162 :   for (const auto & name_bi_pair : AppFactory::instance().registeredObjects())
      66       31594 :     app_types_strings << name_bi_pair.first << " ";
      67       63136 :   MooseEnum app_types_options(app_types_strings.str(), "", true);
      68             : 
      69             :   // Dynamic loading
      70      126272 :   params.addParam<MooseEnum>("app_type",
      71             :                              app_types_options,
      72             :                              "The type of application to build (applications not "
      73             :                              "registered can be loaded with dynamic libraries. Parent "
      74             :                              "application type will be used if not provided.");
      75      126272 :   params.addParam<std::string>("library_path",
      76             :                                "",
      77             :                                "Path to search for dynamic libraries (please "
      78             :                                "avoid committing absolute paths in addition to "
      79             :                                "MOOSE_LIBRARY_PATH)");
      80      126272 :   params.addParam<std::string>(
      81             :       "library_name",
      82             :       "",
      83             :       "The file name of the library (*.la file) that will be dynamically loaded.");
      84       94704 :   params.addParam<bool>("library_load_dependencies",
      85       63136 :                         false,
      86             :                         "Tells MOOSE to manually load library dependencies. This should not be "
      87             :                         "necessary and is here for debugging/troubleshooting.");
      88             : 
      89             :   // Subapp positions
      90      126272 :   params.addParam<std::vector<Point>>(
      91             :       "positions",
      92             :       "The positions of the App locations.  Each set of 3 values will represent a "
      93             :       "Point.  This and 'positions_file' cannot be both supplied. If this and "
      94             :       "'positions_file'/'_objects' are not supplied, a single position (0,0,0) will be used");
      95      126272 :   params.addParam<std::vector<FileName>>("positions_file",
      96             :                                          "Filename(s) that should be looked in for positions. Each"
      97             :                                          " set of 3 values in that file will represent a Point.  "
      98             :                                          "This and 'positions(_objects)' cannot be both supplied");
      99      126272 :   params.addParam<std::vector<PositionsName>>("positions_objects",
     100             :                                               "The name of a Positions object that will contain "
     101             :                                               "the locations of the sub-apps created. This and "
     102             :                                               "'positions(_file)' cannot be both supplied");
     103       94704 :   params.addParam<bool>(
     104             :       "output_in_position",
     105       63136 :       false,
     106             :       "If true this will cause the output from the MultiApp to be 'moved' by its position vector");
     107       94704 :   params.addParam<bool>(
     108             :       "run_in_position",
     109       63136 :       false,
     110             :       "If true this will cause the mesh from the MultiApp to be 'moved' by its position vector");
     111             : 
     112      126272 :   params.addRequiredParam<std::vector<FileName>>(
     113             :       "input_files",
     114             :       "The input file for each App.  If this parameter only contains one input file "
     115             :       "it will be used for all of the Apps.  When using 'positions_from_file' it is "
     116             :       "also admissable to provide one input_file per file.");
     117       94704 :   params.addParam<Real>("bounding_box_inflation",
     118       63136 :                         0.01,
     119             :                         "Relative amount to 'inflate' the bounding box of this MultiApp.");
     120       94704 :   params.addParam<Point>("bounding_box_padding",
     121       63136 :                          RealVectorValue(),
     122             :                          "Additional padding added to the dimensions of the bounding box. The "
     123             :                          "values are added to the x, y and z dimension respectively.");
     124             : 
     125       63136 :   params.addPrivateParam<MPI_Comm>("_mpi_comm");
     126             : 
     127             :   // Set the default execution time
     128       63136 :   params.set<ExecFlagEnum>("execute_on", true) = EXEC_TIMESTEP_BEGIN;
     129             :   // Add the POST_ADAPTIVITY execution flag.
     130             : #ifdef LIBMESH_ENABLE_AMR
     131       31568 :   ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on");
     132       31568 :   exec_enum.addAvailableFlags(EXEC_POST_ADAPTIVITY);
     133       94704 :   params.setDocString("execute_on", exec_enum.getDocString());
     134             : #endif
     135             : 
     136       94704 :   params.addParam<processor_id_type>("max_procs_per_app",
     137       63136 :                                      std::numeric_limits<processor_id_type>::max(),
     138             :                                      "Maximum number of processors to give to each App in this "
     139             :                                      "MultiApp.  Useful for restricting small solves to just a few "
     140             :                                      "procs so they don't get spread out");
     141       94704 :   params.addParam<processor_id_type>("min_procs_per_app",
     142       63136 :                                      1,
     143             :                                      "Minimum number of processors to give to each App in this "
     144             :                                      "MultiApp.  Useful for larger, distributed mesh solves.");
     145       94704 :   params.addParam<bool>(
     146             :       "wait_for_first_app_init",
     147       63136 :       false,
     148             :       "Create the first sub-application on rank 0, then MPI_Barrier before "
     149             :       "creating the next N-1 apps (on all ranks). "
     150             :       "This is only needed if your sub-application needs to perform some setup "
     151             :       "actions in quiet, without other sub-applications working at the same time.");
     152             : 
     153       94704 :   params.addParam<Real>("global_time_offset",
     154       63136 :                         0,
     155             :                         "The time offset relative to the parent application for the purpose of "
     156             :                         "starting a subapp at a different time from the parent application. The "
     157             :                         "global time will be ahead by the offset specified here.");
     158             : 
     159             :   // Resetting subapps
     160      126272 :   params.addParam<std::vector<Real>>(
     161             :       "reset_time",
     162             :       {},
     163             :       "The time(s) at which to reset Apps given by the 'reset_apps' parameter.  "
     164             :       "Resetting an App means that it is destroyed and recreated, possibly "
     165             :       "modeling the insertion of 'new' material for that app.");
     166      126272 :   params.addParam<std::vector<unsigned int>>(
     167             :       "reset_apps",
     168             :       {},
     169             :       "The Apps that will be reset when 'reset_time' is hit.  These are the App "
     170             :       "'numbers' starting with 0 corresponding to the order of the App positions.  "
     171             :       "Resetting an App means that it is destroyed and recreated, possibly modeling "
     172             :       "the insertion of 'new' material for that app.");
     173             : 
     174             :   // Moving subapps
     175       94704 :   params.addParam<Real>(
     176             :       "move_time",
     177       63136 :       std::numeric_limits<Real>::max(),
     178             :       "The time at which Apps designated by move_apps are moved to move_positions.");
     179             : 
     180      126272 :   params.addParam<std::vector<unsigned int>>(
     181             :       "move_apps",
     182             :       {},
     183             :       "Apps, designated by their 'numbers' starting with 0 corresponding to the order "
     184             :       "of the App positions, to be moved at move_time to move_positions");
     185      126272 :   params.addParam<std::vector<Point>>(
     186             :       "move_positions", {}, "The positions corresponding to each move_app.");
     187             : 
     188      126272 :   params.addParam<std::vector<CLIArgString>>(
     189             :       "cli_args",
     190             :       {},
     191             :       "Additional command line arguments to pass to the sub apps. If one set is provided the "
     192             :       "arguments are applied to all, otherwise there must be a set for each sub app.");
     193             : 
     194      126272 :   params.addParam<std::vector<FileName>>(
     195             :       "cli_args_files",
     196             :       "File names that should be looked in for additional command line arguments "
     197             :       "to pass to the sub apps. Each line of a file is set to each sub app. If only "
     198             :       "one line is provided, it will be applied to all sub apps.");
     199             : 
     200             :   // Fixed point iterations
     201      157840 :   params.addRangeCheckedParam<Real>("relaxation_factor",
     202       63136 :                                     1.0,
     203             :                                     "relaxation_factor>0 & relaxation_factor<2",
     204             :                                     "Fraction of newly computed value to keep."
     205             :                                     "Set between 0 and 2.");
     206      189408 :   params.addDeprecatedParam<std::vector<std::string>>(
     207             :       "relaxed_variables",
     208             :       {},
     209             :       "Use transformed_variables.",
     210             :       "List of subapp variables to relax during Multiapp coupling iterations");
     211      126272 :   params.addParam<std::vector<std::string>>(
     212             :       "transformed_variables",
     213             :       {},
     214             :       "List of subapp variables to use coupling algorithm on during Multiapp coupling iterations");
     215      126272 :   params.addParam<std::vector<PostprocessorName>>(
     216             :       "transformed_postprocessors",
     217             :       {},
     218             :       "List of subapp postprocessors to use coupling "
     219             :       "algorithm on during Multiapp coupling iterations");
     220       94704 :   params.addParam<bool>("keep_solution_during_restore",
     221       63136 :                         false,
     222             :                         "This is useful when doing MultiApp coupling iterations. It takes the "
     223             :                         "final solution from the previous coupling iteration"
     224             :                         "and re-uses it as the initial guess for the next coupling iteration");
     225       94704 :   params.addParam<bool>("keep_aux_solution_during_restore",
     226       63136 :                         false,
     227             :                         "This is useful when doing MultiApp coupling iterations. It takes the "
     228             :                         "final auxiliary solution from the previous coupling iteration"
     229             :                         "and re-uses it as the initial guess for the next coupling iteration");
     230       94704 :   params.addParam<bool>(
     231             :       "no_restore",
     232       63136 :       false,
     233             :       "True to turn off restore for this multiapp. This is useful when doing steady-state "
     234             :       "Picard iterations where we want to use the solution of previous Picard iteration as the "
     235             :       "initial guess of the current Picard iteration.");
     236       94704 :   params.addParam<unsigned int>(
     237             :       "max_multiapp_level",
     238       63136 :       10,
     239             :       "Integer set by user that will stop the simulation if the multiapp level "
     240             :       "exceeds it. Useful for preventing infinite loops with multiapp simulations");
     241             : 
     242      157840 :   params.addDeprecatedParam<bool>("clone_master_mesh",
     243       63136 :                                   false,
     244             :                                   "True to clone parent app mesh and use it for this MultiApp.",
     245             :                                   "clone_master_mesh is deprecated, use clone_parent_mesh instead");
     246       63136 :   params.addParam<bool>(
     247       63136 :       "clone_parent_mesh", false, "True to clone parent app mesh and use it for this MultiApp.");
     248             : 
     249       63136 :   params.addPrivateParam<bool>("use_positions", true);
     250       94704 :   params.declareControllable("enable");
     251      157840 :   params.declareControllable("cli_args", {EXEC_PRE_MULTIAPP_SETUP});
     252       63136 :   params.registerBase("MultiApp");
     253             : 
     254      126272 :   params.addParamNamesToGroup("use_displaced_mesh wait_for_first_app_init max_multiapp_level",
     255             :                               "Advanced");
     256      126272 :   params.addParamNamesToGroup("positions positions_file positions_objects run_in_position "
     257             :                               "output_in_position",
     258             :                               "Positions / transformations of the MultiApp frame of reference");
     259      126272 :   params.addParamNamesToGroup("min_procs_per_app max_procs_per_app", "Parallelism");
     260      126272 :   params.addParamNamesToGroup("reset_time reset_apps", "Reset MultiApp");
     261      126272 :   params.addParamNamesToGroup("move_time move_apps move_positions", "Timed move of MultiApps");
     262      126272 :   params.addParamNamesToGroup("relaxation_factor transformed_variables transformed_postprocessors "
     263             :                               "keep_solution_during_restore keep_aux_solution_during_restore "
     264             :                               "no_restore",
     265             :                               "Fixed point iteration");
     266      126272 :   params.addParamNamesToGroup("library_name library_path library_load_dependencies",
     267             :                               "Dynamic loading");
     268       94704 :   params.addParamNamesToGroup("cli_args cli_args_files", "Passing command line argument");
     269       63136 :   return params;
     270       63136 : }
     271             : 
     272        8129 : MultiApp::MultiApp(const InputParameters & parameters)
     273             :   : MooseObject(parameters),
     274             :     SetupInterface(this),
     275             :     Restartable(this, "MultiApps"),
     276       16258 :     PerfGraphInterface(this, std::string("MultiApp::") + _name),
     277       24387 :     _fe_problem(*getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
     278       28730 :     _app_type(isParamValid("app_type") ? std::string(getParam<MooseEnum>("app_type"))
     279        3786 :                                        : _fe_problem.getMooseApp().type()),
     280       16258 :     _use_positions(getParam<bool>("use_positions")),
     281       16258 :     _input_files(getParam<std::vector<FileName>>("input_files")),
     282       16258 :     _wait_for_first_app_init(getParam<bool>("wait_for_first_app_init")),
     283        8129 :     _total_num_apps(0),
     284        8129 :     _my_num_apps(0),
     285        8129 :     _first_local_app(0),
     286        8129 :     _orig_comm(_communicator.get()),
     287        8129 :     _my_communicator(),
     288        8129 :     _my_comm(_my_communicator.get()),
     289        8129 :     _my_rank(0),
     290       16258 :     _inflation(getParam<Real>("bounding_box_inflation")),
     291       16258 :     _bounding_box_padding(getParam<Point>("bounding_box_padding")),
     292       16258 :     _max_procs_per_app(getParam<processor_id_type>("max_procs_per_app")),
     293       16258 :     _min_procs_per_app(getParam<processor_id_type>("min_procs_per_app")),
     294       16258 :     _output_in_position(getParam<bool>("output_in_position")),
     295       16258 :     _global_time_offset(getParam<Real>("global_time_offset")),
     296       16258 :     _reset_times(getParam<std::vector<Real>>("reset_time")),
     297       16258 :     _reset_apps(getParam<std::vector<unsigned int>>("reset_apps")),
     298       16258 :     _reset_happened(false),
     299       16258 :     _move_time(getParam<Real>("move_time")),
     300       16258 :     _move_apps(getParam<std::vector<unsigned int>>("move_apps")),
     301       16258 :     _move_positions(getParam<std::vector<Point>>("move_positions")),
     302        8129 :     _move_happened(false),
     303        8129 :     _has_an_app(true),
     304       16258 :     _cli_args(getParam<std::vector<CLIArgString>>("cli_args")),
     305       16258 :     _keep_solution_during_restore(getParam<bool>("keep_solution_during_restore")),
     306       16258 :     _keep_aux_solution_during_restore(getParam<bool>("keep_aux_solution_during_restore")),
     307       16258 :     _no_restore(getParam<bool>("no_restore")),
     308       16258 :     _run_in_position(getParam<bool>("run_in_position")),
     309       16258 :     _sub_app_backups(declareRestartableDataWithContext<SubAppBackups>("sub_app_backups", this)),
     310       32516 :     _solve_step_timer(registerTimedSection("solveStep", 3, "Executing MultiApps", false)),
     311       32516 :     _init_timer(registerTimedSection("init", 3, "Initializing MultiApp")),
     312       32516 :     _backup_timer(registerTimedSection("backup", 3, "Backing Up MultiApp")),
     313       32516 :     _restore_timer(registerTimedSection("restore", 3, "Restoring MultiApp")),
     314      121935 :     _reset_timer(registerTimedSection("resetApp", 3, "Resetting MultiApp"))
     315             : {
     316       38252 :   if (parameters.isParamSetByUser("cli_args") && parameters.isParamValid("cli_args") &&
     317       11953 :       parameters.isParamValid("cli_args_files"))
     318           6 :     paramError("cli_args",
     319             :                "'cli_args' and 'cli_args_files' cannot be specified simultaneously in MultiApp ");
     320             : 
     321       16252 :   if (!_use_positions && (isParamValid("positions") || isParamValid("positions_file") ||
     322        8126 :                           isParamValid("positions_objects")))
     323           0 :     paramError("use_positions",
     324             :                "This MultiApps has been set to not use positions, "
     325             :                "but a 'positions' parameter has been set.");
     326             : 
     327       24316 :   if ((_reset_apps.size() > 0 && _reset_times.size() == 0) ||
     328       16190 :       (_reset_apps.size() == 0 && _reset_times.size() > 0))
     329           0 :     mooseError("reset_time and reset_apps may only be specified together");
     330             : 
     331             :   // Check that the reset times are sorted by the user
     332        8126 :   auto sorted_times = _reset_times;
     333        8126 :   std::sort(sorted_times.begin(), sorted_times.end());
     334        8126 :   if (_reset_times.size() && _reset_times != sorted_times)
     335           6 :     paramError("reset_time", "List of reset times must be sorted in increasing order");
     336        8123 : }
     337             : 
     338             : void
     339        8102 : MultiApp::init(unsigned int num_apps, bool batch_mode)
     340             : {
     341        8102 :   auto config = rankConfig(
     342             :       processor_id(), n_processors(), num_apps, _min_procs_per_app, _max_procs_per_app, batch_mode);
     343        8102 :   init(num_apps, config);
     344        8099 : }
     345             : 
     346             : void
     347        8102 : MultiApp::init(unsigned int num_apps, const LocalRankConfig & config)
     348             : {
     349        8102 :   TIME_SECTION(_init_timer);
     350             : 
     351        8102 :   _total_num_apps = num_apps;
     352        8102 :   _rank_config = config;
     353        8102 :   buildComm();
     354        8102 :   _sub_app_backups.resize(_my_num_apps);
     355             : 
     356        8102 :   _has_bounding_box.resize(_my_num_apps, false);
     357        8102 :   _reset_happened.resize(_reset_times.size(), false);
     358        8102 :   _bounding_box.resize(_my_num_apps);
     359             : 
     360        8102 :   if ((_cli_args.size() > 1) && (_total_num_apps != _cli_args.size()))
     361           6 :     paramError("cli_args",
     362             :                "The number of items supplied must be 1 or equal to the number of sub apps.");
     363             : 
     364             :   // if cliArgs() != _cli_args, then cliArgs() was overridden and we need to check it
     365        8099 :   auto cla = cliArgs();
     366       16198 :   if (cla != std::vector<std::string>(_cli_args.begin(), _cli_args.end()))
     367             :   {
     368          12 :     if ((cla.size() > 1) && (_total_num_apps != cla.size()))
     369           0 :       mooseError("The number of items supplied as command line argument to subapps must be 1 or "
     370             :                  "equal to the number of sub apps. Note: you use a multiapp that provides its own "
     371             :                  "command line parameters so the error is not in cli_args");
     372             :   }
     373        8099 : }
     374             : 
     375             : void
     376        8120 : MultiApp::setupPositions()
     377             : {
     378        8120 :   if (_use_positions)
     379             :   {
     380        8120 :     fillPositions();
     381        8102 :     init(_positions.size());
     382        8099 :     createApps();
     383             :   }
     384        8033 : }
     385             : 
     386             : void
     387        8099 : MultiApp::createApps()
     388             : {
     389        8099 :   if (!_has_an_app)
     390          52 :     return;
     391             : 
     392       40235 :   TIME_SECTION("createApps", 2, "Instantiating Sub-Apps", false);
     393             : 
     394             :   // Read commandLine arguments that will be used when creating apps
     395        8047 :   readCommandLineArguments();
     396             : 
     397        8032 :   Moose::ScopedCommSwapper swapper(_my_comm);
     398             : 
     399        8032 :   _apps.resize(_my_num_apps);
     400             : 
     401             :   // If the user provided an unregistered app type, see if we can load it dynamically
     402        8032 :   if (!AppFactory::instance().isRegistered(_app_type))
     403           5 :     _app.dynamicAppRegistration(_app_type,
     404             :                                 getParam<std::string>("library_path"),
     405           7 :                                 getParam<std::string>("library_name"),
     406          14 :                                 getParam<bool>("library_load_dependencies"));
     407             : 
     408        8029 :   bool rank_did_quiet_init = false;
     409        8029 :   unsigned int local_app = libMesh::invalid_uint;
     410        8029 :   if (_wait_for_first_app_init)
     411             :   {
     412          14 :     if (hasLocalApp(0))
     413             :     {
     414           7 :       rank_did_quiet_init = true;
     415           7 :       local_app = globalAppToLocal(0);
     416           7 :       createLocalApp(local_app);
     417             :     }
     418             : 
     419          14 :     MPI_Barrier(_orig_comm);
     420             :   }
     421             : 
     422       20115 :   for (unsigned int i = 0; i < _my_num_apps; i++)
     423             :   {
     424       12134 :     if (rank_did_quiet_init && i == local_app)
     425           7 :       continue;
     426       12127 :     createLocalApp(i);
     427             :   }
     428        7981 : }
     429             : 
     430             : void
     431       12134 : MultiApp::createLocalApp(const unsigned int i)
     432             : {
     433       12134 :   createApp(i, _global_time_offset);
     434       12086 : }
     435             : 
     436             : void
     437        7961 : MultiApp::initialSetup()
     438             : {
     439        7961 :   if (!_use_positions)
     440             :     // if not using positions, we create the sub-apps in initialSetup instead of right after
     441             :     // construction of MultiApp
     442           0 :     createApps();
     443        7961 : }
     444             : 
     445             : void
     446        8047 : MultiApp::readCommandLineArguments()
     447             : {
     448       24141 :   if (isParamValid("cli_args_files"))
     449             :   {
     450          51 :     _cli_args_from_file.clear();
     451             : 
     452         102 :     std::vector<FileName> cli_args_files = getParam<std::vector<FileName>>("cli_args_files");
     453         102 :     std::vector<FileName> input_files = getParam<std::vector<FileName>>("input_files");
     454             : 
     455             :     // If we use parameter "cli_args_files", at least one file should be provided
     456          51 :     if (!cli_args_files.size())
     457           6 :       paramError("cli_args_files", "You need to provide at least one commandLine argument file ");
     458             : 
     459             :     // If we multiple input files, then we need to check if the number of input files
     460             :     // match with the number of argument files
     461          48 :     if (cli_args_files.size() != 1 && cli_args_files.size() != input_files.size())
     462           9 :       paramError("cli_args_files",
     463             :                  "The number of commandLine argument files ",
     464             :                  cli_args_files.size(),
     465             :                  " for MultiApp ",
     466           3 :                  name(),
     467             :                  " must either be only one or match the number of input files ",
     468             :                  input_files.size());
     469             : 
     470             :     // Go through all argument files
     471          45 :     std::vector<std::string> cli_args;
     472          96 :     for (unsigned int p_file_it = 0; p_file_it < cli_args_files.size(); p_file_it++)
     473             :     {
     474          57 :       std::string cli_args_file = cli_args_files[p_file_it];
     475             :       // Clear up
     476          57 :       cli_args.clear();
     477             :       // Read the file on the root processor then broadcast it
     478          57 :       if (processor_id() == 0)
     479             :       {
     480          45 :         MooseUtils::checkFileReadable(cli_args_file);
     481             : 
     482          45 :         std::ifstream is(cli_args_file.c_str());
     483             :         // Read by line rather than space separated like the cli_args parameter
     484          45 :         std::string line;
     485         183 :         while (std::getline(is, line))
     486         138 :           cli_args.push_back(line);
     487             : 
     488             :         // We do not allow empty files
     489          45 :         if (!cli_args.size())
     490           6 :           paramError("cli_args_files",
     491             :                      "There is no commandLine argument in the commandLine argument file ",
     492             :                      cli_args_file);
     493             : 
     494             :         // If we have position files, we need to
     495             :         // make sure the number of commandLine argument strings
     496             :         // match with the number of positions
     497          42 :         if (_npositions_inputfile.size())
     498             :         {
     499          39 :           auto num_positions = _npositions_inputfile[p_file_it];
     500             :           // Check if the number of commandLine argument strings equal to
     501             :           // the number of positions
     502          39 :           if (cli_args.size() == 1)
     503           0 :             for (MooseIndex(num_positions) num = 0; num < num_positions; num++)
     504           0 :               _cli_args_from_file.push_back(cli_args.front());
     505          39 :           else if (cli_args.size() == num_positions)
     506         144 :             for (auto && cli_arg : cli_args)
     507         108 :               _cli_args_from_file.push_back(cli_arg);
     508           3 :           else if (cli_args.size() != num_positions)
     509           6 :             paramError("cli_args_files",
     510             :                        "The number of commandLine argument strings ",
     511             :                        cli_args.size(),
     512             :                        " in the file ",
     513             :                        cli_args_file,
     514             :                        " must either be only one or match the number of positions ",
     515             :                        num_positions);
     516             :         }
     517             :         else
     518             :         {
     519             :           // If we do not have position files, we will check if the number of
     520             :           // commandLine argument strings match with the total number of subapps
     521          18 :           for (auto && cli_arg : cli_args)
     522          15 :             _cli_args_from_file.push_back(cli_arg);
     523             :         }
     524          39 :       }
     525          51 :     }
     526             : 
     527             :     // Broad cast all arguments to everyone
     528          39 :     _communicator.broadcast(_cli_args_from_file);
     529          39 :   }
     530             : 
     531        8074 :   if (_cli_args_from_file.size() && _cli_args_from_file.size() != 1 &&
     532          39 :       _cli_args_from_file.size() != _total_num_apps)
     533           6 :     mooseError(" The number of commandLine argument strings ",
     534           3 :                _cli_args_from_file.size(),
     535             :                " must either be only one or match the total "
     536             :                "number of sub apps ",
     537           3 :                _total_num_apps);
     538             : 
     539        8032 :   if (_cli_args_from_file.size() && cliArgs().size())
     540           0 :     mooseError("Cannot set commandLine arguments from both input_file and external files");
     541        8032 : }
     542             : 
     543             : void
     544        8013 : MultiApp::fillPositions()
     545             : {
     546        8013 :   if (_move_apps.size() != _move_positions.size())
     547           0 :     mooseError("The number of apps to move and the positions to move them to must be the same for "
     548             :                "MultiApp ",
     549           0 :                _name);
     550             : 
     551       40065 :   if (isParamValid("positions") + isParamValid("positions_file") +
     552       24039 :           isParamValid("positions_objects") >
     553             :       1)
     554           3 :     mooseError("Only one 'positions' parameter may be specified");
     555             : 
     556       24030 :   if (isParamValid("positions"))
     557             :   {
     558       10054 :     _positions = getParam<std::vector<Point>>("positions");
     559             : 
     560        5027 :     if (_positions.size() < _input_files.size())
     561           3 :       mooseError("Not enough positions for the number of input files provided in MultiApp ",
     562           3 :                  name());
     563             :   }
     564        8949 :   else if (isParamValid("positions_file"))
     565             :   {
     566         168 :     std::vector<FileName> positions_files = getParam<std::vector<FileName>>("positions_file");
     567         168 :     std::vector<FileName> input_files = getParam<std::vector<FileName>>("input_files");
     568             : 
     569          84 :     if (input_files.size() != 1 && positions_files.size() != input_files.size())
     570           3 :       mooseError("Number of input_files for MultiApp ",
     571           3 :                  name(),
     572             :                  " must either be only one or match the number of positions_file files");
     573             : 
     574             :     // Clear out the _input_files because we're going to rebuild it
     575          81 :     if (input_files.size() != 1)
     576          27 :       _input_files.clear();
     577             : 
     578         186 :     for (unsigned int p_file_it = 0; p_file_it < positions_files.size(); p_file_it++)
     579             :     {
     580         108 :       std::string positions_file = positions_files[p_file_it];
     581         108 :       MooseUtils::DelimitedFileReader file(positions_file, &_communicator);
     582         108 :       file.setFormatFlag(MooseUtils::DelimitedFileReader::FormatFlag::ROWS);
     583         108 :       file.read();
     584             : 
     585         108 :       const std::vector<Point> & data = file.getDataAsPoints();
     586         513 :       for (const auto & d : data)
     587         408 :         _positions.push_back(d);
     588             : 
     589             :       // Save the number of positions for this input file
     590         105 :       _npositions_inputfile.push_back(data.size());
     591             : 
     592         513 :       for (unsigned int i = 0; i < data.size(); ++i)
     593         408 :         if (input_files.size() != 1)
     594         108 :           _input_files.push_back(input_files[p_file_it]);
     595         105 :     }
     596          78 :   }
     597        8697 :   else if (isParamValid("positions_objects"))
     598             :   {
     599         930 :     const auto & positions_param_objs = getParam<std::vector<PositionsName>>("positions_objects");
     600         930 :     const auto & input_files = getParam<std::vector<FileName>>("input_files");
     601             : 
     602         465 :     if (input_files.size() != 1 && positions_param_objs.size() != input_files.size())
     603           0 :       mooseError("Number of input_files for MultiApp ",
     604           0 :                  name(),
     605             :                  " must either be only one or match the number of positions_objects specified");
     606             : 
     607             :     // Clear out the _input_files because we're going to rebuild it
     608         465 :     if (input_files.size() != 1)
     609           0 :       _input_files.clear();
     610             : 
     611             :     // Keeps track of where each positions object start in terms of subapp numbers
     612         465 :     unsigned int offset = 0;
     613             : 
     614         966 :     for (const auto p_obj_it : index_range(positions_param_objs))
     615             :     {
     616         504 :       const std::string & positions_name = positions_param_objs[p_obj_it];
     617         504 :       auto positions_obj = &_fe_problem.getPositionsObject(positions_name);
     618             : 
     619         504 :       const auto & data = positions_obj->getPositions(true);
     620             : 
     621             :       // Append all positions from this object
     622        2151 :       for (const auto & d : data)
     623        1650 :         _positions.push_back(d);
     624             : 
     625             :       // Save the number of positions for this input file
     626         501 :       _npositions_inputfile.push_back(data.size());
     627             : 
     628         501 :       if (!positions_obj)
     629           0 :         paramError("positions_objects",
     630           0 :                    "'" + positions_name + "' is not of the expected type. Should be a Positions");
     631             : 
     632             :       // Keep track of which positions is tied to what subapp
     633        2151 :       for (unsigned int i = 0; i < data.size(); ++i)
     634             :       {
     635        1650 :         if (input_files.size() != 1)
     636           0 :           _input_files.push_back(input_files[p_obj_it]);
     637        1650 :         _positions_objs.push_back(positions_obj);
     638        1650 :         _positions_index_offsets.push_back(offset);
     639             :       }
     640         501 :       offset += data.size();
     641             :     }
     642             :   }
     643             :   else
     644             :   {
     645        2434 :     _positions = {Point()};
     646             : 
     647        2434 :     if (_positions.size() < _input_files.size())
     648           3 :       mooseError("Not enough positions for the number of input files provided in MultiApp ",
     649           3 :                  name());
     650             :   }
     651             : 
     652             :   mooseAssert(_input_files.size() == 1 || _positions.size() == _input_files.size(),
     653             :               "Number of positions and input files are not the same!");
     654        7995 : }
     655             : 
     656             : void
     657       65139 : MultiApp::preTransfer(Real /*dt*/, Real target_time)
     658             : {
     659             :   // Get a transient executioner to get a user-set tolerance
     660       65139 :   Real timestep_tol = 1e-13;
     661       65139 :   if (dynamic_cast<TransientBase *>(_fe_problem.getMooseApp().getExecutioner()))
     662       61858 :     timestep_tol =
     663       61858 :         dynamic_cast<TransientBase *>(_fe_problem.getMooseApp().getExecutioner())->timestepTol();
     664             : 
     665             :   // Determination on whether we need to backup the app due to changes below
     666       65139 :   bool backup_apps = false;
     667             : 
     668             :   // First, see if any Apps need to be reset
     669       65847 :   for (unsigned int i = 0; i < _reset_times.size(); i++)
     670             :   {
     671         777 :     if (!_reset_happened[i] && (target_time + timestep_tol >= _reset_times[i]))
     672             :     {
     673          69 :       _reset_happened[i] = true;
     674          69 :       if (_reset_apps.size() > 0)
     675         138 :         for (auto & app : _reset_apps)
     676          69 :           resetApp(app);
     677             : 
     678             :       // If we reset an application, then we delete the old objects, including the coordinate
     679             :       // transformation classes. Consequently we need to reset the coordinate transformation classes
     680             :       // in the associated transfer classes
     681          99 :       for (auto * const transfer : _associated_transfers)
     682          30 :         transfer->getAppInfo();
     683             : 
     684             :       // Similarly we need to transform the mesh again
     685          69 :       if (_run_in_position)
     686           0 :         for (const auto i : make_range(_my_num_apps))
     687             :         {
     688           0 :           auto app_ptr = _apps[i];
     689           0 :           if (usingPositions())
     690           0 :             app_ptr->getExecutioner()->feProblem().coordTransform().transformMesh(
     691           0 :                 app_ptr->getExecutioner()->feProblem().mesh(), _positions[_first_local_app + i]);
     692             :           else
     693           0 :             app_ptr->getExecutioner()->feProblem().coordTransform().transformMesh(
     694           0 :                 app_ptr->getExecutioner()->feProblem().mesh(), Point(0, 0, 0));
     695           0 :         }
     696             : 
     697             :       // If the time step covers multiple reset times, set them all as having 'happened'
     698         158 :       for (unsigned int j = i; j < _reset_times.size(); j++)
     699          89 :         if (target_time + timestep_tol >= _reset_times[j])
     700          79 :           _reset_happened[j] = true;
     701             : 
     702             :       // Backup in case the next solve fails
     703          69 :       backup_apps = true;
     704             : 
     705          69 :       break;
     706             :     }
     707             :   }
     708             : 
     709             :   // Now move any apps that should be moved
     710       65139 :   if (_use_positions && !_move_happened && target_time + timestep_tol >= _move_time)
     711             :   {
     712          54 :     _move_happened = true;
     713         105 :     for (unsigned int i = 0; i < _move_apps.size(); i++)
     714          54 :       moveApp(_move_apps[i], _move_positions[i]);
     715             : 
     716             :     // Backup in case the next solve fails
     717          51 :     backup_apps = true;
     718             :   }
     719             : 
     720       65136 :   if (backup_apps)
     721         101 :     backup();
     722       65136 : }
     723             : 
     724             : Executioner *
     725           0 : MultiApp::getExecutioner(unsigned int app)
     726             : {
     727           0 :   if (!_has_an_app)
     728           0 :     mooseError("No app for ", name(), " on processor ", _orig_rank);
     729             : 
     730           0 :   return _apps[globalAppToLocal(app)]->getExecutioner();
     731             : }
     732             : 
     733             : void
     734        4640 : MultiApp::finalize()
     735             : {
     736       11298 :   for (const auto & app_ptr : _apps)
     737             :   {
     738        6658 :     auto * executioner = app_ptr->getExecutioner();
     739             :     mooseAssert(executioner, "Executioner is nullptr");
     740             : 
     741        6658 :     executioner->feProblem().execute(EXEC_FINAL);
     742        6658 :     executioner->feProblem().outputStep(EXEC_FINAL);
     743             :   }
     744        4640 : }
     745             : 
     746             : void
     747        5004 : MultiApp::postExecute()
     748             : {
     749       12326 :   for (const auto & app_ptr : _apps)
     750             :   {
     751        7322 :     auto * executioner = app_ptr->getExecutioner();
     752             :     mooseAssert(executioner, "Executioner is nullptr");
     753             : 
     754        7322 :     executioner->postExecute();
     755             :   }
     756        5004 : }
     757             : 
     758             : void
     759       25358 : MultiApp::backup()
     760             : {
     761       25358 :   TIME_SECTION(_backup_timer);
     762             : 
     763       25358 :   if (_fe_problem.verboseMultiApps())
     764         926 :     _console << "Backed up MultiApp ... ";
     765             : 
     766       59212 :   for (unsigned int i = 0; i < _my_num_apps; i++)
     767       33854 :     _sub_app_backups[i] = _apps[i]->backup();
     768             : 
     769       25358 :   if (_fe_problem.verboseMultiApps())
     770         926 :     _console << name() << std::endl;
     771       25358 : }
     772             : 
     773             : void
     774       43780 : MultiApp::restore(bool force)
     775             : {
     776       43780 :   TIME_SECTION(_restore_timer);
     777             : 
     778       43780 :   if (force || needsRestoration())
     779             :   {
     780             :     // Must be restarting / recovering from main app so hold off on restoring
     781             :     // Instead - the restore will happen in sub-apps' initialSetup()
     782             :     // Note that _backups was already populated by dataLoad() in the main app
     783        9336 :     if (_fe_problem.getCurrentExecuteOnFlag() == EXEC_INITIAL)
     784         580 :       return;
     785             : 
     786             :     // We temporarily copy and store solutions for all subapps
     787        8756 :     if (_keep_solution_during_restore)
     788             :     {
     789         324 :       _end_solutions.resize(_my_num_apps);
     790             : 
     791         645 :       for (unsigned int i = 0; i < _my_num_apps; i++)
     792             :       {
     793         324 :         _end_solutions[i].resize(_apps[i]->getExecutioner()->feProblem().numSolverSystems());
     794         658 :         for (unsigned int j = 0; j < _apps[i]->getExecutioner()->feProblem().numSolverSystems();
     795             :              j++)
     796             :         {
     797         668 :           _end_solutions[i][j] = _apps[i]
     798             :                                      ->getExecutioner()
     799         334 :                                      ->feProblem()
     800         334 :                                      .getSolverSystem(/*solver_sys=*/j)
     801         334 :                                      .solution()
     802         668 :                                      .clone();
     803             :         }
     804             :         auto & sub_multiapps =
     805         324 :             _apps[i]->getExecutioner()->feProblem().getMultiAppWarehouse().getObjects();
     806             : 
     807             :         // multiapps of each subapp should do the same things
     808             :         // It is implemented recursively
     809         334 :         for (auto & multi_app : sub_multiapps)
     810          13 :           multi_app->keepSolutionDuringRestore(_keep_solution_during_restore);
     811             :       }
     812             :     }
     813             : 
     814             :     // We temporarily copy and store solutions for all subapps
     815        8753 :     if (_keep_aux_solution_during_restore)
     816             :     {
     817         120 :       _end_aux_solutions.resize(_my_num_apps);
     818             : 
     819         240 :       for (unsigned int i = 0; i < _my_num_apps; i++)
     820         120 :         _end_aux_solutions[i] =
     821         240 :             _apps[i]->getExecutioner()->feProblem().getAuxiliarySystem().solution().clone();
     822             :     }
     823             : 
     824        8753 :     if (_fe_problem.verboseMultiApps())
     825           0 :       _console << "Restoring MultiApp ... ";
     826             : 
     827       17692 :     for (unsigned int i = 0; i < _my_num_apps; i++)
     828             :     {
     829        8939 :       _apps[i]->restore(std::move(_sub_app_backups[i]), false);
     830        8939 :       _sub_app_backups[i] = _apps[i]->finalizeRestore();
     831             :       mooseAssert(_sub_app_backups[i], "Should have a backup");
     832             :     }
     833             : 
     834        8753 :     if (_fe_problem.verboseMultiApps())
     835           0 :       _console << name() << std::endl;
     836             : 
     837             :     // Now copy the latest solutions back for each subapp
     838        8753 :     if (_keep_solution_during_restore)
     839             :     {
     840         642 :       for (unsigned int i = 0; i < _my_num_apps; i++)
     841             :       {
     842         652 :         for (unsigned int j = 0; j < _apps[i]->getExecutioner()->feProblem().numSolverSystems();
     843             :              j++)
     844             :         {
     845         331 :           _apps[i]->getExecutioner()->feProblem().getSolverSystem(/*solver_sys=*/j).solution() =
     846         662 :               *_end_solutions[i][j];
     847             : 
     848             :           // We need to synchronize solution so that local_solution has the right values
     849         331 :           _apps[i]->getExecutioner()->feProblem().getSolverSystem(/*solver_sys=*/j).update();
     850             :         }
     851             :       }
     852             : 
     853         321 :       _end_solutions.clear();
     854             :     }
     855             :     // Now copy the latest auxiliary solutions back for each subapp
     856        8753 :     if (_keep_aux_solution_during_restore)
     857             :     {
     858         240 :       for (unsigned int i = 0; i < _my_num_apps; i++)
     859             :       {
     860         120 :         _apps[i]->getExecutioner()->feProblem().getAuxiliarySystem().solution() =
     861         240 :             *_end_aux_solutions[i];
     862             : 
     863             :         // We need to synchronize solution so that local_solution has the right values
     864         120 :         _apps[i]->getExecutioner()->feProblem().getAuxiliarySystem().update();
     865             :       }
     866             : 
     867         120 :       _end_aux_solutions.clear();
     868             :     }
     869             : 
     870             :     // Make sure the displaced mesh on the multiapp is up-to-date with displacement variables
     871       17692 :     for (const auto & app_ptr : _apps)
     872        8939 :       if (app_ptr->feProblem().getDisplacedProblem())
     873          48 :         app_ptr->feProblem().getDisplacedProblem()->updateMesh();
     874             : 
     875             :     // If we are restoring due to a failed solve, make sure reset the solved state in the sub-apps
     876        8753 :     if (!getMooseApp().getExecutioner()->lastSolveConverged())
     877        1430 :       for (auto & app_ptr : _apps)
     878         802 :         app_ptr->getExecutioner()->fixedPointSolve().clearFixedPointStatus();
     879             :   }
     880             :   else
     881             :   {
     882       68888 :     for (unsigned int i = 0; i < _my_num_apps; i++)
     883             :     {
     884       34444 :       for (auto & sub_app :
     885       69320 :            _apps[i]->getExecutioner()->feProblem().getMultiAppWarehouse().getObjects())
     886         432 :         sub_app->restore(false);
     887             :     }
     888             :   }
     889       43777 : }
     890             : 
     891             : void
     892          13 : MultiApp::keepSolutionDuringRestore(bool keep_solution_during_restore)
     893             : {
     894          39 :   if (_pars.isParamSetByUser("keep_solution_during_restore"))
     895           6 :     paramError("keep_solution_during_restore",
     896             :                "This parameter should only be provided in parent app");
     897             : 
     898          10 :   _keep_solution_during_restore = keep_solution_during_restore;
     899          10 : }
     900             : 
     901             : void
     902      376520 : MultiApp::transformBoundingBox(BoundingBox & box, const MultiAppCoordTransform & transform)
     903             : {
     904      376520 :   const Real min_x = box.first(0);
     905      376520 :   const Real max_x = box.second(0);
     906      376520 :   const Real min_y = box.first(1);
     907      376520 :   const Real max_y = box.second(1);
     908      376520 :   const Real min_z = box.first(2);
     909      376520 :   const Real max_z = box.second(2);
     910             : 
     911             :   std::array<Point, 8> box_corners = {{Point(min_x, min_y, min_z),
     912             :                                        Point(max_x, min_y, min_z),
     913             :                                        Point(min_x, max_y, min_z),
     914             :                                        Point(max_x, max_y, min_z),
     915             :                                        Point(min_x, min_y, max_z),
     916             :                                        Point(max_x, min_y, max_z),
     917             :                                        Point(min_x, max_y, max_z),
     918      376520 :                                        Point(max_x, max_y, max_z)}};
     919             : 
     920             :   // transform each corner
     921     3388680 :   for (auto & corner : box_corners)
     922     3012160 :     corner = transform(corner);
     923             : 
     924             :   // Create new bounding box
     925      376520 :   Point new_box_min = box_corners[0];
     926      376520 :   Point new_box_max = new_box_min;
     927     3012160 :   for (const auto p : make_range(1, 8))
     928    10542560 :     for (const auto d : make_range(Moose::dim))
     929             :     {
     930     7906920 :       const Point & pt = box_corners[p];
     931     7906920 :       if (new_box_min(d) > pt(d))
     932        2292 :         new_box_min(d) = pt(d);
     933             : 
     934     7906920 :       if (new_box_max(d) < pt(d))
     935     1065095 :         new_box_max(d) = pt(d);
     936             :     }
     937      376520 :   box.first = new_box_min;
     938      376520 :   box.second = new_box_max;
     939      376520 : }
     940             : 
     941             : BoundingBox
     942      318895 : MultiApp::getBoundingBox(unsigned int app,
     943             :                          bool displaced_mesh,
     944             :                          const MultiAppCoordTransform * const coord_transform)
     945             : {
     946      318895 :   if (!_has_an_app)
     947           0 :     mooseError("No app for ", name(), " on processor ", _orig_rank);
     948             : 
     949      318895 :   unsigned int local_app = globalAppToLocal(app);
     950      318895 :   FEProblemBase & fe_problem_base = _apps[local_app]->getExecutioner()->feProblem();
     951        8256 :   MooseMesh & mesh = (displaced_mesh && fe_problem_base.getDisplacedProblem().get() != NULL)
     952      641153 :                          ? fe_problem_base.getDisplacedProblem()->mesh()
     953      318895 :                          : fe_problem_base.mesh();
     954             : 
     955             :   {
     956      318895 :     Moose::ScopedCommSwapper swapper(_my_comm);
     957      318895 :     if (displaced_mesh)
     958        4128 :       _bounding_box[local_app] = MeshTools::create_bounding_box(mesh);
     959             :     else
     960             :     {
     961      314767 :       if (!_has_bounding_box[local_app])
     962             :       {
     963          92 :         _bounding_box[local_app] = MeshTools::create_bounding_box(mesh);
     964          92 :         _has_bounding_box[local_app] = true;
     965             :       }
     966             :     }
     967      318895 :   }
     968      318895 :   BoundingBox bbox = _bounding_box[local_app];
     969             : 
     970      318895 :   Point min = bbox.min();
     971      318895 :   min -= _bounding_box_padding;
     972      318895 :   Point max = bbox.max();
     973      318895 :   max += _bounding_box_padding;
     974             : 
     975      318895 :   Point inflation_amount = (max - min) * _inflation;
     976             : 
     977      318895 :   Point inflated_min = min - inflation_amount;
     978      318895 :   Point inflated_max = max + inflation_amount;
     979             : 
     980      318895 :   Point shifted_min = inflated_min;
     981      318895 :   Point shifted_max = inflated_max;
     982             : 
     983      637790 :   if ((!coord_transform || coord_transform->skipCoordinateCollapsing()) &&
     984      637790 :       fe_problem_base.getCoordSystem(*(mesh.meshSubdomains().begin())) == Moose::COORD_RZ)
     985             :   {
     986             :     // If the problem is RZ then we're going to invent a box that would cover the whole "3D" app
     987             :     // FIXME: Assuming all subdomains are the same coordinate system type!
     988      308088 :     shifted_min(0) = -inflated_max(0);
     989      308088 :     shifted_min(1) = inflated_min(1);
     990      308088 :     shifted_min(2) = -inflated_max(0);
     991             : 
     992      308088 :     shifted_max(0) = inflated_max(0);
     993      308088 :     shifted_max(1) = inflated_max(1);
     994      308088 :     shifted_max(2) = inflated_max(0);
     995             :   }
     996             : 
     997      318895 :   if (coord_transform)
     998             :   {
     999      318895 :     BoundingBox transformed_bbox(shifted_min, shifted_max);
    1000      318895 :     transformBoundingBox(transformed_bbox, *coord_transform);
    1001      318895 :     return transformed_bbox;
    1002             :   }
    1003             :   else
    1004             :   {
    1005             :     // This is where the app is located.  We need to shift by this amount.
    1006           0 :     Point p = position(app);
    1007             : 
    1008             :     // Shift them to the position they're supposed to be
    1009           0 :     shifted_min += p;
    1010           0 :     shifted_max += p;
    1011           0 :     return BoundingBox(shifted_min, shifted_max);
    1012             :   }
    1013             : }
    1014             : 
    1015             : FEProblemBase &
    1016      485331 : MultiApp::appProblemBase(unsigned int app)
    1017             : {
    1018      485331 :   if (!_has_an_app)
    1019           0 :     mooseError("No app for ", name(), " on processor ", _orig_rank);
    1020             : 
    1021      485331 :   unsigned int local_app = globalAppToLocal(app);
    1022             : 
    1023      485331 :   return _apps[local_app]->getExecutioner()->feProblem();
    1024             : }
    1025             : 
    1026             : FEProblem &
    1027           0 : MultiApp::appProblem(unsigned int app)
    1028             : {
    1029           0 :   mooseDeprecated(
    1030             :       "MultiApp::appProblem() is deprecated, call MultiApp::appProblemBase() instead.\n");
    1031           0 :   if (!_has_an_app)
    1032           0 :     mooseError("No app for ", name(), " on processor ", _orig_rank);
    1033             : 
    1034           0 :   unsigned int local_app = globalAppToLocal(app);
    1035             : 
    1036           0 :   return dynamic_cast<FEProblem &>(_apps[local_app]->getExecutioner()->feProblem());
    1037             : }
    1038             : 
    1039             : const UserObject &
    1040      167775 : MultiApp::appUserObjectBase(unsigned int app, const std::string & name)
    1041             : {
    1042      167775 :   if (!_has_an_app)
    1043           0 :     mooseError("No app for ", MultiApp::name(), " on processor ", _orig_rank);
    1044             : 
    1045      167775 :   return appProblemBase(app).getUserObjectBase(name);
    1046             : }
    1047             : 
    1048             : Real
    1049        3700 : MultiApp::appPostprocessorValue(unsigned int app, const std::string & name)
    1050             : {
    1051        3700 :   if (!_has_an_app)
    1052           0 :     mooseError("No app for ", MultiApp::name(), " on processor ", _orig_rank);
    1053             : 
    1054        3700 :   return appProblemBase(app).getPostprocessorValueByName(name);
    1055             : }
    1056             : 
    1057             : NumericVector<Number> &
    1058         849 : MultiApp::appTransferVector(unsigned int app, std::string var_name)
    1059             : {
    1060         849 :   return *(appProblemBase(app).getSystem(var_name).solution);
    1061             : }
    1062             : 
    1063             : bool
    1064         136 : MultiApp::isFirstLocalRank() const
    1065             : {
    1066         136 :   return _rank_config.is_first_local_rank;
    1067             : }
    1068             : 
    1069             : bool
    1070      814258 : MultiApp::hasLocalApp(unsigned int global_app) const
    1071             : {
    1072      814258 :   if (_has_an_app && global_app >= _first_local_app &&
    1073      726817 :       global_app <= _first_local_app + (_my_num_apps - 1))
    1074      641514 :     return true;
    1075             : 
    1076      172744 :   return false;
    1077             : }
    1078             : 
    1079             : MooseApp *
    1080       11521 : MultiApp::localApp(unsigned int local_app)
    1081             : {
    1082             :   mooseAssert(local_app < _apps.size(), "Index out of range: " + Moose::stringify(local_app));
    1083       11521 :   return _apps[local_app].get();
    1084             : }
    1085             : 
    1086             : void
    1087          69 : MultiApp::resetApp(unsigned int global_app, Real time)
    1088             : {
    1089          69 :   TIME_SECTION(_reset_timer);
    1090             : 
    1091          69 :   Moose::ScopedCommSwapper swapper(_my_comm);
    1092             : 
    1093          69 :   if (hasLocalApp(global_app))
    1094             :   {
    1095          69 :     unsigned int local_app = globalAppToLocal(global_app);
    1096             : 
    1097             :     // Extract the file numbers from the output, so that the numbering is maintained after reset
    1098          69 :     std::map<std::string, unsigned int> m = _apps[local_app]->getOutputWarehouse().getFileNumbers();
    1099             : 
    1100          69 :     createApp(local_app, time);
    1101             : 
    1102             :     // Reset the file numbers of the newly reset apps
    1103          69 :     _apps[local_app]->getOutputWarehouse().setFileNumbers(m);
    1104          69 :   }
    1105          69 : }
    1106             : 
    1107             : void
    1108          54 : MultiApp::moveApp(unsigned int global_app, Point p)
    1109             : {
    1110          54 :   if (_use_positions)
    1111             :   {
    1112          54 :     _positions[global_app] = p;
    1113             : 
    1114          54 :     if (hasLocalApp(global_app))
    1115             :     {
    1116          54 :       unsigned int local_app = globalAppToLocal(global_app);
    1117             : 
    1118          54 :       if (_output_in_position)
    1119          51 :         _apps[local_app]->setOutputPosition(p);
    1120          54 :       if (_run_in_position)
    1121           6 :         paramError("run_in_position", "Moving apps and running apps in position is not supported");
    1122             :     }
    1123             :   }
    1124          51 : }
    1125             : 
    1126             : void
    1127          19 : MultiApp::parentOutputPositionChanged()
    1128             : {
    1129          19 :   if (_use_positions && _output_in_position)
    1130          38 :     for (unsigned int i = 0; i < _apps.size(); i++)
    1131          19 :       _apps[i]->setOutputPosition(_app.getOutputPosition() + _positions[_first_local_app + i]);
    1132          19 : }
    1133             : 
    1134             : void
    1135       12203 : MultiApp::createApp(unsigned int i, Real start_time)
    1136             : {
    1137             :   // Delete the old app if we're resetting
    1138       12203 :   if (_apps[i])
    1139          69 :     _apps[i].reset();
    1140             : 
    1141             :   // Define the app name
    1142       12203 :   const std::string multiapp_name = getMultiAppName(name(), _first_local_app + i, _total_num_apps);
    1143       12203 :   std::string full_name;
    1144             : 
    1145             :   // Only add parent name if the parent is not the main app
    1146       12203 :   if (_app.multiAppLevel() > 0)
    1147         504 :     full_name = _app.name() + "_" + multiapp_name;
    1148             :   else
    1149       11699 :     full_name = multiapp_name;
    1150             : 
    1151       12203 :   InputParameters app_params = AppFactory::instance().getValidParams(_app_type);
    1152       24406 :   app_params.set<FEProblemBase *>("_parent_fep") = &_fe_problem;
    1153       24406 :   app_params.set<std::unique_ptr<Backup> *>("_initial_backup") = &_sub_app_backups[i];
    1154             : 
    1155             :   // Build the CommandLine with the relevant options for this subapp and add the
    1156             :   // cli args from the input file
    1157       12203 :   std::vector<std::string> input_cli_args;
    1158       12203 :   if (cliArgs().size() > 0 || _cli_args_from_file.size() > 0)
    1159        3177 :     input_cli_args = getCommandLineArgs(i);
    1160             :   // FullSolveMultiApp (and its derived classes) always performs a complete fresh
    1161             :   // solve on every execution, so the parent's recovery-related global CLI params
    1162             :   // must not be propagated to those sub-apps.
    1163             :   const std::set<std::string> recover_exclude =
    1164       12203 :       propagateRecoverToSubApps()
    1165       12203 :           ? std::set<std::string>{}
    1166       29019 :           : std::set<std::string>{"recover", "test_checkpoint_half_transient"};
    1167             :   // This will mark all hit CLI command line parameters that are passed to subapps
    1168             :   // as used within the parent app (_app)
    1169       12203 :   auto app_cli = _app.commandLine()->initSubAppCommandLine(
    1170       12203 :       name(), multiapp_name, input_cli_args, recover_exclude);
    1171       12203 :   app_cli->parse();
    1172             : 
    1173       12203 :   if (_fe_problem.verboseMultiApps())
    1174         849 :     _console << COLOR_CYAN << "Creating MultiApp " << name() << " of type " << _app_type
    1175         849 :              << " of level " << _app.multiAppLevel() + 1 << " and number " << _first_local_app + i
    1176         849 :              << " on processor " << processor_id() << " with full name " << full_name
    1177         849 :              << COLOR_DEFAULT << std::endl;
    1178       24406 :   app_params.set<unsigned int>("_multiapp_level") = _app.multiAppLevel() + 1;
    1179       24406 :   app_params.set<unsigned int>("_multiapp_number") = _first_local_app + i;
    1180       24406 :   app_params.set<const MooseMesh *>("_master_mesh") = &_fe_problem.mesh();
    1181             : #ifdef MOOSE_MFEM_ENABLED
    1182             :   // MFEM device must only be set once across all apps
    1183             :   // FIXME: this required that the base app is an MFEM app; itwill
    1184             :   // still fail if multiple MFEM sub-apps are launched from a libMesh base app
    1185        9417 :   if (i == 0)
    1186             :   {
    1187       12404 :     app_params.set<std::shared_ptr<mfem::Device>>("_mfem_device") =
    1188       18606 :         _app.getMFEMDevice(Moose::PassKey<MultiApp>());
    1189        6202 :     const auto & mfem_device_set = _app.getMFEMDevices(Moose::PassKey<MultiApp>());
    1190       12404 :     app_params.set<std::set<std::string>>("_mfem_devices") = mfem_device_set;
    1191             :   }
    1192             :   else
    1193             :   {
    1194        6430 :     app_params.set<std::shared_ptr<mfem::Device>>("_mfem_device") =
    1195        9645 :         _apps[0]->getMFEMDevice(Moose::PassKey<MultiApp>());
    1196        3215 :     const auto & mfem_device_set = _apps[0]->getMFEMDevices(Moose::PassKey<MultiApp>());
    1197        6430 :     app_params.set<std::set<std::string>>("_mfem_devices") = mfem_device_set;
    1198             :   }
    1199             : #endif
    1200       61015 :   if (getParam<bool>("clone_master_mesh") || getParam<bool>("clone_parent_mesh"))
    1201             :   {
    1202         781 :     if (_fe_problem.verboseMultiApps())
    1203           0 :       _console << COLOR_CYAN << "Cloned parent app mesh will be used for MultiApp " << name()
    1204           0 :                << COLOR_DEFAULT << std::endl;
    1205         781 :     app_params.set<bool>("_use_master_mesh") = true;
    1206         781 :     auto displaced_problem = _fe_problem.getDisplacedProblem();
    1207         781 :     if (displaced_problem)
    1208           0 :       app_params.set<const MooseMesh *>("_master_displaced_mesh") = &displaced_problem->mesh();
    1209         781 :   }
    1210             : 
    1211             :   // If only one input file was provided, use it for all the solves
    1212       12203 :   const auto input_index = _input_files.size() == 1 ? 0 : _first_local_app + i;
    1213       12203 :   const auto & input_file = _input_files[input_index];
    1214             : 
    1215             :   // create new parser tree for the application and parse
    1216       12203 :   auto parser = std::make_unique<Parser>(input_file);
    1217       12203 :   parser->setCommandLineParams(app_cli->buildHitParams());
    1218       12203 :   parser->parse();
    1219             : 
    1220             :   // Checks on app type
    1221       12203 :   const auto & app_type = parser->getAppType();
    1222       12203 :   if (app_type.empty() && _app_type.empty())
    1223           0 :     mooseWarning("The application type is not specified for ",
    1224             :                  full_name,
    1225             :                  ". Please use [Application] block to specify the application type.");
    1226       12203 :   if (!app_type.empty() && app_type != _app_type && !AppFactory::instance().isRegistered(app_type))
    1227           3 :     mooseError("In the ",
    1228             :                full_name,
    1229             :                ", '",
    1230             :                app_type,
    1231             :                "' is not a registered application. The registered application is named: '",
    1232           3 :                _app_type,
    1233             :                "'. Please double check the [Application] block to make sure the correct "
    1234             :                "application is provided. \n");
    1235             : 
    1236       12200 :   if (parser->getAppType().empty())
    1237       12188 :     parser->setAppType(_app_type);
    1238             : 
    1239       24400 :   app_params.set<std::shared_ptr<Parser>>("_parser") = std::move(parser);
    1240       24400 :   app_params.set<std::shared_ptr<CommandLine>>("_command_line") = std::move(app_cli);
    1241       12200 :   _apps[i] = AppFactory::instance().create(_app_type, full_name, app_params, _my_comm);
    1242       12200 :   auto & app = _apps[i];
    1243             : 
    1244       12200 :   app->setGlobalTimeOffset(start_time);
    1245       12200 :   app->setOutputFileNumbers(_app.getOutputWarehouse().getFileNumbers());
    1246       12200 :   app->setRestart(_app.isRestarting());
    1247       12200 :   app->setRecover(propagateRecoverToSubApps() && _app.isRecovering());
    1248             : 
    1249       36600 :   if (_use_positions && getParam<bool>("output_in_position"))
    1250        3359 :     app->setOutputPosition(_app.getOutputPosition() + _positions[_first_local_app + i]);
    1251       12200 :   if (_output_in_position && _run_in_position)
    1252           6 :     paramError("run_in_position",
    1253             :                "Sub-apps are already displaced, so they are already output in position");
    1254             : 
    1255             :   // Update the MultiApp level for the app that was just created
    1256       12197 :   app->setupOptions();
    1257             :   // if multiapp does not have file base in Outputs input block, output file base will
    1258             :   // be empty here since setupOptions() does not set the default file base with the multiapp
    1259             :   // input file name. Parent app will create the default file base for multiapp by taking the
    1260             :   // output base of the parent app problem and appending the name of the multiapp plus a number to
    1261             :   // it
    1262       12197 :   if (app->getOutputFileBase().empty())
    1263       12161 :     setAppOutputFileBase(i);
    1264       12197 :   preRunInputFile();
    1265       36591 :   if (_app.multiAppLevel() > getParam<unsigned int>("max_multiapp_level"))
    1266           3 :     mooseError("Maximum multiapp level has been reached. This is likely caused by an infinite loop "
    1267             :                "in your multiapp system. If additional multiapp levels are needed, "
    1268             :                "max_multiapp_level can be specified in the MuliApps block.");
    1269             : 
    1270             :   // Transfer coupling relaxation information to the subapps
    1271       24388 :   _apps[i]->fixedPointConfig().sub_relaxation_factor = getParam<Real>("relaxation_factor");
    1272       12194 :   _apps[i]->fixedPointConfig().sub_transformed_vars =
    1273       36582 :       getParam<std::vector<std::string>>("transformed_variables");
    1274             :   // Handle deprecated parameter
    1275       36582 :   if (!parameters().isParamSetByAddParam("relaxed_variables"))
    1276           0 :     _apps[i]->fixedPointConfig().sub_transformed_vars =
    1277           0 :         getParam<std::vector<std::string>>("relaxed_variables");
    1278       12194 :   _apps[i]->fixedPointConfig().sub_transformed_pps =
    1279       36582 :       getParam<std::vector<PostprocessorName>>("transformed_postprocessors");
    1280             : 
    1281       12194 :   app->runInputFile();
    1282       12158 :   auto fixed_point_solve = &(_apps[i]->getExecutioner()->fixedPointSolve());
    1283       12158 :   if (fixed_point_solve)
    1284       12158 :     fixed_point_solve->allocateStorage(false);
    1285             : 
    1286             :   // Transform the app mesh if requested
    1287       12158 :   if (_run_in_position)
    1288             :   {
    1289         246 :     if (usingPositions())
    1290         492 :       app->getExecutioner()->feProblem().coordTransform().transformMesh(
    1291         246 :           app->getExecutioner()->feProblem().mesh(), _positions[_first_local_app + i]);
    1292             :     else
    1293           0 :       app->getExecutioner()->feProblem().coordTransform().transformMesh(
    1294           0 :           app->getExecutioner()->feProblem().mesh(), Point(0, 0, 0));
    1295             :   }
    1296       36970 : }
    1297             : 
    1298             : std::vector<std::string>
    1299        3177 : MultiApp::getCommandLineArgs(const unsigned int local_app)
    1300             : {
    1301        3177 :   const auto cla = cliArgs();
    1302        3177 :   auto cli_args_param = _cli_args_param;
    1303        3177 :   std::string combined_args;
    1304             : 
    1305             :   // Single set of args from cliArgs() to be provided to all apps
    1306        3177 :   if (cla.size() == 1)
    1307        2383 :     combined_args = cla[0];
    1308             :   // Single "cli_args_files" file to be provided to all apps
    1309         794 :   else if (_cli_args_from_file.size() == 1)
    1310             :   {
    1311           0 :     cli_args_param = "cli_args_files";
    1312           0 :     combined_args = _cli_args_from_file[0];
    1313             :   }
    1314             :   // Unique set of args from cliArgs() to be provided to each app
    1315         794 :   else if (cla.size())
    1316         686 :     combined_args = cla[local_app + _first_local_app];
    1317             :   // Unique set of args from "cli_args_files" to be provided to all apps
    1318             :   else
    1319             :   {
    1320         108 :     cli_args_param = "cli_args_files";
    1321         108 :     combined_args = _cli_args_from_file[local_app + _first_local_app];
    1322             :   }
    1323             : 
    1324             :   // Remove all of the beginning and end whitespace so we can recognize truly empty
    1325        3177 :   combined_args = MooseUtils::trim(combined_args);
    1326             : 
    1327             :   // MooseUtils::split will return a single empty entry if there is nothing,
    1328             :   // so exit early if we have nothing
    1329        3177 :   if (combined_args.empty())
    1330           0 :     return {};
    1331             : 
    1332             :   // Split the argument into a vector of arguments, and make sure
    1333             :   // that we don't have any empty arguments
    1334        6354 :   const auto args = MooseUtils::split(combined_args, ";");
    1335       10545 :   for (const auto & arg : args)
    1336             :   {
    1337        7368 :     if (arg.empty())
    1338             :     {
    1339           0 :       const auto error = "An empty MultiApp command line argument was provided. Your "
    1340             :                          "combined command line string has a ';' with no argument after it.";
    1341           0 :       if (cli_args_param)
    1342           0 :         paramError(*cli_args_param, error);
    1343             :       else
    1344           0 :         mooseError(error);
    1345             :     }
    1346             :   }
    1347             : 
    1348        3177 :   return args;
    1349        3177 : }
    1350             : 
    1351             : LocalRankConfig
    1352        8950 : rankConfig(processor_id_type rank,
    1353             :            processor_id_type nprocs,
    1354             :            dof_id_type napps,
    1355             :            processor_id_type min_app_procs,
    1356             :            processor_id_type max_app_procs,
    1357             :            bool batch_mode)
    1358             : {
    1359        8950 :   if (min_app_procs > nprocs)
    1360           0 :     mooseError("minimum number of procs per app is higher than the available number of procs");
    1361        8950 :   else if (min_app_procs > max_app_procs)
    1362           0 :     mooseError("minimum number of procs per app must be lower than the max procs per app");
    1363             : 
    1364             :   mooseAssert(rank < nprocs, "rank must be smaller than the number of procs");
    1365             : 
    1366             :   // A "slot" is a group of procs/ranks that are grouped together to run a
    1367             :   // single (sub)app/sim in parallel.
    1368             : 
    1369             :   const processor_id_type slot_size =
    1370        8950 :       std::max(std::min(cast_int<processor_id_type>(nprocs / napps), max_app_procs), min_app_procs);
    1371        8950 :   const processor_id_type nslots = std::min(
    1372       17900 :       nprocs / slot_size,
    1373        8950 :       cast_int<processor_id_type>(std::min(
    1374        8950 :           static_cast<dof_id_type>(std::numeric_limits<processor_id_type>::max()), napps)));
    1375             :   mooseAssert(nprocs >= (nslots * slot_size),
    1376             :               "Ensure that leftover procs is represented by an unsigned type");
    1377        8950 :   const processor_id_type leftover_procs = nprocs - nslots * slot_size;
    1378        8950 :   const dof_id_type apps_per_slot = napps / nslots;
    1379        8950 :   const dof_id_type leftover_apps = napps % nslots;
    1380             : 
    1381        8950 :   std::vector<int> slot_for_rank(nprocs);
    1382        8950 :   processor_id_type slot = 0;
    1383        8950 :   processor_id_type procs_in_slot = 0;
    1384       22483 :   for (processor_id_type rankiter = 0; rankiter <= rank; rankiter++)
    1385             :   {
    1386       13533 :     if (slot < nslots)
    1387       13313 :       slot_for_rank[rankiter] = cast_int<int>(slot);
    1388             :     else
    1389         220 :       slot_for_rank[rankiter] = -1;
    1390       13533 :     procs_in_slot++;
    1391             :     // this slot keeps growing until we reach slot size plus possibly an extra
    1392             :     // proc if there were any leftover from the slotization of nprocs - this
    1393             :     // must also make sure we don't go over max app procs.
    1394       13533 :     if (procs_in_slot == slot_size + 1 * (slot < leftover_procs && slot_size < max_app_procs))
    1395             :     {
    1396        9289 :       procs_in_slot = 0;
    1397        9289 :       slot++;
    1398             :     }
    1399             :   }
    1400             : 
    1401        8950 :   if (slot_for_rank[rank] < 0)
    1402             :     // ranks assigned a negative slot don't have any apps running on them.
    1403          88 :     return {0, 0, 0, 0, false, 0};
    1404        8862 :   const processor_id_type slot_num = cast_int<processor_id_type>(slot_for_rank[rank]);
    1405             : 
    1406        8862 :   const bool is_first_local_rank = rank == 0 || (slot_for_rank[rank - 1] != slot_for_rank[rank]);
    1407        8862 :   const dof_id_type n_local_apps = apps_per_slot + 1 * (slot_num < leftover_apps);
    1408             : 
    1409        8862 :   processor_id_type my_first_rank = 0;
    1410       11782 :   for (processor_id_type rankiter = rank; rankiter > 0; rankiter--)
    1411        3238 :     if (slot_for_rank[rank] != slot_for_rank[rankiter])
    1412             :     {
    1413         318 :       my_first_rank = cast_int<processor_id_type>(slot_for_rank[rankiter + 1]);
    1414         318 :       break;
    1415             :     }
    1416             : 
    1417        8862 :   dof_id_type app_index = 0;
    1418       10485 :   for (processor_id_type slot = 0; slot < slot_num; slot++)
    1419             :   {
    1420        1623 :     const dof_id_type num_slot_apps = apps_per_slot + 1 * (slot < leftover_apps);
    1421        1623 :     app_index += num_slot_apps;
    1422             :   }
    1423             : 
    1424        8862 :   if (batch_mode)
    1425         347 :     return {n_local_apps, app_index, 1, slot_num, is_first_local_rank, my_first_rank};
    1426        8515 :   return {n_local_apps, app_index, n_local_apps, app_index, is_first_local_rank, my_first_rank};
    1427        8950 : }
    1428             : 
    1429             : void
    1430        8102 : MultiApp::buildComm()
    1431             : {
    1432             :   int ierr;
    1433             : 
    1434        8102 :   ierr = MPI_Comm_size(_communicator.get(), &_orig_num_procs);
    1435        8102 :   mooseCheckMPIErr(ierr);
    1436        8102 :   ierr = MPI_Comm_rank(_communicator.get(), &_orig_rank);
    1437        8102 :   mooseCheckMPIErr(ierr);
    1438             : 
    1439             : #ifdef LIBMESH_HAVE_SYS_UTSNAME_H
    1440             :   struct utsname sysInfo;
    1441        8102 :   uname(&sysInfo);
    1442        8102 :   _node_name = sysInfo.nodename;
    1443             : #else
    1444             :   _node_name = "Unknown";
    1445             : #endif
    1446             : 
    1447             :   int rank;
    1448        8102 :   ierr = MPI_Comm_rank(_communicator.get(), &rank);
    1449        8102 :   mooseCheckMPIErr(ierr);
    1450             : 
    1451        8102 :   _my_num_apps = _rank_config.num_local_apps;
    1452        8102 :   _first_local_app = _rank_config.first_local_app_index;
    1453             : 
    1454        8102 :   _has_an_app = _rank_config.num_local_apps > 0;
    1455        8102 :   if (_rank_config.first_local_app_index >= _total_num_apps)
    1456           0 :     mooseError("Internal error, a processor has an undefined app.");
    1457             : 
    1458        8102 :   if (_has_an_app)
    1459             :   {
    1460        8050 :     _communicator.split(_rank_config.first_local_app_index, rank, _my_communicator);
    1461        8050 :     ierr = MPI_Comm_rank(_my_comm, &_my_rank);
    1462        8050 :     mooseCheckMPIErr(ierr);
    1463             :   }
    1464             :   else
    1465             :   {
    1466          52 :     _communicator.split(MPI_UNDEFINED, rank, _my_communicator);
    1467          52 :     _my_rank = 0;
    1468             :   }
    1469        8102 : }
    1470             : 
    1471             : unsigned int
    1472      804425 : MultiApp::globalAppToLocal(unsigned int global_app)
    1473             : {
    1474      804425 :   if (global_app >= _first_local_app && global_app <= _first_local_app + (_my_num_apps - 1))
    1475      804425 :     return global_app - _first_local_app;
    1476             : 
    1477           0 :   std::stringstream ss;
    1478           0 :   ss << "Requesting app " << global_app << ", but processor " << processor_id() << " ";
    1479           0 :   if (_my_num_apps == 0)
    1480           0 :     ss << "does not own any apps";
    1481           0 :   else if (_my_num_apps == 1)
    1482           0 :     ss << "owns app " << _first_local_app;
    1483             :   else
    1484           0 :     ss << "owns apps " << _first_local_app << "-" << _first_local_app + (_my_num_apps - 1);
    1485           0 :   ss << ".";
    1486           0 :   mooseError("Invalid global_app!\n", ss.str());
    1487             :   return 0;
    1488           0 : }
    1489             : 
    1490             : void
    1491       12197 : MultiApp::preRunInputFile()
    1492             : {
    1493       12197 : }
    1494             : 
    1495             : void
    1496       14383 : MultiApp::addAssociatedTransfer(MultiAppTransfer & transfer)
    1497             : {
    1498       14383 :   _associated_transfers.push_back(&transfer);
    1499       14383 : }
    1500             : 
    1501             : void
    1502           0 : MultiApp::setAppOutputFileBase()
    1503             : {
    1504           0 :   for (unsigned int i = 0; i < _my_num_apps; ++i)
    1505           0 :     setAppOutputFileBase(i);
    1506           0 : }
    1507             : 
    1508             : std::vector<std::string>
    1509       23479 : MultiApp::cliArgs() const
    1510             : {
    1511             :   // So that we can error out with paramError("cli_args", ...);
    1512       23479 :   _cli_args_param = "cli_args";
    1513       46958 :   return std::vector<std::string>(_cli_args.begin(), _cli_args.end());
    1514             : }
    1515             : 
    1516             : void
    1517       12161 : MultiApp::setAppOutputFileBase(unsigned int index)
    1518             : {
    1519             :   const std::string multiapp_name =
    1520       12161 :       getMultiAppName(name(), _first_local_app + index, _total_num_apps);
    1521       12161 :   _apps[index]->setOutputFileBase(_app.getOutputFileBase() + "_" + multiapp_name);
    1522       12161 : }
    1523             : 
    1524             : std::string
    1525       24364 : MultiApp::getMultiAppName(const std::string & base_name, dof_id_type index, dof_id_type total)
    1526             : {
    1527       24364 :   std::ostringstream multiapp_name;
    1528       48728 :   multiapp_name << base_name << std::setw(std::ceil(std::log10(total))) << std::setprecision(0)
    1529       24364 :                 << std::setfill('0') << std::right << index;
    1530       48728 :   return multiapp_name.str();
    1531       24364 : }
    1532             : 
    1533             : const Point &
    1534      199558 : MultiApp::position(unsigned int app) const
    1535             : {
    1536             :   // If we're not using positions, it won't have changed
    1537      199558 :   if (_positions_objs.empty())
    1538      184181 :     return _positions[app];
    1539             :   else
    1540             :     // Find which Positions object is specifying it, and query a potentially updated value
    1541       15377 :     return _positions_objs[app]->getPosition(app - _positions_index_offsets[app], false);
    1542             : }
    1543             : 
    1544             : void
    1545        2742 : dataStore(std::ostream & stream, SubAppBackups & backups, void * context)
    1546             : {
    1547        2742 :   MultiApp * multi_app = static_cast<MultiApp *>(context);
    1548             :   mooseAssert(multi_app, "Not set");
    1549             : 
    1550        2742 :   multi_app->backup();
    1551             : 
    1552        2742 :   dataStore(stream, static_cast<std::vector<std::unique_ptr<Backup>> &>(backups), nullptr);
    1553        2742 : }
    1554             : 
    1555             : void
    1556         665 : dataLoad(std::istream & stream, SubAppBackups & backups, void * context)
    1557             : {
    1558         665 :   MultiApp * multi_app = static_cast<MultiApp *>(context);
    1559             :   mooseAssert(multi_app, "Not set");
    1560             : 
    1561         665 :   dataLoad(stream, static_cast<std::vector<std::unique_ptr<Backup>> &>(backups), nullptr);
    1562             : 
    1563         665 :   multi_app->restore();
    1564         665 : }

Generated by: LCOV version 1.14