Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
hexahedron_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 [0,1]x[0,1]x[0,1]
17 // for additional information on the finite_element concept requirements, see finite_element.hpp
19 template <int p, int c>
20 struct finite_element<mfem::Geometry::CUBE, H1<p, c> > {
21  static constexpr auto geometry = mfem::Geometry::CUBE;
22  static constexpr auto family = Family::H1;
23  static constexpr int components = c;
24  static constexpr int dim = 3;
25  static constexpr int n = (p + 1);
26  static constexpr int ndof = (p + 1) * (p + 1) * (p + 1);
27  static constexpr int order = p;
28 
29  static constexpr int VALUE = 0, GRADIENT = 1;
30  static constexpr int SOURCE = 0, FLUX = 1;
31 
32  using dof_type = tensor<double, c, p + 1, p + 1, p + 1>;
33 
34  using value_type = typename std::conditional<components == 1, double, tensor<double, components> >::type;
35  using derivative_type =
36  typename std::conditional<components == 1, tensor<double, dim>, tensor<double, components, dim> >::type;
37  using qf_input_type = tuple<value_type, derivative_type>;
38 
39  SMITH_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
40  {
41  auto N_xi = GaussLobattoInterpolation<p + 1>(xi[0]);
42  auto N_eta = GaussLobattoInterpolation<p + 1>(xi[1]);
43  auto N_zeta = GaussLobattoInterpolation<p + 1>(xi[2]);
44 
45  int count = 0;
46 
47  tensor<double, ndof> N{};
48  for (int k = 0; k < p + 1; k++) {
49  for (int j = 0; j < p + 1; j++) {
50  for (int i = 0; i < p + 1; i++) {
51  N[count++] = N_xi[i] * N_eta[j] * N_zeta[k];
52  }
53  }
54  }
55  return N;
56  }
57 
58  SMITH_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
59  {
60  auto N_xi = GaussLobattoInterpolation<p + 1>(xi[0]);
61  auto N_eta = GaussLobattoInterpolation<p + 1>(xi[1]);
62  auto N_zeta = GaussLobattoInterpolation<p + 1>(xi[2]);
63  auto dN_xi = GaussLobattoInterpolationDerivative<p + 1>(xi[0]);
64  auto dN_eta = GaussLobattoInterpolationDerivative<p + 1>(xi[1]);
65  auto dN_zeta = GaussLobattoInterpolationDerivative<p + 1>(xi[2]);
66 
67  int count = 0;
68 
69  // clang-format off
70  tensor<double, ndof, dim> dN{};
71  for (int k = 0; k < p + 1; k++) {
72  for (int j = 0; j < p + 1; j++) {
73  for (int i = 0; i < p + 1; i++) {
74  dN[count++] = {
75  dN_xi[i] * N_eta[j] * N_zeta[k],
76  N_xi[i] * dN_eta[j] * N_zeta[k],
77  N_xi[i] * N_eta[j] * dN_zeta[k]
78  };
79  }
80  }
81  }
82  return dN;
83  // clang-format on
84  }
85 
96  template <bool apply_weights, int q>
97  static constexpr auto calculate_B()
98  {
99  constexpr auto points1D = GaussLegendreNodes<q, mfem::Geometry::SEGMENT>();
100  [[maybe_unused]] constexpr auto weights1D = GaussLegendreWeights<q, mfem::Geometry::SEGMENT>();
101  tensor<double, q, n> B{};
102  for (int i = 0; i < q; i++) {
103  B[i] = GaussLobattoInterpolation<n>(points1D[i]);
104  if constexpr (apply_weights) B[i] = B[i] * weights1D[i];
105  }
106  return B;
107  }
108 
119  template <bool apply_weights, int q>
120  static constexpr auto calculate_G()
121  {
122  constexpr auto points1D = GaussLegendreNodes<q, mfem::Geometry::SEGMENT>();
123  [[maybe_unused]] constexpr auto weights1D = GaussLegendreWeights<q, mfem::Geometry::SEGMENT>();
124  tensor<double, q, n> G{};
125  for (int i = 0; i < q; i++) {
126  G[i] = GaussLobattoInterpolationDerivative<n>(points1D[i]);
127  if constexpr (apply_weights) G[i] = G[i] * weights1D[i];
128  }
129  return G;
130  }
131 
132  template <typename in_t, int q>
133  static auto batch_apply_shape_fn(int j, tensor<in_t, q * q * q> input, const TensorProductQuadratureRule<q>&)
134  {
135  static constexpr bool apply_weights = false;
136  static constexpr auto B = calculate_B<apply_weights, q>();
137  static constexpr auto G = calculate_G<apply_weights, q>();
138 
139  int jx = j % n;
140  int jy = (j % (n * n)) / n;
141  int jz = j / (n * n);
142 
143  using source_t = decltype(get<0>(get<0>(in_t{})) + dot(get<1>(get<0>(in_t{})), tensor<double, dim>{}));
144  using flux_t = decltype(get<0>(get<1>(in_t{})) + dot(get<1>(get<1>(in_t{})), tensor<double, dim>{}));
145 
146  tensor<tuple<source_t, flux_t>, q * q * q> output;
147 
148  for (int qz = 0; qz < q; qz++) {
149  for (int qy = 0; qy < q; qy++) {
150  for (int qx = 0; qx < q; qx++) {
151  double phi_j = B(qx, jx) * B(qy, jy) * B(qz, jz);
152  tensor<double, dim> dphi_j_dxi = {G(qx, jx) * B(qy, jy) * B(qz, jz), B(qx, jx) * G(qy, jy) * B(qz, jz),
153  B(qx, jx) * B(qy, jy) * G(qz, jz)};
154 
155  int Q = (qz * q + qy) * q + qx;
156  const auto& d00 = get<0>(get<0>(input(Q)));
157  const auto& d01 = get<1>(get<0>(input(Q)));
158  const auto& d10 = get<0>(get<1>(input(Q)));
159  const auto& d11 = get<1>(get<1>(input(Q)));
160 
161  output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)};
162  }
163  }
164  }
165 
166  return output;
167  }
168 
169  template <int q>
170  SMITH_HOST_DEVICE static auto interpolate(const dof_type& X, const TensorProductQuadratureRule<q>&)
171  {
172  // we want to compute the following:
173  //
174  // X_q(u, v, w) := (B(u, i) * B(v, j) * B(w, k)) * X_e(i, j, k)
175  //
176  // where
177  // X_q(u, v, w) are the quadrature-point values at position {u, v, w},
178  // B(u, i) is the i^{th} 1D interpolation/differentiation (shape) function,
179  // evaluated at the u^{th} 1D quadrature point, and
180  // X_e(i, j, k) are the values at node {i, j, k} to be interpolated
181  //
182  // this algorithm carries out the above calculation in 3 steps:
183  //
184  // A1(dz, dy, qx) := B(qx, dx) * X_e(dz, dy, dx)
185  // A2(dz, qy, qx) := B(qy, dy) * A1(dz, dy, qx)
186  // X_q(qz, qy, qx) := B(qz, dz) * A2(dz, qy, qx)
187  static constexpr bool apply_weights = false;
188  static constexpr auto B = calculate_B<apply_weights, q>();
189  static constexpr auto G = calculate_G<apply_weights, q>();
190 
191  tensor<double, c, q, q, q> value{};
192  tensor<double, c, dim, q, q, q> gradient{};
193 
194  for (int i = 0; i < c; i++) {
195  auto A10 = contract<2, 1>(X[i], B);
196  auto A11 = contract<2, 1>(X[i], G);
197 
198  auto A20 = contract<1, 1>(A10, B);
199  auto A21 = contract<1, 1>(A11, B);
200  auto A22 = contract<1, 1>(A10, G);
201 
202  value(i) = contract<0, 1>(A20, B);
203  gradient(i, 0) = contract<0, 1>(A21, B);
204  gradient(i, 1) = contract<0, 1>(A22, B);
205  gradient(i, 2) = contract<0, 1>(A20, G);
206  }
207 
208  // transpose the quadrature data into a flat tensor of tuples
209  union {
210  tensor<qf_input_type, q * q * q> one_dimensional;
211  tensor<tuple<tensor<double, c>, tensor<double, c, dim> >, q, q, q> three_dimensional;
212  } output;
213 
214  for (int qz = 0; qz < q; qz++) {
215  for (int qy = 0; qy < q; qy++) {
216  for (int qx = 0; qx < q; qx++) {
217  for (int i = 0; i < c; i++) {
218  get<VALUE>(output.three_dimensional(qz, qy, qx))[i] = value(i, qz, qy, qx);
219  for (int j = 0; j < dim; j++) {
220  get<GRADIENT>(output.three_dimensional(qz, qy, qx))[i][j] = gradient(i, j, qz, qy, qx);
221  }
222  }
223  }
224  }
225  }
226 
227  return output.one_dimensional;
228  }
229 
230  template <typename source_type, typename flux_type, int q>
231  SMITH_HOST_DEVICE static void integrate(const tensor<tuple<source_type, flux_type>, q * q * q>& qf_output,
232  const TensorProductQuadratureRule<q>&, dof_type* element_residual,
233  int step = 1)
234  {
235  if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
236  return;
237  }
238 
239  constexpr int ntrial = std::max(size(source_type{}), size(flux_type{}) / dim) / c;
240 
241  using s_buffer_type = std::conditional_t<is_zero<source_type>{}, zero, tensor<double, q, q, q> >;
242  using f_buffer_type = std::conditional_t<is_zero<flux_type>{}, zero, tensor<double, dim, q, q, q> >;
243 
244  static constexpr bool apply_weights = true;
245  static constexpr auto B = calculate_B<apply_weights, q>();
246  static constexpr auto G = calculate_G<apply_weights, q>();
247 
248  for (int j = 0; j < ntrial; j++) {
249  for (int i = 0; i < c; i++) {
250  s_buffer_type source;
251  f_buffer_type flux;
252 
253  for (int qz = 0; qz < q; qz++) {
254  for (int qy = 0; qy < q; qy++) {
255  for (int qx = 0; qx < q; qx++) {
256  int Q = (qz * q + qy) * q + qx;
257  if constexpr (!is_zero<source_type>{}) {
258  source(qz, qy, qx) = reinterpret_cast<const double*>(&get<SOURCE>(qf_output[Q]))[i * ntrial + j];
259  }
260  if constexpr (!is_zero<flux_type>{}) {
261  for (int k = 0; k < dim; k++) {
262  flux(k, qz, qy, qx) =
263  reinterpret_cast<const double*>(&get<FLUX>(qf_output[Q]))[(i * dim + k) * ntrial + j];
264  }
265  }
266  }
267  }
268  }
269 
270  auto A20 = contract<2, 0>(source, B) + contract<2, 0>(flux(0), G);
271  auto A21 = contract<2, 0>(flux(1), B);
272  auto A22 = contract<2, 0>(flux(2), B);
273 
274  auto A10 = contract<1, 0>(A20, B) + contract<1, 0>(A21, G);
275  auto A11 = contract<1, 0>(A22, B);
276 
277  element_residual[j * step](i) += contract<0, 0>(A10, B) + contract<0, 0>(A11, G);
278  }
279  }
280  }
281 
282 #if 0
283 
284  template <int q>
285  static SMITH_DEVICE auto interpolate(const dof_type& X, const tensor<double, dim, dim>& J,
286  const TensorProductQuadratureRule<q>& rule, cache_type<q>& cache)
287  {
288  // we want to compute the following:
289  //
290  // X_q(u, v, w) := (B(u, i) * B(v, j) * B(w, k)) * X_e(i, j, k)
291  //
292  // where
293  // X_q(u, v, w) are the quadrature-point values at position {u, v, w},
294  // B(u, i) is the i^{th} 1D interpolation/differentiation (shape) function,
295  // evaluated at the u^{th} 1D quadrature point, and
296  // X_e(i, j, k) are the values at node {i, j, k} to be interpolated
297  //
298  // this algorithm carries out the above calculation in 3 steps:
299  //
300  // A1(dz, dy, qx) := B(qx, dx) * X_e(dz, dy, dx)
301  // A2(dz, qy, qx) := B(qy, dy) * A1(dz, dy, qx)
302  // X_q(qz, qy, qx) := B(qz, dz) * A2(dz, qy, qx)
303 
304  int tidx = threadIdx.x % q;
305  int tidy = (threadIdx.x % (q * q)) / q;
306  int tidz = threadIdx.x / (q * q);
307 
308  static constexpr auto points1D = GaussLegendreNodes<q>();
309  static constexpr auto B_ = [=]() {
310  tensor<double, q, n> B{};
311  for (int i = 0; i < q; i++) {
312  B[i] = GaussLobattoInterpolation<n>(points1D[i]);
313  }
314  return B;
315  }();
316 
317  static constexpr auto G_ = [=]() {
318  tensor<double, q, n> G{};
319  for (int i = 0; i < q; i++) {
320  G[i] = GaussLobattoInterpolationDerivative<n>(points1D[i]);
321  }
322  return G;
323  }();
324 
325  __shared__ tensor<double, q, n> B;
326  __shared__ tensor<double, q, n> G;
327  for (int entry = threadIdx.x; entry < n * q; entry += q * q * q) {
328  int i = entry % n;
329  int j = entry / n;
330  B(j, i) = B_(j, i);
331  G(j, i) = G_(j, i);
332  }
333  __syncthreads();
334 
335  tuple<tensor<double, c>, tensor<double, c, 3> > qf_input{};
336 
337  for (int i = 0; i < c; i++) {
338  for (int dz = tidz; dz < n; dz += q) {
339  for (int dy = tidy; dy < n; dy += q) {
340  for (int qx = tidx; qx < q; qx += q) {
341  double sum[2]{};
342  for (int dx = 0; dx < n; dx++) {
343  sum[0] += B(qx, dx) * X(i, dz, dy, dx);
344  sum[1] += G(qx, dx) * X(i, dz, dy, dx);
345  }
346  cache.A1(0, dz, dy, qx) = sum[0];
347  cache.A1(1, dz, dy, qx) = sum[1];
348  }
349  }
350  }
351  __syncthreads();
352 
353  for (int dz = tidz; dz < n; dz += q) {
354  for (int qy = tidy; qy < q; qy += q) {
355  for (int qx = tidx; qx < q; qx += q) {
356  double sum[3]{};
357  for (int dy = 0; dy < n; dy++) {
358  sum[0] += B(qy, dy) * cache.A1(0, dz, dy, qx);
359  sum[1] += B(qy, dy) * cache.A1(1, dz, dy, qx);
360  sum[2] += G(qy, dy) * cache.A1(0, dz, dy, qx);
361  }
362  cache.A2(0, dz, qy, qx) = sum[0];
363  cache.A2(1, dz, qy, qx) = sum[1];
364  cache.A2(2, dz, qy, qx) = sum[2];
365  }
366  }
367  }
368  __syncthreads();
369 
370  for (int qz = tidz; qz < q; qz += q) {
371  for (int qy = tidy; qy < q; qy += q) {
372  for (int qx = tidx; qx < q; qx += q) {
373  for (int dz = 0; dz < n; dz++) {
374  get<0>(qf_input)[i] += B(qz, dz) * cache.A2(0, dz, qy, qx);
375  get<1>(qf_input)[i][0] += B(qz, dz) * cache.A2(1, dz, qy, qx);
376  get<1>(qf_input)[i][1] += B(qz, dz) * cache.A2(2, dz, qy, qx);
377  get<1>(qf_input)[i][2] += G(qz, dz) * cache.A2(0, dz, qy, qx);
378  }
379  }
380  }
381  }
382  }
383 
384  get<1>(qf_input) = dot(get<1>(qf_input), inv(J));
385 
386  return qf_input;
387  }
388 
389  template <typename T1, typename T2, int q>
390  static SMITH_DEVICE void integrate(tuple<T1, T2>& response, const tensor<double, dim, dim>& J,
391  const TensorProductQuadratureRule<q>& rule, cache_type<q>& cache,
392  dof_type& residual)
393  {
394  int tidx = threadIdx.x % q;
395  int tidy = (threadIdx.x % (q * q)) / q;
396  int tidz = threadIdx.x / (q * q);
397 
398  static constexpr auto points1D = GaussLegendreNodes<q>();
399  static constexpr auto weights1D = GaussLegendreWeights<q>();
400  static constexpr auto B_ = [=]() {
401  tensor<double, q, n> B{};
402  for (int i = 0; i < q; i++) {
403  B[i] = GaussLobattoInterpolation<n>(points1D[i]);
404  }
405  return B;
406  }();
407 
408  static constexpr auto G_ = [=]() {
409  tensor<double, q, n> G{};
410  for (int i = 0; i < q; i++) {
411  G[i] = GaussLobattoInterpolationDerivative<n>(points1D[i]);
412  }
413  return G;
414  }();
415 
416  __shared__ tensor<double, q, n> B;
417  __shared__ tensor<double, q, n> G;
418  for (int entry = threadIdx.x; entry < n * q; entry += q * q * q) {
419  int i = entry % n;
420  int j = entry / n;
421  B(j, i) = B_(j, i);
422  G(j, i) = G_(j, i);
423  }
424  __syncthreads();
425 
426  auto dv = det(J) * weights1D[tidx] * weights1D[tidy] * weights1D[tidz];
427 
428  get<0>(response) = get<0>(response) * dv;
429  get<1>(response) = dot(get<1>(response), inv(transpose(J))) * dv;
430 
431  for (int i = 0; i < c; i++) {
432  // this first contraction is performed a little differently, since `response` is not
433  // in shared memory, so each thread can only access its own values
434  for (int qz = tidz; qz < q; qz += q) {
435  for (int qy = tidy; qy < q; qy += q) {
436  for (int dx = tidx; dx < n; dx += q) {
437  cache.A2(0, dx, qy, qz) = 0.0;
438  cache.A2(1, dx, qy, qz) = 0.0;
439  cache.A2(2, dx, qy, qz) = 0.0;
440  }
441  }
442  }
443  __syncthreads();
444 
445  for (int offset = 0; offset < n; offset++) {
446  int dx = (tidx + offset) % n;
447  auto sum = B(tidx, dx) * get<0>(response)(i) + G(tidx, dx) * get<1>(response)(i, 0);
448  atomicAdd(&cache.A2(0, dx, tidz, tidy), sum);
449  atomicAdd(&cache.A2(1, dx, tidz, tidy), B(tidx, dx) * get<1>(response)(i, 1));
450  atomicAdd(&cache.A2(2, dx, tidz, tidy), B(tidx, dx) * get<1>(response)(i, 2));
451  }
452  __syncthreads();
453 
454  for (int qz = tidz; qz < q; qz += q) {
455  for (int dy = tidy; dy < n; dy += q) {
456  for (int dx = tidx; dx < n; dx += q) {
457  double sum[2]{};
458  for (int qy = 0; qy < q; qy++) {
459  sum[0] += B(qy, dy) * cache.A2(0, dx, qz, qy);
460  sum[0] += G(qy, dy) * cache.A2(1, dx, qz, qy);
461  sum[1] += B(qy, dy) * cache.A2(2, dx, qz, qy);
462  }
463  cache.A1(0, qz, dy, dx) = sum[0];
464  cache.A1(1, qz, dy, dx) = sum[1];
465  }
466  }
467  }
468  __syncthreads();
469 
470  for (int dz = tidz; dz < n; dz += q) {
471  for (int dy = tidy; dy < n; dy += q) {
472  for (int dx = tidx; dx < n; dx += q) {
473  double sum = 0.0;
474  for (int qz = 0; qz < q; qz++) {
475  sum += B(qz, dz) * cache.A1(0, qz, dy, dx);
476  sum += G(qz, dz) * cache.A1(1, qz, dy, dx);
477  }
478  residual(i, dz, dy, dx) += sum;
479  }
480  }
481  }
482  }
483  }
484 
485 #endif
486 };
#define SMITH_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc or amdclang and does nothing on ...
Definition: accelerator.hpp:37
#define SMITH_DEVICE
Macro that evaluates to __device__ when compiling with nvcc or amdclang and does nothing on a host co...
Definition: accelerator.hpp:45
constexpr SMITH_HOST_DEVICE auto inv(const isotropic_tensor< T, m, m > &I)
return the inverse of an isotropic tensor
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
constexpr SMITH_HOST_DEVICE auto transpose(const isotropic_tensor< T, m, m > &I)
return the transpose of an isotropic tensor
tensor(const T(&data)[n1]) -> tensor< T, n1 >
class template argument deduction guide for type tensor.
constexpr SMITH_HOST_DEVICE auto det(const isotropic_tensor< T, m, m > &I)
compute the determinant of an isotropic 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