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

Generated by: LCOV version 1.14