diff --git a/modules/heat_transfer/doc/content/source/bcs/GapPerfectConductance.md b/modules/heat_transfer/doc/content/source/bcs/GapPerfectConductance.md new file mode 100644 index 000000000000..47e8ffc25c0f --- /dev/null +++ b/modules/heat_transfer/doc/content/source/bcs/GapPerfectConductance.md @@ -0,0 +1,24 @@ +# GapPerfectConductance + +!syntax description /BCs/GapPerfectConductance + +!alert note title=Often Created by an Action +This object can be set up automatically by using the [ThermalContact](syntax/ThermalContact/index.md) action. + +## Description + +This class enforces that temperatures match across the gap. Specifically, the temperature on the secondary surface will match the temperature on the primary surface. This is accomplished through a penalty constraint. The residual is +\begin{equation} + r = k (T_s - T_p) +\end{equation} +where $k$ is the penalty value, $T_s$ is the temperature on the secondary surface, and $T_p$ is the temperature on the primary surface. + +## Example Input Syntax + +!listing test/tests/gap_perfect_transfer/perfect_transfer_gap.i block=ThermalContact + +!syntax parameters /BCs/GapPerfectConductance + +!syntax inputs /BCs/GapPerfectConductance + +!syntax children /BCs/GapPerfectConductance diff --git a/modules/heat_transfer/include/bcs/GapPerfectConductance.h b/modules/heat_transfer/include/bcs/GapPerfectConductance.h new file mode 100644 index 000000000000..48bf42937ea0 --- /dev/null +++ b/modules/heat_transfer/include/bcs/GapPerfectConductance.h @@ -0,0 +1,38 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "IntegratedBC.h" + +/** + * Boundary condition enforcing perfect gap conductance across gap through the use of a + * penalty parameter. + */ +class GapPerfectConductance : public IntegratedBC +{ +public: + static InputParameters validParams(); + GapPerfectConductance(const InputParameters & parameters); + + virtual ~GapPerfectConductance() {} + +protected: + virtual Real computeQpResidual() override; + virtual Real computeQpJacobian() override; + + /// AuxVariable holding the gap_distance + const VariableValue & _gap_distance; + + /// AuxVariable holding the temperature on the secondary surface + const VariableValue & _gap_temp; + + /// Penatly applied to the difference between the temperature on both sides of the gap + const Real _penalty; +}; diff --git a/modules/heat_transfer/src/actions/ThermalContactAction.C b/modules/heat_transfer/src/actions/ThermalContactAction.C index ae8186dae309..f775d1edf3b2 100644 --- a/modules/heat_transfer/src/actions/ThermalContactAction.C +++ b/modules/heat_transfer/src/actions/ThermalContactAction.C @@ -70,7 +70,11 @@ ThermalContactAction::validParams() "warnings", false, "Whether to output warning messages concerning nodes not being found"); params.addParam( "quadrature", false, "Whether or not to use quadrature point based gap heat transfer"); - + params.addRangeCheckedParam("penalty", + 1e3, + "penalty>0", + "The penalty used in the residual and Jacobian calculations " + "when using the GapPerfectConductance model"); params.addParam( "appended_property_name", "", "Name appended to material properties to make them unique"); params.addRequiredParam( @@ -147,6 +151,10 @@ ThermalContactAction::ThermalContactAction(const InputParameters & params) paramError("check_boundary_restricted", "This parameter cannot be 'false' when 'quadrature=false'"); } + if (params.isParamSetByUser("penalty") && + getParam("type") != "GapPerfectConductance") + paramError("penalty", + "This parameter should only be set by the user when 'type=GapPerfectConductance'."); } void @@ -267,30 +275,42 @@ ThermalContactAction::addBCs() InputParameters params = _factory.getValidParams(object_name); params.applyParameters(parameters()); - if (_quadrature) - { - params.set("paired_boundary") = contact_pair.first; - params.set("use_displaced_mesh") = true; - } - else + if (object_name == "GapPerfectConductance") { + params.set("penalty") = getParam("penalty"); params.set>("gap_distance") = {"penetration"}; params.set>("gap_temp") = {_gap_value_name}; + params.set>("boundary") = {contact_pair.second}; + _problem->addBoundaryCondition( + object_name, "gap_bc_" + name() + "_" + Moose::stringify(bcs_counter), params); } + else + { + if (_quadrature) + { + params.set("paired_boundary") = contact_pair.first; + params.set("use_displaced_mesh") = true; + } + else + { + params.set>("gap_distance") = {"penetration"}; + params.set>("gap_temp") = {_gap_value_name}; + } - params.set>("boundary") = {contact_pair.second}; + params.set>("boundary") = {contact_pair.second}; - _problem->addBoundaryCondition( - object_name, "gap_bc_" + name() + "_" + Moose::stringify(bcs_counter), params); + _problem->addBoundaryCondition( + object_name, "gap_bc_" + name() + "_" + Moose::stringify(bcs_counter), params); - if (_quadrature) - { - // Swap primary and secondary for this one - params.set>("boundary") = {contact_pair.first}; - params.set("paired_boundary") = contact_pair.second; + if (_quadrature) + { + // Swap primary and secondary for this one + params.set>("boundary") = {contact_pair.first}; + params.set("paired_boundary") = contact_pair.second; - _problem->addBoundaryCondition( - object_name, "gap_bc_primary_" + name() + "_" + Moose::stringify(bcs_counter), params); + _problem->addBoundaryCondition( + object_name, "gap_bc_primary_" + name() + "_" + Moose::stringify(bcs_counter), params); + } } bcs_counter++; } diff --git a/modules/heat_transfer/src/bcs/GapPerfectConductance.C b/modules/heat_transfer/src/bcs/GapPerfectConductance.C new file mode 100644 index 000000000000..35f12c0f16ed --- /dev/null +++ b/modules/heat_transfer/src/bcs/GapPerfectConductance.C @@ -0,0 +1,43 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "GapPerfectConductance.h" + +registerMooseObject("HeatTransferApp", GapPerfectConductance); + +InputParameters +GapPerfectConductance::validParams() +{ + InputParameters params = IntegratedBC::validParams(); + params.addClassDescription("Enforces equal temperatures across the gap."); + params.addRequiredCoupledVar("gap_distance", "Distance across the gap."); + params.addRequiredCoupledVar("gap_temp", "Temperature on the other side of the gap."); + params.addParam("penalty", 1e3, "Penalty value to be applied to the constraint."); + return params; +} + +GapPerfectConductance::GapPerfectConductance(const InputParameters & parameters) + : IntegratedBC(parameters), + _gap_distance(coupledValue("gap_distance")), + _gap_temp(coupledValue("gap_temp")), + _penalty(getParam("penalty")) +{ +} + +Real +GapPerfectConductance::computeQpResidual() +{ + return _penalty * (_u[_qp] - _gap_temp[_qp]); +} + +Real +GapPerfectConductance::computeQpJacobian() +{ + return _penalty; +} diff --git a/modules/heat_transfer/test/tests/gap_perfect_transfer/gold/perfect_transfer_gap_out.csv b/modules/heat_transfer/test/tests/gap_perfect_transfer/gold/perfect_transfer_gap_out.csv new file mode 100644 index 000000000000..699559fdc3a5 --- /dev/null +++ b/modules/heat_transfer/test/tests/gap_perfect_transfer/gold/perfect_transfer_gap_out.csv @@ -0,0 +1,22 @@ +time,aveTempLeft,aveTempRight +0,100,100 +0.1,110,110 +0.2,120,120 +0.3,130,130 +0.4,140,140 +0.5,150,150 +0.6,160,160 +0.7,170,170 +0.8,180,180 +0.9,190,190 +1,200,200 +1.1,200,200 +1.2,200,200 +1.3,200,200 +1.4,200,200 +1.5,200,200 +1.6,200,200 +1.7,200,200 +1.8,200,200 +1.9,200,200 +2,200,200 diff --git a/modules/heat_transfer/test/tests/gap_perfect_transfer/perfect_transfer_gap.i b/modules/heat_transfer/test/tests/gap_perfect_transfer/perfect_transfer_gap.i new file mode 100644 index 000000000000..3a16d6b915d3 --- /dev/null +++ b/modules/heat_transfer/test/tests/gap_perfect_transfer/perfect_transfer_gap.i @@ -0,0 +1,134 @@ +# +# 1-D Gap Perfect Heat Transfer +# +# The mesh consists of two element blocks containing one element each. Each +# element is a unit line. They sit next to one another with a unit between +# them. +# +# The temperature of the far left boundary is ramped from 100 to 200 over one +# second and then held fixed. The temperature of the far right boundary +# follows due to the perfect heat transfer. +# + +[Mesh] + [left] + type = GeneratedMeshGenerator + dim = 1 + boundary_name_prefix = left + [] + [right] + type = GeneratedMeshGenerator + dim = 1 + xmin = 2 + xmax = 3 + boundary_name_prefix = right + boundary_id_offset = 2 + [] + [right_block] + type = SubdomainIDGenerator + input = right + subdomain_id = 1 + [] + [collect] + type = CombinerGenerator + inputs = 'left right_block' + [] +[] + +[Functions] + [temperature] + type = PiecewiseLinear + x = '0 1 2' + y = '100 200 200' + [] +[] + +[Variables] + [temperature] + order = FIRST + family = LAGRANGE + initial_condition = 100 + [] +[] + +[Kernels] + [heat] + type = HeatConduction + variable = temperature + [] +[] + +[BCs] + [temp_far_left] + type = FunctionDirichletBC + boundary = 0 + variable = temperature + function = temperature + [] +[] + +[ThermalContact] + [thermal_contact_1] + type = GapPerfectConductance + penalty = 1e3 + variable = temperature + primary = 1 + secondary = 2 + [] +[] + +[Materials] + [heat1] + type = HeatConductionMaterial + block = 0 + specific_heat = 1.0 + thermal_conductivity = 1.0 + [] + [heat2] + type = HeatConductionMaterial + block = 1 + specific_heat = 1.0 + thermal_conductivity = 10.0 + [] +[] + +[Executioner] + type = Transient + + solve_type = 'PJFNK' + + petsc_options_iname = '-pc_type -pc_factor_mat_solver_type' + petsc_options_value = 'lu superlu_dist' + + line_search = 'none' + + nl_abs_tol = 1e-8 + nl_rel_tol = 1e-14 + + l_tol = 1e-3 + l_max_its = 100 + + start_time = 0.0 + dt = 1e-1 + end_time = 2.0 + num_steps = 50 +[] + +[Postprocessors] + [aveTempLeft] + type = SideAverageValue + boundary = 0 + variable = temperature + execute_on = 'initial timestep_end' + [] + [aveTempRight] + type = SideAverageValue + boundary = 3 + variable = temperature + execute_on = 'initial timestep_end' + [] +[] + +[Outputs] + csv = true +[] diff --git a/modules/heat_transfer/test/tests/gap_perfect_transfer/tests b/modules/heat_transfer/test/tests/gap_perfect_transfer/tests new file mode 100644 index 000000000000..94f1160777d5 --- /dev/null +++ b/modules/heat_transfer/test/tests/gap_perfect_transfer/tests @@ -0,0 +1,20 @@ +[Tests] + design = 'GapPerfectConductance.md' + issues = '#28963' + [perfect_conductance] + requirement = 'The system shall support nearly perfect gap conductance (no change in temperature across the gap) through ' + [test] + type = CSVDiff + input = perfect_transfer_gap.i + csvdiff = perfect_transfer_gap_out.csv + detail = 'the use of a penalty parameter, and' + [] + [error_check] + type = RunException + input = perfect_transfer_gap.i + cli_args = "ThermalContact/thermal_contact_1/type=GapHeatTransfer" + expect_err = "This parameter should only be set by the user when 'type=GapPerfectConductance'." + detail = 'error if the penalty is set for other heat transfer types.' + [] + [] +[]