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

Generated by: LCOV version 1.14