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

Generated by: LCOV version 1.14