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