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;
29 static constexpr
int VALUE = 0, GRADIENT = 1;
30 static constexpr
int SOURCE = 0, FLUX = 1;
32 using dof_type = tensor<double, c, p + 1, p + 1, p + 1>;
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>;
39 SMITH_HOST_DEVICE static constexpr tensor<double, ndof> shape_functions(tensor<double, dim> xi)
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]);
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];
58 SMITH_HOST_DEVICE static constexpr tensor<double, ndof, dim> shape_function_gradients(tensor<double, dim> xi)
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]);
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++) {
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]
96 template <
bool apply_weights,
int q>
97 static constexpr
auto calculate_B()
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];
119 template <
bool apply_weights,
int q>
120 static constexpr
auto calculate_G()
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];
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>&)
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>();
140 int jy = (j % (n * n)) / n;
141 int jz = j / (n * n);
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>{}));
146 tensor<tuple<source_t, flux_t>, q * q * q> output;
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)};
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)));
161 output[Q] = {d00 * phi_j +
dot(d01, dphi_j_dxi), d10 * phi_j +
dot(d11, dphi_j_dxi)};
170 SMITH_HOST_DEVICE static auto interpolate(
const dof_type& X,
const TensorProductQuadratureRule<q>&)
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>();
191 tensor<double, c, q, q, q> value{};
192 tensor<double, c, dim, q, q, q> gradient{};
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);
198 auto A20 = contract<1, 1>(A10, B);
199 auto A21 = contract<1, 1>(A11, B);
200 auto A22 = contract<1, 1>(A10, G);
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);
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;
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);
227 return output.one_dimensional;
230 template <
typename source_type,
typename flux_type,
int q>
232 const TensorProductQuadratureRule<q>&, dof_type* element_residual,
235 if constexpr (is_zero<source_type>{} && is_zero<flux_type>{}) {
239 constexpr
int ntrial =
std::max(
size(source_type{}),
size(flux_type{}) / dim) / c;
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> >;
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>();
248 for (
int j = 0; j < ntrial; j++) {
249 for (
int i = 0; i < c; i++) {
250 s_buffer_type source;
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];
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];
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);
274 auto A10 = contract<1, 0>(A20, B) + contract<1, 0>(A21, G);
275 auto A11 = contract<1, 0>(A22, B);
277 element_residual[j * step](i) += contract<0, 0>(A10, B) + contract<0, 0>(A11, G);
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)
304 int tidx = threadIdx.x % q;
305 int tidy = (threadIdx.x % (q * q)) / q;
306 int tidz = threadIdx.x / (q * q);
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]);
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]);
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) {
335 tuple<tensor<double, c>, tensor<double, c, 3> > qf_input{};
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) {
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);
346 cache.A1(0, dz, dy, qx) = sum[0];
347 cache.A1(1, dz, dy, qx) = sum[1];
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) {
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);
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];
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);
384 get<1>(qf_input) =
dot(get<1>(qf_input),
inv(J));
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,
394 int tidx = threadIdx.x % q;
395 int tidy = (threadIdx.x % (q * q)) / q;
396 int tidz = threadIdx.x / (q * q);
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]);
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]);
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) {
426 auto dv =
det(J) * weights1D[tidx] * weights1D[tidy] * weights1D[tidz];
428 get<0>(response) = get<0>(response) * dv;
431 for (
int i = 0; i < c; i++) {
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;
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));
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) {
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);
463 cache.A1(0, qz, dy, dx) = sum[0];
464 cache.A1(1, qz, dy, dx) = sum[1];
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) {
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);
478 residual(i, dz, dy, dx) += sum;
#define SMITH_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc or amdclang and does nothing on ...
#define SMITH_DEVICE
Macro that evaluates to __device__ when compiling with nvcc or amdclang and does nothing on a host co...
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.
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
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