LCOV - code coverage report
Current view: top level - src/actions - ThermalContactAction.C (source / functions) Hit Total Coverage
Test: idaholab/moose heat_transfer: #32971 (54bef8) with base c6cf66 Lines: 209 214 97.7 %
Date: 2026-05-29 20:37:03 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 "ThermalContactAction.h"
      11             : 
      12             : #include "AddVariableAction.h"
      13             : #include "FEProblem.h"
      14             : #include "GapHeatTransfer.h"
      15             : #include "libmesh/string_to_enum.h"
      16             : #include "GapConductance.h"
      17             : #include "GapConductanceConstant.h"
      18             : #include "NonlinearSystem.h"
      19             : 
      20             : // Counter for naming materials
      21             : static unsigned int materials_counter = 0;
      22             : 
      23             : // Counter for naming dirac kernels
      24             : static unsigned int dirac_counter = 0;
      25             : 
      26             : // Counter for naming BCs
      27             : static unsigned int bcs_counter = 0;
      28             : 
      29             : // Counter for naming aux kernels
      30             : static unsigned int aux_kenels_counter = 0;
      31             : 
      32             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_aux_kernel");
      33             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_aux_variable");
      34             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_bc");
      35             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_dirac_kernel");
      36             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_material");
      37             : registerMooseAction("HeatTransferApp", ThermalContactAction, "add_secondary_flux_vector");
      38             : 
      39             : InputParameters
      40        3196 : ThermalContactAction::validParams()
      41             : {
      42        3196 :   InputParameters params = Action::validParams();
      43        3196 :   params.addClassDescription(
      44             :       "Action that controls the creation of all of the necessary objects for "
      45             :       "calculation of Thermal Contact");
      46             : 
      47        6392 :   params.addParam<std::string>(
      48             :       "gap_aux_type",
      49             :       "GapValueAux",
      50             :       "A string representing the Moose object that will be used for computing the gap size");
      51        6392 :   params.addRequiredParam<NonlinearVariableName>("variable", "The variable for thermal contact");
      52        6392 :   params.addParam<std::vector<BoundaryName>>(
      53             :       "primary", "The list of boundary IDs referring to primary sidesets");
      54        6392 :   params.addRequiredParam<std::vector<BoundaryName>>(
      55             :       "secondary", "The list of boundary IDs referring to secondary sidesets");
      56        6392 :   params.addRangeCheckedParam<Real>("tangential_tolerance",
      57             :                                     "tangential_tolerance>=0",
      58             :                                     "Tangential distance to extend edges of contact surfaces");
      59        6392 :   params.addRangeCheckedParam<Real>(
      60             :       "normal_smoothing_distance",
      61             :       "normal_smoothing_distance>=0 & normal_smoothing_distance<=1",
      62             :       "Distance from edge in parametric coordinates over which to smooth contact normal");
      63        6392 :   params.addParam<std::string>("normal_smoothing_method",
      64             :                                "Method to use to smooth normals (edge_based|nodal_normal_based)");
      65             : 
      66        3196 :   MooseEnum orders(AddVariableAction::getNonlinearVariableOrders());
      67        6392 :   params.addParam<MooseEnum>("order", orders, "The finite element order");
      68             : 
      69        6392 :   params.addParam<bool>(
      70        6392 :       "warnings", false, "Whether to output warning messages concerning nodes not being found");
      71        6392 :   params.addParam<bool>(
      72        6392 :       "quadrature", false, "Whether or not to use quadrature point based gap heat transfer");
      73        9588 :   params.addRangeCheckedParam<Real>("penalty",
      74        6392 :                                     1e3,
      75             :                                     "penalty>0",
      76             :                                     "The penalty used in the residual and Jacobian calculations "
      77             :                                     "when using the GapPerfectConductance model");
      78        6392 :   params.addParam<std::string>(
      79             :       "appended_property_name", "", "Name appended to material properties to make them unique");
      80        6392 :   params.addRequiredParam<std::string>(
      81             :       "type",
      82             :       "A string representing the Moose object that will be used for heat conduction over the gap");
      83             : 
      84        6392 :   params.addParam<std::vector<VariableName>>(
      85             :       "displacements",
      86             :       "The displacements appropriate for the simulation geometry and coordinate system");
      87             : 
      88        6392 :   params.addParam<std::vector<AuxVariableName>>(
      89             :       "save_in", {}, "The Auxiliary Variable to (optionally) save the boundary flux in");
      90        9588 :   params.addRangeCheckedParam<Real>("gap_conductivity",
      91        6392 :                                     1.0,
      92             :                                     "gap_conductivity>0",
      93             :                                     "The thermal conductivity of the gap material");
      94        6392 :   params.addParam<FunctionName>(
      95             :       "gap_conductivity_function",
      96             :       "Thermal conductivity of the gap material as a function.  Multiplied by gap_conductivity.");
      97        6392 :   params.addParam<std::vector<VariableName>>(
      98             :       "gap_conductivity_function_variable",
      99             :       "Variable to be used in gap_conductivity_function in place of time");
     100        6392 :   params.addParam<VariableName>("secondary_gap_offset",
     101             :                                 "Offset to gap distance from secondary side");
     102        6392 :   params.addParam<VariableName>("mapped_primary_gap_offset",
     103             :                                 "Offset to gap distance mapped from primary side");
     104        6392 :   params.addParam<bool>(
     105             :       "check_boundary_restricted",
     106        6392 :       true,
     107             :       "Whether to check for multiple element sides on the boundary for the boundary restricted, "
     108             :       "element aux variable set up for thermal contact enforcement. Setting this to false will "
     109             :       "allow contribution to a single element's elemental value(s) from multiple boundary sides "
     110             :       "on the same element (example: when the restricted boundary exists on two or more sides "
     111             :       "of an element, such as at a corner of a mesh");
     112             : 
     113        3196 :   params += GapConductance::actionParameters();
     114        3196 :   params += GapConductanceConstant::actionParameters();
     115             : 
     116        3196 :   params += GeometricSearchInterface::validParams();
     117             : 
     118        6392 :   params.addParamNamesToGroup("primary secondary", "Gap surface definition");
     119        6392 :   params.addParamNamesToGroup(
     120             :       "tangential_tolerance normal_smoothing_distance normal_smoothing_method",
     121             :       "Gap edge and edge normal smoothing");
     122        6392 :   params.addParamNamesToGroup("gap_aux_type secondary_gap_offset mapped_primary_gap_offset",
     123             :                               "Gap size");
     124        6392 :   params.addParamNamesToGroup("order quadrature", "Integration");
     125        6392 :   params.addParamNamesToGroup(
     126             :       "gap_conductivity gap_conductivity_function gap_conductivity_function_variable",
     127             :       "Gap conductivity");
     128        6392 :   params.addParamNamesToGroup("save_in check_boundary_restricted warnings",
     129             :                               "Diagnostics and debug");
     130             : 
     131        3196 :   return params;
     132        3196 : }
     133             : 
     134        3196 : ThermalContactAction::ThermalContactAction(const InputParameters & params)
     135             :   : Action(params),
     136        3196 :     _quadrature(getParam<bool>("quadrature")),
     137        6392 :     _order(getParam<MooseEnum>("order")),
     138        3196 :     _penetration_var_name(_quadrature ? "qpoint_penetration" : "penetration"),
     139        6392 :     _gap_value_name("paired_" + getParam<NonlinearVariableName>("variable")),
     140        6392 :     _gap_conductivity_name("paired_k_" + getParam<NonlinearVariableName>("variable")),
     141        9588 :     _boundary_pairs(getParam<BoundaryName, BoundaryName>("primary", "secondary"))
     142             : {
     143        3196 :   if (!params.get<bool>("check_boundary_restricted"))
     144             :   {
     145          38 :     if (_quadrature)
     146          72 :       paramInfo(
     147             :           "check_boundary_restricted",
     148             :           "This parameter is set to 'false'. Although thermal contact ",
     149             :           "will be correctly enforced, the contact-related output may have issues ",
     150             :           "in cases where where more than one face of an element belongs to a contact surface ",
     151             :           "because the values from only one of the faces will be reported.");
     152             :     else
     153           2 :       paramError("check_boundary_restricted",
     154             :                  "This parameter cannot be 'false' when 'quadrature=false'");
     155             :   }
     156        6444 :   if (params.isParamSetByUser("penalty") &&
     157        3306 :       getParam<std::string>("type") != "GapPerfectConductance")
     158           2 :     paramError("penalty",
     159             :                "This parameter should only be set by the user when 'type=GapPerfectConductance'.");
     160        3192 : }
     161             : 
     162             : void
     163        3156 : ThermalContactAction::act()
     164             : {
     165        3156 :   if (_current_task == "add_aux_kernel")
     166         525 :     addAuxKernels();
     167        2631 :   else if (_current_task == "add_aux_variable")
     168         527 :     addAuxVariables();
     169        2104 :   else if (_current_task == "add_bc")
     170         525 :     addBCs();
     171        1579 :   else if (_current_task == "add_dirac_kernel")
     172         525 :     addDiracKernels();
     173        1054 :   else if (_current_task == "add_material")
     174         527 :     addMaterials();
     175         527 :   else if (_current_task == "add_secondary_flux_vector")
     176         527 :     addSecondaryFluxVector();
     177        3154 : }
     178             : 
     179             : void
     180         525 : ThermalContactAction::addAuxKernels()
     181             : {
     182        1059 :   for (const auto & contact_pair : _boundary_pairs)
     183             :   {
     184             :     // Add gap aux kernel
     185             :     {
     186        1068 :       InputParameters params = _factory.getValidParams(getParam<std::string>("gap_aux_type"));
     187             : 
     188         534 :       params.applySpecificParameters(parameters(),
     189             :                                      {"tangential_tolerance",
     190             :                                       "normal_smoothing_distance",
     191             :                                       "normal_smoothing_method",
     192             :                                       "order",
     193             :                                       "warnings",
     194             :                                       "search_method",
     195             :                                       "check_boundary_restricted"});
     196         534 :       params.set<AuxVariableName>("variable") = _gap_value_name;
     197        2136 :       params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_LINEAR};
     198             : 
     199        1602 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     200         534 :       params.set<BoundaryName>("paired_boundary") = contact_pair.first;
     201        1602 :       params.set<VariableName>("paired_variable") = getParam<NonlinearVariableName>("variable");
     202             : 
     203        2136 :       _problem->addAuxKernel(getParam<std::string>("gap_aux_type"),
     204        1602 :                              "gap_value_" + name() + "_" + Moose::stringify(aux_kenels_counter),
     205             :                              params);
     206             : 
     207         534 :       if (_quadrature)
     208             :       {
     209         948 :         params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.first};
     210         316 :         params.set<BoundaryName>("paired_boundary") = contact_pair.second;
     211             : 
     212        1264 :         _problem->addAuxKernel(getParam<std::string>("gap_aux_type"),
     213         948 :                                "gap_value_primary_" + name() + "_" +
     214         632 :                                    Moose::stringify(aux_kenels_counter),
     215             :                                params);
     216             :       }
     217         534 :     }
     218             : 
     219             :     // Add penetration aux kernel
     220             :     {
     221         534 :       InputParameters params = _factory.getValidParams("PenetrationAux");
     222             : 
     223         534 :       params.applySpecificParameters(parameters(),
     224             :                                      {"tangential_tolerance",
     225             :                                       "normal_smoothing_distance",
     226             :                                       "normal_smoothing_method",
     227             :                                       "order",
     228             :                                       "search_method",
     229             :                                       "check_boundary_restricted"});
     230         534 :       params.set<AuxVariableName>("variable") = _penetration_var_name;
     231        1068 :       if (isParamValid("secondary_gap_offset"))
     232           0 :         params.set<std::vector<VariableName>>("secondary_gap_offset") = {
     233           0 :             getParam<VariableName>("secondary_gap_offset")};
     234        1068 :       if (isParamValid("mapped_primary_gap_offset"))
     235           0 :         params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
     236           0 :             getParam<VariableName>("mapped_primary_gap_offset")};
     237        2136 :       params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_LINEAR};
     238        1602 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     239         534 :       params.set<BoundaryName>("paired_boundary") = contact_pair.first;
     240             : 
     241        1602 :       _problem->addAuxKernel("PenetrationAux",
     242        1602 :                              "penetration_" + name() + "_" + Moose::stringify(aux_kenels_counter++),
     243             :                              params);
     244         534 :     }
     245             :   }
     246        1593 : }
     247             : 
     248             : void
     249         527 : ThermalContactAction::addAuxVariables()
     250             : {
     251             :   // We need to add variables only once per variable name.  However, we don't know how many unique
     252             :   // variable names we will have.  So, we'll always add them.
     253             : 
     254        1054 :   auto order = getParam<MooseEnum>("order");
     255         527 :   std::string family = "LAGRANGE";
     256             : 
     257         527 :   if (_quadrature)
     258             :   {
     259         636 :     order = "CONSTANT";
     260             :     family = "MONOMIAL";
     261             :   }
     262             : 
     263             :   auto var_type =
     264         527 :       AddVariableAction::variableType(FEType(order, Utility::string_to_enum<FEFamily>(family)));
     265         527 :   auto var_params = _factory.getValidParams(var_type);
     266         527 :   var_params.set<MooseEnum>("order") = order;
     267         527 :   var_params.set<MooseEnum>("family") = family;
     268             : 
     269         527 :   _problem->addAuxVariable(var_type, _penetration_var_name, var_params);
     270         527 :   _problem->addAuxVariable(var_type, _gap_value_name, var_params);
     271        1054 : }
     272             : 
     273             : void
     274         525 : ThermalContactAction::addBCs()
     275             : {
     276        1059 :   for (const auto & contact_pair : _boundary_pairs)
     277             :   {
     278        1068 :     const std::string object_name = getParam<std::string>("type");
     279         534 :     InputParameters params = _factory.getValidParams(object_name);
     280         534 :     params.applyParameters(parameters());
     281             : 
     282         534 :     if (object_name == "GapPerfectConductance")
     283             :     {
     284          18 :       params.set<Real>("penalty") = getParam<Real>("penalty");
     285          27 :       params.set<std::vector<VariableName>>("gap_distance") = {"penetration"};
     286          27 :       params.set<std::vector<VariableName>>("gap_temp") = {_gap_value_name};
     287          27 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     288          18 :       _problem->addBoundaryCondition(
     289          27 :           object_name, "gap_bc_" + name() + "_" + Moose::stringify(bcs_counter), params);
     290             :     }
     291             :     else
     292             :     {
     293         525 :       if (_quadrature)
     294             :       {
     295         316 :         params.set<BoundaryName>("paired_boundary") = contact_pair.first;
     296         316 :         params.set<bool>("use_displaced_mesh") = true;
     297             :       }
     298             :       else
     299             :       {
     300         627 :         params.set<std::vector<VariableName>>("gap_distance") = {"penetration"};
     301         627 :         params.set<std::vector<VariableName>>("gap_temp") = {_gap_value_name};
     302             :       }
     303             : 
     304        1575 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     305             : 
     306        1050 :       _problem->addBoundaryCondition(
     307        1575 :           object_name, "gap_bc_" + name() + "_" + Moose::stringify(bcs_counter), params);
     308             : 
     309         525 :       if (_quadrature)
     310             :       {
     311             :         // Swap primary and secondary for this one
     312         948 :         params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.first};
     313         316 :         params.set<BoundaryName>("paired_boundary") = contact_pair.second;
     314             : 
     315         632 :         _problem->addBoundaryCondition(
     316         948 :             object_name, "gap_bc_primary_" + name() + "_" + Moose::stringify(bcs_counter), params);
     317             :       }
     318             :     }
     319         534 :     bcs_counter++;
     320         534 :   }
     321         743 : }
     322             : 
     323             : void
     324         525 : ThermalContactAction::addDiracKernels()
     325             : {
     326         525 :   if (_quadrature)
     327             :     return;
     328             : 
     329         427 :   for (const auto & contact_pair : _boundary_pairs)
     330             :   {
     331         218 :     const std::string object_name = "GapHeatPointSourceMaster";
     332         218 :     InputParameters params = _factory.getValidParams(object_name);
     333         218 :     params.applySpecificParameters(parameters(),
     334             :                                    {"tangential_tolerance",
     335             :                                     "normal_smoothing_distance",
     336             :                                     "normal_smoothing_method",
     337             :                                     "order",
     338             :                                     "search_method",
     339             :                                     "variable"});
     340         218 :     params.set<BoundaryName>("boundary") = contact_pair.first;
     341         218 :     params.set<BoundaryName>("secondary") = contact_pair.second;
     342             : 
     343         436 :     _problem->addDiracKernel(
     344         654 :         object_name, object_name + "_" + name() + "_" + Moose::stringify(dirac_counter++), params);
     345         218 :   }
     346             : }
     347             : 
     348             : void
     349         527 : ThermalContactAction::addMaterials()
     350             : {
     351        1054 :   if (getParam<std::string>("type") != "GapHeatTransfer")
     352             :     return;
     353             : 
     354        1036 :   if (parameters().isParamSetByUser("gap_conductance"))
     355             :   {
     356          54 :     if (parameters().isParamSetByUser("gap_conductivity") ||
     357          54 :         parameters().isParamSetByUser("gap_conductivity_function"))
     358           0 :       mooseError(
     359             :           "Cannot specify both gap_conductance and gap_conductivity or gap_conductivity_function");
     360             : 
     361          63 :     for (const auto & contact_pair : _boundary_pairs)
     362             :     {
     363          36 :       const std::string object_type = "GapConductanceConstant";
     364          36 :       InputParameters params = _factory.getValidParams(object_type);
     365          36 :       params.applyParameters(parameters());
     366         108 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     367          72 :       _problem->addMaterial(object_type,
     368         108 :                             name() + "_" + "gap_value" + "_" + Moose::stringify(materials_counter),
     369             :                             params);
     370             : 
     371          36 :       if (_quadrature)
     372             :       {
     373          27 :         params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.first};
     374          18 :         _problem->addMaterial(object_type,
     375          27 :                               name() + "_" + "gap_value_primary" + "_" +
     376          18 :                                   Moose::stringify(materials_counter),
     377             :                               params);
     378             :       }
     379          36 :       materials_counter++;
     380          36 :     }
     381             :   }
     382             :   else
     383             :   {
     384         491 :     const std::string object_type = "GapConductance";
     385             : 
     386         980 :     for (const auto & contact_pair : _boundary_pairs)
     387             :     {
     388         491 :       InputParameters params = _factory.getValidParams(object_type);
     389         491 :       params.applyParameters(parameters(), {"variable"});
     390             : 
     391         982 :       params.set<std::vector<VariableName>>("variable") = {
     392        2946 :           getParam<NonlinearVariableName>("variable")};
     393        1473 :       params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     394             : 
     395         491 :       if (_quadrature)
     396         618 :         params.set<BoundaryName>("paired_boundary") = contact_pair.first;
     397             :       else
     398             :       {
     399         546 :         params.set<std::vector<VariableName>>("gap_temp") = {_gap_value_name};
     400         546 :         params.set<std::vector<VariableName>>("gap_distance") = {"penetration"};
     401             :       }
     402             : 
     403         982 :       _problem->addMaterial(object_type,
     404        1471 :                             name() + "_" + "gap_value" + "_" + Moose::stringify(materials_counter),
     405             :                             params);
     406             : 
     407         489 :       if (_quadrature)
     408             :       {
     409         614 :         params.set<BoundaryName>("paired_boundary") = contact_pair.second;
     410         921 :         params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.first};
     411             : 
     412         614 :         _problem->addMaterial(object_type,
     413         921 :                               name() + "_" + "gap_value_primary" + "_" +
     414         614 :                                   Moose::stringify(materials_counter),
     415             :                               params);
     416             :       }
     417         489 :       materials_counter++;
     418         489 :     }
     419             :   }
     420         673 : }
     421             : 
     422             : void
     423         527 : ThermalContactAction::addSecondaryFluxVector()
     424             : {
     425         527 :   _problem->getNonlinearSystemBase(/*nl_sys_num=*/0)
     426         527 :       .addVector("secondary_flux", false, libMesh::GHOSTED);
     427         527 :   _problem->getNonlinearSystemBase(/*nl_sys_num=*/0).zeroVectorForResidual("secondary_flux");
     428             : 
     429             :   // It is risky to apply this optimization to contact problems
     430             :   // since the problem configuration may be changed during Jacobian
     431             :   // evaluation. We therefore turn it off for all contact problems so that
     432             :   // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3 or older.
     433         527 :   if (!_problem->isSNESMFReuseBaseSetbyUser())
     434             :     _problem->setSNESMFReuseBase(false, false);
     435         527 : }
     436             : 
     437             : void
     438        9492 : ThermalContactAction::addRelationshipManagers(Moose::RelationshipManagerType input_rm_type)
     439             : {
     440        9492 :   if (!_quadrature)
     441             :     return;
     442             : 
     443       11460 :   for (const auto & contact_pair : _boundary_pairs)
     444             :   {
     445        5730 :     const auto & object_name = getParam<std::string>("type");
     446        5730 :     auto params = _factory.getValidParams(object_name);
     447        5730 :     params.applyParameters(parameters());
     448             : 
     449        5730 :     params.set<BoundaryName>("paired_boundary") = contact_pair.first;
     450        5730 :     params.set<bool>("use_displaced_mesh") = true;
     451       17190 :     params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
     452        5730 :     addRelationshipManagers(input_rm_type, params);
     453        5730 :   }
     454             : }

Generated by: LCOV version 1.14