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