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