MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_interp_pa.cpp
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
13#include "../bilininteg.hpp"
14#include "../gridfunc.hpp"
15#include "../qfunction.hpp"
16
18
19namespace mfem
20{
21
22namespace
23{
24
25void PAHcurlApplyCurl2D(const int c_dofs1D,
26 const int o_dofs1D,
27 const int NE,
28 const Array<real_t> &Bo_,
29 const Array<real_t> &Gc_,
30 const Vector &x_,
31 Vector &y_)
32{
33 auto Bo = Reshape(Bo_.Read(), o_dofs1D, o_dofs1D);
34 auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
35 auto X = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
36 auto Y = Reshape(y_.ReadWrite(), o_dofs1D, o_dofs1D, NE);
37
38 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
39 {
40 for (int iy = 0; iy < c_dofs1D; ++iy)
41 {
42 for (int ix = 0; ix < o_dofs1D; ++ix)
43 {
44 const real_t xv = X(ix + iy * o_dofs1D, e);
45 for (int oy = 0; oy < o_dofs1D; ++oy)
46 {
47 const real_t gy = Gc(oy, iy);
48 for (int ox = 0; ox < o_dofs1D; ++ox)
49 {
50 Y(ox, oy, e) -= Bo(ox, ix) * gy * xv;
51 }
52 }
53 }
54 }
55
56 const int y_nd = c_dofs1D * o_dofs1D;
57 for (int iy = 0; iy < o_dofs1D; ++iy)
58 {
59 for (int ix = 0; ix < c_dofs1D; ++ix)
60 {
61 const real_t xv = X(y_nd + ix + iy * c_dofs1D, e);
62 for (int oy = 0; oy < o_dofs1D; ++oy)
63 {
64 const real_t by = Bo(oy, iy);
65 for (int ox = 0; ox < o_dofs1D; ++ox)
66 {
67 Y(ox, oy, e) += Gc(ox, ix) * by * xv;
68 }
69 }
70 }
71 }
72 });
73}
74
75void PAHcurlApplyCurlTranspose2D(const int c_dofs1D,
76 const int o_dofs1D,
77 const int NE,
78 const Array<real_t> &Bo_,
79 const Array<real_t> &Gc_,
80 const Vector &x_,
81 Vector &y_)
82{
83 auto Bo = Reshape(Bo_.Read(), o_dofs1D, o_dofs1D);
84 auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
85 auto X = Reshape(x_.Read(), o_dofs1D, o_dofs1D, NE);
86 auto Y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
87
88 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
89 {
90 for (int dy = 0; dy < c_dofs1D; ++dy)
91 {
92 for (int dx = 0; dx < o_dofs1D; ++dx)
93 {
94 real_t sum = 0.0;
95 for (int oy = 0; oy < o_dofs1D; ++oy)
96 {
97 const real_t gy = Gc(oy, dy);
98 for (int ox = 0; ox < o_dofs1D; ++ox)
99 {
100 sum -= Bo(ox, dx) * gy * X(ox, oy, e);
101 }
102 }
103 Y(dx + dy * o_dofs1D, e) += sum;
104 }
105 }
106
107 const int y_nd = c_dofs1D * o_dofs1D;
108 for (int dy = 0; dy < o_dofs1D; ++dy)
109 {
110 for (int dx = 0; dx < c_dofs1D; ++dx)
111 {
112 real_t sum = 0.0;
113 for (int oy = 0; oy < o_dofs1D; ++oy)
114 {
115 const real_t by = Bo(oy, dy);
116 for (int ox = 0; ox < o_dofs1D; ++ox)
117 {
118 sum += Gc(ox, dx) * by * X(ox, oy, e);
119 }
120 }
121 Y(y_nd + dx + dy * c_dofs1D, e) += sum;
122 }
123 }
124 });
125}
126
127void PAHdivApplyCurl2D(const int c_dofs1D,
128 const int o_dofs1D,
129 const int NE,
130 const Array<real_t> &Bc_,
131 const Array<real_t> &Gc_,
132 const Vector &x_,
133 Vector &y_)
134{
135 auto Bc = Reshape(Bc_.Read(), c_dofs1D, c_dofs1D);
136 auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
137 auto X = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
138 auto Y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
139
140 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
141 {
142 for (int iy = 0; iy < c_dofs1D; ++iy)
143 {
144 for (int ix = 0; ix < c_dofs1D; ++ix)
145 {
146 const real_t xv = X(ix, iy, e);
147 for (int oy = 0; oy < o_dofs1D; ++oy)
148 {
149 const real_t gy = Gc(oy, iy);
150 for (int ox = 0; ox < c_dofs1D; ++ox)
151 {
152 Y(ox + oy * c_dofs1D, e) += Bc(ox, ix) * gy * xv;
153 }
154 }
155 }
156 }
157
158 const int y_nd = c_dofs1D * o_dofs1D;
159 for (int iy = 0; iy < c_dofs1D; ++iy)
160 {
161 for (int ix = 0; ix < c_dofs1D; ++ix)
162 {
163 const real_t xv = X(ix, iy, e);
164 for (int oy = 0; oy < c_dofs1D; ++oy)
165 {
166 const real_t by = Bc(oy, iy);
167 for (int ox = 0; ox < o_dofs1D; ++ox)
168 {
169 Y(y_nd + ox + oy * o_dofs1D, e) -= Gc(ox, ix) * by * xv;
170 }
171 }
172 }
173 }
174 });
175}
176
177void PAHdivApplyCurlTranspose2D(const int c_dofs1D,
178 const int o_dofs1D,
179 const int NE,
180 const Array<real_t> &Bc_,
181 const Array<real_t> &Gc_,
182 const Vector &x_,
183 Vector &y_)
184{
185 auto Bc = Reshape(Bc_.Read(), c_dofs1D, c_dofs1D);
186 auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
187 auto X = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
188 auto Y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
189
190 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
191 {
192 for (int dy = 0; dy < o_dofs1D; ++dy)
193 {
194 for (int dx = 0; dx < c_dofs1D; ++dx)
195 {
196 const real_t xv = X(dx + dy * c_dofs1D, e);
197 for (int iy = 0; iy < c_dofs1D; ++iy)
198 {
199 const real_t gy = Gc(dy, iy);
200 for (int ix = 0; ix < c_dofs1D; ++ix)
201 {
202 Y(ix, iy, e) += Bc(dx, ix) * gy * xv;
203 }
204 }
205 }
206 }
207
208 const int y_nd = c_dofs1D * o_dofs1D;
209 for (int dy = 0; dy < c_dofs1D; ++dy)
210 {
211 for (int dx = 0; dx < o_dofs1D; ++dx)
212 {
213 const real_t xv = X(y_nd + dx + dy * o_dofs1D, e);
214 for (int iy = 0; iy < c_dofs1D; ++iy)
215 {
216 const real_t by = Bc(dy, iy);
217 for (int ix = 0; ix < c_dofs1D; ++ix)
218 {
219 Y(ix, iy, e) -= Gc(dx, ix) * by * xv;
220 }
221 }
222 }
223 }
224 });
225}
226
227}
228
229// Apply to x corresponding to DOFs in H^1 (domain) the (topological) gradient
230// to get a dof in H(curl) (range). You can think of the range as the "test" space
231// and the domain as the "trial" space, but there's no integration.
232static void PAHcurlApplyGradient2D(const int c_dofs1D,
233 const int o_dofs1D,
234 const int NE,
235 const Array<real_t> &B_,
236 const Array<real_t> &G_,
237 const Vector &x_,
238 Vector &y_)
239{
240 auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
241 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
242
243 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
244 auto y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
245
246 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
247 o_dofs1D <= c_dofs1D, "");
248
249 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
250 {
251 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
252 real_t w[MAX_D1D][MAX_D1D];
253
254 // horizontal part
255 for (int dx = 0; dx < c_dofs1D; ++dx)
256 {
257 for (int ey = 0; ey < c_dofs1D; ++ey)
258 {
259 w[dx][ey] = 0.0;
260 for (int dy = 0; dy < c_dofs1D; ++dy)
261 {
262 w[dx][ey] += B(ey, dy) * x(dx, dy, e);
263 }
264 }
265 }
266
267 for (int ey = 0; ey < c_dofs1D; ++ey)
268 {
269 for (int ex = 0; ex < o_dofs1D; ++ex)
270 {
271 real_t s = 0.0;
272 for (int dx = 0; dx < c_dofs1D; ++dx)
273 {
274 s += G(ex, dx) * w[dx][ey];
275 }
276 const int local_index = ey*o_dofs1D + ex;
277 y(local_index, e) += s;
278 }
279 }
280
281 // vertical part
282 for (int dx = 0; dx < c_dofs1D; ++dx)
283 {
284 for (int ey = 0; ey < o_dofs1D; ++ey)
285 {
286 w[dx][ey] = 0.0;
287 for (int dy = 0; dy < c_dofs1D; ++dy)
288 {
289 w[dx][ey] += G(ey, dy) * x(dx, dy, e);
290 }
291 }
292 }
293
294 for (int ey = 0; ey < o_dofs1D; ++ey)
295 {
296 for (int ex = 0; ex < c_dofs1D; ++ex)
297 {
298 real_t s = 0.0;
299 for (int dx = 0; dx < c_dofs1D; ++dx)
300 {
301 s += B(ex, dx) * w[dx][ey];
302 }
303 const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
304 y(local_index, e) += s;
305 }
306 }
307 });
308}
309
310// Specialization of PAHcurlApplyGradient2D to the case where B is identity
311static void PAHcurlApplyGradient2DBId(const int c_dofs1D,
312 const int o_dofs1D,
313 const int NE,
314 const Array<real_t> &G_,
315 const Vector &x_,
316 Vector &y_)
317{
318 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
319
320 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
321 auto y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
322
323 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
324 o_dofs1D <= c_dofs1D, "");
325
326 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
327 {
328 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
329 real_t w[MAX_D1D][MAX_D1D];
330
331 // horizontal part
332 for (int dx = 0; dx < c_dofs1D; ++dx)
333 {
334 for (int ey = 0; ey < c_dofs1D; ++ey)
335 {
336 const int dy = ey;
337 w[dx][ey] = x(dx, dy, e);
338 }
339 }
340
341 for (int ey = 0; ey < c_dofs1D; ++ey)
342 {
343 for (int ex = 0; ex < o_dofs1D; ++ex)
344 {
345 real_t s = 0.0;
346 for (int dx = 0; dx < c_dofs1D; ++dx)
347 {
348 s += G(ex, dx) * w[dx][ey];
349 }
350 const int local_index = ey*o_dofs1D + ex;
351 y(local_index, e) += s;
352 }
353 }
354
355 // vertical part
356 for (int dx = 0; dx < c_dofs1D; ++dx)
357 {
358 for (int ey = 0; ey < o_dofs1D; ++ey)
359 {
360 w[dx][ey] = 0.0;
361 for (int dy = 0; dy < c_dofs1D; ++dy)
362 {
363 w[dx][ey] += G(ey, dy) * x(dx, dy, e);
364 }
365 }
366 }
367
368 for (int ey = 0; ey < o_dofs1D; ++ey)
369 {
370 for (int ex = 0; ex < c_dofs1D; ++ex)
371 {
372 const int dx = ex;
373 const real_t s = w[dx][ey];
374 const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
375 y(local_index, e) += s;
376 }
377 }
378 });
379}
380
381static void PAHcurlApplyGradientTranspose2D(
382 const int c_dofs1D, const int o_dofs1D, const int NE,
383 const Array<real_t> &B_, const Array<real_t> &G_,
384 const Vector &x_, Vector &y_)
385{
386 auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
387 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
388
389 auto x = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
390 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
391
392 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
393 o_dofs1D <= c_dofs1D, "");
394
395 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
396 {
397 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
398 real_t w[MAX_D1D][MAX_D1D];
399
400 // horizontal part (open x, closed y)
401 for (int dy = 0; dy < c_dofs1D; ++dy)
402 {
403 for (int ex = 0; ex < o_dofs1D; ++ex)
404 {
405 w[dy][ex] = 0.0;
406 for (int ey = 0; ey < c_dofs1D; ++ey)
407 {
408 const int local_index = ey*o_dofs1D + ex;
409 w[dy][ex] += B(ey, dy) * x(local_index, e);
410 }
411 }
412 }
413
414 for (int dy = 0; dy < c_dofs1D; ++dy)
415 {
416 for (int dx = 0; dx < c_dofs1D; ++dx)
417 {
418 real_t s = 0.0;
419 for (int ex = 0; ex < o_dofs1D; ++ex)
420 {
421 s += G(ex, dx) * w[dy][ex];
422 }
423 y(dx, dy, e) += s;
424 }
425 }
426
427 // vertical part (open y, closed x)
428 for (int dy = 0; dy < c_dofs1D; ++dy)
429 {
430 for (int ex = 0; ex < c_dofs1D; ++ex)
431 {
432 w[dy][ex] = 0.0;
433 for (int ey = 0; ey < o_dofs1D; ++ey)
434 {
435 const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
436 w[dy][ex] += G(ey, dy) * x(local_index, e);
437 }
438 }
439 }
440
441 for (int dy = 0; dy < c_dofs1D; ++dy)
442 {
443 for (int dx = 0; dx < c_dofs1D; ++dx)
444 {
445 real_t s = 0.0;
446 for (int ex = 0; ex < c_dofs1D; ++ex)
447 {
448 s += B(ex, dx) * w[dy][ex];
449 }
450 y(dx, dy, e) += s;
451 }
452 }
453 });
454}
455
456// Specialization of PAHcurlApplyGradientTranspose2D to the case where
457// B is identity
458static void PAHcurlApplyGradientTranspose2DBId(
459 const int c_dofs1D, const int o_dofs1D, const int NE,
460 const Array<real_t> &G_,
461 const Vector &x_, Vector &y_)
462{
463 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
464
465 auto x = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
466 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
467
468 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
469 o_dofs1D <= c_dofs1D, "");
470
471 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
472 {
473 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
474 real_t w[MAX_D1D][MAX_D1D];
475
476 // horizontal part (open x, closed y)
477 for (int dy = 0; dy < c_dofs1D; ++dy)
478 {
479 for (int ex = 0; ex < o_dofs1D; ++ex)
480 {
481 const int ey = dy;
482 const int local_index = ey*o_dofs1D + ex;
483 w[dy][ex] = x(local_index, e);
484 }
485 }
486
487 for (int dy = 0; dy < c_dofs1D; ++dy)
488 {
489 for (int dx = 0; dx < c_dofs1D; ++dx)
490 {
491 real_t s = 0.0;
492 for (int ex = 0; ex < o_dofs1D; ++ex)
493 {
494 s += G(ex, dx) * w[dy][ex];
495 }
496 y(dx, dy, e) += s;
497 }
498 }
499
500 // vertical part (open y, closed x)
501 for (int dy = 0; dy < c_dofs1D; ++dy)
502 {
503 for (int ex = 0; ex < c_dofs1D; ++ex)
504 {
505 w[dy][ex] = 0.0;
506 for (int ey = 0; ey < o_dofs1D; ++ey)
507 {
508 const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
509 w[dy][ex] += G(ey, dy) * x(local_index, e);
510 }
511 }
512 }
513
514 for (int dy = 0; dy < c_dofs1D; ++dy)
515 {
516 for (int dx = 0; dx < c_dofs1D; ++dx)
517 {
518 const int ex = dx;
519 const real_t s = w[dy][ex];
520 y(dx, dy, e) += s;
521 }
522 }
523 });
524}
525
526static void PAHcurlApplyGradient3D(const int c_dofs1D,
527 const int o_dofs1D,
528 const int NE,
529 const Array<real_t> &B_,
530 const Array<real_t> &G_,
531 const Vector &x_,
532 Vector &y_)
533{
534 auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
535 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
536
537 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
538 auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
539
540 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
541 o_dofs1D <= c_dofs1D, "");
542
543 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
544 {
545 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
546 real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
547 real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
548
549 // ---
550 // dofs that point parallel to x-axis (open in x, closed in y, z)
551 // ---
552
553 // contract in z
554 for (int ez = 0; ez < c_dofs1D; ++ez)
555 {
556 for (int dx = 0; dx < c_dofs1D; ++dx)
557 {
558 for (int dy = 0; dy < c_dofs1D; ++dy)
559 {
560 w1[dx][dy][ez] = 0.0;
561 for (int dz = 0; dz < c_dofs1D; ++dz)
562 {
563 w1[dx][dy][ez] += B(ez, dz) * x(dx, dy, dz, e);
564 }
565 }
566 }
567 }
568
569 // contract in y
570 for (int ez = 0; ez < c_dofs1D; ++ez)
571 {
572 for (int ey = 0; ey < c_dofs1D; ++ey)
573 {
574 for (int dx = 0; dx < c_dofs1D; ++dx)
575 {
576 w2[dx][ey][ez] = 0.0;
577 for (int dy = 0; dy < c_dofs1D; ++dy)
578 {
579 w2[dx][ey][ez] += B(ey, dy) * w1[dx][dy][ez];
580 }
581 }
582 }
583 }
584
585 // contract in x
586 for (int ez = 0; ez < c_dofs1D; ++ez)
587 {
588 for (int ey = 0; ey < c_dofs1D; ++ey)
589 {
590 for (int ex = 0; ex < o_dofs1D; ++ex)
591 {
592 real_t s = 0.0;
593 for (int dx = 0; dx < c_dofs1D; ++dx)
594 {
595 s += G(ex, dx) * w2[dx][ey][ez];
596 }
597 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
598 y(local_index, e) += s;
599 }
600 }
601 }
602
603 // ---
604 // dofs that point parallel to y-axis (open in y, closed in x, z)
605 // ---
606
607 // contract in z
608 for (int ez = 0; ez < c_dofs1D; ++ez)
609 {
610 for (int dx = 0; dx < c_dofs1D; ++dx)
611 {
612 for (int dy = 0; dy < c_dofs1D; ++dy)
613 {
614 w1[dx][dy][ez] = 0.0;
615 for (int dz = 0; dz < c_dofs1D; ++dz)
616 {
617 w1[dx][dy][ez] += B(ez, dz) * x(dx, dy, dz, e);
618 }
619 }
620 }
621 }
622
623 // contract in y
624 for (int ez = 0; ez < c_dofs1D; ++ez)
625 {
626 for (int ey = 0; ey < o_dofs1D; ++ey)
627 {
628 for (int dx = 0; dx < c_dofs1D; ++dx)
629 {
630 w2[dx][ey][ez] = 0.0;
631 for (int dy = 0; dy < c_dofs1D; ++dy)
632 {
633 w2[dx][ey][ez] += G(ey, dy) * w1[dx][dy][ez];
634 }
635 }
636 }
637 }
638
639 // contract in x
640 for (int ez = 0; ez < c_dofs1D; ++ez)
641 {
642 for (int ey = 0; ey < o_dofs1D; ++ey)
643 {
644 for (int ex = 0; ex < c_dofs1D; ++ex)
645 {
646 real_t s = 0.0;
647 for (int dx = 0; dx < c_dofs1D; ++dx)
648 {
649 s += B(ex, dx) * w2[dx][ey][ez];
650 }
651 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
652 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
653 y(local_index, e) += s;
654 }
655 }
656 }
657
658 // ---
659 // dofs that point parallel to z-axis (open in z, closed in x, y)
660 // ---
661
662 // contract in z
663 for (int ez = 0; ez < o_dofs1D; ++ez)
664 {
665 for (int dx = 0; dx < c_dofs1D; ++dx)
666 {
667 for (int dy = 0; dy < c_dofs1D; ++dy)
668 {
669 w1[dx][dy][ez] = 0.0;
670 for (int dz = 0; dz < c_dofs1D; ++dz)
671 {
672 w1[dx][dy][ez] += G(ez, dz) * x(dx, dy, dz, e);
673 }
674 }
675 }
676 }
677
678 // contract in y
679 for (int ez = 0; ez < o_dofs1D; ++ez)
680 {
681 for (int ey = 0; ey < c_dofs1D; ++ey)
682 {
683 for (int dx = 0; dx < c_dofs1D; ++dx)
684 {
685 w2[dx][ey][ez] = 0.0;
686 for (int dy = 0; dy < c_dofs1D; ++dy)
687 {
688 w2[dx][ey][ez] += B(ey, dy) * w1[dx][dy][ez];
689 }
690 }
691 }
692 }
693
694 // contract in x
695 for (int ez = 0; ez < o_dofs1D; ++ez)
696 {
697 for (int ey = 0; ey < c_dofs1D; ++ey)
698 {
699 for (int ex = 0; ex < c_dofs1D; ++ex)
700 {
701 real_t s = 0.0;
702 for (int dx = 0; dx < c_dofs1D; ++dx)
703 {
704 s += B(ex, dx) * w2[dx][ey][ez];
705 }
706 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
707 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
708 y(local_index, e) += s;
709 }
710 }
711 }
712 });
713}
714
715// Specialization of PAHcurlApplyGradient3D to the case where B is identity
716static void PAHcurlApplyGradient3DBId(const int c_dofs1D,
717 const int o_dofs1D,
718 const int NE,
719 const Array<real_t> &G_,
720 const Vector &x_,
721 Vector &y_)
722{
723 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
724
725 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
726 auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
727
728 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
729 o_dofs1D <= c_dofs1D, "");
730
731 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
732 {
733 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
734
735 real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
736 real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
737
738 // ---
739 // dofs that point parallel to x-axis (open in x, closed in y, z)
740 // ---
741
742 // contract in z
743 for (int ez = 0; ez < c_dofs1D; ++ez)
744 {
745 for (int dx = 0; dx < c_dofs1D; ++dx)
746 {
747 for (int dy = 0; dy < c_dofs1D; ++dy)
748 {
749 const int dz = ez;
750 w1[dx][dy][ez] = x(dx, dy, dz, e);
751 }
752 }
753 }
754
755 // contract in y
756 for (int ez = 0; ez < c_dofs1D; ++ez)
757 {
758 for (int ey = 0; ey < c_dofs1D; ++ey)
759 {
760 for (int dx = 0; dx < c_dofs1D; ++dx)
761 {
762 const int dy = ey;
763 w2[dx][ey][ez] = w1[dx][dy][ez];
764 }
765 }
766 }
767
768 // contract in x
769 for (int ez = 0; ez < c_dofs1D; ++ez)
770 {
771 for (int ey = 0; ey < c_dofs1D; ++ey)
772 {
773 for (int ex = 0; ex < o_dofs1D; ++ex)
774 {
775 real_t s = 0.0;
776 for (int dx = 0; dx < c_dofs1D; ++dx)
777 {
778 s += G(ex, dx) * w2[dx][ey][ez];
779 }
780 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
781 y(local_index, e) += s;
782 }
783 }
784 }
785
786 // ---
787 // dofs that point parallel to y-axis (open in y, closed in x, z)
788 // ---
789
790 // contract in z
791 for (int ez = 0; ez < c_dofs1D; ++ez)
792 {
793 for (int dx = 0; dx < c_dofs1D; ++dx)
794 {
795 for (int dy = 0; dy < c_dofs1D; ++dy)
796 {
797 const int dz = ez;
798 w1[dx][dy][ez] = x(dx, dy, dz, e);
799 }
800 }
801 }
802
803 // contract in y
804 for (int ez = 0; ez < c_dofs1D; ++ez)
805 {
806 for (int ey = 0; ey < o_dofs1D; ++ey)
807 {
808 for (int dx = 0; dx < c_dofs1D; ++dx)
809 {
810 w2[dx][ey][ez] = 0.0;
811 for (int dy = 0; dy < c_dofs1D; ++dy)
812 {
813 w2[dx][ey][ez] += G(ey, dy) * w1[dx][dy][ez];
814 }
815 }
816 }
817 }
818
819 // contract in x
820 for (int ez = 0; ez < c_dofs1D; ++ez)
821 {
822 for (int ey = 0; ey < o_dofs1D; ++ey)
823 {
824 for (int ex = 0; ex < c_dofs1D; ++ex)
825 {
826 const int dx = ex;
827 const real_t s = w2[dx][ey][ez];
828 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
829 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
830 y(local_index, e) += s;
831 }
832 }
833 }
834
835 // ---
836 // dofs that point parallel to z-axis (open in z, closed in x, y)
837 // ---
838
839 // contract in z
840 for (int ez = 0; ez < o_dofs1D; ++ez)
841 {
842 for (int dx = 0; dx < c_dofs1D; ++dx)
843 {
844 for (int dy = 0; dy < c_dofs1D; ++dy)
845 {
846 w1[dx][dy][ez] = 0.0;
847 for (int dz = 0; dz < c_dofs1D; ++dz)
848 {
849 w1[dx][dy][ez] += G(ez, dz) * x(dx, dy, dz, e);
850 }
851 }
852 }
853 }
854
855 // contract in y
856 for (int ez = 0; ez < o_dofs1D; ++ez)
857 {
858 for (int ey = 0; ey < c_dofs1D; ++ey)
859 {
860 for (int dx = 0; dx < c_dofs1D; ++dx)
861 {
862 const int dy = ey;
863 w2[dx][ey][ez] = w1[dx][dy][ez];
864 }
865 }
866 }
867
868 // contract in x
869 for (int ez = 0; ez < o_dofs1D; ++ez)
870 {
871 for (int ey = 0; ey < c_dofs1D; ++ey)
872 {
873 for (int ex = 0; ex < c_dofs1D; ++ex)
874 {
875 const int dx = ex;
876 const real_t s = w2[dx][ey][ez];
877 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
878 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
879 y(local_index, e) += s;
880 }
881 }
882 }
883 });
884}
885
886static void PAHcurlApplyGradientTranspose3D(
887 const int c_dofs1D, const int o_dofs1D, const int NE,
888 const Array<real_t> &B_, const Array<real_t> &G_,
889 const Vector &x_, Vector &y_)
890{
891 auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
892 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
893
894 auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
895 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
896
897 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
898 o_dofs1D <= c_dofs1D, "");
899
900 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
901 {
902 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
903 real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
904 real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
905 // ---
906 // dofs that point parallel to x-axis (open in x, closed in y, z)
907 // ---
908
909 // contract in z
910 for (int dz = 0; dz < c_dofs1D; ++dz)
911 {
912 for (int ex = 0; ex < o_dofs1D; ++ex)
913 {
914 for (int ey = 0; ey < c_dofs1D; ++ey)
915 {
916 w1[ex][ey][dz] = 0.0;
917 for (int ez = 0; ez < c_dofs1D; ++ez)
918 {
919 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
920 w1[ex][ey][dz] += B(ez, dz) * x(local_index, e);
921 }
922 }
923 }
924 }
925
926 // contract in y
927 for (int dz = 0; dz < c_dofs1D; ++dz)
928 {
929 for (int dy = 0; dy < c_dofs1D; ++dy)
930 {
931 for (int ex = 0; ex < o_dofs1D; ++ex)
932 {
933 w2[ex][dy][dz] = 0.0;
934 for (int ey = 0; ey < c_dofs1D; ++ey)
935 {
936 w2[ex][dy][dz] += B(ey, dy) * w1[ex][ey][dz];
937 }
938 }
939 }
940 }
941
942 // contract in x
943 for (int dz = 0; dz < c_dofs1D; ++dz)
944 {
945 for (int dy = 0; dy < c_dofs1D; ++dy)
946 {
947 for (int dx = 0; dx < c_dofs1D; ++dx)
948 {
949 real_t s = 0.0;
950 for (int ex = 0; ex < o_dofs1D; ++ex)
951 {
952 s += G(ex, dx) * w2[ex][dy][dz];
953 }
954 y(dx, dy, dz, e) += s;
955 }
956 }
957 }
958
959 // ---
960 // dofs that point parallel to y-axis (open in y, closed in x, z)
961 // ---
962
963 // contract in z
964 for (int dz = 0; dz < c_dofs1D; ++dz)
965 {
966 for (int ex = 0; ex < c_dofs1D; ++ex)
967 {
968 for (int ey = 0; ey < o_dofs1D; ++ey)
969 {
970 w1[ex][ey][dz] = 0.0;
971 for (int ez = 0; ez < c_dofs1D; ++ez)
972 {
973 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
974 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
975 w1[ex][ey][dz] += B(ez, dz) * x(local_index, e);
976 }
977 }
978 }
979 }
980
981 // contract in y
982 for (int dz = 0; dz < c_dofs1D; ++dz)
983 {
984 for (int dy = 0; dy < c_dofs1D; ++dy)
985 {
986 for (int ex = 0; ex < c_dofs1D; ++ex)
987 {
988 w2[ex][dy][dz] = 0.0;
989 for (int ey = 0; ey < o_dofs1D; ++ey)
990 {
991 w2[ex][dy][dz] += G(ey, dy) * w1[ex][ey][dz];
992 }
993 }
994 }
995 }
996
997 // contract in x
998 for (int dz = 0; dz < c_dofs1D; ++dz)
999 {
1000 for (int dy = 0; dy < c_dofs1D; ++dy)
1001 {
1002 for (int dx = 0; dx < c_dofs1D; ++dx)
1003 {
1004 real_t s = 0.0;
1005 for (int ex = 0; ex < c_dofs1D; ++ex)
1006 {
1007 s += B(ex, dx) * w2[ex][dy][dz];
1008 }
1009 y(dx, dy, dz, e) += s;
1010 }
1011 }
1012 }
1013
1014 // ---
1015 // dofs that point parallel to z-axis (open in z, closed in x, y)
1016 // ---
1017
1018 // contract in z
1019 for (int dz = 0; dz < c_dofs1D; ++dz)
1020 {
1021 for (int ex = 0; ex < c_dofs1D; ++ex)
1022 {
1023 for (int ey = 0; ey < c_dofs1D; ++ey)
1024 {
1025 w1[ex][ey][dz] = 0.0;
1026 for (int ez = 0; ez < o_dofs1D; ++ez)
1027 {
1028 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
1029 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
1030 w1[ex][ey][dz] += G(ez, dz) * x(local_index, e);
1031 }
1032 }
1033 }
1034 }
1035
1036 // contract in y
1037 for (int dz = 0; dz < c_dofs1D; ++dz)
1038 {
1039 for (int dy = 0; dy < c_dofs1D; ++dy)
1040 {
1041 for (int ex = 0; ex < c_dofs1D; ++ex)
1042 {
1043 w2[ex][dy][dz] = 0.0;
1044 for (int ey = 0; ey < c_dofs1D; ++ey)
1045 {
1046 w2[ex][dy][dz] += B(ey, dy) * w1[ex][ey][dz];
1047 }
1048 }
1049 }
1050 }
1051
1052 // contract in x
1053 for (int dz = 0; dz < c_dofs1D; ++dz)
1054 {
1055 for (int dy = 0; dy < c_dofs1D; ++dy)
1056 {
1057 for (int dx = 0; dx < c_dofs1D; ++dx)
1058 {
1059 real_t s = 0.0;
1060 for (int ex = 0; ex < c_dofs1D; ++ex)
1061 {
1062 s += B(ex, dx) * w2[ex][dy][dz];
1063 }
1064 y(dx, dy, dz, e) += s;
1065 }
1066 }
1067 }
1068 });
1069}
1070
1071// Specialization of PAHcurlApplyGradientTranspose3D to the case where
1072// B is identity
1073static void PAHcurlApplyGradientTranspose3DBId(
1074 const int c_dofs1D, const int o_dofs1D, const int NE,
1075 const Array<real_t> &G_,
1076 const Vector &x_, Vector &y_)
1077{
1078 auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
1079
1080 auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
1081 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
1082
1083 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
1084 o_dofs1D <= c_dofs1D, "");
1085
1086 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1087 {
1088 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
1089
1090 real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
1091 real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
1092 // ---
1093 // dofs that point parallel to x-axis (open in x, closed in y, z)
1094 // ---
1095
1096 // contract in z
1097 for (int dz = 0; dz < c_dofs1D; ++dz)
1098 {
1099 for (int ex = 0; ex < o_dofs1D; ++ex)
1100 {
1101 for (int ey = 0; ey < c_dofs1D; ++ey)
1102 {
1103 const int ez = dz;
1104 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
1105 w1[ex][ey][dz] = x(local_index, e);
1106 }
1107 }
1108 }
1109
1110 // contract in y
1111 for (int dz = 0; dz < c_dofs1D; ++dz)
1112 {
1113 for (int dy = 0; dy < c_dofs1D; ++dy)
1114 {
1115 for (int ex = 0; ex < o_dofs1D; ++ex)
1116 {
1117 const int ey = dy;
1118 w2[ex][dy][dz] = w1[ex][ey][dz];
1119 }
1120 }
1121 }
1122
1123 // contract in x
1124 for (int dz = 0; dz < c_dofs1D; ++dz)
1125 {
1126 for (int dy = 0; dy < c_dofs1D; ++dy)
1127 {
1128 for (int dx = 0; dx < c_dofs1D; ++dx)
1129 {
1130 real_t s = 0.0;
1131 for (int ex = 0; ex < o_dofs1D; ++ex)
1132 {
1133 s += G(ex, dx) * w2[ex][dy][dz];
1134 }
1135 y(dx, dy, dz, e) += s;
1136 }
1137 }
1138 }
1139
1140 // ---
1141 // dofs that point parallel to y-axis (open in y, closed in x, z)
1142 // ---
1143
1144 // contract in z
1145 for (int dz = 0; dz < c_dofs1D; ++dz)
1146 {
1147 for (int ex = 0; ex < c_dofs1D; ++ex)
1148 {
1149 for (int ey = 0; ey < o_dofs1D; ++ey)
1150 {
1151 const int ez = dz;
1152 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
1153 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
1154 w1[ex][ey][dz] = x(local_index, e);
1155 }
1156 }
1157 }
1158
1159 // contract in y
1160 for (int dz = 0; dz < c_dofs1D; ++dz)
1161 {
1162 for (int dy = 0; dy < c_dofs1D; ++dy)
1163 {
1164 for (int ex = 0; ex < c_dofs1D; ++ex)
1165 {
1166 w2[ex][dy][dz] = 0.0;
1167 for (int ey = 0; ey < o_dofs1D; ++ey)
1168 {
1169 w2[ex][dy][dz] += G(ey, dy) * w1[ex][ey][dz];
1170 }
1171 }
1172 }
1173 }
1174
1175 // contract in x
1176 for (int dz = 0; dz < c_dofs1D; ++dz)
1177 {
1178 for (int dy = 0; dy < c_dofs1D; ++dy)
1179 {
1180 for (int dx = 0; dx < c_dofs1D; ++dx)
1181 {
1182 const int ex = dx;
1183 real_t s = w2[ex][dy][dz];
1184 y(dx, dy, dz, e) += s;
1185 }
1186 }
1187 }
1188
1189 // ---
1190 // dofs that point parallel to z-axis (open in z, closed in x, y)
1191 // ---
1192
1193 // contract in z
1194 for (int dz = 0; dz < c_dofs1D; ++dz)
1195 {
1196 for (int ex = 0; ex < c_dofs1D; ++ex)
1197 {
1198 for (int ey = 0; ey < c_dofs1D; ++ey)
1199 {
1200 w1[ex][ey][dz] = 0.0;
1201 for (int ez = 0; ez < o_dofs1D; ++ez)
1202 {
1203 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
1204 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
1205 w1[ex][ey][dz] += G(ez, dz) * x(local_index, e);
1206 }
1207 }
1208 }
1209 }
1210
1211 // contract in y
1212 for (int dz = 0; dz < c_dofs1D; ++dz)
1213 {
1214 for (int dy = 0; dy < c_dofs1D; ++dy)
1215 {
1216 for (int ex = 0; ex < c_dofs1D; ++ex)
1217 {
1218 const int ey = dy;
1219 w2[ex][dy][dz] = w1[ex][ey][dz];
1220 }
1221 }
1222 }
1223
1224 // contract in x
1225 for (int dz = 0; dz < c_dofs1D; ++dz)
1226 {
1227 for (int dy = 0; dy < c_dofs1D; ++dy)
1228 {
1229 for (int dx = 0; dx < c_dofs1D; ++dx)
1230 {
1231 const int ex = dx;
1232 real_t s = w2[ex][dy][dz];
1233 y(dx, dy, dz, e) += s;
1234 }
1235 }
1236 }
1237 });
1238}
1239
1241 const FiniteElementSpace &test_fes)
1242{
1243 // Assumes tensor-product elements, with a vector test space and H^1 trial space.
1244 Mesh *mesh = trial_fes.GetMesh();
1245 const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
1246 const FiniteElement *test_fel = test_fes.GetTypicalFE();
1247
1248 const NodalTensorFiniteElement *trial_el =
1249 dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
1250 MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
1251
1252 const VectorTensorFiniteElement *test_el =
1253 dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
1254 MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
1255
1256 const int dims = trial_el->GetDim();
1257 MFEM_VERIFY(dims == 2 || dims == 3, "Bad dimension!");
1258 dim = mesh->Dimension();
1259 MFEM_VERIFY(dim == 2 || dim == 3, "Bad dimension!");
1260 MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(),
1261 "Orders do not match!");
1262 ne = trial_fes.GetNE();
1263
1264 const int order = trial_el->GetOrder();
1265 dofquad_fe = new H1_SegmentElement(order, trial_el->GetBasisType());
1267 mfem::IntegrationRule closed_ir;
1268 closed_ir.SetSize(order + 1);
1269 qf1d.GaussLobatto(order + 1, &closed_ir);
1270 mfem::IntegrationRule open_ir;
1271 open_ir.SetSize(order);
1272 qf1d.GaussLegendre(order, &open_ir);
1273
1274 maps_O_C = &dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
1275 o_dofs1D = maps_O_C->nqpt;
1276 if (trial_el->GetBasisType() == BasisType::GaussLobatto)
1277 {
1278 B_id = true;
1279 c_dofs1D = maps_O_C->ndof;
1280 }
1281 else
1282 {
1283 B_id = false;
1284 maps_C_C = &dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
1285 c_dofs1D = maps_C_C->nqpt;
1286 }
1287}
1288
1290{
1291 if (dim == 3)
1292 {
1293 if (B_id)
1294 {
1295 PAHcurlApplyGradient3DBId(c_dofs1D, o_dofs1D, ne,
1296 maps_O_C->G, x, y);
1297 }
1298 else
1299 {
1300 PAHcurlApplyGradient3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
1301 maps_O_C->G, x, y);
1302 }
1303 }
1304 else if (dim == 2)
1305 {
1306 if (B_id)
1307 {
1308 PAHcurlApplyGradient2DBId(c_dofs1D, o_dofs1D, ne,
1309 maps_O_C->G, x, y);
1310 }
1311 else
1312 {
1313 PAHcurlApplyGradient2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->G,
1314 x, y);
1315 }
1316 }
1317 else
1318 {
1319 mfem_error("Bad dimension!");
1320 }
1321}
1322
1324{
1325 if (dim == 3)
1326 {
1327 if (B_id)
1328 {
1329 PAHcurlApplyGradientTranspose3DBId(c_dofs1D, o_dofs1D, ne,
1330 maps_O_C->G, x, y);
1331 }
1332 else
1333 {
1334 PAHcurlApplyGradientTranspose3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
1335 maps_O_C->G, x, y);
1336 }
1337 }
1338 else if (dim == 2)
1339 {
1340 if (B_id)
1341 {
1342 PAHcurlApplyGradientTranspose2DBId(c_dofs1D, o_dofs1D, ne,
1343 maps_O_C->G, x, y);
1344 }
1345 else
1346 {
1347 PAHcurlApplyGradientTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
1348 maps_O_C->G, x, y);
1349 }
1350 }
1351 else
1352 {
1353 mfem_error("Bad dimension!");
1354 }
1355}
1356
1357static void PAHcurlVecH1IdentityApply2D(const int c_dofs1D,
1358 const int o_dofs1D,
1359 const int NE,
1360 const Array<real_t> &Bclosed,
1361 const Array<real_t> &Bopen,
1362 const Vector &pa_data,
1363 const Vector &x_,
1364 Vector &y_)
1365{
1366 auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
1367 auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
1368
1369 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, 2, NE);
1370 auto y = Reshape(y_.ReadWrite(), (2 * c_dofs1D * o_dofs1D), NE);
1371
1372 auto vk = Reshape(pa_data.Read(), 2, (2 * c_dofs1D * o_dofs1D), NE);
1373
1374 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
1375 o_dofs1D <= c_dofs1D, "");
1376
1377 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1378 {
1379 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
1380
1381 real_t w[2][MAX_D1D][MAX_D1D];
1382
1383 // dofs that point parallel to x-axis (open in x, closed in y)
1384
1385 // contract in y
1386 for (int ey = 0; ey < c_dofs1D; ++ey)
1387 {
1388 for (int dx = 0; dx < c_dofs1D; ++dx)
1389 {
1390 for (int j=0; j<2; ++j)
1391 {
1392 w[j][dx][ey] = 0.0;
1393 for (int dy = 0; dy < c_dofs1D; ++dy)
1394 {
1395 w[j][dx][ey] += Bc(ey, dy) * x(dx, dy, j, e);
1396 }
1397 }
1398 }
1399 }
1400
1401 // contract in x
1402 for (int ey = 0; ey < c_dofs1D; ++ey)
1403 {
1404 for (int ex = 0; ex < o_dofs1D; ++ex)
1405 {
1406 for (int j=0; j<2; ++j)
1407 {
1408 real_t s = 0.0;
1409 for (int dx = 0; dx < c_dofs1D; ++dx)
1410 {
1411 s += Bo(ex, dx) * w[j][dx][ey];
1412 }
1413 const int local_index = ey*o_dofs1D + ex;
1414 y(local_index, e) += s * vk(j, local_index, e);
1415 }
1416 }
1417 }
1418
1419 // dofs that point parallel to y-axis (open in y, closed in x)
1420
1421 // contract in y
1422 for (int ey = 0; ey < o_dofs1D; ++ey)
1423 {
1424 for (int dx = 0; dx < c_dofs1D; ++dx)
1425 {
1426 for (int j=0; j<2; ++j)
1427 {
1428 w[j][dx][ey] = 0.0;
1429 for (int dy = 0; dy < c_dofs1D; ++dy)
1430 {
1431 w[j][dx][ey] += Bo(ey, dy) * x(dx, dy, j, e);
1432 }
1433 }
1434 }
1435 }
1436
1437 // contract in x
1438 for (int ey = 0; ey < o_dofs1D; ++ey)
1439 {
1440 for (int ex = 0; ex < c_dofs1D; ++ex)
1441 {
1442 for (int j=0; j<2; ++j)
1443 {
1444 real_t s = 0.0;
1445 for (int dx = 0; dx < c_dofs1D; ++dx)
1446 {
1447 s += Bc(ex, dx) * w[j][dx][ey];
1448 }
1449 const int local_index = c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
1450 y(local_index, e) += s * vk(j, local_index, e);
1451 }
1452 }
1453 }
1454 });
1455}
1456
1457static void PAHcurlVecH1IdentityApplyTranspose2D(const int c_dofs1D,
1458 const int o_dofs1D,
1459 const int NE,
1460 const Array<real_t> &Bclosed,
1461 const Array<real_t> &Bopen,
1462 const Vector &pa_data,
1463 const Vector &x_,
1464 Vector &y_)
1465{
1466 auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
1467 auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
1468
1469 auto x = Reshape(x_.Read(), (2 * c_dofs1D * o_dofs1D), NE);
1470 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, 2, NE);
1471
1472 auto vk = Reshape(pa_data.Read(), 2, (2 * c_dofs1D * o_dofs1D), NE);
1473
1474 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
1475 o_dofs1D <= c_dofs1D, "");
1476
1477 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1478 {
1479 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
1480
1481 real_t w[2][MAX_D1D][MAX_D1D];
1482
1483 // dofs that point parallel to x-axis (open in x, closed in y)
1484
1485 // contract in x
1486 for (int ey = 0; ey < c_dofs1D; ++ey)
1487 {
1488 for (int dx = 0; dx < c_dofs1D; ++dx)
1489 {
1490 for (int j=0; j<2; ++j) { w[j][dx][ey] = 0.0; }
1491 }
1492 for (int ex = 0; ex < o_dofs1D; ++ex)
1493 {
1494 const int local_index = ey*o_dofs1D + ex;
1495 const real_t xd = x(local_index, e);
1496
1497 for (int dx = 0; dx < c_dofs1D; ++dx)
1498 {
1499 for (int j=0; j<2; ++j)
1500 {
1501 w[j][dx][ey] += Bo(ex, dx) * xd * vk(j, local_index, e);
1502 }
1503 }
1504 }
1505 }
1506
1507 // contract in y
1508 for (int dx = 0; dx < c_dofs1D; ++dx)
1509 {
1510 for (int dy = 0; dy < c_dofs1D; ++dy)
1511 {
1512 for (int j=0; j<2; ++j)
1513 {
1514 real_t s = 0.0;
1515 for (int ey = 0; ey < c_dofs1D; ++ey)
1516 {
1517 s += w[j][dx][ey] * Bc(ey, dy);
1518 }
1519 y(dx, dy, j, e) += s;
1520 }
1521 }
1522 }
1523
1524 // dofs that point parallel to y-axis (open in y, closed in x)
1525
1526 // contract in x
1527 for (int ey = 0; ey < o_dofs1D; ++ey)
1528 {
1529 for (int dx = 0; dx < c_dofs1D; ++dx)
1530 {
1531 for (int j=0; j<2; ++j) { w[j][dx][ey] = 0.0; }
1532 }
1533 for (int ex = 0; ex < c_dofs1D; ++ex)
1534 {
1535 const int local_index = c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
1536 const real_t xd = x(local_index, e);
1537 for (int dx = 0; dx < c_dofs1D; ++dx)
1538 {
1539 for (int j=0; j<2; ++j)
1540 {
1541 w[j][dx][ey] += Bc(ex, dx) * xd * vk(j, local_index, e);
1542 }
1543 }
1544 }
1545 }
1546
1547 // contract in y
1548 for (int dx = 0; dx < c_dofs1D; ++dx)
1549 {
1550 for (int dy = 0; dy < c_dofs1D; ++dy)
1551 {
1552 for (int j=0; j<2; ++j)
1553 {
1554 real_t s = 0.0;
1555 for (int ey = 0; ey < o_dofs1D; ++ey)
1556 {
1557 s += w[j][dx][ey] * Bo(ey, dy);
1558 }
1559 y(dx, dy, j, e) += s;
1560 }
1561 }
1562 }
1563 });
1564}
1565
1566static void PAHcurlVecH1IdentityApply3D(const int c_dofs1D,
1567 const int o_dofs1D,
1568 const int NE,
1569 const Array<real_t> &Bclosed,
1570 const Array<real_t> &Bopen,
1571 const Vector &pa_data,
1572 const Vector &x_,
1573 Vector &y_)
1574{
1575 auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
1576 auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
1577
1578 auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, 3, NE);
1579 auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
1580
1581 auto vk = Reshape(pa_data.Read(), 3, (3 * c_dofs1D * c_dofs1D * o_dofs1D),
1582 NE);
1583 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
1584 o_dofs1D <= c_dofs1D, "");
1585
1586 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1587 {
1588 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
1589
1590 real_t w1[3][MAX_D1D][MAX_D1D][MAX_D1D];
1591 real_t w2[3][MAX_D1D][MAX_D1D][MAX_D1D];
1592
1593 // dofs that point parallel to x-axis (open in x, closed in y, z)
1594
1595 // contract in z
1596 for (int ez = 0; ez < c_dofs1D; ++ez)
1597 {
1598 for (int dx = 0; dx < c_dofs1D; ++dx)
1599 {
1600 for (int dy = 0; dy < c_dofs1D; ++dy)
1601 {
1602 for (int j=0; j<3; ++j)
1603 {
1604 w1[j][dx][dy][ez] = 0.0;
1605 for (int dz = 0; dz < c_dofs1D; ++dz)
1606 {
1607 w1[j][dx][dy][ez] += Bc(ez, dz) * x(dx, dy, dz, j, e);
1608 }
1609 }
1610 }
1611 }
1612 }
1613
1614 // contract in y
1615 for (int ez = 0; ez < c_dofs1D; ++ez)
1616 {
1617 for (int ey = 0; ey < c_dofs1D; ++ey)
1618 {
1619 for (int dx = 0; dx < c_dofs1D; ++dx)
1620 {
1621 for (int j=0; j<3; ++j)
1622 {
1623 w2[j][dx][ey][ez] = 0.0;
1624 for (int dy = 0; dy < c_dofs1D; ++dy)
1625 {
1626 w2[j][dx][ey][ez] += Bc(ey, dy) * w1[j][dx][dy][ez];
1627 }
1628 }
1629 }
1630 }
1631 }
1632
1633 // contract in x
1634 for (int ez = 0; ez < c_dofs1D; ++ez)
1635 {
1636 for (int ey = 0; ey < c_dofs1D; ++ey)
1637 {
1638 for (int ex = 0; ex < o_dofs1D; ++ex)
1639 {
1640 for (int j=0; j<3; ++j)
1641 {
1642 real_t s = 0.0;
1643 for (int dx = 0; dx < c_dofs1D; ++dx)
1644 {
1645 s += Bo(ex, dx) * w2[j][dx][ey][ez];
1646 }
1647 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
1648 y(local_index, e) += s * vk(j, local_index, e);
1649 }
1650 }
1651 }
1652 }
1653
1654 // dofs that point parallel to y-axis (open in y, closed in x, z)
1655
1656 // contract in z
1657 for (int ez = 0; ez < c_dofs1D; ++ez)
1658 {
1659 for (int dx = 0; dx < c_dofs1D; ++dx)
1660 {
1661 for (int dy = 0; dy < c_dofs1D; ++dy)
1662 {
1663 for (int j=0; j<3; ++j)
1664 {
1665 w1[j][dx][dy][ez] = 0.0;
1666 for (int dz = 0; dz < c_dofs1D; ++dz)
1667 {
1668 w1[j][dx][dy][ez] += Bc(ez, dz) * x(dx, dy, dz, j, e);
1669 }
1670 }
1671 }
1672 }
1673 }
1674
1675 // contract in y
1676 for (int ez = 0; ez < c_dofs1D; ++ez)
1677 {
1678 for (int ey = 0; ey < o_dofs1D; ++ey)
1679 {
1680 for (int dx = 0; dx < c_dofs1D; ++dx)
1681 {
1682 for (int j=0; j<3; ++j)
1683 {
1684 w2[j][dx][ey][ez] = 0.0;
1685 for (int dy = 0; dy < c_dofs1D; ++dy)
1686 {
1687 w2[j][dx][ey][ez] += Bo(ey, dy) * w1[j][dx][dy][ez];
1688 }
1689 }
1690 }
1691 }
1692 }
1693
1694 // contract in x
1695 for (int ez = 0; ez < c_dofs1D; ++ez)
1696 {
1697 for (int ey = 0; ey < o_dofs1D; ++ey)
1698 {
1699 for (int ex = 0; ex < c_dofs1D; ++ex)
1700 {
1701 for (int j=0; j<3; ++j)
1702 {
1703 real_t s = 0.0;
1704 for (int dx = 0; dx < c_dofs1D; ++dx)
1705 {
1706 s += Bc(ex, dx) * w2[j][dx][ey][ez];
1707 }
1708 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
1709 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
1710 y(local_index, e) += s * vk(j, local_index, e);
1711 }
1712 }
1713 }
1714 }
1715
1716 // dofs that point parallel to z-axis (open in z, closed in x, y)
1717
1718 // contract in z
1719 for (int ez = 0; ez < o_dofs1D; ++ez)
1720 {
1721 for (int dx = 0; dx < c_dofs1D; ++dx)
1722 {
1723 for (int dy = 0; dy < c_dofs1D; ++dy)
1724 {
1725 for (int j=0; j<3; ++j)
1726 {
1727 w1[j][dx][dy][ez] = 0.0;
1728 for (int dz = 0; dz < c_dofs1D; ++dz)
1729 {
1730 w1[j][dx][dy][ez] += Bo(ez, dz) * x(dx, dy, dz, j, e);
1731 }
1732 }
1733 }
1734 }
1735 }
1736
1737 // contract in y
1738 for (int ez = 0; ez < o_dofs1D; ++ez)
1739 {
1740 for (int ey = 0; ey < c_dofs1D; ++ey)
1741 {
1742 for (int dx = 0; dx < c_dofs1D; ++dx)
1743 {
1744 for (int j=0; j<3; ++j)
1745 {
1746 w2[j][dx][ey][ez] = 0.0;
1747 for (int dy = 0; dy < c_dofs1D; ++dy)
1748 {
1749 w2[j][dx][ey][ez] += Bc(ey, dy) * w1[j][dx][dy][ez];
1750 }
1751 }
1752 }
1753 }
1754 }
1755
1756 // contract in x
1757 for (int ez = 0; ez < o_dofs1D; ++ez)
1758 {
1759 for (int ey = 0; ey < c_dofs1D; ++ey)
1760 {
1761 for (int ex = 0; ex < c_dofs1D; ++ex)
1762 {
1763 for (int j=0; j<3; ++j)
1764 {
1765 real_t s = 0.0;
1766 for (int dx = 0; dx < c_dofs1D; ++dx)
1767 {
1768 s += Bc(ex, dx) * w2[j][dx][ey][ez];
1769 }
1770 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
1771 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
1772 y(local_index, e) += s * vk(j, local_index, e);
1773 }
1774 }
1775 }
1776 }
1777 });
1778}
1779
1780static void PAHcurlVecH1IdentityApplyTranspose3D(const int c_dofs1D,
1781 const int o_dofs1D,
1782 const int NE,
1783 const Array<real_t> &Bclosed,
1784 const Array<real_t> &Bopen,
1785 const Vector &pa_data,
1786 const Vector &x_,
1787 Vector &y_)
1788{
1789 auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
1790 auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
1791
1792 auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
1793 auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, 3, NE);
1794
1795 auto vk = Reshape(pa_data.Read(), 3, (3 * c_dofs1D * c_dofs1D * o_dofs1D),
1796 NE);
1797
1798 MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
1799 o_dofs1D <= c_dofs1D, "");
1800
1801 mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
1802 {
1803 constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
1804
1805 real_t w1[3][MAX_D1D][MAX_D1D][MAX_D1D];
1806 real_t w2[3][MAX_D1D][MAX_D1D][MAX_D1D];
1807
1808 // dofs that point parallel to x-axis (open in x, closed in y, z)
1809
1810 // contract in x
1811 for (int ez = 0; ez < c_dofs1D; ++ez)
1812 {
1813 for (int ey = 0; ey < c_dofs1D; ++ey)
1814 {
1815 for (int j=0; j<3; ++j)
1816 {
1817 for (int dx = 0; dx < c_dofs1D; ++dx)
1818 {
1819 w2[j][dx][ey][ez] = 0.0;
1820 }
1821 for (int ex = 0; ex < o_dofs1D; ++ex)
1822 {
1823 const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
1824 const real_t xv = x(local_index, e) * vk(j, local_index, e);
1825 for (int dx = 0; dx < c_dofs1D; ++dx)
1826 {
1827 w2[j][dx][ey][ez] += xv * Bo(ex, dx);
1828 }
1829 }
1830 }
1831 }
1832 }
1833
1834 // contract in y
1835 for (int ez = 0; ez < c_dofs1D; ++ez)
1836 {
1837 for (int dx = 0; dx < c_dofs1D; ++dx)
1838 {
1839 for (int dy = 0; dy < c_dofs1D; ++dy)
1840 {
1841 for (int j=0; j<3; ++j)
1842 {
1843 w1[j][dx][dy][ez] = 0.0;
1844 for (int ey = 0; ey < c_dofs1D; ++ey)
1845 {
1846 w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bc(ey, dy);
1847 }
1848 }
1849 }
1850 }
1851 }
1852
1853 // contract in z
1854 for (int dx = 0; dx < c_dofs1D; ++dx)
1855 {
1856 for (int dy = 0; dy < c_dofs1D; ++dy)
1857 {
1858 for (int dz = 0; dz < c_dofs1D; ++dz)
1859 {
1860 for (int j=0; j<3; ++j)
1861 {
1862 real_t s = 0.0;
1863 for (int ez = 0; ez < c_dofs1D; ++ez)
1864 {
1865 s += w1[j][dx][dy][ez] * Bc(ez, dz);
1866 }
1867 y(dx, dy, dz, j, e) += s;
1868 }
1869 }
1870 }
1871 }
1872
1873 // dofs that point parallel to y-axis (open in y, closed in x, z)
1874
1875 // contract in x
1876 for (int ez = 0; ez < c_dofs1D; ++ez)
1877 {
1878 for (int ey = 0; ey < o_dofs1D; ++ey)
1879 {
1880 for (int j=0; j<3; ++j)
1881 {
1882 for (int dx = 0; dx < c_dofs1D; ++dx)
1883 {
1884 w2[j][dx][ey][ez] = 0.0;
1885 }
1886 for (int ex = 0; ex < c_dofs1D; ++ex)
1887 {
1888 const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
1889 ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
1890 const real_t xv = x(local_index, e) * vk(j, local_index, e);
1891 for (int dx = 0; dx < c_dofs1D; ++dx)
1892 {
1893 w2[j][dx][ey][ez] += xv * Bc(ex, dx);
1894 }
1895 }
1896 }
1897 }
1898 }
1899
1900 // contract in y
1901 for (int ez = 0; ez < c_dofs1D; ++ez)
1902 {
1903 for (int dx = 0; dx < c_dofs1D; ++dx)
1904 {
1905 for (int dy = 0; dy < c_dofs1D; ++dy)
1906 {
1907 for (int j=0; j<3; ++j)
1908 {
1909 w1[j][dx][dy][ez] = 0.0;
1910 for (int ey = 0; ey < o_dofs1D; ++ey)
1911 {
1912 w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bo(ey, dy);
1913 }
1914 }
1915 }
1916 }
1917 }
1918
1919 // contract in z
1920 for (int dx = 0; dx < c_dofs1D; ++dx)
1921 {
1922 for (int dy = 0; dy < c_dofs1D; ++dy)
1923 {
1924 for (int dz = 0; dz < c_dofs1D; ++dz)
1925 {
1926 for (int j=0; j<3; ++j)
1927 {
1928 real_t s = 0.0;
1929 for (int ez = 0; ez < c_dofs1D; ++ez)
1930 {
1931 s += w1[j][dx][dy][ez] * Bc(ez, dz);
1932 }
1933 y(dx, dy, dz, j, e) += s;
1934 }
1935 }
1936 }
1937 }
1938
1939 // dofs that point parallel to z-axis (open in z, closed in x, y)
1940
1941 // contract in x
1942 for (int ez = 0; ez < o_dofs1D; ++ez)
1943 {
1944 for (int ey = 0; ey < c_dofs1D; ++ey)
1945 {
1946 for (int j=0; j<3; ++j)
1947 {
1948 for (int dx = 0; dx < c_dofs1D; ++dx)
1949 {
1950 w2[j][dx][ey][ez] = 0.0;
1951 }
1952 for (int ex = 0; ex < c_dofs1D; ++ex)
1953 {
1954 const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
1955 ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
1956 const real_t xv = x(local_index, e) * vk(j, local_index, e);
1957 for (int dx = 0; dx < c_dofs1D; ++dx)
1958 {
1959 w2[j][dx][ey][ez] += xv * Bc(ex, dx);
1960 }
1961 }
1962 }
1963 }
1964 }
1965
1966 // contract in y
1967 for (int ez = 0; ez < o_dofs1D; ++ez)
1968 {
1969 for (int dx = 0; dx < c_dofs1D; ++dx)
1970 {
1971 for (int dy = 0; dy < c_dofs1D; ++dy)
1972 {
1973 for (int j=0; j<3; ++j)
1974 {
1975 w1[j][dx][dy][ez] = 0.0;
1976 for (int ey = 0; ey < c_dofs1D; ++ey)
1977 {
1978 w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bc(ey, dy);
1979 }
1980 }
1981 }
1982 }
1983 }
1984
1985 // contract in z
1986 for (int dx = 0; dx < c_dofs1D; ++dx)
1987 {
1988 for (int dy = 0; dy < c_dofs1D; ++dy)
1989 {
1990 for (int dz = 0; dz < c_dofs1D; ++dz)
1991 {
1992 for (int j=0; j<3; ++j)
1993 {
1994 real_t s = 0.0;
1995 for (int ez = 0; ez < o_dofs1D; ++ez)
1996 {
1997 s += w1[j][dx][dy][ez] * Bo(ez, dz);
1998 }
1999 y(dx, dy, dz, j, e) += s;
2000 }
2001 }
2002 }
2003 }
2004 });
2005}
2006
2008 const FiniteElementSpace &test_fes)
2009{
2010 // Assumes tensor-product elements, with a vector test space and H^1 trial space.
2011 Mesh *mesh = trial_fes.GetMesh();
2012 const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
2013 const FiniteElement *test_fel = test_fes.GetTypicalFE();
2014
2015 const NodalTensorFiniteElement *trial_el =
2016 dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
2017 MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
2018
2019 const VectorTensorFiniteElement *test_el =
2020 dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
2021 MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
2022
2023 const int dims = trial_el->GetDim();
2024 MFEM_VERIFY(dims == 2 || dims == 3, "");
2025
2026 dim = mesh->Dimension();
2027 MFEM_VERIFY(dim == 2 || dim == 3, "");
2028
2029 MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
2030
2031 MFEM_VERIFY(vdim == 1, "vdim != 1 with PA is not supported yet!");
2032
2033 ne = trial_fes.GetNE();
2034
2035 const int order = trial_el->GetOrder();
2036 dofquad_fe.reset(new H1_SegmentElement(order));
2038 mfem::IntegrationRule closed_ir;
2039 closed_ir.SetSize(order + 1);
2040 qf1d.GaussLobatto(order + 1, &closed_ir);
2041 mfem::IntegrationRule open_ir;
2042 open_ir.SetSize(order);
2043 qf1d.GaussLegendre(order, &open_ir);
2044
2045 maps_C_C = &dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
2046 maps_O_C = &dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
2047
2048 o_dofs1D = maps_O_C->nqpt;
2049 c_dofs1D = maps_C_C->nqpt;
2050 MFEM_VERIFY(maps_O_C->ndof == c_dofs1D &&
2051 maps_C_C->ndof == c_dofs1D, "Discrepancy in the number of DOFs");
2052
2053 const int ndof_test = (dim == 3) ? 3 * c_dofs1D * c_dofs1D * o_dofs1D
2054 : 2 * c_dofs1D * o_dofs1D;
2055
2056 const IntegrationRule & Nodes = test_el->GetNodes();
2057
2058 pa_data.SetSize(dim * ndof_test * ne, Device::GetMemoryType());
2059 auto op = Reshape(pa_data.HostWrite(), dim, ndof_test, ne);
2060
2061 const Array<int> &dofmap = test_el->GetDofMap();
2062
2063 if (dim == 3)
2064 {
2065 // Note that ND_HexahedronElement uses 6 vectors in tk rather than 3, with
2066 // the last 3 having negative signs. Here the signs are all positive, as
2067 // signs are applied in ElementRestriction.
2068
2069 const real_t tk[9] = { 1.,0.,0., 0.,1.,0., 0.,0.,1. };
2070
2071 for (int c=0; c<3; ++c)
2072 {
2073 for (int i=0; i<ndof_test/3; ++i)
2074 {
2075 const int d = (c*ndof_test/3) + i;
2076 // ND_HexahedronElement sets dof2tk = (dofmap < 0) ? 3+c : c, but here
2077 // no signs should be applied due to ElementRestriction.
2078 const int dof2tk = c;
2079 const int id = (dofmap[d] >= 0) ? dofmap[d] : -1 - dofmap[d];
2080
2081 for (int e=0; e<ne; ++e)
2082 {
2083 real_t v[3];
2085 tr->SetIntPoint(&Nodes.IntPoint(id));
2086 tr->Jacobian().Mult(tk + dof2tk*dim, v);
2087
2088 for (int j=0; j<3; ++j)
2089 {
2090 op(j,d,e) = v[j];
2091 }
2092 }
2093 }
2094 }
2095 }
2096 else // 2D case
2097 {
2098 const real_t tk[4] = { 1.,0., 0.,1. };
2099 for (int c=0; c<2; ++c)
2100 {
2101 for (int i=0; i<ndof_test/2; ++i)
2102 {
2103 const int d = (c*ndof_test/2) + i;
2104 // ND_QuadrilateralElement sets dof2tk = (dofmap < 0) ? 2+c : c, but here
2105 // no signs should be applied due to ElementRestriction.
2106 const int dof2tk = c;
2107 const int id = (dofmap[d] >= 0) ? dofmap[d] : -1 - dofmap[d];
2108
2109 for (int e=0; e<ne; ++e)
2110 {
2111 real_t v[2];
2113 tr->SetIntPoint(&Nodes.IntPoint(id));
2114 tr->Jacobian().Mult(tk + dof2tk*dim, v);
2115
2116 for (int j=0; j<2; ++j)
2117 {
2118 op(j,d,e) = v[j];
2119 }
2120 }
2121 }
2122 }
2123 }
2124}
2125
2127{
2128 if (dim == 3)
2129 {
2130 PAHcurlVecH1IdentityApply3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->B,
2131 pa_data, x, y);
2132 }
2133 else if (dim == 2)
2134 {
2135 PAHcurlVecH1IdentityApply2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->B,
2136 pa_data, x, y);
2137 }
2138 else
2139 {
2140 mfem_error("Bad dimension!");
2141 }
2142}
2143
2145{
2146 if (dim == 3)
2147 {
2148 PAHcurlVecH1IdentityApplyTranspose3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
2149 maps_O_C->B, pa_data, x, y);
2150 }
2151 else if (dim == 2)
2152 {
2153 PAHcurlVecH1IdentityApplyTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
2154 maps_O_C->B, pa_data, x, y);
2155 }
2156 else
2157 {
2158 mfem_error("Bad dimension!");
2159 }
2160}
2161
2163 const FiniteElementSpace &ran_fes)
2164{
2165 Mesh *mesh = dom_fes.GetMesh();
2166 dim = mesh->Dimension();
2167 ne = dom_fes.GetNE();
2168 pa_mode_2d = 0;
2169 MFEM_VERIFY(ne == ran_fes.GetNE(),
2170 "Different meshes for domain and range spaces");
2171
2172 if (dim == 2)
2173 {
2174 pa_data.SetSize(0);
2175 const FiniteElement *dom_fel = dom_fes.GetTypicalFE();
2176 const FiniteElement *ran_fel = ran_fes.GetTypicalFE();
2177 const bool hcurl_to_scalar =
2178 dynamic_cast<const VectorTensorFiniteElement*>(dom_fel) != NULL &&
2179 dom_fel->GetDerivType() == FiniteElement::CURL &&
2180 dynamic_cast<const TensorBasisElement*>(ran_fel) != NULL &&
2181 ran_fel->GetRangeType() == FiniteElement::SCALAR;
2182 const bool scalar_to_hdiv =
2183 dynamic_cast<const TensorBasisElement*>(dom_fel) != NULL &&
2184 dom_fel->GetRangeType() == FiniteElement::SCALAR &&
2185 dynamic_cast<const VectorTensorFiniteElement*>(ran_fel) != NULL &&
2186 ran_fel->GetDerivType() == FiniteElement::DIV;
2187
2188 MFEM_VERIFY(hcurl_to_scalar || scalar_to_hdiv,
2189 "2D CurlInterpolator PA supports H(curl)->scalar and scalar->H(div) only.");
2190
2191 int closed_basis_type = -1;
2192 int open_basis_type = -1;
2193 if (hcurl_to_scalar)
2194 {
2195 const auto *trial_fec = dynamic_cast<const ND_FECollection*>(dom_fes.FEColl());
2196 const auto *range_fec = dynamic_cast<const L2_FECollection*>(ran_fes.FEColl());
2197 MFEM_VERIFY(trial_fec != NULL, "H(curl) domain must use ND_FECollection.");
2198 MFEM_VERIFY(range_fec != NULL, "Scalar range must use L2_FECollection.");
2199 MFEM_VERIFY(ran_fel->GetMapType() == FiniteElement::INTEGRAL,
2200 "2D H(curl)->scalar CurlInterpolator PA supports integral-map scalar range spaces only.");
2201 closed_basis_type = trial_fec->GetClosedBasisType();
2202 open_basis_type = trial_fec->GetOpenBasisType();
2203 MFEM_VERIFY(range_fec->GetBasisType() == open_basis_type,
2204 "Domain/range open basis types do not match.");
2205 pa_mode_2d = 1;
2206 }
2207 else
2208 {
2209 const auto *trial_fec = dynamic_cast<const H1_FECollection*>(dom_fes.FEColl());
2210 const auto *range_fec = dynamic_cast<const RT_FECollection*>(ran_fes.FEColl());
2211 MFEM_VERIFY(trial_fec != NULL, "Scalar domain must use H1_FECollection.");
2212 MFEM_VERIFY(range_fec != NULL, "H(div) range must use RT_FECollection.");
2213 closed_basis_type = trial_fec->GetBasisType();
2214 open_basis_type = range_fec->GetOpenBasisType();
2215 MFEM_VERIFY(range_fec->GetClosedBasisType() == closed_basis_type,
2216 "Domain/range closed basis types do not match.");
2217 pa_mode_2d = 2;
2218 }
2219
2220 const int order = hcurl_to_scalar
2221 ? dynamic_cast<const VectorTensorFiniteElement*>(dom_fel)->GetOrder()
2222 : dynamic_cast<const NodalTensorFiniteElement*>(dom_fel)->GetOrder();
2223 c_dofs1D = order + 1;
2224 o_dofs1D = order;
2225
2226 closed_dofquad_fe.reset(new H1_SegmentElement(order, closed_basis_type));
2227 open_dofquad_fe.reset(new L2_SegmentElement(order - 1, open_basis_type));
2228
2230 mfem::IntegrationRule closed_ir;
2231 closed_ir.SetSize(c_dofs1D);
2232 qf1d.GaussLobatto(c_dofs1D, &closed_ir);
2233
2234 mfem::IntegrationRule open_ir;
2235 open_ir.SetSize(o_dofs1D);
2236 qf1d.GaussLegendre(o_dofs1D, &open_ir);
2237
2238 maps_C_C = &closed_dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
2239 maps_O_C = &closed_dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
2240 maps_O_O = &open_dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
2241
2242 MFEM_VERIFY(maps_C_C->ndof == c_dofs1D && maps_C_C->nqpt == c_dofs1D, "");
2243 MFEM_VERIFY(maps_O_C->ndof == c_dofs1D && maps_O_C->nqpt == o_dofs1D, "");
2244 MFEM_VERIFY(maps_O_O->ndof == o_dofs1D && maps_O_O->nqpt == o_dofs1D, "");
2245 return;
2246 }
2247
2248 closed_dofquad_fe.reset();
2249 open_dofquad_fe.reset();
2250 maps_C_C = nullptr;
2251 maps_O_C = nullptr;
2252 maps_O_O = nullptr;
2253
2254 const VectorTensorFiniteElement *dom_el =
2255 dynamic_cast<const VectorTensorFiniteElement *>(dom_fes.GetTypicalFE());
2256 const VectorTensorFiniteElement *ran_el =
2257 dynamic_cast<const VectorTensorFiniteElement *>(ran_fes.GetTypicalFE());
2258 MFEM_VERIFY(dom_el != NULL, "Only VectorTensorFiniteElement is supported!");
2259 MFEM_VERIFY(ran_el != NULL, "Only VectorTensorFiniteElement is supported!");
2260 MFEM_VERIFY(dom_el->GetDerivType() == FiniteElement::CURL,
2261 "Domain space must be H(curl)");
2262 MFEM_VERIFY(ran_el->GetDerivType() == FiniteElement::DIV,
2263 "Range space must be H(div)");
2264
2265 const int dims = dom_el->GetDim();
2266 MFEM_VERIFY(dims == 3, "");
2267
2268 ndof_o = dom_el->GetOrder();
2269 int ndof_c = ndof_o + 1;
2270 nquad_o = ran_el->GetOrder();
2271 int nquad_c = nquad_o + 1;
2272
2273 // extract the tensor product range dof locations
2274 std::vector<real_t> qc(nquad_c);
2275 std::vector<real_t> qo(nquad_o);
2276 {
2277 const IntegrationRule &ran_nodes = ran_el->GetNodes();
2278 const Array<int> &quad_map = ran_el->GetDofMap();
2279 for (int i = 0; i < nquad_c; ++i)
2280 {
2281 int idx = UnsignIndex(quad_map[i]);
2282 qc[i] = ran_nodes.IntPoint(idx).x;
2283 }
2284 int offset = ndof_c * ndof_o * ndof_o;
2285 for (int i = 0; i < nquad_o; ++i)
2286 {
2287 int idx = UnsignIndex(quad_map[i + offset]);
2288 qo[i] = ran_nodes.IntPoint(idx).x;
2289 }
2290 }
2291
2292 // evaluate closed/open 1D basis (and their derivatives) at closed and
2293 // open quads
2294 // storage order: GCO, BCC, BOO
2295 pa_data.SetSize(ndof_c * nquad_o + ndof_c * nquad_c + ndof_o * nquad_o);
2296 auto ptr = pa_data.HostWrite();
2297 auto &cbasis1d = dom_el->GetBasis1D();
2298 auto &obasis1d = dom_el->GetOpenBasis1D();
2299 Vector b, g;
2300 b.SetSize(ndof_c);
2301 g.SetSize(ndof_c);
2302 for (int j = 0; j < nquad_o; ++j)
2303 {
2304 cbasis1d.Eval(qo[j], b, g);
2305 for (int i = 0; i < ndof_c; ++i)
2306 {
2307 ptr[j + i * nquad_o] = g[i];
2308 }
2309 }
2310 ptr += nquad_o * ndof_c;
2311
2312 for (int j = 0; j < nquad_c; ++j)
2313 {
2314 cbasis1d.Eval(qc[j], b);
2315 for (int i = 0; i < ndof_c; ++i)
2316 {
2317 ptr[j + i * nquad_c] = b[i];
2318 }
2319 }
2320 ptr += ndof_c * nquad_c;
2321
2322 b.SetSize(ndof_o);
2323 for (int j = 0; j < nquad_o; ++j)
2324 {
2325 obasis1d.Eval(qo[j], b);
2326 for (int i = 0; i < ndof_o; ++i)
2327 {
2328 ptr[j + i * nquad_o] = b[i];
2329 }
2330 }
2331}
2332
2341
2343
2345{
2346 if (dim == 2)
2347 {
2348 MFEM_VERIFY(maps_C_C != nullptr && maps_O_C != nullptr,
2349 "2D CurlInterpolator PA data is not assembled.");
2350 if (pa_mode_2d == 1)
2351 {
2352 MFEM_VERIFY(maps_O_O != nullptr,
2353 "2D CurlInterpolator scalar curl map is not assembled.");
2354 PAHcurlApplyCurl2D(c_dofs1D, o_dofs1D, ne, maps_O_O->B, maps_O_C->G,
2355 x, y);
2356 }
2357 else if (pa_mode_2d == 2)
2358 {
2359 PAHdivApplyCurl2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->G,
2360 x, y);
2361 }
2362 else
2363 {
2364 MFEM_ABORT("Unsupported 2D CurlInterpolator mode.");
2365 }
2366 return;
2367 }
2368
2369 ApplyPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x, y);
2370}
2371
2373{
2374 if (dim == 2)
2375 {
2376 MFEM_VERIFY(maps_C_C != nullptr && maps_O_C != nullptr,
2377 "2D CurlInterpolator PA data is not assembled.");
2378 if (pa_mode_2d == 1)
2379 {
2380 MFEM_VERIFY(maps_O_O != nullptr,
2381 "2D CurlInterpolator scalar curl map is not assembled.");
2382 PAHcurlApplyCurlTranspose2D(c_dofs1D, o_dofs1D, ne, maps_O_O->B,
2383 maps_O_C->G, x, y);
2384 }
2385 else if (pa_mode_2d == 2)
2386 {
2387 PAHdivApplyCurlTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
2388 maps_O_C->G, x, y);
2389 }
2390 else
2391 {
2392 MFEM_ABORT("Unsupported 2D CurlInterpolator mode.");
2393 }
2394 return;
2395 }
2396
2397 ApplyTPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x, y);
2398}
2399
2400/// \cond DO_NOT_DOCUMENT
2401
2403CurlInterpolator::ApplyPAKernels::Fallback(int DIM, int, int)
2404{
2405 if (DIM == 3)
2406 {
2407 return internal::CurlInterpolatorApply3DSmem<0, 0>;
2408 }
2409 MFEM_ABORT("Bad dimension!");
2410}
2411
2413CurlInterpolator::ApplyTPAKernels::Fallback(int DIM, int, int)
2414{
2415 if (DIM == 3)
2416 {
2417 return internal::CurlInterpolatorTApply3DSmem<0, 0>;
2418 }
2419 MFEM_ABORT("Bad dimension!");
2420}
2421
2422/// \endcond DO_NOT_DOCUMENT
2423
2424} // namespace mfem
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
void AddMultTransposePA(const Vector &x, Vector &y) const override
Method for partially assembled transposed action.
void AssemblePA(const FiniteElementSpace &dom_fes, const FiniteElementSpace &ran_fes) override
void AddMultPA(const Vector &x, Vector &y) const override
Method for partially assembled action.
static void AddSpecialization()
void(*)(const int ne, const int ndof_o, const int nquad_o, const Vector &pa, const Vector &x, Vector &y) ApplyKernelType
static MemoryType GetMemoryType()
(DEPRECATED) Equivalent to GetDeviceMemoryType().
Definition device.hpp:302
Array< real_t > G
Gradients/divergences/curls of basis functions evaluated at quadrature points.
Definition fe_base.hpp:222
@ TENSOR
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Definition fe_base.hpp:165
Array< real_t > B
Basis functions evaluated at quadrature points.
Definition fe_base.hpp:201
int ndof
Number of degrees of freedom = number of basis functions. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:186
int nqpt
Number of quadrature points. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:190
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
Abstract class for all finite elements.
Definition fe_base.hpp:294
virtual const DofToQuad & GetDofToQuad(const IntegrationRule &ir, DofToQuad::Mode mode) const
Return a DofToQuad structure corresponding to the given IntegrationRule using the given DofToQuad::Mo...
Definition fe_base.cpp:373
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition fe_base.hpp:414
int GetDerivType() const
Returns the FiniteElement::DerivType of the element describing the spatial derivative method implemen...
Definition fe_base.hpp:441
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:381
int GetMapType() const
Returns the FiniteElement::MapType of the element describing how reference functions are mapped to ph...
Definition fe_base.hpp:436
int GetRangeType() const
Returns the FiniteElement::RangeType of the element, one of {SCALAR, VECTOR}.
Definition fe_base.hpp:427
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:476
@ DIV
Implements CalcDivShape methods.
Definition fe_base.hpp:366
@ CURL
Implements CalcCurlShape methods.
Definition fe_base.hpp:367
void AddMultPA(const Vector &x, Vector &y) const override
Method for partially assembled action.
void AddMultTransposePA(const Vector &x, Vector &y) const override
Method for partially assembled transposed action.
void AssemblePA(const FiniteElementSpace &trial_fes, const FiniteElementSpace &test_fes) override
Setup method for PA data.
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
Arbitrary order H1 elements in 1D.
Definition fe_h1.hpp:23
void AddMultTransposePA(const Vector &x, Vector &y) const override
Method for partially assembled transposed action.
void AddMultPA(const Vector &x, Vector &y) const override
Method for partially assembled action.
void AssemblePA(const FiniteElementSpace &trial_fes, const FiniteElementSpace &test_fes) override
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:258
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Arbitrary order L2 elements in 1D on a segment.
Definition fe_l2.hpp:23
Mesh data type.
Definition mesh.hpp:67
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
void GetElementTransformation(int i, IsoparametricTransformation *ElTr) const
Builds the transformation defining the i-th element in ElTr. ElTr must be allocated in advance and wi...
Definition mesh.cpp:361
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:526
A Class that defines 1-D numerical quadrature rules on [0,1].
Definition intrules.hpp:382
static void GaussLegendre(const int np, IntegrationRule *ir)
Definition intrules.cpp:620
static void GaussLobatto(const int np, IntegrationRule *ir)
Definition intrules.cpp:708
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:430
const Array< int > & GetDofMap() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:1353
const Poly_1D::Basis & GetBasis1D() const
Definition fe_base.hpp:1347
const Poly_1D::Basis & GetOpenBasis1D() const
Definition fe_base.hpp:1450
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
real_t b
Definition lissajous.cpp:42
constexpr int DIM
mfem::real_t real_t
void mfem_error(const char *msg)
Definition error.cpp:154
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:138
MFEM_HOST_DEVICE int UnsignIndex(int i)
Definition globals.hpp:118
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