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