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