21 template <
int p,
int c>
22 struct finite_element<mfem::Geometry::TETRAHEDRON, L2<p, c> > {
23 static constexpr
auto geometry = mfem::Geometry::TETRAHEDRON;
24 static constexpr
auto family = Family::L2;
25 static constexpr
int components = c;
26 static constexpr
int dim = 3;
27 static constexpr
int n = (p + 1);
28 static constexpr
int ndof = (p + 1) * (p + 2) * (p + 3) / 6;
31 static constexpr
int VALUE = 0, GRADIENT = 1;
32 static constexpr
int SOURCE = 0, FLUX = 1;
35 typename std::conditional<components == 1, tensor<double, ndof>, tensor<double, ndof, components> >
::type;
37 using dof_type = tensor<double, c, ndof>;
39 using value_type =
typename std::conditional<components == 1, double, tensor<double, components> >
::type;
40 using derivative_type =
41 typename std::conditional<components == 1, tensor<double, dim>, tensor<double, components, dim> >
::type;
42 using qf_input_type = tuple<value_type, derivative_type>;
44 SERAC_HOST_DEVICE static constexpr
double shape_function([[maybe_unused]] tensor<double, dim> xi,
45 [[maybe_unused]]
int i)
47 if constexpr (p == 0) {
51 if constexpr (p == 1) {
54 return 1 - xi[0] - xi[1] - xi[2];
63 if constexpr (p == 2) {
66 return (-1 + xi[0] + xi[1] + xi[2]) * (-1 + 2 * xi[0] + 2 * xi[1] + 2 * xi[2]);
68 return -4 * xi[0] * (-1 + xi[0] + xi[1] + xi[2]);
70 return xi[0] * (-1 + 2 * xi[0]);
72 return -4 * xi[1] * (-1 + xi[0] + xi[1] + xi[2]);
74 return 4 * xi[0] * xi[1];
76 return xi[1] * (-1 + 2 * xi[1]);
78 return -4 * xi[2] * (-1 + xi[0] + xi[1] + xi[2]);
80 return 4 * xi[0] * xi[2];
82 return 4 * xi[1] * xi[2];
84 return xi[2] * (-1 + 2 * xi[2]);
88 if constexpr (p == 3) {
89 constexpr
double sqrt5 = 2.23606797749978981;
92 return -((-1 + xi[0] + xi[1] + xi[2]) *
93 (1 + 5 * xi[0] * xi[0] + 5 * xi[1] * xi[1] + 5 * (-1 + xi[2]) * xi[2] + xi[1] * (-5 + 11 * xi[2]) +
94 xi[0] * (-5 + 11 * xi[1] + 11 * xi[2])));
96 return (5 * xi[0] * (-1 + xi[0] + xi[1] + xi[2]) *
97 (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1] + (3 + sqrt5) * xi[2])) /
100 return (-5 * xi[0] * (-1 + xi[0] + xi[1] + xi[2]) *
101 (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1] + (-3 + sqrt5) * xi[2])) /
104 return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] + xi[2] - xi[1] * xi[2] - xi[2] * xi[2] -
105 xi[0] * (5 + xi[1] + xi[2]));
107 return (5 * xi[1] * (-1 + xi[0] + xi[1] + xi[2]) *
108 (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1] + (3 + sqrt5) * xi[2])) /
111 return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1] + xi[2]);
113 return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.;
115 return (-5 * xi[1] * (-1 + xi[0] + xi[1] + xi[2]) *
116 (1 - sqrt5 + (-3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1] + (-3 + sqrt5) * xi[2])) /
119 return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.;
121 return xi[1] * (1 - xi[0] * xi[0] + 5 * xi[1] * xi[1] + xi[2] - xi[2] * xi[2] - xi[1] * (5 + xi[2]) -
122 xi[0] * (-1 + xi[1] + xi[2]));
124 return (5 * xi[2] * (-1 + xi[0] + xi[1] + xi[2]) *
125 (-439204 - 196418 * sqrt5 + (710647 + 317811 * sqrt5) * xi[0] + (710647 + 317811 * sqrt5) * xi[1] +
126 606965 * xi[2] + 271443 * sqrt5 * xi[2])) /
127 (271443 + 121393 * sqrt5);
129 return -27 * xi[0] * xi[2] * (-1 + xi[0] + xi[1] + xi[2]);
131 return (5 * xi[0] * xi[2] * (-5 - 3 * sqrt5 + (15 + 7 * sqrt5) * xi[0] + 2 * sqrt5 * xi[2])) /
134 return -27 * xi[1] * xi[2] * (-1 + xi[0] + xi[1] + xi[2]);
136 return 27 * xi[0] * xi[1] * xi[2];
138 return (5 * xi[1] * xi[2] * (-5 - 3 * sqrt5 + (15 + 7 * sqrt5) * xi[1] + 2 * sqrt5 * xi[2])) /
141 return (5 * xi[2] * (-1 + xi[0] + xi[1] + xi[2]) *
142 (88555 + 39603 * sqrt5 + (54730 + 24476 * sqrt5) * xi[0] + (54730 + 24476 * sqrt5) * xi[1] -
143 5 * (64079 + 28657 * sqrt5) * xi[2])) /
144 (143285 + 64079 * sqrt5);
146 return (-5 * xi[0] * xi[2] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[2])) / 2.;
148 return (-5 * xi[1] * xi[2] * (2 + (-3 + sqrt5) * xi[1] - (3 + sqrt5) * xi[2])) / 2.;
150 return -(xi[2] * (-1 + xi[0] * xi[0] + xi[1] * xi[1] + xi[1] * (-1 + xi[2]) - 5 * (-1 + xi[2]) * xi[2] +
151 xi[0] * (-1 + xi[1] + xi[2])));
158 SERAC_HOST_DEVICE static constexpr tensor<double, dim> shape_function_gradient(tensor<double, dim> xi,
int i)
161 return {0.0, 0.0, 0.0};
178 return {-3 + 4 * xi[0] + 4 * xi[1] + 4 * xi[2], -3 + 4 * xi[0] + 4 * xi[1] + 4 * xi[2],
179 -3 + 4 * xi[0] + 4 * xi[1] + 4 * xi[2]};
181 return {-4 * (-1 + 2 * xi[0] + xi[1] + xi[2]), -4 * xi[0], -4 * xi[0]};
183 return {-1 + 4 * xi[0], 0, 0};
185 return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1] + xi[2]), -4 * xi[1]};
187 return {4 * xi[1], 4 * xi[0], 0};
189 return {0, -1 + 4 * xi[1], 0};
191 return {-4 * xi[2], -4 * xi[2], -4 * (-1 + xi[0] + xi[1] + 2 * xi[2])};
193 return {4 * xi[2], 0, 4 * xi[0]};
195 return {0, 4 * xi[2], 4 * xi[1]};
197 return {0, 0, -1 + 4 * xi[2]};
202 constexpr
double sqrt5 = 2.23606797749978981;
205 return {-6 - 15 * xi[0] * xi[0] - 16 * xi[1] * xi[1] + xi[1] * (21 - 33 * xi[2]) + (21 - 16 * xi[2]) * xi[2] -
206 4 * xi[0] * (-5 + 8 * xi[1] + 8 * xi[2]),
207 -6 - 16 * xi[0] * xi[0] + 20 * xi[1] + xi[0] * (21 - 32 * xi[1] - 33 * xi[2]) + 21 * xi[2] -
208 (3 * xi[1] + 4 * xi[2]) * (5 * xi[1] + 4 * xi[2]),
209 -6 - 16 * xi[0] * xi[0] + 21 * xi[1] + xi[0] * (21 - 33 * xi[1] - 32 * xi[2]) + 20 * xi[2] -
210 (4 * xi[1] + 3 * xi[2]) * (4 * xi[1] + 5 * xi[2])};
212 return {(5 * (6 * sqrt5 * xi[0] * xi[0] +
213 xi[0] * (-2 - 6 * sqrt5 + 6 * (1 + sqrt5) * xi[1] + 6 * (1 + sqrt5) * xi[2]) +
214 (-1 + xi[1] + xi[2]) * (-1 - sqrt5 + (3 + sqrt5) * xi[1] + (3 + sqrt5) * xi[2]))) /
217 (-4 - 2 * sqrt5 + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1] + 2 * (3 + sqrt5) * xi[2])) /
220 (-4 - 2 * sqrt5 + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1] + 2 * (3 + sqrt5) * xi[2])) /
223 return {-15 * sqrt5 * xi[0] * xi[0] -
224 (5 * (-1 + xi[1] + xi[2]) * (1 - sqrt5 + (-3 + sqrt5) * xi[1] + (-3 + sqrt5) * xi[2])) / 2. -
225 5 * xi[0] * (1 - 3 * sqrt5 + 3 * (-1 + sqrt5) * xi[1] + 3 * (-1 + sqrt5) * xi[2]),
227 (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1] + 2 * (-3 + sqrt5) * xi[2])) /
230 (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1] + 2 * (-3 + sqrt5) * xi[2])) /
233 return {1 + 15 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] + xi[2] - xi[1] * xi[2] - xi[2] * xi[2] -
234 2 * xi[0] * (5 + xi[1] + xi[2]),
235 -(xi[0] * (-1 + xi[0] + 2 * xi[1] + xi[2])), -(xi[0] * (-1 + xi[0] + xi[1] + 2 * xi[2]))};
238 (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1] + 2 * (3 + sqrt5) * xi[2])) /
240 15 * sqrt5 * xi[1] * xi[1] +
241 5 * xi[1] * (-1 - 3 * sqrt5 + 3 * (1 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[2]) +
242 (5 * (-1 + xi[0] + xi[2]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[2])) / 2.,
244 (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1] + 2 * (3 + sqrt5) * xi[2])) /
247 return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1] + xi[2]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1] + xi[2]),
248 -27 * xi[0] * xi[1]};
250 return {(-5 * xi[1] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[1])) / 2.,
251 (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[1])) / 2., 0};
253 return {(-5 * xi[1] *
254 (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1] + 2 * (-3 + sqrt5) * xi[2])) /
256 -15 * sqrt5 * xi[1] * xi[1] -
257 (5 * (-1 + xi[0] + xi[2]) * (1 - sqrt5 + (-3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[2])) / 2. -
258 5 * xi[1] * (1 - 3 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[2]),
260 (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1] + 2 * (-3 + sqrt5) * xi[2])) /
263 return {(5 * xi[1] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[1])) / 2.,
264 (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[1])) / 2., 0};
266 return {-(xi[1] * (-1 + 2 * xi[0] + xi[1] + xi[2])),
267 1 - xi[0] * xi[0] + 15 * xi[1] * xi[1] + xi[2] - xi[2] * xi[2] - 2 * xi[1] * (5 + xi[2]) -
268 xi[0] * (-1 + 2 * xi[1] + xi[2]),
269 -(xi[1] * (-1 + xi[0] + xi[1] + 2 * xi[2]))};
273 (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1] + 3 * (1 + sqrt5) * xi[2])) /
276 (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1] + 3 * (1 + sqrt5) * xi[2])) /
278 (5 * (1 + sqrt5 + (3 + sqrt5) * xi[0] * xi[0] - 2 * (2 + sqrt5) * xi[1] + (3 + sqrt5) * xi[1] * xi[1] +
279 6 * (1 + sqrt5) * xi[1] * xi[2] + 2 * xi[2] * (-1 - 3 * sqrt5 + 3 * sqrt5 * xi[2]) +
280 2 * xi[0] * (-2 - sqrt5 + (3 + sqrt5) * xi[1] + 3 * (1 + sqrt5) * xi[2]))) /
283 return {-27 * xi[2] * (-1 + 2 * xi[0] + xi[1] + xi[2]), -27 * xi[0] * xi[2],
284 -27 * xi[0] * (-1 + xi[0] + xi[1] + 2 * xi[2])};
286 return {(-5 * xi[2] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[2])) / 2., 0,
287 (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[2])) / 2.};
289 return {-27 * xi[1] * xi[2], -27 * xi[2] * (-1 + xi[0] + 2 * xi[1] + xi[2]),
290 -27 * xi[1] * (-1 + xi[0] + xi[1] + 2 * xi[2])};
292 return {27 * xi[1] * xi[2], 27 * xi[0] * xi[2], 27 * xi[0] * xi[1]};
294 return {0, (-5 * xi[2] * (2 - 2 * (3 + sqrt5) * xi[1] + (-3 + sqrt5) * xi[2])) / 2.,
295 (5 * xi[1] * (-2 + (3 + sqrt5) * xi[1] - 2 * (-3 + sqrt5) * xi[2])) / 2.};
299 (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1] + 3 * (-1 + sqrt5) * xi[2])) /
302 (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1] + 3 * (-1 + sqrt5) * xi[2])) /
304 (-5 * (-3 + sqrt5) * xi[0] * xi[0]) / 2. -
305 5 * xi[0] * (2 - sqrt5 + (-3 + sqrt5) * xi[1] + 3 * (-1 + sqrt5) * xi[2]) -
306 (5 * (-1 + sqrt5 + (-3 + sqrt5) * xi[1] * xi[1] + 2 * xi[2] * (1 - 3 * sqrt5 + 3 * sqrt5 * xi[2]) +
307 xi[1] * (4 - 2 * sqrt5 + 6 * (-1 + sqrt5) * xi[2]))) /
310 return {(5 * xi[2] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[2])) / 2., 0,
311 (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[2])) / 2.};
313 return {0, (5 * xi[2] * (-2 - 2 * (-3 + sqrt5) * xi[1] + (3 + sqrt5) * xi[2])) / 2.,
314 (-5 * xi[1] * (2 + (-3 + sqrt5) * xi[1] - 2 * (3 + sqrt5) * xi[2])) / 2.};
316 return {-(xi[2] * (-1 + 2 * xi[0] + xi[1] + xi[2])), -(xi[2] * (-1 + xi[0] + 2 * xi[1] + xi[2])),
317 1 + xi[0] - xi[0] * xi[0] + xi[1] - xi[0] * xi[1] - xi[1] * xi[1] - 2 * (5 + xi[0] + xi[1]) * xi[2] +
325 SERAC_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
327 tensor<double, ndof> output{};
328 for (
int i = 0; i < ndof; i++) {
329 output[i] = shape_function(xi, i);
334 SERAC_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
336 tensor<double, ndof, dim> output{};
337 for (
int i = 0; i < ndof; i++) {
338 output[i] = shape_function_gradient(xi, i);
343 template <
typename in_t,
int q>
344 static auto batch_apply_shape_fn(
int j,
tensor<in_t, nqpts(q)> input,
const TensorProductQuadratureRule<q>&)
346 using source_t = decltype(get<0>(get<0>(in_t{})) +
dot(get<1>(get<0>(in_t{})), tensor<double, dim>{}));
347 using flux_t = decltype(get<0>(get<1>(in_t{})) +
dot(get<1>(get<1>(in_t{})), tensor<double, dim>{}));
349 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TETRAHEDRON>();
351 tensor<tuple<source_t, flux_t>, nqpts(q)> output;
353 for (
int i = 0; i < nqpts(q); i++) {
354 double phi_j = shape_function(xi[i], j);
355 tensor<double, dim> dphi_j_dxi = shape_function_gradient(xi[i], j);
357 const auto& d00 = get<0>(get<0>(input(i)));
358 const auto& d01 = get<1>(get<0>(input(i)));
359 const auto& d10 = get<0>(get<1>(input(i)));
360 const auto& d11 = get<1>(get<1>(input(i)));
362 output[i] = {d00 * phi_j +
dot(d01, dphi_j_dxi), d10 * phi_j +
dot(d11, dphi_j_dxi)};
369 SERAC_HOST_DEVICE static auto interpolate(
const tensor<double, c, ndof>& X,
const TensorProductQuadratureRule<q>&)
371 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TETRAHEDRON>();
375 tensor<tuple<tensor<double, c>, tensor<double, c, dim> >, nqpts(q)> unflattened;
376 tensor<qf_input_type, nqpts(q)> flattened;
379 for (
int i = 0; i < c; i++) {
380 for (
int j = 0; j < nqpts(q); j++) {
381 for (
int k = 0; k < ndof; k++) {
382 get<VALUE>(output.unflattened[j])[i] += X(i, k) * shape_function(xi[j], k);
383 get<GRADIENT>(output.unflattened[j])[i] += X(i, k) * shape_function_gradient(xi[j], k);
388 return output.flattened;
391 template <
typename source_type,
typename flux_type,
int q>
393 const TensorProductQuadratureRule<q>&,
394 tensor<double, c, ndof>* element_residual,
int step = 1)
396 if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
400 using source_component_type = std::conditional_t<is_zero<source_type>{}, zero,
double>;
401 using flux_component_type = std::conditional_t<is_zero<flux_type>{}, zero, tensor<double, dim> >;
403 constexpr
int ntrial =
std::max(
size(source_type{}),
size(flux_type{}) / dim) / c;
404 constexpr
auto integration_points = GaussLegendreNodes<q, mfem::Geometry::TETRAHEDRON>();
405 constexpr
auto integration_weights = GaussLegendreWeights<q, mfem::Geometry::TETRAHEDRON>();
407 for (
int j = 0; j < ntrial; j++) {
408 for (
int i = 0; i < c; i++) {
409 for (
int Q = 0; Q < nqpts(q); Q++) {
410 tensor<double, dim> xi = integration_points[Q];
411 double wt = integration_weights[Q];
413 source_component_type source;
414 if constexpr (!is_zero<source_type>{}) {
415 source =
reinterpret_cast<const double*
>(&get<SOURCE>(qf_output[Q]))[i * ntrial + j];
418 flux_component_type flux;
419 if constexpr (!is_zero<flux_type>{}) {
420 for (
int k = 0; k < dim; k++) {
421 flux[k] =
reinterpret_cast<const double*
>(&get<FLUX>(qf_output[Q]))[(i * dim + k) * ntrial + j];
425 for (
int k = 0; k < ndof; k++) {
426 element_residual[j * step](i, k) +=
427 (source * shape_function(xi, k) +
dot(flux, shape_function_gradient(xi, k))) * wt;
#define SERAC_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc and does nothing on a host compi...
constexpr int num_quadrature_points(mfem::Geometry::Type g, int Q)
return the number of quadrature points in a Gauss-Legendre rule with parameter "Q"
constexpr SERAC_HOST_DEVICE auto dot(const isotropic_tensor< S, m, m > &I, const tensor< T, m, n... > &A)
dot product between an isotropic and (nonisotropic) tensor
tensor(const T(&data)[n1]) -> tensor< T, n1 >
class template argument deduction guide for type tensor.
SERAC_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
constexpr SERAC_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
constexpr SERAC_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.