LCOV - code coverage report
Current view: top level - src/actions - MortarGapHeatTransferAction.C (source / functions) Hit Total Coverage
Test: idaholab/moose heat_transfer: #31405 (292dce) with base fef103 Lines: 186 194 95.9 %
Date: 2025-09-04 07:53:51 Functions: 10 10 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 "MortarGapHeatTransferAction.h"
      11             : 
      12             : #include "AddVariableAction.h"
      13             : #include "FEProblem.h"
      14             : #include "libmesh/string_to_enum.h"
      15             : #include "NonlinearSystem.h"
      16             : 
      17             : #include "ModularGapConductanceConstraint.h"
      18             : #include "GapFluxModelRadiation.h"
      19             : #include "GapFluxModelConduction.h"
      20             : 
      21             : #include <algorithm>
      22             : 
      23             : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "append_mesh_generator");
      24             : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_mortar_variable");
      25             : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_constraint");
      26             : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_user_object");
      27             : 
      28             : InputParameters
      29         308 : MortarGapHeatTransferAction::validParams()
      30             : {
      31         308 :   InputParameters params = Action::validParams();
      32         308 :   params.addClassDescription(
      33             :       "Action that controls the creation of all of the necessary objects for "
      34             :       "calculation of heat transfer through an open/closed gap using a mortar formulation and a "
      35             :       "modular design approach");
      36             : 
      37         616 :   params.addParam<Real>("thermal_lm_scaling",
      38         616 :                         1.,
      39             :                         "Scaling factor to apply to the thermal Lagrange multiplier variable");
      40             : 
      41         308 :   params += ModularGapConductanceConstraint::validParams();
      42         308 :   params += GapFluxModelRadiation::validParams();
      43         308 :   params += GapFluxModelConduction::validParams();
      44             : 
      45         616 :   params.addParam<bool>(
      46             :       "correct_edge_dropping",
      47         616 :       true,
      48             :       "Whether to enable correct edge dropping treatment for mortar constraints. When disabled "
      49             :       "any Lagrange Multiplier degree of freedom on a secondary element without full primary "
      50             :       "contributions will be set (strongly) to 0.");
      51             : 
      52         308 :   params.makeParamNotRequired<SubdomainName>("primary_subdomain");
      53         308 :   params.makeParamNotRequired<SubdomainName>("secondary_subdomain");
      54         308 :   params.makeParamNotRequired<Real>("gap_conductivity");
      55             : 
      56         616 :   params.addParam<MultiMooseEnum>(
      57             :       "gap_flux_options", MortarGapHeatTransfer::gapFluxPhysics, "The gap flux models to build");
      58             : 
      59         616 :   params.addParam<std::vector<UserObjectName>>(
      60             :       "user_created_gap_flux_models",
      61             :       {},
      62             :       "The name of the user objects created by the user to represent gap heat transfer physics");
      63             : 
      64         616 :   params.addParamNamesToGroup("primary_subdomain secondary_subdomain", "Gap surface definition");
      65         616 :   params.addParamNamesToGroup("gap_flux_options user_created_gap_flux_models", "Gap flux models");
      66         616 :   params.addParamNamesToGroup("thermal_lm_scaling correct_edge_dropping",
      67             :                               "Thermal Lagrange multiplier");
      68             : 
      69         308 :   return params;
      70           0 : }
      71             : 
      72         308 : MortarGapHeatTransferAction::MortarGapHeatTransferAction(const InputParameters & params)
      73             :   : Action(params),
      74         308 :     _user_provided_mortar_meshes(false),
      75         308 :     _user_provided_gap_flux_models(
      76         308 :         getParam<std::vector<UserObjectName>>("user_created_gap_flux_models").size() > 0 ? true
      77         308 :                                                                                          : false)
      78             : 
      79             : {
      80         924 :   if (getParam<MultiMooseEnum>("gap_flux_options").size() > 0 && _user_provided_gap_flux_models)
      81           0 :     paramError(
      82             :         "gap_flux_options",
      83             :         "Either create user objects for the action in the input file or provide the desire physics "
      84             :         "to the action via the gap_flux_options parameter. Mixed use is not supported");
      85             : 
      86        1854 :   for (unsigned int i = 0; i < getParam<MultiMooseEnum>("gap_flux_options").size(); i++)
      87         310 :     _gap_flux_models.push_back(static_cast<MortarGapHeatTransfer::UserObjectToBuild>(
      88         930 :         getParam<MultiMooseEnum>("gap_flux_options").get(i)));
      89             : 
      90             :   // We do not currently support building more than one condution or more than one radiation user
      91             :   // object from this action.
      92             :   const unsigned int conduction_build_uos =
      93             :       cast_int<unsigned int>(std::count(_gap_flux_models.cbegin(),
      94             :                                         _gap_flux_models.cend(),
      95             :                                         MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION));
      96             :   const unsigned int radiation_build_uos =
      97             :       cast_int<unsigned int>(std::count(_gap_flux_models.cbegin(),
      98             :                                         _gap_flux_models.cend(),
      99             :                                         MortarGapHeatTransfer::UserObjectToBuild::RADIATION));
     100             : 
     101         308 :   if (conduction_build_uos > 1 || radiation_build_uos > 1)
     102           2 :     paramError("gap_flux_options",
     103             :                "You cannot choose to have more than one conduction or more than one radiation user "
     104             :                "objects when they are built by the action. If you want to superimpose multiple "
     105             :                "physics, you can choose to create your own user objects and pass them to this "
     106             :                "action via 'user_created_gap_flux_models'");
     107             : 
     108         612 :   if (params.isParamSetByUser("primary_subdomain") &&
     109         382 :       params.isParamSetByUser("secondary_subdomain"))
     110             :   {
     111          76 :     mooseInfo("Mortar gap heat transfer action is using the lower-dimensional domains provided by "
     112             :               "the user");
     113          76 :     _user_provided_mortar_meshes = true;
     114             :   }
     115             :   else
     116         230 :     mooseInfo("Mortar gap heat transfer action is creating new lower-dimensional domains");
     117             : 
     118         306 :   if (_user_provided_gap_flux_models)
     119         154 :     mooseInfo(
     120             :         "User decided to create user objects to model physics for the mortar gap heat transfer "
     121             :         "action independently, i.e. not through the mortar gap heat transfer action.");
     122             :   else
     123         152 :     mooseInfo("The mortar gap heat transfer action will add gap heat transfer physics according to "
     124             :               "the gap_flux_options input parameter");
     125             : 
     126             :   const bool wrong_parameters_provided =
     127         460 :       _user_provided_gap_flux_models &&
     128         308 :       (params.isParamSetByUser("gap_conductivity") ||
     129         612 :        params.isParamSetByUser("primary_emissivity") ||
     130         610 :        params.isParamSetByUser("secondary_emissivity") ||
     131         610 :        params.isParamSetByUser("gap_conductivity_function") ||
     132         458 :        params.isParamSetByUser("gap_conductivity_function_variable") ||
     133         458 :        params.isParamSetByUser("min_gap"));
     134             : 
     135         306 :   if (wrong_parameters_provided)
     136           2 :     paramError(
     137             :         "user_created_gap_flux_models",
     138             :         "The mortar gap heat transfer action requires that the input file defines user objects "
     139             :         "with physics or adds physics parameters directly into the action. You have provided both "
     140             :         "user objects and physics parameters (e.g. emissivities, gap conductance, etc.).");
     141         304 : }
     142             : 
     143             : void
     144         304 : MortarGapHeatTransferAction::act()
     145             : {
     146         304 :   if (_current_task == "append_mesh_generator")
     147          76 :     addMortarMesh();
     148         228 :   else if (_current_task == "add_mortar_variable")
     149          76 :     addMortarVariable();
     150         304 :   if (_current_task == "add_constraint")
     151          76 :     addConstraints();
     152         228 :   else if (_current_task == "add_user_object")
     153          76 :     if (!_user_provided_gap_flux_models)
     154          38 :       addUserObjects();
     155         304 : }
     156             : 
     157             : void
     158          57 : MortarGapHeatTransferAction::coreMortarMesh()
     159             : {
     160          57 :   if (!(_app.isRecovering() && _app.isUltimateMaster()) && !_app.useMasterMesh())
     161             :   {
     162          42 :     std::string action_name = MooseUtils::shortName(name());
     163             : 
     164          42 :     const MeshGeneratorName primary_name = action_name + "_primary_subdomain" + "_generator";
     165          42 :     const MeshGeneratorName secondary_name = action_name + "_secondary_subdomain" + "_generator";
     166             : 
     167          42 :     auto primary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
     168          42 :     auto secondary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
     169             : 
     170         126 :     primary_params.set<SubdomainName>("new_block_name") = action_name + "_primary_subdomain";
     171         126 :     secondary_params.set<SubdomainName>("new_block_name") = action_name + "_secondary_subdomain";
     172             : 
     173          84 :     primary_params.set<std::vector<BoundaryName>>("sidesets") = {
     174         168 :         getParam<BoundaryName>("primary_boundary")};
     175          84 :     secondary_params.set<std::vector<BoundaryName>>("sidesets") = {
     176         168 :         getParam<BoundaryName>("secondary_boundary")};
     177             : 
     178          84 :     _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", primary_name, primary_params);
     179          84 :     _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", secondary_name, secondary_params);
     180          42 :   }
     181         183 : }
     182             : 
     183             : void
     184         912 : MortarGapHeatTransferAction::addRelationshipManagers(Moose::RelationshipManagerType input_rm_type)
     185             : {
     186         912 :   checkForExistingSubdomains();
     187         912 :   std::string action_name = MooseUtils::shortName(name());
     188             : 
     189         912 :   auto params = MortarConstraintBase::validParams();
     190        1824 :   params.set<bool>("use_displaced_mesh") = getParam<bool>("use_displaced_mesh");
     191        2736 :   params.set<BoundaryName>("primary_boundary") = getParam<BoundaryName>("primary_boundary");
     192        2736 :   params.set<BoundaryName>("secondary_boundary") = getParam<BoundaryName>("secondary_boundary");
     193             : 
     194         912 :   if (_user_provided_mortar_meshes)
     195             :   {
     196         684 :     params.set<SubdomainName>("primary_subdomain") = getParam<SubdomainName>("primary_subdomain");
     197         456 :     params.set<SubdomainName>("secondary_subdomain") =
     198         456 :         getParam<SubdomainName>("secondary_subdomain");
     199             :   }
     200             :   else
     201             :   {
     202        2052 :     params.set<SubdomainName>("primary_subdomain") = action_name + "_primary_subdomain";
     203        2736 :     params.set<SubdomainName>("secondary_subdomain") = action_name + "_secondary_subdomain";
     204             :   }
     205             : 
     206         912 :   addRelationshipManagers(input_rm_type, params);
     207        1824 : }
     208             : 
     209             : void
     210          76 : MortarGapHeatTransferAction::addMortarVariable()
     211             : {
     212          76 :   checkForExistingSubdomains();
     213             : 
     214          76 :   InputParameters params = _factory.getValidParams("MooseVariableBase");
     215             : 
     216         152 :   const std::string & temperature = getParam<std::vector<VariableName>>("temperature")[0];
     217          76 :   std::string action_name = MooseUtils::shortName(name());
     218             : 
     219          76 :   if (!_problem->hasVariable(temperature))
     220           0 :     mooseError("Temperature variable is missing");
     221             : 
     222             :   const auto primal_type =
     223          76 :       _problem->getVariable(0, temperature, Moose::VarKindType::VAR_SOLVER).feType();
     224             :   const int lm_order = primal_type.order.get_order();
     225             : 
     226          76 :   if (primal_type.family != LAGRANGE)
     227           0 :     mooseError("The mortar thermal action can only be used with LAGRANGE finite elements");
     228             : 
     229         152 :   params.set<MooseEnum>("family") = Utility::enum_to_string<FEFamily>(primal_type.family);
     230         152 :   params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(libMesh::OrderWrapper{lm_order});
     231             : 
     232          76 :   if (_user_provided_mortar_meshes)
     233          38 :     params.set<std::vector<SubdomainName>>("block") = {
     234          76 :         getParam<SubdomainName>("secondary_subdomain")};
     235             :   else
     236         285 :     params.set<std::vector<SubdomainName>>("block") = {action_name + "_secondary_subdomain"};
     237             : 
     238         228 :   params.set<std::vector<Real>>("scaling") = {getParam<Real>("thermal_lm_scaling")};
     239          76 :   auto fe_type = AddVariableAction::feType(params);
     240          76 :   auto var_type = AddVariableAction::variableType(fe_type);
     241             : 
     242         152 :   _problem->addVariable(var_type, action_name + "_thermal_lm", params);
     243         114 : }
     244             : 
     245             : void
     246          76 : MortarGapHeatTransferAction::addConstraints()
     247             : {
     248          76 :   checkForExistingSubdomains();
     249             : 
     250         152 :   InputParameters params = _factory.getValidParams("ModularGapConductanceConstraint");
     251          76 :   const std::string action_name = MooseUtils::shortName(name());
     252             : 
     253          76 :   params.applyParameters(parameters());
     254          76 :   params.set<bool>("use_displaced_mesh") = true;
     255             : 
     256         228 :   params.set<BoundaryName>("primary_boundary") = getParam<BoundaryName>("primary_boundary");
     257         228 :   params.set<BoundaryName>("secondary_boundary") = getParam<BoundaryName>("secondary_boundary");
     258             : 
     259          76 :   if (_user_provided_mortar_meshes)
     260             :   {
     261          57 :     params.set<SubdomainName>("primary_subdomain") = getParam<SubdomainName>("primary_subdomain");
     262          38 :     params.set<SubdomainName>("secondary_subdomain") =
     263          38 :         getParam<SubdomainName>("secondary_subdomain");
     264             :   }
     265             :   else
     266             :   {
     267         171 :     params.set<SubdomainName>("primary_subdomain") = action_name + "_primary_subdomain";
     268         228 :     params.set<SubdomainName>("secondary_subdomain") = action_name + "_secondary_subdomain";
     269             :   }
     270             : 
     271         228 :   params.set<NonlinearVariableName>("variable") = action_name + "_thermal_lm";
     272             : 
     273         152 :   params.set<VariableName>("secondary_variable") =
     274          76 :       getParam<std::vector<VariableName>>("temperature")[0];
     275             : 
     276          76 :   if (!_user_provided_gap_flux_models)
     277             :   {
     278          38 :     std::vector<UserObjectName> uoname_strings(0);
     279             : 
     280         114 :     for (const auto & uo_name : _gap_flux_models)
     281             :     {
     282          76 :       if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION)
     283          38 :         uoname_strings.push_back("gap_flux_model_conduction_object_" +
     284          76 :                                  MooseUtils::shortName(name()));
     285          38 :       else if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::RADIATION)
     286          38 :         uoname_strings.push_back("gap_flux_model_radiation_object_" +
     287          76 :                                  MooseUtils::shortName(name()));
     288             :     }
     289             : 
     290          38 :     params.set<std::vector<UserObjectName>>("gap_flux_models") = uoname_strings;
     291          38 :   }
     292             :   else
     293          76 :     params.set<std::vector<UserObjectName>>("gap_flux_models") =
     294         114 :         getParam<std::vector<UserObjectName>>("user_created_gap_flux_models");
     295             : 
     296         152 :   _problem->addConstraint(
     297         152 :       "ModularGapConductanceConstraint", action_name + "_ModularGapConductanceConstraint", params);
     298          76 : }
     299             : 
     300             : void
     301          76 : MortarGapHeatTransferAction::addMortarMesh()
     302             : {
     303             :   // Evaluate whether we have sufficient information from the user to skip building the
     304             :   // lower-dimensional domains.
     305          76 :   checkForExistingSubdomains();
     306             : 
     307             :   // We may have available lower-dimensional domains (e.g. from a mechanical contact action), whose
     308             :   // subdomains can be reused for adding mortar variables and constraints.
     309          76 :   if (!_user_provided_mortar_meshes)
     310          57 :     coreMortarMesh();
     311          76 : }
     312             : 
     313             : void
     314          38 : MortarGapHeatTransferAction::addUserObjects()
     315             : {
     316             :   // It is risky to apply this optimization to contact problems
     317             :   // since the problem configuration may be changed during Jacobian
     318             :   // evaluation. We therefore turn it off for all contact problems so that
     319             :   // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3 or older.
     320             :   mooseAssert(_problem, "Problem pointer is null");
     321             : 
     322          38 :   if (!_problem->isSNESMFReuseBaseSetbyUser())
     323             :     _problem->setSNESMFReuseBase(false, false);
     324             : 
     325         114 :   for (const auto & uo_name : _gap_flux_models)
     326             :   {
     327          76 :     if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION)
     328             :     {
     329          38 :       auto var_params = _factory.getValidParams("GapFluxModelConduction");
     330             : 
     331          76 :       var_params.set<std::vector<VariableName>>("temperature") =
     332          76 :           getParam<std::vector<VariableName>>("temperature");
     333          76 :       var_params.set<Real>("gap_conductivity") = getParam<Real>("gap_conductivity");
     334             : 
     335          76 :       if (isParamValid("gap_conductivity_function"))
     336           0 :         var_params.set<FunctionName>("gap_conductivity_function") =
     337           0 :             getParam<FunctionName>("gap_conductivity_function");
     338             : 
     339          76 :       if (isParamValid("gap_conductivity_function_variable"))
     340           0 :         var_params.set<std::vector<VariableName>>("gap_conductivity_function_variable") =
     341           0 :             getParam<std::vector<VariableName>>("gap_conductivity_function_variable");
     342             : 
     343          76 :       var_params.set<Real>("min_gap") = getParam<Real>("min_gap");
     344          76 :       var_params.set<unsigned int>("min_gap_order") = getParam<unsigned int>("min_gap_order");
     345             : 
     346          76 :       var_params.set<std::vector<BoundaryName>>("boundary") =
     347          76 :           getParam<std::vector<BoundaryName>>("boundary");
     348             : 
     349          38 :       var_params.set<bool>("use_displaced_mesh") = true;
     350             : 
     351         114 :       _problem->addUserObject("GapFluxModelConduction",
     352          38 :                               "gap_flux_model_conduction_object_" + MooseUtils::shortName(name()),
     353             :                               var_params);
     354          38 :     }
     355          38 :     else if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::RADIATION)
     356             : 
     357             :     {
     358          38 :       auto var_params = _factory.getValidParams("GapFluxModelRadiation");
     359             : 
     360          76 :       var_params.set<Real>("stefan_boltzmann") = getParam<Real>("stefan_boltzmann");
     361          76 :       var_params.set<Real>("primary_emissivity") = getParam<Real>("primary_emissivity");
     362          76 :       var_params.set<Real>("secondary_emissivity") = getParam<Real>("secondary_emissivity");
     363             : 
     364          76 :       var_params.set<std::vector<BoundaryName>>("boundary") =
     365          76 :           getParam<std::vector<BoundaryName>>("boundary");
     366             : 
     367          76 :       var_params.set<std::vector<VariableName>>("temperature") =
     368          76 :           getParam<std::vector<VariableName>>("temperature");
     369             : 
     370          38 :       var_params.set<bool>("use_displaced_mesh") = true;
     371             : 
     372         114 :       _problem->addUserObject("GapFluxModelRadiation",
     373          38 :                               "gap_flux_model_radiation_object_" + MooseUtils::shortName(name()),
     374             :                               var_params);
     375          38 :     }
     376             :   }
     377          38 : }
     378             : 
     379             : void
     380        1140 : MortarGapHeatTransferAction::checkForExistingSubdomains()
     381             : {
     382        2280 :   if (parameters().isParamSetByUser("primary_subdomain") &&
     383        1425 :       parameters().isParamSetByUser("secondary_subdomain"))
     384             :   {
     385         285 :     _user_provided_mortar_meshes = true;
     386             :   }
     387             :   else
     388         855 :     _user_provided_mortar_meshes = false;
     389        1140 : }

Generated by: LCOV version 1.14