https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GapHeatTransfer.C
Go to the documentation of this file.
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 "GapHeatTransfer.h"
11
12// MOOSE includes
13#include "AddVariableAction.h"
14#include "Assembly.h"
15#include "MooseMesh.h"
16#include "MooseVariable.h"
17#include "PenetrationLocator.h"
18#include "SystemBase.h"
19#include "GhostBoundary.h"
20
21#include "libmesh/string_to_enum.h"
22
24
27{
29 params.addClassDescription("Transfers heat across a gap between two "
30 "surfaces dependent on the gap geometry specified.");
31 params.addParam<std::string>(
32 "appended_property_name", "", "Name appended to material properties to make them unique");
33
34 // Common
35 params.addParam<Real>("min_gap", 1.0e-6, "A minimum gap size");
36 params.addParam<Real>("max_gap", 1.0e6, "A maximum gap size");
37 params.addRangeCheckedParam<unsigned int>(
38 "min_gap_order", 0, "min_gap_order<=1", "Order of the Taylor expansion below min_gap");
39
40 // Deprecated parameter
41 MooseEnum coord_types("default XYZ cyl", "default");
43 "coord_type",
44 coord_types,
45 "Gap calculation type (default or XYZ).",
46 "The functionality of this parameter is replaced by 'gap_geometry_type'.");
47
48 MooseEnum gap_geom_types("PLATE CYLINDER SPHERE");
49 params.addParam<MooseEnum>("gap_geometry_type",
50 gap_geom_types,
51 "Gap calculation type. Choices are: " + gap_geom_types.getRawNames());
52
53 params.addParam<RealVectorValue>("cylinder_axis_point_1",
54 "Start point for line defining cylindrical axis");
55 params.addParam<RealVectorValue>("cylinder_axis_point_2",
56 "End point for line defining cylindrical axis");
57 params.addParam<RealVectorValue>("sphere_origin", "Origin for sphere geometry");
58
59 // Quadrature based
60 params.addParam<bool>("quadrature",
61 false,
62 "Whether or not to do Quadrature point based gap heat "
63 "transfer. If this is true then gap_distance and "
64 "gap_temp should NOT be provided (and will be "
65 "ignored) however paired_boundary IS then required.");
66 params.addParam<BoundaryName>("paired_boundary", "The boundary to be penetrated");
67
69 params.addParam<MooseEnum>("order", orders, "The finite element order");
70
71 params.addParam<bool>(
72 "warnings", false, "Whether to output warning messages concerning nodes not being found");
73
74 params.addCoupledVar(
75 "displacements",
76 "The displacements appropriate for the simulation geometry and coordinate system");
77
78 // Node based options
79 params.addCoupledVar("gap_distance", "Distance across the gap");
80 params.addCoupledVar("gap_temp", "Temperature on the other side of the gap");
81
83 "GhostBoundary",
84 Moose::RelationshipManagerType::GEOMETRIC,
85 [](const InputParameters & obj_params, InputParameters & rm_params)
86 {
87 auto & boundary = rm_params.set<std::vector<BoundaryName>>("boundary");
88 boundary = obj_params.get<std::vector<BoundaryName>>("boundary");
89 boundary.push_back(obj_params.get<BoundaryName>("paired_boundary"));
90 });
91
92 return params;
93}
94
96 : IntegratedBC(parameters),
97 _gap_geometry_type(declareRestartableData<GapConductance::GAP_GEOMETRY>("gap_geometry_type",
98 GapConductance::PLATE)),
99 _quadrature(getParam<bool>("quadrature")),
100 _secondary_flux(!_quadrature ? &_sys.getVector("secondary_flux") : NULL),
101 _gap_conductance(getMaterialProperty<Real>("gap_conductance" +
102 getParam<std::string>("appended_property_name"))),
103 _gap_conductance_dT(getMaterialProperty<Real>(
104 "gap_conductance" + getParam<std::string>("appended_property_name") + "_dT")),
105 _min_gap(getParam<Real>("min_gap")),
106 _min_gap_order(getParam<unsigned int>("min_gap_order")),
107 _max_gap(getParam<Real>("max_gap")),
108 _gap_temp(0),
109 _gap_distance(std::numeric_limits<Real>::max()),
110 _edge_multiplier(1.0),
111 _has_info(false),
112 _disp_vars(3, libMesh::invalid_uint),
113 _gap_distance_value(_quadrature ? _zero : coupledValue("gap_distance")),
114 _gap_temp_value(_quadrature ? _zero : coupledValue("gap_temp")),
115 _penetration_locator(
116 !_quadrature ? NULL
117 : &getQuadraturePenetrationLocator(
118 parameters.get<BoundaryName>("paired_boundary"),
119 getParam<std::vector<BoundaryName>>("boundary")[0],
120 Utility::string_to_enum<Order>(parameters.get<MooseEnum>("order")))),
121 _warnings(getParam<bool>("warnings")),
122 _p1(declareRestartableData<Point>("cylinder_axis_point_1", Point(0, 1, 0))),
123 _p2(declareRestartableData<Point>("cylinder_axis_point_2", Point(0, 0, 0))),
124 _pinfo(nullptr),
125 _secondary_side_phi(nullptr),
126 _secondary_side(nullptr),
127 _secondary_j(0)
128{
129 if (isParamValid("displacements"))
130 {
131 // modern parameter scheme for displacements
132 for (unsigned int i = 0; i < coupledComponents("displacements"); ++i)
133 _disp_vars[i] = coupled("displacements", i);
134 }
135
136 if (_quadrature)
137 {
138 if (!parameters.isParamValid("paired_boundary"))
139 mooseError(std::string("No 'paired_boundary' provided for ") + _name);
140 }
141 else
142 {
143 if (!isCoupled("gap_distance"))
144 mooseError(std::string("No 'gap_distance' provided for ") + _name);
145
146 if (!isCoupled("gap_temp"))
147 mooseError(std::string("No 'gap_temp' provided for ") + _name);
148 }
149}
150
151void
153{
154 std::set<SubdomainID> subdomain_ids;
155
156 for (const auto & bnd_elem : *_mesh.getBoundaryElementRange())
157 {
158 Elem * elem = bnd_elem->_elem;
159 subdomain_ids.insert(elem->subdomain_id());
160 }
161
162 if (subdomain_ids.empty())
163 mooseError("No boundary elements found");
164
165 Moose::CoordinateSystemType coord_system = _fe_problem.getCoordSystem(*subdomain_ids.begin());
166
167 for (auto sid : subdomain_ids)
168 if (_fe_problem.getCoordSystem(sid) != coord_system)
169 mooseError("The GapHeatTransfer model requires all boundary elements to have the same "
170 "coordinate system.");
171
174}
175
176Real
178{
180
181 if (!_has_info)
182 return 0.0;
183
184 Real grad_t = (_u[_qp] - _gap_temp) * _edge_multiplier * _gap_conductance[_qp];
185
186 // This is keeping track of this residual contribution so it can be used as the flux on the other
187 // side of the gap.
188 if (!_quadrature)
189 {
190 Threads::spin_mutex::scoped_lock lock(Threads::spin_mutex);
191 const Real secondary_flux = computeSecondaryFluxContribution(grad_t);
192 _secondary_flux->add(_var.dofIndices()[_i], secondary_flux);
193 }
194
195 return _test[_i][_qp] * grad_t;
196}
197
198Real
200{
201 return _coord[_qp] * _JxW[_qp] * _test[_i][_qp] * grad_t;
202}
203
204void
206{
208
209 for (_qp = 0; _qp < _qrule->n_points(); _qp++)
210 {
211 // compute this up front because it only depends on the quadrature point
213
214 for (_i = 0; _i < _test.size(); _i++)
215 for (_j = 0; _j < _phi.size(); _j++)
217
218 // Ok now do the contribution from the secondary side
219 if (_quadrature && _has_info)
220 {
221 std::vector<dof_id_type> secondary_side_dof_indices;
222
223 _sys.dofMap().dof_indices(_secondary_side, secondary_side_dof_indices, _var.number());
224
225 DenseMatrix<Number> K_secondary(_var.dofIndices().size(), secondary_side_dof_indices.size());
226
227 mooseAssert(
228 _secondary_side_phi->size() == secondary_side_dof_indices.size(),
229 "The number of shapes does not match the number of dof indices on the secondary elem");
230
231 for (_i = 0; _i < _test.size(); _i++)
232 for (_secondary_j = 0;
233 _secondary_j < static_cast<unsigned int>(secondary_side_dof_indices.size());
234 ++_secondary_j)
236
238 K_secondary,
240 secondary_side_dof_indices,
242 }
243 }
244
246
248 {
249 unsigned int rows = _local_ke.m();
250 DenseVector<Number> diag(rows);
251 for (unsigned int i = 0; i < rows; i++)
252 diag(i) = _local_ke(i, i);
253
254 Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
255 for (unsigned int i = 0; i < _diag_save_in.size(); i++)
256 _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
257 }
258}
259
260void
261GapHeatTransfer::computeOffDiagJacobian(const unsigned int jvar_num)
262{
263 if (jvar_num == _var.number())
264 {
266 return;
267 }
268
269 const auto & jvar = getVariable(jvar_num);
270
272
273 auto phi_size = jvar.dofIndices().size();
274
275 for (_qp = 0; _qp < _qrule->n_points(); _qp++)
276 {
277 // compute this up front because it only depends on the quadrature point
279
280 for (_i = 0; _i < _test.size(); _i++)
281 for (_j = 0; _j < phi_size; _j++)
283
284 // Ok now do the contribution from the secondary side
285 if (_quadrature && _has_info)
286 {
287 std::vector<dof_id_type> secondary_side_dof_indices;
288
289 _sys.dofMap().dof_indices(_secondary_side, secondary_side_dof_indices, jvar_num);
290
291 DenseMatrix<Number> K_secondary(_var.dofIndices().size(), secondary_side_dof_indices.size());
292
293 mooseAssert(
294 _secondary_side_phi->size() == secondary_side_dof_indices.size(),
295 "The number of shapes does not match the number of dof indices on the secondary elem");
296
297 for (_i = 0; _i < _test.size(); _i++)
298 for (_secondary_j = 0;
299 _secondary_j < static_cast<unsigned int>(secondary_side_dof_indices.size());
300 ++_secondary_j)
301 K_secondary(_i, _secondary_j) +=
303
305 K_secondary,
307 secondary_side_dof_indices,
309 }
310 }
311
313}
314
315Real
317{
318 if (!_has_info)
319 return 0.0;
320
321 return _test[_i][_qp] *
324 _phi[_j][_qp];
325}
326
327Real
335
336Real
338{
339 if (!_has_info)
340 return 0.0;
341
342 unsigned int coupled_component;
343 bool active = false;
344 for (coupled_component = 0; coupled_component < _disp_vars.size(); ++coupled_component)
345 if (jvar == _disp_vars[coupled_component])
346 {
347 active = true;
348 break;
349 }
350
351 Real dRdx = 0.0;
352 if (active)
353 {
354 // Compute dR/du_[xyz]
355 // Residual is based on
356 // h_gap = h_conduction() + h_contact() + h_radiation();
357 // grad_t = (_u[_qp] - _gap_temp) * h_gap;
358 // So we need
359 // (_u[_qp] - _gap_temp) * (dh_gap/du_[xyz]);
360 // Assuming dh_contact/du_[xyz] = dh_radiation/du_[xyz] = 0,
361 // we need dh_conduction/du_[xyz]
362 // Given
363 // h_conduction = gapK / gapLength, then
364 // dh_conduction/du_[xyz] = -gapK/gapLength^2 * dgapLength/du_[xyz]
365 // Given
366 // gapLength = ((u_x-m_x)^2+(u_y-m_y)^2+(u_z-m_z)^2)^1/2
367 // where m_[xyz] is the primary coordinate, then
368 // dGapLength/du_[xyz] =
369 // 1/2*((u_x-m_x)^2+(u_y-m_y)^2+(u_z-m_z)^2)^(-1/2)*2*(u_[xyz]-m_[xyz])
370 // = (u_[xyz]-m_[xyz])/gapLength
371 // This is the normal vector.
372
373 const Real gapL = gapLength();
374
375 // THIS IS NOT THE NORMAL WE NEED.
376 // WE NEED THE NORMAL FROM THE CONSTRAINT, THE NORMAL FROM THE
377 // PRIMARY SURFACE. HOWEVER, THIS IS TRICKY SINCE THE NORMAL
378 // FROM THE PRIMARY SURFACE WAS COMPUTED FOR A POINT ASSOCIATED
379 // WITH A SECONDARY NODE. NOW WE ARE AT A SECONDARY INTEGRATION POINT.
380 //
381 // HOW DO WE GET THE NORMAL WE NEED?
382 //
383 // Until we have the normal we need,
384 // we'll hope that the one we have is close to the negative of the one we need.
385 const Point & normal(_normals[_qp]);
386
387 const Real dgap = dgapLength(-normal(coupled_component));
390 }
391 return _test[_i][_qp] * dRdx * _phi[_j][_qp];
392}
393
394Real
396{
397 if (!_has_info)
398 return 0.0;
399
400 unsigned int coupled_component;
401 bool active = false;
402 for (coupled_component = 0; coupled_component < _disp_vars.size(); ++coupled_component)
403 if (jvar == _disp_vars[coupled_component])
404 {
405 active = true;
406 break;
407 }
408
409 Real dRdx = 0.0;
410 if (active)
411 {
412 const Real gapL = gapLength();
413
414 const Point & normal(_normals[_qp]);
415
416 const Real dgap = dgapLength(-normal(coupled_component));
417
418 // The sign of the secondary side should presumably be opposite that of the primary side
421 }
422 return _test[_i][_qp] * dRdx * (*_secondary_side_phi)[_secondary_j][0];
423}
424
425Real
427{
428 if (_has_info)
430
431 return 1.0;
432}
433
434Real
435GapHeatTransfer::dgapLength(Real normalComponent) const
436{
437 const Real gap_L = gapLength();
438 Real dgap = 0.0;
439
440 if (_min_gap <= gap_L && gap_L <= _max_gap)
441 dgap = normalComponent;
442
443 return dgap;
444}
445
446void
448{
449 if (!_quadrature)
450 {
451 _has_info = true;
454 }
455 else
456 {
458
459 _gap_temp = 0.0;
460 _gap_distance = std::numeric_limits<Real>::max();
461 _has_info = false;
462 _edge_multiplier = 1.0;
463
464 if (auto it = _penetration_locator->_penetration_info.find(qnode->id());
465 it != _penetration_locator->_penetration_info.end() && it->second)
466 {
467 _pinfo = it->second;
469 _has_info = true;
470
474
475 Real tangential_tolerance = _penetration_locator->getTangentialTolerance();
476 if (tangential_tolerance != 0.0)
477 {
478 _edge_multiplier = 1.0 - _pinfo->_tangential_distance / tangential_tolerance;
479 if (_edge_multiplier < 0.0)
480 _edge_multiplier = 0.0;
481 }
482 }
483 else
484 {
485 _pinfo = nullptr;
486 if (_warnings)
487 mooseWarning("No gap value information found for node ",
488 qnode->id(),
489 " on processor ",
490 processor_id());
491 }
492 }
493
496}
registerMooseObject("HeatTransferApp", GapHeatTransfer)
void ErrorVector unsigned int
static MooseEnum getNonlinearVariableOrders()
unsigned int coupledComponents(const std::string &var_name) const
virtual unsigned int coupled(const std::string &var_name, unsigned int comp=0) const
virtual bool isCoupled(const std::string &var_name, unsigned int i=0) const
Generic gap heat transfer model, with h_gap = h_conduction + h_contact + h_radiation.
static void computeGapRadii(const GAP_GEOMETRY gap_geometry_type, const Point &current_point, const Point &p1, const Point &p2, const Real &gap_distance, const Point &current_normal, Real &r1, Real &r2, Real &radius)
Compute current gap radii for surface integration of gas conductance.
static void setGapGeometryParameters(const InputParameters &params, const Moose::CoordinateSystemType coord_sys, unsigned int axisymmetric_radial_coord, GAP_GEOMETRY &gap_geometry_type, Point &p1, Point &p2)
static Real gapLength(const GAP_GEOMETRY &gap_geom, const Real radius, const Real r1, const Real r2, const Real max_gap)
static Real gapAttenuation(Real adjusted_length, Real min_gap, unsigned int min_gap_order)
Generic gap heat transfer model, with h_gap = h_conduction + h_contact + h_radiation.
const PenetrationInfo * _pinfo
The current PenetratationInfo.
unsigned int _secondary_j
The secondary side shape index.
virtual Real computeQpOffDiagJacobian(unsigned jvar) override
Real _edge_multiplier
This is a factor that is used to gradually taper down the conductance if the contact point is off the...
virtual Real computeQpJacobian() override
virtual Real computeQpResidual() override
Real computeSecondaryQpOffDiagJacobian(unsigned int jvar)
compute the displacement Jacobian contributions from the secondary side degrees of freedom
const MaterialProperty< Real > & _gap_conductance_dT
const unsigned int _min_gap_order
void computeOffDiagJacobian(unsigned int jvar) override
const bool _quadrature
static InputParameters validParams()
virtual Real gapLength() const
const VariableValue & _gap_distance_value
virtual Real computeSecondaryFluxContribution(Real grad_t)
const Elem * _secondary_side
The secondary side element (this really is a side element, e.g.
Real computeSecondaryQpJacobian()
compute the Jacobian contributions from the secondary side degrees of freedom
GapConductance::GAP_GEOMETRY & _gap_geometry_type
const std::vector< std::vector< Real > > * _secondary_side_phi
The phi values on the secondary side.
PenetrationLocator * _penetration_locator
const bool _warnings
virtual void computeGapValues()
NumericVector< Number > * _secondary_flux
void computeJacobian() override
const Real _min_gap
virtual Real dgapLength(Real normalComponent) const
const MaterialProperty< Real > & _gap_conductance
GapHeatTransfer(const InputParameters &parameters)
std::vector< unsigned int > _disp_vars
virtual void initialSetup() override
const Real _max_gap
const VariableValue & _gap_temp_value
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
void addCoupledVar(const std::string &name, const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
const unsigned int & _current_side
bool _has_diag_save_in
const QBase *const & _qrule
unsigned int _qp
const MooseArray< Real > & _JxW
unsigned int _i
const Elem *const & _current_elem
std::vector< MooseVariableFEBase * > _diag_save_in
unsigned int _j
const MooseArray< Real > & _coord
const MooseArray< Point > & _q_point
MooseVariable & _var
const VariableValue & _u
static InputParameters validParams()
const VariablePhiValue & _phi
const MooseArray< Point > & _normals
const VariableTestValue & _test
const InputParameters & parameters() const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
const InputParameters & _pars
const std::string & _name
bool isParamValid(const std::string &name) const
Node * getQuadratureNode(const Elem *elem, const unsigned short int side, const unsigned int qp)
libMesh::StoredRange< MooseMesh::const_bnd_elem_iterator, const BndElement * > * getBoundaryElementRange()
void scalingFactor(const std::vector< Real > &factor)
unsigned int number() const
OutputType getValue(const Elem *elem, const std::vector< std::vector< OutputShape > > &phi) const
const std::vector< dof_id_type > & dofIndices() const final
MooseVariableFE< Real > * _variable
const Elem * _side
std::vector< std::vector< Real > > _side_phi
Real _tangential_distance
Real getTangentialTolerance()
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
THREAD_ID _tid
MooseMesh & _mesh
Assembly & _assembly
const MooseVariableFieldBase & getVariable(unsigned int jvar_num) const
SubProblem & _subproblem
SystemBase & _sys
FEProblemBase & _fe_problem
unsigned int getAxisymmetricRadialCoord() const
virtual Assembly & assembly(const THREAD_ID tid, const unsigned int sys_num)=0
Moose::CoordinateSystemType getCoordSystem(SubdomainID sid) const
unsigned int number() const
virtual libMesh::DofMap & dofMap()
DenseMatrix< Number > _local_ke
void accumulateTaggedLocalMatrix()
void prepareMatrixTag(Assembly &assembly, unsigned int ivar, unsigned int jvar)
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
unsigned int m() const
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
virtual void add(const numeric_index_type i, const T value)=0
processor_id_type processor_id() const
CoordinateSystemType
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...