MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_hcurl_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_HCURL_KERNELS_HPP
13#define MFEM_BILININTEG_HCURL_KERNELS_HPP
14
20#include "../bilininteg.hpp"
21
22// Piola transformation in H(curl): w = dF^{-T} \hat{w}
23// curl w = (1 / det (dF)) dF \hat{curl} \hat{w}
24
25namespace mfem
26{
27/// \cond DO_NOT_DOCUMENT
28namespace internal
29{
30
31// PA H(curl) Mass Diagonal 2D kernel
32void PAHcurlMassAssembleDiagonal2D(const int D1D,
33 const int Q1D,
34 const int NE,
35 const bool symmetric,
36 const Array<real_t> &bo,
37 const Array<real_t> &bc,
38 const Vector &pa_data,
39 Vector &diag);
40
41// PA H(curl) Mass Diagonal 3D kernel
42void PAHcurlMassAssembleDiagonal3D(const int D1D,
43 const int Q1D,
44 const int NE,
45 const bool symmetric,
46 const Array<real_t> &bo,
47 const Array<real_t> &bc,
48 const Vector &pa_data,
49 Vector &diag);
50
51// Shared memory PA H(curl) Mass Diagonal 3D kernel
52template<int T_D1D = 0, int T_Q1D = 0>
53inline void SmemPAHcurlMassAssembleDiagonal3D(const int d1d,
54 const int q1d,
55 const int NE,
56 const bool symmetric,
57 const Array<real_t> &bo,
58 const Array<real_t> &bc,
59 const Vector &pa_data,
60 Vector &diag)
61{
62 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
63 "Error: d1d > HCURL_MAX_D1D");
64 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
65 "Error: q1d > HCURL_MAX_Q1D");
66 const int D1D = T_D1D ? T_D1D : d1d;
67 const int Q1D = T_Q1D ? T_Q1D : q1d;
68
69 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
70 auto Bc = Reshape(bc.Read(), Q1D, D1D);
71 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
72 auto D = Reshape(diag.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
73
74 mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
75 {
76 constexpr int VDIM = 3;
77 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
78 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
79 const int D1D = T_D1D ? T_D1D : d1d;
80 const int Q1D = T_Q1D ? T_Q1D : q1d;
81
82 MFEM_SHARED real_t sBo[MQ1D][MD1D];
83 MFEM_SHARED real_t sBc[MQ1D][MD1D];
84
85 real_t op3[3];
86 MFEM_SHARED real_t sop[3][MQ1D][MQ1D];
87
88 MFEM_FOREACH_THREAD(qx,x,Q1D)
89 {
90 MFEM_FOREACH_THREAD(qy,y,Q1D)
91 {
92 MFEM_FOREACH_THREAD(qz,z,Q1D)
93 {
94 op3[0] = op(qx,qy,qz,0,e);
95 op3[1] = op(qx,qy,qz,symmetric ? 3 : 4,e);
96 op3[2] = op(qx,qy,qz,symmetric ? 5 : 8,e);
97 }
98 }
99 }
100
101 const int tidx = MFEM_THREAD_ID(x);
102 const int tidy = MFEM_THREAD_ID(y);
103 const int tidz = MFEM_THREAD_ID(z);
104
105 if (tidz == 0)
106 {
107 MFEM_FOREACH_THREAD(d,y,D1D)
108 {
109 MFEM_FOREACH_THREAD(q,x,Q1D)
110 {
111 sBc[q][d] = Bc(q,d);
112 if (d < D1D-1)
113 {
114 sBo[q][d] = Bo(q,d);
115 }
116 }
117 }
118 }
119 MFEM_SYNC_THREAD;
120
121 int osc = 0;
122 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
123 {
124 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
125 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
126 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
127
128 real_t dxyz = 0.0;
129
130 for (int qz=0; qz < Q1D; ++qz)
131 {
132 if (tidz == qz)
133 {
134 for (int i=0; i<3; ++i)
135 {
136 sop[i][tidx][tidy] = op3[i];
137 }
138 }
139
140 MFEM_SYNC_THREAD;
141
142 MFEM_FOREACH_THREAD(dz,z,D1Dz)
143 {
144 const real_t wz = ((c == 2) ? sBo[qz][dz] : sBc[qz][dz]);
145
146 MFEM_FOREACH_THREAD(dy,y,D1Dy)
147 {
148 MFEM_FOREACH_THREAD(dx,x,D1Dx)
149 {
150 for (int qy = 0; qy < Q1D; ++qy)
151 {
152 const real_t wy = ((c == 1) ? sBo[qy][dy] : sBc[qy][dy]);
153
154 for (int qx = 0; qx < Q1D; ++qx)
155 {
156 const real_t wx = ((c == 0) ? sBo[qx][dx] : sBc[qx][dx]);
157 dxyz += sop[c][qx][qy] * wx * wx * wy * wy * wz * wz;
158 }
159 }
160 }
161 }
162 }
163
164 MFEM_SYNC_THREAD;
165 } // qz loop
166
167 MFEM_FOREACH_THREAD(dz,z,D1Dz)
168 {
169 MFEM_FOREACH_THREAD(dy,y,D1Dy)
170 {
171 MFEM_FOREACH_THREAD(dx,x,D1Dx)
172 {
173 D(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += dxyz;
174 }
175 }
176 }
177
178 osc += D1Dx * D1Dy * D1Dz;
179 } // c loop
180 }); // end of element loop
181}
182
183// PA H(curl) Mass Apply 2D kernel
184void PAHcurlMassApply2D(const int NE, const bool symmetric,
185 const bool scalar_coeff, const Array<real_t> &bo,
186 const Array<real_t> &bc, const Array<real_t> &bot,
187 const Array<real_t> &bct, const Vector &pa_data,
188 const Vector &x, Vector &y, const int TrialD1D,
189 const int TestD1D, const int Q1D);
190
191// PA H(curl) Mass Apply 3D kernel
192void PAHcurlMassApply3D(const int NE, const bool symmetric,
193 [[maybe_unused]] const bool scalar_coeff,
194 const Array<real_t> &bo, const Array<real_t> &bc,
195 const Array<real_t> &bot, const Array<real_t> &bct,
196 const Vector &pa_data, const Vector &x, Vector &y,
197 const int TrialD1D, [[maybe_unused]] const int TestD1D,
198 const int Q1D);
199
200// Shared memory PA H(curl) Mass Apply 3D kernel
201template <int T_D1D = 0, int T_Q1D = 0, int TBATCH = 0, bool ACCUMULATE = true>
202inline void SmemPAHcurlMassApply3D(
203 const int NE, const bool symmetric, [[maybe_unused]] const bool scalar_coeff,
204 const Array<real_t> &bo, const Array<real_t> &bc,
205 [[maybe_unused]] const Array<real_t> &bot,
206 [[maybe_unused]] const Array<real_t> &bct, const Vector &pa_data,
207 const Vector &x, Vector &y, const int d1d = 0,
208 [[maybe_unused]] const int test_d1d = 0, const int q1d = 0)
209{
210 const int D1D = T_D1D ? T_D1D : d1d;
211 const int Q1D = T_Q1D ? T_Q1D : q1d;
212
213 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
214 "Error: d1d > HCURL_MAX_D1D");
215 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
216 "Error: q1d > HCURL_MAX_Q1D");
217 MFEM_ASSERT(Q1D >= D1D, "Expected Q1D >= D1D");
218 const int dataSize = symmetric ? 6 : 9;
219
220 // assume trial space == test space
221 auto Bo = bo.Read();
222 auto Bc = bc.Read();
223 auto op =
224 Reshape(pa_data.Read(), Q1D, Q1D, Q1D, dataSize, NE);
225 auto X_ = Reshape(x.Read(), 3 * (D1D - 1) * D1D * D1D, NE);
226 auto y_ = y.ReadWrite();
227
228 constexpr int MD_ = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
229 constexpr int MQ_ = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
230 constexpr int MDQ_ = std::max(MD_, MQ_);
231 constexpr int MB_ = TBATCH ? TBATCH : 1;
232
234 NE, MDQ_ * MDQ_ * MDQ_, 1, MB_, [=] MFEM_HOST_DEVICE(int e)
235 {
236#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
237 constexpr int nbz = TBATCH ? TBATCH : 1;
238 int tidz = MFEM_THREAD_ID(z);
239#else
240 constexpr int nbz = 1;
241 constexpr int tidz = 0;
242#endif
243
244 constexpr int VDIM = 3;
245 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
246 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
247 constexpr int MDQ = std::max(MD1D, MQ1D);
248
249 // nvcc limit work-around: can't have Y_ be captured first in
250 // if constexpr, so capture y_ and construct Y_ locally
251 // only works on GPU
252 auto Y = Reshape(y_, VDIM * (D1D - 1) * D1D * D1D, NE);
253
254 MFEM_SHARED real_t sBo[MDQ * (MD1D - 1)];
255 MFEM_SHARED real_t sBc[MDQ * MD1D];
256 auto BO = Reshape(sBo, Q1D, D1D - 1);
257 auto BC = Reshape(sBc, Q1D, D1D);
258
259 MFEM_SHARED real_t sX[nbz * VDIM * (MD1D - 1) * MD1D * MD1D];
260 MFEM_SHARED real_t sm0[nbz * VDIM * MDQ * MDQ * MDQ];
261 MFEM_SHARED real_t sm1[nbz * VDIM * MDQ * MDQ * MDQ];
262
263 real_t(*X)[nbz][(MD1D - 1) * MD1D * MD1D] =
264 (real_t(*)[nbz][(MD1D - 1) * MD1D * MD1D])(sX);
265 // shapes of buffers always use MQ1D to mitigate shared memory bank
266 // conflicts
267 real_t(*DDQ)[nbz][MQ1D][MQ1D][MQ1D] =
268 (real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
269 real_t(*DQQ)[nbz][MQ1D][MQ1D][MQ1D] =
270 (real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm1);
271 real_t(*QQQ)[nbz][MQ1D][MQ1D][MQ1D] =
272 (real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
273 real_t(*QQD)[nbz][MQ1D][MQ1D][MQ1D] =
274 (real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm1);
275 real_t(*QDD)[nbz][MQ1D][MQ1D][MQ1D] =
276 (real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
277
278 // load dofs into smem
279 const int offset = (D1D - 1) * D1D * D1D;
280 MFEM_FOREACH_THREAD_DIRECT(ix, x, offset)
281 {
282 for (int dim = 0; dim < VDIM; ++dim)
283 {
284 X[dim][tidz][ix] = X_(ix + dim * offset, e);
285 }
286 }
287 // load basis functions data
288 if (tidz == 0)
289 {
290 MFEM_FOREACH_THREAD_DIRECT(ix, x, D1D * Q1D) { sBc[ix] = Bc[ix]; }
291 MFEM_FOREACH_THREAD_DIRECT(ix, x, (D1D - 1) * Q1D)
292 {
293 sBo[ix] = Bo[ix];
294 }
295 }
296
297 for (int dim0 = 0; dim0 < VDIM; ++dim0)
298 {
299 MFEM_SYNC_THREAD;
300 // sum factor to QQQ = Q_{dim0,dim1} B X_{dim1}
301 for (int dim1 = 0; dim1 < VDIM; ++dim1)
302 {
303 const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
304 const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
305 const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
306
307 // threads assigned to mitigate bank conflicts
308 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, Q1D, D1Dy, D1Dz,
309 Q1D, Q1D, Q1D)
310 {
311 real_t u = 0;
312 for (int dx = 0; dx < D1Dx; ++dx)
313 {
314 real_t b;
315 if (dim1 == 0)
316 {
317 b = BO(qx, dx);
318 }
319 else
320 {
321 b = BC(qx, dx);
322 }
323 u += X[dim1][tidz][dx + (dy + dz * D1Dy) * D1Dx] * b;
324 }
325 DDQ[dim1][tidz][dz][dy][qx] = u;
326 }
327 }
328 MFEM_SYNC_THREAD;
329 for (int dim1 = 0; dim1 < VDIM; ++dim1)
330 {
331 const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
332 const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
333 // const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
334 // threads assigned to mitigate bank conflicts
335 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, Q1D, Q1D, D1Dz,
336 Q1D, Q1D, Q1D)
337 {
338 real_t u = 0;
339 for (int dy = 0; dy < D1Dy; ++dy)
340 {
341 real_t b;
342 if (dim1 == 1)
343 {
344 b = BO(qy, dy);
345 }
346 else
347 {
348 b = BC(qy, dy);
349 }
350 u += DDQ[dim1][tidz][dz][dy][qx] * b;
351 }
352 DQQ[dim1][tidz][dz][qy][qx] = u;
353 }
354 }
355 MFEM_SYNC_THREAD;
356 for (int dim1 = 0; dim1 < VDIM; ++dim1)
357 {
358 const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
359 // const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
360 // const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
361 MFEM_FOREACH_THREAD_DIRECT_3D(qx, qy, qz, x, Q1D, Q1D, Q1D)
362 {
363 real_t u = 0;
364 for (int dz = 0; dz < D1Dz; ++dz)
365 {
366 real_t b;
367 if (dim1 == 2)
368 {
369 b = BO(qz, dz);
370 }
371 else
372 {
373 b = BC(qz, dz);
374 }
375 u += DQQ[dim1][tidz][dz][qy][qx] * b;
376 }
377 // pa_data is row major
378 int idx;
379 if (symmetric)
380 {
381 int row;
382 int col;
383 if (dim0 > dim1)
384 {
385 row = dim1;
386 col = dim0;
387 }
388 else
389 {
390 row = dim0;
391 col = dim1;
392 }
393 idx = col + VDIM * row - row * (row + 1) / 2;
394 }
395 else
396 {
397 idx = dim0 * VDIM + dim1;
398 }
399 QQQ[dim1][tidz][qz][qy][qx] = op(qx, qy, qz, idx, e) * u;
400 }
401 }
402 MFEM_SYNC_THREAD;
403 // sum factor back to Y
404 // Assume bot and bct == bo^t and bc^t respectively (i.e. test ==
405 // trial functions), skip loading them again.
406 {
407 const int D1Dz = (dim0 == 2) ? D1D - 1 : D1D;
408 const int D1Dy = (dim0 == 1) ? D1D - 1 : D1D;
409 const int D1Dx = (dim0 == 0) ? D1D - 1 : D1D;
410 // threads assigned to mitigate bank conflicts
411 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, D1Dz, Q1D, Q1D,
412 Q1D, Q1D, Q1D)
413 {
414 for (int dim1 = 0; dim1 < VDIM; ++dim1)
415 {
416 real_t u = 0;
417 for (int qz = 0; qz < Q1D; ++qz)
418 {
419 real_t b = 0;
420 if (dim0 == 2)
421 {
422 b = BO(qz, dz);
423 }
424 else
425 {
426 b = BC(qz, dz);
427 }
428 u += QQQ[dim1][tidz][qz][qy][qx] * b;
429 }
430 QQD[dim1][tidz][qy][qx][dz] = u;
431 }
432 }
433 MFEM_SYNC_THREAD;
434 // threads assigned to mitigate bank conflicts
435 MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, D1Dy, D1Dz, Q1D,
436 Q1D, Q1D, Q1D)
437 {
438 for (int dim1 = 0; dim1 < VDIM; ++dim1)
439 {
440 real_t u = 0;
441 for (int qy = 0; qy < Q1D; ++qy)
442 {
443 real_t b;
444 if (dim0 == 1)
445 {
446 b = BO(qy, dy);
447 }
448 else
449 {
450 b = BC(qy, dy);
451 }
452 u += QQD[dim1][tidz][qy][qx][dz] * b;
453 }
454 QDD[dim1][tidz][qx][dz][dy] = u;
455 }
456 }
457 MFEM_SYNC_THREAD;
458
459 MFEM_FOREACH_THREAD_DIRECT_3D(dx, dy, dz, x, D1Dx, D1Dy, D1Dz)
460 {
461 int ix = dx + D1Dx * (dy + D1Dy * dz);
462 real_t u = 0;
463 for (int qx = 0; qx < Q1D; ++qx)
464 {
465 real_t b;
466 if (dim0 == 0)
467 {
468 b = BO(qx, dx);
469 }
470 else
471 {
472 b = BC(qx, dx);
473 }
474 for (int dim1 = 0; dim1 < VDIM; ++dim1)
475 {
476 u += QDD[dim1][tidz][qx][dz][dy] * b;
477 }
478 }
479 if constexpr (ACCUMULATE)
480 {
481 Y(ix + dim0 * offset, e) += u;
482 }
483 else
484 {
485 Y(ix + dim0 * offset, e) = u;
486 }
487 }
488 }
489 }
490 }); // end of element loop
491}
492
493// PA H(curl) curl-curl Assemble 2D kernel
494void PACurlCurlSetup2D(const int Q1D,
495 const int NE,
496 const Array<real_t> &w,
497 const Vector &j,
498 Vector &coeff,
499 Vector &op);
500
501// PA H(curl) curl-curl Assemble 3D kernel
502void PACurlCurlSetup3D(const int Q1D,
503 const int coeffDim,
504 const int NE,
505 const Array<real_t> &w,
506 const Vector &j,
507 Vector &coeff,
508 Vector &op);
509
510// PA H(curl) curl-curl Diagonal 2D kernel
511void PACurlCurlAssembleDiagonal2D(const int D1D,
512 const int Q1D,
513 const bool symmetric, // unused
514 const int NE,
515 const Array<real_t> &bo,
516 const Array<real_t> &bc, // unused
517 const Array<real_t> &go, // unused
518 const Array<real_t> &gc,
519 const Vector &pa_data,
520 Vector &diag);
521
522// PA H(curl) curl-curl Diagonal 3D kernel
523template<int T_D1D = 0, int T_Q1D = 0>
524inline void PACurlCurlAssembleDiagonal3D(const int d1d,
525 const int q1d,
526 const bool symmetric,
527 const int NE,
528 const Array<real_t> &bo,
529 const Array<real_t> &bc,
530 const Array<real_t> &go,
531 const Array<real_t> &gc,
532 const Vector &pa_data,
533 Vector &diag)
534{
535 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
536 "Error: d1d > HCURL_MAX_D1D");
537 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
538 "Error: q1d > HCURL_MAX_Q1D");
539 const int D1D = T_D1D ? T_D1D : d1d;
540 const int Q1D = T_Q1D ? T_Q1D : q1d;
541
542 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
543 auto Bc = Reshape(bc.Read(), Q1D, D1D);
544 auto Go = Reshape(go.Read(), Q1D, D1D-1);
545 auto Gc = Reshape(gc.Read(), Q1D, D1D);
546 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, (symmetric ? 6 : 9), NE);
547 auto D = Reshape(diag.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
548
549 const int s = symmetric ? 6 : 9;
550 const int i11 = 0;
551 const int i12 = 1;
552 const int i13 = 2;
553 const int i21 = symmetric ? i12 : 3;
554 const int i22 = symmetric ? 3 : 4;
555 const int i23 = symmetric ? 4 : 5;
556 const int i31 = symmetric ? i13 : 6;
557 const int i32 = symmetric ? i23 : 7;
558 const int i33 = symmetric ? 5 : 8;
559
560 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
561 {
562 // Using (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk), we get
563 // (\nabla\times u) \cdot (\nabla\times u) = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{\nabla}\times\hat{u}
564 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
565 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
566 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
567
568 // For each c, we will keep 9 arrays for derivatives multiplied by the 9 entries of the 3x3 matrix (dF^T C dF),
569 // which may be non-symmetric depending on a possibly non-symmetric matrix coefficient.
570
571 constexpr int VDIM = 3;
572 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
573 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
574 const int D1D = T_D1D ? T_D1D : d1d;
575 const int Q1D = T_Q1D ? T_Q1D : q1d;
576
577 int osc = 0;
578
579 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
580 {
581 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
582 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
583 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
584
585 real_t zt[MQ1D][MQ1D][MD1D][9][3];
586
587 // z contraction
588 for (int qx = 0; qx < Q1D; ++qx)
589 {
590 for (int qy = 0; qy < Q1D; ++qy)
591 {
592 for (int dz = 0; dz < D1Dz; ++dz)
593 {
594 for (int i=0; i<s; ++i)
595 {
596 for (int d=0; d<3; ++d)
597 {
598 zt[qx][qy][dz][i][d] = 0.0;
599 }
600 }
601
602 for (int qz = 0; qz < Q1D; ++qz)
603 {
604 const real_t wz = ((c == 2) ? Bo(qz,dz) : Bc(qz,dz));
605 const real_t wDz = ((c == 2) ? Go(qz,dz) : Gc(qz,dz));
606
607 for (int i=0; i<s; ++i)
608 {
609 zt[qx][qy][dz][i][0] += wz * wz * op(qx,qy,qz,i,e);
610 zt[qx][qy][dz][i][1] += wDz * wz * op(qx,qy,qz,i,e);
611 zt[qx][qy][dz][i][2] += wDz * wDz * op(qx,qy,qz,i,e);
612 }
613 }
614 }
615 }
616 } // end of z contraction
617
618 real_t yt[MQ1D][MD1D][MD1D][9][3][3];
619
620 // y contraction
621 for (int qx = 0; qx < Q1D; ++qx)
622 {
623 for (int dz = 0; dz < D1Dz; ++dz)
624 {
625 for (int dy = 0; dy < D1Dy; ++dy)
626 {
627 for (int i=0; i<s; ++i)
628 {
629 for (int d=0; d<3; ++d)
630 for (int j=0; j<3; ++j)
631 {
632 yt[qx][dy][dz][i][d][j] = 0.0;
633 }
634 }
635
636 for (int qy = 0; qy < Q1D; ++qy)
637 {
638 const real_t wy = ((c == 1) ? Bo(qy,dy) : Bc(qy,dy));
639 const real_t wDy = ((c == 1) ? Go(qy,dy) : Gc(qy,dy));
640
641 for (int i=0; i<s; ++i)
642 {
643 for (int d=0; d<3; ++d)
644 {
645 yt[qx][dy][dz][i][d][0] += wy * wy * zt[qx][qy][dz][i][d];
646 yt[qx][dy][dz][i][d][1] += wDy * wy * zt[qx][qy][dz][i][d];
647 yt[qx][dy][dz][i][d][2] += wDy * wDy * zt[qx][qy][dz][i][d];
648 }
649 }
650 }
651 }
652 }
653 } // end of y contraction
654
655 // x contraction
656 for (int dz = 0; dz < D1Dz; ++dz)
657 {
658 for (int dy = 0; dy < D1Dy; ++dy)
659 {
660 for (int dx = 0; dx < D1Dx; ++dx)
661 {
662 for (int qx = 0; qx < Q1D; ++qx)
663 {
664 const real_t wx = ((c == 0) ? Bo(qx,dx) : Bc(qx,dx));
665 const real_t wDx = ((c == 0) ? Go(qx,dx) : Gc(qx,dx));
666
667 // Using (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk), we get
668 // (\nabla\times u) \cdot (\nabla\times u) = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{\nabla}\times\hat{u}
669 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
670 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
671 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
672
673 /*
674 const double O11 = op(q,0,e);
675 const double O12 = op(q,1,e);
676 const double O13 = op(q,2,e);
677 const double O22 = op(q,3,e);
678 const double O23 = op(q,4,e);
679 const double O33 = op(q,5,e);
680 */
681
682 if (c == 0)
683 {
684 // (u_0)_{x_2} (O22 (u_0)_{x_2} - O23 (u_0)_{x_1}) - (u_0)_{x_1} (O32 (u_0)_{x_2} - O33 (u_0)_{x_1})
685 const real_t sumy = yt[qx][dy][dz][i22][2][0] - yt[qx][dy][dz][i23][1][1]
686 - yt[qx][dy][dz][i32][1][1] + yt[qx][dy][dz][i33][0][2];
687
688 D(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += sumy * wx * wx;
689 }
690 else if (c == 1)
691 {
692 // (u_1)_{x_2} (O11 (u_1)_{x_2} - O13 (u_1)_{x_0}) + (u_1)_{x_0} (-O31 (u_1)_{x_2} + O33 (u_1)_{x_0})
693 const real_t d = (yt[qx][dy][dz][i11][2][0] * wx * wx)
694 - ((yt[qx][dy][dz][i13][1][0] + yt[qx][dy][dz][i31][1][0]) * wDx * wx)
695 + (yt[qx][dy][dz][i33][0][0] * wDx * wDx);
696
697 D(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += d;
698 }
699 else
700 {
701 // (u_2)_{x_1} (O11 (u_2)_{x_1} - O12 (u_2)_{x_0}) - (u_2)_{x_0} (O21 (u_2)_{x_1} - O22 (u_2)_{x_0})
702 const real_t d = (yt[qx][dy][dz][i11][0][2] * wx * wx)
703 - ((yt[qx][dy][dz][i12][0][1] + yt[qx][dy][dz][i21][0][1]) * wDx * wx)
704 + (yt[qx][dy][dz][i22][0][0] * wDx * wDx);
705
706 D(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += d;
707 }
708 }
709 }
710 }
711 } // end of x contraction
712
713 osc += D1Dx * D1Dy * D1Dz;
714 } // loop c
715 }); // end of element loop
716}
717
718// Shared memory PA H(curl) curl-curl Diagonal 3D kernel
719template<int T_D1D = 0, int T_Q1D = 0>
720inline void SmemPACurlCurlAssembleDiagonal3D(const int d1d,
721 const int q1d,
722 const bool symmetric,
723 const int NE,
724 const Array<real_t> &bo,
725 const Array<real_t> &bc,
726 const Array<real_t> &go,
727 const Array<real_t> &gc,
728 const Vector &pa_data,
729 Vector &diag)
730{
731 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
732 "Error: d1d > HCURL_MAX_D1D");
733 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
734 "Error: q1d > HCURL_MAX_Q1D");
735 const int D1D = T_D1D ? T_D1D : d1d;
736 const int Q1D = T_Q1D ? T_Q1D : q1d;
737
738 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
739 auto Bc = Reshape(bc.Read(), Q1D, D1D);
740 auto Go = Reshape(go.Read(), Q1D, D1D-1);
741 auto Gc = Reshape(gc.Read(), Q1D, D1D);
742 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, (symmetric ? 6 : 9), NE);
743 auto D = Reshape(diag.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
744
745 const int s = symmetric ? 6 : 9;
746 const int i11 = 0;
747 const int i12 = 1;
748 const int i13 = 2;
749 const int i21 = symmetric ? i12 : 3;
750 const int i22 = symmetric ? 3 : 4;
751 const int i23 = symmetric ? 4 : 5;
752 const int i31 = symmetric ? i13 : 6;
753 const int i32 = symmetric ? i23 : 7;
754 const int i33 = symmetric ? 5 : 8;
755
756 mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
757 {
758 // Using (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk), we get
759 // (\nabla\times u) \cdot (\nabla\times u) = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{\nabla}\times\hat{u}
760 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
761 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
762 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
763
764 constexpr int VDIM = 3;
765 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
766 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
767 const int D1D = T_D1D ? T_D1D : d1d;
768 const int Q1D = T_Q1D ? T_Q1D : q1d;
769
770 MFEM_SHARED real_t sBo[MQ1D][MD1D];
771 MFEM_SHARED real_t sBc[MQ1D][MD1D];
772 MFEM_SHARED real_t sGo[MQ1D][MD1D];
773 MFEM_SHARED real_t sGc[MQ1D][MD1D];
774
775 real_t ope[9];
776 MFEM_SHARED real_t sop[9][MQ1D][MQ1D];
777
778 MFEM_FOREACH_THREAD(qx,x,Q1D)
779 {
780 MFEM_FOREACH_THREAD(qy,y,Q1D)
781 {
782 MFEM_FOREACH_THREAD(qz,z,Q1D)
783 {
784 for (int i=0; i<s; ++i)
785 {
786 ope[i] = op(qx,qy,qz,i,e);
787 }
788 }
789 }
790 }
791
792 const int tidx = MFEM_THREAD_ID(x);
793 const int tidy = MFEM_THREAD_ID(y);
794 const int tidz = MFEM_THREAD_ID(z);
795
796 if (tidz == 0)
797 {
798 MFEM_FOREACH_THREAD(d,y,D1D)
799 {
800 MFEM_FOREACH_THREAD(q,x,Q1D)
801 {
802 sBc[q][d] = Bc(q,d);
803 sGc[q][d] = Gc(q,d);
804 if (d < D1D-1)
805 {
806 sBo[q][d] = Bo(q,d);
807 sGo[q][d] = Go(q,d);
808 }
809 }
810 }
811 }
812 MFEM_SYNC_THREAD;
813
814 int osc = 0;
815 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
816 {
817 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
818 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
819 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
820
821 real_t dxyz = 0.0;
822
823 for (int qz=0; qz < Q1D; ++qz)
824 {
825 if (tidz == qz)
826 {
827 for (int i=0; i<s; ++i)
828 {
829 sop[i][tidx][tidy] = ope[i];
830 }
831 }
832
833 MFEM_SYNC_THREAD;
834
835 MFEM_FOREACH_THREAD(dz,z,D1Dz)
836 {
837 const real_t wz = ((c == 2) ? sBo[qz][dz] : sBc[qz][dz]);
838 const real_t wDz = ((c == 2) ? sGo[qz][dz] : sGc[qz][dz]);
839
840 MFEM_FOREACH_THREAD(dy,y,D1Dy)
841 {
842 MFEM_FOREACH_THREAD(dx,x,D1Dx)
843 {
844 for (int qy = 0; qy < Q1D; ++qy)
845 {
846 const real_t wy = ((c == 1) ? sBo[qy][dy] : sBc[qy][dy]);
847 const real_t wDy = ((c == 1) ? sGo[qy][dy] : sGc[qy][dy]);
848
849 for (int qx = 0; qx < Q1D; ++qx)
850 {
851 const real_t wx = ((c == 0) ? sBo[qx][dx] : sBc[qx][dx]);
852 const real_t wDx = ((c == 0) ? sGo[qx][dx] : sGc[qx][dx]);
853
854 if (c == 0)
855 {
856 // (u_0)_{x_2} (O22 (u_0)_{x_2} - O23 (u_0)_{x_1}) - (u_0)_{x_1} (O32 (u_0)_{x_2} - O33 (u_0)_{x_1})
857
858 // (u_0)_{x_2} O22 (u_0)_{x_2}
859 dxyz += sop[i22][qx][qy] * wx * wx * wy * wy * wDz * wDz;
860
861 // -(u_0)_{x_2} O23 (u_0)_{x_1} - (u_0)_{x_1} O32 (u_0)_{x_2}
862 dxyz += -(sop[i23][qx][qy] + sop[i32][qx][qy]) * wx * wx * wDy * wy * wDz * wz;
863
864 // (u_0)_{x_1} O33 (u_0)_{x_1}
865 dxyz += sop[i33][qx][qy] * wx * wx * wDy * wDy * wz * wz;
866 }
867 else if (c == 1)
868 {
869 // (u_1)_{x_2} (O11 (u_1)_{x_2} - O13 (u_1)_{x_0}) + (u_1)_{x_0} (-O31 (u_1)_{x_2} + O33 (u_1)_{x_0})
870
871 // (u_1)_{x_2} O11 (u_1)_{x_2}
872 dxyz += sop[i11][qx][qy] * wx * wx * wy * wy * wDz * wDz;
873
874 // -(u_1)_{x_2} O13 (u_1)_{x_0} - (u_1)_{x_0} O31 (u_1)_{x_2}
875 dxyz += -(sop[i13][qx][qy] + sop[i31][qx][qy]) * wDx * wx * wy * wy * wDz * wz;
876
877 // (u_1)_{x_0} O33 (u_1)_{x_0})
878 dxyz += sop[i33][qx][qy] * wDx * wDx * wy * wy * wz * wz;
879 }
880 else
881 {
882 // (u_2)_{x_1} (O11 (u_2)_{x_1} - O12 (u_2)_{x_0}) - (u_2)_{x_0} (O21 (u_2)_{x_1} - O22 (u_2)_{x_0})
883
884 // (u_2)_{x_1} O11 (u_2)_{x_1}
885 dxyz += sop[i11][qx][qy] * wx * wx * wDy * wDy * wz * wz;
886
887 // -(u_2)_{x_1} O12 (u_2)_{x_0} - (u_2)_{x_0} O21 (u_2)_{x_1}
888 dxyz += -(sop[i12][qx][qy] + sop[i21][qx][qy]) * wDx * wx * wDy * wy * wz * wz;
889
890 // (u_2)_{x_0} O22 (u_2)_{x_0}
891 dxyz += sop[i22][qx][qy] * wDx * wDx * wy * wy * wz * wz;
892 }
893 }
894 }
895 }
896 }
897 }
898
899 MFEM_SYNC_THREAD;
900 } // qz loop
901
902 MFEM_FOREACH_THREAD(dz,z,D1Dz)
903 {
904 MFEM_FOREACH_THREAD(dy,y,D1Dy)
905 {
906 MFEM_FOREACH_THREAD(dx,x,D1Dx)
907 {
908 D(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += dxyz;
909 }
910 }
911 }
912
913 osc += D1Dx * D1Dy * D1Dz;
914 } // c loop
915 }); // end of element loop
916}
917
918// PA H(curl) curl-curl Apply/AbsApply 2D kernel
919void PACurlCurlApply2D(const int D1D,
920 const int Q1D,
921 const bool symmetric, // unused
922 const int NE,
923 const Array<real_t> &bo,
924 const Array<real_t> &bc, // unused
925 const Array<real_t> &bot,
926 const Array<real_t> &bct, // unused
927 const Array<real_t> &gc,
928 const Array<real_t> &gct,
929 const Vector &pa_data,
930 const Vector &x,
931 Vector &y,
932 const bool useAbs = false);
933
934// PA H(curl) curl-curl Apply/AbsApply 3D kernel
935template<int T_D1D = 0, int T_Q1D = 0>
936inline void PACurlCurlApply3D(const int d1d,
937 const int q1d,
938 const bool symmetric,
939 const int NE,
940 const Array<real_t> &bo,
941 const Array<real_t> &bc,
942 const Array<real_t> &bot,
943 const Array<real_t> &bct,
944 const Array<real_t> &gc,
945 const Array<real_t> &gct,
946 const Vector &pa_data,
947 const Vector &x,
948 Vector &y,
949 const bool useAbs = false)
950{
951 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
952 "Error: d1d > HCURL_MAX_D1D");
953 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
954 "Error: q1d > HCURL_MAX_Q1D");
955 const int D1D = T_D1D ? T_D1D : d1d;
956 const int Q1D = T_Q1D ? T_Q1D : q1d;
957
958 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
959 auto Bc = Reshape(bc.Read(), Q1D, D1D);
960 auto Bot = Reshape(bot.Read(), D1D-1, Q1D);
961 auto Bct = Reshape(bct.Read(), D1D, Q1D);
962 auto Gc = Reshape(gc.Read(), Q1D, D1D);
963 auto Gct = Reshape(gct.Read(), D1D, Q1D);
964 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, (symmetric ? 6 : 9), NE);
965 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
966 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
967
968 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
969 {
970 // Using (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk),
971 // we get:
972 // (\nabla\times u) \cdot (\nabla\times v)
973 // = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{\nabla}\times\hat{v}
974 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
975 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
976 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
977
978 constexpr int VDIM = 3;
979 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
980 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
981 const int D1D = T_D1D ? T_D1D : d1d;
982 const int Q1D = T_Q1D ? T_Q1D : q1d;
983
984 real_t curl[MQ1D][MQ1D][MQ1D][VDIM];
985 // curl[qz][qy][qx] will be computed as the vector curl at each quadrature point.
986
987 for (int qz = 0; qz < Q1D; ++qz)
988 {
989 for (int qy = 0; qy < Q1D; ++qy)
990 {
991 for (int qx = 0; qx < Q1D; ++qx)
992 {
993 for (int c = 0; c < VDIM; ++c)
994 {
995 curl[qz][qy][qx][c] = 0.0;
996 }
997 }
998 }
999 }
1000
1001 // We treat x, y, z components separately for optimization specific to each.
1002
1003 int osc = 0;
1004
1005 {
1006 // x component
1007 const int D1Dz = D1D;
1008 const int D1Dy = D1D;
1009 const int D1Dx = D1D - 1;
1010
1011 for (int dz = 0; dz < D1Dz; ++dz)
1012 {
1013 real_t gradXY[MQ1D][MQ1D][2];
1014 for (int qy = 0; qy < Q1D; ++qy)
1015 {
1016 for (int qx = 0; qx < Q1D; ++qx)
1017 {
1018 for (int d = 0; d < 2; ++d)
1019 {
1020 gradXY[qy][qx][d] = 0.0;
1021 }
1022 }
1023 }
1024
1025 for (int dy = 0; dy < D1Dy; ++dy)
1026 {
1027 real_t massX[MQ1D];
1028 for (int qx = 0; qx < Q1D; ++qx)
1029 {
1030 massX[qx] = 0.0;
1031 }
1032
1033 for (int dx = 0; dx < D1Dx; ++dx)
1034 {
1035 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
1036 for (int qx = 0; qx < Q1D; ++qx)
1037 {
1038 massX[qx] += t * Bo(qx,dx);
1039 }
1040 }
1041
1042 for (int qy = 0; qy < Q1D; ++qy)
1043 {
1044 const real_t wy = Bc(qy,dy);
1045 const real_t wDy = Gc(qy,dy);
1046 for (int qx = 0; qx < Q1D; ++qx)
1047 {
1048 const real_t wx = massX[qx];
1049 gradXY[qy][qx][0] += wx * wDy;
1050 gradXY[qy][qx][1] += wx * wy;
1051 }
1052 }
1053 }
1054
1055 for (int qz = 0; qz < Q1D; ++qz)
1056 {
1057 const real_t wz = Bc(qz,dz);
1058 const real_t wDz = Gc(qz,dz);
1059 for (int qy = 0; qy < Q1D; ++qy)
1060 {
1061 for (int qx = 0; qx < Q1D; ++qx)
1062 {
1063 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1064 curl[qz][qy][qx][1] += gradXY[qy][qx][1] * wDz; // (u_0)_{x_2}
1065 if (useAbs)
1066 {
1067 // +(u_0)_{x_1}
1068 curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz;
1069 }
1070 else
1071 {
1072 // -(u_0)_{x_1}
1073 curl[qz][qy][qx][2] -= gradXY[qy][qx][0] * wz;
1074 }
1075 }
1076 }
1077 }
1078 }
1079
1080 osc += D1Dx * D1Dy * D1Dz;
1081 }
1082
1083 {
1084 // y component
1085 const int D1Dz = D1D;
1086 const int D1Dy = D1D - 1;
1087 const int D1Dx = D1D;
1088
1089 for (int dz = 0; dz < D1Dz; ++dz)
1090 {
1091 real_t gradXY[MQ1D][MQ1D][2];
1092 for (int qy = 0; qy < Q1D; ++qy)
1093 {
1094 for (int qx = 0; qx < Q1D; ++qx)
1095 {
1096 for (int d = 0; d < 2; ++d)
1097 {
1098 gradXY[qy][qx][d] = 0.0;
1099 }
1100 }
1101 }
1102
1103 for (int dx = 0; dx < D1Dx; ++dx)
1104 {
1105 real_t massY[MQ1D];
1106 for (int qy = 0; qy < Q1D; ++qy)
1107 {
1108 massY[qy] = 0.0;
1109 }
1110
1111 for (int dy = 0; dy < D1Dy; ++dy)
1112 {
1113 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
1114 for (int qy = 0; qy < Q1D; ++qy)
1115 {
1116 massY[qy] += t * Bo(qy,dy);
1117 }
1118 }
1119
1120 for (int qx = 0; qx < Q1D; ++qx)
1121 {
1122 const real_t wx = Bc(qx,dx);
1123 const real_t wDx = Gc(qx,dx);
1124 for (int qy = 0; qy < Q1D; ++qy)
1125 {
1126 const real_t wy = massY[qy];
1127 gradXY[qy][qx][0] += wDx * wy;
1128 gradXY[qy][qx][1] += wx * wy;
1129 }
1130 }
1131 }
1132
1133 for (int qz = 0; qz < Q1D; ++qz)
1134 {
1135 const real_t wz = Bc(qz,dz);
1136 const real_t wDz = Gc(qz,dz);
1137 for (int qy = 0; qy < Q1D; ++qy)
1138 {
1139 for (int qx = 0; qx < Q1D; ++qx)
1140 {
1141 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1142 if (useAbs)
1143 {
1144 // +(u_1)_{x_2}
1145 curl[qz][qy][qx][0] += gradXY[qy][qx][1] * wDz;
1146 }
1147 else
1148 {
1149 // -(u_1)_{x_2}
1150 curl[qz][qy][qx][0] -= gradXY[qy][qx][1] * wDz;
1151 }
1152 curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz; // (u_1)_{x_0}
1153 }
1154 }
1155 }
1156 }
1157
1158 osc += D1Dx * D1Dy * D1Dz;
1159 }
1160
1161 {
1162 // z component
1163 const int D1Dz = D1D - 1;
1164 const int D1Dy = D1D;
1165 const int D1Dx = D1D;
1166
1167 for (int dx = 0; dx < D1Dx; ++dx)
1168 {
1169 real_t gradYZ[MQ1D][MQ1D][2];
1170 for (int qz = 0; qz < Q1D; ++qz)
1171 {
1172 for (int qy = 0; qy < Q1D; ++qy)
1173 {
1174 for (int d = 0; d < 2; ++d)
1175 {
1176 gradYZ[qz][qy][d] = 0.0;
1177 }
1178 }
1179 }
1180
1181 for (int dy = 0; dy < D1Dy; ++dy)
1182 {
1183 real_t massZ[MQ1D];
1184 for (int qz = 0; qz < Q1D; ++qz)
1185 {
1186 massZ[qz] = 0.0;
1187 }
1188
1189 for (int dz = 0; dz < D1Dz; ++dz)
1190 {
1191 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
1192 for (int qz = 0; qz < Q1D; ++qz)
1193 {
1194 massZ[qz] += t * Bo(qz,dz);
1195 }
1196 }
1197
1198 for (int qy = 0; qy < Q1D; ++qy)
1199 {
1200 const real_t wy = Bc(qy,dy);
1201 const real_t wDy = Gc(qy,dy);
1202 for (int qz = 0; qz < Q1D; ++qz)
1203 {
1204 const real_t wz = massZ[qz];
1205 gradYZ[qz][qy][0] += wz * wy;
1206 gradYZ[qz][qy][1] += wz * wDy;
1207 }
1208 }
1209 }
1210
1211 for (int qx = 0; qx < Q1D; ++qx)
1212 {
1213 const real_t wx = Bc(qx,dx);
1214 const real_t wDx = Gc(qx,dx);
1215
1216 for (int qy = 0; qy < Q1D; ++qy)
1217 {
1218 for (int qz = 0; qz < Q1D; ++qz)
1219 {
1220 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1221 curl[qz][qy][qx][0] += gradYZ[qz][qy][1] * wx; // (u_2)_{x_1}
1222 if (useAbs)
1223 {
1224 // +(u_2)_{x_0}
1225 curl[qz][qy][qx][1] += gradYZ[qz][qy][0] * wDx;
1226 }
1227 else
1228 {
1229 // -(u_2)_{x_0}
1230 curl[qz][qy][qx][1] -= gradYZ[qz][qy][0] * wDx;
1231 }
1232 }
1233 }
1234 }
1235 }
1236 }
1237
1238 // Apply D operator.
1239 for (int qz = 0; qz < Q1D; ++qz)
1240 {
1241 for (int qy = 0; qy < Q1D; ++qy)
1242 {
1243 for (int qx = 0; qx < Q1D; ++qx)
1244 {
1245 const real_t O11 = op(qx,qy,qz,0,e);
1246 const real_t O12 = op(qx,qy,qz,1,e);
1247 const real_t O13 = op(qx,qy,qz,2,e);
1248 const real_t O21 = symmetric ? O12 : op(qx,qy,qz,3,e);
1249 const real_t O22 = symmetric ? op(qx,qy,qz,3,e) : op(qx,qy,qz,4,e);
1250 const real_t O23 = symmetric ? op(qx,qy,qz,4,e) : op(qx,qy,qz,5,e);
1251 const real_t O31 = symmetric ? O13 : op(qx,qy,qz,6,e);
1252 const real_t O32 = symmetric ? O23 : op(qx,qy,qz,7,e);
1253 const real_t O33 = symmetric ? op(qx,qy,qz,5,e) : op(qx,qy,qz,8,e);
1254
1255 const real_t c1 = (O11 * curl[qz][qy][qx][0]) + (O12 * curl[qz][qy][qx][1]) +
1256 (O13 * curl[qz][qy][qx][2]);
1257 const real_t c2 = (O21 * curl[qz][qy][qx][0]) + (O22 * curl[qz][qy][qx][1]) +
1258 (O23 * curl[qz][qy][qx][2]);
1259 const real_t c3 = (O31 * curl[qz][qy][qx][0]) + (O32 * curl[qz][qy][qx][1]) +
1260 (O33 * curl[qz][qy][qx][2]);
1261
1262 curl[qz][qy][qx][0] = c1;
1263 curl[qz][qy][qx][1] = c2;
1264 curl[qz][qy][qx][2] = c3;
1265 }
1266 }
1267 }
1268
1269 // x component
1270 osc = 0;
1271 {
1272 const int D1Dz = D1D;
1273 const int D1Dy = D1D;
1274 const int D1Dx = D1D - 1;
1275
1276 for (int qz = 0; qz < Q1D; ++qz)
1277 {
1278 real_t gradXY12[MD1D][MD1D];
1279 real_t gradXY21[MD1D][MD1D];
1280
1281 for (int dy = 0; dy < D1Dy; ++dy)
1282 {
1283 for (int dx = 0; dx < D1Dx; ++dx)
1284 {
1285 gradXY12[dy][dx] = 0.0;
1286 gradXY21[dy][dx] = 0.0;
1287 }
1288 }
1289 for (int qy = 0; qy < Q1D; ++qy)
1290 {
1291 real_t massX[MD1D][2];
1292 for (int dx = 0; dx < D1Dx; ++dx)
1293 {
1294 for (int n = 0; n < 2; ++n)
1295 {
1296 massX[dx][n] = 0.0;
1297 }
1298 }
1299 for (int qx = 0; qx < Q1D; ++qx)
1300 {
1301 for (int dx = 0; dx < D1Dx; ++dx)
1302 {
1303 const real_t wx = Bot(dx,qx);
1304
1305 massX[dx][0] += wx * curl[qz][qy][qx][1];
1306 massX[dx][1] += wx * curl[qz][qy][qx][2];
1307 }
1308 }
1309 for (int dy = 0; dy < D1Dy; ++dy)
1310 {
1311 const real_t wy = Bct(dy,qy);
1312 const real_t wDy = Gct(dy,qy);
1313
1314 for (int dx = 0; dx < D1Dx; ++dx)
1315 {
1316 gradXY21[dy][dx] += massX[dx][0] * wy;
1317 gradXY12[dy][dx] += massX[dx][1] * wDy;
1318 }
1319 }
1320 }
1321
1322 for (int dz = 0; dz < D1Dz; ++dz)
1323 {
1324 const real_t wz = Bct(dz,qz);
1325 const real_t wDz = Gct(dz,qz);
1326 for (int dy = 0; dy < D1Dy; ++dy)
1327 {
1328 for (int dx = 0; dx < D1Dx; ++dx)
1329 {
1330 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1331 const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
1332 if (useAbs)
1333 {
1334 // (u_0)_{x_2} * (op * curl)_1 +
1335 // (u_0)_{x_1} * (op * curl)_2
1336 Y(idx, e) += (gradXY21[dy][dx] * wDz) +
1337 (gradXY12[dy][dx] * wz);
1338 }
1339 else
1340 {
1341 // (u_0)_{x_2} * (op * curl)_1 -
1342 // (u_0)_{x_1} * (op * curl)_2
1343 Y(idx, e) += (gradXY21[dy][dx] * wDz) -
1344 (gradXY12[dy][dx] * wz);
1345 }
1346 }
1347 }
1348 }
1349 } // loop qz
1350
1351 osc += D1Dx * D1Dy * D1Dz;
1352 }
1353
1354 // y component
1355 {
1356 const int D1Dz = D1D;
1357 const int D1Dy = D1D - 1;
1358 const int D1Dx = D1D;
1359
1360 for (int qz = 0; qz < Q1D; ++qz)
1361 {
1362 real_t gradXY02[MD1D][MD1D];
1363 real_t gradXY20[MD1D][MD1D];
1364
1365 for (int dy = 0; dy < D1Dy; ++dy)
1366 {
1367 for (int dx = 0; dx < D1Dx; ++dx)
1368 {
1369 gradXY02[dy][dx] = 0.0;
1370 gradXY20[dy][dx] = 0.0;
1371 }
1372 }
1373 for (int qx = 0; qx < Q1D; ++qx)
1374 {
1375 real_t massY[MD1D][2];
1376 for (int dy = 0; dy < D1Dy; ++dy)
1377 {
1378 massY[dy][0] = 0.0;
1379 massY[dy][1] = 0.0;
1380 }
1381 for (int qy = 0; qy < Q1D; ++qy)
1382 {
1383 for (int dy = 0; dy < D1Dy; ++dy)
1384 {
1385 const real_t wy = Bot(dy,qy);
1386
1387 massY[dy][0] += wy * curl[qz][qy][qx][2];
1388 massY[dy][1] += wy * curl[qz][qy][qx][0];
1389 }
1390 }
1391 for (int dx = 0; dx < D1Dx; ++dx)
1392 {
1393 const real_t wx = Bct(dx,qx);
1394 const real_t wDx = Gct(dx,qx);
1395
1396 for (int dy = 0; dy < D1Dy; ++dy)
1397 {
1398 gradXY02[dy][dx] += massY[dy][0] * wDx;
1399 gradXY20[dy][dx] += massY[dy][1] * wx;
1400 }
1401 }
1402 }
1403
1404 for (int dz = 0; dz < D1Dz; ++dz)
1405 {
1406 const real_t wz = Bct(dz,qz);
1407 const real_t wDz = Gct(dz,qz);
1408 for (int dy = 0; dy < D1Dy; ++dy)
1409 {
1410 for (int dx = 0; dx < D1Dx; ++dx)
1411 {
1412 const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
1413 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1414 if (useAbs)
1415 {
1416 // +(u_1)_{x_2} * (op * curl)_0 +
1417 // (u_1)_{x_0} * (op * curl)_2
1418 Y(idx, e) += (gradXY20[dy][dx] * wDz) +
1419 (gradXY02[dy][dx] * wz);
1420 }
1421 else
1422 {
1423 // -(u_1)_{x_2} * (op * curl)_0 +
1424 // (u_1)_{x_0} * (op * curl)_2
1425 Y(idx, e) += (-gradXY20[dy][dx] * wDz) +
1426 (gradXY02[dy][dx] * wz);
1427 }
1428 }
1429 }
1430 }
1431 } // loop qz
1432
1433 osc += D1Dx * D1Dy * D1Dz;
1434 }
1435
1436 // z component
1437 {
1438 const int D1Dz = D1D - 1;
1439 const int D1Dy = D1D;
1440 const int D1Dx = D1D;
1441
1442 for (int qx = 0; qx < Q1D; ++qx)
1443 {
1444 real_t gradYZ01[MD1D][MD1D];
1445 real_t gradYZ10[MD1D][MD1D];
1446
1447 for (int dy = 0; dy < D1Dy; ++dy)
1448 {
1449 for (int dz = 0; dz < D1Dz; ++dz)
1450 {
1451 gradYZ01[dz][dy] = 0.0;
1452 gradYZ10[dz][dy] = 0.0;
1453 }
1454 }
1455 for (int qy = 0; qy < Q1D; ++qy)
1456 {
1457 real_t massZ[MD1D][2];
1458 for (int dz = 0; dz < D1Dz; ++dz)
1459 {
1460 for (int n = 0; n < 2; ++n)
1461 {
1462 massZ[dz][n] = 0.0;
1463 }
1464 }
1465 for (int qz = 0; qz < Q1D; ++qz)
1466 {
1467 for (int dz = 0; dz < D1Dz; ++dz)
1468 {
1469 const real_t wz = Bot(dz,qz);
1470
1471 massZ[dz][0] += wz * curl[qz][qy][qx][0];
1472 massZ[dz][1] += wz * curl[qz][qy][qx][1];
1473 }
1474 }
1475 for (int dy = 0; dy < D1Dy; ++dy)
1476 {
1477 const real_t wy = Bct(dy,qy);
1478 const real_t wDy = Gct(dy,qy);
1479
1480 for (int dz = 0; dz < D1Dz; ++dz)
1481 {
1482 gradYZ01[dz][dy] += wy * massZ[dz][1];
1483 gradYZ10[dz][dy] += wDy * massZ[dz][0];
1484 }
1485 }
1486 }
1487
1488 for (int dx = 0; dx < D1Dx; ++dx)
1489 {
1490 const real_t wx = Bct(dx,qx);
1491 const real_t wDx = Gct(dx,qx);
1492
1493 for (int dy = 0; dy < D1Dy; ++dy)
1494 {
1495 for (int dz = 0; dz < D1Dz; ++dz)
1496 {
1497 const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
1498 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1499 if (useAbs)
1500 {
1501 // (u_2)_{x_1} * (op * curl)_0 +
1502 // (u_2)_{x_0} * (op * curl)_1
1503 Y(idx, e) += (gradYZ10[dz][dy] * wx) +
1504 (gradYZ01[dz][dy] * wDx);
1505 }
1506 else
1507 {
1508 // (u_2)_{x_1} * (op * curl)_0 -
1509 // (u_2)_{x_0} * (op * curl)_1
1510 Y(idx, e) += (gradYZ10[dz][dy] * wx) -
1511 (gradYZ01[dz][dy] * wDx);
1512 }
1513 }
1514 }
1515 }
1516 } // loop qx
1517 }
1518 }); // end of element loop
1519}
1520
1521// Shared memory PA H(curl) curl-curl Apply/AbsApply 3D kernel
1522template<int T_D1D = 0, int T_Q1D = 0>
1523inline void SmemPACurlCurlApply3D(const int d1d,
1524 const int q1d,
1525 const bool symmetric,
1526 const int NE,
1527 const Array<real_t> &bo,
1528 const Array<real_t> &bc,
1529 const Array<real_t> &bot,
1530 const Array<real_t> &bct,
1531 const Array<real_t> &gc,
1532 const Array<real_t> &gct,
1533 const Vector &pa_data,
1534 const Vector &x,
1535 Vector &y,
1536 const bool useAbs = false)
1537{
1538 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
1539 "Error: d1d > HCURL_MAX_D1D");
1540 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
1541 "Error: q1d > HCURL_MAX_Q1D");
1542 const int D1D = T_D1D ? T_D1D : d1d;
1543 const int Q1D = T_Q1D ? T_Q1D : q1d;
1544
1545 // Using (\nabla\times u) F = 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk), we get
1546 // (\nabla\times u) \cdot (\nabla\times v) = 1/det(dF)^2 \hat{\nabla}\times\hat{u}^T dF^T dF \hat{\nabla}\times\hat{v}
1547 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1548 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1549 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1550
1551 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
1552 auto Bc = Reshape(bc.Read(), Q1D, D1D);
1553 auto Gc = Reshape(gc.Read(), Q1D, D1D);
1554 auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
1555 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
1556 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
1557
1558 const int s = symmetric ? 6 : 9;
1559
1560 auto device_kernel = [=] MFEM_DEVICE (int e)
1561 {
1562 constexpr int VDIM = 3;
1563 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
1564 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
1565 const int D1D = T_D1D ? T_D1D : d1d;
1566 const int Q1D = T_Q1D ? T_Q1D : q1d;
1567
1568 MFEM_SHARED real_t sBo[MD1D][MQ1D];
1569 MFEM_SHARED real_t sBc[MD1D][MQ1D];
1570 MFEM_SHARED real_t sGc[MD1D][MQ1D];
1571
1572 real_t ope[9];
1573 MFEM_SHARED real_t sop[9][MQ1D][MQ1D];
1574 MFEM_SHARED real_t curl[MQ1D][MQ1D][3];
1575
1576 MFEM_SHARED real_t sX[MD1D][MD1D][MD1D];
1577
1578 MFEM_FOREACH_THREAD(qx,x,Q1D)
1579 {
1580 MFEM_FOREACH_THREAD(qy,y,Q1D)
1581 {
1582 MFEM_FOREACH_THREAD(qz,z,Q1D)
1583 {
1584 for (int i=0; i<s; ++i)
1585 {
1586 ope[i] = op(qx,qy,qz,i,e);
1587 }
1588 }
1589 }
1590 }
1591
1592 const int tidx = MFEM_THREAD_ID(x);
1593 const int tidy = MFEM_THREAD_ID(y);
1594 const int tidz = MFEM_THREAD_ID(z);
1595
1596 if (tidz == 0)
1597 {
1598 MFEM_FOREACH_THREAD(d,y,D1D)
1599 {
1600 MFEM_FOREACH_THREAD(q,x,Q1D)
1601 {
1602 sBc[d][q] = Bc(q,d);
1603 sGc[d][q] = Gc(q,d);
1604 if (d < D1D-1)
1605 {
1606 sBo[d][q] = Bo(q,d);
1607 }
1608 }
1609 }
1610 }
1611 MFEM_SYNC_THREAD;
1612
1613 for (int qz=0; qz < Q1D; ++qz)
1614 {
1615 if (tidz == qz)
1616 {
1617 MFEM_FOREACH_THREAD(qy,y,Q1D)
1618 {
1619 MFEM_FOREACH_THREAD(qx,x,Q1D)
1620 {
1621 for (int i=0; i<3; ++i)
1622 {
1623 curl[qy][qx][i] = 0.0;
1624 }
1625 }
1626 }
1627 }
1628
1629 int osc = 0;
1630 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
1631 {
1632 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
1633 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
1634 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
1635
1636 MFEM_FOREACH_THREAD(dz,z,D1Dz)
1637 {
1638 MFEM_FOREACH_THREAD(dy,y,D1Dy)
1639 {
1640 MFEM_FOREACH_THREAD(dx,x,D1Dx)
1641 {
1642 sX[dz][dy][dx] = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
1643 }
1644 }
1645 }
1646 MFEM_SYNC_THREAD;
1647
1648 if (tidz == qz)
1649 {
1650 if (c == 0)
1651 {
1652 for (int i=0; i<s; ++i)
1653 {
1654 sop[i][tidx][tidy] = ope[i];
1655 }
1656 }
1657
1658 MFEM_FOREACH_THREAD(qy,y,Q1D)
1659 {
1660 MFEM_FOREACH_THREAD(qx,x,Q1D)
1661 {
1662 real_t u = 0.0;
1663 real_t v = 0.0;
1664
1665 // We treat x, y, z components separately for optimization specific to each.
1666 if (c == 0) // x component
1667 {
1668 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1669
1670 for (int dz = 0; dz < D1Dz; ++dz)
1671 {
1672 const real_t wz = sBc[dz][qz];
1673 const real_t wDz = sGc[dz][qz];
1674
1675 for (int dy = 0; dy < D1Dy; ++dy)
1676 {
1677 const real_t wy = sBc[dy][qy];
1678 const real_t wDy = sGc[dy][qy];
1679
1680 for (int dx = 0; dx < D1Dx; ++dx)
1681 {
1682 const real_t wx = sX[dz][dy][dx] * sBo[dx][qx];
1683 u += wx * wDy * wz;
1684 v += wx * wy * wDz;
1685 }
1686 }
1687 }
1688
1689 curl[qy][qx][1] += v; // (u_0)_{x_2}
1690 if (useAbs) { curl[qy][qx][2] += u; } // +(u_0)_{x_1}
1691 else { curl[qy][qx][2] -= u; } // -(u_0)_{x_1}
1692 }
1693 else if (c == 1) // y component
1694 {
1695 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1696
1697 for (int dz = 0; dz < D1Dz; ++dz)
1698 {
1699 const real_t wz = sBc[dz][qz];
1700 const real_t wDz = sGc[dz][qz];
1701
1702 for (int dy = 0; dy < D1Dy; ++dy)
1703 {
1704 const real_t wy = sBo[dy][qy];
1705
1706 for (int dx = 0; dx < D1Dx; ++dx)
1707 {
1708 const real_t t = sX[dz][dy][dx];
1709 const real_t wx = t * sBc[dx][qx];
1710 const real_t wDx = t * sGc[dx][qx];
1711
1712 u += wDx * wy * wz;
1713 v += wx * wy * wDz;
1714 }
1715 }
1716 }
1717
1718 if (useAbs) { curl[qy][qx][0] += v; } // +(u_1)_{x_2}
1719 else { curl[qy][qx][0] -= v; } // -(u_1)_{x_2}
1720 curl[qy][qx][2] += u; // (u_1)_{x_0}
1721 }
1722 else // z component
1723 {
1724 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1725
1726 for (int dz = 0; dz < D1Dz; ++dz)
1727 {
1728 const real_t wz = sBo[dz][qz];
1729
1730 for (int dy = 0; dy < D1Dy; ++dy)
1731 {
1732 const real_t wy = sBc[dy][qy];
1733 const real_t wDy = sGc[dy][qy];
1734
1735 for (int dx = 0; dx < D1Dx; ++dx)
1736 {
1737 const real_t t = sX[dz][dy][dx];
1738 const real_t wx = t * sBc[dx][qx];
1739 const real_t wDx = t * sGc[dx][qx];
1740
1741 u += wDx * wy * wz;
1742 v += wx * wDy * wz;
1743 }
1744 }
1745 }
1746
1747 curl[qy][qx][0] += v; // (u_2)_{x_1}
1748 if (useAbs) { curl[qy][qx][1] += u; }// +(u_2)_{x_0}
1749 else { curl[qy][qx][1] -= u; } // -(u_2)_{x_0}
1750 }
1751 } // qx
1752 } // qy
1753 } // tidz == qz
1754
1755 osc += D1Dx * D1Dy * D1Dz;
1756 MFEM_SYNC_THREAD;
1757 } // c
1758
1759 real_t dxyz1 = 0.0;
1760 real_t dxyz2 = 0.0;
1761 real_t dxyz3 = 0.0;
1762
1763 MFEM_FOREACH_THREAD(dz,z,D1D)
1764 {
1765 const real_t wcz = sBc[dz][qz];
1766 const real_t wcDz = sGc[dz][qz];
1767 const real_t wz = (dz < D1D-1) ? sBo[dz][qz] : 0.0;
1768
1769 MFEM_FOREACH_THREAD(dy,y,D1D)
1770 {
1771 MFEM_FOREACH_THREAD(dx,x,D1D)
1772 {
1773 for (int qy = 0; qy < Q1D; ++qy)
1774 {
1775 const real_t wcy = sBc[dy][qy];
1776 const real_t wcDy = sGc[dy][qy];
1777 const real_t wy = (dy < D1D-1) ? sBo[dy][qy] : 0.0;
1778
1779 for (int qx = 0; qx < Q1D; ++qx)
1780 {
1781 const real_t O11 = sop[0][qx][qy];
1782 const real_t O12 = sop[1][qx][qy];
1783 const real_t O13 = sop[2][qx][qy];
1784 const real_t O21 = symmetric ? O12 : sop[3][qx][qy];
1785 const real_t O22 = symmetric ? sop[3][qx][qy] : sop[4][qx][qy];
1786 const real_t O23 = symmetric ? sop[4][qx][qy] : sop[5][qx][qy];
1787 const real_t O31 = symmetric ? O13 : sop[6][qx][qy];
1788 const real_t O32 = symmetric ? O23 : sop[7][qx][qy];
1789 const real_t O33 = symmetric ? sop[5][qx][qy] : sop[8][qx][qy];
1790
1791 const real_t c1 = (O11 * curl[qy][qx][0]) + (O12 * curl[qy][qx][1]) +
1792 (O13 * curl[qy][qx][2]);
1793 const real_t c2 = (O21 * curl[qy][qx][0]) + (O22 * curl[qy][qx][1]) +
1794 (O23 * curl[qy][qx][2]);
1795 const real_t c3 = (O31 * curl[qy][qx][0]) + (O32 * curl[qy][qx][1]) +
1796 (O33 * curl[qy][qx][2]);
1797
1798 const real_t wcx = sBc[dx][qx];
1799 const real_t wDx = sGc[dx][qx];
1800
1801 if (dx < D1D-1)
1802 {
1803 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1804 const real_t wx = sBo[dx][qx];
1805 if (useAbs)
1806 {
1807 // (u_0)_{x_2} * (op * curl)_1 +
1808 // (u_0)_{x_1} * (op * curl)_2
1809 dxyz1 += (wx * c2 * wcy * wcDz) +
1810 (wx * c3 * wcDy * wcz);
1811 }
1812 else
1813 {
1814 // (u_0)_{x_2} * (op * curl)_1 -
1815 // (u_0)_{x_1} * (op * curl)_2
1816 dxyz1 += (wx * c2 * wcy * wcDz) -
1817 (wx * c3 * wcDy * wcz);
1818 }
1819 }
1820
1821 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1822 if (useAbs)
1823 {
1824 // +(u_1)_{x_2} * (op * curl)_0 +
1825 // (u_1)_{x_0} * (op * curl)_2
1826 dxyz2 += (wy * c1 * wcx * wcDz) +
1827 (wy * c3 * wDx * wcz);
1828 }
1829 else
1830 {
1831 // -(u_1)_{x_2} * (op * curl)_0 +
1832 // (u_1)_{x_0} * (op * curl)_2
1833 dxyz2 += (-wy * c1 * wcx * wcDz) +
1834 (wy * c3 * wDx * wcz);
1835 }
1836
1837 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1838 if (useAbs)
1839 {
1840 // (u_2)_{x_1} * (op * curl)_0 +
1841 // (u_2)_{x_0} * (op * curl)_1
1842 dxyz3 += (wcDy * wz * c1 * wcx) +
1843 (wcy * wz * c2 * wDx);
1844 }
1845 else
1846 {
1847 // (u_2)_{x_1} * (op * curl)_0 -
1848 // (u_2)_{x_0} * (op * curl)_1
1849 dxyz3 += (wcDy * wz * c1 * wcx) -
1850 (wcy * wz * c2 * wDx);
1851 }
1852 } // qx
1853 } // qy
1854 } // dx
1855 } // dy
1856 } // dz
1857
1858 MFEM_SYNC_THREAD;
1859
1860 MFEM_FOREACH_THREAD(dz,z,D1D)
1861 {
1862 MFEM_FOREACH_THREAD(dy,y,D1D)
1863 {
1864 MFEM_FOREACH_THREAD(dx,x,D1D)
1865 {
1866 if (dx < D1D-1)
1867 {
1868 Y(dx + ((dy + (dz * D1D)) * (D1D-1)), e) += dxyz1;
1869 }
1870 if (dy < D1D-1)
1871 {
1872 Y(dx + ((dy + (dz * (D1D-1))) * D1D) + ((D1D-1)*D1D*D1D), e) += dxyz2;
1873 }
1874 if (dz < D1D-1)
1875 {
1876 Y(dx + ((dy + (dz * D1D)) * D1D) + (2*(D1D-1)*D1D*D1D), e) += dxyz3;
1877 }
1878 }
1879 }
1880 }
1881 } // qz
1882 }; // end of element loop
1883
1884 auto host_kernel = [&] MFEM_LAMBDA (int)
1885 {
1886 MFEM_ABORT_KERNEL("This kernel should only be used on GPU.");
1887 };
1888
1889 ForallWrap<3>(true, NE, device_kernel, host_kernel, Q1D, Q1D, Q1D);
1890}
1891
1892// PA H(curl)-L2 value Assemble 2D kernel
1893void PAHcurlL2Setup2D(const int Q1D,
1894 const int NE,
1895 const Array<real_t> &w,
1896 Vector &coeff,
1897 Vector &op);
1898
1899// PA H(curl)-L2 integral Assemble 2D kernel
1900void PAHcurlL2IntSetup2D(const int Q1D, const int NE, const Array<real_t> &w,
1901 Vector &coeff, const Vector &detJ, Vector &op);
1902
1903// PA H(curl)-L2 Assemble 3D kernel
1904void PAHcurlL2Setup3D(const int NQ,
1905 const int coeffDim,
1906 const int NE,
1907 const Array<real_t> &w,
1908 Vector &coeff,
1909 Vector &op);
1910
1911// PA H(curl)-L2 Apply 2D kernel
1912void PAHcurlL2Apply2D(const int D1D,
1913 const int D1Dtest,
1914 const int Q1D,
1915 const int NE,
1916 const Array<real_t> &bo,
1917 const Array<real_t> &bot,
1918 const Array<real_t> &bt,
1919 const Array<real_t> &gc,
1920 const Vector &pa_data,
1921 const Vector &x,
1922 Vector &y);
1923
1924// PA H(curl)-L2 Apply Transpose 2D kernel
1925void PAHcurlL2ApplyTranspose2D(const int D1D,
1926 const int D1Dtest,
1927 const int Q1D,
1928 const int NE,
1929 const Array<real_t> &bo,
1930 const Array<real_t> &bot,
1931 const Array<real_t> &b,
1932 const Array<real_t> &gct,
1933 const Vector &pa_data,
1934 const Vector &x,
1935 Vector &y);
1936
1937// PA H(curl)-L2 Apply 3D kernel
1938template<int T_D1D = 0, int T_Q1D = 0>
1939inline void PAHcurlL2Apply3D(const int d1d,
1940 const int q1d,
1941 const int coeffDim,
1942 const int NE,
1943 const Array<real_t> &bo,
1944 const Array<real_t> &bc,
1945 const Array<real_t> &bot,
1946 const Array<real_t> &bct,
1947 const Array<real_t> &gc,
1948 const Vector &pa_data,
1949 const Vector &x,
1950 Vector &y)
1951{
1952 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
1953 "Error: d1d > HCURL_MAX_D1D");
1954 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
1955 "Error: q1d > HCURL_MAX_Q1D");
1956 const int D1D = T_D1D ? T_D1D : d1d;
1957 const int Q1D = T_Q1D ? T_Q1D : q1d;
1958
1959 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
1960 auto Bc = Reshape(bc.Read(), Q1D, D1D);
1961 auto Bot = Reshape(bot.Read(), D1D-1, Q1D);
1962 auto Bct = Reshape(bct.Read(), D1D, Q1D);
1963 auto Gc = Reshape(gc.Read(), Q1D, D1D);
1964 auto op = Reshape(pa_data.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
1965 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
1966 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
1967
1968 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1969 {
1970 // Using u = dF^{-T} \hat{u} and (\nabla\times u) F =
1971 // 1/det(dF) dF \hat{\nabla}\times\hat{u} (p. 78 of Monk), we get:
1972 // (\nabla\times u) \cdot v
1973 // = 1/det(dF) \hat{\nabla}\times\hat{u}^T dF^T dF^{-T} \hat{v}
1974 // = 1/det(dF) \hat{\nabla}\times\hat{u}^T \hat{v}
1975 // If c = 0, \hat{\nabla}\times\hat{u} reduces to [0, (u_0)_{x_2}, -(u_0)_{x_1}]
1976 // If c = 1, \hat{\nabla}\times\hat{u} reduces to [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
1977 // If c = 2, \hat{\nabla}\times\hat{u} reduces to [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
1978
1979 constexpr int VDIM = 3;
1980 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
1981 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
1982 const int D1D = T_D1D ? T_D1D : d1d;
1983 const int Q1D = T_Q1D ? T_Q1D : q1d;
1984
1985 real_t curl[MQ1D][MQ1D][MQ1D][VDIM];
1986 // curl[qz][qy][qx] will be computed as the vector curl at each quadrature point.
1987
1988 for (int qz = 0; qz < Q1D; ++qz)
1989 {
1990 for (int qy = 0; qy < Q1D; ++qy)
1991 {
1992 for (int qx = 0; qx < Q1D; ++qx)
1993 {
1994 for (int c = 0; c < VDIM; ++c)
1995 {
1996 curl[qz][qy][qx][c] = 0.0;
1997 }
1998 }
1999 }
2000 }
2001
2002 // We treat x, y, z components separately for optimization specific to each.
2003
2004 int osc = 0;
2005
2006 {
2007 // x component
2008 const int D1Dz = D1D;
2009 const int D1Dy = D1D;
2010 const int D1Dx = D1D - 1;
2011
2012 for (int dz = 0; dz < D1Dz; ++dz)
2013 {
2014 real_t gradXY[MQ1D][MQ1D][2];
2015 for (int qy = 0; qy < Q1D; ++qy)
2016 {
2017 for (int qx = 0; qx < Q1D; ++qx)
2018 {
2019 for (int d = 0; d < 2; ++d)
2020 {
2021 gradXY[qy][qx][d] = 0.0;
2022 }
2023 }
2024 }
2025
2026 for (int dy = 0; dy < D1Dy; ++dy)
2027 {
2028 real_t massX[MQ1D];
2029 for (int qx = 0; qx < Q1D; ++qx)
2030 {
2031 massX[qx] = 0.0;
2032 }
2033
2034 for (int dx = 0; dx < D1Dx; ++dx)
2035 {
2036 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
2037 for (int qx = 0; qx < Q1D; ++qx)
2038 {
2039 massX[qx] += t * Bo(qx,dx);
2040 }
2041 }
2042
2043 for (int qy = 0; qy < Q1D; ++qy)
2044 {
2045 const real_t wy = Bc(qy,dy);
2046 const real_t wDy = Gc(qy,dy);
2047 for (int qx = 0; qx < Q1D; ++qx)
2048 {
2049 const real_t wx = massX[qx];
2050 gradXY[qy][qx][0] += wx * wDy;
2051 gradXY[qy][qx][1] += wx * wy;
2052 }
2053 }
2054 }
2055
2056 for (int qz = 0; qz < Q1D; ++qz)
2057 {
2058 const real_t wz = Bc(qz,dz);
2059 const real_t wDz = Gc(qz,dz);
2060 for (int qy = 0; qy < Q1D; ++qy)
2061 {
2062 for (int qx = 0; qx < Q1D; ++qx)
2063 {
2064 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
2065 curl[qz][qy][qx][1] += gradXY[qy][qx][1] * wDz; // (u_0)_{x_2}
2066 curl[qz][qy][qx][2] -= gradXY[qy][qx][0] * wz; // -(u_0)_{x_1}
2067 }
2068 }
2069 }
2070 }
2071
2072 osc += D1Dx * D1Dy * D1Dz;
2073 }
2074
2075 {
2076 // y component
2077 const int D1Dz = D1D;
2078 const int D1Dy = D1D - 1;
2079 const int D1Dx = D1D;
2080
2081 for (int dz = 0; dz < D1Dz; ++dz)
2082 {
2083 real_t gradXY[MQ1D][MQ1D][2];
2084 for (int qy = 0; qy < Q1D; ++qy)
2085 {
2086 for (int qx = 0; qx < Q1D; ++qx)
2087 {
2088 for (int d = 0; d < 2; ++d)
2089 {
2090 gradXY[qy][qx][d] = 0.0;
2091 }
2092 }
2093 }
2094
2095 for (int dx = 0; dx < D1Dx; ++dx)
2096 {
2097 real_t massY[MQ1D];
2098 for (int qy = 0; qy < Q1D; ++qy)
2099 {
2100 massY[qy] = 0.0;
2101 }
2102
2103 for (int dy = 0; dy < D1Dy; ++dy)
2104 {
2105 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
2106 for (int qy = 0; qy < Q1D; ++qy)
2107 {
2108 massY[qy] += t * Bo(qy,dy);
2109 }
2110 }
2111
2112 for (int qx = 0; qx < Q1D; ++qx)
2113 {
2114 const real_t wx = Bc(qx,dx);
2115 const real_t wDx = Gc(qx,dx);
2116 for (int qy = 0; qy < Q1D; ++qy)
2117 {
2118 const real_t wy = massY[qy];
2119 gradXY[qy][qx][0] += wDx * wy;
2120 gradXY[qy][qx][1] += wx * wy;
2121 }
2122 }
2123 }
2124
2125 for (int qz = 0; qz < Q1D; ++qz)
2126 {
2127 const real_t wz = Bc(qz,dz);
2128 const real_t wDz = Gc(qz,dz);
2129 for (int qy = 0; qy < Q1D; ++qy)
2130 {
2131 for (int qx = 0; qx < Q1D; ++qx)
2132 {
2133 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
2134 curl[qz][qy][qx][0] -= gradXY[qy][qx][1] * wDz; // -(u_1)_{x_2}
2135 curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz; // (u_1)_{x_0}
2136 }
2137 }
2138 }
2139 }
2140
2141 osc += D1Dx * D1Dy * D1Dz;
2142 }
2143
2144 {
2145 // z component
2146 const int D1Dz = D1D - 1;
2147 const int D1Dy = D1D;
2148 const int D1Dx = D1D;
2149
2150 for (int dx = 0; dx < D1Dx; ++dx)
2151 {
2152 real_t gradYZ[MQ1D][MQ1D][2];
2153 for (int qz = 0; qz < Q1D; ++qz)
2154 {
2155 for (int qy = 0; qy < Q1D; ++qy)
2156 {
2157 for (int d = 0; d < 2; ++d)
2158 {
2159 gradYZ[qz][qy][d] = 0.0;
2160 }
2161 }
2162 }
2163
2164 for (int dy = 0; dy < D1Dy; ++dy)
2165 {
2166 real_t massZ[MQ1D];
2167 for (int qz = 0; qz < Q1D; ++qz)
2168 {
2169 massZ[qz] = 0.0;
2170 }
2171
2172 for (int dz = 0; dz < D1Dz; ++dz)
2173 {
2174 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
2175 for (int qz = 0; qz < Q1D; ++qz)
2176 {
2177 massZ[qz] += t * Bo(qz,dz);
2178 }
2179 }
2180
2181 for (int qy = 0; qy < Q1D; ++qy)
2182 {
2183 const real_t wy = Bc(qy,dy);
2184 const real_t wDy = Gc(qy,dy);
2185 for (int qz = 0; qz < Q1D; ++qz)
2186 {
2187 const real_t wz = massZ[qz];
2188 gradYZ[qz][qy][0] += wz * wy;
2189 gradYZ[qz][qy][1] += wz * wDy;
2190 }
2191 }
2192 }
2193
2194 for (int qx = 0; qx < Q1D; ++qx)
2195 {
2196 const real_t wx = Bc(qx,dx);
2197 const real_t wDx = Gc(qx,dx);
2198
2199 for (int qy = 0; qy < Q1D; ++qy)
2200 {
2201 for (int qz = 0; qz < Q1D; ++qz)
2202 {
2203 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
2204 curl[qz][qy][qx][0] += gradYZ[qz][qy][1] * wx; // (u_2)_{x_1}
2205 curl[qz][qy][qx][1] -= gradYZ[qz][qy][0] * wDx; // -(u_2)_{x_0}
2206 }
2207 }
2208 }
2209 }
2210 }
2211
2212 // Apply D operator.
2213 for (int qz = 0; qz < Q1D; ++qz)
2214 {
2215 for (int qy = 0; qy < Q1D; ++qy)
2216 {
2217 for (int qx = 0; qx < Q1D; ++qx)
2218 {
2219 const real_t O11 = op(0,qx,qy,qz,e);
2220 if (coeffDim == 1)
2221 {
2222 for (int c = 0; c < VDIM; ++c)
2223 {
2224 curl[qz][qy][qx][c] *= O11;
2225 }
2226 }
2227 else
2228 {
2229 const real_t O21 = op(1,qx,qy,qz,e);
2230 const real_t O31 = op(2,qx,qy,qz,e);
2231 const real_t O12 = op(3,qx,qy,qz,e);
2232 const real_t O22 = op(4,qx,qy,qz,e);
2233 const real_t O32 = op(5,qx,qy,qz,e);
2234 const real_t O13 = op(6,qx,qy,qz,e);
2235 const real_t O23 = op(7,qx,qy,qz,e);
2236 const real_t O33 = op(8,qx,qy,qz,e);
2237 const real_t curlX = curl[qz][qy][qx][0];
2238 const real_t curlY = curl[qz][qy][qx][1];
2239 const real_t curlZ = curl[qz][qy][qx][2];
2240 curl[qz][qy][qx][0] = (O11*curlX)+(O12*curlY)+(O13*curlZ);
2241 curl[qz][qy][qx][1] = (O21*curlX)+(O22*curlY)+(O23*curlZ);
2242 curl[qz][qy][qx][2] = (O31*curlX)+(O32*curlY)+(O33*curlZ);
2243 }
2244 }
2245 }
2246 }
2247
2248 for (int qz = 0; qz < Q1D; ++qz)
2249 {
2250 real_t massXY[MD1D][MD1D];
2251
2252 osc = 0;
2253
2254 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
2255 {
2256 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
2257 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
2258 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
2259
2260 for (int dy = 0; dy < D1Dy; ++dy)
2261 {
2262 for (int dx = 0; dx < D1Dx; ++dx)
2263 {
2264 massXY[dy][dx] = 0;
2265 }
2266 }
2267 for (int qy = 0; qy < Q1D; ++qy)
2268 {
2269 real_t massX[MD1D];
2270 for (int dx = 0; dx < D1Dx; ++dx)
2271 {
2272 massX[dx] = 0.0;
2273 }
2274 for (int qx = 0; qx < Q1D; ++qx)
2275 {
2276 for (int dx = 0; dx < D1Dx; ++dx)
2277 {
2278 massX[dx] += curl[qz][qy][qx][c] * ((c == 0) ? Bot(dx,qx) : Bct(dx,qx));
2279 }
2280 }
2281
2282 for (int dy = 0; dy < D1Dy; ++dy)
2283 {
2284 const real_t wy = (c == 1) ? Bot(dy,qy) : Bct(dy,qy);
2285 for (int dx = 0; dx < D1Dx; ++dx)
2286 {
2287 massXY[dy][dx] += massX[dx] * wy;
2288 }
2289 }
2290 }
2291
2292 for (int dz = 0; dz < D1Dz; ++dz)
2293 {
2294 const real_t wz = (c == 2) ? Bot(dz,qz) : Bct(dz,qz);
2295 for (int dy = 0; dy < D1Dy; ++dy)
2296 {
2297 for (int dx = 0; dx < D1Dx; ++dx)
2298 {
2299 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += massXY[dy][dx] * wz;
2300 }
2301 }
2302 }
2303
2304 osc += D1Dx * D1Dy * D1Dz;
2305 } // loop c
2306 } // loop qz
2307 }); // end of element loop
2308}
2309
2310// Shared memory PA H(curl)-L2 Apply 3D kernel
2311template<int T_D1D = 0, int T_Q1D = 0>
2312inline void SmemPAHcurlL2Apply3D(const int d1d,
2313 const int q1d,
2314 const int coeffDim,
2315 const int NE,
2316 const Array<real_t> &bo,
2317 const Array<real_t> &bc,
2318 const Array<real_t> &gc,
2319 const Vector &pa_data,
2320 const Vector &x,
2321 Vector &y)
2322{
2323 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
2324 "Error: d1d > HCURL_MAX_D1D");
2325 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
2326 "Error: q1d > HCURL_MAX_Q1D");
2327 const int D1D = T_D1D ? T_D1D : d1d;
2328 const int Q1D = T_Q1D ? T_Q1D : q1d;
2329
2330 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
2331 auto Bc = Reshape(bc.Read(), Q1D, D1D);
2332 auto Gc = Reshape(gc.Read(), Q1D, D1D);
2333 auto op = Reshape(pa_data.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
2334 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
2335 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
2336
2337 auto device_kernel = [=] MFEM_DEVICE (int e)
2338 {
2339 constexpr int VDIM = 3;
2340 constexpr int maxCoeffDim = 9;
2341 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
2342 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
2343 const int D1D = T_D1D ? T_D1D : d1d;
2344 const int Q1D = T_Q1D ? T_Q1D : q1d;
2345
2346 MFEM_SHARED real_t sBo[MD1D][MQ1D];
2347 MFEM_SHARED real_t sBc[MD1D][MQ1D];
2348 MFEM_SHARED real_t sGc[MD1D][MQ1D];
2349
2350 real_t opc[maxCoeffDim];
2351 MFEM_SHARED real_t sop[maxCoeffDim][MQ1D][MQ1D];
2352 MFEM_SHARED real_t curl[MQ1D][MQ1D][3];
2353
2354 MFEM_SHARED real_t sX[MD1D][MD1D][MD1D];
2355
2356 MFEM_FOREACH_THREAD(qx,x,Q1D)
2357 {
2358 MFEM_FOREACH_THREAD(qy,y,Q1D)
2359 {
2360 MFEM_FOREACH_THREAD(qz,z,Q1D)
2361 {
2362 for (int i=0; i<coeffDim; ++i)
2363 {
2364 opc[i] = op(i,qx,qy,qz,e);
2365 }
2366 }
2367 }
2368 }
2369
2370 const int tidx = MFEM_THREAD_ID(x);
2371 const int tidy = MFEM_THREAD_ID(y);
2372 const int tidz = MFEM_THREAD_ID(z);
2373
2374 if (tidz == 0)
2375 {
2376 MFEM_FOREACH_THREAD(d,y,D1D)
2377 {
2378 MFEM_FOREACH_THREAD(q,x,Q1D)
2379 {
2380 sBc[d][q] = Bc(q,d);
2381 sGc[d][q] = Gc(q,d);
2382 if (d < D1D-1)
2383 {
2384 sBo[d][q] = Bo(q,d);
2385 }
2386 }
2387 }
2388 }
2389 MFEM_SYNC_THREAD;
2390
2391 for (int qz=0; qz < Q1D; ++qz)
2392 {
2393 if (tidz == qz)
2394 {
2395 MFEM_FOREACH_THREAD(qy,y,Q1D)
2396 {
2397 MFEM_FOREACH_THREAD(qx,x,Q1D)
2398 {
2399 for (int i=0; i<3; ++i)
2400 {
2401 curl[qy][qx][i] = 0.0;
2402 }
2403 }
2404 }
2405 }
2406
2407 int osc = 0;
2408 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
2409 {
2410 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
2411 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
2412 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
2413
2414 MFEM_FOREACH_THREAD(dz,z,D1Dz)
2415 {
2416 MFEM_FOREACH_THREAD(dy,y,D1Dy)
2417 {
2418 MFEM_FOREACH_THREAD(dx,x,D1Dx)
2419 {
2420 sX[dz][dy][dx] = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
2421 }
2422 }
2423 }
2424 MFEM_SYNC_THREAD;
2425
2426 if (tidz == qz)
2427 {
2428 if (c == 0)
2429 {
2430 for (int i=0; i<coeffDim; ++i)
2431 {
2432 sop[i][tidx][tidy] = opc[i];
2433 }
2434 }
2435
2436 MFEM_FOREACH_THREAD(qy,y,Q1D)
2437 {
2438 MFEM_FOREACH_THREAD(qx,x,Q1D)
2439 {
2440 real_t u = 0.0;
2441 real_t v = 0.0;
2442
2443 // We treat x, y, z components separately for optimization specific to each.
2444 if (c == 0) // x component
2445 {
2446 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
2447
2448 for (int dz = 0; dz < D1Dz; ++dz)
2449 {
2450 const real_t wz = sBc[dz][qz];
2451 const real_t wDz = sGc[dz][qz];
2452
2453 for (int dy = 0; dy < D1Dy; ++dy)
2454 {
2455 const real_t wy = sBc[dy][qy];
2456 const real_t wDy = sGc[dy][qy];
2457
2458 for (int dx = 0; dx < D1Dx; ++dx)
2459 {
2460 const real_t wx = sX[dz][dy][dx] * sBo[dx][qx];
2461 u += wx * wDy * wz;
2462 v += wx * wy * wDz;
2463 }
2464 }
2465 }
2466
2467 curl[qy][qx][1] += v; // (u_0)_{x_2}
2468 curl[qy][qx][2] -= u; // -(u_0)_{x_1}
2469 }
2470 else if (c == 1) // y component
2471 {
2472 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
2473
2474 for (int dz = 0; dz < D1Dz; ++dz)
2475 {
2476 const real_t wz = sBc[dz][qz];
2477 const real_t wDz = sGc[dz][qz];
2478
2479 for (int dy = 0; dy < D1Dy; ++dy)
2480 {
2481 const real_t wy = sBo[dy][qy];
2482
2483 for (int dx = 0; dx < D1Dx; ++dx)
2484 {
2485 const real_t t = sX[dz][dy][dx];
2486 const real_t wx = t * sBc[dx][qx];
2487 const real_t wDx = t * sGc[dx][qx];
2488
2489 u += wDx * wy * wz;
2490 v += wx * wy * wDz;
2491 }
2492 }
2493 }
2494
2495 curl[qy][qx][0] -= v; // -(u_1)_{x_2}
2496 curl[qy][qx][2] += u; // (u_1)_{x_0}
2497 }
2498 else // z component
2499 {
2500 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
2501
2502 for (int dz = 0; dz < D1Dz; ++dz)
2503 {
2504 const real_t wz = sBo[dz][qz];
2505
2506 for (int dy = 0; dy < D1Dy; ++dy)
2507 {
2508 const real_t wy = sBc[dy][qy];
2509 const real_t wDy = sGc[dy][qy];
2510
2511 for (int dx = 0; dx < D1Dx; ++dx)
2512 {
2513 const real_t t = sX[dz][dy][dx];
2514 const real_t wx = t * sBc[dx][qx];
2515 const real_t wDx = t * sGc[dx][qx];
2516
2517 u += wDx * wy * wz;
2518 v += wx * wDy * wz;
2519 }
2520 }
2521 }
2522
2523 curl[qy][qx][0] += v; // (u_2)_{x_1}
2524 curl[qy][qx][1] -= u; // -(u_2)_{x_0}
2525 }
2526 } // qx
2527 } // qy
2528 } // tidz == qz
2529
2530 osc += D1Dx * D1Dy * D1Dz;
2531 MFEM_SYNC_THREAD;
2532 } // c
2533
2534 real_t dxyz1 = 0.0;
2535 real_t dxyz2 = 0.0;
2536 real_t dxyz3 = 0.0;
2537
2538 MFEM_FOREACH_THREAD(dz,z,D1D)
2539 {
2540 const real_t wcz = sBc[dz][qz];
2541 const real_t wz = (dz < D1D-1) ? sBo[dz][qz] : 0.0;
2542
2543 MFEM_FOREACH_THREAD(dy,y,D1D)
2544 {
2545 MFEM_FOREACH_THREAD(dx,x,D1D)
2546 {
2547 for (int qy = 0; qy < Q1D; ++qy)
2548 {
2549 const real_t wcy = sBc[dy][qy];
2550 const real_t wy = (dy < D1D-1) ? sBo[dy][qy] : 0.0;
2551
2552 for (int qx = 0; qx < Q1D; ++qx)
2553 {
2554 const real_t O11 = sop[0][qx][qy];
2555 real_t c1, c2, c3;
2556 if (coeffDim == 1)
2557 {
2558 c1 = O11 * curl[qy][qx][0];
2559 c2 = O11 * curl[qy][qx][1];
2560 c3 = O11 * curl[qy][qx][2];
2561 }
2562 else
2563 {
2564 const real_t O21 = sop[1][qx][qy];
2565 const real_t O31 = sop[2][qx][qy];
2566 const real_t O12 = sop[3][qx][qy];
2567 const real_t O22 = sop[4][qx][qy];
2568 const real_t O32 = sop[5][qx][qy];
2569 const real_t O13 = sop[6][qx][qy];
2570 const real_t O23 = sop[7][qx][qy];
2571 const real_t O33 = sop[8][qx][qy];
2572 c1 = (O11*curl[qy][qx][0])+(O12*curl[qy][qx][1])+(O13*curl[qy][qx][2]);
2573 c2 = (O21*curl[qy][qx][0])+(O22*curl[qy][qx][1])+(O23*curl[qy][qx][2]);
2574 c3 = (O31*curl[qy][qx][0])+(O32*curl[qy][qx][1])+(O33*curl[qy][qx][2]);
2575 }
2576
2577 const real_t wcx = sBc[dx][qx];
2578
2579 if (dx < D1D-1)
2580 {
2581 const real_t wx = sBo[dx][qx];
2582 dxyz1 += c1 * wx * wcy * wcz;
2583 }
2584
2585 dxyz2 += c2 * wcx * wy * wcz;
2586 dxyz3 += c3 * wcx * wcy * wz;
2587 } // qx
2588 } // qy
2589 } // dx
2590 } // dy
2591 } // dz
2592
2593 MFEM_SYNC_THREAD;
2594
2595 MFEM_FOREACH_THREAD(dz,z,D1D)
2596 {
2597 MFEM_FOREACH_THREAD(dy,y,D1D)
2598 {
2599 MFEM_FOREACH_THREAD(dx,x,D1D)
2600 {
2601 if (dx < D1D-1)
2602 {
2603 Y(dx + ((dy + (dz * D1D)) * (D1D-1)), e) += dxyz1;
2604 }
2605 if (dy < D1D-1)
2606 {
2607 Y(dx + ((dy + (dz * (D1D-1))) * D1D) + ((D1D-1)*D1D*D1D), e) += dxyz2;
2608 }
2609 if (dz < D1D-1)
2610 {
2611 Y(dx + ((dy + (dz * D1D)) * D1D) + (2*(D1D-1)*D1D*D1D), e) += dxyz3;
2612 }
2613 }
2614 }
2615 }
2616 } // qz
2617 }; // end of element loop
2618
2619 auto host_kernel = [&] MFEM_LAMBDA (int)
2620 {
2621 MFEM_ABORT_KERNEL("This kernel should only be used on GPU.");
2622 };
2623
2624 ForallWrap<3>(true, NE, device_kernel, host_kernel, Q1D, Q1D, Q1D);
2625}
2626
2627// PA H(curl)-L2 Apply Transpose 3D kernel
2628template<int T_D1D = 0, int T_Q1D = 0>
2629inline void PAHcurlL2ApplyTranspose3D(const int d1d,
2630 const int q1d,
2631 const int coeffDim,
2632 const int NE,
2633 const Array<real_t> &bo,
2634 const Array<real_t> &bc,
2635 const Array<real_t> &bot,
2636 const Array<real_t> &bct,
2637 const Array<real_t> &gct,
2638 const Vector &pa_data,
2639 const Vector &x,
2640 Vector &y)
2641{
2642 // See PAHcurlL2Apply3D for comments.
2643 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
2644 "Error: d1d > HCURL_MAX_D1D");
2645 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
2646 "Error: q1d > HCURL_MAX_Q1D");
2647 const int D1D = T_D1D ? T_D1D : d1d;
2648 const int Q1D = T_Q1D ? T_Q1D : q1d;
2649
2650 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
2651 auto Bc = Reshape(bc.Read(), Q1D, D1D);
2652 auto Bot = Reshape(bot.Read(), D1D-1, Q1D);
2653 auto Bct = Reshape(bct.Read(), D1D, Q1D);
2654 auto Gct = Reshape(gct.Read(), D1D, Q1D);
2655 auto op = Reshape(pa_data.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
2656 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
2657 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
2658
2659 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
2660 {
2661 constexpr int VDIM = 3;
2662 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
2663 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
2664 const int D1D = T_D1D ? T_D1D : d1d;
2665 const int Q1D = T_Q1D ? T_Q1D : q1d;
2666
2667 real_t mass[MQ1D][MQ1D][MQ1D][VDIM];
2668
2669 for (int qz = 0; qz < Q1D; ++qz)
2670 {
2671 for (int qy = 0; qy < Q1D; ++qy)
2672 {
2673 for (int qx = 0; qx < Q1D; ++qx)
2674 {
2675 for (int c = 0; c < VDIM; ++c)
2676 {
2677 mass[qz][qy][qx][c] = 0.0;
2678 }
2679 }
2680 }
2681 }
2682
2683 int osc = 0;
2684
2685 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
2686 {
2687 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
2688 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
2689 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
2690
2691 for (int dz = 0; dz < D1Dz; ++dz)
2692 {
2693 real_t massXY[MQ1D][MQ1D];
2694 for (int qy = 0; qy < Q1D; ++qy)
2695 {
2696 for (int qx = 0; qx < Q1D; ++qx)
2697 {
2698 massXY[qy][qx] = 0.0;
2699 }
2700 }
2701
2702 for (int dy = 0; dy < D1Dy; ++dy)
2703 {
2704 real_t massX[MQ1D];
2705 for (int qx = 0; qx < Q1D; ++qx)
2706 {
2707 massX[qx] = 0.0;
2708 }
2709
2710 for (int dx = 0; dx < D1Dx; ++dx)
2711 {
2712 const real_t t = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
2713 for (int qx = 0; qx < Q1D; ++qx)
2714 {
2715 massX[qx] += t * ((c == 0) ? Bo(qx,dx) : Bc(qx,dx));
2716 }
2717 }
2718
2719 for (int qy = 0; qy < Q1D; ++qy)
2720 {
2721 const real_t wy = (c == 1) ? Bo(qy,dy) : Bc(qy,dy);
2722 for (int qx = 0; qx < Q1D; ++qx)
2723 {
2724 const real_t wx = massX[qx];
2725 massXY[qy][qx] += wx * wy;
2726 }
2727 }
2728 }
2729
2730 for (int qz = 0; qz < Q1D; ++qz)
2731 {
2732 const real_t wz = (c == 2) ? Bo(qz,dz) : Bc(qz,dz);
2733 for (int qy = 0; qy < Q1D; ++qy)
2734 {
2735 for (int qx = 0; qx < Q1D; ++qx)
2736 {
2737 mass[qz][qy][qx][c] += massXY[qy][qx] * wz;
2738 }
2739 }
2740 }
2741 }
2742
2743 osc += D1Dx * D1Dy * D1Dz;
2744 } // loop (c) over components
2745
2746 // Apply D operator.
2747 for (int qz = 0; qz < Q1D; ++qz)
2748 {
2749 for (int qy = 0; qy < Q1D; ++qy)
2750 {
2751 for (int qx = 0; qx < Q1D; ++qx)
2752 {
2753 const real_t O11 = op(0,qx,qy,qz,e);
2754 if (coeffDim == 1)
2755 {
2756 for (int c = 0; c < VDIM; ++c)
2757 {
2758 mass[qz][qy][qx][c] *= O11;
2759 }
2760 }
2761 else
2762 {
2763 const real_t O12 = op(1,qx,qy,qz,e);
2764 const real_t O13 = op(2,qx,qy,qz,e);
2765 const real_t O21 = op(3,qx,qy,qz,e);
2766 const real_t O22 = op(4,qx,qy,qz,e);
2767 const real_t O23 = op(5,qx,qy,qz,e);
2768 const real_t O31 = op(6,qx,qy,qz,e);
2769 const real_t O32 = op(7,qx,qy,qz,e);
2770 const real_t O33 = op(8,qx,qy,qz,e);
2771 const real_t massX = mass[qz][qy][qx][0];
2772 const real_t massY = mass[qz][qy][qx][1];
2773 const real_t massZ = mass[qz][qy][qx][2];
2774 mass[qz][qy][qx][0] = (O11*massX)+(O12*massY)+(O13*massZ);
2775 mass[qz][qy][qx][1] = (O21*massX)+(O22*massY)+(O23*massZ);
2776 mass[qz][qy][qx][2] = (O31*massX)+(O32*massY)+(O33*massZ);
2777 }
2778 }
2779 }
2780 }
2781
2782 // x component
2783 osc = 0;
2784 {
2785 const int D1Dz = D1D;
2786 const int D1Dy = D1D;
2787 const int D1Dx = D1D - 1;
2788
2789 for (int qz = 0; qz < Q1D; ++qz)
2790 {
2791 real_t gradXY12[MD1D][MD1D];
2792 real_t gradXY21[MD1D][MD1D];
2793
2794 for (int dy = 0; dy < D1Dy; ++dy)
2795 {
2796 for (int dx = 0; dx < D1Dx; ++dx)
2797 {
2798 gradXY12[dy][dx] = 0.0;
2799 gradXY21[dy][dx] = 0.0;
2800 }
2801 }
2802 for (int qy = 0; qy < Q1D; ++qy)
2803 {
2804 real_t massX[MD1D][2];
2805 for (int dx = 0; dx < D1Dx; ++dx)
2806 {
2807 for (int n = 0; n < 2; ++n)
2808 {
2809 massX[dx][n] = 0.0;
2810 }
2811 }
2812 for (int qx = 0; qx < Q1D; ++qx)
2813 {
2814 for (int dx = 0; dx < D1Dx; ++dx)
2815 {
2816 const real_t wx = Bot(dx,qx);
2817
2818 massX[dx][0] += wx * mass[qz][qy][qx][1];
2819 massX[dx][1] += wx * mass[qz][qy][qx][2];
2820 }
2821 }
2822 for (int dy = 0; dy < D1Dy; ++dy)
2823 {
2824 const real_t wy = Bct(dy,qy);
2825 const real_t wDy = Gct(dy,qy);
2826
2827 for (int dx = 0; dx < D1Dx; ++dx)
2828 {
2829 gradXY21[dy][dx] += massX[dx][0] * wy;
2830 gradXY12[dy][dx] += massX[dx][1] * wDy;
2831 }
2832 }
2833 }
2834
2835 for (int dz = 0; dz < D1Dz; ++dz)
2836 {
2837 const real_t wz = Bct(dz,qz);
2838 const real_t wDz = Gct(dz,qz);
2839 for (int dy = 0; dy < D1Dy; ++dy)
2840 {
2841 for (int dx = 0; dx < D1Dx; ++dx)
2842 {
2843 // \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
2844 // (u_0)_{x_2} * (op * curl)_1 - (u_0)_{x_1} * (op * curl)_2
2845 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
2846 e) += (gradXY21[dy][dx] * wDz) - (gradXY12[dy][dx] * wz);
2847 }
2848 }
2849 }
2850 } // loop qz
2851
2852 osc += D1Dx * D1Dy * D1Dz;
2853 }
2854
2855 // y component
2856 {
2857 const int D1Dz = D1D;
2858 const int D1Dy = D1D - 1;
2859 const int D1Dx = D1D;
2860
2861 for (int qz = 0; qz < Q1D; ++qz)
2862 {
2863 real_t gradXY02[MD1D][MD1D];
2864 real_t gradXY20[MD1D][MD1D];
2865
2866 for (int dy = 0; dy < D1Dy; ++dy)
2867 {
2868 for (int dx = 0; dx < D1Dx; ++dx)
2869 {
2870 gradXY02[dy][dx] = 0.0;
2871 gradXY20[dy][dx] = 0.0;
2872 }
2873 }
2874 for (int qx = 0; qx < Q1D; ++qx)
2875 {
2876 real_t massY[MD1D][2];
2877 for (int dy = 0; dy < D1Dy; ++dy)
2878 {
2879 massY[dy][0] = 0.0;
2880 massY[dy][1] = 0.0;
2881 }
2882 for (int qy = 0; qy < Q1D; ++qy)
2883 {
2884 for (int dy = 0; dy < D1Dy; ++dy)
2885 {
2886 const real_t wy = Bot(dy,qy);
2887
2888 massY[dy][0] += wy * mass[qz][qy][qx][2];
2889 massY[dy][1] += wy * mass[qz][qy][qx][0];
2890 }
2891 }
2892 for (int dx = 0; dx < D1Dx; ++dx)
2893 {
2894 const real_t wx = Bct(dx,qx);
2895 const real_t wDx = Gct(dx,qx);
2896
2897 for (int dy = 0; dy < D1Dy; ++dy)
2898 {
2899 gradXY02[dy][dx] += massY[dy][0] * wDx;
2900 gradXY20[dy][dx] += massY[dy][1] * wx;
2901 }
2902 }
2903 }
2904
2905 for (int dz = 0; dz < D1Dz; ++dz)
2906 {
2907 const real_t wz = Bct(dz,qz);
2908 const real_t wDz = Gct(dz,qz);
2909 for (int dy = 0; dy < D1Dy; ++dy)
2910 {
2911 for (int dx = 0; dx < D1Dx; ++dx)
2912 {
2913 // \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
2914 // -(u_1)_{x_2} * (op * curl)_0 + (u_1)_{x_0} * (op * curl)_2
2915 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
2916 e) += (-gradXY20[dy][dx] * wDz) + (gradXY02[dy][dx] * wz);
2917 }
2918 }
2919 }
2920 } // loop qz
2921
2922 osc += D1Dx * D1Dy * D1Dz;
2923 }
2924
2925 // z component
2926 {
2927 const int D1Dz = D1D - 1;
2928 const int D1Dy = D1D;
2929 const int D1Dx = D1D;
2930
2931 for (int qx = 0; qx < Q1D; ++qx)
2932 {
2933 real_t gradYZ01[MD1D][MD1D];
2934 real_t gradYZ10[MD1D][MD1D];
2935
2936 for (int dy = 0; dy < D1Dy; ++dy)
2937 {
2938 for (int dz = 0; dz < D1Dz; ++dz)
2939 {
2940 gradYZ01[dz][dy] = 0.0;
2941 gradYZ10[dz][dy] = 0.0;
2942 }
2943 }
2944 for (int qy = 0; qy < Q1D; ++qy)
2945 {
2946 real_t massZ[MD1D][2];
2947 for (int dz = 0; dz < D1Dz; ++dz)
2948 {
2949 for (int n = 0; n < 2; ++n)
2950 {
2951 massZ[dz][n] = 0.0;
2952 }
2953 }
2954 for (int qz = 0; qz < Q1D; ++qz)
2955 {
2956 for (int dz = 0; dz < D1Dz; ++dz)
2957 {
2958 const real_t wz = Bot(dz,qz);
2959
2960 massZ[dz][0] += wz * mass[qz][qy][qx][0];
2961 massZ[dz][1] += wz * mass[qz][qy][qx][1];
2962 }
2963 }
2964 for (int dy = 0; dy < D1Dy; ++dy)
2965 {
2966 const real_t wy = Bct(dy,qy);
2967 const real_t wDy = Gct(dy,qy);
2968
2969 for (int dz = 0; dz < D1Dz; ++dz)
2970 {
2971 gradYZ01[dz][dy] += wy * massZ[dz][1];
2972 gradYZ10[dz][dy] += wDy * massZ[dz][0];
2973 }
2974 }
2975 }
2976
2977 for (int dx = 0; dx < D1Dx; ++dx)
2978 {
2979 const real_t wx = Bct(dx,qx);
2980 const real_t wDx = Gct(dx,qx);
2981
2982 for (int dy = 0; dy < D1Dy; ++dy)
2983 {
2984 for (int dz = 0; dz < D1Dz; ++dz)
2985 {
2986 // \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
2987 // (u_2)_{x_1} * (op * curl)_0 - (u_2)_{x_0} * (op * curl)_1
2988 Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
2989 e) += (gradYZ10[dz][dy] * wx) - (gradYZ01[dz][dy] * wDx);
2990 }
2991 }
2992 }
2993 } // loop qx
2994 }
2995 });
2996}
2997
2998// PA H(curl)-L2 Apply Transpose 3D kernel
2999template<int T_D1D = 0, int T_Q1D = 0>
3000inline void SmemPAHcurlL2ApplyTranspose3D(const int d1d,
3001 const int q1d,
3002 const int coeffDim,
3003 const int NE,
3004 const Array<real_t> &bo,
3005 const Array<real_t> &bc,
3006 const Array<real_t> &gc,
3007 const Vector &pa_data,
3008 const Vector &x,
3009 Vector &y)
3010{
3011 MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
3012 "Error: d1d > HCURL_MAX_D1D");
3013 MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
3014 "Error: q1d > HCURL_MAX_Q1D");
3015 const int D1D = T_D1D ? T_D1D : d1d;
3016 const int Q1D = T_Q1D ? T_Q1D : q1d;
3017
3018 auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
3019 auto Bc = Reshape(bc.Read(), Q1D, D1D);
3020 auto Gc = Reshape(gc.Read(), Q1D, D1D);
3021 auto op = Reshape(pa_data.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
3022 auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
3023 auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
3024
3025 auto device_kernel = [=] MFEM_DEVICE (int e)
3026 {
3027 constexpr int VDIM = 3;
3028 constexpr int maxCoeffDim = 9;
3029 constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
3030 constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
3031 const int D1D = T_D1D ? T_D1D : d1d;
3032 const int Q1D = T_Q1D ? T_Q1D : q1d;
3033
3034 MFEM_SHARED real_t sBo[MD1D][MQ1D];
3035 MFEM_SHARED real_t sBc[MD1D][MQ1D];
3036 MFEM_SHARED real_t sGc[MD1D][MQ1D];
3037
3038 real_t opc[maxCoeffDim];
3039 MFEM_SHARED real_t sop[maxCoeffDim][MQ1D][MQ1D];
3040 MFEM_SHARED real_t mass[MQ1D][MQ1D][3];
3041
3042 MFEM_SHARED real_t sX[MD1D][MD1D][MD1D];
3043
3044 MFEM_FOREACH_THREAD(qx,x,Q1D)
3045 {
3046 MFEM_FOREACH_THREAD(qy,y,Q1D)
3047 {
3048 MFEM_FOREACH_THREAD(qz,z,Q1D)
3049 {
3050 for (int i=0; i<coeffDim; ++i)
3051 {
3052 opc[i] = op(i,qx,qy,qz,e);
3053 }
3054 }
3055 }
3056 }
3057
3058 const int tidx = MFEM_THREAD_ID(x);
3059 const int tidy = MFEM_THREAD_ID(y);
3060 const int tidz = MFEM_THREAD_ID(z);
3061
3062 if (tidz == 0)
3063 {
3064 MFEM_FOREACH_THREAD(d,y,D1D)
3065 {
3066 MFEM_FOREACH_THREAD(q,x,Q1D)
3067 {
3068 sBc[d][q] = Bc(q,d);
3069 sGc[d][q] = Gc(q,d);
3070 if (d < D1D-1)
3071 {
3072 sBo[d][q] = Bo(q,d);
3073 }
3074 }
3075 }
3076 }
3077 MFEM_SYNC_THREAD;
3078
3079 for (int qz=0; qz < Q1D; ++qz)
3080 {
3081 if (tidz == qz)
3082 {
3083 MFEM_FOREACH_THREAD(qy,y,Q1D)
3084 {
3085 MFEM_FOREACH_THREAD(qx,x,Q1D)
3086 {
3087 for (int i=0; i<3; ++i)
3088 {
3089 mass[qy][qx][i] = 0.0;
3090 }
3091 }
3092 }
3093 }
3094
3095 int osc = 0;
3096 for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
3097 {
3098 const int D1Dz = (c == 2) ? D1D - 1 : D1D;
3099 const int D1Dy = (c == 1) ? D1D - 1 : D1D;
3100 const int D1Dx = (c == 0) ? D1D - 1 : D1D;
3101
3102 MFEM_FOREACH_THREAD(dz,z,D1Dz)
3103 {
3104 MFEM_FOREACH_THREAD(dy,y,D1Dy)
3105 {
3106 MFEM_FOREACH_THREAD(dx,x,D1Dx)
3107 {
3108 sX[dz][dy][dx] = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
3109 }
3110 }
3111 }
3112 MFEM_SYNC_THREAD;
3113
3114 if (tidz == qz)
3115 {
3116 if (c == 0)
3117 {
3118 for (int i=0; i<coeffDim; ++i)
3119 {
3120 sop[i][tidx][tidy] = opc[i];
3121 }
3122 }
3123
3124 MFEM_FOREACH_THREAD(qy,y,Q1D)
3125 {
3126 MFEM_FOREACH_THREAD(qx,x,Q1D)
3127 {
3128 real_t u = 0.0;
3129
3130 for (int dz = 0; dz < D1Dz; ++dz)
3131 {
3132 const real_t wz = (c == 2) ? sBo[dz][qz] : sBc[dz][qz];
3133
3134 for (int dy = 0; dy < D1Dy; ++dy)
3135 {
3136 const real_t wy = (c == 1) ? sBo[dy][qy] : sBc[dy][qy];
3137
3138 for (int dx = 0; dx < D1Dx; ++dx)
3139 {
3140 const real_t wx = sX[dz][dy][dx] * ((c == 0) ? sBo[dx][qx] : sBc[dx][qx]);
3141 u += wx * wy * wz;
3142 }
3143 }
3144 }
3145
3146 mass[qy][qx][c] += u;
3147 } // qx
3148 } // qy
3149 } // tidz == qz
3150
3151 osc += D1Dx * D1Dy * D1Dz;
3152 MFEM_SYNC_THREAD;
3153 } // c
3154
3155 real_t dxyz1 = 0.0;
3156 real_t dxyz2 = 0.0;
3157 real_t dxyz3 = 0.0;
3158
3159 MFEM_FOREACH_THREAD(dz,z,D1D)
3160 {
3161 const real_t wcz = sBc[dz][qz];
3162 const real_t wcDz = sGc[dz][qz];
3163 const real_t wz = (dz < D1D-1) ? sBo[dz][qz] : 0.0;
3164
3165 MFEM_FOREACH_THREAD(dy,y,D1D)
3166 {
3167 MFEM_FOREACH_THREAD(dx,x,D1D)
3168 {
3169 for (int qy = 0; qy < Q1D; ++qy)
3170 {
3171 const real_t wcy = sBc[dy][qy];
3172 const real_t wcDy = sGc[dy][qy];
3173 const real_t wy = (dy < D1D-1) ? sBo[dy][qy] : 0.0;
3174
3175 for (int qx = 0; qx < Q1D; ++qx)
3176 {
3177 const real_t O11 = sop[0][qx][qy];
3178 real_t c1, c2, c3;
3179 if (coeffDim == 1)
3180 {
3181 c1 = O11 * mass[qy][qx][0];
3182 c2 = O11 * mass[qy][qx][1];
3183 c3 = O11 * mass[qy][qx][2];
3184 }
3185 else
3186 {
3187 const real_t O12 = sop[1][qx][qy];
3188 const real_t O13 = sop[2][qx][qy];
3189 const real_t O21 = sop[3][qx][qy];
3190 const real_t O22 = sop[4][qx][qy];
3191 const real_t O23 = sop[5][qx][qy];
3192 const real_t O31 = sop[6][qx][qy];
3193 const real_t O32 = sop[7][qx][qy];
3194 const real_t O33 = sop[8][qx][qy];
3195
3196 c1 = (O11*mass[qy][qx][0])+(O12*mass[qy][qx][1])+(O13*mass[qy][qx][2]);
3197 c2 = (O21*mass[qy][qx][0])+(O22*mass[qy][qx][1])+(O23*mass[qy][qx][2]);
3198 c3 = (O31*mass[qy][qx][0])+(O32*mass[qy][qx][1])+(O33*mass[qy][qx][2]);
3199 }
3200
3201 const real_t wcx = sBc[dx][qx];
3202 const real_t wDx = sGc[dx][qx];
3203
3204 if (dx < D1D-1)
3205 {
3206 const real_t wx = sBo[dx][qx];
3207 dxyz1 += (wx * c2 * wcy * wcDz) - (wx * c3 * wcDy * wcz);
3208 }
3209
3210 dxyz2 += (-wy * c1 * wcx * wcDz) + (wy * c3 * wDx * wcz);
3211
3212 dxyz3 += (wcDy * wz * c1 * wcx) - (wcy * wz * c2 * wDx);
3213 } // qx
3214 } // qy
3215 } // dx
3216 } // dy
3217 } // dz
3218
3219 MFEM_SYNC_THREAD;
3220
3221 MFEM_FOREACH_THREAD(dz,z,D1D)
3222 {
3223 MFEM_FOREACH_THREAD(dy,y,D1D)
3224 {
3225 MFEM_FOREACH_THREAD(dx,x,D1D)
3226 {
3227 if (dx < D1D-1)
3228 {
3229 Y(dx + ((dy + (dz * D1D)) * (D1D-1)), e) += dxyz1;
3230 }
3231 if (dy < D1D-1)
3232 {
3233 Y(dx + ((dy + (dz * (D1D-1))) * D1D) + ((D1D-1)*D1D*D1D), e) += dxyz2;
3234 }
3235 if (dz < D1D-1)
3236 {
3237 Y(dx + ((dy + (dz * D1D)) * D1D) + (2*(D1D-1)*D1D*D1D), e) += dxyz3;
3238 }
3239 }
3240 }
3241 }
3242 } // qz
3243 }; // end of element loop
3244
3245 auto host_kernel = [&] MFEM_LAMBDA (int)
3246 {
3247 MFEM_ABORT_KERNEL("This kernel should only be used on GPU.");
3248 };
3249
3250 ForallWrap<3>(true, NE, device_kernel, host_kernel, Q1D, Q1D, Q1D);
3251}
3252
3253} // namespace internal
3254
3255template<int DIM, int T_D1D, int T_Q1D>
3256CurlCurlIntegrator::ApplyKernelType CurlCurlIntegrator::ApplyPAKernels::Kernel()
3257{
3258 if constexpr (DIM == 2)
3259 {
3260 return internal::PACurlCurlApply2D;
3261 }
3262 else if constexpr (DIM == 3)
3263 {
3265 {
3266 return internal::SmemPACurlCurlApply3D<T_D1D, T_Q1D>;
3267 }
3268 else
3269 {
3270 return internal::PACurlCurlApply3D;
3271 }
3272 }
3273 MFEM_ABORT("");
3274}
3275
3276template <int DIM, int T_D1D, int T_Q1D>
3278CurlCurlIntegrator::DiagonalPAKernels::Kernel()
3279{
3280 if constexpr (DIM == 2)
3281 {
3282 return internal::PACurlCurlAssembleDiagonal2D;
3283 }
3284 else if constexpr (DIM == 3)
3285 {
3287 {
3288 return internal::SmemPACurlCurlAssembleDiagonal3D<T_D1D, T_Q1D>;
3289 }
3290 else
3291 {
3292 return internal::PACurlCurlAssembleDiagonal3D;
3293 }
3294 }
3295 MFEM_ABORT("");
3296}
3297/// \endcond DO_NOT_DOCUMENT
3298} // namespace mfem
3299
3300#endif
void(*)(const int, const int, const bool, const int, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Array< real_t > &, const Vector &, Vector &) DiagonalKernelType
arguments: d1d, q1d, symmetric, ne, Bo, Bc, Go, Gc, pa_data, diag
void(*)( const int, const int, const bool, const 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 bool) ApplyKernelType
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition device.hpp:271
int dim
Definition ex24.cpp:53
real_t b
Definition lissajous.cpp:42
constexpr int DIM
mfem::real_t real_t
void ForallWrap(const bool use_dev, const int N, d_lambda &&d_body, h_lambda &&h_body, const int X=0, const int Y=0, const int Z=0, const int G=0)
Forall host & device kernel dispatch.
Definition forall.hpp:1042
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
void forall_3D(int N, int X, int Y, int Z, lambda &&body)
Definition forall.hpp:1244
float real_t
Definition config.hpp:46
void forall(int N, lambda &&body)
Definition forall.hpp:1134
@ DEVICE_MASK
Biwise-OR of all device backends.
Definition device.hpp:104
static const DeviceDofQuadLimits & Get()
Return a const reference to the DeviceDofQuadLimits singleton.
Definition forall.hpp:138