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

Generated by: LCOV version 1.14