LCOV - code coverage report
Current view: top level - src/base - MooseApp.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33416 (b10b36) with base 9fbd27 Lines: 1434 1658 86.5 %
Date: 2026-07-23 16:15:30 Functions: 107 118 90.7 %
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             : #ifdef HAVE_GPERFTOOLS
      11             : #include "gperftools/profiler.h"
      12             : #include "gperftools/heap-profiler.h"
      13             : #endif
      14             : 
      15             : // MOOSE includes
      16             : #include "MooseRevision.h"
      17             : #include "AppFactory.h"
      18             : #include "DisplacedProblem.h"
      19             : #include "NonlinearSystemBase.h"
      20             : #include "AuxiliarySystem.h"
      21             : #include "MooseSyntax.h"
      22             : #include "MooseInit.h"
      23             : #include "Executioner.h"
      24             : #include "Executor.h"
      25             : #include "PetscSupport.h"
      26             : #include "Conversion.h"
      27             : #include "CommandLine.h"
      28             : #include "InfixIterator.h"
      29             : #include "MultiApp.h"
      30             : #include "MooseUtils.h"
      31             : #include "MooseObjectAction.h"
      32             : #include "InputParameterWarehouse.h"
      33             : #include "SystemInfo.h"
      34             : #include "MooseMesh.h"
      35             : #include "FileOutput.h"
      36             : #include "ConsoleUtils.h"
      37             : #include "JsonSyntaxTree.h"
      38             : #include "JsonInputFileFormatter.h"
      39             : #include "RelationshipManager.h"
      40             : #include "ProxyRelationshipManager.h"
      41             : #include "Registry.h"
      42             : #include "SerializerGuard.h"
      43             : #include "PerfGraphInterface.h" // For TIME_SECTION
      44             : #include "SolutionInvalidInterface.h"
      45             : #include "Attributes.h"
      46             : #include "MooseApp.h"
      47             : #include "CommonOutputAction.h"
      48             : #include "CastUniquePointer.h"
      49             : #include "NullExecutor.h"
      50             : #include "ExecFlagRegistry.h"
      51             : #include "SolutionInvalidity.h"
      52             : #include "MooseServer.h"
      53             : #include "RestartableDataWriter.h"
      54             : #include "StringInputStream.h"
      55             : #include "MooseMain.h"
      56             : #include "FEProblemBase.h"
      57             : #include "Parser.h"
      58             : #include "CSGBase.h"
      59             : #include "Capabilities.h"
      60             : 
      61             : // Regular expression includes
      62             : #include "pcrecpp.h"
      63             : 
      64             : #include "libmesh/exodusII_io.h"
      65             : #include "libmesh/mesh_refinement.h"
      66             : #include "libmesh/string_to_enum.h"
      67             : #include "libmesh/checkpoint_io.h"
      68             : #include "libmesh/mesh_base.h"
      69             : #include "libmesh/petsc_solver_exception.h"
      70             : 
      71             : // System include for dynamic library methods
      72             : #ifdef LIBMESH_HAVE_DLOPEN
      73             : #include <dlfcn.h>
      74             : #include <sys/utsname.h> // utsname
      75             : #endif
      76             : 
      77             : #if __has_include(<torch/xpu.h>)
      78             : #include <torch/xpu.h>
      79             : #define MOOSE_HAVE_XPU 1
      80             : #endif
      81             : 
      82             : // C++ includes
      83             : #include <numeric> // std::accumulate
      84             : #include <atomic>
      85             : #include <fstream>
      86             : #include <iterator>
      87             : #include <sys/types.h>
      88             : #include <unistd.h>
      89             : #include <cstdlib> // for system()
      90             : #include <chrono>
      91             : #include <thread>
      92             : #include <filesystem>
      93             : 
      94             : using namespace libMesh;
      95             : 
      96             : namespace
      97             : {
      98             : /**
      99             :  * Return a temporary checkpoint path used to move mesh topology through CheckpointIO.
     100             :  *
     101             :  * @param app App whose output file base, communicator, and processor id determine temporary path
     102             :  *            ownership
     103             :  * @param purpose Short label included in the directory name, e.g. "backup" or "restore"
     104             :  * @param shared Whether all ranks in the app communicator should use one shared checkpoint
     105             :  *               directory. Backup performs one collective checkpoint write, then captures each
     106             :  *               checkpoint entry as a relative path and byte contents in the Backup object. Restore
     107             :  *               recreates that checkpoint layout once before calling CheckpointIO::read().
     108             :  */
     109             : std::filesystem::path
     110          10 : temporaryBackupMeshPath(const MooseApp & app, const std::string & purpose, const bool shared)
     111             : {
     112             :   static std::atomic<unsigned long> counter = 0;
     113             : 
     114          10 :   std::string dirname;
     115          10 :   if (!shared || app.processor_id() == 0)
     116             :   {
     117          10 :     const auto file_base = std::filesystem::path(app.getOutputFileBase()).filename().string();
     118          10 :     const auto dirname_base = (file_base.empty() ? "moose" : file_base) + "_" + purpose + "_mesh";
     119          10 :     const auto tmp_dir = std::filesystem::temp_directory_path();
     120          10 :     std::error_code err;
     121             : 
     122             :     do
     123             :     {
     124          10 :       dirname = dirname_base + "_" + std::to_string(counter++);
     125          10 :       if (!shared)
     126           0 :         dirname += "_" + std::to_string(app.processor_id());
     127             : 
     128          10 :       err.clear();
     129          10 :     } while (!std::filesystem::create_directory(tmp_dir / dirname, err) && !err);
     130             : 
     131          10 :     if (err)
     132           0 :       mooseError("Unable to create temporary mesh ",
     133             :                  purpose,
     134             :                  " directory ",
     135           0 :                  std::filesystem::absolute(tmp_dir / dirname),
     136             :                  ": ",
     137           0 :                  err.message());
     138          10 :   }
     139             : 
     140          10 :   if (shared)
     141          10 :     app.comm().broadcast(dirname);
     142             : 
     143          10 :   const auto root = std::filesystem::temp_directory_path() / dirname;
     144             : 
     145          20 :   return root / "mesh.cpr";
     146          10 : }
     147             : 
     148             : std::string
     149          16 : readBackupMeshFile(const std::filesystem::path & path)
     150             : {
     151          16 :   std::ifstream file(path, std::ios::in | std::ios::binary);
     152          16 :   if (!file.is_open())
     153           0 :     mooseError("Unable to open temporary mesh backup file ",
     154           0 :                std::filesystem::absolute(path),
     155             :                " for reading");
     156             : 
     157          32 :   return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
     158          16 : }
     159             : 
     160             : void
     161           4 : writeBackupMeshFile(const std::filesystem::path & path, const std::string & contents)
     162             : {
     163           4 :   std::error_code err;
     164           4 :   if (!std::filesystem::create_directories(path.parent_path(), err) && err)
     165           0 :     mooseError("Unable to create temporary mesh backup directory ",
     166           0 :                std::filesystem::absolute(path.parent_path()),
     167             :                ": ",
     168           0 :                err.message());
     169             : 
     170           4 :   std::ofstream file(path, std::ios::out | std::ios::binary);
     171           4 :   if (!file.is_open())
     172           0 :     mooseError("Unable to open temporary mesh backup file ",
     173           0 :                std::filesystem::absolute(path),
     174             :                " for writing");
     175             : 
     176           4 :   file.write(contents.data(), contents.size());
     177           4 : }
     178             : 
     179             : void
     180       43367 : packMeshBackup(const MooseApp & app, Backup & backup)
     181             : {
     182       43367 :   backup.mesh_files.clear();
     183             : 
     184       43367 :   if (!app.getExecutioner())
     185       43359 :     return;
     186             : 
     187       43367 :   if (!app.meshChangedForBackup())
     188       43359 :     return;
     189             : 
     190           8 :   const auto mesh_path = temporaryBackupMeshPath(app, "backup", true);
     191             :   {
     192           8 :     CheckpointIO io(app.feProblem().mesh().getMesh(), false);
     193           8 :     io.write(mesh_path.string());
     194           8 :   }
     195             : 
     196             :   // CheckpointIO::write() is collective; wait until all ranks have finished writing split files
     197             :   // before each rank packs the shared checkpoint tree into its Backup.
     198           8 :   app.comm().barrier();
     199             : 
     200          32 :   for (const auto & entry : std::filesystem::recursive_directory_iterator(mesh_path))
     201          24 :     if (entry.is_regular_file())
     202             :     {
     203             :       const auto relative_path =
     204          16 :           std::filesystem::relative(entry.path(), mesh_path).generic_string();
     205          16 :       backup.mesh_files.emplace_back(relative_path, readBackupMeshFile(entry.path()));
     206          24 :     }
     207             : 
     208             :   // Keep the shared checkpoint tree alive until all ranks have finished reading from it.
     209           8 :   app.comm().barrier();
     210             : 
     211           8 :   if (app.processor_id() == 0)
     212             :   {
     213           8 :     std::error_code err;
     214           8 :     std::filesystem::remove_all(mesh_path.parent_path(), err);
     215             :   }
     216           8 : }
     217             : 
     218             : bool
     219        9707 : restoreMeshBackup(const MooseApp & app, Backup & backup, MooseMesh & mesh)
     220             : {
     221        9707 :   if (backup.mesh_files.empty())
     222        9705 :     return false;
     223             : 
     224           2 :   const auto mesh_path = temporaryBackupMeshPath(app, "restore", true);
     225           2 :   if (app.processor_id() == 0)
     226           6 :     for (const auto & [relative_path, contents] : backup.mesh_files)
     227           4 :       writeBackupMeshFile(mesh_path / relative_path, contents);
     228             : 
     229             :   // Rank 0 recreates the shared checkpoint tree, then all ranks collectively read their pieces.
     230           2 :   app.comm().barrier();
     231             : 
     232           2 :   auto & mesh_base = mesh.getMesh();
     233           2 :   mesh_base.clear();
     234             : 
     235             :   {
     236           2 :     CheckpointIO io(mesh_base, false);
     237           2 :     io.read(mesh_path.string());
     238           2 :   }
     239             : 
     240             :   // This checkpoint is used only to restore mesh topology.  The restored equation-system data is
     241             :   // loaded from the Backup stream after the mesh is prepared, so discard any DOF indices that
     242             :   // CheckpointIO carried with the mesh and let the systems own the final numbering.
     243          52 :   for (auto & node : mesh_base.node_ptr_range())
     244          52 :     node->clear_dofs();
     245          42 :   for (auto & elem : mesh_base.element_ptr_range())
     246          42 :     elem->clear_dofs();
     247             : 
     248           2 :   backup.mesh_files.clear();
     249             : 
     250             :   // Keep the shared checkpoint tree alive until every rank has completed CheckpointIO::read().
     251           2 :   app.comm().barrier();
     252             : 
     253           2 :   if (app.processor_id() == 0)
     254             :   {
     255           2 :     std::error_code err;
     256           2 :     std::filesystem::remove_all(mesh_path.parent_path(), err);
     257             :   }
     258             : 
     259           2 :   return true;
     260           2 : }
     261             : }
     262             : 
     263             : void
     264      191986 : MooseApp::addAppParam(InputParameters & params)
     265             : {
     266      959930 :   params.addCommandLineParam<std::string>(
     267             :       "app_to_run", "--app <type>", "Specify the application type to run (case-sensitive)");
     268      191986 : }
     269             : 
     270             : void
     271      123906 : MooseApp::addInputParam(InputParameters & params)
     272             : {
     273      619530 :   params.addCommandLineParam<std::vector<std::string>>(
     274             :       "input_file", "-i <input file(s)>", "Specify input file(s); multiple files are merged");
     275      123906 : }
     276             : 
     277             : InputParameters
     278       68080 : MooseApp::validParams()
     279             : {
     280       68080 :   InputParameters params = MooseBase::validParams();
     281             : 
     282       68080 :   MooseApp::addAppParam(params);
     283       68080 :   MooseApp::addInputParam(params);
     284             : 
     285      408480 :   params.addCommandLineParam<bool>("display_version", "-v --version", "Print application version");
     286             : 
     287      544640 :   params.addOptionalValuedCommandLineParam<std::string>(
     288             :       "mesh_only",
     289             :       "--mesh-only <optional path>",
     290             :       "",
     291             :       "Build and output the mesh only (Default: \"<input_file_name>_in.e\")");
     292      544640 :   params.addOptionalValuedCommandLineParam<std::string>(
     293             :       "csg_only",
     294             :       "--csg-only <optional path>",
     295             :       "",
     296             :       "Setup and output the input mesh in CSG format only (Default: "
     297             :       "\"<input_file_name>_out_csg.json\")");
     298      408480 :   params.addCommandLineParam<bool>(
     299             :       "show_input", "--show-input", "Shows the parsed input file before running the simulation");
     300      136160 :   params.setGlobalCommandLineParam("show_input");
     301      408480 :   params.addCommandLineParam<bool>(
     302             :       "show_outputs", "--show-outputs", "Shows the output execution time information");
     303      136160 :   params.setGlobalCommandLineParam("show_outputs");
     304      408480 :   params.addCommandLineParam<bool>(
     305             :       "show_controls", "--show-controls", "Shows the Control logic available and executed");
     306      136160 :   params.setGlobalCommandLineParam("show_controls");
     307             : 
     308      408480 :   params.addCommandLineParam<bool>(
     309             :       "no_color", "--no-color", "Disable coloring of all Console outputs");
     310      136160 :   params.setGlobalCommandLineParam("no_color");
     311             : 
     312      272320 :   MooseEnum colors("auto on off", "on");
     313      408480 :   params.addCommandLineParam<MooseEnum>(
     314             :       "color", "--color <auto,on,off=on>", colors, "Whether to use color in console output");
     315      136160 :   params.setGlobalCommandLineParam("color");
     316             : 
     317      408480 :   params.addCommandLineParam<bool>("help", "-h --help", "Displays CLI usage statement");
     318      408480 :   params.addCommandLineParam<bool>(
     319             :       "minimal",
     320             :       "--minimal",
     321             :       "Ignore input file and build a minimal application with Transient executioner");
     322             : 
     323      408480 :   params.addCommandLineParam<bool>(
     324             :       "language_server",
     325             :       "--language-server",
     326             :       "Starts a process to communicate with development tools using the language server protocol");
     327             : 
     328      408480 :   params.addCommandLineParam<bool>("dump", "--dump", "Shows a dump of available input file syntax");
     329      408480 :   params.addCommandLineParam<std::string>(
     330             :       "dump_search",
     331             :       "--dump-search <search>",
     332             :       "Shows a dump of available input syntax matching a search");
     333      408480 :   params.addCommandLineParam<bool>("registry", "--registry", "Lists all known objects and actions");
     334      408480 :   params.addCommandLineParam<bool>(
     335             :       "registry_hit", "--registry-hit", "Lists all known objects and actions in hit format");
     336      408480 :   params.addCommandLineParam<bool>(
     337             :       "use_executor", "--executor", "Use the new Executor system instead of Executioners");
     338             : 
     339      408480 :   params.addCommandLineParam<bool>(
     340             :       "show_type", "--show-type", "Return the name of the application object");
     341      408480 :   params.addCommandLineParam<bool>("yaml", "--yaml", "Dumps all input file syntax in YAML format");
     342      408480 :   params.addCommandLineParam<std::string>(
     343             :       "yaml_search", "--yaml-search", "Dumps input file syntax matching a search in YAML format");
     344      408480 :   params.addCommandLineParam<bool>("json", "--json", "Dumps all input file syntax in JSON format");
     345      408480 :   params.addCommandLineParam<std::string>(
     346             :       "json_search", "--json-search", "Dumps input file syntax matching a search in JSON format");
     347      408480 :   params.addCommandLineParam<bool>(
     348             :       "syntax", "--syntax", "Dumps the associated Action syntax paths ONLY");
     349      408480 :   params.addCommandLineParam<bool>(
     350             :       "show_docs", "--docs", "Print url/path to the documentation website");
     351      408480 :   params.addCommandLineParam<bool>(
     352             :       "show_capabilities", "--show-capabilities", "Dumps the capability registry in JSON format.");
     353      408480 :   params.addCommandLineParam<std::string>(
     354             :       "required_capabilities",
     355             :       "--required-capabilities",
     356             :       "A list of conditions that is checked against the registered capabilities (see "
     357             :       "--show-capabilities). The executable will terminate early if the conditions are not met.");
     358      408480 :   params.addCommandLineParam<std::string>(
     359             :       "testharness_capabilities",
     360             :       "--testharness-capabilities",
     361             :       "Path to JSON from the TestHarness that contains capabilities to be appended.");
     362             : 
     363      408480 :   params.addCommandLineParam<std::string>(
     364             :       "check_capabilities",
     365             :       "--check-capabilities",
     366             :       "A list of conditions that is checked against the registered capabilities. Will exit based "
     367             :       "on whether or not the capaiblities are fulfilled. Does not check dynamically loaded apps.");
     368      408480 :   params.addCommandLineParam<bool>("check_input",
     369             :                                    "--check-input",
     370             :                                    "Check the input file (i.e. requires -i <filename>) and quit");
     371      136160 :   params.setGlobalCommandLineParam("check_input");
     372      408480 :   params.addCommandLineParam<bool>(
     373             :       "show_inputs",
     374             :       "--show-copyable-inputs",
     375             :       "Shows the directories able to be copied into a user-writable location");
     376             : 
     377      408480 :   params.addCommandLineParam<std::string>(
     378             :       "copy_inputs",
     379             :       "--copy-inputs <dir>",
     380             :       "Copies installed inputs (e.g. tests, examples, etc.) to a directory <appname>_<dir>");
     381             :   // TODO: Should this remain a bool? It can't be a regular argument because it contains
     382             :   // values that have dashes in it, so it'll get treated as another arg
     383      544640 :   params.addOptionalValuedCommandLineParam<std::string>(
     384             :       "run",
     385             :       "--run <test harness args>",
     386             :       "",
     387             :       "Runs the inputs in the current directory copied to a "
     388             :       "user-writable location by \"--copy-inputs\"");
     389             : 
     390      408480 :   params.addCommandLineParam<bool>(
     391             :       "list_constructed_objects",
     392             :       "--list-constructed-objects",
     393             :       "List all moose object type names constructed by the master app factory");
     394             : 
     395      544640 :   params.addOptionalValuedCommandLineParam<std::string>(
     396             :       "citations",
     397             :       "--citations [file]",
     398             :       "",
     399             :       "List the papers (in BibTeX format) that should be cited for the framework, PETSc, and the "
     400             :       "modules and objects used in this simulation; optionally write them to [file] instead of the "
     401             :       "console");
     402             : 
     403      408480 :   params.addCommandLineParam<unsigned int>(
     404             :       "n_threads", "--n-threads=<n>", "Runs the specified number of threads per process");
     405             :   // This probably shouldn't be global, but the implications of removing this are currently
     406             :   // unknown and we need to manage it with libmesh better
     407      136160 :   params.setGlobalCommandLineParam("n_threads");
     408             : 
     409      408480 :   params.addCommandLineParam<bool>("allow_unused",
     410             :                                    "-w --allow-unused",
     411             :                                    "Warn about unused input file options instead of erroring");
     412      136160 :   params.setGlobalCommandLineParam("allow_unused");
     413      408480 :   params.addCommandLineParam<bool>(
     414             :       "error_unused", "-e --error-unused", "Error when encountering unused input file options");
     415      136160 :   params.setGlobalCommandLineParam("error_unused");
     416      408480 :   params.addCommandLineParam<bool>(
     417             :       "error_override",
     418             :       "-o --error-override",
     419             :       "Error when encountering overridden or parameters supplied multiple times");
     420      136160 :   params.setGlobalCommandLineParam("error_override");
     421      408480 :   params.addCommandLineParam<bool>(
     422             :       "error_deprecated", "--error-deprecated", "Turn deprecated code messages into Errors");
     423      136160 :   params.setGlobalCommandLineParam("error_deprecated");
     424             : 
     425      408480 :   params.addCommandLineParam<bool>("distributed_mesh",
     426             :                                    "--distributed-mesh",
     427             :                                    "Forces the use of a distributed finite element mesh");
     428             :   // Would prefer that this parameter isn't global, but we rely on it too much
     429             :   // in tests to be able to go back on that decision now
     430      136160 :   params.setGlobalCommandLineParam("distributed_mesh");
     431             : 
     432      408480 :   params.addCommandLineParam<std::string>(
     433             :       "split_mesh",
     434             :       "--split-mesh <splits>",
     435             :       "Comma-separated list of numbers of chunks to split the mesh into");
     436             : 
     437             :   // TODO: remove the logic now that this is global
     438      408480 :   params.addCommandLineParam<std::string>(
     439             :       "split_file", "--split-file <filename>", "Name of split mesh file(s) to write/read");
     440             : 
     441      408480 :   params.addCommandLineParam<bool>("use_split", "--use-split", "Use split distributed mesh files");
     442             : 
     443      408480 :   params.addCommandLineParam<unsigned int>(
     444             :       "refinements", "-r <num refinements>", "Specify additional initial uniform mesh refinements");
     445             : 
     446      544640 :   params.addOptionalValuedCommandLineParam<std::string>(
     447             :       "recover",
     448             :       "--recover <optional file base>",
     449             :       "",
     450             :       "Continue the calculation. Without <file base>, the most recent recovery file will be used");
     451      136160 :   params.setGlobalCommandLineParam("recover");
     452      408480 :   params.addCommandLineParam<bool>(
     453             :       "force_restart",
     454             :       "--force-restart",
     455             :       "Forcefully load checkpoints despite possible incompatibilities");
     456      136160 :   params.setGlobalCommandLineParam("force_restart");
     457             : 
     458      340400 :   params.addCommandLineParam<bool>("suppress_header",
     459             :                                    "--suppress-header",
     460      136160 :                                    false,
     461             :                                    "Disables the output of the application header.");
     462      136160 :   params.setGlobalCommandLineParam("suppress_header");
     463             : 
     464      408480 :   params.addCommandLineParam<bool>(
     465             :       "test_checkpoint_half_transient",
     466             :       "--test-checkpoint-half-transient",
     467             :       "Run half of a transient with checkpoints enabled; used by the TestHarness");
     468      136160 :   params.setGlobalCommandLineParam("test_checkpoint_half_transient");
     469             : 
     470      408480 :   params.addCommandLineParam<bool>("test_restep",
     471             :                                    "--test-restep",
     472             :                                    "Test re-running the middle timestep; used by the TestHarness");
     473             : 
     474      408480 :   params.addCommandLineParam<bool>(
     475             :       "trap_fpe",
     476             :       "--trap-fpe",
     477             :       "Enable floating point exception handling in critical sections of code"
     478             : #ifdef DEBUG
     479             :       " (automatic due to debug build)"
     480             : #endif
     481             :   );
     482      136160 :   params.setGlobalCommandLineParam("trap_fpe");
     483             : 
     484      408480 :   params.addCommandLineParam<bool>(
     485             :       "no_trap_fpe",
     486             :       "--no-trap-fpe",
     487             :       "Disable floating point exception handling in critical sections of code"
     488             : #ifndef DEBUG
     489             :       " (unused due to non-debug build)"
     490             : #endif
     491             :   );
     492             : 
     493      136160 :   params.setGlobalCommandLineParam("no_trap_fpe");
     494             : 
     495      408480 :   params.addCommandLineParam<bool>(
     496             :       "no_gdb_backtrace", "--no-gdb-backtrace", "Disables gdb backtraces.");
     497      136160 :   params.setGlobalCommandLineParam("no_gdb_backtrace");
     498             : 
     499      408480 :   params.addCommandLineParam<bool>("error", "--error", "Turn all warnings into errors");
     500      136160 :   params.setGlobalCommandLineParam("error");
     501             : 
     502      408480 :   params.addCommandLineParam<bool>("timing",
     503             :                                    "-t --timing",
     504             :                                    "Enable all performance logging for timing; disables screen "
     505             :                                    "output of performance logs for all Console objects");
     506      136160 :   params.setGlobalCommandLineParam("timing");
     507      408480 :   params.addCommandLineParam<bool>(
     508             :       "no_timing", "--no-timing", "Disabled performance logging; overrides -t or --timing");
     509      136160 :   params.setGlobalCommandLineParam("no_timing");
     510             : 
     511      408480 :   params.addCommandLineParam<bool>(
     512             :       "allow_test_objects", "--allow-test-objects", "Register test objects and syntax");
     513      136160 :   params.setGlobalCommandLineParam("allow_test_objects");
     514             : 
     515             :   // Options ignored by MOOSE but picked up by libMesh, these are here so that they are displayed in
     516             :   // the application help
     517      408480 :   params.addCommandLineParam<bool>(
     518             :       "keep_cout",
     519             :       "--keep-cout",
     520             :       "Keep standard output from all processors when running in parallel");
     521      136160 :   params.setGlobalCommandLineParam("keep_cout");
     522      408480 :   params.addCommandLineParam<bool>(
     523             :       "redirect_stdout",
     524             :       "--redirect-stdout",
     525             :       "Keep standard output from all processors when running in parallel");
     526      136160 :   params.setGlobalCommandLineParam("redirect_stdout");
     527             : 
     528      544640 :   params.addCommandLineParam<std::string>(
     529             :       "timpi_sync",
     530             :       "--timpi-sync <type=nbx>",
     531             :       "nbx",
     532             :       "Changes the sync type used in spare parallel communitations within TIMPI");
     533      136160 :   params.setGlobalCommandLineParam("timpi_sync");
     534             : 
     535             :   // Options for debugging
     536      408480 :   params.addCommandLineParam<std::string>("start_in_debugger",
     537             :                                           "--start-in-debugger <debugger>",
     538             :                                           "Start the application and attach a debugger; this will "
     539             :                                           "launch xterm windows using <debugger>");
     540             : 
     541      408480 :   params.addCommandLineParam<unsigned int>(
     542             :       "stop_for_debugger",
     543             :       "--stop-for-debugger <seconds>",
     544             :       "Pauses the application during startup for <seconds> to allow for connection of debuggers");
     545             : 
     546      408480 :   params.addCommandLineParam<bool>(
     547             :       "perf_graph_live_all", "--perf-graph-live-all", "Forces printing of ALL progress messages");
     548      136160 :   params.setGlobalCommandLineParam("perf_graph_live_all");
     549             : 
     550      408480 :   params.addCommandLineParam<bool>(
     551             :       "disable_perf_graph_live", "--disable-perf-graph-live", "Disables PerfGraph live printing");
     552      136160 :   params.setGlobalCommandLineParam("disable_perf_graph_live");
     553             : 
     554      204240 :   params.addParam<bool>(
     555      136160 :       "automatic_automatic_scaling", false, "Whether to turn on automatic scaling by default");
     556             : 
     557      272320 :   const MooseEnum compute_device_type("cpu cuda mps hip ceed-cpu ceed-cuda ceed-hip xpu", "cpu");
     558      408480 :   params.addCommandLineParam<MooseEnum>(
     559             :       "compute_device",
     560             :       "--compute-device",
     561             :       compute_device_type,
     562             :       "The device type we want to run accelerated (libtorch, MFEM) computations on.");
     563             : 
     564             : #ifdef HAVE_GPERFTOOLS
     565             :   params.addCommandLineParam<std::string>(
     566             :       "gperf_profiler_on",
     567             :       "--gperf-profiler-on <ranks>",
     568             :       "To generate profiling report only on comma-separated list of MPI ranks");
     569             : #endif
     570             : 
     571      340400 :   params.addCommandLineParam<bool>(
     572             :       "show_data_params",
     573             :       "--show-data-params",
     574      136160 :       false,
     575             :       "Show found paths for all DataFileName parameters in the header");
     576      340400 :   params.addCommandLineParam<bool>("show_data_paths",
     577             :                                    "--show-data-paths",
     578      136160 :                                    false,
     579             :                                    "Show registered data paths for searching in the header");
     580             : 
     581      136160 :   params.addPrivateParam<std::shared_ptr<CommandLine>>("_command_line");
     582      136160 :   params.addPrivateParam<std::shared_ptr<Parallel::Communicator>>("_comm");
     583      136160 :   params.addPrivateParam<unsigned int>("_multiapp_level");
     584       68080 :   params.addPrivateParam<unsigned int>("_multiapp_number");
     585      204240 :   params.addPrivateParam<bool>("_use_master_mesh", false);
     586      136160 :   params.addPrivateParam<const MooseMesh *>("_master_mesh");
     587       68080 :   params.addPrivateParam<const MooseMesh *>("_master_displaced_mesh");
     588      204240 :   params.addPrivateParam<std::unique_ptr<Backup> *>("_initial_backup", nullptr);
     589      136160 :   params.addPrivateParam<std::shared_ptr<Parser>>("_parser");
     590             : #ifdef MOOSE_MFEM_ENABLED
     591      102246 :   params.addPrivateParam<std::shared_ptr<mfem::Device>>("_mfem_device");
     592      102246 :   params.addPrivateParam<std::set<std::string>>("_mfem_devices");
     593             : #endif
     594             : 
     595      204240 :   params.addParam<bool>(
     596             :       "use_legacy_material_output",
     597      136160 :       true,
     598             :       "Set false to allow material properties to be output on INITIAL, not just TIMESTEP_END.");
     599      204240 :   params.addParam<bool>(
     600             :       "use_legacy_initial_residual_evaluation_behavior",
     601      136160 :       true,
     602             :       "The legacy behavior performs an often times redundant residual evaluation before the "
     603             :       "solution modifying objects are executed prior to the initial (0th nonlinear iteration) "
     604             :       "residual evaluation. The new behavior skips that redundant residual evaluation unless the "
     605             :       "parameter Executioner/use_pre_SMO_residual is set to true.");
     606             : 
     607      136160 :   params.addParam<bool>(
     608             :       MeshGeneratorSystem::allow_data_driven_param,
     609       68080 :       false,
     610             :       "Set true to enable data-driven mesh generation, which is an experimental feature");
     611             : 
     612      340400 :   params.addCommandLineParam<bool>(
     613             :       "parse_neml2_only",
     614             :       "--parse-neml2-only",
     615             :       "Executes the [NEML2] block to parse the input file and terminate.");
     616             : 
     617       68080 :   MooseApp::addAppParam(params);
     618             : 
     619       68080 :   params.registerBase("Application");
     620             : 
     621      136160 :   return params;
     622       68080 : }
     623             : 
     624       68103 : MooseApp::MooseApp(const InputParameters & parameters)
     625             :   : PerfGraphInterface(*this, "MooseApp"),
     626       68103 :     ParallelObject(*parameters.get<std::shared_ptr<Parallel::Communicator>>(
     627       68103 :         "_comm")), // Can't call getParam() before pars is set
     628             :     // The use of AppFactory::getAppParams() is atrocious. However, a long time ago
     629             :     // we decided to copy construct parameters in each derived application...
     630             :     // which means that the "parameters" we get if someone derives from MooseApp are
     631             :     // actually a copy of the ones built by the factory. Because we have unique
     632             :     // application names, this allows us to reference (using _pars and MooseBase)
     633             :     // the actual const parameters that the AppFactory made for this application
     634      136206 :     MooseBase(*this, AppFactory::instance().getAppParams(parameters)),
     635      136206 :     _comm(getParam<std::shared_ptr<Parallel::Communicator>>("_comm")),
     636       68103 :     _file_base_set_by_user(false),
     637       68103 :     _output_position_set(false),
     638       68103 :     _start_time_set(false),
     639       68103 :     _start_time(0.0),
     640       68103 :     _global_time_offset(0.0),
     641       68103 :     _input_parameter_warehouse(std::make_unique<InputParameterWarehouse>()),
     642       68103 :     _action_factory(*this),
     643       68103 :     _action_warehouse(*this, _syntax, _action_factory),
     644       68103 :     _output_warehouse(*this),
     645      272412 :     _parser(getCheckedPointerParam<std::shared_ptr<Parser>>("_parser")),
     646      272412 :     _command_line(getCheckedPointerParam<std::shared_ptr<CommandLine>>("_command_line")),
     647       68103 :     _builder(*this, _action_warehouse, *_parser),
     648      136206 :     _restartable_data(libMesh::n_threads()),
     649       68103 :     _perf_graph(createRecoverablePerfGraph()),
     650       68103 :     _solution_invalidity(createRecoverableSolutionInvalidity()),
     651       68103 :     _rank_map(*_comm, _perf_graph),
     652      136206 :     _use_executor(getParam<bool>("use_executor")),
     653       68103 :     _null_executor(NULL),
     654       68103 :     _use_nonlinear(true),
     655       68103 :     _use_eigen_value(false),
     656       68103 :     _enable_unused_check(ERROR_UNUSED),
     657       68103 :     _factory(*this),
     658       68103 :     _error_overridden(false),
     659      136206 :     _early_exit_param(""),
     660       68103 :     _ready_to_exit(false),
     661       68103 :     _exit_code(0),
     662       68103 :     _initial_from_file(false),
     663      136206 :     _distributed_mesh_on_command_line(getParam<bool>("distributed_mesh")),
     664       68103 :     _recover(false),
     665       68103 :     _restart(false),
     666       68103 :     _split_mesh(false),
     667      136206 :     _use_split(getParam<bool>("use_split")),
     668      136206 :     _force_restart(getParam<bool>("force_restart")),
     669             : #ifdef DEBUG
     670             :     _trap_fpe(true),
     671             : #else
     672       68103 :     _trap_fpe(false),
     673             : #endif
     674       68103 :     _test_checkpoint_half_transient(parameters.get<bool>("test_checkpoint_half_transient")),
     675       68103 :     _test_restep(parameters.get<bool>("test_restep")),
     676      136206 :     _check_input(getParam<bool>("check_input")),
     677      160700 :     _multiapp_level(isParamValid("_multiapp_level") ? getParam<unsigned int>("_multiapp_level")
     678             :                                                     : 0),
     679      160700 :     _multiapp_number(isParamValid("_multiapp_number") ? getParam<unsigned int>("_multiapp_number")
     680             :                                                       : 0),
     681      136206 :     _use_master_mesh(getParam<bool>("_use_master_mesh")),
     682      160700 :     _master_mesh(isParamValid("_master_mesh") ? getParam<const MooseMesh *>("_master_mesh")
     683             :                                               : nullptr),
     684      136206 :     _master_displaced_mesh(isParamValid("_master_displaced_mesh")
     685       68103 :                                ? getParam<const MooseMesh *>("_master_displaced_mesh")
     686             :                                : nullptr),
     687       68103 :     _mesh_generator_system(*this),
     688       68103 :     _chain_control_system(*this),
     689       68103 :     _rd_reader(*this, _restartable_data, forceRestart()),
     690       68103 :     _execute_flags(moose::internal::ExecFlagRegistry::getExecFlagRegistry().getFlags()),
     691       68103 :     _output_buffer_cache(nullptr),
     692      136206 :     _automatic_automatic_scaling(getParam<bool>("automatic_automatic_scaling")),
     693      322337 :     _initial_backup(getParam<std::unique_ptr<Backup> *>("_initial_backup"))
     694             : #ifdef MOOSE_LIBTORCH_ENABLED
     695             :     ,
     696        2850 :     _libtorch_device(determineLibtorchDeviceType(getParam<MooseEnum>("compute_device")))
     697             : #endif
     698             : #ifdef MOOSE_MFEM_ENABLED
     699             :     ,
     700      123566 :     _mfem_device(isParamValid("_mfem_device")
     701       51147 :                      ? getParam<std::shared_ptr<mfem::Device>>("_mfem_device")
     702             :                      : nullptr),
     703      121206 :     _mfem_devices(isParamValid("_mfem_devices") ? getParam<std::set<std::string>>("_mfem_devices")
     704      664911 :                                                 : std::set<std::string>{})
     705             : #endif
     706             : {
     707       68103 :   if (&parameters != &_pars)
     708             :   {
     709           4 :     const std::string bad_params = "(InputParameters parameters)";
     710           2 :     const std::string good_params = "(const InputParameters & parameters)";
     711           2 :     const std::string source_constructor = type() + "::" + type();
     712           2 :     mooseDoOnce(
     713             :         mooseDeprecatedNoTrace(type(),
     714             :                                " copy-constructs its input parameters.\n\n",
     715             :                                "This is deprecated and will not be allowed in the future.\n\n",
     716             :                                "In ",
     717             :                                type(),
     718             :                                ".C, change:\n  ",
     719             :                                source_constructor,
     720             :                                bad_params,
     721             :                                " -> ",
     722             :                                source_constructor,
     723             :                                good_params,
     724             :                                "\n\n",
     725             :                                "In ",
     726             :                                type(),
     727             :                                ".h, change:\n  ",
     728             :                                type(),
     729             :                                bad_params,
     730             :                                "; -> ",
     731             :                                type(),
     732             :                                good_params,
     733             :                                ";"));
     734           6 :   }
     735             : 
     736             :   mooseAssert(_command_line->hasParsed(), "Command line has not parsed");
     737             :   mooseAssert(_parser->queryRoot(), "Parser has not parsed");
     738             : 
     739             :   // Set the TIMPI sync type via --timpi-sync
     740      136202 :   const auto & timpi_sync = getParam<std::string>("timpi_sync");
     741       68101 :   const_cast<Parallel::Communicator &>(comm()).sync_type(timpi_sync);
     742             : 
     743             : #ifdef HAVE_GPERFTOOLS
     744             :   if (isUltimateMaster())
     745             :   {
     746             :     bool has_cpu_profiling = false;
     747             :     bool has_heap_profiling = false;
     748             :     static std::string cpu_profile_file;
     749             :     static std::string heap_profile_file;
     750             : 
     751             :     // For CPU profiling, users need to have environment 'MOOSE_PROFILE_BASE'
     752             :     if (std::getenv("MOOSE_PROFILE_BASE"))
     753             :     {
     754             :       has_cpu_profiling = true;
     755             :       cpu_profile_file =
     756             :           std::getenv("MOOSE_PROFILE_BASE") + std::to_string(_comm->rank()) + ".prof";
     757             :       // create directory if needed
     758             :       auto name = MooseUtils::splitFileName(cpu_profile_file);
     759             :       if (!name.first.empty())
     760             :       {
     761             :         if (processor_id() == 0)
     762             :           MooseUtils::makedirs(name.first.c_str());
     763             :         _comm->barrier();
     764             :       }
     765             :     }
     766             : 
     767             :     // For Heap profiling, users need to have 'MOOSE_HEAP_BASE'
     768             :     if (std::getenv("MOOSE_HEAP_BASE"))
     769             :     {
     770             :       has_heap_profiling = true;
     771             :       heap_profile_file = std::getenv("MOOSE_HEAP_BASE") + std::to_string(_comm->rank());
     772             :       // create directory if needed
     773             :       auto name = MooseUtils::splitFileName(heap_profile_file);
     774             :       if (!name.first.empty())
     775             :       {
     776             :         if (processor_id() == 0)
     777             :           MooseUtils::makedirs(name.first.c_str());
     778             :         _comm->barrier();
     779             :       }
     780             :     }
     781             : 
     782             :     // turn on profiling only on selected ranks
     783             :     if (isParamSetByUser("gperf_profiler_on"))
     784             :     {
     785             :       auto rankstr = getParam<std::string>("gperf_profiler_on");
     786             :       std::vector<processor_id_type> ranks;
     787             :       bool success = MooseUtils::tokenizeAndConvert(rankstr, ranks, ", ");
     788             :       if (!success)
     789             :         mooseError("Invalid argument for --gperf-profiler-on: '", rankstr, "'");
     790             :       for (auto & rank : ranks)
     791             :       {
     792             :         if (rank >= _comm->size())
     793             :           mooseError("Invalid argument for --gperf-profiler-on: ",
     794             :                      rank,
     795             :                      " is greater than or equal to ",
     796             :                      _comm->size());
     797             :         if (rank == _comm->rank())
     798             :         {
     799             :           _cpu_profiling = has_cpu_profiling;
     800             :           _heap_profiling = has_heap_profiling;
     801             :         }
     802             :       }
     803             :     }
     804             :     else
     805             :     {
     806             :       _cpu_profiling = has_cpu_profiling;
     807             :       _heap_profiling = has_heap_profiling;
     808             :     }
     809             : 
     810             :     if (_cpu_profiling)
     811             :       if (!ProfilerStart(cpu_profile_file.c_str()))
     812             :         mooseError("CPU profiler is not started properly");
     813             : 
     814             :     if (_heap_profiling)
     815             :     {
     816             :       HeapProfilerStart(heap_profile_file.c_str());
     817             :       if (!IsHeapProfilerRunning())
     818             :         mooseError("Heap profiler is not started properly");
     819             :     }
     820             :   }
     821             : #else
     822       68101 :   if (std::getenv("MOOSE_PROFILE_BASE") || std::getenv("MOOSE_HEAP_BASE"))
     823           0 :     mooseError("gperftool is not available for CPU or heap profiling");
     824             : #endif
     825             : 
     826             :   // If this will be a language server then turn off output until that starts
     827      340505 :   if (isParamValid("language_server") && getParam<bool>("language_server"))
     828           0 :     _output_buffer_cache = Moose::out.rdbuf(nullptr);
     829             : 
     830       68101 :   Registry::addKnownLabel(_type);
     831       68101 :   Moose::registerAll(_factory, _action_factory, _syntax);
     832             : 
     833       68101 :   _the_warehouse = std::make_unique<TheWarehouse>();
     834      136202 :   _the_warehouse->registerAttribute<AttribMatrixTags>("matrix_tags", 0);
     835      136202 :   _the_warehouse->registerAttribute<AttribVectorTags>("vector_tags", 0);
     836      136202 :   _the_warehouse->registerAttribute<AttribExecOns>("exec_ons", 0);
     837      136202 :   _the_warehouse->registerAttribute<AttribSubdomains>("subdomains", 0);
     838      136202 :   _the_warehouse->registerAttribute<AttribBoundaries>("boundaries", 0);
     839      136202 :   _the_warehouse->registerAttribute<AttribThread>("thread", 0);
     840      136202 :   _the_warehouse->registerAttribute<AttribExecutionOrderGroup>("execution_order_group", 0);
     841      136202 :   _the_warehouse->registerAttribute<AttribPreIC>("pre_ic", 0);
     842      136202 :   _the_warehouse->registerAttribute<AttribPreAux>("pre_aux");
     843      136202 :   _the_warehouse->registerAttribute<AttribPostAux>("post_aux");
     844      136202 :   _the_warehouse->registerAttribute<AttribName>("name", "dummy");
     845      136202 :   _the_warehouse->registerAttribute<AttribSystem>("system", "dummy");
     846      136202 :   _the_warehouse->registerAttribute<AttribKokkos>("kokkos", false);
     847      136202 :   _the_warehouse->registerAttribute<AttribVar>("variable", -1);
     848      136202 :   _the_warehouse->registerAttribute<AttribInterfaces>("interfaces", 0);
     849      136202 :   _the_warehouse->registerAttribute<AttribSysNum>("sys_num", libMesh::invalid_uint);
     850      136202 :   _the_warehouse->registerAttribute<AttribResidualObject>("residual_object");
     851      136202 :   _the_warehouse->registerAttribute<AttribSorted>("sorted");
     852      136202 :   _the_warehouse->registerAttribute<AttribDisplaced>("displaced", -1);
     853             : 
     854       68101 :   _perf_graph.enableLivePrint();
     855             : 
     856       68249 :   if (_check_input && isParamSetByUser("recover"))
     857           0 :     mooseError("Cannot run --check-input with --recover. Recover files might not exist");
     858             : 
     859      204303 :   if (isParamSetByUser("start_in_debugger") && isUltimateMaster())
     860             :   {
     861           0 :     auto command = getParam<std::string>("start_in_debugger");
     862             : 
     863           0 :     Moose::out << "Starting in debugger using: " << command << std::endl;
     864             : 
     865           0 :     auto hostname = MooseUtils::hostname();
     866             : 
     867           0 :     std::stringstream command_stream;
     868             : 
     869             :     // This will start XTerm and print out some info first... then run the debugger
     870           0 :     command_stream << "xterm -e \"echo 'Rank: " << processor_id() << "  Hostname: " << hostname
     871           0 :                    << "  PID: " << getpid() << "'; echo ''; ";
     872             : 
     873             :     // Figure out how to run the debugger
     874           0 :     if (command.find("lldb") != std::string::npos || command.find("gdb") != std::string::npos)
     875           0 :       command_stream << command << " -p " << getpid();
     876             :     else
     877           0 :       mooseError("Unknown debugger: ",
     878             :                  command,
     879             :                  "\nIf this is truly what you meant then contact moose-users to have a discussion "
     880             :                  "about adding your debugger.");
     881             : 
     882             :     // Finish up the command
     883           0 :     command_stream << "\"" << " & ";
     884           0 :     std::string command_string = command_stream.str();
     885           0 :     Moose::out << "Running: " << command_string << std::endl;
     886             : 
     887           0 :     int ret = std::system(command_string.c_str());
     888           0 :     libmesh_ignore(ret);
     889             : 
     890             :     // Sleep to allow time for the debugger to attach
     891           0 :     std::this_thread::sleep_for(std::chrono::seconds(10));
     892           0 :   }
     893             : 
     894      204303 :   if (isParamSetByUser("stop_for_debugger") && isUltimateMaster())
     895             :   {
     896          27 :     Moose::out << "\nStopping for " << getParam<unsigned int>("stop_for_debugger")
     897           9 :                << " seconds to allow attachment from a debugger.\n";
     898             : 
     899           9 :     Moose::out << "\nAll of the processes you can connect to:\n";
     900           9 :     Moose::out << "rank - hostname - pid\n";
     901             : 
     902           9 :     auto hostname = MooseUtils::hostname();
     903             : 
     904             :     {
     905             :       // The 'false' turns off the serialization warning
     906           9 :       SerializerGuard sg(_communicator, false); // Guarantees that the processors print in order
     907           9 :       Moose::err << processor_id() << " - " << hostname << " - " << getpid() << "\n";
     908           9 :     }
     909             : 
     910           9 :     Moose::out << "\nWaiting...\n" << std::endl;
     911             : 
     912             :     // Sleep to allow time for the debugger to attach
     913          18 :     std::this_thread::sleep_for(std::chrono::seconds(getParam<unsigned int>("stop_for_debugger")));
     914           9 :   }
     915             : 
     916       68101 :   if (_master_mesh && isUltimateMaster())
     917           0 :     mooseError("Mesh can be passed in only for sub-apps");
     918             : 
     919       68101 :   if (_master_displaced_mesh && !_master_mesh)
     920           0 :     mooseError("_master_mesh should have been set when _master_displaced_mesh is set");
     921             : 
     922             : #ifdef MOOSE_MFEM_ENABLED
     923       51145 :   if (_mfem_device)
     924             :   {
     925             :     mooseAssert(!isUltimateMaster(),
     926             :                 "The MFEM device should only be auto-set for sub-applications");
     927             :     mooseAssert(!_mfem_devices.empty(),
     928             :                 "If we are a sub-application and we have an MFEM device object, then we must know "
     929             :                 "its configuration string");
     930             :   }
     931             : #endif
     932             : 
     933             :   // Data specifically associated with the mesh (meta-data) that will read from the restart
     934             :   // file early during the simulation setup so that they are available to Actions and other objects
     935             :   // that need them during the setup process. Most of the restartable data isn't made available
     936             :   // until all objects have been created and all Actions have been executed (i.e. initialSetup).
     937       68101 :   registerRestartableDataMapName(MooseApp::MESH_META_DATA, MooseApp::MESH_META_DATA_SUFFIX);
     938             : 
     939       68101 :   if (_pars.have_parameter<bool>("use_legacy_dirichlet_bc"))
     940           0 :     mooseDeprecated("The parameter 'use_legacy_dirichlet_bc' is no longer valid.\n\n",
     941             :                     "All Dirichlet boundary conditions are preset by default.\n\n",
     942             :                     "Remove said parameter in ",
     943           0 :                     name(),
     944             :                     " to remove this deprecation warning.");
     945             : 
     946       68101 :   if (_test_restep && _test_checkpoint_half_transient)
     947           0 :     mooseError("Cannot use --test-restep and --test-checkpoint-half-transient together");
     948             : 
     949       68101 :   Moose::out << std::flush;
     950             : 
     951             : #ifdef MOOSE_KOKKOS_ENABLED
     952             : #ifdef MOOSE_ENABLE_KOKKOS_GPU
     953        2359 :   queryKokkosGPUs();
     954             : #endif
     955             : #endif
     956       68185 : }
     957             : 
     958             : std::optional<MooseEnum>
     959        1584 : MooseApp::getComputeDevice() const
     960             : {
     961        4752 :   if (isParamSetByUser("compute_device"))
     962        3123 :     return getParam<MooseEnum>("compute_device");
     963         543 :   return {};
     964             : }
     965             : 
     966      447692 : MooseApp::~MooseApp()
     967             : {
     968             : #ifdef HAVE_GPERFTOOLS
     969             :   // CPU profiling stop
     970             :   if (_cpu_profiling)
     971             :     ProfilerStop();
     972             :   // Heap profiling stop
     973             :   if (_heap_profiling)
     974             :     HeapProfilerStop();
     975             : #endif
     976       63956 :   _action_warehouse.clear();
     977       63956 :   _the_warehouse.reset();
     978       63956 :   _executioner.reset();
     979             : 
     980             :   // Don't wait for implicit destruction of input parameter storage
     981       63956 :   _input_parameter_warehouse.reset();
     982             : 
     983             :   // This is dirty, but I don't know what else to do. Obviously, others
     984             :   // have had similar problems if you look above. In specific, the
     985             :   // dlclose below on macs is destructing some data that does not
     986             :   // belong to it in garbage collection. So... don't even give
     987             :   // dlclose an option
     988       63956 :   _restartable_data.clear();
     989             : 
     990             :   // Remove this app's parameters from the AppFactory. This allows
     991             :   // for creating an app with this name again in the same execution,
     992             :   // which needs to be done when resetting applications in MultiApp
     993       63956 :   AppFactory::instance().clearAppParams(parameters(), {});
     994             : 
     995             : #ifdef LIBMESH_HAVE_DLOPEN
     996             :   // Close any open dynamic libraries
     997       63964 :   for (const auto & lib_pair : _lib_handles)
     998           8 :     dlclose(lib_pair.second.library_handle);
     999             : #endif
    1000             : 
    1001             : #ifdef MOOSE_KOKKOS_ENABLED
    1002       48367 :   deallocateKokkosMemoryPool();
    1003             : #endif
    1004       63956 : }
    1005             : 
    1006             : std::string
    1007           0 : MooseApp::getFrameworkVersion() const
    1008             : {
    1009           0 :   return MOOSE_VERSION;
    1010             : }
    1011             : 
    1012             : std::string
    1013          24 : MooseApp::getVersion() const
    1014             : {
    1015          48 :   return MOOSE_VERSION;
    1016             : }
    1017             : 
    1018             : std::string
    1019          24 : MooseApp::getPrintableVersion() const
    1020             : {
    1021          24 :   return getPrintableName() + " Version: " + getVersion();
    1022             : }
    1023             : 
    1024             : void
    1025       67547 : MooseApp::setupOptions()
    1026             : {
    1027      337735 :   TIME_SECTION("setupOptions", 5, "Setting Up Options");
    1028             : 
    1029             :   // Print the header, this is as early as possible
    1030       67655 :   if (header().length() && !getParam<bool>("suppress_header"))
    1031          27 :     _console << header() << std::endl;
    1032             : 
    1033      202641 :   if (getParam<bool>("error_unused"))
    1034         309 :     setCheckUnusedFlag(true);
    1035      201714 :   else if (getParam<bool>("allow_unused"))
    1036         120 :     setCheckUnusedFlag(false);
    1037             : 
    1038      202641 :   if (getParam<bool>("error_override"))
    1039       65624 :     setErrorOverridden();
    1040             : 
    1041      202641 :   if (getParam<bool>("trap_fpe"))
    1042             :   {
    1043           0 :     _trap_fpe = true;
    1044           0 :     _perf_graph.setActive(false);
    1045           0 :     if (getParam<bool>("no_trap_fpe"))
    1046           0 :       mooseError("Cannot use both \"--trap-fpe\" and \"--no-trap-fpe\" flags.");
    1047             :   }
    1048      202641 :   else if (getParam<bool>("no_trap_fpe"))
    1049         107 :     _trap_fpe = false;
    1050             : 
    1051             :   // Turn all warnings in MOOSE to errors (almost see next logic block)
    1052      135094 :   Moose::_warnings_are_errors = getParam<bool>("error");
    1053             : 
    1054             :   // Deprecated messages can be toggled to errors independently from everything else.
    1055      135094 :   Moose::_deprecated_is_error = getParam<bool>("error_deprecated");
    1056             : 
    1057       67547 :   if (isUltimateMaster()) // makes sure coloring isn't reset incorrectly in multi-app settings
    1058             :   {
    1059             :     // Set from command line
    1060      110606 :     auto color = getParam<MooseEnum>("color");
    1061      165909 :     if (!isParamSetByUser("color"))
    1062             :     {
    1063             :       // Set from deprecated --no-color
    1064      164538 :       if (getParam<bool>("no_color"))
    1065           0 :         color = "off";
    1066             :       // Set from environment
    1067             :       else
    1068             :       {
    1069       54846 :         char * c_color = std::getenv("MOOSE_COLOR");
    1070       54846 :         if (c_color)
    1071           0 :           color.assign(std::string(c_color), "While assigning environment variable MOOSE_COLOR");
    1072             :       }
    1073             :     }
    1074             : 
    1075       55303 :     if (color == "auto")
    1076           0 :       Moose::setColorConsole(true);
    1077       55303 :     else if (color == "on")
    1078       54846 :       Moose::setColorConsole(true, true);
    1079         457 :     else if (color == "off")
    1080         457 :       Moose::setColorConsole(false);
    1081             :     else
    1082             :       mooseAssert(false, "Should not hit");
    1083             : 
    1084             :     // After setting color so that non-yellow deprecated is honored
    1085      165909 :     if (getParam<bool>("no_color"))
    1086           0 :       mooseDeprecated("The --no-color flag is deprecated. Use '--color off' instead.");
    1087       55303 :   }
    1088             : 
    1089             : // If there's no threading model active, but the user asked for
    1090             : // --n-threads > 1 on the command line, throw a mooseError.  This is
    1091             : // intended to prevent situations where the user has potentially
    1092             : // built MOOSE incorrectly (neither TBB nor pthreads found) and is
    1093             : // asking for multiple threads, not knowing that there will never be
    1094             : // any threads launched.
    1095             : #if !LIBMESH_USING_THREADS
    1096             :   if (libMesh::command_line_value("--n-threads", 1) > 1)
    1097             :     mooseError("You specified --n-threads > 1, but there is no threading model active!");
    1098             : #endif
    1099             : 
    1100             :   // Capability checking
    1101             :   {
    1102             :     // Augment capabilities from the TestHarness
    1103       67547 :     std::optional<std::set<std::string>> ignore_capabilities;
    1104      202641 :     if (isParamValid("testharness_capabilities"))
    1105             :     {
    1106        1179 :       if (!isParamValid("required_capabilities"))
    1107           2 :         mooseError(
    1108             :             "--testharness-capabilities: Should not be specified without --required-capabilities");
    1109             : 
    1110             :       const auto file_path = std::filesystem::absolute(
    1111         782 :           std::filesystem::path(getParam<std::string>("testharness_capabilities")));
    1112             : 
    1113         391 :       std::ifstream file(file_path);
    1114         391 :       if (!file)
    1115           2 :         mooseError("--testharness-capabilities: Could not open ", file_path);
    1116             : 
    1117         389 :       nlohmann::json root;
    1118             :       try
    1119             :       {
    1120         389 :         file >> root;
    1121         387 :         if (const auto it = root.find("capabilities"); it != root.end())
    1122         383 :           Moose::internal::Capabilities::getCapabilities({}).augment(*it, {});
    1123         385 :         if (const auto it = root.find("ignore_capabilities"); it != root.end())
    1124           6 :           ignore_capabilities = it->get<std::set<std::string>>();
    1125             :       }
    1126           4 :       catch (const std::exception & e)
    1127             :       {
    1128           8 :         mooseError(
    1129           4 :             "--testharness-capabilities: Failed to load capabilities ", file_path, ":\n", e.what());
    1130           4 :       }
    1131         401 :     }
    1132             : 
    1133      202617 :     if (isParamValid("required_capabilities"))
    1134             :     {
    1135             :       using Moose::internal::CapabilityRegistry;
    1136             : 
    1137       15692 :       const auto & required_capabilities = getParam<std::string>("required_capabilities");
    1138             : 
    1139        7846 :       CapabilityRegistry::CheckOptions options;
    1140             :       // Allowed to be unknown
    1141        7846 :       options.certain = false;
    1142             :       // Add ignored capabilities, if any
    1143        7846 :       if (ignore_capabilities)
    1144           6 :         options.ignore_capabilities = *ignore_capabilities;
    1145             : 
    1146        7846 :       CapabilityRegistry::CheckResult result;
    1147             :       try
    1148             :       {
    1149       15692 :         result = Moose::internal::Capabilities::getCapabilities({}).check(required_capabilities,
    1150        7833 :                                                                           options);
    1151             :       }
    1152          13 :       catch (const std::exception & e)
    1153             :       {
    1154          13 :         mooseError("--required-capablities: ", e.what());
    1155           4 :       }
    1156             : 
    1157        7833 :       if (result.state < CapabilityRegistry::CheckState::UNKNOWN)
    1158             :       {
    1159          55 :         mooseInfo("Required capabilities '", required_capabilities, "' not fulfilled.");
    1160          55 :         _ready_to_exit = true;
    1161             :         // we use code 77 as "skip" in the Testharness
    1162          55 :         _exit_code = 77;
    1163          55 :         return;
    1164             :       }
    1165        7778 :       if (result.state == CapabilityRegistry::CheckState::UNKNOWN)
    1166           3 :         mooseError("Required capabilities '",
    1167             :                    required_capabilities,
    1168             :                    "' are not specific enough. A comparison test is performed on an undefined "
    1169             :                    "capability. Disambiguate this requirement by adding an existence/non-existence "
    1170             :                    "requirement. Example: 'unknown<1.2.3' should become 'unknown & unknown<1.2.3' "
    1171             :                    "or '!unknown | unknown<1.2.3'");
    1172        7893 :     }
    1173       67535 :   }
    1174             : 
    1175             :   // Build a minimal running application, ignoring the input file.
    1176      202404 :   if (getParam<bool>("minimal"))
    1177           8 :     createMinimalApp();
    1178             : 
    1179      202380 :   else if (getParam<bool>("display_version"))
    1180             :   {
    1181          24 :     Moose::out << getPrintableVersion() << std::endl;
    1182          24 :     _early_exit_param = "--version";
    1183          24 :     _ready_to_exit = true;
    1184          24 :     return;
    1185             :   }
    1186      202308 :   else if (getParam<bool>("help"))
    1187             :   {
    1188           3 :     _command_line->printUsage();
    1189           3 :     _early_exit_param = "--help";
    1190           3 :     _ready_to_exit = true;
    1191             :   }
    1192      337159 :   else if (getParam<bool>("dump") || isParamSetByUser("dump_search"))
    1193             :   {
    1194             :     const std::string search =
    1195          36 :         isParamSetByUser("dump_search") ? getParam<std::string>("dump_search") : "";
    1196             : 
    1197           9 :     JsonSyntaxTree tree(search);
    1198             : 
    1199             :     {
    1200          45 :       TIME_SECTION("dump", 1, "Building Syntax Tree");
    1201           9 :       _builder.buildJsonSyntaxTree(tree);
    1202           9 :     }
    1203             : 
    1204             :     // Check if second arg is valid or not
    1205           9 :     if ((tree.getRoot()).is_object())
    1206             :     {
    1207             :       // Turn off live printing so that it doesn't mess with the dump
    1208           6 :       _perf_graph.disableLivePrint();
    1209             : 
    1210           6 :       JsonInputFileFormatter formatter;
    1211           6 :       Moose::out << "\n### START DUMP DATA ###\n"
    1212           6 :                  << formatter.toString(tree.getRoot()) << "\n### END DUMP DATA ###" << std::endl;
    1213           6 :       _early_exit_param = "--dump";
    1214           6 :       _ready_to_exit = true;
    1215           6 :     }
    1216             :     else
    1217           3 :       mooseError("Search parameter '", search, "' was not found in the registered syntax.");
    1218           6 :   }
    1219      202272 :   else if (getParam<bool>("registry"))
    1220             :   {
    1221           9 :     _perf_graph.disableLivePrint();
    1222             : 
    1223           9 :     Moose::out << "Label\tType\tName\tClass\tFile\n";
    1224             : 
    1225           9 :     auto & objmap = Registry::allObjects();
    1226          27 :     for (auto & entry : objmap)
    1227       15105 :       for (auto & obj : entry.second)
    1228       30174 :         Moose::out << entry.first << "\tobject\t" << obj->name() << "\t" << obj->_classname << "\t"
    1229       15087 :                    << obj->_file << "\n";
    1230             : 
    1231           9 :     auto & actmap = Registry::allActions();
    1232          27 :     for (auto & entry : actmap)
    1233             :     {
    1234        2061 :       for (auto & act : entry.second)
    1235        2043 :         Moose::out << entry.first << "\taction\t" << act->_name << "\t" << act->_classname << "\t"
    1236        2043 :                    << act->_file << "\n";
    1237             :     }
    1238           9 :     _early_exit_param = "--registry";
    1239           9 :     _ready_to_exit = true;
    1240             :   }
    1241      202245 :   else if (getParam<bool>("registry_hit"))
    1242             :   {
    1243           9 :     _perf_graph.disableLivePrint();
    1244             : 
    1245           9 :     Moose::out << "### START REGISTRY DATA ###\n";
    1246             : 
    1247           9 :     hit::Section root("");
    1248          18 :     auto sec = new hit::Section("registry");
    1249           9 :     root.addChild(sec);
    1250          18 :     auto objsec = new hit::Section("objects");
    1251           9 :     sec->addChild(objsec);
    1252             : 
    1253           9 :     auto & objmap = Registry::allObjects();
    1254          27 :     for (auto & entry : objmap)
    1255       15105 :       for (auto & obj : entry.second)
    1256             :       {
    1257       30174 :         auto ent = new hit::Section("entry");
    1258       15087 :         objsec->addChild(ent);
    1259       30174 :         ent->addChild(new hit::Field("label", hit::Field::Kind::String, entry.first));
    1260       60348 :         ent->addChild(new hit::Field("type", hit::Field::Kind::String, "object"));
    1261       45261 :         ent->addChild(new hit::Field("name", hit::Field::Kind::String, obj->name()));
    1262       30174 :         ent->addChild(new hit::Field("class", hit::Field::Kind::String, obj->_classname));
    1263       45261 :         ent->addChild(new hit::Field("file", hit::Field::Kind::String, obj->_file));
    1264             :       }
    1265             : 
    1266          18 :     auto actsec = new hit::Section("actions");
    1267           9 :     sec->addChild(actsec);
    1268           9 :     auto & actmap = Registry::allActions();
    1269          27 :     for (auto & entry : actmap)
    1270        2061 :       for (auto & act : entry.second)
    1271             :       {
    1272        4086 :         auto ent = new hit::Section("entry");
    1273        2043 :         actsec->addChild(ent);
    1274        4086 :         ent->addChild(new hit::Field("label", hit::Field::Kind::String, entry.first));
    1275        8172 :         ent->addChild(new hit::Field("type", hit::Field::Kind::String, "action"));
    1276        4086 :         ent->addChild(new hit::Field("task", hit::Field::Kind::String, act->_name));
    1277        4086 :         ent->addChild(new hit::Field("class", hit::Field::Kind::String, act->_classname));
    1278        6129 :         ent->addChild(new hit::Field("file", hit::Field::Kind::String, act->_file));
    1279             :       }
    1280             : 
    1281           9 :     Moose::out << root.render();
    1282             : 
    1283           9 :     Moose::out << "\n### END REGISTRY DATA ###\n";
    1284           9 :     _early_exit_param = "--registry_hit";
    1285           9 :     _ready_to_exit = true;
    1286           9 :   }
    1287      337012 :   else if (getParam<bool>("yaml") || isParamSetByUser("yaml_search"))
    1288             :   {
    1289             :     const std::string search =
    1290          72 :         isParamSetByUser("yaml_search") ? getParam<std::string>("yaml_search") : "";
    1291          18 :     _perf_graph.disableLivePrint();
    1292             : 
    1293          18 :     _builder.initSyntaxFormatter(Moose::Builder::YAML, true);
    1294          18 :     _builder.buildFullTree(search);
    1295             : 
    1296          18 :     _early_exit_param = "--yaml";
    1297          18 :     _ready_to_exit = true;
    1298          18 :   }
    1299      336916 :   else if (getParam<bool>("json") || isParamSetByUser("json_search"))
    1300             :   {
    1301             :     const std::string search =
    1302          60 :         isParamSetByUser("json_search") ? getParam<std::string>("json_search") : "";
    1303          15 :     _perf_graph.disableLivePrint();
    1304             : 
    1305          15 :     JsonSyntaxTree tree(search);
    1306          15 :     _builder.buildJsonSyntaxTree(tree);
    1307             : 
    1308          75 :     outputMachineReadableData(
    1309          30 :         "json", "**START JSON DATA**\n", "\n**END JSON DATA**", tree.getRoot().dump(2));
    1310          15 :     _early_exit_param = "--json";
    1311          15 :     _ready_to_exit = true;
    1312          15 :   }
    1313      202119 :   else if (getParam<bool>("syntax"))
    1314             :   {
    1315           0 :     _perf_graph.disableLivePrint();
    1316             : 
    1317           0 :     std::multimap<std::string, Syntax::ActionInfo> syntax = _syntax.getAssociatedActions();
    1318           0 :     std::stringstream ss;
    1319           0 :     for (const auto & it : syntax)
    1320           0 :       ss << it.first << "\n";
    1321           0 :     outputMachineReadableData("syntax", "**START SYNTAX DATA**\n", "**END SYNTAX DATA**", ss.str());
    1322           0 :     _early_exit_param = "--syntax";
    1323           0 :     _ready_to_exit = true;
    1324           0 :   }
    1325      202119 :   else if (getParam<bool>("show_type"))
    1326             :   {
    1327           9 :     _perf_graph.disableLivePrint();
    1328             : 
    1329           9 :     Moose::out << "MooseApp Type: " << type() << std::endl;
    1330           9 :     _early_exit_param = "--show-type";
    1331           9 :     _ready_to_exit = true;
    1332             :   }
    1333      202092 :   else if (getParam<bool>("show_capabilities"))
    1334             :   {
    1335          19 :     _perf_graph.disableLivePrint();
    1336          95 :     outputMachineReadableData("show_capabilities",
    1337             :                               "**START JSON DATA**\n",
    1338             :                               "\n**END JSON DATA**",
    1339          38 :                               Moose::internal::Capabilities::getCapabilities({}).dump());
    1340          19 :     _ready_to_exit = true;
    1341             :   }
    1342      202035 :   else if (isParamValid("check_capabilities"))
    1343             :   {
    1344             :     using Moose::internal::CapabilityRegistry;
    1345             : 
    1346           6 :     _perf_graph.disableLivePrint();
    1347          12 :     const auto & capabilities = getParam<std::string>("check_capabilities");
    1348             : 
    1349           6 :     CapabilityRegistry::CheckResult result;
    1350             :     try
    1351             :     {
    1352          10 :       result = Moose::internal::Capabilities::getCapabilities({}).check(capabilities);
    1353             :     }
    1354           2 :     catch (const std::exception & e)
    1355             :     {
    1356           2 :       mooseError("--check-capablities: ", e.what());
    1357           2 :     }
    1358             : 
    1359           4 :     const bool pass = result.state == CapabilityRegistry::CheckState::CERTAIN_PASS;
    1360           4 :     _console << "Capabilities '" << capabilities << "' are " << (pass ? "" : "not ") << "fulfilled."
    1361           4 :              << std::endl;
    1362           4 :     _ready_to_exit = true;
    1363           4 :     if (!pass)
    1364           2 :       _exit_code = 77;
    1365           4 :     return;
    1366           6 :   }
    1367       67339 :   else if (!getInputFileNames().empty())
    1368             :   {
    1369      201981 :     if (isParamSetByUser("recover"))
    1370             :     {
    1371             :       // We need to set the flag manually here since the recover parameter is a string type (takes
    1372             :       // an optional filename)
    1373        4023 :       _recover = true;
    1374        8046 :       const auto & recover = getParam<std::string>("recover");
    1375        4023 :       if (recover.size())
    1376          39 :         _restart_recover_base = recover;
    1377             :     }
    1378             : 
    1379       67327 :     _builder.build();
    1380             : 
    1381             :     // Lambda to check for mutually exclusive parameters
    1382             :     auto isExclusiveParamSetByUser =
    1383      259876 :         [this](const std::vector<std::string> & group, const std::string & param)
    1384             :     {
    1385      259876 :       auto is_set = isParamSetByUser(param);
    1386      259876 :       if (is_set)
    1387       21905 :         for (const auto & p : group)
    1388       17524 :           if (p != param && isParamSetByUser(p))
    1389           0 :             mooseError("Parameters '" + p + "' and '" + param +
    1390             :                        "' are mutually exclusive. Please choose only one of them.");
    1391      259876 :       return is_set;
    1392       67157 :     };
    1393             : 
    1394             :     // The following parameters set the final task and so are mutually exclusive.
    1395             :     const std::vector<std::string> final_task_params = {
    1396      201471 :         "csg_only", "mesh_only", "split_mesh", "parse_neml2_only"};
    1397      134314 :     if (isExclusiveParamSetByUser(final_task_params, "csg_only"))
    1398             :     {
    1399             :       // Error checking on incompatible command line options
    1400          79 :       if (_distributed_mesh_on_command_line)
    1401           3 :         mooseError("--csg-only cannot be used in conjunction with --distributed-mesh");
    1402         152 :       const bool has_mesh_split = isParamSetByUser("split_file") || _use_split;
    1403          76 :       if (has_mesh_split)
    1404           3 :         mooseError("--csg-only is not compatible with any mesh splitting options");
    1405         219 :       if (isParamSetByUser("refinements"))
    1406           3 :         mooseError("--csg-only cannot be used in conjunction with -r refinements option");
    1407          70 :       if (!isUltimateMaster())
    1408           0 :         mooseError("--csg-only option cannot be used as a Subapp");
    1409          70 :       if (_recover)
    1410           3 :         mooseError("--csg-only option cannot be used in recovery mode");
    1411             : 
    1412         134 :       _syntax.registerTaskName("execute_csg_generators", true);
    1413         268 :       _syntax.addDependency("execute_csg_generators", "execute_mesh_generators");
    1414         268 :       _syntax.addDependency("recover_meta_data", "execute_csg_generators");
    1415             : 
    1416         134 :       _syntax.registerTaskName("csg_only", true);
    1417         268 :       _syntax.addDependency("csg_only", "recover_meta_data");
    1418         268 :       _syntax.addDependency("set_mesh_base", "csg_only");
    1419         201 :       _action_warehouse.setFinalTask("csg_only");
    1420             :     }
    1421      134156 :     else if (isExclusiveParamSetByUser(final_task_params, "mesh_only"))
    1422             :     {
    1423             :       // If we are looking to just check the input, there is no need to
    1424             :       // call MeshOnlyAction and generate a mesh
    1425        4213 :       if (_check_input)
    1426          33 :         _action_warehouse.setFinalTask("setup_mesh_complete");
    1427             :       else
    1428             :       {
    1429        8404 :         _syntax.registerTaskName("mesh_only", true);
    1430       16808 :         _syntax.addDependency("mesh_only", "setup_mesh_complete");
    1431       16808 :         _syntax.addDependency("determine_system_type", "mesh_only");
    1432       12606 :         _action_warehouse.setFinalTask("mesh_only");
    1433             :       }
    1434             :     }
    1435      125730 :     else if (isExclusiveParamSetByUser(final_task_params, "split_mesh"))
    1436             :     {
    1437          89 :       _split_mesh = true;
    1438         178 :       _syntax.registerTaskName("split_mesh", true);
    1439         356 :       _syntax.addDependency("split_mesh", "setup_mesh_complete");
    1440         356 :       _syntax.addDependency("determine_system_type", "split_mesh");
    1441         267 :       _action_warehouse.setFinalTask("split_mesh");
    1442             :     }
    1443      125552 :     else if (isExclusiveParamSetByUser(final_task_params, "parse_neml2_only"))
    1444             :     {
    1445           0 :       _syntax.registerTaskName("parse_neml2");
    1446           0 :       _syntax.addDependency("determine_system_type", "parse_neml2");
    1447           0 :       _action_warehouse.setFinalTask("parse_neml2");
    1448             :     }
    1449       67145 :     _action_warehouse.build();
    1450             : 
    1451             :     // Setup the AppFileBase for use by the Outputs or other systems that need output file info
    1452             :     {
    1453             :       // Extract the CommonOutputAction
    1454       67145 :       const auto common_actions = _action_warehouse.getActions<CommonOutputAction>();
    1455             :       mooseAssert(common_actions.size() <= 1, "Should not be more than one CommonOutputAction");
    1456       67145 :       const Action * common = common_actions.empty() ? nullptr : *common_actions.begin();
    1457             : 
    1458             :       // If file_base is set in CommonOutputAction through parsing input, obtain the file_base
    1459      201435 :       if (common && common->isParamValid("file_base"))
    1460             :       {
    1461       30612 :         _output_file_base = common->getParam<std::string>("file_base");
    1462       15306 :         _file_base_set_by_user = true;
    1463             :       }
    1464       51839 :       else if (isUltimateMaster())
    1465             :       {
    1466             :         // if this app is a master, we use the first input file name as the default file base.
    1467             :         // use proximate here because the input file is an absolute path
    1468       39631 :         const auto & base = getLastInputFileName();
    1469       39631 :         size_t pos = base.find_last_of('.');
    1470       39631 :         _output_file_base = base.substr(0, pos);
    1471             :         // Note: we did not append "_out" in the file base here because we do not want to
    1472             :         //       have it in between the input file name and the object name for Output/*
    1473             :         //       syntax.
    1474             :       }
    1475             :       // default file base for multiapps is set by MultiApp
    1476       67145 :     }
    1477       67145 :   }
    1478             :   // No input file provided but we have other arguments (so don't just show print usage)
    1479          36 :   else if (!isParamSetByUser("input_file") && _command_line->getArguments().size() > 2)
    1480             :   {
    1481             :     mooseAssert(getInputFileNames().empty(), "Should be empty");
    1482             : 
    1483           6 :     if (_check_input)
    1484           3 :       mooseError("You specified --check-input, but did not provide an input file. Add -i "
    1485             :                  "<inputfile> to your command line.");
    1486             : 
    1487           3 :     mooseError("No input files specified. Add -i <inputfile> to your command line.");
    1488             :   }
    1489          30 :   else if (isParamValid("language_server") && getParam<bool>("language_server"))
    1490             :   {
    1491           0 :     _perf_graph.disableLivePrint();
    1492             : 
    1493             :     // Reset output to the buffer what was cached before it was turned it off
    1494           0 :     if (!Moose::out.rdbuf() && _output_buffer_cache)
    1495           0 :       Moose::out.rdbuf(_output_buffer_cache);
    1496             : 
    1497             :     // Start a language server that communicates using an iostream connection
    1498           0 :     MooseServer moose_server(*this);
    1499             : 
    1500           0 :     moose_server.run();
    1501             : 
    1502           0 :     _early_exit_param = "--language-server";
    1503           0 :     _ready_to_exit = true;
    1504           0 :   }
    1505             : 
    1506             :   else /* The catch-all case for bad options or missing options, etc. */
    1507             :   {
    1508           6 :     _command_line->printUsage();
    1509           6 :     _early_exit_param = "bad or missing";
    1510           6 :     _ready_to_exit = true;
    1511           6 :     _exit_code = 1;
    1512             :   }
    1513             : 
    1514       67247 :   Moose::out << std::flush;
    1515       67346 : }
    1516             : 
    1517             : const std::vector<std::string> &
    1518      157033 : MooseApp::getInputFileNames() const
    1519             : {
    1520             :   mooseAssert(_parser, "Parser is not set");
    1521      157033 :   return _parser->getInputFileNames();
    1522             : }
    1523             : 
    1524             : const std::string &
    1525       39631 : MooseApp::getLastInputFileName() const
    1526             : {
    1527             :   mooseAssert(_parser, "Parser is not set");
    1528       39631 :   return _parser->getLastInputFileName();
    1529             : }
    1530             : 
    1531             : std::string
    1532      151251 : MooseApp::getOutputFileBase(bool for_non_moose_build_output) const
    1533             : {
    1534      151251 :   if (_file_base_set_by_user || for_non_moose_build_output || _multiapp_level)
    1535       49926 :     return _output_file_base;
    1536             :   else
    1537      101325 :     return _output_file_base + "_out";
    1538             : }
    1539             : 
    1540             : void
    1541       12208 : MooseApp::setOutputFileBase(const std::string & output_file_base)
    1542             : {
    1543       12208 :   _output_file_base = output_file_base;
    1544             : 
    1545             :   // Reset the file base in the outputs
    1546       12208 :   _output_warehouse.resetFileBase();
    1547             : 
    1548             :   // Reset the file base in multiapps (if they have been constructed yet)
    1549       12208 :   if (getExecutioner())
    1550           0 :     for (auto & multi_app : feProblem().getMultiAppWarehouse().getObjects())
    1551           0 :       multi_app->setAppOutputFileBase();
    1552             : 
    1553       12208 :   _file_base_set_by_user = true;
    1554       12208 : }
    1555             : 
    1556             : void
    1557       67324 : MooseApp::runInputFile()
    1558             : {
    1559      201972 :   TIME_SECTION("runInputFile", 3);
    1560             : 
    1561             :   // If early exit param has been set, then just return
    1562       67324 :   if (_ready_to_exit)
    1563         177 :     return;
    1564             : 
    1565       67147 :   _action_warehouse.executeAllActions();
    1566             : 
    1567      194490 :   if (isParamSetByUser("csg_only"))
    1568             :   {
    1569          64 :     _early_exit_param = "--csg-only";
    1570          64 :     _ready_to_exit = true;
    1571             :   }
    1572      194298 :   else if (isParamSetByUser("mesh_only"))
    1573             :   {
    1574        3763 :     _early_exit_param = "--mesh-only";
    1575        3763 :     _ready_to_exit = true;
    1576             :   }
    1577      183009 :   else if (isParamSetByUser("split_mesh"))
    1578             :   {
    1579          89 :     _early_exit_param = "--split-mesh";
    1580          89 :     _ready_to_exit = true;
    1581             :   }
    1582      182742 :   else if (isParamSetByUser("parse_neml2_only"))
    1583             :   {
    1584           0 :     _early_exit_param = "--parse-neml2-only";
    1585           0 :     _ready_to_exit = true;
    1586             :   }
    1587      182742 :   else if (getParam<bool>("list_constructed_objects"))
    1588             :   {
    1589             :     // TODO: ask multiapps for their constructed objects
    1590           0 :     _early_exit_param = "--list-constructed-objects";
    1591           0 :     _ready_to_exit = true;
    1592           0 :     std::stringstream ss;
    1593           0 :     for (const auto & obj : _factory.getConstructedObjects())
    1594           0 :       ss << obj << '\n';
    1595           0 :     outputMachineReadableData(
    1596           0 :         "list_constructed_objects", "**START OBJECT DATA**\n", "\n**END OBJECT DATA**", ss.str());
    1597           0 :   }
    1598       65029 : }
    1599             : 
    1600             : void
    1601       59771 : MooseApp::errorCheck()
    1602             : {
    1603       59771 :   bool warn = _enable_unused_check == WARN_UNUSED;
    1604       59771 :   bool err = _enable_unused_check == ERROR_UNUSED;
    1605             : 
    1606       59771 :   _builder.errorCheck(*_comm, warn, err);
    1607             : 
    1608             :   // Return early for mesh only mode, since we want error checking to run even though
    1609             :   // an executor is not created for this case
    1610      179232 :   if (isParamSetByUser("mesh_only"))
    1611           8 :     return;
    1612             : 
    1613       59736 :   if (!_executor.get() && !_executioner.get())
    1614             :   {
    1615           0 :     if (!_early_exit_param.empty())
    1616             :     {
    1617             :       mooseAssert(_check_input,
    1618             :                   "Something went wrong, we should only get here if _check_input is true.");
    1619           0 :       mooseError(
    1620             :           "Incompatible command line arguments provided. --check-input cannot be called with ",
    1621           0 :           _early_exit_param,
    1622             :           ".");
    1623             :     }
    1624             :     // We should never get here
    1625           0 :     mooseError("The Executor is being called without being initialized. This is likely "
    1626             :                "caused by "
    1627             :                "incompatible command line arguments");
    1628             :   }
    1629             : 
    1630       59736 :   auto apps = feProblem().getMultiAppWarehouse().getObjects();
    1631       67605 :   for (auto app : apps)
    1632       19437 :     for (unsigned int i = 0; i < app->numLocalApps(); i++)
    1633       19437 :       app->localApp(i)->errorCheck();
    1634       59733 : }
    1635             : 
    1636             : void
    1637       52746 : MooseApp::executeExecutioner()
    1638             : {
    1639      158238 :   TIME_SECTION("executeExecutioner", 3);
    1640             : 
    1641             :   // If ready to exit has been set, then just return
    1642       52746 :   if (_ready_to_exit)
    1643        4082 :     return;
    1644             : 
    1645             :   // run the simulation
    1646       48664 :   if (_use_executor && _executor)
    1647             :   {
    1648          18 :     LibmeshPetscCall(Moose::PetscSupport::petscSetupOutput(_command_line.get()));
    1649          18 :     _executor->init();
    1650          18 :     errorCheck();
    1651          18 :     auto result = _executor->exec();
    1652          18 :     if (!result.convergedAll())
    1653           0 :       mooseError(result.str());
    1654          18 :   }
    1655       48646 :   else if (_executioner)
    1656             :   {
    1657       48646 :     LibmeshPetscCall(Moose::PetscSupport::petscSetupOutput(_command_line.get()));
    1658       48646 :     _executioner->init();
    1659       48129 :     errorCheck();
    1660       48107 :     _executioner->execute();
    1661       47755 :     if (!_executioner->lastSolveConverged())
    1662         218 :       setExitCode(1);
    1663             :   }
    1664             :   else
    1665           0 :     mooseError("No executioner was specified (go fix your input file)");
    1666       51855 : }
    1667             : 
    1668             : bool
    1669     1526627 : MooseApp::isRecovering() const
    1670             : {
    1671     1526627 :   return _recover;
    1672             : }
    1673             : 
    1674             : bool
    1675      577261 : MooseApp::isRestarting() const
    1676             : {
    1677      577261 :   return _restart;
    1678             : }
    1679             : 
    1680             : bool
    1681      127718 : MooseApp::isSplitMesh() const
    1682             : {
    1683      127718 :   return _split_mesh;
    1684             : }
    1685             : 
    1686             : bool
    1687           0 : MooseApp::hasRestartRecoverFileBase() const
    1688             : {
    1689           0 :   return !_restart_recover_base.empty();
    1690             : }
    1691             : 
    1692             : bool
    1693           0 : MooseApp::hasRecoverFileBase() const
    1694             : {
    1695           0 :   mooseDeprecated("MooseApp::hasRecoverFileBase is deprecated, use "
    1696             :                   "MooseApp::hasRestartRecoverFileBase() instead.");
    1697           0 :   return !_restart_recover_base.empty();
    1698             : }
    1699             : 
    1700             : void
    1701     1028534 : MooseApp::registerRestartableNameWithFilter(const std::string & name,
    1702             :                                             Moose::RESTARTABLE_FILTER filter)
    1703             : {
    1704             :   using Moose::RESTARTABLE_FILTER;
    1705     1028534 :   switch (filter)
    1706             :   {
    1707     1028534 :     case RESTARTABLE_FILTER::RECOVERABLE:
    1708     1028534 :       _recoverable_data_names.insert(name);
    1709     1028534 :       break;
    1710           0 :     default:
    1711           0 :       mooseError("Unknown filter");
    1712             :   }
    1713     1028534 : }
    1714             : 
    1715             : std::vector<std::filesystem::path>
    1716       13013 : MooseApp::backup(const std::filesystem::path & folder_base)
    1717             : {
    1718       65065 :   TIME_SECTION("backup", 2, "Backing Up Application to File");
    1719             : 
    1720       13013 :   preBackup();
    1721             : 
    1722       13013 :   RestartableDataWriter writer(*this, _restartable_data);
    1723       26023 :   return writer.write(folder_base);
    1724       13010 : }
    1725             : 
    1726             : std::unique_ptr<Backup>
    1727       33662 : MooseApp::backup()
    1728             : {
    1729      168310 :   TIME_SECTION("backup", 2, "Backing Up Application");
    1730             : 
    1731       33662 :   RestartableDataWriter writer(*this, _restartable_data);
    1732             : 
    1733       33662 :   preBackup();
    1734             : 
    1735       33662 :   auto backup = std::make_unique<Backup>();
    1736       33662 :   packMeshBackup(*this, *backup);
    1737       33662 :   writer.write(*backup->header, *backup->data);
    1738             : 
    1739       67324 :   return backup;
    1740       33662 : }
    1741             : 
    1742             : bool
    1743        7895 : MooseApp::hasInitialBackupMesh() const
    1744             : {
    1745        7895 :   return hasInitialBackup() && !(*_initial_backup)->mesh_files.empty();
    1746             : }
    1747             : 
    1748             : void
    1749           2 : MooseApp::restoreMeshFromInitialBackup(MooseMesh & mesh)
    1750             : {
    1751             :   mooseAssert(hasInitialBackup(), "Missing initial backup");
    1752           2 :   _restored_initial_backup_mesh = restoreMeshBackup(*this, **_initial_backup, mesh);
    1753           2 : }
    1754             : 
    1755             : void
    1756        3767 : MooseApp::restore(const std::filesystem::path & folder_base, const bool for_restart)
    1757             : {
    1758       18835 :   TIME_SECTION("restore", 2, "Restoring Application from File");
    1759             : 
    1760        3767 :   const DataNames filter_names = for_restart ? getRecoverableData() : DataNames{};
    1761             : 
    1762        3767 :   _rd_reader.setInput(folder_base);
    1763        3767 :   _rd_reader.restore(filter_names);
    1764             : 
    1765        3734 :   postRestore(for_restart);
    1766        3734 : }
    1767             : 
    1768             : void
    1769        9705 : MooseApp::restore(std::unique_ptr<Backup> backup, const bool for_restart)
    1770             : {
    1771       48525 :   TIME_SECTION("restore", 2, "Restoring Application");
    1772             : 
    1773        9705 :   const DataNames filter_names = for_restart ? getRecoverableData() : DataNames{};
    1774             : 
    1775        9705 :   if (!backup)
    1776           0 :     mooseError("MooseApp::restore(): Provided backup is not initialized");
    1777             : 
    1778        9705 :   auto header = std::move(backup->header);
    1779             :   mooseAssert(header, "Header not available");
    1780             : 
    1781        9705 :   auto data = std::move(backup->data);
    1782             :   mooseAssert(data, "Data not available");
    1783             : 
    1784        9705 :   if (restoreMeshBackup(*this, *backup, feProblem().mesh()))
    1785             :   {
    1786           0 :     _restored_initial_backup_mesh = true;
    1787           0 :     feProblem().mesh().prepare(/*mesh_to_clone=*/nullptr);
    1788           0 :     feProblem().meshChanged(/*intermediate_change=*/false,
    1789             :                             /*contract_mesh=*/false,
    1790             :                             /*clean_refinement_flags=*/false);
    1791             :   }
    1792             : 
    1793        9705 :   _rd_reader.setInput(std::move(header), std::move(data));
    1794        9705 :   _rd_reader.restore(filter_names);
    1795             : 
    1796        9705 :   postRestore(for_restart);
    1797        9705 : }
    1798             : 
    1799             : void
    1800         781 : MooseApp::restoreFromInitialBackup(const bool for_restart)
    1801             : {
    1802             :   mooseAssert(hasInitialBackup(), "Missing initial backup");
    1803         781 :   restore(std::move(*_initial_backup), for_restart);
    1804         781 : }
    1805             : 
    1806             : std::unique_ptr<Backup>
    1807       13439 : MooseApp::finalizeRestore()
    1808             : {
    1809       13439 :   if (!_rd_reader.isRestoring())
    1810           0 :     mooseError("MooseApp::finalizeRestore(): Not currently restoring");
    1811             : 
    1812             :   // This gives us access to the underlying streams so that we can return it if needed
    1813       13439 :   auto input_streams = _rd_reader.clear();
    1814             : 
    1815       13439 :   std::unique_ptr<Backup> backup;
    1816             : 
    1817             :   // Give them back a backup if this restore started from a Backup, in which case
    1818             :   // the two streams in the Backup are formed into StringInputStreams
    1819       13439 :   if (auto header_string_input = dynamic_cast<StringInputStream *>(input_streams.header.get()))
    1820             :   {
    1821        9705 :     auto data_string_input = dynamic_cast<StringInputStream *>(input_streams.data.get());
    1822             :     mooseAssert(data_string_input, "Should also be a string input");
    1823             : 
    1824        9705 :     auto header_sstream = header_string_input->release();
    1825             :     mooseAssert(header_sstream, "Header not available");
    1826             : 
    1827        9705 :     auto data_sstream = data_string_input->release();
    1828             :     mooseAssert(data_sstream, "Data not available");
    1829             : 
    1830        9705 :     backup = std::make_unique<Backup>();
    1831        9705 :     backup->header = std::move(header_sstream);
    1832        9705 :     backup->data = std::move(data_sstream);
    1833        9705 :     packMeshBackup(*this, *backup);
    1834        9705 :   }
    1835             : 
    1836       26878 :   return backup;
    1837       13439 : }
    1838             : 
    1839             : void
    1840         429 : MooseApp::setCheckUnusedFlag(bool warn_is_error)
    1841             : {
    1842         429 :   _enable_unused_check = warn_is_error ? ERROR_UNUSED : WARN_UNUSED;
    1843         429 : }
    1844             : 
    1845             : void
    1846           0 : MooseApp::disableCheckUnusedFlag()
    1847             : {
    1848           0 :   _enable_unused_check = OFF;
    1849           0 : }
    1850             : 
    1851             : FEProblemBase &
    1852     9830865 : MooseApp::feProblem() const
    1853             : {
    1854             :   mooseAssert(_executor.get() || _executioner.get(), "No executioner yet, calling too early!");
    1855     9830865 :   return _executor.get() ? _executor->feProblem() : _executioner->feProblem();
    1856             : }
    1857             : 
    1858             : void
    1859          51 : MooseApp::addExecutor(const std::string & type,
    1860             :                       const std::string & name,
    1861             :                       const InputParameters & params)
    1862             : {
    1863          51 :   std::shared_ptr<Executor> executor = _factory.create<Executor>(type, name, params);
    1864             : 
    1865          51 :   if (_executors.count(executor->name()) > 0)
    1866           0 :     mooseError("an executor with name '", executor->name(), "' already exists");
    1867          51 :   _executors[executor->name()] = executor;
    1868          51 : }
    1869             : 
    1870             : void
    1871          57 : MooseApp::addExecutorParams(const std::string & type,
    1872             :                             const std::string & name,
    1873             :                             const InputParameters & params)
    1874             : {
    1875          57 :   _executor_params[name] = std::make_pair(type, std::make_unique<InputParameters>(params));
    1876          57 : }
    1877             : 
    1878             : const Parser &
    1879     1636310 : MooseApp::parser() const
    1880             : {
    1881             :   mooseAssert(_parser, "Not set");
    1882     1636310 :   return *_parser;
    1883             : }
    1884             : 
    1885             : Parser &
    1886     1636310 : MooseApp::parser()
    1887             : {
    1888     1636310 :   return const_cast<Parser &>(std::as_const(*this).parser());
    1889             : }
    1890             : 
    1891             : void
    1892          60 : MooseApp::recursivelyCreateExecutors(const std::string & current_executor_name,
    1893             :                                      std::list<std::string> & possible_roots,
    1894             :                                      std::list<std::string> & current_branch)
    1895             : {
    1896             :   // Did we already make this one?
    1897          60 :   if (_executors.find(current_executor_name) != _executors.end())
    1898           0 :     return;
    1899             : 
    1900             :   // Is this one already on the current branch (i.e. there is a cycle)
    1901          60 :   if (std::find(current_branch.begin(), current_branch.end(), current_executor_name) !=
    1902         120 :       current_branch.end())
    1903             :   {
    1904           3 :     std::stringstream exec_names_string;
    1905             : 
    1906           3 :     auto branch_it = current_branch.begin();
    1907             : 
    1908           3 :     exec_names_string << *branch_it++;
    1909             : 
    1910           6 :     for (; branch_it != current_branch.end(); ++branch_it)
    1911           3 :       exec_names_string << ", " << *branch_it;
    1912             : 
    1913           3 :     exec_names_string << ", " << current_executor_name;
    1914             : 
    1915           3 :     mooseError("Executor cycle detected: ", exec_names_string.str());
    1916           0 :   }
    1917             : 
    1918          57 :   current_branch.push_back(current_executor_name);
    1919             : 
    1920             :   // Build the dependencies first
    1921          57 :   const auto & params = *_executor_params[current_executor_name].second;
    1922             : 
    1923        2661 :   for (const auto & param : params)
    1924             :   {
    1925        2610 :     if (params.have_parameter<ExecutorName>(param.first))
    1926             :     {
    1927         111 :       const auto & dependency_name = params.get<ExecutorName>(param.first);
    1928             : 
    1929         111 :       possible_roots.remove(dependency_name);
    1930             : 
    1931         111 :       if (!dependency_name.empty())
    1932          33 :         recursivelyCreateExecutors(dependency_name, possible_roots, current_branch);
    1933             :     }
    1934             :   }
    1935             : 
    1936             :   // Add this Executor
    1937          51 :   const auto & type = _executor_params[current_executor_name].first;
    1938          51 :   addExecutor(type, current_executor_name, params);
    1939             : 
    1940          51 :   current_branch.pop_back();
    1941             : }
    1942             : 
    1943             : void
    1944       62276 : MooseApp::createExecutors()
    1945             : {
    1946             :   // Do we have any?
    1947       62276 :   if (_executor_params.empty())
    1948       62252 :     return;
    1949             : 
    1950             :   // Holds the names of Executors that may be the root executor
    1951          24 :   std::list<std::string> possibly_root;
    1952             : 
    1953             :   // What is already built
    1954          24 :   std::map<std::string, bool> already_built;
    1955             : 
    1956             :   // The Executors that are currently candidates for being roots
    1957          24 :   std::list<std::string> possible_roots;
    1958             : 
    1959             :   // The current line of dependencies - used for finding cycles
    1960          24 :   std::list<std::string> current_branch;
    1961             : 
    1962             :   // Build the NullExecutor
    1963             :   {
    1964          48 :     auto params = _factory.getValidParams("NullExecutor");
    1965          96 :     _null_executor = _factory.create<NullExecutor>("NullExecutor", "_null_executor", params);
    1966          24 :   }
    1967             : 
    1968          72 :   for (const auto & params_entry : _executor_params)
    1969             :   {
    1970          51 :     const auto & name = params_entry.first;
    1971             : 
    1972             :     // Did we already make this one?
    1973          51 :     if (_executors.find(name) != _executors.end())
    1974          24 :       continue;
    1975             : 
    1976          27 :     possible_roots.emplace_back(name);
    1977             : 
    1978          27 :     recursivelyCreateExecutors(name, possible_roots, current_branch);
    1979             :   }
    1980             : 
    1981             :   // If there is more than one possible root - error
    1982          21 :   if (possible_roots.size() > 1)
    1983             :   {
    1984           3 :     auto root_string_it = possible_roots.begin();
    1985             : 
    1986           3 :     std::stringstream roots_string;
    1987             : 
    1988           3 :     roots_string << *root_string_it++;
    1989             : 
    1990           6 :     for (; root_string_it != possible_roots.end(); ++root_string_it)
    1991           3 :       roots_string << ", " << *root_string_it;
    1992             : 
    1993           3 :     mooseError("Multiple Executor roots found: ", roots_string.str());
    1994           0 :   }
    1995             : 
    1996             :   // Set the root executor
    1997          18 :   _executor = _executors[possible_roots.front()];
    1998          18 : }
    1999             : 
    2000             : Executor &
    2001          24 : MooseApp::getExecutor(const std::string & name, bool fail_if_not_found)
    2002             : {
    2003          24 :   auto it = _executors.find(name);
    2004             : 
    2005          24 :   if (it != _executors.end())
    2006          24 :     return *it->second;
    2007             : 
    2008           0 :   if (fail_if_not_found)
    2009           0 :     mooseError("Executor not found: ", name);
    2010             : 
    2011           0 :   return *_null_executor;
    2012             : }
    2013             : 
    2014             : Executioner *
    2015     4104289 : MooseApp::getExecutioner() const
    2016             : {
    2017     4104289 :   return _executioner.get() ? _executioner.get() : _executor.get();
    2018             : }
    2019             : 
    2020             : void
    2021       65624 : MooseApp::setErrorOverridden()
    2022             : {
    2023       65624 :   _error_overridden = true;
    2024       65624 : }
    2025             : 
    2026             : void
    2027       55315 : MooseApp::run()
    2028             : {
    2029      165945 :   TIME_SECTION("run", 3);
    2030      165945 :   if (getParam<bool>("show_docs"))
    2031             :   {
    2032           0 :     auto binname = appBinaryName();
    2033           0 :     if (binname == "")
    2034           0 :       mooseError("could not locate installed tests to run (unresolved binary/app name)");
    2035           0 :     auto docspath = MooseUtils::docsDir(binname);
    2036           0 :     if (docspath == "")
    2037           0 :       mooseError("no installed documentation found");
    2038             : 
    2039           0 :     auto docmsgfile = MooseUtils::pathjoin(docspath, "docmsg.txt");
    2040           0 :     std::string docmsg = "file://" + MooseUtils::realpath(docspath) + "/index.html";
    2041           0 :     if (MooseUtils::pathExists(docmsgfile) && MooseUtils::checkFileReadable(docmsgfile))
    2042             :     {
    2043           0 :       std::ifstream ifs(docmsgfile);
    2044             :       std::string content((std::istreambuf_iterator<char>(ifs)),
    2045           0 :                           (std::istreambuf_iterator<char>()));
    2046           0 :       content.replace(content.find("$LOCAL_SITE_HOME"), content.length(), docmsg);
    2047           0 :       docmsg = content;
    2048           0 :     }
    2049             : 
    2050           0 :     Moose::out << docmsg << "\n";
    2051           0 :     _early_exit_param = "--docs";
    2052           0 :     _ready_to_exit = true;
    2053           0 :     return;
    2054           0 :   }
    2055             : 
    2056       55315 :   if (showInputs() || copyInputs() || runInputs())
    2057             :   {
    2058           9 :     _early_exit_param = "--show-input, --copy-inputs, or --run";
    2059           9 :     _ready_to_exit = true;
    2060           9 :     return;
    2061             :   }
    2062             : 
    2063             :   try
    2064             :   {
    2065      276515 :     TIME_SECTION("setup", 2, "Setting Up");
    2066       55303 :     setupOptions();
    2067       55083 :     runInputFile();
    2068       52840 :   }
    2069          38 :   catch (Parser::Error & err)
    2070             :   {
    2071             :     mooseAssert(_parser->getThrowOnError(), "Should be true");
    2072           2 :     throw;
    2073           2 :   }
    2074          24 :   catch (MooseRuntimeError & err)
    2075             :   {
    2076             :     mooseAssert(Moose::_throw_on_error, "Should be true");
    2077          24 :     throw;
    2078          24 :   }
    2079          12 :   catch (std::exception & err)
    2080             :   {
    2081          12 :     mooseError(err.what());
    2082           0 :   }
    2083             : 
    2084       52802 :   if (!_check_input)
    2085             :   {
    2086      263730 :     TIME_SECTION("execute", 2, "Executing");
    2087       52746 :     executeExecutioner();
    2088       51855 :   }
    2089             :   else
    2090             :   {
    2091          56 :     errorCheck();
    2092             :     // Output to stderr, so it is easier for peacock to get the result
    2093          51 :     Moose::err << "Syntax OK" << std::endl;
    2094             :   }
    2095             : 
    2096      155718 :   if (isParamSetByUser("citations"))
    2097          33 :     requestCitations();
    2098       51943 : }
    2099             : 
    2100             : void
    2101          48 : MooseApp::collectCitations(std::map<std::string, std::string> & citations) const
    2102             : {
    2103             :   // Gather the citations that apply to this app: for every object type actually constructed, the
    2104             :   // citations registered for its owning app/module. The framework paper is tied to "MooseApp", so
    2105             :   // it is gathered whenever a MooseApp object is used; apps composed of MooseApp inherit it. The
    2106             :   // map is keyed by BibTeX key so a citation shared across apps is folded in only once.
    2107         696 :   for (const auto & objname : _factory.getConstructedObjects())
    2108             :   {
    2109             :     mooseAssert(Registry::isRegisteredObj(objname),
    2110             :                 "Constructed object '" + objname + "' is not registered");
    2111         648 :     const auto & app_citations = Registry::getCitations(Registry::objData(objname)._label);
    2112         648 :     citations.insert(app_citations.begin(), app_citations.end());
    2113          48 :   }
    2114             : 
    2115             :   // Credit the finite element backend actually used in the run. These are mutually exclusive, so
    2116             :   // only the backend in use is cited.
    2117          48 :   std::string backend = "libMesh";
    2118             : #ifdef MOOSE_MFEM_ENABLED
    2119          36 :   if ((_executor || _executioner) && feProblem().feBackend() == Moose::FEBackend::MFEM)
    2120           6 :     backend = "MFEM";
    2121             : #endif
    2122          48 :   const auto & backend_citations = Registry::getCitations(backend);
    2123          48 :   citations.insert(backend_citations.begin(), backend_citations.end());
    2124             : 
    2125             :   // Recurse into the MultiApp subapps so that objects/modules used only inside subapps are still
    2126             :   // attributed. Each subapp is a separate MooseApp whose run() (and thus requestCitations()) is
    2127             :   // never called, so the master gathers their citations here. feProblem() asserts when there is no
    2128             :   // executioner, so only descend once one exists; nested MultiApps are handled by the recursion.
    2129          48 :   if (_executor || _executioner)
    2130          63 :     for (const auto & multi_app : feProblem().getMultiAppWarehouse().getObjects())
    2131          30 :       for (const auto i : make_range(multi_app->numLocalApps()))
    2132          15 :         multi_app->localApp(i)->collectCitations(citations);
    2133          48 : }
    2134             : 
    2135             : void
    2136          33 : MooseApp::requestCitations()
    2137             : {
    2138             :   // Collect the de-duplicated citations across this app and, recursively, every MultiApp subapp.
    2139          33 :   std::map<std::string, std::string> citations;
    2140          33 :   collectCitations(citations);
    2141             : 
    2142             :   // MultiApp subapps are distributed across the MPI ranks, so each rank has collected citations
    2143             :   // only for the subapps it owns. PETSc prints the citation list from rank 0 alone, so gather every
    2144             :   // rank's citations onto all ranks; otherwise a module used only by a subapp that lives off rank 0
    2145             :   // would be omitted.
    2146          33 :   std::vector<std::string> flattened;
    2147          33 :   flattened.reserve(citations.size() * 2);
    2148         105 :   for (const auto & [key, bibtex] : citations)
    2149             :   {
    2150          72 :     flattened.push_back(key);
    2151          72 :     flattened.push_back(bibtex);
    2152             :   }
    2153          33 :   _comm->allgather(flattened);
    2154         153 :   for (std::size_t i = 0; i + 1 < flattened.size(); i += 2)
    2155             :   {
    2156         120 :     [[maybe_unused]] const auto [it, inserted] = citations.emplace(flattened[i], flattened[i + 1]);
    2157             :     mooseAssert(inserted || it->second == flattened[i + 1],
    2158             :                 "The same citation key was registered with different BibTeX entries");
    2159             :   }
    2160             : 
    2161             :   // Register the resolved BibTeX entries with PETSc and enable its -citations option. PETSc prints
    2162             :   // them, together with the run-specific citations from any PETSc solvers/preconditioners actually
    2163             :   // used, at PetscFinalize (to the console or, if a file name was given, to that file).
    2164         105 :   for (const auto & citation : citations)
    2165          72 :     Moose::PetscSupport::registerPetscCitation(citation.second);
    2166             : 
    2167         132 :   Moose::PetscSupport::setSinglePetscOption("-citations", getParam<std::string>("citations"));
    2168          33 : }
    2169             : 
    2170             : bool
    2171       55315 : MooseApp::showInputs() const
    2172             : {
    2173      165945 :   if (getParam<bool>("show_inputs"))
    2174             :   {
    2175           6 :     const auto show_inputs_syntax = _pars.getCommandLineMetadata("show_inputs").switches;
    2176           3 :     std::vector<std::string> dirs;
    2177           3 :     const auto installable_inputs = getInstallableInputs();
    2178             : 
    2179           3 :     if (installable_inputs == "")
    2180             :     {
    2181             :       Moose::out
    2182             :           << "Show inputs has not been overriden in this application.\nContact the developers of "
    2183           0 :              "this appication and request that they override \"MooseApp::getInstallableInputs\".\n";
    2184             :     }
    2185             :     else
    2186             :     {
    2187             :       mooseAssert(!show_inputs_syntax.empty(), "show_inputs sytnax should not be empty");
    2188             : 
    2189           3 :       MooseUtils::tokenize(installable_inputs, dirs, 1, " ");
    2190           3 :       Moose::out << "The following directories are installable into a user-writeable directory:\n\n"
    2191           3 :                  << installable_inputs << '\n'
    2192           3 :                  << "\nTo install one or more directories of inputs, execute the binary with the \""
    2193           3 :                  << show_inputs_syntax[0] << "\" flag. e.g.:\n$ "
    2194           6 :                  << _command_line->getExecutableName() << ' ' << show_inputs_syntax[0] << ' '
    2195           3 :                  << dirs[0] << '\n';
    2196             :     }
    2197           3 :     return true;
    2198           3 :   }
    2199       55312 :   return false;
    2200             : }
    2201             : 
    2202             : std::string
    2203           0 : MooseApp::getInstallableInputs() const
    2204             : {
    2205           0 :   return "tests";
    2206             : }
    2207             : 
    2208             : bool
    2209       55312 : MooseApp::copyInputs()
    2210             : {
    2211      165936 :   if (isParamSetByUser("copy_inputs"))
    2212             :   {
    2213           6 :     if (comm().size() > 1)
    2214           0 :       mooseError("The --copy-inputs option should not be ran in parallel");
    2215             : 
    2216             :     // Get command line argument following --copy-inputs on command line
    2217          12 :     auto dir_to_copy = getParam<std::string>("copy_inputs");
    2218             : 
    2219           6 :     if (dir_to_copy.empty())
    2220           0 :       mooseError("Error retrieving directory to copy");
    2221           6 :     if (dir_to_copy.back() != '/')
    2222           6 :       dir_to_copy += '/';
    2223             : 
    2224             :     // This binary name is the actual binary. That is, if we called a symlink it'll
    2225             :     // be the name of what the symlink points to
    2226           6 :     auto binname = appBinaryName();
    2227           6 :     if (binname == "")
    2228           0 :       mooseError("could not locate installed tests to run (unresolved binary/app name)");
    2229             : 
    2230             :     auto src_dir = MooseUtils::installedInputsDir(
    2231             :         binname,
    2232             :         dir_to_copy,
    2233          18 :         "Rerun binary with " + _pars.getCommandLineMetadata("show_inputs").switches[0] +
    2234           6 :             " to get a list of installable directories.");
    2235             : 
    2236             :     // Use the command line here because if we have a symlink to another binary,
    2237             :     // we want to dump into a directory that is named after the symlink not the true binary
    2238           6 :     auto dst_dir = _command_line->getExecutableNameBase() + "/" + dir_to_copy;
    2239           6 :     auto cmdname = _command_line->getExecutableName();
    2240           6 :     if (cmdname.find_first_of("/") != std::string::npos)
    2241           0 :       cmdname = cmdname.substr(cmdname.find_first_of("/") + 1, std::string::npos);
    2242             : 
    2243           6 :     if (MooseUtils::pathExists(dst_dir))
    2244           3 :       mooseError(
    2245             :           "The directory \"./",
    2246             :           dst_dir,
    2247             :           "\" already exists.\nTo update/recopy the contents of this directory, rename (\"mv ",
    2248             :           dst_dir,
    2249             :           " new_dir_name\") or remove (\"rm -r ",
    2250             :           dst_dir,
    2251             :           "\") the existing directory.\nThen re-run \"",
    2252             :           cmdname,
    2253             :           " --copy-inputs ",
    2254             :           dir_to_copy,
    2255             :           "\".");
    2256             : 
    2257           3 :     std::string cmd = "mkdir -p " + dst_dir + "; rsync -av " + src_dir + " " + dst_dir;
    2258             : 
    2259          15 :     TIME_SECTION("copy_inputs", 2, "Copying Inputs");
    2260             : 
    2261             :     mooseAssert(comm().size() == 1, "Should be run in serial");
    2262           3 :     const auto return_value = system(cmd.c_str());
    2263           3 :     if (!WIFEXITED(return_value))
    2264           0 :       mooseError("Process exited unexpectedly");
    2265           3 :     setExitCode(WEXITSTATUS(return_value));
    2266           3 :     if (exitCode() == 0)
    2267           3 :       Moose::out << "Directory successfully copied into ./" << dst_dir << '\n';
    2268           3 :     return true;
    2269           3 :   }
    2270       55306 :   return false;
    2271             : }
    2272             : 
    2273             : bool
    2274       55306 : MooseApp::runInputs()
    2275             : {
    2276      165918 :   if (isParamSetByUser("run"))
    2277             :   {
    2278           3 :     if (comm().size() > 1)
    2279           0 :       mooseError("The --run option should not be ran in parallel");
    2280             : 
    2281             :     // Pass everything after --run on the cli to the TestHarness
    2282           6 :     const auto find_run_it = std::as_const(*_command_line).findCommandLineParam("run");
    2283           3 :     const auto & cl_entries = std::as_const(*_command_line).getEntries();
    2284             :     mooseAssert(find_run_it != cl_entries.end(), "Didn't find the option");
    2285           3 :     std::string test_args;
    2286           6 :     for (auto it = std::next(find_run_it); it != cl_entries.end(); ++it)
    2287           6 :       for (const auto & arg : it->raw_args)
    2288             :       {
    2289           3 :         test_args += " " + arg;
    2290           3 :         libMesh::add_command_line_name(arg);
    2291             :       }
    2292             : 
    2293           3 :     auto working_dir = MooseUtils::getCurrentWorkingDir();
    2294           3 :     if (MooseUtils::findTestRoot() == "")
    2295             :     {
    2296           0 :       auto bin_name = appBinaryName();
    2297           0 :       if (bin_name == "")
    2298           0 :         mooseError("Could not locate binary name relative to installed location");
    2299             : 
    2300           0 :       auto cmd_name = Moose::getExecutableName();
    2301           0 :       mooseError(
    2302             :           "Could not locate installed tests from the current working directory:",
    2303             :           working_dir,
    2304             :           ".\nMake sure you are executing this command from within a writable installed inputs ",
    2305             :           "directory.\nRun \"",
    2306             :           cmd_name,
    2307             :           " --copy-inputs <dir>\" to copy the contents of <dir> to a \"./",
    2308             :           bin_name,
    2309             :           "_<dir>\" directory.\nChange into that directory and try \"",
    2310             :           cmd_name,
    2311             :           " --run <dir>\" again.");
    2312           0 :     }
    2313             : 
    2314             :     // Set this application as the app name for the moose_test_runner script that we're running
    2315           3 :     setenv("MOOSE_TEST_RUNNER_APP_NAME", appBinaryName().c_str(), true);
    2316             : 
    2317           3 :     const std::string cmd = MooseUtils::runTestsExecutable() + test_args;
    2318           3 :     Moose::out << "Working Directory: " << working_dir << "\nRunning Command: " << cmd << std::endl;
    2319             :     mooseAssert(comm().size() == 1, "Should be run in serial");
    2320           3 :     const auto return_value = system(cmd.c_str());
    2321           3 :     if (!WIFEXITED(return_value))
    2322           0 :       mooseError("Process exited unexpectedly");
    2323           3 :     setExitCode(WEXITSTATUS(return_value));
    2324           3 :     return true;
    2325           3 :   }
    2326             : 
    2327       55303 :   return false;
    2328             : }
    2329             : 
    2330             : Moose::Capability &
    2331      472264 : MooseApp::addCapabilityInternal(const std::string_view capability,
    2332             :                                 const Moose::Capability::Value & value,
    2333             :                                 const std::string_view doc)
    2334             : {
    2335             :   try
    2336             :   {
    2337      472264 :     return Moose::internal::Capabilities::getCapabilities({}).add(capability, value, doc);
    2338             :   }
    2339           6 :   catch (const std::exception & e)
    2340             :   {
    2341           6 :     ::mooseError(e.what());
    2342           6 :   }
    2343             : }
    2344             : 
    2345             : void
    2346        3429 : MooseApp::setOutputPosition(const Point & p)
    2347             : {
    2348        3429 :   _output_position_set = true;
    2349        3429 :   _output_position = p;
    2350        3429 :   _output_warehouse.meshChanged();
    2351             : 
    2352        3429 :   if (_executioner.get())
    2353          70 :     _executioner->parentOutputPositionChanged();
    2354        3429 : }
    2355             : 
    2356             : std::list<std::string>
    2357        3286 : MooseApp::getCheckpointDirectories() const
    2358             : {
    2359             :   // Storage for the directory names
    2360        3286 :   std::list<std::string> checkpoint_dirs;
    2361             : 
    2362             :   // Add the directories added with Outputs/checkpoint=true input syntax
    2363        3286 :   checkpoint_dirs.push_back(getOutputFileBase() + "_cp");
    2364             : 
    2365             :   // Add the directories from any existing checkpoint output objects
    2366        6572 :   const auto & actions = _action_warehouse.getActionListByName("add_output");
    2367       19902 :   for (const auto & action : actions)
    2368             :   {
    2369             :     // Get the parameters from the MooseObjectAction
    2370       16616 :     MooseObjectAction * moose_object_action = dynamic_cast<MooseObjectAction *>(action);
    2371       16616 :     if (!moose_object_action)
    2372        3384 :       continue;
    2373             : 
    2374       13232 :     const InputParameters & params = moose_object_action->getObjectParams();
    2375       39696 :     if (moose_object_action->getParam<std::string>("type") == "Checkpoint")
    2376             :     {
    2377             :       // Unless file_base was explicitly set by user, we cannot rely on it, as it will be changed
    2378             :       // later
    2379             :       const std::string cp_dir =
    2380          14 :           _file_base_set_by_user ? params.get<std::string>("file_base")
    2381          32 :                                  : (getOutputFileBase(true) + "_" + moose_object_action->name());
    2382          23 :       checkpoint_dirs.push_back(cp_dir + "_cp");
    2383          23 :     }
    2384             :   }
    2385        3286 :   return checkpoint_dirs;
    2386           0 : }
    2387             : 
    2388             : std::list<std::string>
    2389        3286 : MooseApp::getCheckpointFiles() const
    2390             : {
    2391        3286 :   auto checkpoint_dirs = getCheckpointDirectories();
    2392        6572 :   return MooseUtils::getFilesInDirs(checkpoint_dirs, false);
    2393        3286 : }
    2394             : 
    2395             : void
    2396        4014 : MooseApp::setStartTime(Real time)
    2397             : {
    2398        4014 :   _start_time_set = true;
    2399        4014 :   _start_time = time;
    2400        4014 : }
    2401             : 
    2402             : std::string
    2403          36 : MooseApp::getFileName(bool stripLeadingPath) const
    2404             : {
    2405          36 :   return _builder.getPrimaryFileName(stripLeadingPath);
    2406             : }
    2407             : 
    2408             : OutputWarehouse &
    2409    31687333 : MooseApp::getOutputWarehouse()
    2410             : {
    2411    31687333 :   return _output_warehouse;
    2412             : }
    2413             : 
    2414             : const OutputWarehouse &
    2415       89667 : MooseApp::getOutputWarehouse() const
    2416             : {
    2417       89667 :   return _output_warehouse;
    2418             : }
    2419             : 
    2420             : std::string
    2421          10 : MooseApp::appNameToLibName(const std::string & app_name) const
    2422             : {
    2423          10 :   std::string library_name(app_name);
    2424             : 
    2425             :   // Strip off the App part (should always be the last 3 letters of the name)
    2426          10 :   size_t pos = library_name.find("App");
    2427          10 :   if (pos != library_name.length() - 3)
    2428           0 :     mooseError("Invalid application name: ", library_name);
    2429          10 :   library_name.erase(pos);
    2430             : 
    2431             :   // Now get rid of the camel case, prepend lib, and append the method and suffix
    2432          40 :   return std::string("lib") + MooseUtils::camelCaseToUnderscore(library_name) + '-' +
    2433          20 :          QUOTE(METHOD) + ".la";
    2434          10 : }
    2435             : 
    2436             : std::string
    2437           0 : MooseApp::libNameToAppName(const std::string & library_name) const
    2438             : {
    2439           0 :   std::string app_name(library_name);
    2440             : 
    2441             :   // Strip off the leading "lib" and trailing ".la"
    2442           0 :   if (pcrecpp::RE("lib(.+?)(?:-\\w+)?\\.la").Replace("\\1", &app_name) == 0)
    2443           0 :     mooseError("Invalid library name: ", app_name);
    2444             : 
    2445           0 :   return MooseUtils::underscoreToCamelCase(app_name, true);
    2446           0 : }
    2447             : 
    2448             : RestartableDataValue &
    2449     4388305 : MooseApp::registerRestartableData(std::unique_ptr<RestartableDataValue> data,
    2450             :                                   THREAD_ID tid,
    2451             :                                   bool read_only,
    2452             :                                   const RestartableDataMapName & metaname)
    2453             : {
    2454     4388305 :   if (!metaname.empty() && tid != 0)
    2455           0 :     mooseError(
    2456             :         "The meta data storage for '", metaname, "' is not threaded, so the tid must be zero.");
    2457             : 
    2458             :   mooseAssert(metaname.empty() ||
    2459             :                   _restartable_meta_data.find(metaname) != _restartable_meta_data.end(),
    2460             :               "The desired meta data name does not exist: " + metaname);
    2461             : 
    2462             :   // Select the data store for saving this piece of restartable data (mesh or everything else)
    2463             :   auto & data_map =
    2464     4388305 :       metaname.empty() ? _restartable_data[tid] : _restartable_meta_data[metaname].first;
    2465             : 
    2466     4388305 :   RestartableDataValue * stored_data = data_map.findData(data->name());
    2467     4388305 :   if (stored_data)
    2468             :   {
    2469     1622563 :     if (data->typeId() != stored_data->typeId())
    2470           0 :       mooseError("Type mismatch found in RestartableData registration of '",
    2471           0 :                  data->name(),
    2472             :                  "'\n\n  Stored type: ",
    2473           0 :                  stored_data->type(),
    2474             :                  "\n  New type: ",
    2475           0 :                  data->type());
    2476             :   }
    2477             :   else
    2478     2765742 :     stored_data = &data_map.addData(std::move(data));
    2479             : 
    2480     4388305 :   if (!read_only)
    2481     2765736 :     stored_data->setDeclared({});
    2482             : 
    2483     4388305 :   return *stored_data;
    2484             : }
    2485             : 
    2486             : RestartableDataValue &
    2487           0 : MooseApp::registerRestartableData(const std::string & libmesh_dbg_var(name),
    2488             :                                   std::unique_ptr<RestartableDataValue> data,
    2489             :                                   THREAD_ID tid,
    2490             :                                   bool read_only,
    2491             :                                   const RestartableDataMapName & metaname)
    2492             : {
    2493           0 :   mooseDeprecated("The use of MooseApp::registerRestartableData with a data name is "
    2494             :                   "deprecated.\n\nUse the call without a name instead.");
    2495             : 
    2496             :   mooseAssert(name == data->name(), "Inconsistent name");
    2497           0 :   return registerRestartableData(std::move(data), tid, read_only, metaname);
    2498             : }
    2499             : 
    2500             : bool
    2501      200165 : MooseApp::hasRestartableMetaData(const std::string & name,
    2502             :                                  const RestartableDataMapName & metaname) const
    2503             : {
    2504      200165 :   auto it = _restartable_meta_data.find(metaname);
    2505      200165 :   if (it == _restartable_meta_data.end())
    2506           0 :     return false;
    2507      200165 :   return it->second.first.hasData(name);
    2508             : }
    2509             : 
    2510             : RestartableDataValue &
    2511        1090 : MooseApp::getRestartableMetaData(const std::string & name,
    2512             :                                  const RestartableDataMapName & metaname,
    2513             :                                  THREAD_ID tid)
    2514             : {
    2515        1090 :   if (tid != 0)
    2516           0 :     mooseError(
    2517             :         "The meta data storage for '", metaname, "' is not threaded, so the tid must be zero.");
    2518             : 
    2519             :   // Get metadata reference from RestartableDataMap and return a (non-const) reference to its value
    2520        1090 :   auto & restartable_data_map = getRestartableDataMap(metaname);
    2521        1090 :   RestartableDataValue * const data = restartable_data_map.findData(name);
    2522        1090 :   if (!data)
    2523           0 :     mooseError("Unable to find RestartableDataValue object with name " + name +
    2524             :                " in RestartableDataMap");
    2525             : 
    2526        1090 :   return *data;
    2527             : }
    2528             : 
    2529             : void
    2530        7283 : MooseApp::possiblyLoadRestartableMetaData(const RestartableDataMapName & name,
    2531             :                                           const std::filesystem::path & folder_base)
    2532             : {
    2533        7283 :   const auto & map_name = getRestartableDataMapName(name);
    2534        7283 :   const auto meta_data_folder_base = metaDataFolderBase(folder_base, map_name);
    2535        7283 :   if (RestartableDataReader::isAvailable(meta_data_folder_base))
    2536             :   {
    2537        3603 :     RestartableDataReader reader(*this, getRestartableDataMap(name), forceRestart());
    2538        3603 :     reader.setErrorOnLoadWithDifferentNumberOfProcessors(false);
    2539        3603 :     reader.setInput(meta_data_folder_base);
    2540        3603 :     reader.restore();
    2541        3603 :   }
    2542        7283 : }
    2543             : 
    2544             : void
    2545        3322 : MooseApp::loadRestartableMetaData(const std::filesystem::path & folder_base)
    2546             : {
    2547        6644 :   for (const auto & name_map_pair : _restartable_meta_data)
    2548        3322 :     possiblyLoadRestartableMetaData(name_map_pair.first, folder_base);
    2549        3322 : }
    2550             : 
    2551             : std::vector<std::filesystem::path>
    2552       11431 : MooseApp::writeRestartableMetaData(const RestartableDataMapName & name,
    2553             :                                    const std::filesystem::path & folder_base)
    2554             : {
    2555       11431 :   if (processor_id() != 0)
    2556           0 :     mooseError("MooseApp::writeRestartableMetaData(): Should only run on processor 0");
    2557             : 
    2558       11431 :   const auto & map_name = getRestartableDataMapName(name);
    2559       11431 :   const auto meta_data_folder_base = metaDataFolderBase(folder_base, map_name);
    2560             : 
    2561       11431 :   RestartableDataWriter writer(*this, getRestartableDataMap(name));
    2562       22862 :   return writer.write(meta_data_folder_base);
    2563       11431 : }
    2564             : 
    2565             : std::vector<std::filesystem::path>
    2566       11368 : MooseApp::writeRestartableMetaData(const std::filesystem::path & folder_base)
    2567             : {
    2568       11368 :   std::vector<std::filesystem::path> paths;
    2569             : 
    2570       11368 :   if (processor_id() == 0)
    2571       22736 :     for (const auto & name_map_pair : _restartable_meta_data)
    2572             :     {
    2573       11368 :       const auto map_paths = writeRestartableMetaData(name_map_pair.first, folder_base);
    2574       11368 :       paths.insert(paths.end(), map_paths.begin(), map_paths.end());
    2575       11368 :     }
    2576             : 
    2577       11368 :   return paths;
    2578           0 : }
    2579             : 
    2580             : void
    2581           5 : MooseApp::dynamicAppRegistration(const std::string & app_name,
    2582             :                                  std::string library_path,
    2583             :                                  const std::string & library_name,
    2584             :                                  bool lib_load_deps)
    2585             : {
    2586             : #ifdef LIBMESH_HAVE_DLOPEN
    2587           5 :   Parameters params;
    2588          10 :   params.set<std::string>("app_name") = app_name;
    2589           5 :   params.set<RegistrationType>("reg_type") = APPLICATION;
    2590          15 :   params.set<std::string>("registration_method") = app_name + "__registerApps";
    2591           5 :   params.set<std::string>("library_path") = library_path;
    2592             : 
    2593             :   const auto effective_library_name =
    2594           5 :       library_name.empty() ? appNameToLibName(app_name) : library_name;
    2595           5 :   params.set<std::string>("library_name") = effective_library_name;
    2596          10 :   params.set<bool>("library_load_dependencies") = lib_load_deps;
    2597             : 
    2598           5 :   const auto paths = getLibrarySearchPaths(library_path);
    2599           5 :   std::ostringstream oss;
    2600             : 
    2601           5 :   auto successfully_loaded = false;
    2602           5 :   if (paths.empty())
    2603             :     oss << '"' << app_name << "\" is not a registered application name.\n"
    2604             :         << "No search paths were set. We made no attempts to locate the corresponding library "
    2605           1 :            "file.\n";
    2606             :   else
    2607             :   {
    2608           4 :     dynamicRegistration(params);
    2609             : 
    2610             :     // At this point the application should be registered so check it
    2611           4 :     if (!AppFactory::instance().isRegistered(app_name))
    2612             :     {
    2613             :       oss << '"' << app_name << "\" is not a registered application name.\n"
    2614             :           << "Unable to locate library archive for \"" << app_name
    2615             :           << "\".\nWe attempted to locate the library archive \"" << effective_library_name
    2616           2 :           << "\" in the following paths:\n\t";
    2617           2 :       std::copy(paths.begin(), paths.end(), infix_ostream_iterator<std::string>(oss, "\n\t"));
    2618             :     }
    2619             :     else
    2620           2 :       successfully_loaded = true;
    2621             :   }
    2622             : 
    2623           5 :   if (!successfully_loaded)
    2624             :   {
    2625             :     oss << "\nMake sure you have compiled the library and either set the \"library_path\" "
    2626           3 :            "variable in your input file or exported \"MOOSE_LIBRARY_PATH\".\n";
    2627             : 
    2628           3 :     mooseError(oss.str());
    2629             :   }
    2630             : 
    2631             : #else
    2632             :   libmesh_ignore(app_name, library_path, library_name, lib_load_deps);
    2633             :   mooseError("Dynamic Loading is either not supported or was not detected by libMesh configure.");
    2634             : #endif
    2635           2 : }
    2636             : 
    2637             : void
    2638           9 : MooseApp::dynamicAllRegistration(const std::string & app_name,
    2639             :                                  Factory * factory,
    2640             :                                  ActionFactory * action_factory,
    2641             :                                  Syntax * syntax,
    2642             :                                  std::string library_path,
    2643             :                                  const std::string & library_name)
    2644             : {
    2645             : #ifdef LIBMESH_HAVE_DLOPEN
    2646           9 :   Parameters params;
    2647          18 :   params.set<std::string>("app_name") = app_name;
    2648           9 :   params.set<RegistrationType>("reg_type") = REGALL;
    2649          27 :   params.set<std::string>("registration_method") = app_name + "__registerAll";
    2650           9 :   params.set<std::string>("library_path") = library_path;
    2651          18 :   params.set<std::string>("library_name") =
    2652          27 :       library_name.empty() ? appNameToLibName(app_name) : library_name;
    2653             : 
    2654          18 :   params.set<Factory *>("factory") = factory;
    2655          18 :   params.set<Syntax *>("syntax") = syntax;
    2656          27 :   params.set<ActionFactory *>("action_factory") = action_factory;
    2657           9 :   params.set<bool>("library_load_dependencies") = false;
    2658             : 
    2659           9 :   dynamicRegistration(params);
    2660             : #else
    2661             :   libmesh_ignore(app_name, factory, action_factory, syntax, library_path, library_name);
    2662             :   mooseError("Dynamic Loading is either not supported or was not detected by libMesh configure.");
    2663             : #endif
    2664           9 : }
    2665             : 
    2666             : void
    2667          13 : MooseApp::dynamicRegistration(const Parameters & params)
    2668             : {
    2669          13 :   const auto paths = getLibrarySearchPaths(params.get<std::string>("library_path"));
    2670          13 :   const auto library_name = params.get<std::string>("library_name");
    2671             : 
    2672             :   // Attempt to dynamically load the library
    2673          26 :   for (const auto & path : paths)
    2674          13 :     if (MooseUtils::checkFileReadable(path + '/' + library_name, false, false))
    2675          10 :       loadLibraryAndDependencies(
    2676          20 :           path + '/' + library_name, params, params.get<bool>("library_load_dependencies"));
    2677          13 : }
    2678             : 
    2679             : void
    2680          10 : MooseApp::loadLibraryAndDependencies(const std::string & library_filename,
    2681             :                                      const Parameters & params,
    2682             :                                      const bool load_dependencies)
    2683             : {
    2684          10 :   std::string line;
    2685          10 :   std::string dl_lib_filename;
    2686             : 
    2687             :   // This RE looks for absolute path libtool filenames (i.e. begins with a slash and ends with a
    2688             :   // .la)
    2689          10 :   pcrecpp::RE re_deps("(/\\S*\\.la)");
    2690             : 
    2691          10 :   std::ifstream la_handle(library_filename.c_str());
    2692          10 :   if (la_handle.is_open())
    2693             :   {
    2694         200 :     while (std::getline(la_handle, line))
    2695             :     {
    2696             :       // Look for the system dependent dynamic library filename to open
    2697         200 :       if (line.find("dlname=") != std::string::npos)
    2698             :         // Magic numbers are computed from length of this string "dlname=' and line minus that
    2699             :         // string plus quotes"
    2700          10 :         dl_lib_filename = line.substr(8, line.size() - 9);
    2701             : 
    2702         200 :       if (line.find("dependency_libs=") != std::string::npos)
    2703             :       {
    2704          10 :         if (load_dependencies)
    2705             :         {
    2706           0 :           pcrecpp::StringPiece input(line);
    2707           0 :           pcrecpp::StringPiece depend_library;
    2708           0 :           while (re_deps.FindAndConsume(&input, &depend_library))
    2709             :             // Recurse here to load dependent libraries in depth-first order
    2710           0 :             loadLibraryAndDependencies(depend_library.as_string(), params, load_dependencies);
    2711             :         }
    2712             : 
    2713             :         // There's only one line in the .la file containing the dependency libs so break after
    2714             :         // finding it
    2715          10 :         break;
    2716             :       }
    2717             :     }
    2718          10 :     la_handle.close();
    2719             :   }
    2720             : 
    2721             :   // This should only occur if we have static linkage.
    2722          10 :   if (dl_lib_filename.empty())
    2723           0 :     return;
    2724             : 
    2725          10 :   const auto & [dir, file_name] = MooseUtils::splitFileName(library_filename);
    2726             : 
    2727             :   // Time to load the library, First see if we've already loaded this particular dynamic library
    2728             :   //     1) make sure we haven't already loaded this library
    2729             :   // AND 2) make sure we have a library name (we won't for static linkage)
    2730             :   // Note: Here was are going to assume uniqueness based on the filename alone. This has significant
    2731             :   // implications for applications that have "diamond" inheritance of libraries (usually
    2732             :   // modules). We will only load one of those libraries, versions be damned.
    2733          10 :   auto dyn_lib_it = _lib_handles.find(file_name);
    2734          10 :   if (dyn_lib_it == _lib_handles.end())
    2735             :   {
    2736             :     // Assemble the actual filename using the base path of the *.la file and the dl_lib_filename
    2737          10 :     const auto dl_lib_full_path = MooseUtils::pathjoin(dir, dl_lib_filename);
    2738             : 
    2739          10 :     MooseUtils::checkFileReadable(dl_lib_full_path, false, /*throw_on_unreadable=*/true);
    2740             : 
    2741             : #ifdef LIBMESH_HAVE_DLOPEN
    2742          10 :     void * const lib_handle = dlopen(dl_lib_full_path.c_str(), RTLD_LAZY);
    2743             : #else
    2744             :     void * const lib_handle = nullptr;
    2745             : #endif
    2746             : 
    2747          10 :     if (!lib_handle)
    2748           0 :       mooseError("The library file \"",
    2749             :                  dl_lib_full_path,
    2750             :                  "\" exists and has proper permissions, but cannot by dynamically loaded.\nThis "
    2751             :                  "generally means that the loader was unable to load one or more of the "
    2752             :                  "dependencies listed in the supplied library (see otool or ldd).\n",
    2753           0 :                  dlerror());
    2754             : 
    2755          10 :     DynamicLibraryInfo lib_info;
    2756          10 :     lib_info.library_handle = lib_handle;
    2757          10 :     lib_info.full_path = library_filename;
    2758             : 
    2759          10 :     auto insert_ret = _lib_handles.insert(std::make_pair(file_name, lib_info));
    2760             :     mooseAssert(insert_ret.second == true, "Error inserting into lib_handles map");
    2761             : 
    2762          10 :     dyn_lib_it = insert_ret.first;
    2763          10 :   }
    2764             : 
    2765             :   // Library has been loaded, check to see if we've called the requested registration method
    2766          10 :   const auto registration_method = params.get<std::string>("registration_method");
    2767          10 :   auto & entry_sym_from_curr_lib = dyn_lib_it->second.entry_symbols;
    2768             : 
    2769          10 :   if (entry_sym_from_curr_lib.find(registration_method) == entry_sym_from_curr_lib.end())
    2770             :   {
    2771             :     // get the pointer to the method in the library.  The dlsym()
    2772             :     // function returns a null pointer if the symbol cannot be found,
    2773             :     // we also explicitly set the pointer to NULL if dlsym is not
    2774             :     // available.
    2775             : #ifdef LIBMESH_HAVE_DLOPEN
    2776             :     void * registration_handle =
    2777          10 :         dlsym(dyn_lib_it->second.library_handle, registration_method.c_str());
    2778             : #else
    2779             :     void * registration_handle = nullptr;
    2780             : #endif
    2781             : 
    2782          10 :     if (registration_handle)
    2783             :     {
    2784          10 :       switch (params.get<RegistrationType>("reg_type"))
    2785             :       {
    2786           2 :         case APPLICATION:
    2787             :         {
    2788             :           using register_app_t = void (*)();
    2789           2 :           register_app_t * const reg_ptr = reinterpret_cast<register_app_t *>(&registration_handle);
    2790           2 :           (*reg_ptr)();
    2791           2 :           break;
    2792             :         }
    2793           8 :         case REGALL:
    2794             :         {
    2795             :           using register_app_t = void (*)(Factory *, ActionFactory *, Syntax *);
    2796           8 :           register_app_t * const reg_ptr = reinterpret_cast<register_app_t *>(&registration_handle);
    2797           8 :           (*reg_ptr)(params.get<Factory *>("factory"),
    2798           8 :                      params.get<ActionFactory *>("action_factory"),
    2799           8 :                      params.get<Syntax *>("syntax"));
    2800           8 :           break;
    2801             :         }
    2802           0 :         default:
    2803           0 :           mooseError("Unhandled RegistrationType");
    2804             :       }
    2805             : 
    2806          10 :       entry_sym_from_curr_lib.insert(registration_method);
    2807             :     }
    2808             :     else
    2809             :     {
    2810             : 
    2811             : #if defined(DEBUG) && defined(LIBMESH_HAVE_DLOPEN)
    2812             :       // We found a dynamic library that doesn't have a dynamic
    2813             :       // registration method in it. This shouldn't be an error, so
    2814             :       // we'll just move on.
    2815             :       if (!registration_handle)
    2816             :         mooseWarning("Unable to find extern \"C\" method \"",
    2817             :                      registration_method,
    2818             :                      "\" in library: ",
    2819             :                      dyn_lib_it->first,
    2820             :                      ".\n",
    2821             :                      "This doesn't necessarily indicate an error condition unless you believe that "
    2822             :                      "the method should exist in that library.\n",
    2823             :                      dlerror());
    2824             : #endif
    2825             :     }
    2826             :   }
    2827          10 : }
    2828             : 
    2829             : std::set<std::string>
    2830          15 : MooseApp::getLoadedLibraryPaths() const
    2831             : {
    2832             :   // Return the paths but not the open file handles
    2833          15 :   std::set<std::string> paths;
    2834          17 :   for (const auto & it : _lib_handles)
    2835           2 :     paths.insert(it.first);
    2836             : 
    2837          15 :   return paths;
    2838           0 : }
    2839             : 
    2840             : std::set<std::string>
    2841          18 : MooseApp::getLibrarySearchPaths(const std::string & library_path) const
    2842             : {
    2843          18 :   std::set<std::string> paths;
    2844             : 
    2845          18 :   if (!library_path.empty())
    2846             :   {
    2847          17 :     std::vector<std::string> tmp_paths;
    2848          17 :     MooseUtils::tokenize(library_path, tmp_paths, 1, ":");
    2849             : 
    2850          17 :     paths.insert(tmp_paths.begin(), tmp_paths.end());
    2851          17 :   }
    2852             : 
    2853          18 :   char * moose_lib_path_env = std::getenv("MOOSE_LIBRARY_PATH");
    2854          18 :   if (moose_lib_path_env)
    2855             :   {
    2856           0 :     std::string moose_lib_path(moose_lib_path_env);
    2857           0 :     std::vector<std::string> tmp_paths;
    2858           0 :     MooseUtils::tokenize(moose_lib_path, tmp_paths, 1, ":");
    2859             : 
    2860           0 :     paths.insert(tmp_paths.begin(), tmp_paths.end());
    2861           0 :   }
    2862             : 
    2863          18 :   return paths;
    2864           0 : }
    2865             : 
    2866             : InputParameterWarehouse &
    2867     7489072 : MooseApp::getInputParameterWarehouse()
    2868             : {
    2869     7489072 :   return *_input_parameter_warehouse;
    2870             : }
    2871             : 
    2872             : std::string
    2873          95 : MooseApp::header() const
    2874             : {
    2875         190 :   return std::string("");
    2876             : }
    2877             : 
    2878             : void
    2879       12693 : MooseApp::setRestart(bool value)
    2880             : {
    2881       12693 :   _restart = value;
    2882       12693 : }
    2883             : 
    2884             : void
    2885       12247 : MooseApp::setRecover(bool value)
    2886             : {
    2887       12247 :   _recover = value;
    2888       12247 : }
    2889             : 
    2890             : void
    2891           8 : MooseApp::createMinimalApp()
    2892             : {
    2893          40 :   TIME_SECTION("createMinimalApp", 3, "Creating Minimal App");
    2894             : 
    2895             :   // SetupMeshAction
    2896             :   {
    2897             :     // Build the Action parameters
    2898          24 :     InputParameters action_params = _action_factory.getValidParams("SetupMeshAction");
    2899           8 :     action_params.set<std::string>("type") = "GeneratedMesh";
    2900             : 
    2901             :     // Create The Action
    2902             :     std::shared_ptr<MooseObjectAction> action = std::static_pointer_cast<MooseObjectAction>(
    2903          32 :         _action_factory.create("SetupMeshAction", "Mesh", action_params));
    2904             : 
    2905             :     // Set the object parameters
    2906           8 :     InputParameters & params = action->getObjectParams();
    2907          32 :     params.set<MooseEnum>("dim") = "1";
    2908           8 :     params.set<unsigned int>("nx") = 1;
    2909             : 
    2910             :     // Add Action to the warehouse
    2911           8 :     _action_warehouse.addActionBlock(action);
    2912           8 :   }
    2913             : 
    2914             :   // Executioner
    2915             :   {
    2916             :     // Build the Action parameters
    2917          24 :     InputParameters action_params = _action_factory.getValidParams("CreateExecutionerAction");
    2918           8 :     action_params.set<std::string>("type") = "Transient";
    2919             : 
    2920             :     // Create the action
    2921             :     std::shared_ptr<MooseObjectAction> action = std::static_pointer_cast<MooseObjectAction>(
    2922          32 :         _action_factory.create("CreateExecutionerAction", "Executioner", action_params));
    2923             : 
    2924             :     // Set the object parameters
    2925           8 :     InputParameters & params = action->getObjectParams();
    2926          16 :     params.set<unsigned int>("num_steps") = 1;
    2927           8 :     params.set<Real>("dt") = 1;
    2928             : 
    2929             :     // Add Action to the warehouse
    2930           8 :     _action_warehouse.addActionBlock(action);
    2931           8 :   }
    2932             : 
    2933             :   // Problem
    2934             :   {
    2935             :     // Build the Action parameters
    2936          24 :     InputParameters action_params = _action_factory.getValidParams("CreateProblemDefaultAction");
    2937           8 :     action_params.set<bool>("_solve") = false;
    2938             : 
    2939             :     // Create the action
    2940             :     std::shared_ptr<Action> action = std::static_pointer_cast<Action>(
    2941          32 :         _action_factory.create("CreateProblemDefaultAction", "Problem", action_params));
    2942             : 
    2943             :     // Add Action to the warehouse
    2944           8 :     _action_warehouse.addActionBlock(action);
    2945           8 :   }
    2946             : 
    2947             :   // Outputs
    2948             :   {
    2949             :     // Build the Action parameters
    2950          24 :     InputParameters action_params = _action_factory.getValidParams("CommonOutputAction");
    2951           8 :     action_params.set<bool>("console") = false;
    2952             : 
    2953             :     // Create action
    2954             :     std::shared_ptr<Action> action =
    2955          32 :         _action_factory.create("CommonOutputAction", "Outputs", action_params);
    2956             : 
    2957             :     // Add Action to the warehouse
    2958           8 :     _action_warehouse.addActionBlock(action);
    2959           8 :   }
    2960             : 
    2961           8 :   _action_warehouse.build();
    2962           8 : }
    2963             : 
    2964             : bool
    2965           0 : MooseApp::hasRelationshipManager(const std::string & name) const
    2966             : {
    2967           0 :   return std::find_if(_relationship_managers.begin(),
    2968             :                       _relationship_managers.end(),
    2969           0 :                       [&name](const std::shared_ptr<RelationshipManager> & rm)
    2970           0 :                       { return rm->name() == name; }) != _relationship_managers.end();
    2971             : }
    2972             : 
    2973             : namespace
    2974             : {
    2975             : void
    2976      481219 : donateForWhom(const RelationshipManager & donor, RelationshipManager & acceptor)
    2977             : {
    2978      481219 :   auto & existing_for_whom = acceptor.forWhom();
    2979             : 
    2980             :   // Take all the for_whoms from the donor, and give them to the acceptor
    2981      967271 :   for (auto & fw : donor.forWhom())
    2982             :   {
    2983      486052 :     if (std::find(existing_for_whom.begin(), existing_for_whom.end(), fw) ==
    2984      972104 :         existing_for_whom.end())
    2985       92729 :       acceptor.addForWhom(fw);
    2986             :   }
    2987      481219 : }
    2988             : }
    2989             : 
    2990             : bool
    2991      597549 : MooseApp::addRelationshipManager(std::shared_ptr<RelationshipManager> new_rm)
    2992             : {
    2993             :   // We prefer to always add geometric RMs. There is no hurt to add RMs for replicated mesh
    2994             :   // since MeshBase::delete_remote_elements{} is a no-op (empty) for replicated mesh.
    2995             :   // The motivation here is that MooseMesh::_use_distributed_mesh may not be properly set
    2996             :   // at the time we are adding geometric relationship managers. We deleted the following
    2997             :   // old logic to add all geometric RMs regardless of there is a distributed mesh or not.
    2998             :   // Otherwise, all geometric RMs will be improperly ignored for a distributed mesh generator.
    2999             : 
    3000             :   // if (!_action_warehouse.mesh()->isDistributedMesh() && !_split_mesh &&
    3001             :   //    (relationship_manager->isType(Moose::RelationshipManagerType::GEOMETRIC) &&
    3002             :   //     !(relationship_manager->isType(Moose::RelationshipManagerType::ALGEBRAIC) ||
    3003             :   //       relationship_manager->isType(Moose::RelationshipManagerType::COUPLING))))
    3004             :   //  return false;
    3005             : 
    3006      597549 :   bool add = true;
    3007             : 
    3008      597549 :   std::set<std::shared_ptr<RelationshipManager>> rms_to_erase;
    3009             : 
    3010     1031146 :   for (const auto & existing_rm : _relationship_managers)
    3011             :   {
    3012      910845 :     if (*existing_rm >= *new_rm)
    3013             :     {
    3014      477248 :       add = false;
    3015      477248 :       donateForWhom(*new_rm, *existing_rm);
    3016      477248 :       break;
    3017             :     }
    3018             :     // The new rm did not provide less or the same amount/type of ghosting as the existing rm, but
    3019             :     // what about the other way around?
    3020      433597 :     else if (*new_rm >= *existing_rm)
    3021        3971 :       rms_to_erase.emplace(existing_rm);
    3022             :   }
    3023             : 
    3024      597549 :   if (add)
    3025             :   {
    3026      120301 :     _relationship_managers.emplace(new_rm);
    3027      124272 :     for (const auto & rm_to_erase : rms_to_erase)
    3028             :     {
    3029        3971 :       donateForWhom(*rm_to_erase, *new_rm);
    3030        3971 :       removeRelationshipManager(rm_to_erase);
    3031             :     }
    3032             :   }
    3033             : 
    3034             :   // Inform the caller whether the object was added or not
    3035      597549 :   return add;
    3036      597549 : }
    3037             : 
    3038             : const std::string &
    3039       19682 : MooseApp::checkpointSuffix()
    3040             : {
    3041       36666 :   static const std::string suffix = "-mesh.cpa.gz";
    3042       19682 :   return suffix;
    3043             : }
    3044             : 
    3045             : std::filesystem::path
    3046       18714 : MooseApp::metaDataFolderBase(const std::filesystem::path & folder_base,
    3047             :                              const std::string & map_suffix)
    3048             : {
    3049       37428 :   return RestartableDataIO::restartableDataFolder(folder_base /
    3050       56142 :                                                   std::filesystem::path("meta_data" + map_suffix));
    3051             : }
    3052             : 
    3053             : std::filesystem::path
    3054       16780 : MooseApp::restartFolderBase(const std::filesystem::path & folder_base) const
    3055             : {
    3056       16780 :   auto folder = folder_base;
    3057       16780 :   folder += "-restart-" + std::to_string(processor_id());
    3058       33560 :   return RestartableDataIO::restartableDataFolder(folder);
    3059       16780 : }
    3060             : 
    3061             : const hit::Node *
    3062     2827924 : MooseApp::getCurrentActionHitNode() const
    3063             : {
    3064     2827924 :   if (const auto action = _action_warehouse.getCurrentAction())
    3065     1189879 :     return action->parameters().getHitNode();
    3066     1638045 :   return nullptr;
    3067             : }
    3068             : 
    3069             : bool
    3070          25 : MooseApp::hasRMClone(const RelationshipManager & template_rm, const MeshBase & mesh) const
    3071             : {
    3072          25 :   auto it = _template_to_clones.find(&template_rm);
    3073             :   // C++ does short circuiting so we're safe here
    3074          25 :   return (it != _template_to_clones.end()) && (it->second.find(&mesh) != it->second.end());
    3075             : }
    3076             : 
    3077             : RelationshipManager &
    3078          25 : MooseApp::getRMClone(const RelationshipManager & template_rm, const MeshBase & mesh) const
    3079             : {
    3080          25 :   auto outer_it = _template_to_clones.find(&template_rm);
    3081          25 :   if (outer_it == _template_to_clones.end())
    3082           0 :     mooseError("The template rm does not exist in our _template_to_clones map");
    3083             : 
    3084          25 :   auto & mesh_to_clone_map = outer_it->second;
    3085          25 :   auto inner_it = mesh_to_clone_map.find(&mesh);
    3086          25 :   if (inner_it == mesh_to_clone_map.end())
    3087           0 :     mooseError("We should have the mesh key in our mesh");
    3088             : 
    3089          50 :   return *inner_it->second;
    3090             : }
    3091             : 
    3092             : void
    3093        3971 : MooseApp::removeRelationshipManager(std::shared_ptr<RelationshipManager> rm)
    3094             : {
    3095        3971 :   auto * const mesh = _action_warehouse.mesh().get();
    3096        3971 :   if (!mesh)
    3097           0 :     mooseError("The MooseMesh should exist");
    3098             : 
    3099        3971 :   const MeshBase * const undisp_lm_mesh = mesh->getMeshPtr();
    3100        3971 :   RelationshipManager * undisp_clone = nullptr;
    3101        3971 :   if (undisp_lm_mesh && hasRMClone(*rm, *undisp_lm_mesh))
    3102             :   {
    3103          25 :     undisp_clone = &getRMClone(*rm, *undisp_lm_mesh);
    3104          25 :     const_cast<MeshBase *>(undisp_lm_mesh)->remove_ghosting_functor(*undisp_clone);
    3105             :   }
    3106             : 
    3107        3971 :   auto & displaced_mesh = _action_warehouse.displacedMesh();
    3108        3971 :   MeshBase * const disp_lm_mesh = displaced_mesh ? &displaced_mesh->getMesh() : nullptr;
    3109        3971 :   RelationshipManager * disp_clone = nullptr;
    3110        3971 :   if (disp_lm_mesh && hasRMClone(*rm, *disp_lm_mesh))
    3111             :   {
    3112           0 :     disp_clone = &getRMClone(*rm, *disp_lm_mesh);
    3113           0 :     disp_lm_mesh->remove_ghosting_functor(*disp_clone);
    3114             :   }
    3115             : 
    3116        3971 :   if (_executioner)
    3117             :   {
    3118          25 :     auto & problem = feProblem();
    3119          25 :     if (undisp_clone)
    3120             :     {
    3121          25 :       problem.removeAlgebraicGhostingFunctor(*undisp_clone);
    3122          25 :       problem.removeCouplingGhostingFunctor(*undisp_clone);
    3123             :     }
    3124             : 
    3125          25 :     auto * dp = problem.getDisplacedProblem().get();
    3126          25 :     if (dp && disp_clone)
    3127           0 :       dp->removeAlgebraicGhostingFunctor(*disp_clone);
    3128             :   }
    3129             : 
    3130        3971 :   _factory.releaseSharedObjects(*rm);
    3131        3971 :   _relationship_managers.erase(rm);
    3132        3971 : }
    3133             : 
    3134             : RelationshipManager &
    3135      146640 : MooseApp::createRMFromTemplateAndInit(const RelationshipManager & template_rm,
    3136             :                                       MooseMesh & moose_mesh,
    3137             :                                       MeshBase & mesh,
    3138             :                                       const DofMap * const dof_map)
    3139             : {
    3140      146640 :   auto & mesh_to_clone = _template_to_clones[&template_rm];
    3141      146640 :   auto it = mesh_to_clone.find(&mesh);
    3142      146640 :   if (it != mesh_to_clone.end())
    3143             :   {
    3144             :     // We've already created a clone for this mesh
    3145       29247 :     auto & clone_rm = *it->second;
    3146       29247 :     if (!clone_rm.dofMap() && dof_map)
    3147             :       // We didn't have a DofMap before, but now we do, so we should re-init
    3148        9133 :       clone_rm.init(moose_mesh, mesh, dof_map);
    3149       20114 :     else if (clone_rm.dofMap() && dof_map && (clone_rm.dofMap() != dof_map))
    3150           0 :       mooseError("Attempting to create and initialize an existing clone with a different DofMap. "
    3151             :                  "This should not happen.");
    3152             : 
    3153       29247 :     return clone_rm;
    3154             :   }
    3155             : 
    3156             :   // It's possible that this method is going to get called for multiple different MeshBase
    3157             :   // objects. If that happens, then we *cannot* risk having a MeshBase object with a ghosting
    3158             :   // functor that is init'd with another MeshBase object. So the safe thing to do is to make a
    3159             :   // different RM for every MeshBase object that gets called here. Then the
    3160             :   // RelationshipManagers stored here in MooseApp are serving as a template only
    3161      117393 :   auto pr = mesh_to_clone.emplace(
    3162      234786 :       std::make_pair(&const_cast<const MeshBase &>(mesh),
    3163      234786 :                      dynamic_pointer_cast<RelationshipManager>(template_rm.clone())));
    3164             :   mooseAssert(pr.second, "An insertion should have happened");
    3165      117393 :   auto & clone_rm = *pr.first->second;
    3166      117393 :   clone_rm.init(moose_mesh, mesh, dof_map);
    3167      117393 :   return clone_rm;
    3168             : }
    3169             : 
    3170             : void
    3171       71333 : MooseApp::attachRelationshipManagers(MeshBase & mesh, MooseMesh & moose_mesh)
    3172             : {
    3173      178651 :   for (auto & rm : _relationship_managers)
    3174             :   {
    3175      107318 :     if (rm->isType(Moose::RelationshipManagerType::GEOMETRIC))
    3176             :     {
    3177       66774 :       if (rm->attachGeometricEarly())
    3178             :       {
    3179       50485 :         mesh.add_ghosting_functor(createRMFromTemplateAndInit(*rm, moose_mesh, mesh));
    3180       50485 :         _attached_relationship_managers[Moose::RelationshipManagerType::GEOMETRIC].insert(rm.get());
    3181             :       }
    3182             :       else
    3183             :       {
    3184             :         // If we have a geometric ghosting functor that can't be attached early, then we have to
    3185             :         // prevent the mesh from deleting remote elements
    3186       16289 :         moose_mesh.allowRemoteElementRemoval(false);
    3187             : 
    3188       16289 :         if (const MeshBase * const moose_mesh_base = moose_mesh.getMeshPtr())
    3189             :         {
    3190           0 :           if (moose_mesh_base != &mesh)
    3191           0 :             mooseError("The MooseMesh MeshBase and the MeshBase we're trying to attach "
    3192             :                        "relationship managers to are different");
    3193             :         }
    3194             :         else
    3195             :           // The MeshBase isn't attached to the MooseMesh yet, so have to tell it not to remove
    3196             :           // remote elements independently
    3197       16289 :           mesh.allow_remote_element_removal(false);
    3198             :       }
    3199             :     }
    3200             :   }
    3201       71333 : }
    3202             : 
    3203             : void
    3204      249529 : MooseApp::attachRelationshipManagers(Moose::RelationshipManagerType rm_type,
    3205             :                                      bool attach_geometric_rm_final)
    3206             : {
    3207      692357 :   for (auto & rm : _relationship_managers)
    3208             :   {
    3209      442828 :     if (!rm->isType(rm_type))
    3210      236041 :       continue;
    3211             : 
    3212             :     // RM is already attached (this also handles the geometric early case)
    3213      206787 :     if (_attached_relationship_managers[rm_type].count(rm.get()))
    3214       95923 :       continue;
    3215             : 
    3216      110864 :     if (rm_type == Moose::RelationshipManagerType::GEOMETRIC)
    3217             :     {
    3218             :       // The problem is not built yet - so the ActionWarehouse currently owns the mesh
    3219       32102 :       MooseMesh * const mesh = _action_warehouse.mesh().get();
    3220             : 
    3221             :       // "attach_geometric_rm_final = true" inidicate that it is the last chance to attach
    3222             :       // geometric RMs. Therefore, we need to attach them.
    3223       32102 :       if (!rm->attachGeometricEarly() && !attach_geometric_rm_final)
    3224             :         // Will attach them later (during algebraic). But also, we need to tell the mesh that we
    3225             :         // shouldn't be deleting remote elements yet
    3226       16196 :         mesh->allowRemoteElementRemoval(false);
    3227             :       else
    3228             :       {
    3229       15906 :         MeshBase & undisp_mesh_base = mesh->getMesh();
    3230             :         const DofMap * const undisp_sys_dof_map =
    3231       15906 :             _executioner ? &feProblem().getSolverSystem(0).dofMap() : nullptr;
    3232       15906 :         undisp_mesh_base.add_ghosting_functor(
    3233       15906 :             createRMFromTemplateAndInit(*rm, *mesh, undisp_mesh_base, undisp_sys_dof_map));
    3234             : 
    3235             :         // In the final stage, if there is a displaced mesh, we need to
    3236             :         // clone ghosting functors for displacedMesh
    3237       15906 :         if (auto & disp_moose_mesh = _action_warehouse.displacedMesh();
    3238       15906 :             attach_geometric_rm_final && disp_moose_mesh)
    3239             :         {
    3240        1487 :           MeshBase & disp_mesh_base = _action_warehouse.displacedMesh()->getMesh();
    3241        1487 :           const DofMap * disp_sys_dof_map = nullptr;
    3242        1487 :           if (_executioner && feProblem().getDisplacedProblem())
    3243        1487 :             disp_sys_dof_map = &feProblem().getDisplacedProblem()->solverSys(0).dofMap();
    3244        1487 :           disp_mesh_base.add_ghosting_functor(
    3245        1487 :               createRMFromTemplateAndInit(*rm, *disp_moose_mesh, disp_mesh_base, disp_sys_dof_map));
    3246             :         }
    3247       14419 :         else if (_action_warehouse.displacedMesh())
    3248           0 :           mooseError("The displaced mesh should not yet exist at the time that we are attaching "
    3249             :                      "early geometric relationship managers.");
    3250             : 
    3251             :         // Mark this RM as attached
    3252             :         mooseAssert(!_attached_relationship_managers[rm_type].count(rm.get()), "Already attached");
    3253       15906 :         _attached_relationship_managers[rm_type].insert(rm.get());
    3254             :       }
    3255             :     }
    3256             :     else // rm_type is algebraic or coupling
    3257             :     {
    3258       78762 :       if (!_executioner && !_executor)
    3259           0 :         mooseError("We must have an executioner by now or else we do not have to data to add "
    3260             :                    "algebraic or coupling functors to in MooseApp::attachRelationshipManagers");
    3261             : 
    3262             :       // Now we've built the problem, so we can use it
    3263       78762 :       auto & problem = feProblem();
    3264       78762 :       auto & undisp_moose_mesh = problem.mesh();
    3265       78762 :       auto & undisp_sys = feProblem().getSolverSystem(0);
    3266       78762 :       auto & undisp_sys_dof_map = undisp_sys.dofMap();
    3267       78762 :       auto & undisp_mesh = undisp_moose_mesh.getMesh();
    3268             : 
    3269       78762 :       if (rm->useDisplacedMesh() && problem.getDisplacedProblem())
    3270             :       {
    3271        4320 :         if (rm_type == Moose::RelationshipManagerType::COUPLING)
    3272             :           // We actually need to add this to the FEProblemBase NonlinearSystemBase's DofMap
    3273             :           // because the DisplacedProblem "nonlinear" DisplacedSystem doesn't have any matrices
    3274             :           // for which to do coupling. It's actually horrifying to me that we are adding a
    3275             :           // coupling functor, that is going to determine its couplings based on a displaced
    3276             :           // MeshBase object, to a System associated with the undisplaced MeshBase object (there
    3277             :           // is only ever one EquationSystems object per MeshBase object and visa versa). So here
    3278             :           // I'm left with the choice of whether to pass in a MeshBase object that is *not* the
    3279             :           // MeshBase object that will actually determine the couplings or to pass in the MeshBase
    3280             :           // object that is inconsistent with the System DofMap that we are adding the coupling
    3281             :           // functor for! Let's err on the side of *libMesh* consistency and pass properly paired
    3282             :           // MeshBase-DofMap
    3283         204 :           problem.addCouplingGhostingFunctor(
    3284         102 :               createRMFromTemplateAndInit(*rm, undisp_moose_mesh, undisp_mesh, &undisp_sys_dof_map),
    3285             :               /*to_mesh = */ false);
    3286             : 
    3287        4218 :         else if (rm_type == Moose::RelationshipManagerType::ALGEBRAIC)
    3288             :         {
    3289        4218 :           auto & displaced_problem = *problem.getDisplacedProblem();
    3290        4218 :           auto & disp_moose_mesh = displaced_problem.mesh();
    3291        4218 :           auto & disp_mesh = disp_moose_mesh.getMesh();
    3292        4218 :           const DofMap * const disp_nl_dof_map = &displaced_problem.solverSys(0).dofMap();
    3293        8436 :           displaced_problem.addAlgebraicGhostingFunctor(
    3294        4218 :               createRMFromTemplateAndInit(*rm, disp_moose_mesh, disp_mesh, disp_nl_dof_map),
    3295             :               /*to_mesh = */ false);
    3296             :         }
    3297             :       }
    3298             :       else // undisplaced
    3299             :       {
    3300       74442 :         if (rm_type == Moose::RelationshipManagerType::COUPLING)
    3301       89184 :           problem.addCouplingGhostingFunctor(
    3302       44592 :               createRMFromTemplateAndInit(*rm, undisp_moose_mesh, undisp_mesh, &undisp_sys_dof_map),
    3303             :               /*to_mesh = */ false);
    3304             : 
    3305       29850 :         else if (rm_type == Moose::RelationshipManagerType::ALGEBRAIC)
    3306       59700 :           problem.addAlgebraicGhostingFunctor(
    3307       29850 :               createRMFromTemplateAndInit(*rm, undisp_moose_mesh, undisp_mesh, &undisp_sys_dof_map),
    3308             :               /*to_mesh = */ false);
    3309             :       }
    3310             : 
    3311             :       // Mark this RM as attached
    3312             :       mooseAssert(!_attached_relationship_managers[rm_type].count(rm.get()), "Already attached");
    3313       78762 :       _attached_relationship_managers[rm_type].insert(rm.get());
    3314             :     }
    3315             :   }
    3316      249529 : }
    3317             : 
    3318             : std::vector<std::pair<std::string, std::string>>
    3319          91 : MooseApp::getRelationshipManagerInfo() const
    3320             : {
    3321          91 :   std::vector<std::pair<std::string, std::string>> info_strings;
    3322          91 :   info_strings.reserve(_relationship_managers.size());
    3323             : 
    3324         369 :   for (const auto & rm : _relationship_managers)
    3325             :   {
    3326         278 :     std::stringstream oss;
    3327         278 :     oss << rm->getInfo();
    3328             : 
    3329         278 :     auto & for_whom = rm->forWhom();
    3330             : 
    3331         278 :     if (!for_whom.empty())
    3332             :     {
    3333         278 :       oss << " for ";
    3334             : 
    3335         278 :       std::copy(for_whom.begin(), for_whom.end(), infix_ostream_iterator<std::string>(oss, ", "));
    3336             :     }
    3337             : 
    3338         278 :     info_strings.emplace_back(std::make_pair(Moose::stringify(rm->getType()), oss.str()));
    3339         278 :   }
    3340             : 
    3341             :   // List the libMesh GhostingFunctors - Not that in libMesh all of the algebraic and coupling
    3342             :   // Ghosting Functors are also attached to the mesh. This should catch them all.
    3343          91 :   const auto & mesh = _action_warehouse.getMesh();
    3344          91 :   if (mesh)
    3345             :   {
    3346             :     // Let us use an ordered map to avoid stochastic console behaviors.
    3347             :     // I believe we won't have many RMs, and there is no performance issue.
    3348             :     // Deterministic behaviors are good for setting up regression tests
    3349          91 :     std::map<std::string, unsigned int> counts;
    3350             : 
    3351         182 :     for (auto & gf : as_range(mesh->getMesh().ghosting_functors_begin(),
    3352         754 :                               mesh->getMesh().ghosting_functors_end()))
    3353             :     {
    3354         481 :       const auto * gf_ptr = dynamic_cast<const RelationshipManager *>(gf);
    3355         481 :       if (!gf_ptr)
    3356             :         // Count how many occurences of the same Ghosting Functor types we are encountering
    3357         309 :         counts[demangle(typeid(*gf).name())]++;
    3358             :     }
    3359             : 
    3360         273 :     for (const auto & pair : counts)
    3361         182 :       info_strings.emplace_back(std::make_pair(
    3362         528 :           "Default", pair.first + (pair.second > 1 ? " x " + std::to_string(pair.second) : "")));
    3363          91 :   }
    3364             : 
    3365             :   // List the libMesh GhostingFunctors - Not that in libMesh all of the algebraic and coupling
    3366             :   // Ghosting Functors are also attached to the mesh. This should catch them all.
    3367          91 :   const auto & d_mesh = _action_warehouse.getDisplacedMesh();
    3368          91 :   if (d_mesh)
    3369             :   {
    3370             :     // Let us use an ordered map to avoid stochastic console behaviors.
    3371             :     // I believe we won't have many RMs, and there is no performance issue.
    3372             :     // Deterministic behaviors are good for setting up regression tests
    3373           9 :     std::map<std::string, unsigned int> counts;
    3374             : 
    3375          18 :     for (auto & gf : as_range(d_mesh->getMesh().ghosting_functors_begin(),
    3376          99 :                               d_mesh->getMesh().ghosting_functors_end()))
    3377             :     {
    3378          72 :       const auto * gf_ptr = dynamic_cast<const RelationshipManager *>(gf);
    3379          72 :       if (!gf_ptr)
    3380             :         // Count how many occurences of the same Ghosting Functor types we are encountering
    3381          45 :         counts[demangle(typeid(*gf).name())]++;
    3382             :     }
    3383             : 
    3384          27 :     for (const auto & pair : counts)
    3385          18 :       info_strings.emplace_back(
    3386          36 :           std::make_pair("Default",
    3387          36 :                          pair.first + (pair.second > 1 ? " x " + std::to_string(pair.second) : "") +
    3388             :                              " for DisplacedMesh"));
    3389           9 :   }
    3390             : 
    3391          91 :   return info_strings;
    3392           0 : }
    3393             : 
    3394             : void
    3395       66355 : MooseApp::checkMetaDataIntegrity() const
    3396             : {
    3397      132710 :   for (auto map_iter = _restartable_meta_data.begin(); map_iter != _restartable_meta_data.end();
    3398       66355 :        ++map_iter)
    3399             :   {
    3400       66355 :     const RestartableDataMapName & name = map_iter->first;
    3401       66355 :     const RestartableDataMap & meta_data = map_iter->second.first;
    3402             : 
    3403       66355 :     std::vector<std::string> not_declared;
    3404             : 
    3405      258428 :     for (const auto & data : meta_data)
    3406      192073 :       if (!data.declared())
    3407           0 :         not_declared.push_back(data.name());
    3408             : 
    3409       66355 :     if (!not_declared.empty())
    3410             :     {
    3411           0 :       std::ostringstream oss;
    3412           0 :       std::copy(
    3413             :           not_declared.begin(), not_declared.end(), infix_ostream_iterator<std::string>(oss, ", "));
    3414             : 
    3415           0 :       mooseError("The following '",
    3416             :                  name,
    3417             :                  "' meta-data properties were retrieved but never declared: ",
    3418           0 :                  oss.str());
    3419           0 :     }
    3420       66355 :   }
    3421       66355 : }
    3422             : 
    3423             : const RestartableDataMapName MooseApp::MESH_META_DATA = "MeshMetaData";
    3424             : const RestartableDataMapName MooseApp::MESH_META_DATA_SUFFIX = "mesh";
    3425             : 
    3426             : RestartableDataMap &
    3427       16151 : MooseApp::getRestartableDataMap(const RestartableDataMapName & name)
    3428             : {
    3429       16151 :   auto iter = _restartable_meta_data.find(name);
    3430       16151 :   if (iter == _restartable_meta_data.end())
    3431           3 :     mooseError("Unable to find RestartableDataMap object for the supplied name '",
    3432             :                name,
    3433             :                "', did you call registerRestartableDataMapName in the application constructor?");
    3434       32296 :   return iter->second.first;
    3435             : }
    3436             : 
    3437             : bool
    3438           0 : MooseApp::hasRestartableDataMap(const RestartableDataMapName & name) const
    3439             : {
    3440           0 :   return _restartable_meta_data.count(name);
    3441             : }
    3442             : 
    3443             : void
    3444       68101 : MooseApp::registerRestartableDataMapName(const RestartableDataMapName & name, std::string suffix)
    3445             : {
    3446       68101 :   if (!suffix.empty())
    3447       68101 :     std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
    3448       68101 :   suffix.insert(0, "_");
    3449       68101 :   _restartable_meta_data.emplace(
    3450      136202 :       std::make_pair(name, std::make_pair(RestartableDataMap(), suffix)));
    3451       68101 : }
    3452             : 
    3453             : const std::string &
    3454       18714 : MooseApp::getRestartableDataMapName(const RestartableDataMapName & name) const
    3455             : {
    3456       18714 :   const auto it = _restartable_meta_data.find(name);
    3457       18714 :   if (it == _restartable_meta_data.end())
    3458           0 :     mooseError("MooseApp::getRestartableDataMapName: The name '", name, "' is not registered");
    3459       37428 :   return it->second.second;
    3460             : }
    3461             : 
    3462             : PerfGraph &
    3463       68103 : MooseApp::createRecoverablePerfGraph()
    3464             : {
    3465       68103 :   registerRestartableNameWithFilter("perf_graph", Moose::RESTARTABLE_FILTER::RECOVERABLE);
    3466             : 
    3467             :   auto perf_graph =
    3468             :       std::make_unique<RestartableData<PerfGraph>>("perf_graph",
    3469           0 :                                                    this,
    3470      136206 :                                                    type() + " (" + name() + ')',
    3471             :                                                    *this,
    3472             :                                                    getParam<bool>("perf_graph_live_all"),
    3473      476721 :                                                    !getParam<bool>("disable_perf_graph_live"));
    3474             : 
    3475      119285 :   return dynamic_cast<RestartableData<PerfGraph> &>(
    3476      136206 :              registerRestartableData(std::move(perf_graph), 0, false))
    3477      136206 :       .set();
    3478       68103 : }
    3479             : 
    3480             : SolutionInvalidity &
    3481       68103 : MooseApp::createRecoverableSolutionInvalidity()
    3482             : {
    3483       68103 :   registerRestartableNameWithFilter("solution_invalidity", Moose::RESTARTABLE_FILTER::RECOVERABLE);
    3484             : 
    3485             :   auto solution_invalidity =
    3486       68103 :       std::make_unique<RestartableData<SolutionInvalidity>>("solution_invalidity", nullptr, *this);
    3487             : 
    3488      119285 :   return dynamic_cast<RestartableData<SolutionInvalidity> &>(
    3489      136206 :              registerRestartableData(std::move(solution_invalidity), 0, false))
    3490      136206 :       .set();
    3491       68103 : }
    3492             : 
    3493             : bool
    3494      318608 : MooseApp::constructingMeshGenerators() const
    3495             : {
    3496      377298 :   return _action_warehouse.getCurrentTaskName() == "create_added_mesh_generators" ||
    3497      377298 :          _mesh_generator_system.appendingMeshGenerators();
    3498             : }
    3499             : 
    3500             : #ifdef MOOSE_LIBTORCH_ENABLED
    3501             : torch::DeviceType
    3502        2395 : MooseApp::determineLibtorchDeviceType(const MooseEnum & device_enum) const
    3503             : {
    3504        2395 :   const auto pname = "--compute-device";
    3505        2395 :   if (device_enum == "cuda")
    3506             :   {
    3507             : #ifdef __linux__
    3508        1661 :     if (!torch::cuda::is_available())
    3509           0 :       mooseError(pname, "=cuda: CUDA support is not available in the linked libtorch library");
    3510        1661 :     return torch::kCUDA;
    3511             : #else
    3512             :     mooseError(pname, "=cuda: CUDA is not supported on your platform");
    3513             : #endif
    3514             :   }
    3515         734 :   else if (device_enum == "mps")
    3516             :   {
    3517             : #ifdef __APPLE__
    3518             :     if (!torch::mps::is_available())
    3519             :       mooseError(pname, "=mps: MPS support is not available in the linked libtorch library");
    3520             :     return torch::kMPS;
    3521             : #else
    3522           0 :     mooseError(pname, "=mps: MPS is not supported on your platform");
    3523             : #endif
    3524             :   }
    3525         734 :   else if (device_enum == "xpu")
    3526             :   {
    3527             : #ifdef MOOSE_HAVE_XPU
    3528           0 :     if (!torch::xpu::is_available())
    3529           0 :       mooseError(pname, "=xpu: XPU support is not available in the linked libtorch library");
    3530           0 :     return torch::kXPU;
    3531             : #else
    3532             :     mooseError(pname, "=xpu: XPU is not supported in the current application");
    3533             : #endif
    3534             :   }
    3535         734 :   else if (device_enum != "cpu")
    3536           0 :     mooseError("The device '",
    3537             :                device_enum,
    3538             :                "' is not currently supported by the MOOSE libtorch integration.");
    3539         734 :   return torch::kCPU;
    3540             : }
    3541             : #endif
    3542             : 
    3543             : void
    3544          34 : MooseApp::outputMachineReadableData(const std::string & param,
    3545             :                                     const std::string & start_marker,
    3546             :                                     const std::string & end_marker,
    3547             :                                     const std::string & data) const
    3548             : {
    3549             :   // Bool parameter, just to screen
    3550          34 :   if (_pars.have_parameter<bool>(param))
    3551             :   {
    3552          34 :     Moose::out << start_marker << data << end_marker << std::endl;
    3553          34 :     return;
    3554             :   }
    3555             : 
    3556             :   // String parameter, to file
    3557           0 :   const auto & filename = getParam<std::string>(param);
    3558             :   // write to file
    3559           0 :   std::ofstream out(filename.c_str());
    3560           0 :   if (out.is_open())
    3561             :   {
    3562           0 :     std::ofstream out(filename.c_str());
    3563           0 :     out << data << std::flush;
    3564           0 :     out.close();
    3565           0 :   }
    3566             :   else
    3567           0 :     mooseError("Unable to open file `", filename, "` for writing ", param, " data to it.");
    3568           0 : }
    3569             : 
    3570             : Moose::Capability &
    3571       67468 : MooseApp::addBoolCapability(const std::string_view capability,
    3572             :                             const bool value,
    3573             :                             const std::string_view doc)
    3574             : {
    3575       67468 :   return addCapabilityInternal(capability, value, doc);
    3576             : }
    3577             : 
    3578             : Moose::Capability &
    3579      134932 : MooseApp::addIntCapability(const std::string_view capability,
    3580             :                            const int value,
    3581             :                            const std::string_view doc)
    3582             : {
    3583      134932 :   return addCapabilityInternal(capability, value, doc);
    3584             : }
    3585             : 
    3586             : Moose::Capability &
    3587      269860 : MooseApp::addStringCapability(const std::string_view capability,
    3588             :                               const std::string_view value,
    3589             :                               const std::string_view doc)
    3590             : {
    3591      539722 :   return addCapabilityInternal(capability, std::string(value), doc);
    3592             : }
    3593             : 
    3594             : Moose::Capability &
    3595           6 : MooseApp::addCapability(const std::string_view capability,
    3596             :                         const Moose::Capability::Value & value,
    3597             :                         const std::string_view doc)
    3598             : {
    3599             : 
    3600             :   // Warn deprecation on the first time this is added so that we
    3601             :   // don't get multiple warnings if the app is registered more
    3602             :   // than once
    3603          18 :   if (!Moose::internal::Capabilities::getCapabilities({}).query(std::string(capability)))
    3604           4 :     ::mooseDeprecated("MooseApp::addCapability() is deprecated (adding capability '",
    3605             :                       capability,
    3606             :                       "'); use one of MooseApp::add[Bool,Int,String]Capability instead.");
    3607             : 
    3608           4 :   return addCapabilityInternal(capability, value, doc);
    3609             : }
    3610             : 
    3611             : bool
    3612           2 : MooseApp::isRelocated()
    3613             : {
    3614           2 :   return Moose::internal::Capabilities::getCapabilities({}).isRelocated();
    3615             : }
    3616             : 
    3617             : bool
    3618           2 : MooseApp::isInTree()
    3619             : {
    3620           2 :   return Moose::internal::Capabilities::getCapabilities({}).isInTree();
    3621             : }
    3622             : 
    3623             : #ifdef MOOSE_MFEM_ENABLED
    3624             : void
    3625        1584 : MooseApp::setMFEMDevice(const std::string & device_string,
    3626             :                         bool gpu_aware_mpi,
    3627             :                         Moose::PassKey<MFEMProblemSolve>)
    3628             : {
    3629        3168 :   const auto string_vec = MooseUtils::split(device_string, ",");
    3630        1584 :   auto string_set = std::set<std::string>(string_vec.begin(), string_vec.end());
    3631        1584 :   if (!_mfem_device)
    3632             :   {
    3633        1341 :     _mfem_device = std::make_shared<mfem::Device>(device_string);
    3634        1341 :     _mfem_devices = std::move(string_set);
    3635        2682 :     _mfem_device->SetGPUAwareMPI(mfem::GetEnv("MFEM_GPU_AWARE_MPI") ? true : gpu_aware_mpi);
    3636        1341 :     _mfem_device->Print(Moose::out);
    3637             :   }
    3638         243 :   else if (!device_string.empty() && string_set != _mfem_devices)
    3639           0 :     mooseError("Attempted to configure with "
    3640             :                "MFEM devices '",
    3641           0 :                MooseUtils::join(string_set, " "),
    3642             :                "', but we have already "
    3643             :                "configured the MFEM device "
    3644             :                "object with the devices '",
    3645           0 :                MooseUtils::join(_mfem_devices, " "),
    3646             :                "'");
    3647        2925 : }
    3648             : #endif

Generated by: LCOV version 1.14