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