LCOV - code coverage report
Current view: top level - src/components - HSCoupler2D3D.C (source / functions) Hit Total Coverage
Test: idaholab/moose thermal_hydraulics: #32971 (54bef8) with base c6cf66 Lines: 110 111 99.1 %
Date: 2026-05-29 20:41:18 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "HSCoupler2D3D.h"
      11             : #include "HeatStructureCylindricalBase.h"
      12             : #include "HeatStructureFromFile3D.h"
      13             : #include "MeshAlignment2D3D.h"
      14             : #include "THMMesh.h"
      15             : 
      16             : registerMooseObject("ThermalHydraulicsApp", HSCoupler2D3D);
      17             : 
      18             : InputParameters
      19          96 : HSCoupler2D3D::validParams()
      20             : {
      21          96 :   InputParameters params = BoundaryBase::validParams();
      22             : 
      23         192 :   params.addRequiredParam<std::string>("heat_structure_2d", "The 2D heat structure to couple");
      24         192 :   params.addRequiredParam<std::string>("heat_structure_3d", "The 3D heat structure to couple");
      25         192 :   params.addRequiredParam<BoundaryName>("boundary_2d",
      26             :                                         "The boundary of the 2D heat structure to couple");
      27         192 :   params.addRequiredParam<BoundaryName>("boundary_3d",
      28             :                                         "The boundary of the 3D heat structure to couple");
      29             : 
      30         192 :   params.addParam<bool>("include_radiation", true, "Include radiation component of heat flux");
      31         192 :   params.addParam<FunctionName>(
      32             :       "emissivity_2d",
      33             :       "Emissivity of the 2D heat structure boundary as a function of temperature [K]");
      34         192 :   params.addParam<FunctionName>(
      35             :       "emissivity_3d",
      36             :       "Emissivity of the 3D heat structure boundary as a function of temperature [K]");
      37         192 :   params.addRequiredParam<FunctionName>("gap_thickness",
      38             :                                         "Gap thickness [m] as a function of temperature [K]");
      39         192 :   params.addRequiredParam<FunctionName>(
      40             :       "gap_thermal_conductivity",
      41             :       "Gap thermal conductivity [W/(m-K)] as a function of temperature [K]");
      42         192 :   params.addParam<FunctionName>(
      43         192 :       "gap_htc", 0, "Gap heat transfer coefficient [W/(m^2-K)] as a function of temperature [K]");
      44         288 :   params.addRangeCheckedParam<Real>("symmetry_factor",
      45         192 :                                     1.0,
      46             :                                     "symmetry_factor>=1.0",
      47             :                                     "Azimuthal symmetry correction factor (>= 1.0). Equal to 2*pi "
      48             :                                     "divided by the azimuthal angle covered by the 3D mesh.");
      49             : 
      50          96 :   params.addClassDescription("Couples a 2D heat structure boundary to a 3D heat structure boundary "
      51             :                              "using gap heat transfer.");
      52             : 
      53          96 :   return params;
      54           0 : }
      55             : 
      56          48 : HSCoupler2D3D::HSCoupler2D3D(const InputParameters & parameters)
      57             :   : BoundaryBase(parameters),
      58             : 
      59          48 :     _hs_name_2d(getParam<std::string>("heat_structure_2d")),
      60          96 :     _hs_name_3d(getParam<std::string>("heat_structure_3d")),
      61          96 :     _boundary_2d(getParam<BoundaryName>("boundary_2d")),
      62          96 :     _boundary_3d(getParam<BoundaryName>("boundary_3d")),
      63          96 :     _symmetry_factor(getParam<Real>("symmetry_factor")),
      64             : 
      65          96 :     _mesh_alignment(constMesh())
      66             : {
      67          48 :   addDependency(_hs_name_2d);
      68          48 :   addDependency(_hs_name_3d);
      69          48 : }
      70             : 
      71             : void
      72          48 : HSCoupler2D3D::setupMesh()
      73             : {
      74             :   BoundaryBase::setupMesh();
      75             : 
      76          48 :   if (hasComponentByName<HeatStructureCylindricalBase>(_hs_name_2d) &&
      77          46 :       hasComponentByName<HeatStructureFromFile3D>(_hs_name_3d))
      78             :   {
      79             :     const auto & hs_2d = getComponentByName<HeatStructureCylindricalBase>(_hs_name_2d);
      80          44 :     const auto & hs_3d = getComponentByName<HeatStructureFromFile3D>(_hs_name_3d);
      81             : 
      82          44 :     if (hs_2d.hasBoundary(_boundary_2d) && hs_3d.hasBoundary(_boundary_3d))
      83             :     {
      84             :       // Initialize the alignment mapping
      85          40 :       _mesh_alignment.initialize(hs_2d.getBoundaryInfo(_boundary_2d),
      86             :                                  hs_3d.getBoundaryInfo(_boundary_3d),
      87          40 :                                  hs_2d.getPosition(),
      88          80 :                                  hs_2d.getDirection());
      89             : 
      90             :       // Add entries to sparsity pattern for coupling
      91          40 :       if (_mesh_alignment.meshesAreAligned())
      92        3438 :         for (const auto & elem_id : _mesh_alignment.getSecondaryElemIDs())
      93             :         {
      94        3400 :           if (_mesh_alignment.hasCoupledPrimaryElemID(elem_id))
      95        3400 :             getTHMProblem().augmentSparsity(elem_id,
      96        3400 :                                             _mesh_alignment.getCoupledPrimaryElemID(elem_id));
      97             :         }
      98             :     }
      99             :   }
     100          48 : }
     101             : 
     102             : void
     103          48 : HSCoupler2D3D::check() const
     104             : {
     105             :   BoundaryBase::check();
     106             : 
     107          96 :   if (getParam<bool>("include_radiation"))
     108             :   {
     109          36 :     if (!(isParamValid("emissivity_2d") && isParamValid("emissivity_3d")))
     110           2 :       logError("If 'include_radiation' is 'true', then 'emissivity_2d' and 'emissivity_3d' are "
     111             :                "required.");
     112             :   }
     113             :   else
     114             :   {
     115         148 :     if (isParamValid("emissivity_2d") || isParamValid("emissivity_3d"))
     116           2 :       logError("If 'include_radiation' is 'false', then neither 'emissivity_2d' nor "
     117             :                "'emissivity_3d' can be specified.");
     118             :   }
     119             : 
     120          48 :   if (hasComponentByName<HeatStructureCylindricalBase>(_hs_name_2d))
     121             :   {
     122             :     const auto & hs = getComponentByName<HeatStructureCylindricalBase>(_hs_name_2d);
     123          46 :     if (!hs.hasBoundary(_boundary_2d))
     124           2 :       logError("The heat structure '",
     125           2 :                _hs_name_2d,
     126             :                "' does not have the boundary '",
     127             :                _boundary_2d,
     128             :                "'.");
     129             :   }
     130             :   else
     131           2 :     logError("There is no 2D cylindrical heat structure with the name '", _hs_name_2d, "'.");
     132             : 
     133          48 :   if (hasComponentByName<HeatStructureFromFile3D>(_hs_name_3d))
     134             :   {
     135             :     const auto & hs = getComponentByName<HeatStructureFromFile3D>(_hs_name_3d);
     136          46 :     if (!hs.hasBoundary(_boundary_3d))
     137           2 :       logError("The heat structure '",
     138           2 :                _hs_name_3d,
     139             :                "' does not have the boundary '",
     140             :                _boundary_3d,
     141             :                "'.");
     142             :   }
     143             :   else
     144           2 :     logError("There is no 3D heat structure with the name '", _hs_name_3d, "'.");
     145             : 
     146          48 :   if (hasComponentByName<HeatStructureCylindricalBase>(_hs_name_2d) &&
     147          46 :       hasComponentByName<HeatStructureFromFile3D>(_hs_name_3d) &&
     148             :       !_mesh_alignment.meshesAreAligned())
     149           6 :     logError("The meshes of the heat structures are not aligned.");
     150             : 
     151          48 :   const unsigned int needed_ad_container_size = 4 * _mesh_alignment.getMaxCouplingSize() + 6;
     152          48 :   if (MOOSE_AD_MAX_DOFS_PER_ELEM < needed_ad_container_size)
     153           2 :     logError("MOOSE must be configured with a larger AD container size (>= ",
     154             :              needed_ad_container_size,
     155             :              "). See HSCoupler2D3D's documentation for more information.");
     156          48 : }
     157             : 
     158             : void
     159          34 : HSCoupler2D3D::addMooseObjects()
     160             : {
     161             :   // add side UO on 2D boundary to cache temperature values by element ID
     162          68 :   const UserObjectName temperature_2d_uo_name = genName(name(), "2d_uo");
     163             :   {
     164          34 :     const std::string class_name = "StoreVariableByElemIDSideUserObject";
     165          34 :     InputParameters params = _factory.getValidParams(class_name);
     166         102 :     params.set<std::vector<BoundaryName>>("boundary") = {_boundary_2d};
     167         102 :     params.set<std::vector<VariableName>>("variable") = {HeatConductionModel::TEMPERATURE};
     168         170 :     params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR, EXEC_NONLINEAR};
     169             :     // This UO needs to execute before the UO on the 3D boundary
     170          34 :     params.set<int>("execution_order_group") = -1;
     171          34 :     getTHMProblem().addUserObject(class_name, temperature_2d_uo_name, params);
     172          34 :   }
     173             : 
     174             :   // get the radius of the 2D heat structure boundary
     175          34 :   const auto & hs_2d = getComponentByName<HeatStructureCylindricalBase>(_hs_name_2d);
     176          34 :   const auto radius_2d = hs_2d.getInnerRadius() + hs_2d.getTotalWidth();
     177             : 
     178             :   // add side UO on 3D boundary to compute heat fluxes across each 3D boundary
     179          68 :   const UserObjectName hs_coupler_2d3d_uo_name = genName(name(), "3d_uo");
     180             :   {
     181          34 :     const std::string class_name = "HSCoupler2D3DUserObject";
     182          34 :     InputParameters params = _factory.getValidParams(class_name);
     183         102 :     params.set<std::vector<BoundaryName>>("boundary") = {_boundary_3d};
     184         102 :     params.set<std::vector<VariableName>>("temperature") = {HeatConductionModel::TEMPERATURE};
     185          34 :     params.set<Real>("radius_2d") = radius_2d;
     186          34 :     params.set<Real>("symmetry_factor") = _symmetry_factor;
     187          68 :     if (getParam<bool>("include_radiation"))
     188             :     {
     189          24 :       params.set<FunctionName>("emissivity_2d") = getParam<FunctionName>("emissivity_2d");
     190          24 :       params.set<FunctionName>("emissivity_3d") = getParam<FunctionName>("emissivity_3d");
     191             :     }
     192         102 :     params.set<FunctionName>("gap_thickness") = getParam<FunctionName>("gap_thickness");
     193          68 :     params.set<FunctionName>("gap_thermal_conductivity") =
     194          34 :         getParam<FunctionName>("gap_thermal_conductivity");
     195         102 :     params.set<FunctionName>("gap_htc") = getParam<FunctionName>("gap_htc");
     196          34 :     params.set<UserObjectName>("temperature_2d_uo") = temperature_2d_uo_name;
     197          34 :     params.set<MeshAlignment2D3D *>("mesh_alignment") = &_mesh_alignment;
     198         170 :     params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR, EXEC_NONLINEAR};
     199          34 :     getTHMProblem().addUserObject(class_name, hs_coupler_2d3d_uo_name, params);
     200          34 :   }
     201             : 
     202             :   // add BC on 2D boundary
     203             :   {
     204          34 :     const std::string class_name = "HSCoupler2D3DBC";
     205          34 :     InputParameters params = _factory.getValidParams(class_name);
     206          68 :     params.set<NonlinearVariableName>("variable") = HeatConductionModel::TEMPERATURE;
     207         102 :     params.set<std::vector<BoundaryName>>("boundary") = {_boundary_2d};
     208          34 :     params.set<UserObjectName>("hs_coupler_2d3d_uo") = hs_coupler_2d3d_uo_name;
     209          34 :     getTHMProblem().addBoundaryCondition(class_name, genName(name(), class_name, "2d"), params);
     210          34 :   }
     211             : 
     212             :   // add BC on 3D boundary
     213             :   {
     214          34 :     const std::string class_name = "HSCoupler2D3DBC";
     215          34 :     InputParameters params = _factory.getValidParams(class_name);
     216          68 :     params.set<NonlinearVariableName>("variable") = HeatConductionModel::TEMPERATURE;
     217         102 :     params.set<std::vector<BoundaryName>>("boundary") = {_boundary_3d};
     218          34 :     params.set<UserObjectName>("hs_coupler_2d3d_uo") = hs_coupler_2d3d_uo_name;
     219          34 :     getTHMProblem().addBoundaryCondition(class_name, genName(name(), class_name, "3d"), params);
     220          34 :   }
     221         238 : }

Generated by: LCOV version 1.14