Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
triangle_H1.inl
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
13 // this specialization defines shape functions (and their gradients) that
14 // interpolate at Gauss-Lobatto nodes for the appropriate polynomial order
15 //
16 // note: mfem assumes the parent element domain is the convex hull of {{0,0}, {1,0}, {0,1}}
17 // for additional information on the finite_element concept requirements, see finite_element.hpp
18 //
19 // for detailed information about node locations for different polynomial orders, see
20 // simplex_basis_function_unit_tests.cpp
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;
31 
32  static constexpr int VALUE = 0, GRADIENT = 1;
33  static constexpr int SOURCE = 0, FLUX = 1;
34 
35  using residual_type =
36  typename std::conditional<components == 1, tensor<double, ndof>, tensor<double, ndof, components> >::type;
37 
38  using dof_type = tensor<double, c, ndof>;
39  using dof_type_if = dof_type;
40 
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>;
45 
46  /*
47 
48  interpolation nodes and their associated numbering:
49 
50  linear
51  2
52  | .
53  | .
54  | .
55  | .
56  | .
57  0-----------1
58 
59  quadratic
60  5
61  | .
62  | .
63  3 4
64  | .
65  | .
66  0-----1-----2
67 
68 
69  cubic
70  9
71  | .
72  7 8
73  | .
74  4 5 6
75  | .
76  0---1---2---3
77 
78  */
79 
80  SMITH_HOST_DEVICE static constexpr double shape_function([[maybe_unused]] tensor<double, dim> xi, int i)
81  {
82  // linear
83  if constexpr (n == 2) {
84  switch (i) {
85  case 0:
86  return 1 - xi[0] - xi[1];
87  case 1:
88  return xi[0];
89  case 2:
90  return xi[1];
91  }
92  }
93 
94  // quadratic
95  if constexpr (n == 3) {
96  switch (i) {
97  case 0:
98  return (-1 + xi[0] + xi[1]) * (-1 + 2 * xi[0] + 2 * xi[1]);
99  case 1:
100  return -4 * xi[0] * (-1 + xi[0] + xi[1]);
101  case 2:
102  return xi[0] * (-1 + 2 * xi[0]);
103  case 3:
104  return -4 * xi[1] * (-1 + xi[0] + xi[1]);
105  case 4:
106  return 4 * xi[0] * xi[1];
107  case 5:
108  return xi[1] * (-1 + 2 * xi[1]);
109  }
110  }
111 
112  // cubic
113  if constexpr (n == 4) {
114  constexpr double sqrt5 = 2.23606797749978981;
115  switch (i) {
116  case 0:
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])));
119  case 1:
120  return (5 * xi[0] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1])) / 2.;
121  case 2:
122  return (-5 * xi[0] * (-1 + xi[0] + xi[1]) * (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1])) / 2.;
123  case 3:
124  return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - xi[0] * (5 + xi[1]));
125  case 4:
126  return (5 * xi[1] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1])) / 2.;
127  case 5:
128  return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1]);
129  case 6:
130  return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.;
131  case 7:
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])) /
134  (-5 + sqrt5);
135  case 8:
136  return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.;
137  case 9:
138  return xi[1] * (1 + xi[0] - xi[0] * xi[0] - xi[0] * xi[1] + 5 * (-1 + xi[1]) * xi[1]);
139  }
140  }
141 
142  return 0.0;
143  }
144 
145  SMITH_HOST_DEVICE static constexpr tensor<double, dim> shape_function_gradient(
146  [[maybe_unused]] tensor<double, dim> xi, int i)
147  {
148  // linear
149  if constexpr (n == 2) {
150  switch (i) {
151  case 0:
152  return {-1.0, -1.0};
153  case 1:
154  return {1.0, 0.0};
155  case 2:
156  return {0.0, 1.0};
157  }
158  }
159 
160  // quadratic
161  if constexpr (n == 3) {
162  switch (i) {
163  case 0:
164  return {-3 + 4 * xi[0] + 4 * xi[1], -3 + 4 * xi[0] + 4 * xi[1]};
165  case 1:
166  return {-4 * (-1 + 2 * xi[0] + xi[1]), -4 * xi[0]};
167  case 2:
168  return {-1 + 4 * xi[0], 0};
169  case 3:
170  return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1])};
171  case 4:
172  return {4 * xi[1], 4 * xi[0]};
173  case 5:
174  return {0, -1 + 4 * xi[1]};
175  }
176  }
177 
178  // cubic
179  if constexpr (n == 4) {
180  constexpr double sqrt5 = 2.23606797749978981;
181  switch (i) {
182  case 0:
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]};
185  case 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]))) /
188  2.,
189  (5 * xi[0] * (-2 * (2 + sqrt5) + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1])) / 2.};
190  case 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]))) /
193  2.,
194  (-5 * xi[0] * (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1])) / 2.};
195  case 3:
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]))};
198  case 4:
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]))) /
202  2.};
203  case 5:
204  return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1])};
205  case 6:
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.};
208  case 7:
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]))) /
212  2.};
213  case 8:
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.};
216  case 9:
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]};
219  }
220  }
221 
222  return {};
223  }
224 
225  SMITH_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
226  {
227  tensor<double, ndof> output{};
228  for (int i = 0; i < ndof; i++) {
229  output[i] = shape_function(xi, i);
230  }
231  return output;
232  }
233 
234  SMITH_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
235  {
236  tensor<double, ndof, dim> output{};
237  for (int i = 0; i < ndof; i++) {
238  output[i] = shape_function_gradient(xi, i);
239  }
240  return output;
241  }
242 
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>&)
245  {
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>{}));
248 
249  constexpr auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
250 
251  static constexpr int Q = q * (q + 1) / 2;
252  tensor<tuple<source_t, flux_t>, Q> output;
253 
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);
257 
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)));
262 
263  output[i] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)};
264  }
265 
266  return output;
267  }
268 
269  template <int q>
270  SMITH_HOST_DEVICE static auto interpolate(const tensor<double, c, ndof>& X, const TensorProductQuadratureRule<q>&)
271  {
272  constexpr auto xi = GaussLegendreNodes<q, mfem::Geometry::TRIANGLE>();
273  static constexpr int num_quadrature_points = q * (q + 1) / 2;
274 
275  // transpose the quadrature data into a flat tensor of tuples
276  union {
277  tensor<tuple<tensor<double, c>, tensor<double, c, dim> >, num_quadrature_points> unflattened;
278  tensor<qf_input_type, num_quadrature_points> flattened;
279  } output{};
280 
281  for (int i = 0; i < c; i++) {
282  for (int j = 0; j < num_quadrature_points; j++) {
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);
286  }
287  }
288  }
289 
290  return output.flattened;
291  }
292 
293  template <typename source_type, typename flux_type, int q>
294  SMITH_HOST_DEVICE static void integrate(const tensor<tuple<source_type, flux_type>, q*(q + 1) / 2>& qf_output,
295  const TensorProductQuadratureRule<q>&,
296  tensor<double, c, ndof>* element_residual, int step = 1)
297  {
298  if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
299  return;
300  }
301 
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> >;
304 
305  constexpr int num_quadrature_points = q * (q + 1) / 2;
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>();
309 
310  for (int j = 0; j < ntrial; j++) {
311  for (int i = 0; i < c; i++) {
312  for (int Q = 0; Q < num_quadrature_points; Q++) {
313  tensor<double, 2> xi = integration_points[Q];
314  double wt = integration_weights[Q];
315 
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];
319  }
320 
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];
325  }
326  }
327 
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;
331  }
332  }
333  }
334  }
335  }
336 };
#define SMITH_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc or amdclang and does nothing on ...
Definition: accelerator.hpp:37
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"
Definition: geometry.hpp:31
constexpr SMITH_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
Definition: tuple.hpp:376
SMITH_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
Definition: dual.hpp:229
constexpr SMITH_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
Definition: tensor.hpp:1939
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