https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
21template <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
35template <typename T>
37 : InterfaceKernelBase(parameters),
39 false,
40 Moose::VarKindType::VAR_SOLVER,
41 std::is_same<T, Real>::value
42 ? Moose::VarFieldType::VAR_FIELD_STANDARD
43 : Moose::VarFieldType::VAR_FIELD_VECTOR),
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 _same_system(_var.sys().number() == _neighbor_var.sys().number())
61{
63
65
66 if (!parameters.isParamValid("boundary"))
68 "In order to use an interface kernel, you must specify a boundary where it will live.");
69}
70
71template <typename T>
72void
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
99template <typename T>
100void
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 if (_same_system)
123 computeElemNeighResidual(Moose::Neighbor);
124}
125
126template <typename T>
127void
129{
130 mooseAssert(type == Moose::ElementElement || type == Moose::NeighborNeighbor,
131 "With AD you should need one call per side");
132
133 const ADTemplateVariableTestValue<T> & test_space =
134 (type == Moose::ElementElement || type == Moose::ElementNeighbor) ? _test : _test_neighbor;
135
136 std::vector<ADReal> residuals(test_space.size(), 0);
137
138 switch (type)
139 {
141 resid_type = Moose::Element;
142 break;
144 resid_type = Moose::Element;
145 break;
147 resid_type = Moose::Neighbor;
148 break;
150 resid_type = Moose::Neighbor;
151 break;
152 default:
153 mooseError("Unknown DGJacobianType ", type);
154 }
155
156 for (_qp = 0; _qp < _qrule->n_points(); _qp++)
157 {
158 initQpResidual(resid_type);
159 const auto jxw_c = _ad_JxW[_qp] * _ad_coord[_qp];
160 for (_i = 0; _i < test_space.size(); _i++)
161 residuals[_i] += jxw_c * computeQpResidual(resid_type);
162 }
163
164 const bool element_var_is_var = (type == Moose::ElementElement || type == Moose::ElementNeighbor);
165 addJacobian(_assembly,
166 residuals,
167 element_var_is_var ? _var.dofIndices() : _neighbor_var.dofIndicesNeighbor(),
168 element_var_is_var ? _var.scalingFactor() : _neighbor_var.scalingFactor());
169}
170
171template <typename T>
172void
174{
175 // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
176 // associated only with the lower-dimensional geometric entity that is the boundary between two
177 // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
178 // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
179 // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
180 // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
181 // to execute in the former case. In the future we should remove this and add some kind of "block"
182 // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
183 // this return
184 if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
185 !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
186 return;
187
188 precalculateJacobian();
189
190 computeElemNeighJacobian(Moose::ElementElement);
191 if (_same_system)
192 computeElemNeighJacobian(Moose::NeighborNeighbor);
193}
194
195template <typename T>
196void
198{
199 mooseAssert(type == Moose::ElementElement || type == Moose::NeighborNeighbor,
200 "With AD you should need one call per side");
201
202 const ADTemplateVariableTestValue<T> & test_space =
203 type == Moose::ElementElement ? _test : _test_neighbor;
204
205 if (type == Moose::ElementElement)
206 resid_type = Moose::Element;
207 else
208 resid_type = Moose::Neighbor;
209
210 std::vector<ADReal> residuals(test_space.size(), 0);
211
212 for (_qp = 0; _qp < _qrule->n_points(); _qp++)
213 {
214 initQpResidual(resid_type);
215 const auto jxw_c = _ad_JxW[_qp] * _ad_coord[_qp];
216 for (_i = 0; _i < test_space.size(); _i++)
217 residuals[_i] += jxw_c * computeQpResidual(resid_type);
218 }
219
220 // We assert earlier that the type cannot be Moose::ElementNeighbor (nor Moose::NeighborElement)
221 addJacobian(_assembly,
222 residuals,
223 type == Moose::ElementElement ? _var.dofIndices()
224 : _neighbor_var.dofIndicesNeighbor(),
225 type == Moose::ElementElement ? _var.scalingFactor() : _neighbor_var.scalingFactor());
226}
227
228template <typename T>
229void
231{
232 // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
233 // associated only with the lower-dimensional geometric entity that is the boundary between two
234 // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
235 // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
236 // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
237 // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
238 // to execute in the former case. In the future we should remove this and add some kind of "block"
239 // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
240 // this return
241 if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
242 !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
243 return;
244
245 if (jvar != _var.number())
246 // We only need to do these computations a single time because AD computes all the derivatives
247 // at once
248 return;
249
250 precalculateOffDiagJacobian(jvar);
251
252 // Again AD does Jacobians all at once so we only need to call with ElementElement
253 computeOffDiagElemNeighJacobian(Moose::ElementElement, jvar);
254}
255
256template <typename T>
257void
259{
260 // in the gmsh mesh format (at least in the version 2 format) the "sideset" physical entities are
261 // associated only with the lower-dimensional geometric entity that is the boundary between two
262 // higher-dimensional element faces. It does not have a sidedness to it like the exodus format
263 // does. Consequently we may naively try to execute an interface kernel twice, one time where _var
264 // has dofs on _current_elem *AND* _neighbor_var has dofs on _neighbor_elem, and the other time
265 // where _var has dofs on _neighbor_elem and _neighbor_var has dofs on _current_elem. We only want
266 // to execute in the former case. In the future we should remove this and add some kind of "block"
267 // awareness to interface kernels to avoid all the unnecessary reinit that happens before we hit
268 // this return
269 if (!_var.activeOnSubdomain(_current_elem->subdomain_id()) ||
270 !_neighbor_var.activeOnSubdomain(_neighbor_elem->subdomain_id()))
271 return;
272
273 // We don't care about any contribution to the neighbor Jacobian rows if it's not in the system
274 // we are currently working with (the variable's system)
275 if (!_same_system)
276 return;
277
278 if (jvar != _neighbor_var.number())
279 // We only need to do these computations a single time because AD computes all the derivatives
280 // at once
281 return;
282
283 precalculateOffDiagJacobian(jvar);
284
285 // Again AD does Jacobians all at once so we only need to call with NeighborNeighbor
286 computeOffDiagElemNeighJacobian(Moose::NeighborNeighbor, jvar);
287}
288
289// Explicitly instantiates the two versions of the ADInterfaceKernelTempl class
290template class ADInterfaceKernelTempl<Real>;
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
typename OutputTools< T >::VariableTestValue ADTemplateVariableTestValue
Definition MooseTypes.h:673
ADInterfaceKernel and ADVectorInterfaceKernel is responsible for interfacing physics across subdomain...
void computeOffDiagElemNeighJacobian(Moose::DGJacobianType type, unsigned int jvar)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
void computeElemNeighResidual(Moose::DGResidualType type)
Using the passed DGResidual type, selects the correct test function space and residual block,...
virtual void computeNeighborOffDiagJacobian(unsigned int jvar) override final
Selects the correct Jacobian type and routine to call for the secondary variable jacobian.
void computeJacobian() override final
Computes the jacobian for the current side.
void computeResidual() override final
Computes the residual for the current side.
void computeElemNeighJacobian(Moose::DGJacobianType type)
Using the passed DGJacobian type, selects the correct test function and trial function spaces and jac...
ADInterfaceKernelTempl(const InputParameters &parameters)
static InputParameters validParams()
virtual void computeElementOffDiagJacobian(unsigned int jvar) override final
Selects the correct Jacobian type and routine to call for the primary variable jacobian.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void registerBase(const std::string &value)
This method must be called from every base "Moose System" to create linkage with the Action System.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
InterfaceKernelBase is the base class for all InterfaceKernel type classes.
static InputParameters validParams()
void addMooseVariableDependency(MooseVariableFieldBase *var)
Call this function to add the passed in MooseVariableFieldBase as a variable that this object depends...
Class for stuff related to variables.
MooseVariableFE< T > * mooseVariable() const
Return the MooseVariableFE object that this interface acts on.
Enhances MooseVariableInterface interface provide values from neighbor elements.
SubProblem & _subproblem
Reference to this kernel's SubProblem.
virtual void haveADObjects(bool have_ad_objects)
Method for setting whether we have any ad objects.
Definition SubProblem.h:775
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
DGResidualType
Definition MooseTypes.h:798
@ Element
Definition MooseTypes.h:799
@ Neighbor
Definition MooseTypes.h:800
DGJacobianType
Definition MooseTypes.h:804
@ NeighborNeighbor
Definition MooseTypes.h:808
@ ElementElement
Definition MooseTypes.h:805
@ NeighborElement
Definition MooseTypes.h:807
@ ElementNeighbor
Definition MooseTypes.h:806