21 template <
int p,
int c>
22 struct finite_element<mfem::Geometry::TRIANGLE, L2<p, c> > {
23 static constexpr
auto geometry = mfem::Geometry::TRIANGLE;
24 static constexpr
auto family = Family::L2;
25 static constexpr
int components = c;
26 static constexpr
int dim = 2;
27 static constexpr
int n = (p + 1);
28 static constexpr
int ndof = (p + 1) * (p + 2) / 2;
30 static constexpr
int VALUE = 0, GRADIENT = 1;
31 static constexpr
int SOURCE = 0, FLUX = 1;
34 typename std::conditional<components == 1, tensor<double, ndof>, tensor<double, ndof, components> >
::type;
36 using dof_type = tensor<double, c, ndof>;
37 using dof_type_if = tensor<double, c, 2, 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>;
78 SMITH_HOST_DEVICE static constexpr
double shape_function([[maybe_unused]] tensor<double, dim> xi,
79 [[maybe_unused]]
int i)
82 if constexpr (n == 1) {
87 if constexpr (n == 2) {
90 return 1 - xi[0] - xi[1];
99 if constexpr (n == 3) {
102 return (-1 + xi[0] + xi[1]) * (-1 + 2 * xi[0] + 2 * xi[1]);
104 return -4 * xi[0] * (-1 + xi[0] + xi[1]);
106 return xi[0] * (-1 + 2 * xi[0]);
108 return -4 * xi[1] * (-1 + xi[0] + xi[1]);
110 return 4 * xi[0] * xi[1];
112 return xi[1] * (-1 + 2 * xi[1]);
117 if constexpr (n == 4) {
118 constexpr
double sqrt5 = 2.23606797749978981;
121 return -((-1 + xi[0] + xi[1]) *
122 (1 + 5 * xi[0] * xi[0] + 5 * (-1 + xi[1]) * xi[1] + xi[0] * (-5 + 11 * xi[1])));
124 return (5 * xi[0] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1])) / 2.;
126 return (-5 * xi[0] * (-1 + xi[0] + xi[1]) * (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1])) / 2.;
128 return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - xi[0] * (5 + xi[1]));
130 return (5 * xi[1] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1])) / 2.;
132 return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1]);
134 return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.;
136 return (5 * xi[1] * (-1 + xi[0] + xi[1]) *
137 (5 - 3 * sqrt5 + 2 * (-5 + 2 * sqrt5) * xi[0] + 5 * (-1 + sqrt5) * xi[1])) /
140 return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.;
142 return xi[1] * (1 + xi[0] - xi[0] * xi[0] - xi[0] * xi[1] + 5 * (-1 + xi[1]) * xi[1]);
150 [[maybe_unused]] tensor<double, dim> xi, [[maybe_unused]]
int i)
153 if constexpr (n == 1) {
158 if constexpr (n == 2) {
170 if constexpr (n == 3) {
173 return {-3 + 4 * xi[0] + 4 * xi[1], -3 + 4 * xi[0] + 4 * xi[1]};
175 return {-4 * (-1 + 2 * xi[0] + xi[1]), -4 * xi[0]};
177 return {-1 + 4 * xi[0], 0};
179 return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1])};
181 return {4 * xi[1], 4 * xi[0]};
183 return {0, -1 + 4 * xi[1]};
188 if constexpr (n == 4) {
189 constexpr
double sqrt5 = 2.23606797749978981;
192 return {-6 - 15 * xi[0] * xi[0] + 4 * xi[0] * (5 - 8 * xi[1]) + (21 - 16 * xi[1]) * xi[1],
193 -6 - 16 * xi[0] * xi[0] + xi[0] * (21 - 32 * xi[1]) + 5 * (4 - 3 * xi[1]) * xi[1]};
195 return {(5 * (6 * sqrt5 * xi[0] * xi[0] + xi[0] * (-2 - 6 * sqrt5 + 6 * (1 + sqrt5) * xi[1]) +
196 (-1 + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[1]))) /
198 (5 * xi[0] * (-2 * (2 + sqrt5) + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1])) / 2.};
200 return {(-5 * (6 * sqrt5 * xi[0] * xi[0] + (-1 + xi[1]) * (1 - sqrt5 + (-3 + sqrt5) * xi[1]) +
201 xi[0] * (2 - 6 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) /
203 (-5 * xi[0] * (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1])) / 2.};
205 return {1 + 15 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - 2 * xi[0] * (5 + xi[1]),
206 -(xi[0] * (-1 + xi[0] + 2 * xi[1]))};
208 return {(5 * xi[1] * (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1])) / 2.,
209 (5 * (1 + sqrt5 - 2 * (2 + sqrt5) * xi[0] + (3 + sqrt5) * xi[0] * xi[0] +
210 6 * (1 + sqrt5) * xi[0] * xi[1] + 2 * xi[1] * (-1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]))) /
213 return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1])};
215 return {(-5 * xi[1] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[1])) / 2.,
216 (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[1])) / 2.};
218 return {(-5 * xi[1] * (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1])) / 2.,
219 (-5 * (-1 + sqrt5 + (-3 + sqrt5) * xi[0] * xi[0] + 2 * xi[1] * (1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]) +
220 xi[0] * (4 - 2 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) /
223 return {(5 * xi[1] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[1])) / 2.,
224 (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[1])) / 2.};
226 return {-(xi[1] * (-1 + 2 * xi[0] + xi[1])),
227 1 + xi[0] - xi[0] * xi[0] - 2 * (5 + xi[0]) * xi[1] + 15 * xi[1] * xi[1]};
234 SMITH_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
236 tensor<double, ndof> output{};
237 for (
int i = 0; i < ndof; i++) {
238 output[i] = shape_function(xi, i);
243 SMITH_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
245 tensor<double, ndof, dim> output{};
246 for (
int i = 0; i < ndof; i++) {
247 output[i] = shape_function_gradient(xi, i);
252 template <
typename in_t,
int q>
253 static auto batch_apply_shape_fn(
int j,
const tensor<in_t, q*(q + 1) / 2>& input,
254 const TensorProductQuadratureRule<q>&)
256 using source_t = decltype(get<0>(get<0>(in_t{})) +
dot(get<1>(get<0>(in_t{})), tensor<double, 2>{}));
257 using flux_t = decltype(get<0>(get<1>(in_t{})) +
dot(get<1>(get<1>(in_t{})), tensor<double, 2>{}));
259 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
261 static constexpr
int Q = q * (q + 1) / 2;
262 tensor<tuple<source_t, flux_t>, Q> output;
264 for (
int i = 0; i < Q; i++) {
265 double phi_j = shape_function(xi[i], j);
266 tensor<double, dim> dphi_j_dxi = shape_function_gradient(xi[i], j);
268 const auto& d00 = get<0>(get<0>(input(i)));
269 const auto& d01 = get<1>(get<0>(input(i)));
270 const auto& d10 = get<0>(get<1>(input(i)));
271 const auto& d11 = get<1>(get<1>(input(i)));
273 output[i] = {d00 * phi_j +
dot(d01, dphi_j_dxi), d10 * phi_j +
dot(d11, dphi_j_dxi)};
279 template <
typename T,
int q>
280 static auto batch_apply_shape_fn_interior_face(
int jx,
const tensor<T, q*(q + 1) / 2>& input,
281 const TensorProductQuadratureRule<q>&)
283 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
285 using source0_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{})));
286 using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{})));
288 static constexpr
int Q = q * (q + 1) / 2;
289 tensor<tuple<source0_t, source1_t>, Q> output;
291 for (
int i = 0; i < Q; i++) {
295 double phi0_j = shape_function(xi[i], j) * (s == 0);
296 double phi1_j = shape_function(xi[i], j) * (s == 1);
298 const auto& d00 = get<0>(get<0>(input(i)));
299 const auto& d01 = get<1>(get<0>(input(i)));
300 const auto& d10 = get<0>(get<1>(input(i)));
301 const auto& d11 = get<1>(get<1>(input(i)));
303 output[i] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j};
310 SMITH_HOST_DEVICE static auto interpolate(
const tensor<double, c, ndof>& X,
const TensorProductQuadratureRule<q>&)
312 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
318 tensor<qf_input_type, num_quadrature_points> flattened;
321 for (
int i = 0; i < c; i++) {
323 for (
int k = 0; k < ndof; k++) {
324 get<VALUE>(output.unflattened[j])[i] += X(i, k) * shape_function(xi[j], k);
325 get<GRADIENT>(output.unflattened[j])[i] += X(i, k) * shape_function_gradient(xi[j], k);
330 return output.flattened;
335 SMITH_HOST_DEVICE static auto interpolate(
const dof_type_if& X,
const TensorProductQuadratureRule<q>&)
337 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
343 for (
int i = 0; i < c; i++) {
345 for (
int k = 0; k < ndof; k++) {
346 if constexpr (c == 1) {
347 get<0>(output[j]) += X[i][0][k] * shape_function(xi[j], k);
348 get<1>(output[j]) += X[i][1][k] * shape_function(xi[j], k);
350 get<0>(output[j])[i] += X[i][0][k] * shape_function(xi[j], k);
351 get<1>(output[j])[i] += X[i][1][k] * shape_function(xi[j], k);
360 template <
typename source_type,
typename flux_type,
int q>
362 const TensorProductQuadratureRule<q>&,
363 tensor<double, c, ndof>* element_residual,
int step = 1)
365 if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
369 using source_component_type = std::conditional_t<is_zero<source_type>{}, zero,
double>;
370 using flux_component_type = std::conditional_t<is_zero<flux_type>{}, zero, tensor<double, dim> >;
373 constexpr
int ntrial =
std::max(
size(source_type{}),
size(flux_type{}) / dim) / c;
374 constexpr
auto integration_points = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
375 constexpr
auto integration_weights = GaussLegendreWeights<q, mfem::Geometry::TRIANGLE>();
377 for (
int j = 0; j < ntrial; j++) {
378 for (
int i = 0; i < c; i++) {
380 tensor<double, 2> xi = integration_points[Q];
381 double wt = integration_weights[Q];
383 source_component_type source;
384 if constexpr (!is_zero<source_type>{}) {
385 source =
reinterpret_cast<const double*
>(&get<SOURCE>(qf_output[Q]))[i * ntrial + j];
388 flux_component_type flux;
389 if constexpr (!is_zero<flux_type>{}) {
390 for (
int k = 0; k < dim; k++) {
391 flux[k] =
reinterpret_cast<const double*
>(&get<FLUX>(qf_output[Q]))[(i * dim + k) * ntrial + j];
395 for (
int k = 0; k < ndof; k++) {
396 element_residual[j * step](i, k) +=
397 (source * shape_function(xi, k) +
dot(flux, shape_function_gradient(xi, k))) * wt;
404 template <
typename T,
int q>
406 const TensorProductQuadratureRule<q>&, dof_type_if* element_residual,
407 [[maybe_unused]]
int step = 1)
409 constexpr
int ntrial =
size(T{}) / c;
411 constexpr
auto integration_points = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
412 constexpr
auto integration_weights = GaussLegendreWeights<q, mfem::Geometry::TRIANGLE>();
414 for (
int j = 0; j < ntrial; j++) {
415 for (
int i = 0; i < c; i++) {
417 tensor<double, 2> xi = integration_points[Q];
418 double wt = integration_weights[Q];
420 double source_0 =
reinterpret_cast<const double*
>(&get<0>(qf_output[Q]))[i * ntrial + j];
421 double source_1 =
reinterpret_cast<const double*
>(&get<1>(qf_output[Q]))[i * ntrial + j];
423 for (
int k = 0; k < ndof; k++) {
424 element_residual[j * step](i, 0, k) += (source_0 * shape_function(xi, k)) * wt;
425 element_residual[j * step](i, 1, k) += (source_1 * shape_function(xi, k)) * wt;
#define SMITH_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 SMITH_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
SMITH_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
constexpr SMITH_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
tensor(const T(&data)[n1]) -> tensor< T, n1 >
class template argument deduction guide for type tensor.
constexpr SMITH_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