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 : #include "ActionWarehouse.h"
11 : #include "ActionFactory.h"
12 : #include "Parser.h"
13 : #include "MooseObjectAction.h"
14 : #include "PhysicsBase.h"
15 : #include "InputFileFormatter.h"
16 : #include "InputParameters.h"
17 : #include "MooseMesh.h"
18 : #include "AddVariableAction.h"
19 : #include "AddAuxVariableAction.h"
20 : #include "XTermConstants.h"
21 : #include "InfixIterator.h"
22 : #include "FEProblem.h"
23 : #include "MemoryUtils.h"
24 : #include "InputParameterWarehouse.h"
25 :
26 : #include "DependencyResolver.h"
27 :
28 : #include "libmesh/simple_range.h"
29 :
30 : std::vector<UserObjectName>
31 71096 : ActionWarehouse::getUserObjectParamDependencies(const InputParameters & params) const
32 : {
33 71096 : std::vector<UserObjectName> dependencies;
34 1413062 : for (const auto & [_, param] : params)
35 : {
36 1341966 : if (const auto dependency =
37 1341966 : dynamic_cast<const libMesh::Parameters::Parameter<UserObjectName> *>(param.get()))
38 : {
39 196 : const auto & uo_name = dependency->get();
40 196 : if (!uo_name.empty())
41 115 : dependencies.push_back(uo_name);
42 : }
43 1341770 : else if (const auto vector_dependency =
44 1341770 : dynamic_cast<const libMesh::Parameters::Parameter<std::vector<UserObjectName>> *>(
45 2683540 : param.get()))
46 12 : for (const auto & uo_name : vector_dependency->get())
47 0 : if (!uo_name.empty())
48 0 : dependencies.push_back(uo_name);
49 : }
50 71096 : return dependencies;
51 0 : }
52 :
53 : void
54 62147 : ActionWarehouse::sortUserObjectActions(std::list<Action *> & actions) const
55 : {
56 : // We key on act->name() because for a MooseObjectAction the action name is exactly the name the
57 : // UserObject is constructed under (see AddUserObjectAction), so a UserObjectName parameter value
58 : // matches the action that builds the referenced object.
59 62147 : std::map<std::string, Action *> action_for_uo_name;
60 133243 : for (const auto act : actions)
61 71096 : if (dynamic_cast<MooseObjectAction *>(act))
62 8752 : action_for_uo_name.emplace(act->name(), act);
63 : // A Physics does not build its UserObjects under its own action name, so it declares the names
64 : // it supplies through getSuppliedUserObjects(). Key those to the Physics action so an object
65 : // referencing a Physics-supplied UserObject is constructed after the Physics adds it.
66 62344 : else if (const auto physics = dynamic_cast<const PhysicsBase *>(act))
67 36 : for (const auto & uo_name : physics->getSuppliedUserObjects())
68 36 : action_for_uo_name.emplace(uo_name, act);
69 :
70 : // Seed the resolver in the current (input-file) order so that independent UserObjects keep their
71 : // input order (DependencyResolver resolves in insertion order modulo dependencies).
72 62147 : DependencyResolver<Action *> resolver;
73 133243 : for (const auto act : actions)
74 71096 : resolver.addItem(act);
75 :
76 133243 : for (const auto act : actions)
77 : {
78 : // A MooseObjectAction carries its dependencies in the parameters of the object it builds. Any
79 : // other action that consumes a UserObject (e.g. a Physics that programmatically adds one) names
80 : // it in the action's own parameters, so the dependency is ordered against just the same.
81 71096 : const auto moose_object_action = dynamic_cast<MooseObjectAction *>(act);
82 : const auto & relevant_params =
83 71096 : moose_object_action ? moose_object_action->getObjectParams() : act->parameters();
84 71211 : for (const auto & dep_name : getUserObjectParamDependencies(relevant_params))
85 : {
86 115 : const auto it = action_for_uo_name.find(dep_name);
87 115 : if (it != action_for_uo_name.end() && it->second != act)
88 : // act references the UserObject built by it->second, which must be constructed first
89 112 : resolver.addEdge(it->second, act);
90 71096 : }
91 : }
92 :
93 : // A UserObjectName parameter does not necessarily denote a construction-time dependency: many
94 : // UserObjects only resolve a referenced UserObject during initialSetup() or execution. Mutually
95 : // referencing UserObjects therefore form a cycle here even though they construct fine in input
96 : // order. So a cycle is not an error - fall back to the original input order, which is the
97 : // behavior prior to this sorting. A genuine construction-time cycle still surfaces as the usual
98 : // "UserObject not found" error when the object is constructed.
99 : try
100 : {
101 62147 : const auto & sorted = resolver.getSortedValues();
102 62135 : actions.assign(sorted.begin(), sorted.end());
103 : }
104 12 : catch (const CyclicDependencyException<Action *> &)
105 : {
106 12 : }
107 62147 : }
108 :
109 68103 : ActionWarehouse::ActionWarehouse(MooseApp & app, Syntax & syntax, ActionFactory & factory)
110 : : ConsoleStreamInterface(app),
111 68103 : _app(app),
112 68103 : _syntax(syntax),
113 68103 : _action_factory(factory),
114 68103 : _generator_valid(false),
115 68103 : _show_action_dependencies(false),
116 68103 : _show_actions(false),
117 68103 : _show_parser(false),
118 68103 : _current_action(nullptr),
119 68103 : _mesh(nullptr),
120 136206 : _displaced_mesh(nullptr)
121 : {
122 68103 : }
123 :
124 63958 : ActionWarehouse::~ActionWarehouse() {}
125 :
126 : void
127 4369 : ActionWarehouse::setFinalTask(const std::string & task)
128 : {
129 4369 : if (!_syntax.hasTask(task))
130 0 : mooseError("cannot use unregistered task '", task, "' as final task");
131 4369 : _final_task = task;
132 4369 : }
133 :
134 : void
135 67153 : ActionWarehouse::build()
136 : {
137 67153 : _ordered_names = _syntax.getSortedTask();
138 10311976 : for (const auto & name : _ordered_names)
139 10244823 : buildBuildableActions(name);
140 67153 : }
141 :
142 : void
143 63956 : ActionWarehouse::clear()
144 : {
145 3292932 : for (auto & ptr : _all_ptrs)
146 3228976 : ptr.reset();
147 :
148 63956 : _action_blocks.clear();
149 63956 : _generator_valid = false;
150 :
151 : // Due to the way ActionWarehouse is cleaned up (see MooseApp's
152 : // destructor) we must guarantee that ActionWarehouse::clear()
153 : // releases all the resources which have to be released _before_ the
154 : // _comm object owned by the MooseApp is destroyed.
155 63956 : _problem.reset();
156 63956 : _displaced_mesh.reset();
157 63956 : _mesh.reset();
158 63956 : }
159 :
160 : void
161 3418037 : ActionWarehouse::addActionBlock(std::shared_ptr<Action> action)
162 : {
163 : /**
164 : * Note: This routine uses the XTerm colors directly which is not advised for general purpose
165 : * output coloring. Most users should prefer using Problem::colorText() which respects the
166 : * "color_output" option for terminals that do not support coloring. Since this routine is
167 : * intended for debugging only and runs before several objects exist in the system, we are just
168 : * using the constants directly.
169 : */
170 : std::string registered_identifier =
171 3418037 : action->parameters().get<std::string>("registered_identifier");
172 3418037 : std::set<std::string> tasks;
173 :
174 3418037 : if (_show_parser)
175 0 : Moose::err << COLOR_DEFAULT << "Parsing Syntax: " << COLOR_GREEN << action->name()
176 0 : << '\n'
177 0 : << COLOR_DEFAULT << "Building Action: " << COLOR_DEFAULT << action->type()
178 0 : << '\n'
179 0 : << COLOR_DEFAULT << "Registered Identifier: " << COLOR_GREEN << registered_identifier
180 0 : << '\n'
181 0 : << COLOR_DEFAULT << "Specific Task: " << COLOR_CYAN
182 0 : << action->specificTaskName() << std::endl;
183 :
184 : /**
185 : * We need to see if the current Action satisfies multiple tasks. There are a few cases to
186 : * consider:
187 : *
188 : * 1. The current Action is registered with multiple syntax blocks. In this case we can only use
189 : * the current instance to satisfy the specific task listed for this syntax block. We can
190 : * detect this case by inspecting whether it has a "specific task name" set in the Action
191 : * instance.
192 : *
193 : * 2. This action does not have a valid "registered identifier" set in the Action instance. This
194 : * means that this Action was not built by the Parser. It was most likely created through a
195 : * Meta-Action (See Case 3 for exception). In this case, the ActionFactory itself would have
196 : * already set the task it found from the build info used to construct the Action so we'd
197 : * arbitrarily satisfy a single task in this case.
198 : *
199 : * 3. The current Action is registered with only a single syntax block. In this case we can simply
200 : * re-use the current instance to act and satisfy _all_ registered tasks. This is the normal
201 : * case where we have a Parser-built Action that does not have a specific task name to satisfy.
202 : * We will use the Action "type" to retrieve the list of tasks that this Action may satisfy.
203 : */
204 3418037 : if (action->specificTaskName() != "") // Case 1
205 463950 : tasks.insert(action->specificTaskName());
206 3226172 : else if (registered_identifier == "" &&
207 3770342 : _syntax.getNonDeprecatedSyntaxByAction(action->type()).size() > 1) // Case 2
208 : {
209 75 : std::set<std::string> local_tasks = action->getAllTasks();
210 : mooseAssert(local_tasks.size() == 1, "More than one task inside of the " << action->name());
211 75 : tasks.insert(*local_tasks.begin());
212 75 : }
213 : else // Case 3
214 2954012 : tasks = _action_factory.getTasksByAction(action->type());
215 :
216 : // TODO: Now we need to weed out the double registrations!
217 8695086 : for (const auto & task : tasks)
218 : {
219 : // Some error checking
220 5277052 : if (!_syntax.hasTask(task))
221 0 : mooseError("A(n) ", task, " is not a registered task");
222 :
223 : // Make sure that the ObjectAction task and Action task are consistent
224 : // otherwise that means that is action was built by the wrong type
225 5277052 : std::shared_ptr<MooseObjectAction> moa = std::dynamic_pointer_cast<MooseObjectAction>(action);
226 5277052 : if (moa.get())
227 : {
228 1162318 : const InputParameters & mparams = moa->getObjectParams();
229 :
230 1162318 : if (mparams.hasBase())
231 : {
232 1162318 : const std::string & base = mparams.getBase();
233 1162318 : if (!_syntax.verifyMooseObjectTask(base, task))
234 3 : mooseError("Task ", task, " is not registered to build ", base, " derived objects");
235 : }
236 : else
237 0 : mooseError("Unable to locate registered base parameter for ", moa->getMooseObjectType());
238 : }
239 :
240 : // Add the current task to current action
241 5277049 : action->appendTask(task);
242 :
243 5277049 : if (_show_parser)
244 0 : Moose::err << COLOR_YELLOW << "Adding Action: " << COLOR_DEFAULT << action->type()
245 0 : << " (" << COLOR_YELLOW << task << COLOR_DEFAULT << ")" << std::endl;
246 :
247 : // Add it to the warehouse
248 5277049 : _action_blocks[task].push_back(action.get());
249 5277049 : }
250 3418034 : _all_ptrs.push_back(action);
251 :
252 3418034 : if (_show_parser)
253 0 : Moose::err << std::endl;
254 3418034 : }
255 :
256 : ActionIterator
257 9688630 : ActionWarehouse::actionBlocksWithActionBegin(const std::string & task)
258 : {
259 9688630 : return _action_blocks[task].begin();
260 : }
261 :
262 : ActionIterator
263 14791617 : ActionWarehouse::actionBlocksWithActionEnd(const std::string & task)
264 : {
265 14791617 : return _action_blocks[task].end();
266 : }
267 :
268 : const std::vector<std::shared_ptr<Action>> &
269 189221 : ActionWarehouse::allActionBlocks() const
270 : {
271 189221 : return _all_ptrs;
272 : }
273 :
274 : const std::list<Action *> &
275 316213 : ActionWarehouse::getActionListByName(const std::string & task) const
276 : {
277 316213 : const auto it = _action_blocks.find(task);
278 316213 : if (it == _action_blocks.end())
279 40037 : return _empty_action_list;
280 : else
281 276176 : return it->second;
282 : }
283 :
284 : bool
285 303393 : ActionWarehouse::hasActions(const std::string & task) const
286 : {
287 303393 : auto it = _action_blocks.find(task);
288 303393 : return it != _action_blocks.end() && !it->second.empty();
289 : }
290 :
291 : void
292 10244823 : ActionWarehouse::buildBuildableActions(const std::string & task)
293 : {
294 10244823 : if (_syntax.shouldAutoBuild(task) && _action_blocks[task].empty())
295 : {
296 1735830 : bool ret_value = false;
297 1735830 : auto it_pair = _action_factory.getActionsByTask(task);
298 3488424 : for (const auto & action_pair : as_range(it_pair))
299 : {
300 1752594 : const auto & type = action_pair.second;
301 1752594 : InputParameters params = _action_factory.getValidParams(type);
302 3505188 : params.set<ActionWarehouse *>("awh") = this;
303 :
304 1752594 : std::string name = "auto_" + type;
305 1752594 : std::transform(
306 48721543 : name.begin(), name.end(), name.begin(), [](const auto v) { return std::tolower(v); });
307 :
308 1752594 : if (params.areAllRequiredParamsValid())
309 : {
310 1636260 : params.set<std::string>("registered_identifier") = "(AutoBuilt)";
311 1636260 : addActionBlock(_action_factory.create(type, name, params));
312 1636260 : ret_value = true;
313 : }
314 1752594 : }
315 :
316 1735830 : if (!ret_value)
317 99570 : _unsatisfied_dependencies.insert(task);
318 : }
319 10244823 : }
320 :
321 : void
322 61010 : ActionWarehouse::checkUnsatisfiedActions() const
323 : {
324 61010 : std::stringstream oss;
325 61010 : bool empty = true;
326 :
327 152320 : for (const auto & udep : _unsatisfied_dependencies)
328 : {
329 91310 : if (_action_blocks.find(udep) == _action_blocks.end())
330 : {
331 0 : if (empty)
332 0 : empty = false;
333 : else
334 0 : oss << " ";
335 0 : oss << udep;
336 : }
337 : }
338 :
339 61010 : if (!empty)
340 0 : mooseError(
341 0 : std::string(
342 0 : "The following unsatisfied actions where found while setting up the MOOSE problem:\n") +
343 0 : oss.str() + "\n");
344 61010 : }
345 :
346 : void
347 18 : ActionWarehouse::printActionDependencySets() const
348 : {
349 : /**
350 : * Note: This routine uses the XTerm colors directly which is not advised for general purpose
351 : * output coloring.
352 : * Most users should prefer using Problem::colorText() which respects the "color_output" option
353 : * for terminals
354 : * that do not support coloring. Since this routine is intended for debugging only and runs
355 : * before several
356 : * objects exist in the system, we are just using the constants directly.
357 : */
358 18 : std::ostringstream oss;
359 :
360 18 : const auto & ordered_names = _syntax.getSortedTaskSet();
361 1944 : for (const auto & task_vector : ordered_names)
362 : {
363 1926 : oss << "[DBG][ACT] (" << COLOR_YELLOW;
364 1926 : std::copy(
365 : task_vector.begin(), task_vector.end(), infix_ostream_iterator<std::string>(oss, ", "));
366 1926 : oss << COLOR_DEFAULT << ")\n";
367 :
368 1926 : std::set<std::string> task_set(task_vector.begin(), task_vector.end());
369 4656 : for (const auto & task : task_set)
370 : {
371 2730 : if (_action_blocks.find(task) == _action_blocks.end())
372 1620 : continue;
373 :
374 2358 : for (const auto & act : _action_blocks.at(task))
375 : {
376 : // The Syntax of the Action if it exists
377 1248 : if (act->name() != "")
378 1248 : oss << "[DBG][ACT]\t" << COLOR_GREEN << act->name() << COLOR_DEFAULT << '\n';
379 :
380 : // The task sets
381 1248 : oss << "[DBG][ACT]\t" << act->type();
382 1248 : const std::set<std::string> tasks = act->getAllTasks();
383 1248 : if (tasks.size() > 1)
384 : {
385 738 : oss << " (";
386 : // Break the current Action's tasks into 2 sets, those intersecting with current set and
387 : // then the difference.
388 738 : std::set<std::string> intersection, difference;
389 738 : std::set_intersection(tasks.begin(),
390 : tasks.end(),
391 : task_set.begin(),
392 : task_set.end(),
393 : std::inserter(intersection, intersection.end()));
394 738 : std::set_difference(tasks.begin(),
395 : tasks.end(),
396 : intersection.begin(),
397 : intersection.end(),
398 : std::inserter(difference, difference.end()));
399 :
400 738 : oss << COLOR_CYAN;
401 738 : std::copy(intersection.begin(),
402 : intersection.end(),
403 : infix_ostream_iterator<std::string>(oss, ", "));
404 738 : oss << COLOR_MAGENTA << (difference.empty() ? "" : ", ");
405 738 : std::copy(
406 : difference.begin(), difference.end(), infix_ostream_iterator<std::string>(oss, ", "));
407 738 : oss << COLOR_DEFAULT << ")";
408 738 : }
409 1248 : oss << '\n';
410 1248 : }
411 : }
412 1926 : }
413 :
414 18 : if (_show_action_dependencies)
415 18 : _console << oss.str() << std::endl;
416 18 : }
417 :
418 : void
419 67147 : ActionWarehouse::executeAllActions()
420 : {
421 67147 : _completed_tasks.clear();
422 :
423 67147 : if (_show_action_dependencies)
424 : {
425 18 : _console << "[DBG][ACT] Action Dependency Sets:\n";
426 18 : printActionDependencySets();
427 :
428 18 : _console << "\n[DBG][ACT] Executing actions:" << std::endl;
429 : }
430 :
431 9620576 : for (const auto & task : _ordered_names)
432 : {
433 9559662 : executeActionsWithAction(task);
434 9557345 : std::scoped_lock lock(_completed_tasks_mutex);
435 9557345 : _completed_tasks.insert(task);
436 9557345 : if (_final_task != "" && task == _final_task)
437 3916 : break;
438 9557345 : }
439 :
440 64830 : if (_show_actions)
441 : {
442 : MemoryUtils::Stats stats;
443 66 : MemoryUtils::getMemoryStats(stats);
444 : auto usage =
445 66 : MemoryUtils::convertBytes(stats._physical_memory, MemoryUtils::MemUnits::Megabytes);
446 66 : _console << "[DBG][ACT] Finished executing all actions with memory usage " << usage << "MB\n"
447 66 : << std::endl;
448 : }
449 64830 : }
450 :
451 : void
452 9559662 : ActionWarehouse::executeActionsWithAction(const std::string & task)
453 : {
454 : // Set the current task name
455 9559662 : _current_task = task;
456 :
457 : // UserObjects may reference other UserObjects in their constructors (e.g. through a
458 : // UserObjectName parameter). Construct them in dependency order so the input file does not have
459 : // to declare a referenced UserObject before the UserObject that uses it.
460 9559662 : if (task == "add_user_object")
461 62147 : sortUserObjectActions(_action_blocks[task]);
462 :
463 14548063 : for (auto it = actionBlocksWithActionBegin(task); it != actionBlocksWithActionEnd(task); ++it)
464 : {
465 4990718 : _current_action = *it;
466 :
467 4990718 : if (_show_actions)
468 : {
469 : MemoryUtils::Stats stats;
470 5200 : MemoryUtils::getMemoryStats(stats);
471 : auto usage =
472 5200 : MemoryUtils::convertBytes(stats._physical_memory, MemoryUtils::MemUnits::Megabytes);
473 5200 : _console << "[DBG][ACT] "
474 5200 : << "TASK (" << COLOR_YELLOW << std::setw(24) << task << COLOR_DEFAULT << ") "
475 5200 : << "TYPE (" << COLOR_YELLOW << std::setw(32) << _current_action->type()
476 5200 : << COLOR_DEFAULT << ") "
477 5200 : << "NAME (" << COLOR_YELLOW << std::setw(16) << _current_action->name()
478 5200 : << COLOR_DEFAULT << ") Memory usage " << usage << "MB" << std::endl;
479 : }
480 :
481 4990718 : _current_action->timedAct();
482 : }
483 :
484 9557345 : _current_action = nullptr;
485 9557345 : }
486 :
487 : void
488 33834 : ActionWarehouse::printInputFile(std::ostream & out)
489 : {
490 33834 : InputFileFormatter tree(false);
491 :
492 33834 : std::map<std::string, std::vector<Action *>>::iterator iter;
493 :
494 33834 : std::vector<Action *> ordered_actions;
495 5188384 : for (const auto & block : _action_blocks)
496 7882611 : for (const auto & act : block.second)
497 2728061 : ordered_actions.push_back(act);
498 :
499 2761895 : for (const auto & act : ordered_actions)
500 : {
501 2728061 : std::string name;
502 2728061 : if (act->parameters().blockFullpath() != "")
503 1428799 : name = act->parameters().blockFullpath();
504 : else
505 1299262 : name = act->name();
506 2728061 : const std::set<std::string> & tasks = act->getAllTasks();
507 : mooseAssert(!tasks.empty(), "Task list is empty");
508 :
509 : bool is_parent;
510 2728061 : if (_syntax.isAssociated(name, &is_parent) != "")
511 : {
512 1411258 : const auto & all_params = _app.getInputParameterWarehouse().getInputParameters();
513 1411258 : InputParameters & params = *(all_params.find(act->uniqueActionName())->second.get());
514 :
515 : // temporarily allow input parameter copies required by the input file formatter
516 1411258 : params.allowCopy(true);
517 :
518 : // TODO: Do we need to insert more nodes for each task?
519 1411258 : tree.insertNode(name, *tasks.begin(), true, ¶ms);
520 1411258 : params.allowCopy(false);
521 :
522 1411258 : MooseObjectAction * moose_object_action = dynamic_cast<MooseObjectAction *>(act);
523 1411258 : if (moose_object_action)
524 : {
525 616355 : InputParameters obj_params = moose_object_action->getObjectParams();
526 616355 : tree.insertNode(name, *tasks.begin(), false, &obj_params);
527 616355 : }
528 : }
529 2728061 : }
530 :
531 33834 : out << tree.print("");
532 33834 : }
533 :
534 : std::shared_ptr<FEProblem>
535 0 : ActionWarehouse::problem()
536 : {
537 0 : mooseDeprecated(
538 : "ActionWarehouse::problem() is deprecated, please use ActionWarehouse::problemBase() \n");
539 0 : return std::dynamic_pointer_cast<FEProblem>(_problem);
540 : }
541 :
542 : std::string
543 90 : ActionWarehouse::getCurrentActionName() const
544 : {
545 90 : return getCurrentAction()->parameters().getHitNode()->fullpath();
546 : }
547 :
548 : const std::string &
549 0 : ActionWarehouse::getMooseAppName()
550 : {
551 0 : return _app.name();
552 : }
553 :
554 : bool
555 211187 : ActionWarehouse::hasTask(const std::string & task) const
556 : {
557 211187 : return _action_factory.isRegisteredTask(task);
558 : }
559 :
560 : bool
561 182898 : ActionWarehouse::isTaskComplete(const std::string & task) const
562 : {
563 182898 : if (!hasTask(task))
564 0 : mooseError("\"", task, "\" is not a registered task.");
565 182898 : std::scoped_lock lock(_completed_tasks_mutex);
566 365796 : return _completed_tasks.count(task);
567 182898 : }
|