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