Compare commits
785
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea02b5bd36 | ||
|
|
ae93d82ccd | ||
|
|
4eb88fbfa5 | ||
|
|
5a90acfed6 | ||
|
|
69cea35209 | ||
|
|
026493bea7 | ||
|
|
fef354dfc5 | ||
|
|
15519823c4 | ||
|
|
127390c6a7 | ||
|
|
a62661f3cc | ||
|
|
e76ffd82e6 | ||
|
|
6288f0b741 | ||
|
|
54f8470412 | ||
|
|
5059d631ba | ||
|
|
a3d652e2c2 | ||
|
|
0de221816e | ||
|
|
cbdd94b0ee | ||
|
|
b09e705f66 | ||
|
|
d07af7ce8b | ||
|
|
f9c67d7a9e | ||
|
|
a017191010 | ||
|
|
b02893686a | ||
|
|
008ac526d1 | ||
|
|
22fe453fda | ||
|
|
4d39303ad9 | ||
|
|
0a115bf6a8 | ||
|
|
a87dadb8b3 | ||
|
|
3835a5e5e8 | ||
|
|
1ea4785ab3 | ||
|
|
a109c7e8ad | ||
|
|
cb78abf671 | ||
|
|
5e32ae87b4 | ||
|
|
1963a63075 | ||
|
|
6e36cbfb5f | ||
|
|
253f578ae4 | ||
|
|
984b269972 | ||
|
|
52ff1fdbaf | ||
|
|
ac53af01ef | ||
|
|
5f5b15b5f6 | ||
|
|
d17263e7e5 | ||
|
|
2f852723f8 | ||
|
|
e41811ad7f | ||
|
|
37034f28a6 | ||
|
|
43f8ea13dd | ||
|
|
c5363ebaaf | ||
|
|
c11d9acab8 | ||
|
|
f0525f62e3 | ||
|
|
ae478a45cd | ||
|
|
9e66156622 | ||
|
|
09df072897 | ||
|
|
0641035fb9 | ||
|
|
b5ff969e5f | ||
|
|
67b5011d50 | ||
|
|
29f26a96ea | ||
|
|
e5ae0c8b61 | ||
|
|
bd074035ae | ||
|
|
aad3a6be39 | ||
|
|
1d3ffaa0bd | ||
|
|
42774a1e21 | ||
|
|
ac2beeb4ed | ||
|
|
b44d75655e | ||
|
|
b8fcc35cbc | ||
|
|
5588024a50 | ||
|
|
48a270679c | ||
|
|
3d99a9962a | ||
|
|
c6d495ac37 | ||
|
|
9ad90f4491 | ||
|
|
8c0a137dae | ||
|
|
8315140852 | ||
|
|
3118e38912 | ||
|
|
c34ab2dd69 | ||
|
|
8a474b7365 | ||
|
|
b3e19a2e7a | ||
|
|
54b85f7a1e | ||
|
|
835b0b72cf | ||
|
|
25fa866918 | ||
|
|
ea56560951 | ||
|
|
e2e497bece | ||
|
|
d77ce29bd6 | ||
|
|
6e3a32574b | ||
|
|
e2b2df0345 | ||
|
|
a63be294bd | ||
|
|
e66f6b239f | ||
|
|
3ab6b3af2d | ||
|
|
38c199008f | ||
|
|
fa28a928e4 | ||
|
|
24365aa14d | ||
|
|
2b7360ecab | ||
|
|
c6159ef184 | ||
|
|
537112ebb8 | ||
|
|
956da4de04 | ||
|
|
6699fc9ab0 | ||
|
|
1912c74587 | ||
|
|
45747103f1 | ||
|
|
dbd8d3d7d3 | ||
|
|
bbc7658395 | ||
|
|
a1dedbb916 | ||
|
|
06234a875f | ||
|
|
2ce31f6841 | ||
|
|
6859612af9 | ||
|
|
d9d963cc88 | ||
|
|
669b6e6e49 | ||
|
|
070c9f2787 | ||
|
|
631792da83 | ||
|
|
001f6e6137 | ||
|
|
d5a0e90e11 | ||
|
|
99f88877e6 | ||
|
|
dd38d5fb80 | ||
|
|
9434bb5a8e | ||
|
|
bcbd52a318 | ||
|
|
a0fd156f64 | ||
|
|
458348a217 | ||
|
|
746ff7813b | ||
|
|
0120b8d50a | ||
|
|
91b624b9ec | ||
|
|
359387131d | ||
|
|
ad3c02a99c | ||
|
|
af8fd2bdb8 | ||
|
|
640042d6f9 | ||
|
|
a954093bad | ||
|
|
94d7d2fd0a | ||
|
|
b91255d889 | ||
|
|
4979dc1701 | ||
|
|
98834ea898 | ||
|
|
6558b84cb2 | ||
|
|
6b23a6bb26 | ||
|
|
dc7c8fb6d7 | ||
|
|
d1b85413f5 | ||
|
|
25c2975d86 | ||
|
|
53b047c906 | ||
|
|
d43343b629 | ||
|
|
157de7e776 | ||
|
|
337ad6b26d | ||
|
|
6dfb871840 | ||
|
|
5bc8702b6d | ||
|
|
4f1bcca3b1 | ||
|
|
3afd220361 | ||
|
|
bd91fb1317 | ||
|
|
a5f883bf12 | ||
|
|
8524697c43 | ||
|
|
d98920592c | ||
|
|
ae400812cf | ||
|
|
31c50b0df8 | ||
|
|
6bef88f661 | ||
|
|
565829126e | ||
|
|
589403aae2 | ||
|
|
8cc350380b | ||
|
|
979cf18e6c | ||
|
|
fce20d5b0a | ||
|
|
beb78bfddd | ||
|
|
1c6e0a6cf8 | ||
|
|
0a0117d4bc | ||
|
|
f8c68782b6 | ||
|
|
cf821ea682 | ||
|
|
fd451123c9 | ||
|
|
eb39463b86 | ||
|
|
b8da77edd5 | ||
|
|
5a5be923a3 | ||
|
|
3fcceb4aeb | ||
|
|
fc807c84d7 | ||
|
|
7abbfe3846 | ||
|
|
1634aa538e | ||
|
|
ffee08a1ac | ||
|
|
8a1640725b | ||
|
|
3b536d765c | ||
|
|
d47b17c968 | ||
|
|
f78553cc86 | ||
|
|
f3d0946c5e | ||
|
|
46f27fc8bb | ||
|
|
a2949dc8fd | ||
|
|
04e70e9c7c | ||
|
|
8b103e1215 | ||
|
|
e386c1a655 | ||
|
|
52eb4ff76e | ||
|
|
4d648556e6 | ||
|
|
29276d0b0c | ||
|
|
ade5ef80f4 | ||
|
|
03d23a3fcf | ||
|
|
0ccc9b7346 | ||
|
|
7344659f4b | ||
|
|
26f4d3927c | ||
|
|
f21d9518ac | ||
|
|
f3360ac118 | ||
|
|
a2046cc42e | ||
|
|
7caef9cb33 | ||
|
|
940943268b | ||
|
|
bbdcb2a70f | ||
|
|
0790d37990 | ||
|
|
cfe9f14900 | ||
|
|
cd0b3e23ee | ||
|
|
a2c2292ff2 | ||
|
|
7c7aed6764 | ||
|
|
1fc980db10 | ||
|
|
36204b0668 | ||
|
|
9cf6b614d5 | ||
|
|
f6e8d72851 | ||
|
|
f71553f6db | ||
|
|
ee0aa9bca7 | ||
|
|
c604c9a098 | ||
|
|
2ced1ceb84 | ||
|
|
edf2614651 | ||
|
|
36a687fcb1 | ||
|
|
3cf8d47182 | ||
|
|
6210ea712d | ||
|
|
9fd16ab65d | ||
|
|
66a543a91a | ||
|
|
cc7c996425 | ||
|
|
ac39347119 | ||
|
|
b56959d59e | ||
|
|
62323926b0 | ||
|
|
3b1e054c71 | ||
|
|
0ba5eb8422 | ||
|
|
f737d30630 | ||
|
|
b6a0ef770a | ||
|
|
70557e1f14 | ||
|
|
e58c14da9c | ||
|
|
080ee85544 | ||
|
|
780d2be2d8 | ||
|
|
b42e537101 | ||
|
|
f99c7feb53 | ||
|
|
dd43d1fa3c | ||
|
|
2635018243 | ||
|
|
daa2fe7c98 | ||
|
|
98ae71ab6d | ||
|
|
cd799dba86 | ||
|
|
00008800b8 | ||
|
|
d2cce0a4e5 | ||
|
|
6c1cd2f64b | ||
|
|
52e5d573bf | ||
|
|
e7f8bbbd85 | ||
|
|
60eb10c5d7 | ||
|
|
63b141992d | ||
|
|
3086f877c2 | ||
|
|
c35eee7742 | ||
|
|
56bd1da125 | ||
|
|
dc5e0b0c63 | ||
|
|
64688f369f | ||
|
|
e46c3929d1 | ||
|
|
fc53b03b5c | ||
|
|
b938d99486 | ||
|
|
859f15088c | ||
|
|
53c1a9de7d | ||
|
|
3dae59b3df | ||
|
|
f97b4339d8 | ||
|
|
fc63c5e90b | ||
|
|
9212cd91d3 | ||
|
|
3aed767737 | ||
|
|
0354670dab | ||
|
|
dea5c1ef15 | ||
|
|
d7f3fde747 | ||
|
|
87e9aacde3 | ||
|
|
97b2561289 | ||
|
|
98b1a68c59 | ||
|
|
cba0b592ca | ||
|
|
b5ad6241e2 | ||
|
|
06e6cdc94d | ||
|
|
c1d0e50948 | ||
|
|
4153ae8584 | ||
|
|
e024892d8e | ||
|
|
c633d5bf0e | ||
|
|
83efa2e336 | ||
|
|
416ed22555 | ||
|
|
88be18f03c | ||
|
|
b2a50919b9 | ||
|
|
edd8bd98f0 | ||
|
|
add0e99d9d | ||
|
|
67850da1f7 | ||
|
|
57e99ba620 | ||
|
|
8bb331cefa | ||
|
|
92fa66c5d4 | ||
|
|
f671f7c453 | ||
|
|
a9ebb665f7 | ||
|
|
051fc969ab | ||
|
|
253ebf4e2f | ||
|
|
5a65c4ee58 | ||
|
|
acb01b6e29 | ||
|
|
52c1a3c0b8 | ||
|
|
b360a4e68b | ||
|
|
83f553f4a9 | ||
|
|
5f4d49691b | ||
|
|
632c6e34e1 | ||
|
|
012036d007 | ||
|
|
a7676c6443 | ||
|
|
bcde2ec4f1 | ||
|
|
19507830f0 | ||
|
|
777efb0582 | ||
|
|
8f42d760fc | ||
|
|
cdbc8c710c | ||
|
|
76525c8ce3 | ||
|
|
f6d100b119 | ||
|
|
f7a5a79d34 | ||
|
|
63022c131a | ||
|
|
53365490b7 | ||
|
|
c75c5f2ea8 | ||
|
|
c97450809d | ||
|
|
34ea1d5b07 | ||
|
|
d1f560d652 | ||
|
|
cbe0582fe3 | ||
|
|
7fe4c3c199 | ||
|
|
779c17c86d | ||
|
|
8048e1d433 | ||
|
|
ced77961a5 | ||
|
|
68eb708311 | ||
|
|
5144997003 | ||
|
|
c322d7cbb0 | ||
|
|
baa2c246f9 | ||
|
|
1a32b51338 | ||
|
|
ca1915983d | ||
|
|
d1cbdf0766 | ||
|
|
ba63e32ded | ||
|
|
82e260ab3a | ||
|
|
337dd7c274 | ||
|
|
6f3ecfe129 | ||
|
|
7514594c32 | ||
|
|
ccdaa48f69 | ||
|
|
5b3cbad98c | ||
|
|
490b60c272 | ||
|
|
3b5c10c1b3 | ||
|
|
9aaa7f13a3 | ||
|
|
71f45d285c | ||
|
|
8c1dd9faca | ||
|
|
4fea4ee7f6 | ||
|
|
07605d3097 | ||
|
|
02aed483e6 | ||
|
|
facb0b5434 | ||
|
|
cb32b5ee8e | ||
|
|
987203005d | ||
|
|
cd50b6b806 | ||
|
|
74ef6490a7 | ||
|
|
f963df773c | ||
|
|
31786ff0e0 | ||
|
|
7275a2fbab | ||
|
|
b13fb27f7e | ||
|
|
e55d0882c3 | ||
|
|
4d21ab6a4d | ||
|
|
34fb676fd5 | ||
|
|
a55d495f1f | ||
|
|
f6efa8ea54 | ||
|
|
f0c0994a4e | ||
|
|
e192397308 | ||
|
|
f519d59644 | ||
|
|
3c17ed88c5 | ||
|
|
159f8a1404 | ||
|
|
0a754a78b9 | ||
|
|
3cf27e9243 | ||
|
|
a3cf3f4bb2 | ||
|
|
35f508ca8c | ||
|
|
a9f4652335 | ||
|
|
a2de8287c7 | ||
|
|
acea00376b | ||
|
|
13fed2e691 | ||
|
|
1332ba5ee7 | ||
|
|
a252286472 | ||
|
|
8bab223de4 | ||
|
|
96392055d6 | ||
|
|
36f1b769c5 | ||
|
|
3e074b3c7e | ||
|
|
d8773cad73 | ||
|
|
773606139c | ||
|
|
b1f7cb2766 | ||
|
|
df47dd65c1 | ||
|
|
af30acae94 | ||
|
|
c102d5ecf6 | ||
|
|
b1cf2dfe9a | ||
|
|
c0479e88fc | ||
|
|
e797278779 | ||
|
|
422a752600 | ||
|
|
2ab1ae4ff6 | ||
|
|
b5826ff9d5 | ||
|
|
a7dcc17d63 | ||
|
|
f5b73dbaeb | ||
|
|
df3be0125c | ||
|
|
10c6c8ad22 | ||
|
|
4c62788e60 | ||
|
|
ab36b3caae | ||
|
|
be659ae4b6 | ||
|
|
5ceda33e57 | ||
|
|
2189509521 | ||
|
|
2ceee164c5 | ||
|
|
1f3790ba2c | ||
|
|
38aaa74b52 | ||
|
|
f6e0903d60 | ||
|
|
a077bb3a43 | ||
|
|
828dee43f6 | ||
|
|
907213b3aa | ||
|
|
3eae5645ae | ||
|
|
2f6c1d9b6a | ||
|
|
7717cc5e6c | ||
|
|
a6da18db40 | ||
|
|
4c491ae4a7 | ||
|
|
142528c55d | ||
|
|
21590c1e60 | ||
|
|
c07d124c4c | ||
|
|
cf69527e0a | ||
|
|
945b88c1a8 | ||
|
|
e186ca4e30 | ||
|
|
add7281658 | ||
|
|
98c6b0e32f | ||
|
|
506c34719a | ||
|
|
2a035e92d2 | ||
|
|
eccb30bdfe | ||
|
|
160a52a55e | ||
|
|
83440de8cd | ||
|
|
26ba53a52e | ||
|
|
2cde33b8dd | ||
|
|
e2815c1864 | ||
|
|
0f1880a9da | ||
|
|
25cfc0991d | ||
|
|
cbd035e06d | ||
|
|
242703e266 | ||
|
|
7ced559b0d | ||
|
|
06d1cd025f | ||
|
|
40f8216355 | ||
|
|
fc8ee486b6 | ||
|
|
3dae2a01dc | ||
|
|
3b5068ca79 | ||
|
|
5b145be17d | ||
|
|
76f382eba6 | ||
|
|
6f9cbed618 | ||
|
|
aab40b23d2 | ||
|
|
b211337360 | ||
|
|
6c40c5b643 | ||
|
|
1aa9e65cfb | ||
|
|
e05523f36e | ||
|
|
090cdf3135 | ||
|
|
dba53a19a2 | ||
|
|
7295d1414f | ||
|
|
de1d7406ed | ||
|
|
6c7679f7c4 | ||
|
|
9139e09a30 | ||
|
|
c2bde81c14 | ||
|
|
c6d30c5c82 | ||
|
|
47f6ca5693 | ||
|
|
3f30c5b970 | ||
|
|
80df37426f | ||
|
|
36be48efa9 | ||
|
|
d2a94aa354 | ||
|
|
bb44da48dd | ||
|
|
6bec321019 | ||
|
|
62def1c528 | ||
|
|
faf44555ba | ||
|
|
7d74666511 | ||
|
|
8046bb2cd6 | ||
|
|
030e4b00d1 | ||
|
|
6c2269d817 | ||
|
|
d0b1f3f816 | ||
|
|
be444d4b38 | ||
|
|
e62696f749 | ||
|
|
12d1dcb155 | ||
|
|
07d59a1843 | ||
|
|
ec5ad279e7 | ||
|
|
522dc28e5a | ||
|
|
1c47f03aba | ||
|
|
6fd0e8e50a | ||
|
|
841df1a9bf | ||
|
|
0b49ea9594 | ||
|
|
fba17c6579 | ||
|
|
789f5d2e49 | ||
|
|
07b543000f | ||
|
|
f89aba61ca | ||
|
|
8cae2c313e | ||
|
|
888651e796 | ||
|
|
def1a9938d | ||
|
|
7b12197e94 | ||
|
|
e50c543dde | ||
|
|
4317659edc | ||
|
|
29b78f70e9 | ||
|
|
ae9ee68c82 | ||
|
|
7c417d7f47 | ||
|
|
f22201a855 | ||
|
|
4d5426a314 | ||
|
|
6baa15afba | ||
|
|
b936a27147 | ||
|
|
05fef0a0d2 | ||
|
|
2927be94bf | ||
|
|
f181ceb58d | ||
|
|
e160e5cfaf | ||
|
|
5b4ae30871 | ||
|
|
862eb07599 | ||
|
|
b4c814efd4 | ||
|
|
8a15099f67 | ||
|
|
5272fbf7fa | ||
|
|
10de3a3f34 | ||
|
|
bd49a2d320 | ||
|
|
a0d509fed6 | ||
|
|
127867dc00 | ||
|
|
5e2bac632f | ||
|
|
cba6871889 | ||
|
|
cf0ad68faa | ||
|
|
ef386590ad | ||
|
|
e199da273e | ||
|
|
5b928c919e | ||
|
|
8464b3d1e7 | ||
|
|
40f20d1da5 | ||
|
|
7e1bccc757 | ||
|
|
89b7c48cf7 | ||
|
|
351afd3fbb | ||
|
|
68abff7ce2 | ||
|
|
4dc9708669 | ||
|
|
a2f32603f8 | ||
|
|
8532980ef8 | ||
|
|
c7f632e548 | ||
|
|
c99bd6515e | ||
|
|
c659a98754 | ||
|
|
05ef2ea97d | ||
|
|
350e235920 | ||
|
|
a79aaeac07 | ||
|
|
8ab27a16cc | ||
|
|
04691da19a | ||
|
|
89fa1e05b2 | ||
|
|
0685ed9e49 | ||
|
|
00de67dd3a | ||
|
|
c8d39a3090 | ||
|
|
2e4bb12589 | ||
|
|
0a683fdcfd | ||
|
|
7e1d57f8b1 | ||
|
|
38abb9bee4 | ||
|
|
455d279c3d | ||
|
|
e0569296e7 | ||
|
|
0b6881390f | ||
|
|
3655d08ca3 | ||
|
|
35041601d4 | ||
|
|
4caffa2ebe | ||
|
|
0bea92acf0 | ||
|
|
0ba784d08f | ||
|
|
8b41801be5 | ||
|
|
c622f40713 | ||
|
|
d79e2af2e3 | ||
|
|
7b5a0a42d1 | ||
|
|
f89f0eaad4 | ||
|
|
2758154175 | ||
|
|
0aec7595bd | ||
|
|
c5064e1884 | ||
|
|
ce48acbf30 | ||
|
|
25d80486e0 | ||
|
|
9a1baddd61 | ||
|
|
01eab3864d | ||
|
|
455c52f520 | ||
|
|
95e00dec12 | ||
|
|
e27726337c | ||
|
|
0b82b5b4cb | ||
|
|
a501fd9571 | ||
|
|
6933b983a6 | ||
|
|
ab024fa6b1 | ||
|
|
b0efc18e0f | ||
|
|
4d40cc2152 | ||
|
|
3d9d7a9915 | ||
|
|
e19a00f18a | ||
|
|
9548a87a66 | ||
|
|
a589ae90b9 | ||
|
|
47bbba2f5c | ||
|
|
3553f91f44 | ||
|
|
45f32fb9c6 | ||
|
|
41807f5444 | ||
|
|
659559d43b | ||
|
|
c00f044824 | ||
|
|
238cd67d5e | ||
|
|
96d9068077 | ||
|
|
e91f9b6e0c | ||
|
|
81f990cc8e | ||
|
|
70fca5a453 | ||
|
|
00a7f10b95 | ||
|
|
a4c097f47c | ||
|
|
7198ad8872 | ||
|
|
52b00a08a9 | ||
|
|
9a6048658b | ||
|
|
d16dae3366 | ||
|
|
ee84d9e365 | ||
|
|
a0d2d4ab3d | ||
|
|
0a08801004 | ||
|
|
4af565819e | ||
|
|
bcd5ef92c6 | ||
|
|
bb271475ea | ||
|
|
97f8b1b16b | ||
|
|
488e93ad74 | ||
|
|
d448fd0811 | ||
|
|
c0fee4482a | ||
|
|
098bb72f34 | ||
|
|
51961fe66d | ||
|
|
14011b604f | ||
|
|
c9233a2045 | ||
|
|
ce9a500498 | ||
|
|
6edf26214c | ||
|
|
b008b0b8da | ||
|
|
8b8e7b47c8 | ||
|
|
1ef48cd785 | ||
|
|
a8c681b67c | ||
|
|
a2002aa05b | ||
|
|
59ef835780 | ||
|
|
857f10f6a4 | ||
|
|
a68de24413 | ||
|
|
177fee04e5 | ||
|
|
5be94b463d | ||
|
|
045cbad46f | ||
|
|
af482bdbcc | ||
|
|
c1e587a504 | ||
|
|
2aad85ca57 | ||
|
|
c505933d7f | ||
|
|
a96222708d | ||
|
|
0addaa7342 | ||
|
|
5218b40945 | ||
|
|
5b5af88f32 | ||
|
|
51037a7724 | ||
|
|
e325b07545 | ||
|
|
6083094f16 | ||
|
|
bb07ec79f4 | ||
|
|
33157ddb60 | ||
|
|
2a4e4b7d5a | ||
|
|
d160b52f49 | ||
|
|
dd153bcf7a | ||
|
|
fb4659bc32 | ||
|
|
3344049b13 | ||
|
|
4a49b90052 | ||
|
|
18af2042c7 | ||
|
|
6be8650f71 | ||
|
|
4f9686bd8b | ||
|
|
9b54f750c9 | ||
|
|
fa552415d3 | ||
|
|
5ced2e61ff | ||
|
|
e954950e6d | ||
|
|
7e589d7ee8 | ||
|
|
94ae58fa83 | ||
|
|
69a0b82c89 | ||
|
|
6d297c8ec0 | ||
|
|
d00b172172 | ||
|
|
6b802a0ec0 | ||
|
|
31f4f5b2bb | ||
|
|
0e135ff1d0 | ||
|
|
2b95924226 | ||
|
|
9ff6c53ace | ||
|
|
e3b013c3e8 | ||
|
|
2b8ef63bc1 | ||
|
|
67e3b289b6 | ||
|
|
8d1b7f061a | ||
|
|
6f9c761bfa | ||
|
|
0ce2e7551c | ||
|
|
c07e081832 | ||
|
|
528bd5a5a3 | ||
|
|
a994abd7d3 | ||
|
|
4566dc299d | ||
|
|
352403bfc5 | ||
|
|
0c10a41589 | ||
|
|
ddd54fc459 | ||
|
|
5ef5b333e5 | ||
|
|
cd5feebfe5 | ||
|
|
4c1e6e91f2 | ||
|
|
fcba41be04 | ||
|
|
82e55c8aea | ||
|
|
9d1f84d81a | ||
|
|
befd4656fc | ||
|
|
81cd6e8fe6 | ||
|
|
347d0e6384 | ||
|
|
eafeb15cd0 | ||
|
|
9178cb687a | ||
|
|
83a34a3e77 | ||
|
|
95705c8121 | ||
|
|
09222e3313 | ||
|
|
ebbf706a55 | ||
|
|
f1f683061d | ||
|
|
26f0dad6fa | ||
|
|
808b83c282 | ||
|
|
ef9d32f1a4 | ||
|
|
9130cb06e1 | ||
|
|
90bc48532f | ||
|
|
21360beede | ||
|
|
e878db0fee | ||
|
|
ce386c1409 | ||
|
|
06f892e457 | ||
|
|
2f894ef8fa | ||
|
|
e63ba7dd04 | ||
|
|
232642b5d3 | ||
|
|
c668bb6fa2 | ||
|
|
ea655797da | ||
|
|
b309b6d789 | ||
|
|
6956e10323 | ||
|
|
e8dc1eb31c | ||
|
|
2f4f06d549 | ||
|
|
e9352448f1 | ||
|
|
6db2519021 | ||
|
|
230d8830c5 | ||
|
|
3373ee5c34 | ||
|
|
1ecdd8e7b0 | ||
|
|
8f0398d330 | ||
|
|
d208f6a951 | ||
|
|
29662bf5c6 | ||
|
|
59ccdced1a | ||
|
|
fb149d7d12 | ||
|
|
15ff1a42cd | ||
|
|
09fcd3ecca | ||
|
|
35857b5379 | ||
|
|
1450a58374 | ||
|
|
d398bac010 | ||
|
|
c5be56264c | ||
|
|
d3c669d482 | ||
|
|
270f83bd35 | ||
|
|
e327a173da | ||
|
|
e6c48e3987 | ||
|
|
c9ad580d33 | ||
|
|
040c337ce7 | ||
|
|
1d46fbba57 | ||
|
|
4804824873 | ||
|
|
1a2b8291f5 | ||
|
|
8f3eafd251 | ||
|
|
3ec7ae873d | ||
|
|
1acaa833b6 | ||
|
|
a07f893b03 | ||
|
|
f0f1885090 | ||
|
|
604cb61b00 | ||
|
|
ff6546dd8f | ||
|
|
10c9adf8c8 | ||
|
|
786caf20c3 | ||
|
|
4430df415a | ||
|
|
5acdaf28a5 | ||
|
|
b482ce2880 | ||
|
|
5ab2c0b6e3 | ||
|
|
ce900ec687 | ||
|
|
19df0ed57d | ||
|
|
a5cd125342 | ||
|
|
220e76ec83 | ||
|
|
c5af057556 | ||
|
|
60f281ddad | ||
|
|
2408687812 | ||
|
|
f054912423 | ||
|
|
dca045d48a | ||
|
|
c35ca25dfa | ||
|
|
cea73f8a02 | ||
|
|
bf4f411d9c | ||
|
|
a50ef7f774 | ||
|
|
a897af7764 | ||
|
|
e6ce209f64 | ||
|
|
3c40606626 | ||
|
|
23f808fa9d | ||
|
|
54cb4f7ce0 | ||
|
|
576c5d26d6 | ||
|
|
c3c983d1e3 | ||
|
|
c97a82b14c | ||
|
|
44d2e66858 | ||
|
|
53728d12d9 | ||
|
|
e8a356baf8 | ||
|
|
482854800a | ||
|
|
d417a515d2 | ||
|
|
b39cfbfaa3 | ||
|
|
cb5d07181f | ||
|
|
920099da20 | ||
|
|
004fb5757d | ||
|
|
0783faddd4 | ||
|
|
9123848ff4 | ||
|
|
bfffaa3171 | ||
|
|
a3e894ec50 | ||
|
|
d99c508a68 | ||
|
|
053c71a885 | ||
|
|
59c44959b3 | ||
|
|
1beab25885 | ||
|
|
3835b45427 | ||
|
|
7af8a82cc2 | ||
|
|
3376d6d744 | ||
|
|
93c87515ab | ||
|
|
3769f8d788 | ||
|
|
e62a12af26 | ||
|
|
e9e0cd2af3 | ||
|
|
c285a77bf1 | ||
|
|
de179042a2 | ||
|
|
6f5396fe11 | ||
|
|
2b12687eb6 | ||
|
|
777ad30e16 | ||
|
|
e25c40abaa | ||
|
|
97f794057e | ||
|
|
de171f2751 | ||
|
|
183569f3b7 | ||
|
|
c428ad7ae1 | ||
|
|
50eef85cd5 | ||
|
|
7792ced114 | ||
|
|
4109c06129 | ||
|
|
bdb1afeb74 | ||
|
|
fc556b7c08 | ||
|
|
06bfe53f55 | ||
|
|
a38bda120c | ||
|
|
0a99b6fa99 | ||
|
|
84fd3097a7 | ||
|
|
ac6efafd94 | ||
|
|
7bf66519a4 | ||
|
|
f8c2878969 | ||
|
|
197698dc02 | ||
|
|
2e37747182 | ||
|
|
c57e3b7d50 |
@@ -114,6 +114,13 @@ jobs:
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp32
|
||||
- os: macos-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp32
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -154,7 +161,8 @@ jobs:
|
||||
- name: get MPI (Linux)
|
||||
if: matrix.mpi == 'par' && matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get install mpich libmpich-dev
|
||||
sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
export OMPI_MCA_rmaps_base_oversubscribe=1
|
||||
|
||||
- name: get lcov (Linux)
|
||||
if: matrix.codecov == 'YES' && matrix.os == 'ubuntu-latest'
|
||||
@@ -191,7 +199,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
|
||||
|
||||
@@ -80,6 +80,7 @@ examples/sol_p.*
|
||||
examples/sol_r.*
|
||||
examples/sol_i.*
|
||||
examples/ex6p-checkpoint.*
|
||||
examples/order.*
|
||||
examples/ex9.mesh
|
||||
examples/ex9-mesh.*
|
||||
examples/ex9-init.*
|
||||
@@ -235,6 +236,8 @@ miniapps/meshing/minimal-surface
|
||||
miniapps/meshing/pminimal-surface
|
||||
miniapps/meshing/polar-nc
|
||||
miniapps/meshing/mesh-quality
|
||||
miniapps/meshing/hpref
|
||||
miniapps/meshing/phpref
|
||||
miniapps/meshing/mobius-strip.mesh
|
||||
miniapps/meshing/klein-bottle.mesh
|
||||
miniapps/meshing/toroid-*.mesh
|
||||
@@ -252,6 +255,10 @@ miniapps/meshing/sol.gf
|
||||
miniapps/meshing/optimized*
|
||||
miniapps/meshing/perturbed*
|
||||
miniapps/meshing/polar-nc.mesh
|
||||
miniapps/meshing/mesh.*
|
||||
miniapps/meshing/order.*
|
||||
miniapps/meshing/sol.*
|
||||
miniapps/meshing/refined.mesh
|
||||
|
||||
miniapps/mtop/parheat
|
||||
miniapps/mtop/ParHeat*
|
||||
|
||||
@@ -8,159 +8,218 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.7.1 (development)
|
||||
Version 4.8.1 (development)
|
||||
===========================
|
||||
- Refactored ALGOIM cut integration rules. The interface is unified with
|
||||
the interface for moment based cut integration rules.
|
||||
- Altered (Par)GridFunction::Compute*Error functions to ensure they return
|
||||
non-negative values and therefore behave as "norms".
|
||||
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Only on single
|
||||
patch meshes. Only implemented for serial computations.
|
||||
- Added high-order basis functions on pyramid-shaped elements for all spaces in
|
||||
the de Rham complex based on the paper "Orientation embedded high order shape
|
||||
functions for the exact sequence elements of all shapes" by Fuentes, Keith,
|
||||
Demkowicz and Nagaraj (doi.org/10.1016/j.camwa.2015.04.027). Positive basis
|
||||
functions (Bernstein basis) for H1 and L2 on pyramids were also added.
|
||||
|
||||
- Added support for boundary constraints to the hybridization class.
|
||||
- Added support for parallel p- and hp-refinement on quad/hex meshes. For hp, we
|
||||
currently support only isotropic refinement with L2 or H1 spaces. See the new
|
||||
miniapps hpref and phpref in the miniapps/meshing/ directory.
|
||||
|
||||
- Added support for external boundary submeshes with nonconformal mesh adaptation.
|
||||
- Added several improvements for hyperbolic problems:
|
||||
* Assembly of Jacobians in HyperbolicFormIntegrator
|
||||
* Component-wise upwinded flux (ComponentwiseUpwindFlux)
|
||||
* Average fluxes in NumericalFlux (formerly RiemannSolver) and FluxFunction
|
||||
|
||||
- Added assembly of Jacobians to `HyperbolicFormIntegrator`.
|
||||
- Added convenience methods to class FiniteElementSpace to directly identify all
|
||||
degrees of freedom on the exterior faces of the domain, without referencing
|
||||
boundary element attributes (GetExteriorVDofs and GetExteriorTrueDofs).
|
||||
|
||||
- Added average fluxes to `NumericalFlux` (formerly `RiemannSolver`)
|
||||
and `FluxFunction`.
|
||||
- Altered (Par)GridFunction::Compute*Error functions to ensure they return
|
||||
non-negative values and therefore behave as "norms".
|
||||
|
||||
- Added component-wise upwinded flux (`ComponentwiseUpwindFlux`).
|
||||
- SubMesh and ParSubMesh have been extended to work on nonconforming meshes.
|
||||
Extracting volume and exterior surface submeshes are both supported.
|
||||
|
||||
- Added support for H(div) spaces in class QuadratureInterpolator. For now, only
|
||||
- LinearFormIntegrator, BilinearFormIntegrator and NonlinearFormIntegrator now
|
||||
inherit from a base class Integrator that centralizes the logic for selecting
|
||||
quadrature rules. This includes a virtual method GetDefaultIntegrationRule,
|
||||
which should be favored over directly defining a default integration rule in
|
||||
the element-level assembly routines. The latter is still possible, by leaving
|
||||
the new virtual method as its default base implementation of returning NULL.
|
||||
|
||||
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Currently only for
|
||||
single patch meshes in serial.
|
||||
|
||||
- Refactored ALGOIM cut integration rules. The interface is unified with the
|
||||
interface for moment based cut integration rules.
|
||||
|
||||
- FiniteElementSpace has new methods to directly set the prolongation and
|
||||
restriction operators to user-specified sparse matrices.
|
||||
|
||||
- Added support for H(div) spaces in class QuadratureInterpolator. Currently only
|
||||
(vector) VALUES, (vector) PHYSICAL_VALUES, and PHYSICAL_MAGNITUDES evaluations
|
||||
are implemented. [PR #4669]
|
||||
are implemented.
|
||||
|
||||
- Added support for boundary constraints to class Hybridization.
|
||||
|
||||
- Added support for custom interpolation procedure in FindPointsGSLIB.
|
||||
|
||||
- Added GSLIB-based gather-scatter operator.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added native AD support for numerous TMOP metrics that didn't have first or
|
||||
second derivative implementations.
|
||||
- Added support for nonuniform anisotropic mesh refinement on serial quad/hex
|
||||
meshes with arbitrary spacing in each direction. This enables in particular
|
||||
3:1 refinement, as demonstrated in the new meshing miniapp ref321.
|
||||
|
||||
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
|
||||
are also supported.
|
||||
- Added capabilities for optimization and adaptation of periodic meshes with
|
||||
TMOP. The internals of TMOP were modified so that the optimization problem is
|
||||
always solved with respect to mesh displacements.
|
||||
|
||||
- Added support for nonuniform anisotropic (nonconforming) mesh refinement with
|
||||
arbitrary spacing in each direction, for quadrilateral (2D) and hexahedral
|
||||
(3D) meshes. This enables in particular 3:1 refinement, as demonstrated in the
|
||||
new meshing miniapp ref321.
|
||||
- New convenience methods for manipulating boundary attribute markers in class
|
||||
Mesh: MarkExternalBoundaries, MarkNamedBoundaries, UnmarkInternalBoundaries
|
||||
and UnmarkNamedBoundaries. See Examples 1/1p and 11p for basic usage.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
|
||||
- Added a new method, GetExteriorFaceMarker, to the serial and parallel mesh
|
||||
classes for identifying faces on the exterior of the mesh irrespective of
|
||||
their presence in the list of "boundary elements".
|
||||
|
||||
- Added an MFEM example for the eikonal equation. This new solver is based on
|
||||
the proximal Galerkin method introduced by Keith and Surowiec.
|
||||
- Added native AD support for computing the derivatives of numerous TMOP metrics
|
||||
that didn't have first or second derivative implementations.
|
||||
|
||||
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
|
||||
See miniapps/toys/spiral.cpp.
|
||||
- Added ExodusII output capability which can handle in particular pyramid and
|
||||
wedge element types. Mixed meshes are also supported.
|
||||
|
||||
- Added a command line option to all miniapps (`-p` or `--send-port`) for
|
||||
specifying the GLVis server socket port (19916 by default).
|
||||
- Added InverseElementTransformation::InitGuessType::EdgeScan as an alternative
|
||||
initial guess type. This guess type tries solving with multiple initial guesses
|
||||
along the r/s/t=0 edges of the element until a valid solution is found or all
|
||||
initial guess points are exhausted.
|
||||
|
||||
- Added new convenience constructors for NURBS patches and knot vectors.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Extended FindPointsGSLIB to support general field interpolation on GPUs. Note
|
||||
that this requires that switch from gslib v1.0.7 to v1.0.9.
|
||||
|
||||
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
|
||||
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
|
||||
|
||||
- A new GPU kernel dispatch mechanism was introduced. Users can instantiate
|
||||
specialized kernels for specific combinations of (for example) polynomial
|
||||
degree and number of quadrature points using
|
||||
`DiffusionIntegrator::AddSpecialization` and
|
||||
`MassIntegrator::AddSpecialization` (this functionality may be added to more
|
||||
integrators in the future).
|
||||
degree and number of quadrature points using method AddSpecialization in
|
||||
classes DiffusionIntegrator and MassIntegrator (this functionality may be
|
||||
added to more integrators in the future).
|
||||
|
||||
- Calls to slower fallback kernels can be reported to `mfem::err` by setting
|
||||
the environment variable `MFEM_REPORT_KERNELS` to any value other than `NO`
|
||||
or by explicitly calling `KernelReporter::Enable`. Users can then add
|
||||
specializations for these kernels to achieve higher performance.
|
||||
- Added BatchInverseElementTransformation to batch InverseElementTransformation
|
||||
searches. Batch searches are currently limited to meshes with a single element
|
||||
geometry type of SEGMENT, SQUARE, or CUBE. Additional element geometry types
|
||||
may be added in the future. Mixed element order meshes are supported. Batch
|
||||
searches can be performed on the CPU (serial per MPI rank) or GPU. Embedded
|
||||
elements (SEGMENT in 2D/3D space or SQUARE in 3D space) are supported, however
|
||||
the existing solvers may struggle to find a valid solution.
|
||||
|
||||
- Element assembly kernels have been added for low-order refined to
|
||||
high-order transfer operators. New kernels can be offloaded as device
|
||||
kernels. Example usage may be found in lor-transfer.cpp under miniapps/tools.
|
||||
- Calls to slower fallback kernels can be reported to mfem::err by setting the
|
||||
environment variable MFEM_REPORT_KERNELS to any value other than NO or by
|
||||
explicitly calling KernelReporter::Enable. For higher performance, users can
|
||||
then add specializations for these kernels.
|
||||
|
||||
- Added support for GPU accelerated FindPointsGSLIB. Note that this will require
|
||||
the users to switch from gslib v1.0.7 to v1.0.9.
|
||||
- Element assembly kernels have been added for low-order refined -> high-order
|
||||
transfer operators. New kernels can be offloaded as device kernels. Example
|
||||
usage may be found in lor-transfer.cpp in the miniapps/tools/ directory.
|
||||
|
||||
- Added GPU acceleration and element assembly for DivDivIntegrator and
|
||||
NormalTraceJumpIntegrator.
|
||||
|
||||
- Allow BlockLowerTriangularPreconditioner to run on GPU.
|
||||
|
||||
- Use device vectors in GMRES, FGMRES and other iterative methods.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added GPU acceleration of the algebraic hybridization solver for grad-div
|
||||
problems in H(div). See Example 4.
|
||||
|
||||
- Added a self-contained implementation of the Method of Moving Asymptotes (MMA)
|
||||
for solving optimization problems.
|
||||
|
||||
- Added support for SUNDIALS v7. See the section "API changes" for some small
|
||||
changes related to this new version.
|
||||
|
||||
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
|
||||
`TimeDependentOperator::Mult` only when the associated ODE operator is
|
||||
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
|
||||
otherwise `TimeDependentOperator::ExplicitMult` is used. A check has been
|
||||
added to `ARKStepSolver` to verify that the associated ODE operator is not in
|
||||
explicit form when a mass matrix solver is enabled via a call to either the
|
||||
`UseMFEMMassLinearSolver` or `UseSundialsMassLinearSolver` methods. This is
|
||||
because enabling a mass matrix solver assumes that F(u,k,t) = M k in the
|
||||
associated ODE operator.
|
||||
- Changed the name of class IterativeSolverMonitor to IterativeSolverController,
|
||||
which now allows for specifying convergence by a user defined criterion. For
|
||||
backward compatibility, the old name is still available.
|
||||
|
||||
- Added support for custom interpolation procedure in FindPointsGSLIB.
|
||||
- Refactored the ARKStepSolver class (ARKODE interface) to use the Mult() method
|
||||
of TimeDependentOperator only when the associated ODE operator is expressed in
|
||||
explicit form (i.e., TimeDependentOperator::isExplicit()), otherwise the
|
||||
method ExplicitMult() is used. A check has been added to ARKStepSolver to
|
||||
verify that the associated ODE operator is not in explicit form when a mass
|
||||
matrix solver is enabled via a call to either the UseMFEMMassLinearSolver or
|
||||
UseSundialsMassLinearSolver methods. This is because enabling a mass matrix
|
||||
solver assumes that F(u,k,t) = M k in the associated ODE operator.
|
||||
|
||||
- `FiniteElementSpace` has new methods to directly set prolongation and
|
||||
restriction operators to arbitrary sparse matrices.
|
||||
- Added ODE solvers selection routines. This creates a uniformity across
|
||||
examples, miniapps and other executables in regard to ODE (time-integrator)
|
||||
selection.
|
||||
|
||||
- There are new convenience constructors for NURBS patches and knot vectors.
|
||||
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
|
||||
This is relevant for AB/AM and gen-alpha solvers.
|
||||
|
||||
- Added convenience methods for manipulating boundary attribute marker arrays;
|
||||
`(Par)Mesh::MarkExternalBoundaries`, `(Par)Mesh::UnmarkInternalBoundaries`,
|
||||
`(Par)Mesh::MarkNamedBoundaries`, and `(Par)Mesh::UnmarkNamedBoundaries`.
|
||||
See examples `ex1.cpp`, `ex1p.cpp`, and `ex11p.cpp` for basic usage.
|
||||
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
|
||||
read/write functionality.
|
||||
|
||||
- Added `(Par)Mesh::GetExteriorFaceMarker` for identifying faces on the
|
||||
exterior of the mesh irrespective of their presence in the list of "boundary
|
||||
elements".
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added an MFEM example for the eikonal equation (examples/ex40) based on the
|
||||
proximal Galerkin method introduced by Keith and Surowiec.
|
||||
|
||||
- Added methods to `(Par)FiniteElementSpace` to identify all degrees of freedom
|
||||
located on the exterior of the domain without reference to the list of
|
||||
"boundary elements"; `GetExteriorVDofs` and `GetExteriorTrueDofs`.
|
||||
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
|
||||
|
||||
- `LinearFormIntegrator` and `NonlinearFormIntegrator` (including
|
||||
`BilinearFormIntegrator`) now all inherit from a base class `Integrator`
|
||||
that combines some logic related to selecting quadrature rules. This includes
|
||||
a virtual method `Integrator::GetDefaultIntegrationRule`, which should be
|
||||
favored over directly defining a default integration rule in the element-level
|
||||
assembly routines (although the latter is still possible, by leaving the new
|
||||
virtual method as its default base implementation of returning `NULL`).
|
||||
- Fixed element visualization in the Mesh explorer miniapp.
|
||||
|
||||
- Added a command line option to all miniapps (-p or --send-port) for
|
||||
specifying the GLVis server socket port (19916 by default).
|
||||
|
||||
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
|
||||
See miniapps/toys/spiral.cpp.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Updated the benchmarks (in tests/benchmarks) to work with the latest Google
|
||||
Benchmarks classes (version 1.9.1). Renamed the MFEM_ENABLE_GOOGLE_BENCHMARKS
|
||||
CMake option to just MFEM_ENABLE_BENCHMARKS.
|
||||
|
||||
- Changed the name of `IterativeSolverMonitor` to `IterativeSolverController`
|
||||
which now allows for declaring convergence by a user defined criterion. For
|
||||
backward compatibility, the old name is still available.
|
||||
- Updated the minimum CMake version requirements:
|
||||
* CMake >= 3.12 for CPU builds,
|
||||
* CMake >= 3.17 for CUDA builds, and
|
||||
* CMake >= 3.14 for HIP builds (CMake >= 3.12 may work as well, not tested).
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- API change: 'TMOP_Metric_skew2D' has been marked as deprecated.
|
||||
- In class GridFunction, fec was renamed to fec_owned.
|
||||
|
||||
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
|
||||
- RiemannSolver was renamed to NumericalFlux (the old name has been deprecated
|
||||
through typedef).
|
||||
|
||||
- API change: `RiemannSolver` was renamed to `NumericalFlux` (the old name has
|
||||
been been deprecated through typedef)
|
||||
|
||||
- API change: support for SUNDIALS v7:
|
||||
* the SUNDIALS types `realtype` and `booleantype` are no longer defined by v7
|
||||
and therefore MFEM now uses the new type names `sunrealtype` and
|
||||
`sunbooleantype`, respectively, which MFEM defines when using SUNDIALS < v6
|
||||
- API changes due to SUNDIALS v7:
|
||||
* the SUNDIALS types realtype and booleantype are no longer defined by v7
|
||||
and therefore MFEM now uses the new type names sunrealtype and
|
||||
sunbooleantype, respectively, which MFEM defines when using SUNDIALS < v6
|
||||
where these types were not defined.
|
||||
* The SUNDIALS macro `SUNLS_SUCCESS` and some other `*_SUCCESS` macros were
|
||||
removed and replaced by `SUN_SUCCESS` in v7, so to avoid tedious checks for
|
||||
SUNDIALS versions, MFEM now defines and uses the constant `SUN_SUCCESS` when
|
||||
* The SUNDIALS macro SUNLS_SUCCESS and some other *_SUCCESS macros were
|
||||
removed and replaced by SUN_SUCCESS in v7, so to avoid tedious checks for
|
||||
SUNDIALS versions, MFEM now defines and uses the constant SUN_SUCCESS when
|
||||
using SUNDIALS < v7.
|
||||
* The constants `SUN_PREC_*`, introduced by SUNDIALS v6 are now introduced by
|
||||
* The constants SUN_PREC_*, introduced by SUNDIALS v6 are now introduced by
|
||||
MFEM when using SUNDIALS < v6 to avoid tedious version checks.
|
||||
|
||||
- TMOP_Metric_skew2D has been marked as deprecated.
|
||||
|
||||
|
||||
Version 4.7, released on May 7, 2024
|
||||
====================================
|
||||
@@ -185,9 +244,6 @@ Meshing improvements
|
||||
|
||||
- Added support for internal boundary elements in nonconforming meshes.
|
||||
|
||||
- Added ExodusII output capability. The writer can handle first-order (Pyramid5,
|
||||
Wedge6, Hex8, Tet4) and second-order FE types (Pyramid14, Wedge18, Hex27, Tet10).
|
||||
|
||||
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
|
||||
|
||||
Discretization improvements
|
||||
@@ -247,15 +303,6 @@ New and updated examples and miniapps
|
||||
- Added two new example codes: 38 and 39/39p described above. Substantially
|
||||
updated Example 18/18p.
|
||||
|
||||
- Added ODE solvers selection routines. This creates a uniformity across examples,
|
||||
miniapps and other executables in regard to ODE(time-integrator) selection.
|
||||
|
||||
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
|
||||
This is relevant for AB/AM and gen-alpha solvers.
|
||||
|
||||
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
|
||||
read/write functionality.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Updated the Doxygen documentation style, which now requires Doxygen version
|
||||
@@ -269,8 +316,6 @@ Miscellaneous
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
- Added GSLIB-based gather-scatter operator.
|
||||
|
||||
|
||||
Version 4.6, released on September 27, 2023
|
||||
===========================================
|
||||
|
||||
+24
-23
@@ -12,7 +12,8 @@
|
||||
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
|
||||
# Version 3.8 fixes the handling of CMAKE_CXX_STANDARD for try_compile.
|
||||
# Version 3.8 or newer is required for direct CUDA support.
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
# Version 3.12 or newer is required for setting maximum policy version.
|
||||
cmake_minimum_required(VERSION 3.12.0...4.0.0)
|
||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
|
||||
"Path to optional user configuration file.")
|
||||
@@ -58,7 +59,7 @@ project(mfem NONE)
|
||||
# Current version of MFEM, see also `makefile`.
|
||||
# mfem_VERSION = (string)
|
||||
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
|
||||
set(${PROJECT_NAME}_VERSION 4.7.1)
|
||||
set(${PROJECT_NAME}_VERSION 4.8.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
@@ -104,6 +105,13 @@ endif()
|
||||
# Include xSDK default CMake file.
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/config/XSDKDefaults.cmake")
|
||||
|
||||
# Path to MFEM's CMake modules and utilities.
|
||||
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
|
||||
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
|
||||
|
||||
# Load MFEM CMake utilities.
|
||||
include(MfemCmakeUtilities)
|
||||
|
||||
# Enable languages.
|
||||
enable_language(CXX)
|
||||
if (MINGW)
|
||||
@@ -116,16 +124,15 @@ if (MFEM_USE_CUDA)
|
||||
if (MFEM_USE_HIP)
|
||||
message(FATAL_ERROR " *** MFEM_USE_HIP cannot be combined with MFEM_USE_CUDA.")
|
||||
endif()
|
||||
# CUDAToolkit was added in CMake 3.17, so we require at least CMake 3.17 when
|
||||
# CUDA is enabled:
|
||||
if (CMAKE_VERSION VERSION_LESS 3.17.0)
|
||||
message(FATAL_ERROR "CUDA support requires CMake >= 3.17")
|
||||
endif()
|
||||
# Use ${CMAKE_CXX_COMPILER} as the cuda host compiler.
|
||||
if (NOT CMAKE_CUDA_HOST_COMPILER)
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
enable_language(CUDA)
|
||||
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
|
||||
"CUDA standard to use.")
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
|
||||
"Force the use of the chosen CUDA standard.")
|
||||
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
|
||||
set(CUDA_FLAGS "--expt-extended-lambda")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
|
||||
@@ -140,18 +147,19 @@ if (MFEM_USE_CUDA)
|
||||
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
|
||||
endif()
|
||||
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.12.0)
|
||||
# CMake versions 3.8 and 3.9 require this to work; 3.10 and 3.11 are not
|
||||
# tested and may not actually need this (but should be ok to keep).
|
||||
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
|
||||
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
enable_language(CUDA)
|
||||
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
|
||||
"CUDA standard to use.")
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
|
||||
"Force the use of the chosen CUDA standard.")
|
||||
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
set(CUSPARSE_FOUND TRUE)
|
||||
set(CUBLAS_FOUND TRUE)
|
||||
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
|
||||
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
|
||||
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
|
||||
mfem_culib_set_libraries(CUSPARSE cusparse)
|
||||
mfem_culib_set_libraries(CUBLAS cublas)
|
||||
endif()
|
||||
|
||||
if (XSDK_ENABLE_C)
|
||||
@@ -164,13 +172,6 @@ endif()
|
||||
# Suppress warnings about MACOSX_RPATH
|
||||
set(CMAKE_MACOSX_RPATH OFF CACHE BOOL "")
|
||||
|
||||
# CMake needs to know where to find things
|
||||
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
|
||||
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
|
||||
|
||||
# Load MFEM CMake utilities.
|
||||
include(MfemCmakeUtilities)
|
||||
|
||||
string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UC)
|
||||
mfem_version_to_int(${${PROJECT_NAME}_VERSION} ${PROJECT_NAME_UC}_VERSION)
|
||||
set(${PROJECT_NAME_UC}_VERSION_STRING ${${PROJECT_NAME}_VERSION})
|
||||
|
||||
@@ -120,7 +120,9 @@ The MFEM source code has the following structure:
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── eltrans
|
||||
│ ├── fe
|
||||
│ ├── gslib
|
||||
│ ├── integ
|
||||
│ ├── lor
|
||||
│ ├── moonolith
|
||||
@@ -128,6 +130,7 @@ The MFEM source code has the following structure:
|
||||
│ └── tmop
|
||||
├── general
|
||||
├── linalg
|
||||
│ ├── batched
|
||||
│ └── simd
|
||||
├── mesh
|
||||
│ └── submesh
|
||||
|
||||
@@ -122,9 +122,14 @@ Parallel build:
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
(this build requires CMake 3.8 or newer)
|
||||
(this build requires CMake 3.17 or newer)
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES
|
||||
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES -DCUDA_ARCH=sm_70
|
||||
make -j 4
|
||||
|
||||
HIP build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
make -j 4
|
||||
|
||||
Example codes (serial/parallel, depending on the build):
|
||||
@@ -1002,7 +1007,7 @@ Configuration variables (CMake)
|
||||
===============================
|
||||
See the configuration file config/defaults.cmake for the default settings.
|
||||
|
||||
Note: the option MFEM_USE_CUDA requires CMake version 3.8 or newer!
|
||||
Note: the option MFEM_USE_CUDA requires CMake version 3.17 or newer!
|
||||
|
||||
Non-standard CMake variables for compilers:
|
||||
CXX - If set, overwrite the auto-detected C++ compiler, serial build
|
||||
@@ -1161,6 +1166,19 @@ larger problems, there are two options:
|
||||
1. Building hypre with '--enable-bigint' defines the local and global indices to
|
||||
be 64-bit. This is convenient, but requires more memory than necessary.
|
||||
|
||||
2. Building hypre with '--enable-mixedint' defines the local indiced to be
|
||||
2. Building hypre with '--enable-mixedint' defines the local indices to be
|
||||
32-bit, while using a 64-bit storage for global indices. This option is
|
||||
currently tested only in ex1p, and may not work in more general settings.
|
||||
|
||||
Specific options for HIP
|
||||
========================
|
||||
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
|
||||
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
|
||||
Specific options for RAJA+HIP+MPI
|
||||
=================================
|
||||
RAJA uses CMake's built-in HIP support (added in CMake 3.21), while MFEM uses
|
||||
the older HIP C++ library build/linkage. To ensure proper build and linkage
|
||||
check that `CMAKE_CXX_COMPILER` and `CMAKE_HIP_COMPILER` are set to the same
|
||||
compiler. This is especially important when using an MPI compiler (for example
|
||||
crayCC) where some linker flags may get dropped if these two are not identical.
|
||||
|
||||
@@ -25,7 +25,12 @@ if (HYPRE_FOUND)
|
||||
find_package(rocsparse REQUIRED)
|
||||
find_package(rocrand REQUIRED)
|
||||
endif()
|
||||
return()
|
||||
if (HYPRE_LIBRARIES AND HYPRE_INCLUDE_DIRS AND HYPRE_VERSION)
|
||||
find_package_handle_standard_args(HYPRE
|
||||
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
@@ -77,9 +82,10 @@ endif()
|
||||
|
||||
if (HYPRE_FOUND AND HYPRE_USING_CUDA)
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
|
||||
get_target_property(CURAND_LIBRARIES CUDA::curand LOCATION)
|
||||
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
|
||||
# Initialize CUSPARSE_LIBRARIES, CURAND_LIBRARIES, and CUBLAS_LIBRARIES:
|
||||
mfem_culib_set_libraries(CUSPARSE cusparse)
|
||||
mfem_culib_set_libraries(CURAND curand)
|
||||
mfem_culib_set_libraries(CUBLAS cublas)
|
||||
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
|
||||
${CUBLAS_LIBRARIES})
|
||||
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
|
||||
@@ -95,3 +101,7 @@ if (HYPRE_FOUND AND HYPRE_USING_HIP)
|
||||
"HYPRE libraries + dependencies." FORCE)
|
||||
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(HYPRE
|
||||
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
|
||||
)
|
||||
|
||||
@@ -19,8 +19,10 @@ mfem_find_package(MAGMA MAGMA MAGMA_DIR "include" "magma.h" "lib" "magma"
|
||||
"Paths to headers required by MAGMA." "Libraries required by MAGMA.")
|
||||
|
||||
if (MAGMA_FOUND AND MFEM_USE_CUDA)
|
||||
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
|
||||
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
|
||||
mfem_culib_set_libraries(CUSPARSE cusparse)
|
||||
mfem_culib_set_libraries(CUBLAS cublas)
|
||||
list(APPEND MAGMA_LIBRARIES ${CUSPARSE_LIBRARIES} ${CUBLAS_LIBRARIES})
|
||||
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
|
||||
"MAGMA libraries + dependencies." FORCE)
|
||||
|
||||
@@ -123,16 +123,9 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
|
||||
|
||||
# If CUDA is enabled, tag source files to be compiled with nvcc.
|
||||
if (MFEM_USE_CUDA)
|
||||
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST} PROPERTIES LANGUAGE CUDA)
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
else()
|
||||
set(LIST_)
|
||||
foreach(item IN LISTS EXTRA_OPTIONS_LIST)
|
||||
list(APPEND LIST_ "-Xcompiler=${item}")
|
||||
endforeach()
|
||||
set(EXTRA_OPTIONS_LIST ${LIST_})
|
||||
endif()
|
||||
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
|
||||
PROPERTIES LANGUAGE CUDA)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
endif()
|
||||
|
||||
# Actually add the executable
|
||||
@@ -157,6 +150,21 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Macro for setting variables like '<culib>_LIBRARIES' where <culib> is a CUDA
|
||||
# library like cublas. This macro assumes that the CUDAToolkit module was loaded
|
||||
# successfully. Example usage:
|
||||
# mfem_culib_set_libraries(CUBLAS cublas)
|
||||
macro(mfem_culib_set_libraries _CULIB _culib)
|
||||
# The following command does not work with older CMake versions, e.g. 3.20:
|
||||
# get_target_property(${_CULIB}_LIBRARIES CUDA::${_culib} LOCATION)
|
||||
# Therefore, we use the respective internal variable:
|
||||
set(${_CULIB}_LIBRARIES ${CUDA_${_culib}_LIBRARY})
|
||||
if (NOT ${_CULIB}_LIBRARIES)
|
||||
message(FATAL_ERROR
|
||||
"Error setting ${_CULIB}_LIBRARIES: ${${_CULIB}_LIBRARIES}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
# Auxiliary function, used in mfem_find_package().
|
||||
function(mfem_find_component Prefix DirVar IncSuffixes Header LibSuffixes Lib
|
||||
|
||||
+13
-16
@@ -484,8 +484,8 @@ SIDRE_LIB = \
|
||||
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
|
||||
PUMI_DIR = @MFEM_DIR@/../pumi-2.1.0
|
||||
PUMI_OPT = -I$(PUMI_DIR)/include
|
||||
PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
|
||||
-llion -lmth -lapf_zoltan -lspr
|
||||
PUMI_LIB = -L$(PUMI_DIR)/lib64 -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf\
|
||||
-lpcu -lgmi -lparma -llion -lmth -lapf_zoltan -lspr
|
||||
|
||||
# HIOP
|
||||
HIOP_DIR = @MFEM_DIR@/../hiop/install
|
||||
@@ -568,7 +568,11 @@ RAJA_LIB = $(XLINKER)-rpath,$(RAJA_DIR)/lib -L$(RAJA_DIR)/lib -lRAJA $(CAMP_LIB)
|
||||
# UMPIRE library configuration
|
||||
UMPIRE_DIR = @MFEM_DIR@/../umpire
|
||||
UMPIRE_OPT = -I$(UMPIRE_DIR)/include $(if $(CAMP_DIR), -I$(CAMP_DIR)/include)
|
||||
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -lumpire $(CAMP_LIB)
|
||||
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -L$(UMPIRE_DIR)/lib64 -lumpire $(CAMP_LIB)
|
||||
ifdef FMT_DIR
|
||||
UMPIRE_OPT += -I$(FMT_DIR)/include
|
||||
UMPIRE_LIB += -L$(FMT_DIR)/lib -L$(FMT_DIR)/lib64 -lfmt
|
||||
endif
|
||||
|
||||
# MKL CPardiso library configuration
|
||||
MKL_CPARDISO_DIR ?=
|
||||
@@ -600,20 +604,13 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
# If you want to enable automatic differentiation at compile time, use the
|
||||
# options below, adapted to your configuration. To be more flexible, we
|
||||
# recommend using the Enzyme plugin during link time optimization. One option is
|
||||
# to add your options to the global compiler/linker flags like
|
||||
#
|
||||
# BASE_FLAGS += -flto
|
||||
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
|
||||
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
|
||||
#
|
||||
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
|
||||
ENZYME_VERSION ?= 14
|
||||
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
|
||||
ENZYME_DIR = @MFEM_DIR@/../enzyme
|
||||
ENZYME_LLVM_VERSION = 19
|
||||
ENZYME_OPT = -fplugin=$(ENZYME_DIR)/lib/ClangEnzyme-$(ENZYME_LLVM_VERSION).$(SO_EXT)
|
||||
ENZYME_LIB = ""
|
||||
ifeq ($(MFEM_USE_ENZYME),YES)
|
||||
BASE_FLAGS = -std=c++17
|
||||
endif
|
||||
|
||||
# Google Benchmark, SUNDIALS >= 6.4.0, STRUMPACK, RAJA, UMPIRE, and Tribol require C++14:
|
||||
ifneq ($(filter YES,$(MFEM_USE_BENCHMARK) $(MFEM_USE_SUNDIALS) $(MFEM_USE_STRUMPACK) $(MFEM_USE_RAJA) $(MFEM_USE_UMPIRE) $(MFEM_USE_TRIBOL)),)
|
||||
|
||||
@@ -17,7 +17,7 @@ We provide two containers, which you can either build or use directly from
|
||||
|
||||
In the above, "ghcr.io" means "GitHub Container Registry" and
|
||||
is the [GitHub packages](https://github.com/features/packages) registry that supports
|
||||
Docker images and other OCI artifacts.
|
||||
Docker images and other OCI artifacts.
|
||||
|
||||
### Ubuntu
|
||||
|
||||
@@ -132,7 +132,7 @@ examples.
|
||||
|
||||
> Use this build for a development environment with spack and mfem
|
||||
|
||||
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
|
||||
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
|
||||
however you can build it locally too:
|
||||
|
||||
```bash
|
||||
@@ -197,7 +197,7 @@ Average reduction factor = 0.140201
|
||||
|
||||
This container is likely ideal for someone that wants to develop mfem itself.
|
||||
For other use cases, we recommend using the slimmer image. As an example,
|
||||
if you want to develop with your own code base (and mfem as is in the container)
|
||||
if you want to develop with your own code base (and mfem as is in the container)
|
||||
you can bind to somewhere else in the container (e.g., src)
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 186a320..39e5356 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1,4 +1,4 @@
|
||||
-cmake_minimum_required(VERSION 2.8)
|
||||
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
|
||||
project(METIS)
|
||||
|
||||
set(GKLIB_PATH "${CMAKE_SOURCE_DIR}/GKlib" CACHE PATH "path to GKlib")
|
||||
diff --git a/GKlib/CMakeLists.txt b/GKlib/CMakeLists.txt
|
||||
index 67b600a..44321a6 100644
|
||||
--- a/GKlib/CMakeLists.txt
|
||||
+++ b/GKlib/CMakeLists.txt
|
||||
@@ -1,4 +1,4 @@
|
||||
-cmake_minimum_required(VERSION 2.8)
|
||||
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
|
||||
project(GKlib)
|
||||
|
||||
get_filename_component(abs "." ABSOLUTE)
|
||||
@@ -22,6 +22,7 @@ vcpkg_extract_source_archive_ex(
|
||||
fix-linux-build-error.patch
|
||||
install-metisConfig.patch
|
||||
fix-INT_MIN_define.patch
|
||||
cmake4.patch
|
||||
)
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
1
|
||||
1 7 0 1 2 3 4
|
||||
|
||||
boundary
|
||||
5
|
||||
1 3 3 2 1 0
|
||||
2 2 0 1 4
|
||||
3 2 1 2 4
|
||||
4 2 2 3 4
|
||||
5 2 3 0 4
|
||||
|
||||
vertices
|
||||
5
|
||||
3
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
0 0 0.7071067811865475
|
||||
@@ -0,0 +1,108 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
16
|
||||
1 5 12 13 16 15 21 22 25 24
|
||||
1 6 9 12 8 18 21 17
|
||||
1 6 11 8 12 20 17 21
|
||||
1 6 3 2 12 6 5 15
|
||||
1 6 11 12 2 14 15 5
|
||||
1 6 3 12 0 4 13 1
|
||||
1 6 9 0 12 10 1 13
|
||||
1 7 12 13 22 21 19
|
||||
1 7 15 16 13 12 7
|
||||
1 7 12 21 24 15 23
|
||||
1 7 9 12 21 18 19
|
||||
1 7 11 20 21 12 23
|
||||
1 7 9 10 13 12 19
|
||||
1 7 11 12 15 14 23
|
||||
1 7 3 6 15 12 7
|
||||
1 7 3 12 13 4 7
|
||||
|
||||
boundary
|
||||
39
|
||||
1 3 5 6 3 2
|
||||
2 2 6 7 3
|
||||
2 2 7 4 3
|
||||
3 3 3 4 1 0
|
||||
4 2 11 12 8
|
||||
4 2 9 8 12
|
||||
5 2 3 12 2
|
||||
5 2 11 2 12
|
||||
6 3 0 1 10 9
|
||||
7 2 10 19 9
|
||||
7 2 18 9 19
|
||||
8 3 8 9 18 17
|
||||
9 2 4 13 1
|
||||
9 2 10 1 13
|
||||
10 2 4 7 13
|
||||
10 2 16 13 7
|
||||
11 3 13 16 25 22
|
||||
12 2 10 13 19
|
||||
12 2 22 19 13
|
||||
13 2 6 15 7
|
||||
13 2 16 7 15
|
||||
14 2 6 5 15
|
||||
14 2 14 15 5
|
||||
15 2 14 23 15
|
||||
15 2 24 15 23
|
||||
16 3 16 15 24 25
|
||||
17 3 5 2 11 14
|
||||
18 2 3 0 12
|
||||
18 2 9 12 0
|
||||
19 3 11 8 17 20
|
||||
20 2 14 11 23
|
||||
20 2 20 23 11
|
||||
21 2 18 21 17
|
||||
21 2 20 17 21
|
||||
22 2 18 19 21
|
||||
22 2 22 21 19
|
||||
23 3 21 22 25 24
|
||||
24 2 20 21 23
|
||||
24 2 24 23 21
|
||||
|
||||
vertices
|
||||
26
|
||||
3
|
||||
0 -1 -1
|
||||
1 -1 -1
|
||||
-1 0 -1
|
||||
0 0 -1
|
||||
1 0 -1
|
||||
-1 1 -1
|
||||
0 1 -1
|
||||
1 1 -1
|
||||
-1 -1 0
|
||||
0 -1 0
|
||||
1 -1 0
|
||||
-1 0 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
-1 1 0
|
||||
0 1 0
|
||||
1 1 0
|
||||
-1 -1 1
|
||||
0 -1 1
|
||||
1 -1 1
|
||||
-1 0 1
|
||||
0 0 1
|
||||
1 0 1
|
||||
-1 1 1
|
||||
0 1 1
|
||||
1 1 1
|
||||
@@ -1,9 +1,9 @@
|
||||
MFEM INLINE mesh v1.0
|
||||
|
||||
type = pyramid
|
||||
nx = 4
|
||||
ny = 4
|
||||
nz = 4
|
||||
nx = 2
|
||||
ny = 2
|
||||
nz = 2
|
||||
sx = 1.0
|
||||
sy = 1.0
|
||||
sz = 1.0
|
||||
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v4.7.1
|
||||
PROJECT_NUMBER = v4.8.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -951,7 +951,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/integrators/nlconvection \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/interface \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/solvers \
|
||||
@MFEM_SOURCE_DIR@/fem/eltrans \
|
||||
@MFEM_SOURCE_DIR@/fem/fe \
|
||||
@MFEM_SOURCE_DIR@/fem/gslib \
|
||||
@MFEM_SOURCE_DIR@/fem/integ \
|
||||
@MFEM_SOURCE_DIR@/fem/lor \
|
||||
@MFEM_SOURCE_DIR@/fem/moonolith \
|
||||
@MFEM_SOURCE_DIR@/fem/qinterp \
|
||||
@@ -969,6 +972,8 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/adjoint \
|
||||
@MFEM_SOURCE_DIR@/miniapps/autodiff \
|
||||
@MFEM_SOURCE_DIR@/miniapps/common \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
@MFEM_SOURCE_DIR@/miniapps/gslib \
|
||||
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
|
||||
@@ -986,12 +991,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/performance \
|
||||
@MFEM_SOURCE_DIR@/miniapps/shifted \
|
||||
@MFEM_SOURCE_DIR@/miniapps/solvers \
|
||||
@MFEM_SOURCE_DIR@/miniapps/spde \
|
||||
@MFEM_SOURCE_DIR@/miniapps/tools \
|
||||
@MFEM_SOURCE_DIR@/miniapps/toys \
|
||||
@MFEM_SOURCE_DIR@/miniapps/tribol \
|
||||
@MFEM_SOURCE_DIR@/miniapps/spde \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util
|
||||
@MFEM_SOURCE_DIR@/miniapps/tribol
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
||||
+16
-16
@@ -42,10 +42,10 @@ namespace mfem {
|
||||
* - mfem::forall functions in forall.hpp
|
||||
*
|
||||
* <H3>Example codes</H3>
|
||||
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Laplace problem (same discretization as ex0 but with more sophisticated options)
|
||||
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Laplace problem (same discretization as ex0p but with more sophisticated options)
|
||||
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Poisson problem (same discretization as ex0 but with more sophisticated options)
|
||||
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Poisson problem (same discretization as ex0p but with more sophisticated options)
|
||||
* - <a class="el" href="ex2_8cpp_source.html">Example 2</a>: vector FEM for linear elasticity
|
||||
* - <a class="el" href="ex2p_8cpp_source.html">Example 2p</a>: parallel vector FEM for linear elasticity
|
||||
* - <a class="el" href="ex3_8cpp_source.html">Example 3</a>: Nedelec H(curl) FEM for the definite Maxwell problem
|
||||
@@ -54,12 +54,12 @@ namespace mfem {
|
||||
* - <a class="el" href="ex4p_8cpp_source.html">Example 4p</a>: parallel Raviart-Thomas H(div) FEM for the grad-div problem
|
||||
* - <a class="el" href="ex5_8cpp_source.html">Example 5</a>: mixed pressure-velocity FEM for the Darcy problem
|
||||
* - <a class="el" href="ex5p_8cpp_source.html">Example 5p</a>: parallel mixed pressure-velocity FEM for the Darcy problem
|
||||
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Laplace problem
|
||||
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Laplace problem
|
||||
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: Laplace problem on a surface (the unit sphere)
|
||||
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel Laplace problem on a surface (the unit sphere)
|
||||
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
|
||||
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
|
||||
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Poisson problem
|
||||
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Poisson problem
|
||||
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: screened Poisson problem on a surface (the unit sphere)
|
||||
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel screened Poisson problem on a surface (the unit sphere)
|
||||
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
|
||||
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
|
||||
* - <a class="el" href="ex9_8cpp_source.html">Example 9</a>: Discontinuous Galerkin (DG) time-dependent advection
|
||||
* - <a class="el" href="ex9p_8cpp_source.html">Example 9p</a>: parallel Discontinuous Galerkin (DG) time-dependent advection
|
||||
* - <a class="el" href="ex10_8cpp_source.html">Example 10</a>: time-dependent implicit nonlinear elasticity
|
||||
@@ -67,8 +67,8 @@ namespace mfem {
|
||||
* - <a class="el" href="ex11p_8cpp_source.html">Example 11p</a>: parallel Laplace eigensolver
|
||||
* - <a class="el" href="ex12p_8cpp_source.html">Example 12p</a>: parallel linear elasticity eigensolver
|
||||
* - <a class="el" href="ex13p_8cpp_source.html">Example 13p</a>: parallel Maxwell eigensolver
|
||||
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Laplace problem
|
||||
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Laplace problem
|
||||
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Poisson problem
|
||||
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Poisson problem
|
||||
* - <a class="el" href="ex15_8cpp_source.html">Example 15</a>: dynamic AMR for Laplace with prescribed time-dependent source
|
||||
* - <a class="el" href="ex15p_8cpp_source.html">Example 15p</a>: parallel dynamic AMR for Laplace with prescribed time-dependent source
|
||||
* - <a class="el" href="ex16_8cpp_source.html">Example 16</a>: time-dependent nonlinear heat equation
|
||||
@@ -90,8 +90,8 @@ namespace mfem {
|
||||
* - <a class="el" href="ex24p_8cpp_source.html">Example 24p</a>: parallel mixed finite element spaces and interpolators
|
||||
* - <a class="el" href="ex25_8cpp_source.html">Example 25</a>: simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
|
||||
* - <a class="el" href="ex25p_8cpp_source.html">Example 25p</a>: parallel simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
|
||||
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Poisson problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Poisson problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex27_8cpp_source.html">Example 27</a>: boundary conditions for the Laplace problem
|
||||
* - <a class="el" href="ex27p_8cpp_source.html">Example 27p</a>: parallel boundary conditions for the Laplace problem
|
||||
* - <a class="el" href="ex28_8cpp_source.html">Example 28</a>: sliding contact in elasticity
|
||||
@@ -230,8 +230,8 @@ namespace mfem {
|
||||
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
|
||||
* - <a class="el" href="par__example_8cpp_source.html">Parallel AD</a>: Parallel p-Laplacian example
|
||||
* - <a class="el" href="seq__example_8cpp_source.html">Serial AD</a>: Serial p-Laplacian example
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// mpirun -np 4 ex1p --amgx-file amg_pcg.json
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
+537
-159
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/dfem/,)
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES =
|
||||
PAR_EXAMPLES = laghos
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
ifeq ($(MFEM_USE_ENZYME),NO)
|
||||
$(EXAMPLES):
|
||||
$(error MFEM is not configured with ENZYME)
|
||||
endif
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI_NP = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
|
||||
RUN_MPI = $(RUN_MPI_NP) $(MFEM_MPI_NP)
|
||||
SERIAL_NAME := Serial dFEM example
|
||||
PARALLEL_NAME := Parallel dFEM example
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, $(SERIAL_NAME))
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
// ex0 -m ../data/square-disc.mesh -o 2
|
||||
//
|
||||
// Description: This example code demonstrates the most basic usage of MFEM to
|
||||
// define a simple finite element discretization of the Laplace
|
||||
// define a simple finite element discretization of the Poisson
|
||||
// problem -Delta u = 1 with zero Dirichlet boundary conditions.
|
||||
// General 2D/3D mesh files and finite element polynomial degrees
|
||||
// can be specified by command line options.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Description: This example code demonstrates the most basic parallel usage of
|
||||
// MFEM to define a simple finite element discretization of the
|
||||
// Laplace problem -Delta u = 1 with zero Dirichlet boundary
|
||||
// Poisson problem -Delta u = 1 with zero Dirichlet boundary
|
||||
// conditions. General 2D/3D serial mesh files and finite element
|
||||
// polynomial degrees can be specified by command line options.
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// discontinuous Galerkin (DG) finite element discretization of
|
||||
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
|
||||
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
|
||||
// boundary conditions. Finite element spaces of any order,
|
||||
// including zero on regular grids, are supported. The example
|
||||
// highlights the use of discontinuous spaces and DG-specific face
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// discontinuous Galerkin (DG) finite element discretization of
|
||||
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
|
||||
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
|
||||
// boundary conditions. Finite element spaces of any order,
|
||||
// including zero on regular grids, are supported. The example
|
||||
// highlights the use of discontinuous spaces and DG-specific face
|
||||
|
||||
+2
-2
@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
|
||||
// 6. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the inner loop.
|
||||
BilinearForm a(&fespace);
|
||||
LinearForm b(&fespace);
|
||||
@@ -446,7 +446,7 @@ real_t bdr_func(const Vector &pt, real_t t)
|
||||
return composite_func(pt, t, front, ball);
|
||||
}
|
||||
|
||||
// Laplace of the exact solution, used for the right hand side.
|
||||
// Laplacian of the exact solution, used for the right hand side.
|
||||
real_t rhs_func(const Vector &pt, real_t t)
|
||||
{
|
||||
return composite_func(pt, t, front_laplace, ball_laplace);
|
||||
|
||||
+2
-2
@@ -181,7 +181,7 @@ int main(int argc, char *argv[])
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
|
||||
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the inner loop.
|
||||
ParBilinearForm a(&fespace);
|
||||
ParLinearForm b(&fespace);
|
||||
@@ -507,7 +507,7 @@ real_t bdr_func(const Vector &pt, real_t t)
|
||||
return composite_func(pt, t, front, ball);
|
||||
}
|
||||
|
||||
// Laplace of the exact solution, used for the right hand side.
|
||||
// Laplacian of the exact solution, used for the right hand side.
|
||||
real_t rhs_func(const Vector &pt, real_t t)
|
||||
{
|
||||
return composite_func(pt, t, front_laplace, ball_laplace);
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ WaveOperator::WaveOperator(FiniteElementSpace &f,
|
||||
M->AddDomainIntegrator(new MassIntegrator());
|
||||
M->Assemble();
|
||||
|
||||
// Apply Bcs
|
||||
// Apply BCs
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
K->FormSystemMatrix(ess_tdof_list, Kmat);
|
||||
M->FormSystemMatrix(ess_tdof_list, Mmat);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
// ex26 -m ../data/beam-hex.mesh -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
|
||||
// as in Example 1.
|
||||
//
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
// mpirun -np 4 ex26p -d ceed-cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
|
||||
// as in Example 1.
|
||||
//
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// finite element discretization of a PDE on a 2 dimensional
|
||||
// surface embedded in a 3 dimensional domain. In this case we
|
||||
// solve the Laplace problem -Div(sigma Grad u) = 1, with
|
||||
// solve the Poisson problem -Div(sigma Grad u) = 1, with
|
||||
// homogeneous Dirichlet boundary conditions, where sigma is an
|
||||
// anisotropic diffusion constant defined as a 3x3 matrix
|
||||
// coefficient.
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// finite element discretization of a PDE on a 2 dimensional
|
||||
// surface embedded in a 3 dimensional domain. In this case we
|
||||
// solve the Laplace problem -Div(sigma Grad u) = 1, with
|
||||
// solve the Poisson problem -Div(sigma Grad u) = 1, with
|
||||
// homogeneous Dirichlet boundary conditions, where sigma is an
|
||||
// anisotropic diffusion constant defined as a 3x3 matrix
|
||||
// coefficient.
|
||||
|
||||
+3
-3
@@ -390,7 +390,7 @@ public:
|
||||
|
||||
|
||||
/**
|
||||
@brief Class for surface linearform integrator
|
||||
@brief Class for surface linear form integrator
|
||||
|
||||
Integrator to demonstrate the use of the surface integration rule on an
|
||||
implicit surface defined by a level-set.
|
||||
@@ -460,7 +460,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Class for subdomain linearform integrator
|
||||
@brief Class for subdomain linear form integrator
|
||||
|
||||
Integrator to demonstrate the use of the subdomain integration rule within
|
||||
an area defined by an implicit surface defined by a level-set.
|
||||
@@ -546,7 +546,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&ref_levels, "-r", "--refine", "Number of meh refinements");
|
||||
args.AddOption(&method, "-m", "--method",
|
||||
"Cut integration method: 0 for moments-based, 1 for Algoim.");
|
||||
args.AddOption(&inttype, "-i", "--integrationtype",
|
||||
args.AddOption(&inttype, "-i", "--integration-type",
|
||||
"IntegrationType to demonstrate");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
//
|
||||
// The particular problem being solved here is nearly the same
|
||||
// as that in example 1 i.e. a simple finite element
|
||||
// discretization of the Laplace problem -Delta u = 1 with
|
||||
// discretization of the Poisson problem -Delta u = 1 with
|
||||
// homogeneous Dirichlet boundary conditions and, in this case,
|
||||
// an inhomogeneous diffusion coefficient. The diffusion
|
||||
// coefficient is given a small default value throughout the
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
//
|
||||
// The particular problem being solved here is nearly the same
|
||||
// as that in example 1 i.e. a simple finite element
|
||||
// discretization of the Laplace problem -Delta u = 1 with
|
||||
// discretization of the Poisson problem -Delta u = 1 with
|
||||
// homogeneous Dirichlet boundary conditions and, in this case,
|
||||
// an inhomogeneous diffusion coefficient. The diffusion
|
||||
// coefficient is given a small default value throughout the
|
||||
|
||||
+5
-7
@@ -65,6 +65,7 @@ int main(int argc, char *argv[])
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool pa = false;
|
||||
bool ea = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
@@ -83,18 +84,14 @@ int main(int argc, char *argv[])
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
|
||||
"--no-element-assembly", "Enable Element Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
args.ParseCheck();
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
@@ -166,6 +163,7 @@ int main(int argc, char *argv[])
|
||||
Coefficient *beta = new ConstantCoefficient(1.0);
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
|
||||
+5
-13
@@ -71,6 +71,7 @@ int main(int argc, char *argv[])
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool pa = false;
|
||||
bool ea = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
@@ -89,24 +90,14 @@ int main(int argc, char *argv[])
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
|
||||
"--no-element-assembly", "Enable Element Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
args.ParseCheck();
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
@@ -194,6 +185,7 @@ int main(int argc, char *argv[])
|
||||
Coefficient *beta = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
// ex6 -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the Laplace
|
||||
// refinement loop. The problem being solved is again the Poisson
|
||||
// equation -Delta u = 1 with homogeneous Dirichlet boundary
|
||||
// conditions. The problem is solved on a sequence of meshes which
|
||||
// are locally refined in a conforming (triangles, tetrahedrons)
|
||||
@@ -113,7 +113,7 @@ int main(int argc, char *argv[])
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
|
||||
// 6. As in Example 1, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
BilinearForm a(&fespace);
|
||||
if (pa)
|
||||
|
||||
+104
-7
@@ -3,6 +3,7 @@
|
||||
// Compile with: make ex6p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -pref
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 1
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 2 -h1
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 2 -cs
|
||||
@@ -28,7 +29,7 @@
|
||||
// mpirun -np 4 ex6p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the Laplace
|
||||
// refinement loop. The problem being solved is again the Poisson
|
||||
// equation -Delta u = 1 with homogeneous Dirichlet boundary
|
||||
// conditions. The problem is solved on a sequence of meshes which
|
||||
// are locally refined in a conforming (triangles, tetrahedrons)
|
||||
@@ -41,6 +42,12 @@
|
||||
// from coarse to fine meshes, restarting from a checkpoint, as
|
||||
// well as persistent GLVis visualization are also illustrated.
|
||||
//
|
||||
// There is also the option to use hp-refinement. Real
|
||||
// applications should use some problem-dependent criteria for
|
||||
// selecting between h- and p-refinement, but in this example, we
|
||||
// simply alternate between refinement types to demonstrate the
|
||||
// capabilities.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
@@ -69,6 +76,8 @@ int main(int argc, char *argv[])
|
||||
bool smooth_rt = true;
|
||||
bool restart = false;
|
||||
bool visualization = true;
|
||||
bool rebalance = true;
|
||||
bool usePRefinement = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -90,6 +99,10 @@ int main(int argc, char *argv[])
|
||||
"Stop after reaching this many degrees of freedom.");
|
||||
args.AddOption(&smooth_rt, "-rt", "--smooth-rt", "-h1", "--smooth-h1",
|
||||
"Represent the smooth flux in RT or vector H1 space.");
|
||||
args.AddOption(&usePRefinement, "-pref", "--p-refine", "-no-pref",
|
||||
"--no-p-refine", "Alternate between h- and p-refinement.");
|
||||
args.AddOption(&rebalance, "-reb", "--rebalance", "-no-reb",
|
||||
"--no-rebalance", "Load balance the nonconforming mesh.");
|
||||
args.AddOption(&restart, "-res", "--restart", "-no-res", "--no-restart",
|
||||
"Restart computation from the last checkpoint.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -109,6 +122,15 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (usePRefinement && rebalance)
|
||||
{
|
||||
rebalance = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Load balancing is not performed with p-refinements.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
@@ -186,7 +208,7 @@ int main(int argc, char *argv[])
|
||||
ParFiniteElementSpace fespace(pmesh, &fec);
|
||||
|
||||
// 11. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa)
|
||||
@@ -321,7 +343,15 @@ int main(int argc, char *argv[])
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << x << flush;
|
||||
if (usePRefinement)
|
||||
{
|
||||
std::unique_ptr<GridFunction> vis_x = x.ProlongateToMaxOrder();
|
||||
sout << "solution\n" << *pmesh << *vis_x << flush;
|
||||
}
|
||||
else
|
||||
{
|
||||
sout << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
}
|
||||
|
||||
if (global_dofs >= max_dofs)
|
||||
@@ -337,8 +367,31 @@ int main(int argc, char *argv[])
|
||||
// estimator to obtain element errors, then it selects elements to be
|
||||
// refined and finally it modifies the mesh. The Stop() method can be
|
||||
// used to determine if a stopping criterion was met.
|
||||
refiner.Apply(*pmesh);
|
||||
if (refiner.Stop())
|
||||
|
||||
// Simply alternate between h- and p-refinement.
|
||||
const bool pRefine = usePRefinement && ((it % 2) == 1);
|
||||
bool stop = false;
|
||||
Array<pRefinement> prefinements;
|
||||
if (pRefine)
|
||||
{
|
||||
Array<Refinement> refinements;
|
||||
refiner.MarkWithoutRefining(*pmesh, refinements);
|
||||
stop = pmesh->ReduceInt(refinements.Size()) == 0LL;
|
||||
|
||||
prefinements.SetSize(refinements.Size());
|
||||
for (int i=0; i<refinements.Size(); ++i)
|
||||
{
|
||||
prefinements[i].index = refinements[i].index;
|
||||
prefinements[i].delta = 1; // Increase the element order by 1
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
refiner.Apply(*pmesh);
|
||||
stop = refiner.Stop();
|
||||
}
|
||||
|
||||
if (stop)
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
@@ -352,12 +405,20 @@ int main(int argc, char *argv[])
|
||||
// to any GridFunctions over the space. In this case, the update
|
||||
// matrix is an interpolation matrix so the updated GridFunction will
|
||||
// still represent the same function as before refinement.
|
||||
fespace.Update();
|
||||
if (pRefine)
|
||||
{
|
||||
fespace.PRefineAndUpdate(prefinements);
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace.Update();
|
||||
}
|
||||
|
||||
x.Update();
|
||||
|
||||
// 25. Load balance the mesh, and update the space and solution. Currently
|
||||
// available only for nonconforming meshes.
|
||||
if (pmesh->Nonconforming())
|
||||
if (pmesh->Nonconforming() && rebalance)
|
||||
{
|
||||
pmesh->Rebalance();
|
||||
|
||||
@@ -389,6 +450,42 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// Save result
|
||||
if (usePRefinement)
|
||||
{
|
||||
L2_FECollection fecL2(0, dim);
|
||||
ParFiniteElementSpace l2fespace(pmesh, &fecL2);
|
||||
ParGridFunction xo(&l2fespace); // Element order field
|
||||
xo = 0.0;
|
||||
|
||||
for (int e=0; e<pmesh->GetNE(); ++e)
|
||||
{
|
||||
const int p_elem = fespace.GetElementOrder(e);
|
||||
Array<int> dofs;
|
||||
l2fespace.GetElementDofs(e, dofs);
|
||||
xo[dofs[0]] = p_elem;
|
||||
}
|
||||
|
||||
ostringstream mesh_name, sol_name, order_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
order_name << "order." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->ParPrint(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
|
||||
std::unique_ptr<ParGridFunction> vis_x = x.ProlongateToMaxOrder();
|
||||
vis_x->Save(sol_ofs);
|
||||
|
||||
ofstream order_ofs(order_name.str().c_str());
|
||||
order_ofs.precision(8);
|
||||
xo.Save(order_ofs);
|
||||
}
|
||||
|
||||
delete smooth_flux_fes;
|
||||
delete smooth_flux_fec;
|
||||
delete pmesh;
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// triangulation of a unit sphere and a simple isoparametric
|
||||
// finite element discretization of the Laplace problem with mass
|
||||
// term, -Delta u + u = f.
|
||||
// finite element discretization of the screened Poisson problem,
|
||||
// -Delta u + u = f.
|
||||
//
|
||||
// The example highlights mesh generation, the use of mesh
|
||||
// refinement, high-order meshes and finite elements, as well as
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// triangulation of a unit sphere and a simple isoparametric
|
||||
// finite element discretization of the Laplace problem with mass
|
||||
// term, -Delta u + u = f.
|
||||
// finite element discretization of the screened Poisson problem,
|
||||
// -Delta u + u = f.
|
||||
//
|
||||
// The example highlights mesh generation, the use of mesh
|
||||
// refinement, high-order meshes and finite elements, as well as
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of the Discontinuous
|
||||
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
|
||||
// use high-order continuous trial space, a high-order interfacial
|
||||
// (trace) space, and a high-order discontinuous test space
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
//
|
||||
// Description: This example code demonstrates the use of the Discontinuous
|
||||
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
|
||||
// use high-order continuous trial space, a high-order interfacial
|
||||
// (trace) space, and a high-order discontinuous test space
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
// ex1 -m ../../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"id": "public-white",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Laplace problem\n",
|
||||
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Poisson problem\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"-\\Delta u = 1\n",
|
||||
|
||||
+4
-1
@@ -68,6 +68,9 @@ endif
|
||||
ifeq ($(MFEM_USE_CALIPER),YES)
|
||||
SUBDIRS += caliper
|
||||
endif
|
||||
ifeq ($(MFEM_USE_ENZYME),YES)
|
||||
SUBDIRS += dfem
|
||||
endif
|
||||
|
||||
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
|
||||
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
|
||||
@@ -189,7 +192,7 @@ clean-build:
|
||||
clean-exec:
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh ex6p-checkpoint.*
|
||||
@rm -rf Example5* Example9* Example15* Example16* Example23* ParaView
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* order.*
|
||||
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
|
||||
@rm -f deformed.* velocity.* elastic_energy.* mode_* mode_deriv_* flux.*
|
||||
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// mpirun -np 4 ex1p -pa -d cuda --petscopts rc_ex1p_device
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// mpirun -np 4 ex6p -m ../../data/amr-quad.mesh -nonoverlapping
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the Laplace
|
||||
// refinement loop. The problem being solved is again the Poisson
|
||||
// equation -Delta u = 1 with homogeneous Dirichlet boundary
|
||||
// conditions. The problem is solved on a sequence of meshes which
|
||||
// are locally refined in a conforming (triangles, tetrahedrons)
|
||||
@@ -131,7 +131,7 @@ int main(int argc, char *argv[])
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
|
||||
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
ParBilinearForm a(&fespace);
|
||||
ParLinearForm b(&fespace);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// creating a symbolic link to the above directory in ../../data.
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// creating a symbolic link to the above directory in ../../data.
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Sample runs: mpirun -np 8 ex6p
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the Laplace
|
||||
// refinement loop. The problem being solved is again the Poisson
|
||||
// equation -Delta u = 1 with homogeneous Dirichlet boundary
|
||||
// conditions. The problem is solved on a sequence of meshes which
|
||||
// are adapted in a conforming (tetrahedrons) manner according
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
|
||||
@@ -35,6 +35,7 @@ set(SRCS
|
||||
integ/bilininteg_mass_ea.cpp
|
||||
integ/bilininteg_mixedcurl_pa.cpp
|
||||
integ/bilininteg_mixedvecgrad_pa.cpp
|
||||
integ/bilininteg_trace_jump_ea.cpp
|
||||
integ/bilininteg_transpose_ea.cpp
|
||||
integ/bilininteg_vecdiffusion_mf.cpp
|
||||
integ/bilininteg_vecdiffusion_pa.cpp
|
||||
@@ -46,6 +47,7 @@ set(SRCS
|
||||
integ/bilininteg_diffusion_kernels.cpp
|
||||
integ/bilininteg_elasticity_kernels.cpp
|
||||
integ/bilininteg_hcurl_kernels.cpp
|
||||
integ/bilininteg_hdiv_ea.cpp
|
||||
integ/bilininteg_hdiv_kernels.cpp
|
||||
integ/bilininteg_hcurlhdiv_kernels.cpp
|
||||
integ/bilininteg_mass_kernels.cpp
|
||||
@@ -63,6 +65,7 @@ set(SRCS
|
||||
dgmassinv.cpp
|
||||
doftrans.cpp
|
||||
eltrans.cpp
|
||||
batchitrans.cpp
|
||||
estimators.cpp
|
||||
fe.cpp
|
||||
fe/face_map_utils.cpp
|
||||
@@ -73,6 +76,7 @@ set(SRCS
|
||||
fe/fe_nd.cpp
|
||||
fe/fe_nurbs.cpp
|
||||
fe/fe_pos.cpp
|
||||
fe/fe_pyramid.cpp
|
||||
fe/fe_rt.cpp
|
||||
fe/fe_ser.cpp
|
||||
fe_coll.cpp
|
||||
@@ -80,6 +84,7 @@ set(SRCS
|
||||
geom.cpp
|
||||
gridfunc.cpp
|
||||
hybridization.cpp
|
||||
hybridization_ext.cpp
|
||||
intrules.cpp
|
||||
intrules_cut.cpp
|
||||
ceed/interface/basis.cpp
|
||||
@@ -193,6 +198,7 @@ set(HDRS
|
||||
fe/fe_nd.hpp
|
||||
fe/fe_nurbs.hpp
|
||||
fe/fe_pos.hpp
|
||||
fe/fe_pyramid.hpp
|
||||
fe/fe_rt.hpp
|
||||
fe/fe_ser.hpp
|
||||
fe_coll.hpp
|
||||
@@ -201,6 +207,7 @@ set(HDRS
|
||||
geom.hpp
|
||||
gridfunc.hpp
|
||||
hybridization.hpp
|
||||
hybridization_ext.hpp
|
||||
intrules.hpp
|
||||
intrules_cut.hpp
|
||||
kernel_dispatch.hpp
|
||||
|
||||
+2035
File diff suppressed because it is too large
Load Diff
+75
-50
@@ -71,15 +71,11 @@ BilinearForm::BilinearForm(FiniteElementSpace * f)
|
||||
sequence = f->GetSequence();
|
||||
mat = mat_e = NULL;
|
||||
extern_bfs = 0;
|
||||
element_matrices = NULL;
|
||||
static_cond = NULL;
|
||||
hybridization = NULL;
|
||||
precompute_sparsity = 0;
|
||||
diag_policy = DIAG_KEEP;
|
||||
|
||||
assembly = AssemblyLevel::LEGACY;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
}
|
||||
|
||||
BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
|
||||
@@ -89,15 +85,11 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
|
||||
sequence = f->GetSequence();
|
||||
mat_e = NULL;
|
||||
extern_bfs = 1;
|
||||
element_matrices = NULL;
|
||||
static_cond = NULL;
|
||||
hybridization = NULL;
|
||||
precompute_sparsity = ps;
|
||||
diag_policy = DIAG_KEEP;
|
||||
|
||||
assembly = AssemblyLevel::LEGACY;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
domain_integs = bf->domain_integs;
|
||||
@@ -127,16 +119,16 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
break;
|
||||
case AssemblyLevel::FULL:
|
||||
SetDiagonalPolicy( DIAG_ONE ); // Only diagonal policy supported on device
|
||||
ext = new FABilinearFormExtension(this);
|
||||
ext.reset(new FABilinearFormExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
ext = new EABilinearFormExtension(this);
|
||||
ext.reset(new EABilinearFormExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PABilinearFormExtension(this);
|
||||
ext.reset(new PABilinearFormExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::NONE:
|
||||
ext = new MFBilinearFormExtension(this);
|
||||
ext.reset(new MFBilinearFormExtension(this));
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("BilinearForm: unknown assembly level");
|
||||
@@ -145,14 +137,13 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
|
||||
void BilinearForm::EnableStaticCondensation()
|
||||
{
|
||||
delete static_cond;
|
||||
if (assembly != AssemblyLevel::LEGACY)
|
||||
{
|
||||
static_cond = NULL;
|
||||
static_cond.reset();
|
||||
MFEM_WARNING("Static condensation not supported for this assembly level");
|
||||
return;
|
||||
}
|
||||
static_cond = new StaticCondensation(fes);
|
||||
static_cond.reset(new StaticCondensation(fes));
|
||||
if (static_cond->ReducesTrueVSize())
|
||||
{
|
||||
bool symmetric = false; // TODO
|
||||
@@ -161,8 +152,7 @@ void BilinearForm::EnableStaticCondensation()
|
||||
}
|
||||
else
|
||||
{
|
||||
delete static_cond;
|
||||
static_cond = NULL;
|
||||
static_cond.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,15 +160,18 @@ void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
|
||||
BilinearFormIntegrator *constr_integ,
|
||||
const Array<int> &ess_tdof_list)
|
||||
{
|
||||
delete hybridization;
|
||||
if (assembly != AssemblyLevel::LEGACY)
|
||||
if (assembly != AssemblyLevel::LEGACY && assembly != AssemblyLevel::ELEMENT)
|
||||
{
|
||||
delete constr_integ;
|
||||
hybridization = NULL;
|
||||
hybridization.reset();
|
||||
MFEM_WARNING("Hybridization not supported for this assembly level");
|
||||
return;
|
||||
}
|
||||
hybridization = new Hybridization(fes, constr_space);
|
||||
hybridization.reset(new Hybridization(fes, constr_space));
|
||||
if (assembly == AssemblyLevel::ELEMENT)
|
||||
{
|
||||
hybridization->EnableDeviceExecution();
|
||||
}
|
||||
hybridization->SetConstraintIntegrator(constr_integ);
|
||||
hybridization->Init(ess_tdof_list);
|
||||
}
|
||||
@@ -231,8 +224,8 @@ void BilinearForm::Finalize (int skip_zeros)
|
||||
if (!static_cond) { mat->Finalize(skip_zeros); }
|
||||
if (mat_e) { mat_e->Finalize(skip_zeros); }
|
||||
if (static_cond) { static_cond->Finalize(); }
|
||||
if (hybridization) { hybridization->Finalize(); }
|
||||
}
|
||||
if (hybridization) { hybridization->Finalize(); }
|
||||
}
|
||||
|
||||
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
|
||||
@@ -465,6 +458,10 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
if (ext)
|
||||
{
|
||||
ext->Assemble();
|
||||
if (hybridization)
|
||||
{
|
||||
hybridization->AssembleElementMatrices(GetElementMatrices());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -835,7 +832,19 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
|
||||
if (hybridization)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
ConstrainedOperator A_constrained(this, ess_tdof_list);
|
||||
A_constrained.EliminateRHS(x, b);
|
||||
hybridization->ReduceRHS(b, B);
|
||||
X.SetSize(B.Size());
|
||||
X = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
@@ -903,7 +912,16 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->FormSystemMatrix(ess_tdof_list, A);
|
||||
if (hybridization)
|
||||
{
|
||||
const int remove_zeros = 0;
|
||||
Finalize(remove_zeros);
|
||||
A.Reset(&hybridization->GetMatrix(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ext->FormSystemMatrix(ess_tdof_list, A);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -944,7 +962,7 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
void BilinearForm::RecoverFEMSolution(const Vector &X,
|
||||
const Vector &b, Vector &x)
|
||||
{
|
||||
if (ext)
|
||||
if (ext && !hybridization)
|
||||
{
|
||||
ext->RecoverFEMSolution(X, b, x);
|
||||
return;
|
||||
@@ -1001,16 +1019,26 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
|
||||
|
||||
void BilinearForm::ComputeElementMatrices()
|
||||
{
|
||||
if (element_matrices || domain_integs.Size() == 0 || fes->GetNE() == 0)
|
||||
if (element_matrices) { return; }
|
||||
|
||||
if (auto *ea_ext = dynamic_cast<EABilinearFormExtension*>(ext.get()))
|
||||
{
|
||||
element_matrices.reset(new DenseTensor);
|
||||
ea_ext->GetElementMatrices(*element_matrices, ElementDofOrdering::NATIVE, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (domain_integs.Size() == 0 || fes->GetNE() == 0)
|
||||
{
|
||||
element_matrices.reset(new DenseTensor);
|
||||
return;
|
||||
}
|
||||
|
||||
int num_elements = fes->GetNE();
|
||||
int num_dofs_per_el = fes->GetTypicalFE()->GetDof() * fes->GetVDim();
|
||||
|
||||
element_matrices = new DenseTensor(num_dofs_per_el, num_dofs_per_el,
|
||||
num_elements);
|
||||
element_matrices.reset(new DenseTensor(num_dofs_per_el, num_dofs_per_el,
|
||||
num_elements));
|
||||
|
||||
DenseMatrix tmp;
|
||||
IsoparametricTransformation eltrans;
|
||||
@@ -1041,6 +1069,12 @@ void BilinearForm::ComputeElementMatrices()
|
||||
}
|
||||
}
|
||||
|
||||
const DenseTensor &BilinearForm::GetElementMatrices()
|
||||
{
|
||||
ComputeElementMatrices(); // Won't recompute if element_matrices exists
|
||||
return *element_matrices;
|
||||
}
|
||||
|
||||
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy)
|
||||
@@ -1228,15 +1262,13 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
|
||||
delete mat_e;
|
||||
mat_e = NULL;
|
||||
FreeElementMatrices();
|
||||
delete static_cond;
|
||||
static_cond = NULL;
|
||||
static_cond.reset();
|
||||
|
||||
if (full_update)
|
||||
{
|
||||
delete mat;
|
||||
mat = NULL;
|
||||
delete hybridization;
|
||||
hybridization = NULL;
|
||||
hybridization.reset();
|
||||
sequence = fes->GetSequence();
|
||||
}
|
||||
else
|
||||
@@ -1259,9 +1291,6 @@ BilinearForm::~BilinearForm()
|
||||
{
|
||||
delete mat_e;
|
||||
delete mat;
|
||||
delete element_matrices;
|
||||
delete static_cond;
|
||||
delete hybridization;
|
||||
|
||||
if (!extern_bfs)
|
||||
{
|
||||
@@ -1273,8 +1302,6 @@ BilinearForm::~BilinearForm()
|
||||
for (k=0; k < boundary_face_integs.Size(); k++)
|
||||
{ delete boundary_face_integs[k]; }
|
||||
}
|
||||
|
||||
delete ext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1301,7 +1328,6 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
|
||||
mat = NULL;
|
||||
mat_e = NULL;
|
||||
extern_bfs = 1;
|
||||
ext = NULL;
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
domain_integs = mbf->domain_integs;
|
||||
@@ -1331,22 +1357,22 @@ void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
case AssemblyLevel::LEGACY:
|
||||
break;
|
||||
case AssemblyLevel::FULL:
|
||||
// ext = new FAMixedBilinearFormExtension(this);
|
||||
// ext.reset(new FAMixedBilinearFormExtension(this));
|
||||
// Use the original BilinearForm implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
mfem_error("Element assembly not supported yet... stay tuned!");
|
||||
// ext = new EAMixedBilinearFormExtension(this);
|
||||
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
|
||||
// ext.reset(new EAMixedBilinearFormExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PAMixedBilinearFormExtension(this);
|
||||
ext.reset(new PAMixedBilinearFormExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::NONE:
|
||||
mfem_error("Matrix-free action not supported yet... stay tuned!");
|
||||
// ext = new MFMixedBilinearFormExtension(this);
|
||||
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
|
||||
// ext.reset(new MFMixedBilinearFormExtension(this));
|
||||
break;
|
||||
default:
|
||||
mfem_error("Unknown assembly level");
|
||||
MFEM_ABORT("Unknown assembly level");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2343,7 +2369,6 @@ MixedBilinearForm::~MixedBilinearForm()
|
||||
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
|
||||
{ delete boundary_trace_face_integs[i]; }
|
||||
}
|
||||
delete ext;
|
||||
}
|
||||
|
||||
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
@@ -2360,16 +2385,16 @@ void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
// Use the original implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
mfem_error("Element assembly not supported yet... stay tuned!");
|
||||
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PADiscreteLinearOperatorExtension(this);
|
||||
ext.reset(new PADiscreteLinearOperatorExtension(this));
|
||||
break;
|
||||
case AssemblyLevel::NONE:
|
||||
mfem_error("Matrix-free action not supported yet... stay tuned!");
|
||||
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
|
||||
break;
|
||||
default:
|
||||
mfem_error("Unknown assembly level");
|
||||
MFEM_ABORT("Unknown assembly level");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-13
@@ -83,7 +83,7 @@ protected:
|
||||
/** @brief Extension for supporting Full Assembly (FA),
|
||||
Element Assembly (EA),Partial Assembly (PA),
|
||||
or Matrix Free assembly (MF). */
|
||||
BilinearFormExtension *ext;
|
||||
std::unique_ptr<BilinearFormExtension> ext;
|
||||
|
||||
/** Indicates if the sparse matrix is sorted after assembly when using
|
||||
Full Assembly (FA). */
|
||||
@@ -122,10 +122,10 @@ protected:
|
||||
mutable DenseMatrix elemmat;
|
||||
mutable Array<int> vdofs;
|
||||
|
||||
DenseTensor *element_matrices; ///< Owned.
|
||||
std::unique_ptr<DenseTensor> element_matrices;
|
||||
|
||||
StaticCondensation *static_cond; ///< Owned.
|
||||
Hybridization *hybridization; ///< Owned.
|
||||
std::unique_ptr<StaticCondensation> static_cond;
|
||||
std::unique_ptr<Hybridization> hybridization;
|
||||
|
||||
/** @brief This data member allows one to specify what should be done to the
|
||||
diagonal matrix entries and corresponding RHS values upon elimination of
|
||||
@@ -148,13 +148,11 @@ protected:
|
||||
BilinearForm() : Matrix (0)
|
||||
{
|
||||
fes = NULL; sequence = -1;
|
||||
mat = mat_e = NULL; extern_bfs = 0; element_matrices = NULL;
|
||||
static_cond = NULL; hybridization = NULL;
|
||||
mat = mat_e = NULL; extern_bfs = 0;
|
||||
precompute_sparsity = 0;
|
||||
diag_policy = DIAG_KEEP;
|
||||
assembly = AssemblyLevel::LEGACY;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -214,7 +212,7 @@ public:
|
||||
/// Returns the assembly level
|
||||
AssemblyLevel GetAssemblyLevel() const { return assembly; }
|
||||
|
||||
Hybridization *GetHybridization() const { return hybridization; }
|
||||
Hybridization *GetHybridization() const { return hybridization.get(); }
|
||||
|
||||
/** @brief Enable the use of static condensation. For details see the
|
||||
description for class StaticCondensation in fem/staticcond.hpp This
|
||||
@@ -224,7 +222,7 @@ public:
|
||||
|
||||
/** @brief Check if static condensation was actually enabled by a previous
|
||||
call to EnableStaticCondensation(). */
|
||||
bool StaticCondensationIsEnabled() const { return static_cond; }
|
||||
bool StaticCondensationIsEnabled() const { return static_cond != nullptr; }
|
||||
|
||||
/// Return the trace FE space associated with static condensation.
|
||||
FiniteElementSpace *SCFESpace() const
|
||||
@@ -569,12 +567,20 @@ public:
|
||||
void RecoverFEMSolution(const Vector &X, const Vector &b,
|
||||
Vector &x) override;
|
||||
|
||||
/// Compute and store internally all element matrices.
|
||||
/// @brief Compute and store internally all element matrices.
|
||||
///
|
||||
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
|
||||
/// use efficient (device-accelerated) assembly of the element matrices.
|
||||
void ComputeElementMatrices();
|
||||
|
||||
/// Free the memory used by the element matrices.
|
||||
void FreeElementMatrices()
|
||||
{ delete element_matrices; element_matrices = NULL; }
|
||||
void FreeElementMatrices() { element_matrices.reset(); }
|
||||
|
||||
/// @brief Return a DenseTensor containing the assembled element matrices.
|
||||
///
|
||||
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
|
||||
/// use efficient (device-accelerated) assembly of the element matrices.
|
||||
const DenseTensor &GetElementMatrices();
|
||||
|
||||
/// Compute the element matrix of the given element
|
||||
/** The element matrix is computed by calling the domain integrators
|
||||
@@ -760,7 +766,7 @@ protected:
|
||||
|
||||
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
|
||||
Partial Assembly (PA), or Matrix Free assembly (MF). */
|
||||
MixedBilinearFormExtension *ext;
|
||||
std::unique_ptr<MixedBilinearFormExtension> ext;
|
||||
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in
|
||||
MixedBilinearForm#domain_integs, MixedBilinearForm#boundary_integs,
|
||||
|
||||
+288
-78
@@ -16,6 +16,7 @@
|
||||
#include "bilinearform.hpp"
|
||||
#include "pbilinearform.hpp"
|
||||
#include "pgridfunc.hpp"
|
||||
#include "fe/face_map_utils.hpp"
|
||||
#include "ceed/interface/util.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -864,52 +865,137 @@ void EABilinearFormExtension::Assemble()
|
||||
ne = trial_fes->GetMesh()->GetNE();
|
||||
elemDofs = trial_fes->GetTypicalFE()->GetDof();
|
||||
|
||||
ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
|
||||
ea_data.UseDevice(true);
|
||||
Vector ea_data_tmp;
|
||||
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int integratorCount = integrators.Size();
|
||||
if ( integratorCount == 0 )
|
||||
auto add_with_markers = [&](const Vector &ea_1, Vector &ea_2, const int ne_,
|
||||
const Array<int> &markers, const Array<int> &attrs,
|
||||
const bool add)
|
||||
{
|
||||
ea_data = 0.0;
|
||||
}
|
||||
for (int i = 0; i < integratorCount; ++i)
|
||||
if (ne_ == 0) { return; }
|
||||
const int sz = ea_1.Size() / ne_;
|
||||
const int *d_m = markers.Read();
|
||||
const int *d_a = attrs.Read();
|
||||
const auto d_ea_1 = Reshape(ea_1.Read(), sz, ne_);
|
||||
auto d_ea_2 = Reshape(add ? ea_2.ReadWrite() : ea_2.Write(), sz, ne_);
|
||||
|
||||
mfem::forall(sz*ne_, [=] MFEM_HOST_DEVICE (int idx)
|
||||
{
|
||||
const int i = idx % sz;
|
||||
const int e = idx / sz;
|
||||
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
|
||||
if (add)
|
||||
{
|
||||
d_ea_2(i, e) += val;
|
||||
}
|
||||
else
|
||||
{
|
||||
d_ea_2(i, e) = val;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
{
|
||||
integrators[i]->AssembleEA(*a->FESpace(), ea_data, i);
|
||||
ea_data.SetSize(ne*elemDofs*elemDofs);
|
||||
ea_data.UseDevice(true);
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
Array<Array<int>*> &markers_array = *a->GetDBFI_Marker();
|
||||
|
||||
if (integrators.Size() == 0) { ea_data = 0.0; }
|
||||
|
||||
for (int i = 0; i < integrators.Size(); ++i)
|
||||
{
|
||||
const bool add = (i > 0);
|
||||
const Array<int> *markers = markers_array[i];
|
||||
if (markers == nullptr)
|
||||
{
|
||||
integrators[i]->AssembleEA(*a->FESpace(), ea_data, add);
|
||||
}
|
||||
else
|
||||
{
|
||||
ea_data_tmp.SetSize(ea_data.Size());
|
||||
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
|
||||
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
|
||||
elem_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
faceDofs = trial_fes->GetTypicalTraceElement()->GetDof();
|
||||
|
||||
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
|
||||
"Element assembly does not support AddBoundaryIntegrator yet.");
|
||||
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int intFaceIntegratorCount = intFaceIntegrators.Size();
|
||||
if (intFaceIntegratorCount>0)
|
||||
{
|
||||
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
|
||||
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
}
|
||||
for (int i = 0; i < intFaceIntegratorCount; ++i)
|
||||
{
|
||||
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
|
||||
ea_data_int,
|
||||
ea_data_ext,
|
||||
i);
|
||||
Array<BilinearFormIntegrator*> &bdr_integs = *a->GetBBFI();
|
||||
Array<Array<int>*> &markers_array = *a->GetBBFI_Marker();
|
||||
const int n_bdr_integs = bdr_integs.Size();
|
||||
if (n_bdr_integs > 0)
|
||||
{
|
||||
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
|
||||
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
|
||||
}
|
||||
for (int i = 0; i < n_bdr_integs; ++i)
|
||||
{
|
||||
const bool add = (i > 0);
|
||||
const Array<int> *markers = markers_array[i];
|
||||
if (markers == nullptr)
|
||||
{
|
||||
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_bdr, add);
|
||||
}
|
||||
else
|
||||
{
|
||||
ea_data_tmp.SetSize(ea_data_bdr.Size());
|
||||
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
|
||||
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
|
||||
bdr_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
|
||||
if (boundFaceIntegratorCount>0)
|
||||
{
|
||||
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
|
||||
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
ea_data_bdr = 0.0;
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int intFaceIntegratorCount = intFaceIntegrators.Size();
|
||||
if (intFaceIntegratorCount>0)
|
||||
{
|
||||
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
|
||||
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs);
|
||||
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs);
|
||||
}
|
||||
for (int i = 0; i < intFaceIntegratorCount; ++i)
|
||||
{
|
||||
const bool add = (i > 0);
|
||||
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
|
||||
ea_data_int,
|
||||
ea_data_ext,
|
||||
add);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < boundFaceIntegratorCount; ++i)
|
||||
|
||||
{
|
||||
bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr,i);
|
||||
Array<BilinearFormIntegrator*> &bdr_face_integs = *a->GetBFBFI();
|
||||
Array<Array<int>*> &markers_array = *a->GetBFBFI_Marker();
|
||||
const int n_bdr_face_integs = bdr_face_integs.Size();
|
||||
if (n_bdr_face_integs > 0)
|
||||
{
|
||||
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
|
||||
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
|
||||
}
|
||||
for (int i = 0; i < n_bdr_face_integs; ++i)
|
||||
{
|
||||
const bool add = (i > 0);
|
||||
const Array<int> *markers = markers_array[i];
|
||||
if (markers == nullptr)
|
||||
{
|
||||
bdr_face_integs[i]->AssembleEABoundaryFaces(
|
||||
*a->FESpace(), ea_data_bdr, add);
|
||||
}
|
||||
else
|
||||
{
|
||||
ea_data_tmp.SetSize(ea_data_bdr.Size());
|
||||
bdr_face_integs[i]->AssembleEABoundaryFaces(*a->FESpace(),
|
||||
ea_data_tmp,
|
||||
add);
|
||||
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
|
||||
bdr_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (factorize_face_terms && int_face_restrict_lex)
|
||||
@@ -1021,34 +1107,29 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
|
||||
// Treatment of boundary faces
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int bFISz = bdrFaceIntegrators.Size();
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
|
||||
{
|
||||
// Apply the Boundary Face Restriction
|
||||
bdr_face_restrict_lex->Mult(x, bdr_face_X);
|
||||
if (bdr_face_X.Size()>0)
|
||||
bdr_face_Y = 0.0;
|
||||
// Apply the boundary face matrices
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
bdr_face_Y = 0.0;
|
||||
// Apply the boundary face matrices
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(i, j, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
}
|
||||
res += A(i, j, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1149,35 +1230,164 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
}
|
||||
|
||||
// Treatment of boundary faces
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int bFISz = bdrFaceIntegrators.Size();
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
|
||||
{
|
||||
// Apply the Boundary Face Restriction
|
||||
bdr_face_restrict_lex->Mult(x, bdr_face_X);
|
||||
if (bdr_face_X.Size()>0)
|
||||
bdr_face_Y = 0.0;
|
||||
// Apply the boundary face matrices transposed
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
bdr_face_Y = 0.0;
|
||||
// Apply the boundary face matrices transposed
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(j, i, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
res += A(j, i, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::GetElementMatrices(
|
||||
DenseTensor &element_matrices, ElementDofOrdering ordering, bool add_bdr)
|
||||
{
|
||||
// Ensure the EA data is assembled
|
||||
if (ea_data.Size() == 0) { Assemble(); }
|
||||
|
||||
const int ndofs = elemDofs;
|
||||
element_matrices.SetSize(ndofs, ndofs, ne);
|
||||
const int N = element_matrices.TotalSize();
|
||||
|
||||
const auto d_ea_data = Reshape(ea_data.Read(), ndofs, ndofs, ne);
|
||||
auto d_element_matrices = Reshape(element_matrices.Write(),
|
||||
ndofs, ndofs,
|
||||
ne);
|
||||
|
||||
const int *d_dof_map = nullptr;
|
||||
Array<int> dof_map;
|
||||
if (ordering == ElementDofOrdering::NATIVE)
|
||||
{
|
||||
const TensorBasisElement* tbe =
|
||||
dynamic_cast<const TensorBasisElement*>(trial_fes->GetFE(0));
|
||||
if (tbe)
|
||||
{
|
||||
// Deep copy to avoid issues with host device (see similar comment in
|
||||
// HybridizationExtension::ConstructC).
|
||||
dof_map = tbe->GetDofMap();
|
||||
d_dof_map = dof_map.Read();
|
||||
}
|
||||
}
|
||||
|
||||
if (d_dof_map)
|
||||
{
|
||||
// Reordering required
|
||||
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
|
||||
{
|
||||
const int e = idx / ndofs / ndofs;
|
||||
const int i = idx % ndofs;
|
||||
const int j = (idx / ndofs) % ndofs;
|
||||
const int ii_s = d_dof_map[i];
|
||||
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
|
||||
const int s_i = (ii_s >= 0) ? 1 : -1;
|
||||
const int jj_s = d_dof_map[j];
|
||||
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
|
||||
const int s_j = (jj_s >= 0) ? 1 : -1;
|
||||
d_element_matrices(ii, jj, e) = s_i*s_j*d_ea_data(j, i, e);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// No reordering required
|
||||
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
|
||||
{
|
||||
const int e = idx / ndofs / ndofs;
|
||||
const int i = idx % ndofs;
|
||||
const int j = (idx / ndofs) % ndofs;
|
||||
d_element_matrices(i, j, e) = d_ea_data(j, i, e);
|
||||
});
|
||||
}
|
||||
|
||||
if (add_bdr && ea_data_bdr.Size() > 0)
|
||||
{
|
||||
const int ndof_face = faceDofs;
|
||||
const auto d_ea_bdr = Reshape(ea_data_bdr.Read(),
|
||||
ndof_face, ndof_face, nf_bdr);
|
||||
|
||||
// Get all the local face maps (mapping from lexicographic face index to
|
||||
// lexicographic volume index, depending on the local face index).
|
||||
const Mesh &mesh = *trial_fes->GetMesh();
|
||||
const int dim = mesh.Dimension();
|
||||
const int n_faces_per_el = 2*dim; // assuming tensor product
|
||||
Array<int> face_maps(ndof_face * n_faces_per_el);
|
||||
for (int lf_i = 0; lf_i < n_faces_per_el; ++lf_i)
|
||||
{
|
||||
Array<int> face_map(ndof_face);
|
||||
trial_fes->GetFE(0)->GetFaceMap(lf_i, face_map);
|
||||
for (int i = 0; i < ndof_face; ++i)
|
||||
{
|
||||
face_maps[i + lf_i*ndof_face] = face_map[i];
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> face_info(nf_bdr * 2);
|
||||
{
|
||||
int fidx = 0;
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
|
||||
if (!finfo.IsBoundary()) { continue; }
|
||||
face_info[0 + fidx*2] = finfo.element[0].local_face_id;
|
||||
face_info[1 + fidx*2] = finfo.element[0].index;
|
||||
fidx++;
|
||||
}
|
||||
}
|
||||
|
||||
const auto d_face_maps = Reshape(face_maps.Read(), ndof_face, n_faces_per_el);
|
||||
const auto d_face_info = Reshape(face_info.Read(), 2, nf_bdr);
|
||||
|
||||
const bool reorder = (ordering == ElementDofOrdering::NATIVE);
|
||||
|
||||
mfem::forall_2D(nf_bdr, ndof_face, ndof_face, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
const int lf_i = d_face_info(0, f);
|
||||
const int e = d_face_info(1, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex_face, x, ndof_face)
|
||||
{
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i_lex = d_face_maps(i_lex_face, lf_i);
|
||||
|
||||
const int ii_s = d_dof_map[i_lex];
|
||||
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
|
||||
|
||||
const int i = reorder ? ii : i_lex;
|
||||
const int s_i = (ii_s < 0 && reorder) ? -1 : 1;
|
||||
|
||||
MFEM_FOREACH_THREAD(j_lex_face, y, ndof_face)
|
||||
{
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int j_lex = d_face_maps(j_lex_face, lf_i);
|
||||
|
||||
const int jj_s = d_dof_map[j_lex];
|
||||
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
|
||||
|
||||
const int j = reorder ? jj : j_lex;
|
||||
const int s_j = (jj_s < 0 && reorder) ? -1 : 1;
|
||||
|
||||
AtomicAdd(d_element_matrices(i, j, e),
|
||||
s_i*s_j*d_ea_bdr(i_lex_face, j_lex_face, f));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Data and methods for fully-assembled bilinear forms
|
||||
|
||||
@@ -154,6 +154,17 @@ public:
|
||||
void Assemble() override;
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
/// @brief Populates @a element_matrices with the element matrices.
|
||||
///
|
||||
/// The element matrices are converted from row-major (how they are stored in
|
||||
/// @a ea_data) to column-major format.
|
||||
///
|
||||
/// If @a ordering is ElementDofOrdering::NATIVE, then the matrices are
|
||||
/// reordered from the lexicographic ordering used internally.
|
||||
void GetElementMatrices(DenseTensor &element_matrices,
|
||||
ElementDofOrdering ordering,
|
||||
bool add_bdr);
|
||||
};
|
||||
|
||||
/// Data and methods for fully-assembled bilinear forms
|
||||
|
||||
+31
-5
@@ -72,6 +72,14 @@ void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEABoundary(const FiniteElementSpace &fes,
|
||||
Vector &emat,
|
||||
const bool add)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleEABoundary(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
|
||||
&fes,
|
||||
Vector &ea_data_int,
|
||||
@@ -82,6 +90,16 @@ void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEAInteriorFaces(
|
||||
const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes,
|
||||
Vector &emat,
|
||||
const bool add)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
|
||||
&fes,
|
||||
Vector &ea_data_bdr,
|
||||
@@ -2829,17 +2847,18 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
dim = trial_fe.GetDim();
|
||||
sdim = Trans.GetSpaceDim();
|
||||
int trial_dof = trial_fe.GetDof();
|
||||
int test_dof = test_fe.GetDof();
|
||||
real_t c;
|
||||
|
||||
dshape.SetSize (trial_dof, dim);
|
||||
gshape.SetSize (trial_dof, dim);
|
||||
Jadj.SetSize (dim);
|
||||
divshape.SetSize (dim*trial_dof);
|
||||
gshape.SetSize (trial_dof, sdim);
|
||||
Jadj.SetSize (dim, sdim);
|
||||
divshape.SetSize (sdim*trial_dof);
|
||||
shape.SetSize (test_dof);
|
||||
|
||||
elmat.SetSize (test_dof, dim*trial_dof);
|
||||
elmat.SetSize (test_dof, sdim*trial_dof);
|
||||
|
||||
const IntegrationRule *ir = GetIntegrationRule(trial_fe, test_fe, Trans);
|
||||
|
||||
@@ -2853,13 +2872,15 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
|
||||
trial_fe.CalcDShape (ip, dshape);
|
||||
test_fe.CalcPhysShape (Trans, shape);
|
||||
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
CalcAdjugate(Trans.Jacobian(), Jadj);
|
||||
|
||||
Mult (dshape, Jadj, gshape);
|
||||
|
||||
gshape.GradToDiv (divshape);
|
||||
|
||||
c = ip.weight;
|
||||
if (dim != sdim) { c /= Trans.Weight(); }
|
||||
if (Q)
|
||||
{
|
||||
c *= Q -> Eval (Trans, ip);
|
||||
@@ -2900,6 +2921,11 @@ void DivDivIntegrator::AssembleElementMatrix(
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order = 2 * el.GetOrder() - 2; // <--- OK for RTk
|
||||
if (el.Space() == FunctionSpace::Uk)
|
||||
{
|
||||
order += 2;
|
||||
}
|
||||
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
|
||||
|
||||
+39
-7
@@ -124,11 +124,25 @@ public:
|
||||
/// Assemble diagonal and add it to Vector @a diag.
|
||||
virtual void AssembleDiagonalMF(Vector &diag);
|
||||
|
||||
virtual void AssembleEABoundary(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add = true);
|
||||
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add = true);
|
||||
|
||||
/// @brief Method defining element assembly for mixed trace integrators.
|
||||
///
|
||||
/// This is the element assembly analogue of AssembleFaceMatrix(const
|
||||
/// FiniteElement&, const FiniteElement&, const FiniteElement&,
|
||||
/// FaceElementTransformations&, DenseMatrix&).
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes,
|
||||
Vector &emat,
|
||||
const bool add = true);
|
||||
|
||||
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add = true);
|
||||
@@ -383,6 +397,7 @@ public:
|
||||
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleEAInteriorFaces;
|
||||
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
@@ -494,6 +509,7 @@ public:
|
||||
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleEAInteriorFaces;
|
||||
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
@@ -2350,6 +2366,8 @@ protected:
|
||||
const FaceGeometricFactors *face_geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
|
||||
void AssembleEA_(Vector &ea, const bool add);
|
||||
|
||||
public:
|
||||
|
||||
using ApplyKernelType = void(*)(const int, const Array<real_t>&,
|
||||
@@ -2390,7 +2408,10 @@ public:
|
||||
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
void AssembleDiagonalPA(Vector &diag) override;
|
||||
virtual void AssembleEABoundary(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
virtual void AssembleDiagonalPA(Vector &diag) override;
|
||||
|
||||
void AssembleDiagonalMF(Vector &diag) override;
|
||||
|
||||
@@ -2914,6 +2935,8 @@ public:
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
void AssembleDiagonalPA(Vector& diag) override;
|
||||
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
const Coefficient *GetCoefficient() const { return Q; }
|
||||
};
|
||||
@@ -2935,7 +2958,7 @@ private:
|
||||
Vector pa_data;
|
||||
const DofToQuad *trial_maps, *test_maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq;
|
||||
int dim, sdim, ne, nq;
|
||||
int trial_dofs1D, test_dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
@@ -2981,11 +3004,6 @@ class DivDivIntegrator: public BilinearFormIntegrator
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AssembleDiagonalPA(Vector& diag) override;
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape, te_divshape;
|
||||
@@ -3012,6 +3030,13 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AssembleDiagonalPA(Vector& diag) override;
|
||||
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add) override;
|
||||
|
||||
const Coefficient *GetCoefficient() const { return Q; }
|
||||
};
|
||||
|
||||
@@ -3311,6 +3336,7 @@ public:
|
||||
|
||||
void AddMultPA(const Vector&, Vector&) const override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleEAInteriorFaces;
|
||||
void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
@@ -3624,6 +3650,12 @@ public:
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleEAInteriorFaces;
|
||||
void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes,
|
||||
Vector &emat,
|
||||
const bool add = true) override;
|
||||
};
|
||||
|
||||
/** Integrator for the DPG form:$ \langle v, w \rangle $ over a face (the interface) where
|
||||
|
||||
+4
-4
@@ -487,7 +487,7 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
|
||||
if (RealInteg() && ImagInteg())
|
||||
{
|
||||
// Modify RHS and offdiagonal blocks (imaginary parts of the matrix) to
|
||||
// Modify RHS and off-diagonal blocks (imaginary parts of the matrix) to
|
||||
// conform with standard essential BC treatment
|
||||
if (A_i.Is<ConstrainedOperator>())
|
||||
{
|
||||
@@ -576,7 +576,7 @@ SesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
|
||||
if (RealInteg() && ImagInteg())
|
||||
{
|
||||
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
|
||||
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
|
||||
// with standard essential BC treatment
|
||||
if (A_i.Is<ConstrainedOperator>())
|
||||
{
|
||||
@@ -1236,7 +1236,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
d_B_r[j] = d_X_r[j];
|
||||
d_B_i[j] = d_X_i[j];
|
||||
});
|
||||
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
|
||||
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
|
||||
// with standard essential BC treatment
|
||||
if (A_i.Type() == Operator::Hypre_ParCSR)
|
||||
{
|
||||
@@ -1324,7 +1324,7 @@ ParSesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
|
||||
if (RealInteg() && ImagInteg())
|
||||
{
|
||||
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
|
||||
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
|
||||
// with standard essential BC treatment
|
||||
if ( A_i.Type() == Operator::Hypre_ParCSR )
|
||||
{
|
||||
|
||||
+459
-7
@@ -27,6 +27,9 @@ using action_t =
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
using assemble_derivative_hypreparmatrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
|
||||
|
||||
using restriction_callback_t =
|
||||
std::function<void(std::vector<Vector> &,
|
||||
const std::vector<Vector> &,
|
||||
@@ -47,15 +50,20 @@ public:
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose) :
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
Operator(height, width),
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
daction_l(daction_l_size),
|
||||
daction_l_size(daction_l_size),
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
transpose_direction(transpose_direction),
|
||||
daction_transpose_l(daction_transpose_l_size),
|
||||
prolongation_transpose(prolongation_transpose)
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
@@ -75,7 +83,7 @@ public:
|
||||
|
||||
void Mult(const Vector &direction_t, Vector &y) const override
|
||||
{
|
||||
// daction_l.SetSize(height);
|
||||
daction_l.SetSize(daction_l_size);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(direction, direction_t, direction_l);
|
||||
@@ -88,7 +96,7 @@ public:
|
||||
|
||||
void MultTranspose(const Vector &direction_t, Vector &y) const override
|
||||
{
|
||||
// daction_l.SetSize(width);
|
||||
daction_l.SetSize(width);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(transpose_direction, direction_t, direction_l);
|
||||
@@ -99,20 +107,36 @@ public:
|
||||
prolongation_transpose(daction_l, y);
|
||||
};
|
||||
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a matrix");
|
||||
|
||||
for (int i = 0; i < assemble_derivative_hypreparmatrix_callbacks.size(); i++)
|
||||
{
|
||||
assemble_derivative_hypreparmatrix_callbacks[i](fields_e, A);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<derivative_action_t> derivative_actions;
|
||||
FieldDescriptor direction;
|
||||
mutable Vector daction_l;
|
||||
const int daction_l_size;
|
||||
|
||||
std::vector<derivative_action_t> derivative_actions_transpose;
|
||||
FieldDescriptor transpose_direction;
|
||||
mutable Vector daction_transpose_l;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
|
||||
mutable Vector direction_l;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
};
|
||||
|
||||
class DifferentiableOperator : public Operator
|
||||
@@ -179,11 +203,12 @@ public:
|
||||
residual_l.Size(),
|
||||
daction_transpose_callbacks[derivative_id],
|
||||
fields[test_space_field_idx],
|
||||
GetTrueVSize(fields[test_space_field_idx]),
|
||||
GetVSize(fields[test_space_field_idx]),
|
||||
solutions_l,
|
||||
parameters_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose);
|
||||
prolongation_transpose,
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -194,6 +219,10 @@ private:
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
@@ -325,6 +354,11 @@ void DifferentiableOperator::AddDomainIntegrator(
|
||||
inputs_vdim[i] = mfem::get<i>(inputs).vdim;
|
||||
});
|
||||
|
||||
if ( mesh.GetNE() == 0)
|
||||
{
|
||||
MFEM_ABORT("Mesh with no elements is not yet supported!");
|
||||
}
|
||||
|
||||
Array<int> elem_attributes;
|
||||
elem_attributes.SetSize(mesh.GetNE());
|
||||
for (int i = 0; i < mesh.GetNE(); ++i)
|
||||
@@ -742,6 +776,424 @@ void DifferentiableOperator::AddDomainIntegrator(
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
|
||||
// Create assembly callbacks for derivatives
|
||||
// TODO: Host only for now
|
||||
for_constexpr([&](auto derivative_id)
|
||||
{
|
||||
// Field index of the derivative
|
||||
const size_t d_field_idx = FindIdx(derivative_id, fields);
|
||||
|
||||
// First Input index of the derivative
|
||||
const size_t d_input_idx = [d_field_idx, &input_to_field]
|
||||
{
|
||||
for (size_t i = 0; i < input_to_field.size(); i++)
|
||||
{
|
||||
if (input_to_field[i] == d_field_idx)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size_t(SIZE_MAX);
|
||||
}();
|
||||
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, element_dof_ordering, d_field_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
const auto input_is_dependent = dependency_map[derivative_id];
|
||||
|
||||
// auto dependent_input_dtq_maps =
|
||||
// get_marked_entries(input_dtq_maps, input_is_dependent);
|
||||
|
||||
const int trial_vdim = GetVDim(fields[d_field_idx]);
|
||||
|
||||
const int num_trial_dof_1d =
|
||||
input_dtq_maps[d_input_idx].B.GetShape()[DofToQuadMap::Index::DOF];
|
||||
|
||||
const int num_trial_dof =
|
||||
get_restriction<entity_t>(fields[d_field_idx], element_dof_ordering)->Height() /
|
||||
inputs_vdim[d_input_idx] / num_entities;
|
||||
|
||||
int total_trial_op_dim = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
if (input_is_dependent[s] == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
total_trial_op_dim += input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
|
||||
});
|
||||
|
||||
const int da_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
|
||||
|
||||
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
|
||||
[=, fields = this->fields]
|
||||
(std::vector<Vector> &fields_e, HypreParMatrix &A) mutable
|
||||
{
|
||||
Vector direction_e(get_restriction<entity_t>(fields[d_field_idx],
|
||||
element_dof_ordering)->Height());
|
||||
|
||||
auto shmem = shmem_cache.ReadWrite();
|
||||
auto wrapped_fields_e = wrap_fields(fields_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size, num_entities);
|
||||
|
||||
Vector a_qp_mem(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim *
|
||||
num_qp * num_elements);
|
||||
auto a_qp = Reshape(a_qp_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_elements);
|
||||
|
||||
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
|
||||
num_elements);
|
||||
Ae_mem = 0.0;
|
||||
|
||||
auto A_e = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim, num_elements);
|
||||
|
||||
for (int e = 0; e < num_elements; e++)
|
||||
{
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, direction_shmem,
|
||||
input_shmem, shadow_shmem, residual_shmem, scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps,
|
||||
output_dtq_maps, wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
size_t m_offset = 0;
|
||||
for_constexpr_with_arg([&](auto s, auto&& input_fop)
|
||||
{
|
||||
if (input_is_dependent[s] == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
|
||||
|
||||
auto d_qp = Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
d_qp(j, m, q) = 1.0;
|
||||
|
||||
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
d_qp(j, m, q) = 0.0;
|
||||
|
||||
auto f = Reshape(&r(0), test_vdim, test_op_dim);
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
a_qp(i, k, j, m + m_offset, q, e) = f(i, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_offset += trial_op_dim;
|
||||
}, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
Vector fhat_mem(test_vdim * test_op_dim * num_qp);
|
||||
auto fhat = Reshape(fhat_mem.ReadWrite(), test_vdim, test_op_dim, num_qp);
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
for (int Jx = 0; Jx < num_trial_dof_1d; Jx++)
|
||||
{
|
||||
for (int Jy = 0; Jy < num_trial_dof_1d; Jy++)
|
||||
{
|
||||
const int J = Jy + Jx * num_trial_dof_1d;
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
fhat_mem = 0.0;
|
||||
size_t m_offset = 0;
|
||||
for_constexpr_with_arg([&](auto s, auto&& input_fop)
|
||||
{
|
||||
if (input_is_dependent[s] == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
|
||||
|
||||
auto &B = input_dtq_maps[s].B;
|
||||
auto &G = input_dtq_maps[s].G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<decltype(input_fop)>>::value)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<decltype(input_fop)>>::value)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
|
||||
if (m == 0)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy);
|
||||
}
|
||||
else
|
||||
{
|
||||
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for field operator");
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}, inputs);
|
||||
|
||||
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
bvtfhat, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for 3D");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int J = 0; J < num_trial_dof; J++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
fhat_mem = 0.0;
|
||||
size_t m_offset = 0;
|
||||
for_constexpr_with_arg([&](auto s, auto&& input_fop)
|
||||
{
|
||||
if (input_is_dependent[s] == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
|
||||
|
||||
auto B = is_value_fop<std::decay_t<decltype(input_fop)>>::value ?
|
||||
input_dtq_maps[s].B : input_dtq_maps[s].G;
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
|
||||
fhat(i, k, q) += f * B(q, m, J);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}, inputs);
|
||||
|
||||
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
bvtfhat, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool same_test_and_trial = false;
|
||||
for (int s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
if (output_to_field[0] == input_to_field[s])
|
||||
{
|
||||
same_test_and_trial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldDescriptor *trial_field = nullptr;
|
||||
for (int s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
trial_field = &fields[input_to_field[s]];
|
||||
}
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&trial_field->data);
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
SparseMatrix mat(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
|
||||
if (test_fes == nullptr)
|
||||
{
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
|
||||
// if (same_test_and_trial && use_sum_factorization)
|
||||
// {
|
||||
// const ElementRestriction &rest =
|
||||
// static_cast<const ElementRestriction&>(
|
||||
// *test_fes->GetElementRestriction(element_dof_ordering));
|
||||
// rest.FillSparseMatrix(Ae_mem, mat);
|
||||
// }
|
||||
// else
|
||||
{
|
||||
for (int e = 0; e < num_elements; e++)
|
||||
{
|
||||
auto tmp = Reshape(Ae_mem.ReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim, num_elements);
|
||||
DenseMatrix A_e(&tmp(0, 0, e), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim);
|
||||
|
||||
Array<int> test_vdofs, trial_vdofs;
|
||||
test_fes->GetElementVDofs(e, test_vdofs);
|
||||
GetElementVDofs(*trial_field, e, trial_vdofs);
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
Array<int> test_vdofs_mapped(test_vdofs.Size()),
|
||||
trial_vdofs_mapped(trial_vdofs.Size());
|
||||
|
||||
const Array<int> &test_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*test_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (test_dofmap.Size() == 0)
|
||||
{
|
||||
test_vdofs_mapped = test_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(test_dofmap.Size() == num_test_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < test_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_test_dof; i++)
|
||||
{
|
||||
test_vdofs_mapped[i + vd * num_test_dof] =
|
||||
test_vdofs[test_dofmap[i] + vd * num_test_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Array<int> &trial_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*trial_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (trial_dofmap.Size() == 0)
|
||||
{
|
||||
trial_vdofs_mapped = trial_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(trial_dofmap.Size() == num_trial_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < trial_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_trial_dof; i++)
|
||||
{
|
||||
trial_vdofs_mapped[i + vd * num_trial_dof] =
|
||||
trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat.AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, A_e, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat.AddSubMatrix(test_vdofs, trial_vdofs, A_e, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
mat.Finalize();
|
||||
|
||||
if (same_test_and_trial)
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
&mat);
|
||||
A = *RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
trial_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
trial_fes->GetDofOffsets(),
|
||||
&mat);
|
||||
A = *RAP(test_fes->Dof_TrueDof_Matrix(), &tmp, trial_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+98
-3
@@ -290,6 +290,91 @@ void pretty_print(const std::unordered_map<K,std::array<T,N>>& map)
|
||||
std::cout << "}\n";
|
||||
}
|
||||
|
||||
void print_mpi_root(const std::string& msg)
|
||||
{
|
||||
auto myrank = Mpi::WorldRank();
|
||||
if (myrank == 0)
|
||||
{
|
||||
std::cout << msg << std::endl;
|
||||
std::cout.flush(); // Ensure output is flushed
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief print with MPI rank synchronization
|
||||
///
|
||||
/// @param msg Message to print
|
||||
void print_mpi_sync(const std::string& msg)
|
||||
{
|
||||
auto myrank = Mpi::WorldRank();
|
||||
auto nranks = Mpi::WorldSize();
|
||||
|
||||
if (nranks == 1)
|
||||
{
|
||||
// Single process case - just print directly
|
||||
std::cout << msg << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// First gather string lengths
|
||||
int msg_len = msg.length();
|
||||
std::vector<int> lengths(nranks);
|
||||
MPI_Gather(&msg_len, 1, MPI_INT,
|
||||
lengths.data(), 1, MPI_INT,
|
||||
0, MPI_COMM_WORLD);
|
||||
|
||||
if (myrank == 0)
|
||||
{
|
||||
// Rank 0: Allocate receive buffer based on gathered lengths
|
||||
std::vector<std::string> messages(nranks);
|
||||
messages[0] = msg; // Store rank 0's message
|
||||
|
||||
// Receive messages from other ranks
|
||||
for (int r = 1; r < nranks; r++)
|
||||
{
|
||||
std::vector<char> buffer(lengths[r] + 1);
|
||||
MPI_Recv(buffer.data(), lengths[r], MPI_CHAR,
|
||||
r, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
||||
messages[r] = std::string(buffer.data(), lengths[r]);
|
||||
}
|
||||
|
||||
// Print all messages in rank order
|
||||
for (int r = 0; r < nranks; r++)
|
||||
{
|
||||
std::cout << "[Rank " << r << "] " << messages[r] << std::endl;
|
||||
}
|
||||
std::cout.flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other ranks: Send message to rank 0
|
||||
MPI_Send(msg.c_str(), msg_len, MPI_CHAR,
|
||||
0, 0, MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
// Final barrier to ensure completion
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
/// @brief Pretty print an mfem::Vector with MPI rank
|
||||
///
|
||||
/// @param v Vector to print
|
||||
/// @param myrank MPI rank
|
||||
/// @param comm MPI communicator
|
||||
void pretty_print_mpi(const mfem::Vector& v)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (int i = 0; i < v.Size(); i++)
|
||||
{
|
||||
ss << v(i);
|
||||
if (i < v.Size() - 1) { ss << ", "; }
|
||||
}
|
||||
ss << "]";
|
||||
|
||||
print_mpi_sync(ss.str());
|
||||
}
|
||||
|
||||
|
||||
template <typename ... Ts>
|
||||
constexpr auto decay_types(mfem::tuple<Ts...> const &)
|
||||
-> mfem::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
|
||||
@@ -485,10 +570,11 @@ void forall(func_t f,
|
||||
class FDJacobian : public Operator
|
||||
{
|
||||
public:
|
||||
FDJacobian(const Operator &op, const Vector &x) :
|
||||
FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps = 0.0) :
|
||||
Operator(op.Height(), op.Width()),
|
||||
op(op),
|
||||
x(x)
|
||||
x(x),
|
||||
fixed_eps(fixed_eps)
|
||||
{
|
||||
f.SetSize(Height());
|
||||
xpev.SetSize(Width());
|
||||
@@ -506,7 +592,15 @@ public:
|
||||
// finite difference matrix-vector products in Newton-Krylov solvers for
|
||||
// implicit climate dynamics with spectral elements. Procedia Computer
|
||||
// Science, 51, pp.2036-2045.
|
||||
real_t eps = lambda * (lambda + xnorm / v.Norml2());
|
||||
real_t eps;
|
||||
if (fixed_eps > 0.0)
|
||||
{
|
||||
eps = fixed_eps;
|
||||
}
|
||||
else
|
||||
{
|
||||
eps = lambda * (lambda + xnorm / v.Norml2());
|
||||
}
|
||||
|
||||
for (int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
@@ -533,6 +627,7 @@ private:
|
||||
Vector x, f;
|
||||
mutable Vector xpev;
|
||||
real_t lambda = 1.0e-6;
|
||||
real_t fixed_eps;
|
||||
real_t xnorm;
|
||||
};
|
||||
|
||||
|
||||
+59
-19
@@ -201,13 +201,16 @@ const DenseTensor ND_DofTransformation
|
||||
::TInv(const_cast<real_t *>(TInv_data), 2, 2, 6);
|
||||
|
||||
ND_DofTransformation::ND_DofTransformation(int size, int p, int num_edges,
|
||||
int num_tri_faces)
|
||||
int num_faces,
|
||||
int face_types[])
|
||||
: StatelessDofTransformation(size)
|
||||
, order(p)
|
||||
, nedofs(p)
|
||||
, nfdofs(p*(p-1))
|
||||
, ntdofs(p*(p-1))
|
||||
, nqdofs(2*p*(p-1))
|
||||
, nedges(num_edges)
|
||||
, nfaces(num_tri_faces)
|
||||
, nfaces(num_faces)
|
||||
, ftypes(face_types)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -221,6 +224,7 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_DofTransformation");
|
||||
|
||||
int of = 0;
|
||||
real_t data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2;
|
||||
@@ -228,11 +232,19 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
if (ftypes[f] == Geometry::TRIANGLE)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
for (int i=0; i<ntdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + of + 2*i];
|
||||
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.Mult(v2, &v[nedges*nedofs + of + 2*i]);
|
||||
}
|
||||
of += ntdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
of += nqdofs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,6 +259,7 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_DofTransformation");
|
||||
|
||||
int of = 0;
|
||||
real_t data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2Inv;
|
||||
@@ -254,11 +267,19 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
if (ftypes[f] == Geometry::TRIANGLE)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
for (int i=0; i<ntdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + of + 2*i];
|
||||
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.Mult(v2, &v[nedges*nedofs + of + 2*i]);
|
||||
}
|
||||
of += ntdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
of += nqdofs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,6 +293,7 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_DofTransformation");
|
||||
|
||||
int of = 0;
|
||||
real_t data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2Inv;
|
||||
@@ -279,12 +301,21 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
if (ftypes[f] == Geometry::TRIANGLE)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
for (int i=0; i<ntdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + of + 2*i];
|
||||
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
|
||||
}
|
||||
of += ntdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
of += nqdofs;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +329,7 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_DofTransformation");
|
||||
|
||||
int of = 0;
|
||||
real_t data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2;
|
||||
@@ -305,11 +337,19 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
if (ftypes[f] == Geometry::TRIANGLE)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
for (int i=0; i<ntdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + of + 2*i];
|
||||
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
|
||||
}
|
||||
of += ntdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
of += nqdofs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-10
@@ -306,13 +306,16 @@ private:
|
||||
static const DenseTensor T, TInv;
|
||||
|
||||
protected:
|
||||
const int order; // basis function order
|
||||
const int nedofs; // number of DoFs per edge
|
||||
const int nfdofs; // number of DoFs per face
|
||||
const int nedges; // number of edges per element
|
||||
const int nfaces; // number of triangular faces per element
|
||||
const int order; // basis function order
|
||||
const int nedofs; // number of DoFs per edge
|
||||
const int ntdofs; // number of DoFs per triangular face
|
||||
const int nqdofs; // number of DoFs per quadrilateral face
|
||||
const int nedges; // number of edges per element
|
||||
const int nfaces; // number of faces per element
|
||||
const int *ftypes; // Pointer to array of Geometry::Type for each face
|
||||
|
||||
ND_DofTransformation(int size, int order, int num_edges, int num_tri_faces);
|
||||
ND_DofTransformation(int size, int order, int num_edges, int num_faces,
|
||||
int *face_types);
|
||||
|
||||
public:
|
||||
// Return the 2x2 transformation operator for the given face orientation
|
||||
@@ -322,7 +325,7 @@ public:
|
||||
static const DenseMatrix & GetFaceInverseTransform(int ori)
|
||||
{ return TInv(ori); }
|
||||
|
||||
bool IsIdentity() const override { return nfdofs < 2; }
|
||||
bool IsIdentity() const override { return ntdofs < 2; }
|
||||
|
||||
void TransformPrimal(const Array<int> & Fo, real_t *v) const override;
|
||||
void InvTransformPrimal(const Array<int> & Fo, real_t *v) const override;
|
||||
@@ -334,9 +337,11 @@ public:
|
||||
/// triangles
|
||||
class ND_TriDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
private:
|
||||
const int face_type[1] = { Geometry::TRIANGLE };
|
||||
public:
|
||||
ND_TriDofTransformation(int order)
|
||||
: ND_DofTransformation(order*(order + 2), order, 3, 1)
|
||||
: ND_DofTransformation(order*(order + 2), order, 3, 1, (int *)face_type)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -345,7 +350,9 @@ class ND_TetDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TetDofTransformation(int order)
|
||||
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4)
|
||||
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4,
|
||||
(int *)Geometry::Constants<Geometry::TETRAHEDRON>::
|
||||
FaceTypes)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -355,7 +362,21 @@ class ND_WedgeDofTransformation : public ND_DofTransformation
|
||||
public:
|
||||
ND_WedgeDofTransformation(int order)
|
||||
: ND_DofTransformation(3 * order * ((order + 1) * (order + 2))/2,
|
||||
order, 9, 2)
|
||||
order, 9, 5,
|
||||
(int *)Geometry::Constants<Geometry::PRISM>::
|
||||
FaceTypes)
|
||||
{}
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on pyramid elements
|
||||
class ND_PyramidDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_PyramidDofTransformation(int order)
|
||||
: ND_DofTransformation(2 * order * (order * (order + 1) + 2),
|
||||
order, 8, 5,
|
||||
(int *)Geometry::Constants<Geometry::PYRAMID>::
|
||||
FaceTypes)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
+53
-10
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "../mesh/mesh_headers.hpp"
|
||||
#include "fem.hpp"
|
||||
#include "eltrans/eltrans_basis.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mfem
|
||||
@@ -66,7 +68,6 @@ const DenseMatrix &ElementTransformation::EvalInverseJ()
|
||||
return invJ;
|
||||
}
|
||||
|
||||
|
||||
int InverseElementTransformation::FindClosestPhysPoint(
|
||||
const Vector& pt, const IntegrationRule &ir)
|
||||
{
|
||||
@@ -180,12 +181,13 @@ int InverseElementTransformation::NewtonSolve(const Vector &pt,
|
||||
const int dim = T->GetDimension();
|
||||
const int sdim = T->GetSpaceDim();
|
||||
IntegrationPoint xip, prev_xip;
|
||||
real_t xd[3], yd[3], dxd[3], dx_norm = -1.0, err_phys, real_dx_norm = -1.0;
|
||||
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim);
|
||||
real_t xd[3], yd[3], dxd[3], dxpd[3], dx_norm = -1.0, err_phys,
|
||||
real_dx_norm = -1.0;
|
||||
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim), dx_prev(dxpd, dim);
|
||||
bool hit_bdr = false, prev_hit_bdr = false;
|
||||
|
||||
// Use ip0 as initial guess:
|
||||
xip = *ip0;
|
||||
xip = ip0;
|
||||
xip.Get(xd, dim); // xip -> x
|
||||
if (print_level >= 3)
|
||||
{
|
||||
@@ -342,16 +344,18 @@ int InverseElementTransformation::Transform(const Vector &pt,
|
||||
switch (init_guess_type)
|
||||
{
|
||||
case Center:
|
||||
ip0 = &Geometries.GetCenter(T->GetGeometryType());
|
||||
ip0 = Geometries.GetCenter(T->GetGeometryType());
|
||||
break;
|
||||
|
||||
case ClosestPhysNode:
|
||||
case ClosestRefNode:
|
||||
{
|
||||
const int order = std::max(T->Order()+rel_qpts_order, 0);
|
||||
const int order = qpts_order >= 0
|
||||
? qpts_order
|
||||
: std::max(T->Order() + rel_qpts_order, 0);
|
||||
if (order == 0)
|
||||
{
|
||||
ip0 = &Geometries.GetCenter(T->GetGeometryType());
|
||||
ip0 = Geometries.GetCenter(T->GetGeometryType());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -359,11 +363,45 @@ int InverseElementTransformation::Transform(const Vector &pt,
|
||||
int closest_idx = (init_guess_type == ClosestPhysNode) ?
|
||||
FindClosestPhysPoint(pt, RefG.RefPts) :
|
||||
FindClosestRefPoint(pt, RefG.RefPts);
|
||||
ip0 = &RefG.RefPts.IntPoint(closest_idx);
|
||||
ip0 = RefG.RefPts.IntPoint(closest_idx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EdgeScan:
|
||||
{
|
||||
const int order = qpts_order >= 0
|
||||
? qpts_order
|
||||
: std::max(T->Order() + rel_qpts_order, 0);
|
||||
if (order == 0)
|
||||
{
|
||||
ip0 = Geometries.GetCenter(T->GetGeometryType());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &ir = *refiner.EdgeScan(T->GetGeometryType(), order + 1);
|
||||
int res = Outside;
|
||||
int npts = ir.GetNPoints();
|
||||
// will return Inside if any test point reports Inside, Outside if
|
||||
// all points report Outside, else Unknown
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
ip0 = ir.IntPoint(i);
|
||||
int tmp_res = NewtonSolve(pt, ip);
|
||||
switch (tmp_res)
|
||||
{
|
||||
case Inside:
|
||||
return Inside;
|
||||
case Outside:
|
||||
break;
|
||||
case Unknown:
|
||||
res = Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GivenPoint:
|
||||
break;
|
||||
|
||||
@@ -444,6 +482,8 @@ int IsoparametricTransformation::OrderJ() const
|
||||
return (FElem->GetOrder()-1);
|
||||
case FunctionSpace::Qk:
|
||||
return (FElem->GetOrder());
|
||||
case FunctionSpace::Uk:
|
||||
return (FElem->GetOrder());
|
||||
default:
|
||||
MFEM_ABORT("unsupported finite element");
|
||||
}
|
||||
@@ -458,6 +498,8 @@ int IsoparametricTransformation::OrderW() const
|
||||
return (FElem->GetOrder() - 1) * FElem->GetDim();
|
||||
case FunctionSpace::Qk:
|
||||
return (FElem->GetOrder() * FElem->GetDim() - 1);
|
||||
case FunctionSpace::Uk:
|
||||
return (FElem->GetOrder() * FElem->GetDim() - 1);
|
||||
default:
|
||||
MFEM_ABORT("unsupported finite element");
|
||||
}
|
||||
@@ -477,6 +519,8 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
|
||||
return ((k-1)*(d-1)+(l-1));
|
||||
case FunctionSpace::Qk:
|
||||
return (k*(d-1)+(l-1));
|
||||
case FunctionSpace::Uk:
|
||||
return (k*(d-1)+(l-1));
|
||||
default:
|
||||
MFEM_ABORT("unsupported finite element");
|
||||
}
|
||||
@@ -712,5 +756,4 @@ real_t FaceElementTransformations::CheckConsistency(int print_level,
|
||||
|
||||
return max_dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+281
-30
@@ -17,9 +17,13 @@
|
||||
#include "intrules.hpp"
|
||||
#include "fe.hpp"
|
||||
|
||||
#include "kernel_dispatch.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class GridFunction;
|
||||
|
||||
class ElementTransformation
|
||||
{
|
||||
protected:
|
||||
@@ -198,40 +202,46 @@ public:
|
||||
/// Algorithms for selecting an initial guess.
|
||||
enum InitGuessType
|
||||
{
|
||||
Center = 0, ///< Use the center of the reference element.
|
||||
Center = 0, ///< Use the center of the reference element.
|
||||
ClosestPhysNode = 1, /**<
|
||||
Use the point returned by FindClosestPhysPoint() from a reference-space
|
||||
grid of type and size controlled by SetInitGuessPointsType() and
|
||||
SetInitGuessRelOrder(), respectively. */
|
||||
ClosestRefNode = 2, /**<
|
||||
Use the point returned by FindClosestRefPoint() from a reference-space
|
||||
grid of type and size controlled by SetInitGuessPointsType() and
|
||||
SetInitGuessRelOrder(), respectively. */
|
||||
GivenPoint = 3 ///< Use a specific point, set with SetInitialGuess().
|
||||
Use the point returned by FindClosestPhysPoint() from a reference-space
|
||||
grid of type and size controlled by SetInitGuessPointsType() and
|
||||
SetInitGuessRelOrder(), respectively. */
|
||||
ClosestRefNode = 2, /**<
|
||||
Use the point returned by FindClosestRefPoint() from a reference-space
|
||||
grid of type and size controlled by SetInitGuessPointsType() and
|
||||
SetInitGuessRelOrder(), respectively. */
|
||||
GivenPoint = 3, ///< Use a specific point, set with SetInitialGuess().
|
||||
EdgeScan =
|
||||
4, /**< Performs full solves on multiple points along the r/s/t=0 edges
|
||||
of the element. It is recommended that SetInitGuessRelOrder() is
|
||||
chosen such that max(trans_order+order,0)+1 <= 4 with
|
||||
SetInitGuessPointsType() as Quadrature1D::ClosedUniform. @see
|
||||
GeometryRefiner::EdgeScan */
|
||||
};
|
||||
|
||||
/// Solution strategy.
|
||||
enum SolverType
|
||||
{
|
||||
Newton = 0, /**<
|
||||
Use Newton's algorithm, without restricting the reference-space points
|
||||
(iterates) to the reference element. */
|
||||
Newton = 0, /**<
|
||||
Use Newton's algorithm, without restricting the reference-space points
|
||||
(iterates) to the reference element. */
|
||||
NewtonSegmentProject = 1, /**<
|
||||
Use Newton's algorithm, restricting the reference-space points to the
|
||||
reference element by scaling back the Newton increments, i.e.
|
||||
projecting new iterates, x_new, lying outside the element, to the
|
||||
intersection of the line segment [x_old, x_new] with the boundary. */
|
||||
NewtonElementProject = 2 /**<
|
||||
Use Newton's algorithm, restricting the reference-space points to the
|
||||
reference element by projecting new iterates, x_new, lying outside the
|
||||
element, to the point on the boundary closest (in reference-space) to
|
||||
x_new. */
|
||||
Use Newton's algorithm, restricting the reference-space points to the
|
||||
reference element by scaling back the Newton increments, i.e.
|
||||
projecting new iterates, x_new, lying outside the element, to the
|
||||
intersection of the line segment [x_old, x_new] with the boundary. */
|
||||
NewtonElementProject = 2, /**<
|
||||
Use Newton's algorithm, restricting the reference-space points to the
|
||||
reference element by projecting new iterates, x_new, lying outside the
|
||||
element, to the point on the boundary closest (in reference-space) to
|
||||
x_new. */
|
||||
};
|
||||
|
||||
/// Values returned by Transform().
|
||||
enum TransformResult
|
||||
{
|
||||
Inside = 0, ///< The point is inside the element
|
||||
Inside = 0, ///< The point is inside the element
|
||||
Outside = 1, ///< The point is _probably_ outside the element
|
||||
Unknown = 2 ///< The algorithm failed to determine where the point is
|
||||
};
|
||||
@@ -241,9 +251,11 @@ protected:
|
||||
ElementTransformation *T;
|
||||
|
||||
// Parameters of the inversion algorithms:
|
||||
const IntegrationPoint *ip0;
|
||||
IntegrationPoint ip0;
|
||||
int init_guess_type; // algorithm to use
|
||||
GeometryRefiner refiner; // geometry refiner for initial guess
|
||||
int qpts_order; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
|
||||
// rel_qpts_order.
|
||||
int rel_qpts_order; // num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
|
||||
int solver_type; // solution strategy to use
|
||||
int max_iter; // max. number of Newton iterations
|
||||
@@ -284,19 +296,19 @@ public:
|
||||
tolerances. */
|
||||
InverseElementTransformation(ElementTransformation *Trans = NULL)
|
||||
: T(Trans),
|
||||
ip0(NULL),
|
||||
init_guess_type(Center),
|
||||
refiner(Quadrature1D::OpenHalfUniform),
|
||||
qpts_order(-1),
|
||||
rel_qpts_order(-1),
|
||||
solver_type(NewtonElementProject),
|
||||
max_iter(16),
|
||||
#ifdef MFEM_USE_DOUBLE
|
||||
ref_tol(1e-15),
|
||||
phys_rtol(1e-15),
|
||||
phys_rtol(4e-15),
|
||||
ip_tol(1e-8),
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
ref_tol(1e-7),
|
||||
phys_rtol(1e-7),
|
||||
ref_tol(4e-7),
|
||||
phys_rtol(1e-6),
|
||||
ip_tol(1e-4),
|
||||
#endif
|
||||
print_level(-1)
|
||||
@@ -314,16 +326,28 @@ public:
|
||||
/** @brief Set the initial guess for subsequent calls to Transform(),
|
||||
switching to the #GivenPoint #InitGuessType at the same time. */
|
||||
void SetInitialGuess(const IntegrationPoint &init_ip)
|
||||
{ ip0 = &init_ip; SetInitialGuessType(GivenPoint); }
|
||||
{ ip0 = init_ip; SetInitialGuessType(GivenPoint); }
|
||||
|
||||
/// Set the Quadrature1D type used for the `Closest*` initial guess types.
|
||||
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
|
||||
/// guess types.
|
||||
void SetInitGuessPointsType(int q_type) { refiner.SetType(q_type); }
|
||||
|
||||
/// Set the relative order used for the `Closest*` initial guess types.
|
||||
/** The number of points in each spatial direction is given by the formula
|
||||
max(trans_order+order,0)+1, where trans_order is the order of the current
|
||||
ElementTransformation. */
|
||||
void SetInitGuessRelOrder(int order) { rel_qpts_order = order; }
|
||||
void SetInitGuessRelOrder(int order)
|
||||
{
|
||||
qpts_order = -1;
|
||||
rel_qpts_order = order;
|
||||
}
|
||||
|
||||
/** The number of points in each spatial direction is given by the formula
|
||||
order+1. */
|
||||
void SetInitGuessOrder(int order)
|
||||
{
|
||||
qpts_order = order;
|
||||
}
|
||||
|
||||
/** @brief Specify which algorithm to use for solving the transformation
|
||||
equation, i.e. when calling the Transform() method. */
|
||||
@@ -373,6 +397,233 @@ public:
|
||||
virtual int Transform(const Vector &pt, IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Performs batch inverse element transforms. Currently only supports
|
||||
* non-mixed meshes with SEGMENT, SQUARE, or CUBE geometries. Mixed
|
||||
* element order meshes are projected onto an equivalent uniform order mesh.
|
||||
*/
|
||||
class BatchInverseElementTransformation
|
||||
{
|
||||
// nodes grid function, not owned
|
||||
const GridFunction *gf_ = nullptr;
|
||||
// initial guess algorithm to use
|
||||
InverseElementTransformation::InitGuessType init_guess_type =
|
||||
InverseElementTransformation::ClosestPhysNode;
|
||||
int qpts_order = -1; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
|
||||
// rel_qpts_order.
|
||||
// num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
|
||||
int rel_qpts_order = 0;
|
||||
// solution strategy to use
|
||||
InverseElementTransformation::SolverType solver_type =
|
||||
InverseElementTransformation::NewtonElementProject;
|
||||
// basis type stored in points1d
|
||||
int basis_type = BasisType::Invalid;
|
||||
// initial guess points type. Quadrature1D::Invalid is used for match
|
||||
// basis_type.
|
||||
int guess_points_type = Quadrature1D::Invalid;
|
||||
// max. number of Newton iterations
|
||||
int max_iter = 16;
|
||||
// internal element node locations cache
|
||||
Vector node_pos;
|
||||
#ifdef MFEM_USE_DOUBLE
|
||||
// reference space tolerance
|
||||
real_t ref_tol = 1e-15;
|
||||
// physical space tolerance (relative)
|
||||
real_t phys_rtol = 4e-15;
|
||||
#else
|
||||
// reference space tolerance
|
||||
real_t ref_tol = 4e-7;
|
||||
// physical space tolerance (relative)
|
||||
real_t phys_rtol = 1e-6;
|
||||
#endif
|
||||
// not owned, location of tensor product basis nodes in reference space
|
||||
const Array<real_t> *points1d = nullptr;
|
||||
|
||||
public:
|
||||
/// Uninitialized BatchInverseElementTransformation. Users must call
|
||||
/// UpdateNodes before Transform.
|
||||
BatchInverseElementTransformation();
|
||||
///
|
||||
/// Constructs a BatchInverseElementTransformation given @a nodes representing
|
||||
/// the mesh nodes.
|
||||
///
|
||||
BatchInverseElementTransformation(const GridFunction &nodes,
|
||||
MemoryType d_mt = MemoryType::DEFAULT);
|
||||
///
|
||||
/// Constructs a BatchInverseElementTransformation for a given @a mesh.
|
||||
/// mesh.GetNodes() must not be null.
|
||||
///
|
||||
BatchInverseElementTransformation(const Mesh &mesh,
|
||||
MemoryType d_mt = MemoryType::DEFAULT);
|
||||
|
||||
~BatchInverseElementTransformation();
|
||||
|
||||
/** @brief Choose how the initial guesses for subsequent calls to Transform()
|
||||
will be selected. ClosestRefNode is currently not supported. */
|
||||
void SetInitialGuessType(InverseElementTransformation::InitGuessType itype)
|
||||
{
|
||||
MFEM_ASSERT(itype != InverseElementTransformation::ClosestRefNode,
|
||||
"ClosestRefNode is currently not supported");
|
||||
init_guess_type = itype;
|
||||
}
|
||||
|
||||
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
|
||||
/// guess types.
|
||||
void SetInitGuessPointsType(int q_type) { guess_points_type = q_type; }
|
||||
|
||||
/// Set the relative order used for the `Closest*` initial guess types.
|
||||
/** The number of points in each spatial direction is given by the formula
|
||||
max(trans_order+order,0)+1, where trans_order is the order of the
|
||||
current ElementTransformation. */
|
||||
void SetInitGuessRelOrder(int order)
|
||||
{
|
||||
qpts_order = -1;
|
||||
rel_qpts_order = order;
|
||||
}
|
||||
|
||||
/** The number of points in each spatial direction is given by the formula
|
||||
order+1. */
|
||||
void SetInitGuessOrder(int order) { qpts_order = order; }
|
||||
|
||||
/// @b Gets the basis type nodes are projected onto, or BasisType::Invalid if
|
||||
/// uninitialized.
|
||||
int GetBasisType() const { return basis_type; }
|
||||
|
||||
/** @brief Specify which algorithm to use for solving the transformation
|
||||
equation, i.e. when calling the Transform() method. NewtonSegmentProject
|
||||
is currently not supported. */
|
||||
void SetSolverType(InverseElementTransformation::SolverType stype)
|
||||
{
|
||||
MFEM_ASSERT(stype != InverseElementTransformation::NewtonSegmentProject,
|
||||
"NewtonSegmentProject is currently not supported");
|
||||
solver_type = stype;
|
||||
}
|
||||
|
||||
/// Set the maximum number of iterations when solving for a reference point.
|
||||
void SetMaxIter(int max_it) { max_iter = max_it; }
|
||||
|
||||
/// Set the reference-space convergence tolerance.
|
||||
void SetReferenceTol(real_t ref_sp_tol) { ref_tol = ref_sp_tol; }
|
||||
|
||||
/// Set the relative physical-space convergence tolerance.
|
||||
void SetPhysicalRelTol(real_t phys_rel_tol) { phys_rtol = phys_rel_tol; }
|
||||
|
||||
/**
|
||||
* @brief Updates internal datastructures if @a nodes change. Some version
|
||||
* of UpdateNodes must be called at least once before calls to Transform if
|
||||
* nodes have changed.
|
||||
*/
|
||||
void UpdateNodes(const GridFunction &nodes,
|
||||
MemoryType d_mt = MemoryType::DEFAULT);
|
||||
/**
|
||||
* @brief Updates internal datastructures if @a mesh nodes change. Some version
|
||||
* of UpdateNodes must be called at least once before calls to Transform if
|
||||
* mesh nodes have changed. mesh.GetNodes() must not be null.
|
||||
*/
|
||||
void UpdateNodes(const Mesh &mesh, MemoryType d_mt = MemoryType::DEFAULT);
|
||||
|
||||
/** @brief Performs a batch request of a set of points belonging to the given
|
||||
elements.
|
||||
@a pts list of physical point coordinates ordered by
|
||||
Ordering::Type::byNODES.
|
||||
@a elems which element index to search for each corresponding point in
|
||||
@a pts
|
||||
@a types output search classification (@see
|
||||
InverseElementTransformation::TransformResult).
|
||||
@a refs result reference point coordinates ordered by
|
||||
Ordering::Type::byNODES. If using InitGuessType::GivenPoint, this should
|
||||
contain the initial guess for each point.
|
||||
@a use_device hint for if device acceleration should be used.
|
||||
Device acceleration is currently only implemented for meshes containing
|
||||
only a single tensor product basis element type.
|
||||
@a iters optional array storing how many iterations was spent on each
|
||||
tested point
|
||||
*/
|
||||
void Transform(const Vector &pts, const Array<int> &elems, Array<int> &types,
|
||||
Vector &refs, bool use_device = true,
|
||||
Array<int> *iters = nullptr) const;
|
||||
|
||||
using ClosestPhysPointKernelType = void (*)(int, int, int, int,
|
||||
const real_t *, const real_t *,
|
||||
const int *, const real_t *,
|
||||
const real_t *, real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, use_device
|
||||
MFEM_REGISTER_KERNELS(FindClosestPhysPoint, ClosestPhysPointKernelType,
|
||||
(int, int, bool));
|
||||
|
||||
using ClosestPhysDofKernelType = void (*)(int, int, int,
|
||||
const real_t *, const real_t *,
|
||||
const int *, const real_t *,
|
||||
real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, use_device
|
||||
MFEM_REGISTER_KERNELS(FindClosestPhysDof, ClosestPhysDofKernelType,
|
||||
(int, int, bool));
|
||||
|
||||
using ClosestRefDofKernelType = void (*)(int, int, int, const real_t *,
|
||||
const real_t *, const int *,
|
||||
const real_t *, real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, use_device
|
||||
MFEM_REGISTER_KERNELS(FindClosestRefDof, ClosestRefDofKernelType,
|
||||
(int, int, bool));
|
||||
|
||||
using ClosestRefPointKernelType = void (*)(int, int, int, int, const real_t *,
|
||||
const real_t *, const int *,
|
||||
const real_t *, const real_t *,
|
||||
real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, use_device
|
||||
MFEM_REGISTER_KERNELS(FindClosestRefPoint, ClosestRefPointKernelType,
|
||||
(int, int, bool));
|
||||
|
||||
using NewtonKernelType = void (*)(real_t, real_t, int, int, int, int,
|
||||
const real_t *, const real_t *,
|
||||
const int *, const real_t *, int *, int*,
|
||||
real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, SolverType, use_device
|
||||
MFEM_REGISTER_KERNELS(NewtonSolve, NewtonKernelType,
|
||||
(int, int, InverseElementTransformation::SolverType,
|
||||
bool));
|
||||
|
||||
using NewtonEdgeScanKernelType = void (*)(real_t, real_t, int, int, int, int,
|
||||
const real_t *, const real_t *,
|
||||
const int *, const real_t *,
|
||||
const real_t *, int, int *, int *,
|
||||
real_t *);
|
||||
|
||||
// specialization params: Geom, SDim, SolverType, use_device
|
||||
MFEM_REGISTER_KERNELS(NewtonEdgeScan, NewtonEdgeScanKernelType,
|
||||
(int, int, InverseElementTransformation::SolverType,
|
||||
bool));
|
||||
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
template <int Dim, int SDim>
|
||||
static void AddFindClosestSpecialization()
|
||||
{
|
||||
FindClosestPhysPoint::Specialization<Dim, SDim, true>::Add();
|
||||
FindClosestRefPoint::Specialization<Dim, SDim, true>::Add();
|
||||
FindClosestPhysPoint::Specialization<Dim, SDim, false>::Add();
|
||||
FindClosestRefPoint::Specialization<Dim, SDim, false>::Add();
|
||||
FindClosestPhysDof::Specialization<Dim, SDim, true>::Add();
|
||||
FindClosestRefDof::Specialization<Dim, SDim, true>::Add();
|
||||
FindClosestPhysDof::Specialization<Dim, SDim, false>::Add();
|
||||
FindClosestRefDof::Specialization<Dim, SDim, false>::Add();
|
||||
}
|
||||
|
||||
template <int Dim, int SDim, InverseElementTransformation::SolverType SType>
|
||||
static void AddNewtonSolveSpecialization()
|
||||
{
|
||||
NewtonSolve::Specialization<Dim, SDim, SType, true>::Add();
|
||||
NewtonEdgeScan::Specialization<Dim, SDim, SType, true>::Add();
|
||||
NewtonSolve::Specialization<Dim, SDim, SType, false>::Add();
|
||||
NewtonEdgeScan::Specialization<Dim, SDim, SType, false>::Add();
|
||||
}
|
||||
};
|
||||
|
||||
/// A standard isoparametric element transformation
|
||||
class IsoparametricTransformation : public ElementTransformation
|
||||
{
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_ELTRANS_BASIS
|
||||
#define MFEM_ELTRANS_BASIS
|
||||
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
#include "../geom.hpp"
|
||||
|
||||
// this file contains utilities for computing nodal basis functions and their
|
||||
// derivatives in device kernels
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace eltrans
|
||||
{
|
||||
|
||||
/// Various utilities for working with different element geometries
|
||||
template <int GeomType> struct GeometryUtils;
|
||||
|
||||
template <> struct GeometryUtils<Geometry::SEGMENT>
|
||||
{
|
||||
static constexpr MFEM_HOST_DEVICE int Dimension() { return 1; }
|
||||
|
||||
/// @b true if the given point x in ref space is inside the element
|
||||
static bool MFEM_HOST_DEVICE inside(real_t x) { return x >= 0 && x <= 1; }
|
||||
|
||||
/// @b Bound the reference coordinate @a x += dx to be inside the segment.
|
||||
/// @a dx is updated to be dx = project(x+dx) - x
|
||||
/// @return true if x + dx hit a boundary
|
||||
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &dx)
|
||||
{
|
||||
real_t tmp = x;
|
||||
x += dx;
|
||||
if (x < 0)
|
||||
{
|
||||
x = 0;
|
||||
dx = x - tmp;
|
||||
return true;
|
||||
}
|
||||
if (x > 1)
|
||||
{
|
||||
x = 1;
|
||||
dx = x - tmp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct GeometryUtils<Geometry::SQUARE>
|
||||
{
|
||||
static constexpr MFEM_HOST_DEVICE int Dimension() { return 2; }
|
||||
/// @b true if the given point (x,y) in ref space is inside the element
|
||||
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y)
|
||||
{
|
||||
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1);
|
||||
}
|
||||
|
||||
/// @b Bound the reference coordinate @a (x,y) += (dx,dy) to be inside the
|
||||
/// square.
|
||||
/// @a dx and @a dy are updated to be (dx,dy) = project(x+dx,y+dy) - (x,y)
|
||||
/// @return true if (x,y) + (dx,dy) hit a boundary
|
||||
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &dx,
|
||||
real_t &dy)
|
||||
{
|
||||
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
|
||||
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
|
||||
return x_cond || y_cond;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct GeometryUtils<Geometry::CUBE>
|
||||
{
|
||||
static constexpr MFEM_HOST_DEVICE int Dimension() { return 3; }
|
||||
/// @b true if the given point (x,y,z) in ref space is inside the element
|
||||
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y, real_t z)
|
||||
{
|
||||
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1) && (z >= 0) && (z <= 1);
|
||||
}
|
||||
|
||||
/// @b Bound the reference coordinate @a (x,y,z) += (dx,dy,dz) to be inside
|
||||
/// the cube.
|
||||
/// @a dx, @a dy, and @ dz are updated to be
|
||||
/// (dx,dy,dz) = project(x+dx,y+dy,z+dz) - (x,y,z)
|
||||
/// @return true if (x,y,z) + (dx,dy,dz) hit a boundary
|
||||
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &z,
|
||||
real_t &dx, real_t &dy, real_t &dz)
|
||||
{
|
||||
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
|
||||
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
|
||||
bool z_cond = GeometryUtils<Geometry::SEGMENT>::project(z, dz);
|
||||
return x_cond || y_cond || z_cond;
|
||||
}
|
||||
};
|
||||
|
||||
/// 1D Lagrange basis from [0, 1]
|
||||
class Lagrange
|
||||
{
|
||||
public:
|
||||
/// interpolant node locations, in reference space
|
||||
const real_t *z;
|
||||
|
||||
/// number of points
|
||||
int pN;
|
||||
|
||||
/// @b Evaluates the @a i'th Lagrange polynomial at @a x
|
||||
real_t MFEM_HOST_DEVICE eval(real_t x, int i) const
|
||||
{
|
||||
real_t u0 = 1;
|
||||
real_t den = 1;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
real_t d_j = (x - z[j]);
|
||||
u0 = d_j * u0;
|
||||
den *= (z[i] - z[j]);
|
||||
}
|
||||
}
|
||||
den = 1 / den;
|
||||
return u0 * den;
|
||||
}
|
||||
|
||||
/// @b Evaluates the @a i'th Lagrange polynomial and its first derivative at
|
||||
/// @a x
|
||||
void MFEM_HOST_DEVICE eval_d1(real_t &p, real_t &d1, real_t x, int i) const
|
||||
{
|
||||
real_t u0 = 1;
|
||||
real_t u1 = 0;
|
||||
real_t den = 1;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
real_t d_j = (x - z[j]);
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
den *= (z[i] - z[j]);
|
||||
}
|
||||
}
|
||||
den = 1 / den;
|
||||
p = u0 * den;
|
||||
d1 = u1 * den;
|
||||
}
|
||||
|
||||
/// @b Evaluates the @a i'th Lagrange polynomial and its first and second
|
||||
/// derivatives at @a x
|
||||
void MFEM_HOST_DEVICE eval_d2(real_t &p, real_t &d1, real_t &d2, real_t x,
|
||||
int i) const
|
||||
{
|
||||
real_t u0 = 1;
|
||||
real_t u1 = 0;
|
||||
real_t u2 = 0;
|
||||
real_t den = 1;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
real_t d_j = (x - z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
den *= (z[i] - z[j]);
|
||||
}
|
||||
}
|
||||
den = 1 / den;
|
||||
p = den * u0;
|
||||
d1 = den * u1;
|
||||
d2 = 2 * den * u2;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace eltrans
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -623,7 +623,7 @@ private:
|
||||
|
||||
public:
|
||||
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
|
||||
@param di_ The bilinearform to compute the interface flux.
|
||||
@param di_ The bilinear form to compute the interface flux.
|
||||
@param sol_ The solution field whose error is to be estimated.
|
||||
@param flux_fes_ The finite element space for the interface flux.
|
||||
@param attributes_ The attributes of the subdomain(s) for which the
|
||||
@@ -635,7 +635,7 @@ public:
|
||||
const Array<int> &attributes_ = Array<int>());
|
||||
|
||||
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
|
||||
@param di_ The bilinearform to compute the interface flux.
|
||||
@param di_ The bilinear form to compute the interface flux.
|
||||
@param sol_ The solution field whose error is to be estimated.
|
||||
@param flux_fes_ The finite element space for the interface flux.
|
||||
@param attributes_ The attributes of the subdomain(s) for which the
|
||||
|
||||
+41
-17
@@ -69,16 +69,16 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to element 1, return the corresponding face DOF index ordered
|
||||
/// lexicographically relative to element 2.
|
||||
/// relative to element the element (where the local face is face_id), and
|
||||
/// return the corresponding face DOF index ordered lexicographically relative
|
||||
/// to the face itself.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id1, const int face_id2,
|
||||
const int orientation, const int size1d,
|
||||
const int index)
|
||||
inline int PermuteFace2D(const int face_id, const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
int new_index;
|
||||
// Convert from element 1 lex ordering to native ordering
|
||||
if (face_id1 == 2 || face_id1 == 3)
|
||||
if (face_id == 2 || face_id == 3)
|
||||
{
|
||||
new_index = size1d-1-index;
|
||||
}
|
||||
@@ -91,7 +91,18 @@ inline int PermuteFace2D(const int face_id1, const int face_id2,
|
||||
{
|
||||
new_index = size1d-1-new_index;
|
||||
}
|
||||
// Covert to element 2 lex ordering
|
||||
return new_index;
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to element 1, return the corresponding face DOF index ordered
|
||||
/// lexicographically relative to element 2.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id1, const int face_id2,
|
||||
const int orientation, const int size1d,
|
||||
const int index)
|
||||
{
|
||||
const int new_index = PermuteFace2D(face_id1, orientation, size1d, index);
|
||||
return ToLexOrdering2D(face_id2, size1d, new_index);
|
||||
}
|
||||
|
||||
@@ -116,26 +127,22 @@ inline int ToLexOrdering3D(const int face_id, const int size1d, const int i,
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Given the index of a face DOF in lexicographic ordering relative
|
||||
/// element 1, permute the index so that it is lexicographically ordered
|
||||
/// relative to element 2.
|
||||
///
|
||||
/// The given face corresponds to local face index @a face_id1 relative to
|
||||
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
|
||||
/// @brief Given the index of a face DOF in lexicographic ordering relative the
|
||||
/// element (where the local face id is @a face_id), permute the index so that
|
||||
/// it is lexicographically ordered relative to the face itself.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace3D(const int face_id1, const int face_id2,
|
||||
const int orientation,
|
||||
inline int PermuteFace3D(const int face_id, const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
int i=0, j=0, new_i=0, new_j=0;
|
||||
i = index%size1d;
|
||||
j = index/size1d;
|
||||
// Convert from lex ordering
|
||||
if (face_id1==3 || face_id1==4)
|
||||
if (face_id==3 || face_id==4)
|
||||
{
|
||||
i = size1d-1-i;
|
||||
}
|
||||
else if (face_id1==0)
|
||||
else if (face_id==0)
|
||||
{
|
||||
j = size1d-1-j;
|
||||
}
|
||||
@@ -175,6 +182,23 @@ inline int PermuteFace3D(const int face_id1, const int face_id2,
|
||||
new_j = (size1d-1-j);
|
||||
break;
|
||||
}
|
||||
return new_i + new_j*size1d;
|
||||
}
|
||||
|
||||
/// @brief Given the index of a face DOF in lexicographic ordering relative
|
||||
/// element 1, permute the index so that it is lexicographically ordered
|
||||
/// relative to element 2.
|
||||
///
|
||||
/// The given face corresponds to local face index @a face_id1 relative to
|
||||
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace3D(const int face_id1, const int face_id2,
|
||||
const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
const int new_index = PermuteFace3D(face_id1, orientation, size1d, index);
|
||||
const int new_i = new_index%size1d;
|
||||
const int new_j = new_index/size1d;
|
||||
return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
|
||||
}
|
||||
|
||||
|
||||
+95
-62
@@ -973,6 +973,22 @@ void NodalFiniteElement::ProjectDiv(
|
||||
}
|
||||
}
|
||||
|
||||
void NodalFiniteElement::ReorderLexToNative(int ncomp,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(lex_ordering.Size() == dof, "Permutation is not defined by FE.");
|
||||
MFEM_ASSERT(dofs.Size() == ncomp * dof, "Wrong input size.");
|
||||
|
||||
Vector dofs_native(ncomp * dof);
|
||||
for (int i = 0; i < dof; i++)
|
||||
{
|
||||
for (int c = 0; c < ncomp; c++)
|
||||
{
|
||||
dofs_native(c*dof + lex_ordering[i]) = dofs(c*dof + i);
|
||||
}
|
||||
}
|
||||
dofs = dofs_native;
|
||||
}
|
||||
|
||||
VectorFiniteElement::VectorFiniteElement(int D, Geometry::Type G,
|
||||
int Do, int O, int M, int F)
|
||||
@@ -2168,6 +2184,64 @@ void Poly_1D::CalcDBinomTerms(const int p, const real_t x, const real_t y,
|
||||
}
|
||||
}
|
||||
|
||||
void Poly_1D::CalcDxBinomTerms(const int p, const real_t x, const real_t y,
|
||||
real_t *u)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Binom(p);
|
||||
real_t z = 1.;
|
||||
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
u[i] = i * b[i]*z;
|
||||
z *= x;
|
||||
}
|
||||
u[p] = i * z;
|
||||
z = y;
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
u[i] *= z;
|
||||
z *= y;
|
||||
}
|
||||
u[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Poly_1D::CalcDyBinomTerms(const int p, const real_t x, const real_t y,
|
||||
real_t *u)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Binom(p);
|
||||
real_t z = x;
|
||||
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
u[i] = b[i]*z;
|
||||
z *= x;
|
||||
}
|
||||
u[p] = 0.;
|
||||
z = 1.;
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
u[i] *= (p - i) * z;
|
||||
z *= y;
|
||||
}
|
||||
u[0] = p * z;
|
||||
}
|
||||
}
|
||||
|
||||
void Poly_1D::CalcLegendre(const int p, const real_t x, real_t *u)
|
||||
{
|
||||
// use the recursive definition for [-1,1]:
|
||||
@@ -2267,99 +2341,58 @@ void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u, real_t *d,
|
||||
}
|
||||
}
|
||||
|
||||
const real_t *Poly_1D::GetPoints(const int p, const int btype)
|
||||
const Array<real_t>* Poly_1D::GetPointsArray(const int p, const int btype)
|
||||
{
|
||||
Array<real_t*> *pts;
|
||||
Array<real_t> *val;
|
||||
BasisType::Check(btype);
|
||||
const int qtype = BasisType::GetQuadrature1D(btype);
|
||||
if (qtype == Quadrature1D::Invalid) { return NULL; }
|
||||
if (qtype == Quadrature1D::Invalid) { return nullptr; }
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (Poly1DGetPoints)
|
||||
#endif
|
||||
{
|
||||
auto it = points_container.find(btype);
|
||||
if (it != points_container.end())
|
||||
std::pair<int, int> key(btype, p);
|
||||
auto it = points_container.find(key);
|
||||
if (it == points_container.end())
|
||||
{
|
||||
pts = it->second;
|
||||
it = points_container.emplace(key, new Array<real_t>(p + 1, h_mt)).first;
|
||||
val = it->second.get();
|
||||
real_t* hptr = val->HostWrite();
|
||||
quad_func.GivePolyPoints(p + 1, hptr, qtype);
|
||||
}
|
||||
else
|
||||
{
|
||||
pts = new Array<real_t*>(h_mt);
|
||||
points_container[btype] = pts;
|
||||
}
|
||||
if (pts->Size() <= p)
|
||||
{
|
||||
pts->SetSize(p + 1, NULL);
|
||||
}
|
||||
if ((*pts)[p] == NULL)
|
||||
{
|
||||
(*pts)[p] = new real_t[p + 1];
|
||||
quad_func.GivePolyPoints(p + 1, (*pts)[p], qtype);
|
||||
val = it->second.get();
|
||||
}
|
||||
}
|
||||
return (*pts)[p];
|
||||
return val;
|
||||
}
|
||||
|
||||
Poly_1D::Basis &Poly_1D::GetBasis(const int p, const int btype)
|
||||
{
|
||||
Array<Basis*> *bases;
|
||||
BasisType::Check(btype);
|
||||
Basis* val;
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (Poly1DGetBasis)
|
||||
#endif
|
||||
{
|
||||
auto it = bases_container.find(btype);
|
||||
if (it != bases_container.end())
|
||||
{
|
||||
bases = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we haven't been asked for basis or points of this type yet
|
||||
bases = new Array<Basis*>(h_mt);
|
||||
bases_container[btype] = bases;
|
||||
}
|
||||
if (bases->Size() <= p)
|
||||
{
|
||||
bases->SetSize(p + 1, NULL);
|
||||
}
|
||||
if ((*bases)[p] == NULL)
|
||||
std::pair<int, int> key(btype, p);
|
||||
auto it = bases_container.find(key);
|
||||
if (it == bases_container.end())
|
||||
{
|
||||
EvalType etype;
|
||||
if (btype == BasisType::Positive) { etype = Positive; }
|
||||
else if (btype == BasisType::IntegratedGLL) { etype = Integrated; }
|
||||
else { etype = Barycentric; }
|
||||
(*bases)[p] = new Basis(p, GetPoints(p, btype), etype);
|
||||
it = bases_container
|
||||
.emplace(key, new Basis(p, GetPoints(p, btype), etype))
|
||||
.first;
|
||||
}
|
||||
val = it->second.get();
|
||||
}
|
||||
return *(*bases)[p];
|
||||
}
|
||||
|
||||
Poly_1D::~Poly_1D()
|
||||
{
|
||||
for (PointsMap::iterator it = points_container.begin();
|
||||
it != points_container.end() ; ++it)
|
||||
{
|
||||
Array<real_t*>& pts = *it->second;
|
||||
for (int i = 0; i < pts.Size(); ++i)
|
||||
{
|
||||
delete [] pts[i];
|
||||
}
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
for (BasisMap::iterator it = bases_container.begin();
|
||||
it != bases_container.end() ; ++it)
|
||||
{
|
||||
Array<Basis*>& bases = *it->second;
|
||||
for (int i = 0; i < bases.Size(); ++i)
|
||||
{
|
||||
delete bases[i];
|
||||
}
|
||||
delete it->second;
|
||||
}
|
||||
return *val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+61
-11
@@ -15,8 +15,12 @@
|
||||
#include "../intrules.hpp"
|
||||
#include "../geom.hpp"
|
||||
#include "../doftrans.hpp"
|
||||
#include "../../general/hash.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -223,9 +227,10 @@ class FunctionSpace
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Pk, ///< Polynomials of order k
|
||||
Qk, ///< Tensor products of polynomials of order k
|
||||
rQk ///< Refined tensor products of polynomials of order k
|
||||
Pk, ///< Polynomials of order k
|
||||
Qk, ///< Tensor products of polynomials of order k
|
||||
rQk, ///< Refined tensor products of polynomials of order k
|
||||
Uk ///< Rational polynomials of order k
|
||||
};
|
||||
};
|
||||
|
||||
@@ -794,6 +799,12 @@ public:
|
||||
TensorBasisElement::GetDofMap, but it is also available for non-tensor
|
||||
elements. */
|
||||
const Array<int> &GetLexicographicOrdering() const { return lex_ordering; }
|
||||
|
||||
/// Given a lexicographically ordered Vector @a dofs, containing @a ncomp
|
||||
/// components of the size of the scalar FiniteElement, reorder its entries
|
||||
/// into native (H1) ordering.
|
||||
/// The function assumes that GetLexicographicOrdering() is not empty.
|
||||
void ReorderLexToNative(int ncomp, Vector &dofs) const;
|
||||
};
|
||||
|
||||
/** @brief Intermediate class for finite elements whose basis functions return
|
||||
@@ -1038,8 +1049,14 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
typedef std::map<int, Array<real_t*>*> PointsMap;
|
||||
typedef std::map<int, Array<Basis*>*> BasisMap;
|
||||
/// key: (btype, p), value: underlying storage Array
|
||||
typedef std::unordered_map<std::pair<int, int>,
|
||||
std::unique_ptr<Basis>, PairHasher>
|
||||
BasisMap;
|
||||
/// key: (btype, p), value: underlying storage Array
|
||||
typedef std::unordered_map<std::pair<int, int>,
|
||||
std::unique_ptr<Array<real_t>>, PairHasher>
|
||||
PointsMap;
|
||||
|
||||
MemoryType h_mt;
|
||||
PointsMap points_container;
|
||||
@@ -1073,17 +1090,40 @@ public:
|
||||
@return A pointer to an array containing the `p+1` coordinates of the
|
||||
points. Returns NULL if the BasisType has no associated set of
|
||||
points. */
|
||||
const real_t *GetPoints(const int p, const int btype);
|
||||
const Array<real_t>* GetPointsArray(const int p, const int btype);
|
||||
|
||||
/** @brief Get the coordinates of the points of the given BasisType,
|
||||
@a btype.
|
||||
|
||||
@param[in] p The polynomial degree; the number of points is `p+1`.
|
||||
@param[in] btype The BasisType.
|
||||
@param[in] on_device true if the requested pointer should be accessible
|
||||
from the device.
|
||||
|
||||
@return A pointer to an array containing the `p+1` coordinates of the
|
||||
points. Returns NULL if the BasisType has no associated set of
|
||||
points. */
|
||||
const real_t *GetPoints(const int p, const int btype,
|
||||
bool on_device = false)
|
||||
{
|
||||
return GetPointsArray(p, btype)->Read(on_device);
|
||||
}
|
||||
|
||||
/// Get coordinates of an open (GaussLegendre) set of points if degree @a p
|
||||
const real_t *OpenPoints(const int p,
|
||||
const int btype = BasisType::GaussLegendre)
|
||||
{ return GetPoints(p, btype); }
|
||||
const int btype = BasisType::GaussLegendre,
|
||||
bool on_device = false)
|
||||
{
|
||||
return GetPoints(p, btype, on_device);
|
||||
}
|
||||
|
||||
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
|
||||
const real_t *ClosedPoints(const int p,
|
||||
const int btype = BasisType::GaussLobatto)
|
||||
{ return GetPoints(p, btype); }
|
||||
const int btype = BasisType::GaussLobatto,
|
||||
bool on_device = false)
|
||||
{
|
||||
return GetPoints(p, btype, on_device);
|
||||
}
|
||||
|
||||
/** @brief Get a Poly_1D::Basis object of the given degree and BasisType,
|
||||
@a btype.
|
||||
@@ -1158,6 +1198,16 @@ public:
|
||||
in the already allocated @a d array.*/
|
||||
static void CalcDBinomTerms(const int p, const real_t x, const real_t y,
|
||||
real_t *d);
|
||||
/** @brief Compute the derivatives (w.r.t. x) of the terms in the expansion
|
||||
of the binomial (x + y)^p. Store the results in the already allocated
|
||||
@a d array.*/
|
||||
static void CalcDxBinomTerms(const int p, const real_t x, const real_t y,
|
||||
real_t *d);
|
||||
/** @brief Compute the derivatives (w.r.t. y) of the terms in the expansion
|
||||
of the binomial (x + y)^p. Store the results in the already allocated
|
||||
@a d array.*/
|
||||
static void CalcDyBinomTerms(const int p, const real_t x, const real_t y,
|
||||
real_t *d);
|
||||
|
||||
/** @brief Compute the values of the Bernstein basis functions of order
|
||||
@a p at coordinate @a x and store the results in the already allocated
|
||||
@@ -1186,7 +1236,7 @@ public:
|
||||
static void CalcLegendre(const int p, const real_t x, real_t *u);
|
||||
static void CalcLegendre(const int p, const real_t x, real_t *u, real_t *d);
|
||||
|
||||
~Poly_1D();
|
||||
~Poly_1D() = default;
|
||||
};
|
||||
|
||||
extern MFEM_EXPORT Poly_1D poly1d;
|
||||
|
||||
+573
-34
@@ -1341,7 +1341,7 @@ const
|
||||
|
||||
|
||||
LinearPyramidFiniteElement::LinearPyramidFiniteElement()
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1, FunctionSpace::Uk)
|
||||
{
|
||||
Nodes.IntPoint(0).x = 0.0;
|
||||
Nodes.IntPoint(0).y = 0.0;
|
||||
@@ -3015,7 +3015,7 @@ void P0WdgFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
|
||||
|
||||
P0PyrFiniteElement::P0PyrFiniteElement()
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Qk)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Uk)
|
||||
{
|
||||
Nodes.IntPoint(0).x = 0.375;
|
||||
Nodes.IntPoint(0).y = 0.375;
|
||||
@@ -4818,7 +4818,7 @@ void Nedelec1WdgFiniteElement::ProjectGrad(const FiniteElement &fe,
|
||||
|
||||
|
||||
Nedelec1PyrFiniteElement::Nedelec1PyrFiniteElement()
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL)
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL, FunctionSpace::Uk)
|
||||
{
|
||||
// not real nodes ...
|
||||
Nodes.IntPoint(0).x = 0.5;
|
||||
@@ -4866,7 +4866,11 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
|
||||
{
|
||||
// We must return the limit of the basis functions as z->1. In order to
|
||||
// remain inside the pyramid in this limit the x and y coordinates must
|
||||
// be approaching 0. The resulting limiting basis function values are:
|
||||
// be approaching 0. Unfortunately we obtain different limits if we
|
||||
// approach (0,0,1) from different directions. The values provided below
|
||||
// are the limits as x->(1-z)/2 and y->(1-z)/2 i.e. along the line from
|
||||
// the center of the base of the pyramid towards the apex. The resulting
|
||||
// limiting basis function values are:
|
||||
shape(0,0) = 0.;
|
||||
shape(0,1) = 0.;
|
||||
shape(0,2) = 0.;
|
||||
@@ -4883,21 +4887,21 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
|
||||
shape(3,1) = 0.;
|
||||
shape(3,2) = 0.;
|
||||
|
||||
shape(4,0) = 1.;
|
||||
shape(4,1) = 1.;
|
||||
shape(4,2) = 1.;
|
||||
shape(4,0) = 0.5;
|
||||
shape(4,1) = 0.5;
|
||||
shape(4,2) = 0.75;
|
||||
|
||||
shape(5,0) = - 1.;
|
||||
shape(5,1) = 0.;
|
||||
shape(5,2) = 0.;
|
||||
shape(5,0) = - 0.5;
|
||||
shape(5,1) = 0.5;
|
||||
shape(5,2) = 0.25;
|
||||
|
||||
shape(6,0) = 0.;
|
||||
shape(6,1) = 0.;
|
||||
shape(6,2) = 0.;
|
||||
shape(6,0) = - 0.5;
|
||||
shape(6,1) = - 0.5;
|
||||
shape(6,2) = - 0.25;
|
||||
|
||||
shape(7,0) = 0.;
|
||||
shape(7,1) = - 1.;
|
||||
shape(7,2) = 0.;
|
||||
shape(7,0) = 0.5;
|
||||
shape(7,1) = - 0.5;
|
||||
shape(7,2) = 0.25;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -4952,36 +4956,36 @@ const
|
||||
// In order to remain inside the pyramid in this limit the x and y
|
||||
// coordinates must be approaching 0. The resulting limiting basis
|
||||
// function values are:
|
||||
curl_shape(0,0) = 0.;
|
||||
curl_shape(0,1) = - 2.;
|
||||
curl_shape(0,0) = - 0.5;
|
||||
curl_shape(0,1) = - 1.5;
|
||||
curl_shape(0,2) = 1.;
|
||||
|
||||
curl_shape(1,0) = 0.;
|
||||
curl_shape(1,1) = 0.;
|
||||
curl_shape(1,0) = 0.5;
|
||||
curl_shape(1,1) = - 0.5;
|
||||
curl_shape(1,2) = 1.;
|
||||
|
||||
curl_shape(2,0) = 0.;
|
||||
curl_shape(2,1) = 0.;
|
||||
curl_shape(2,0) = 0.5;
|
||||
curl_shape(2,1) = - 0.5;
|
||||
curl_shape(2,2) = - 1.;
|
||||
|
||||
curl_shape(3,0) = 2.;
|
||||
curl_shape(3,1) = 0.;
|
||||
curl_shape(3,0) = 1.5;
|
||||
curl_shape(3,1) = 0.5;
|
||||
curl_shape(3,2) = - 1.;
|
||||
|
||||
curl_shape(4,0) = - 2.;
|
||||
curl_shape(4,1) = 2.;
|
||||
curl_shape(4,0) = - 1.;
|
||||
curl_shape(4,1) = 1.;
|
||||
curl_shape(4,2) = 0.;
|
||||
|
||||
curl_shape(5,0) = 0.;
|
||||
curl_shape(5,1) = - 2.;
|
||||
curl_shape(5,0) = - 1.;
|
||||
curl_shape(5,1) = - 1.;
|
||||
curl_shape(5,2) = 0.;
|
||||
|
||||
curl_shape(6,0) = 0.;
|
||||
curl_shape(6,1) = 0.;
|
||||
curl_shape(6,0) = 1.;
|
||||
curl_shape(6,1) = - 1.;
|
||||
curl_shape(6,2) = 0.;
|
||||
|
||||
curl_shape(7,0) = 2.;
|
||||
curl_shape(7,1) = 0.;
|
||||
curl_shape(7,0) = 1.;
|
||||
curl_shape(7,1) = 1.;
|
||||
curl_shape(7,2) = 0.;
|
||||
|
||||
return;
|
||||
@@ -5119,6 +5123,540 @@ void Nedelec1PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
|
||||
}
|
||||
|
||||
|
||||
Nedelec2PyrFiniteElement::Nedelec2PyrFiniteElement()
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, 28, 2, H_CURL, FunctionSpace::Uk)
|
||||
{
|
||||
const real_t *eop = poly1d.OpenPoints(2 - 1);
|
||||
const real_t fop = 1. / 3.;
|
||||
|
||||
// not real nodes ...
|
||||
Nodes.IntPoint(0).Set3(eop[0], 0., 0.);
|
||||
Nodes.IntPoint(1).Set3(eop[1], 0., 0.);
|
||||
|
||||
Nodes.IntPoint(2).Set3(1.0, eop[0], 0.);
|
||||
Nodes.IntPoint(3).Set3(1.0, eop[1], 0.);
|
||||
|
||||
Nodes.IntPoint(4).Set3(eop[0], 1.0, 0.);
|
||||
Nodes.IntPoint(5).Set3(eop[1], 1.0, 0.);
|
||||
|
||||
Nodes.IntPoint(6).Set3(0., eop[0], 0.);
|
||||
Nodes.IntPoint(7).Set3(0., eop[1], 0.);
|
||||
|
||||
Nodes.IntPoint(8).Set3(0., 0., eop[0]);
|
||||
Nodes.IntPoint(9).Set3(0., 0., eop[1]);
|
||||
|
||||
Nodes.IntPoint(10).Set3(eop[1], 0., eop[0]);
|
||||
Nodes.IntPoint(11).Set3(eop[0], 0., eop[1]);
|
||||
|
||||
Nodes.IntPoint(12).Set3(eop[1], eop[1], eop[0]);
|
||||
Nodes.IntPoint(13).Set3(eop[0], eop[0], eop[1]);
|
||||
|
||||
Nodes.IntPoint(14).Set3(0., eop[1], eop[0]);
|
||||
Nodes.IntPoint(15).Set3(0., eop[0], eop[1]);
|
||||
|
||||
Nodes.IntPoint(16).Set3(eop[0], 0.5, 0.);
|
||||
Nodes.IntPoint(17).Set3(eop[1], 0.5, 0.);
|
||||
|
||||
Nodes.IntPoint(18).Set3(0.5, eop[0], 0.);
|
||||
Nodes.IntPoint(19).Set3(0.5, eop[1], 0.);
|
||||
|
||||
Nodes.IntPoint(20).Set3(fop, 0., fop);
|
||||
Nodes.IntPoint(21).Set3(fop, 0., fop);
|
||||
|
||||
Nodes.IntPoint(22).Set3(2.*fop, fop, fop);
|
||||
Nodes.IntPoint(23).Set3(2.*fop, fop, fop);
|
||||
|
||||
Nodes.IntPoint(24).Set3(fop, 2.*fop, fop);
|
||||
Nodes.IntPoint(25).Set3(fop, 2.*fop, fop);
|
||||
|
||||
Nodes.IntPoint(26).Set3(0., fop, fop);
|
||||
Nodes.IntPoint(27).Set3(0., fop, fop);
|
||||
|
||||
{
|
||||
int n = 28;
|
||||
DenseMatrix I(n,n);
|
||||
DenseMatrix vecs(n,3);
|
||||
I = 0.0;
|
||||
|
||||
for (int i=0; i<n; i++)
|
||||
{
|
||||
CalcVShape(Nodes.IntPoint(i), vecs);
|
||||
for (int j=0; j<n; j++)
|
||||
{
|
||||
I(j,i) = vecs(j,0)*tk[i][0]+vecs(j,1)*tk[i][1]+vecs(j,2)*tk[i][2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Nedelec2PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const
|
||||
{
|
||||
shape = 0.0;
|
||||
|
||||
const real_t one = 1.0;
|
||||
const real_t x = ip.x, y = ip.y, z = ip.z;
|
||||
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
|
||||
const real_t sq3 = sqrt(3.0);
|
||||
const real_t tol = 1e-6;
|
||||
|
||||
if (oz <= tol)
|
||||
{
|
||||
// We must return the limit of the basis functions as z->1. In order to
|
||||
// remain inside the pyramid in this limit the x and y coordinates must
|
||||
// be approaching 0. The resulting limiting basis function values are:
|
||||
shape(0,0) = 0.;
|
||||
shape(0,1) = 0.;
|
||||
shape(0,2) = 0.;
|
||||
|
||||
shape(1,0) = 0.;
|
||||
shape(1,1) = 0.;
|
||||
shape(1,2) = 0.;
|
||||
|
||||
shape(2,0) = 0.;
|
||||
shape(2,1) = 0.;
|
||||
shape(2,2) = 0.;
|
||||
|
||||
shape(3,0) = 0.;
|
||||
shape(3,1) = 0.;
|
||||
shape(3,2) = 0.;
|
||||
|
||||
shape(4,0) = 0.;
|
||||
shape(4,1) = 0.;
|
||||
shape(4,2) = 0.;
|
||||
|
||||
shape(5,0) = 0.;
|
||||
shape(5,1) = 0.;
|
||||
shape(5,2) = 0.;
|
||||
|
||||
shape(6,0) = 0.;
|
||||
shape(6,1) = 0.;
|
||||
shape(6,2) = 0.;
|
||||
|
||||
shape(7,0) = 0.;
|
||||
shape(7,1) = 0.;
|
||||
shape(7,2) = 0.;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const real_t ozi = one / oz;
|
||||
|
||||
const real_t me0120[3] = {oy, 0., x * oy * ozi};
|
||||
const real_t me1120[3] = {(x - ox) * oy, 0., (x - ox) * x * oy * ozi};
|
||||
|
||||
const real_t me0121[3] = {y, 0., x * y * ozi};
|
||||
const real_t me1121[3] = {(x - ox) * y, 0., (x - ox) * x * y * ozi};
|
||||
|
||||
const real_t me0210[3] = {0., ox, ox * y * ozi};
|
||||
const real_t me1210[3] = {0., ox * (y - oy), ox * y * (y - oy) * ozi};
|
||||
|
||||
const real_t me0211[3] = {0., x, x * y * ozi};
|
||||
const real_t me1211[3] = {0., x * (y - oy), x * y * (y - oy) * ozi};
|
||||
|
||||
const real_t te01[3] = {oy * z * ozi, ox * z * ozi,
|
||||
(ox * oy + (x * oy + ox * y) * z) * ozi * ozi
|
||||
};
|
||||
const real_t te11[3] = {oy * z * (z * oz - ox * oy) * ozi * ozi,
|
||||
ox * z * (z * oz - ox * oy) * ozi * ozi,
|
||||
(ox * oy + z * (x * oy + ox * y)) *
|
||||
(z * oz - ox * oy) * ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t te02[3] = {-oy * z * ozi, x * z * ozi,
|
||||
x * (y * z + oy * oz) * ozi * ozi
|
||||
};
|
||||
const real_t te12[3] = {oy * z * (x * oy - z * oz) * ozi * ozi,
|
||||
-x * z * (x * oy - z * oz) * ozi * ozi,
|
||||
-x * (y * z + oy * oz) * (x * oy - z * oz)
|
||||
* ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t te03[3] = {-y * z * ozi, -x * z * ozi,
|
||||
x * y * (one - 2_r * z) * ozi * ozi
|
||||
};
|
||||
const real_t te13[3] = {y * z * (x * y - z * oz) * ozi * ozi,
|
||||
x * z * (x * y - z * oz) * ozi * ozi,
|
||||
-x * y * (one - 2_r * z) * (x * y - z * oz)
|
||||
* ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t te04[3] = {y * z * ozi, -ox * z * ozi,
|
||||
y * (x * z + ox * oz) * ozi * ozi
|
||||
};
|
||||
const real_t te14[3] = {-y * z * (ox * y - z * oz) * ozi * ozi,
|
||||
ox * z * (ox * y - z * oz) * ozi * ozi,
|
||||
-y * (x * z + ox * oz) * (ox * y - z * oz)
|
||||
* ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t qI02[3] = {-y * oy * ozi, 0., -x * y * oy * ozi * ozi};
|
||||
const real_t qI12[3] = {-(x - ox) * y * oy * ozi * ozi, 0.,
|
||||
-(x - ox) * x * y * oy * ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t qII02[3] = {0., -x * ox * ozi, -x * y * ox * ozi * ozi};
|
||||
const real_t qII12[3] = {0., -x * ox * (y - oy) * ozi * ozi,
|
||||
-x * ox * y * (y - oy) * ozi * ozi * ozi
|
||||
};
|
||||
|
||||
const real_t tI120[3] = {oy * z, 0., x * oy * z * ozi};
|
||||
const real_t tI121[3] = {y * z, 0., x * y * z * ozi};
|
||||
const real_t tI210[3] = {0., ox * z, ox * y * z * ozi};
|
||||
const real_t tI211[3] = {0., x * z, x * y * z * ozi};
|
||||
|
||||
const real_t tII120[3] = {-ox * oy * z * ozi, 0., x * ox * oy * ozi};
|
||||
const real_t tII121[3] = {-ox * y * z * ozi, 0., x * ox * y * ozi};
|
||||
const real_t tII210[3] = {0., -ox * oy * z * ozi, ox * y * oy * ozi};
|
||||
const real_t tII211[3] = {0., -x * oy * z * ozi, x * y * oy * ozi};
|
||||
|
||||
// Edge 0,1
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(0,d) = 0.5 * me0120[d] + qI02[d]
|
||||
- sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(1,d) = 0.5 * me0120[d] + qI02[d]
|
||||
+ sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
|
||||
}
|
||||
|
||||
// Edge 1,2
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(2,d) = 0.5 * me0211[d] + qII02[d]
|
||||
- sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(3,d) = 0.5 * me0211[d] + qII02[d]
|
||||
+ sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
|
||||
}
|
||||
|
||||
// Edge 3,2
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(4,d) = 0.5 * me0121[d] + qI02[d]
|
||||
- sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(5,d) = 0.5 * me0121[d] + qI02[d]
|
||||
+ sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
|
||||
}
|
||||
|
||||
// Edge 0,3
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(6,d) = 0.5 * me0210[d] + qII02[d]
|
||||
- sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(7,d) = 0.5 * me0210[d] + qII02[d]
|
||||
+ sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
|
||||
}
|
||||
|
||||
// Edge 0,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(8,d) = 0.5 * te01[d] - sq3 * 0.5 * te11[d]
|
||||
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(9,d) = 0.5 * te01[d] + sq3 * 0.5 * te11[d]
|
||||
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
|
||||
}
|
||||
|
||||
// Edge 1,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(10,d) = 0.5 * te02[d] - sq3 * 0.5 * te12[d]
|
||||
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(11,d) = 0.5 * te02[d] + sq3 * 0.5 * te12[d]
|
||||
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
|
||||
}
|
||||
|
||||
// Edge 2,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(12,d) = 0.5 * te03[d] - sq3 * 0.5 * te13[d]
|
||||
- 1.5 * (tII211[d] + tII121[d]);
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(13,d) = 0.5 * te03[d] + sq3 * 0.5 * te13[d]
|
||||
- 1.5 * (tII211[d] + tII121[d]);
|
||||
}
|
||||
|
||||
// Edge 3,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(14,d) = 0.5 * te04[d] - sq3 * 0.5 * te14[d]
|
||||
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(15,d) = 0.5 * te04[d] + sq3 * 0.5 * te14[d]
|
||||
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
|
||||
}
|
||||
|
||||
// Quadrilateral face
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(16,d) = -2. * qI02[d] + 2. * sq3 * qI12[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(17,d) = -2. * qI02[d] - 2. * sq3 * qI12[d];
|
||||
}
|
||||
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(18,d) = 2. * qII02[d] - 2. * sq3 * qII12[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(19,d) = 2. * qII02[d] + 2. * sq3 * qII12[d];
|
||||
}
|
||||
|
||||
// Triangular face 0,1,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(20,d) = 3. * tI120[d] - 3. * tII120[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(21,d) = 3. * tI120[d] + 6. * tII120[d];
|
||||
}
|
||||
|
||||
// Triangular face 1,2,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(22,d) = 3. * tI211[d] - 3. * tII211[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(23,d) = 3. * tI211[d] + 6. * tII211[d];
|
||||
}
|
||||
|
||||
// Triangular face 2,3,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(24,d) = -6. * tI121[d] - 3. * tII121[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(25,d) = 3. * tI121[d] + 6. * tII121[d];
|
||||
}
|
||||
|
||||
// Triangular face 3,0,4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(26,d) = -6. * tI210[d] - 3. * tII210[d];
|
||||
}
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
shape(27,d) = 3. * tI210[d] + 6. * tII210[d];
|
||||
}
|
||||
}
|
||||
|
||||
void Nedelec2PyrFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape)
|
||||
const
|
||||
{
|
||||
const real_t one = 1.0;
|
||||
const real_t x = ip.x, y = ip.y, z = ip.z, z2 = 2. * z;
|
||||
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
|
||||
|
||||
const real_t tol = 1e-6;
|
||||
|
||||
if (oz <= tol)
|
||||
{
|
||||
// We must return the limit of the basis function derivatives as z->1.
|
||||
// In order to remain inside the pyramid in this limit the x and y
|
||||
// coordinates must be approaching 0. The resulting limiting basis
|
||||
// function values are:
|
||||
curl_shape(0,0) = 0.;
|
||||
curl_shape(0,1) = - 2.;
|
||||
curl_shape(0,2) = 1.;
|
||||
|
||||
curl_shape(1,0) = 0.;
|
||||
curl_shape(1,1) = 0.;
|
||||
curl_shape(1,2) = 1.;
|
||||
|
||||
curl_shape(2,0) = 0.;
|
||||
curl_shape(2,1) = 0.;
|
||||
curl_shape(2,2) = - 1.;
|
||||
|
||||
curl_shape(3,0) = 2.;
|
||||
curl_shape(3,1) = 0.;
|
||||
curl_shape(3,2) = - 1.;
|
||||
|
||||
curl_shape(4,0) = - 2.;
|
||||
curl_shape(4,1) = 2.;
|
||||
curl_shape(4,2) = 0.;
|
||||
|
||||
curl_shape(5,0) = 0.;
|
||||
curl_shape(5,1) = - 2.;
|
||||
curl_shape(5,2) = 0.;
|
||||
|
||||
curl_shape(6,0) = 0.;
|
||||
curl_shape(6,1) = 0.;
|
||||
curl_shape(6,2) = 0.;
|
||||
|
||||
curl_shape(7,0) = 2.;
|
||||
curl_shape(7,1) = 0.;
|
||||
curl_shape(7,2) = 0.;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
real_t ozi = one / oz;
|
||||
|
||||
curl_shape(0,0) = - x * ozi;
|
||||
curl_shape(0,1) = - 2. + y * ozi;
|
||||
curl_shape(0,2) = 1.;
|
||||
|
||||
curl_shape(1,0) = x * ozi;
|
||||
curl_shape(1,1) = - y * ozi;
|
||||
curl_shape(1,2) = 1.;
|
||||
|
||||
curl_shape(2,0) = x * ozi;
|
||||
curl_shape(2,1) = - y * ozi;
|
||||
curl_shape(2,2) = - 1.;
|
||||
|
||||
curl_shape(3,0) = (2. - x - z2) * ozi;
|
||||
curl_shape(3,1) = y * ozi;
|
||||
curl_shape(3,2) = - 1.;
|
||||
|
||||
curl_shape(4,0) = - 2. * ox * ozi;
|
||||
curl_shape(4,1) = 2. * oy * ozi;
|
||||
curl_shape(4,2) = 0.;
|
||||
|
||||
curl_shape(5,0) = - 2. * x * ozi;
|
||||
curl_shape(5,1) = - 2. * oy * ozi;
|
||||
curl_shape(5,2) = 0.;
|
||||
|
||||
curl_shape(6,0) = 2. * x * ozi;
|
||||
curl_shape(6,1) = - 2. * y * ozi;
|
||||
curl_shape(6,2) = 0.;
|
||||
|
||||
curl_shape(7,0) = 2. * ox * ozi;
|
||||
curl_shape(7,1) = 2. * y * ozi;
|
||||
curl_shape(7,2) = 0.;
|
||||
}
|
||||
|
||||
const real_t Nedelec2PyrFiniteElement::tk[28][3] =
|
||||
{
|
||||
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
|
||||
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
|
||||
{0,0,1}, {0,0,1}, {-1,0,1}, {-1,0,1},
|
||||
{-1,-1,1}, {-1,-1,1}, {0,-1,1}, {0,-1,1},
|
||||
{1,0,0}, {1,0,0}, {0,-1,0}, {0,-1,0},
|
||||
{1,0,0}, {0,0,1}, {0,1,0}, {-1,0,1},
|
||||
{-1,0,0}, {-1,-1,1}, {0,-1,0}, {0,-1,1}
|
||||
};
|
||||
|
||||
void Nedelec2PyrFiniteElement::GetLocalInterpolation (
|
||||
ElementTransformation &Trans, DenseMatrix &I) const
|
||||
{
|
||||
int k, j;
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix vshape(dof, dim);
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
for (k = 0; k < dof; k++)
|
||||
{
|
||||
CalcVShape (Nodes.IntPoint(k), vshape);
|
||||
for (j = 0; j < dof; j++)
|
||||
{
|
||||
real_t d = ( vshape(j,0)*tk[k][0] + vshape(j,1)*tk[k][1] +
|
||||
vshape(j,2)*tk[k][2] );
|
||||
if (j == k) { d -= 1.0; }
|
||||
if (fabs(d) > 1.0e-12)
|
||||
{
|
||||
mfem::err << "Nedelec1PyrFiniteElement::GetLocalInterpolation (...)\n"
|
||||
" k = " << k << ", j = " << j << ", d = " << d << endl;
|
||||
mfem_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IntegrationPoint ip;
|
||||
ip.x = ip.y = ip.z = 0.0;
|
||||
Trans.SetIntPoint (&ip);
|
||||
// Trans must be linear
|
||||
const DenseMatrix &J = Trans.Jacobian();
|
||||
real_t vk[3];
|
||||
Vector xk (vk, 3);
|
||||
|
||||
for (k = 0; k < dof; k++)
|
||||
{
|
||||
Trans.Transform (Nodes.IntPoint (k), xk);
|
||||
ip.x = vk[0]; ip.y = vk[1]; ip.z = vk[2];
|
||||
CalcVShape (ip, vshape);
|
||||
// vk = J tk
|
||||
vk[0] = J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2];
|
||||
vk[1] = J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2];
|
||||
vk[2] = J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2];
|
||||
for (j = 0; j < dof; j++)
|
||||
if (fabs (I(k,j) = (vshape(j,0)*vk[0]+vshape(j,1)*vk[1]+
|
||||
vshape(j,2)*vk[2])) < 1.0e-12)
|
||||
{
|
||||
I(k,j) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Nedelec2PyrFiniteElement::Project (
|
||||
VectorCoefficient &vc, ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
real_t vk[3];
|
||||
Vector xk (vk, 3);
|
||||
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
Trans.SetIntPoint (&Nodes.IntPoint (k));
|
||||
const DenseMatrix &J = Trans.Jacobian();
|
||||
|
||||
vc.Eval (xk, Trans, Nodes.IntPoint (k));
|
||||
// xk^t J tk
|
||||
dofs(k) =
|
||||
vk[0] * ( J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2] ) +
|
||||
vk[1] * ( J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2] ) +
|
||||
vk[2] * ( J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2] );
|
||||
}
|
||||
}
|
||||
|
||||
void Nedelec2PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const
|
||||
{
|
||||
DenseMatrix dshape(fe.GetDof(), 3);
|
||||
Vector grad_k(fe.GetDof());
|
||||
|
||||
grad.SetSize(dof, fe.GetDof());
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
fe.CalcDShape(Nodes.IntPoint(k), dshape);
|
||||
dshape.Mult(tk[k], grad_k);
|
||||
for (int j = 0; j < grad_k.Size(); j++)
|
||||
{
|
||||
grad(k,j) = (fabs(grad_k(j)) < 1e-12) ? 0.0 : grad_k(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RT0HexFiniteElement::RT0HexFiniteElement()
|
||||
: VectorFiniteElement(3, Geometry::CUBE, 6, 1, H_DIV, FunctionSpace::Qk)
|
||||
{
|
||||
@@ -5950,7 +6488,8 @@ void RT0WdgFiniteElement::ProjectCurl(const FiniteElement &fe,
|
||||
}
|
||||
|
||||
RT0PyrFiniteElement::RT0PyrFiniteElement(bool rt0tets)
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV), rt0(rt0tets)
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV, FunctionSpace::Uk),
|
||||
rt0(rt0tets)
|
||||
{
|
||||
// not real nodes ...
|
||||
Nodes.IntPoint(0).x = 0.5;
|
||||
@@ -6073,7 +6612,7 @@ void RT0PyrFiniteElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
|
||||
const real_t RT0PyrFiniteElement::nk[5][3] =
|
||||
{{0.,0.,-1.}, {0,-.5,0}, {.5,0,.5}, {0,.5,.5}, {-.5,0,0}};
|
||||
{{0.,0.,-1}, {0,-1,0}, {1,0,1}, {0,1,1}, {-1,0,0}};
|
||||
|
||||
void RT0PyrFiniteElement::GetLocalInterpolation (
|
||||
ElementTransformation &Trans, DenseMatrix &I) const
|
||||
|
||||
@@ -1018,6 +1018,34 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 2nd order Nedelec element on a pyramid
|
||||
class Nedelec2PyrFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
private:
|
||||
static const real_t tk[28][3];
|
||||
|
||||
public:
|
||||
/// Construct the Nedelec2PyrFiniteElement
|
||||
Nedelec2PyrFiniteElement();
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_ND(Trans, shape); }
|
||||
virtual void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const;
|
||||
virtual void GetLocalInterpolation (ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
using FiniteElement::Project;
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const;
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 0th order Raviert-Thomas element on a cube
|
||||
class RT0HexFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
|
||||
@@ -1040,4 +1040,829 @@ void H1_WedgeElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
H1_FuentesPyramidElement::H1_FuentesPyramidElement(const int p, const int btype)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID,
|
||||
p * (p * p + 3) + 1, // Fuentes et al
|
||||
p, FunctionSpace::Uk)
|
||||
{
|
||||
zmax = 0.0;
|
||||
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
tmp_i.SetSize(p + 1);
|
||||
tmp1_ij.SetSize(p + 1, p + 1);
|
||||
tmp2_ij.SetSize(p + 1, dim);
|
||||
tmp_ijk.SetSize(p + 1, p + 1, dim);
|
||||
tmp_u.SetSize(dof);
|
||||
tmp_du.SetSize(dof, dim);
|
||||
#else
|
||||
Vector tmp_i(p + 1);
|
||||
DenseMatrix tmp1_ij(p + 1, p + 1);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
|
||||
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
|
||||
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
|
||||
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
|
||||
|
||||
// edges
|
||||
int o = 5;
|
||||
for (int i = 1; i < p; i++) // (0,1)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,3)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (2,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
|
||||
}
|
||||
|
||||
// quadrilateral face
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// triangular faces
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,1,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (1,2,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3((cp[i] + cp[p-i-j])/w, cp[i]/w, cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (2,3,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, (cp[i] + cp[p-i-j])/w, cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (3,0,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i-j]/w, cp[j]/w);
|
||||
}
|
||||
|
||||
// Points based on Fuentes' interior bubbles
|
||||
for (int k = 1; k < p; k++)
|
||||
{
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i] * (1.0 - cp[k]),
|
||||
cp[j] * (1.0 - cp[k]),
|
||||
cp[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_ASSERT(o == dof,
|
||||
"Number of nodes does not match the "
|
||||
"number of degrees of freedom");
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
Vector col(T.GetColumn(m), dof);
|
||||
calcBasis(order, ip, tmp_i, tmp1_ij, col);
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector tmp_i(p + 1);
|
||||
Vector tmp_u(dof);
|
||||
DenseMatrix tmp1_ij(p + 1, p + 1);
|
||||
#endif
|
||||
|
||||
calcBasis(p, ip, tmp_i, tmp1_ij, tmp_u);
|
||||
|
||||
Ti.Mult(tmp_u, shape);
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector tmp_i(p + 1);
|
||||
DenseMatrix tmp1_ij(p + 1, p + 1);
|
||||
DenseMatrix tmp2_ij(p + 1, dim);
|
||||
DenseTensor tmp_ijk(p + 1, p + 1, dim);
|
||||
DenseMatrix tmp_du(dof, dim);
|
||||
#endif
|
||||
|
||||
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, tmp_du);
|
||||
Ti.Mult(tmp_du, dshape);
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::CalcRawShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector tmp_i(p + 1);
|
||||
DenseMatrix tmp1_ij(p + 1, p + 1);
|
||||
#endif
|
||||
|
||||
calcBasis(p, ip, tmp_i, tmp1_ij, shape);
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::CalcRawDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector tmp_i(p + 1);
|
||||
DenseMatrix tmp1_ij(p + 1, p + 1);
|
||||
DenseMatrix tmp2_ij(p + 1, dim);
|
||||
DenseTensor tmp_ijk(p + 1, p + 1, dim);
|
||||
#endif
|
||||
|
||||
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, dshape);
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::calcBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
Vector &phi_i, DenseMatrix &phi_ij,
|
||||
Vector &u) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
real_t mu;
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Vertices
|
||||
u[0] = lam1(x, y, z);
|
||||
u[1] = lam2(x, y, z);
|
||||
u[2] = lam3(x, y, z);
|
||||
u[3] = lam4(x, y, z);
|
||||
u[4] = lam5(x, y, z);
|
||||
|
||||
o += 5;
|
||||
|
||||
// Mixed edges (base edges)
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
// (a,b) = (1,2), c = 0
|
||||
phi_E(p, nu01(z, xy, 1), phi_i);
|
||||
mu = mu0(z, xy, 2);
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = mu * phi_i[i];
|
||||
}
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = mu * phi_i[i];
|
||||
}
|
||||
// (a,b) = (2,1), c = 0
|
||||
phi_E(p, nu01(z, xy, 2), phi_i);
|
||||
mu = mu0(z, xy, 1);
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = mu * phi_i[i];
|
||||
}
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = mu * phi_i[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4 * (p - 1); i++, o++)
|
||||
{
|
||||
u[o] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Triangle edges (upright edges)
|
||||
if (p >= 2)
|
||||
{
|
||||
phi_E(p, lam15(x, y, z), phi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
{
|
||||
u[o] = phi_i[i];
|
||||
}
|
||||
phi_E(p, lam25(x, y, z), phi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
{
|
||||
u[o] = phi_i[i];
|
||||
}
|
||||
phi_E(p, lam35(x, y, z), phi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
{
|
||||
u[o] = phi_i[i];
|
||||
}
|
||||
phi_E(p, lam45(x, y, z), phi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
{
|
||||
u[o] = phi_i[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Quadrilateral face
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
|
||||
mu = mu0(z);
|
||||
for (int j = 2; j <= p; j++)
|
||||
{
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = mu * phi_ij(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 2; j <= p; j++)
|
||||
{
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular faces
|
||||
if (CheckZ(z) && p >= 3)
|
||||
{
|
||||
// (a,b) = (1,2), c = 0
|
||||
phi_T(p, nu012(z, xy, 1), phi_ij);
|
||||
mu = mu0(z, xy, 2);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
{
|
||||
u[o] = mu * phi_ij(i,j);
|
||||
}
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
{
|
||||
u[o] = mu * phi_ij(i,j);
|
||||
}
|
||||
// (a,b) = (2,1), c = 0
|
||||
phi_T(p, nu012(z, xy, 2), phi_ij);
|
||||
mu = mu0(z, xy, 1);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
{
|
||||
u[o] = mu * phi_ij(i,j);
|
||||
}
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
{
|
||||
u[o] = mu * phi_ij(i,j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
|
||||
{
|
||||
u[o] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
|
||||
phi_E(p, mu01(z), phi_i);
|
||||
for (int k = 2; k <= p; k++)
|
||||
{
|
||||
for (int j = 2; j <= p; j++)
|
||||
{
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = phi_ij(i,j) * phi_i(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
|
||||
{
|
||||
u[o]= 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void H1_FuentesPyramidElement::calcGradBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
Vector &phi_i,
|
||||
DenseMatrix &dphi_i,
|
||||
DenseMatrix &phi_ij,
|
||||
DenseTensor &dphi_ij,
|
||||
DenseMatrix &du) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
real_t mu;
|
||||
Vector dmu(3);
|
||||
Vector dlam(3);
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Vertices
|
||||
dlam = grad_lam1(x, y, z);
|
||||
for (int d=0; d<3; d++) { du(0, d) = dlam(d); }
|
||||
dlam = grad_lam2(x, y, z);
|
||||
for (int d=0; d<3; d++) { du(1, d) = dlam(d); }
|
||||
dlam = grad_lam3(x, y, z);
|
||||
for (int d=0; d<3; d++) { du(2, d) = dlam(d); }
|
||||
dlam = grad_lam4(x, y, z);
|
||||
for (int d=0; d<3; d++) { du(3, d) = dlam(d); }
|
||||
dlam = grad_lam5(x, y, z);
|
||||
for (int d=0; d<3; d++) { du(4, d) = dlam(d); }
|
||||
|
||||
o += 5;
|
||||
|
||||
// Mixed edges (base edges)
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
// (a,b) = (1,2), c = 0
|
||||
phi_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), phi_i, dphi_i);
|
||||
mu = mu0(z, xy, 2);
|
||||
dmu = grad_mu0(z, xy, 2);;
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
|
||||
}
|
||||
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmu = grad_mu1(z, xy, 2);;
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 0
|
||||
phi_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), phi_i, dphi_i);
|
||||
mu = mu0(z, xy, 1);
|
||||
dmu = grad_mu0(z, xy, 1);;
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmu = grad_mu1(z, xy, 1);;
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4 * (p - 1); i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Triangle edges (upright edges)
|
||||
if (p >= 2)
|
||||
{
|
||||
phi_E(p, lam15(x, y, z), grad_lam15(x,y,z), phi_i, dphi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dphi_i(i, d);
|
||||
}
|
||||
|
||||
phi_E(p, lam25(x, y, z), grad_lam25(x, y, z), phi_i, dphi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dphi_i(i, d);
|
||||
}
|
||||
|
||||
phi_E(p, lam35(x, y, z), grad_lam35(x, y, z), phi_i, dphi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dphi_i(i, d);
|
||||
}
|
||||
|
||||
phi_E(p, lam45(x, y, z), grad_lam45(x, y, z), phi_i, dphi_i);
|
||||
for (int i = 2; i<= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dphi_i(i, d);
|
||||
}
|
||||
}
|
||||
|
||||
// Quadrilateral face
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
|
||||
mu = mu0(z);
|
||||
dmu = grad_mu0(z);
|
||||
for (int j = 2; j <= p; j++)
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 2; j <= p; j++)
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular faces
|
||||
if (CheckZ(z) && p >= 3)
|
||||
{
|
||||
// (a,b) = (1,2), c = 0
|
||||
phi_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), phi_ij, dphi_ij);
|
||||
mu = mu0(z, xy, 2);
|
||||
dmu = grad_mu0(z, xy, 2);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
|
||||
}
|
||||
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmu = grad_mu1(z, xy, 2);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 0
|
||||
phi_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), phi_ij, dphi_ij);
|
||||
mu = mu0(z, xy, 1);
|
||||
dmu = grad_mu0(z, xy, 1);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmu = grad_mu1(z, xy, 1);
|
||||
for (int i = 2; i < p; i++)
|
||||
for (int j = 1; i + j <= p; j++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
if (CheckZ(z) && p >= 2)
|
||||
{
|
||||
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
|
||||
phi_E(p, mu01(z), grad_mu01(z), phi_i, dphi_i);
|
||||
for (int k = 2; k <= p; k++)
|
||||
for (int j = 2; j <= p; j++)
|
||||
for (int i = 2; i <= p; i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
du(o, d) = dphi_ij(i, j, d) * phi_i(k) +
|
||||
phi_ij(i, j) * dphi_i(k, d);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
du(o, d) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
H1_BergotPyramidElement::H1_BergotPyramidElement(const int p, const int btype)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID,
|
||||
(p + 1) * (p + 2) * (2 * p + 3) / 6, // Bergot (JSC)
|
||||
p, FunctionSpace::Uk)
|
||||
{
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(p + 1);
|
||||
shape_y.SetSize(p + 1);
|
||||
shape_z.SetSize(p + 1);
|
||||
dshape_x.SetSize(p + 1);
|
||||
dshape_y.SetSize(p + 1);
|
||||
dshape_z.SetSize(p + 1);
|
||||
dshape_z_dt.SetSize(p + 1);
|
||||
ddshape_x.SetSize(p + 1);
|
||||
ddshape_y.SetSize(p + 1);
|
||||
ddshape_z.SetSize(p + 1);
|
||||
u.SetSize(dof);
|
||||
du.SetSize(dof, dim);
|
||||
ddu.SetSize(dof, (dim * (dim + 1)) / 2);
|
||||
#else
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
|
||||
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
|
||||
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
|
||||
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
|
||||
|
||||
// edges
|
||||
int o = 5;
|
||||
for (int i = 1; i < p; i++) // (0,1)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,3)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (2,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,4)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
|
||||
}
|
||||
|
||||
// quadrilateral face
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// triangular faces
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,1,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (1,2,4)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(1.0 - cp[j]/w, cp[i]/w, cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (3,4,2)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[j]/w, 1.0 - cp[i]/w, cp[i]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,4,3)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
|
||||
}
|
||||
|
||||
// interior
|
||||
for (int k = 1; k < p - 1; k++)
|
||||
{
|
||||
for (int j = 1; j < p - k; j++)
|
||||
{
|
||||
real_t wjk = cp[j] + cp[k] + cp[p-j-k];
|
||||
for (int i = 1; i < p - k; i++)
|
||||
{
|
||||
real_t wik = cp[i] + cp[k] + cp[p-i-k];
|
||||
real_t w = wik * wjk * cp[p-k];
|
||||
Nodes.IntPoint(o++).Set3(cp[i] * (cp[j] + cp[p-j-k]) / w,
|
||||
cp[j] * (cp[i] + cp[p-i-k]) / w,
|
||||
cp[k] * cp[p-k] / w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_ASSERT(o == dof,
|
||||
"Number of nodes does not match the "
|
||||
"number of degrees of freedom");
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
|
||||
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
real_t z = ip.z;
|
||||
|
||||
poly1d.CalcLegendre(p, x, shape_x.GetData());
|
||||
poly1d.CalcLegendre(p, y, shape_y.GetData());
|
||||
|
||||
o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
|
||||
z, 1.0, shape_z);
|
||||
|
||||
for (int k = 0; k <= p - maxij; k++)
|
||||
{
|
||||
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void H1_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(order+1);
|
||||
Vector shape_y(order+1);
|
||||
Vector shape_z(order+1);
|
||||
Vector u(dof);
|
||||
#endif
|
||||
|
||||
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
real_t z = ip.z;
|
||||
|
||||
poly1d.CalcLegendre(p, x, shape_x.GetData());
|
||||
poly1d.CalcLegendre(p, y, shape_y.GetData());
|
||||
|
||||
int o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
|
||||
shape_z);
|
||||
for (int k = 0; k <= p - maxij; k++)
|
||||
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij);
|
||||
}
|
||||
|
||||
Ti.Mult(u, shape);
|
||||
}
|
||||
|
||||
void H1_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix du(dof, dim);
|
||||
Vector shape_x(order+1);
|
||||
Vector shape_y(order+1);
|
||||
Vector shape_z(order+1);
|
||||
Vector dshape_x(order+1);
|
||||
Vector dshape_y(order+1);
|
||||
Vector dshape_z(order+1);
|
||||
Vector dshape_z_dt(order+1);
|
||||
#endif
|
||||
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
real_t z = ip.z;
|
||||
|
||||
poly1d.CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
|
||||
poly1d.CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
|
||||
|
||||
int o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
|
||||
shape_z, dshape_z, dshape_z_dt);
|
||||
|
||||
for (int k = 0; k <= p - maxij; k++, o++)
|
||||
{
|
||||
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1);
|
||||
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1);
|
||||
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
|
||||
pow(1.0 - ip.z, maxij) +
|
||||
(ip.x * dshape_x(i) * shape_y(j) +
|
||||
ip.y * shape_x(i) * dshape_y(j)) *
|
||||
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
|
||||
maxij * shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Mult(du, dshape);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FE_H1
|
||||
|
||||
#include "fe_base.hpp"
|
||||
#include "fe_pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -148,6 +149,73 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
|
||||
|
||||
This implementation is closely based on the finite elements
|
||||
described in section 9.1 of the paper "Orientation embedded high
|
||||
order shape functions for the exact sequence elements of all shapes"
|
||||
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
|
||||
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
|
||||
*/
|
||||
class H1_FuentesPyramidElement
|
||||
: public NodalFiniteElement, public FuentesPyramid
|
||||
{
|
||||
private:
|
||||
mutable real_t zmax;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector tmp_i, tmp_u;
|
||||
mutable DenseMatrix tmp1_ij, tmp2_ij, tmp_du;
|
||||
mutable DenseTensor tmp_ijk;
|
||||
#endif
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
void calcBasis(const int p, const IntegrationPoint &ip,
|
||||
Vector &phi_i, DenseMatrix &phi_ij, Vector &u) const;
|
||||
void calcGradBasis(const int p, const IntegrationPoint &ip,
|
||||
Vector &phi_i, DenseMatrix &dphi_i,
|
||||
DenseMatrix &phi_ij, DenseTensor &dphi_ij,
|
||||
DenseMatrix &du) const;
|
||||
|
||||
public:
|
||||
H1_FuentesPyramidElement(const int p,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
void CalcRawShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
void CalcRawDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
|
||||
real_t GetZetaMax() const { return zmax; }
|
||||
};
|
||||
|
||||
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
|
||||
|
||||
This implementation is based on the finite elements described in the
|
||||
2010 paper "Higher-Order Finite Elements for Hybrid Meshes Using New
|
||||
Nodal Pyramidal Elements" by Morgane Bergot, Gary Cohen, and Marc
|
||||
Durufle, see https://hal.archives-ouvertes.fr/hal-00454261.
|
||||
*/
|
||||
class H1_BergotPyramidElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_z;
|
||||
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt, u;
|
||||
mutable Vector ddshape_x, ddshape_y, ddshape_z;
|
||||
mutable DenseMatrix du, ddu;
|
||||
#endif
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
public:
|
||||
H1_BergotPyramidElement(const int p,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -923,4 +923,321 @@ void L2_WedgeElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
L2_FuentesPyramidElement::L2_FuentesPyramidElement(const int p, const int btype)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, ((p + 1)*(p + 1)*(p + 1)),
|
||||
p, FunctionSpace::Uk)
|
||||
{
|
||||
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
|
||||
|
||||
// These basis functions are not independent on a closed set of
|
||||
// interpolation points when p >= 1. For this reason we force the points
|
||||
// to be open in the z direction whenever closed points are requested.
|
||||
// This should be regarded as a limitation of this choice of basis function.
|
||||
// If a truly closed set of points is needed consider using
|
||||
// L2_BergotPyramidElement instead.
|
||||
real_t a = 1.0;
|
||||
if (IsClosedType(btype) && p > 0)
|
||||
{
|
||||
a = (poly1d.GetPoints(p, BasisType::GaussLegendre))[p];
|
||||
}
|
||||
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(p + 1);
|
||||
shape_y.SetSize(p + 1);
|
||||
shape_z.SetSize(p + 1);
|
||||
dshape_x.SetSize(p + 1);
|
||||
dshape_y.SetSize(p + 1);
|
||||
dshape_z.SetSize(p + 1);
|
||||
u.SetSize(dof);
|
||||
du.SetSize(dof, dim);
|
||||
#else
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
#endif
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(op[i] * (1.0 - a * op[k]),
|
||||
op[j] * (1.0 - a * op[k]),
|
||||
a * op[k]);
|
||||
}
|
||||
|
||||
MFEM_ASSERT(o == dof,
|
||||
"Number of nodes does not match the "
|
||||
"number of degrees of freedom");
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
|
||||
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
|
||||
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
|
||||
|
||||
o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++, o++)
|
||||
{
|
||||
T(o, m) = shape_x[i] * shape_y[j] * shape_z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void L2_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
Vector u(dof);
|
||||
#endif
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
|
||||
if (z < 1.0)
|
||||
{
|
||||
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
|
||||
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
shape_x = 0.0; shape_x(0) = 1.0;
|
||||
shape_y = 0.0; shape_y(0) = 1.0;
|
||||
}
|
||||
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++, o++)
|
||||
{
|
||||
u[o] = shape_x[i] * shape_y[j] * shape_z[k];
|
||||
}
|
||||
|
||||
Ti.Mult(u, shape);
|
||||
}
|
||||
|
||||
void L2_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
Vector dshape_x(p + 1);
|
||||
Vector dshape_y(p + 1);
|
||||
Vector dshape_z(p + 1);
|
||||
DenseMatrix du(dof, dim);
|
||||
#endif
|
||||
|
||||
Poly_1D::CalcLegendre(p, ip.x / (1.0 - ip.z), shape_x.GetData(),
|
||||
dshape_x.GetData());
|
||||
Poly_1D::CalcLegendre(p, ip.y / (1.0 - ip.z), shape_y.GetData(),
|
||||
dshape_y.GetData());
|
||||
Poly_1D::CalcLegendre(p, ip.z, shape_z.GetData(), dshape_z.GetData());
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++, o++)
|
||||
{
|
||||
du(o, 0) = dshape_x[i] * shape_y[j] * shape_z[k] / (1.0 - ip.z);
|
||||
du(o, 1) = shape_x[i] * dshape_y[j] * shape_z[k] / (1.0 - ip.z);
|
||||
du(o, 2) = shape_x[i] * shape_y[j] * dshape_z[k] +
|
||||
(ip.x * dshape_x[i] * shape_y[j] +
|
||||
ip.y * shape_x[i] * dshape_y[j]) *
|
||||
shape_z[k] / pow(1.0 - ip.z, 2);
|
||||
}
|
||||
Ti.Mult(du, dshape);
|
||||
}
|
||||
|
||||
L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype)
|
||||
: NodalFiniteElement(3, Geometry::PYRAMID, (p + 1)*(p + 2)*(2*p + 3)/6,
|
||||
p, FunctionSpace::Pk)
|
||||
{
|
||||
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(p + 1);
|
||||
shape_y.SetSize(p + 1);
|
||||
shape_z.SetSize(p + 1);
|
||||
dshape_x.SetSize(p + 1);
|
||||
dshape_y.SetSize(p + 1);
|
||||
dshape_z.SetSize(p + 1);
|
||||
dshape_z_dt.SetSize(p + 1);
|
||||
u.SetSize(dof);
|
||||
du.SetSize(dof, dim);
|
||||
#else
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
Vector dshape_z_dt(p + 1);
|
||||
#endif
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 0; j <= p - k; j++)
|
||||
{
|
||||
const real_t wjk = op[j] + op[k] + op[p-j-k];
|
||||
for (int i = 0; i <= p - k; i++)
|
||||
{
|
||||
const real_t wik = op[i] + op[k] + op[p-i-k];
|
||||
const real_t w = wik * wjk * op[p-k];
|
||||
Nodes.IntPoint(o++).Set3(op[i] * (op[j] + op[p-j-k]) / w,
|
||||
op[j] * (op[j] + op[p-j-k]) / w,
|
||||
op[k] * op[p-k] / w);
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_ASSERT(o == dof,
|
||||
"Number of nodes does not match the "
|
||||
"number of degrees of freedom");
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
|
||||
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
const real_t z = ip.z;
|
||||
|
||||
poly1d.CalcLegendre(p, x, shape_x.GetData());
|
||||
poly1d.CalcLegendre(p, y, shape_y.GetData());
|
||||
|
||||
o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
|
||||
z, 1.0, shape_z);
|
||||
|
||||
for (int k = 0; k <= p - maxij; k++)
|
||||
{
|
||||
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void L2_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
Vector u(dof);
|
||||
#endif
|
||||
|
||||
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
const real_t z = ip.z;
|
||||
|
||||
poly1d.CalcLegendre(p, x, shape_x.GetData());
|
||||
poly1d.CalcLegendre(p, y, shape_y.GetData());
|
||||
|
||||
int o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
|
||||
shape_z);
|
||||
|
||||
for (int k = 0; k <= p - maxij; k++)
|
||||
{
|
||||
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Mult(u, shape);
|
||||
}
|
||||
|
||||
void L2_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(p + 1);
|
||||
Vector shape_y(p + 1);
|
||||
Vector shape_z(p + 1);
|
||||
Vector dshape_x(p + 1);
|
||||
Vector dshape_y(p + 1);
|
||||
Vector dshape_z(p + 1);
|
||||
Vector dshape_z_dt(p + 1);
|
||||
DenseMatrix du(dof, dim);
|
||||
#endif
|
||||
|
||||
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
|
||||
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
|
||||
const real_t z = ip.z;
|
||||
|
||||
Poly_1D::CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
|
||||
Poly_1D::CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
|
||||
|
||||
int o = 0;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
int maxij = std::max(i, j);
|
||||
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
|
||||
shape_z, dshape_z, dshape_z_dt);
|
||||
|
||||
for (int k = 0; k <= p - maxij; k++, o++)
|
||||
{
|
||||
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1);
|
||||
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1);
|
||||
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
|
||||
pow(1.0 - ip.z, maxij) +
|
||||
(ip.x * dshape_x(i) * shape_y(j) +
|
||||
ip.y * shape_x(i) * dshape_y(j)) *
|
||||
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
|
||||
((maxij > 0) ? (maxij * shape_x(i) * shape_y(j) * shape_z(k) *
|
||||
pow(1.0 - ip.z, maxij - 1)) : 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ti.Mult(du, dshape);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FE_L2
|
||||
|
||||
#include "fe_base.hpp"
|
||||
#include "fe_pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -183,6 +184,56 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/** Arbitrary order L2 basis functions defined on pyramid-shaped elements
|
||||
|
||||
This implementation is closely based on the finite elements
|
||||
described in section 9.4 of the paper "Orientation embedded high
|
||||
order shape functions for the exact sequence elements of all shapes"
|
||||
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
|
||||
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
|
||||
*/
|
||||
class L2_FuentesPyramidElement
|
||||
: public NodalFiniteElement, public FuentesPyramid
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_z;
|
||||
mutable Vector dshape_x, dshape_y, dshape_z;
|
||||
mutable Vector u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
public:
|
||||
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
|
||||
L2_FuentesPyramidElement(const int p,
|
||||
const int btype = BasisType::GaussLegendre);
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
};
|
||||
|
||||
/// Arbitrary order L2 elements in 3D on a pyramid
|
||||
class L2_BergotPyramidElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_z;
|
||||
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt;
|
||||
mutable Vector u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
public:
|
||||
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
|
||||
L2_BergotPyramidElement(const int p,
|
||||
const int btype = BasisType::GaussLegendre);
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1581,6 +1581,954 @@ void ND_WedgeElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
const real_t ND_FuentesPyramidElement::tk[27] =
|
||||
{
|
||||
1., 0., 0., 0., 1., 0., 0., 0., 1.,
|
||||
-1., 0., 1., -1.,-1., 1., 0.,-1., 1.,
|
||||
-1., 0., 0., 0.,-1., 0., -M_SQRT1_2,-M_SQRT1_2,M_SQRT2
|
||||
};
|
||||
|
||||
ND_FuentesPyramidElement::ND_FuentesPyramidElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, p * (3 * p * p + 5), p,
|
||||
H_CURL, FunctionSpace::Uk),
|
||||
dof2tk(dof), doftrans(p)
|
||||
{
|
||||
zmax = 0.0;
|
||||
|
||||
const real_t *eop = poly1d.OpenPoints(p - 1);
|
||||
const real_t *top = (p > 1) ? poly1d.OpenPoints(p - 2) : NULL;
|
||||
const real_t *qop = poly1d.OpenPoints(p - 1, ob_type);
|
||||
const real_t *qcp = poly1d.ClosedPoints(p, cb_type);
|
||||
|
||||
const int pm2 = p - 2;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
tmp_E_E_ij.SetSize(p, dim);
|
||||
tmp_dE_E_ij.SetSize(p, dim);
|
||||
tmp_E_Q1_ijk.SetSize(p, p + 1, dim);
|
||||
tmp_dE_Q1_ijk.SetSize(p, p + 1, dim);
|
||||
tmp_E_Q2_ijk.SetSize(p, p + 1, dim);
|
||||
tmp_dE_Q2_ijk.SetSize(p, p + 1, dim);
|
||||
tmp_E_T_ijk.SetSize(p - 1, p, dim);
|
||||
tmp_dE_T_ijk.SetSize(p - 1, p, dim);
|
||||
tmp_phi_Q1_ij.SetSize(p + 1, p + 1);
|
||||
tmp_dphi_Q1_ij.SetSize(p + 1, p + 1, dim);
|
||||
tmp_phi_Q2_ij.SetSize(p + 1, p + 1);
|
||||
tmp_dphi_Q2_ij.SetSize(p + 1, p + 1, dim);
|
||||
tmp_phi_E_i.SetSize(p + 1);
|
||||
tmp_dphi_E_i.SetSize(p + 1, dim);
|
||||
u.SetSize(dof, dim);
|
||||
curlu.SetSize(dof, dim);
|
||||
#else
|
||||
DenseMatrix tmp_E_E_ij(p, dim);
|
||||
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
|
||||
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
|
||||
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
|
||||
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
|
||||
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
|
||||
Vector tmp_phi_E_i(p + 1);
|
||||
DenseMatrix tmp_dphi_E_i(p + 1, dim);
|
||||
DenseMatrix u(dof, dim);
|
||||
#endif
|
||||
|
||||
int o = 0;
|
||||
|
||||
// edges
|
||||
for (int i = 0; i < p; i++) // (0, 1)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(eop[i], 0., 0.);
|
||||
dof2tk[o++] = 0;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (1, 2)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(1., eop[i], 0.);
|
||||
dof2tk[o++] = 1;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (3, 2)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(eop[i], 1., 0.);
|
||||
dof2tk[o++] = 0;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (0, 3)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(0., eop[i], 0.);
|
||||
dof2tk[o++] = 1;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (0, 4)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(0., 0., eop[i]);
|
||||
dof2tk[o++] = 2;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (1, 4)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(1. - eop[i], 0., eop[i]);
|
||||
dof2tk[o++] = 3;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (2, 4)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(1. - eop[i], 1. - eop[i], eop[i]);
|
||||
dof2tk[o++] = 4;
|
||||
}
|
||||
for (int i = 0; i < p; i++) // (3, 4)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(0., 1. - eop[i], eop[i]);
|
||||
dof2tk[o++] = 5;
|
||||
}
|
||||
|
||||
// quadrilateral face (3, 2, 1, 0)
|
||||
// x-components
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 0; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(qop[i], qcp[p-j], 0.);
|
||||
dof2tk[o++] = 0; // (1 0 0)
|
||||
}
|
||||
|
||||
// y-components
|
||||
for (int j = 0; j < p; j++)
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(qcp[i], qop[p-1-j], 0.);
|
||||
dof2tk[o++] = 7; // (0 -1 0)
|
||||
}
|
||||
|
||||
// triangular faces
|
||||
for (int j = 0; j <= pm2; j++) // (0, 1, 4)
|
||||
for (int i = 0; i + j <= pm2; i++)
|
||||
{
|
||||
real_t w = top[i] + top[j] + top[pm2-i-j];
|
||||
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
|
||||
dof2tk[o++] = 0;
|
||||
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
|
||||
dof2tk[o++] = 2;
|
||||
}
|
||||
for (int j = 0; j <= pm2; j++) // (1, 2, 4)
|
||||
for (int i = 0; i + j <= pm2; i++)
|
||||
{
|
||||
real_t w = top[i] + top[j] + top[pm2-i-j];
|
||||
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
|
||||
dof2tk[o++] = 1;
|
||||
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
|
||||
dof2tk[o++] = 3;
|
||||
}
|
||||
for (int j = 0; j <= pm2; j++) // (2, 3, 4)
|
||||
for (int i = 0; i + j <= pm2; i++)
|
||||
{
|
||||
real_t w = top[i] + top[j] + top[pm2-i-j];
|
||||
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
|
||||
top[j]/w);
|
||||
dof2tk[o++] = 6;
|
||||
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
|
||||
top[j]/w);
|
||||
dof2tk[o++] = 4;
|
||||
}
|
||||
for (int j = 0; j <= pm2; j++) // (3, 0, 4)
|
||||
for (int i = 0; i + j <= pm2; i++)
|
||||
{
|
||||
real_t w = top[i] + top[j] + top[pm2-i-j];
|
||||
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
|
||||
dof2tk[o++] = 7;
|
||||
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
|
||||
dof2tk[o++] = 5;
|
||||
}
|
||||
|
||||
// interior
|
||||
// x-components
|
||||
for (int k = 1; k < p; k++)
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 0; i < p; i++)
|
||||
{
|
||||
real_t w = 1.0 - qcp[k];
|
||||
Nodes.IntPoint(o).Set3(qop[i]*w, qcp[j]*w, qcp[k]);
|
||||
dof2tk[o++] = 0;
|
||||
}
|
||||
// y-components
|
||||
for (int k = 1; k < p; k++)
|
||||
for (int j = 0; j < p; j++)
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
real_t w = 1.0 - qcp[k];
|
||||
Nodes.IntPoint(o).Set3(qcp[i]*w, qop[j]*w, qcp[k]);
|
||||
dof2tk[o++] = 1;
|
||||
}
|
||||
// z-components
|
||||
for (int k = 0; k < p; k++)
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
real_t w = 1.0 - qop[k];
|
||||
Nodes.IntPoint(o).Set3(qcp[i]*w, qcp[j]*w, qop[k]);
|
||||
dof2tk[o++] = 8;
|
||||
}
|
||||
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
|
||||
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
|
||||
tmp_phi_E_i, tmp_dphi_E_i, u);
|
||||
|
||||
const Vector tm({tk[3*dof2tk[m]], tk[3*dof2tk[m]+1], tk[3*dof2tk[m]+2]});
|
||||
u.Mult(tm, T.GetColumn(m));
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix tmp_E_E_ij(p, dim);
|
||||
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
|
||||
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
|
||||
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
|
||||
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
|
||||
Vector tmp_phi_E_i(p + 1);
|
||||
DenseMatrix tmp_dphi_E_i(p + 1, dim);
|
||||
DenseMatrix u(dof, dim);
|
||||
#endif
|
||||
|
||||
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
|
||||
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
|
||||
tmp_phi_E_i, tmp_dphi_E_i, u);
|
||||
|
||||
Ti.Mult(u, shape);
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix tmp_E_E_ij(p, dim);
|
||||
DenseMatrix tmp_dE_E_ij(p, dim);
|
||||
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
|
||||
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
|
||||
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
|
||||
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
|
||||
Vector tmp_phi_E_i(p + 1);
|
||||
DenseMatrix tmp_dphi_E_i(p + 1, dim);
|
||||
DenseMatrix curlu(dof, dim);
|
||||
#endif
|
||||
|
||||
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
|
||||
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
|
||||
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
|
||||
curlu);
|
||||
|
||||
Ti.Mult(curlu, curl_shape);
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::CalcRawVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix tmp_E_E_ij(p, dim);
|
||||
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
|
||||
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
|
||||
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
|
||||
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
|
||||
Vector tmp_phi_E_i(p + 1);
|
||||
DenseMatrix tmp_dphi_E_i(p + 1, dim);
|
||||
#endif
|
||||
|
||||
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
|
||||
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
|
||||
tmp_phi_E_i, tmp_dphi_E_i, shape);
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::CalcRawCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix tmp_E_E_ij(p, dim);
|
||||
DenseMatrix tmp_dE_E_ij(p, dim);
|
||||
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
|
||||
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
|
||||
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
|
||||
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
|
||||
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
|
||||
Vector tmp_phi_E_i(p + 1);
|
||||
DenseMatrix tmp_dphi_E_i(p + 1, dim);
|
||||
#endif
|
||||
|
||||
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
|
||||
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
|
||||
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
|
||||
dshape);
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::calcBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
DenseMatrix & E_E_ik,
|
||||
DenseTensor & E_Q1_ijk,
|
||||
DenseTensor & E_Q2_ijk,
|
||||
DenseTensor & E_T_ijk,
|
||||
DenseMatrix & phi_Q1_ij,
|
||||
DenseTensor & dphi_Q1_ij,
|
||||
DenseMatrix & phi_Q2_ij,
|
||||
Vector & phi_E_k,
|
||||
DenseMatrix & dphi_E_k,
|
||||
DenseMatrix &W) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y}), dmu(3);
|
||||
real_t mu, mu2;
|
||||
|
||||
if (std::fabs(1.0 - z) < apex_tol)
|
||||
{
|
||||
z = 1.0 - apex_tol;
|
||||
y = 0.5 * (1.0 - z);
|
||||
x = 0.5 * (1.0 - z);
|
||||
xy(0) = x; xy(1) = y;
|
||||
}
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
W = 0.0;
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Mixed Edges
|
||||
if (z < 1.0)
|
||||
{
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
E_E(p, nu01(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_E_ik(i, k);
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_E_ik(i, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
E_E(p, nu01(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_E_ik(i, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_E_ik(i, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangle Edges
|
||||
if (z < 1.0)
|
||||
{
|
||||
E_E(p, lam15(x, y, z), lam15_grad_lam15(x, y, z), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = E_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam25(x, y, z), lam25_grad_lam25(x, y, z), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = E_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam35(x, y, z), lam35_grad_lam35(x, y, z), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = E_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam45(x, y, z), lam45_grad_lam45(x, y, z), E_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = E_E_ik(i, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Quadrilateral Face
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
mu = mu0(z);
|
||||
mu2 = mu * mu;
|
||||
|
||||
// Family I
|
||||
E_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1), mu01(z, xy, 2),
|
||||
E_Q1_ijk);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu2 * E_Q1_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// Family II
|
||||
E_Q(p, mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2), mu01(z, xy, 1),
|
||||
E_Q2_ijk);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu2 * E_Q2_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular Faces
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
// Family I
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
E_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
E_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// Family II
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
E_T(p, nu120(z, xy, 1), nu12_grad_nu12(z, xy, 1), E_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
E_T(p, nu120(z, xy, 2), nu12_grad_nu12(z, xy, 2), E_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
W(o, k) = mu * E_T_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
// Family I
|
||||
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1), mu01(z, xy, 2),
|
||||
grad_mu01(z, xy, 2), phi_Q1_ij, dphi_Q1_ij);
|
||||
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
|
||||
for (int k=2; k<=p; k++)
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
for (int l=0; l<3; l++)
|
||||
W(o, l) = dphi_Q1_ij(i, j, l) * phi_E_k(k) +
|
||||
phi_Q1_ij(i, j) * dphi_E_k(k, l);
|
||||
|
||||
// Family II
|
||||
mu = mu0(z);
|
||||
for (int k=2; k<=p; k++)
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
W(o, l) = mu * E_Q1_ijk(i, j, l) * phi_E_k(k);
|
||||
}
|
||||
|
||||
// Family III
|
||||
for (int k=2; k<=p; k++)
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
W(o, l) = mu * E_Q2_ijk(i, j, l) * phi_E_k(k);
|
||||
}
|
||||
|
||||
// Family IV
|
||||
// Re-using mu from Family II
|
||||
dmu = grad_mu0(z);
|
||||
phi_Q(p, mu01(z, xy, 2), mu01(z, xy, 1), phi_Q2_ij);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const int n = std::max(i,j);
|
||||
const real_t nmu = n * pow(mu, n-1);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
W(o, l) = nmu * phi_Q2_ij(i, j) * dmu(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ND_FuentesPyramidElement::calcCurlBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
DenseMatrix & E_E_ik,
|
||||
DenseMatrix & dE_E_ik,
|
||||
DenseTensor & E_Q1_ijk,
|
||||
DenseTensor & dE_Q1_ijk,
|
||||
DenseTensor & E_Q2_ijk,
|
||||
DenseTensor & dE_Q2_ijk,
|
||||
DenseTensor & E_T_ijk,
|
||||
DenseTensor & dE_T_ijk,
|
||||
DenseMatrix & phi_Q2_ij,
|
||||
DenseTensor & dphi_Q2_ij,
|
||||
Vector & phi_E_k,
|
||||
DenseMatrix & dphi_E_k,
|
||||
DenseMatrix & dW) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y}), dmu(3);
|
||||
Vector dmuxE(3), E(3), dphi(3), muphi(3);
|
||||
|
||||
real_t mu, mu2;
|
||||
|
||||
if (std::fabs(1.0 - z) < apex_tol)
|
||||
{
|
||||
z = 1.0 - apex_tol;
|
||||
y = 0.5 * (1.0 - z);
|
||||
x = 0.5 * (1.0 - z);
|
||||
xy(0) = x; xy(1) = y;
|
||||
}
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
dW = 0.0;
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Mixed Edges
|
||||
if (z < 1.0)
|
||||
{
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
dmu = grad_mu0(z, xy, 2);
|
||||
E_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmu = grad_mu1(z, xy, 2);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
dmu = grad_mu0(z, xy, 1);
|
||||
E_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmu = grad_mu1(z, xy, 1);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Triangle Edges
|
||||
if (z < 1.0)
|
||||
{
|
||||
E_E(p, lam15(x, y, z), grad_lam15(x, y, z), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = dE_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam25(x, y, z), grad_lam25(x, y, z), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = dE_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam35(x, y, z), grad_lam35(x, y, z), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = dE_E_ik(i, k);
|
||||
}
|
||||
|
||||
E_E(p, lam45(x, y, z), grad_lam45(x, y, z), E_E_ik, dE_E_ik);
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = dE_E_ik(i, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Quadrilateral Face
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
mu = mu0(z);
|
||||
mu2 = mu * mu;
|
||||
dmu = grad_mu0(z);
|
||||
|
||||
// Family I
|
||||
E_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), E_Q1_ijk, dE_Q1_ijk);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_Q1_ijk(i, j, 0);
|
||||
E(1) = E_Q1_ijk(i, j, 1);
|
||||
E(2) = E_Q1_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu2 * dE_Q1_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// Family II
|
||||
E_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
|
||||
mu01(z, xy, 1), grad_mu01(z, xy, 1), E_Q2_ijk, dE_Q2_ijk);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_Q2_ijk(i, j, 0);
|
||||
E(1) = E_Q2_ijk(i, j, 1);
|
||||
E(2) = E_Q2_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu2 * dE_Q2_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular Faces
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
// Family I
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
dmu = grad_mu0(z, xy, 2);
|
||||
E_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), E_T_ijk, dE_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmu = grad_mu1(z, xy, 2);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
dmu = grad_mu0(z, xy, 1);
|
||||
E_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), E_T_ijk, dE_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmu = grad_mu1(z, xy, 1);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// Family II
|
||||
// (a, b) = (1, 2), c = 0
|
||||
mu = mu0(z, xy, 2);
|
||||
dmu = grad_mu0(z, xy, 2);
|
||||
E_T(p, nu120(z, xy, 1), grad_nu120(z, xy, 1), E_T_ijk, dE_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (1, 2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmu = grad_mu1(z, xy, 2);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 0
|
||||
mu = mu0(z, xy, 1);
|
||||
dmu = grad_mu0(z, xy, 1);
|
||||
E_T(p, nu120(z, xy, 2), grad_nu120(z, xy, 2), E_T_ijk, dE_T_ijk);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a, b) = (2, 1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmu = grad_mu1(z, xy, 1);
|
||||
for (int j=1; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
E(0) = E_T_ijk(i, j, 0);
|
||||
E(1) = E_T_ijk(i, j, 1);
|
||||
E(2) = E_T_ijk(i, j, 2);
|
||||
dmu.cross3D(E, dmuxE);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
// Family I
|
||||
// Curl is zero so skip these functions
|
||||
o += (p - 1) * (p - 1) * (p - 1);
|
||||
|
||||
// Family II
|
||||
mu = mu0(z);
|
||||
dmu = grad_mu0(z);
|
||||
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
|
||||
for (int k=2; k<=p; k++)
|
||||
{
|
||||
dphi(0) = dphi_E_k(k, 0);
|
||||
dphi(1) = dphi_E_k(k, 1);
|
||||
dphi(2) = dphi_E_k(k, 2);
|
||||
add(mu, dphi, phi_E_k(k), dmu, muphi);
|
||||
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_Q1_ijk(i, j, 0);
|
||||
E(1) = E_Q1_ijk(i, j, 1);
|
||||
E(2) = E_Q1_ijk(i, j, 2);
|
||||
muphi.cross3D(E, dmuxE);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dW(o, l) = mu * dE_Q1_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Family III
|
||||
for (int k=2; k<=p; k++)
|
||||
{
|
||||
dphi(0) = dphi_E_k(k, 0);
|
||||
dphi(1) = dphi_E_k(k, 1);
|
||||
dphi(2) = dphi_E_k(k, 2);
|
||||
add(mu, dphi, phi_E_k(k), dmu, muphi);
|
||||
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_Q2_ijk(i, j, 0);
|
||||
E(1) = E_Q2_ijk(i, j, 1);
|
||||
E(2) = E_Q2_ijk(i, j, 2);
|
||||
muphi.cross3D(E, dmuxE);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dW(o, l) = mu * dE_Q2_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Family IV
|
||||
// Re-using mu from Family II
|
||||
dmu = grad_mu0(z);
|
||||
phi_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2), mu01(z, xy, 1),
|
||||
grad_mu01(z, xy, 1), phi_Q2_ij, dphi_Q2_ij);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const int n = std::max(i,j);
|
||||
const real_t nmu = n * pow(mu, n-1);
|
||||
|
||||
dphi(0) = dphi_Q2_ij(i, j, 0);
|
||||
dphi(1) = dphi_Q2_ij(i, j, 1);
|
||||
dphi(2) = dphi_Q2_ij(i, j, 2);
|
||||
dphi.cross3D(dmu, muphi);
|
||||
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dW(o, l) = nmu * muphi(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ND_R1D_PointElement::ND_R1D_PointElement(int p)
|
||||
: VectorFiniteElement(1, Geometry::POINT, 2, p,
|
||||
H_CURL, FunctionSpace::Pk)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "fe_base.hpp"
|
||||
#include "fe_h1.hpp"
|
||||
#include "fe_pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -415,6 +416,126 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** Arbitrary order H(Curl) basis functions defined on pyramid-shaped elements
|
||||
|
||||
This implementation is closely based on the finite elements
|
||||
described in section 9.2 of the paper "Orientation embedded high
|
||||
order shape functions for the exact sequence elements of all shapes"
|
||||
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
|
||||
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
|
||||
*/
|
||||
class ND_FuentesPyramidElement
|
||||
: public VectorFiniteElement, public FuentesPyramid
|
||||
{
|
||||
private:
|
||||
static const real_t tk[27];
|
||||
|
||||
mutable real_t zmax;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable DenseMatrix tmp_E_E_ij;
|
||||
mutable DenseMatrix tmp_dE_E_ij;
|
||||
mutable DenseTensor tmp_E_Q1_ijk;
|
||||
mutable DenseTensor tmp_dE_Q1_ijk;
|
||||
mutable DenseTensor tmp_E_Q2_ijk;
|
||||
mutable DenseTensor tmp_dE_Q2_ijk;
|
||||
mutable DenseTensor tmp_E_T_ijk;
|
||||
mutable DenseTensor tmp_dE_T_ijk;
|
||||
mutable DenseMatrix tmp_phi_Q1_ij;
|
||||
mutable DenseTensor tmp_dphi_Q1_ij;
|
||||
mutable DenseMatrix tmp_phi_Q2_ij;
|
||||
mutable DenseTensor tmp_dphi_Q2_ij;
|
||||
mutable Vector tmp_phi_E_i;
|
||||
mutable DenseMatrix tmp_dphi_E_i;
|
||||
mutable DenseMatrix u;
|
||||
mutable DenseMatrix curlu;
|
||||
#endif
|
||||
Array<int> dof2tk;
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
ND_PyramidDofTransformation doftrans;
|
||||
|
||||
void calcBasis(const int p, const IntegrationPoint & ip,
|
||||
DenseMatrix & E_E_ik,
|
||||
DenseTensor & E_Q1_ijk,
|
||||
DenseTensor & E_Q2_ijk,
|
||||
DenseTensor & E_T_ijk,
|
||||
DenseMatrix & phi_Q1_ij,
|
||||
DenseTensor & dphi_Q1_ij,
|
||||
DenseMatrix & phi_Q2_ij,
|
||||
Vector & phi_E_k,
|
||||
DenseMatrix & dphi_E_k,
|
||||
DenseMatrix & W) const;
|
||||
|
||||
void calcCurlBasis(const int p, const IntegrationPoint & ip,
|
||||
DenseMatrix & E_E_ik,
|
||||
DenseMatrix & dE_E_ik,
|
||||
DenseTensor & E_Q1_ijk,
|
||||
DenseTensor & dE_Q1_ijk,
|
||||
DenseTensor & E_Q2_ijk,
|
||||
DenseTensor & dE_Q2_ijk,
|
||||
DenseTensor & E_T_ijk,
|
||||
DenseTensor & dE_T_ijk,
|
||||
DenseMatrix & phi_Q2_ij,
|
||||
DenseTensor & dphi_Q2_ij,
|
||||
Vector & phi_E_k,
|
||||
DenseMatrix & dphi_E_k,
|
||||
DenseMatrix & dW) const;
|
||||
|
||||
public:
|
||||
ND_FuentesPyramidElement(const int p,
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_ND(Trans, shape); }
|
||||
virtual void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const;
|
||||
virtual void GetLocalInterpolation(ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_ND(*this, tk, dof2tk, Trans, I); }
|
||||
virtual void GetLocalRestriction(ElementTransformation &Trans,
|
||||
DenseMatrix &R) const
|
||||
{ LocalRestriction_ND(tk, dof2tk, Trans, R); }
|
||||
virtual void GetTransferMatrix(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
virtual void Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const
|
||||
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
|
||||
virtual void ProjectMatrixCoefficient(
|
||||
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
|
||||
{ ProjectMatrixCoefficient_ND(tk, dof2tk, mc, T, dofs); }
|
||||
virtual void Project(const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ Project_ND(tk, dof2tk, fe, Trans, I); }
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const
|
||||
{ ProjectGrad_ND(tk, dof2tk, fe, Trans, grad); }
|
||||
|
||||
virtual void ProjectCurl(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const
|
||||
{ ProjectCurl_ND(tk, dof2tk, fe, Trans, curl); }
|
||||
|
||||
void CalcRawVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
|
||||
void CalcRawCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
|
||||
real_t GetZetaMax() const { return zmax; }
|
||||
};
|
||||
|
||||
|
||||
/// A 0D Nedelec finite element for the boundary of a 1D domain
|
||||
/** ND_R1D_PointElement provides a representation of the trace of a three
|
||||
component Nedelec basis restricted to 1D.
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public:
|
||||
int GetPatch () const { return patch; }
|
||||
/// Set which patch should be evaluated
|
||||
void SetPatch (int p) const { patch = p; }
|
||||
/// Set which elemenet should be evaluated
|
||||
/// Set which element should be evaluated
|
||||
int GetElement () const { return elem; }
|
||||
/// Get which element is currently considered
|
||||
void SetElement (int e) const { elem = e; }
|
||||
|
||||
@@ -1042,6 +1042,435 @@ void H1Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
H1Pos_PyramidElement::H1Pos_PyramidElement(const int p)
|
||||
: PositiveFiniteElement(3, Geometry::PYRAMID,
|
||||
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
|
||||
FunctionSpace::Uk),
|
||||
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
|
||||
{
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
m_shape_1d.SetSize(order + 1);
|
||||
m_shape.SetSize(nterms);
|
||||
m_dshape.SetSize(nterms, dim);
|
||||
#endif
|
||||
|
||||
Index idx;
|
||||
|
||||
// vertices
|
||||
dof_map[idx(p,0,0,0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set3(0., 0., 0.);
|
||||
dof_map[idx(0,p,0,0,0)] = 1;
|
||||
Nodes.IntPoint(1).Set3(1., 0., 0.);
|
||||
dof_map[idx(0,0,p,0,0)] = 2;
|
||||
Nodes.IntPoint(2).Set3(1., 1., 0.);
|
||||
dof_map[idx(0,0,0,p,0)] = 3;
|
||||
Nodes.IntPoint(3).Set3(0., 1., 0.);
|
||||
dof_map[idx(0,0,0,0,p)] = 4;
|
||||
Nodes.IntPoint(4).Set3(0., 0., 1.);
|
||||
|
||||
// edges (see Geometry::Constants<Geometry::PYRAMID>::Edges
|
||||
// in fem/geom.cpp)
|
||||
int o = 5;
|
||||
for (int i = 1; i < p; i++) // (0,1)
|
||||
{
|
||||
dof_map[idx(p-i,i,0,0,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., 0.);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,2)
|
||||
{
|
||||
dof_map[idx(0,p-i,i,0,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(1.0, real_t(i)/p, 0.);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,2)
|
||||
{
|
||||
dof_map[idx(0,0,i,p-i,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, 1., 0.);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,3)
|
||||
{
|
||||
dof_map[idx(p-i,0,0,i,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(0., real_t(i)/p, 0.);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,4)
|
||||
{
|
||||
dof_map[idx(p-i,0,0,0,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(0., 0., real_t(i)/p);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,4)
|
||||
{
|
||||
dof_map[idx(0,p-i,0,0,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, 0., real_t(i)/p);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (2,4)
|
||||
{
|
||||
dof_map[idx(0,0,p-i,0,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, real_t(p-i)/p, real_t(i)/p);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (3,4)
|
||||
{
|
||||
dof_map[idx(0,0,0,p-i,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(0., real_t(p-i)/p, real_t(i)/p);
|
||||
}
|
||||
|
||||
// faces (see Geometry::Constants<Geometry::PYRAMID>::FaceVert
|
||||
// in fem/geom.cpp)
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
int i1 = j;
|
||||
int i2 = 0;
|
||||
int i3 = 0;
|
||||
int i4 = p - j;
|
||||
const int i5 = 0;
|
||||
|
||||
for (int i = 1; i <= p - j; i++) // (3,2,1,0)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
|
||||
}
|
||||
for (int i = p - j + 1; i < p; i++) // (3,2,1,0)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0, 1, 4)
|
||||
{
|
||||
dof_map[idx(p-i-j,i,0,0,j)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., real_t(j)/p);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (1, 2, 4)
|
||||
{
|
||||
dof_map[idx(0,p-i-j,i,0,j)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(p-j)/p, real_t(i)/p, real_t(j)/p);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (2, 3, 4)
|
||||
{
|
||||
dof_map[idx(0,0,p-i-j,i,j)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(p-i-j)/p, real_t(p-j)/p, real_t(j)/p);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (3, 0, 4)
|
||||
{
|
||||
dof_map[idx(i,0,0,p-i-j,j)] = o;
|
||||
Nodes.IntPoint(o++).Set3(0., real_t(p-i-j)/p, real_t(j)/p);
|
||||
}
|
||||
|
||||
// interior
|
||||
for (int k = 1; k < p; k++)
|
||||
for (int j = 1; j + k < p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = 0;
|
||||
int i4 = j;
|
||||
const int i5 = k;
|
||||
|
||||
for (int i = 1; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
for (int i = j + 1; i + k < p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_PyramidElement::CalcShape(const int p, const real_t x,
|
||||
const real_t y, const real_t z,
|
||||
real_t *shape_1d,
|
||||
real_t *shape)
|
||||
{
|
||||
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
|
||||
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
|
||||
|
||||
const real_t l1 = lam1(x, y, z);
|
||||
const real_t l2 = lam2(x, y, z);
|
||||
const real_t l3 = lam3(x, y, z);
|
||||
const real_t l4 = lam4(x, y, z);
|
||||
const real_t l5 = lam5(x, y, z);
|
||||
|
||||
// The basis functions are the terms in the expansion:
|
||||
// (l1 + l2 + l3 + l4 + l5)^p =
|
||||
// \sum_{l=0}^p \binom{p}{l} l5^l
|
||||
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
|
||||
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
|
||||
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
|
||||
Index idx;
|
||||
const int *bp = Poly_1D::Binom(p);
|
||||
real_t l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
shape_1d[i2] *= ei345;
|
||||
shape[o] += shape_1d[i2];
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_PyramidElement::CalcDShape(const int p, const real_t x,
|
||||
const real_t y, const real_t z,
|
||||
real_t *dshape_1d, real_t *dshape)
|
||||
{
|
||||
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
|
||||
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
|
||||
|
||||
const real_t l1 = lam1(x, y, z);
|
||||
const real_t l2 = lam2(x, y, z);
|
||||
const real_t l3 = lam3(x, y, z);
|
||||
const real_t l4 = lam4(x, y, z);
|
||||
const real_t l5 = lam5(x, y, z);
|
||||
|
||||
const Vector dl1 = grad_lam1(x, y, z);
|
||||
const Vector dl2 = grad_lam2(x, y, z);
|
||||
const Vector dl3 = grad_lam3(x, y, z);
|
||||
const Vector dl4 = grad_lam4(x, y, z);
|
||||
const Vector dl5 = grad_lam5(x, y, z);
|
||||
|
||||
// The basis functions are the terms in the expansion:
|
||||
// (l1 + l2 + l3 + l4 + l5)^p
|
||||
// We will compute the derivative by first computing the derivatives
|
||||
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
|
||||
// the results together.
|
||||
Index idx;
|
||||
|
||||
// Derivative w.r.t. l1 times grad(l1)
|
||||
const int *bp = Poly_1D::Binom(p);
|
||||
real_t l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l2 times grad(l2)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl2*dl2[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l3 times grad(l3)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl3*dl3[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l4 times grad(l4)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 1; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl4*dl4[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l5 times grad(l5)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 1; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = i5*bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl5*dl5[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
}
|
||||
|
||||
void H1Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector m_shape_1d(order + 1);
|
||||
Vector m_shape(nterms);
|
||||
#endif
|
||||
|
||||
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
|
||||
|
||||
for (auto const& it : dof_map)
|
||||
{
|
||||
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
|
||||
}
|
||||
}
|
||||
|
||||
void H1Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector m_shape_1d(order + 1);
|
||||
DenseMatrix m_dshape(nterms, 3);
|
||||
#endif
|
||||
|
||||
CalcDShape(order, ip.x, ip.y, ip.z,
|
||||
m_shape_1d.GetData(), m_dshape.GetData());
|
||||
|
||||
for (auto const& it : dof_map)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
dshape(it.second, d) = m_dshape(it.first, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
L2Pos_SegmentElement::L2Pos_SegmentElement(const int p)
|
||||
: PositiveTensorFiniteElement(1, p, L2_DOF_MAP)
|
||||
{
|
||||
@@ -1448,4 +1877,327 @@ void L2Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
|
||||
: PositiveFiniteElement(3, Geometry::PYRAMID,
|
||||
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
|
||||
FunctionSpace::Uk),
|
||||
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
|
||||
{
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
m_shape_1d.SetSize(order + 1);
|
||||
m_shape.SetSize(nterms);
|
||||
m_dshape.SetSize(nterms, dim);
|
||||
#endif
|
||||
|
||||
Index idx;
|
||||
|
||||
// interior
|
||||
for (int o = 0, k = 0; k <= p; k++)
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = -1;
|
||||
int i4 = j + 1;
|
||||
const int i5 = k;
|
||||
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
for (int i = j + 1; i + k <= p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
void L2Pos_PyramidElement::CalcShape(const int p, const real_t x,
|
||||
const real_t y, const real_t z,
|
||||
real_t *shape_1d,
|
||||
real_t *shape)
|
||||
{
|
||||
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
|
||||
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
|
||||
|
||||
const real_t l1 = lam1(x, y, z);
|
||||
const real_t l2 = lam2(x, y, z);
|
||||
const real_t l3 = lam3(x, y, z);
|
||||
const real_t l4 = lam4(x, y, z);
|
||||
const real_t l5 = lam5(x, y, z);
|
||||
|
||||
// The basis functions are the terms in the expansion:
|
||||
// (l1 + l2 + l3 + l4 + l5)^p =
|
||||
// \sum_{l=0}^p \binom{p}{l} l5^l
|
||||
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
|
||||
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
|
||||
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
|
||||
Index idx;
|
||||
const int *bp = Poly_1D::Binom(p);
|
||||
real_t l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
shape_1d[i2] *= ei345;
|
||||
shape[o] += shape_1d[i2];
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
void L2Pos_PyramidElement::CalcDShape(const int p, const real_t x,
|
||||
const real_t y, const real_t z,
|
||||
real_t *dshape_1d, real_t *dshape)
|
||||
{
|
||||
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
|
||||
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
|
||||
|
||||
const real_t l1 = lam1(x, y, z);
|
||||
const real_t l2 = lam2(x, y, z);
|
||||
const real_t l3 = lam3(x, y, z);
|
||||
const real_t l4 = lam4(x, y, z);
|
||||
const real_t l5 = lam5(x, y, z);
|
||||
|
||||
const Vector dl1 = grad_lam1(x, y, z);
|
||||
const Vector dl2 = grad_lam2(x, y, z);
|
||||
const Vector dl3 = grad_lam3(x, y, z);
|
||||
const Vector dl4 = grad_lam4(x, y, z);
|
||||
const Vector dl5 = grad_lam5(x, y, z);
|
||||
|
||||
// The basis functions are the terms in the expansion:
|
||||
// (l1 + l2 + l3 + l4 + l5)^p
|
||||
// We will compute the derivative by first computing the derivatives
|
||||
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
|
||||
// the results together.
|
||||
Index idx;
|
||||
|
||||
// Derivative w.r.t. l1 times grad(l1)
|
||||
const int *bp = Poly_1D::Binom(p);
|
||||
real_t l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l2 times grad(l2)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl2*dl2[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l3 times grad(l3)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl3*dl3[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l4 times grad(l4)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 0; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 1; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl4*dl4[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
|
||||
// Derivative w.r.t. l5 times grad(l5)
|
||||
l5i5 = 1.;
|
||||
for (int i5 = 1; i5 <= p; i5++)
|
||||
{
|
||||
const int *bpi5 = Poly_1D::Binom(p - i5);
|
||||
const real_t ei5 = i5*bp[i5]*l5i5;
|
||||
real_t l4i4 = 1.;
|
||||
for (int i4 = 0; i4 <= p - i5; i4++)
|
||||
{
|
||||
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
|
||||
const real_t ei45 = ei5*bpi5[i4]*l4i4;
|
||||
real_t l3i3 = 1.;
|
||||
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
|
||||
{
|
||||
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
|
||||
real_t ei345 = ei45*bpi45[i3]*l3i3;
|
||||
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
|
||||
{
|
||||
const int i1 = p - i5 - i4 - i3 - i2;
|
||||
const int o = idx(i1,i2,i3,i4,i5);
|
||||
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
|
||||
for (int d = 0; d < 3; d++)
|
||||
{
|
||||
dshape[o + d * nterms] += dshape_dl5*dl5[d];
|
||||
}
|
||||
}
|
||||
l3i3 *= l3;
|
||||
}
|
||||
l4i4 *= l4;
|
||||
}
|
||||
l5i5 *= l5;
|
||||
}
|
||||
}
|
||||
|
||||
void L2Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector m_shape_1d(order + 1);
|
||||
Vector m_shape(nterms);
|
||||
#endif
|
||||
|
||||
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
|
||||
|
||||
for (auto const& it : dof_map)
|
||||
{
|
||||
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
|
||||
}
|
||||
}
|
||||
|
||||
void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector m_shape_1d(order + 1);
|
||||
DenseMatrix m_dshape(nterms, 3);
|
||||
#endif
|
||||
|
||||
CalcDShape(order, ip.x, ip.y, ip.z,
|
||||
m_shape_1d.GetData(), m_dshape.GetData());
|
||||
|
||||
for (auto const& it : dof_map)
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
dshape(it.second, d) = m_dshape(it.first, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FE_POS
|
||||
|
||||
#include "fe_base.hpp"
|
||||
#include "fe_pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -256,6 +257,69 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// Arbitrary order H1 elements in 3D utilizing the Bernstein basis on a pyramid
|
||||
///
|
||||
/// The pyramid affine-related coordinates $\lambda_i$ for $i=1,\ldots,5$ can
|
||||
/// be used to define a positive H1 basis by noting that $\lambda_i \ge 0$
|
||||
/// inside the pyramid for all $i$ and that $\sum_{i=1}^5\lambda_i=1$. This
|
||||
/// leads to $1 = (\sum_{i=1}^5\lambda_i)^p$. The terms of this product,
|
||||
/// expanded as a polynomial in the $\lambda_i$, can be used as a Bernstein
|
||||
/// basis of order $p$ on a pyramid.
|
||||
class H1Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
|
||||
{
|
||||
protected:
|
||||
const int nterms;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector m_shape_1d;
|
||||
mutable Vector m_shape;
|
||||
mutable DenseMatrix m_dshape;
|
||||
#endif
|
||||
std::map<int,int> dof_map;
|
||||
|
||||
struct Index
|
||||
{
|
||||
Index() = default;
|
||||
int operator()(int i1, int i2, int i3, int i4, int i5)
|
||||
{
|
||||
const int p = i1 + i2 + i3 + i4 + i5;
|
||||
const int min24 = std::min(i2,i4);
|
||||
i1 += min24;
|
||||
i2 -= min24;
|
||||
i3 += min24;
|
||||
i4 -= min24;
|
||||
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
|
||||
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
|
||||
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
|
||||
- (p + 2) * ((i4 - 1) * i4) / 2
|
||||
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
|
||||
+ 2 * (5 + 5 * p + p * p))) / 24;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/// Construct the H1Pos_PyramidElement of order @a p
|
||||
H1Pos_PyramidElement(const int p);
|
||||
|
||||
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
|
||||
// The size of shape_1d should be at least p+1.
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *shape_1d, real_t *shape);
|
||||
|
||||
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
|
||||
// The size of dshape_1d should be at least p+1.
|
||||
static void CalcDShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *dshape_1d, real_t *dshape);
|
||||
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
|
||||
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
|
||||
// needed above
|
||||
int GetNumTerms() const { return nterms; }
|
||||
};
|
||||
|
||||
|
||||
/// Arbitrary order L2 elements in 1D utilizing the Bernstein basis on a segment
|
||||
class L2Pos_SegmentElement : public PositiveTensorFiniteElement
|
||||
{
|
||||
@@ -369,6 +433,61 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/// Arbitrary order L2 elements in 3D utilizing the Bernstein basis on a pyramid
|
||||
class L2Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
|
||||
{
|
||||
protected:
|
||||
const int nterms;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector m_shape_1d;
|
||||
mutable Vector m_shape;
|
||||
mutable DenseMatrix m_dshape;
|
||||
#endif
|
||||
std::map<int,int> dof_map;
|
||||
|
||||
struct Index
|
||||
{
|
||||
Index() = default;
|
||||
int operator()(int i1, int i2, int i3, int i4, int i5)
|
||||
{
|
||||
const int p = i1 + i2 + i3 + i4 + i5;
|
||||
const int min24 = std::min(i2,i4);
|
||||
i1 += min24;
|
||||
i2 -= min24;
|
||||
i3 += min24;
|
||||
i4 -= min24;
|
||||
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
|
||||
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
|
||||
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
|
||||
- (p + 2) * ((i4 - 1) * i4) / 2
|
||||
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
|
||||
+ 2 * (5 + 5 * p + p * p))) / 24;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
|
||||
// needed below
|
||||
int GetNumTerms() const { return nterms; }
|
||||
|
||||
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
|
||||
// The size of shape_1d should be at least p+1.
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *shape_1d, real_t *shape);
|
||||
|
||||
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
|
||||
// The size of dshape_1d should be at least p+1.
|
||||
static void CalcDShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *dshape_1d, real_t *dshape);
|
||||
|
||||
public:
|
||||
/// Construct the L2Pos_PyramidElement of order @a p
|
||||
L2Pos_PyramidElement(const int p);
|
||||
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,510 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_FE_PYRAMID
|
||||
#define MFEM_FE_PYRAMID
|
||||
|
||||
#include "fe_base.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** Base class for arbitrary order basis functions on pyramid-shaped elements
|
||||
|
||||
This base class provides a common class to store temporary vectors,
|
||||
matrices, and tensors computed by various functions defined on
|
||||
pyramid-shaped elements.
|
||||
|
||||
The function names defined here are chosen to reflect, as closely as
|
||||
possible, those used in the paper "Orientation embedded high order
|
||||
shape functions for the exact sequence elements of all shapes" by
|
||||
Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
|
||||
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
|
||||
|
||||
@note Many of the functions below, e.g. lam1, lam2, etc. and related
|
||||
functions, are singular or multi-valued at the apex of the pyramid. The
|
||||
values returned near the apex are computed in the limit z->1 using
|
||||
(x, y, z) = ((1-z)/2, (1-z)/2, z) i.e. along the line from the center
|
||||
of the base to the apex.
|
||||
*/
|
||||
class FuentesPyramid
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable DenseMatrix phi_E_mtmp;
|
||||
mutable Vector phi_Q_vtmp1;
|
||||
mutable Vector phi_Q_vtmp2;
|
||||
mutable DenseMatrix phi_Q_mtmp1;
|
||||
mutable DenseMatrix phi_Q_mtmp2;
|
||||
mutable Vector phi_T_vtmp1;
|
||||
mutable Vector phi_T_vtmp2;
|
||||
mutable Vector phi_T_vtmp3;
|
||||
mutable Vector phi_T_vtmp4;
|
||||
mutable DenseMatrix phi_T_mtmp1;
|
||||
mutable Vector E_E_vtmp;
|
||||
mutable Vector E_Q_vtmp;
|
||||
mutable DenseMatrix E_Q_mtmp1;
|
||||
mutable DenseMatrix E_Q_mtmp2;
|
||||
mutable DenseMatrix E_Q_mtmp3;
|
||||
mutable Vector E_T_vtmp1;
|
||||
mutable Vector E_T_vtmp2;
|
||||
mutable Vector E_T_vtmp3;
|
||||
mutable DenseMatrix E_T_mtmp1;
|
||||
mutable DenseMatrix E_T_mtmp2;
|
||||
mutable DenseMatrix V_Q_mtmp1;
|
||||
mutable DenseMatrix V_Q_mtmp2;
|
||||
mutable Vector V_T_vtmp1;
|
||||
mutable Vector V_T_vtmp2;
|
||||
mutable DenseMatrix V_T_mtmp1;
|
||||
mutable Vector VT_T_vtmp1;
|
||||
mutable Vector VT_T_vtmp2;
|
||||
mutable DenseMatrix VT_T_mtmp1;
|
||||
mutable DenseTensor VT_T_ttmp1;
|
||||
mutable Vector V_L_vtmp1;
|
||||
mutable Vector V_L_vtmp2;
|
||||
mutable DenseMatrix V_L_mtmp1;
|
||||
mutable DenseMatrix V_L_mtmp2;
|
||||
mutable Vector V_R_vtmp;
|
||||
mutable DenseMatrix V_R_mtmp;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static constexpr real_t one = 1.0;
|
||||
static constexpr real_t zero = 0.0;
|
||||
static constexpr real_t apex_tol = 1e-8;
|
||||
|
||||
public:
|
||||
FuentesPyramid() = default;
|
||||
|
||||
static bool CheckZ(real_t z) { return std::abs(z - 1.0) > apex_tol; }
|
||||
|
||||
/// Pyramid "Affine" Coordinates
|
||||
static real_t lam1(real_t x, real_t y, real_t z)
|
||||
{ return CheckZ(z) ? (1.0 - x - z) * (1.0 - y - z) / (1.0 - z): 0.0; }
|
||||
static real_t lam2(real_t x, real_t y, real_t z)
|
||||
{ return CheckZ(z) ? x * (1.0 - y - z) / (1.0 - z): 0.0; }
|
||||
static real_t lam3(real_t x, real_t y, real_t z)
|
||||
{ return CheckZ(z) ? x * y / (1.0 - z): 0.0; }
|
||||
static real_t lam4(real_t x, real_t y, real_t z)
|
||||
{ return CheckZ(z) ? (1.0 - x - z) * y / (1.0 - z): 0.0; }
|
||||
static real_t lam5(real_t x, real_t y, real_t z)
|
||||
{ return CheckZ(z) ? z : 1.0; }
|
||||
|
||||
/// Gradients of the "Affine" Coordinates
|
||||
static Vector grad_lam1(real_t x, real_t y, real_t z);
|
||||
static Vector grad_lam2(real_t x, real_t y, real_t z);
|
||||
static Vector grad_lam3(real_t x, real_t y, real_t z);
|
||||
static Vector grad_lam4(real_t x, real_t y, real_t z);
|
||||
static Vector grad_lam5(real_t x, real_t y, real_t z);
|
||||
|
||||
/// Two component vectors associated with edges touching the apex
|
||||
static Vector lam15(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam1(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam25(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam2(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam35(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam3(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam45(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam4(x, y, z), lam5(x, y, z)}); }
|
||||
|
||||
/// Gradients of the above two component vectors
|
||||
static DenseMatrix grad_lam15(real_t x, real_t y, real_t z);
|
||||
static DenseMatrix grad_lam25(real_t x, real_t y, real_t z);
|
||||
static DenseMatrix grad_lam35(real_t x, real_t y, real_t z);
|
||||
static DenseMatrix grad_lam45(real_t x, real_t y, real_t z);
|
||||
|
||||
/// Computes $\lambda_i \nabla \lambda_5 - \lambda_5 \nabla \lambda_i$
|
||||
static Vector lam15_grad_lam15(real_t x, real_t y, real_t z);
|
||||
static Vector lam25_grad_lam25(real_t x, real_t y, real_t z);
|
||||
static Vector lam35_grad_lam35(real_t x, real_t y, real_t z);
|
||||
static Vector lam45_grad_lam45(real_t x, real_t y, real_t z);
|
||||
|
||||
/// Three component vectors associated with triangular faces
|
||||
static Vector lam125(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam1(x, y, z), lam2(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam235(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam2(x, y, z), lam3(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam345(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam3(x, y, z), lam4(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam435(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam4(x, y, z), lam3(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam415(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam4(x, y, z), lam1(x, y, z), lam5(x, y, z)}); }
|
||||
static Vector lam145(real_t x, real_t y, real_t z)
|
||||
{ return Vector({lam1(x, y, z), lam4(x, y, z), lam5(x, y, z)}); }
|
||||
|
||||
/// Vector functions related to the normals to the triangular faces
|
||||
///
|
||||
/// Computes
|
||||
/// $
|
||||
/// \lambda_i \nabla\lambda_j \times \nabla \lambda_5
|
||||
/// + \lambda_j \nabla\lambda_5 \times \nabla \lambda_i
|
||||
/// + \lambda_5 \nabla\lambda_i \times \nabla \lambda_j
|
||||
/// $
|
||||
static Vector lam125_grad_lam125(real_t x, real_t y, real_t z);
|
||||
static Vector lam235_grad_lam235(real_t x, real_t y, real_t z);
|
||||
static Vector lam345_grad_lam345(real_t x, real_t y, real_t z);
|
||||
static Vector lam435_grad_lam435(real_t x, real_t y, real_t z);
|
||||
static Vector lam415_grad_lam415(real_t x, real_t y, real_t z);
|
||||
static Vector lam145_grad_lam145(real_t x, real_t y, real_t z);
|
||||
|
||||
/// Divergences of the above "normal" vector functions divided by 3
|
||||
static real_t div_lam125_grad_lam125(real_t x, real_t y, real_t z);
|
||||
static real_t div_lam235_grad_lam235(real_t x, real_t y, real_t z);
|
||||
static real_t div_lam345_grad_lam345(real_t x, real_t y, real_t z);
|
||||
static real_t div_lam435_grad_lam435(real_t x, real_t y, real_t z);
|
||||
static real_t div_lam415_grad_lam415(real_t x, real_t y, real_t z);
|
||||
static real_t div_lam145_grad_lam145(real_t x, real_t y, real_t z);
|
||||
|
||||
static real_t mu0(real_t z)
|
||||
{ return 1.0 - z; }
|
||||
static real_t mu1(real_t z)
|
||||
{ return z; }
|
||||
|
||||
static Vector grad_mu0(real_t z)
|
||||
{ return Vector({0.0, 0.0, -1.0}); }
|
||||
static Vector grad_mu1(real_t z)
|
||||
{ return Vector({0.0, 0.0, 1.0}); }
|
||||
|
||||
static Vector mu01(real_t z)
|
||||
{ return Vector({mu0(z), mu1(z)}); }
|
||||
|
||||
static DenseMatrix grad_mu01(real_t z);
|
||||
|
||||
static real_t mu0(real_t z, const Vector &xy, unsigned int ab)
|
||||
{ return 1.0 - xy[ab-1] / (1.0 - z); }
|
||||
static real_t mu1(real_t z, const Vector &xy, unsigned int ab)
|
||||
{ return xy[ab-1] / (1.0 - z); }
|
||||
|
||||
static Vector grad_mu0(real_t z, const Vector xy, unsigned int ab);
|
||||
static Vector grad_mu1(real_t z, const Vector xy, unsigned int ab);
|
||||
|
||||
static Vector mu01(real_t z, Vector xy, unsigned int ab)
|
||||
{ return Vector({mu0(z, xy, ab), mu1(z, xy, ab)}); }
|
||||
|
||||
static DenseMatrix grad_mu01(real_t z, Vector xy, unsigned int ab);
|
||||
static Vector mu01_grad_mu01(real_t z, Vector xy, unsigned int ab);
|
||||
|
||||
static real_t nu0(real_t z, Vector xy, unsigned int ab)
|
||||
{ return 1.0 - xy[ab-1] - z; }
|
||||
static real_t nu1(real_t z, Vector xy, unsigned int ab) { return xy[ab-1]; }
|
||||
static real_t nu2(real_t z, Vector xy, unsigned int ab) { return z; }
|
||||
|
||||
static Vector grad_nu0(real_t z, const Vector xy, unsigned int ab);
|
||||
static Vector grad_nu1(real_t z, const Vector xy, unsigned int ab);
|
||||
static Vector grad_nu2(real_t z, const Vector xy, unsigned int ab);
|
||||
|
||||
static Vector nu01(real_t z, Vector xy, unsigned int ab)
|
||||
{ return Vector({nu0(z, xy, ab), nu1(z, xy, ab)}); }
|
||||
static Vector nu12(real_t z, Vector xy, unsigned int ab)
|
||||
{ return Vector({nu1(z, xy, ab), nu2(z, xy, ab)}); }
|
||||
static Vector nu012(real_t z, Vector xy, unsigned int ab)
|
||||
{ return Vector({nu0(z, xy, ab), nu1(z, xy, ab), nu2(z, xy, ab)}); }
|
||||
static Vector nu120(real_t z, Vector xy, unsigned int ab)
|
||||
{ return Vector({nu1(z, xy, ab), nu2(z, xy, ab), nu0(z, xy, ab)}); }
|
||||
|
||||
static DenseMatrix grad_nu01(real_t z, Vector xy, unsigned int ab);
|
||||
static DenseMatrix grad_nu012(real_t z, Vector xy, unsigned int ab);
|
||||
static DenseMatrix grad_nu120(real_t z, Vector xy, unsigned int ab);
|
||||
|
||||
static Vector nu01_grad_nu01(real_t z, Vector xy, unsigned int ab);
|
||||
static Vector nu12_grad_nu12(real_t z, Vector xy, unsigned int ab);
|
||||
static Vector nu012_grad_nu012(real_t z, Vector xy, unsigned int ab);
|
||||
|
||||
/// Shifted and Scaled Legendre Polynomials
|
||||
/** Implements a scaled and shifted set of Legendre polynomials
|
||||
|
||||
$P_i(x;t) = P_i(x / t) * t^i$
|
||||
|
||||
where @a t >= 0.0, @a x $\in [0,t]$, and $P_i$ is the shifted Legendre
|
||||
polynomial defined on $[0,1]$ rather than the usual $[-1,1]$. The
|
||||
entries stored in @a u correspond to the values of
|
||||
$P_0$, $P_1$, ... $P_p$.
|
||||
|
||||
@a u must be at least @a p + 1 in length
|
||||
*/
|
||||
static void CalcScaledLegendre(int p, real_t x, real_t t,
|
||||
real_t *u);
|
||||
static void CalcScaledLegendre(int p, real_t x, real_t t,
|
||||
real_t *u, real_t *dudx, real_t *dudt);
|
||||
|
||||
static void CalcScaledLegendre(int p, real_t x, real_t t,
|
||||
Vector &u);
|
||||
static void CalcScaledLegendre(int p, real_t x, real_t t,
|
||||
Vector &u, Vector &dudx, Vector &dudt);
|
||||
|
||||
/// Integrated Legendre Polynomials
|
||||
/** These are the integrals of the shifted and scaled Legendre polynomials
|
||||
provided above and defined as:
|
||||
|
||||
$L_i(x;t) = \int_0^x P_{i-1}(y;t)dy\mbox{ for }i>=1$
|
||||
|
||||
These polynomials are computed as:
|
||||
|
||||
$L_0(x;t) = 0$, $L_1(x;t) = x$,
|
||||
|
||||
$2(2i-1)L_i(x;t) = P_i(x;t) - t^2 P_{i-2}(x;t)\mbox{ for }i>=2$
|
||||
|
||||
@a u must be at least @a p + 1 in length
|
||||
*/
|
||||
static void CalcIntegratedLegendre(int p, real_t x,
|
||||
real_t t, real_t *u);
|
||||
static void CalcIntegratedLegendre(int p, real_t x,
|
||||
real_t t, real_t *u,
|
||||
real_t *dudx, real_t *dudt);
|
||||
|
||||
static void CalcIntegratedLegendre(int p, real_t x,
|
||||
real_t t, Vector &u);
|
||||
static void CalcIntegratedLegendre(int p, real_t x,
|
||||
real_t t, Vector &u,
|
||||
Vector &dudx, Vector &dudt);
|
||||
|
||||
/** @a u must be at least @a p + 1 in length */
|
||||
static void CalcHomogenizedScaLegendre(int p, real_t s0, real_t s1,
|
||||
real_t *u);
|
||||
static void CalcHomogenizedScaLegendre(int p,
|
||||
real_t s0, real_t s1,
|
||||
real_t *u,
|
||||
real_t *duds0, real_t *duds1);
|
||||
static void CalcHomogenizedScaLegendre(int p, real_t s0, real_t s1,
|
||||
Vector &u);
|
||||
static void CalcHomogenizedScaLegendre(int p,
|
||||
real_t s0, real_t s1,
|
||||
Vector &u,
|
||||
Vector &duds0, Vector &duds1);
|
||||
|
||||
/** @a u must be at least @a p + 1 in length */
|
||||
static void CalcHomogenizedIntLegendre(int p,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u);
|
||||
static void CalcHomogenizedIntLegendre(int p,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u,
|
||||
real_t *dudt0, real_t *dudt1);
|
||||
static void CalcHomogenizedIntLegendre(int p,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u);
|
||||
static void CalcHomogenizedIntLegendre(int p,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u,
|
||||
Vector &dudt0, Vector &dudt1);
|
||||
|
||||
/// Shifted and Scaled Jacobi Polynomials
|
||||
/** Implements a scaled and shifted set of Jacobi polynomials
|
||||
|
||||
$P^\alpha_i(x / t) * t^i$
|
||||
|
||||
where @a alpha $= \alpha >-1$, @a t $>= 0.0$, @a x $\in [0,t]$, and
|
||||
$P^\alpha_i$ is the shifted Jacobi polynomial defined on $[0,1]$ rather
|
||||
than the usual $[-1,1]$. The entries stored in @a u correspond to the
|
||||
values of $P^\alpha_0$, $P^\alpha_1$, ... $P^\alpha_p$.
|
||||
|
||||
@note Jacobi polynomials typically posses two parameters,
|
||||
$P^{\alpha, \beta}_i$, but we only consider the special case where
|
||||
$\beta=0$.
|
||||
|
||||
@a u must be at least @a p + 1 in length
|
||||
*/
|
||||
static void CalcScaledJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
real_t *u);
|
||||
static void CalcScaledJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
real_t *u, real_t *dudx, real_t *dudt);
|
||||
|
||||
static void CalcScaledJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
Vector &u);
|
||||
static void CalcScaledJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
Vector &u, Vector &dudx, Vector &dudt);
|
||||
|
||||
/// Integrated Jacobi Polynomials
|
||||
/** These are the integrals of the shifted and scaled Jacobi polynomials
|
||||
provided above and defined as:
|
||||
|
||||
$L^\alpha_i(x;t) = \int_0^x P^\alpha_{i-1}(y;t)dy\mbox{ for }i>=1$
|
||||
|
||||
These polynomials are computed as:
|
||||
|
||||
$L^\alpha_0(x;t) = 0$, $L^\alpha_1(x;t) = x$,
|
||||
|
||||
$L^\alpha_i(x;t) = a_i P^\alpha_i(x;t) + b_i t P^\alpha_{i-1}(x;t)
|
||||
- c_i t^2 P^\alpha_{i-2}(x;t)\mbox{ for }i>=2$
|
||||
|
||||
With
|
||||
|
||||
$a_i = (i + \alpha) / (2i + \alpha - 1)(2i + \alpha)$
|
||||
|
||||
$b_i = \alpha / (2i + \alpha - 2)(2i + \alpha)$
|
||||
|
||||
$c_i = (i - 1) / (2i + \alpha - 2)(2i + \alpha - 1)$
|
||||
|
||||
@a u must be at least @a p + 1 in length
|
||||
*/
|
||||
static void CalcIntegratedJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
real_t *u);
|
||||
static void CalcIntegratedJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
real_t *u, real_t *dudx, real_t *dudt);
|
||||
|
||||
static void CalcIntegratedJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
Vector &u)
|
||||
{ CalcIntegratedJacobi(p, alpha, x, t, u.GetData()); }
|
||||
static void CalcIntegratedJacobi(int p, real_t alpha,
|
||||
real_t x, real_t t,
|
||||
Vector &u, Vector &dudx, Vector &dudt)
|
||||
{
|
||||
CalcIntegratedJacobi(p, alpha, x, t, u.GetData(),
|
||||
dudx.GetData(), dudt.GetData());
|
||||
}
|
||||
|
||||
/** @a u must be at least @a p + 1 in length */
|
||||
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u)
|
||||
{ CalcScaledJacobi(p, alpha, t1, t0 + t1, u); }
|
||||
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u,
|
||||
real_t *dudt0, real_t *dudt1);
|
||||
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u);
|
||||
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u,
|
||||
Vector &dudt0, Vector &dudt1);
|
||||
|
||||
/** @a u must be at least @a p + 1 in length */
|
||||
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u)
|
||||
{ CalcIntegratedJacobi(p, alpha, t1, t0 + t1, u); }
|
||||
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
real_t *u,
|
||||
real_t *dudt0, real_t *dudt1);
|
||||
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u);
|
||||
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
|
||||
real_t t0, real_t t1,
|
||||
Vector &u,
|
||||
Vector &dudt0, Vector &dudt1);
|
||||
|
||||
/** @a u must be at least @a p + 1 in length */
|
||||
static void phi_E(int p, real_t s0, real_t s1, real_t *u);
|
||||
static void phi_E(int p, real_t s0, real_t s1, real_t *u,
|
||||
real_t *duds0, real_t *duds1);
|
||||
static void phi_E(int p, Vector s, Vector &u);
|
||||
static void phi_E(int p, Vector s, Vector &u, DenseMatrix &duds);
|
||||
|
||||
/** @a grad_s must be 2x3 */
|
||||
void phi_E(int p, Vector s, const DenseMatrix &grad_s,
|
||||
Vector &u, DenseMatrix &grad_u) const;
|
||||
|
||||
/** @a u must be at least (p+1)x(p+1) in size */
|
||||
void phi_Q(int p, Vector s, Vector t, DenseMatrix &u) const;
|
||||
void phi_Q(int p, Vector s, const DenseMatrix &grad_s,
|
||||
Vector t, const DenseMatrix &grad_t,
|
||||
DenseMatrix &u, DenseTensor &grad_u) const;
|
||||
void phi_T(int p, Vector nu, DenseMatrix &u) const;
|
||||
void phi_T(int p, Vector nu, const DenseMatrix &grad_nu,
|
||||
DenseMatrix &u, DenseTensor &grad_u) const;
|
||||
|
||||
/** This is a vector-valued function associated with an edge of a pyramid
|
||||
|
||||
The vector @a s contains two coordinate values and @a ds is related to the
|
||||
gradient of these coordinates with respect to the reference coordinates i.e.
|
||||
sds = s0 grad s1 - s1 grad s0
|
||||
*/
|
||||
void E_E(int p, Vector s, Vector sds, DenseMatrix &u) const;
|
||||
void E_E(int p, Vector s, const DenseMatrix &grad_s, DenseMatrix &u,
|
||||
DenseMatrix &curl_u) const;
|
||||
|
||||
void E_Q(int p, Vector s, Vector ds, Vector t,
|
||||
DenseTensor &u) const;
|
||||
void E_Q(int p, Vector s, const DenseMatrix &grad_s,
|
||||
Vector t, const DenseMatrix &grad_t,
|
||||
DenseTensor &u, DenseTensor &curl_u) const;
|
||||
|
||||
void E_T(int p, Vector s, Vector sds, DenseTensor &u) const;
|
||||
void E_T(int p, Vector s, const DenseMatrix &grad_s,
|
||||
DenseTensor &u, DenseTensor &curl_u) const;
|
||||
|
||||
/** This is a vector-valued function associated with the quadrilateral face
|
||||
of a pyramid
|
||||
|
||||
The vectors @a s and @a t contain pairs of coordinate values and @a ds and
|
||||
@a dt are related to derivatives of these coordinates:
|
||||
|
||||
ds = s0 grad s1 - s1 grad s0
|
||||
|
||||
dt = t0 grad t1 - t1 grad t0
|
||||
*/
|
||||
void V_Q(int p, Vector s, Vector ds, Vector t, Vector dt,
|
||||
DenseTensor &u) const;
|
||||
|
||||
/** This is a vector-valued function associated with the triangular faces of
|
||||
a pyramid
|
||||
|
||||
The vector @a s contains three coordinate values and @a sdsxds is related to
|
||||
derivatives of these coordinates with respect to the reference coordinates:
|
||||
|
||||
sdsxds = s0 grad s1 x grad s2 + s1 grad s2 x grad s0 +
|
||||
s2 grad s0 x grad s1
|
||||
*/
|
||||
void V_T(int p, Vector s, Vector sdsxds, DenseTensor &u) const;
|
||||
|
||||
/** This computes V_T as above and its divergence
|
||||
|
||||
The vector @a s contains three coordinate values and @a sdsxds is related to
|
||||
derivatives of these coordinates with respect to the reference coordinates:
|
||||
|
||||
sdsxds = s0 grad s1 x grad s2 + s1 grad s2 x grad s0 +
|
||||
s2 grad s0 x grad s1
|
||||
|
||||
The scalar @a dsdsxds is the divergence of sdsxds:
|
||||
|
||||
dsdsxds = grad s0 dot (grad s1 x grad s2)
|
||||
*/
|
||||
void V_T(int p, Vector s, Vector sdsxds, real_t dsdsxds,
|
||||
DenseTensor &u, DenseMatrix &du) const;
|
||||
|
||||
void VT_T(int p, Vector s, Vector sds, Vector sdsxds,
|
||||
real_t mu, Vector grad_mu, DenseTensor &u) const;
|
||||
void VT_T(int p, Vector s, Vector sds, Vector sdsxds,
|
||||
Vector grad_s2, real_t mu, Vector grad_mu,
|
||||
DenseTensor &u, DenseMatrix &du) const;
|
||||
|
||||
/** This implements $V^\unlhd_{ij}$ from the Fuentes paper
|
||||
|
||||
@a u must be at least (p+1)x(p+1)x3
|
||||
*/
|
||||
void V_L(int p, Vector sx, const DenseMatrix &grad_sx,
|
||||
Vector sy, const DenseMatrix &grad_sy,
|
||||
real_t t, Vector grad_t, DenseTensor &u) const;
|
||||
|
||||
/** This implements $V^\unrhd_i$ from the Fuentes paper
|
||||
|
||||
@a u must be at least (p+1)x3 */
|
||||
void V_R(int p, Vector s, const DenseMatrix &grad_s,
|
||||
real_t mu, Vector grad_mu,
|
||||
real_t t, Vector grad_t, DenseMatrix &u) const;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1264,6 +1264,742 @@ void RT_WedgeElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
const real_t RT_FuentesPyramidElement::nk[24] =
|
||||
{
|
||||
0,0,-1, 0,-1,0, 1,0,1, 0,1,1, -1,0,0,
|
||||
M_SQRT2,0,M_SQRT1_2, 0,M_SQRT2,M_SQRT1_2, 0,0,1
|
||||
};
|
||||
|
||||
RT_FuentesPyramidElement::RT_FuentesPyramidElement(const int p)
|
||||
: VectorFiniteElement(3, Geometry::PYRAMID, (p + 1)*(3*p*(p + 2) + 5),
|
||||
p + 1, H_DIV, FunctionSpace::Uk),
|
||||
dof2nk(dof)
|
||||
{
|
||||
zmax = 0.0;
|
||||
|
||||
const real_t *iop = poly1d.OpenPoints(p);
|
||||
const real_t *icp = poly1d.ClosedPoints(p + 1);
|
||||
const real_t *bop = poly1d.OpenPoints(p);
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
tmp1_i.SetSize(p + 2);
|
||||
tmp1_ij.SetSize(p + 2, p + 2);
|
||||
tmp2_ij.SetSize(p + 2, dim);
|
||||
tmp3_ij.SetSize(p + 2, dim);
|
||||
tmp4_ij.SetSize(p + 1, p + 1);
|
||||
tmp1_ijk.SetSize(p + 1, p + 1, dim);
|
||||
tmp2_ijk.SetSize(p + 1, p + 1, dim);
|
||||
tmp3_ijk.SetSize(p + 1, p + 1, dim);
|
||||
tmp4_ijk.SetSize(p + 1, p + 2, dim);
|
||||
tmp5_ijk.SetSize(p + 1, p + 2, dim);
|
||||
tmp6_ijk.SetSize(p + 2, p + 2, dim);
|
||||
tmp7_ijk.SetSize(p + 2, p + 2, dim);
|
||||
u.SetSize(dof, dim);
|
||||
divu.SetSize(dof);
|
||||
#else
|
||||
Vector tmp1_i(p + 2);
|
||||
DenseMatrix tmp1_ij(p + 2, p + 2);
|
||||
DenseMatrix tmp2_ij(p + 2, dim);
|
||||
DenseMatrix tmp3_ij(p + 2, dim);
|
||||
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
|
||||
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
|
||||
DenseMatrix u(dof, dim);
|
||||
#endif
|
||||
|
||||
int o = 0;
|
||||
|
||||
// quadrilateral face
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++) // (3,2,1,0)
|
||||
{
|
||||
Nodes.IntPoint(o).Set3(bop[i], bop[p-j], 0.);
|
||||
dof2nk[o++] = 0;
|
||||
}
|
||||
// triangular faces
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i + j <= p; i++) // (0,1,4)
|
||||
{
|
||||
real_t w = bop[i] + bop[j] + bop[p-i-j];
|
||||
Nodes.IntPoint(o).Set3(bop[i]/w, 0., bop[j]/w);
|
||||
dof2nk[o++] = 1;
|
||||
}
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i + j <= p; i++) // (1,2,4)
|
||||
{
|
||||
real_t w = bop[i] + bop[j] + bop[p-i-j];
|
||||
Nodes.IntPoint(o).Set3(1.-bop[j]/w, bop[i]/w, bop[j]/w);
|
||||
dof2nk[o++] = 2;
|
||||
}
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = p - j; i >= 0; i--) // (2,3,4)
|
||||
{
|
||||
real_t w = bop[i] + bop[j] + bop[p-i-j];
|
||||
Nodes.IntPoint(o).Set3(bop[i]/w, 1.0-bop[j]/w, bop[j]/w);
|
||||
dof2nk[o++] = 3;
|
||||
}
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = p - j; i >= 0; i--) // (3,0,4)
|
||||
{
|
||||
real_t w = bop[i] + bop[j] + bop[p-i-j];
|
||||
Nodes.IntPoint(o).Set3(0., bop[i]/w, bop[j]/w);
|
||||
dof2nk[o++] = 4;
|
||||
}
|
||||
|
||||
// interior
|
||||
// x-components
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 1; i <= p; i++)
|
||||
{
|
||||
real_t w = 1.0 - iop[k];
|
||||
Nodes.IntPoint(o).Set3(icp[i]*w, iop[j]*w, iop[k]);
|
||||
dof2nk[o++] = 5;
|
||||
}
|
||||
// y-components
|
||||
for (int k = 0; k <= p; k++)
|
||||
for (int j = 1; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
real_t w = 1.0 - iop[k];
|
||||
Nodes.IntPoint(o).Set3(iop[i]*w, icp[j]*w, iop[k]);
|
||||
dof2nk[o++] = 6;
|
||||
}
|
||||
// z-components
|
||||
for (int k = 1; k <= p; k++)
|
||||
for (int j = 0; j <= p; j++)
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
real_t w = 1.0 - icp[k];
|
||||
Nodes.IntPoint(o).Set3(iop[i]*w, iop[j]*w, icp[k]);
|
||||
dof2nk[o++] = 7;
|
||||
}
|
||||
|
||||
DenseMatrix T(dof);
|
||||
|
||||
for (int m = 0; m < dof; m++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
const Vector nm({nk[3*dof2nk[m]], nk[3*dof2nk[m]+1], nk[3*dof2nk[m]+2]});
|
||||
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
|
||||
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
|
||||
tmp7_ijk,
|
||||
tmp3_ij, u);
|
||||
u.Mult(nm, T.GetColumn(m));
|
||||
}
|
||||
|
||||
Ti.Factor(T);
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int p = order - 1;
|
||||
|
||||
Vector tmp1_i(p + 2);
|
||||
DenseMatrix tmp1_ij(p + 2, p + 2);
|
||||
DenseMatrix tmp2_ij(p + 2, dim);
|
||||
DenseMatrix tmp3_ij(p + 2, dim);
|
||||
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
|
||||
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
|
||||
DenseMatrix u(dof, dim);
|
||||
#endif
|
||||
|
||||
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
|
||||
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
|
||||
tmp7_ijk, tmp3_ij, u);
|
||||
|
||||
Ti.Mult(u, shape);
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::CalcRawVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int p = order - 1;
|
||||
|
||||
Vector tmp1_i(p + 2);
|
||||
DenseMatrix tmp1_ij(p + 2, p + 2);
|
||||
DenseMatrix tmp2_ij(p + 2, dim);
|
||||
DenseMatrix tmp3_ij(p + 2, dim);
|
||||
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
|
||||
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
|
||||
#endif
|
||||
|
||||
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
|
||||
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
|
||||
tmp7_ijk, tmp3_ij, shape);
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int p = order - 1;
|
||||
|
||||
Vector tmp1_i(p + 2);
|
||||
DenseMatrix tmp1_ij(p + 2, p + 2);
|
||||
DenseMatrix tmp2_ij(p + 2, dim);
|
||||
DenseMatrix tmp3_ij(p + 2, dim);
|
||||
DenseMatrix tmp4_ij(p + 1, p + 1);
|
||||
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
|
||||
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
|
||||
Vector divu(dof);
|
||||
#endif
|
||||
divu = 0.0;
|
||||
|
||||
calcDivBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
|
||||
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ij, tmp4_ijk, tmp5_ijk,
|
||||
tmp6_ijk, tmp7_ijk, tmp3_ij, divu);
|
||||
|
||||
Ti.Mult(divu, divshape);
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::CalcRawDivShape(const IntegrationPoint &ip,
|
||||
Vector &dshape) const
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int p = order - 1;
|
||||
|
||||
Vector tmp1_i(p + 2);
|
||||
DenseMatrix tmp1_ij(p + 2, p + 2);
|
||||
DenseMatrix tmp2_ij(p + 2, dim);
|
||||
DenseMatrix tmp3_ij(p + 2, dim);
|
||||
DenseMatrix tmp4_ij(p + 1, p + 1);
|
||||
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
|
||||
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
|
||||
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
|
||||
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
|
||||
#endif
|
||||
|
||||
calcDivBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
|
||||
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ij, tmp4_ijk, tmp5_ijk,
|
||||
tmp6_ijk, tmp7_ijk, tmp3_ij, dshape);
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::calcBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
Vector &phi_k,
|
||||
DenseMatrix &phi_ij,
|
||||
DenseMatrix &dphi_k,
|
||||
DenseTensor &VQ_ijk,
|
||||
DenseTensor &VT_ijk,
|
||||
DenseTensor &VTT_ijk,
|
||||
DenseTensor &E_ijk,
|
||||
DenseTensor &dE_ijk,
|
||||
DenseTensor &dphi_ijk,
|
||||
DenseTensor &VL_ijk,
|
||||
DenseMatrix &VR_ij,
|
||||
DenseMatrix &F) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
real_t mu;
|
||||
|
||||
if (std::fabs(1.0 - z) < apex_tol)
|
||||
{
|
||||
z = 1.0 - apex_tol;
|
||||
y = 0.5 * (1.0 - z);
|
||||
x = 0.5 * (1.0 - z);
|
||||
xy(0) = x; xy(1) = y;
|
||||
}
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
F = 0.0;
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Quadrilateral face
|
||||
if (z < 1.0)
|
||||
{
|
||||
V_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2),
|
||||
VQ_ijk);
|
||||
|
||||
const real_t muz3 = pow(mu0(z), 3);
|
||||
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
F(o, k) = muz3 * VQ_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular faces
|
||||
if (z < 1.0)
|
||||
{
|
||||
Vector dmuz;
|
||||
|
||||
// (a,b) = (1,2), c = 0
|
||||
V_T(p, nu012(z, xy, 1), nu012_grad_nu012(z, xy, 1), VT_ijk);
|
||||
mu = mu0(z, xy, 2);
|
||||
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 2);
|
||||
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
|
||||
nu012_grad_nu012(z, xy, 1), mu, dmuz, VTT_ijk);
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
|
||||
}
|
||||
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 2);
|
||||
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
|
||||
nu012_grad_nu012(z, xy, 1), mu, dmuz, VTT_ijk);
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 0
|
||||
V_T(p, nu012(z, xy, 2), nu012_grad_nu012(z, xy, 2), VT_ijk);
|
||||
mu = mu0(z, xy, 1);
|
||||
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 1);
|
||||
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
|
||||
nu012_grad_nu012(z, xy, 2), mu, dmuz, VTT_ijk);
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 1);
|
||||
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
|
||||
nu012_grad_nu012(z, xy, 2), mu, dmuz, VTT_ijk);
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
// Family I
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
E_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), E_ijk, dE_ijk);
|
||||
phi_E(p, mu01(z), grad_mu01(z), phi_k, dphi_k);
|
||||
const real_t muz = mu0(z);
|
||||
const Vector dmuz(grad_mu0(z));
|
||||
|
||||
Vector dmuphi(3), E(3), v(3);
|
||||
|
||||
for (int k=2; k<=p; k++)
|
||||
{
|
||||
dmuphi(0) = muz * dphi_k(k,0) + dmuz(0) * phi_k(k);
|
||||
dmuphi(1) = muz * dphi_k(k,1) + dmuz(1) * phi_k(k);
|
||||
dmuphi(2) = muz * dphi_k(k,2) + dmuz(2) * phi_k(k);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_ijk(i,j,0); E(1) = E_ijk(i,j,1); E(2) = E_ijk(i,j,2);
|
||||
dmuphi.cross3D(E, v);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muz * phi_k(k) * dE_ijk(i,j,l) + v(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Family II
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
E_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
|
||||
mu01(z, xy, 1), grad_mu01(z, xy, 1), E_ijk, dE_ijk);
|
||||
// Re-using phi_E from Family I
|
||||
const real_t muz = mu0(z);
|
||||
const Vector dmuz(grad_mu0(z));
|
||||
|
||||
Vector dmuphi(3), E(3), v(3);
|
||||
|
||||
for (int k=2; k<=p; k++)
|
||||
{
|
||||
dmuphi(0) = muz * dphi_k(k,0) + dmuz(0) * phi_k(k);
|
||||
dmuphi(1) = muz * dphi_k(k,1) + dmuz(1) * phi_k(k);
|
||||
dmuphi(2) = muz * dphi_k(k,2) + dmuz(2) * phi_k(k);
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
{
|
||||
E(0) = E_ijk(i,j,0); E(1) = E_ijk(i,j,1); E(2) = E_ijk(i,j,2);
|
||||
dmuphi.cross3D(E, v);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muz * phi_k(k) * dE_ijk(i,j,l) + v(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Family III
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
phi_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
|
||||
mu01(z, xy, 1), grad_mu01(z, xy, 1), phi_ij, dphi_ijk);
|
||||
const real_t muz = mu0(z);
|
||||
const Vector dmuz(grad_mu0(z));
|
||||
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const int n = std::max(i,j);
|
||||
const real_t nmu = n * pow(muz, n-1);
|
||||
F(o, 0) = nmu * (dphi_ijk(i,j,1) * dmuz(2) -
|
||||
dphi_ijk(i,j,2) * dmuz(1));
|
||||
F(o, 1) = nmu * (dphi_ijk(i,j,2) * dmuz(0) -
|
||||
dphi_ijk(i,j,0) * dmuz(2));
|
||||
F(o, 2) = nmu * (dphi_ijk(i,j,0) * dmuz(1) -
|
||||
dphi_ijk(i,j,1) * dmuz(0));
|
||||
}
|
||||
}
|
||||
// Family IV
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
// Re-using V_Q from Quadrilateral Face
|
||||
phi_E(p, mu01(z), phi_k);
|
||||
|
||||
const real_t muz2 = pow(mu0(z), 2);
|
||||
|
||||
for (int k=2; k<=p; k++)
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muz2 * VQ_ijk(i, j, l) * phi_k(k);
|
||||
}
|
||||
|
||||
}
|
||||
// Family V
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
V_L(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), mu0(z), grad_mu0(z), VL_ijk);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const int n = std::max(i, j);
|
||||
const real_t muzi = pow(muz, n-1);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muzi * VL_ijk(i, j, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Family VI
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
V_R(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu1(z, xy, 2), grad_mu1(z, xy, 2), mu0(z), grad_mu0(z), VR_ij);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const real_t muzi = pow(muz, i-1);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muzi * VR_ij(i, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Family VII
|
||||
if (z < 1.0 && p >= 2)
|
||||
{
|
||||
V_R(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
|
||||
mu1(z, xy, 1), grad_mu1(z,xy,1), mu0(z), grad_mu0(z), VR_ij);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const real_t muzi = pow(muz, i-1);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
F(o, l) = muzi * VR_ij(i, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RT_FuentesPyramidElement::calcDivBasis(const int p,
|
||||
const IntegrationPoint &ip,
|
||||
Vector &phi_k,
|
||||
DenseMatrix &phi_ij,
|
||||
DenseMatrix &dphi_k,
|
||||
DenseTensor &VQ_ijk,
|
||||
DenseTensor &VT_ijk,
|
||||
DenseTensor &VTT_ijk,
|
||||
DenseMatrix &dVTT_ij,
|
||||
DenseTensor &E_ijk,
|
||||
DenseTensor &dE_ijk,
|
||||
DenseTensor &dphi_ijk,
|
||||
DenseTensor &VL_ijk,
|
||||
DenseMatrix &VR_ij,
|
||||
Vector &dF) const
|
||||
{
|
||||
real_t x = ip.x;
|
||||
real_t y = ip.y;
|
||||
real_t z = ip.z;
|
||||
Vector xy({x,y});
|
||||
real_t mu;
|
||||
|
||||
bool limz1 = false;
|
||||
if (std::fabs(1.0 - z) < apex_tol)
|
||||
{
|
||||
limz1 = true;
|
||||
z = 1.0 - apex_tol;
|
||||
y = 0.5 * (1.0 - z);
|
||||
x = 0.5 * (1.0 - z);
|
||||
xy(0) = x; xy(1) = y;
|
||||
}
|
||||
zmax = std::max(z, zmax);
|
||||
|
||||
dF = 0.0;
|
||||
|
||||
int o = 0;
|
||||
|
||||
// Quadrilateral face
|
||||
{
|
||||
V_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2),
|
||||
VQ_ijk);
|
||||
|
||||
const real_t muz2 = pow(mu0(z), 2);
|
||||
const Vector dmuz = grad_mu0(z);
|
||||
|
||||
const int o0 = o;
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dF(o) += 3.0 * muz2 * dmuz(k) * VQ_ijk(i, j, k);
|
||||
}
|
||||
|
||||
// Overwrite lowest order quadrilateral face DoF with known limiting
|
||||
// value
|
||||
if (limz1)
|
||||
{
|
||||
dF(o0) = -3.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Triangular faces
|
||||
{
|
||||
Vector dmuz;
|
||||
|
||||
// (a,b) = (1,2), c = 0
|
||||
V_T(p, nu012(z, xy, 1), nu012_grad_nu012(z, xy, 1), VT_ijk);
|
||||
mu = mu0(z, xy, 2);
|
||||
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 2);
|
||||
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
|
||||
nu012_grad_nu012(z, xy, 1), grad_nu2(z, xy, 1), mu, dmuz,
|
||||
VTT_ijk, dVTT_ij);
|
||||
const int o1 = o;
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
dF(o) = 0.5 * dVTT_ij(i, j);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a,b) = (1,2), c = 1
|
||||
mu = mu1(z, xy, 2);
|
||||
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 2);
|
||||
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
|
||||
nu012_grad_nu012(z, xy, 1), grad_nu2(z, xy, 1), mu, dmuz,
|
||||
VTT_ijk, dVTT_ij);
|
||||
const int o2 = o;
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
dF(o) = 0.5 * dVTT_ij(i, j);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 0
|
||||
V_T(p, nu012(z, xy, 2), nu012_grad_nu012(z, xy, 2), VT_ijk);
|
||||
mu = mu0(z, xy, 1);
|
||||
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 1);
|
||||
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
|
||||
nu012_grad_nu012(z, xy, 2), grad_nu2(z, xy, 2), mu, dmuz,
|
||||
VTT_ijk, dVTT_ij);
|
||||
const int o3 = o;
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
dF(o) = 0.5 * dVTT_ij(i, j);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// (a,b) = (2,1), c = 1
|
||||
mu = mu1(z, xy, 1);
|
||||
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 1);
|
||||
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
|
||||
nu012_grad_nu012(z, xy, 2), grad_nu2(z, xy, 2), mu, dmuz,
|
||||
VTT_ijk, dVTT_ij);
|
||||
const int o4 = o;
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i+j<p; i++, o++)
|
||||
{
|
||||
dF(o) = 0.5 * dVTT_ij(i, j);
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite lowest order triangular face DoFs with known limiting values
|
||||
if (limz1)
|
||||
{
|
||||
dF(o1) = 1.5;
|
||||
dF(o2) = -1.5;
|
||||
dF(o3) = -1.5;
|
||||
dF(o4) = 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
// Interior
|
||||
// Family I
|
||||
if (p >= 2)
|
||||
{
|
||||
// Divergence is zero so skip ahead
|
||||
o += (p-1) * (p-1) * p;
|
||||
}
|
||||
|
||||
// Family II
|
||||
if (p >= 2)
|
||||
{
|
||||
// Divergence is zero so skip ahead
|
||||
o += (p-1) * (p-1) * p;
|
||||
}
|
||||
// Family III
|
||||
if (p >= 2)
|
||||
{
|
||||
// Divergence is zero so skip ahead
|
||||
o += (p-1) * (p-1);
|
||||
}
|
||||
// Family IV
|
||||
if (p >= 2)
|
||||
{
|
||||
// Re-using V_Q from Quadrilateral Face
|
||||
phi_E(p, mu01(z), grad_mu01(z), phi_k, dphi_k);
|
||||
|
||||
const real_t muz2 = pow(mu0(z), 2);
|
||||
const Vector dmuz = grad_mu0(z);
|
||||
|
||||
for (int k=2; k<=p; k++)
|
||||
for (int j=0; j<p; j++)
|
||||
for (int i=0; i<p; i++, o++)
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dF(o) += (muz2 * dphi_k(k, l) +
|
||||
2.0 * mu0(z) * phi_k(k) * dmuz(l)) * VQ_ijk(i, j, l);
|
||||
}
|
||||
}
|
||||
// Family V
|
||||
if (p >= 2)
|
||||
{
|
||||
V_L(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu01(z, xy, 2), grad_mu01(z, xy, 2), mu0(z), grad_mu0(z), VL_ijk);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
const Vector dmuz = grad_mu1(z);
|
||||
|
||||
for (int j=2; j<=p; j++)
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const int n = std::max(i, j);
|
||||
const real_t muzi = pow(muz, n-2);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dF(o) += (n-1) * muzi * dmuz(l) * VL_ijk(i, j, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Family VI
|
||||
if (p >= 2)
|
||||
{
|
||||
V_R(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
|
||||
mu1(z, xy, 2), grad_mu1(z, xy, 2), mu0(z), grad_mu0(z), VR_ij);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
const Vector dmuz = grad_mu1(z);
|
||||
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const real_t muzi = pow(muz, i-2);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dF(o) += (i-1) * muzi * dmuz(l) * VR_ij(i, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Family VII
|
||||
if (p >= 2)
|
||||
{
|
||||
V_R(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
|
||||
mu1(z, xy, 1), grad_mu1(z,xy,1), mu0(z), grad_mu0(z), VR_ij);
|
||||
|
||||
const real_t muz = mu1(z);
|
||||
const Vector dmuz = grad_mu1(z);
|
||||
|
||||
for (int i=2; i<=p; i++, o++)
|
||||
{
|
||||
const real_t muzi = pow(muz, i-2);
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
dF(o) += (i-1) * muzi * dmuz(l) * VR_ij(i, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const real_t RT_R1D_SegmentElement::nk[9] = { 1.,0.,0., 0.,1.,0., 0.,0.,1. };
|
||||
|
||||
RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "fe_base.hpp"
|
||||
#include "fe_h1.hpp"
|
||||
#include "fe_l2.hpp"
|
||||
#include "fe_pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -334,6 +335,109 @@ public:
|
||||
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
|
||||
};
|
||||
|
||||
/** Arbitrary order H(Div) basis functions defined on pyramid-shaped elements
|
||||
|
||||
This implementation is closely based on the finite elements
|
||||
described in section 9.3 of the paper "Orientation embedded high
|
||||
order shape functions for the exact sequence elements of all shapes"
|
||||
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
|
||||
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
|
||||
*/
|
||||
class RT_FuentesPyramidElement
|
||||
: public VectorFiniteElement, public FuentesPyramid
|
||||
{
|
||||
private:
|
||||
static const real_t nk[24];
|
||||
|
||||
mutable real_t zmax;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector tmp1_i;
|
||||
mutable DenseMatrix tmp1_ij;
|
||||
mutable DenseMatrix tmp2_ij;
|
||||
mutable DenseMatrix tmp3_ij;
|
||||
mutable DenseMatrix tmp4_ij;
|
||||
mutable DenseTensor tmp1_ijk;
|
||||
mutable DenseTensor tmp2_ijk;
|
||||
mutable DenseTensor tmp3_ijk;
|
||||
mutable DenseTensor tmp4_ijk;
|
||||
mutable DenseTensor tmp5_ijk;
|
||||
mutable DenseTensor tmp6_ijk;
|
||||
mutable DenseTensor tmp7_ijk;
|
||||
mutable DenseMatrix u;
|
||||
mutable Vector divu;
|
||||
#endif
|
||||
Array<int> dof2nk;
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
void calcBasis(const int p, const IntegrationPoint &ip,
|
||||
Vector &phi_k,
|
||||
DenseMatrix &phi_ij,
|
||||
DenseMatrix &dphi_k,
|
||||
DenseTensor &VQ_ijk,
|
||||
DenseTensor &VT_ijk,
|
||||
DenseTensor &VTT_ijk,
|
||||
DenseTensor &E_ijk, DenseTensor &dE_ijk,
|
||||
DenseTensor &dphi_ijk,
|
||||
DenseTensor &VL_ijk,
|
||||
DenseMatrix &VR_ij,
|
||||
DenseMatrix &F) const;
|
||||
|
||||
void calcDivBasis(const int p, const IntegrationPoint &ip,
|
||||
Vector &phi_k,
|
||||
DenseMatrix &phi_ij,
|
||||
DenseMatrix &dphi_k,
|
||||
DenseTensor &VQ_ijk,
|
||||
DenseTensor &VT_ijk,
|
||||
DenseTensor &VTT_ijk, DenseMatrix &dVTT_ij,
|
||||
DenseTensor &E_ijk, DenseTensor &dE_ijk,
|
||||
DenseTensor &dphi_ijk,
|
||||
DenseTensor &VL_ijk,
|
||||
DenseMatrix &VR_ij,
|
||||
Vector &dF) const;
|
||||
|
||||
public:
|
||||
RT_FuentesPyramidElement(const int p);
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_RT(Trans, shape); }
|
||||
virtual void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const;
|
||||
virtual void GetLocalInterpolation(ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_RT(*this, nk, dof2nk, Trans, I); }
|
||||
virtual void GetLocalRestriction(ElementTransformation &Trans,
|
||||
DenseMatrix &R) const
|
||||
{ LocalRestriction_RT(nk, dof2nk, Trans, R); }
|
||||
virtual void GetTransferMatrix(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_RT(CheckVectorFE(fe), nk, dof2nk, Trans, I); }
|
||||
using FiniteElement::Project;
|
||||
virtual void Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const
|
||||
{ Project_RT(nk, dof2nk, vc, Trans, dofs); }
|
||||
virtual void ProjectMatrixCoefficient(
|
||||
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
|
||||
{ ProjectMatrixCoefficient_RT(nk, dof2nk, mc, T, dofs); }
|
||||
virtual void Project(const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ Project_RT(nk, dof2nk, fe, Trans, I); }
|
||||
virtual void ProjectCurl(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const
|
||||
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
|
||||
|
||||
void CalcRawVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
|
||||
void CalcRawDivShape(const IntegrationPoint &ip,
|
||||
Vector &dshape) const;
|
||||
|
||||
real_t GetZetaMax() const { return zmax; }
|
||||
};
|
||||
|
||||
/// Arbitrary order, three component, Raviart-Thomas elements in 1D on a segment
|
||||
/** RT_R1D_SegmentElement provides a representation of a three component
|
||||
|
||||
+64
-56
@@ -106,8 +106,6 @@ int FiniteElementCollection::HasFaceDofs(Geometry::Type geom, int p) const
|
||||
case Geometry::CUBE:
|
||||
return GetNumDof(Geometry::SQUARE, p);
|
||||
case Geometry::PRISM:
|
||||
return max(GetNumDof(Geometry::TRIANGLE, p),
|
||||
GetNumDof(Geometry::SQUARE, p));
|
||||
case Geometry::PYRAMID:
|
||||
return max(GetNumDof(Geometry::TRIANGLE, p),
|
||||
GetNumDof(Geometry::SQUARE, p));
|
||||
@@ -310,6 +308,14 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
|
||||
FiniteElement::INTEGRAL,
|
||||
BasisType::GetType(name[12]));
|
||||
}
|
||||
else if (!strncmp(name, "RT_R1D",6))
|
||||
{
|
||||
fec = new RT_R1D_FECollection(atoi(name+11),atoi(name + 7));
|
||||
}
|
||||
else if (!strncmp(name, "RT_R2D",6))
|
||||
{
|
||||
fec = new RT_R2D_FECollection(atoi(name+11),atoi(name + 7));
|
||||
}
|
||||
else if (!strncmp(name, "RT_", 3))
|
||||
{
|
||||
fec = new RT_FECollection(atoi(name + 7), atoi(name + 3));
|
||||
@@ -330,6 +336,14 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
|
||||
BasisType::GetType(name[9]),
|
||||
BasisType::GetType(name[10]));
|
||||
}
|
||||
else if (!strncmp(name, "ND_R1D",6))
|
||||
{
|
||||
fec = new ND_R1D_FECollection(atoi(name+11),atoi(name + 7));
|
||||
}
|
||||
else if (!strncmp(name, "ND_R2D",6))
|
||||
{
|
||||
fec = new ND_R2D_FECollection(atoi(name+11),atoi(name + 7));
|
||||
}
|
||||
else if (!strncmp(name, "ND_", 3))
|
||||
{
|
||||
fec = new ND_FECollection(atoi(name + 7), atoi(name + 3));
|
||||
@@ -1673,7 +1687,8 @@ const int *RT1_3DFECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
}
|
||||
|
||||
|
||||
H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
|
||||
const int pyrtype)
|
||||
: FiniteElementCollection(p)
|
||||
, dim(dim)
|
||||
{
|
||||
@@ -1874,12 +1889,24 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_dof[Geometry::TETRAHEDRON] = (TriDof*pm3)/3;
|
||||
H1_dof[Geometry::CUBE] = QuadDof*pm1;
|
||||
H1_dof[Geometry::PRISM] = TriDof*pm1;
|
||||
H1_dof[Geometry::PYRAMID] = 0;
|
||||
if (pyrtype == 0 || b_type == BasisType::Positive)
|
||||
{
|
||||
H1_dof[Geometry::PYRAMID] = pm2*pm1*(2*p-3)/6; // Bergot (JSC)
|
||||
}
|
||||
else if (pyrtype == 1)
|
||||
{
|
||||
H1_dof[Geometry::PYRAMID] = pm1*pm1*pm1; // Fuentes
|
||||
}
|
||||
else
|
||||
{
|
||||
H1_dof[Geometry::PYRAMID] = (p-1)*(p-2)/2;
|
||||
}
|
||||
if (b_type == BasisType::Positive)
|
||||
{
|
||||
H1_Elements[Geometry::TETRAHEDRON] = new H1Pos_TetrahedronElement(p);
|
||||
H1_Elements[Geometry::CUBE] = new H1Pos_HexahedronElement(p);
|
||||
H1_Elements[Geometry::PRISM] = new H1Pos_WedgeElement(p);
|
||||
H1_Elements[Geometry::PYRAMID] = new H1Pos_PyramidElement(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1887,8 +1914,15 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
new H1_TetrahedronElement(p, btype);
|
||||
H1_Elements[Geometry::CUBE] = new H1_HexahedronElement(p, btype);
|
||||
H1_Elements[Geometry::PRISM] = new H1_WedgeElement(p, btype);
|
||||
if (pyrtype == 0)
|
||||
{
|
||||
H1_Elements[Geometry::PYRAMID] = new H1_BergotPyramidElement(p, btype);
|
||||
}
|
||||
else
|
||||
{
|
||||
H1_Elements[Geometry::PYRAMID] = new H1_FuentesPyramidElement(p, btype);
|
||||
}
|
||||
}
|
||||
H1_Elements[Geometry::PYRAMID] = new LinearPyramidFiniteElement;
|
||||
|
||||
const int &TetDof = H1_dof[Geometry::TETRAHEDRON];
|
||||
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
|
||||
@@ -1986,17 +2020,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
const FiniteElement *
|
||||
H1_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
|
||||
{
|
||||
return H1_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error_mode == RETURN_NULL) { return nullptr; }
|
||||
MFEM_ABORT("H1 Pyramid basis functions are not yet supported "
|
||||
"for order > 1.");
|
||||
return NULL;
|
||||
}
|
||||
return H1_Elements[GeomType];
|
||||
}
|
||||
|
||||
const int *H1_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
@@ -2099,7 +2123,7 @@ H1_Trace_FECollection::H1_Trace_FECollection(const int p, const int dim,
|
||||
|
||||
|
||||
L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
const int map_type)
|
||||
const int map_type, const int pyr_type)
|
||||
: FiniteElementCollection(p)
|
||||
, dim(dim)
|
||||
, m_type(map_type)
|
||||
@@ -2230,6 +2254,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
L2_Elements[Geometry::TETRAHEDRON] = new L2Pos_TetrahedronElement(p);
|
||||
L2_Elements[Geometry::CUBE] = new L2Pos_HexahedronElement(p);
|
||||
L2_Elements[Geometry::PRISM] = new L2Pos_WedgeElement(p);
|
||||
L2_Elements[Geometry::PYRAMID] = new L2Pos_PyramidElement(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2237,13 +2262,24 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
new L2_TetrahedronElement(p, btype);
|
||||
L2_Elements[Geometry::CUBE] = new L2_HexahedronElement(p, btype);
|
||||
L2_Elements[Geometry::PRISM] = new L2_WedgeElement(p, btype);
|
||||
if (pyr_type == 0)
|
||||
{
|
||||
L2_Elements[Geometry::PYRAMID] = new L2_BergotPyramidElement(p, btype);
|
||||
}
|
||||
else
|
||||
{
|
||||
L2_Elements[Geometry::PYRAMID] = new L2_FuentesPyramidElement(p, btype);
|
||||
}
|
||||
}
|
||||
L2_Elements[Geometry::PYRAMID] = new P0PyrFiniteElement;
|
||||
|
||||
L2_Elements[Geometry::TETRAHEDRON]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::CUBE]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::PRISM]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::PYRAMID]->SetMapType(map_type);
|
||||
if (b_type != BasisType::Positive)
|
||||
{
|
||||
L2_Elements[Geometry::PYRAMID]->SetMapType(map_type);
|
||||
}
|
||||
// Trace element use the default Gauss-Legendre nodal points for positive basis
|
||||
if (b_type == BasisType::Positive)
|
||||
{
|
||||
@@ -2259,7 +2295,9 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
const int TetDof = L2_Elements[Geometry::TETRAHEDRON]->GetDof();
|
||||
const int HexDof = L2_Elements[Geometry::CUBE]->GetDof();
|
||||
const int PriDof = L2_Elements[Geometry::PRISM]->GetDof();
|
||||
const int MaxDof = std::max(TetDof, std::max(PriDof, HexDof));
|
||||
const int PyrDof = L2_Elements[Geometry::PYRAMID]->GetDof();
|
||||
const int MaxDof = std::max(std::max(TetDof, PyrDof),
|
||||
std::max(PriDof, HexDof));
|
||||
|
||||
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
|
||||
for (int i = 1; i < 24; i++)
|
||||
@@ -2367,17 +2405,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
const FiniteElement *
|
||||
L2_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 0)
|
||||
{
|
||||
return L2_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error_mode == RETURN_NULL) { return nullptr; }
|
||||
MFEM_ABORT("L2 Pyramid basis functions are not yet supported "
|
||||
"for order > 0.");
|
||||
return NULL;
|
||||
}
|
||||
return L2_Elements[GeomType];
|
||||
}
|
||||
|
||||
const int *L2_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
@@ -2475,8 +2503,8 @@ RT_FECollection::RT_FECollection(const int order, const int dim,
|
||||
RT_Elements[Geometry::PRISM] = new RT_WedgeElement(p);
|
||||
RT_dof[Geometry::PRISM] = p*pp1*(3*p + 4)/2;
|
||||
|
||||
RT_Elements[Geometry::PYRAMID] = new RT0PyrFiniteElement(false);
|
||||
RT_dof[Geometry::PYRAMID] = 0;
|
||||
RT_Elements[Geometry::PYRAMID] = new RT_FuentesPyramidElement(p);
|
||||
RT_dof[Geometry::PYRAMID] = 3*p*pp1*pp1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2623,17 +2651,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
|
||||
const FiniteElement *
|
||||
RT_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
|
||||
{
|
||||
return RT_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error_mode == RETURN_NULL) { return nullptr; }
|
||||
MFEM_ABORT("RT Pyramid basis functions are not yet supported "
|
||||
"for order > 0.");
|
||||
return NULL;
|
||||
}
|
||||
return RT_Elements[GeomType];
|
||||
}
|
||||
|
||||
const int *RT_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
@@ -2901,25 +2919,15 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
ND_Elements[Geometry::PRISM] = new ND_WedgeElement(p);
|
||||
ND_dof[Geometry::PRISM] = p*pm1*(3*p-4)/2;
|
||||
|
||||
ND_Elements[Geometry::PYRAMID] = new Nedelec1PyrFiniteElement;
|
||||
ND_dof[Geometry::PYRAMID] = 0;
|
||||
ND_Elements[Geometry::PYRAMID] = new ND_FuentesPyramidElement(p);
|
||||
ND_dof[Geometry::PYRAMID] = 3*p*pm1*pm1;
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
ND_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
|
||||
{
|
||||
return ND_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error_mode == RETURN_NULL) { return nullptr; }
|
||||
MFEM_ABORT("ND Pyramid basis functions are not yet supported "
|
||||
"for order > 1.");
|
||||
return NULL;
|
||||
}
|
||||
return ND_Elements[GeomType];
|
||||
}
|
||||
|
||||
const StatelessDofTransformation *
|
||||
|
||||
+8
-2
@@ -119,12 +119,16 @@ public:
|
||||
| ND@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | Nedelec vector elements |
|
||||
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
|
||||
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
|
||||
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
|
||||
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
|
||||
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
|
||||
@@ -278,7 +282,8 @@ protected:
|
||||
|
||||
public:
|
||||
explicit H1_FECollection(const int p, const int dim = 3,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
const int btype = BasisType::GaussLobatto,
|
||||
const int pyrtype = 1);
|
||||
|
||||
const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const override;
|
||||
@@ -354,7 +359,8 @@ private:
|
||||
public:
|
||||
L2_FECollection(const int p, const int dim,
|
||||
const int btype = BasisType::GaussLegendre,
|
||||
const int map_type = FiniteElement::VALUE);
|
||||
const int map_type = FiniteElement::VALUE,
|
||||
const int pyrtype = 1);
|
||||
|
||||
const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const override;
|
||||
|
||||
+568
-128
File diff suppressed because it is too large
Load Diff
+138
-31
@@ -78,7 +78,6 @@ Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
|
||||
return (dof >= 0) ? vd+vdim*dof : -1-(vd+vdim*(-1-dof));
|
||||
}
|
||||
|
||||
|
||||
/// Constants describing the possible orderings of the DOFs in one element.
|
||||
enum class ElementDofOrdering
|
||||
{
|
||||
@@ -87,17 +86,33 @@ enum class ElementDofOrdering
|
||||
interpolation from the DOFs to quadrature points does not use the
|
||||
tensor-product structure. */
|
||||
NATIVE,
|
||||
/// Lexicographic ordering for tensor-product FiniteElements.
|
||||
/** This ordering can be used only with tensor-product elements. */
|
||||
/// Lexicographic: DOFs are listed in order of increasing x-coordinate,
|
||||
/// followed by increasing y-coordinate, and z-coordinate.
|
||||
/** This ordering is usually used with tensor-product elements, but it is
|
||||
also supported by some non-tensor elements. */
|
||||
LEXICOGRAPHIC
|
||||
};
|
||||
|
||||
/** Represents the index of an element to p-refine, plus a change to the order
|
||||
of that element. */
|
||||
struct pRefinement
|
||||
{
|
||||
int index; ///< Mesh element number
|
||||
int delta; ///< Change to element order
|
||||
|
||||
pRefinement() = default;
|
||||
|
||||
pRefinement(int element, int change)
|
||||
: index(element), delta(change) {}
|
||||
};
|
||||
|
||||
// Forward declarations
|
||||
class NURBSExtension;
|
||||
class BilinearFormIntegrator;
|
||||
class QuadratureSpace;
|
||||
class QuadratureInterpolator;
|
||||
class FaceQuadratureInterpolator;
|
||||
class PRefinementTransferOperator;
|
||||
|
||||
|
||||
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
|
||||
@@ -250,22 +265,44 @@ protected:
|
||||
/// Number of degrees of freedom. Number of unknowns is #ndofs * #vdim.
|
||||
int ndofs;
|
||||
|
||||
bool variableOrder = false;
|
||||
|
||||
/** Polynomial order for each element. If empty, all elements are assumed
|
||||
to be of the default order (fec->GetOrder()). */
|
||||
Array<char> elem_order;
|
||||
|
||||
int nvdofs, nedofs, nfdofs, nbdofs;
|
||||
int nvdofs, nedofs, nfdofs, nbdofs, lnedofs, lnfdofs;
|
||||
int uni_fdof; ///< # of single face DOFs if all faces uniform; -1 otherwise
|
||||
int *bdofs; ///< internal DOFs of elements if mixed/var-order; NULL otherwise
|
||||
|
||||
/** Variable order spaces only: DOF assignments for edges and faces, see
|
||||
/** Variable-order spaces only: DOF assignments for edges and faces, see
|
||||
docs in MakeDofTable. For constant order spaces the tables are empty. */
|
||||
Table var_edge_dofs;
|
||||
Table var_face_dofs; ///< NOTE: also used for spaces with mixed faces
|
||||
|
||||
// Temporary data for condensing all DOFs to local DOFs.
|
||||
Table loc_var_edge_dofs, loc_var_face_dofs;
|
||||
|
||||
/** Map from all DOFs of all orders on each entity to local DOFs of orders
|
||||
occurring on a local element containing the entity. */
|
||||
Array<int> all2local;
|
||||
|
||||
/// Bit-mask representing a set of orders needed by an edge/face.
|
||||
typedef std::uint64_t VarOrderBits;
|
||||
static constexpr int MaxVarOrder = 8*sizeof(VarOrderBits) - 1;
|
||||
|
||||
/** Additional data for the var_*_dofs tables: individual variant orders
|
||||
(these are basically alternate J arrays for var_edge/face_dofs). */
|
||||
Array<char> var_edge_orders, var_face_orders;
|
||||
Array<char> loc_var_edge_orders, loc_var_face_orders;
|
||||
Array<char> ghost_edge_orders, ghost_face_orders;
|
||||
|
||||
/// Minimum order among neighboring elements.
|
||||
mutable Array<int> edge_min_nghb_order, face_min_nghb_order;
|
||||
|
||||
/// Marker arrays for ghost master entities to be skipped in conforming
|
||||
/// interpolation constraints.
|
||||
Array<bool> skip_edge, skip_face;
|
||||
|
||||
// precalculated DOFs for each element, boundary element, and face
|
||||
mutable Table *elem_dof; // owned (except in NURBS FE space)
|
||||
@@ -302,9 +339,18 @@ protected:
|
||||
/// Operator computing the action of the transpose of the restriction.
|
||||
mutable std::unique_ptr<Operator> R_transpose;
|
||||
|
||||
/** Stores the previous FiniteElementSpace, before p-refinement, in the case
|
||||
that @a PTh is constructed by PRefineAndUpdate(). */
|
||||
std::unique_ptr<FiniteElementSpace> fesPrev;
|
||||
|
||||
/// Transformation to apply to GridFunctions after space Update().
|
||||
OperatorHandle Th;
|
||||
|
||||
std::shared_ptr<PRefinementTransferOperator> PTh;
|
||||
|
||||
/// Flag to indicate whether the last update was for p-refinement.
|
||||
bool lastUpdatePRef = false;
|
||||
|
||||
/// The element restriction operators, see GetElementRestriction().
|
||||
mutable OperatorHandle L2E_nat, L2E_lex;
|
||||
/// The face restriction operators, see GetFaceRestriction().
|
||||
@@ -341,7 +387,10 @@ protected:
|
||||
|
||||
void UpdateNURBS();
|
||||
|
||||
/** Helper function for constructing the data in this class, for initial
|
||||
construction or updates (e.g. h- or p-refinement). */
|
||||
void Construct();
|
||||
|
||||
void Destroy();
|
||||
|
||||
void ConstructDoFTransArray();
|
||||
@@ -351,6 +400,12 @@ protected:
|
||||
void BuildBdrElementToDofTable() const;
|
||||
void BuildFaceToDofTable() const;
|
||||
|
||||
/** Get all @a edges and @a faces (in 3D) on boundary elements with attribute
|
||||
marked as essential in @a bdr_attr_is_ess. */
|
||||
void GetEssentialBdrEdgesFaces(const Array<int> &bdr_attr_is_ess,
|
||||
std::set<int> & edges,
|
||||
std::set<int> & faces) const;
|
||||
|
||||
/** @brief Initialize internal data that enables the use of the methods
|
||||
GetElementForDof() and GetLocalDofForDof(). */
|
||||
void BuildDofToArrays_() const;
|
||||
@@ -365,9 +420,8 @@ protected:
|
||||
boundary. */
|
||||
void BuildNURBSFaceToDofTable() const;
|
||||
|
||||
/// Bit-mask representing a set of orders needed by an edge/face.
|
||||
typedef std::uint64_t VarOrderBits;
|
||||
static constexpr int MaxVarOrder = 8*sizeof(VarOrderBits) - 1;
|
||||
/// Sets @a all2local. See documentation of @a all2local for details.
|
||||
void SetVarOrderLocalDofs();
|
||||
|
||||
/// Return the minimum order (least significant bit set) in the bit mask.
|
||||
static int MinOrder(VarOrderBits bits);
|
||||
@@ -375,20 +429,52 @@ protected:
|
||||
/// Return element order: internal version of GetElementOrder without checks.
|
||||
int GetElementOrderImpl(int i) const;
|
||||
|
||||
/** In a variable order space, calculate a bitmask of polynomial orders that
|
||||
need to be represented on each edge and face. */
|
||||
void CalcEdgeFaceVarOrders(Array<VarOrderBits> &edge_orders,
|
||||
Array<VarOrderBits> &face_orders) const;
|
||||
/// Returns true if the space is H1 and has variable-order elements.
|
||||
bool IsVariableOrderH1() const
|
||||
{
|
||||
return variableOrder &&
|
||||
dynamic_cast<const H1_FECollection*>(fec);
|
||||
}
|
||||
|
||||
/** Build the table var_edge_dofs (or var_face_dofs) in a variable order
|
||||
space; return total edge/face DOFs. */
|
||||
int MakeDofTable(int ent_dim, const Array<int> &entity_orders,
|
||||
/** In a variable-order space, calculate a bitmask of polynomial orders that
|
||||
need to be represented on each edge and face. */
|
||||
void CalcEdgeFaceVarOrders(
|
||||
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders,
|
||||
Array<VarOrderBits> &edge_elem_orders,
|
||||
Array<VarOrderBits> &face_elem_orders,
|
||||
Array<bool> &skip_edges, Array<bool> &skip_faces) const;
|
||||
|
||||
/// Helper function for ParFiniteElementSpace.
|
||||
virtual void ApplyGhostElementOrdersToEdgesAndFaces(
|
||||
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders) const;
|
||||
|
||||
/// Helper function for ParFiniteElementSpace.
|
||||
virtual void GhostFaceOrderToEdges(
|
||||
const Array<VarOrderBits> &face_orders,
|
||||
Array<VarOrderBits> &edge_orders) const { }
|
||||
|
||||
/// Returns true if order propagation is done, for variable-order spaces.
|
||||
virtual bool OrderPropagation(const std::set<int> &edges,
|
||||
const std::set<int> &faces,
|
||||
Array<VarOrderBits> &edge_orders,
|
||||
Array<VarOrderBits> &face_orders) const
|
||||
{ return edges.size() == 0 && faces.size() == 0; };
|
||||
|
||||
/// Returns the number of ghost edges (nonzero in ParFiniteElementSpace).
|
||||
virtual int NumGhostEdges() const { return 0; }
|
||||
|
||||
/// Returns the number of ghost faces (nonzero in ParFiniteElementSpace).
|
||||
virtual int NumGhostFaces() const { return 0; }
|
||||
|
||||
/** Build the table var_edge_dofs (or var_face_dofs) in a variable-order
|
||||
space; return total edge/face DOFs. */
|
||||
int MakeDofTable(int ent_dim, const Array<VarOrderBits> &entity_orders,
|
||||
Table &entity_dofs, Array<char> *var_ent_order);
|
||||
|
||||
/// Search row of a DOF table for a DOF set of size 'ndof', return first DOF.
|
||||
int FindDofs(const Table &var_dof_table, int row, int ndof) const;
|
||||
|
||||
/** In a variable order space, return edge DOFs associated with a polynomial
|
||||
/** In a variable-order space, return edge DOFs associated with a polynomial
|
||||
order that has 'ndof' degrees of freedom. */
|
||||
int FindEdgeDof(int edge, int ndof) const
|
||||
{ return FindDofs(var_edge_dofs, edge, ndof); }
|
||||
@@ -421,6 +507,11 @@ protected:
|
||||
/// Calculate the cP and cR matrices for a nonconforming mesh.
|
||||
void BuildConformingInterpolation() const;
|
||||
|
||||
/** In variable-order spaces, enforce the minimum order rule on edges and
|
||||
faces, by adding constraints to @a deps for high-order DOFs to
|
||||
interpolate the lowest-order DOFs per mesh entity. */
|
||||
void VariableOrderMinimumRule(SparseMatrix & deps) const;
|
||||
|
||||
static void AddDependencies(SparseMatrix& deps, Array<int>& master_dofs,
|
||||
Array<int>& slave_dofs, DenseMatrix& I,
|
||||
int skipfirst = 0);
|
||||
@@ -571,8 +662,7 @@ public:
|
||||
|
||||
FiniteElementSpace(Mesh *mesh,
|
||||
const FiniteElementCollection *fec,
|
||||
int vdim = 1, int ordering = Ordering::byNODES)
|
||||
{ Constructor(mesh, NULL, fec, vdim, ordering); }
|
||||
int vdim = 1, int ordering = Ordering::byNODES);
|
||||
|
||||
/// Construct a NURBS FE space based on the given NURBSExtension, @a ext.
|
||||
/** @note If the pointer @a ext is NULL, this constructor is equivalent to
|
||||
@@ -580,8 +670,7 @@ public:
|
||||
NURBSExtension, @a ext. */
|
||||
FiniteElementSpace(Mesh *mesh, NURBSExtension *ext,
|
||||
const FiniteElementCollection *fec,
|
||||
int vdim = 1, int ordering = Ordering::byNODES)
|
||||
{ Constructor(mesh, ext, fec, vdim, ordering); }
|
||||
int vdim = 1, int ordering = Ordering::byNODES);
|
||||
|
||||
/// Copy assignment not supported
|
||||
FiniteElementSpace& operator=(const FiniteElementSpace&) = delete;
|
||||
@@ -606,18 +695,18 @@ public:
|
||||
|
||||
/// Sets the order of the i'th finite element.
|
||||
/** By default, all elements are assumed to be of fec->GetOrder(). Once
|
||||
SetElementOrder is called, the space becomes a variable order space. */
|
||||
SetElementOrder is called, the space becomes a variable-order space. */
|
||||
void SetElementOrder(int i, int p);
|
||||
|
||||
/// Returns the order of the i'th finite element.
|
||||
int GetElementOrder(int i) const;
|
||||
|
||||
/// Return the maximum polynomial order.
|
||||
int GetMaxElementOrder() const
|
||||
/// Return the maximum polynomial order over all elements.
|
||||
virtual int GetMaxElementOrder() const
|
||||
{ return IsVariableOrder() ? elem_order.Max() : fec->GetOrder(); }
|
||||
|
||||
/// Returns true if the space contains elements of varying polynomial orders.
|
||||
bool IsVariableOrder() const { return elem_order.Size(); }
|
||||
bool IsVariableOrder() const { return variableOrder; }
|
||||
|
||||
/// The returned SparseMatrix is owned by the FiniteElementSpace. The method
|
||||
/// returns nullptr if the matrix is identity.
|
||||
@@ -739,7 +828,7 @@ public:
|
||||
/** NOTE: it is recommended to use GetElementOrder in new code. */
|
||||
int GetOrder(int i) const { return GetElementOrder(i); }
|
||||
|
||||
/** Return the order of an edge. In a variable order space, return the order
|
||||
/** Return the order of an edge. In a variable-order space, return the order
|
||||
of a specific variant, or -1 if there are no more variants. */
|
||||
int GetEdgeOrder(int edge, int variant = 0) const;
|
||||
|
||||
@@ -906,7 +995,7 @@ public:
|
||||
/// @brief Returns the indices of the degrees of freedom for the specified
|
||||
/// face, including the DOFs for the edges and the vertices of the face.
|
||||
///
|
||||
/// In variable order spaces, multiple variants of DOFs can be returned.
|
||||
/// In variable-order spaces, multiple variants of DOFs can be returned.
|
||||
/// See GetEdgeDofs() for more details.
|
||||
/// @return Order of the selected variant, or -1 if there are no more
|
||||
/// variants.
|
||||
@@ -918,7 +1007,7 @@ public:
|
||||
/// @brief Returns the indices of the degrees of freedom for the specified
|
||||
/// edge, including the DOFs for the vertices of the edge.
|
||||
///
|
||||
/// In variable order spaces, multiple sets of DOFs may exist on an edge,
|
||||
/// In variable-order spaces, multiple sets of DOFs may exist on an edge,
|
||||
/// corresponding to the different polynomial orders of incident elements.
|
||||
/// The 'variant' parameter is the zero-based index of the desired DOF set.
|
||||
/// The variants are ordered from lowest polynomial degree to the highest.
|
||||
@@ -1366,17 +1455,32 @@ public:
|
||||
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
|
||||
OperatorHandle &T) const;
|
||||
|
||||
/** @brief Reflect changes in the mesh: update number of DOFs, etc. Also, calculate
|
||||
GridFunction transformation operator (unless want_transform is false).
|
||||
Safe to call multiple times, does nothing if space already up to date. */
|
||||
/** @brief Reflect changes in the mesh: update number of DOFs, etc. Also,
|
||||
calculate GridFunction transformation operator (unless want_transform is
|
||||
false). Safe to call multiple times, does nothing if space already up to
|
||||
date. */
|
||||
virtual void Update(bool want_transform = true);
|
||||
|
||||
/** P-refine and update the space. If @a want_transfer, also maintain the old
|
||||
space and a transfer operator accessible by GetPrefUpdateOperator(). */
|
||||
virtual void PRefineAndUpdate(const Array<pRefinement> & refs,
|
||||
bool want_transfer = true);
|
||||
|
||||
/** Return true iff p-refinement is supported in this space. Current support
|
||||
is only for L2 or H1 spaces on purely quadrilateral or hexahedral
|
||||
meshes. */
|
||||
bool PRefinementSupported();
|
||||
|
||||
/// Get the GridFunction update operator.
|
||||
const Operator* GetUpdateOperator() { Update(); return Th.Ptr(); }
|
||||
|
||||
/// Return the update operator in the given OperatorHandle, @a T.
|
||||
void GetUpdateOperator(OperatorHandle &T) { T = Th; }
|
||||
|
||||
/** Returns @a PTh, the transfer operator from the previous space to the
|
||||
current space, after p-refinement. */
|
||||
std::shared_ptr<const PRefinementTransferOperator> GetPrefUpdateOperator();
|
||||
|
||||
/** @brief Set the ownership of the update operator: if set to false, the
|
||||
Operator returned by GetUpdateOperator() must be deleted outside the
|
||||
FiniteElementSpace. */
|
||||
@@ -1399,13 +1503,16 @@ public:
|
||||
GridFunction to check if it is up to date with the space. */
|
||||
long GetSequence() const { return sequence; }
|
||||
|
||||
/// Return a flag indicating whether the last update was for p-refinement.
|
||||
bool LastUpdatePRef() const { return lastUpdatePRef; }
|
||||
|
||||
/// Return whether or not the space is discontinuous (L2)
|
||||
bool IsDGSpace() const
|
||||
{
|
||||
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
|
||||
}
|
||||
|
||||
/** In variable order spaces on nonconforming (NC) meshes, this function
|
||||
/** In variable-order spaces on nonconforming (NC) meshes, this function
|
||||
controls whether strict conformity is enforced in cases where coarse
|
||||
edges/faces have higher polynomial order than their fine NC neighbors.
|
||||
In the default (strict) case, the coarse side polynomial order is
|
||||
|
||||
+123
@@ -1667,6 +1667,129 @@ RefinedGeometry *GeometryRefiner::Refine(Geometry::Type Geom, int Times,
|
||||
return RG;
|
||||
}
|
||||
|
||||
const IntegrationRule *GeometryRefiner::EdgeScan(Geometry::Type Geom,
|
||||
int NPts1d)
|
||||
{
|
||||
IntegrationRule *res = nullptr;
|
||||
NPts1d = std::max(NPts1d, 1);
|
||||
std::array<int, 3> key = {Type, static_cast<int>(Geom), NPts1d};
|
||||
auto iter = SGeom.find(key);
|
||||
if (iter == SGeom.end())
|
||||
{
|
||||
// create new scan geometry
|
||||
switch (Geom)
|
||||
{
|
||||
case Geometry::POINT:
|
||||
case Geometry::SEGMENT:
|
||||
{
|
||||
iter = SGeom.emplace(key, new IntegrationRule(1)).first;
|
||||
res = iter->second.get();
|
||||
res->IntPoint(0).x = 0;
|
||||
} break;
|
||||
case Geometry::TRIANGLE:
|
||||
case Geometry::SQUARE:
|
||||
{
|
||||
const real_t *cp =
|
||||
poly1d.GetPoints(NPts1d - 1, BasisType::GetNodalBasis(Type));
|
||||
if (cp[0] == 0)
|
||||
{
|
||||
// don't repeat origin
|
||||
iter = SGeom.emplace(key, new IntegrationRule(2 * NPts1d - 1)).first;
|
||||
res = iter->second.get();
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i).x = cp[i];
|
||||
res->IntPoint(i).y = 0;
|
||||
}
|
||||
for (int i = 1; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i - 1 + NPts1d).x = 0;
|
||||
res->IntPoint(i - 1 + NPts1d).y = cp[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iter = SGeom.emplace(key, new IntegrationRule(2 * NPts1d)).first;
|
||||
res = iter->second.get();
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i).x = cp[i];
|
||||
res->IntPoint(i).y = 0;
|
||||
}
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i + NPts1d).x = 0;
|
||||
res->IntPoint(i + NPts1d).y = cp[i];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Geometry::CUBE:
|
||||
case Geometry::TETRAHEDRON:
|
||||
case Geometry::PYRAMID:
|
||||
case Geometry::PRISM:
|
||||
{
|
||||
const real_t *cp =
|
||||
poly1d.GetPoints(NPts1d - 1, BasisType::GetNodalBasis(Type));
|
||||
if (cp[0] == 0)
|
||||
{
|
||||
// don't repeat origin
|
||||
iter = SGeom.emplace(key, new IntegrationRule(3 * NPts1d - 2)).first;
|
||||
res = iter->second.get();
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i).x = cp[i];
|
||||
res->IntPoint(i).y = 0;
|
||||
res->IntPoint(i).z = 0;
|
||||
}
|
||||
for (int i = 1; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i + NPts1d - 1).x = 0;
|
||||
res->IntPoint(i + NPts1d - 1).y = cp[i];
|
||||
res->IntPoint(i + NPts1d - 1).z = 0;
|
||||
}
|
||||
for (int i = 1; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i + 2 * NPts1d - 2).x = 0;
|
||||
res->IntPoint(i + 2 * NPts1d - 2).y = 0;
|
||||
res->IntPoint(i + 2 * NPts1d - 2).z = cp[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iter = SGeom.emplace(key, new IntegrationRule(3 * NPts1d)).first;
|
||||
res = iter->second.get();
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i).x = cp[i];
|
||||
res->IntPoint(i).y = 0;
|
||||
res->IntPoint(i).z = 0;
|
||||
}
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i + NPts1d).x = 0;
|
||||
res->IntPoint(i + NPts1d).y = cp[i];
|
||||
res->IntPoint(i + NPts1d).z = 0;
|
||||
}
|
||||
for (int i = 0; i < NPts1d; ++i)
|
||||
{
|
||||
res->IntPoint(i + 2 * NPts1d).x = 0;
|
||||
res->IntPoint(i + 2 * NPts1d).y = 0;
|
||||
res->IntPoint(i + 2 * NPts1d).z = cp[i];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Geometry::INVALID:
|
||||
case Geometry::NUM_GEOMETRIES:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = iter->second.get();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const IntegrationRule *GeometryRefiner::RefineInterior(Geometry::Type Geom,
|
||||
int Times)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/densemat.hpp"
|
||||
#include "intrules.hpp"
|
||||
#include "../general/hash.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -327,7 +331,13 @@ class GeometryRefiner
|
||||
{
|
||||
private:
|
||||
int Type; // Quadrature1D type (ClosedUniform is default)
|
||||
/// Cache of RefinedGeometry for Refine
|
||||
Array<RefinedGeometry *> RGeom[Geometry::NumGeom];
|
||||
/// Cache of integration rules for EdgeScan
|
||||
/// key: (type, geom, times)
|
||||
std::unordered_map<std::array<int, 3>, std::unique_ptr<IntegrationRule>,
|
||||
ArrayHasher>
|
||||
SGeom;
|
||||
Array<IntegrationRule *> IntPts[Geometry::NumGeom];
|
||||
|
||||
RefinedGeometry *FindInRGeom(Geometry::Type Geom, int Times,
|
||||
@@ -344,6 +354,9 @@ public:
|
||||
|
||||
RefinedGeometry *Refine(Geometry::Type Geom, int Times, int ETimes = 1);
|
||||
|
||||
/// Get an integration rule which scans along the r/s/t=0 edges of the element.
|
||||
const IntegrationRule *EdgeScan(Geometry::Type Geom, int NPts1d);
|
||||
|
||||
/// @note This method always uses Quadrature1D::OpenUniform points.
|
||||
const IntegrationRule *RefineInterior(Geometry::Type Geom, int Times);
|
||||
|
||||
|
||||
+77
-30
@@ -15,6 +15,7 @@
|
||||
#include "linearform.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
#include "quadinterpolator.hpp"
|
||||
#include "transfer.hpp"
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include "../general/text.hpp"
|
||||
|
||||
@@ -177,23 +178,50 @@ void GridFunction::Update()
|
||||
}*/
|
||||
fes_sequence = fes->GetSequence();
|
||||
|
||||
const Operator *T = fes->GetUpdateOperator();
|
||||
if (T)
|
||||
if (fes->LastUpdatePRef())
|
||||
{
|
||||
Vector old_data;
|
||||
old_data.Swap(*this);
|
||||
SetSize(T->Height());
|
||||
UseDevice(true);
|
||||
T->Mult(old_data, *this);
|
||||
UpdatePRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSize(fes->GetVSize());
|
||||
const Operator *T = fes->GetUpdateOperator();
|
||||
if (T)
|
||||
{
|
||||
Vector old_data;
|
||||
old_data.Swap(*this);
|
||||
SetSize(T->Height());
|
||||
UseDevice(true);
|
||||
T->Mult(old_data, *this);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSize(fes->GetVSize());
|
||||
}
|
||||
}
|
||||
|
||||
if (t_vec.Size() > 0) { SetTrueVector(); }
|
||||
}
|
||||
|
||||
void GridFunction::UpdatePRef()
|
||||
{
|
||||
const std::shared_ptr<const PRefinementTransferOperator> Tp =
|
||||
fes->GetPrefUpdateOperator();
|
||||
if (Tp)
|
||||
{
|
||||
Vector old_data;
|
||||
old_data.Swap(*this);
|
||||
MFEM_VERIFY(Tp->Width() == old_data.Size(),
|
||||
"Wrong size of PRefinementTransferOperator in UpdatePRef");
|
||||
SetSize(Tp->Height());
|
||||
UseDevice(true);
|
||||
Tp->Mult(old_data, *this);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Transfer operator undefined in GridFunction::UpdatePRef");
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::SetSpace(FiniteElementSpace *f)
|
||||
{
|
||||
if (f != fes) { Destroy(); }
|
||||
@@ -1161,31 +1189,30 @@ void GridFunction::GetVectorFieldValues(
|
||||
Array<int> vdofs;
|
||||
ElementTransformation *transf;
|
||||
|
||||
int d, k, n, sdim, dof;
|
||||
|
||||
n = ir.GetNPoints();
|
||||
const int n = ir.GetNPoints();
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
sdim = fes->GetMesh()->SpaceDimension();
|
||||
const int dof = fe->GetDof();
|
||||
const int sdim = fes->GetMesh()->SpaceDimension();
|
||||
const int vdim = std::max(sdim, fe->GetRangeDim());
|
||||
// int *dofs = &vdofs[comp*dof];
|
||||
transf = fes->GetElementTransformation(i);
|
||||
transf->Transform(ir, tr);
|
||||
vals.SetSize(n, sdim);
|
||||
DenseMatrix vshape(dof, sdim);
|
||||
Vector loc_data, val(sdim);
|
||||
vals.SetSize(n, vdim);
|
||||
DenseMatrix vshape(dof, vdim);
|
||||
Vector loc_data, val(vdim);
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
for (k = 0; k < n; k++)
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(k);
|
||||
transf->SetIntPoint(&ip);
|
||||
fe->CalcVShape(*transf, vshape);
|
||||
vshape.MultTranspose(loc_data, val);
|
||||
for (d = 0; d < sdim; d++)
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
vals(k,d) = val(d);
|
||||
}
|
||||
@@ -1254,37 +1281,33 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp)
|
||||
{
|
||||
FiniteElementSpace *new_fes = vec_field.FESpace();
|
||||
|
||||
int d, i, k, ind, dof, sdim;
|
||||
Array<int> overlap(new_fes->GetVSize());
|
||||
Array<int> new_vdofs;
|
||||
DenseMatrix vals, tr;
|
||||
|
||||
sdim = fes->GetMesh()->SpaceDimension();
|
||||
overlap = 0;
|
||||
vec_field = 0.0;
|
||||
|
||||
for (i = 0; i < new_fes->GetNE(); i++)
|
||||
for (int i = 0; i < new_fes->GetNE(); i++)
|
||||
{
|
||||
const FiniteElement *fe = new_fes->GetFE(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
GetVectorFieldValues(i, ir, vals, tr, comp);
|
||||
new_fes->GetElementVDofs(i, new_vdofs);
|
||||
dof = fe->GetDof();
|
||||
for (d = 0; d < sdim; d++)
|
||||
const int dof = fe->GetDof();
|
||||
for (int d = 0; d < vals.Width(); d++)
|
||||
{
|
||||
for (k = 0; k < dof; k++)
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
if ( (ind=new_vdofs[dof*d+k]) < 0 )
|
||||
{
|
||||
ind = -1-ind, vals(k, d) = - vals(k, d);
|
||||
}
|
||||
vec_field(ind) += vals(k, d);
|
||||
real_t s;
|
||||
int ind = FiniteElementSpace::DecodeDof(new_vdofs[dof*d+k], s);
|
||||
vec_field(ind) += s * vals(k, d);
|
||||
overlap[ind]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
vec_field(i) /= overlap[i];
|
||||
}
|
||||
@@ -3975,6 +3998,30 @@ void GridFunction::LegacyNCReorder()
|
||||
Vector::Swap(tmp);
|
||||
}
|
||||
|
||||
std::unique_ptr<GridFunction> GridFunction::ProlongateToMaxOrder() const
|
||||
{
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
const FiniteElementCollection *fesc = fes->FEColl();
|
||||
const int vdim = fes->GetVDim();
|
||||
|
||||
// Find the max order in the space
|
||||
int maxOrder = fes->GetMaxElementOrder();
|
||||
|
||||
// Create a space of maximum order over all elements for output
|
||||
FiniteElementCollection *fecMax = fesc->Clone(maxOrder);
|
||||
FiniteElementSpace *fesMax = new FiniteElementSpace(mesh, fecMax, vdim,
|
||||
fes->GetOrdering());
|
||||
|
||||
GridFunction *xMax = new GridFunction(fesMax);
|
||||
|
||||
// Interpolate in the maximum-order space
|
||||
PRefinementTransferOperator P(*fes, *fesMax);
|
||||
P.Mult(*this, *xMax);
|
||||
|
||||
xMax->MakeOwner(fecMax);
|
||||
return std::unique_ptr<GridFunction>(xMax);
|
||||
}
|
||||
|
||||
real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
GridFunction &u,
|
||||
GridFunction &flux, Vector &error_estimates,
|
||||
|
||||
@@ -433,6 +433,10 @@ public:
|
||||
*/
|
||||
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff, AvgType type);
|
||||
|
||||
/** @brief Return a GridFunction with the values of this, prolongated to the
|
||||
maximum order of all elements in the mesh. */
|
||||
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
|
||||
|
||||
protected:
|
||||
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
|
||||
shared vdofs and counts in how many zones each vdof appears. */
|
||||
@@ -463,6 +467,9 @@ protected:
|
||||
// AccumulateAndCountZones().
|
||||
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
|
||||
|
||||
/// P-refinement version of Update().
|
||||
void UpdatePRef();
|
||||
|
||||
public:
|
||||
/** @brief For each vdof, counts how many elements contain the vdof,
|
||||
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
|
||||
|
||||
+40
-29
@@ -256,7 +256,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
(mesh->GetElementType(0)==Element::QUADRILATERAL ||
|
||||
mesh->GetElementType(0) == Element::HEXAHEDRON));
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MPI_C_BOOL,
|
||||
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
|
||||
MPI_LAND, gsl_comm->c);
|
||||
#endif
|
||||
|
||||
@@ -489,33 +489,34 @@ void FindPointsGSLIB::SetupDevice()
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering)
|
||||
{
|
||||
if (!DEV.setup_device) { SetupDevice(); }
|
||||
if (!DEV.setup_device)
|
||||
{
|
||||
SetupDevice();
|
||||
}
|
||||
DEV.find_device = true;
|
||||
|
||||
const int id = gsl_comm->id,
|
||||
np = gsl_comm->np;
|
||||
const int id = gsl_comm->id, np = gsl_comm->np;
|
||||
|
||||
gsl_mfem_ref.SetSize(points_cnt*dim);
|
||||
gsl_mfem_ref.SetSize(points_cnt * dim);
|
||||
gsl_mfem_elem.SetSize(points_cnt);
|
||||
|
||||
gsl_ref.UseDevice(true);
|
||||
gsl_dist.UseDevice(true);
|
||||
// Initialize arrays for all points (gsl_code is set to not found on device)
|
||||
gsl_ref = -1.0;
|
||||
gsl_mfem_ref = 0.0;
|
||||
gsl_elem = 0;
|
||||
gsl_ref = -1.0;
|
||||
gsl_mfem_ref = 0.0;
|
||||
gsl_elem = 0;
|
||||
gsl_mfem_elem = 0;
|
||||
gsl_proc = id;
|
||||
gsl_proc = id;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
FindPointsLocal2(point_pos, point_pos_ordering,
|
||||
gsl_code, gsl_elem, gsl_ref, gsl_dist, points_cnt);
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPointsLocal3(point_pos, point_pos_ordering,
|
||||
gsl_code, gsl_elem, gsl_ref, gsl_dist, points_cnt);
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
}
|
||||
|
||||
// Sync from device to host
|
||||
@@ -535,28 +536,32 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
// and gsl_code using element type, gsl_mfem_ref, and gsl_dist.
|
||||
for (int index = 0; index < points_cnt; index++)
|
||||
{
|
||||
if (gsl_code[index] == CODE_NOT_FOUND) { continue; }
|
||||
if (gsl_code[index] == CODE_NOT_FOUND)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
gsl_mfem_elem[index] = gsl_elem[index];
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
gsl_mfem_ref(index*dim + d) = 0.5*(gsl_ref(index*dim + d)+1.0);
|
||||
gsl_mfem_ref(index * dim + d) = 0.5 * (gsl_ref(index * dim + d) + 1.0);
|
||||
}
|
||||
IntegrationPoint ip;
|
||||
if (dim == 2)
|
||||
{
|
||||
ip.Set2(gsl_mfem_ref.GetData() + index*dim);
|
||||
ip.Set2(gsl_mfem_ref.GetData() + index * dim);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
ip.Set3(gsl_mfem_ref.GetData() + index*dim);
|
||||
ip.Set3(gsl_mfem_ref.GetData() + index * dim);
|
||||
}
|
||||
const int elem = gsl_elem[index];
|
||||
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(elem);
|
||||
const Geometry::Type gt = fe->GetGeomType(); // assumes quad/hex
|
||||
int setcode = Geometry::CheckPoint(gt, ip, -rbtol) ?
|
||||
CODE_INTERNAL : CODE_BORDER;
|
||||
gsl_code[index] = setcode==CODE_BORDER && gsl_dist(index)>bdr_tol ?
|
||||
CODE_NOT_FOUND : setcode;
|
||||
int setcode =
|
||||
Geometry::CheckPoint(gt, ip, -rbtol) ? CODE_INTERNAL : CODE_BORDER;
|
||||
gsl_code[index] = setcode == CODE_BORDER && gsl_dist(index) > bdr_tol
|
||||
? CODE_NOT_FOUND
|
||||
: setcode;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -709,13 +714,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
FindPointsLocal2(point_pos_l, point_pos_ordering,
|
||||
gsl_code_l, gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
|
||||
FindPointsLocal2(point_pos_l, point_pos_ordering, gsl_code_l,
|
||||
gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPointsLocal3(point_pos_l, point_pos_ordering,
|
||||
gsl_code_l, gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
|
||||
FindPointsLocal3(point_pos_l, point_pos_ordering, gsl_code_l,
|
||||
gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
|
||||
}
|
||||
|
||||
gsl_ref_l.HostRead();
|
||||
@@ -1473,12 +1478,18 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
|
||||
else if (gt == Geometry::SQUARE)
|
||||
{
|
||||
ir_split_temp = ir_split[1];
|
||||
el_to_split = gf_in->FESpace()->IsVariableOrder();
|
||||
// "split" if input mesh is not a tensor basis or has mixed order
|
||||
el_to_split =
|
||||
gf_in->FESpace()->IsVariableOrder() ||
|
||||
dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
|
||||
}
|
||||
else if (gt == Geometry::CUBE)
|
||||
{
|
||||
ir_split_temp = ir_split[0];
|
||||
el_to_split = gf_in->FESpace()->IsVariableOrder();
|
||||
// "split" if input mesh is not a tensor basis or has mixed order
|
||||
el_to_split =
|
||||
gf_in->FESpace()->IsVariableOrder() ||
|
||||
dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1722,7 +1733,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
(mesh->GetElementType(0)==Element::QUADRILATERAL ||
|
||||
mesh->GetElementType(0) == Element::HEXAHEDRON));
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MPI_C_BOOL,
|
||||
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
|
||||
MPI_LAND, gsl_comm->c);
|
||||
#endif
|
||||
|
||||
|
||||
+10
-16
@@ -94,7 +94,7 @@ protected:
|
||||
// Tolerance to ignore points found beyond the mesh boundary.
|
||||
// i.e. if ||x*-x(r)||_2^2 > bdr_tol, we mark point as not found.
|
||||
double bdr_tol;
|
||||
// Use CPU functions for mesh/gridfunction on device for gslib1.0.7
|
||||
// Use CPU functions for Mesh/GridFunction on device for gslib1.0.7
|
||||
bool gpu_to_cpu_fallback = false;
|
||||
|
||||
// Device specific data used for FindPoints
|
||||
@@ -139,22 +139,16 @@ protected:
|
||||
|
||||
// Device functions
|
||||
// FindPoints locally on device for 3D.
|
||||
void FindPointsLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
void FindPointsLocal3(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
// FindPoints locally on device for 2D.
|
||||
void FindPointsLocal2(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
// Interpolate on device for 3D.
|
||||
void InterpolateLocal3(const Vector &field_in,
|
||||
@@ -181,7 +175,7 @@ protected:
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of gridfunction to be interpolated.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
Assumed ordering is NDOFSxVDIMxNEL
|
||||
@param[in] nel Number of elements in the mesh.
|
||||
@param[in] ncomp Number of components in the field.
|
||||
@@ -244,8 +238,8 @@ public:
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12, const int npt_max = 256);
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in Function values that will be interpolated on the
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user