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

Generated by: LCOV version 1.14