MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_mass_kernels.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
12#ifndef MFEM_BILININTEG_MASS_KERNELS_HPP
13#define MFEM_BILININTEG_MASS_KERNELS_HPP
14
20#include "../bilininteg.hpp"
21
23
24namespace mfem
25{
26
27/// \cond DO_NOT_DOCUMENT
28
29namespace internal
30{
31
32// PA Mass Diagonal 1D kernel
33inline void PAMassAssembleDiagonal1D(const int NE, const Array<real_t> &b,
34 const Vector &d, Vector &y, const int D1D,
35 const int Q1D)
36{
37 auto B = Reshape(b.Read(), Q1D, D1D);
38 auto D = Reshape(d.Read(), Q1D, NE);
39 auto Y = Reshape(y.ReadWrite(), D1D, NE);
40 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
41 {
42 for (int dx = 0; dx < D1D; ++dx)
43 {
44 for (int qx = 0; qx < Q1D; ++qx)
45 {
46 Y(dx, e) += B(qx, dx) * B(qx, dx) * D(qx, e);
47 }
48 }
49 });
50}
51
52template <bool ACCUMULATE = true>
53MFEM_HOST_DEVICE inline
54void PAMassApply1D_Element(const int e,
55 const int NE,
56 const real_t *b_,
57 const real_t *bt_,
58 const real_t *d_,
59 const real_t *x_,
60 real_t *y_,
61 const int d1d = 0,
62 const int q1d = 0)
63{
64 const int D1D = d1d;
65 const int Q1D = q1d;
66 auto B = ConstDeviceMatrix(b_, Q1D, D1D);
67 auto Bt = ConstDeviceMatrix(bt_, D1D, Q1D);
68 auto D = ConstDeviceMatrix(d_, Q1D, NE);
69 auto X = ConstDeviceMatrix(x_, D1D, NE);
70 auto Y = DeviceMatrix(y_, D1D, NE);
71
72 if (!ACCUMULATE)
73 {
74 for (int dx = 0; dx < D1D; ++dx)
75 {
76 Y(dx, e) = 0.0;
77 }
78 }
79
80 real_t XQ[DofQuadLimits::MAX_Q1D];
81 for (int qx = 0; qx < Q1D; ++qx)
82 {
83 XQ[qx] = 0.0;
84 }
85 for (int dx = 0; dx < D1D; ++dx)
86 {
87 const real_t s = X(dx,e);
88 for (int qx = 0; qx < Q1D; ++qx)
89 {
90 XQ[qx] += B(qx,dx)*s;
91 }
92 }
93 for (int qx = 0; qx < Q1D; ++qx)
94 {
95 const double q = XQ[qx]*D(qx,e);
96 for (int dx = 0; dx < D1D; ++dx)
97 {
98 Y(dx,e) += Bt(dx,qx) * q;
99 }
100 }
101}
102
103// PA Mass Apply 1D kernel
104inline void PAMassApply1D(const int NE, const Array<real_t> &b_,
105 const Array<real_t> &bt_, const Vector &d_,
106 const Vector &x_, Vector &y_, const int d1d = 0,
107 const int q1d = 0)
108{
109 MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
110 MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
111
112 const auto B = b_.Read();
113 const auto Bt = bt_.Read();
114 const auto D = d_.Read();
115 const auto X = x_.Read();
116 auto Y = y_.ReadWrite();
117
118 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
119 {
120 internal::PAMassApply1D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
121 });
122}
123
124// PA Mass Diagonal 2D kernel
125template<int T_D1D = 0, int T_Q1D = 0>
126inline void PAMassAssembleDiagonal2D(const int NE,
127 const Array<real_t> &b,
128 const Vector &d,
129 Vector &y,
130 const int d1d = 0,
131 const int q1d = 0)
132{
133 const int D1D = T_D1D ? T_D1D : d1d;
134 const int Q1D = T_Q1D ? T_Q1D : q1d;
135 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
136 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
137 auto B = Reshape(b.Read(), Q1D, D1D);
138 auto D = Reshape(d.Read(), Q1D, Q1D, NE);
139 auto Y = Reshape(y.ReadWrite(), D1D, D1D, NE);
140 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
141 {
142 const int D1D = T_D1D ? T_D1D : d1d;
143 const int Q1D = T_Q1D ? T_Q1D : q1d;
144 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
145 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
146 real_t QD[MQ1][MD1];
147 for (int qx = 0; qx < Q1D; ++qx)
148 {
149 for (int dy = 0; dy < D1D; ++dy)
150 {
151 QD[qx][dy] = 0.0;
152 for (int qy = 0; qy < Q1D; ++qy)
153 {
154 QD[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
155 }
156 }
157 }
158 for (int dy = 0; dy < D1D; ++dy)
159 {
160 for (int dx = 0; dx < D1D; ++dx)
161 {
162 for (int qx = 0; qx < Q1D; ++qx)
163 {
164 Y(dx,dy,e) += B(qx, dx) * B(qx, dx) * QD[qx][dy];
165 }
166 }
167 }
168 });
169}
170
171namespace mass
172{
173constexpr int ipow(int x, int p) { return p == 0 ? 1 : x*ipow(x, p-1); }
174constexpr int D(int D1D) { return (11 - D1D) / 2; }
175constexpr int NBZ(int D1D)
176{
177 return ipow(2, D(D1D) >= 0 ? D(D1D) : 0);
178}
179constexpr int NBZ3D(int MDQ)
180{
181 return MDQ > 0 ? std::min<int>(
182 (128 + MDQ * MDQ * MDQ - 1) / (MDQ * MDQ * MDQ), 64)
183 : 1;
184}
185}
186
187// Shared memory PA Mass Diagonal 2D kernel
188template<int T_D1D = 0, int T_Q1D = 0>
189inline void SmemPAMassAssembleDiagonal2D(const int NE,
190 const Array<real_t> &b_,
191 const Vector &d_,
192 Vector &y_,
193 const int d1d = 0,
194 const int q1d = 0)
195{
196 static constexpr int T_NBZ = mass::NBZ(T_D1D);
197 static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
198 const int D1D = T_D1D ? T_D1D : d1d;
199 const int Q1D = T_Q1D ? T_Q1D : q1d;
200 const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D;
201 const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
202 MFEM_VERIFY(D1D <= max_d1d, "");
203 MFEM_VERIFY(Q1D <= max_q1d, "");
204 auto b = Reshape(b_.Read(), Q1D, D1D);
205 auto D = Reshape(d_.Read(), Q1D, Q1D, NE);
206 auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
207 mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
208 {
209 const int tidz = MFEM_THREAD_ID(z);
210 const int D1D = T_D1D ? T_D1D : d1d;
211 const int Q1D = T_Q1D ? T_Q1D : q1d;
212 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
213 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
214 MFEM_SHARED real_t B[MQ1][MD1];
215 MFEM_SHARED real_t QDZ[NBZ][MQ1][MD1];
216 real_t (*QD)[MD1] = (real_t (*)[MD1])(QDZ + tidz);
217 if (tidz == 0)
218 {
219 MFEM_FOREACH_THREAD(d,y,D1D)
220 {
221 MFEM_FOREACH_THREAD(q,x,Q1D)
222 {
223 B[q][d] = b(q,d);
224 }
225 }
226 }
227 MFEM_SYNC_THREAD;
228 MFEM_FOREACH_THREAD(qx,x,Q1D)
229 {
230 MFEM_FOREACH_THREAD(dy,y,D1D)
231 {
232 QD[qx][dy] = 0.0;
233 for (int qy = 0; qy < Q1D; ++qy)
234 {
235 QD[qx][dy] += B[qy][dy] * B[qy][dy] * D(qx, qy, e);
236 }
237 }
238 }
239 MFEM_SYNC_THREAD;
240 MFEM_FOREACH_THREAD(dy,y,D1D)
241 {
242 MFEM_FOREACH_THREAD(dx,x,D1D)
243 {
244 for (int qx = 0; qx < Q1D; ++qx)
245 {
246 // might need absolute values on next line
247 Y(dx,dy,e) += B[qx][dx] * B[qx][dx] * QD[qx][dy];
248 }
249 }
250 }
251 });
252}
253
254// PA Mass Diagonal 3D kernel
255template<int T_D1D = 0, int T_Q1D = 0>
256inline void PAMassAssembleDiagonal3D(const int NE,
257 const Array<real_t> &b,
258 const Vector &d,
259 Vector &y,
260 const int d1d = 0,
261 const int q1d = 0)
262{
263 const int D1D = T_D1D ? T_D1D : d1d;
264 const int Q1D = T_Q1D ? T_Q1D : q1d;
265 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
266 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
267 auto B = Reshape(b.Read(), Q1D, D1D);
268 auto D = Reshape(d.Read(), Q1D, Q1D, Q1D, NE);
269 auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, NE);
270 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
271 {
272 const int D1D = T_D1D ? T_D1D : d1d;
273 const int Q1D = T_Q1D ? T_Q1D : q1d;
274 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
275 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
276 real_t QQD[MQ1][MQ1][MD1];
277 real_t QDD[MQ1][MD1][MD1];
278 for (int qx = 0; qx < Q1D; ++qx)
279 {
280 for (int qy = 0; qy < Q1D; ++qy)
281 {
282 for (int dz = 0; dz < D1D; ++dz)
283 {
284 QQD[qx][qy][dz] = 0.0;
285 for (int qz = 0; qz < Q1D; ++qz)
286 {
287 QQD[qx][qy][dz] += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
288 }
289 }
290 }
291 }
292 for (int qx = 0; qx < Q1D; ++qx)
293 {
294 for (int dz = 0; dz < D1D; ++dz)
295 {
296 for (int dy = 0; dy < D1D; ++dy)
297 {
298 QDD[qx][dy][dz] = 0.0;
299 for (int qy = 0; qy < Q1D; ++qy)
300 {
301 QDD[qx][dy][dz] += B(qy, dy) * B(qy, dy) * QQD[qx][qy][dz];
302 }
303 }
304 }
305 }
306 for (int dz = 0; dz < D1D; ++dz)
307 {
308 for (int dy = 0; dy < D1D; ++dy)
309 {
310 for (int dx = 0; dx < D1D; ++dx)
311 {
312 real_t t = 0.0;
313 for (int qx = 0; qx < Q1D; ++qx)
314 {
315 t += B(qx, dx) * B(qx, dx) * QDD[qx][dy][dz];
316 }
317 Y(dx, dy, dz, e) += t;
318 }
319 }
320 }
321 });
322}
323
324// Shared memory PA Mass Diagonal 3D kernel
325template<int T_D1D = 0, int T_Q1D = 0>
326inline void SmemPAMassAssembleDiagonal3D(const int NE,
327 const Array<real_t> &b_,
328 const Vector &d_,
329 Vector &y_,
330 const int d1d = 0,
331 const int q1d = 0)
332{
333 const int D1D = T_D1D ? T_D1D : d1d;
334 const int Q1D = T_Q1D ? T_Q1D : q1d;
335 const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D;
336 const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
337 MFEM_VERIFY(D1D <= max_d1d, "");
338 MFEM_VERIFY(Q1D <= max_q1d, "");
339 auto b = Reshape(b_.Read(), Q1D, D1D);
340 auto D = Reshape(d_.Read(), Q1D, Q1D, Q1D, NE);
341 auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
342 mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
343 {
344 const int tidz = MFEM_THREAD_ID(z);
345 const int D1D = T_D1D ? T_D1D : d1d;
346 const int Q1D = T_Q1D ? T_Q1D : q1d;
347 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
348 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
349 MFEM_SHARED real_t B[MQ1][MD1];
350 MFEM_SHARED real_t QQD[MQ1][MQ1][MD1];
351 MFEM_SHARED real_t QDD[MQ1][MD1][MD1];
352 if (tidz == 0)
353 {
354 MFEM_FOREACH_THREAD(d,y,D1D)
355 {
356 MFEM_FOREACH_THREAD(q,x,Q1D)
357 {
358 B[q][d] = b(q,d);
359 }
360 }
361 }
362 MFEM_SYNC_THREAD;
363 MFEM_FOREACH_THREAD(qx,x,Q1D)
364 {
365 MFEM_FOREACH_THREAD(qy,y,Q1D)
366 {
367 MFEM_FOREACH_THREAD(dz,z,D1D)
368 {
369 QQD[qx][qy][dz] = 0.0;
370 for (int qz = 0; qz < Q1D; ++qz)
371 {
372 QQD[qx][qy][dz] += B[qz][dz] * B[qz][dz] * D(qx, qy, qz, e);
373 }
374 }
375 }
376 }
377 MFEM_SYNC_THREAD;
378 MFEM_FOREACH_THREAD(qx,x,Q1D)
379 {
380 MFEM_FOREACH_THREAD(dz,z,D1D)
381 {
382 MFEM_FOREACH_THREAD(dy,y,D1D)
383 {
384 QDD[qx][dy][dz] = 0.0;
385 for (int qy = 0; qy < Q1D; ++qy)
386 {
387 QDD[qx][dy][dz] += B[qy][dy] * B[qy][dy] * QQD[qx][qy][dz];
388 }
389 }
390 }
391 }
392 MFEM_SYNC_THREAD;
393 MFEM_FOREACH_THREAD(dz,z,D1D)
394 {
395 MFEM_FOREACH_THREAD(dy,y,D1D)
396 {
397 MFEM_FOREACH_THREAD(dx,x,D1D)
398 {
399 real_t t = 0.0;
400 for (int qx = 0; qx < Q1D; ++qx)
401 {
402 t += B[qx][dx] * B[qx][dx] * QDD[qx][dy][dz];
403 }
404 Y(dx, dy, dz, e) += t;
405 }
406 }
407 }
408 });
409}
410
411#ifdef MFEM_USE_OCCA
412// OCCA PA Mass Apply 2D kernel
413void OccaPAMassApply2D(const int D1D,
414 const int Q1D,
415 const int NE,
416 const Array<real_t> &B,
417 const Array<real_t> &Bt,
418 const Vector &D,
419 const Vector &X,
420 Vector &Y);
421
422// OCCA PA Mass Apply 3D kernel
423void OccaPAMassApply3D(const int D1D,
424 const int Q1D,
425 const int NE,
426 const Array<real_t> &B,
427 const Array<real_t> &Bt,
428 const Vector &D,
429 const Vector &X,
430 Vector &Y);
431#endif // MFEM_USE_OCCA
432
433template <bool ACCUMULATE = true>
434MFEM_HOST_DEVICE inline
435void PAMassApply2D_Element(const int e,
436 const int NE,
437 const real_t *b_,
438 const real_t *bt_,
439 const real_t *d_,
440 const real_t *x_,
441 real_t *y_,
442 const int d1d = 0,
443 const int q1d = 0)
444{
445 const int D1D = d1d;
446 const int Q1D = q1d;
447 auto B = ConstDeviceMatrix(b_, Q1D, D1D);
448 auto Bt = ConstDeviceMatrix(bt_, D1D, Q1D);
449 auto D = ConstDeviceCube(d_, Q1D, Q1D, NE);
450 auto X = ConstDeviceCube(x_, D1D, D1D, NE);
451 auto Y = DeviceCube(y_, D1D, D1D, NE);
452
453 if (!ACCUMULATE)
454 {
455 for (int dy = 0; dy < D1D; ++dy)
456 {
457 for (int dx = 0; dx < D1D; ++dx)
458 {
459 Y(dx, dy, e) = 0.0;
460 }
461 }
462 }
463
464 constexpr int max_D1D = DofQuadLimits::MAX_D1D;
465 constexpr int max_Q1D = DofQuadLimits::MAX_Q1D;
466 real_t sol_xy[max_Q1D][max_Q1D];
467 for (int qy = 0; qy < Q1D; ++qy)
468 {
469 for (int qx = 0; qx < Q1D; ++qx)
470 {
471 sol_xy[qy][qx] = 0.0;
472 }
473 }
474 for (int dy = 0; dy < D1D; ++dy)
475 {
476 real_t sol_x[max_Q1D];
477 for (int qy = 0; qy < Q1D; ++qy)
478 {
479 sol_x[qy] = 0.0;
480 }
481 for (int dx = 0; dx < D1D; ++dx)
482 {
483 const real_t s = X(dx,dy,e);
484 for (int qx = 0; qx < Q1D; ++qx)
485 {
486 sol_x[qx] += B(qx,dx)* s;
487 }
488 }
489 for (int qy = 0; qy < Q1D; ++qy)
490 {
491 const real_t d2q = B(qy,dy);
492 for (int qx = 0; qx < Q1D; ++qx)
493 {
494 sol_xy[qy][qx] += d2q * sol_x[qx];
495 }
496 }
497 }
498 for (int qy = 0; qy < Q1D; ++qy)
499 {
500 for (int qx = 0; qx < Q1D; ++qx)
501 {
502 sol_xy[qy][qx] *= D(qx,qy,e);
503 }
504 }
505 for (int qy = 0; qy < Q1D; ++qy)
506 {
507 real_t sol_x[max_D1D];
508 for (int dx = 0; dx < D1D; ++dx)
509 {
510 sol_x[dx] = 0.0;
511 }
512 for (int qx = 0; qx < Q1D; ++qx)
513 {
514 const real_t s = sol_xy[qy][qx];
515 for (int dx = 0; dx < D1D; ++dx)
516 {
517 sol_x[dx] += Bt(dx,qx) * s;
518 }
519 }
520 for (int dy = 0; dy < D1D; ++dy)
521 {
522 const real_t q2d = Bt(dy,qy);
523 for (int dx = 0; dx < D1D; ++dx)
524 {
525 Y(dx,dy,e) += q2d * sol_x[dx];
526 }
527 }
528 }
529}
530
531template<int T_D1D, int T_Q1D, int T_NBZ, bool ACCUMULATE = true>
532MFEM_HOST_DEVICE inline
533void SmemPAMassApply2D_Element(const int e,
534 const int NE,
535 const real_t *b_,
536 const real_t *d_,
537 const real_t *x_,
538 real_t *y_,
539 int d1d = 0,
540 int q1d = 0)
541{
542 const int D1D = T_D1D ? T_D1D : d1d;
543 const int Q1D = T_Q1D ? T_Q1D : q1d;
544 constexpr int NBZ = T_NBZ ? T_NBZ : 1;
545
546 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
547 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
548 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
549
550 auto b = ConstDeviceMatrix(b_, Q1D, D1D);
551 auto D = ConstDeviceCube(d_, Q1D, Q1D, NE);
552 auto x = ConstDeviceCube(x_, D1D, D1D, NE);
553 auto Y = DeviceCube(y_, D1D, D1D, NE);
554
555 const int tidz = MFEM_THREAD_ID(z);
556
557 MFEM_SHARED real_t BBt[MQ1*MD1];
558 real_t (*B)[MD1] = (real_t (*)[MD1]) BBt;
559 real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) BBt;
560 MFEM_SHARED real_t sm0[NBZ][MDQ*MDQ];
561 MFEM_SHARED real_t sm1[NBZ][MDQ*MDQ];
562 real_t (*X)[MD1] = (real_t (*)[MD1]) (sm0 + tidz);
563 real_t (*DQ)[MQ1] = (real_t (*)[MQ1]) (sm1 + tidz);
564 real_t (*QQ)[MQ1] = (real_t (*)[MQ1]) (sm0 + tidz);
565 real_t (*QD)[MD1] = (real_t (*)[MD1]) (sm1 + tidz);
566
567
568 MFEM_FOREACH_THREAD(dy,y,D1D)
569 {
570 MFEM_FOREACH_THREAD(dx,x,D1D)
571 {
572 X[dy][dx] = x(dx,dy,e);
573 }
574 }
575 if (tidz == 0)
576 {
577 MFEM_FOREACH_THREAD(dy,y,D1D)
578 {
579 MFEM_FOREACH_THREAD(q,x,Q1D)
580 {
581 B[q][dy] = b(q,dy);
582 }
583 }
584 }
585 MFEM_SYNC_THREAD;
586 MFEM_FOREACH_THREAD(dy,y,D1D)
587 {
588 MFEM_FOREACH_THREAD(qx,x,Q1D)
589 {
590 real_t dq = 0.0;
591 for (int dx = 0; dx < D1D; ++dx)
592 {
593 dq += X[dy][dx] * B[qx][dx];
594 }
595 DQ[dy][qx] = dq;
596 }
597 }
598 MFEM_SYNC_THREAD;
599 MFEM_FOREACH_THREAD(qy,y,Q1D)
600 {
601 MFEM_FOREACH_THREAD(qx,x,Q1D)
602 {
603 real_t qq = 0.0;
604 for (int dy = 0; dy < D1D; ++dy)
605 {
606 qq += DQ[dy][qx] * B[qy][dy];
607 }
608 QQ[qy][qx] = qq * D(qx, qy, e);
609 }
610 }
611 MFEM_SYNC_THREAD;
612 if (tidz == 0)
613 {
614 MFEM_FOREACH_THREAD(dy,y,D1D)
615 {
616 MFEM_FOREACH_THREAD(q,x,Q1D)
617 {
618 Bt[dy][q] = b(q,dy);
619 }
620 }
621 }
622 MFEM_SYNC_THREAD;
623 MFEM_FOREACH_THREAD(qy,y,Q1D)
624 {
625 MFEM_FOREACH_THREAD(dx,x,D1D)
626 {
627 real_t dq = 0.0;
628 for (int qx = 0; qx < Q1D; ++qx)
629 {
630 dq += QQ[qy][qx] * Bt[dx][qx];
631 }
632 QD[qy][dx] = dq;
633 }
634 }
635 MFEM_SYNC_THREAD;
636 MFEM_FOREACH_THREAD(dy,y,D1D)
637 {
638 MFEM_FOREACH_THREAD(dx,x,D1D)
639 {
640 real_t dd = 0.0;
641 for (int qy = 0; qy < Q1D; ++qy)
642 {
643 dd += (QD[qy][dx] * Bt[dy][qy]);
644 }
645 if (ACCUMULATE)
646 {
647 Y(dx, dy, e) += dd;
648 }
649 else
650 {
651 Y(dx, dy, e) = dd;
652 }
653 }
654 }
655}
656
657template <bool ACCUMULATE = true>
658MFEM_HOST_DEVICE inline
659void PAMassApply3D_Element(const int e,
660 const int NE,
661 const real_t *b_,
662 const real_t *bt_,
663 const real_t *d_,
664 const real_t *x_,
665 real_t *y_,
666 const int d1d,
667 const int q1d)
668{
669 const int D1D = d1d;
670 const int Q1D = q1d;
671 auto B = ConstDeviceMatrix(b_, Q1D, D1D);
672 auto Bt = ConstDeviceMatrix(bt_, D1D, Q1D);
673 auto D = DeviceTensor<4,const real_t>(d_, Q1D, Q1D, Q1D, NE);
674 auto X = DeviceTensor<4,const real_t>(x_, D1D, D1D, D1D, NE);
675 auto Y = DeviceTensor<4,real_t>(y_, D1D, D1D, D1D, NE);
676
677 if (!ACCUMULATE)
678 {
679 for (int dz = 0; dz < D1D; ++dz)
680 {
681 for (int dy = 0; dy < D1D; ++dy)
682 {
683 for (int dx = 0; dx < D1D; ++dx)
684 {
685 Y(dx, dy, dz, e) = 0.0;
686 }
687 }
688 }
689 }
690
691 constexpr int max_D1D = DofQuadLimits::MAX_D1D;
692 constexpr int max_Q1D = DofQuadLimits::MAX_Q1D;
693 real_t sol_xyz[max_Q1D][max_Q1D][max_Q1D];
694 for (int qz = 0; qz < Q1D; ++qz)
695 {
696 for (int qy = 0; qy < Q1D; ++qy)
697 {
698 for (int qx = 0; qx < Q1D; ++qx)
699 {
700 sol_xyz[qz][qy][qx] = 0.0;
701 }
702 }
703 }
704 for (int dz = 0; dz < D1D; ++dz)
705 {
706 real_t sol_xy[max_Q1D][max_Q1D];
707 for (int qy = 0; qy < Q1D; ++qy)
708 {
709 for (int qx = 0; qx < Q1D; ++qx)
710 {
711 sol_xy[qy][qx] = 0.0;
712 }
713 }
714 for (int dy = 0; dy < D1D; ++dy)
715 {
716 real_t sol_x[max_Q1D];
717 for (int qx = 0; qx < Q1D; ++qx)
718 {
719 sol_x[qx] = 0;
720 }
721 for (int dx = 0; dx < D1D; ++dx)
722 {
723 const real_t s = X(dx,dy,dz,e);
724 for (int qx = 0; qx < Q1D; ++qx)
725 {
726 sol_x[qx] += B(qx,dx) * s;
727 }
728 }
729 for (int qy = 0; qy < Q1D; ++qy)
730 {
731 const real_t wy = B(qy,dy);
732 for (int qx = 0; qx < Q1D; ++qx)
733 {
734 sol_xy[qy][qx] += wy * sol_x[qx];
735 }
736 }
737 }
738 for (int qz = 0; qz < Q1D; ++qz)
739 {
740 const real_t wz = B(qz,dz);
741 for (int qy = 0; qy < Q1D; ++qy)
742 {
743 for (int qx = 0; qx < Q1D; ++qx)
744 {
745 sol_xyz[qz][qy][qx] += wz * sol_xy[qy][qx];
746 }
747 }
748 }
749 }
750 for (int qz = 0; qz < Q1D; ++qz)
751 {
752 for (int qy = 0; qy < Q1D; ++qy)
753 {
754 for (int qx = 0; qx < Q1D; ++qx)
755 {
756 sol_xyz[qz][qy][qx] *= D(qx,qy,qz,e);
757 }
758 }
759 }
760 for (int qz = 0; qz < Q1D; ++qz)
761 {
762 real_t sol_xy[max_D1D][max_D1D];
763 for (int dy = 0; dy < D1D; ++dy)
764 {
765 for (int dx = 0; dx < D1D; ++dx)
766 {
767 sol_xy[dy][dx] = 0;
768 }
769 }
770 for (int qy = 0; qy < Q1D; ++qy)
771 {
772 real_t sol_x[max_D1D];
773 for (int dx = 0; dx < D1D; ++dx)
774 {
775 sol_x[dx] = 0;
776 }
777 for (int qx = 0; qx < Q1D; ++qx)
778 {
779 const real_t s = sol_xyz[qz][qy][qx];
780 for (int dx = 0; dx < D1D; ++dx)
781 {
782 sol_x[dx] += Bt(dx,qx) * s;
783 }
784 }
785 for (int dy = 0; dy < D1D; ++dy)
786 {
787 const real_t wy = Bt(dy,qy);
788 for (int dx = 0; dx < D1D; ++dx)
789 {
790 sol_xy[dy][dx] += wy * sol_x[dx];
791 }
792 }
793 }
794 for (int dz = 0; dz < D1D; ++dz)
795 {
796 const real_t wz = Bt(dz,qz);
797 for (int dy = 0; dy < D1D; ++dy)
798 {
799 for (int dx = 0; dx < D1D; ++dx)
800 {
801 Y(dx,dy,dz,e) += wz * sol_xy[dy][dx];
802 }
803 }
804 }
805 }
806}
807
808template <int T_D1D, int T_Q1D, int TBATCH, bool ACCUMULATE = true>
809MFEM_HOST_DEVICE inline void
810SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
811 const real_t *d_, const real_t *x_, real_t *y_,
812 int d1d = 0, int q1d = 0)
813{
814 static_assert(TBATCH > 0, "TBATCH must be positive");
815#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
816 constexpr int tbatch = TBATCH;
817 const int tidz = MFEM_THREAD_ID(z);
818#else
819 // host always batch size 1
820 constexpr int tbatch = 1;
821 constexpr int tidz = 0;
822#endif
823 const int D1D = T_D1D ? T_D1D : d1d;
824 const int Q1D = T_Q1D ? T_Q1D : q1d;
825 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
826 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
827 constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
828
829 auto b = ConstDeviceMatrix(b_, Q1D, D1D);
830 auto d = DeviceTensor<4,const real_t>(d_, Q1D, Q1D, Q1D, NE);
831 auto x = DeviceTensor<4,const real_t>(x_, D1D, D1D, D1D, NE);
832 auto y = DeviceTensor<4,real_t>(y_, D1D, D1D, D1D, NE);
833
834 MFEM_SHARED real_t sDQ[MQ1*MD1];
835 real_t (*B)[MD1] = (real_t (*)[MD1]) sDQ;
836 real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) sDQ;
837 MFEM_SHARED real_t sm0[tbatch][MDQ*MDQ*MDQ];
838 MFEM_SHARED real_t sm1[tbatch][MDQ*MDQ*MDQ];
839 real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+tidz);
840 real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) (sm1+tidz);
841 real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm0+tidz);
842 real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm1+tidz);
843 real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) (sm0+tidz);
844 real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm1+tidz);
845 MFEM_FOREACH_THREAD(dy, y, D1D)
846 {
847 MFEM_FOREACH_THREAD(dx, x, D1D)
848 {
849 MFEM_UNROLL(MD1)
850 for (int dz = 0; dz < D1D; ++dz)
851 {
852 X[dz][dy][dx] = x(dx, dy, dz, e);
853 }
854 }
855 MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
856 }
857 if (tidz == 0)
858 {
859 MFEM_FOREACH_THREAD(dy, y, D1D)
860 {
861 MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
862 }
863 }
864 MFEM_SYNC_THREAD;
865 MFEM_FOREACH_THREAD(dy, y, D1D)
866 {
867 MFEM_FOREACH_THREAD(qx, x, Q1D)
868 {
869 real_t u[D1D];
870 MFEM_UNROLL(MD1)
871 for (int dz = 0; dz < D1D; dz++)
872 {
873 u[dz] = 0;
874 }
875 MFEM_UNROLL(MD1)
876 for (int dx = 0; dx < D1D; ++dx)
877 {
878 MFEM_UNROLL(MD1)
879 for (int dz = 0; dz < D1D; ++dz)
880 {
881 u[dz] += X[dz][dy][dx] * B[qx][dx];
882 }
883 }
884 MFEM_UNROLL(MD1)
885 for (int dz = 0; dz < D1D; ++dz)
886 {
887 DDQ[dz][dy][qx] = u[dz];
888 }
889 }
890 }
891 MFEM_SYNC_THREAD;
892 MFEM_FOREACH_THREAD(qy, y, Q1D)
893 {
894 MFEM_FOREACH_THREAD(qx, x, Q1D)
895 {
896 real_t u[D1D];
897 MFEM_UNROLL(MD1)
898 for (int dz = 0; dz < D1D; dz++)
899 {
900 u[dz] = 0;
901 }
902 MFEM_UNROLL(MD1)
903 for (int dy = 0; dy < D1D; ++dy)
904 {
905 MFEM_UNROLL(MD1)
906 for (int dz = 0; dz < D1D; dz++)
907 {
908 u[dz] += DDQ[dz][dy][qx] * B[qy][dy];
909 }
910 }
911 MFEM_UNROLL(MD1)
912 for (int dz = 0; dz < D1D; dz++)
913 {
914 DQQ[dz][qy][qx] = u[dz];
915 }
916 }
917 }
918 MFEM_SYNC_THREAD;
919 MFEM_FOREACH_THREAD(qy, y, Q1D)
920 {
921 MFEM_FOREACH_THREAD(qx, x, Q1D)
922 {
923 real_t u[Q1D];
924 MFEM_UNROLL(MQ1)
925 for (int qz = 0; qz < Q1D; qz++)
926 {
927 u[qz] = 0;
928 }
929 MFEM_UNROLL(MD1)
930 for (int dz = 0; dz < D1D; ++dz)
931 {
932 MFEM_UNROLL(MQ1)
933 for (int qz = 0; qz < Q1D; qz++)
934 {
935 u[qz] += DQQ[dz][qy][qx] * B[qz][dz];
936 }
937 }
938 MFEM_UNROLL(MQ1)
939 for (int qz = 0; qz < Q1D; qz++)
940 {
941 QQQ[qz][qy][qx] = u[qz] * d(qx, qy, qz, e);
942 }
943 }
944 }
945 MFEM_SYNC_THREAD;
946 if (tidz == 0)
947 {
948 MFEM_FOREACH_THREAD(di, y, D1D)
949 {
950 MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[di][q] = b(q, di); }
951 }
952 }
953 MFEM_SYNC_THREAD;
954 MFEM_FOREACH_THREAD(qy, y, Q1D)
955 {
956 MFEM_FOREACH_THREAD(dx, x, D1D)
957 {
958 real_t u[Q1D];
959 MFEM_UNROLL(MQ1)
960 for (int qz = 0; qz < Q1D; ++qz)
961 {
962 u[qz] = 0;
963 }
964 MFEM_UNROLL(MQ1)
965 for (int qx = 0; qx < Q1D; ++qx)
966 {
967 MFEM_UNROLL(MQ1)
968 for (int qz = 0; qz < Q1D; ++qz)
969 {
970 u[qz] += QQQ[qz][qy][qx] * Bt[dx][qx];
971 }
972 }
973 MFEM_UNROLL(MQ1)
974 for (int qz = 0; qz < Q1D; ++qz)
975 {
976 QQD[qz][qy][dx] = u[qz];
977 }
978 }
979 }
980 MFEM_SYNC_THREAD;
981 MFEM_FOREACH_THREAD(dy, y, D1D)
982 {
983 MFEM_FOREACH_THREAD(dx, x, D1D)
984 {
985 real_t u[Q1D];
986 MFEM_UNROLL(MQ1)
987 for (int qz = 0; qz < Q1D; ++qz)
988 {
989 u[qz] = 0;
990 }
991 MFEM_UNROLL(MQ1)
992 for (int qy = 0; qy < Q1D; ++qy)
993 {
994 MFEM_UNROLL(MQ1)
995 for (int qz = 0; qz < Q1D; ++qz)
996 {
997 u[qz] += QQD[qz][qy][dx] * Bt[dy][qy];
998 }
999 }
1000 MFEM_UNROLL(MQ1)
1001 for (int qz = 0; qz < Q1D; ++qz)
1002 {
1003 QDD[qz][dy][dx] = u[qz];
1004 }
1005 }
1006 }
1007 MFEM_SYNC_THREAD;
1008 MFEM_FOREACH_THREAD(dy, y, D1D)
1009 {
1010 MFEM_FOREACH_THREAD(dx, x, D1D)
1011 {
1012 real_t u[D1D];
1013 MFEM_UNROLL(MD1)
1014 for (int dz = 0; dz < D1D; ++dz)
1015 {
1016 u[dz] = 0;
1017 }
1018 MFEM_UNROLL(MQ1)
1019 for (int qz = 0; qz < Q1D; ++qz)
1020 {
1021 MFEM_UNROLL(MD1)
1022 for (int dz = 0; dz < D1D; ++dz)
1023 {
1024 u[dz] += QDD[qz][dy][dx] * Bt[dz][qz];
1025 }
1026 }
1027 MFEM_UNROLL(MD1)
1028 for (int dz = 0; dz < D1D; ++dz)
1029 {
1030 if (ACCUMULATE)
1031 {
1032 y(dx, dy, dz, e) += u[dz];
1033 }
1034 else
1035 {
1036 y(dx, dy, dz, e) = u[dz];
1037 }
1038 }
1039 }
1040 }
1041 MFEM_SYNC_THREAD;
1042}
1043
1044// PA Mass Apply 2D kernel
1045template<int T_D1D = 0, int T_Q1D = 0>
1046inline void PAMassApply2D(const int NE,
1047 const Array<real_t> &b_,
1048 const Array<real_t> &bt_,
1049 const Vector &d_,
1050 const Vector &x_,
1051 Vector &y_,
1052 const int d1d = 0,
1053 const int q1d = 0)
1054{
1055 MFEM_VERIFY(T_D1D ? T_D1D : d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
1056 MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
1057
1058 const auto B = b_.Read();
1059 const auto Bt = bt_.Read();
1060 const auto D = d_.Read();
1061 const auto X = x_.Read();
1062 auto Y = y_.ReadWrite();
1063
1064 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1065 {
1066 internal::PAMassApply2D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
1067 });
1068}
1069
1070// Shared memory PA Mass Apply 2D kernel
1071template<int T_D1D = 0, int T_Q1D = 0>
1072inline void SmemPAMassApply2D(const int NE,
1073 const Array<real_t> &b_,
1074 const Array<real_t> &bt_,
1075 const Vector &d_,
1076 const Vector &x_,
1077 Vector &y_,
1078 const int d1d = 0,
1079 const int q1d = 0)
1080{
1081 MFEM_CONTRACT_VAR(bt_);
1082 static constexpr int T_NBZ = mass::NBZ(T_D1D);
1083 static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
1084 const int D1D = T_D1D ? T_D1D : d1d;
1085 const int Q1D = T_Q1D ? T_Q1D : q1d;
1086 const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D;
1087 const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
1088 MFEM_VERIFY(D1D <= max_d1d, "");
1089 MFEM_VERIFY(Q1D <= max_q1d, "");
1090 const auto b = b_.Read();
1091 const auto D = d_.Read();
1092 const auto x = x_.Read();
1093 auto Y = y_.ReadWrite();
1094 mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
1095 {
1096 internal::SmemPAMassApply2D_Element<T_D1D,T_Q1D,T_NBZ>(
1097 e, NE, b, D, x, Y, d1d, q1d);
1098 });
1099}
1100
1101// PA Mass Apply 3D kernel
1102template<int T_D1D = 0, int T_Q1D = 0>
1103inline void PAMassApply3D(const int NE,
1104 const Array<real_t> &b_,
1105 const Array<real_t> &bt_,
1106 const Vector &d_,
1107 const Vector &x_,
1108 Vector &y_,
1109 const int d1d = 0,
1110 const int q1d = 0)
1111{
1112 MFEM_VERIFY(T_D1D ? T_D1D : d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
1113 MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
1114
1115 const auto B = b_.Read();
1116 const auto Bt = bt_.Read();
1117 const auto D = d_.Read();
1118 const auto X = x_.Read();
1119 auto Y = y_.ReadWrite();
1120
1121 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1122 {
1123 internal::PAMassApply3D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
1124 });
1125}
1126
1127// Shared memory PA Mass Apply 3D kernel
1128template<int T_D1D = 0, int T_Q1D = 0, int TBATCH=1>
1129inline void SmemPAMassApply3D(const int NE,
1130 const Array<real_t> &b_,
1131 const Array<real_t> &bt_,
1132 const Vector &d_,
1133 const Vector &x_,
1134 Vector &y_,
1135 const int d1d = 0,
1136 const int q1d = 0)
1137{
1138 static_assert(T_D1D > 0, "T_D1D must be positive");
1139 static_assert(T_Q1D > 0, "T_Q1D must be positive");
1140 static_assert(TBATCH > 0, "TBATCH must be positive");
1141 MFEM_CONTRACT_VAR(bt_);
1142 const int D1D = T_D1D ? T_D1D : d1d;
1143 const int Q1D = T_Q1D ? T_Q1D : q1d;
1144 const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D;
1145 const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
1146 MFEM_VERIFY(D1D <= max_d1d, "");
1147 MFEM_VERIFY(Q1D <= max_q1d, "");
1148 const auto b = b_.Read();
1149 const auto d = d_.Read();
1150 const auto x = x_.Read();
1151 auto y = y_.ReadWrite();
1153 [=] MFEM_HOST_DEVICE(int e)
1154 {
1155 internal::SmemPAMassApply3D_Element<T_D1D, T_Q1D, TBATCH>(e, NE, b, d, x,
1156 y, d1d, q1d);
1157 });
1158}
1159
1160template<int T_D1D = 0, int T_Q1D = 0>
1161inline void EAMassAssemble1D(const int NE,
1162 const Array<real_t> &basis,
1163 const Vector &padata,
1164 Vector &eadata,
1165 const bool add,
1166 const int d1d = 0,
1167 const int q1d = 0)
1168{
1169 const int D1D = T_D1D ? T_D1D : d1d;
1170 const int Q1D = T_Q1D ? T_Q1D : q1d;
1171 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
1172 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
1173 const auto B = Reshape(basis.Read(), Q1D, D1D);
1174 const auto D = Reshape(padata.Read(), Q1D, NE);
1175 auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, NE);
1176 mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
1177 {
1178 const int D1D = T_D1D ? T_D1D : d1d;
1179 const int Q1D = T_Q1D ? T_Q1D : q1d;
1180 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
1181 MFEM_FOREACH_THREAD(i1,x,D1D)
1182 {
1183 real_t r_Bi[MQ1];
1184 for (int q = 0; q < Q1D; q++) { r_Bi[q] = B(q,i1); }
1185 MFEM_FOREACH_THREAD(j1,y,D1D)
1186 {
1187 real_t r_Bj[MQ1];
1188 for (int q = 0; q < Q1D; q++) { r_Bj[q] = B(q,j1); }
1189
1190 real_t val = 0.0;
1191 for (int k1 = 0; k1 < Q1D; ++k1)
1192 {
1193 val += r_Bi[k1] * r_Bj[k1] * D(k1, e);
1194 }
1195 if (add)
1196 {
1197 M(i1, j1, e) += val;
1198 }
1199 else
1200 {
1201 M(i1, j1, e) = val;
1202 }
1203 }
1204 }
1205 });
1206}
1207
1208template<int T_D1D = 0, int T_Q1D = 0>
1209inline void EAMassAssemble2D(const int NE,
1210 const Array<real_t> &basis,
1211 const Vector &padata,
1212 Vector &eadata,
1213 const bool add,
1214 const int d1d = 0,
1215 const int q1d = 0)
1216{
1217 const int D1D = T_D1D ? T_D1D : d1d;
1218 const int Q1D = T_Q1D ? T_Q1D : q1d;
1219 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
1220 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
1221 auto B = Reshape(basis.Read(), Q1D, D1D);
1222 auto D = Reshape(padata.Read(), Q1D, Q1D, NE);
1223 auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, D1D, D1D,
1224 NE);
1225 mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
1226 {
1227 const int D1D = T_D1D ? T_D1D : d1d;
1228 const int Q1D = T_Q1D ? T_Q1D : q1d;
1229 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
1230 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
1231 real_t r_B[MQ1][MD1];
1232 for (int d = 0; d < D1D; d++)
1233 {
1234 for (int q = 0; q < Q1D; q++)
1235 {
1236 r_B[q][d] = B(q,d);
1237 }
1238 }
1239 MFEM_SHARED real_t s_D[MQ1][MQ1];
1240 MFEM_FOREACH_THREAD(k1,x,Q1D)
1241 {
1242 MFEM_FOREACH_THREAD(k2,y,Q1D)
1243 {
1244 s_D[k1][k2] = D(k1,k2,e);
1245 }
1246 }
1247 MFEM_SYNC_THREAD;
1248 MFEM_FOREACH_THREAD(i1,x,D1D)
1249 {
1250 MFEM_FOREACH_THREAD(i2,y,D1D)
1251 {
1252 for (int j1 = 0; j1 < D1D; ++j1)
1253 {
1254 for (int j2 = 0; j2 < D1D; ++j2)
1255 {
1256 real_t val = 0.0;
1257 for (int k1 = 0; k1 < Q1D; ++k1)
1258 {
1259 for (int k2 = 0; k2 < Q1D; ++k2)
1260 {
1261 val += r_B[k1][i1] * r_B[k1][j1]
1262 * r_B[k2][i2] * r_B[k2][j2]
1263 * s_D[k1][k2];
1264 }
1265 }
1266 if (add)
1267 {
1268 M(i1, i2, j1, j2, e) += val;
1269 }
1270 else
1271 {
1272 M(i1, i2, j1, j2, e) = val;
1273 }
1274 }
1275 }
1276 }
1277 }
1278 });
1279}
1280
1281template<int T_D1D = 0, int T_Q1D = 0>
1282inline void EAMassAssemble3D(const int NE,
1283 const Array<real_t> &basis,
1284 const Vector &padata,
1285 Vector &eadata,
1286 const bool add,
1287 const int d1d = 0,
1288 const int q1d = 0)
1289{
1290 const int D1D = T_D1D ? T_D1D : d1d;
1291 const int Q1D = T_Q1D ? T_Q1D : q1d;
1292 MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
1293 MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
1294 auto B = Reshape(basis.Read(), Q1D, D1D);
1295 auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, NE);
1296 auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, D1D, D1D,
1297 D1D, D1D, NE);
1298 mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
1299 {
1300 const int D1D = T_D1D ? T_D1D : d1d;
1301 const int Q1D = T_Q1D ? T_Q1D : q1d;
1302 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
1303 constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
1304 constexpr int DQ = T_D1D * T_Q1D;
1305
1306 // For quadratic and lower it's better to use registers but for higher-order you start to
1307 // spill and it's better to use shared memory
1308 constexpr bool USE_REG = DQ != 0 && DQ <= 12;
1309 constexpr int MD1r = USE_REG ? MD1 : 1;
1310 constexpr int MQ1r = USE_REG ? MQ1 : 1;
1311 constexpr int MD1s = USE_REG ? 1 : MD1;
1312 constexpr int MQ1s = USE_REG ? 1 : MQ1;
1313
1314 MFEM_SHARED real_t s_B[MQ1s][MD1s];
1315 real_t r_B[MQ1r][MD1r];
1316 real_t (*l_B)[MD1] = nullptr;
1317 if (USE_REG)
1318 {
1319 for (int d = 0; d < D1D; d++)
1320 {
1321 for (int q = 0; q < Q1D; q++)
1322 {
1323 r_B[q][d] = B(q,d);
1324 }
1325 }
1326 l_B = (real_t (*)[MD1])r_B;
1327 }
1328 else
1329 {
1330 if (MFEM_THREAD_ID(z) == 0)
1331 {
1332 MFEM_FOREACH_THREAD(d,x,D1D)
1333 {
1334 MFEM_FOREACH_THREAD(q,y,Q1D)
1335 {
1336 s_B[q][d] = B(q,d);
1337 }
1338 }
1339 }
1340 l_B = (real_t (*)[MD1])s_B;
1341 }
1342
1343 MFEM_SHARED real_t s_D[MQ1][MQ1][MQ1];
1344 MFEM_FOREACH_THREAD(k1,x,Q1D)
1345 {
1346 MFEM_FOREACH_THREAD(k2,y,Q1D)
1347 {
1348 MFEM_FOREACH_THREAD(k3,z,Q1D)
1349 {
1350 s_D[k1][k2][k3] = D(k1,k2,k3,e);
1351 }
1352 }
1353 }
1354 MFEM_SYNC_THREAD;
1355 MFEM_FOREACH_THREAD(i1,x,D1D)
1356 {
1357 MFEM_FOREACH_THREAD(i2,y,D1D)
1358 {
1359 MFEM_FOREACH_THREAD(i3,z,D1D)
1360 {
1361 for (int j1 = 0; j1 < D1D; ++j1)
1362 {
1363 for (int j2 = 0; j2 < D1D; ++j2)
1364 {
1365 for (int j3 = 0; j3 < D1D; ++j3)
1366 {
1367 real_t val = 0.0;
1368 for (int k1 = 0; k1 < Q1D; ++k1)
1369 {
1370 for (int k2 = 0; k2 < Q1D; ++k2)
1371 {
1372 for (int k3 = 0; k3 < Q1D; ++k3)
1373 {
1374 val += l_B[k1][i1] * l_B[k1][j1]
1375 * l_B[k2][i2] * l_B[k2][j2]
1376 * l_B[k3][i3] * l_B[k3][j3]
1377 * s_D[k1][k2][k3];
1378 }
1379 }
1380 }
1381 if (add)
1382 {
1383 M(i1, i2, i3, j1, j2, j3, e) += val;
1384 }
1385 else
1386 {
1387 M(i1, i2, i3, j1, j2, j3, e) = val;
1388 }
1389 }
1390 }
1391 }
1392 }
1393 }
1394 }
1395 });
1396}
1397
1398} // namespace internal
1399
1400namespace
1401{
1402using ApplyKernelType = MassIntegrator::ApplyKernelType;
1403using DiagonalKernelType = MassIntegrator::DiagonalKernelType;
1404}
1405
1406template<int DIM, int D1D, int Q1D>
1407ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
1408{
1409 if constexpr (DIM == 1) { return internal::PAMassApply1D; }
1410 else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<D1D, Q1D>; }
1411 else if constexpr (DIM == 3)
1412 {
1413 constexpr int MDQ = D1D >= Q1D ? D1D : Q1D;
1414 // max 64 threads in z limit in cuda and hip
1415 if constexpr (MDQ > 0)
1416 {
1417 return internal::SmemPAMassApply3D<D1D, Q1D,
1418 internal::mass::NBZ3D(MDQ)>;
1419 }
1420 }
1421 else { MFEM_ABORT(""); }
1422 return nullptr;
1423}
1424
1425inline ApplyKernelType MassIntegrator::ApplyPAKernels::Fallback(
1426 int dim, int, int)
1427{
1428 if (dim == 1) { return internal::PAMassApply1D; }
1429 else if (dim == 2) { return internal::PAMassApply2D; }
1430 else if (dim == 3) { return internal::PAMassApply3D; }
1431 else { MFEM_ABORT(""); }
1432 return nullptr;
1433}
1434
1435template<int DIM, int D1D, int Q1D>
1436DiagonalKernelType MassIntegrator::DiagonalPAKernels::Kernel()
1437{
1438 if constexpr (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
1439 else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<D1D, Q1D>; }
1440 else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<D1D, Q1D>; }
1441 else { MFEM_ABORT(""); }
1442 return nullptr;
1443}
1444
1445inline DiagonalKernelType MassIntegrator::DiagonalPAKernels::Fallback(
1446 int dim, int, int)
1447{
1448 if (dim == 1) { return internal::PAMassAssembleDiagonal1D; }
1449 else if (dim == 2) { return internal::PAMassAssembleDiagonal2D; }
1450 else if (dim == 3) { return internal::PAMassAssembleDiagonal3D; }
1451 else { MFEM_ABORT(""); }
1452 return nullptr;
1453}
1454
1455/// \endcond DO_NOT_DOCUMENT
1456
1457} // namespace mfem
1458
1459#endif
void(*)(const int, const Array< real_t > &, const Array< real_t > &, const Vector &, const Vector &, Vector &, const int, const int) ApplyKernelType
void(*)(const int, const Array< real_t > &, const Vector &, Vector &, const int, const int) DiagonalKernelType
int dim
Definition ex24.cpp:53
real_t b
Definition lissajous.cpp:42
constexpr int DIM
mfem::real_t real_t
DeviceTensor< 3, real_t > DeviceCube
Definition dtensor.hpp:153
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
DeviceTensor< 3, const real_t > ConstDeviceCube
Definition dtensor.hpp:154
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:386
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:414
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_batch(int N, int X, int Y, int BZ, lambda &&body)
Definition forall.hpp:1232
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
void forall_3D(int N, int X, int Y, int Z, lambda &&body)
Definition forall.hpp:1244
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
real_t p(const Vector &x, real_t t)
static const DeviceDofQuadLimits & Get()
Return a const reference to the DeviceDofQuadLimits singleton.
Definition forall.hpp:138
int MAX_D1D
Maximum number of 1D nodal points.
Definition forall.hpp:126
int MAX_Q1D
Maximum number of 1D quadrature points.
Definition forall.hpp:127