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

Generated by: LCOV version 1.14