MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_diffusion_pa_simplices.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11#pragma once
12
18#include "../bilininteg.hpp"
19
20namespace mfem
21{
22
23/// \cond DO_NOT_DOCUMENT
24
25namespace internal
26{
27
28/* This function computes the action of the diffusion integrator for the Bernstein basis on triangles.
29 The key components are an O(p^{d+1}) routine for evaluating the Bernstein polynomial
30 \sum_{\alpha} c_{\alpha} B_{\alpha}^{p}(x) simultaneously at all quadrature points x
31 (stored in the array C2 and roughly corresponding to Algorithm 1 of [1])and an O(p^{d+1})
32 routine for evaluating the Bernstein moments \int_{K} f(x) * B_{\alpha}^{p}(x) dx for all
33 \alpha (stored in the array F2 and roughly corresponding to Algorithm 3 of [1]).
34
35 [1] Bernstein–Bézier finite elements of arbitrary order and optimal assembly procedures.
36 Ainsworth, M., Andriamaro, G., & Davydov, O. (2011).
37 SIAM Journal on Scientific Computing, 33(6), 3087-3109.
38 */
39template<int T_D1D = 0, int T_Q1D = 0>
40inline void PADiffusionApplyTriangle(const int NE,
41 const bool symmetric,
42 const Array<int> &lex_map_,
43 const Array<int> &/*forward_map2d_*/,
44 const Array<int> &/*inverse_map2d_*/,
45 const Array<int> &/*forward_map3d_*/,
46 const Array<int> &/*inverse_map3d_*/,
47 const Array<real_t> &ga1_,
48 const Array<real_t> &ga2_,
49 const Array<real_t> &/*ga3_*/,
50 const Array<real_t> &ga1t_,
51 const Array<real_t> &ga2t_,
52 const Array<real_t> &/*ga3t_*/,
53 const Vector &d_,
54 const Vector &x_,
55 Vector &y_,
56 const int d1d = 0,
57 const int q1d = 0)
58{
59 const int D1D = T_D1D ? T_D1D : d1d;
60 const int Q1D = T_Q1D ? T_Q1D : q1d;
61 const int BASIS_DIM = D1D * (D1D+1) / 2;
62 const int p2 = (D1D-1) * (D1D-1);
63
64 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX, "");
65 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX, "");
66
67 const auto lex_map = lex_map_.Read();
68 const auto Ga1 = ConstDeviceMatrix(ga1_.Read(), D1D-1, Q1D);
69 const auto Ga2 = ConstDeviceCube(ga2_.Read(), D1D-1, D1D-1, Q1D);
70 const auto Ga1t = ConstDeviceMatrix(ga1t_.Read(), Q1D, D1D-1);
71 const auto Ga2t = ConstDeviceCube(ga2t_.Read(), Q1D, D1D-1, D1D-1);
72 const auto D = Reshape(d_.Read(), Q1D, Q1D, symmetric ? 3 : 4, NE);
73 const auto X = Reshape(x_.Read(), BASIS_DIM, NE);
74 auto Y = Reshape(y_.ReadWrite(), BASIS_DIM, NE);
75
76 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
77 {
78 const int D1D = T_D1D ? T_D1D : d1d;
79 const int Q1D = T_Q1D ? T_Q1D : q1d;
80
81 constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
82 constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
83
84 real_t cin[2 * (max_D1D-1) * (max_D1D-1)];
85 real_t C1[2 * (max_D1D-1) * max_Q1D];
86 real_t C2[2 * max_Q1D * max_Q1D];
87 real_t fin[2 * max_Q1D * max_Q1D];
88 real_t F1[2 * (max_D1D-1) * max_Q1D];
89 real_t F2[2 * (max_D1D-1) * (max_D1D-1)];
90
91 for (int a1 = 0; a1 < D1D-1; ++a1)
92 {
93 for (int a2 = 0; a2 < D1D-1-a1; ++a2)
94 {
95 const int q = 2*(a2 + (D1D-1)*a1);
96 cin[q] = 0.0;
97 cin[1+q] = 0.0;
98 F2[q] = 0.0;
99 F2[1+q] = 0.0;
100 }
101 for (int i2 = 0; i2 < Q1D; ++i2)
102 {
103 const int q = 2*(i2 + Q1D*a1);
104 C1[q] = 0.0;
105 C1[1+q] = 0.0;
106 F1[q] = 0.0;
107 F1[1+q] = 0.0;
108 }
109 }
110 for (int i1 = 0; i1 < Q1D; ++i1)
111 {
112 for (int i2 = 0; i2 < Q1D; ++i2)
113 {
114 const int q = 2*(i2 + Q1D*i1);
115 C2[q] = 0.0;
116 C2[1+q] = 0.0;
117 }
118 }
119
120 // cin contains the vector coefficient
121 // cin_{\beta} = \sum_{k=1}^{3} \nabla\lambda_{k} * X_{\beta + e_{k}},
122 // where \lambda_{k} are the standard barycentric coordinates and e_{k}
123 // is the unit vector with nonzero value in entry k. C2 will contain the
124 // value of the Bernstein polynomial
125 // \sum_{\beta} cin_{\beta} * B_{\beta}^{p-1}(\Phi(t1,t2)),
126 // where \Phi is the Duffy transform and (t1,t2) is a Stroud quadrature node
127 // in the unit square.
128 for (int a1 = 0; a1 < D1D-1; ++a1)
129 {
130 for (int a2 = 0; a2 < D1D-a1-1; ++a2)
131 {
132 // k=0, component 0
133 int idx = lex_map[a2 + D1D*(a1+1)];
134 const int a1a2 = 2*(a1 + (D1D-1)*a2);
135 cin[a1a2] += X(idx, e);
136
137 // // k=1, component 0
138 // idx = lex_map[(a2+1) + D1D*a1];
139 // cin[a1a2] += X(idx, e) * 0.0;
140
141 // k=2, component 0
142 idx = lex_map[a2 + D1D*a1];
143 cin[a1a2] -= X(idx, e);
144
145 // // k=0, component 1
146 // idx = lex_map[a2 + D1D*(a1+1)];
147 // cin[1 + a1a2] += X(idx, e) * 0.0;
148
149 // k=1, component 1
150 idx = lex_map[(a2+1) + D1D*a1];
151 cin[1 + a1a2] += X(idx, e);
152
153 // k=2, component 1
154 idx = lex_map[a2 + D1D*a1];
155 cin[1 + a1a2] -= X(idx, e);
156 }
157 }
158
159 // C1 contains the Bernstein polynomial on a triangle evaluated at the quadrature
160 // point in the first spatial dimension
161 for (int i2 = 0; i2 < Q1D; i2++)
162 {
163 for (int a1 = 0; a1 < D1D-1; a1++)
164 {
165 const int a1i2 = 2*(i2 + Q1D*a1);
166 for (int a2 = 0; a2 < D1D-a1-1; a2++)
167 {
168 const int a1a2 = 2*(a1 + (D1D-1)*a2);
169 const real_t Gai = Ga2t(i2, a1, a2);
170 C1[a1i2] += cin[a1a2] * Gai;
171 C1[1 + a1i2] += cin[1 + a1a2] * Gai;
172 }
173 }
174 }
175
176 // C2 contains the Bernstein polynomial on a triangle with coefficients cin evaluated at
177 // all of the Stroud quadrature nodes. E.g. if (t1,t2) is a Stroud node, then
178 // C2[i,j] = \sum_{\alpha} cin_{\alpha} * B_{\alpha}^{p-1}(\Phi(t1,t2)),
179 // where \Phi is the Duffy transform.
180 for (int i1 = 0; i1 < Q1D; i1++)
181 {
182 for (int a1 = 0; a1 < D1D-1; a1++)
183 {
184 const real_t Gai = Ga1t(i1, a1);
185 for (int i2 = 0; i2 < Q1D; i2++)
186 {
187 const int i1i2 = 2*(i2 + Q1D*i1);
188 const int a1i2 = 2*(i2 + Q1D*a1);
189 C2[i1i2] += C1[a1i2] * Gai;
190 C2[1 + i1i2] += C1[1 + a1i2] * Gai;
191 }
192 }
193 }
194
195 // now evaluate the Bernstein moments
196 // fin contains (B_{K})^{-1} * D(x) * (B_{K})^{-T} * C2(x). the result stored in F2
197 // will be all Bernstein moments
198 // \int_{K} B_{\alpha}^{p-1}(x) * fin(x) dx.
199 for (int i1 = 0; i1 < Q1D; ++i1)
200 {
201 for (int i2 = 0; i2 < Q1D; ++i2)
202 {
203 const real_t O11 = D(i1, i2, 0, e);
204 const real_t O21 = D(i1, i2, 1, e);
205 const real_t O12 = symmetric ? O21 : D(i1, i2, 2, e);
206 const real_t O22 = symmetric ? D(i1, i2, 2, e) : D(i1, i2, 3, e);
207
208 const int i1i2 = 2*(i2 + Q1D*i1);
209 fin[i1i2] = O11 * C2[i1i2] + O12 * C2[1 + i1i2];
210 fin[1 + i1i2] = O21 * C2[i1i2] + O22 * C2[1 + i1i2];
211 }
212 }
213
214 // F1 computes the Bernstein moment over the first ragged tensor dimension.
215 for (int i1 = 0; i1 < Q1D; i1++)
216 {
217 for (int a1 = 0; a1 < D1D-1; a1++)
218 {
219 const real_t Gai = Ga1(a1, i1);
220 for (int i2 = 0; i2 < Q1D; i2++)
221 {
222 const int i1i2 = 2*(i2 + Q1D*i1);
223 const int a1i2 = 2*(i2 + Q1D*a1);
224 F1[a1i2] += fin[i1i2] * Gai;
225 F1[1 + a1i2] += fin[1 + i1i2] * Gai;
226 }
227 }
228 }
229
230 // F2 computes the Bernstein moment over the second/last ragged tensor dimension.
231 for (int i2 = 0; i2 < Q1D; i2++)
232 {
233 for (int a1 = 0; a1 < D1D-1; a1++)
234 {
235 const int a1i2 = 2*(i2 + Q1D*a1);
236 for (int a2 = 0; a2 < D1D-a1-1; a2++)
237 {
238 const int a1a2 = 2*(a2 + (D1D-1)*a1);
239 const real_t Gai = Ga2(a2, a1, i2);
240 F2[a1a2] += F1[a1i2] * Gai;
241 F2[1 + a1a2] += F1[1 + a1i2] * Gai;
242 }
243 }
244 }
245
246 // compute contributions to local RHS. we have
247 // Y_{\alpha + e_{k}} = p^{2} * \nabla\lambda_{k} * F2_{\alpha}
248 // where \lambda_{k} is the kth barycentric coordinate and
249 // e_{k} is the unit vector with nonzero entry k, for k=1,2,3.
250 for (int a1 = 0; a1 < D1D-1; ++a1)
251 {
252 for (int a2 = 0; a2 < D1D-a1-1; ++a2)
253 {
254 // k=0
255 int idx = lex_map[a2 + D1D*(a1+1)];
256 const int a2a1 = 2*(a2 + (D1D-1)*a1);
257 Y(idx,e) += p2 * F2[a2a1];
258
259 // k=1
260 idx = lex_map[(a2+1) + D1D*a1];
261 Y(idx,e) += p2 * F2[1 + a2a1];
262
263 // k=2
264 idx = lex_map[a2 + D1D*a1];
265 Y(idx,e) -= p2 * (F2[a2a1] + F2[1 + a2a1]);
266 }
267 }
268 });
269}
270
271template<int T_D1D = 0, int T_Q1D = 0>
272inline void SmemPADiffusionApplyTriangle(const int NE,
273 const bool symmetric,
274 const Array<int> &lex_map_,
275 const Array<int> &/*forward_map2d_*/,
276 const Array<int> &/*inverse_map2d_*/,
277 const Array<int> &/*forward_map3d_*/,
278 const Array<int> &/*inverse_map3d_*/,
279 const Array<real_t> &ga1_,
280 const Array<real_t> &ga2_,
281 const Array<real_t> &/*ga3_*/,
282 const Array<real_t> &ga1t_,
283 const Array<real_t> &ga2t_,
284 const Array<real_t> &/*ga3t_*/,
285 const Vector &d_,
286 const Vector &x_,
287 Vector &y_,
288 const int d1d = 0,
289 const int q1d = 0)
290{
291 const int D1D = T_D1D ? T_D1D : d1d;
292 const int Q1D = T_Q1D ? T_Q1D : q1d;
293 const int BASIS_DIM = D1D * (D1D+1) / 2;
294 const int p2 = (D1D-1) * (D1D-1);
295
296 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX, "");
297 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX, "");
298
299 const auto map = DeviceTensor<2,const int>(lex_map_.Read(), D1D, D1D);
300 const auto ga1 = ConstDeviceMatrix(ga1_.Read(), D1D-1, Q1D);
301 const auto ga2 = ConstDeviceCube(ga2_.Read(), D1D-1, D1D-1, Q1D);
302 const auto ga1t = ConstDeviceMatrix(ga1t_.Read(), Q1D, D1D-1);
303 const auto ga2t = ConstDeviceCube(ga2t_.Read(), Q1D, D1D-1, D1D-1);
304 const auto D = Reshape(d_.Read(), Q1D, Q1D, symmetric ? 3 : 4, NE);
305 const auto x = Reshape(x_.Read(), BASIS_DIM, NE);
306 auto Y = Reshape(y_.ReadWrite(), BASIS_DIM, NE);
307
308 const int T1D = (Q1D > D1D) ? Q1D : D1D;
309 constexpr int T_T1D = (T_Q1D > T_D1D) ? T_Q1D : T_D1D;
310
311 mfem::forall_2D<T_T1D*T_T1D>(NE, T1D, T1D, [=] MFEM_HOST_DEVICE (int e)
312 {
313 const int tidz = MFEM_THREAD_ID(z);
314 const int D1D = T_D1D ? T_D1D : d1d;
315 const int Q1D = T_Q1D ? T_Q1D : q1d;
316
317 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
318 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
319 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
320 constexpr int BASIS_DIM = MD1 * (MD1+1) / 2;
321
322 MFEM_SHARED real_t sBG[2][MQ1*MD1*MD1];
323 auto Ga1 = (real_t (*)[MD1]) (sBG+0);
324 auto Ga2 = (real_t (*)[MD1][MD1]) (sBG+1);
325 auto Ga1t = (real_t (*)[MQ1]) (sBG+0);
326 auto Ga2t = (real_t (*)[MD1][MQ1]) (sBG+1);
327 MFEM_SHARED real_t Xz[BASIS_DIM];
328 MFEM_SHARED real_t GD[2][MDQ][MDQ];
329 MFEM_SHARED real_t GQ[2][MDQ][MDQ];
330 auto X = (real_t (*))(Xz + tidz);
331 auto DQ0 = (real_t (*)[MD1])(GD[0]);
332 auto DQ1 = (real_t (*)[MD1])(GD[1]);
333 auto QQ0 = (real_t (*)[MQ1])(GQ[0]);
334 auto QQ1 = (real_t (*)[MQ1])(GQ[1]);
335 auto QD0 = (real_t (*)[MQ1])(GD[0]);
336 auto QD1 = (real_t (*)[MQ1])(GD[1]);
337 MFEM_SHARED int s_lex[MD1*MD1];
338 auto lex_map = (int (*)[MD1])(s_lex);
339
340 // load in input vector and basis data
341 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D)
342 {
343 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1)
344 {
345 const int idx = map(a2,a1);
346 lex_map[a1][a2] = idx;
347 X[idx] = x(idx,e);
348 }
349 }
350 MFEM_SYNC_THREAD;
351 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D-1)
352 {
353 MFEM_FOREACH_THREAD_DIRECT(i1,x,Q1D)
354 {
355 Ga1[i1][a1] = ga1(a1,i1);
356 for (int a2 = 0; a2 < D1D-a1-1; a2++)
357 {
358 Ga2[i1][a1][a2] = ga2(a2,a1,i1);
359 }
360 }
361 }
362 MFEM_SYNC_THREAD;
363 // DQ corresponds to C1 in AAD algorithm
364 MFEM_FOREACH_THREAD_DIRECT(i2,y,Q1D)
365 {
366 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D-1)
367 {
368 real_t uu = 0.0, vv = 0.0;
369 for (int a2 = 0; a2 < D1D-a1-1; ++a2)
370 {
371 real_t u = 0.0, v = 0.0;
372 // k=0, component 0
373 int idx = lex_map[a1+1][a2];
374 u += X[idx];
375
376 // k=2, component 0
377 idx = lex_map[a1][a2];
378 u -= X[idx];
379
380 // k=1, component 1
381 idx = lex_map[a1][a2+1];
382 v += X[idx];
383
384 // k=2, component 1
385 idx = lex_map[a1][a2];
386 v -= X[idx];
387
388 const real_t Gai = Ga2[i2][a1][a2];
389 uu += u * Gai;
390 vv += v * Gai;
391 }
392 DQ0[i2][a1] = uu;
393 DQ1[i2][a1] = vv;
394 }
395 }
396 MFEM_SYNC_THREAD;
397 // QQ corresponds to C2 in AAD algorithm
398 MFEM_FOREACH_THREAD_DIRECT(i1,y,Q1D)
399 {
400 MFEM_FOREACH_THREAD_DIRECT(i2,x,Q1D)
401 {
402 real_t u = 0.0, v = 0.0;
403 for (int a1 = 0; a1 < D1D-1; a1++)
404 {
405 const real_t Gai = Ga1[i1][a1];
406 u += DQ0[i2][a1] * Gai;
407 v += DQ1[i2][a1] * Gai;
408 }
409 QQ0[i1][i2] = u;
410 QQ1[i1][i2] = v;
411 }
412 }
413 MFEM_SYNC_THREAD;
414 MFEM_FOREACH_THREAD_DIRECT(i1,y,Q1D)
415 {
416 MFEM_FOREACH_THREAD_DIRECT(i2,x,Q1D)
417 {
418 const real_t O11 = D(i1, i2, 0, e);
419 const real_t O21 = D(i1, i2, 1, e);
420 const real_t O12 = symmetric ? O21 : D(i1, i2, 2, e);
421 const real_t O22 = symmetric ? D(i1, i2, 2, e) : D(i1, i2, 3, e);
422 const real_t gX = QQ0[i1][i2];
423 const real_t gY = QQ1[i1][i2];
424
425 QQ0[i1][i2] = (O11 * gX) + (O12 * gY);
426 QQ1[i1][i2] = (O21 * gX) + (O22 * gY);
427 }
428 }
429 MFEM_SYNC_THREAD;
430 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D-1)
431 {
432 MFEM_FOREACH_THREAD_DIRECT(i1,x,Q1D)
433 {
434 Ga1t[a1][i1] = ga1t(i1,a1);
435 for (int a2 = 0; a2 < D1D-a1-1; a2++)
436 {
437 Ga2t[a2][a1][i1] = ga2t(i1,a1,a2);
438 }
439 }
440 }
441 MFEM_SYNC_THREAD;
442 // DQ corresponds to F1 in AAD algorithm
443 MFEM_FOREACH_THREAD_DIRECT(i2,y,Q1D)
444 {
445 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D-1)
446 {
447 real_t u = 0.0, v = 0.0;
448 for (int i1 = 0; i1 < Q1D; i1++)
449 {
450 u += QQ0[i1][i2] * Ga1t[a1][i1];
451 v += QQ1[i1][i2] * Ga1t[a1][i1];
452 }
453 QD0[a1][i2] = u;
454 QD1[a1][i2] = v;
455 }
456 }
457 MFEM_SYNC_THREAD;
458 // compute F2 from AAD algorithm and add contributions to RHS
459 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D-1)
460 {
461 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1-1)
462 {
463 real_t u = 0.0, v = 0.0;
464 for (int i2 = 0; i2 < Q1D; i2++)
465 {
466 u += QD0[a1][i2] * Ga2t[a2][a1][i2];
467 v += QD1[a1][i2] * Ga2t[a2][a1][i2];
468 }
469 // k=0
470 int idx = lex_map[a1+1][a2];
471 Y(idx,e) += p2 * u;
472
473 // k=1
474 idx = lex_map[a1][a2+1];
475 Y(idx,e) += p2 * v;
476
477 // k=2
478 idx = lex_map[a1][a2];
479 Y(idx,e) -= p2 * (u + v);
480 }
481 }
482 });
483}
484
485/* This function computes the action of the diffusion integrator for the Bernstein basis on tetrahedrons.
486 The key components are an O(p^{d+1}) routine for evaluating the Bernstein polynomial
487 \sum_{\alpha} c_{\alpha} B_{\alpha}^{p}(x) simultaneously at all quadrature points x
488 (stored in the array C3 and roughly corresponding to Algorithm 1 of [1])and an O(p^{d+1})
489 routine for evaluating the Bernstein moments \int_{K} f(x) * B_{\alpha}^{p}(x) dx for all
490 \alpha (stored in the array F3 and roughly corresponding to Algorithm 3 of [1]).
491
492 [1] Bernstein–Bézier finite elements of arbitrary order and optimal assembly procedures.
493 Ainsworth, M., Andriamaro, G., & Davydov, O. (2011).
494 SIAM Journal on Scientific Computing, 33(6), 3087-3109.
495 */
496template<int T_D1D = 0, int T_Q1D = 0>
497inline void PADiffusionApplyTetrahedron(const int NE,
498 const bool symmetric,
499 const Array<int> &lex_map_,
500 const Array<int> &forward_map2d_,
501 const Array<int> &inverse_map2d_,
502 const Array<int> &/*forward_map3d_*/,
503 const Array<int> &inverse_map3d_,
504 const Array<real_t> &ga1_,
505 const Array<real_t> &ga2_,
506 const Array<real_t> &/*ga3_*/,
507 const Array<real_t> &ga1t_,
508 const Array<real_t> &ga2t_,
509 const Array<real_t> &ga3t_,
510 const Vector &d_,
511 const Vector &x_,
512 Vector &y_,
513 const int d1d = 0,
514 const int q1d = 0)
515{
516 const int D1D = T_D1D ? T_D1D : d1d;
517 const int Q1D = T_Q1D ? T_Q1D : q1d;
518 const int BASIS_DIM3D = D1D * (D1D+1) * (D1D+2) / 6;
519 const int BASIS_DIM2D_DIFF = (D1D-1) * D1D / 2;
520 const int BASIS_DIM3D_DIFF = (D1D-1) * D1D * (D1D+1) / 6;
521 const int p2 = (D1D-1) * (D1D-1);
522
523 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX, "");
524 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX, "");
525
526 const auto lex_map = lex_map_.Read();
527 const auto forward_map2d = forward_map2d_.Read();
528 const auto inverse_map2d = inverse_map2d_.Read();
529 const auto inverse_map3d = inverse_map3d_.Read();
530 const auto Ga1 = ConstDeviceMatrix(ga1_.Read(), D1D-1, Q1D);
531 const auto Ga2 = ConstDeviceMatrix(ga2_.Read(), BASIS_DIM2D_DIFF, Q1D);
532 const auto Ga1t = ConstDeviceMatrix(ga1t_.Read(), Q1D, D1D-1);
533 const auto Ga2t = ConstDeviceMatrix(ga2t_.Read(), Q1D, BASIS_DIM2D_DIFF);
534 const auto Ga3t = ConstDeviceMatrix(ga3t_.Read(), Q1D, BASIS_DIM3D_DIFF);
535 const auto D = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
536 const auto X = Reshape(x_.Read(), BASIS_DIM3D, NE);
537 auto Y = Reshape(y_.ReadWrite(), BASIS_DIM3D, NE);
538
539 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
540 {
541
542 const int D1D = T_D1D ? T_D1D : d1d;
543 const int Q1D = T_Q1D ? T_Q1D : q1d;
544
545 constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
546 constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
547
548 constexpr int basis_dim2d = (int) 3 * (max_D1D-1) * (max_D1D) / 2;
549 real_t C1[(int) 3 * basis_dim2d * max_Q1D];
550 real_t C2[3 * (max_D1D-1) * max_Q1D * max_Q1D];
551 real_t C3[3 * max_Q1D * max_Q1D * max_Q1D];
552 real_t F1[3 * (max_D1D-1) * max_Q1D * max_Q1D];
553 real_t F2[(int) 3 * basis_dim2d * max_Q1D];
554
555 for (int i3 = 0; i3 < Q1D; i3++)
556 {
557 for (int i2 = 0; i2 < Q1D; i2++)
558 {
559 for (int i1 = 0; i1 < Q1D; i1++)
560 {
561 const int q = 3*(i1 + Q1D*(i2 + Q1D*i3));
562 C3[q] = 0.0;
563 C3[1+q] = 0.0;
564 C3[2+q] = 0.0;
565 }
566 for (int a1 = 0; a1 < D1D-1; a1++)
567 {
568 const int q = 3*(a1 + (D1D-1)*(i2 + Q1D*i3));
569 C2[q] = 0.0;
570 C2[1+q] = 0.0;
571 C2[2+q] = 0.0;
572 F1[q] = 0.0;
573 F1[1+q] = 0.0;
574 F1[2+q] = 0.0;
575 }
576 }
577
578 for (int a = 0; a < BASIS_DIM2D_DIFF; a++)
579 {
580 const int q = 3*(a + BASIS_DIM2D_DIFF*i3);
581 C1[q] = 0.0;
582 C1[1+q] = 0.0;
583 C1[2+q] = 0.0;
584 F2[q] = 0.0;
585 F2[1+q] = 0.0;
586 F2[2+q] = 0.0;
587 }
588 }
589
590 // C1 contains the Bernstein polynomial on a triangle evaluated at the quadrature
591 // point in the first spatial dimension
592 for (int a = 0; a < BASIS_DIM3D_DIFF; a++)
593 {
594 const int a1 = inverse_map3d[3*a];
595 const int a2 = inverse_map3d[1 + 3*a];
596 const int a3 = inverse_map3d[2 + 3*a];
597 const int a_2d = forward_map2d[a2 + (D1D-1)*a1];
598
599 // aggregate input vector
600 real_t u = 0.0, v = 0.0, w = 0.0;
601 // k=3, component 0
602 int idx = lex_map[a3 + D1D*(a2 + D1D*a1)];
603 u -= X(idx, e);
604
605 // k=3, component 1
606 v -= X(idx, e);
607
608 // k=3, component 2
609 w -= X(idx, e);
610
611 // k=2, component 2
612 idx = lex_map[a3+1 + D1D*(a2 + D1D*a1)];
613 w += X(idx, e);
614
615 // k=1, component 1 (not computed because \nabla\lambda_{k}
616 // component is 0)
617 idx = lex_map[a3 + D1D*(a2+1 + D1D*a1)];
618 v += X(idx, e);
619
620 // k=0, component 0
621 idx = lex_map[a3 + D1D*(a2 + D1D*(a1 + 1))];
622 u += X(idx, e);
623
624 for (int i3 = 0; i3 < Q1D; i3++)
625 {
626 const int a1a2i3 = 3*(i3 + Q1D*a_2d);
627 const real_t Gai = Ga3t(i3,a);
628
629 C1[a1a2i3] += u * Gai;
630 C1[1 + a1a2i3] += v * Gai;
631 C1[2 + a1a2i3] += w * Gai;
632 }
633 }
634
635 // C2 contains the Bernstein polynomial on a triangle evaluated at the quadrature
636 // point in the second spatial dimension
637 for (int a = 0; a < BASIS_DIM2D_DIFF; a++)
638 {
639 const int a1 = inverse_map2d[2*a];
640 for (int i3 = 0; i3 < Q1D; i3++)
641 {
642 const int a1a2i3 = 3*(i3 + Q1D*a);
643 const real_t C1x = C1[a1a2i3];
644 const real_t C1y = C1[1 + a1a2i3];
645 const real_t C1z = C1[2 + a1a2i3];
646 for (int i2 = 0; i2 < Q1D; i2++)
647 {
648 const real_t Gai = Ga2t(i2,a);
649
650 const int a1i2i3 = 3*(i2 + Q1D*(i3 + Q1D*a1));
651 C2[a1i2i3] += C1x * Gai;
652 C2[1 + a1i2i3] += C1y * Gai;
653 C2[2 + a1i2i3] += C1z * Gai;
654 }
655 }
656 }
657
658 for (int a1 = 0; a1 < D1D-1; a1++)
659 {
660 for (int i3 = 0; i3 < Q1D; i3++)
661 {
662 for (int i2 = 0; i2 < Q1D; i2++)
663 {
664 const int a1i2i3 = 3*(i2 + Q1D*(i3 + Q1D*a1));
665 const real_t C2x = C2[a1i2i3];
666 const real_t C2y = C2[1 + a1i2i3];
667 const real_t C2z = C2[2 + a1i2i3];
668 for (int i1 = 0; i1 < Q1D; i1++)
669 {
670 const real_t Gai = Ga1t(i1,a1);
671 const int i1i2i3 = 3*(i1 + Q1D*(i2 + Q1D*i3));
672 C3[i1i2i3] += C2x * Gai;
673 C3[1 + i1i2i3] += C2y * Gai;
674 C3[2 + i1i2i3] += C2z * Gai;
675 }
676 }
677 }
678 }
679
680 // F1 computes the Bernstein moment over the first ragged tensor dimension.
681 for (int i3 = 0; i3 < Q1D; i3++)
682 {
683 for (int i2 = 0; i2 < Q1D; i2++)
684 {
685 for (int i1 = 0; i1 < Q1D; i1++)
686 {
687 const real_t O11 = D(i1,i2,i3,0,e);
688 const real_t O12 = D(i1,i2,i3,1,e);
689 const real_t O13 = D(i1,i2,i3,2,e);
690 const real_t O21 = symmetric ? O12 : D(i1,i2,i3,3,e);
691 const real_t O22 = symmetric ? D(i1,i2,i3,3,e) : D(i1,i2,i3,4,e);
692 const real_t O23 = symmetric ? D(i1,i2,i3,4,e) : D(i1,i2,i3,5,e);
693 const real_t O31 = symmetric ? O13 : D(i1,i2,i3,6,e);
694 const real_t O32 = symmetric ? O23 : D(i1,i2,i3,7,e);
695 const real_t O33 = symmetric ? D(i1,i2,i3,5,e) : D(i1,i2,i3,8,e);
696
697 const int i1i2i3 = 3*(i1 + Q1D*(i2 + Q1D*i3));
698 real_t gX = C3[i1i2i3];
699 real_t gY = C3[1 + i1i2i3];
700 real_t gZ = C3[2 + i1i2i3];
701
702 const real_t fin1 = O11 * gX + O12 * gY + O13 * gZ;
703 const real_t fin2 = O21 * gX + O22 * gY + O23 * gZ;
704 const real_t fin3 = O31 * gX + O32 * gY + O33 * gZ;
705 for (int a1 = 0; a1 < D1D-1; a1++)
706 {
707 const real_t Gai = Ga1(a1,i1);
708 const int a1i2i3 = 3*(a1 + (D1D-1)*(i2 + Q1D*i3));
709 F1[a1i2i3] += fin1 * Gai;
710 F1[1 + a1i2i3] += fin2 * Gai;
711 F1[2 + a1i2i3] += fin3 * Gai;
712 }
713 }
714 }
715 }
716
717 // F2 computes the Bernstein moment over the second ragged tensor dimension.
718 for (int i3 = 0; i3 < Q1D; i3++)
719 {
720 for (int i2 = 0; i2 < Q1D; i2++)
721 {
722 for (int a = 0; a < BASIS_DIM2D_DIFF; a++)
723 {
724 const int a1 = inverse_map2d[2*a];
725 const real_t Gai = Ga2(a,i2);
726
727 const int a1a2i3 = 3*(a + BASIS_DIM2D_DIFF*i3);
728 const int a1i2i3 = 3*(a1 + (D1D-1)*(i2 + Q1D*i3));
729 F2[a1a2i3] += F1[a1i2i3] * Gai;
730 F2[1 + a1a2i3] += F1[1 + a1i2i3] * Gai;
731 F2[2 + a1a2i3] += F1[2 + a1i2i3] * Gai;
732 }
733 }
734 }
735
736 for (int a = 0; a < BASIS_DIM3D_DIFF; a++)
737 {
738 const int a1 = inverse_map3d[3*a];
739 const int a2 = inverse_map3d[1 + 3*a];
740 const int a3 = inverse_map3d[2 + 3*a];
741 const int a_2d = forward_map2d[a2 + (D1D-1)*a1];
742
743 real_t u = 0.0, v = 0.0, w = 0.0;
744 // const int a1a2a3 = 3*(a3 + a1a2);
745 for (int i3 = 0; i3 < Q1D; i3++)
746 {
747 // const int idx = i3 + Q1D*a;
748 const real_t Gai = Ga3t(i3,a);
749 const int a1a2i3 = 3*(a_2d + BASIS_DIM2D_DIFF*i3);
750 u += F2[a1a2i3] * Gai;
751 v += F2[1 + a1a2i3] * Gai;
752 w += F2[2 + a1a2i3] * Gai;
753 }
754
755 // k=3
756 int idx = lex_map[a3 + D1D*(a2 + D1D*a1)];
757 Y(idx,e) -= p2 * (u + v + w);
758
759 // k=0
760 idx = lex_map[a3 + D1D*(a2 + D1D*(a1+1))];
761 Y(idx,e) += p2 * u;
762
763 // k=1
764 idx = lex_map[a3 + D1D*(a2+1 + D1D*a1)];
765 Y(idx,e) += p2 * v;
766
767 // k=2
768 idx = lex_map[a3+1 + D1D*(a2 + D1D*a1)];
769 Y(idx,e) += p2 * w;
770 }
771 });
772}
773
774// collapsed algorithm with bulk loading basis
775template<int T_D1D = 0, int T_Q1D = 0>
776inline void SmemPADiffusionApplyTetrahedron(const int NE,
777 const bool symmetric,
778 const Array<int> &lex_map_,
779 const Array<int> &forward_map2d_,
780 const Array<int> &inverse_map2d_,
781 const Array<int> &forward_map3d_,
782 const Array<int> &/*inverse_map3d_*/,
783 const Array<real_t> &ga1_,
784 const Array<real_t> &ga2_,
785 const Array<real_t> &ga3_,
786 const Array<real_t> &ga1t_,
787 const Array<real_t> &ga2t_,
788 const Array<real_t> &ga3t_,
789 const Vector &d_,
790 const Vector &x_,
791 Vector &y_,
792 const int d1d = 0,
793 const int q1d = 0)
794{
795 const int D1D = T_D1D ? T_D1D : d1d;
796 const int Q1D = T_Q1D ? T_Q1D : q1d;
797 const int BASIS_DIM3D = D1D * (D1D+1) * (D1D+2) / 6;
798 const int BASIS_DIM2D_DIFF = (D1D-1) * D1D / 2;
799 const int BASIS_DIM3D_DIFF = (D1D-1) * D1D * (D1D+1) / 6;
800 const int p2 = (D1D-1) * (D1D-1);
801
802 const int MQ1 = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX;
803 const int MD1 = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX;
804 MFEM_VERIFY(D1D <= MD1, "");
805 MFEM_VERIFY(Q1D <= MQ1, "");
806
807 const auto forward_map3d__ =
808 DeviceTensor<3,const int>(forward_map3d_.Read(), D1D-1, D1D-1, D1D-1);
809 const auto forward_map2d__ =
810 DeviceTensor<2,const int>(forward_map2d_.Read(), D1D-1, D1D-1);
811 const auto inverse_map2d__ =
812 DeviceTensor<2,const int>(inverse_map2d_.Read(), 2, BASIS_DIM2D_DIFF);
813 const auto lex_map__ =
814 DeviceTensor<3,const int>(lex_map_.Read(), D1D, D1D, D1D);
815
816 const auto ga1 = ConstDeviceMatrix(ga1_.Read(), D1D-1, Q1D);
817 const auto ga2 = ConstDeviceMatrix(ga2_.Read(), BASIS_DIM2D_DIFF, Q1D);
818 const auto ga3 = ConstDeviceMatrix(ga3_.Read(), BASIS_DIM3D_DIFF, Q1D);
819 const auto ga1t = ConstDeviceMatrix(ga1t_.Read(), Q1D, D1D-1);
820 const auto ga2t = ConstDeviceMatrix(ga2t_.Read(), Q1D, BASIS_DIM2D_DIFF);
821 const auto ga3t = ConstDeviceMatrix(ga3t_.Read(), Q1D, BASIS_DIM3D_DIFF);
822 const auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
823 const auto x = Reshape(x_.Read(), BASIS_DIM3D, NE);
824 auto y = Reshape(y_.ReadWrite(), BASIS_DIM3D, NE);
825
826 const int T1D = (Q1D > D1D) ? Q1D : D1D;
827 constexpr int T_T1D = (T_Q1D > T_D1D) ? T_Q1D : T_D1D;
828
830 [=] MFEM_HOST_DEVICE (int e)
831 {
832 const int D1D = T_D1D ? T_D1D : d1d;
833 const int Q1D = T_Q1D ? T_Q1D : q1d;
834
835 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
836 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
837 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
838 constexpr int BASIS_DIM2D_DIFF = (MD1 > 1) ? (MD1-1) * MD1 / 2 : 1;
839 constexpr int BASIS_DIM3D_DIFF = (MD1 > 1) ? (MD1-1) * MD1 * (MD1 + 1) / 6 : 1;
840
841 MFEM_SHARED real_t sBG3[BASIS_DIM3D_DIFF*MQ1];
842 MFEM_SHARED real_t sBG2[BASIS_DIM2D_DIFF*MQ1];
843 MFEM_SHARED real_t sBG1[MD1*MQ1];
844 auto Ga1 = (real_t (*)[MD1]) sBG1;
845 auto Ga2 = (real_t (*)[BASIS_DIM2D_DIFF]) sBG2;
846 auto Ga3 = (real_t (*)[BASIS_DIM3D_DIFF]) sBG3;
847 auto Ga1t = (real_t (*)[MQ1]) sBG1;
848 auto Ga2t = (real_t (*)[MQ1]) sBG2;
849 auto Ga3t = (real_t (*)[MQ1]) sBG3;
850 MFEM_SHARED real_t sm0[3][MDQ*MDQ*MDQ];
851 MFEM_SHARED real_t sm1[3][MDQ*MDQ*MDQ];
852 auto X = (real_t (*)) (sm0+0);
853 auto DDQ0 = (real_t (*)[MQ1]) (sm1+0);
854 auto DDQ1 = (real_t (*)[MQ1]) (sm1+1);
855 auto DDQ2 = (real_t (*)[MQ1]) (sm1+2);
856 auto DQQ0 = (real_t (*)[MQ1][MQ1]) (sm0+0);
857 auto DQQ1 = (real_t (*)[MQ1][MQ1]) (sm0+1);
858 auto DQQ2 = (real_t (*)[MQ1][MQ1]) (sm0+2);
859 auto QQQ0 = (real_t (*)[MQ1][MQ1]) (sm1+0);
860 auto QQQ1 = (real_t (*)[MQ1][MQ1]) (sm1+1);
861 auto QQQ2 = (real_t (*)[MQ1][MQ1]) (sm1+2);
862 auto QQD0 = (real_t (*)[MQ1][MQ1]) (sm0+0);
863 auto QQD1 = (real_t (*)[MQ1][MQ1]) (sm0+1);
864 auto QQD2 = (real_t (*)[MQ1][MQ1]) (sm0+2);
865 auto QDD0 = (real_t (*)[MQ1]) (sm1+0);
866 auto QDD1 = (real_t (*)[MQ1]) (sm1+1);
867 auto QDD2 = (real_t (*)[MQ1]) (sm1+2);
868 MFEM_SHARED int s3D[MD1*MD1*MD1];
869 MFEM_SHARED int s3D_lex[MD1*MD1*MD1];
870 MFEM_SHARED int s2D[MD1*MD1];
871 auto forward_map3d = (int (*)[MD1][MD1]) s3D;
872 auto forward_map2d = (int (*)[MD1]) s2D;
873 auto lex_map = (int (*)[MD1][MD1]) s3D_lex;
874 MFEM_SHARED int s2D_inv[BASIS_DIM2D_DIFF*2];
875 auto inverse_map2d = (int (*)[2]) s2D_inv;
876
877 MFEM_FOREACH_THREAD_DIRECT(i3a1,y,Q1D*D1D)
878 {
879 const int i3 = (int) i3a1 / D1D;
880 const int a1 = i3a1 % D1D;
881 if (a1 < D1D-1)
882 {
883 Ga1[i3][a1] = ga1(a1,i3);
884 }
885 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1)
886 {
887 if (a1 < D1D-1 && a2 < D1D-a1-1)
888 {
889 const int a_2d = forward_map2d__(a2, a1);
890 forward_map2d[a1][a2] = a_2d;
891 inverse_map2d[a_2d][0] = inverse_map2d__(0,a_2d);
892 inverse_map2d[a_2d][1] = inverse_map2d__(1,a_2d);
893 Ga2[i3][a_2d] = ga2(a_2d,i3);
894 }
895 MFEM_UNROLL(MD1)
896 for (int a3 = 0; a3 < D1D-a1-a2; a3++)
897 {
898 if (a1 < D1D-1 && a2 < D1D-a1-1 && a3 < D1D-a1-a2-1)
899 {
900 const int a = forward_map3d__(a3, a2, a1);
901 forward_map3d[a1][a2][a3] = a;
902 Ga3[i3][a] = ga3(a,i3);
903 }
904 const int idx = lex_map__(a3, a2, a1);
905 lex_map[a1][a2][a3] = idx;
906 X[idx] = x(idx,e);
907 }
908 }
909 }
910 MFEM_SYNC_THREAD;
911 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
912 {
913 const int a1 = inverse_map2d[a_2d][0];
914 const int a2 = inverse_map2d[a_2d][1];
915 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
916 {
917 real_t uu = 0.0, vv = 0.0, ww = 0.0;
918 MFEM_UNROLL(MD1)
919 for (int a3 = 0; a3 < D1D-a1-a2-1; a3++)
920 {
921 const int a = forward_map3d[a1][a2][a3];
922 real_t u = 0.0, v = 0.0, w = 0.0;
923
924 int idx = lex_map[a1][a2][a3];
925 u -= X[idx];
926 v -= X[idx];
927 w -= X[idx];
928
929 idx = lex_map[a1][a2][a3+1];
930 w += X[idx];
931
932 idx = lex_map[a1][a2+1][a3];
933 v += X[idx];
934
935 idx = lex_map[a1+1][a2][a3];
936 u += X[idx];
937
938 const real_t Gai = Ga3[i3][a];
939 uu += u * Gai;
940 vv += v * Gai;
941 ww += w * Gai;
942 }
943 DDQ0[a_2d][i3] = uu;
944 DDQ1[a_2d][i3] = vv;
945 DDQ2[a_2d][i3] = ww;
946 }
947 }
948 MFEM_SYNC_THREAD;
949 MFEM_FOREACH_THREAD_DIRECT(a1i2,y,Q1D*(D1D-1))
950 {
951 const int a1 = (int) a1i2 / Q1D;
952 const int i2 = a1i2 % Q1D;
953 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
954 {
955 real_t u = 0.0, v = 0.0, w = 0.0;
956 MFEM_UNROLL(MD1)
957 for (int a2 = 0; a2 < D1D-a1-1; a2++)
958 {
959 const int a_2d = forward_map2d[a1][a2];
960 u += DDQ0[a_2d][i3] * Ga2[i2][a_2d];
961 v += DDQ1[a_2d][i3] * Ga2[i2][a_2d];
962 w += DDQ2[a_2d][i3] * Ga2[i2][a_2d];
963 }
964 DQQ0[a1][i2][i3] = u;
965 DQQ1[a1][i2][i3] = v;
966 DQQ2[a1][i2][i3] = w;
967 }
968 }
969 MFEM_SYNC_THREAD;
970 MFEM_FOREACH_THREAD_DIRECT(i1i2,y,Q1D*Q1D)
971 {
972 const int i2 = i1i2 % Q1D;
973 const int i1 = (int) i1i2 / Q1D;
974 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
975 {
976 real_t u = 0.0, v = 0.0, w = 0.0;
977 MFEM_UNROLL(MD1)
978 for (int a1 = 0; a1 < D1D-1; a1++)
979 {
980 u += DQQ0[a1][i2][i3] * Ga1[i1][a1];
981 v += DQQ1[a1][i2][i3] * Ga1[i1][a1];
982 w += DQQ2[a1][i2][i3] * Ga1[i1][a1];
983 }
984 const real_t O11 = d(i1,i2,i3,0,e);
985 const real_t O12 = d(i1,i2,i3,1,e);
986 const real_t O13 = d(i1,i2,i3,2,e);
987 const real_t O21 = symmetric ? O12 : d(i1,i2,i3,3,e);
988 const real_t O22 = symmetric ? d(i1,i2,i3,3,e) : d(i1,i2,i3,4,e);
989 const real_t O23 = symmetric ? d(i1,i2,i3,4,e) : d(i1,i2,i3,5,e);
990 const real_t O31 = symmetric ? O13 : d(i1,i2,i3,6,e);
991 const real_t O32 = symmetric ? O23 : d(i1,i2,i3,7,e);
992 const real_t O33 = symmetric ? d(i1,i2,i3,5,e) : d(i1,i2,i3,8,e);
993 const real_t gX = u;
994 const real_t gY = v;
995 const real_t gZ = w;
996 QQQ0[i1][i2][i3] = O11 * gX + O12 * gY + O13 * gZ;
997 QQQ1[i1][i2][i3] = O21 * gX + O22 * gY + O23 * gZ;
998 QQQ2[i1][i2][i3] = O31 * gX + O32 * gY + O33 * gZ;
999 }
1000 }
1001 MFEM_SYNC_THREAD;
1002 MFEM_FOREACH_THREAD_DIRECT(a1i1,y,Q1D*(D1D-1)) // load in Ba1
1003 {
1004 const int i1 = a1i1 % Q1D;
1005 const int a1 = (int) a1i1 / Q1D;
1006 Ga1t[a1][i1] = ga1t(i1,a1);
1007 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1-1)
1008 {
1009 const int a_2d = forward_map2d[a1][a2];
1010 Ga2t[a_2d][i1] = ga2t(i1,a_2d);
1011
1012 for (int a3 = 0; a3 < D1D-a1-a2-1; a3++)
1013 {
1014 const int a = forward_map3d[a1][a2][a3];
1015 Ga3t[a][i1] = ga3t(i1,a);
1016 }
1017 }
1018 }
1019 MFEM_SYNC_THREAD;
1020 MFEM_FOREACH_THREAD_DIRECT(i2i3,y,Q1D*Q1D)
1021 {
1022 const int i3 = i2i3 % Q1D;
1023 const int i2 = (int) i2i3 / Q1D;
1024 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D-1)
1025 {
1026 real_t u = 0.0, v = 0.0, w = 0.0;
1027 MFEM_UNROLL(MQ1)
1028 for (int i1 = 0; i1 < Q1D; i1++)
1029 {
1030 u += QQQ0[i1][i2][i3] * Ga1t[a1][i1];
1031 v += QQQ1[i1][i2][i3] * Ga1t[a1][i1];
1032 w += QQQ2[i1][i2][i3] * Ga1t[a1][i1];
1033 }
1034 QQD0[a1][i2][i3] = u;
1035 QQD1[a1][i2][i3] = v;
1036 QQD2[a1][i2][i3] = w;
1037 }
1038 }
1039 MFEM_SYNC_THREAD;
1040 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1041 {
1042 const int a1 = inverse_map2d[a_2d][0];
1043 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
1044 {
1045 real_t u = 0.0, v = 0.0, w = 0.0;
1046 MFEM_UNROLL(MQ1)
1047 for (int i2 = 0; i2 < Q1D; i2++)
1048 {
1049 u += QQD0[a1][i2][i3] * Ga2t[a_2d][i2];
1050 v += QQD1[a1][i2][i3] * Ga2t[a_2d][i2];
1051 w += QQD2[a1][i2][i3] * Ga2t[a_2d][i2];
1052 }
1053 QDD0[a_2d][i3] = u;
1054 QDD1[a_2d][i3] = v;
1055 QDD2[a_2d][i3] = w;
1056 }
1057 }
1058 MFEM_SYNC_THREAD;
1059
1060 // compute u,v,w once per (a_2d, a3)
1061 auto uvw = (real_t (*)[BASIS_DIM2D_DIFF][MD1]) sm0;
1062 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1063 {
1064 const int a1 = inverse_map2d[a_2d][0];
1065 const int a2 = inverse_map2d[a_2d][1];
1066 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2-1)
1067 {
1068 real_t u = 0.0, v = 0.0, w = 0.0;
1069 const int a = forward_map3d[a1][a2][a3];
1070 MFEM_UNROLL(MQ1)
1071 for (int i3 = 0; i3 < Q1D; i3++)
1072 {
1073 u += QDD0[a_2d][i3] * Ga3t[a][i3];
1074 v += QDD1[a_2d][i3] * Ga3t[a][i3];
1075 w += QDD2[a_2d][i3] * Ga3t[a][i3];
1076 }
1077 uvw[0][a_2d][a3] = u;
1078 uvw[1][a_2d][a3] = v;
1079 uvw[2][a_2d][a3] = w;
1080 }
1081 }
1082 MFEM_SYNC_THREAD;
1083
1084 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1085 {
1086 const int a1 = inverse_map2d[a_2d][0];
1087 const int a2 = inverse_map2d[a_2d][1];
1088 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2-1)
1089 {
1090 const real_t u = uvw[0][a_2d][a3];
1091 const real_t v = uvw[1][a_2d][a3];
1092 const real_t w = uvw[2][a_2d][a3];
1093 const int idx = lex_map[a1][a2][a3];
1094 y(idx,e) -= p2 * (u + v + w);
1095 }
1096 }
1097 MFEM_SYNC_THREAD;
1098
1099 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1100 {
1101 const int a1 = inverse_map2d[a_2d][0];
1102 const int a2 = inverse_map2d[a_2d][1];
1103 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2-1)
1104 {
1105 const real_t u = uvw[0][a_2d][a3];
1106 const int idx = lex_map[a1+1][a2][a3];
1107 y(idx,e) += p2 * u;
1108 }
1109 }
1110 MFEM_SYNC_THREAD;
1111
1112 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1113 {
1114 const int a1 = inverse_map2d[a_2d][0];
1115 const int a2 = inverse_map2d[a_2d][1];
1116 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2-1)
1117 {
1118 const real_t v = uvw[1][a_2d][a3];
1119 const int idx = lex_map[a1][a2+1][a3];
1120 y(idx,e) += p2 * v;
1121 }
1122 }
1123 MFEM_SYNC_THREAD;
1124
1125 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D_DIFF)
1126 {
1127 const int a1 = inverse_map2d[a_2d][0];
1128 const int a2 = inverse_map2d[a_2d][1];
1129 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2-1)
1130 {
1131 const real_t w = uvw[2][a_2d][a3];
1132 const int idx = lex_map[a1][a2][a3+1];
1133 y(idx,e) += p2 * w;
1134 }
1135 }
1136 });
1137}
1138} // namespace internal
1139
1140template<int DIM, int D1D, int Q1D>
1142DiffusionIntegrator::ApplySimplexPAKernels::Kernel()
1143{
1144 if constexpr (DIM == 2)
1145 {
1146 return internal::SmemPADiffusionApplyTriangle<D1D, Q1D>;
1147 }
1148 else if constexpr (DIM == 3)
1149 {
1150 return internal::SmemPADiffusionApplyTetrahedron<D1D, Q1D>;
1151 }
1152 else { MFEM_ABORT(""); }
1153 return nullptr;
1154}
1155
1157DiffusionIntegrator::ApplySimplexPAKernels::Fallback(int dim, int, int)
1158{
1159 if (dim == 2)
1160 {
1161 return internal::PADiffusionApplyTriangle;
1162 }
1163 else if (dim == 3)
1164 {
1165 return internal::PADiffusionApplyTetrahedron;
1166 }
1167 else { MFEM_ABORT(""); }
1168 return nullptr;
1169}
1170
1171/// \endcond DO_NOT_DOCUMENT
1172
1173} // namespace mfem
1174
void(*)(const int, const bool, const Array< int > &, const Array< int > &, const Array< int > &, const Array< int > &, const Array< int > &, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Vector &, const Vector &, Vector &, const int, const int) ApplySimplexKernelType
int dim
Definition ex24.cpp:53
real_t a
Definition lissajous.cpp:41
constexpr int DIM
mfem::real_t real_t
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
DeviceTensor< 3, const real_t > ConstDeviceCube
Definition dtensor.hpp:154
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:138
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
float real_t
Definition config.hpp:46
DeviceTensor< 2, const real_t > ConstDeviceMatrix
Definition dtensor.hpp:151
void forall(int N, lambda &&body)
Definition forall.hpp:1134
static const DeviceDofQuadLimits & Get()
Return a const reference to the DeviceDofQuadLimits singleton.
Definition forall.hpp:138
int MAX_D1D_SIMPLEX
Maximum number of 1D nodal points for simplices.
Definition forall.hpp:128
int MAX_Q1D_SIMPLEX
Maximum number of 1D quadrature points for simplices.
Definition forall.hpp:129