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