22 template <
int p,
int c>
23 struct finite_element<mfem::Geometry::TRIANGLE, H1<p, c> > {
24 static constexpr
auto geometry = mfem::Geometry::TRIANGLE;
25 static constexpr
auto family = Family::H1;
26 static constexpr
int components = c;
27 static constexpr
int dim = 2;
28 static constexpr
int order = p;
29 static constexpr
int n = (p + 1);
30 static constexpr
int ndof = (p + 1) * (p + 2) / 2;
32 static constexpr
int VALUE = 0, GRADIENT = 1;
33 static constexpr
int SOURCE = 0, FLUX = 1;
36 typename std::conditional<components == 1, tensor<double, ndof>, tensor<double, ndof, components> >
::type;
38 using dof_type = tensor<double, c, ndof>;
39 using dof_type_if = dof_type;
41 using value_type =
typename std::conditional<components == 1, double, tensor<double, components> >
::type;
42 using derivative_type =
43 typename std::conditional<components == 1, tensor<double, dim>, tensor<double, components, dim> >
::type;
44 using qf_input_type = tuple<value_type, derivative_type>;
80 SMITH_HOST_DEVICE static constexpr
double shape_function([[maybe_unused]] tensor<double, dim> xi,
int i)
83 if constexpr (n == 2) {
86 return 1 - xi[0] - xi[1];
95 if constexpr (n == 3) {
98 return (-1 + xi[0] + xi[1]) * (-1 + 2 * xi[0] + 2 * xi[1]);
100 return -4 * xi[0] * (-1 + xi[0] + xi[1]);
102 return xi[0] * (-1 + 2 * xi[0]);
104 return -4 * xi[1] * (-1 + xi[0] + xi[1]);
106 return 4 * xi[0] * xi[1];
108 return xi[1] * (-1 + 2 * xi[1]);
113 if constexpr (n == 4) {
114 constexpr
double sqrt5 = 2.23606797749978981;
117 return -((-1 + xi[0] + xi[1]) *
118 (1 + 5 * xi[0] * xi[0] + 5 * (-1 + xi[1]) * xi[1] + xi[0] * (-5 + 11 * xi[1])));
120 return (5 * xi[0] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1])) / 2.;
122 return (-5 * xi[0] * (-1 + xi[0] + xi[1]) * (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1])) / 2.;
124 return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - xi[0] * (5 + xi[1]));
126 return (5 * xi[1] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1])) / 2.;
128 return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1]);
130 return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.;
132 return (5 * xi[1] * (-1 + xi[0] + xi[1]) *
133 (5 - 3 * sqrt5 + 2 * (-5 + 2 * sqrt5) * xi[0] + 5 * (-1 + sqrt5) * xi[1])) /
136 return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.;
138 return xi[1] * (1 + xi[0] - xi[0] * xi[0] - xi[0] * xi[1] + 5 * (-1 + xi[1]) * xi[1]);
146 [[maybe_unused]] tensor<double, dim> xi,
int i)
149 if constexpr (n == 2) {
161 if constexpr (n == 3) {
164 return {-3 + 4 * xi[0] + 4 * xi[1], -3 + 4 * xi[0] + 4 * xi[1]};
166 return {-4 * (-1 + 2 * xi[0] + xi[1]), -4 * xi[0]};
168 return {-1 + 4 * xi[0], 0};
170 return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1])};
172 return {4 * xi[1], 4 * xi[0]};
174 return {0, -1 + 4 * xi[1]};
179 if constexpr (n == 4) {
180 constexpr
double sqrt5 = 2.23606797749978981;
183 return {-6 - 15 * xi[0] * xi[0] + 4 * xi[0] * (5 - 8 * xi[1]) + (21 - 16 * xi[1]) * xi[1],
184 -6 - 16 * xi[0] * xi[0] + xi[0] * (21 - 32 * xi[1]) + 5 * (4 - 3 * xi[1]) * xi[1]};
186 return {(5 * (6 * sqrt5 * xi[0] * xi[0] + xi[0] * (-2 - 6 * sqrt5 + 6 * (1 + sqrt5) * xi[1]) +
187 (-1 + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[1]))) /
189 (5 * xi[0] * (-2 * (2 + sqrt5) + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1])) / 2.};
191 return {(-5 * (6 * sqrt5 * xi[0] * xi[0] + (-1 + xi[1]) * (1 - sqrt5 + (-3 + sqrt5) * xi[1]) +
192 xi[0] * (2 - 6 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) /
194 (-5 * xi[0] * (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1])) / 2.};
196 return {1 + 15 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - 2 * xi[0] * (5 + xi[1]),
197 -(xi[0] * (-1 + xi[0] + 2 * xi[1]))};
199 return {(5 * xi[1] * (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1])) / 2.,
200 (5 * (1 + sqrt5 - 2 * (2 + sqrt5) * xi[0] + (3 + sqrt5) * xi[0] * xi[0] +
201 6 * (1 + sqrt5) * xi[0] * xi[1] + 2 * xi[1] * (-1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]))) /
204 return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1])};
206 return {(-5 * xi[1] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[1])) / 2.,
207 (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[1])) / 2.};
209 return {(-5 * xi[1] * (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1])) / 2.,
210 (-5 * (-1 + sqrt5 + (-3 + sqrt5) * xi[0] * xi[0] + 2 * xi[1] * (1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]) +
211 xi[0] * (4 - 2 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) /
214 return {(5 * xi[1] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[1])) / 2.,
215 (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[1])) / 2.};
217 return {-(xi[1] * (-1 + 2 * xi[0] + xi[1])),
218 1 + xi[0] - xi[0] * xi[0] - 2 * (5 + xi[0]) * xi[1] + 15 * xi[1] * xi[1]};
225 SMITH_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
227 tensor<double, ndof> output{};
228 for (
int i = 0; i < ndof; i++) {
229 output[i] = shape_function(xi, i);
234 SMITH_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
236 tensor<double, ndof, dim> output{};
237 for (
int i = 0; i < ndof; i++) {
238 output[i] = shape_function_gradient(xi, i);
243 template <
typename in_t,
int q>
244 static auto batch_apply_shape_fn(
int j,
tensor<in_t, q*(q + 1) / 2> input,
const TensorProductQuadratureRule<q>&)
246 using source_t = decltype(get<0>(get<0>(in_t{})) +
dot(get<1>(get<0>(in_t{})), tensor<double, 2>{}));
247 using flux_t = decltype(get<0>(get<1>(in_t{})) +
dot(get<1>(get<1>(in_t{})), tensor<double, 2>{}));
249 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
251 static constexpr
int Q = q * (q + 1) / 2;
252 tensor<tuple<source_t, flux_t>, Q> output;
254 for (
int i = 0; i < Q; i++) {
255 double phi_j = shape_function(xi[i], j);
256 tensor<double, dim> dphi_j_dxi = shape_function_gradient(xi[i], j);
258 const auto& d00 = get<0>(get<0>(input(i)));
259 const auto& d01 = get<1>(get<0>(input(i)));
260 const auto& d10 = get<0>(get<1>(input(i)));
261 const auto& d11 = get<1>(get<1>(input(i)));
263 output[i] = {d00 * phi_j +
dot(d01, dphi_j_dxi), d10 * phi_j +
dot(d11, dphi_j_dxi)};
270 SMITH_HOST_DEVICE static auto interpolate(
const tensor<double, c, ndof>& X,
const TensorProductQuadratureRule<q>&)
272 constexpr
auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
278 tensor<qf_input_type, num_quadrature_points> flattened;
281 for (
int i = 0; i < c; i++) {
283 for (
int k = 0; k < ndof; k++) {
284 get<VALUE>(output.unflattened[j])[i] += X(i, k) * shape_function(xi[j], k);
285 get<GRADIENT>(output.unflattened[j])[i] += X(i, k) * shape_function_gradient(xi[j], k);
290 return output.flattened;
293 template <
typename source_type,
typename flux_type,
int q>
295 const TensorProductQuadratureRule<q>&,
296 tensor<double, c, ndof>* element_residual,
int step = 1)
298 if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
302 using source_component_type = std::conditional_t<is_zero<source_type>{}, zero,
double>;
303 using flux_component_type = std::conditional_t<is_zero<flux_type>{}, zero, tensor<double, dim> >;
306 constexpr
int ntrial =
std::max(
size(source_type{}),
size(flux_type{}) / dim) / c;
307 constexpr
auto integration_points = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
308 constexpr
auto integration_weights = GaussLegendreWeights<q, mfem::Geometry::TRIANGLE>();
310 for (
int j = 0; j < ntrial; j++) {
311 for (
int i = 0; i < c; i++) {
313 tensor<double, 2> xi = integration_points[Q];
314 double wt = integration_weights[Q];
316 source_component_type source;
317 if constexpr (!is_zero<source_type>{}) {
318 source =
reinterpret_cast<const double*
>(&get<SOURCE>(qf_output[Q]))[i * ntrial + j];
321 flux_component_type flux;
322 if constexpr (!is_zero<flux_type>{}) {
323 for (
int k = 0; k < dim; k++) {
324 flux[k] =
reinterpret_cast<const double*
>(&get<FLUX>(qf_output[Q]))[(i * dim + k) * ntrial + j];
328 for (
int k = 0; k < ndof; k++) {
329 element_residual[j * step](i, k) +=
330 (source * shape_function(xi, k) +
dot(flux, shape_function_gradient(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