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 1556 : PhysicsBase::validParams()
25 : {
26 1556 : InputParameters params = Action::validParams();
27 1556 : params.addClassDescription("Creates all the objects necessary to solve a particular physics");
28 :
29 1556 : params.addParam<std::vector<SubdomainName>>(
30 : "block", {}, "Blocks (subdomains) that this Physics is active on.");
31 :
32 1556 : MooseEnum transient_options("true false same_as_problem", "same_as_problem");
33 1556 : params.addParam<MooseEnum>(
34 : "transient", transient_options, "Whether the physics is to be solved as a transient");
35 :
36 1556 : params.addParam<bool>("verbose", false, "Flag to facilitate debugging a Physics");
37 :
38 : // Numerical solve parameters
39 4668 : 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 1556 : MooseEnum pc_options("default defer", "defer");
45 1556 : 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 4668 : params.addParam<bool>("initialize_variables_from_mesh_file",
52 3112 : false,
53 : "Determines if the variables that are added by the action are initialized"
54 : "from the mesh file (only for Exodus format)");
55 1556 : 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 1556 : params.addParamNamesToGroup("initialize_variables_from_mesh_file initial_from_file_timestep",
60 : "Restart from Exodus");
61 :
62 : // Options to turn off tasks
63 4668 : params.addParam<bool>(
64 3112 : "dont_create_solver_variables", false, "Whether to skip the 'add_variable' task");
65 1556 : params.addParam<bool>("dont_create_ics", false, "Whether to skip the 'add_ic' task");
66 4668 : params.addParam<bool>(
67 3112 : "dont_create_kernels", false, "Whether to skip the 'add_kernel' task for each kernel type");
68 4668 : params.addParam<bool>("dont_create_bcs",
69 3112 : false,
70 : "Whether to skip the 'add_bc' task for each boundary condition type");
71 1556 : params.addParam<bool>("dont_create_functions", false, "Whether to skip the 'add_function' task");
72 4668 : params.addParam<bool>(
73 3112 : "dont_create_aux_variables", false, "Whether to skip the 'add_aux_variable' task");
74 4668 : params.addParam<bool>(
75 3112 : "dont_create_aux_kernels", false, "Whether to skip the 'add_aux_kernel' task");
76 4668 : params.addParam<bool>("dont_create_materials",
77 3112 : false,
78 : "Whether to skip the 'add_material' task for each material type");
79 4668 : params.addParam<bool>(
80 : "dont_create_user_objects",
81 3112 : 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 4668 : params.addParam<bool>(
85 3112 : "dont_create_correctors", false, "Whether to skip the 'add_correctors' task");
86 4668 : params.addParam<bool>(
87 3112 : "dont_create_postprocessors", false, "Whether to skip the 'add_postprocessors' task");
88 4668 : params.addParam<bool>("dont_create_vectorpostprocessors",
89 3112 : false,
90 : "Whether to skip the 'add_vectorpostprocessors' task");
91 1556 : 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 1556 : params.addParamNamesToGroup("active inactive", "Advanced");
99 1556 : params.addParamNamesToGroup("preconditioning", "Numerical scheme");
100 3112 : return params;
101 3112 : }
102 :
103 278 : PhysicsBase::PhysicsBase(const InputParameters & parameters)
104 : : Action(parameters),
105 : InputParametersChecksUtils<PhysicsBase>(this),
106 278 : _system_names(getParam<std::vector<SolverSystemName>>("system_names")),
107 278 : _verbose(getParam<bool>("verbose")),
108 278 : _preconditioning(getParam<MooseEnum>("preconditioning")),
109 278 : _blocks(getParam<std::vector<SubdomainName>>("block")),
110 834 : _is_transient(getParam<MooseEnum>("transient"))
111 : {
112 278 : checkSecondParamSetOnlyIfFirstOneTrue("initialize_variables_from_mesh_file",
113 : "initial_from_file_timestep");
114 278 : prepareCopyVariablesFromMesh();
115 278 : addRequiredPhysicsTask("init_physics");
116 278 : addRequiredPhysicsTask("copy_vars_physics");
117 278 : addRequiredPhysicsTask("check_integrity_early_physics");
118 278 : }
119 :
120 : void
121 2098 : PhysicsBase::act()
122 : {
123 2098 : mooseDoOnce(checkRequiredTasks());
124 :
125 : // Lets a derived Physics class implement additional tasks
126 2098 : actOnAdditionalTasks();
127 :
128 : // Initialization and variables
129 2090 : if (_current_task == "init_physics")
130 258 : initializePhysics();
131 1832 : else if (_current_task == "add_variable" && !getParam<bool>("dont_create_solver_variables"))
132 246 : addSolverVariables();
133 1586 : else if (_current_task == "add_ic" && !getParam<bool>("dont_create_ics"))
134 230 : addInitialConditions();
135 :
136 : // Kernels
137 1356 : else if (_current_task == "add_kernel" && !getParam<bool>("dont_create_kernels"))
138 130 : addFEKernels();
139 1226 : else if (_current_task == "add_nodal_kernel" && !getParam<bool>("dont_create_kernels"))
140 0 : addNodalKernels();
141 1318 : else if ((_current_task == "add_fv_kernel" || _current_task == "add_linear_fv_kernel") &&
142 1318 : !getParam<bool>("dont_create_kernels"))
143 92 : addFVKernels();
144 1134 : else if (_current_task == "add_dirac_kernel" && !getParam<bool>("dont_create_kernels"))
145 0 : addDiracKernels();
146 1134 : else if (_current_task == "add_dg_kernel" && !getParam<bool>("dont_create_kernels"))
147 0 : addDGKernels();
148 1134 : else if (_current_task == "add_scalar_kernel" && !getParam<bool>("dont_create_kernels"))
149 0 : addScalarKernels();
150 1134 : else if (_current_task == "add_interface_kernel" && !getParam<bool>("dont_create_kernels"))
151 0 : addInterfaceKernels();
152 1134 : else if (_current_task == "add_fv_ik" && !getParam<bool>("dont_create_kernels"))
153 0 : addFVInterfaceKernels();
154 :
155 : // Boundary conditions
156 1134 : else if (_current_task == "add_bc" && !getParam<bool>("dont_create_bcs"))
157 130 : addFEBCs();
158 1004 : else if (_current_task == "add_nodal_bc" && !getParam<bool>("dont_create_bcs"))
159 0 : addNodalBCs();
160 1096 : else if ((_current_task == "add_fv_bc" || _current_task == "add_linear_fv_bc") &&
161 1096 : !getParam<bool>("dont_create_bcs"))
162 92 : addFVBCs();
163 912 : else if (_current_task == "add_periodic_bc" && !getParam<bool>("dont_create_bcs"))
164 0 : addPeriodicBCs();
165 :
166 : // Auxiliary quantities
167 912 : else if (_current_task == "add_function" && !getParam<bool>("dont_create_functions"))
168 0 : addFunctions();
169 912 : else if (_current_task == "add_aux_variable" && !getParam<bool>("dont_create_aux_variables"))
170 0 : addAuxiliaryVariables();
171 912 : else if (_current_task == "add_aux_kernel" && !getParam<bool>("dont_create_aux_kernels"))
172 0 : addAuxiliaryKernels();
173 912 : else if (_current_task == "add_material" && !getParam<bool>("dont_create_materials"))
174 0 : addMaterials();
175 912 : else if (_current_task == "add_functor_material" && !getParam<bool>("dont_create_materials"))
176 0 : addFunctorMaterials();
177 :
178 : // Multiapp
179 912 : else if (_current_task == "add_multi_app")
180 0 : addMultiApps();
181 912 : else if (_current_task == "add_transfer")
182 0 : addTransfers();
183 :
184 : // User objects and output
185 912 : else if (_current_task == "add_user_object" && !getParam<bool>("dont_create_user_objects"))
186 0 : addUserObjects();
187 912 : else if (_current_task == "add_corrector" && !getParam<bool>("dont_create_correctors"))
188 0 : addCorrectors();
189 912 : else if (_current_task == "add_postprocessor" && !getParam<bool>("dont_create_postprocessors"))
190 222 : addPostprocessors();
191 690 : else if (_current_task == "add_vector_postprocessor" &&
192 690 : !getParam<bool>("dont_create_vectorpostprocessors"))
193 0 : addVectorPostprocessors();
194 690 : else if (_current_task == "add_reporter")
195 0 : addReporters();
196 690 : else if (_current_task == "add_output")
197 0 : addOutputs();
198 :
199 : // Equation solver-related tasks
200 690 : else if (_current_task == "add_preconditioning")
201 222 : addPreconditioning();
202 468 : else if (_current_task == "add_executioner")
203 0 : addExecutioner();
204 468 : else if (_current_task == "add_executor")
205 0 : addExecutors();
206 :
207 : // Checks
208 468 : else if (_current_task == "check_integrity_early_physics")
209 242 : checkIntegrityEarly();
210 226 : else if (_current_task == "check_integrity")
211 0 : checkIntegrity();
212 :
213 : // Exodus restart capabilities
214 2062 : if (_current_task == "copy_vars_physics")
215 : {
216 226 : copyVariablesFromMesh(solverVariableNames(), true);
217 226 : if (_aux_var_names.size() > 0)
218 0 : copyVariablesFromMesh(auxVariableNames(), false);
219 : }
220 2062 : }
221 :
222 : void
223 278 : PhysicsBase::prepareCopyVariablesFromMesh() const
224 : {
225 278 : if (getParam<bool>("initialize_variables_from_mesh_file"))
226 0 : _app.setExodusFileRestart(true);
227 :
228 278 : checkSecondParamSetOnlyIfFirstOneTrue("initialize_variables_from_mesh_file",
229 : "initial_from_file_timestep");
230 278 : }
231 :
232 : bool
233 222 : PhysicsBase::isTransient() const
234 : {
235 : mooseAssert(_problem, "We don't have a problem yet");
236 222 : if (_is_transient == "true")
237 0 : return true;
238 222 : else if (_is_transient == "false")
239 0 : return false;
240 : else
241 222 : 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 20 : PhysicsBase::getSubdomainIDs(const std::set<SubdomainName> & blocks) const
254 : {
255 : const bool not_block_restricted =
256 20 : (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 20 : not_block_restricted ? _mesh->meshSubdomains() : _mesh->getSubdomainIDs(blocks);
264 20 : 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 128 : PhysicsBase::addComponent(const ActionComponent & component)
305 : {
306 256 : for (const auto & block : component.blocks())
307 128 : _blocks.push_back(block);
308 128 : }
309 :
310 : void
311 698 : PhysicsBase::addRelationshipManagers(Moose::RelationshipManagerType input_rm_type)
312 : {
313 698 : InputParameters params = getAdditionalRMParams();
314 698 : Action::addRelationshipManagers(input_rm_type, params);
315 698 : }
316 :
317 : const ActionComponent &
318 184 : PhysicsBase::getActionComponent(const ComponentName & comp_name) const
319 : {
320 184 : return _awh.getAction<ActionComponent>(comp_name);
321 : }
322 :
323 : void
324 258 : 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 258 : 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 258 : if (_blocks.empty())
336 126 : _blocks.push_back("ANY_BLOCK_ID");
337 :
338 : mooseAssert(_mesh, "We should have a mesh to find the dimension");
339 258 : if (_blocks.size())
340 258 : _dim = _mesh->getBlocksMaxDimension(_blocks);
341 : else
342 0 : _dim = _mesh->dimension();
343 :
344 : // Forward physics verbosity to problem to output the setup
345 258 : if (_verbose)
346 0 : getProblem().setVerboseProblem(_verbose);
347 :
348 : // If the derived physics need additional initialization very early on
349 258 : 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 258 : const auto & problem_nl_systems = getProblem().getNonlinearSystemNames();
356 258 : const auto & problem_lin_systems = getProblem().getLinearSystemNames();
357 516 : for (const auto & sys_name : _system_names)
358 262 : if (std::find(problem_nl_systems.begin(), problem_nl_systems.end(), sys_name) ==
359 266 : problem_nl_systems.end() &&
360 4 : std::find(problem_lin_systems.begin(), problem_lin_systems.end(), sys_name) ==
361 266 : 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 512 : for (const auto & sys_name : _system_names)
367 258 : _system_numbers.push_back(getProblem().solverSysNum(sys_name));
368 254 : }
369 :
370 : void
371 242 : PhysicsBase::checkIntegrityEarly() const
372 : {
373 242 : 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 238 : 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 234 : unsigned int var_i = 0;
386 468 : for (const auto & var_name : _solver_var_names)
387 : {
388 234 : const auto & sys_name = _system_names.size() == 1 ? _system_names[0] : _system_names[var_i++];
389 234 : 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 234 : }
397 :
398 : void
399 226 : PhysicsBase::copyVariablesFromMesh(const std::vector<VariableName> & variables_to_copy,
400 : bool are_solver_var)
401 : {
402 226 : 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 226 : }
419 :
420 : bool
421 246 : PhysicsBase::variableExists(const VariableName & var_name, bool error_if_aux) const
422 : {
423 246 : 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 246 : 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 196 : 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 196 : if (_system_names.size() == 1)
457 192 : 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 186 : PhysicsBase::checkRequiredTasks() const
469 : {
470 186 : const auto registered_tasks = _action_factory.getTasksByAction(type());
471 :
472 : // Check for missing tasks
473 1286 : for (const auto & required_task : _required_tasks)
474 1100 : 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 186 : }
482 :
483 : void
484 1020 : 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 1020 : if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") == blocks.end())
490 556 : params.set<std::vector<SubdomainName>>("block") = blocks;
491 1020 : 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 1020 : }
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 42 : 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 42 : if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end())
549 0 : return true;
550 42 : else if (blocks.size() != _mesh->meshSubdomains().size())
551 4 : return false;
552 :
553 114 : for (const auto mesh_block : _mesh->meshSubdomains())
554 : {
555 76 : const auto & subdomain_name = _mesh->getSubdomainName(mesh_block);
556 : // Check subdomain name
557 76 : if (!subdomain_name.empty() &&
558 76 : 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 76 : else if (std::find(blocks.begin(), blocks.end(), std::to_string(mesh_block)) == blocks.end())
562 0 : return false;
563 : }
564 38 : 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 222 : PhysicsBase::addPetscPairsToPetscOptions(
576 : const std::vector<std::pair<MooseEnumItem, std::string>> & petsc_pair_options)
577 : {
578 222 : Moose::PetscSupport::PetscOptions & po = _problem->getPetscOptions();
579 444 : for (const auto solver_sys_num : _system_numbers)
580 222 : Moose::PetscSupport::addPetscPairsToPetscOptions(
581 : petsc_pair_options,
582 222 : _problem->mesh().dimension(),
583 444 : _problem->getSolverSystem(solver_sys_num).prefix(),
584 : *this,
585 : po);
586 222 : }
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 246 : PhysicsBase::shouldCreateVariable(const VariableName & var_name,
603 : const std::vector<SubdomainName> & blocks,
604 : const bool error_if_aux)
605 : {
606 246 : if (!variableExists(var_name, error_if_aux))
607 196 : return true;
608 : // check block restriction
609 50 : auto & var = _problem->getVariable(0, var_name);
610 : const bool not_block_restricted =
611 62 : (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end()) ||
612 12 : allMeshBlocks(blocks);
613 50 : if (!var.blockRestricted() || (!not_block_restricted && var.hasBlocks(blocks)))
614 20 : return false;
615 :
616 : // This is an edge case, which might warrant a warning
617 30 : if (allMeshBlocks(var.blocks()) && not_block_restricted)
618 26 : 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 222 : 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 222 : if (!isTransient())
684 112 : 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 110 : const auto var = &_problem->getVariable(0, var_name);
696 110 : const auto var_id = var->number();
697 110 : const auto sys_num = var->sys().number();
698 : const auto time_vector_tag =
699 110 : (sys_num < _problem->numNonlinearSystems())
700 110 : ? 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 110 : bool all_blocks_covered = true;
706 110 : 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 232 : for (const auto & block : blocks)
710 : {
711 122 : std::vector<MooseObject *> time_kernels;
712 122 : if (block != "ANY_BLOCK_ID")
713 : {
714 24 : const auto bid = _mesh->getSubdomainID(block);
715 24 : _problem->theWarehouse()
716 48 : .query()
717 24 : .template condition<AttribSysNum>(sys_num)
718 24 : .template condition<AttribVar>(var_id)
719 24 : .template condition<AttribSubdomains>(bid)
720 : // we use the time tag as a proxy for time derivatives
721 24 : .template condition<AttribVectorTags>(time_vector_tag)
722 24 : .queryInto(time_kernels);
723 : }
724 : else
725 98 : _problem->theWarehouse()
726 196 : .query()
727 98 : .template condition<AttribSysNum>(sys_num)
728 98 : .template condition<AttribVar>(var_id)
729 : // we use the time tag as a proxy for time derivatives
730 98 : .template condition<AttribVectorTags>(time_vector_tag)
731 98 : .queryInto(time_kernels);
732 :
733 122 : if (time_kernels.size())
734 : {
735 16 : if (block == "ANY_BLOCK_ID")
736 : {
737 34 : for (const auto & time_kernel : time_kernels)
738 18 : if (const auto blk = dynamic_cast<BlockRestrictable *>(time_kernel))
739 18 : 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 106 : all_blocks_covered = false;
746 122 : }
747 :
748 : // From the set of covered blocks, see if the blocks we needed are found
749 110 : if (all_blocks_covered)
750 : {
751 16 : std::set<SubdomainName> blocks_set(blocks.begin(), blocks.end());
752 16 : const auto blocks_ids = getSubdomainIDs(blocks_set);
753 16 : if (blocks_ids != blocks_ids_covered)
754 4 : all_blocks_covered = false;
755 16 : }
756 110 : const bool has_some_blocks = !blocks_ids_covered.empty();
757 110 : if (!has_some_blocks)
758 94 : return true;
759 16 : if (all_blocks_covered)
760 : {
761 12 : 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 12 : 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 106 : }
777 :
778 : void
779 46 : PhysicsBase::reportPotentiallyMissedParameters(const std::vector<std::string> & param_names,
780 : const std::string & object_type) const
781 : {
782 46 : std::vector<std::string> defaults_unused;
783 46 : std::vector<std::string> user_values_unused;
784 138 : for (const auto & param : param_names)
785 : {
786 92 : if (isParamSetByUser(param))
787 14 : user_values_unused.push_back(param);
788 78 : else if (isParamValid(param))
789 78 : defaults_unused.push_back(param);
790 : }
791 46 : 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 46 : 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 42 : }
|