https://mooseframework.inl.gov
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 
23 registerMooseObject("HeatTransferApp", GapHeatTransfer);
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",
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 
151 void
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 
176 Real
178 {
180 
181  if (!_has_info)
182  return 0.0;
183 
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 
198 Real
200 {
201  return _coord[_qp] * _JxW[_qp] * _test[_i][_qp] * grad_t;
202 }
203 
204 void
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)
235  K_secondary(_i, _secondary_j) += _JxW[_qp] * _coord[_qp] * computeSecondaryQpJacobian();
236 
238  K_secondary,
239  _var.dofIndices(),
240  secondary_side_dof_indices,
241  _var.scalingFactor());
242  }
243  }
244 
246 
247  if (_has_diag_save_in)
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 
260 void
261 GapHeatTransfer::computeOffDiagJacobian(const unsigned int jvar_num)
262 {
263  if (jvar_num == _var.number())
264  {
265  computeJacobian();
266  return;
267  }
268 
269  const auto & jvar = getVariable(jvar_num);
270 
271  prepareMatrixTag(_assembly, _var.number(), jvar_num);
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++)
282  _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpOffDiagJacobian(jvar_num);
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,
306  _var.dofIndices(),
307  secondary_side_dof_indices,
308  _var.scalingFactor());
309  }
310  }
311 
313 }
314 
315 Real
317 {
318  if (!_has_info)
319  return 0.0;
320 
321  return _test[_i][_qp] *
324  _phi[_j][_qp];
325 }
326 
327 Real
329 {
330  return _test[_i][_qp] *
333  (*_secondary_side_phi)[_secondary_j][0];
334 }
335 
336 Real
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 
394 Real
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 
425 Real
427 {
428  if (_has_info)
430 
431  return 1.0;
432 }
433 
434 Real
435 GapHeatTransfer::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 
446 void
448 {
449  if (!_quadrature)
450  {
451  _has_info = true;
454  }
455  else
456  {
459 
460  _gap_temp = 0.0;
461  _gap_distance = std::numeric_limits<Real>::max();
462  _has_info = false;
463  _edge_multiplier = 1.0;
464 
465  if (_pinfo)
466  {
468  _has_info = true;
469 
473 
474  Real tangential_tolerance = _penetration_locator->getTangentialTolerance();
475  if (tangential_tolerance != 0.0)
476  {
477  _edge_multiplier = 1.0 - _pinfo->_tangential_distance / tangential_tolerance;
478  if (_edge_multiplier < 0.0)
479  _edge_multiplier = 0.0;
480  }
481  }
482  else
483  {
484  if (_warnings)
485  mooseWarning("No gap value information found for node ",
486  qnode->id(),
487  " on processor ",
488  processor_id());
489  }
490  }
491 
494 }
MooseMesh & _mesh
const VariableTestValue & _test
unsigned int getAxisymmetricRadialCoord() const
virtual bool isCoupled(const std::string &var_name, unsigned int i=0) const
GapConductance::GAP_GEOMETRY & _gap_geometry_type
virtual void computeGapValues()
virtual unsigned int coupled(const std::string &var_name, unsigned int comp=0) const
unsigned int _j
Order
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
virtual Real computeSecondaryFluxContribution(Real grad_t)
std::vector< MooseVariableFEBase *> _diag_save_in
const unsigned int invalid_uint
const MooseArray< Point > & _normals
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void computeJacobian() override
Real getValue(const Elem *elem, const std::vector< std::vector< OutputShape >> &phi) const
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
unsigned int number() const
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
const Elem *const & _current_elem
Real computeSecondaryQpJacobian()
compute the Jacobian contributions from the secondary side degrees of freedom
Real getTangentialTolerance()
T & set(const std::string &name, bool quiet_mode=false)
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 InputParameters validParams()
static Real gapLength(const GAP_GEOMETRY &gap_geom, const Real radius, const Real r1, const Real r2, const Real max_gap)
unsigned int _i
unsigned int m() const
Generic gap heat transfer model, with h_gap = h_conduction + h_contact + h_radiation.
static Real gapAttenuation(Real adjusted_length, Real min_gap, unsigned int min_gap_order)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
std::vector< std::vector< Real > > _side_phi
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
const MaterialProperty< Real > & _gap_conductance_dT
std::string getRawNames() const
std::map< dof_id_type, PenetrationInfo *> & _penetration_info
THREAD_ID _tid
const VariablePhiValue & _phi
DenseMatrix< Number > _local_ke
const MooseVariableFieldBase & getVariable(unsigned int jvar_num) const
void mooseWarning(Args &&... args) const
unsigned int _qp
Real _edge_multiplier
This is a factor that is used to gradually taper down the conductance if the contact point is off the...
const bool _quadrature
auto max(const L &left, const R &right)
bool isParamValid(const std::string &name) const
virtual Real computeQpJacobian() override
SystemBase & _sys
const MooseArray< Point > & _q_point
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
Real computeSecondaryQpOffDiagJacobian(unsigned int jvar)
compute the displacement Jacobian contributions from the secondary side degrees of freedom ...
virtual libMesh::DofMap & dofMap()
const MaterialProperty< Real > & _gap_conductance
const std::vector< std::vector< Real > > * _secondary_side_phi
The phi values on the secondary side.
const Real _min_gap
static MooseEnum getNonlinearVariableOrders()
const std::vector< dof_id_type > & dofIndices() const final
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)
SubProblem & _subproblem
T string_to_enum(const std::string &s)
virtual Real dgapLength(Real normalComponent) const
const Real _max_gap
FEProblemBase & _fe_problem
Generic gap heat transfer model, with h_gap = h_conduction + h_contact + h_radiation.
const Elem * _secondary_side
The secondary side element (this really is a side element, e.g.
virtual Real gapLength() const
std::vector< unsigned int > _disp_vars
unsigned int number() const
const Elem * _side
MooseVariableFE< Real > * _variable
const PenetrationInfo * _pinfo
The current PenetratationInfo.
void accumulateTaggedLocalMatrix()
const MooseArray< Real > & _coord
const std::string _name
NumericVector< Number > * _secondary_flux
Node * getQuadratureNode(const Elem *elem, const unsigned short int side, const unsigned int qp)
void computeOffDiagJacobian(unsigned int jvar) override
const bool _warnings
Assembly & _assembly
void addCoupledVar(const std::string &name, const std::string &doc_string)
PenetrationLocator * _penetration_locator
MooseVariable & _var
const VariableValue & _gap_temp_value
unsigned int coupledComponents(const std::string &var_name) const
const QBase *const & _qrule
Real _tangential_distance
const unsigned int & _current_side
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
GapHeatTransfer(const InputParameters &parameters)
virtual Real computeQpResidual() override
const VariableValue & _gap_distance_value
CoordinateSystemType
virtual Assembly & assembly(const THREAD_ID tid, const unsigned int sys_num)=0
registerMooseObject("HeatTransferApp", GapHeatTransfer)
void mooseError(Args &&... args) const
const InputParameters & _pars
void addClassDescription(const std::string &doc_string)
const unsigned int _min_gap_order
const InputParameters & parameters() const
Moose::CoordinateSystemType getCoordSystem(SubdomainID sid) const
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
libMesh::StoredRange< MooseMesh::const_bnd_elem_iterator, const BndElement *> * getBoundaryElementRange()
void prepareMatrixTag(Assembly &assembly, unsigned int ivar, unsigned int jvar)
bool _has_diag_save_in
virtual void add(const numeric_index_type i, const Number value)=0
processor_id_type processor_id() const
virtual Real computeQpOffDiagJacobian(unsigned jvar) override
static InputParameters validParams()
virtual void initialSetup() override
void ErrorVector unsigned int
const VariableValue & _u
const Elem & get(const ElemType type_in)
unsigned int _secondary_j
The secondary side shape index.
const MooseArray< Real > & _JxW
void scalingFactor(const std::vector< Real > &factor)
bool isParamValid(const std::string &name) const