LCOV - code coverage report
Current view: top level - src/physics - PhysicsBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #31706 (f8ed4a) with base bb0a08 Lines: 310 442 70.1 %
Date: 2025-11-03 17:23:24 Functions: 24 32 75.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14