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