LCOV - code coverage report
Current view: top level - src/base - Simulation.C (source / functions) Hit Total Coverage
Test: idaholab/moose thermal_hydraulics: #30301 (3b550b) with base 2ad78d Lines: 411 500 82.2 %
Date: 2025-07-30 13:02:48 Functions: 36 43 83.7 %
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 "Simulation.h"
      11             : #include "FEProblemBase.h"
      12             : #include "AddVariableAction.h"
      13             : #include "MooseObjectAction.h"
      14             : #include "Transient.h"
      15             : #include "HeatConductionModel.h"
      16             : #include "HeatStructureInterface.h"
      17             : #include "FlowChannelBase.h"
      18             : #include "FlowJunction.h"
      19             : 
      20             : #include "ClosuresBase.h"
      21             : #include "FluidProperties.h"
      22             : #include "THMControl.h"
      23             : #include "TerminateControl.h"
      24             : #include "THMMesh.h"
      25             : #include "RelationshipManager.h"
      26             : #include "NonlinearSystemBase.h"
      27             : #include "TimeIntegrator.h"
      28             : #include "ExplicitTimeIntegrator.h"
      29             : #include "ExplicitEuler.h"
      30             : #include "ExplicitRK2.h"
      31             : #include "ExplicitTVDRK2.h"
      32             : 
      33             : #include "libmesh/string_to_enum.h"
      34             : 
      35             : using namespace libMesh;
      36             : 
      37             : std::map<VariableName, int> Simulation::_component_variable_order_map;
      38             : 
      39             : void
      40       85528 : Simulation::setComponentVariableOrder(const VariableName & var, int index)
      41             : {
      42       85528 :   _component_variable_order_map[var] = index;
      43       85528 : }
      44             : 
      45        3904 : Simulation::Simulation(FEProblemBase & fe_problem, const InputParameters & pars)
      46             :   : ParallelObject(fe_problem.comm()),
      47        3904 :     LoggingInterface(_log),
      48        3904 :     _thm_mesh(*static_cast<THMMesh *>(pars.get<MooseMesh *>("mesh"))),
      49        3904 :     _fe_problem(fe_problem),
      50        3904 :     _thm_app(static_cast<ThermalHydraulicsApp &>(*pars.get<MooseApp *>("_moose_app"))),
      51        3904 :     _thm_factory(_thm_app.getFactory()),
      52        3904 :     _thm_pars(pars),
      53             :     _flow_fe_type(FEType(CONSTANT, MONOMIAL)),
      54        3904 :     _implicit_time_integration(true),
      55        3904 :     _check_jacobian(false),
      56        3904 :     _output_vector_velocity(true),
      57        7808 :     _zero(0)
      58             : {
      59        3904 :   bool second_order_mesh = pars.get<bool>("2nd_order_mesh");
      60        3904 :   HeatConductionModel::_fe_type =
      61        3904 :       second_order_mesh ? FEType(SECOND, LAGRANGE) : FEType(FIRST, LAGRANGE);
      62        3904 : }
      63             : 
      64        3723 : Simulation::~Simulation()
      65             : {
      66       14167 :   for (auto && k : _control_data)
      67       10444 :     delete k.second;
      68       11169 : }
      69             : 
      70             : void
      71       18241 : Simulation::augmentSparsity(const dof_id_type & elem_id1, const dof_id_type & elem_id2)
      72             : {
      73             :   auto it = _sparsity_elem_augmentation.find(elem_id1);
      74       18241 :   if (it == _sparsity_elem_augmentation.end())
      75       15501 :     it = _sparsity_elem_augmentation.insert(_sparsity_elem_augmentation.begin(),
      76       15501 :                                             {elem_id1, std::vector<dof_id_type>()});
      77       18241 :   it->second.push_back(elem_id2);
      78             : 
      79             :   it = _sparsity_elem_augmentation.find(elem_id2);
      80       18241 :   if (it == _sparsity_elem_augmentation.end())
      81       11016 :     it = _sparsity_elem_augmentation.insert(_sparsity_elem_augmentation.begin(),
      82       11016 :                                             {elem_id2, std::vector<dof_id_type>()});
      83       18241 :   it->second.push_back(elem_id1);
      84       18241 : }
      85             : 
      86             : void
      87        3900 : Simulation::buildMesh()
      88             : {
      89        3900 :   if (_components.size() == 0)
      90             :     return;
      91             : 
      92             :   // build mesh
      93       19395 :   for (auto && comp : _components)
      94       15638 :     comp->executeSetupMesh();
      95             : }
      96             : 
      97             : void
      98        3270 : Simulation::setupQuadrature()
      99             : {
     100        3270 :   if (_components.size() == 0)
     101             :     return;
     102             : 
     103             :   Order order = CONSTANT;
     104             :   unsigned int n_flow_channels = 0;
     105             :   unsigned int n_heat_structures = 0;
     106             : 
     107       16110 :   for (auto && comp : _components)
     108             :   {
     109       12983 :     auto flow_channel = dynamic_cast<FlowChannelBase *>(comp.get());
     110       12983 :     if (flow_channel != nullptr)
     111        3323 :       n_flow_channels++;
     112             : 
     113       12983 :     auto hs_interface = dynamic_cast<HeatStructureInterface *>(comp.get());
     114       12983 :     if (hs_interface)
     115        1762 :       n_heat_structures++;
     116             :   }
     117             : 
     118        3127 :   if (n_flow_channels > 0)
     119             :   {
     120             :     const FEType & fe_type = getFlowFEType();
     121        2045 :     if (fe_type.default_quadrature_order() > order)
     122             :       order = fe_type.default_quadrature_order();
     123             :   }
     124        3127 :   if (n_heat_structures > 0)
     125             :   {
     126             :     const FEType & fe_type = HeatConductionModel::feType();
     127             :     if (fe_type.default_quadrature_order() > order)
     128             :       order = fe_type.default_quadrature_order();
     129             :   }
     130             : 
     131        3127 :   _fe_problem.createQRules(QGAUSS, order, order, order);
     132             : }
     133             : 
     134             : void
     135        3900 : Simulation::initSimulation()
     136             : {
     137             :   // sort the components using dependency resolver
     138        3900 :   DependencyResolver<std::shared_ptr<Component>> dependency_resolver;
     139       19538 :   for (const auto & comp : _components)
     140             :   {
     141       15638 :     dependency_resolver.addNode(comp);
     142       26424 :     for (const auto & dep : comp->getDependencies())
     143       10786 :       if (hasComponent(dep))
     144       10727 :         dependency_resolver.addEdge(_comp_by_name[dep], comp);
     145             :   }
     146             : 
     147        3900 :   _components = dependency_resolver.dfs();
     148        3900 : }
     149             : 
     150             : void
     151        3891 : Simulation::initComponents()
     152             : {
     153             :   // initialize components
     154       19431 :   for (auto && comp : _components)
     155       15542 :     comp->executeInit();
     156             : 
     157             :   // perform secondary initialization, which relies on init() being called
     158             :   // already for all components
     159       19429 :   for (auto && comp : _components)
     160       15540 :     comp->executeInitSecondary();
     161        3889 : }
     162             : 
     163             : void
     164        3889 : Simulation::identifyLoops()
     165             : {
     166             :   // loop over junctions and boundaries (non-geometrical components)
     167       19429 :   for (const auto & component : _components)
     168             :   {
     169             :     const auto flow_connection =
     170       15540 :         MooseSharedNamespace::dynamic_pointer_cast<Component1DConnection>(component);
     171       15540 :     if (flow_connection)
     172             :     {
     173             :       // create vector of names of this component and its connected flow channels, and then sort
     174             :       // them
     175        6451 :       std::vector<std::string> names = flow_connection->getConnectedComponentNames();
     176        6451 :       names.push_back(component->name());
     177        6451 :       std::sort(names.begin(), names.end());
     178             : 
     179             :       // pick first name alphabetically to be the proposed loop name
     180        6451 :       std::string proposed_loop_name = names[0];
     181             : 
     182       21125 :       for (const std::string & name : names)
     183             :       {
     184             :         // if the name is not yet in the map
     185       14674 :         if (_component_name_to_loop_name.find(name) == _component_name_to_loop_name.end())
     186             :           // just add the new map key; nothing else needs updating
     187       10588 :           _component_name_to_loop_name[name] = proposed_loop_name;
     188             :         else
     189             :         {
     190             :           // compare to the existing loop name for this component to make sure the
     191             :           // proposed loop name is first alphabetically
     192        4086 :           const std::string current_loop_name = _component_name_to_loop_name[name];
     193             :           // if proposed loop name comes later, change map values of the current
     194             :           // loop name to be the proposed name, and then update the proposed name
     195             :           // to be the current name
     196        4086 :           if (proposed_loop_name > current_loop_name)
     197             :           {
     198       15921 :             for (auto && entry : _component_name_to_loop_name)
     199       13011 :               if (entry.second == proposed_loop_name)
     200             :                 entry.second = current_loop_name;
     201             :             proposed_loop_name = current_loop_name;
     202             :           }
     203             :           // if proposed loop name comes earlier, change map values of the current
     204             :           // loop name to be the proposed name
     205        1176 :           else if (proposed_loop_name < current_loop_name)
     206             :           {
     207        2723 :             for (auto && entry : _component_name_to_loop_name)
     208        2358 :               if (entry.second == current_loop_name)
     209             :                 entry.second = proposed_loop_name;
     210             :           }
     211             :         }
     212             :       }
     213        6451 :     }
     214             :   }
     215             : 
     216             :   // get the list of loops
     217             :   std::vector<std::string> loops;
     218       14477 :   for (const auto & entry : _component_name_to_loop_name)
     219       10588 :     if (std::find(loops.begin(), loops.end(), entry.second) == loops.end())
     220        2682 :       loops.push_back(entry.second);
     221             : 
     222             :   // fill the map of loop name to model ID
     223        6571 :   for (const auto & loop : loops)
     224             :   {
     225             :     // find a flow channel in this loop and get its model ID
     226             :     THM::FlowModelID model_id;
     227             :     bool found_model_id = false;
     228        3479 :     for (const auto & component : _components)
     229             :     {
     230             :       const auto flow_chan_base_component =
     231        3473 :           MooseSharedNamespace::dynamic_pointer_cast<FlowChannelBase>(component);
     232        3473 :       if (flow_chan_base_component && (_component_name_to_loop_name[component->name()] == loop))
     233             :       {
     234        2676 :         model_id = flow_chan_base_component->getFlowModelID();
     235             :         found_model_id = true;
     236             :         break;
     237             :       }
     238             :     }
     239             :     // set the value in the map or throw an error
     240             :     if (found_model_id)
     241        2676 :       _loop_name_to_model_id[loop] = model_id;
     242             :     else
     243             :       logError("No FlowChannelBase-derived components were found in loop '", loop, "'");
     244             :   }
     245        3889 : }
     246             : 
     247             : void
     248          16 : Simulation::printComponentLoops() const
     249             : {
     250             :   // get the list of loops
     251             :   std::vector<std::string> loops;
     252         152 :   for (auto && entry : _component_name_to_loop_name)
     253         136 :     if (std::find(loops.begin(), loops.end(), entry.second) == loops.end())
     254          32 :       loops.push_back(entry.second);
     255             : 
     256             :   // for each loop
     257             :   Moose::out << "\nListing of component loops:" << std::endl;
     258          48 :   for (unsigned int i = 0; i < loops.size(); i++)
     259             :   {
     260          32 :     Moose::out << "\n  Loop " << i + 1 << ":" << std::endl;
     261             : 
     262             :     // print out each component in the loop
     263         304 :     for (auto && entry : _component_name_to_loop_name)
     264         272 :       if (entry.second == loops[i])
     265             :         Moose::out << "    " << entry.first << std::endl;
     266             :   }
     267             :   Moose::out << std::endl;
     268          16 : }
     269             : 
     270             : void
     271         505 : Simulation::addSimVariable(bool nl, const VariableName & name, FEType fe_type, Real scaling_factor)
     272             : {
     273         505 :   checkVariableNameLength(name);
     274             : 
     275         505 :   if (fe_type.family != SCALAR)
     276           0 :     mooseError("This method should only be used for scalar variables.");
     277             : 
     278         505 :   if (_vars.find(name) == _vars.end()) // variable is new
     279             :   {
     280         505 :     VariableInfo vi;
     281             :     InputParameters & params = vi._params;
     282             : 
     283         505 :     vi._nl = nl;
     284             :     vi._var_type = "MooseVariableScalar";
     285         505 :     params = _thm_factory.getValidParams(vi._var_type);
     286             : 
     287         505 :     auto family = AddVariableAction::getNonlinearVariableFamilies();
     288         505 :     family = Utility::enum_to_string(fe_type.family);
     289         505 :     params.set<MooseEnum>("family") = family;
     290             : 
     291         505 :     auto order = AddVariableAction::getNonlinearVariableOrders();
     292         505 :     order = Utility::enum_to_string<Order>(fe_type.order);
     293         505 :     params.set<MooseEnum>("order") = order;
     294             : 
     295         505 :     if (nl)
     296         486 :       params.set<std::vector<Real>>("scaling") = {scaling_factor};
     297         262 :     else if (!MooseUtils::absoluteFuzzyEqual(scaling_factor, 1.0))
     298           0 :       mooseError("Aux variables cannot be provided a residual scaling factor.");
     299             : 
     300         505 :     _vars[name] = vi;
     301         505 :   }
     302             :   else
     303             :     // One of the two cases is true:
     304             :     // - This variable was previously added as a scalar variable, and scalar
     305             :     //   variables should not be added more than once, since there is no block
     306             :     //   restriction to extend, as there is in the field variable version of this
     307             :     //   method.
     308             :     // - This variable was previously added as a field variable, and a variable
     309             :     //   may have only one type (this method is used for scalar variables only).
     310           0 :     mooseError("The variable '", name, "' was already added.");
     311         505 : }
     312             : 
     313             : void
     314       72044 : Simulation::addSimVariable(bool nl,
     315             :                            const VariableName & name,
     316             :                            FEType fe_type,
     317             :                            const std::vector<SubdomainName> & subdomain_names,
     318             :                            Real scaling_factor)
     319             : {
     320       72044 :   checkVariableNameLength(name);
     321             : 
     322       72044 :   if (fe_type.family == SCALAR)
     323             :     mooseDeprecated(
     324             :         "The version of Simulation::addSimVariable() with subdomain names can no longer be used "
     325             :         "with scalar variables since scalar variables cannot be block-restricted. Use the version "
     326             :         "of Simulation::addSimVariable() without subdomain names instead.");
     327             : 
     328             : #ifdef DEBUG
     329             :   for (const auto & subdomain_name : subdomain_names)
     330             :     mooseAssert(subdomain_name != "ANY_BLOCK_ID",
     331             :                 "'ANY_BLOCK_ID' cannot be used for adding field variables in components.");
     332             : #endif
     333             : 
     334       72044 :   if (_vars.find(name) == _vars.end()) // variable is new
     335             :   {
     336       44140 :     VariableInfo vi;
     337             :     InputParameters & params = vi._params;
     338             : 
     339       44140 :     vi._nl = nl;
     340             :     vi._var_type = "MooseVariable";
     341       44140 :     params = _thm_factory.getValidParams(vi._var_type);
     342       44140 :     params.set<std::vector<SubdomainName>>("block") = subdomain_names;
     343             : 
     344       44140 :     auto family = AddVariableAction::getNonlinearVariableFamilies();
     345       44140 :     family = Utility::enum_to_string(fe_type.family);
     346       44140 :     params.set<MooseEnum>("family") = family;
     347             : 
     348       44140 :     auto order = AddVariableAction::getNonlinearVariableOrders();
     349       44140 :     order = Utility::enum_to_string<Order>(fe_type.order);
     350       44140 :     params.set<MooseEnum>("order") = order;
     351             : 
     352       44140 :     if (nl)
     353       25500 :       params.set<std::vector<Real>>("scaling") = {scaling_factor};
     354       31390 :     else if (!MooseUtils::absoluteFuzzyEqual(scaling_factor, 1.0))
     355           0 :       mooseError("Aux variables cannot be provided a residual scaling factor.");
     356             : 
     357       44140 :     _vars[name] = vi;
     358       44140 :   }
     359             :   else // variable was previously added
     360             :   {
     361       27904 :     VariableInfo & vi = _vars[name];
     362       27904 :     InputParameters & params = vi._params;
     363             : 
     364       27904 :     if (vi._nl != nl)
     365           0 :       mooseError("The variable '",
     366             :                  name,
     367             :                  "' has already been added in a different system (nonlinear or aux).");
     368             : 
     369       27904 :     if (vi._var_type != "MooseVariable")
     370           0 :       mooseError("The variable '",
     371             :                  name,
     372             :                  "' has already been added with a different type than 'MooseVariable'.");
     373             : 
     374       27904 :     auto family = AddVariableAction::getNonlinearVariableFamilies();
     375       55808 :     family = Utility::enum_to_string(fe_type.family);
     376       27904 :     if (!params.get<MooseEnum>("family").compareCurrent(family))
     377           0 :       mooseError("The variable '", name, "' has already been added with a different FE family.");
     378             : 
     379       27904 :     auto order = AddVariableAction::getNonlinearVariableOrders();
     380       55808 :     order = Utility::enum_to_string<Order>(fe_type.order);
     381       27904 :     if (!params.get<MooseEnum>("order").compareCurrent(order))
     382           0 :       mooseError("The variable '", name, "' has already been added with a different FE order.");
     383             : 
     384             :     // If already block-restricted, extend the block restriction
     385       55808 :     if (params.isParamValid("block"))
     386             :     {
     387       27904 :       auto blocks = params.get<std::vector<SubdomainName>>("block");
     388       56160 :       for (const auto & subdomain_name : subdomain_names)
     389       28256 :         if (std::find(blocks.begin(), blocks.end(), subdomain_name) == blocks.end())
     390       27346 :           blocks.push_back(subdomain_name);
     391       27904 :       params.set<std::vector<SubdomainName>>("block") = blocks;
     392       27904 :     }
     393             :     else
     394           0 :       params.set<std::vector<SubdomainName>>("block") = subdomain_names;
     395             : 
     396       55808 :     if (params.isParamValid("scaling"))
     397        6492 :       if (!MooseUtils::absoluteFuzzyEqual(params.get<std::vector<Real>>("scaling")[0],
     398             :                                           scaling_factor))
     399           0 :         mooseError(
     400             :             "The variable '", name, "' has already been added with a different scaling factor.");
     401       27904 :   }
     402       72044 : }
     403             : 
     404             : void
     405           0 : Simulation::addSimVariable(bool nl,
     406             :                            const std::string & var_type,
     407             :                            const VariableName & name,
     408             :                            const InputParameters & params)
     409             : {
     410           0 :   checkVariableNameLength(name);
     411             : 
     412           0 :   if (_vars.find(name) == _vars.end()) // variable is new
     413             :   {
     414           0 :     VariableInfo vi;
     415           0 :     vi._nl = nl;
     416             :     vi._var_type = var_type;
     417           0 :     vi._params = params;
     418             : 
     419           0 :     _vars[name] = vi;
     420             :   }
     421             :   else // variable was previously added
     422             :   {
     423           0 :     VariableInfo & vi = _vars[name];
     424           0 :     InputParameters & vi_params = vi._params;
     425             : 
     426           0 :     if (vi._nl != nl)
     427           0 :       mooseError("The variable '",
     428             :                  name,
     429             :                  "' has already been added in a different system (nonlinear or aux).");
     430             : 
     431           0 :     if (vi._var_type != var_type)
     432           0 :       mooseError("The variable '",
     433             :                  name,
     434             :                  "' has already been added with a different type than '",
     435             :                  var_type,
     436             :                  "'.");
     437             : 
     438             :     // Check that all valid parameters (other than 'block') are consistent
     439           0 :     for (auto it = params.begin(); it != params.end(); it++)
     440             :     {
     441           0 :       const std::string param_name = it->first;
     442           0 :       if (param_name == "block")
     443             :       {
     444           0 :         if (vi_params.isParamValid("block"))
     445             :         {
     446           0 :           auto blocks = vi_params.get<std::vector<SubdomainName>>("block");
     447           0 :           const auto new_blocks = params.get<std::vector<SubdomainName>>("block");
     448           0 :           for (const auto & subdomain_name : new_blocks)
     449           0 :             if (std::find(blocks.begin(), blocks.end(), subdomain_name) == blocks.end())
     450           0 :               blocks.push_back(subdomain_name);
     451           0 :           vi_params.set<std::vector<SubdomainName>>("block") = blocks;
     452           0 :         }
     453             :         else
     454           0 :           mooseError("The variable '", name, "' was added previously without block restriction.");
     455             :       }
     456           0 :       else if (params.isParamValid(param_name))
     457             :       {
     458           0 :         if (vi_params.isParamValid(param_name))
     459             :         {
     460           0 :           if (params.rawParamVal(param_name) != vi_params.rawParamVal(param_name))
     461           0 :             mooseError("The variable '",
     462             :                        name,
     463             :                        "' was added previously with a different value for the parameter '",
     464             :                        param_name,
     465             :                        "'.");
     466             :         }
     467             :         else
     468           0 :           mooseError("The variable '",
     469             :                      name,
     470             :                      "' was added previously without the parameter '",
     471             :                      param_name,
     472             :                      "'.");
     473             :       }
     474             :     }
     475             :   }
     476           0 : }
     477             : 
     478             : void
     479       72549 : Simulation::checkVariableNameLength(const std::string & name) const
     480             : {
     481       72549 :   if (name.size() > THM::MAX_VARIABLE_LENGTH)
     482           0 :     mooseError(
     483             :         "Variable name '", name, "' is too long. The limit is ", THM::MAX_VARIABLE_LENGTH, ".");
     484       72549 : }
     485             : 
     486             : void
     487           0 : Simulation::addControl(const std::string & type, const std::string & name, InputParameters params)
     488             : {
     489           0 :   params.addPrivateParam<FEProblemBase *>("_fe_problem_base", &_fe_problem);
     490           0 :   std::shared_ptr<Control> control = _thm_factory.create<Control>(type, name, params);
     491           0 :   _fe_problem.getControlWarehouse().addObject(control);
     492           0 : }
     493             : 
     494             : void
     495       67289 : Simulation::addSimInitialCondition(const std::string & type,
     496             :                                    const std::string & name,
     497             :                                    InputParameters params)
     498             : {
     499       67289 :   if (hasInitialConditionsFromFile())
     500             :     return;
     501             : 
     502       67281 :   if (_ics.find(name) == _ics.end())
     503             :   {
     504       67281 :     ICInfo ic(type, params);
     505       67281 :     _ics[name] = ic;
     506             :   }
     507             :   else
     508           0 :     mooseError("Initial condition with name '", name, "' already exists.");
     509             : }
     510             : 
     511             : void
     512        6458 : Simulation::addConstantIC(const VariableName & var_name,
     513             :                           Real value,
     514             :                           const std::vector<SubdomainName> & block_names)
     515             : {
     516        6458 :   if (hasInitialConditionsFromFile())
     517          60 :     return;
     518             : 
     519        6398 :   std::string blk_str = block_names[0];
     520        6416 :   for (unsigned int i = 1; i < block_names.size(); i++)
     521          36 :     blk_str += ":" + block_names[i];
     522             : 
     523        6398 :   std::string class_name = "ConstantIC";
     524        6398 :   InputParameters params = _thm_factory.getValidParams(class_name);
     525        6398 :   params.set<VariableName>("variable") = var_name;
     526        6398 :   params.set<Real>("value") = value;
     527        6398 :   params.set<std::vector<SubdomainName>>("block") = block_names;
     528       12796 :   addSimInitialCondition(class_name, genName(var_name, blk_str, "ic"), params);
     529        6398 : }
     530             : 
     531             : void
     532        4734 : Simulation::addFunctionIC(const VariableName & var_name,
     533             :                           const std::string & func_name,
     534             :                           const std::vector<SubdomainName> & block_names)
     535             : {
     536        4734 :   if (hasInitialConditionsFromFile())
     537          20 :     return;
     538             : 
     539        4714 :   std::string blk_str = block_names[0];
     540        5453 :   for (unsigned int i = 1; i < block_names.size(); i++)
     541        1478 :     blk_str += ":" + block_names[i];
     542             : 
     543        4714 :   std::string class_name = "FunctionIC";
     544        4714 :   InputParameters params = _thm_factory.getValidParams(class_name);
     545        4714 :   params.set<VariableName>("variable") = var_name;
     546        9428 :   params.set<std::vector<SubdomainName>>("block") = block_names;
     547        9428 :   params.set<FunctionName>("function") = func_name;
     548        9428 :   addSimInitialCondition(class_name, genName(var_name, blk_str, "ic"), params);
     549        4714 : }
     550             : 
     551             : void
     552         489 : Simulation::addConstantScalarIC(const VariableName & var_name, Real value)
     553             : {
     554         489 :   if (hasInitialConditionsFromFile())
     555           0 :     return;
     556             : 
     557         489 :   std::string class_name = "ScalarConstantIC";
     558         489 :   InputParameters params = _thm_factory.getValidParams(class_name);
     559         489 :   params.set<VariableName>("variable") = var_name;
     560         489 :   params.set<Real>("value") = value;
     561         978 :   addSimInitialCondition(class_name, genName(var_name, "ic"), params);
     562         489 : }
     563             : 
     564             : void
     565           0 : Simulation::addComponentScalarIC(const VariableName & var_name, const std::vector<Real> & value)
     566             : {
     567           0 :   if (hasInitialConditionsFromFile())
     568           0 :     return;
     569             : 
     570           0 :   std::string class_name = "ScalarComponentIC";
     571           0 :   InputParameters params = _thm_factory.getValidParams(class_name);
     572           0 :   params.set<VariableName>("variable") = var_name;
     573           0 :   params.set<std::vector<Real>>("values") = value;
     574           0 :   addSimInitialCondition(class_name, genName(var_name, "ic"), params);
     575           0 : }
     576             : 
     577             : std::vector<VariableName>
     578        3592 : Simulation::sortAddedComponentVariables() const
     579             : {
     580             :   // Check that no index in order map is used more than once.
     581             :   // Also, convert the map to a vector of pairs to be sorted.
     582             :   std::set<int> indices;
     583             :   std::vector<std::pair<VariableName, int>> registered_var_index_pairs;
     584       32328 :   for (const auto & var_and_index : _component_variable_order_map)
     585             :   {
     586       28736 :     registered_var_index_pairs.push_back(var_and_index);
     587             : 
     588       28736 :     const auto ind = var_and_index.second;
     589       28736 :     auto insert_return = indices.insert(ind);
     590       28736 :     if (!insert_return.second)
     591           0 :       mooseError("The index ", ind, " was used for multiple component variables.");
     592             :   }
     593             : 
     594             :   // Collect all of the added variable names into an unsorted vector.
     595             :   std::vector<VariableName> vars_unsorted;
     596       48237 :   for (const auto & var_and_data : _vars)
     597       44645 :     vars_unsorted.push_back(var_and_data.first);
     598             : 
     599             :   // The sorting works as follows. For those variables that are listed in
     600             :   // _component_variable_order_map, these are ordered before those that are not,
     601             :   // in the order of their indices in the map. Those not in the map are sorted
     602             :   // alphabetically.
     603             : 
     604             :   // Sort registered_var_index_pairs by value (index)
     605        3592 :   std::sort(registered_var_index_pairs.begin(),
     606             :             registered_var_index_pairs.end(),
     607             :             [](const std::pair<VariableName, int> & a, const std::pair<VariableName, int> & b)
     608       71840 :             { return a.second < b.second; });
     609             : 
     610             :   // Loop over the ordered, registered variable names and add a variable to the
     611             :   // sorted list if in vars_unsorted. When this happens, delete the element from
     612             :   // vars_unsorted, leaving only unregistered variable names after the loop.
     613             :   std::vector<VariableName> vars_sorted;
     614       32328 :   for (const auto & var_index_pair : registered_var_index_pairs)
     615             :   {
     616       28736 :     const auto & var = var_index_pair.first;
     617       28736 :     if (std::find(vars_unsorted.begin(), vars_unsorted.end(), var) != vars_unsorted.end())
     618             :     {
     619       11388 :       vars_sorted.push_back(var);
     620       11388 :       vars_unsorted.erase(std::remove(vars_unsorted.begin(), vars_unsorted.end(), var),
     621             :                           vars_unsorted.end());
     622             :     }
     623             :   }
     624             : 
     625             :   // Sort the remaining (unregistered) variables alphabetically and then add
     626             :   // them to the end of the full list.
     627        3592 :   std::sort(vars_unsorted.begin(), vars_unsorted.end());
     628        3592 :   vars_sorted.insert(vars_sorted.end(), vars_unsorted.begin(), vars_unsorted.end());
     629             : 
     630        3592 :   return vars_sorted;
     631        3592 : }
     632             : 
     633             : void
     634        3735 : Simulation::addVariables()
     635             : {
     636        3735 :   TransientBase * trex = dynamic_cast<TransientBase *>(getApp().getExecutioner());
     637        3735 :   if (trex)
     638             :   {
     639             :     Moose::TimeIntegratorType ti_type = trex->getTimeScheme();
     640             :     // This is only needed for the listed time integrators that are using the original approach to
     641             :     // explicit integration in MOOSE.  Currently, the new time integrators like
     642             :     // ActuallyExplicitEuler do not need the implicit flag to be set.
     643        3663 :     if (ti_type == Moose::TI_EXPLICIT_TVD_RK_2 || ti_type == Moose::TI_EXPLICIT_MIDPOINT ||
     644             :         ti_type == Moose::TI_EXPLICIT_EULER)
     645           0 :       _implicit_time_integration = false;
     646             :   }
     647             : 
     648        3735 :   if (_components.size() == 0)
     649         143 :     return;
     650             : 
     651             :   // Cache the variables that components request to add
     652       18504 :   for (auto && comp : _components)
     653       14912 :     comp->addVariables();
     654             : 
     655             :   // Sort the variables for a consistent ordering
     656        3592 :   const auto var_names = sortAddedComponentVariables();
     657             : 
     658             :   // Report the ordering if the executioner is verbose
     659       10776 :   if (_fe_problem.getParam<MooseEnum>("verbose_setup") != "false")
     660             :   {
     661           0 :     std::stringstream ss;
     662           0 :     ss << "The system ordering of variables added by Components is as follows:\n";
     663           0 :     for (const auto & var : var_names)
     664           0 :       ss << "  " << var << "\n";
     665           0 :     mooseInfo(ss.str());
     666           0 :   }
     667             : 
     668             :   // Add the variables to the problem
     669       48237 :   for (const auto & name : var_names)
     670             :   {
     671       44645 :     VariableInfo & vi = _vars[name];
     672             : 
     673       44645 :     if (vi._nl)
     674       12993 :       _fe_problem.addVariable(vi._var_type, name, vi._params);
     675             :     else
     676       31652 :       _fe_problem.addAuxVariable(vi._var_type, name, vi._params);
     677             :   }
     678             : 
     679        3592 :   if (hasInitialConditionsFromFile())
     680          54 :     setupInitialConditionsFromFile();
     681             :   else
     682        3538 :     setupInitialConditionObjects();
     683        3592 : }
     684             : 
     685             : void
     686          54 : Simulation::setupInitialConditionsFromFile()
     687             : {
     688         108 :   const UserObjectName suo_name = genName("thm", "suo");
     689             :   {
     690          54 :     const std::string class_name = "SolutionUserObject";
     691          54 :     InputParameters params = _thm_factory.getValidParams(class_name);
     692         108 :     params.set<MeshFileName>("mesh") = _thm_pars.get<FileName>("initial_from_file");
     693          54 :     params.set<std::string>("timestep") = _thm_pars.get<std::string>("initial_from_file_timestep");
     694          54 :     _fe_problem.addUserObject(class_name, suo_name, params);
     695          54 :   }
     696             : 
     697         552 :   for (auto && v : _vars)
     698             :   {
     699             :     const VariableName & var_name = v.first;
     700             :     const VariableInfo & vi = v.second;
     701             : 
     702         498 :     if (vi._var_type == "MooseVariableScalar")
     703             :     {
     704           8 :       std::string class_name = "ScalarSolutionIC";
     705           8 :       InputParameters params = _thm_factory.getValidParams(class_name);
     706           8 :       params.set<VariableName>("variable") = var_name;
     707           8 :       params.set<VariableName>("from_variable") = var_name;
     708           8 :       params.set<UserObjectName>("solution_uo") = suo_name;
     709          16 :       _fe_problem.addInitialCondition(class_name, genName(var_name, "ic"), params);
     710           8 :     }
     711             :     else
     712             :     {
     713         490 :       std::string class_name = "SolutionIC";
     714         490 :       InputParameters params = _thm_factory.getValidParams(class_name);
     715         490 :       params.set<VariableName>("variable") = var_name;
     716         490 :       params.set<VariableName>("from_variable") = var_name;
     717         490 :       params.set<UserObjectName>("solution_uo") = suo_name;
     718         980 :       if (vi._params.isParamValid("block"))
     719         490 :         params.set<std::vector<SubdomainName>>("block") =
     720        1470 :             vi._params.get<std::vector<SubdomainName>>("block");
     721         980 :       _fe_problem.addInitialCondition(class_name, genName(var_name, "ic"), params);
     722         490 :     }
     723             :   }
     724          54 : }
     725             : 
     726             : void
     727        3538 : Simulation::setupInitialConditionObjects()
     728             : {
     729       70819 :   for (auto && i : _ics)
     730             :   {
     731       67281 :     const std::string & name = i.first;
     732             :     ICInfo & ic = i.second;
     733       67281 :     _fe_problem.addInitialCondition(ic._type, name, ic._params);
     734             :   }
     735        3538 : }
     736             : 
     737             : void
     738        3735 : Simulation::addMooseObjects()
     739             : {
     740       18647 :   for (auto && comp : _components)
     741       14912 :     comp->addMooseObjects();
     742        3735 : }
     743             : 
     744             : void
     745        3900 : Simulation::addRelationshipManagers()
     746             : {
     747             :   {
     748        3900 :     const std::string class_name = "AugmentSparsityBetweenElements";
     749        3900 :     auto params = _thm_factory.getValidParams(class_name);
     750        3900 :     params.set<Moose::RelationshipManagerType>("rm_type") =
     751             :         Moose::RelationshipManagerType::COUPLING | Moose::RelationshipManagerType::ALGEBRAIC |
     752             :         Moose::RelationshipManagerType::GEOMETRIC;
     753        3900 :     params.set<std::string>("for_whom") = _fe_problem.name();
     754        3900 :     params.set<MooseMesh *>("mesh") = &_thm_mesh;
     755        3900 :     params.set<std::map<dof_id_type, std::vector<dof_id_type>> *>("_elem_map") =
     756        3900 :         &_sparsity_elem_augmentation;
     757             :     auto rm =
     758        3900 :         _thm_factory.create<RelationshipManager>(class_name, "thm:sparsity_btw_elems", params);
     759       11700 :     if (!_thm_app.addRelationshipManager(rm))
     760           0 :       _thm_factory.releaseSharedObjects(*rm);
     761        3900 :   }
     762             : 
     763       19538 :   for (auto && comp : _components)
     764       15638 :     comp->addRelationshipManagers(Moose::RelationshipManagerType::COUPLING |
     765             :                                   Moose::RelationshipManagerType::ALGEBRAIC |
     766             :                                   Moose::RelationshipManagerType::GEOMETRIC);
     767        3900 : }
     768             : 
     769             : void
     770        3748 : Simulation::setupCoordinateSystem()
     771             : {
     772        7496 :   MultiMooseEnum coord_types("XYZ RZ RSPHERICAL");
     773             :   std::vector<SubdomainName> blocks;
     774             : 
     775       19296 :   for (auto && comp : _components)
     776             :   {
     777       15548 :     if (comp->parent() == nullptr)
     778             :     {
     779       15548 :       const auto & subdomains = comp->getSubdomainNames();
     780       15548 :       const auto & coord_sys = comp->getCoordSysTypes();
     781             : 
     782       23412 :       for (unsigned int i = 0; i < subdomains.size(); i++)
     783             :       {
     784        7864 :         blocks.push_back(subdomains[i]);
     785             :         // coord_types.push_back("XYZ");
     786       23592 :         coord_types.setAdditionalValue(coord_sys[i] == Moose::COORD_RZ ? "RZ" : "XYZ");
     787             :       }
     788             :     }
     789             :   }
     790        3748 :   _fe_problem.setCoordSystem(blocks, coord_types);
     791             : 
     792             :   // RZ geometries are always aligned with x-axis
     793        7496 :   MooseEnum rz_coord_axis("X=0 Y=1", "X");
     794        3748 :   _fe_problem.setAxisymmetricCoordAxis(rz_coord_axis);
     795        3748 : }
     796             : 
     797             : void
     798        3891 : Simulation::setupMesh()
     799             : {
     800        3891 :   if (_components.size() == 0)
     801             :     return;
     802             : 
     803        3748 :   setupCoordinateSystem();
     804             : }
     805             : 
     806             : void
     807        3729 : Simulation::couplingMatrixIntegrityCheck() const
     808             : {
     809        3729 :   if (!_fe_problem.shouldSolve())
     810             :     return;
     811             : 
     812             :   const TimeIntegrator * ti = nullptr;
     813             :   const auto & time_integrators =
     814        3468 :       _fe_problem.getNonlinearSystemBase(/*nl_sys_num=*/0).getTimeIntegrators();
     815        3468 :   if (!time_integrators.empty())
     816             :     ti = time_integrators.front().get();
     817             :   // Yes, this is horrible. Don't ask why...
     818        3412 :   if ((dynamic_cast<const ExplicitTimeIntegrator *>(ti) != nullptr) ||
     819        3210 :       (dynamic_cast<const ExplicitEuler *>(ti) != nullptr) ||
     820        6678 :       (dynamic_cast<const ExplicitRK2 *>(ti) != nullptr) ||
     821        3210 :       (dynamic_cast<const ExplicitTVDRK2 *>(ti) != nullptr))
     822             :     return;
     823             : 
     824        3210 :   const CouplingMatrix * cm = _fe_problem.couplingMatrix(/*nl_sys_num=*/0);
     825        3210 :   if (cm == nullptr)
     826           0 :     mooseError("Coupling matrix does not exists. Something really bad happened.");
     827             : 
     828             :   bool full = true;
     829       15519 :   for (unsigned int i = 0; i < cm->size(); i++)
     830       82230 :     for (unsigned int j = 0; j < cm->size(); j++)
     831             :       full &= (*cm)(i, j);
     832             : 
     833        3210 :   if (!full)
     834           0 :     mooseError(
     835             :         "Single matrix preconditioning with full coupling is required to run. Please, check that "
     836             :         "your input file has the following preconditioning block:\n\n"
     837             :         "[Preconditioning]\n"
     838             :         "  [pc]\n"
     839             :         "    type = SMP\n"
     840             :         "    full = true\n"
     841             :         "  []\n"
     842             :         "[].\n");
     843             : }
     844             : 
     845             : void
     846        3889 : Simulation::integrityCheck() const
     847             : {
     848        3889 :   if (_components.size() == 0)
     849         223 :     return;
     850             : 
     851        3746 :   if (_check_jacobian)
     852             :     return;
     853             : 
     854             :   // go over components and put flow channels into one "bucket"
     855             :   std::vector<Component *> flow_channels;
     856       18936 :   for (auto && comp : _components)
     857             :   {
     858       15270 :     auto flow_channel = dynamic_cast<FlowChannelBase *>(comp.get());
     859       15270 :     if (flow_channel != nullptr)
     860        4050 :       flow_channels.push_back(flow_channel);
     861             :   }
     862             : 
     863             :   // initialize number of connected flow channel inlets and outlets to zero
     864             :   std::map<std::string, unsigned int> flow_channel_inlets;
     865             :   std::map<std::string, unsigned int> flow_channel_outlets;
     866        7716 :   for (auto && comp : flow_channels)
     867             :   {
     868        4050 :     flow_channel_inlets[comp->name()] = 0;
     869        4050 :     flow_channel_outlets[comp->name()] = 0;
     870             :   }
     871             : 
     872             :   // mark connections of any Component1DConnection components
     873       18936 :   for (const auto & comp : _components)
     874             :   {
     875       15270 :     auto pc_comp = dynamic_cast<Component1DConnection *>(comp.get());
     876       15270 :     if (pc_comp != nullptr)
     877             :     {
     878       14454 :       for (const auto & connection : pc_comp->getConnections())
     879             :       {
     880        8098 :         if (connection._end_type == Component1DConnection::IN)
     881        4048 :           flow_channel_inlets[connection._component_name]++;
     882        4050 :         else if (connection._end_type == Component1DConnection::OUT)
     883        4050 :           flow_channel_outlets[connection._component_name]++;
     884             :       }
     885             :     }
     886             :   }
     887             : 
     888             :   // finally, check that each flow channel has exactly one input and one output
     889        7716 :   for (auto && comp : flow_channels)
     890             :   {
     891        4050 :     if (flow_channel_inlets[comp->name()] == 0)
     892           6 :       logError("Component '", comp->name(), "' does not have connected inlet.");
     893        4044 :     else if (flow_channel_inlets[comp->name()] > 1)
     894           2 :       logError("Multiple inlets specified for component '", comp->name(), "'.");
     895             : 
     896        4050 :     if (flow_channel_outlets[comp->name()] == 0)
     897           0 :       logError("Component '", comp->name(), "' does not have connected outlet.");
     898        4050 :     else if (flow_channel_outlets[comp->name()] > 1)
     899           0 :       logError("Multiple outlets specified for component '", comp->name(), "'.");
     900             :   }
     901             : 
     902             :   // let components check themselves
     903       18936 :   for (auto && comp : _components)
     904       15270 :     comp->executeCheck();
     905             : 
     906        3666 :   _log.emitLoggedWarnings();
     907        3662 :   _log.emitLoggedErrors();
     908             : }
     909             : 
     910             : void
     911        3729 : Simulation::controlDataIntegrityCheck()
     912             : {
     913        3729 :   if (_check_jacobian)
     914             :     return;
     915             : 
     916             :   // check that control data are consistent
     917       14095 :   for (auto && i : _control_data)
     918             :   {
     919       10446 :     if (!i.second->getDeclared())
     920             :       logError("Control data '",
     921           2 :                i.first,
     922             :                "' was requested, but was not declared by any active control object.");
     923             :   }
     924             : 
     925        3649 :   _log.emitLoggedErrors();
     926             : 
     927        7294 :   auto & ctrl_wh = _fe_problem.getControlWarehouse()[EXEC_TIMESTEP_BEGIN];
     928             : 
     929             :   // initialize THM control objects
     930       14655 :   for (auto && i : ctrl_wh.getObjects())
     931             :   {
     932       11008 :     THMControl * ctrl = dynamic_cast<THMControl *>(i.get());
     933       11008 :     if (ctrl != nullptr)
     934       11008 :       ctrl->init();
     935             :   }
     936             : 
     937       14655 :   for (auto && i : ctrl_wh.getObjects())
     938             :   {
     939       11008 :     THMControl * ctrl = dynamic_cast<THMControl *>(i.get());
     940             :     // if it is a THM control
     941       11008 :     if (ctrl != nullptr)
     942             :     {
     943             :       // get its dependencies on control data
     944             :       auto & cd_deps = ctrl->getControlDataDependencies();
     945       11417 :       for (auto && cd_name : cd_deps)
     946             :       {
     947         409 :         ControlDataValue * cdv = _control_data[cd_name];
     948             :         // find out which control object built the control data
     949         409 :         std::string dep_name = cdv->getControl()->name();
     950             :         auto & deps = ctrl->getDependencies();
     951             :         // and if it is not in its dependency list, add it
     952         409 :         auto it = std::find(deps.begin(), deps.end(), dep_name);
     953         409 :         if (it == deps.end())
     954         409 :           deps.push_back(dep_name);
     955             :       }
     956             :     }
     957             :   }
     958             : 
     959             :   // Find all `TerminateControl`s and all their dependencies. Then add those
     960             :   // objects into TIMESTEP_END control warehouse
     961             :   MooseObjectWarehouse<Control> & ctrl_wh_tse =
     962        7294 :       _fe_problem.getControlWarehouse()[EXEC_TIMESTEP_END];
     963       14655 :   for (auto && i : ctrl_wh.getObjects())
     964             :   {
     965       11008 :     if (TerminateControl * ctrl = dynamic_cast<TerminateControl *>(i.get()))
     966             :     {
     967             :       std::list<const THMControl *> l;
     968          10 :       l.push_back(ctrl);
     969          40 :       while (l.size() > 0)
     970             :       {
     971          30 :         const THMControl * ctrl = l.front();
     972             :         auto & cd_deps = ctrl->getControlDataDependencies();
     973          50 :         for (auto && cd_name : cd_deps)
     974             :         {
     975          20 :           ControlDataValue * cdv = _control_data[cd_name];
     976          20 :           l.push_back(cdv->getControl());
     977             :         }
     978          60 :         ctrl_wh_tse.addObject(ctrl_wh.getObject(ctrl->name()));
     979             :         l.pop_front();
     980             :       }
     981             :     }
     982             :   }
     983             : }
     984             : 
     985             : void
     986           0 : Simulation::run()
     987             : {
     988           0 : }
     989             : 
     990             : void
     991       15640 : Simulation::addComponent(const std::string & type, const std::string & name, InputParameters params)
     992             : {
     993       15640 :   std::shared_ptr<Component> comp = _thm_factory.create<Component>(type, name, params);
     994       15638 :   if (_comp_by_name.find(name) == _comp_by_name.end())
     995       15638 :     _comp_by_name[name] = comp;
     996             :   else
     997             :     logError("Component with name '", name, "' already exists");
     998       15638 :   _components.push_back(comp);
     999       15638 : }
    1000             : 
    1001             : bool
    1002       10822 : Simulation::hasComponent(const std::string & name) const
    1003             : {
    1004             :   auto it = _comp_by_name.find(name);
    1005       10822 :   return (it != _comp_by_name.end());
    1006             : }
    1007             : 
    1008             : void
    1009        2628 : Simulation::addClosures(const std::string & type, const std::string & name, InputParameters params)
    1010             : {
    1011        2628 :   std::shared_ptr<ClosuresBase> obj_ptr = _thm_factory.create<ClosuresBase>(type, name, params);
    1012        2628 :   if (_closures_by_name.find(name) == _closures_by_name.end())
    1013        2628 :     _closures_by_name[name] = obj_ptr;
    1014             :   else
    1015             :     logError("A closures object with the name '", name, "' already exists.");
    1016        2628 : }
    1017             : 
    1018             : bool
    1019           0 : Simulation::hasClosures(const std::string & name) const
    1020             : {
    1021           0 :   return _closures_by_name.find(name) != _closures_by_name.end();
    1022             : }
    1023             : 
    1024             : std::shared_ptr<ClosuresBase>
    1025        4179 : Simulation::getClosures(const std::string & name) const
    1026             : {
    1027             :   auto it = _closures_by_name.find(name);
    1028        4179 :   if (it != _closures_by_name.end())
    1029        4179 :     return it->second;
    1030             :   else
    1031           0 :     mooseError("The requested closures object '", name, "' does not exist.");
    1032             : }
    1033             : 
    1034             : void
    1035        3640 : Simulation::addFileOutputter(const std::string & name)
    1036             : {
    1037        3640 :   _outputters_all.push_back(name);
    1038        3640 :   _outputters_file.push_back(name);
    1039        3640 : }
    1040             : 
    1041             : void
    1042        3729 : Simulation::addScreenOutputter(const std::string & name)
    1043             : {
    1044        3729 :   _outputters_all.push_back(name);
    1045        3729 :   _outputters_screen.push_back(name);
    1046        3729 : }
    1047             : 
    1048             : std::vector<OutputName>
    1049           0 : Simulation::getOutputsVector(const std::string & key) const
    1050             : {
    1051           0 :   std::string key_lowercase = key;
    1052             :   std::transform(key_lowercase.begin(), key_lowercase.end(), key_lowercase.begin(), ::tolower);
    1053             : 
    1054             :   std::vector<OutputName> outputs;
    1055           0 :   if (key_lowercase == "none")
    1056           0 :     outputs.push_back("none"); // provide non-existent name, so it does not get printed out
    1057           0 :   else if (key_lowercase == "screen")
    1058           0 :     outputs = _outputters_screen;
    1059           0 :   else if (key_lowercase == "file")
    1060           0 :     outputs = _outputters_file;
    1061           0 :   else if (key_lowercase == "both")
    1062           0 :     outputs = _outputters_all;
    1063             :   else
    1064           0 :     mooseError("The outputs vector key '" + key_lowercase + "' is invalid");
    1065             : 
    1066           0 :   return outputs;
    1067           0 : }
    1068             : 
    1069             : bool
    1070       87145 : Simulation::hasInitialConditionsFromFile() const
    1071             : {
    1072      174290 :   return _thm_pars.isParamValid("initial_from_file");
    1073             : }
    1074             : 
    1075             : void
    1076       31899 : Simulation::advanceState()
    1077             : {
    1078      108118 :   for (auto && i : _control_data)
    1079       76219 :     i.second->copyValuesBack();
    1080       31899 : }

Generated by: LCOV version 1.14