Compare commits
936
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b01646b7a | ||
|
|
8104f3f5aa | ||
|
|
ceb6032407 | ||
|
|
f60dc802ac | ||
|
|
2c878bb1a0 | ||
|
|
70c5482f11 | ||
|
|
ccaab0b1e8 | ||
|
|
8c2f69a39b | ||
|
|
94cb24e0ea | ||
|
|
982f17726b | ||
|
|
ff3014588c | ||
|
|
26b1019d51 | ||
|
|
c465d9f142 | ||
|
|
a474074525 | ||
|
|
fca203a579 | ||
|
|
0fed4cb6a1 | ||
|
|
ffd51d8212 | ||
|
|
59267d1cc4 | ||
|
|
4959a7dc81 | ||
|
|
8fc7f260de | ||
|
|
e4337d10e1 | ||
|
|
f81695df71 | ||
|
|
41b76c0eba | ||
|
|
1825fa356f | ||
|
|
6329213cbb | ||
|
|
a87cc08116 | ||
|
|
8c6c49bd1a | ||
|
|
4987074377 | ||
|
|
b68a2d4929 | ||
|
|
9f945a5863 | ||
|
|
96b35e0067 | ||
|
|
2b3c9ad149 | ||
|
|
62992c0005 | ||
|
|
c004a77cab | ||
|
|
ce93704976 | ||
|
|
95204e94a7 | ||
|
|
c63602dc00 | ||
|
|
5bb00767ef | ||
|
|
000af14c09 | ||
|
|
3f81c6ca26 | ||
|
|
43aa4fccb0 | ||
|
|
6ceeafaa83 | ||
|
|
836a9ec10d | ||
|
|
840bb685ed | ||
|
|
06a0d49a23 | ||
|
|
a3ca3a7b58 | ||
|
|
82b9dc5e32 | ||
|
|
f10d4c175a | ||
|
|
ad99d96067 | ||
|
|
64108af968 | ||
|
|
b3147f05f4 | ||
|
|
6d743d862d | ||
|
|
24be7bca7b | ||
|
|
c21460e89e | ||
|
|
54eea15b77 | ||
|
|
5750f09a1d | ||
|
|
a5e62babfd | ||
|
|
a05fce3e00 | ||
|
|
0270187325 | ||
|
|
f0826d5f8e | ||
|
|
26cc4698f5 | ||
|
|
db2a272a38 | ||
|
|
527f1b9631 | ||
|
|
960d31f17b | ||
|
|
6ae95a8c1f | ||
|
|
34a8c77c43 | ||
|
|
9bb600a87c | ||
|
|
d723d05b8f | ||
|
|
f34f4600b7 | ||
|
|
626284bc29 | ||
|
|
3a80136f70 | ||
|
|
9dbf656433 | ||
|
|
3612f1e978 | ||
|
|
0563180e55 | ||
|
|
fb8c6eb0fe | ||
|
|
3b42070ad0 | ||
|
|
f690d3956a | ||
|
|
7f77e267fd | ||
|
|
a6cb7f98f0 | ||
|
|
e8464c77da | ||
|
|
182f2e16a7 | ||
|
|
f89ef2f04d | ||
|
|
aac94d0f67 | ||
|
|
c96273e7a0 | ||
|
|
ce54c08b50 | ||
|
|
3b506facdc | ||
|
|
01f8360f8f | ||
|
|
12bf4a450d | ||
|
|
74ed3102d2 | ||
|
|
7b083081dc | ||
|
|
2204bc003f | ||
|
|
eec41dd6c0 | ||
|
|
113b446634 | ||
|
|
90a68678f4 | ||
|
|
56ae55580c | ||
|
|
9f2d103b4b | ||
|
|
b9452cf300 | ||
|
|
7e3b442102 | ||
|
|
081635ffd3 | ||
|
|
488927e6c1 | ||
|
|
2d87af4491 | ||
|
|
1fe5baac92 | ||
|
|
4e320842a5 | ||
|
|
922d89dd62 | ||
|
|
ac66d17c67 | ||
|
|
5b9286caec | ||
|
|
c645b1d486 | ||
|
|
11a53d4359 | ||
|
|
31a811ad1c | ||
|
|
aa06e17686 | ||
|
|
839cd667ac | ||
|
|
be968b3f52 | ||
|
|
3537ab556e | ||
|
|
c64cfd544e | ||
|
|
d9c1c34fdf | ||
|
|
97c32689f0 | ||
|
|
58453228f3 | ||
|
|
2366da7257 | ||
|
|
d07af7ce8b | ||
|
|
3db3013271 | ||
|
|
9c08ccf864 | ||
|
|
b38fa26468 | ||
|
|
5330247ae8 | ||
|
|
f9c67d7a9e | ||
|
|
713f178392 | ||
|
|
c0ce07fb2d | ||
|
|
71f7398979 | ||
|
|
943461a328 | ||
|
|
9c27d41fbe | ||
|
|
1909fcc489 | ||
|
|
754cc7a4f8 | ||
|
|
5b57ce88dd | ||
|
|
4807e96c73 | ||
|
|
379aafca6c | ||
|
|
1f91051e55 | ||
|
|
b321442fab | ||
|
|
171fafc021 | ||
|
|
3aa19b7a3f | ||
|
|
019da753cd | ||
|
|
9359f0b10a | ||
|
|
0524c5b012 | ||
|
|
e1a18c2868 | ||
|
|
a700036428 | ||
|
|
c9e6c6e74a | ||
|
|
0dfe9eaff8 | ||
|
|
dc578cc8d3 | ||
|
|
c0444b07f5 | ||
|
|
cc2c00946d | ||
|
|
18ff1d4772 | ||
|
|
a447d0bfbd | ||
|
|
1318146a27 | ||
|
|
22dffb6326 | ||
|
|
ce19020781 | ||
|
|
a017191010 | ||
|
|
b02893686a | ||
|
|
008ac526d1 | ||
|
|
22fe453fda | ||
|
|
4d39303ad9 | ||
|
|
a87dadb8b3 | ||
|
|
cb78abf671 | ||
|
|
5e32ae87b4 | ||
|
|
1963a63075 | ||
|
|
6e36cbfb5f | ||
|
|
253f578ae4 | ||
|
|
52ff1fdbaf | ||
|
|
ac53af01ef | ||
|
|
5f5b15b5f6 | ||
|
|
d17263e7e5 | ||
|
|
2f852723f8 | ||
|
|
e41811ad7f | ||
|
|
37034f28a6 | ||
|
|
43f8ea13dd | ||
|
|
c5363ebaaf | ||
|
|
c11d9acab8 | ||
|
|
f0525f62e3 | ||
|
|
ae478a45cd | ||
|
|
9e66156622 | ||
|
|
09df072897 | ||
|
|
ff9467c07d | ||
|
|
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 | ||
|
|
8c85525be5 | ||
|
|
8a15086e1a | ||
|
|
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 | ||
|
|
4c8174c71c | ||
|
|
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 | ||
|
|
6e4b512b41 | ||
|
|
26ba53a52e | ||
|
|
2cde33b8dd | ||
|
|
e2815c1864 | ||
|
|
0f1880a9da | ||
|
|
25cfc0991d | ||
|
|
cbd035e06d | ||
|
|
242703e266 | ||
|
|
7ced559b0d | ||
|
|
06d1cd025f | ||
|
|
40f8216355 | ||
|
|
fc8ee486b6 | ||
|
|
3dae2a01dc | ||
|
|
3b5068ca79 | ||
|
|
5b145be17d | ||
|
|
76f382eba6 | ||
|
|
6f9cbed618 | ||
|
|
aab40b23d2 | ||
|
|
b211337360 | ||
|
|
6c40c5b643 | ||
|
|
1aa9e65cfb | ||
|
|
e05523f36e | ||
|
|
090cdf3135 | ||
|
|
dc162a194d | ||
|
|
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 | ||
|
|
1043d3db04 | ||
|
|
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 | ||
|
|
ed49e3eabc | ||
|
|
a7efe17b0b | ||
|
|
a0d2d4ab3d | ||
|
|
0a08801004 | ||
|
|
4af565819e | ||
|
|
bcd5ef92c6 | ||
|
|
bb271475ea | ||
|
|
97f8b1b16b | ||
|
|
488e93ad74 | ||
|
|
d448fd0811 | ||
|
|
c0fee4482a | ||
|
|
098bb72f34 | ||
|
|
8a6d972814 | ||
|
|
5706c247a4 | ||
|
|
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 | ||
|
|
2be6e5a074 | ||
|
|
278b33942a | ||
|
|
080163deac | ||
|
|
d398bac010 | ||
|
|
c5be56264c | ||
|
|
d3c669d482 | ||
|
|
270f83bd35 | ||
|
|
e327a173da | ||
|
|
e6c48e3987 | ||
|
|
c9ad580d33 | ||
|
|
040c337ce7 | ||
|
|
1d46fbba57 | ||
|
|
4804824873 | ||
|
|
1a2b8291f5 | ||
|
|
8f3eafd251 | ||
|
|
559e4e20a6 | ||
|
|
3ec7ae873d | ||
|
|
1acaa833b6 | ||
|
|
a07f893b03 | ||
|
|
f0f1885090 | ||
|
|
42d0c95bc8 | ||
|
|
947c8c9be9 | ||
|
|
cf82f2830c | ||
|
|
cad2293df6 | ||
|
|
604cb61b00 | ||
|
|
ff6546dd8f | ||
|
|
10c9adf8c8 | ||
|
|
786caf20c3 | ||
|
|
4430df415a | ||
|
|
5acdaf28a5 | ||
|
|
c416ac9fff | ||
|
|
48e6b88370 | ||
|
|
a00d2610e3 | ||
|
|
bc9e624ce8 | ||
|
|
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'
|
||||
|
||||
@@ -45,14 +45,15 @@ jobs:
|
||||
|
||||
- name: Get MPI (Linux)
|
||||
run: |
|
||||
sudo apt-get install mpich libmpich-dev
|
||||
sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
export OMPI_MCA_rmaps_base_oversubscribe=1
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -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*
|
||||
@@ -274,8 +281,10 @@ miniapps/navier/navier_shear
|
||||
miniapps/navier/navier_3dfoc
|
||||
miniapps/navier/navier_turbchan
|
||||
miniapps/navier/navier_cht
|
||||
miniapps/navier/navier_windtunnel
|
||||
miniapps/navier/tgv_out*.txt
|
||||
miniapps/navier/*_output
|
||||
miniapps/navier/inputs/
|
||||
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
|
||||
@@ -8,159 +8,225 @@
|
||||
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".
|
||||
- Added support for variational resampling of H1 vector fields to ParMoonolith
|
||||
integration.
|
||||
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential boundary
|
||||
conditions. A new function Vector::SetSubVectorHost has been added in cases
|
||||
where host execution is always needed (e.g. when the DOFs array is small).
|
||||
|
||||
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 +251,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 +310,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 +323,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
|
||||
===========================================
|
||||
|
||||
+30
-24
@@ -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})
|
||||
@@ -387,6 +388,11 @@ if (MFEM_USE_GSLIB)
|
||||
find_package(GSLIB REQUIRED)
|
||||
endif()
|
||||
|
||||
# HDF5
|
||||
if (MFEM_USE_HDF5)
|
||||
find_package(HDF5 REQUIRED)
|
||||
endif()
|
||||
|
||||
# NetCDF
|
||||
if (MFEM_USE_NETCDF)
|
||||
find_package(NetCDF REQUIRED)
|
||||
@@ -568,7 +574,7 @@ find_package(Threads REQUIRED)
|
||||
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
|
||||
# be before SuiteSparse.
|
||||
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
|
||||
@@ -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):
|
||||
@@ -418,6 +423,10 @@ MFEM_USE_GNUTLS = YES/NO
|
||||
When MFEM_USE_GNUTLS is enabled, the additional build options, GNUTLS_*, are
|
||||
also used, see below.
|
||||
|
||||
MFEM_USE_HDF5 = YES/NO
|
||||
The HDF5 library is used for input and output of HDF5 files, for example
|
||||
Cubit mesh files or VTKHDF files for ParaView.
|
||||
|
||||
MFEM_USE_NETCDF = YES/NO
|
||||
NetCDF is the library that is used by the SNL Cubit mesh generator to create
|
||||
Genesis mesh files. This option enables a reader for these files, which
|
||||
@@ -728,6 +737,9 @@ The specific libraries and their options are:
|
||||
Options: GNUTLS_OPT, GNUTLS_LIB.
|
||||
Versions: GnuTLS >= 2.12.0, older versions may work too.
|
||||
|
||||
- HDF5 (optional), used when MFEM_USE_HDF5 = YES, required for reading and
|
||||
writing files in VTKHDF format.
|
||||
|
||||
- NetCDF (optional), used when MFEM_USE_NETCDF = YES, required for reading Cubit
|
||||
mesh files. Also requires installation of HDF5 and ZLIB, as explained at the
|
||||
NetCDF web site. Note that we use the plain vanilla "C" version of NetCDF, you
|
||||
@@ -1002,7 +1014,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
|
||||
@@ -1033,6 +1045,7 @@ MFEM_USE_STRUMPACK
|
||||
MFEM_USE_GINKGO
|
||||
MFEM_USE_AMGX
|
||||
MFEM_USE_GNUTLS
|
||||
MFEM_USE_HDF5
|
||||
MFEM_USE_NETCDF
|
||||
MFEM_USE_MPFR
|
||||
MFEM_USE_ZLIB
|
||||
@@ -1161,6 +1174,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.
|
||||
|
||||
@@ -41,6 +41,7 @@ set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
set(MFEM_USE_HIOP @MFEM_USE_HIOP@)
|
||||
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
|
||||
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
|
||||
set(MFEM_USE_HDF5 @MFEM_USE_HDF5@)
|
||||
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
|
||||
set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
|
||||
set(MFEM_USE_SLEPC @MFEM_USE_SLEPC@)
|
||||
|
||||
@@ -132,6 +132,9 @@
|
||||
// Enable Conduit support.
|
||||
#cmakedefine MFEM_USE_CONDUIT
|
||||
|
||||
// Enable functionality based on the HDF5 library (reading VTKHDF files).
|
||||
#cmakedefine MFEM_USE_HDF5
|
||||
|
||||
// Enable functionality based on the NetCDF library (reading CUBIT files).
|
||||
#cmakedefine MFEM_USE_NETCDF
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -869,7 +877,8 @@ function(mfem_export_mk_files)
|
||||
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
|
||||
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
|
||||
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
|
||||
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
|
||||
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME
|
||||
MFEM_USE_HDF5)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
|
||||
@@ -93,17 +93,17 @@ macro (RESOLVE_LIBRARIES LIBS LINK_LINE)
|
||||
set (_directory_list ${_directory_list} ${libpath})
|
||||
set (token ${libname})
|
||||
endif (token MATCHES "^/")
|
||||
set (_lib "NOTFOUND" CACHE FILEPATH "Cleared" FORCE)
|
||||
set (_lib "NOTFOUND")
|
||||
find_library (_lib ${token} HINTS ${_directory_list} ${_root})
|
||||
if (_lib)
|
||||
string (REPLACE "//" "/" _lib ${_lib})
|
||||
string (REPLACE "//" "/" _lib ${_lib})
|
||||
list (APPEND _libs_found ${_lib})
|
||||
else (_lib)
|
||||
message (STATUS "Unable to find library ${token}")
|
||||
endif (_lib)
|
||||
unset(_lib CACHE)
|
||||
endif (token MATCHES "-L([^\" ]+|\"[^\"]+\")")
|
||||
endforeach (token)
|
||||
set (_lib "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE)
|
||||
# only the LAST occurrence of each library is required since there should be no circular dependencies
|
||||
if (_libs_found)
|
||||
list (REVERSE _libs_found)
|
||||
|
||||
@@ -132,6 +132,9 @@
|
||||
// Enable Conduit support.
|
||||
// #define MFEM_USE_CONDUIT
|
||||
|
||||
// Enable functionality based on the HDF5 library
|
||||
// #define MFEM_USE_HDF5
|
||||
|
||||
// Enable functionality based on the NetCDF library (reading CUBIT files).
|
||||
// #define MFEM_USE_NETCDF
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
|
||||
MFEM_USE_HDF5 = @MFEM_USE_HDF5@
|
||||
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
|
||||
MFEM_USE_PETSC = @MFEM_USE_PETSC@
|
||||
MFEM_USE_SLEPC = @MFEM_USE_SLEPC@
|
||||
|
||||
@@ -43,6 +43,7 @@ option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
|
||||
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
|
||||
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
|
||||
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
|
||||
option(MFEM_USE_HDF5 "Enable HDF5 usage" OFF)
|
||||
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
|
||||
option(MFEM_USE_PETSC "Enable PETSc support." OFF)
|
||||
option(MFEM_USE_SLEPC "Enable SLEPc support." OFF)
|
||||
|
||||
+14
-4
@@ -145,6 +145,7 @@ MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
MFEM_USE_GNUTLS = NO
|
||||
MFEM_USE_HDF5 = NO
|
||||
MFEM_USE_NETCDF = NO
|
||||
MFEM_USE_PETSC = NO
|
||||
MFEM_USE_SLEPC = NO
|
||||
@@ -401,9 +402,14 @@ MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
|
||||
GNUTLS_OPT =
|
||||
GNUTLS_LIB = -lgnutls
|
||||
|
||||
# HDF5 library configuration
|
||||
HDF5_DIR = $(HOME)/local
|
||||
HDF5_OPT = -I$(HDF5_DIR)/include
|
||||
HDF5_LIB = $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib -lhdf5_hl -lhdf5 \
|
||||
$(ZLIB_LIB)
|
||||
|
||||
# NetCDF library configuration
|
||||
NETCDF_DIR = $(HOME)/local
|
||||
HDF5_DIR = $(HOME)/local
|
||||
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
|
||||
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
|
||||
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
|
||||
@@ -484,8 +490,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 +574,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 ?=
|
||||
|
||||
@@ -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
|
||||
|
||||
+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",
|
||||
|
||||
+1
-1
@@ -189,7 +189,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
|
||||
|
||||
@@ -16,10 +16,16 @@
|
||||
// multi-physics applications.
|
||||
//
|
||||
// This particular example is only for serial runtimes.
|
||||
// For non-conforming meshes please have a look at example
|
||||
// "ex2p.cpp".
|
||||
|
||||
#include "example_utils.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifndef MFEM_USE_MOONOLITH
|
||||
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
|
||||
#endif
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
@@ -215,8 +221,8 @@ int main(int argc, char *argv[])
|
||||
mfem::out << "l2 error: src: " << src_err << ", dest: " << dest_err
|
||||
<< std::endl;
|
||||
|
||||
plot(*src_mesh, src_fun, "source");
|
||||
plot(*dest_mesh, dest_fun, "destination");
|
||||
plot(*src_mesh, src_fun, "source", 0);
|
||||
plot(*dest_mesh, dest_fun, "destination", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+54
-16
@@ -8,18 +8,23 @@
|
||||
// mpirun -np 4 ex1p -s ../../data/inline-hex.mesh -d ../../data/inline-tet.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM for transferring
|
||||
// discrete fields from one finite element mesh to another. The
|
||||
// discrete fields from one conforming finite element mesh to another. The
|
||||
// meshes can be of arbitrary shape and completely unrelated with
|
||||
// each other. This feature can be used for implementing immersed
|
||||
// domain methods for fluid-structure interaction or general
|
||||
// multi-physics applications.
|
||||
//
|
||||
// This particular example is for parallel runtimes. Vector FE is
|
||||
// an experimental feature in parallel.
|
||||
// an experimental feature in parallel. For non-conforming meshes
|
||||
// please have a look at example "ex2p.cpp".
|
||||
|
||||
#include "example_utils.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifndef MFEM_USE_MOONOLITH
|
||||
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
|
||||
#endif
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
@@ -50,6 +55,8 @@ int main(int argc, char *argv[])
|
||||
int dest_fe_order = 1;
|
||||
bool visualization = true;
|
||||
bool use_vector_fe = false;
|
||||
bool use_h1 = true;
|
||||
bool use_vector_space = false;
|
||||
bool verbose = false;
|
||||
bool assemble_mass_and_coupling_together = true;
|
||||
|
||||
@@ -72,14 +79,28 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&verbose, "-verb", "--verbose", "--no-verb", "--no-verbose",
|
||||
"Enable/Disable verbose output");
|
||||
args.AddOption(&use_vector_fe, "-vfe", "--use_vector_fe", "-no-vfe",
|
||||
"--no-vector_fe", "Use vector finite elements (Experimental)");
|
||||
"--no-vector_fe",
|
||||
"Use RT|ND vector finite elements (Experimental)");
|
||||
args.AddOption(&use_vector_space, "-vfs", "--use_vector_space", "-no-vfs",
|
||||
"--no-vector_space",
|
||||
"Use Lagrange vector finite elements (Experimental)");
|
||||
args.AddOption(&use_h1, "-h1", "--use-h1", "-nh1", "--no-h1",
|
||||
"Use H1 collection");
|
||||
args.AddOption(&assemble_mass_and_coupling_together, "-act",
|
||||
"--assemble_mass_and_coupling_together", "-no-act",
|
||||
"--no-assemble_mass_and_coupling_together",
|
||||
"Assemble mass and coupling operators together (better for non-affine elements)");
|
||||
"Assemble mass and coupling operators together (better for "
|
||||
"non-affine elements)");
|
||||
args.Parse();
|
||||
check_options(args);
|
||||
|
||||
if (use_vector_fe && use_vector_space)
|
||||
{
|
||||
mfem::err <<
|
||||
"WARNING: use_vector_fe and use_vector_space options"
|
||||
"are both true, ignoring use_vector_fe\n";
|
||||
}
|
||||
|
||||
shared_ptr<Mesh> src_mesh, dest_mesh;
|
||||
|
||||
ifstream imesh;
|
||||
@@ -169,17 +190,30 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
src_fe_coll =
|
||||
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
|
||||
dest_fe_coll =
|
||||
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
|
||||
|
||||
if (use_h1)
|
||||
{
|
||||
src_fe_coll =
|
||||
make_shared<H1_FECollection>(source_fe_order, src_mesh->Dimension());
|
||||
dest_fe_coll =
|
||||
make_shared<H1_FECollection>(dest_fe_order, dest_mesh->Dimension());
|
||||
}
|
||||
else
|
||||
{
|
||||
src_fe_coll =
|
||||
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
|
||||
dest_fe_coll =
|
||||
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
|
||||
}
|
||||
}
|
||||
|
||||
auto src_fe =
|
||||
make_shared<ParFiniteElementSpace>(p_src_mesh.get(), src_fe_coll.get());
|
||||
auto src_fe = make_shared<ParFiniteElementSpace>(
|
||||
p_src_mesh.get(), src_fe_coll.get(),
|
||||
use_vector_space ? src_mesh->Dimension() : 1);
|
||||
|
||||
auto dest_fe =
|
||||
make_shared<ParFiniteElementSpace>(p_dest_mesh.get(), dest_fe_coll.get());
|
||||
auto dest_fe = make_shared<ParFiniteElementSpace>(
|
||||
p_dest_mesh.get(), dest_fe_coll.get(),
|
||||
use_vector_space ? dest_mesh->Dimension() : 1);
|
||||
|
||||
ParGridFunction src_fun(src_fe.get());
|
||||
|
||||
@@ -189,7 +223,7 @@ int main(int argc, char *argv[])
|
||||
// To be used with vector fe
|
||||
VectorFunctionCoefficient vector_coeff(dim, &vector_fun);
|
||||
|
||||
if (use_vector_fe)
|
||||
if (use_vector_fe || use_vector_space)
|
||||
{
|
||||
src_fun.ProjectCoefficient(vector_coeff);
|
||||
src_fun.Update();
|
||||
@@ -209,7 +243,11 @@ int main(int argc, char *argv[])
|
||||
assemble_mass_and_coupling_together);
|
||||
assembler.SetVerbose(verbose);
|
||||
|
||||
if (use_vector_fe)
|
||||
if (use_vector_space)
|
||||
{
|
||||
assembler.AddMortarIntegrator(make_shared<LagrangeVectorL2MortarIntegrator>());
|
||||
}
|
||||
else if (use_vector_fe)
|
||||
{
|
||||
assembler.AddMortarIntegrator(make_shared<VectorL2MortarIntegrator>());
|
||||
}
|
||||
@@ -243,8 +281,8 @@ int main(int argc, char *argv[])
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
plot(*p_src_mesh, src_fun, "source");
|
||||
plot(*p_dest_mesh, dest_fun, "destination");
|
||||
plot(*p_src_mesh, src_fun, "source", 0);
|
||||
plot(*p_dest_mesh, dest_fun, "destination", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "example_utils.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifndef MFEM_USE_MOONOLITH
|
||||
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
|
||||
#endif
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
@@ -186,8 +190,8 @@ int main(int argc, char *argv[])
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
plot(*p_src_mesh, src_fun, "source");
|
||||
plot(*p_dest_mesh, dest_fun, "destination");
|
||||
plot(*p_src_mesh, src_fun, "source", 0);
|
||||
plot(*p_dest_mesh, dest_fun, "destination", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -84,7 +84,8 @@ void vector_fun(const mfem::Vector &x, mfem::Vector &f)
|
||||
f = n;
|
||||
}
|
||||
|
||||
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title)
|
||||
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title,
|
||||
const int plot_number = 0)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
@@ -103,5 +104,18 @@ inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title)
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x
|
||||
<< "window_title '"<< title << "'\n" << flush;
|
||||
|
||||
sol_sock << "window_geometry ";
|
||||
sol_sock << (plot_number * 600) << " " << 0 << " " << 600 << " " << 600 <<
|
||||
"\n";
|
||||
|
||||
if (mesh.Dimension() == 2)
|
||||
{
|
||||
sol_sock << "keys jRmclA\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sol_sock << "keys rmclAa\n";
|
||||
}
|
||||
sol_sock << flush;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -186,6 +191,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
|
||||
@@ -194,6 +200,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 SetAssemblyLevel(), 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 SetAssemblyLevel(), 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
|
||||
|
||||
+7
-2
@@ -240,7 +240,9 @@ public:
|
||||
Vector argument instead of Vector. */
|
||||
MFEM_DEPRECATED FunctionCoefficient(real_t (*f)(Vector &))
|
||||
{
|
||||
Function = reinterpret_cast<real_t(*)(const Vector&)>(f);
|
||||
// Cast first to (void*) to suppress a warning from newer version of
|
||||
// Clang when using -Wextra.
|
||||
Function = reinterpret_cast<real_t(*)(const Vector&)>((void*)f);
|
||||
TDFunction = NULL;
|
||||
}
|
||||
|
||||
@@ -250,7 +252,10 @@ public:
|
||||
MFEM_DEPRECATED FunctionCoefficient(real_t (*tdf)(Vector &, real_t))
|
||||
{
|
||||
Function = NULL;
|
||||
TDFunction = reinterpret_cast<real_t(*)(const Vector&,real_t)>(tdf);
|
||||
// Cast first to (void*) to suppress a warning from newer version of
|
||||
// Clang when using -Wextra.
|
||||
TDFunction =
|
||||
reinterpret_cast<real_t(*)(const Vector&,real_t)>((void*)tdf);
|
||||
}
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
|
||||
+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 )
|
||||
{
|
||||
|
||||
+118
-52
@@ -12,6 +12,7 @@
|
||||
#include "fem.hpp"
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include "../mesh/vtk.hpp"
|
||||
#include "../mesh/vtkhdf.hpp"
|
||||
#include "../general/binaryio.hpp"
|
||||
#include "../general/text.hpp"
|
||||
#include "picojson.h"
|
||||
@@ -758,18 +759,10 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
|
||||
}
|
||||
}
|
||||
|
||||
ParaViewDataCollection::ParaViewDataCollection(const std::string&
|
||||
collection_name,
|
||||
Mesh *mesh_)
|
||||
: DataCollection(collection_name, mesh_),
|
||||
levels_of_detail(1),
|
||||
pv_data_format(VTKFormat::BINARY),
|
||||
high_order_output(false),
|
||||
restart_mode(false)
|
||||
ParaViewDataCollectionBase::ParaViewDataCollectionBase(
|
||||
const std::string &name, Mesh *mesh) : DataCollection(name, mesh)
|
||||
{
|
||||
cycle = 0; // always include a valid cycle index in file names
|
||||
|
||||
compression_level = -1; // default zlib compression level, equivalent to 6
|
||||
cycle = 0;
|
||||
#ifdef MFEM_USE_ZLIB
|
||||
compression = true; // if we have zlib, enable compression
|
||||
#else
|
||||
@@ -777,16 +770,53 @@ ParaViewDataCollection::ParaViewDataCollection(const std::string&
|
||||
#endif
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
|
||||
void ParaViewDataCollectionBase::SetLevelsOfDetail(int levels_of_detail_)
|
||||
{
|
||||
levels_of_detail = levels_of_detail_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::Load(int )
|
||||
void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
|
||||
{
|
||||
MFEM_WARNING("ParaViewDataCollection::Load() is not implemented!");
|
||||
high_order_output = high_order_output_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
|
||||
{
|
||||
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
|
||||
"Compression level must be between -1 and 9 (inclusive).");
|
||||
compression_level = compression_level_;
|
||||
compression = compression_level_ != 0;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
int ParaViewDataCollectionBase::GetCompressionLevel() const
|
||||
{
|
||||
return compression ? compression_level : 0;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetDataFormat(VTKFormat fmt)
|
||||
{
|
||||
pv_data_format = fmt;
|
||||
}
|
||||
|
||||
bool ParaViewDataCollectionBase::IsBinaryFormat() const
|
||||
{
|
||||
return pv_data_format != VTKFormat::ASCII;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::UseRestartMode(bool restart_mode_)
|
||||
{
|
||||
restart_mode = restart_mode_;
|
||||
}
|
||||
|
||||
ParaViewDataCollection::ParaViewDataCollection(
|
||||
const std::string& collection_name, Mesh *mesh_)
|
||||
: ParaViewDataCollectionBase(collection_name, mesh_) { }
|
||||
|
||||
std::string ParaViewDataCollection::GenerateCollectionPath()
|
||||
{
|
||||
return prefix_path + DataCollection::GetCollectionName();
|
||||
@@ -901,7 +931,7 @@ void ParaViewDataCollection::Save()
|
||||
// Initialize new pvd file.
|
||||
pvd_stream.open(pvdname,std::ios::out|std::ios::trunc);
|
||||
pvd_stream << "<?xml version=\"1.0\"?>\n";
|
||||
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
|
||||
pvd_stream << "<VTKFile type=\"Collection\" version=\"2.2\"";
|
||||
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
pvd_stream << "<Collection>" << std::endl;
|
||||
}
|
||||
@@ -1001,7 +1031,7 @@ void ParaViewDataCollection::WritePVTUHeader(std::ostream &os)
|
||||
{
|
||||
os << "<?xml version=\"1.0\"?>\n";
|
||||
os << "<VTKFile type=\"PUnstructuredGrid\"";
|
||||
os << " version =\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
os << " version =\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
|
||||
|
||||
os << "<PPoints>\n";
|
||||
@@ -1042,7 +1072,7 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
|
||||
{
|
||||
os << " compressor=\"vtkZLibDataCompressor\"";
|
||||
}
|
||||
os << " version=\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
os << " version=\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
os << "<UnstructuredGrid>\n";
|
||||
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel());
|
||||
|
||||
@@ -1115,39 +1145,6 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
|
||||
os << "</DataArray>" << std::endl;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SetDataFormat(VTKFormat fmt)
|
||||
{
|
||||
pv_data_format = fmt;
|
||||
}
|
||||
|
||||
bool ParaViewDataCollection::IsBinaryFormat() const
|
||||
{
|
||||
return pv_data_format != VTKFormat::ASCII;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SetHighOrderOutput(bool high_order_output_)
|
||||
{
|
||||
high_order_output = high_order_output_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SetCompressionLevel(int compression_level_)
|
||||
{
|
||||
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
|
||||
"Compression level must be between -1 and 9 (inclusive).");
|
||||
compression_level = compression_level_;
|
||||
compression = compression_level_ != 0;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::UseRestartMode(bool restart_mode_)
|
||||
{
|
||||
restart_mode = restart_mode_;
|
||||
}
|
||||
|
||||
const char *ParaViewDataCollection::GetDataFormatString() const
|
||||
{
|
||||
if (pv_data_format == VTKFormat::ASCII)
|
||||
@@ -1172,9 +1169,78 @@ const char *ParaViewDataCollection::GetDataTypeString() const
|
||||
}
|
||||
}
|
||||
|
||||
int ParaViewDataCollection::GetCompressionLevel() const
|
||||
#ifdef MFEM_USE_HDF5
|
||||
|
||||
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
|
||||
const std::string &collection_name, Mesh *mesh)
|
||||
: ParaViewDataCollectionBase(collection_name, mesh)
|
||||
{ }
|
||||
|
||||
void ParaViewHDFDataCollection::EnsureVTKHDF()
|
||||
{
|
||||
return compression ? compression_level : 0;
|
||||
if (!vtkhdf)
|
||||
{
|
||||
if (!prefix_path.empty())
|
||||
{
|
||||
const int error_code = create_directory(prefix_path, mesh, myid);
|
||||
MFEM_VERIFY(error_code == 0, "Error creating directory " << prefix_path);
|
||||
}
|
||||
|
||||
std::string fname = prefix_path + name + ".vtkhdf";
|
||||
bool use_mpi = false;
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ParMesh *pmesh = dynamic_cast<ParMesh*>(mesh))
|
||||
{
|
||||
use_mpi = true;
|
||||
#ifdef MFEM_PARALLEL_HDF5
|
||||
vtkhdf.reset(new VTKHDF(fname, pmesh->GetComm(), {restart_mode, time}));
|
||||
#else
|
||||
MFEM_ABORT("Requires HDF5 library with parallel support enabled");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!use_mpi)
|
||||
{
|
||||
vtkhdf.reset(new VTKHDF(fname, {restart_mode, time}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FP_T>
|
||||
void ParaViewHDFDataCollection::TSave()
|
||||
{
|
||||
EnsureVTKHDF();
|
||||
|
||||
if (compression)
|
||||
{
|
||||
vtkhdf->EnableCompression(compression_level >= 0 ? compression_level : 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
vtkhdf->DisableCompression();
|
||||
}
|
||||
|
||||
vtkhdf->SaveMesh<FP_T>(*mesh, high_order_output, levels_of_detail);
|
||||
for (const auto &field : field_map)
|
||||
{
|
||||
vtkhdf->SaveGridFunction<FP_T>(*field.second, field.first);
|
||||
}
|
||||
vtkhdf->UpdateSteps(time);
|
||||
vtkhdf->Flush();
|
||||
}
|
||||
|
||||
void ParaViewHDFDataCollection::Save()
|
||||
{
|
||||
switch (pv_data_format)
|
||||
{
|
||||
case VTKFormat::BINARY32: TSave<float>(); break;
|
||||
case VTKFormat::BINARY: TSave<double>(); break;
|
||||
default: MFEM_ABORT("Unsupported VTK format.");
|
||||
}
|
||||
}
|
||||
|
||||
ParaViewHDFDataCollection::~ParaViewHDFDataCollection() = default;
|
||||
|
||||
#endif
|
||||
|
||||
} // end namespace MFEM
|
||||
|
||||
+110
-61
@@ -502,59 +502,26 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// Helper class for ParaView visualization data
|
||||
class ParaViewDataCollection : public DataCollection
|
||||
/// Abstract base class for ParaViewDataCollection and ParaViewHDFDataCollection
|
||||
class ParaViewDataCollectionBase : public DataCollection
|
||||
{
|
||||
private:
|
||||
int levels_of_detail;
|
||||
int compression_level;
|
||||
std::fstream pvd_stream;
|
||||
VTKFormat pv_data_format;
|
||||
bool high_order_output;
|
||||
bool restart_mode;
|
||||
|
||||
protected:
|
||||
void WritePVTUHeader(std::ostream &out);
|
||||
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
|
||||
void SaveDataVTU(std::ostream &out, int ref);
|
||||
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
|
||||
const char *GetDataFormatString() const;
|
||||
const char *GetDataTypeString() const;
|
||||
/// @brief If compression is enabled, return the compression level, otherwise
|
||||
/// return 0.
|
||||
int GetCompressionLevel() const;
|
||||
|
||||
std::string GenerateCollectionPath();
|
||||
std::string GenerateVTUFileName(const std::string &prefix, int rank);
|
||||
std::string GenerateVTUPath();
|
||||
std::string GeneratePVDFileName();
|
||||
std::string GeneratePVTUFileName(const std::string &prefix);
|
||||
std::string GeneratePVTUPath();
|
||||
|
||||
|
||||
int levels_of_detail = 1;
|
||||
int compression_level = -1;
|
||||
bool high_order_output = false;
|
||||
bool restart_mode = false;
|
||||
VTKFormat pv_data_format = VTKFormat::BINARY;
|
||||
public:
|
||||
/// Constructor. The collection name is used when saving the data.
|
||||
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
|
||||
Before saving the data collection, some parameters in the collection can
|
||||
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
|
||||
ParaViewDataCollection(const std::string& collection_name,
|
||||
mfem::Mesh *mesh_ = NULL);
|
||||
ParaViewDataCollectionBase(const std::string &name, Mesh *mesh);
|
||||
|
||||
/// Set refinement levels - every element is uniformly split based on
|
||||
/// levels_of_detail_. The initial value is 1.
|
||||
void SetLevelsOfDetail(int levels_of_detail_);
|
||||
|
||||
/// Save the collection - the directory name is constructed based on the
|
||||
/// cycle value
|
||||
void Save() override;
|
||||
|
||||
/// Set the data format for the ParaView output files. Possible options are
|
||||
/// VTKFormat::ASCII, VTKFormat::BINARY, and VTKFormat::BINARY32.
|
||||
/// The ASCII and BINARY options output double precision data, whereas the
|
||||
/// BINARY32 option outputs single precision data.
|
||||
/// @brief Set the refinement level.
|
||||
///
|
||||
/// The initial format is VTKFormat::BINARY.
|
||||
void SetDataFormat(VTKFormat fmt);
|
||||
/// In "low-order mode", every element is uniformly split based on the levels
|
||||
/// of detail. In "high-order mode", this sets the polynomial degree for the
|
||||
/// element transformations.
|
||||
///
|
||||
/// The initial value is 1.
|
||||
void SetLevelsOfDetail(int levels_of_detail_);
|
||||
|
||||
/// @brief Set the zlib compression level.
|
||||
///
|
||||
@@ -570,28 +537,110 @@ public:
|
||||
/// Any nonzero compression level will enable compression.
|
||||
void SetCompressionLevel(int compression_level_);
|
||||
|
||||
/// Enable or disable zlib compression. If the input is true, use the default
|
||||
/// zlib compression level (unless the compression level has previously been
|
||||
/// set by calling SetCompressionLevel()).
|
||||
/// @brief Enable or disable zlib compression.
|
||||
///
|
||||
/// If the input is true, use the default zlib compression level (unless the
|
||||
/// compression level has previously been set by calling
|
||||
/// SetCompressionLevel()).
|
||||
void SetCompression(bool compression_) override;
|
||||
|
||||
/// @brief Sets whether or not to output the data as high-order elements
|
||||
/// (false by default).
|
||||
///
|
||||
/// Reading high-order data requires ParaView 5.5 or later.
|
||||
void SetHighOrderOutput(bool high_order_output_);
|
||||
|
||||
/// If compression is enabled, return the compression level, else return 0.
|
||||
int GetCompressionLevel() const;
|
||||
|
||||
/// @brief Set the data format for the ParaView output files.
|
||||
///
|
||||
/// Possible options are VTKFormat::ASCII, VTKFormat::BINARY, and
|
||||
/// VTKFormat::BINARY32. The ASCII and BINARY options output double precision
|
||||
/// data, whereas the BINARY32 option outputs single precision data.
|
||||
///
|
||||
/// The initial format is VTKFormat::BINARY.
|
||||
///
|
||||
/// VTKFormat::ASCII is not supported by ParaViewHDFDataCollection.
|
||||
void SetDataFormat(VTKFormat fmt);
|
||||
|
||||
/// Returns true if the output format is BINARY or BINARY32, false if ASCII.
|
||||
bool IsBinaryFormat() const;
|
||||
|
||||
/// Sets whether or not to output the data as high-order elements (false
|
||||
/// by default). Reading high-order data requires ParaView 5.5 or later.
|
||||
void SetHighOrderOutput(bool high_order_output_);
|
||||
|
||||
/// Enable or disable restart mode. If restart is enabled, new writes will
|
||||
/// preserve timestep metadata for any solutions prior to the currently
|
||||
/// defined time.
|
||||
/// @brief Enable or disable restart mode.
|
||||
///
|
||||
/// If restart is enabled, new writes will preserve timestep metadata for any
|
||||
/// solutions prior to the currently defined time.
|
||||
///
|
||||
/// Initially, restart mode is disabled.
|
||||
void UseRestartMode(bool restart_mode_);
|
||||
|
||||
/// Load the collection - not implemented in the ParaView writer
|
||||
void Load(int cycle_ = 0) override;
|
||||
};
|
||||
|
||||
/// Writer for ParaView visualization (PVD and VTU format)
|
||||
class ParaViewDataCollection : public ParaViewDataCollectionBase
|
||||
{
|
||||
private:
|
||||
std::fstream pvd_stream;
|
||||
|
||||
protected:
|
||||
void WritePVTUHeader(std::ostream &out);
|
||||
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
|
||||
void SaveDataVTU(std::ostream &out, int ref);
|
||||
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
|
||||
const char *GetDataFormatString() const;
|
||||
const char *GetDataTypeString() const;
|
||||
|
||||
std::string GenerateCollectionPath();
|
||||
std::string GenerateVTUFileName(const std::string &prefix, int rank);
|
||||
std::string GenerateVTUPath();
|
||||
std::string GeneratePVDFileName();
|
||||
std::string GeneratePVTUFileName(const std::string &prefix);
|
||||
std::string GeneratePVTUPath();
|
||||
|
||||
public:
|
||||
/// Constructor. The collection name is used when saving the data.
|
||||
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
|
||||
Before saving the data collection, some parameters in the collection can
|
||||
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
|
||||
ParaViewDataCollection(const std::string& collection_name,
|
||||
Mesh *mesh_ = nullptr);
|
||||
|
||||
/// Save the collection - the directory name is constructed based on the
|
||||
/// cycle value
|
||||
void Save() override;
|
||||
};
|
||||
|
||||
#ifdef MFEM_USE_HDF5
|
||||
|
||||
/// Writer for ParaView visualization (%VTKHDF format)
|
||||
class ParaViewHDFDataCollection : public ParaViewDataCollectionBase
|
||||
{
|
||||
/// The low-level VTKHDF object for I/O (pointer to implementation idiom).
|
||||
std::unique_ptr<class VTKHDF> vtkhdf;
|
||||
|
||||
/// Create the VTKHDF object if it doesn't exist already.
|
||||
void EnsureVTKHDF();
|
||||
|
||||
/// Save the collection (templated on floating point type).
|
||||
template <typename FP_T> void TSave();
|
||||
|
||||
public:
|
||||
/// @brief Constructor. The collection name is used when saving the data.
|
||||
///
|
||||
/// If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
|
||||
/// Before saving the data collection, some parameters in the collection can
|
||||
/// be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc.
|
||||
ParaViewHDFDataCollection(const std::string& collection_name,
|
||||
Mesh *mesh_ = nullptr);
|
||||
|
||||
/// Save the collection.
|
||||
void Save() override;
|
||||
|
||||
/// Destructor.
|
||||
~ParaViewHDFDataCollection();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
+65
-52
@@ -17,7 +17,10 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
|
||||
struct DGMassInvKernels { DGMassInvKernels(); };
|
||||
|
||||
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
|
||||
Coefficient *coeff,
|
||||
const IntegrationRule *ir,
|
||||
int btype)
|
||||
: Solver(fes_orig.GetTrueVSize()),
|
||||
@@ -27,6 +30,8 @@ DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
|
||||
fes_orig.GetTypicalFE()->GetMapType()),
|
||||
fes(fes_orig.GetMesh(), &fec)
|
||||
{
|
||||
static DGMassInvKernels kernels;
|
||||
|
||||
MFEM_VERIFY(fes.IsDGSpace(), "Space must be DG.");
|
||||
MFEM_VERIFY(!fes.IsVariableOrder(), "Variable orders not supported.");
|
||||
|
||||
@@ -46,7 +51,7 @@ DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
|
||||
const FiniteElement &fe = *fes.GetTypicalFE();
|
||||
d2q = &fe_orig.GetDofToQuad(fe.GetNodes(), mode);
|
||||
|
||||
int n = d2q->ndof;
|
||||
const int n = d2q->ndof;
|
||||
Array<real_t> B_inv = d2q->B; // deep copy
|
||||
Array<int> ipiv(n);
|
||||
// solver basis to original
|
||||
@@ -71,7 +76,7 @@ DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
|
||||
// Only need transformed RHS if basis is different
|
||||
if (btype_orig != btype) { b2_.SetSize(height); }
|
||||
|
||||
M = new BilinearForm(&fes);
|
||||
M.reset(new BilinearForm(&fes));
|
||||
M->AddDomainIntegrator(m); // M assumes ownership of m
|
||||
M->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
|
||||
@@ -79,19 +84,19 @@ DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
|
||||
Update();
|
||||
}
|
||||
|
||||
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
int btype)
|
||||
: DGMassInverse(fes_, &coeff, nullptr, btype) { }
|
||||
|
||||
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
const IntegrationRule &ir, int btype)
|
||||
: DGMassInverse(fes_, &coeff, &ir, btype) { }
|
||||
|
||||
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_,
|
||||
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_,
|
||||
const IntegrationRule &ir, int btype)
|
||||
: DGMassInverse(fes_, nullptr, &ir, btype) { }
|
||||
|
||||
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, int btype)
|
||||
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, int btype)
|
||||
: DGMassInverse(fes_, nullptr, nullptr, btype) { }
|
||||
|
||||
void DGMassInverse::SetOperator(const Operator &op)
|
||||
@@ -112,10 +117,7 @@ void DGMassInverse::Update()
|
||||
diag_inv.Reciprocal();
|
||||
}
|
||||
|
||||
DGMassInverse::~DGMassInverse()
|
||||
{
|
||||
delete M;
|
||||
}
|
||||
DGMassInverse::~DGMassInverse() = default;
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
@@ -269,47 +271,58 @@ void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
|
||||
const int d1d = m->dofs1D;
|
||||
const int q1d = m->quad1D;
|
||||
|
||||
const int id = (d1d << 4) | q1d;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x11: return DGMassCGIteration<2,1,1>(Mu, u);
|
||||
case 0x22: return DGMassCGIteration<2,2,2>(Mu, u);
|
||||
case 0x33: return DGMassCGIteration<2,3,3>(Mu, u);
|
||||
case 0x35: return DGMassCGIteration<2,3,5>(Mu, u);
|
||||
case 0x44: return DGMassCGIteration<2,4,4>(Mu, u);
|
||||
case 0x46: return DGMassCGIteration<2,4,6>(Mu, u);
|
||||
case 0x55: return DGMassCGIteration<2,5,5>(Mu, u);
|
||||
case 0x57: return DGMassCGIteration<2,5,7>(Mu, u);
|
||||
case 0x66: return DGMassCGIteration<2,6,6>(Mu, u);
|
||||
case 0x68: return DGMassCGIteration<2,6,8>(Mu, u);
|
||||
default: return DGMassCGIteration<2>(Mu, u); // Fallback
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x22: return DGMassCGIteration<3,2,2>(Mu, u);
|
||||
case 0x23: return DGMassCGIteration<3,2,3>(Mu, u);
|
||||
case 0x33: return DGMassCGIteration<3,3,3>(Mu, u);
|
||||
case 0x34: return DGMassCGIteration<3,3,4>(Mu, u);
|
||||
case 0x35: return DGMassCGIteration<3,3,5>(Mu, u);
|
||||
case 0x44: return DGMassCGIteration<3,4,4>(Mu, u);
|
||||
case 0x45: return DGMassCGIteration<3,4,5>(Mu, u);
|
||||
case 0x46: return DGMassCGIteration<3,4,6>(Mu, u);
|
||||
case 0x48: return DGMassCGIteration<3,4,8>(Mu, u);
|
||||
case 0x55: return DGMassCGIteration<3,5,5>(Mu, u);
|
||||
case 0x56: return DGMassCGIteration<3,5,6>(Mu, u);
|
||||
case 0x57: return DGMassCGIteration<3,5,7>(Mu, u);
|
||||
case 0x58: return DGMassCGIteration<3,5,8>(Mu, u);
|
||||
case 0x66: return DGMassCGIteration<3,6,6>(Mu, u);
|
||||
case 0x67: return DGMassCGIteration<3,6,7>(Mu, u);
|
||||
default: return DGMassCGIteration<3>(Mu, u); // Fallback
|
||||
}
|
||||
}
|
||||
CGKernels::Run(dim, d1d, q1d, *this, Mu, u);
|
||||
}
|
||||
|
||||
DGMassInvKernels::DGMassInvKernels()
|
||||
{
|
||||
using k = DGMassInverse::CGKernels;
|
||||
// 2D
|
||||
k::Specialization<2,1,1>::Add();
|
||||
k::Specialization<2,2,2>::Add();
|
||||
k::Specialization<2,3,3>::Add();
|
||||
k::Specialization<2,3,5>::Add();
|
||||
k::Specialization<2,4,4>::Add();
|
||||
k::Specialization<2,4,6>::Add();
|
||||
k::Specialization<2,5,5>::Add();
|
||||
k::Specialization<2,5,7>::Add();
|
||||
k::Specialization<2,6,6>::Add();
|
||||
k::Specialization<2,6,8>::Add();
|
||||
// 3D
|
||||
k::Specialization<3,2,2>::Add();
|
||||
k::Specialization<3,2,3>::Add();
|
||||
k::Specialization<3,3,3>::Add();
|
||||
k::Specialization<3,3,4>::Add();
|
||||
k::Specialization<3,3,5>::Add();
|
||||
k::Specialization<3,4,4>::Add();
|
||||
k::Specialization<3,4,5>::Add();
|
||||
k::Specialization<3,4,6>::Add();
|
||||
k::Specialization<3,4,8>::Add();
|
||||
k::Specialization<3,5,5>::Add();
|
||||
k::Specialization<3,5,6>::Add();
|
||||
k::Specialization<3,5,7>::Add();
|
||||
k::Specialization<3,5,8>::Add();
|
||||
k::Specialization<3,6,6>::Add();
|
||||
k::Specialization<3,6,7>::Add();
|
||||
}
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
|
||||
{
|
||||
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
|
||||
}
|
||||
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
|
||||
int dim, int, int)
|
||||
{
|
||||
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
|
||||
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
|
||||
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
|
||||
else { MFEM_ABORT("Unsupported dimension."); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+15
-9
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "../linalg/operator.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "kernel_dispatch.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -32,7 +34,7 @@ protected:
|
||||
const DofToQuad *d2q; ///< Change of basis. Not owned.
|
||||
Array<real_t> B_; ///< Inverse of change of basis.
|
||||
Array<real_t> Bt_; ///< Inverse of change of basis, transposed.
|
||||
class BilinearForm *M; ///< Mass bilinear form, owned.
|
||||
std::unique_ptr<class BilinearForm> M; ///< Mass bilinear form.
|
||||
class MassIntegrator *m; ///< Mass integrator, owned by the form @ref M.
|
||||
Vector diag_inv; ///< Jacobi preconditioner.
|
||||
real_t rel_tol = 1e-12; ///< Relative CG tolerance.
|
||||
@@ -48,7 +50,7 @@ protected:
|
||||
///
|
||||
/// Custom coefficient and integration rule are used if @a coeff and @a ir
|
||||
/// are non-NULL.
|
||||
DGMassInverse(FiniteElementSpace &fes_, Coefficient *coeff,
|
||||
DGMassInverse(const FiniteElementSpace &fes_, Coefficient *coeff,
|
||||
const IntegrationRule *ir, int btype);
|
||||
public:
|
||||
/// @brief Construct the DG inverse mass operator for @a fes_.
|
||||
@@ -61,36 +63,37 @@ public:
|
||||
/// The solution and right-hand side used for the solver are not affected by
|
||||
/// this basis (they correspond to the basis of @a fes_). @a btype is only
|
||||
/// used internally, and only has an effect on the convergence rate.
|
||||
DGMassInverse(FiniteElementSpace &fes_, int btype=BasisType::GaussLegendre);
|
||||
DGMassInverse(const FiniteElementSpace &fes_,
|
||||
int btype=BasisType::GaussLegendre);
|
||||
/// @brief Construct the DG inverse mass operator for @a fes_ with
|
||||
/// Coefficient @a coeff.
|
||||
///
|
||||
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
|
||||
/// btype.
|
||||
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
int btype=BasisType::GaussLegendre);
|
||||
/// @brief Construct the DG inverse mass operator for @a fes_ with
|
||||
/// Coefficient @a coeff and IntegrationRule @a ir.
|
||||
///
|
||||
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
|
||||
/// btype.
|
||||
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
|
||||
const IntegrationRule &ir, int btype=BasisType::GaussLegendre);
|
||||
/// @brief Construct the DG inverse mass operator for @a fes_ with
|
||||
/// IntegrationRule @a ir.
|
||||
///
|
||||
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
|
||||
/// btype.
|
||||
DGMassInverse(FiniteElementSpace &fes_, const IntegrationRule &ir,
|
||||
DGMassInverse(const FiniteElementSpace &fes_, const IntegrationRule &ir,
|
||||
int btype=BasisType::GaussLegendre);
|
||||
/// @brief Solve the system M b = u.
|
||||
///
|
||||
/// If @ref iterative_mode is @a true, @a u is used as an initial guess.
|
||||
void Mult(const Vector &b, Vector &u) const;
|
||||
void Mult(const Vector &b, Vector &u) const override;
|
||||
/// Same as Mult() since the mass matrix is symmetric.
|
||||
void MultTranspose(const Vector &b, Vector &u) const { Mult(b, u); }
|
||||
void MultTranspose(const Vector &b, Vector &u) const override { Mult(b, u); }
|
||||
/// Not implemented. Aborts.
|
||||
void SetOperator(const Operator &op);
|
||||
void SetOperator(const Operator &op) override;
|
||||
/// Set the relative tolerance.
|
||||
void SetRelTol(const real_t rel_tol_);
|
||||
/// Set the absolute tolerance.
|
||||
@@ -107,6 +110,9 @@ public:
|
||||
/// extended lambda used in an mfem::forall kernel (nvcc limitation)
|
||||
template<int DIM, int D1D = 0, int Q1D = 0>
|
||||
void DGMassCGIteration(const Vector &b_, Vector &u_) const;
|
||||
|
||||
using CGKernelType = void(DGMassInverse::*)(const Vector &b_, Vector &u) const;
|
||||
MFEM_REGISTER_KERNELS(CGKernels, CGKernelType, (int, int, int));
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -37,6 +37,13 @@ void DGMassApply(const int e,
|
||||
constexpr bool use_smem = (D1D > 0 && Q1D > 0);
|
||||
constexpr bool ACCUM = false;
|
||||
constexpr int NBZ = 1;
|
||||
|
||||
if (DIM == 1)
|
||||
{
|
||||
PAMassApply1D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_smem)
|
||||
{
|
||||
// cannot specialize functions below with D1D or Q1D equal to zero
|
||||
@@ -172,6 +179,43 @@ real_t DGMassDot(const int e,
|
||||
return s_dot[0];
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void DGMassBasis1D(const int e,
|
||||
const int NE,
|
||||
const real_t *b_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int d1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
|
||||
const auto b = Reshape(b_, D1D, D1D);
|
||||
const auto x = Reshape(x_, D1D, NE);
|
||||
auto y = Reshape(y_, D1D, NE);
|
||||
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
real_t Y[MD1];
|
||||
|
||||
MFEM_FOREACH_THREAD(i,x,D1D)
|
||||
{
|
||||
real_t val = 0.0;
|
||||
for (int j = 0; j < D1D; ++j)
|
||||
{
|
||||
val += b(i,j)*x(j,e);
|
||||
}
|
||||
Y[i] = val;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i,x,D1D)
|
||||
{
|
||||
y(i,e) = Y[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void DGMassBasis2D(const int e,
|
||||
@@ -269,7 +313,11 @@ void DGMassBasis(const int e,
|
||||
real_t *y_,
|
||||
const int d1d = 0)
|
||||
{
|
||||
if (DIM == 2)
|
||||
if (DIM == 1)
|
||||
{
|
||||
DGMassBasis1D<T_D1D>(e, NE, b_, x_, y_, d1d);
|
||||
}
|
||||
else if (DIM == 2)
|
||||
{
|
||||
DGMassBasis2D<T_D1D>(e, NE, b_, x_, y_, d1d);
|
||||
}
|
||||
|
||||
+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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user