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