https://mooseframework.inl.gov
ADInterfaceKernel.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 "ADInterfaceKernel.h"
11 
12 // MOOSE includes
13 #include "Assembly.h"
14 #include "MooseVariableFE.h"
15 #include "SystemBase.h"
16 #include "ADUtils.h"
17 
18 // libmesh includes
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 ADInterfaceKernelTempl 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(_var.adSln()),
47  _grad_u(_var.adGradSln()),
48  _ad_JxW(_assembly.adJxWFace()),
49  _ad_coord(_assembly.adCoordTransformation()),
50  _ad_q_point(_assembly.adQPoints()),
51  _phi(_assembly.phiFace(_var)),
52  _test(_var.phiFace()),
53  _grad_test(_var.adGradPhiFace()),
54  _neighbor_var(*getVarHelper<MooseVariableFE<T>>("neighbor_var", 0)),
55  _neighbor_value(_neighbor_var.adSlnNeighbor()),
56  _grad_neighbor_value(_neighbor_var.adGradSlnNeighbor()),
57  _phi_neighbor(_assembly.phiFaceNeighbor(_neighbor_var)),
58  _test_neighbor(_neighbor_var.phiFaceNeighbor()),
59  _grad_test_neighbor(_neighbor_var.gradPhiFaceNeighbor())
60 
61 {
63 
65 
66  if (!parameters.isParamValid("boundary"))
67  mooseError(
68  "In order to use an interface kernel, you must specify a boundary where it will live.");
69 }
70 
71 template <typename T>
72 void
74 {
75  bool is_elem;
76  if (type == Moose::Element)
77  is_elem = true;
78  else
79  is_elem = false;
80 
81  const ADTemplateVariableTestValue<T> & test_space = is_elem ? _test : _test_neighbor;
82 
83  if (is_elem)
84  prepareVectorTag(_assembly, _var.number());
85  else
86  prepareVectorTagNeighbor(_assembly, _neighbor_var.number());
87 
88  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
89  {
90  initQpResidual(type);
91  const auto jxw_p = _JxW[_qp] * _coord[_qp];
92  for (_i = 0; _i < test_space.size(); _i++)
93  _local_re(_i) += jxw_p * raw_value(computeQpResidual(type));
94  }
95 
96  accumulateTaggedLocalResidual();
97 }
98 
99 template <typename T>
100 void
102 {
103  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
104  // associated only with the lower-dimensional geometric entity that is the boundary between two
105  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
106  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
107  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
108  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
109  // to execute in the former case. In the future we should remove this and add some kind of "block"
110  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
111  // this return
112  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
113  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
114  return;
115 
116  precalculateResidual();
117 
118  // Compute the residual for this element
119  computeElemNeighResidual(Moose::Element);
120 
121  // Compute the residual for the neighbor
122  computeElemNeighResidual(Moose::Neighbor);
123 }
124 
125 template <typename T>
126 void
128 {
129  mooseAssert(type == Moose::ElementElement || type == Moose::NeighborNeighbor,
130  "With AD you should need one call per side");
131 
132  const ADTemplateVariableTestValue<T> & test_space =
133  (type == Moose::ElementElement || type == Moose::ElementNeighbor) ? _test : _test_neighbor;
134 
135  std::vector<ADReal> residuals(test_space.size(), 0);
136 
137  switch (type)
138  {
140  resid_type = Moose::Element;
141  break;
143  resid_type = Moose::Element;
144  break;
146  resid_type = Moose::Neighbor;
147  break;
149  resid_type = Moose::Neighbor;
150  break;
151  default:
152  mooseError("Unknown DGJacobianType ", type);
153  }
154 
155  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
156  {
157  initQpResidual(resid_type);
158  const auto jxw_c = _ad_JxW[_qp] * _ad_coord[_qp];
159  for (_i = 0; _i < test_space.size(); _i++)
160  residuals[_i] += jxw_c * computeQpResidual(resid_type);
161  }
162 
163  const bool element_var_is_var = (type == Moose::ElementElement || type == Moose::ElementNeighbor);
164  addJacobian(_assembly,
165  residuals,
166  element_var_is_var ? _var.dofIndices() : _neighbor_var.dofIndicesNeighbor(),
167  element_var_is_var ? _var.scalingFactor() : _neighbor_var.scalingFactor());
168 }
169 
170 template <typename T>
171 void
173 {
174  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
175  // associated only with the lower-dimensional geometric entity that is the boundary between two
176  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
177  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
178  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
179  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
180  // to execute in the former case. In the future we should remove this and add some kind of "block"
181  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
182  // this return
183  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
184  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
185  return;
186 
187  precalculateJacobian();
188 
189  computeElemNeighJacobian(Moose::ElementElement);
190  computeElemNeighJacobian(Moose::NeighborNeighbor);
191 }
192 
193 template <typename T>
194 void
196 {
197  mooseAssert(type == Moose::ElementElement || type == Moose::NeighborNeighbor,
198  "With AD you should need one call per side");
199 
200  const ADTemplateVariableTestValue<T> & test_space =
201  type == Moose::ElementElement ? _test : _test_neighbor;
202 
203  if (type == Moose::ElementElement)
204  resid_type = Moose::Element;
205  else
206  resid_type = Moose::Neighbor;
207 
208  std::vector<ADReal> residuals(test_space.size(), 0);
209 
210  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
211  {
212  initQpResidual(resid_type);
213  const auto jxw_c = _ad_JxW[_qp] * _ad_coord[_qp];
214  for (_i = 0; _i < test_space.size(); _i++)
215  residuals[_i] += jxw_c * computeQpResidual(resid_type);
216  }
217 
218  // We assert earlier that the type cannot be Moose::ElementNeighbor (nor Moose::NeighborElement)
219  addJacobian(_assembly,
220  residuals,
221  type == Moose::ElementElement ? _var.dofIndices()
222  : _neighbor_var.dofIndicesNeighbor(),
223  type == Moose::ElementElement ? _var.scalingFactor() : _neighbor_var.scalingFactor());
224 }
225 
226 template <typename T>
227 void
229 {
230  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
231  // associated only with the lower-dimensional geometric entity that is the boundary between two
232  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
233  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
234  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
235  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
236  // to execute in the former case. In the future we should remove this and add some kind of "block"
237  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
238  // this return
239  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
240  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
241  return;
242 
243  if (jvar != _var.number())
244  // We only need to do these computations a single time because AD computes all the derivatives
245  // at once
246  return;
247 
248  precalculateOffDiagJacobian(jvar);
249 
250  // Again AD does Jacobians all at once so we only need to call with ElementElement
251  computeOffDiagElemNeighJacobian(Moose::ElementElement, jvar);
252 }
253 
254 template <typename T>
255 void
257 {
258  // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
259  // associated only with the lower-dimensional geometric entity that is the boundary between two
260  // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
261  // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
262  // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
263  // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
264  // to execute in the former case. In the future we should remove this and add some kind of "block"
265  // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
266  // this return
267  if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
268  !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
269  return;
270 
271  if (jvar != _neighbor_var.number())
272  // We only need to do these computations a single time because AD computes all the derivatives
273  // at once
274  return;
275 
276  precalculateOffDiagJacobian(jvar);
277 
278  // Again AD does Jacobians all at once so we only need to call with NeighborNeighbor
279  computeOffDiagElemNeighJacobian(Moose::NeighborNeighbor, jvar);
280 }
281 
282 // Explicitly instantiates the two versions of the ADInterfaceKernelTempl class
283 template class ADInterfaceKernelTempl<Real>;
VarFieldType
Definition: MooseTypes.h:721
void computeOffDiagElemNeighJacobian(Moose::DGJacobianType type, unsigned int jvar)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
static InputParameters validParams()
virtual void haveADObjects(bool have_ad_objects)
Method for setting whether we have any ad objects.
Definition: SubProblem.h:767
Class for stuff related to variables.
Definition: Adaptivity.h:31
void computeElemNeighJacobian(Moose::DGJacobianType type)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
typename OutputTools< T >::VariableTestValue ADTemplateVariableTestValue
Definition: MooseTypes.h:623
static InputParameters validParams()
auto raw_value(const Eigen::Map< T > &in)
Definition: EigenADReal.h:73
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
void computeResidual() override final
Computes the residual for the current side.
DGResidualType
Definition: MooseTypes.h:743
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.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
SubProblem & _subproblem
Reference to this kernel&#39;s SubProblem.
VarKindType
Framework-wide stuff.
Definition: MooseTypes.h:714
ADInterfaceKernel and ADVectorInterfaceKernel is responsible for interfacing physics across subdomain...
virtual void computeNeighborOffDiagJacobian(unsigned int jvar) override final
Selects the correct Jacobian type and routine to call for the secondary variable jacobian.
virtual void computeElementOffDiagJacobian(unsigned int jvar) override final
Selects the correct Jacobian type and routine to call for the primary variable jacobian.
DGJacobianType
Definition: MooseTypes.h:749
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
void computeElemNeighResidual(Moose::DGResidualType type)
Using the passed DGResidual type, selects the correct test function space and residual block...
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
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...
void computeJacobian() override final
Computes the jacobian for the current side.
ADInterfaceKernelTempl(const InputParameters &parameters)
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.