https://mooseframework.inl.gov
InterfaceKernel.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 "InterfaceKernel.h"
11 
12 // MOOSE includes
13 #include "Assembly.h"
14 #include "MooseVariableFE.h"
15 #include "SystemBase.h"
16 #include "AuxiliarySystem.h"
17 #include "FEProblemBase.h"
18 
19 #include "libmesh/quadrature.h"
20 
21 template <typename T>
24 {
26  if (std::is_same<T, Real>::value)
27  params.registerBase("InterfaceKernel");
28  else if (std::is_same<T, RealVectorValue>::value)
29  params.registerBase("VectorInterfaceKernel");
30  else
31  ::mooseError("unsupported InterfaceKernelTempl specialization");
32  return params;
33 }
34 
35 template <typename T>
37  : InterfaceKernelBase(parameters),
39  false,
41  std::is_same<T, Real>::value
44  _var(*this->mooseVariable()),
45  _normals(_assembly.normals()),
46  _u(_is_implicit ? _var.sln() : _var.slnOld()),
47  _grad_u(_is_implicit ? _var.gradSln() : _var.gradSlnOld()),
48  _phi(_assembly.phiFace(_var)),
49  _grad_phi(_assembly.gradPhiFace(_var)),
50  _test(_var.phiFace()),
51  _grad_test(_var.gradPhiFace()),
52  _neighbor_var(*getVarHelper<MooseVariableFE<T>>("neighbor_var", 0)),
53  _neighbor_value(_is_implicit ? _neighbor_var.slnNeighbor() : _neighbor_var.slnOldNeighbor()),
54  _grad_neighbor_value(_neighbor_var.gradSlnNeighbor()),
55  _phi_neighbor(_assembly.phiFaceNeighbor(_neighbor_var)),
56  _grad_phi_neighbor(_assembly.gradPhiFaceNeighbor(_neighbor_var)),
57  _test_neighbor(_neighbor_var.phiFaceNeighbor()),
58  _grad_test_neighbor(_neighbor_var.gradPhiFaceNeighbor()),
59  _same_system(_var.sys().number() == _neighbor_var.sys().number())
60 {
61  // Neighbor variable dependency is added by
62  // NeighborCoupleableMooseVariableDependencyIntermediateInterface
64 
65  if (!parameters.isParamValid("boundary"))
66  mooseError(
67  "In order to use an interface kernel, you must specify a boundary where it will live.");
68 
69  if (parameters.isParamSetByUser("save_in"))
70  {
71  if (_save_in_strings.size() != _save_in_var_side.size())
72  mooseError("save_in and save_in_var_side must be the same length");
73  else
74  {
75  for (unsigned i = 0; i < _save_in_strings.size(); ++i)
76  {
78 
80  mooseError("Trying to use solution variable " + _save_in_strings[i] +
81  " as a save_in variable in " + name());
82 
83  if (_save_in_var_side[i] == "m")
84  {
85  if (var->feType() != _var.feType())
86  mooseError(
87  "Error in " + name() +
88  ". There is a mismatch between the fe_type of the save-in Auxiliary variable "
89  "and the fe_type of the the primary side nonlinear "
90  "variable this interface kernel object is acting on.");
92  }
93  else
94  {
95  if (var->feType() != _neighbor_var.feType())
96  mooseError(
97  "Error in " + name() +
98  ". There is a mismatch between the fe_type of the save-in Auxiliary variable "
99  "and the fe_type of the the secondary side nonlinear "
100  "variable this interface kernel object is acting on.");
102  }
103 
106  }
107  }
108  }
109 
112 
113  if (parameters.isParamSetByUser("diag_save_in"))
114  {
116  mooseError("diag_save_in and diag_save_in_var_side must be the same length");
117  else
118  {
119  for (unsigned i = 0; i < _diag_save_in_strings.size(); ++i)
120  {
122 
124  mooseError("Trying to use solution variable " + _diag_save_in_strings[i] +
125  " as a save_in variable in " + name());
126 
127  if (_diag_save_in_var_side[i] == "m")
128  {
129  if (var->feType() != _var.feType())
130  mooseError(
131  "Error in " + name() +
132  ". There is a mismatch between the fe_type of the save-in Auxiliary variable "
133  "and the fe_type of the the primary side nonlinear "
134  "variable this interface kernel object is acting on.");
136  }
137  else
138  {
139  if (var->feType() != _neighbor_var.feType())
140  mooseError(
141  "Error in " + name() +
142  ". There is a mismatch between the fe_type of the save-in Auxiliary variable "
143  "and the fe_type of the the secondary side nonlinear "
144  "variable this interface kernel object is acting on.");
146  }
147 
150  }
151  }
152  }
153 
156 }
157 
158 template <typename T>
159 void
161 {
162  bool is_elem;
163  if (type == Moose::Element)
164  is_elem = true;
165  else
166  is_elem = false;
167 
168  const TemplateVariableTestValue & test_space = is_elem ? _test : _test_neighbor;
169 
170  if (is_elem)
171  prepareVectorTag(_assembly, _var.number());
172  else
173  prepareVectorTagNeighbor(_assembly, _neighbor_var.number());
174 
175  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
176  {
177  initQpResidual(type);
178  for (_i = 0; _i < test_space.size(); _i++)
179  _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual(type);
180  }
181 
182  accumulateTaggedLocalResidual();
183 
184  // To save the diagonal of the Jacobian
185  if (_has_primary_residuals_saved_in && is_elem)
186  for (const auto & var : _primary_save_in_residual_variables)
187  var->sys().solution().add_vector(_local_re, var->dofIndices());
188  else if (_has_secondary_residuals_saved_in && !is_elem)
189  for (const auto & var : _secondary_save_in_residual_variables)
190  var->sys().solution().add_vector(_local_re, var->dofIndicesNeighbor());
191 }
192 
193 template <typename T>
194 void
196 {
197  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
198  // associated only with the lower-dimensional geometric entity that is the boundary between two
199  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
200  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
201  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
202  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
203  // to execute in the former case. In the future we should remove this and add some kind of "block"
204  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
205  // this return
206  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
207  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
208  return;
209 
210  precalculateResidual();
211 
212  // Compute the residual for this element
213  computeElemNeighResidual(Moose::Element);
214 
215  // Compute the residual for the neighbor
216  // This also prevents computing a residual if the neighbor variable is auxiliary
217  if (_same_system)
218  computeElemNeighResidual(Moose::Neighbor);
219 }
220 
221 template <typename T>
222 void
224 {
225  const TemplateVariableTestValue & test_space =
226  (type == Moose::ElementElement || type == Moose::ElementNeighbor) ? _test : _test_neighbor;
227  const TemplateVariableTestValue & loc_phi =
228  (type == Moose::ElementElement || type == Moose::NeighborElement) ? _phi : _phi_neighbor;
229 
230  unsigned int ivar, jvar;
231 
232  switch (type)
233  {
235  ivar = jvar = _var.number();
236  break;
238  ivar = _var.number(), jvar = _neighbor_var.number();
239  break;
241  ivar = _neighbor_var.number(), jvar = _var.number();
242  break;
244  ivar = _neighbor_var.number(), jvar = _neighbor_var.number();
245  break;
246  default:
247  mooseError("Unknown DGJacobianType ", type);
248  }
249 
250  if (type == Moose::ElementElement)
251  prepareMatrixTag(_assembly, ivar, jvar);
252  else
253  prepareMatrixTagNeighbor(_assembly, ivar, jvar, type);
254 
255  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
256  {
257  initQpJacobian(type);
258  for (_i = 0; _i < test_space.size(); _i++)
259  for (_j = 0; _j < loc_phi.size(); _j++)
260  _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJacobian(type);
261  }
262 
263  accumulateTaggedLocalMatrix();
264 
265  // To save the diagonal of the Jacobian
266  if (_has_primary_jacobians_saved_in && type == Moose::ElementElement)
267  {
268  auto rows = _local_ke.m();
269  DenseVector<Number> diag(rows);
270  for (decltype(rows) i = 0; i < rows; i++)
271  diag(i) = _local_ke(i, i);
272 
273  for (const auto & var : _primary_save_in_jacobian_variables)
274  var->sys().solution().add_vector(diag, var->dofIndices());
275  }
276  else if (_has_secondary_jacobians_saved_in && type == Moose::NeighborNeighbor)
277  {
278  auto rows = _local_ke.m();
279  DenseVector<Number> diag(rows);
280  for (decltype(rows) i = 0; i < rows; i++)
281  diag(i) = _local_ke(i, i);
282 
283  for (const auto & var : _secondary_save_in_jacobian_variables)
284  var->sys().solution().add_vector(diag, var->dofIndicesNeighbor());
285  }
286 }
287 
288 template <typename T>
289 void
291 {
292  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
293  // associated only with the lower-dimensional geometric entity that is the boundary between two
294  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
295  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
296  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
297  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
298  // to execute in the former case. In the future we should remove this and add some kind of "block"
299  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
300  // this return
301  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
302  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
303  return;
304 
305  precalculateJacobian();
306 
307  computeElemNeighJacobian(Moose::ElementElement);
308  if (_same_system)
309  computeElemNeighJacobian(Moose::NeighborNeighbor);
310 }
311 
312 template <typename T>
313 void
315  unsigned int jvar)
316 {
317  const TemplateVariableTestValue & test_space =
318  (type == Moose::ElementElement || type == Moose::ElementNeighbor) ? _test : _test_neighbor;
319  const TemplateVariableTestValue & loc_phi =
320  (type == Moose::ElementElement || type == Moose::NeighborElement) ? _phi : _phi_neighbor;
321 
322  unsigned int ivar;
323 
324  if (type == Moose::ElementElement || type == Moose::ElementNeighbor)
325  ivar = _var.number();
326  else
327  ivar = _neighbor_var.number();
328 
329  if (type == Moose::ElementElement)
330  prepareMatrixTag(_assembly, ivar, jvar);
331  else
332  prepareMatrixTagNeighbor(_assembly, ivar, jvar, type);
333 
334  // Prevent calling of Jacobian computation if jvar doesn't lie in the current block
335  if ((_local_ke.m() == test_space.size()) && (_local_ke.n() == loc_phi.size()))
336  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
337  {
338  initQpOffDiagJacobian(type, jvar);
339  for (_i = 0; _i < test_space.size(); _i++)
340  for (_j = 0; _j < loc_phi.size(); _j++)
341  _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpOffDiagJacobian(type, jvar);
342  }
343 
344  accumulateTaggedLocalMatrix();
345 }
346 
347 template <typename T>
348 void
350 {
351  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
352  // associated only with the lower-dimensional geometric entity that is the boundary between two
353  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
354  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
355  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
356  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
357  // to execute in the former case. In the future we should remove this and add some kind of "block"
358  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
359  // this return
360  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
361  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
362  return;
363 
364  bool is_jvar_not_interface_var = true;
365  if (jvar == _var.number())
366  {
367  precalculateJacobian();
368  computeElemNeighJacobian(Moose::ElementElement);
369  is_jvar_not_interface_var = false;
370  }
371  if (jvar == _neighbor_var.number() && _same_system)
372  {
373  precalculateJacobian();
374  computeElemNeighJacobian(Moose::ElementNeighbor);
375  is_jvar_not_interface_var = false;
376  }
377 
378  if (is_jvar_not_interface_var)
379  {
380  precalculateOffDiagJacobian(jvar);
381  computeOffDiagElemNeighJacobian(Moose::ElementElement, jvar);
382  computeOffDiagElemNeighJacobian(Moose::ElementNeighbor, jvar);
383  }
384 }
385 
386 template <typename T>
387 void
389 {
390  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
391  // associated only with the lower-dimensional geometric entity that is the boundary between two
392  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
393  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
394  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
395  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
396  // to execute in the former case. In the future we should remove this and add some kind of "block"
397  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
398  // this return
399  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
400  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
401  return;
402 
403  // We don't care about any contribution to the neighbor Jacobian rows if it's not in the system
404  // we are currently working with (the variable's system)
405  if (!_same_system)
406  return;
407 
408  bool is_jvar_not_interface_var = true;
409  if (jvar == _var.number())
410  {
411  precalculateJacobian();
412  computeElemNeighJacobian(Moose::NeighborElement);
413  is_jvar_not_interface_var = false;
414  }
415  if (jvar == _neighbor_var.number())
416  {
417  precalculateJacobian();
418  computeElemNeighJacobian(Moose::NeighborNeighbor);
419  is_jvar_not_interface_var = false;
420  }
421 
422  if (is_jvar_not_interface_var)
423  {
424  precalculateOffDiagJacobian(jvar);
425  computeOffDiagElemNeighJacobian(Moose::NeighborElement, jvar);
426  computeOffDiagElemNeighJacobian(Moose::NeighborNeighbor, jvar);
427  }
428 }
429 
430 template <typename T>
431 void
433 {
434  computeResidual();
435 
436  if (!isImplicit())
437  return;
438 
439  for (const auto & [ivariable, jvariable] : _fe_problem.couplingEntries(_tid, _sys.number()))
440  {
441  if (ivariable->isFV())
442  continue;
443 
444  const unsigned int ivar = ivariable->number();
445  const unsigned int jvar = jvariable->number();
446 
447  prepareShapes(jvar);
448  prepareNeighborShapes(jvar);
449 
450  if (_var.number() == ivar)
451  computeElementOffDiagJacobian(jvar);
452 
453  if (_same_system)
454  if (_neighbor_var.number() == ivar)
455  computeNeighborOffDiagJacobian(jvar);
456  }
457 }
458 
459 // Explicitly instantiates the two versions of the InterfaceKernelTempl class
460 template class InterfaceKernelTempl<Real>;
VarFieldType
Definition: MooseTypes.h:770
const libMesh::FEType & feType() const
Get the type of finite element object.
std::vector< MooseVariableFEBase * > _secondary_save_in_residual_variables
The aux variables to save the secondary contributions to.
static InputParameters validParams()
MultiMooseEnum _diag_save_in_var_side
MultiMooseEnum specifying whether jacobian save-in aux variables correspond to primary or secondary s...
Class for stuff related to variables.
Definition: Adaptivity.h:33
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
InterfaceKernel and VectorInterfaceKernel is responsible for interfacing physics across subdomains...
InterfaceKernelTempl(const InputParameters &parameters)
const InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
unsigned int size() const
Return the number of active items in the MultiMooseEnum.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::vector< AuxVariableName > _diag_save_in_strings
The names of the aux variables that will be used to save-in jacobians (includes both primary and seco...
DGResidualType
Definition: MooseTypes.h:792
THREAD_ID _tid
The thread ID for this kernel.
MultiMooseEnum _save_in_var_side
MultiMooseEnum specifying whether residual save-in aux variables correspond to primary or secondary s...
bool _has_primary_jacobians_saved_in
Whether there are primary jacobian aux variables.
SystemBase & _sys
Reference to the EquationSystem object.
void registerBase(const std::string &value)
This method must be called from every base "Moose System" to create linkage with the Action System...
MooseVariableFE< T > * mooseVariable() const
Return the MooseVariableFE object that this interface acts on.
Enhances MooseVariableInterface interface provide values from neighbor elements.
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
const MooseVariableFE< T > & _neighbor_var
Coupled neighbor variable.
virtual void computeElementOffDiagJacobian(unsigned int jvar) override
Selects the correct Jacobian type and routine to call for the primary variable jacobian.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
virtual void computeElemNeighResidual(Moose::DGResidualType type)
Using the passed DGResidual type, selects the correct test function space and residual block...
virtual void computeResidualAndJacobian() override
Computes the residual and Jacobian for the current side.
std::vector< MooseVariableFEBase * > _primary_save_in_jacobian_variables
The aux variables to save the diagonal Jacobian contributions of the primary variables to...
SubProblem & _subproblem
Reference to this kernel&#39;s SubProblem.
VarKindType
Framework-wide stuff.
Definition: MooseTypes.h:763
virtual void computeOffDiagElemNeighJacobian(Moose::DGJacobianType type, unsigned int jvar)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
virtual void computeJacobian() override
Computes the jacobian for the current side.
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name)=0
Returns the variable reference for requested MooseVariable which may be in any system.
std::vector< MooseVariableFEBase * > _primary_save_in_residual_variables
The aux variables to save the primary residual contributions to.
virtual bool hasVariable(const std::string &var_name) const
Query a system for a variable.
Definition: SystemBase.C:852
DGJacobianType
Definition: MooseTypes.h:798
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
virtual void computeElemNeighJacobian(Moose::DGJacobianType type)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
void addMooseVariableDependency(MooseVariableFieldBase *var)
Call this function to add the passed in MooseVariableFieldBase as a variable that this object depends...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void addVariableToZeroOnResidual(std::string var_name)
Adds this variable to the list of variables to be zeroed during each residual evaluation.
Definition: SystemBase.C:175
std::vector< MooseVariableFEBase * > _secondary_save_in_jacobian_variables
The aux variables to save the diagonal Jacobian contributions of the secondary variables to...
bool _has_secondary_residuals_saved_in
Whether there are secondary residual aux variables.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:281
std::vector< AuxVariableName > _save_in_strings
The names of the aux variables that will be used to save-in residuals (includes both primary and seco...
bool _has_primary_residuals_saved_in
Whether there are primary residual aux variables.
virtual void addVariableToZeroOnJacobian(std::string var_name)
Adds this variable to the list of variables to be zeroed during each Jacobian evaluation.
Definition: SystemBase.C:181
InterfaceKernelBase is the base class for all InterfaceKernel type classes.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
bool _has_secondary_jacobians_saved_in
Whether there are secondary jacobian aux variables.
MooseVariableFE< T > & _var
The primary side MooseVariable.
SystemBase & sys()
Get the system this variable is part of.
virtual void computeResidual() override
Computes the residual for the current side.
static InputParameters validParams()
virtual void computeNeighborOffDiagJacobian(unsigned int jvar) override
Selects the correct Jacobian type and routine to call for the secondary variable jacobian.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.