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 "PhysicsBase.h"
11 : #include "MooseUtils.h"
12 : #include "FEProblemBase.h"
13 :
14 : #include "NonlinearSystemBase.h"
15 : #include "AuxiliarySystem.h"
16 : #include "BlockRestrictable.h"
17 : #include "ActionComponent.h"
18 : #include "InitialConditionBase.h"
19 : #include "FVInitialConditionBase.h"
20 : #include "MooseVariableScalar.h"
21 : #include "LinearSystem.h"
22 :
23 : InputParameters
24 782 : PhysicsBase::validParams()
25 : {
26 782 : InputParameters params = Action::validParams();
27 1564 : params.addClassDescription("Creates all the objects necessary to solve a particular physics");
28 :
29 3128 : params.addParam<std::vector<SubdomainName>>(
30 : "block", {}, "Blocks (subdomains) that this Physics is active on.");
31 :
32 3128 : MooseEnum transient_options("true false same_as_problem", "same_as_problem");
33 3128 : params.addParam<MooseEnum>(
34 : "transient", transient_options, "Whether the physics is to be solved as a transient");
35 :
36 3128 : params.addParam<bool>("verbose", false, "Flag to facilitate debugging a Physics");
37 :
38 : // Numerical solve parameters
39 3910 : params.addParam<std::vector<SolverSystemName>>(
40 : "system_names",
41 : {"nl0"},
42 : "Name of the solver system(s) for the variables. If a single name is specified, "
43 : "that system is used for all solver variables.");
44 3128 : MooseEnum pc_options("default defer", "defer");
45 3128 : params.addParam<MooseEnum>("preconditioning",
46 : pc_options,
47 : "Which preconditioning to use/add for this Physics, or whether to "
48 : "defer to the Preconditioning block, or another Physics");
49 :
50 : // Restart parameters
51 2346 : params.addParam<bool>("initialize_variables_from_mesh_file",
52 1564 : false,
53 : "Determines if the variables that are added by the action are initialized"
54 : "from the mesh file (only for Exodus format)");
55 3128 : params.addParam<std::string>(
56 : "initial_from_file_timestep",
57 : "LATEST",
58 : "Gives the time step number (or \"LATEST\") for which to read the Exodus solution");
59 3128 : params.addParamNamesToGroup("initialize_variables_from_mesh_file initial_from_file_timestep",
60 : "Restart from Exodus");
61 :
62 : // Options to turn off tasks
63 2346 : params.addParam<bool>("dont_create_solver_variables",
64 1564 : false,
65 : "Whether to skip the 'add_variable'/'add_variables_physics' task(s)");
66 2346 : params.addParam<bool>(
67 1564 : "dont_create_ics", false, "Whether to skip the 'add_ic'/'add_fv_ic/add_ics_physics' task(s)");
68 2346 : params.addParam<bool>(
69 1564 : "dont_create_kernels", false, "Whether to skip the 'add_kernel' task for each kernel type");
70 2346 : params.addParam<bool>("dont_create_bcs",
71 1564 : false,
72 : "Whether to skip the 'add_bc' task for each boundary condition type");
73 3128 : params.addParam<bool>("dont_create_functions", false, "Whether to skip the 'add_function' task");
74 2346 : params.addParam<bool>(
75 1564 : "dont_create_aux_variables", false, "Whether to skip the 'add_aux_variable' task");
76 2346 : params.addParam<bool>(
77 1564 : "dont_create_aux_kernels", false, "Whether to skip the 'add_aux_kernel' task");
78 2346 : params.addParam<bool>(
79 : "dont_create_materials",
80 1564 : false,
81 : "Whether to skip the 'add_material'/'add_materials_physics' task(s) for each material type");
82 2346 : params.addParam<bool>(
83 : "dont_create_user_objects",
84 1564 : false,
85 : "Whether to skip the 'add_user_object' task. This does not apply to UserObject derived "
86 : "classes being created on a different task (for example: postprocessors, VPPs, correctors)");
87 2346 : params.addParam<bool>(
88 2346 : "dont_create_correctors", false, "Whether to skip the 'add_correctors' task");
89 2346 : params.addParam<bool>(
90 1564 : "dont_create_postprocessors", false, "Whether to skip the 'add_postprocessors' task");
91 2346 : params.addParam<bool>("dont_create_vectorpostprocessors",
92 1564 : false,
93 : "Whether to skip the 'add_vectorpostprocessors' task");
94 3128 : params.addParamNamesToGroup(
95 : "dont_create_solver_variables dont_create_ics dont_create_kernels dont_create_bcs "
96 : "dont_create_functions dont_create_aux_variables dont_create_aux_kernels "
97 : "dont_create_materials dont_create_user_objects dont_create_correctors "
98 : "dont_create_postprocessors dont_create_vectorpostprocessors",
99 : "Reduce Physics object creation");
100 :
101 3128 : params.addParamNamesToGroup("active inactive", "Advanced");
102 2346 : params.addParamNamesToGroup("preconditioning system_names", "Numerical scheme");
103 1564 : return params;
104 1564 : }
105 :
106 278 : PhysicsBase::PhysicsBase(const InputParameters & parameters)
107 : : Action(parameters),
108 : InputParametersChecksUtils<PhysicsBase>(this),
109 278 : _system_names(getParam<std::vector<SolverSystemName>>("system_names")),
110 556 : _verbose(getParam<bool>("verbose")),
111 556 : _preconditioning(getParam<MooseEnum>("preconditioning")),
112 556 : _blocks(getParam<std::vector<SubdomainName>>("block")),
113 1390 : _is_transient(getParam<MooseEnum>("transient"))
114 : {
115 1112 : checkSecondParamSetOnlyIfFirstOneTrue("initialize_variables_from_mesh_file",
116 : "initial_from_file_timestep");
117 278 : prepareCopyVariablesFromMesh();
118 556 : addRequiredPhysicsTask("init_physics");
119 556 : addRequiredPhysicsTask("copy_vars_physics");
120 278 : addRequiredPhysicsTask("check_integrity_early_physics");
121 278 : }
122 :
123 : void
124 2108 : PhysicsBase::act()
125 : {
126 2108 : mooseDoOnce(checkRequiredTasks());
127 :
128 : // Lets a derived Physics class implement additional tasks
129 2108 : actOnAdditionalTasks();
130 :
131 : // Initialization and variables
132 2102 : if (_current_task == "init_physics")
133 263 : initializePhysics();
134 2075 : else if ((_current_task == "add_variable" || _current_task == "add_variables_physics") &&
135 2547 : !getParam<bool>("dont_create_solver_variables"))
136 236 : addSolverVariables();
137 1600 : else if ((_current_task == "add_ic" || _current_task == "add_fv_ic" ||
138 3427 : _current_task == "add_ics_physics") &&
139 2275 : !getParam<bool>("dont_create_ics"))
140 224 : addInitialConditions();
141 :
142 : // Kernels
143 1379 : else if (_current_task == "add_interpolation_method_physics" &&
144 1379 : !getParam<bool>("dont_create_kernels"))
145 0 : addFVInterpolationMethods();
146 1631 : else if (_current_task == "add_kernel" && !getParam<bool>("dont_create_kernels"))
147 126 : addFEKernels();
148 1253 : else if (_current_task == "add_nodal_kernel" && !getParam<bool>("dont_create_kernels"))
149 0 : addNodalKernels();
150 1343 : else if ((_current_task == "add_fv_kernel" || _current_task == "add_linear_fv_kernel") &&
151 1523 : !getParam<bool>("dont_create_kernels"))
152 90 : addFVKernels();
153 1163 : else if (_current_task == "add_dirac_kernel" && !getParam<bool>("dont_create_kernels"))
154 0 : addDiracKernels();
155 1163 : else if (_current_task == "add_dg_kernel" && !getParam<bool>("dont_create_kernels"))
156 0 : addDGKernels();
157 1163 : else if (_current_task == "add_scalar_kernel" && !getParam<bool>("dont_create_kernels"))
158 0 : addScalarKernels();
159 1163 : else if (_current_task == "add_interface_kernel" && !getParam<bool>("dont_create_kernels"))
160 0 : addInterfaceKernels();
161 1163 : else if (_current_task == "add_fv_ik" && !getParam<bool>("dont_create_kernels"))
162 0 : addFVInterfaceKernels();
163 :
164 : // Boundary conditions
165 1415 : else if (_current_task == "add_bc" && !getParam<bool>("dont_create_bcs"))
166 126 : addFEBCs();
167 1037 : else if (_current_task == "add_nodal_bc" && !getParam<bool>("dont_create_bcs"))
168 0 : addNodalBCs();
169 1127 : else if ((_current_task == "add_fv_bc" || _current_task == "add_linear_fv_bc") &&
170 1307 : !getParam<bool>("dont_create_bcs"))
171 90 : addFVBCs();
172 947 : else if (_current_task == "add_periodic_bc" && !getParam<bool>("dont_create_bcs"))
173 0 : addPeriodicBCs();
174 :
175 : // Auxiliary quantities
176 947 : else if (_current_task == "add_function" && !getParam<bool>("dont_create_functions"))
177 0 : addFunctions();
178 947 : else if (_current_task == "add_aux_variable" && !getParam<bool>("dont_create_aux_variables"))
179 0 : addAuxiliaryVariables();
180 947 : else if (_current_task == "add_aux_kernel" && !getParam<bool>("dont_create_aux_kernels"))
181 0 : addAuxiliaryKernels();
182 947 : else if ((_current_task == "add_material" || _current_task == "add_materials_physics") &&
183 947 : !getParam<bool>("dont_create_materials"))
184 0 : addMaterials();
185 947 : else if (_current_task == "add_functor_material" && !getParam<bool>("dont_create_materials"))
186 0 : addFunctorMaterials();
187 :
188 : // Multiapp
189 947 : else if (_current_task == "add_multi_app")
190 0 : addMultiApps();
191 947 : else if (_current_task == "add_transfer")
192 0 : addTransfers();
193 :
194 : // User objects and output
195 983 : else if (_current_task == "add_user_object" && !getParam<bool>("dont_create_user_objects"))
196 18 : addUserObjects();
197 929 : else if (_current_task == "add_corrector" && !getParam<bool>("dont_create_correctors"))
198 0 : addCorrectors();
199 1365 : else if (_current_task == "add_postprocessor" && !getParam<bool>("dont_create_postprocessors"))
200 218 : addPostprocessors();
201 711 : else if (_current_task == "add_vector_postprocessor" &&
202 711 : !getParam<bool>("dont_create_vectorpostprocessors"))
203 0 : addVectorPostprocessors();
204 711 : else if (_current_task == "add_reporter")
205 0 : addReporters();
206 711 : else if (_current_task == "add_output")
207 0 : addOutputs();
208 :
209 : // Equation solver-related tasks
210 711 : else if (_current_task == "add_preconditioning")
211 221 : addPreconditioning();
212 490 : else if (_current_task == "add_executioner")
213 0 : addExecutioner();
214 490 : else if (_current_task == "add_executor")
215 0 : addExecutors();
216 :
217 : // Checks
218 490 : else if (_current_task == "check_integrity_early_physics")
219 251 : checkIntegrityEarly();
220 239 : else if (_current_task == "check_integrity")
221 0 : checkIntegrity();
222 :
223 : // Exodus restart capabilities
224 2081 : if (_current_task == "copy_vars_physics")
225 : {
226 239 : copyVariablesFromMesh(solverVariableNames(), true);
227 239 : if (_aux_var_names.size() > 0)
228 0 : copyVariablesFromMesh(auxVariableNames(), false);
229 : }
230 2081 : }
231 :
232 : void
233 18 : PhysicsBase::addUserObject(const std::string & uo_type,
234 : const std::string & uo_name,
235 : InputParameters & params)
236 : {
237 : mooseAssert(
238 : [&]()
239 : {
240 : const auto supplied = getSuppliedUserObjects();
241 : return std::find(supplied.begin(), supplied.end(), uo_name) != supplied.end();
242 : }(),
243 : "The UserObject '" + uo_name + "' added by Physics '" + name() +
244 : "' was not declared in getSuppliedUserObjects(). Declare it there so its construction "
245 : "order can be resolved.");
246 18 : getProblem().addUserObject(uo_type, uo_name, params);
247 18 : }
248 :
249 : void
250 278 : PhysicsBase::prepareCopyVariablesFromMesh() const
251 : {
252 834 : if (getParam<bool>("initialize_variables_from_mesh_file"))
253 0 : _app.setExodusFileRestart(true);
254 :
255 1112 : checkSecondParamSetOnlyIfFirstOneTrue("initialize_variables_from_mesh_file",
256 : "initial_from_file_timestep");
257 278 : }
258 :
259 : bool
260 216 : PhysicsBase::isTransient() const
261 : {
262 : mooseAssert(_problem, "We don't have a problem yet");
263 216 : if (_is_transient == "true")
264 0 : return true;
265 216 : else if (_is_transient == "false")
266 0 : return false;
267 : else
268 216 : return getProblem().isTransient();
269 : }
270 :
271 : unsigned int
272 0 : PhysicsBase::dimension() const
273 : {
274 : mooseAssert(_mesh, "We dont have a mesh yet");
275 : mooseAssert(_dim < 4, "Dimension has not been set yet");
276 0 : return _dim;
277 : }
278 :
279 : std::set<SubdomainID>
280 18 : PhysicsBase::getSubdomainIDs(const std::set<SubdomainName> & blocks) const
281 : {
282 : const bool not_block_restricted =
283 18 : (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end()) ||
284 0 : allMeshBlocks(blocks);
285 : mooseAssert(_mesh, "Should have a mesh");
286 : // use a set for simplicity. Note that subdomain names are unique, except maybe the empty one,
287 : // which cannot be specified by the user to the Physics.
288 : // MooseMesh::getSubdomainIDs cannot deal with the 'ANY_BLOCK_ID' name
289 : std::set<SubdomainID> block_ids_set =
290 18 : not_block_restricted ? _mesh->meshSubdomains() : _mesh->getSubdomainIDs(blocks);
291 18 : return block_ids_set;
292 : }
293 :
294 : std::vector<std::string>
295 3 : PhysicsBase::getSubdomainNamesAndIDs(const std::set<SubdomainID> & blocks) const
296 : {
297 : mooseAssert(_mesh, "Should have a mesh");
298 3 : std::vector<std::string> sub_names_ids;
299 3 : sub_names_ids.reserve(blocks.size());
300 6 : for (const auto bid : blocks)
301 : {
302 3 : const auto bname = _mesh->getSubdomainName(bid);
303 9 : sub_names_ids.push_back((bname.empty() ? "(unnamed)" : bname) + " (" + std::to_string(bid) +
304 : ")");
305 3 : }
306 3 : return sub_names_ids;
307 0 : }
308 :
309 : void
310 0 : PhysicsBase::addBlocks(const std::vector<SubdomainName> & blocks)
311 : {
312 0 : if (blocks.size())
313 : {
314 0 : _blocks.insert(_blocks.end(), blocks.begin(), blocks.end());
315 0 : _dim = _mesh->getBlocksMaxDimension(_blocks);
316 : }
317 0 : }
318 :
319 : void
320 0 : PhysicsBase::addBlocksById(const std::vector<SubdomainID> & block_ids)
321 : {
322 0 : if (block_ids.size())
323 : {
324 0 : for (const auto bid : block_ids)
325 0 : _blocks.push_back(_mesh->getSubdomainName(bid));
326 0 : _dim = _mesh->getBlocksMaxDimension(_blocks);
327 : }
328 0 : }
329 :
330 : void
331 120 : PhysicsBase::addComponent(const ActionComponent & component)
332 : {
333 240 : for (const auto & block : component.blocks())
334 120 : _blocks.push_back(block);
335 120 : }
336 :
337 : void
338 728 : PhysicsBase::addRelationshipManagers(Moose::RelationshipManagerType input_rm_type)
339 : {
340 728 : InputParameters params = getAdditionalRMParams();
341 728 : Action::addRelationshipManagers(input_rm_type, params);
342 728 : }
343 :
344 : const ActionComponent &
345 174 : PhysicsBase::getActionComponent(const ComponentName & comp_name) const
346 : {
347 174 : return _awh.getAction<ActionComponent>(comp_name);
348 : }
349 :
350 : void
351 263 : PhysicsBase::initializePhysics()
352 : {
353 : // Annoying edge case. We cannot use ANY_BLOCK_ID for kernels and variables since errors got
354 : // added downstream for using it, we cannot leave it empty as that sets all objects to not live
355 : // on any block
356 789 : if (isParamSetByUser("block") && _blocks.empty())
357 0 : paramError("block",
358 : "Empty block restriction is not supported. Comment out the Physics if you are "
359 : "trying to disable it.");
360 :
361 : // Components should have added their blocks already.
362 263 : if (_blocks.empty())
363 137 : _blocks.push_back("ANY_BLOCK_ID");
364 :
365 : mooseAssert(_mesh, "We should have a mesh to find the dimension");
366 263 : if (_blocks.size())
367 263 : _dim = _mesh->getBlocksMaxDimension(_blocks);
368 : else
369 0 : _dim = _mesh->dimension();
370 :
371 : // Forward physics verbosity to problem to output the setup
372 263 : if (_verbose)
373 0 : getProblem().setVerboseProblem(_verbose);
374 :
375 : // If the derived physics need additional initialization very early on
376 263 : initializePhysicsAdditional();
377 :
378 : // Check that the systems exist in the Problem
379 : // TODO: try to add the systems to the problem from here instead
380 : // NOTE: this must be performed after the "Additional" initialization because the list
381 : // of systems might have been adjusted once the dimension of the Physics is known
382 263 : const auto & problem_nl_systems = getProblem().getNonlinearSystemNames();
383 263 : const auto & problem_lin_systems = getProblem().getLinearSystemNames();
384 526 : for (const auto & sys_name : _system_names)
385 266 : if (std::find(problem_nl_systems.begin(), problem_nl_systems.end(), sys_name) ==
386 269 : problem_nl_systems.end() &&
387 3 : std::find(problem_lin_systems.begin(), problem_lin_systems.end(), sys_name) ==
388 269 : problem_lin_systems.end() &&
389 3 : solverVariableNames().size())
390 3 : mooseError("System '", sys_name, "' is not found in the Problem");
391 :
392 : // Cache system number as it makes some logic easier
393 523 : for (const auto & sys_name : _system_names)
394 263 : _system_numbers.push_back(getProblem().solverSysNum(sys_name));
395 260 : }
396 :
397 : void
398 251 : PhysicsBase::checkIntegrityEarly() const
399 : {
400 251 : if (_is_transient == "true" && !getProblem().isTransient())
401 6 : paramError("transient", "We cannot solve a physics as transient in a steady problem");
402 :
403 : // Check that there is a system for each variable
404 248 : if (_system_names.size() != 1 && _system_names.size() != _solver_var_names.size())
405 3 : paramError("system_names",
406 : "There should be one system name per solver variable (potentially repeated), or a "
407 3 : "single system name for all variables. Currently you have '" +
408 6 : std::to_string(_system_names.size()) + "' systems specified for '" +
409 6 : std::to_string(_solver_var_names.size()) + "' solver variables.");
410 :
411 : // Check that each variable is present in the expected system
412 245 : unsigned int var_i = 0;
413 472 : for (const auto & var_name : _solver_var_names)
414 : {
415 227 : const auto & sys_name = _system_names.size() == 1 ? _system_names[0] : _system_names[var_i++];
416 227 : if (!_problem->getSolverSystem(_problem->solverSysNum(sys_name)).hasVariable(var_name) &&
417 0 : !_problem->getSolverSystem(_problem->solverSysNum(sys_name)).hasScalarVariable(var_name))
418 0 : paramError("system_names",
419 0 : "We expected system '" + sys_name + "' to contain variable '" + var_name +
420 : "' but it did not. Make sure the system names closely match the ordering of "
421 : "the variables in the Physics.");
422 : }
423 245 : }
424 :
425 : void
426 239 : PhysicsBase::copyVariablesFromMesh(const std::vector<VariableName> & variables_to_copy,
427 : bool are_solver_var)
428 : {
429 717 : if (getParam<bool>("initialize_variables_from_mesh_file"))
430 : {
431 0 : mooseInfoRepeated("Adding Exodus restart for " + std::to_string(variables_to_copy.size()) +
432 0 : " variables: " + Moose::stringify(variables_to_copy));
433 : // TODO Check that the variable types and orders are actually supported for exodus restart
434 0 : for (const auto i : index_range(variables_to_copy))
435 : {
436 0 : SystemBase & system = are_solver_var ? getProblem().getSystemBase(_system_numbers.size() == 1
437 0 : ? _system_numbers[0]
438 0 : : _system_numbers[i])
439 0 : : getProblem().systemBaseAuxiliary();
440 0 : const auto & var_name = variables_to_copy[i];
441 0 : system.addVariableToCopy(
442 0 : var_name, var_name, getParam<std::string>("initial_from_file_timestep"));
443 : }
444 : }
445 239 : }
446 :
447 : bool
448 236 : PhysicsBase::variableExists(const VariableName & var_name, bool error_if_aux) const
449 : {
450 236 : if (error_if_aux && _problem->getAuxiliarySystem().hasVariable(var_name))
451 0 : mooseError("Variable '",
452 : var_name,
453 : "' is supposed to be nonlinear for physics '",
454 0 : name(),
455 : "' but it is already defined as auxiliary");
456 : else
457 236 : return _problem->hasVariable(var_name);
458 : }
459 :
460 : bool
461 0 : PhysicsBase::solverVariableExists(const VariableName & var_name) const
462 : {
463 0 : return _problem->hasSolverVariable(var_name);
464 : }
465 :
466 : const SolverSystemName &
467 0 : PhysicsBase::getSolverSystem(unsigned int variable_index) const
468 : {
469 : mooseAssert(!_system_names.empty(), "We should have a solver system name");
470 0 : if (_system_names.size() == 1)
471 0 : return _system_names[0];
472 : else
473 : // We trust that the system names and the variable names match one-to-one as it is enforced by
474 : // the checkIntegrityEarly() routine.
475 0 : return _system_names[variable_index];
476 : }
477 :
478 : const SolverSystemName &
479 191 : PhysicsBase::getSolverSystem(const VariableName & var_name) const
480 : {
481 : mooseAssert(!_system_names.empty(), "We should have a solver system name");
482 : // No need to look if only one system for the Physics
483 191 : if (_system_names.size() == 1)
484 188 : return _system_names[0];
485 :
486 : // We trust that the system names and the variable names match one-to-one as it is enforced by
487 : // the checkIntegrityEarly() routine.
488 3 : for (const auto variable_index : index_range(_solver_var_names))
489 3 : if (var_name == _solver_var_names[variable_index])
490 3 : return _system_names[variable_index];
491 0 : mooseError("Variable '", var_name, "' was not found within the Physics solver variables.");
492 : }
493 :
494 : void
495 194 : PhysicsBase::checkRequiredTasks() const
496 : {
497 194 : const auto registered_tasks = _action_factory.getTasksByAction(type());
498 :
499 : // Check for missing tasks
500 1286 : for (const auto & required_task : _required_tasks)
501 1092 : if (!registered_tasks.count(required_task))
502 0 : mooseWarning("Task '" + required_task +
503 0 : "' has been declared as required by a Physics parent class of derived class '" +
504 0 : type() +
505 : "' but this task is not registered to the derived class. Registered tasks for "
506 0 : "this Physics are: " +
507 0 : Moose::stringify(registered_tasks));
508 194 : }
509 :
510 : void
511 994 : PhysicsBase::assignBlocks(InputParameters & params, const std::vector<SubdomainName> & blocks) const
512 : {
513 : // We only set the blocks if we don't have `ANY_BLOCK_ID` defined because the subproblem
514 : // (through the mesh) errors out if we use this keyword during the addVariable/Kernel
515 : // functions
516 994 : if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") == blocks.end())
517 1074 : params.set<std::vector<SubdomainName>>("block") = blocks;
518 994 : if (blocks.empty())
519 0 : mooseInfoRepeated("Empty block restriction assigned to an object created by Physics '" +
520 0 : name() + "'.\n Did you mean to do this?");
521 994 : }
522 :
523 : bool
524 0 : PhysicsBase::checkBlockRestrictionIdentical(const std::string & object_name,
525 : const std::vector<SubdomainName> & blocks,
526 : bool error_if_not_identical) const
527 : {
528 : // If identical, we can return fast
529 0 : if (_blocks == blocks)
530 0 : return true;
531 : // If one is block restricted to anywhere and the other is block restricted to anywhere manually
532 0 : if ((std::find(_blocks.begin(), _blocks.end(), "ANY_BLOCK_ID") != _blocks.end() &&
533 0 : allMeshBlocks(blocks)) ||
534 0 : (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end() &&
535 0 : allMeshBlocks(_blocks)))
536 0 : return true;
537 :
538 : // Copy, sort and unique is the only way to check that they are actually the same
539 0 : auto copy_blocks = _blocks;
540 0 : auto copy_blocks_other = blocks;
541 0 : std::sort(copy_blocks.begin(), copy_blocks.end());
542 0 : copy_blocks.erase(unique(copy_blocks.begin(), copy_blocks.end()), copy_blocks.end());
543 0 : std::sort(copy_blocks_other.begin(), copy_blocks_other.end());
544 0 : copy_blocks_other.erase(unique(copy_blocks_other.begin(), copy_blocks_other.end()),
545 0 : copy_blocks_other.end());
546 :
547 0 : if (copy_blocks == copy_blocks_other)
548 0 : return true;
549 0 : std::vector<SubdomainName> diff;
550 0 : std::set_difference(copy_blocks.begin(),
551 : copy_blocks.end(),
552 : copy_blocks_other.begin(),
553 : copy_blocks_other.end(),
554 : std::inserter(diff, diff.begin()));
555 0 : if (error_if_not_identical)
556 0 : mooseError("Physics '",
557 0 : name(),
558 : "' and object '",
559 : object_name,
560 : "' have different block restrictions.\nPhysics: ",
561 0 : Moose::stringify(_blocks),
562 : "\nObject: ",
563 0 : Moose::stringify(blocks),
564 : "\nDifference: ",
565 0 : Moose::stringify(diff));
566 : else
567 0 : return false;
568 0 : }
569 :
570 : bool
571 0 : PhysicsBase::hasBlocks(const std::vector<SubdomainName> & blocks) const
572 : {
573 : mooseAssert(_blocks.size(), "hasBlocks called before blocks were initialized");
574 0 : return std::all_of(blocks.begin(),
575 : blocks.end(),
576 0 : [this](const SubdomainName & block)
577 0 : { return std::find(_blocks.begin(), _blocks.end(), block) != _blocks.end(); });
578 : }
579 :
580 : bool
581 39 : PhysicsBase::allMeshBlocks(const std::vector<SubdomainName> & blocks) const
582 : {
583 : mooseAssert(_mesh, "The mesh should exist already");
584 : // Try to return faster without examining every single block
585 39 : if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end())
586 0 : return true;
587 39 : else if (blocks.size() != _mesh->meshSubdomains().size())
588 3 : return false;
589 :
590 108 : for (const auto mesh_block : _mesh->meshSubdomains())
591 : {
592 72 : const auto & subdomain_name = _mesh->getSubdomainName(mesh_block);
593 : // Check subdomain name
594 72 : if (!subdomain_name.empty() &&
595 72 : std::find(blocks.begin(), blocks.end(), subdomain_name) == blocks.end())
596 0 : return false;
597 : // no subdomain name, check the IDs being used as names instead
598 72 : else if (std::find(blocks.begin(), blocks.end(), std::to_string(mesh_block)) == blocks.end())
599 0 : return false;
600 : }
601 36 : return true;
602 : }
603 :
604 : bool
605 0 : PhysicsBase::allMeshBlocks(const std::set<SubdomainName> & blocks) const
606 : {
607 0 : std::vector<SubdomainName> blocks_vec(blocks.begin(), blocks.end());
608 0 : return allMeshBlocks(blocks_vec);
609 0 : }
610 :
611 : void
612 221 : PhysicsBase::addPetscPairsToPetscOptions(
613 : const std::vector<std::pair<MooseEnumItem, std::string>> & petsc_pair_options)
614 : {
615 221 : Moose::PetscSupport::PetscOptions & po = _problem->getPetscOptions();
616 442 : for (const auto solver_sys_num : _system_numbers)
617 221 : Moose::PetscSupport::addPetscPairsToPetscOptions(
618 : petsc_pair_options,
619 221 : _problem->mesh().dimension(),
620 442 : _problem->getSolverSystem(solver_sys_num).prefix(),
621 : *this,
622 : po);
623 221 : }
624 :
625 : bool
626 3 : PhysicsBase::isVariableFV(const VariableName & var_name) const
627 : {
628 3 : const auto var = &_problem->getVariable(0, var_name);
629 3 : return var->isFV();
630 : }
631 :
632 : bool
633 0 : PhysicsBase::isVariableScalar(const VariableName & var_name) const
634 : {
635 0 : return _problem->hasScalarVariable(var_name);
636 : }
637 :
638 : bool
639 236 : PhysicsBase::shouldCreateVariable(const VariableName & var_name,
640 : const std::vector<SubdomainName> & blocks,
641 : const bool error_if_aux)
642 : {
643 236 : if (!variableExists(var_name, error_if_aux))
644 191 : return true;
645 : // check block restriction
646 45 : auto & var = _problem->getVariable(0, var_name);
647 : const bool not_block_restricted =
648 57 : (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end()) ||
649 12 : allMeshBlocks(blocks);
650 45 : if (!var.blockRestricted() || (!not_block_restricted && var.hasBlocks(blocks)))
651 18 : return false;
652 :
653 : // This is an edge case, which might warrant a warning
654 27 : if (allMeshBlocks(var.blocks()) && not_block_restricted)
655 24 : return false;
656 : else
657 9 : mooseError("Variable '" + var_name + "' already exists with subdomain restriction '" +
658 9 : Moose::stringify(var.blocks()) + "' which does not include the subdomains '" +
659 9 : Moose::stringify(blocks) + "', required for this Physics.");
660 : }
661 :
662 : bool
663 3 : PhysicsBase::shouldCreateIC(const VariableName & var_name,
664 : const std::vector<SubdomainName> & blocks,
665 : const bool ic_is_default_ic,
666 : const bool error_if_already_defined) const
667 : {
668 : // Handle recover
669 3 : if (ic_is_default_ic && (_app.isRestarting() || _app.isRecovering()))
670 0 : return false;
671 : // do not set initial conditions if we are loading fields from the mesh file
672 9 : if (getParam<bool>("initialize_variables_from_mesh_file"))
673 0 : return false;
674 : // Different type of ICs, not block restrictable
675 : mooseAssert(!isVariableScalar(var_name), "shouldCreateIC not implemented for scalar variables");
676 :
677 : // Process the desired block restriction into a set of subdomain IDs
678 3 : std::set<SubdomainName> blocks_set(blocks.begin(), blocks.end());
679 3 : const auto blocks_ids_set = getSubdomainIDs(blocks_set);
680 :
681 : // Check whether there are any ICs for this variable already in the problem
682 3 : std::set<SubdomainID> blocks_ids_covered;
683 : bool has_all_blocks;
684 3 : if (isVariableFV(var_name))
685 : {
686 0 : has_all_blocks = _problem->getFVInitialConditionWarehouse().hasObjectsForVariableAndBlocks(
687 : var_name, blocks_ids_set, blocks_ids_covered, /*tid =*/0);
688 : // FV variables can be initialized by non-FV ICs
689 0 : std::set<SubdomainID> blocks_ids_covered_fe;
690 : const bool has_all_blocks_from_feics =
691 0 : _problem->getInitialConditionWarehouse().hasObjectsForVariableAndBlocks(
692 : var_name, blocks_ids_set, blocks_ids_covered_fe, /*tid =*/0);
693 : // Note we are missing the case with complete but split coverage
694 0 : has_all_blocks = has_all_blocks || has_all_blocks_from_feics;
695 0 : blocks_ids_covered.insert(blocks_ids_covered_fe.begin(), blocks_ids_covered_fe.end());
696 0 : }
697 : else
698 3 : has_all_blocks = _problem->getInitialConditionWarehouse().hasObjectsForVariableAndBlocks(
699 : var_name, blocks_ids_set, blocks_ids_covered, /*tid =*/0);
700 :
701 3 : const bool has_some_blocks = !blocks_ids_covered.empty();
702 3 : if (!has_some_blocks)
703 0 : return true;
704 :
705 3 : if (has_all_blocks)
706 : {
707 3 : if (error_if_already_defined)
708 9 : mooseError("ICs for variable '" + var_name + "' have already been defined for blocks '" +
709 9 : Moose::stringify(blocks) + "'.");
710 : else
711 0 : return false;
712 : }
713 :
714 : // Partial overlap between Physics is not implemented.
715 0 : mooseError("There is a partial overlap between the subdomains covered by pre-existing initial "
716 0 : "conditions (ICs), defined on blocks (ids): " +
717 0 : Moose::stringify(getSubdomainNamesAndIDs(blocks_ids_covered)) +
718 0 : "\n and a newly created IC for variable '" + var_name +
719 0 : "', to be defined on blocks: " + Moose::stringify(blocks) +
720 : ".\nWe should be creating the Physics' IC only for non-covered blocks. This is not "
721 : "implemented at this time.");
722 0 : }
723 :
724 : bool
725 216 : PhysicsBase::shouldCreateTimeDerivative(const VariableName & var_name,
726 : const std::vector<SubdomainName> & blocks,
727 : const bool error_if_already_defined) const
728 : {
729 : // Follow the transient setting of the Physics
730 216 : if (!isTransient())
731 108 : return false;
732 :
733 : // Variable is either nonlinear (FV/FE), nodal nonlinear (field of ODEs), linear, or scalar.
734 : // The warehouses hosting the time kernels are different for each of these types
735 : // Different type of time derivatives, not block restrictable
736 : mooseAssert(!isVariableScalar(var_name),
737 : "shouldCreateTimeDerivative not implemented for scalar variables");
738 : mooseAssert(!_problem->hasAuxiliaryVariable(var_name),
739 : "Should not be called with auxiliary variables");
740 :
741 : // Get solver system type
742 108 : const auto var = &_problem->getVariable(0, var_name);
743 108 : const auto var_id = var->number();
744 108 : const auto sys_num = var->sys().number();
745 : const auto time_vector_tag =
746 108 : (sys_num < _problem->numNonlinearSystems())
747 108 : ? var->sys().timeVectorTag()
748 : // this is not quite correct. Many kernels can contribute to RHS time vector on paper
749 0 : : dynamic_cast<LinearSystem *>(&var->sys())->rightHandSideTimeVectorTag();
750 :
751 : // We just use the warehouse, it should cover every time derivative object type
752 108 : bool all_blocks_covered = true;
753 108 : std::set<SubdomainID> blocks_ids_covered;
754 : // we examine subdomain by subdomain, because mutiple kernels could be covering every block in
755 : // the 'blocks' parameter
756 228 : for (const auto & block : blocks)
757 : {
758 120 : std::vector<MooseObject *> time_kernels;
759 120 : if (block != "ANY_BLOCK_ID")
760 : {
761 24 : const auto bid = _mesh->getSubdomainID(block);
762 24 : _problem->theWarehouse()
763 48 : .query()
764 24 : .template condition<AttribSysNum>(sys_num)
765 24 : .template condition<AttribVar>(var_id)
766 24 : .template condition<AttribSubdomains>(bid)
767 : // we use the time tag as a proxy for time derivatives
768 24 : .template condition<AttribVectorTags>(time_vector_tag)
769 24 : .queryInto(time_kernels);
770 : }
771 : else
772 96 : _problem->theWarehouse()
773 192 : .query()
774 96 : .template condition<AttribSysNum>(sys_num)
775 96 : .template condition<AttribVar>(var_id)
776 : // we use the time tag as a proxy for time derivatives
777 96 : .template condition<AttribVectorTags>(time_vector_tag)
778 96 : .queryInto(time_kernels);
779 :
780 120 : if (time_kernels.size())
781 : {
782 15 : if (block == "ANY_BLOCK_ID")
783 : {
784 32 : for (const auto & time_kernel : time_kernels)
785 17 : if (const auto blk = dynamic_cast<BlockRestrictable *>(time_kernel))
786 17 : blocks_ids_covered.insert(blk->blockIDs().begin(), blk->blockIDs().end());
787 : }
788 : else
789 0 : blocks_ids_covered.insert(_mesh->getSubdomainID(block));
790 : }
791 : else
792 105 : all_blocks_covered = false;
793 120 : }
794 :
795 : // From the set of covered blocks, see if the blocks we needed are found
796 108 : if (all_blocks_covered)
797 : {
798 15 : std::set<SubdomainName> blocks_set(blocks.begin(), blocks.end());
799 15 : const auto blocks_ids = getSubdomainIDs(blocks_set);
800 15 : if (blocks_ids != blocks_ids_covered)
801 3 : all_blocks_covered = false;
802 15 : }
803 108 : const bool has_some_blocks = !blocks_ids_covered.empty();
804 108 : if (!has_some_blocks)
805 93 : return true;
806 15 : if (all_blocks_covered)
807 : {
808 12 : if (error_if_already_defined)
809 0 : mooseError("A time kernel for variable '" + var_name +
810 0 : "' has already been defined on blocks '" + Moose::stringify(blocks) + "'.");
811 : else
812 12 : return false;
813 : }
814 :
815 : // Partial overlap between Physics is not implemented.
816 6 : mooseError("There is a partial overlap between the subdomains covered by pre-existing time "
817 3 : "derivative kernel(s), defined on blocks (ids): " +
818 9 : Moose::stringify(getSubdomainNamesAndIDs(blocks_ids_covered)) +
819 6 : "\nand a newly created time derivative kernel for variable " + var_name +
820 9 : ", to be defined on blocks: " + Moose::stringify(blocks) +
821 : ".\nWe should be creating the Physics' time derivative only for non-covered "
822 : "blocks. This is not implemented at this time.");
823 105 : }
824 :
825 : void
826 42 : PhysicsBase::reportPotentiallyMissedParameters(const std::vector<std::string> & param_names,
827 : const std::string & object_type,
828 : const std::string & object_name) const
829 : {
830 42 : std::vector<std::string> defaults_unused;
831 42 : std::vector<std::string> user_values_unused;
832 126 : for (const auto & param : param_names)
833 : {
834 84 : if (isParamSetByUser(param))
835 12 : user_values_unused.push_back(param);
836 72 : else if (isParamValid(param))
837 72 : defaults_unused.push_back(param);
838 : }
839 : const std::string object_name_string =
840 84 : object_name.empty() ? "" : ("and name '" + object_name + "' ");
841 :
842 42 : if (defaults_unused.size() && _verbose)
843 0 : mooseInfoRepeated("Defaults for parameters '" + Moose::stringify(defaults_unused) +
844 0 : "' for object of type '" + object_type + "' " + object_name_string +
845 : "were not used because the object was not created by this Physics.");
846 42 : if (user_values_unused.size())
847 : {
848 12 : if (_app.unusedFlagIsWarning())
849 18 : mooseWarning(
850 36 : "User-specifed values for parameters '" + Moose::stringify(user_values_unused) +
851 27 : "' for object of type '" + object_type + "' " + object_name_string +
852 : "were not used because the corresponding object was not created by this Physics.");
853 3 : else if (_app.unusedFlagIsError())
854 12 : mooseError("User-specified values for parameters '" + Moose::stringify(user_values_unused) +
855 3 : "' for object of type '" + object_type + "' " + object_name_string +
856 : "were not used because the corresponding object was not created by this Physics.");
857 : }
858 39 : }
|