LCOV - code coverage report
Current view: top level - src/physics - PhysicsBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 309 436 70.9 %
Date: 2025-07-17 01:28:37 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        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 : }

Generated by: LCOV version 1.14