MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_hcurlhdiv_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_HCURLHDIV_KERNELS_HPP
13#define MFEM_BILININTEG_HCURLHDIV_KERNELS_HPP
14
20#include "../bilininteg.hpp"
21
22/// \cond DO_NOT_DOCUMENT
23namespace mfem
24{
25
26namespace internal
27{
28
29// PA H(curl)-H(div) Mass Apply 2D kernel
30void PAHcurlHdivMassSetup2D(const int Q1D,
31 const int coeffDim,
32 const int NE,
33 const bool transpose,
34 const Array<real_t> &w_,
35 const Vector &j,
36 Vector &coeff_,
37 Vector &op);
38
39// PA H(curl)-H(div) Mass Assemble 3D kernel
40void PAHcurlHdivMassSetup3D(const int Q1D,
41 const int coeffDim,
42 const int NE,
43 const bool transpose,
44 const Array<real_t> &w_,
45 const Vector &j,
46 Vector &coeff_,
47 Vector &op);
48
49// PA H(curl)-H(div) Mass Apply 2D kernel
50void PAHcurlHdivMassApply2D(const int D1D,
51 const int D1Dtest,
52 const int Q1D,
53 const int NE,
54 const bool scalarCoeff,
55 const bool trialHcurl,
56 const bool transpose,
57 const Array<real_t> &Bo_,
58 const Array<real_t> &Bc_,
59 const Array<real_t> &Bot_,
60 const Array<real_t> &Bct_,
61 const Vector &op_,
62 const Vector &x_,
63 Vector &y_);
64
65/// H(curl) test, H(div) trial
66inline void
67PAHcurlHdivMassApply2D(const int NE, const bool, const bool scalarCoeff,
68 const Array<real_t> &Bo_, const Array<real_t> &Bc_,
69 const Array<real_t> &Bot_, const Array<real_t> &Bct_,
70 const Vector &op_, const Vector &x_, Vector &y_,
71 const int D1D, const int D1Dtest, const int Q1D)
72{
73 return PAHcurlHdivMassApply2D(D1D, D1Dtest, Q1D, NE, scalarCoeff, false,
74 false, Bo_, Bc_, Bot_, Bct_, op_, x_, y_);
75}
76
77/// H(div) test, H(curl) trial
78inline void
79PAHdivHcurlMassApply2D(const int NE, const bool, const bool scalarCoeff,
80 const Array<real_t> &Bo_, const Array<real_t> &Bc_,
81 const Array<real_t> &Bot_, const Array<real_t> &Bct_,
82 const Vector &op_, const Vector &x_, Vector &y_,
83 const int D1D, const int D1Dtest, const int Q1D)
84{
85 return PAHcurlHdivMassApply2D(D1D, D1Dtest, Q1D, NE, scalarCoeff, true,
86 false, Bo_, Bc_, Bot_, Bct_, op_, x_, y_);
87}
88
89// PA H(curl)-H(div) Mass Apply 3D kernel
90void PAHcurlHdivMassApply3D(const int D1D,
91 const int D1Dtest,
92 const int Q1D,
93 const int NE,
94 const bool scalarCoeff,
95 const bool trialHcurl,
96 const bool transpose,
97 const Array<real_t> &Bo_,
98 const Array<real_t> &Bc_,
99 const Array<real_t> &Bot_,
100 const Array<real_t> &Bct_,
101 const Vector &op_,
102 const Vector &x_,
103 Vector &y_);
104
105/// H(curl) test, H(div) trial
106inline void
107PAHcurlHdivMassApply3D(const int NE, const bool, const bool scalarCoeff,
108 const Array<real_t> &Bo_, const Array<real_t> &Bc_,
109 const Array<real_t> &Bot_, const Array<real_t> &Bct_,
110 const Vector &op_, const Vector &x_, Vector &y_,
111 const int D1D, const int D1Dtest, const int Q1D)
112{
113 PAHcurlHdivMassApply3D(D1D, D1Dtest, Q1D, NE, scalarCoeff, false, false, Bo_,
114 Bc_, Bot_, Bct_, op_, x_, y_);
115}
116
117/// H(div) test, H(curl) trial
118inline void
119PAHdivHcurlMassApply3D(const int NE, const bool, const bool scalarCoeff,
120 const Array<real_t> &Bo_, const Array<real_t> &Bc_,
121 const Array<real_t> &Bot_, const Array<real_t> &Bct_,
122 const Vector &op_, const Vector &x_, Vector &y_,
123 const int D1D, const int D1Dtest, const int Q1D)
124{
125 PAHcurlHdivMassApply3D(D1D, D1Dtest, Q1D, NE, scalarCoeff, true, false, Bo_,
126 Bc_, Bot_, Bct_, op_, x_, y_);
127}
128
129// PA H(curl)-H(div) Curl Apply 3D kernel
130template<int T_D1D = 0, int T_D1D_TEST = 0, int T_Q1D = 0>
131inline void PAHcurlHdivApply3D(const int d1d,
132 const int d1dtest,
133 const int q1d,
134 const int NE,
135 const Array<real_t> &bo,
136 const Array<real_t> &bc,
137 const Array<real_t> &bot,
138 const Array<real_t> &bct,
139 const Array<real_t> &gc,
140 const Vector &pa_data,
141 const Vector &x,
142 Vector &y)
143{
144 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
145 "Error: d1d > HCURL_MAX_D1D");
146 MFEM_VERIFY(T_D1D_TEST || d1dtest <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
147 "Error: d1dtest > HCURL_MAX_D1D");
148 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
149 "Error: q1d > HCURL_MAX_Q1D");
150 const int D1D = T_D1D ? T_D1D : d1d;
151 const int D1Dtest = T_D1D_TEST ? T_D1D_TEST : d1dtest;
152 const int Q1D = T_Q1D ? T_Q1D : q1d;
153
154 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
155 auto Bc = Reshape(bc.Read(), Q1D, D1D);
156 auto Bot = Reshape(bot.Read(), D1Dtest-1, Q1D);
157 auto Bct = Reshape(bct.Read(), D1Dtest, Q1D);
158 auto Gc = Reshape(gc.Read(), Q1D, D1D);
159 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, 6, NE);
160 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
161 auto Y = Reshape(y.ReadWrite(), 3*(D1Dtest-1)*(D1Dtest-1)*D1Dtest, NE);
162
163 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
164 {
165 // Using Piola transformations (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u}
166 // for u in H(curl) and w = (1 / det (dF)) dF \hat{w} for w in H(div), we get
167 // (\nabla\times u) \cdot w = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{w}
168 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
169 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
170 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
171
172 constexpr int VDIM = 3;
173 constexpr int MD1D = T_D1D ? T_D1D :
174 DofQuadLimits::HCURL_MAX_D1D; // Assuming HDIV_MAX_D1D <= HCURL_MAX_D1D
175 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
176 const int D1D = T_D1D ? T_D1D : d1d;
177 const int D1Dtest = T_D1D_TEST ? T_D1D_TEST : d1dtest;
178 const int Q1D = T_Q1D ? T_Q1D : q1d;
179
180 real_t curl[MQ1D][MQ1D][MQ1D][VDIM];
181 // curl[qz][qy][qx] will be computed as the vector curl at each quadrature point.
182
183 for (int qz = 0; qz < Q1D; ++qz)
184 {
185 for (int qy = 0; qy < Q1D; ++qy)
186 {
187 for (int qx = 0; qx < Q1D; ++qx)
188 {
189 for (int c = 0; c < VDIM; ++c)
190 {
191 curl[qz][qy][qx][c] = 0.0;
192 }
193 }
194 }
195 }
196
197 // We treat x, y, z components separately for optimization specific to each.
198
199 int osc = 0;
200
201 {
202 // x component
203 const int D1Dz = D1D;
204 const int D1Dy = D1D;
205 const int D1Dx = D1D - 1;
206
207 for (int dz = 0; dz < D1Dz; ++dz)
208 {
209 real_t gradXY[MQ1D][MQ1D][2];
210 for (int qy = 0; qy < Q1D; ++qy)
211 {
212 for (int qx = 0; qx < Q1D; ++qx)
213 {
214 for (int d = 0; d < 2; ++d)
215 {
216 gradXY[qy][qx][d] = 0.0;
217 }
218 }
219 }
220
221 for (int dy = 0; dy < D1Dy; ++dy)
222 {
223 real_t massX[MQ1D];
224 for (int qx = 0; qx < Q1D; ++qx)
225 {
226 massX[qx] = 0.0;
227 }
228
229 for (int dx = 0; dx < D1Dx; ++dx)
230 {
231 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
232 for (int qx = 0; qx < Q1D; ++qx)
233 {
234 massX[qx] += t * Bo(qx,dx);
235 }
236 }
237
238 for (int qy = 0; qy < Q1D; ++qy)
239 {
240 const real_t wy = Bc(qy,dy);
241 const real_t wDy = Gc(qy,dy);
242 for (int qx = 0; qx < Q1D; ++qx)
243 {
244 const real_t wx = massX[qx];
245 gradXY[qy][qx][0] += wx * wDy;
246 gradXY[qy][qx][1] += wx * wy;
247 }
248 }
249 }
250
251 for (int qz = 0; qz < Q1D; ++qz)
252 {
253 const real_t wz = Bc(qz,dz);
254 const real_t wDz = Gc(qz,dz);
255 for (int qy = 0; qy < Q1D; ++qy)
256 {
257 for (int qx = 0; qx < Q1D; ++qx)
258 {
259 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
260 curl[qz][qy][qx][1] += gradXY[qy][qx][1] * wDz; // (u_0)_{x_2}
261 curl[qz][qy][qx][2] -= gradXY[qy][qx][0] * wz; // -(u_0)_{x_1}
262 }
263 }
264 }
265 }
266
267 osc += D1Dx * D1Dy * D1Dz;
268 }
269
270 {
271 // y component
272 const int D1Dz = D1D;
273 const int D1Dy = D1D - 1;
274 const int D1Dx = D1D;
275
276 for (int dz = 0; dz < D1Dz; ++dz)
277 {
278 real_t gradXY[MQ1D][MQ1D][2];
279 for (int qy = 0; qy < Q1D; ++qy)
280 {
281 for (int qx = 0; qx < Q1D; ++qx)
282 {
283 for (int d = 0; d < 2; ++d)
284 {
285 gradXY[qy][qx][d] = 0.0;
286 }
287 }
288 }
289
290 for (int dx = 0; dx < D1Dx; ++dx)
291 {
292 real_t massY[MQ1D];
293 for (int qy = 0; qy < Q1D; ++qy)
294 {
295 massY[qy] = 0.0;
296 }
297
298 for (int dy = 0; dy < D1Dy; ++dy)
299 {
300 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
301 for (int qy = 0; qy < Q1D; ++qy)
302 {
303 massY[qy] += t * Bo(qy,dy);
304 }
305 }
306
307 for (int qx = 0; qx < Q1D; ++qx)
308 {
309 const real_t wx = Bc(qx,dx);
310 const real_t wDx = Gc(qx,dx);
311 for (int qy = 0; qy < Q1D; ++qy)
312 {
313 const real_t wy = massY[qy];
314 gradXY[qy][qx][0] += wDx * wy;
315 gradXY[qy][qx][1] += wx * wy;
316 }
317 }
318 }
319
320 for (int qz = 0; qz < Q1D; ++qz)
321 {
322 const real_t wz = Bc(qz,dz);
323 const real_t wDz = Gc(qz,dz);
324 for (int qy = 0; qy < Q1D; ++qy)
325 {
326 for (int qx = 0; qx < Q1D; ++qx)
327 {
328 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
329 curl[qz][qy][qx][0] -= gradXY[qy][qx][1] * wDz; // -(u_1)_{x_2}
330 curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz; // (u_1)_{x_0}
331 }
332 }
333 }
334 }
335
336 osc += D1Dx * D1Dy * D1Dz;
337 }
338
339 {
340 // z component
341 const int D1Dz = D1D - 1;
342 const int D1Dy = D1D;
343 const int D1Dx = D1D;
344
345 for (int dx = 0; dx < D1Dx; ++dx)
346 {
347 real_t gradYZ[MQ1D][MQ1D][2];
348 for (int qz = 0; qz < Q1D; ++qz)
349 {
350 for (int qy = 0; qy < Q1D; ++qy)
351 {
352 for (int d = 0; d < 2; ++d)
353 {
354 gradYZ[qz][qy][d] = 0.0;
355 }
356 }
357 }
358
359 for (int dy = 0; dy < D1Dy; ++dy)
360 {
361 real_t massZ[MQ1D];
362 for (int qz = 0; qz < Q1D; ++qz)
363 {
364 massZ[qz] = 0.0;
365 }
366
367 for (int dz = 0; dz < D1Dz; ++dz)
368 {
369 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
370 for (int qz = 0; qz < Q1D; ++qz)
371 {
372 massZ[qz] += t * Bo(qz,dz);
373 }
374 }
375
376 for (int qy = 0; qy < Q1D; ++qy)
377 {
378 const real_t wy = Bc(qy,dy);
379 const real_t wDy = Gc(qy,dy);
380 for (int qz = 0; qz < Q1D; ++qz)
381 {
382 const real_t wz = massZ[qz];
383 gradYZ[qz][qy][0] += wz * wy;
384 gradYZ[qz][qy][1] += wz * wDy;
385 }
386 }
387 }
388
389 for (int qx = 0; qx < Q1D; ++qx)
390 {
391 const real_t wx = Bc(qx,dx);
392 const real_t wDx = Gc(qx,dx);
393
394 for (int qy = 0; qy < Q1D; ++qy)
395 {
396 for (int qz = 0; qz < Q1D; ++qz)
397 {
398 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
399 curl[qz][qy][qx][0] += gradYZ[qz][qy][1] * wx; // (u_2)_{x_1}
400 curl[qz][qy][qx][1] -= gradYZ[qz][qy][0] * wDx; // -(u_2)_{x_0}
401 }
402 }
403 }
404 }
405 }
406
407 // Apply D operator.
408 for (int qz = 0; qz < Q1D; ++qz)
409 {
410 for (int qy = 0; qy < Q1D; ++qy)
411 {
412 for (int qx = 0; qx < Q1D; ++qx)
413 {
414 const real_t O11 = op(qx,qy,qz,0,e);
415 const real_t O12 = op(qx,qy,qz,1,e);
416 const real_t O13 = op(qx,qy,qz,2,e);
417 const real_t O22 = op(qx,qy,qz,3,e);
418 const real_t O23 = op(qx,qy,qz,4,e);
419 const real_t O33 = op(qx,qy,qz,5,e);
420
421 const real_t c1 = (O11 * curl[qz][qy][qx][0]) + (O12 * curl[qz][qy][qx][1]) +
422 (O13 * curl[qz][qy][qx][2]);
423 const real_t c2 = (O12 * curl[qz][qy][qx][0]) + (O22 * curl[qz][qy][qx][1]) +
424 (O23 * curl[qz][qy][qx][2]);
425 const real_t c3 = (O13 * curl[qz][qy][qx][0]) + (O23 * curl[qz][qy][qx][1]) +
426 (O33 * curl[qz][qy][qx][2]);
427
428 curl[qz][qy][qx][0] = c1;
429 curl[qz][qy][qx][1] = c2;
430 curl[qz][qy][qx][2] = c3;
431 }
432 }
433 }
434
435 for (int qz = 0; qz < Q1D; ++qz)
436 {
437 real_t massXY[MD1D][MD1D];
438
439 osc = 0;
440
441 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
442 {
443 const int D1Dz = (c == 2) ? D1Dtest : D1Dtest - 1;
444 const int D1Dy = (c == 1) ? D1Dtest : D1Dtest - 1;
445 const int D1Dx = (c == 0) ? D1Dtest : D1Dtest - 1;
446
447 for (int dy = 0; dy < D1Dy; ++dy)
448 {
449 for (int dx = 0; dx < D1Dx; ++dx)
450 {
451 massXY[dy][dx] = 0;
452 }
453 }
454 for (int qy = 0; qy < Q1D; ++qy)
455 {
456 real_t massX[MD1D];
457 for (int dx = 0; dx < D1Dx; ++dx)
458 {
459 massX[dx] = 0;
460 }
461 for (int qx = 0; qx < Q1D; ++qx)
462 {
463 for (int dx = 0; dx < D1Dx; ++dx)
464 {
465 massX[dx] += curl[qz][qy][qx][c] *
466 ((c == 0) ? Bct(dx,qx) : Bot(dx,qx));
467 }
468 }
469 for (int dy = 0; dy < D1Dy; ++dy)
470 {
471 const real_t wy = (c == 1) ? Bct(dy,qy) : Bot(dy,qy);
472 for (int dx = 0; dx < D1Dx; ++dx)
473 {
474 massXY[dy][dx] += massX[dx] * wy;
475 }
476 }
477 }
478
479 for (int dz = 0; dz < D1Dz; ++dz)
480 {
481 const real_t wz = (c == 2) ? Bct(dz,qz) : Bot(dz,qz);
482 for (int dy = 0; dy < D1Dy; ++dy)
483 {
484 for (int dx = 0; dx < D1Dx; ++dx)
485 {
486 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) +=
487 massXY[dy][dx] * wz;
488 }
489 }
490 }
491
492 osc += D1Dx * D1Dy * D1Dz;
493 } // loop c
494 } // loop qz
495 }); // end of element loop
496}
497
498// PA H(curl)-H(div) Curl Apply Transpose 3D kernel
499template<int T_D1D = 0, int T_D1D_TEST = 0, int T_Q1D = 0>
500inline void PAHcurlHdivApplyTranspose3D(const int d1d,
501 const int d1dtest,
502 const int q1d,
503 const int NE,
504 const Array<real_t> &bo,
505 const Array<real_t> &bc,
506 const Array<real_t> &bot,
507 const Array<real_t> &bct,
508 const Array<real_t> &gct,
509 const Vector &pa_data,
510 const Vector &x,
511 Vector &y)
512{
513 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
514 "Error: d1d > HCURL_MAX_D1D");
515 MFEM_VERIFY(T_D1D_TEST || d1dtest <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
516 "Error: d1dtest > HCURL_MAX_D1D");
517 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
518 "Error: q1d > HCURL_MAX_Q1D");
519 const int D1D = T_D1D ? T_D1D : d1d;
520 const int D1Dtest = T_D1D_TEST ? T_D1D_TEST : d1dtest;
521 const int Q1D = T_Q1D ? T_Q1D : q1d;
522
523 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
524 auto Bc = Reshape(bc.Read(), Q1D, D1D);
525 auto Bot = Reshape(bot.Read(), D1Dtest-1, Q1D);
526 auto Bct = Reshape(bct.Read(), D1Dtest, Q1D);
527 auto Gct = Reshape(gct.Read(), D1D, Q1D);
528 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, 6, NE);
529 auto X = Reshape(x.Read(), 3*(D1Dtest-1)*(D1Dtest-1)*D1Dtest, NE);
530 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
531
532 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
533 {
534 // Using Piola transformations (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u}
535 // for u in H(curl) and w = (1 / det (dF)) dF \hat{w} for w in H(div), we get
536 // (\nabla\times u) \cdot w = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{w}
537 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
538 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
539 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
540
541 constexpr int VDIM = 3;
542 constexpr int MD1D = T_D1D ? T_D1D :
543 DofQuadLimits::HCURL_MAX_D1D; // Assuming HDIV_MAX_D1D <= HCURL_MAX_D1D
544 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
545 const int D1D = T_D1D ? T_D1D : d1d;
546 const int D1Dtest = T_D1D_TEST ? T_D1D_TEST : d1dtest;
547 const int Q1D = T_Q1D ? T_Q1D : q1d;
548
549 real_t mass[MQ1D][MQ1D][MQ1D][VDIM];
550
551 for (int qz = 0; qz < Q1D; ++qz)
552 {
553 for (int qy = 0; qy < Q1D; ++qy)
554 {
555 for (int qx = 0; qx < Q1D; ++qx)
556 {
557 for (int c = 0; c < VDIM; ++c)
558 {
559 mass[qz][qy][qx][c] = 0.0;
560 }
561 }
562 }
563 }
564
565 int osc = 0;
566
567 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
568 {
569 const int D1Dz = (c == 2) ? D1Dtest : D1Dtest - 1;
570 const int D1Dy = (c == 1) ? D1Dtest : D1Dtest - 1;
571 const int D1Dx = (c == 0) ? D1Dtest : D1Dtest - 1;
572
573 for (int dz = 0; dz < D1Dz; ++dz)
574 {
575 real_t massXY[MQ1D][MQ1D];
576 for (int qy = 0; qy < Q1D; ++qy)
577 {
578 for (int qx = 0; qx < Q1D; ++qx)
579 {
580 massXY[qy][qx] = 0.0;
581 }
582 }
583
584 for (int dy = 0; dy < D1Dy; ++dy)
585 {
586 real_t massX[MQ1D];
587 for (int qx = 0; qx < Q1D; ++qx)
588 {
589 massX[qx] = 0.0;
590 }
591
592 for (int dx = 0; dx < D1Dx; ++dx)
593 {
594 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
595 for (int qx = 0; qx < Q1D; ++qx)
596 {
597 massX[qx] += t * ((c == 0) ? Bc(qx,dx) : Bo(qx,dx));
598 }
599 }
600
601 for (int qy = 0; qy < Q1D; ++qy)
602 {
603 const real_t wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
604 for (int qx = 0; qx < Q1D; ++qx)
605 {
606 const real_t wx = massX[qx];
607 massXY[qy][qx] += wx * wy;
608 }
609 }
610 }
611
612 for (int qz = 0; qz < Q1D; ++qz)
613 {
614 const real_t wz = (c == 2) ? Bc(qz,dz) : Bo(qz,dz);
615 for (int qy = 0; qy < Q1D; ++qy)
616 {
617 for (int qx = 0; qx < Q1D; ++qx)
618 {
619 mass[qz][qy][qx][c] += massXY[qy][qx] * wz;
620 }
621 }
622 }
623 }
624
625 osc += D1Dx * D1Dy * D1Dz;
626 } // loop (c) over components
627
628 // Apply D operator.
629 for (int qz = 0; qz < Q1D; ++qz)
630 {
631 for (int qy = 0; qy < Q1D; ++qy)
632 {
633 for (int qx = 0; qx < Q1D; ++qx)
634 {
635 const real_t O11 = op(qx,qy,qz,0,e);
636 const real_t O12 = op(qx,qy,qz,1,e);
637 const real_t O13 = op(qx,qy,qz,2,e);
638 const real_t O22 = op(qx,qy,qz,3,e);
639 const real_t O23 = op(qx,qy,qz,4,e);
640 const real_t O33 = op(qx,qy,qz,5,e);
641 const real_t massX = mass[qz][qy][qx][0];
642 const real_t massY = mass[qz][qy][qx][1];
643 const real_t massZ = mass[qz][qy][qx][2];
644 mass[qz][qy][qx][0] = (O11*massX)+(O12*massY)+(O13*massZ);
645 mass[qz][qy][qx][1] = (O12*massX)+(O22*massY)+(O23*massZ);
646 mass[qz][qy][qx][2] = (O13*massX)+(O23*massY)+(O33*massZ);
647 }
648 }
649 }
650
651 // x component
652 osc = 0;
653 {
654 const int D1Dz = D1D;
655 const int D1Dy = D1D;
656 const int D1Dx = D1D - 1;
657
658 for (int qz = 0; qz < Q1D; ++qz)
659 {
660 real_t gradXY12[MD1D][MD1D];
661 real_t gradXY21[MD1D][MD1D];
662
663 for (int dy = 0; dy < D1Dy; ++dy)
664 {
665 for (int dx = 0; dx < D1Dx; ++dx)
666 {
667 gradXY12[dy][dx] = 0.0;
668 gradXY21[dy][dx] = 0.0;
669 }
670 }
671 for (int qy = 0; qy < Q1D; ++qy)
672 {
673 real_t massX[MD1D][2];
674 for (int dx = 0; dx < D1Dx; ++dx)
675 {
676 for (int n = 0; n < 2; ++n)
677 {
678 massX[dx][n] = 0.0;
679 }
680 }
681 for (int qx = 0; qx < Q1D; ++qx)
682 {
683 for (int dx = 0; dx < D1Dx; ++dx)
684 {
685 const real_t wx = Bot(dx,qx);
686
687 massX[dx][0] += wx * mass[qz][qy][qx][1];
688 massX[dx][1] += wx * mass[qz][qy][qx][2];
689 }
690 }
691 for (int dy = 0; dy < D1Dy; ++dy)
692 {
693 const real_t wy = Bct(dy,qy);
694 const real_t wDy = Gct(dy,qy);
695
696 for (int dx = 0; dx < D1Dx; ++dx)
697 {
698 gradXY21[dy][dx] += massX[dx][0] * wy;
699 gradXY12[dy][dx] += massX[dx][1] * wDy;
700 }
701 }
702 }
703
704 for (int dz = 0; dz < D1Dz; ++dz)
705 {
706 const real_t wz = Bct(dz,qz);
707 const real_t wDz = Gct(dz,qz);
708 for (int dy = 0; dy < D1Dy; ++dy)
709 {
710 for (int dx = 0; dx < D1Dx; ++dx)
711 {
712 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
713 // (u_0)_{x_2} * (op * curl)_1 - (u_0)_{x_1} * (op * curl)_2
714 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
715 e) += (gradXY21[dy][dx] * wDz) - (gradXY12[dy][dx] * wz);
716 }
717 }
718 }
719 } // loop qz
720
721 osc += D1Dx * D1Dy * D1Dz;
722 }
723
724 // y component
725 {
726 const int D1Dz = D1D;
727 const int D1Dy = D1D - 1;
728 const int D1Dx = D1D;
729
730 for (int qz = 0; qz < Q1D; ++qz)
731 {
732 real_t gradXY02[MD1D][MD1D];
733 real_t gradXY20[MD1D][MD1D];
734
735 for (int dy = 0; dy < D1Dy; ++dy)
736 {
737 for (int dx = 0; dx < D1Dx; ++dx)
738 {
739 gradXY02[dy][dx] = 0.0;
740 gradXY20[dy][dx] = 0.0;
741 }
742 }
743 for (int qx = 0; qx < Q1D; ++qx)
744 {
745 real_t massY[MD1D][2];
746 for (int dy = 0; dy < D1Dy; ++dy)
747 {
748 massY[dy][0] = 0.0;
749 massY[dy][1] = 0.0;
750 }
751 for (int qy = 0; qy < Q1D; ++qy)
752 {
753 for (int dy = 0; dy < D1Dy; ++dy)
754 {
755 const real_t wy = Bot(dy,qy);
756
757 massY[dy][0] += wy * mass[qz][qy][qx][2];
758 massY[dy][1] += wy * mass[qz][qy][qx][0];
759 }
760 }
761 for (int dx = 0; dx < D1Dx; ++dx)
762 {
763 const real_t wx = Bct(dx,qx);
764 const real_t wDx = Gct(dx,qx);
765
766 for (int dy = 0; dy < D1Dy; ++dy)
767 {
768 gradXY02[dy][dx] += massY[dy][0] * wDx;
769 gradXY20[dy][dx] += massY[dy][1] * wx;
770 }
771 }
772 }
773
774 for (int dz = 0; dz < D1Dz; ++dz)
775 {
776 const real_t wz = Bct(dz,qz);
777 const real_t wDz = Gct(dz,qz);
778 for (int dy = 0; dy < D1Dy; ++dy)
779 {
780 for (int dx = 0; dx < D1Dx; ++dx)
781 {
782 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
783 // -(u_1)_{x_2} * (op * curl)_0 + (u_1)_{x_0} * (op * curl)_2
784 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
785 e) += (-gradXY20[dy][dx] * wDz) + (gradXY02[dy][dx] * wz);
786 }
787 }
788 }
789 } // loop qz
790
791 osc += D1Dx * D1Dy * D1Dz;
792 }
793
794 // z component
795 {
796 const int D1Dz = D1D - 1;
797 const int D1Dy = D1D;
798 const int D1Dx = D1D;
799
800 for (int qx = 0; qx < Q1D; ++qx)
801 {
802 real_t gradYZ01[MD1D][MD1D];
803 real_t gradYZ10[MD1D][MD1D];
804
805 for (int dy = 0; dy < D1Dy; ++dy)
806 {
807 for (int dz = 0; dz < D1Dz; ++dz)
808 {
809 gradYZ01[dz][dy] = 0.0;
810 gradYZ10[dz][dy] = 0.0;
811 }
812 }
813 for (int qy = 0; qy < Q1D; ++qy)
814 {
815 real_t massZ[MD1D][2];
816 for (int dz = 0; dz < D1Dz; ++dz)
817 {
818 for (int n = 0; n < 2; ++n)
819 {
820 massZ[dz][n] = 0.0;
821 }
822 }
823 for (int qz = 0; qz < Q1D; ++qz)
824 {
825 for (int dz = 0; dz < D1Dz; ++dz)
826 {
827 const real_t wz = Bot(dz,qz);
828
829 massZ[dz][0] += wz * mass[qz][qy][qx][0];
830 massZ[dz][1] += wz * mass[qz][qy][qx][1];
831 }
832 }
833 for (int dy = 0; dy < D1Dy; ++dy)
834 {
835 const real_t wy = Bct(dy,qy);
836 const real_t wDy = Gct(dy,qy);
837
838 for (int dz = 0; dz < D1Dz; ++dz)
839 {
840 gradYZ01[dz][dy] += wy * massZ[dz][1];
841 gradYZ10[dz][dy] += wDy * massZ[dz][0];
842 }
843 }
844 }
845
846 for (int dx = 0; dx < D1Dx; ++dx)
847 {
848 const real_t wx = Bct(dx,qx);
849 const real_t wDx = Gct(dx,qx);
850
851 for (int dy = 0; dy < D1Dy; ++dy)
852 {
853 for (int dz = 0; dz < D1Dz; ++dz)
854 {
855 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
856 // (u_2)_{x_1} * (op * curl)_0 - (u_2)_{x_0} * (op * curl)_1
857 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
858 e) += (gradYZ10[dz][dy] * wx) - (gradYZ01[dz][dy] * wDx);
859 }
860 }
861 }
862 } // loop qx
863 }
864 }); // end of element loop
865}
866
867namespace curlinterp
868{
869constexpr int NBZ3D(int ndof_o, int nquad_o, int mdq)
870{
871 if (ndof_o <= 0 || nquad_o <= 0)
872 {
873 return 1;
874 }
875 int ndof_c = ndof_o + 1;
876 int nquad_c = nquad_o + 1;
877 // z dimension is capped at 64 on nvidia and amd gpus
878 int tmp =
879 std::min((128 + mdq * mdq * (mdq - 1) - 1) / (mdq * mdq * (mdq - 1)), 64);
880 int smem_req =
881 sizeof(mfem::real_t) *
882 ((3 * ndof_c * ndof_c * ndof_o + 2 * 2 * mdq * mdq * mdq) * tmp +
883 ndof_c * nquad_o + ndof_c * nquad_c + ndof_o * nquad_o);
884 // assume GPU has at least 48k shared memory
885 return std::max(std::min(tmp, (48 * 1024 + smem_req - 1) / smem_req), 1);
886}
887}
888
889template <int T_NDOF_O, int T_NQUAD_O>
890void CurlInterpolatorApply3DSmem(const int ne, const int ndof_o,
891 const int nquad_o, const Vector &pa,
892 const Vector &x_, Vector &y_)
893{
894 constexpr int mnd_o = T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
895 constexpr int mnq_o =
896 T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
897 constexpr int mndq = std::max(mnd_o + 1, mnq_o + 1);
898 constexpr int tbatch = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, mndq);
899 MFEM_VERIFY(ndof_o <= mnd_o, "Error: H(curl) order larger than supported");
900 MFEM_VERIFY(nquad_o <= mnq_o, "Error: H(div) order larger than supported");
901 int mnq = std::max(ndof_o + 1, nquad_o + 1);
902 auto pa_data = pa.Read();
903 auto x_d = x_.Read();
904 auto y_d = y_.ReadWrite();
905 mfem::forall_2D_batch<mndq * mndq * (mndq - 1) * tbatch>(
906 ne, mnq * mnq * (mnq - 1), 1, tbatch, [=] MFEM_HOST_DEVICE(int e)
907 {
908 constexpr int MND_O =
909 T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
910 constexpr int MNQ_O =
911 T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
912 constexpr int MNDQ = std::max(MND_O + 1, MNQ_O + 1);
913#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
914 constexpr int nbz = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, MNDQ);
915 int tidz = MFEM_THREAD_ID(z);
916 // Make mnq a local variable since capturing would result in different
917 // captures between host/device versions, and spuriously fails
918 int mnq = std::max(ndof_o + 1, nquad_o + 1);
919#else
920 constexpr int nbz = 1;
921 constexpr int tidz = 0;
922#endif
923 const int NDOF_O = T_NDOF_O ? T_NDOF_O : ndof_o;
924 const int NQUAD_O = T_NQUAD_O ? T_NQUAD_O : nquad_o;
925 const int NDOF_C = NDOF_O + 1;
926 const int NQUAD_C = NQUAD_O + 1;
927 MFEM_SHARED real_t
928 sBG[(MND_O + 1) * MNQ_O + (MND_O + 1) * (MNQ_O + 1) + MND_O * MNQ_O];
929 auto X_ = Reshape(x_d, 3 * NDOF_C * NDOF_C * NDOF_O, ne);
930 auto Y = Reshape(y_d, 3 * NQUAD_C * NQUAD_O * NQUAD_O, ne);
931 auto Gco = Reshape(sBG, NQUAD_O, NDOF_C);
932 auto Bcc = Reshape(sBG + NDOF_C * NQUAD_O, NQUAD_C, NDOF_C);
933 auto Boo =
934 Reshape(sBG + NDOF_C * NQUAD_O + NDOF_C * NQUAD_C, NQUAD_O, NDOF_O);
935 MFEM_SHARED real_t X[3][nbz][MND_O * (MND_O + 1) * (MND_O + 1)];
936 MFEM_SHARED real_t sm0[nbz * 2 * MNDQ * MNDQ * MNDQ];
937 MFEM_SHARED real_t sm1[nbz * 2 * MNDQ * MNDQ * MNDQ];
938
939 // shapes of buffers always use MNDQ to mitigate shared memory bank
940 // conflicts
941 real_t(*DDQ)[nbz][MNDQ][MNDQ][MNDQ] =
942 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
943 real_t(*DQQ)[nbz][MNDQ][MNDQ][MNDQ] =
944 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm1);
945 real_t(*QQQ)[nbz][MNDQ][MNDQ][MNDQ] =
946 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
947 const int offset = NDOF_O * NDOF_C * NDOF_C;
948 const int offsetq = NQUAD_C * NQUAD_O * NQUAD_O;
949 MFEM_FOREACH_THREAD_DIRECT(ix, x, offset)
950 {
951 for (int dim = 0; dim < 3; ++dim)
952 {
953 X[dim][tidz][ix] = X_(ix + dim * offset, e);
954 }
955 }
956 // load basis functions data
957 if (tidz == 0)
958 {
959 auto npts = NDOF_C * NQUAD_O + NDOF_C * NQUAD_C + NDOF_O * NQUAD_O;
960 MFEM_FOREACH_THREAD(ix, x, npts) { sBG[ix] = pa_data[ix]; }
961 }
962 MFEM_SYNC_THREAD;
963
964 // x: Vz Bcc Gco Boo - Vy Bcc Boo Gco
965 // threads assigned to mitigate bank conflicts
966 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_C, NDOF_C,
967 NDOF_O, mnq, mnq, mnq - 1)
968 {
969 real_t u = 0;
970 for (int dx = 0; dx < NDOF_C; ++dx)
971 {
972 u += X[2][tidz][dx + (dy + dz * NDOF_C) * NDOF_C] * Bcc(qx, dx);
973 }
974 DDQ[0][tidz][dz][dy][qx] = u;
975 }
976 // threads assigned to mitigate bank conflicts
977 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_C, NDOF_O,
978 NDOF_C, mnq, mnq - 1, mnq)
979 {
980 real_t u = 0;
981 for (int dx = 0; dx < NDOF_C; ++dx)
982 {
983 u += X[1][tidz][dx + (dy + dz * NDOF_O) * NDOF_C] * Bcc(qx, dx);
984 }
985 DDQ[1][tidz][dz][dy][qx] = u;
986 }
987 MFEM_SYNC_THREAD;
988
989 // threads assigned to mitigate bank conflicts
990 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_C, NQUAD_O,
991 NDOF_O, mnq, mnq, mnq - 1)
992 {
993 real_t u = 0;
994 for (int dy = 0; dy < NDOF_C; ++dy)
995 {
996 u += DDQ[0][tidz][dz][dy][qx] * Gco(qy, dy);
997 }
998 DQQ[0][tidz][dz][qy][qx] = u;
999 }
1000 // threads assigned to mitigate bank conflicts
1001 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_C, NQUAD_O,
1002 NDOF_C, mnq, mnq - 1, mnq)
1003 {
1004 real_t u = 0;
1005 for (int dy = 0; dy < NDOF_O; ++dy)
1006 {
1007 u += DDQ[1][tidz][dz][dy][qx] * Boo(qy, dy);
1008 }
1009 DQQ[1][tidz][dz][qy][qx] = u;
1010 }
1011 MFEM_SYNC_THREAD;
1012
1013 // threads assigned to mitigate bank conflicts
1014 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_C, NQUAD_O,
1015 NQUAD_O, mnq, mnq, mnq - 1)
1016 {
1017 real_t u = 0;
1018 for (int dz = 0; dz < NDOF_O; ++dz)
1019 {
1020 u += DQQ[0][tidz][dz][qy][qx] * Boo(qz, dz);
1021 }
1022 QQQ[0][tidz][qz][qy][qx] = u;
1023 }
1024 MFEM_SYNC_THREAD;
1025 // threads assigned to mitigate bank conflicts
1026 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_C, NQUAD_O,
1027 NQUAD_O, mnq, mnq, mnq - 1)
1028 {
1029 real_t u = 0;
1030 for (int dz = 0; dz < NDOF_C; ++dz)
1031 {
1032 u += DQQ[1][tidz][dz][qy][qx] * Gco(qz, dz);
1033 }
1034 Y(qx + (qy + qz * NQUAD_O) * NQUAD_C, e) =
1035 QQQ[0][tidz][qz][qy][qx] - u;
1036 }
1037 MFEM_SYNC_THREAD;
1038
1039 // y: Vx Boo Bcc Gco - Vz Gco Bcc Boo
1040 // threads assigned to mitigate bank conflicts
1041 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
1042 NDOF_C, mnq - 1, mnq, mnq)
1043 {
1044 real_t u = 0;
1045 for (int dx = 0; dx < NDOF_O; ++dx)
1046 {
1047 u += X[0][tidz][dx + (dy + dz * NDOF_C) * NDOF_O] * Boo(qx, dx);
1048 }
1049 DDQ[0][tidz][dz][dy][qx] = u;
1050 }
1051 // threads assigned to mitigate bank conflicts
1052 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
1053 NDOF_O, mnq, mnq, mnq - 1)
1054 {
1055 real_t u = 0;
1056 for (int dx = 0; dx < NDOF_C; ++dx)
1057 {
1058 u += X[2][tidz][dx + (dy + dz * NDOF_C) * NDOF_C] * Gco(qx, dx);
1059 }
1060 DDQ[1][tidz][dz][dy][qx] = u;
1061 }
1062 MFEM_SYNC_THREAD;
1063
1064 // threads assigned to mitigate bank conflicts
1065 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_C,
1066 NDOF_C, mnq - 1, mnq, mnq)
1067 {
1068 real_t u = 0;
1069 for (int dy = 0; dy < NDOF_C; ++dy)
1070 {
1071 u += DDQ[0][tidz][dz][dy][qx] * Bcc(qy, dy);
1072 }
1073 DQQ[0][tidz][dz][qy][qx] = u;
1074 }
1075 // threads assigned to mitigate bank conflicts
1076 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_C,
1077 NDOF_O, mnq - 1, mnq, mnq)
1078 {
1079 real_t u = 0;
1080 for (int dy = 0; dy < NDOF_C; ++dy)
1081 {
1082 u += DDQ[1][tidz][dz][dy][qx] * Bcc(qy, dy);
1083 }
1084 DQQ[1][tidz][dz][qy][qx] = u;
1085 }
1086 MFEM_SYNC_THREAD;
1087
1088 // threads assigned to mitigate bank conflicts
1089 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_C,
1090 NQUAD_O, mnq, mnq, mnq - 1)
1091 {
1092 real_t u = 0;
1093 for (int dz = 0; dz < NDOF_C; ++dz)
1094 {
1095 u += DQQ[0][tidz][dz][qy][qx] * Gco(qz, dz);
1096 }
1097 QQQ[0][tidz][qz][qy][qx] = u;
1098 }
1099 MFEM_SYNC_THREAD;
1100 // threads assigned to mitigate bank conflicts
1101 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_C,
1102 NQUAD_O, mnq, mnq, mnq - 1)
1103 {
1104 real_t u = 0;
1105 for (int dz = 0; dz < NDOF_O; ++dz)
1106 {
1107 u += DQQ[1][tidz][dz][qy][qx] * Boo(qz, dz);
1108 }
1109 Y(qx + (qy + qz * NQUAD_C) * NQUAD_O + offsetq, e) =
1110 QQQ[0][tidz][qz][qy][qx] - u;
1111 }
1112 MFEM_SYNC_THREAD;
1113
1114 // z: Vy Gco Boo Bcc - Vx Boo Gco Bcc
1115 // threads assigned to mitigate bank conflicts
1116 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_O,
1117 NDOF_C, mnq, mnq - 1, mnq)
1118 {
1119 real_t u = 0;
1120 for (int dx = 0; dx < NDOF_C; ++dx)
1121 {
1122 u += X[1][tidz][dx + (dy + dz * NDOF_O) * NDOF_C] * Gco(qx, dx);
1123 }
1124 DDQ[0][tidz][dz][dy][qx] = u;
1125 }
1126 // threads assigned to mitigate bank conflicts
1127 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
1128 NDOF_C, mnq - 1, mnq, mnq)
1129 {
1130 real_t u = 0;
1131 for (int dx = 0; dx < NDOF_O; ++dx)
1132 {
1133 u += X[0][tidz][dx + (dy + dz * NDOF_C) * NDOF_O] * Boo(qx, dx);
1134 }
1135 DDQ[1][tidz][dz][dy][qx] = u;
1136 }
1137 MFEM_SYNC_THREAD;
1138
1139 // threads assigned to mitigate bank conflicts
1140 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_O,
1141 NDOF_C, mnq, mnq - 1, mnq)
1142 {
1143 real_t u = 0;
1144 for (int dy = 0; dy < NDOF_O; ++dy)
1145 {
1146 u += DDQ[0][tidz][dz][dy][qx] * Boo(qy, dy);
1147 }
1148 DQQ[0][tidz][dz][qy][qx] = u;
1149 }
1150 // threads assigned to mitigate bank conflicts
1151 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_O,
1152 NDOF_C, mnq, mnq - 1, mnq)
1153 {
1154 real_t u = 0;
1155 for (int dy = 0; dy < NDOF_C; ++dy)
1156 {
1157 u += DDQ[1][tidz][dz][dy][qx] * Gco(qy, dy);
1158 }
1159 DQQ[1][tidz][dz][qy][qx] = u;
1160 }
1161 MFEM_SYNC_THREAD;
1162
1163 // threads assigned to mitigate bank conflicts
1164 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_O,
1165 NQUAD_C, mnq, mnq - 1, mnq)
1166 {
1167 real_t u = 0;
1168 for (int dz = 0; dz < NDOF_C; ++dz)
1169 {
1170 u += DQQ[0][tidz][dz][qy][qx] * Bcc(qz, dz);
1171 }
1172 QQQ[0][tidz][qz][qy][qx] = u;
1173 }
1174 MFEM_SYNC_THREAD;
1175 // threads assigned to mitigate bank conflicts
1176 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_O,
1177 NQUAD_C, mnq, mnq - 1, mnq)
1178 {
1179 real_t u = 0;
1180 for (int dz = 0; dz < NDOF_C; ++dz)
1181 {
1182 u += DQQ[1][tidz][dz][qy][qx] * Bcc(qz, dz);
1183 }
1184 Y(qx + (qy + qz * NQUAD_O) * NQUAD_O + 2 * offsetq, e) =
1185 QQQ[0][tidz][qz][qy][qx] - u;
1186 }
1187 MFEM_SYNC_THREAD;
1188 });
1189}
1190
1191template <int T_NDOF_O, int T_NQUAD_O>
1192void CurlInterpolatorTApply3DSmem(const int ne, const int ndof_o,
1193 const int nquad_o, const Vector &pa,
1194 const Vector &x_, Vector &y_)
1195{
1196 constexpr int mnd_o = T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
1197 constexpr int mnq_o =
1198 T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
1199 constexpr int mndq = std::max(mnd_o + 1, mnq_o + 1);
1200 constexpr int tbatch = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, mndq);
1201 MFEM_VERIFY(ndof_o <= mnd_o, "Error: H(curl) order larger than supported");
1202 MFEM_VERIFY(nquad_o <= mnq_o, "Error: H(div) order larger than supported");
1203 int mnq = std::max(ndof_o + 1, nquad_o + 1);
1204 auto pa_data = pa.Read();
1205 auto x_d = x_.Read();
1206 auto y_d = y_.ReadWrite();
1207 mfem::forall_2D_batch<mndq * mndq * (mndq - 1) * tbatch>(
1208 ne, mnq * mnq * (mnq - 1), 1, tbatch, [=] MFEM_HOST_DEVICE(int e)
1209 {
1210 constexpr int MND_O =
1211 T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
1212 constexpr int MNQ_O =
1213 T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
1214 constexpr int MNDQ = std::max(MND_O + 1, MNQ_O + 1);
1215#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
1216 constexpr int nbz = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, MNDQ);
1217 int tidz = MFEM_THREAD_ID(z);
1218 // Make mnq a local variable since capturing would result in different
1219 // captures between host/device versions, and spuriously fails
1220 int mnq = std::max(ndof_o + 1, nquad_o + 1);
1221#else
1222 constexpr int nbz = 1;
1223 constexpr int tidz = 0;
1224#endif
1225 const int NDOF_O = T_NDOF_O ? T_NDOF_O : ndof_o;
1226 const int NQUAD_O = T_NQUAD_O ? T_NQUAD_O : nquad_o;
1227 const int NDOF_C = NDOF_O + 1;
1228 const int NQUAD_C = NQUAD_O + 1;
1229 MFEM_SHARED real_t
1230 sBG[(MND_O + 1) * MNQ_O + (MND_O + 1) * (MNQ_O + 1) + MND_O * MNQ_O];
1231 auto X_ = Reshape(x_d, 3 * NQUAD_C * NQUAD_O * NQUAD_O, ne);
1232 auto Y = Reshape(y_d, 3 * NDOF_C * NDOF_C * NDOF_O, ne);
1233 auto Gco = Reshape(sBG, NQUAD_O, NDOF_C);
1234 auto Bcc = Reshape(sBG + NDOF_C * NQUAD_O, NQUAD_C, NDOF_C);
1235 auto Boo =
1236 Reshape(sBG + NDOF_C * NQUAD_O + NDOF_C * NQUAD_C, NQUAD_O, NDOF_O);
1237 MFEM_SHARED real_t X[3][nbz][MNQ_O * MNQ_O * (MNQ_O + 1)];
1238 MFEM_SHARED real_t sm0[nbz * 2 * MNDQ * MNDQ * MNDQ];
1239 MFEM_SHARED real_t sm1[nbz * 2 * MNDQ * MNDQ * MNDQ];
1240
1241 // shapes of buffers always use MNDQ to mitigate shared memory bank
1242 // conflicts
1243 real_t(*QQD)[nbz][MNDQ][MNDQ][MNDQ] =
1244 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
1245 real_t(*QDD)[nbz][MNDQ][MNDQ][MNDQ] =
1246 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm1);
1247 real_t(*DDD)[nbz][MNDQ][MNDQ][MNDQ] =
1248 (real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
1249 const int offset = NDOF_O * NDOF_C * NDOF_C;
1250 const int offsetq = NQUAD_C * NQUAD_O * NQUAD_O;
1251 MFEM_FOREACH_THREAD_DIRECT(ix, x, offsetq)
1252 {
1253 for (int dim = 0; dim < 3; ++dim)
1254 {
1255 X[dim][tidz][ix] = X_(ix + dim * offsetq, e);
1256 }
1257 }
1258 // load basis functions data
1259 if (tidz == 0)
1260 {
1261 auto npts = NDOF_C * NQUAD_O + NDOF_C * NQUAD_C + NDOF_O * NQUAD_O;
1262 MFEM_FOREACH_THREAD(ix, x, npts) { sBG[ix] = pa_data[ix]; }
1263 }
1264 MFEM_SYNC_THREAD;
1265
1266 // x: Vy Boo Bcc Gco - Vz Boo Gco Bcc
1267 // threads assigned to mitigate bank conflicts
1268 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
1269 NQUAD_C, mnq, mnq - 1, mnq)
1270 {
1271 real_t u = 0;
1272 for (int qz = 0; qz < NQUAD_O; ++qz)
1273 {
1274 u += X[1][tidz][qx + (qy + qz * NQUAD_C) * NQUAD_O] * Gco(qz, dz);
1275 }
1276 QQD[0][tidz][qy][qx][dz] = u;
1277 }
1278 // threads assigned to mitigate bank conflicts
1279 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
1280 NQUAD_O, mnq, mnq, mnq - 1)
1281 {
1282 real_t u = 0;
1283 for (int qz = 0; qz < NQUAD_C; ++qz)
1284 {
1285 u += X[2][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_O] * Bcc(qz, dz);
1286 }
1287 QQD[1][tidz][qy][qx][dz] = u;
1288 }
1289 MFEM_SYNC_THREAD;
1290
1291 // threads assigned to mitigate bank conflicts
1292 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_C,
1293 NQUAD_O, mnq, mnq, mnq - 1)
1294 {
1295 real_t u = 0;
1296 for (int qy = 0; qy < NQUAD_C; ++qy)
1297 {
1298 u += QQD[0][tidz][qy][qx][dz] * Bcc(qy, dy);
1299 }
1300 QDD[0][tidz][qx][dz][dy] = u;
1301 }
1302 // threads assigned to mitigate bank conflicts
1303 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_C,
1304 NQUAD_O, mnq, mnq, mnq - 1)
1305 {
1306 real_t u = 0;
1307 for (int qy = 0; qy < NQUAD_O; ++qy)
1308 {
1309 u += QQD[1][tidz][qy][qx][dz] * Gco(qy, dy);
1310 }
1311 QDD[1][tidz][qx][dz][dy] = u;
1312 }
1313 MFEM_SYNC_THREAD;
1314
1315 // threads assigned to mitigate bank conflicts
1316 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_O, NDOF_C,
1317 NDOF_C, mnq - 1, mnq, mnq)
1318 {
1319 real_t u = 0;
1320 for (int qx = 0; qx < NQUAD_O; ++qx)
1321 {
1322 u += QDD[0][tidz][qx][dz][dy] * Boo(qx, dx);
1323 }
1324 DDD[0][tidz][dz][dy][dx] = u;
1325 }
1326 MFEM_SYNC_THREAD;
1327 // threads assigned to mitigate bank conflicts
1328 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_O, NDOF_C,
1329 NDOF_C, mnq - 1, mnq, mnq)
1330 {
1331 real_t u = 0;
1332 for (int qx = 0; qx < NQUAD_O; ++qx)
1333 {
1334 u += QDD[1][tidz][qx][dz][dy] * Boo(qx, dx);
1335 }
1336 Y(dx + (dy + dz * NDOF_C) * NDOF_O, e) =
1337 DDD[0][tidz][dz][dy][dx] - u;
1338 }
1339 MFEM_SYNC_THREAD;
1340
1341 // y: Vz Gco Boo Bcc - Vx Bcc Boo Gco
1342 // threads assigned to mitigate bank conflicts
1343 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
1344 NQUAD_O, mnq, mnq, mnq - 1)
1345 {
1346 real_t u = 0;
1347 for (int qz = 0; qz < NQUAD_C; ++qz)
1348 {
1349 u += X[2][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_O] * Bcc(qz, dz);
1350 }
1351 QQD[0][tidz][qy][qx][dz] = u;
1352 }
1353 // threads assigned to mitigate bank conflicts
1354 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_C,
1355 NQUAD_O, mnq, mnq, mnq - 1)
1356 {
1357 real_t u = 0;
1358 for (int qz = 0; qz < NQUAD_O; ++qz)
1359 {
1360 u += X[0][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_C] * Gco(qz, dz);
1361 }
1362 QQD[1][tidz][qy][qx][dz] = u;
1363 }
1364 MFEM_SYNC_THREAD;
1365
1366 // threads assigned to mitigate bank conflicts
1367 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_O, NDOF_C,
1368 NQUAD_O, mnq, mnq, mnq - 1)
1369 {
1370 real_t u = 0;
1371 for (int qy = 0; qy < NQUAD_O; ++qy)
1372 {
1373 u += QQD[0][tidz][qy][qx][dz] * Boo(qy, dy);
1374 }
1375 QDD[0][tidz][qx][dz][dy] = u;
1376 }
1377 // threads assigned to mitigate bank conflicts
1378 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_O, NDOF_C,
1379 NQUAD_C, mnq - 1, mnq, mnq)
1380 {
1381 real_t u = 0;
1382 for (int qy = 0; qy < NQUAD_O; ++qy)
1383 {
1384 u += QQD[1][tidz][qy][qx][dz] * Boo(qy, dy);
1385 }
1386 QDD[1][tidz][qx][dz][dy] = u;
1387 }
1388 MFEM_SYNC_THREAD;
1389
1390 // threads assigned to mitigate bank conflicts
1391 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_O,
1392 NDOF_C, mnq, mnq - 1, mnq)
1393 {
1394 real_t u = 0;
1395 for (int qx = 0; qx < NQUAD_O; ++qx)
1396 {
1397 u += QDD[0][tidz][qx][dz][dy] * Gco(qx, dx);
1398 }
1399 DDD[0][tidz][dz][dy][dx] = u;
1400 }
1401 MFEM_SYNC_THREAD;
1402 // threads assigned to mitigate bank conflicts
1403 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_O,
1404 NDOF_C, mnq, mnq - 1, mnq)
1405 {
1406 real_t u = 0;
1407 for (int qx = 0; qx < NQUAD_C; ++qx)
1408 {
1409 u += QDD[1][tidz][qx][dz][dy] * Bcc(qx, dx);
1410 }
1411 Y(dx + (dy + dz * NDOF_O) * NDOF_C + offset, e) =
1412 DDD[0][tidz][dz][dy][dx] - u;
1413 }
1414 MFEM_SYNC_THREAD;
1415
1416 // z: Vx Bcc Gco Boo - Vy Gco Bcc Boo
1417 // threads assigned to mitigate bank conflicts
1418 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_O, NQUAD_C,
1419 NQUAD_O, mnq, mnq, mnq - 1)
1420 {
1421 real_t u = 0;
1422 for (int qz = 0; qz < NQUAD_O; ++qz)
1423 {
1424 u += X[0][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_C] * Boo(qz, dz);
1425 }
1426 QQD[0][tidz][qy][qx][dz] = u;
1427 }
1428 // threads assigned to mitigate bank conflicts
1429 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_O, NQUAD_O,
1430 NQUAD_C, mnq, mnq - 1, mnq)
1431 {
1432 real_t u = 0;
1433 for (int qz = 0; qz < NQUAD_O; ++qz)
1434 {
1435 u += X[1][tidz][qx + (qy + qz * NQUAD_C) * NQUAD_O] * Boo(qz, dz);
1436 }
1437 QQD[1][tidz][qy][qx][dz] = u;
1438 }
1439 MFEM_SYNC_THREAD;
1440
1441 // threads assigned to mitigate bank conflicts
1442 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_O,
1443 NQUAD_C, mnq, mnq - 1, mnq)
1444 {
1445 real_t u = 0;
1446 for (int qy = 0; qy < NQUAD_O; ++qy)
1447 {
1448 u += QQD[0][tidz][qy][qx][dz] * Gco(qy, dy);
1449 }
1450 QDD[0][tidz][qx][dz][dy] = u;
1451 }
1452 // threads assigned to mitigate bank conflicts
1453 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_O,
1454 NQUAD_O, mnq, mnq, mnq - 1)
1455 {
1456 real_t u = 0;
1457 for (int qy = 0; qy < NQUAD_C; ++qy)
1458 {
1459 u += QQD[1][tidz][qy][qx][dz] * Bcc(qy, dy);
1460 }
1461 QDD[1][tidz][qx][dz][dy] = u;
1462 }
1463 MFEM_SYNC_THREAD;
1464
1465 // threads assigned to mitigate bank conflicts
1466 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_C,
1467 NDOF_O, mnq, mnq, mnq - 1)
1468 {
1469 real_t u = 0;
1470 for (int qx = 0; qx < NQUAD_C; ++qx)
1471 {
1472 u += QDD[0][tidz][qx][dz][dy] * Bcc(qx, dx);
1473 }
1474 DDD[0][tidz][dz][dy][dx] = u;
1475 }
1476 MFEM_SYNC_THREAD;
1477 // threads assigned to mitigate bank conflicts
1478 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_C,
1479 NDOF_O, mnq, mnq, mnq - 1)
1480 {
1481 real_t u = 0;
1482 for (int qx = 0; qx < NQUAD_O; ++qx)
1483 {
1484 u += QDD[1][tidz][qx][dz][dy] * Gco(qx, dx);
1485 }
1486 Y(dx + (dy + dz * NDOF_C) * NDOF_C + 2 * offset, e) =
1487 DDD[0][tidz][dz][dy][dx] - u;
1488 }
1489 MFEM_SYNC_THREAD;
1490 });
1491}
1492
1493} // namespace internal
1494
1495template <int DIM, int NDOF_O, int NQUAD_O>
1497CurlInterpolator::ApplyPAKernels::Kernel()
1498{
1499 if constexpr (DIM == 3)
1500 {
1501 return internal::CurlInterpolatorApply3DSmem<NDOF_O, NQUAD_O>;
1502 }
1503 MFEM_ABORT("Bad dimension!");
1504}
1505
1506template <int DIM, int NDOF_O, int NQUAD_O>
1508CurlInterpolator::ApplyTPAKernels::Kernel()
1509{
1510 if constexpr (DIM == 3)
1511 {
1512 return internal::CurlInterpolatorTApply3DSmem<NDOF_O, NQUAD_O>;
1513 }
1514 MFEM_ABORT("Bad dimension!");
1515}
1516
1517} // namespace mfem
1518
1519/// \endcond DO_NOT_DOCUMENT
1520
1521#endif
void(*)(const int ne, const int ndof_o, const int nquad_o, const Vector &pa, const Vector &x, Vector &y) ApplyKernelType
int dim
Definition ex24.cpp:53
constexpr int DIM
mfem::real_t real_t
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
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
float real_t
Definition config.hpp:46
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