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