MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_mass_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 mass 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 <bool ACCUMULATE = true>
40MFEM_HOST_DEVICE inline
41void PAMassApplyTriangle_Element(const int e,
42 const int NE,
43 const int BASIS_DIM,
44 const int *lex_map_,
45 const real_t *ba1_,
46 const real_t *ba2_,
47 const real_t *ba1t_,
48 const real_t *ba2t_,
49 const real_t *d_,
50 const real_t *x_,
51 real_t *y_,
52 const int d1d = 0,
53 const int q1d = 0)
54{
55 const int D1D = d1d, Q1D = q1d;
56 constexpr int max_D1D = DofQuadLimits::MAX_D1D_SIMPLEX;
57 constexpr int max_Q1D = DofQuadLimits::MAX_Q1D_SIMPLEX;
58
59 const auto lex_map = DeviceTensor<2,const int>(lex_map_, D1D, D1D);
60 const auto Ba1 = ConstDeviceMatrix(ba1_, D1D, Q1D);
61 const auto Ba2 = ConstDeviceCube(ba2_, D1D, D1D, Q1D);
62 const auto Ba1t = ConstDeviceMatrix(ba1t_, Q1D, D1D);
63 const auto Ba2t = ConstDeviceCube(ba2t_, Q1D, D1D, D1D);
64
65 const auto D = ConstDeviceCube(d_, Q1D, Q1D, NE);
66 const auto X = ConstDeviceMatrix(x_, BASIS_DIM, NE);
67 auto Y = DeviceMatrix(y_, BASIS_DIM, NE);
68
69 if (!ACCUMULATE)
70 {
71 for (int idx = 0; idx < BASIS_DIM; idx++)
72 {
73 Y(idx, e) = 0.0;
74 }
75 }
76
77 // C2 will contain the Bernstein polynomial with coefficients X
78 // evaluated at all of the quadrature nodes in O(p^{d+1}). we have
79 // C2[t1,t2] = \sum_{\alpha} X_{\alpha} * B_{\alpha}^{p}(\Phi(t1,t2)),
80 // where \Phi is the Duffy transformation and (t1,t2) is a Stroud node.
81 real_t C2[max_Q1D * max_Q1D];
82 real_t C1[max_D1D * max_Q1D];
83
84 for (int i1 = 0; i1 < Q1D; i1++)
85 {
86 for (int i2 = 0; i2 < Q1D; i2++)
87 {
88 const int q = i2 + Q1D*i1;
89 C2[q] = 0.0;
90 }
91 for (int a1 = 0; a1 < D1D; a1++)
92 {
93 const int q = a1 + D1D*i1;
94 C1[q] = 0.0;
95 }
96 }
97
98 // quad to dofs operation (i.e. evaluating Bernstein polynomial at all quad nodes),
99 // step 1: convert first quadrature index to first multiindex.
100 for (int i2 = 0; i2 < Q1D; i2++)
101 {
102 for (int a1 = 0; a1 < D1D; a1++)
103 {
104 const int a1i2 = a1 + D1D*i2;
105 for (int a2 = 0; a2 < D1D-a1; a2++)
106 {
107 const int idx = lex_map(a2, a1);
108 C1[a1i2] += X(idx, e) * Ba2(a2, a1, i2);
109 }
110 }
111 }
112 // quad to dofs operation, step 2: convert second quadrature index to second
113 // multiindex. C2 contains the Bernstein polynomial on a triangle with
114 // coefficients X evaluated at all of the Stroud quadrature nodes. E.g. if
115 // (t1,t2) is a Stroud node, then
116 // C2[i,j] = \sum_{\alpha} X_{\alpha} * B_{\alpha}^{p-1}(\Phi(t1,t2)),
117 // where \Phi is the Duffy transform.
118 for (int i1 = 0; i1 < Q1D; i1++)
119 {
120 for (int a1 = 0; a1 < D1D; a1++)
121 {
122 const real_t Bai = Ba1(a1, i1);
123 for (int i2 = 0; i2 < Q1D; i2++)
124 {
125 C2[i2 + Q1D*i1] += C1[a1 + D1D*i2] * Bai;
126 }
127 }
128 }
129 for (int i1 = 0; i1 < Q1D; i1++)
130 {
131 for (int i2 = 0; i2 < Q1D; i2++)
132 {
133 C2[i2 + Q1D*i1] *= D(i1, i2, e);
134 }
135 }
136 // dofs to quad operation (i.e. evaluating all Bernstein moments of the form
137 // \int_{K} B_{\alpha}^{p}(x) * C2(x) dx), step 1: convert first multiindex to
138 // first quadrature index. Note: here, C1 corresponds to F1 in the AAD
139 // algorithm.
140 for (int i2 = 0; i2 < Q1D; i2++)
141 {
142 for (int a1 = 0; a1 < D1D; a1++)
143 {
144 C1[a1 + D1D*i2] = 0.0;
145 }
146 }
147 for (int i1 = 0; i1 < Q1D; i1++)
148 {
149 for (int a1 = 0; a1 < D1D; a1++)
150 {
151 const real_t Bai = Ba1t(i1, a1);
152 for (int i2 = 0; i2 < Q1D; i2++)
153 {
154 C1[i2 + Q1D*a1] += C2[i2 + Q1D*i1] * Bai;
155 }
156 }
157 }
158 // dofs to quad operation, step 2: convert second multiindex to second
159 // quadrature index. The contribution to the local RHS is
160 // Y_{\alpha} = F2_{\alpha}.
161 for (int a1 = 0; a1 < D1D; a1++)
162 {
163 for (int i2 = 0; i2 < Q1D; i2++)
164 {
165 const int a1i2 = i2 + Q1D*a1;
166 for (int a2 = 0; a2 < D1D-a1; a2++)
167 {
168 const int idx = lex_map(a2, a1);
169 Y(idx,e) += C1[a1i2] * Ba2t(i2, a1, a2);
170 }
171 }
172 }
173}
174
175// PA Mass Apply 2D kernel on triangles (Bernstein only)
176template<int T_D1D = 0, int T_Q1D = 0>
177inline void PAMassApplyTriangle(const int NE,
178 const Array<int> &lex_map_,
179 const Array<int> &/*forward_map2d_*/,
180 const Array<int> &/*inverse_map2d_*/,
181 const Array<int> &/*forward_map3d_*/,
182 const Array<int> &/*inverse_map3d_*/,
183 const Array<real_t> &ba1_,
184 const Array<real_t> &ba2_,
185 const Array<real_t> &/*ba3_*/,
186 const Array<real_t> &ba1t_,
187 const Array<real_t> &ba2t_,
188 const Array<real_t> &/*ba3t_*/,
189 const Vector &d_,
190 const Vector &x_,
191 Vector &y_,
192 const int d1d = 0,
193 const int q1d = 0)
194{
195 const int D1D = T_D1D ? T_D1D : d1d;
196 const int Q1D = T_Q1D ? T_Q1D : q1d;
197 const int BASIS_DIM = D1D * (D1D + 1) / 2;
198
199 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX, "");
200 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX, "");
201
202 const auto lex_map = lex_map_.Read();
203 const auto Ba1 = ba1_.Read();
204 const auto Ba2 = ba2_.Read();
205 const auto Ba1t = ba1t_.Read();
206 const auto Ba2t = ba2t_.Read();
207 const auto D = d_.Read();
208 const auto X = x_.Read();
209 auto Y = y_.ReadWrite();
210
211 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
212 {
213 internal::PAMassApplyTriangle_Element(e, NE, BASIS_DIM,
214 lex_map, Ba1, Ba2, Ba1t, Ba2t, D,
215 X, Y,
216 d1d, q1d);
217 });
218}
219
220template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
221MFEM_HOST_DEVICE inline
222void SmemPAMassApplyTriangle_Element(const int e,
223 const int NE,
224 const int *lex_map_,
225 const real_t *ba1_,
226 const real_t *ba2_,
227 const real_t *ba1t_,
228 const real_t *ba2t_,
229 const real_t *d_,
230 const real_t *x_,
231 real_t *y_,
232 const int d1d = 0,
233 const int q1d = 0)
234{
235 const int D1D = T_D1D ? T_D1D : d1d;
236 const int Q1D = T_Q1D ? T_Q1D : q1d;
237
238 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
239 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
240 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
241 constexpr int BASIS_DIM = MD1 * (MD1+1) / 2;
242
243 const auto map = DeviceTensor<2,const int>(lex_map_, D1D, D1D);
244 const auto ba1 = ConstDeviceMatrix(ba1_, D1D, Q1D);
245 const auto ba2 = ConstDeviceCube(ba2_, D1D, D1D, Q1D);
246 const auto ba1t = ConstDeviceMatrix(ba1t_, Q1D, D1D);
247 const auto ba2t = ConstDeviceCube(ba2t_, Q1D, D1D, D1D);
248 const auto D = ConstDeviceCube(d_, Q1D, Q1D, NE);
249 const auto x = ConstDeviceMatrix(x_, BASIS_DIM, NE);
250 auto Y = DeviceMatrix(y_, BASIS_DIM, NE);
251
252 MFEM_SHARED real_t B[2][MQ1*MD1*MD1];
253 auto Ba1 = (real_t (*)[MD1]) (B+0);
254 auto Ba2 = (real_t (*)[MD1][MD1]) (B+1);
255 auto Ba1t = (real_t (*)[MQ1]) (B+0);
256 auto Ba2t = (real_t (*)[MD1][MQ1]) (B+1);
257 MFEM_SHARED real_t Xz[BASIS_DIM];
258 MFEM_SHARED real_t sm0[MDQ*MDQ], sm1[MDQ*MDQ];
259 auto X = (real_t (*)) (Xz);
260 auto DQ = (real_t (*)[MD1]) (sm1);
261 auto QQ = (real_t (*)[MQ1]) (sm0);
262 auto QD = (real_t (*)[MQ1]) (sm1);
263 MFEM_SHARED int s_lex[MD1*MD1];
264 auto lex_map = (int (*)[MD1])(s_lex);
265
266 // load in input vector and basis data
267 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D)
268 {
269 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1)
270 {
271 const int idx = map(a2,a1);
272 lex_map[a1][a2] = idx;
273 X[idx] = x(idx,e);
274 }
275 }
276 MFEM_SYNC_THREAD;
277 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D)
278 {
279 MFEM_FOREACH_THREAD_DIRECT(i1,x,Q1D)
280 {
281 Ba1[i1][a1] = ba1(a1,i1);
282 for (int a2 = 0; a2 < D1D-a1; ++a2)
283 {
284 Ba2[i1][a1][a2] = ba2(a2,a1,i1);
285 }
286 }
287 }
288 MFEM_SYNC_THREAD;
289 // quad to dofs operation, step 1: convert first quadrature index to first
290 // multiindex. DQ corresponds to C1 in the AAD algorithm.
291 MFEM_FOREACH_THREAD_DIRECT(i2,y,Q1D)
292 {
293 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D)
294 {
295 real_t u = 0.0;
296 for (int a2 = 0; a2 < D1D-a1; ++a2)
297 {
298 int idx = lex_map[a1][a2];
299 u += X[idx] * Ba2[i2][a1][a2];
300 }
301 DQ[i2][a1] = u;
302 }
303 }
304 MFEM_SYNC_THREAD;
305 // quad to dofs operation, step 2: convert second quadrature index to second
306 // multiindex. QQ corresponds to C2 in the AAD algorithm, which contains the Bernstein
307 // polynomial on a triangle with coefficients X evaluated at
308 // all of the Stroud quadrature nodes. E.g. if (t1,t2) is a Stroud node, then
309 // C2[i,j] = \sum_{\alpha} X_{\alpha} * B_{\alpha}^{p-1}(\Phi(t1,t2)),
310 // where \Phi is the Duffy transform.
311 MFEM_FOREACH_THREAD_DIRECT(i1,y,Q1D)
312 {
313 MFEM_FOREACH_THREAD_DIRECT(i2,x,Q1D)
314 {
315 real_t u = 0.0;
316 for (int a1 = 0; a1 < D1D; ++a1)
317 {
318 u += DQ[i2][a1] * Ba1[i1][a1];
319 }
320 QQ[i1][i2] = u * D(i1, i2, e);
321 }
322 }
323 MFEM_SYNC_THREAD;
324 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D)
325 {
326 MFEM_FOREACH_THREAD_DIRECT(i1,x,Q1D)
327 {
328 Ba1t[a1][i1] = ba1t(i1,a1);
329 for (int a2 = 0; a2 < D1D-a1; ++a2)
330 {
331 Ba2t[a2][a1][i1] = ba2t(i1,a1,a2);
332 }
333 }
334 }
335 MFEM_SYNC_THREAD;
336 // dofs to quad operation, step 1: convert first multiindex to first quadrature
337 // index. DQ corresponds to F1 in the AAD algorithm, with F0 corresponding to
338 // C2 * D.
339 MFEM_FOREACH_THREAD_DIRECT(i2,y,Q1D)
340 {
341 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D)
342 {
343 real_t u = 0.0;
344 for (int i1 = 0; i1 < Q1D; ++i1)
345 {
346 u += QQ[i1][i2] * Ba1t[a1][i1];
347 }
348 QD[a1][i2] = u;
349 }
350 }
351 MFEM_SYNC_THREAD;
352 // dofs to quad operation, step 2: convert second multiindex to second
353 // quadrature index. u corresponds to F2 in the AAD algorithm.
354 // The contribution to the local RHS is: Y_{\alpha} = F2_{\alpha}.
355 MFEM_FOREACH_THREAD_DIRECT(a1,y,D1D)
356 {
357 MFEM_FOREACH_THREAD_DIRECT(a2,x,D1D-a1)
358 {
359 real_t u = 0.0;
360 for (int i2 = 0; i2 < Q1D; ++i2)
361 {
362 u += QD[a1][i2] * Ba2t[a2][a1][i2];
363 }
364 int idx = lex_map[a1][a2];
365 if (ACCUMULATE)
366 {
367 Y(idx,e) += u;
368 }
369 else
370 {
371 Y(idx,e) = u;
372 }
373 }
374 }
375}
376
377// PA Mass Apply 2D kernel on triangles with shared memory
378template<int T_D1D = 0, int T_Q1D = 0>
379inline void SmemPAMassApplyTriangle(const int NE,
380 const Array<int> &lex_map_,
381 const Array<int> &/*forward_map2d_*/,
382 const Array<int> &/*inverse_map2d_*/,
383 const Array<int> &/*forward_map3d_*/,
384 const Array<int> &/*inverse_map3d_*/,
385 const Array<real_t> &ba1_,
386 const Array<real_t> &ba2_,
387 const Array<real_t> &/*ba3_*/, // unused in 2D...
388 const Array<real_t> &ba1t_,
389 const Array<real_t> &ba2t_,
390 const Array<real_t> &/*ba3t_*/, // unused in 2D...
391 const Vector &d_,
392 const Vector &x_,
393 Vector &y_,
394 const int d1d = 0,
395 const int q1d = 0)
396{
397 const int D1D = T_D1D ? T_D1D : d1d;
398 const int Q1D = T_Q1D ? T_Q1D : q1d;
399
400 const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX;
401 const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX;
402 MFEM_VERIFY(D1D <= max_d1d, "");
403 MFEM_VERIFY(Q1D <= max_q1d, "");
404
405 const auto lex_map = lex_map_.Read();
406 const auto Ba1 = ba1_.Read(), Ba2 = ba2_.Read();
407 const auto Ba1t = ba1t_.Read(), Ba2t = ba2t_.Read();
408 const auto D = d_.Read();
409 const auto X = x_.Read();
410 auto Y = y_.ReadWrite();
411
412 const int T1D = (Q1D > D1D) ? Q1D : D1D;
413 constexpr int T_T1D = (T_Q1D > T_D1D) ? T_Q1D : T_D1D;
414
415 mfem::forall_2D<T_T1D*T_T1D>(NE, T1D, T1D, [=] MFEM_HOST_DEVICE (int e)
416 {
417 internal::SmemPAMassApplyTriangle_Element<T_D1D, T_Q1D>
418 (e, NE, lex_map, Ba1, Ba2, Ba1t, Ba2t, D, X, Y, d1d, q1d);
419 });
420}
421
422/* This function computes the action of the mass integrator for the Bernstein basis on tetrahedrons.
423 The key components are an O(p^{d+1}) routine for evaluating the Bernstein polynomial
424 \sum_{\alpha} c_{\alpha} B_{\alpha}^{p}(x) simultaneously at all quadrature points x
425 (stored in the array C3 and roughly corresponding to Algorithm 1 of [1])and an O(p^{d+1})
426 routine for evaluating the Bernstein moments \int_{K} f(x) * B_{\alpha}^{p}(x) dx for all
427 \alpha (stored in the array F3 and roughly corresponding to Algorithm 3 of [1]).
428
429 [1] Bernstein–Bézier finite elements of arbitrary order and optimal assembly procedures.
430 Ainsworth, M., Andriamaro, G., & Davydov, O. (2011).
431 SIAM Journal on Scientific Computing, 33(6), 3087-3109.
432 */
433template <bool ACCUMULATE = true>
434MFEM_HOST_DEVICE inline
435void PAMassApplyTetrahedron_Element(const int e,
436 const int NE,
437 const int BASIS_DIM,
438 const int BASIS_DIM2D,
439 const int *forward_map2d,
440 const int */*inverse_map2d*/,
441 const int *forward_map3d,
442 const int */*inverse_map3d*/,
443 const real_t *ba1_,
444 const real_t *ba2_,
445 const real_t *ba3_,
446 const real_t *ba1t_,
447 const real_t *ba2t_,
448 const real_t *ba3t_,
449 const real_t *d_,
450 const real_t *x_,
451 real_t *y_,
452 const int d1d = 0,
453 const int q1d = 0)
454{
455 const int D1D = d1d, Q1D = q1d;
456
457 const auto Ba1 = ConstDeviceMatrix(ba1_, D1D, Q1D);
458 const auto Ba2 = ConstDeviceMatrix(ba2_, BASIS_DIM2D, Q1D);
459 const auto Ba3 = ConstDeviceMatrix(ba3_, BASIS_DIM, Q1D);
460 const auto Ba1t = ConstDeviceMatrix(ba1t_, Q1D, D1D);
461 const auto Ba2t = ConstDeviceMatrix(ba2t_, Q1D, BASIS_DIM2D);
462 const auto Ba3t = ConstDeviceMatrix(ba3t_, Q1D, BASIS_DIM);
463 const auto D = DeviceTensor<4,const real_t>(d_, Q1D, Q1D, Q1D, NE);
464 const auto X = ConstDeviceMatrix(x_, BASIS_DIM, NE);
465 auto Y = DeviceMatrix(y_, BASIS_DIM, NE);
466
467 if (!ACCUMULATE)
468 {
469 for (int idx = 0; idx < BASIS_DIM; idx++)
470 {
471 Y(idx, e) = 0.0;
472 }
473 }
474
475 // C3 will contain the Bernstein polynomial with coefficients X
476 // evaluated at all of the quadrature nodes in O(p^{d+1}). we have
477 // C3[t1,t2] = \sum_{\alpha} X_{\alpha} * B_{\alpha}^{p}(\Phi(t1,t2)),
478 // where \Phi is the Duffy transformation and (t1,t2) is a Stroud node.
479
480 // evaluate Bernstein polynomial over the first ragged tensor dimension
481 constexpr int max_D1D = DofQuadLimits::MAX_D1D_SIMPLEX;
482 constexpr int max_Q1D = DofQuadLimits::MAX_Q1D_SIMPLEX;
483 constexpr int BASIS_DIM2D_ = max_D1D * (max_D1D) / 2;
484
485 real_t C3[max_Q1D][max_Q1D][max_Q1D];
486 for (int i3 = 0; i3 < Q1D; i3++)
487 {
488 for (int i2 = 0; i2 < Q1D; i2++)
489 {
490 for (int i1 = 0; i1 < Q1D; i1++)
491 {
492 C3[i3][i2][i1] = 0.0;
493 }
494 }
495 }
496
497 for (int a1 = 0; a1 < D1D; a1++)
498 {
499 real_t C2[max_Q1D][max_Q1D];
500 for (int i3 = 0; i3 < Q1D; i3++)
501 {
502 for (int i2 = 0; i2 < Q1D; i2++)
503 {
504 C2[i3][i2] = 0.0;
505 }
506 }
507
508 for (int a2 = 0; a2 < D1D-a1; a2++)
509 {
510 real_t C1[max_Q1D];
511 for (int i3 = 0; i3 < Q1D; i3++)
512 {
513 C1[i3] = 0.0;
514 }
515
516 for (int a3 = 0; a3 < D1D-a1-a2; a3++)
517 {
518 const int a = forward_map3d[a3 + D1D*(a2 + D1D*a1)];
519 const real_t s = X(a,e);
520 for (int i3 = 0; i3 < Q1D; i3++)
521 {
522 C1[i3] += s * Ba3t(i3,a);
523 }
524 }
525
526 const int a_2d = forward_map2d[a2 + D1D*a1];
527 for (int i3 = 0; i3 < Q1D; i3++)
528 {
529 const real_t s = C1[i3];
530 for (int i2 = 0; i2 < Q1D; i2++)
531 {
532 C2[i3][i2] += Ba2t(i2,a_2d) * s;
533 }
534 }
535 }
536
537 for (int i3 = 0; i3 < Q1D; i3++)
538 {
539 for (int i2 = 0; i2 < Q1D; i2++)
540 {
541 const real_t s = C2[i3][i2];
542 for (int i1 = 0; i1 < Q1D; i1++)
543 {
544 C3[i3][i2][i1] += Ba1t(i1,a1) * s;
545 }
546 }
547 }
548 }
549
550 for (int i3 = 0; i3 < Q1D; i3++)
551 {
552 for (int i2 = 0; i2 < Q1D; i2++)
553 {
554 for (int i1 = 0; i1 < Q1D; i1++)
555 {
556 C3[i3][i2][i1] *= D(i1,i2,i3,e);
557 }
558 }
559 }
560
561 for (int i3 = 0; i3 < Q1D; i3++)
562 {
563 real_t F2[BASIS_DIM2D_];
564 for (int a = 0; a < BASIS_DIM2D; a++)
565 {
566 F2[a] = 0.0;
567 }
568
569 for (int i2 = 0; i2 < Q1D; i2++)
570 {
571 real_t F1[max_D1D];
572 for (int a1 = 0; a1 < D1D; a1++)
573 {
574 F1[a1] = 0.0;
575 }
576
577 for (int i1 = 0; i1 < Q1D; i1++)
578 {
579 const real_t s = C3[i3][i2][i1];
580 for (int a1 = 0; a1 < D1D; a1++)
581 {
582 F1[a1] += Ba1(a1,i1) * s;
583 }
584 }
585
586 for (int a1 = 0; a1 < D1D; a1++)
587 {
588 const real_t s = F1[a1];
589 for (int a2 = 0; a2 < D1D-a1; a2++)
590 {
591 const int a_2d = forward_map2d[a2 + D1D*a1];
592 F2[a_2d] += Ba2(a_2d,i2) * s;
593 }
594 }
595 }
596
597 for (int a1 = 0; a1 < D1D; a1++)
598 {
599 for (int a2 = 0; a2 < D1D-a1; a2++)
600 {
601 const int a_2d = forward_map2d[a2 + D1D*a1];
602 const real_t s = F2[a_2d];
603 for (int a3 = 0; a3 < D1D-a1-a2; a3++)
604 {
605 const int a = forward_map3d[a3 + D1D*(a2 + D1D*a1)];
606 Y(a,e) += Ba3(a,i3) * s;
607 }
608 }
609 }
610 }
611}
612
613// PA Mass Apply 3D kernel on tetrahedrons (Bernstein only)
614template<int T_D1D = 0, int T_Q1D = 0>
615inline void PAMassApplyTetrahedron(const int NE,
616 const Array<int> &/*lex_map_*/,
617 const Array<int> &forward_map2d_,
618 const Array<int> &inverse_map2d_,
619 const Array<int> &forward_map3d_,
620 const Array<int> &inverse_map3d_,
621 const Array<real_t> &ba1_,
622 const Array<real_t> &ba2_,
623 const Array<real_t> &ba3_,
624 const Array<real_t> &ba1t_,
625 const Array<real_t> &ba2t_,
626 const Array<real_t> &ba3t_,
627 const Vector &d_,
628 const Vector &x_,
629 Vector &y_,
630 const int d1d = 0,
631 const int q1d = 0)
632{
633 const int D1D = T_D1D ? T_D1D : d1d;
634 const int Q1D = T_Q1D ? T_Q1D : q1d;
635 const int BASIS_DIM = D1D * (D1D + 1) * (D1D + 2) / 6;
636 const int BASIS_DIM2D = D1D * (D1D + 1) / 2;
637
638 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D_SIMPLEX, "");
639 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D_SIMPLEX, "");
640
641 const auto forward_map2d = forward_map2d_.Read();
642 const auto inverse_map2d = inverse_map2d_.Read();
643 const auto forward_map3d = forward_map3d_.Read();
644 const auto inverse_map3d = inverse_map3d_.Read();
645 const auto Ba1 = ba1_.Read();
646 const auto Ba2 = ba2_.Read();
647 const auto Ba3 = ba3_.Read();
648 const auto Ba1t = ba1t_.Read();
649 const auto Ba2t = ba2t_.Read();
650 const auto Ba3t = ba3t_.Read();
651 const auto D = d_.Read();
652 const auto X = x_.Read();
653 auto Y = y_.ReadWrite();
654
655 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
656 {
657 internal::PAMassApplyTetrahedron_Element(e, NE, BASIS_DIM, BASIS_DIM2D,
658 forward_map2d, inverse_map2d,
659 forward_map3d, inverse_map3d,
660 Ba1, Ba2, Ba3, Ba1t, Ba2t, Ba3t,
661 D, X, Y, d1d, q1d);
662 });
663}
664
665// current optimal version with 2D collapsed loops...
666template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
667MFEM_HOST_DEVICE inline
668void SmemPAMassApplyTetrahedron_Element(const int e,
669 const int NE,
670 const int BASIS_DIM,
671 const int BASIS_DIM2D,
672 // const int *lex_map,
673 const int *forward_map2d_,
674 const int *inverse_map2d_,
675 const int *forward_map3d_,
676 // const int *inverse_map3d_,
677 const real_t *ba1_,
678 const real_t *ba2_,
679 const real_t *ba3_,
680 const real_t *ba1t_,
681 const real_t *ba2t_,
682 const real_t *ba3t_,
683 const real_t *d_,
684 const real_t *x_,
685 real_t *y_,
686 const int d1d = 0,
687 const int q1d = 0)
688{
689 constexpr int D1D = T_D1D ? T_D1D : d1d;
690 constexpr int Q1D = T_Q1D ? T_Q1D : q1d;
691
692 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D_SIMPLEX;
693 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D_SIMPLEX;
694 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
695 constexpr int BASIS_DIM2D_ = MD1 * (MD1 + 1) / 2;
696 constexpr int BASIS_DIM_ = MD1 * (MD1 + 1) * (MD1 + 2) / 6;
697
698 const auto ba1 = ConstDeviceMatrix(ba1_, D1D, Q1D);
699 const auto ba2 = ConstDeviceMatrix(ba2_, BASIS_DIM2D, Q1D);
700 const auto ba3 = ConstDeviceMatrix(ba3_, BASIS_DIM, Q1D);
701 const auto ba1t = ConstDeviceMatrix(ba1t_, Q1D, D1D);
702 const auto ba2t = ConstDeviceMatrix(ba2t_, Q1D, BASIS_DIM2D);
703 const auto ba3t = ConstDeviceMatrix(ba3t_, Q1D, BASIS_DIM);
704 const auto d = DeviceTensor<4,const real_t>(d_, Q1D, Q1D, Q1D, NE);
705 const auto x = ConstDeviceMatrix(x_, BASIS_DIM, NE);
706 auto y = DeviceMatrix(y_, BASIS_DIM, NE);
707
708 const auto forward_map3d__ =
709 DeviceTensor<3,const int>(forward_map3d_, D1D, D1D, D1D);
710 const auto forward_map2d__ =
711 DeviceTensor<2,const int>(forward_map2d_, D1D, D1D);
712 const auto inverse_map2d__ =
713 DeviceTensor<2,const int>(inverse_map2d_, 2, BASIS_DIM2D);
714
715 MFEM_SHARED real_t sDQ[BASIS_DIM_*MQ1];
716 auto Ba1 = (real_t (*)[MD1]) sDQ;
717 auto Ba1t = (real_t (*)[MQ1]) sDQ;
718 auto Ba2 = (real_t (*)[BASIS_DIM2D_]) sDQ;
719 auto Ba2t = (real_t (*)[MQ1]) sDQ;
720 auto Ba3 = (real_t (*)[BASIS_DIM_]) sDQ;
721 auto Ba3t = (real_t (*)[MQ1]) sDQ;
722 MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
723 MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
724 auto X = (real_t (*)) sm0;
725 auto C1 = (real_t (*)[MQ1]) sm1;
726 auto C2 = (real_t (*)[MQ1][MQ1]) sm0;
727 auto C3 = (real_t (*)[MQ1][MQ1]) sm1;
728 auto F1 = (real_t (*)[MQ1][MD1]) sm0;
729 auto F2 = (real_t (*)[MQ1]) sm1;
730 MFEM_SHARED int s3D[MD1*MD1*MD1];
731 MFEM_SHARED int s2D[MD1*MD1];
732 auto forward_map3d = (int (*)[MD1][MD1]) s3D;
733 auto forward_map2d = (int (*)[MD1]) s2D;
734 MFEM_SHARED int s2D_inv[BASIS_DIM2D_*2];
735 auto inverse_map2d = (int (*)[2]) s2D_inv;
736
737 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D)
738 {
739 inverse_map2d[a_2d][0] = inverse_map2d__(0,a_2d);
740 inverse_map2d[a_2d][1] = inverse_map2d__(1,a_2d);
741 const int a1 = inverse_map2d[a_2d][0];
742 const int a2 = inverse_map2d[a_2d][1];
743 const int a_2d_ = forward_map2d__(a2, a1);
744 forward_map2d[a1][a2] = a_2d_;
745 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
746 {
747 MFEM_UNROLL(MD1)
748 for (int a3 = 0; a3 < D1D-a1-a2; ++a3)
749 {
750 const int a = forward_map3d__(a3, a2, a1);
751 forward_map3d[a1][a2][a3] = a;
752 X[a] = x(a,e);
753 Ba3[i3][a] = ba3(a,i3);
754 }
755 }
756 }
757 MFEM_SYNC_THREAD;
758 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D)
759 {
760 const int a1 = inverse_map2d[a_2d][0];
761 const int a2 = inverse_map2d[a_2d][1];
762 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
763 {
764 real_t u = 0.0;
765 MFEM_UNROLL(MD1)
766 for (int a3 = 0; a3 < D1D-a1-a2; ++a3)
767 {
768 const int a = forward_map3d[a1][a2][a3];
769 u += X[a] * Ba3[i3][a];
770 }
771 C1[a_2d][i3] = u;
772 }
773 }
774 MFEM_SYNC_THREAD;
775 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D) // load in Ba2
776 {
777 MFEM_FOREACH_THREAD_DIRECT(i2,x,Q1D)
778 {
779 Ba2[i2][a_2d] = ba2(a_2d,i2);
780 }
781 }
782 MFEM_SYNC_THREAD;
783 MFEM_FOREACH_THREAD_DIRECT(a1i2,y,Q1D*D1D)
784 {
785 const int i2 = a1i2 % Q1D;
786 const int a1 = (int) a1i2 / Q1D;
787 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
788 {
789 real_t u = 0.0;
790 MFEM_UNROLL(MD1)
791 for (int a2 = 0; a2 < D1D-a1; a2++)
792 {
793 const int a_2d = forward_map2d[a1][a2];
794 u += C1[a_2d][i3] * Ba2[i2][a_2d];
795 }
796 C2[a1][i2][i3] = u;
797 }
798 }
799 MFEM_SYNC_THREAD;
800 MFEM_FOREACH_THREAD_DIRECT(a1i1,y,Q1D*D1D) // load in Ba1
801 {
802 const int i1 = a1i1 % Q1D;
803 const int a1 = (int) a1i1 / Q1D;
804 Ba1[i1][a1] = ba1(a1,i1);
805 }
806 MFEM_SYNC_THREAD;
807 MFEM_FOREACH_THREAD_DIRECT(i2i3,y,Q1D*Q1D)
808 {
809 const int i3 = i2i3 % Q1D;
810 const int i2 = (int) i2i3 / Q1D;
811 MFEM_FOREACH_THREAD_DIRECT(i1,x,Q1D)
812 {
813 real_t u = 0.0;
814 MFEM_UNROLL(MD1)
815 for (int a1 = 0; a1 < D1D; a1++)
816 {
817 u += C2[a1][i2][i3] * Ba1[i1][a1];
818 }
819 C3[i2][i3][i1] = u * d(i1,i2,i3,e);
820 }
821 }
822 MFEM_SYNC_THREAD;
823 MFEM_FOREACH_THREAD_DIRECT(a1i1,y,Q1D*D1D) // load in Ba1
824 {
825 const int i1 = a1i1 % Q1D;
826 const int a1 = (int) a1i1 / Q1D;
827 Ba1t[a1][i1] = ba1t(i1,a1);
828 }
829 MFEM_SYNC_THREAD;
830 MFEM_FOREACH_THREAD_DIRECT(i2i3,y,Q1D*Q1D)
831 {
832 const int i3 = i2i3 % Q1D;
833 const int i2 = (int) i2i3 / Q1D;
834 MFEM_FOREACH_THREAD_DIRECT(a1,x,D1D)
835 {
836 real_t u = 0.0;
837 MFEM_UNROLL(MQ1)
838 for (int i1 = 0; i1 < Q1D; i1++)
839 {
840 u += C3[i2][i3][i1] * Ba1t[a1][i1];
841 }
842 F1[i2][i3][a1] = u;
843 }
844 }
845 MFEM_SYNC_THREAD;
846 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D) // load in Ba2
847 {
848 MFEM_FOREACH_THREAD_DIRECT(i2,x,Q1D)
849 {
850 Ba2t[a_2d][i2] = ba2t(i2,a_2d);
851 }
852 }
853 MFEM_SYNC_THREAD;
854 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D)
855 {
856 const int a1 = inverse_map2d[a_2d][0];
857 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
858 {
859 real_t u = 0.0;
860 MFEM_UNROLL(MQ1)
861 for (int i2 = 0; i2 < Q1D; i2++)
862 {
863 u += F1[i2][i3][a1] * Ba2t[a_2d][i2];
864 }
865 F2[a_2d][i3] = u;
866 }
867 }
868 MFEM_SYNC_THREAD;
869 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D) // load in Ba3t
870 {
871 const int a1 = inverse_map2d[a_2d][0];
872 const int a2 = inverse_map2d[a_2d][1];
873 MFEM_FOREACH_THREAD_DIRECT(i3,x,Q1D)
874 {
875 // MFEM_UNROLL(MD1)
876 for (int a3 = 0; a3 < D1D-a1-a2; ++a3)
877 {
878 const int a = forward_map3d[a1][a2][a3];
879 Ba3t[a][i3] = ba3t(i3,a);
880 }
881 }
882 }
883 MFEM_SYNC_THREAD;
884 MFEM_FOREACH_THREAD_DIRECT(a_2d,y,BASIS_DIM2D)
885 {
886 const int a1 = inverse_map2d[a_2d][0];
887 const int a2 = inverse_map2d[a_2d][1];
888 MFEM_FOREACH_THREAD_DIRECT(a3,x,D1D-a1-a2)
889 {
890 real_t u = 0.0;
891 const int a = forward_map3d[a1][a2][a3];
892 MFEM_UNROLL(MQ1)
893 for (int i3 = 0; i3 < Q1D; i3++)
894 {
895 u += F2[a_2d][i3] * Ba3t[a][i3];
896 }
897 if (ACCUMULATE)
898 {
899 y(a,e) += u;
900 }
901 else
902 {
903 y(a,e) = u;
904 }
905 }
906 }
907 MFEM_SYNC_THREAD;
908}
909
910// Shared memory PA Mass Apply 3D Kernel on tetrahedrons (Bernstein only)
911template<int T_D1D = 0, int T_Q1D = 0>
912inline void SmemPAMassApplyTetrahedron(const int NE,
913 const Array<int> &/*lex_map_*/,
914 const Array<int> &forward_map2d_,
915 const Array<int> &inverse_map2d_,
916 const Array<int> &forward_map3d_,
917 const Array<int> &/*inverse_map3d_*/,
918 const Array<real_t> &ba1_,
919 const Array<real_t> &ba2_,
920 const Array<real_t> &ba3_,
921 const Array<real_t> &ba1t_,
922 const Array<real_t> &ba2t_,
923 const Array<real_t> &ba3t_,
924 const Vector &d_,
925 const Vector &x_,
926 Vector &y_,
927 const int d1d = 0,
928 const int q1d = 0)
929{
930 const int D1D = T_D1D ? T_D1D : d1d;
931 const int Q1D = T_Q1D ? T_Q1D : q1d;
932 const int BASIS_DIM = D1D * (D1D + 1) * (D1D + 2) / 6;
933 const int BASIS_DIM2D = D1D * (D1D + 1) / 2;
934
935 constexpr int max_q1d =
937 constexpr int max_d1d =
939 MFEM_VERIFY(D1D <= max_d1d, "");
940 MFEM_VERIFY(Q1D <= max_q1d, "");
941
942 const auto forward_map2d = forward_map2d_.Read();
943 const auto inverse_map2d = inverse_map2d_.Read();
944 const auto forward_map3d = forward_map3d_.Read();
945 const auto Ba1 = ba1_.Read();
946 const auto Ba2 = ba2_.Read();
947 const auto Ba3 = ba3_.Read();
948 const auto Ba1t = ba1t_.Read();
949 const auto Ba2t = ba2t_.Read();
950 const auto Ba3t = ba3t_.Read();
951 const auto D = d_.Read();
952 const auto X = x_.Read();
953 auto Y = y_.ReadWrite();
954
955 const int T1D = (Q1D > D1D) ? Q1D : D1D;
956 constexpr int T_T1D = (T_Q1D > T_D1D) ? T_Q1D : T_D1D;
957
959 [=] MFEM_HOST_DEVICE (int e)
960 {
961 internal::SmemPAMassApplyTetrahedron_Element<T_D1D, T_Q1D>
962 (e, NE, BASIS_DIM, BASIS_DIM2D,
963 forward_map2d, inverse_map2d, forward_map3d,
964 Ba1, Ba2, Ba3, Ba1t, Ba2t, Ba3t, D, X, Y,
965 d1d, q1d);
966 });
967}
968
969} // namespace internal
970
971template<int DIM, int T_D1D, int T_Q1D>
973MassIntegrator::ApplySimplexPAKernels::Kernel()
974{
975 if constexpr (DIM == 2)
976 {
977 return internal::SmemPAMassApplyTriangle<T_D1D,T_Q1D>;
978 }
979 else if constexpr (DIM == 3)
980 {
981 return internal::SmemPAMassApplyTetrahedron<T_D1D, T_Q1D>;
982 }
983 else { MFEM_ABORT(""); }
984 return nullptr;
985}
986
988MassIntegrator::ApplySimplexPAKernels::Fallback(int dim, int, int)
989{
990 if (dim == 2)
991 {
992 return internal::PAMassApplyTriangle;
993 }
994 else if (dim == 3)
995 {
996 return internal::PAMassApplyTetrahedron;
997 }
998 else { MFEM_ABORT(""); }
999 return nullptr;
1000}
1001
1002/// \endcond DO_NOT_DOCUMENT
1003
1004} // namespace mfem
void(*)(const int, 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
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
DeviceTensor< 2, real_t > DeviceMatrix
Definition dtensor.hpp:150
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