Compare commits
619
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c968516f36 | ||
|
|
9a9d1ea967 | ||
|
|
46ee2ab5dc | ||
|
|
fb672667cc | ||
|
|
1d35fafd21 | ||
|
|
a0ed1bfbca | ||
|
|
0c08279225 | ||
|
|
bc84ce3b47 | ||
|
|
36abe386e0 | ||
|
|
a4868f2a98 | ||
|
|
21321b3abc | ||
|
|
bb4f39c3d7 | ||
|
|
b605a29988 | ||
|
|
7967e13f1d | ||
|
|
b615f22b66 | ||
|
|
e0fe515f21 | ||
|
|
269ee766db | ||
|
|
da8a221097 | ||
|
|
20d6e63df0 | ||
|
|
2288cdcb7f | ||
|
|
233337c9d1 | ||
|
|
4d9cd853b7 | ||
|
|
27352658c3 | ||
|
|
b8a303a07a | ||
|
|
cee9bf3bb2 | ||
|
|
a901754de5 | ||
|
|
03da41c0f5 | ||
|
|
c6ec74db41 | ||
|
|
8854247f86 | ||
|
|
755e4501e1 | ||
|
|
1ec73c3bf6 | ||
|
|
3a15fe3d96 | ||
|
|
8b000dd222 | ||
|
|
a4d6889332 | ||
|
|
e0fbc5e3aa | ||
|
|
03da9d7789 | ||
|
|
9c8874a38c | ||
|
|
bb2460cbd0 | ||
|
|
0648e50e70 | ||
|
|
45e8125fd6 | ||
|
|
25056defeb | ||
|
|
20cd965ed8 | ||
|
|
1fda9c2391 | ||
|
|
2cec0353b1 | ||
|
|
9d22775395 | ||
|
|
94625fad8f | ||
|
|
c9a9c71ff5 | ||
|
|
764d9919b5 | ||
|
|
8efbd4e46f | ||
|
|
b94ac358e4 | ||
|
|
d4d4b79522 | ||
|
|
120f4cb043 | ||
|
|
4f1597c1bc | ||
|
|
56beedbdcb | ||
|
|
520a9c5125 | ||
|
|
ed521022cd | ||
|
|
7b35a47626 | ||
|
|
def35c8a15 | ||
|
|
c53a016d06 | ||
|
|
ea2b42c49d | ||
|
|
4e35b3d8f1 | ||
|
|
c7e066a0d3 | ||
|
|
29681677a1 | ||
|
|
9d72af995f | ||
|
|
b099252dcf | ||
|
|
d696fc2cea | ||
|
|
8dcd0d6349 | ||
|
|
2951d5f98e | ||
|
|
4b27589abf | ||
|
|
8338aa85e6 | ||
|
|
0a5730eac3 | ||
|
|
e20bb381ca | ||
|
|
89e23a93f5 | ||
|
|
4816fa0849 | ||
|
|
6fc7a8ca5a | ||
|
|
22c8b607dc | ||
|
|
91deab3c75 | ||
|
|
87cee25894 | ||
|
|
1bcddddfdb | ||
|
|
da68672955 | ||
|
|
a5dd4b862b | ||
|
|
a3fcb89049 | ||
|
|
bd6f3d51c8 | ||
|
|
8d95f71305 | ||
|
|
aee0cb1dc6 | ||
|
|
7dded1fdcf | ||
|
|
477c475c1a | ||
|
|
34fdcddb8d | ||
|
|
91fa9936ef | ||
|
|
d95e6e0adc | ||
|
|
cc0dcaad09 | ||
|
|
82d3b8b92e | ||
|
|
960379a43d | ||
|
|
3782ece6b3 | ||
|
|
e9782cf437 | ||
|
|
823c7a952d | ||
|
|
2bc553633e | ||
|
|
8be00d1115 | ||
|
|
bdd2bfbb79 | ||
|
|
9a9f64d8bb | ||
|
|
1ea4cd14da | ||
|
|
63b72c4153 | ||
|
|
054593bd4d | ||
|
|
9c90e3830a | ||
|
|
a8e83d2f0d | ||
|
|
298a4bc32a | ||
|
|
65866edd70 | ||
|
|
85b96208f0 | ||
|
|
b825a46061 | ||
|
|
d6cbd4f99a | ||
|
|
c691658232 | ||
|
|
157ffad537 | ||
|
|
cfdb7d2a03 | ||
|
|
a95a2dc251 | ||
|
|
a7737e65ab | ||
|
|
dda669bd70 | ||
|
|
802345aa91 | ||
|
|
1e84d8a9c5 | ||
|
|
ce30630f5f | ||
|
|
0055de1734 | ||
|
|
954757f7de | ||
|
|
7ad1da790c | ||
|
|
4290365459 | ||
|
|
04d6fa900d | ||
|
|
9fec57261b | ||
|
|
9e8a710c92 | ||
|
|
c5d2f364ac | ||
|
|
fca154fbd4 | ||
|
|
804bdb498a | ||
|
|
1546398ee8 | ||
|
|
9237b4cc2e | ||
|
|
a02927005d | ||
|
|
6256741216 | ||
|
|
a5fcfa02e8 | ||
|
|
2fc9b97cb4 | ||
|
|
a5ca81806b | ||
|
|
d4513550f9 | ||
|
|
d1befb2ea6 | ||
|
|
e11a093e72 | ||
|
|
943234617b | ||
|
|
36389366ac | ||
|
|
dbd2b5556b | ||
|
|
288657ebbd | ||
|
|
6570ca9c7a | ||
|
|
71943e120c | ||
|
|
5a26bd936c | ||
|
|
ef65351cc9 | ||
|
|
5a7807055b | ||
|
|
08b46a12dc | ||
|
|
78124a649d | ||
|
|
0e9d10c53d | ||
|
|
911eb07565 | ||
|
|
2efec6390b | ||
|
|
756bc524ab | ||
|
|
bade193d79 | ||
|
|
4363cd2dc2 | ||
|
|
cbbe609ff8 | ||
|
|
23caac9573 | ||
|
|
0eb3001197 | ||
|
|
3eb0e321f4 | ||
|
|
58c276b1d9 | ||
|
|
b81c67a061 | ||
|
|
fbd6aa17e0 | ||
|
|
199192c0f6 | ||
|
|
05034b8917 | ||
|
|
27989c68bd | ||
|
|
8ba6b88ee0 | ||
|
|
df03268c01 | ||
|
|
32aea6ed2d | ||
|
|
7e7322cb86 | ||
|
|
d8b9c7881b | ||
|
|
58f0e28453 | ||
|
|
9d48f23dca | ||
|
|
1947748db6 | ||
|
|
7aca441524 | ||
|
|
689522beb5 | ||
|
|
c0254e3835 | ||
|
|
85e1855d1b | ||
|
|
51eb3c0026 | ||
|
|
c1c348f24f | ||
|
|
043c6ee860 | ||
|
|
790b4b9bce | ||
|
|
69aea4ce75 | ||
|
|
d7d173a215 | ||
|
|
d7ac021db2 | ||
|
|
2b1d4eb30c | ||
|
|
c134457322 | ||
|
|
33bd60f7c7 | ||
|
|
8f3e61a4d3 | ||
|
|
351806ce77 | ||
|
|
d865ac444e | ||
|
|
b14a9b4663 | ||
|
|
cdd560e6a2 | ||
|
|
4c9ddf93e4 | ||
|
|
8410c205a3 | ||
|
|
f714dcfd57 | ||
|
|
53fb6e3977 | ||
|
|
9407050e6a | ||
|
|
0515209ffd | ||
|
|
3446b46420 | ||
|
|
f8f18f8722 | ||
|
|
e7eabeb5e2 | ||
|
|
71f25f5e16 | ||
|
|
678a9db016 | ||
|
|
c09246351d | ||
|
|
86a8d39e54 | ||
|
|
54854b0908 | ||
|
|
8fa48c2425 | ||
|
|
3e9b8605f9 | ||
|
|
d84aa5a355 | ||
|
|
c11c5cf654 | ||
|
|
1c8d25c6ed | ||
|
|
be6d5e2b01 | ||
|
|
8abdc6500e | ||
|
|
21df84f320 | ||
|
|
9f5860fda2 | ||
|
|
c5a03405cf | ||
|
|
33d0d7dbfe | ||
|
|
c085ec6544 | ||
|
|
1f860fbfaf | ||
|
|
c43420c375 | ||
|
|
ecbdd73c54 | ||
|
|
d30e13c543 | ||
|
|
ba2b8aea52 | ||
|
|
f1353bd6e9 | ||
|
|
3cb0bee255 | ||
|
|
bda0b9aba5 | ||
|
|
e85af79c16 | ||
|
|
06c4da64af | ||
|
|
b841c9df71 | ||
|
|
31a6329964 | ||
|
|
57b5d23e4b | ||
|
|
9f83167010 | ||
|
|
2b4085e2dd | ||
|
|
a69ea6d698 | ||
|
|
43c50376eb | ||
|
|
a617205ee1 | ||
|
|
620f906765 | ||
|
|
2620effa65 | ||
|
|
3da9bdc39a | ||
|
|
2ab00899fd | ||
|
|
7009449ef5 | ||
|
|
34482860b0 | ||
|
|
e4a066edf4 | ||
|
|
886a4c89e4 | ||
|
|
b36ed0e9e9 | ||
|
|
8f4aafdebc | ||
|
|
26eea4f2fd | ||
|
|
313f6856e8 | ||
|
|
bf97e92be2 | ||
|
|
d7a04ca4ac | ||
|
|
28b1ac0c9d | ||
|
|
4f3c73b3eb | ||
|
|
0eb2d21602 | ||
|
|
efcf608a5d | ||
|
|
f1d56c4068 | ||
|
|
94698f27d7 | ||
|
|
30dac8986c | ||
|
|
0691354c84 | ||
|
|
5c0b2a6b62 | ||
|
|
c25b84fbd8 | ||
|
|
8be13975b0 | ||
|
|
7fce328f12 | ||
|
|
d6c1edc9a3 | ||
|
|
e72ed572c4 | ||
|
|
bfa68b0b18 | ||
|
|
56b1a1715a | ||
|
|
3514c0f0d4 | ||
|
|
1181253c61 | ||
|
|
d379628b54 | ||
|
|
74498373c9 | ||
|
|
ec45731607 | ||
|
|
1cc3d81866 | ||
|
|
19b57a28b7 | ||
|
|
233316269a | ||
|
|
e34c6b6013 | ||
|
|
ea593def25 | ||
|
|
5791aa4629 | ||
|
|
46627b8655 | ||
|
|
18cff41dac | ||
|
|
f316ec7d5e | ||
|
|
b5366e3ad3 | ||
|
|
0fe2887b1c | ||
|
|
03ad5fbae5 | ||
|
|
800be7971a | ||
|
|
ee06c0eb44 | ||
|
|
a018c66a9e | ||
|
|
fb9449c47d | ||
|
|
14be9236bc | ||
|
|
45d38fd1ba | ||
|
|
1555bfe3a5 | ||
|
|
a3b12b6f97 | ||
|
|
f0e8e6e19a | ||
|
|
734ae021f0 | ||
|
|
2c23f5b0bd | ||
|
|
c82d9bd8a0 | ||
|
|
a17fb5b587 | ||
|
|
bc404c0f85 | ||
|
|
1e6ee60790 | ||
|
|
8e59709ab8 | ||
|
|
f4d0cef65c | ||
|
|
1bef281b45 | ||
|
|
95a86d2bd8 | ||
|
|
ccfee841cb | ||
|
|
7f9cba0daa | ||
|
|
1ae0d6bef6 | ||
|
|
b7ff8749a3 | ||
|
|
ff11a6b572 | ||
|
|
6031b7b2a3 | ||
|
|
3efda06ca8 | ||
|
|
55c7f5821e | ||
|
|
4ffa0434f7 | ||
|
|
2a30ba157f | ||
|
|
1d1a33b365 | ||
|
|
6c8349dbbe | ||
|
|
e4b760a30b | ||
|
|
18ca7ab2c9 | ||
|
|
3f097ae869 | ||
|
|
f912b347a2 | ||
|
|
90a6dc7c99 | ||
|
|
17455e7b08 | ||
|
|
81f209953e | ||
|
|
b3d4579618 | ||
|
|
47855ff8cf | ||
|
|
115a11bf79 | ||
|
|
b81cce6f45 | ||
|
|
550ca6b702 | ||
|
|
5be6c032db | ||
|
|
1eafad0af9 | ||
|
|
e61ac08b8d | ||
|
|
37b22e4e48 | ||
|
|
767f4bde18 | ||
|
|
e09d02ff2f | ||
|
|
da9a797b46 | ||
|
|
92d8e7f46b | ||
|
|
a147b61e98 | ||
|
|
14323a6da5 | ||
|
|
b30809a663 | ||
|
|
e68e96cd00 | ||
|
|
d9e7737354 | ||
|
|
01479824f5 | ||
|
|
389f1ef5ce | ||
|
|
a72d85fa6c | ||
|
|
0fd9dcceb3 | ||
|
|
346f5b28c9 | ||
|
|
3867b49d3b | ||
|
|
e4a32db1dc | ||
|
|
84cb244bc2 | ||
|
|
11b878612b | ||
|
|
a8bbdf4fd4 | ||
|
|
b66d56ee9b | ||
|
|
9852e93449 | ||
|
|
dc45953e0d | ||
|
|
b4ebf12ff8 | ||
|
|
f9cc9c6c15 | ||
|
|
28094ba952 | ||
|
|
72c7405b08 | ||
|
|
2ce3063b2c | ||
|
|
dbd90b50cb | ||
|
|
b640cbe587 | ||
|
|
5a4ba178bb | ||
|
|
f4c44a86bc | ||
|
|
a2e780d18d | ||
|
|
2855a6daa3 | ||
|
|
490026763d | ||
|
|
087a87bf40 | ||
|
|
43c5a04aae | ||
|
|
cda6ee0580 | ||
|
|
a3639fe70c | ||
|
|
78c5f51309 | ||
|
|
3880a48af5 | ||
|
|
9f7f84defb | ||
|
|
2421b48f56 | ||
|
|
2cd2d11215 | ||
|
|
ce5fd63d0a | ||
|
|
27db3a4121 | ||
|
|
e3c45310b0 | ||
|
|
e11d19e3a9 | ||
|
|
b0c30784b6 | ||
|
|
a35ef68c2a | ||
|
|
2fca844393 | ||
|
|
a7aa6c5a7c | ||
|
|
698183a8db | ||
|
|
f6c2f10dee | ||
|
|
fa61508248 | ||
|
|
d2baadad26 | ||
|
|
fdec992469 | ||
|
|
0aa68f431f | ||
|
|
d2288ef6fd | ||
|
|
6f882ed87e | ||
|
|
813a7de323 | ||
|
|
cc4f5625e2 | ||
|
|
5dcf6f7ead | ||
|
|
f4baef8b5d | ||
|
|
0e4b0f7bda | ||
|
|
7317d4d139 | ||
|
|
e8ed1a4c02 | ||
|
|
e9e684599f | ||
|
|
6c994fea99 | ||
|
|
3b05995fd1 | ||
|
|
3cfca882af | ||
|
|
5e99ffc9a3 | ||
|
|
bcbb24dce0 | ||
|
|
797113ff71 | ||
|
|
41af83b10c | ||
|
|
4da2ea6f08 | ||
|
|
9eb0f5c0c3 | ||
|
|
4a1a5dfa55 | ||
|
|
59edd7255c | ||
|
|
6d01e152de | ||
|
|
3e23785a13 | ||
|
|
52a0337f5e | ||
|
|
70cb8fcc04 | ||
|
|
0366ad2468 | ||
|
|
0e22b182a6 | ||
|
|
f021091e9e | ||
|
|
21b592778a | ||
|
|
c3233944ad | ||
|
|
49eb2715ba | ||
|
|
dd097f2cf5 | ||
|
|
a8eba4053f | ||
|
|
5d3be16590 | ||
|
|
2df1b13811 | ||
|
|
899a2fc7cb | ||
|
|
bd0fa51539 | ||
|
|
b151c909f3 | ||
|
|
ce7f94ec1f | ||
|
|
ee6b9fdc2f | ||
|
|
c418868c83 | ||
|
|
7a0344a6bb | ||
|
|
1d28cfe699 | ||
|
|
51812480bd | ||
|
|
61cc19ca22 | ||
|
|
5e48080f3d | ||
|
|
af73851cb2 | ||
|
|
338e4288ca | ||
|
|
dca4cd510a | ||
|
|
2b23f35ec7 | ||
|
|
ea569c5806 | ||
|
|
5957053d85 | ||
|
|
4ba8a2e148 | ||
|
|
e494dc8255 | ||
|
|
9fec24c86c | ||
|
|
664d0a543a | ||
|
|
20c3c30c87 | ||
|
|
083c4845db | ||
|
|
a23c634e37 | ||
|
|
556f6d72f5 | ||
|
|
a355f28eae | ||
|
|
e2efc259b6 | ||
|
|
3ebb7a7b65 | ||
|
|
a75b1ca9c0 | ||
|
|
8ff6d69f74 | ||
|
|
02136f8f5b | ||
|
|
b1468c14fd | ||
|
|
d3281a8a86 | ||
|
|
a7d59d35e0 | ||
|
|
70680d6187 | ||
|
|
35aeecb5c0 | ||
|
|
44f2a63f16 | ||
|
|
948462da6e | ||
|
|
fddefa7838 | ||
|
|
62a01ee670 | ||
|
|
e23768f5f7 | ||
|
|
53d4f78fdb | ||
|
|
eeb71eee37 | ||
|
|
3630a8f8a1 | ||
|
|
048904b731 | ||
|
|
2496b33699 | ||
|
|
eb9022540e | ||
|
|
1ff8b6811d | ||
|
|
cf269700a8 | ||
|
|
cf127c8b14 | ||
|
|
37adfbc405 | ||
|
|
cb0c205bd1 | ||
|
|
2ed6fdc85a | ||
|
|
0a142864e9 | ||
|
|
f975c36e49 | ||
|
|
471bc1f10d | ||
|
|
8f52418e5c | ||
|
|
41a095e972 | ||
|
|
effc07cdbf | ||
|
|
15b35a01ba | ||
|
|
46ac9d937f | ||
|
|
ff746a8af6 | ||
|
|
df36d0f352 | ||
|
|
765ebcecaa | ||
|
|
b3a08b91d6 | ||
|
|
726b5f99ff | ||
|
|
a943683063 | ||
|
|
b6fb45f384 | ||
|
|
d119fa7636 | ||
|
|
4a7c643f99 | ||
|
|
41ea219782 | ||
|
|
2dfd2ccfc5 | ||
|
|
ae9a8b2897 | ||
|
|
b54ee3537f | ||
|
|
2d3aba5d87 | ||
|
|
f4c66c56d6 | ||
|
|
3c3face72c | ||
|
|
762551da72 | ||
|
|
293a374a74 | ||
|
|
39be93547e | ||
|
|
7559d37c58 | ||
|
|
90eed63144 | ||
|
|
d46b421417 | ||
|
|
e6bc4e5a0e | ||
|
|
1d7029c5d6 | ||
|
|
5596d38532 | ||
|
|
389580efef | ||
|
|
2029636109 | ||
|
|
1ca38f826c | ||
|
|
03ec8d78e2 | ||
|
|
3fbeff1db7 | ||
|
|
3d7ac596da | ||
|
|
57e1693fc7 | ||
|
|
4745e062f2 | ||
|
|
a631ab7e77 | ||
|
|
67ca28a501 | ||
|
|
6d334a925a | ||
|
|
8183e1729d | ||
|
|
30f3e42232 | ||
|
|
ef1e0caed1 | ||
|
|
adbe1bfe3a | ||
|
|
5590b87f5e | ||
|
|
f28cd12995 | ||
|
|
e0aba0647d | ||
|
|
cd4593bf8e | ||
|
|
4d92694fc5 | ||
|
|
0cc5280e34 | ||
|
|
a4214d22f4 | ||
|
|
f12f0efb31 | ||
|
|
ce1576411c | ||
|
|
1c4f617fd5 | ||
|
|
b234475774 | ||
|
|
5da1d7ddf3 | ||
|
|
cff3c6cb6f | ||
|
|
7ef863b731 | ||
|
|
09b31f0526 | ||
|
|
5044d9cd45 | ||
|
|
2edddb700e | ||
|
|
1d79ab00ba | ||
|
|
85e08b67a0 | ||
|
|
e3c3150958 | ||
|
|
c513bb1276 | ||
|
|
7559524573 | ||
|
|
7ac34aab22 | ||
|
|
17edacb630 | ||
|
|
703ba47151 | ||
|
|
57caab145a | ||
|
|
911fbfbe82 | ||
|
|
0b3b21dbb2 | ||
|
|
823fd86a87 | ||
|
|
2d3c1bc79a | ||
|
|
1d556b93e7 | ||
|
|
00fcb1b37f | ||
|
|
9445358bc9 | ||
|
|
e4becc6e02 | ||
|
|
b294248b06 | ||
|
|
9b1c3b718d | ||
|
|
e9826f9c69 | ||
|
|
83ca977d71 | ||
|
|
6739574668 | ||
|
|
9f153f9fcb | ||
|
|
00d35316b9 | ||
|
|
84a3fe000b | ||
|
|
7354e1ce6c | ||
|
|
d5089996cc | ||
|
|
9787ad0d3b | ||
|
|
26fb49960e | ||
|
|
5cb2c82d59 | ||
|
|
9e7f8ce838 | ||
|
|
41e4360d2e | ||
|
|
263dd0f019 | ||
|
|
433f9a4c51 | ||
|
|
08856158ce | ||
|
|
b21bf9a2d9 | ||
|
|
8d705f4230 | ||
|
|
0a6c72c52d | ||
|
|
8cefee8799 | ||
|
|
e9b631116f | ||
|
|
6468880b2a | ||
|
|
89a0e08f29 | ||
|
|
7471a22505 | ||
|
|
779ce337f4 | ||
|
|
54b24da610 | ||
|
|
1a332939a0 | ||
|
|
2af3577482 | ||
|
|
bbd26e1835 | ||
|
|
e8842c506a | ||
|
|
214313291b | ||
|
|
783136234a | ||
|
|
dd85ae3384 | ||
|
|
294a71c705 | ||
|
|
0b632bf3d4 | ||
|
|
cfbaf5a6bf | ||
|
|
2e2b8faba5 | ||
|
|
bf9ce2c6a4 | ||
|
|
9f858378ca | ||
|
|
c2b948a036 | ||
|
|
0771f904c0 | ||
|
|
3b49d70f35 | ||
|
|
7c077e656d | ||
|
|
b9f146b46f | ||
|
|
07438814ce | ||
|
|
328c914481 | ||
|
|
1766007d8a | ||
|
|
6311548ac6 | ||
|
|
9d84c17e0b | ||
|
|
23c1fc6452 | ||
|
|
3117d0e8ab | ||
|
|
a9b720b79d | ||
|
|
3fe878efaa | ||
|
|
4c0def024c | ||
|
|
bf7c26ebf9 | ||
|
|
73014e34c5 | ||
|
|
fabce12b76 | ||
|
|
8fbfdb19fe | ||
|
|
fb5e610ca5 |
@@ -0,0 +1,154 @@
|
||||
# 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.
|
||||
---
|
||||
name: Sanitizer Config
|
||||
description: Sets up environment variables for MFEM sanitizer workflow
|
||||
|
||||
inputs:
|
||||
DEBUG:
|
||||
description: If true, use intermediate caches to speed up the workflow
|
||||
by reusing previous builds.
|
||||
default: false
|
||||
|
||||
REPOSITORY:
|
||||
description: Repository to checkout
|
||||
default: mfem/mfem
|
||||
|
||||
BRANCH:
|
||||
description: Branch to checkout
|
||||
default: ubsan
|
||||
|
||||
CLANG_VER:
|
||||
description: CLANG version to use
|
||||
default: 18
|
||||
|
||||
# https://github.com/llvm/llvm-project/releases
|
||||
LLVM_VER:
|
||||
description: LLVM version to use
|
||||
default: 19.1.7
|
||||
|
||||
# https://github.com/hypre-space/hypre/releases
|
||||
HYPRE_VER:
|
||||
description: HYPRE version to use
|
||||
default: 2.19.0
|
||||
|
||||
METIS_VER:
|
||||
description: METIS version to use
|
||||
default: 4.0.3
|
||||
|
||||
CTEST:
|
||||
description: CTest command to use
|
||||
default: ctest -j --test-load $(nproc)
|
||||
--schedule-random
|
||||
--stop-on-failure --output-on-failure
|
||||
--test-dir
|
||||
|
||||
# https://clang.llvm.org/docs/AddressSanitizer.html
|
||||
ASAN_OPTIONS:
|
||||
default: detect_leaks=1,
|
||||
strict_init_order=1,
|
||||
strict_string_checks=1,
|
||||
check_initialization_order=1,
|
||||
detect_stack_use_after_return=1
|
||||
ASAN_CXXFLAGS:
|
||||
default: -fsanitize=address
|
||||
-fsanitize-address-use-after-scope
|
||||
ASAN_LDFLAGS:
|
||||
default: -fsanitize=address
|
||||
|
||||
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
||||
UBSAN_OPTIONS:
|
||||
default: halt_on_error=1, print_stacktrace=1
|
||||
UBSAN_CXXFLAGS:
|
||||
default: -fsanitize=undefined
|
||||
UBSAN_LDFLAGS:
|
||||
default: -fsanitize=undefined
|
||||
|
||||
# https://clang.llvm.org/docs/MemorySanitizer.html
|
||||
MSAN_OPTIONS:
|
||||
default: "poison_in_dtor=1"
|
||||
MSAN_CXXFLAGS:
|
||||
default: -fsanitize=memory
|
||||
-fsanitize-memory-track-origins
|
||||
-fsanitize-memory-use-after-dtor
|
||||
MSAN_LDFLAGS:
|
||||
default: -fsanitize=memory
|
||||
|
||||
LSAN_DIR:
|
||||
description: LSAN suppression directory
|
||||
default: lsan
|
||||
|
||||
LSAN_FILE:
|
||||
description: LSAN suppression file
|
||||
default: lsan.supp
|
||||
|
||||
NO_FLAGS:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Env (Inputs)
|
||||
run: |
|
||||
echo DEBUG=${{inputs.DEBUG}} >> $GITHUB_ENV
|
||||
echo REPOSITORY=${{inputs.REPOSITORY}} >> $GITHUB_ENV
|
||||
echo BRANCH=${{inputs.BRANCH}} >> $GITHUB_ENV
|
||||
echo CLANG_VER=${{inputs.CLANG_VER}} >> $GITHUB_ENV
|
||||
echo LLVM_VER=${{inputs.LLVM_VER}} >> $GITHUB_ENV
|
||||
echo HYPRE_VER=${{inputs.HYPRE_VER}} >> $GITHUB_ENV
|
||||
echo METIS_VER=${{inputs.METIS_VER}} >> $GITHUB_ENV
|
||||
echo CTEST=${{inputs.CTEST}} >> $GITHUB_ENV
|
||||
echo ASAN_OPTIONS=${{inputs.ASAN_OPTIONS}} >> $GITHUB_ENV
|
||||
echo UBSAN_OPTIONS=${{inputs.UBSAN_OPTIONS}} >> $GITHUB_ENV
|
||||
echo MSAN_OPTIONS=${{inputs.MSAN_OPTIONS}} >> $GITHUB_ENV
|
||||
echo LSAN_DIR=${{inputs.LSAN_DIR}} >> $GITHUB_ENV
|
||||
echo LSAN_FILE=${{inputs.LSAN_FILE}} >> $GITHUB_ENV
|
||||
echo ASAN_CXXFLAGS=${{inputs.ASAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo ASAN_LDFLAGS=${{inputs.ASAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo UBSAN_CXXFLAGS=${{inputs.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (dir)
|
||||
run: |
|
||||
echo LLVM_DIR=${{github.workspace}}/llvm >> $GITHUB_ENV
|
||||
echo HYPRE_DIR=hypre-${{inputs.HYPRE_VER}} >> $GITHUB_ENV
|
||||
echo METIS_DIR=metis-${{inputs.METIS_VER}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (bis)
|
||||
run: |
|
||||
echo CC=clang-${{inputs.CLANG_VER}} >> $GITHUB_ENV
|
||||
echo CXX=clang++-${{inputs.CLANG_VER}} >> $GITHUB_ENV
|
||||
echo LLVM_INC=${{env.LLVM_DIR}}/include/c++/v1 >> $GITHUB_ENV
|
||||
echo LLVM_LIB=${{env.LLVM_DIR}}/lib >> $GITHUB_ENV
|
||||
echo HYPRE_TGZ=v${{inputs.HYPRE_VER}}.tar.gz >> $GITHUB_ENV
|
||||
echo METIS_TGZ=metis-${{inputs.METIS_VER}}.tar.gz >> $GITHUB_ENV
|
||||
LSAN_SUPPRESSIONS="${{github.workspace}}/${{inputs.LSAN_DIR}}/${{inputs.LSAN_FILE}}"
|
||||
echo "LSAN_OPTIONS=suppressions=$LSAN_SUPPRESSIONS" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (ter)
|
||||
if: ${{ inputs.NO_FLAGS != 'true' }}
|
||||
run: |
|
||||
echo LLVM_CXXFLAGS=-stdlib=libc++ -I${{env.LLVM_INC}} -Isystem${{env.LLVM_INC}} >> $GITHUB_ENV
|
||||
echo LLVM_LDFLAGS=-L${{env.LLVM_LIB}} -lc++abi -Wl,-rpath,${{env.LLVM_LIB}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (quater)
|
||||
if: ${{ inputs.NO_FLAGS != 'true' }}
|
||||
run: |
|
||||
echo CXXFLAGS=${{env.LLVM_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo LDFLAGS=${{env.LLVM_LDFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
@@ -0,0 +1,91 @@
|
||||
# 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.
|
||||
---
|
||||
name: 'MFEM Compilation'
|
||||
description: 'MFEM Compilation'
|
||||
|
||||
inputs:
|
||||
par:
|
||||
description: 'Whether to build for parallel (true/false)'
|
||||
default: false
|
||||
sanitizer:
|
||||
description: 'Sanitizer to use (asan, msan, ubsan)'
|
||||
default: asan
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
path: mfem/build
|
||||
key: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
|
||||
- uses: ./.github/actions/sanitize/setup
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
- name: Build with ASAN
|
||||
if: inputs.sanitizer == 'asan'
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.ASAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Build with MSAN
|
||||
if: inputs.sanitizer == 'msan'
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Build with UBSAN
|
||||
if: inputs.sanitizer == 'ubsan'
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- uses: mfem/github-actions/build-mfem@v2.5
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
env:
|
||||
CXXFLAGS: ${{env.CXXFLAGS}}
|
||||
LDFLAGS: ${{env.LDFLAGS}}
|
||||
with:
|
||||
mpi: ${{inputs.par == 'false' && 'seq' || 'par'}}
|
||||
mfem-dir: mfem
|
||||
os: ${{runner.os}}
|
||||
library-only: true
|
||||
build-system: cmake
|
||||
hypre-dir: ${{env.HYPRE_DIR}}
|
||||
metis-dir: ${{env.METIS_DIR}}
|
||||
config-options: >-
|
||||
-GNinja
|
||||
-DMPICXX=${{env.CXX}}
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
-DMFEM_USE_MEMALLOC=OFF
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_VERBOSE_MAKEFILE=ON
|
||||
-DCMAKE_CXX_COMPILER=${{env.CXX}}
|
||||
-DCMAKE_CXX_FLAGS_RELEASE='-g -O1 -fno-omit-frame-pointer'
|
||||
|
||||
- name: Delete object files
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: find . -type f -name '*.o' -delete
|
||||
shell: bash
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
overwrite: false
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
---
|
||||
name: 'Install MPI'
|
||||
description: 'Installs MPI and set up its environment variables'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install
|
||||
run: sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
shell: bash
|
||||
|
||||
- name: Env
|
||||
run: |
|
||||
echo PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe >> $GITHUB_ENV
|
||||
echo MPI_INC=$(mpicxx --showme:compile) >> $GITHUB_ENV
|
||||
echo MPI_LIB=$(mpicxx --showme:link) >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (bis)
|
||||
run: |
|
||||
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
|
||||
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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.
|
||||
---
|
||||
name: 'Restore state'
|
||||
description: 'Restore state to be able to run checks, tests'
|
||||
|
||||
inputs:
|
||||
par:
|
||||
description: 'Whether to build for parallel (true/false)'
|
||||
default: false
|
||||
sanitizer:
|
||||
description: 'Sanitizer to use (asan, msan, ubsan)'
|
||||
default: asan
|
||||
cache-path:
|
||||
description: 'path to what needs to be restored'
|
||||
default: none
|
||||
cache-skip:
|
||||
description: 'Skip cache restoration'
|
||||
default: false
|
||||
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: 'Output from a specific step'
|
||||
value: ${{steps.debug.outputs.cache-hit}}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
path: ${{inputs.cache-path}}
|
||||
key: ${{github.job}}-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
|
||||
- uses: ./.github/actions/sanitize/setup
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
- name: Ninja Patch
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
sed -i -e 's/CXX_STATIC_LIBRARY_LINKER__mfem_Release.*/CUSTOM_COMMAND/' build.ninja
|
||||
sed -i -e '/build tests\/unit\/all:/ s/tests\/unit\/[^ ]*unit_tests[^ ]*//g' build.ninja
|
||||
sed -i -e '/^add_test(\[=\[\(unit_tests\|punit_tests\)\]=\]/ s/)/ "--input-file .\/list-test-names-${{matrix.tag}}" "--min-duration 1")/' tests/unit/CTestTestfile.cmake
|
||||
shell: bash
|
||||
|
||||
- name: Copy Data
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
ninja cmake_object_order_depends_target_unit_tests
|
||||
cp -pR ../tests/unit/data tests/unit
|
||||
shell: bash
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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.
|
||||
---
|
||||
name: 'Setup state'
|
||||
description: 'Sets up the state to be able to run build & run'
|
||||
|
||||
inputs:
|
||||
par:
|
||||
description: 'Whether to build for parallel (true/false)'
|
||||
default: false
|
||||
sanitizer:
|
||||
description: 'Sanitizer to use (asan, msan, ubsan)'
|
||||
default: asan
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-libcxx-${{env.LLVM_VER}}-${{inputs.sanitizer}}
|
||||
|
||||
- uses: ./.github/actions/sanitize/mpi
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for Hypre
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
|
||||
- name: Hypre/Metis links
|
||||
if: ${{inputs.par == 'true'}}
|
||||
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
- uses: actions/checkout@v4 # Checkout the repository
|
||||
with:
|
||||
path: mfem
|
||||
# ref: ${{env.BRANCH}}
|
||||
# repository: ${{env.REPOSITORY}}
|
||||
@@ -7,18 +7,17 @@
|
||||
|
||||
https://mfem.org
|
||||
|
||||
|
||||
This directory contains the GitHub CI scripts for MFEM.
|
||||
|
||||
Note that some of these scripts use the shared MFEM GitHub Actions from the external mfem/github-actions repository:
|
||||
|
||||
https://github.com/mfem/github-actions
|
||||
<https://github.com/mfem/github-actions>
|
||||
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.1`, the `v2.1` suffix denotes the branch in the above from which the action is taken.
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
|
||||
|
||||
The current CI workflows are:
|
||||
|
||||
### `repo-check.yml`
|
||||
## `repo-check.yml`
|
||||
|
||||
Runs a number of static repository-level sanity checks.
|
||||
|
||||
@@ -30,19 +29,39 @@ Runs a number of static repository-level sanity checks.
|
||||
|
||||
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
|
||||
|
||||
### `mfem-analysis.yml` (`build-analysis`)
|
||||
## `mfem-analysis.yml` (`build-analysis`)
|
||||
|
||||
Checks if the code builds and satisfies minimal requirements.
|
||||
|
||||
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
|
||||
|
||||
### `builds-and-tests.yml`
|
||||
## `builds-and-tests.yml`
|
||||
|
||||
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
|
||||
|
||||
Uses the following GitHub Actions from https://github.com/mfem/github-actions:
|
||||
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
|
||||
|
||||
- `mfem/github-actions/build-hypre`
|
||||
- `mfem/github-actions/build-metis`
|
||||
- `mfem/github-actions/build-mfem`
|
||||
- `mfem/github-actions/upload-coverage`
|
||||
|
||||
## Sanitizer Workflow for MFEM Verification
|
||||
|
||||
This workflow validates MFEM unit tests, examples, and miniapps using sanitizer tools.
|
||||
|
||||
- `sanitizers.yml` orchestrates:
|
||||
- Building and caching dependencies: HYPRE, METIS, LSAN suppression file, and LLVM libcxx.
|
||||
- Launching fine-grained jobs for serial (ASAN, MSAN, UBSAN) and parallel (ASAN, UBSAN) sanitizers.
|
||||
- `sanitize-tests.yml` is a reusable workflow accepting `par` mode (`true` for parallel) and `sanitizer` (ASAN, MSAN, or UBSAN) as inputs. It executes the following jobs:
|
||||
- **Build**: Compiles the MFEM library with specified parallel and sanitizer settings.
|
||||
- **Check**: Runs verification checks.
|
||||
- Parallel jobs to test the following: **Examples**, **Miniapps** and **Unit tests**
|
||||
|
||||
The workflow leverages composite actions in `.github/actions/sanitize/`:
|
||||
|
||||
- `config`: Centralizes settings for the sanitizer workflow.
|
||||
- `mfem`: Manages the MFEM library build process.
|
||||
- `mpi`: Installs MPI and applies additional compilation flags.
|
||||
- `restore`: Restores the testing environment state.
|
||||
- `setup`: Builds or restores cached dependencies.
|
||||
|
||||
@@ -58,6 +58,7 @@ jobs:
|
||||
build-system: [make, cmake]
|
||||
hypre-target: [int32]
|
||||
precision: [fp64]
|
||||
enzyme: [false]
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
build-system: cmake
|
||||
@@ -80,15 +81,17 @@ jobs:
|
||||
codecov: YES
|
||||
- os: ubuntu-latest
|
||||
target: dbg
|
||||
config-opts: 'CPPFLAGS+=-Og'
|
||||
config-opts: "CPPFLAGS+=-Og"
|
||||
- os: macos-latest
|
||||
codecov: NO
|
||||
- os: windows-latest
|
||||
codecov: NO
|
||||
# config-opts: '-G "Ninja Multi-Config"'
|
||||
- os: windows-latest
|
||||
target: opt
|
||||
mpi: par
|
||||
config-opts: '-DBUILD_SHARED_LIBS=ON'
|
||||
config-opts: "-DBUILD_SHARED_LIBS=ON"
|
||||
# config-opts: '-DBUILD_SHARED_LIBS=ON -G "Ninja Multi-Config"'
|
||||
- os: ubuntu-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
@@ -96,7 +99,7 @@ jobs:
|
||||
build-system: cmake
|
||||
hypre-target: int32
|
||||
precision: fp64
|
||||
config-opts: '-DCMAKE_INSTALL_PREFIX=../cmake-install'
|
||||
config-opts: "-DCMAKE_INSTALL_PREFIX=../cmake-install"
|
||||
# This option can be set to pass additional configuration options to
|
||||
# the MFEM configuration command.
|
||||
# config-opts: '-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
@@ -121,7 +124,17 @@ jobs:
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp32
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
- os: macos-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp64
|
||||
enzyme: true
|
||||
config-opts: MFEM_USE_ENZYME=YES ENZYME_DIR=$(brew --prefix enzyme)
|
||||
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}${{ matrix.enzyme && '-enzyme' || '' }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -131,8 +144,8 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: easimon/maximize-build-space@v8
|
||||
with:
|
||||
overprovision-lvm: 'true'
|
||||
remove-android: 'true'
|
||||
overprovision-lvm: "true"
|
||||
remove-android: "true"
|
||||
|
||||
# Checkout MFEM in "mfem" subdirectory. Final path:
|
||||
# /home/runner/work/mfem/mfem/mfem
|
||||
@@ -144,6 +157,17 @@ jobs:
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Windows environment - PowerShell [debug]
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
ls env: | fl
|
||||
|
||||
- name: Windows environment - Bash [debug]
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
env
|
||||
shell: bash
|
||||
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
@@ -258,6 +282,18 @@ jobs:
|
||||
run: |
|
||||
vcpkg install metis-mfem --triplet=x64-windows-static --overlay-ports=${{ env.MFEM_TOP_DIR }}/config/vcpkg/ports
|
||||
|
||||
# It's usually fine to build the above TPLs with a different compiler.
|
||||
#
|
||||
- name: install Enzyme (macOS w/ Enzyme)
|
||||
if: matrix.enzyme && matrix.os == 'macos-latest'
|
||||
run: |
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew update
|
||||
brew install llvm@19 enzyme
|
||||
echo "LLVM_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV
|
||||
echo "OMPI_CC=$(brew --prefix llvm@19)/bin/clang" >> $GITHUB_ENV
|
||||
echo "OMPI_CXX=$(brew --prefix llvm@19)/bin/clang++" >> $GITHUB_ENV
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# 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.
|
||||
|
||||
name: "Sanitizer"
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
Serial:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: MFEM Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
- name: MFEM Build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
mpi: seq
|
||||
hypre-dir: unused-hypre-dir
|
||||
metis-dir: unused-metis-dir
|
||||
mfem-dir: mfem
|
||||
build-system: make
|
||||
library-only: false
|
||||
config-options:
|
||||
CXX="clang++-18"
|
||||
CXXFLAGS="-g -O1 -std=c++11
|
||||
-fsanitize=address
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize-address-use-after-scope"
|
||||
|
||||
- name: MFEM Info
|
||||
working-directory: mfem
|
||||
run: make info
|
||||
|
||||
- name: MFEM Sanitize
|
||||
working-directory: mfem
|
||||
run:
|
||||
ASAN_OPTIONS="detect_leaks=1,
|
||||
strict_init_order=1,
|
||||
strict_string_checks=1,
|
||||
check_initialization_order=1,
|
||||
detect_stack_use_after_return=1"
|
||||
make test
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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.
|
||||
---
|
||||
name: build-hypre
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-hypre:
|
||||
runs-on: ubuntu-latest
|
||||
name: 2.19.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{env.HYPRE_TGZ}}
|
||||
dir: ${{env.HYPRE_DIR}}
|
||||
target: int32
|
||||
precision: fp64
|
||||
build-system: make
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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.
|
||||
---
|
||||
name: build-libcxx
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-llvm-libcxx:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
sanitizer: [asan, msan, ubsan]
|
||||
include:
|
||||
- sanitizer: asan
|
||||
llvm_use_sanitizer: "Address"
|
||||
- sanitizer: msan
|
||||
llvm_use_sanitizer: "MemoryWithOrigins"
|
||||
- sanitizer: ubsan
|
||||
llvm_use_sanitizer: "Undefined"
|
||||
name: ${{matrix.sanitizer}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
|
||||
- name: Clone
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: >
|
||||
git clone --filter=blob:none --depth=1
|
||||
--branch llvmorg-${{env.LLVM_VER}}
|
||||
--no-checkout https://github.com/llvm/llvm-project.git llvm-project
|
||||
- name: Checkout
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
working-directory: llvm-project
|
||||
run: |
|
||||
git sparse-checkout set --cone
|
||||
git checkout llvmorg-${{env.LLVM_VER}}
|
||||
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
|
||||
- name: Mkdir
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: mkdir ${{env.LLVM_DIR}}
|
||||
- name: CMake
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
working-directory: ${{env.LLVM_DIR}}
|
||||
run: >
|
||||
VERBOSE=1
|
||||
cmake -GNinja ../llvm-project/runtimes/
|
||||
-DCMAKE_C_COMPILER=${{env.CC}}
|
||||
-DCMAKE_CXX_COMPILER=${{env.CXX}}
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
-DCMAKE_INSTALL_PREFIX=/usr
|
||||
-DLLVM_USE_SANITIZER=${{matrix.llvm_use_sanitizer}}
|
||||
-DLLVM_BUILD_32_BITS=OFF
|
||||
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF
|
||||
-DLLVM_INCLUDE_TESTS=OFF
|
||||
-DLIBCXX_INCLUDE_TESTS=OFF
|
||||
-DLIBCXX_INCLUDE_BENCHMARKS=OFF
|
||||
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi'
|
||||
- name: Build
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
working-directory: ${{env.LLVM_DIR}}
|
||||
run: cmake --build . -- cxx cxxabi
|
||||
@@ -0,0 +1,38 @@
|
||||
# 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.
|
||||
---
|
||||
name: build-file-lsan
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-file-lsan:
|
||||
runs-on: ubuntu-latest
|
||||
name: lsan.supp
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
key: build-lsan-suppression-file
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p ${{env.LSAN_DIR}}
|
||||
cat << EOF > ${{env.LSAN_DIR}}/${{env.LSAN_FILE}}
|
||||
leak:libevent_core-2.1.so
|
||||
leak:ompi_mpi_finalize
|
||||
leak:ompi_mpi_init
|
||||
leak:PMPI_Init
|
||||
leak:strdup
|
||||
EOF
|
||||
@@ -0,0 +1,36 @@
|
||||
# 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.
|
||||
---
|
||||
name: build-metis
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-metis:
|
||||
runs-on: ubuntu-latest
|
||||
name: 4.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{env.METIS_TGZ}}
|
||||
dir: ${{env.METIS_DIR}}
|
||||
@@ -0,0 +1,197 @@
|
||||
# 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.
|
||||
---
|
||||
name: Sanitize
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
par:
|
||||
description: 'Whether to build for parallel (true/false)'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
sanitizer:
|
||||
description: 'Sanitizer to use (asan, msan, ubsan)'
|
||||
required: true
|
||||
default: asan
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/mfem
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
check:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/examples/${{env.ex}}
|
||||
- name: MFEM Check
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: ninja -v check
|
||||
|
||||
examples:
|
||||
needs: [check]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/examples/ex1
|
||||
- name: Build Examples
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: ninja -v examples
|
||||
- name: Test Examples
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
${{env.CTEST}} examples ${{env.exclude}} --show-only
|
||||
${{env.CTEST}} examples ${{env.exclude}}
|
||||
|
||||
miniapps:
|
||||
needs: [check]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/miniapps/meshing/minimal-surface
|
||||
- name: Build Miniapps
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: ninja -v miniapps
|
||||
- name: Test Miniapps
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
${{env.CTEST}} miniapps ${{env.exclude}} --show-only
|
||||
${{env.CTEST}} miniapps ${{env.exclude}}
|
||||
|
||||
tests-miniapps:
|
||||
needs: [check]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
|
||||
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/sedov_tests_cpu
|
||||
- name: Build Tests Unit Miniapps
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: ninja -v tests/unit/all
|
||||
- name: Run Tests Unit Miniapps
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}} --show-only
|
||||
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}}
|
||||
|
||||
tests-unit-build:
|
||||
needs: [check]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
- name: Build Unit Tests
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: ninja -v ${{env.unit_tests}}
|
||||
- name: Delete object files
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: find . -type f -name '*.o' -delete
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
overwrite: false
|
||||
|
||||
tests-unit-run:
|
||||
needs: [tests-unit-build]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
tag: [0, 1, 2, 3]
|
||||
name: tests-unit-run-${{matrix.tag}}
|
||||
env:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
np: ${{inputs.par && '_np=2' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build/tests/unit
|
||||
- name: Split Unit Tests
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: |
|
||||
chmod 755 ${{env.unit_tests}}
|
||||
./${{env.unit_tests}} --list-test-names-only | tail -n +2 > list-test-names
|
||||
shuf list-test-names -o list-test-names
|
||||
split --verbose -n l/4 -d -a 1 list-test-names list-test-names-
|
||||
- name: Cat Unit Tests ${{matrix.tag}}
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: cat list-test-names-${{matrix.tag}}
|
||||
- name: Run Unit Tests ${{matrix.tag}}
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build
|
||||
run: |
|
||||
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}" --show-only
|
||||
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}"
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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.
|
||||
---
|
||||
name: Sanitizers
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "next"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}}-${{github.ref}}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
# Build steps for dependencies
|
||||
build-hypre:
|
||||
uses: ./.github/workflows/sanitize-build-hypre.yml
|
||||
|
||||
build-metis:
|
||||
uses: ./.github/workflows/sanitize-build-metis.yml
|
||||
|
||||
build-lsan:
|
||||
uses: ./.github/workflows/sanitize-build-lsan.yml
|
||||
|
||||
build-libcxx:
|
||||
uses: ./.github/workflows/sanitize-build-libcxx.yml
|
||||
|
||||
# Serial sanitizers: asan, msan, ubsan
|
||||
seq-asan:
|
||||
needs: [build-libcxx]
|
||||
uses: ./.github/workflows/sanitize-tests.yml
|
||||
with:
|
||||
sanitizer: asan
|
||||
|
||||
seq-msan:
|
||||
needs: [build-libcxx]
|
||||
uses: ./.github/workflows/sanitize-tests.yml
|
||||
with:
|
||||
sanitizer: msan
|
||||
|
||||
seq-ubsan:
|
||||
needs: [build-libcxx]
|
||||
uses: ./.github/workflows/sanitize-tests.yml
|
||||
with:
|
||||
sanitizer: ubsan
|
||||
|
||||
# Parallel sanitizers: asan, ubsan
|
||||
par-asan:
|
||||
needs: [build-libcxx, build-hypre, build-metis]
|
||||
uses: ./.github/workflows/sanitize-tests.yml
|
||||
with:
|
||||
par: true
|
||||
sanitizer: asan
|
||||
par-ubsan:
|
||||
needs: [build-libcxx, build-hypre, build-metis]
|
||||
uses: ./.github/workflows/sanitize-tests.yml
|
||||
with:
|
||||
par: true
|
||||
sanitizer: ubsan
|
||||
+14
-2
@@ -201,6 +201,9 @@ examples/superlu/sol.*
|
||||
miniapps/adjoint/cvsRoberts_ASAi_dns
|
||||
miniapps/adjoint/adjoint_advection_diffusion
|
||||
|
||||
miniapps/dfem/dfem-minimal-surface
|
||||
miniapps/dfem/dfem-minimal-surface-output
|
||||
|
||||
miniapps/electromagnetics/volta
|
||||
miniapps/electromagnetics/tesla
|
||||
miniapps/electromagnetics/maxwell
|
||||
@@ -229,6 +232,7 @@ miniapps/meshing/fit-node-position
|
||||
miniapps/meshing/trimmer
|
||||
miniapps/meshing/reflector
|
||||
miniapps/meshing/ref321
|
||||
miniapps/meshing/mesh-bounding-boxes
|
||||
miniapps/meshing/mesh-optimizer
|
||||
miniapps/meshing/pmesh-optimizer
|
||||
miniapps/meshing/pmesh-fitting
|
||||
@@ -259,6 +263,8 @@ miniapps/meshing/mesh.*
|
||||
miniapps/meshing/order.*
|
||||
miniapps/meshing/sol.*
|
||||
miniapps/meshing/refined.mesh
|
||||
miniapps/meshing/bounding-box*
|
||||
miniapps/meshing/jacobian-determinant*
|
||||
|
||||
miniapps/mtop/parheat
|
||||
miniapps/mtop/ParHeat*
|
||||
@@ -294,6 +300,7 @@ miniapps/nurbs/nurbs_solenoidal
|
||||
miniapps/nurbs/nurbs_printfunc
|
||||
miniapps/nurbs/nurbs_patch_ex1
|
||||
miniapps/nurbs/nurbs_curveint
|
||||
miniapps/nurbs/nurbs_surface
|
||||
miniapps/nurbs/refined.mesh
|
||||
miniapps/nurbs/mesh.*
|
||||
miniapps/nurbs/sol_?.gf
|
||||
@@ -312,6 +319,7 @@ miniapps/nurbs/nurbs_naca_cmesh
|
||||
miniapps/nurbs/naca-cmesh.mesh
|
||||
miniapps/nurbs/glvis_naca-cmesh.mesh
|
||||
miniapps/nurbs/Naca_cmesh
|
||||
miniapps/nurbs/*-Surface.mesh
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
@@ -333,6 +341,7 @@ miniapps/shifted/lsf_integral
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/gridfunction-bounds
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
miniapps/tools/get-values
|
||||
@@ -399,12 +408,15 @@ miniapps/spde/ParaView
|
||||
|
||||
miniapps/tribol/contact-patch-test
|
||||
|
||||
miniapps/diag-smoothers/abs-l1-jacobi
|
||||
miniapps/diag-smoothers/mg-abs-l1-jacobi
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
tests/unit/punit_tests
|
||||
tests/unit/cunit_tests
|
||||
tests/unit/pcunit_tests
|
||||
tests/unit/gpu_unit_tests
|
||||
tests/unit/pgpu_unit_tests
|
||||
tests/unit/sedov_tests_*
|
||||
tests/unit/psedov_tests_*
|
||||
tests/unit/tmop_pa_tests_*
|
||||
|
||||
@@ -10,14 +10,71 @@
|
||||
|
||||
Version 4.8.1 (development)
|
||||
===========================
|
||||
- Added support for variational resampling of H1 vector fields to ParMoonolith
|
||||
integration.
|
||||
|
||||
Starting with this version, MFEM requires a C++17 compiler.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Introduced dFEM: a new MFEM capability for Automatic Differentiation (AD) of
|
||||
nonlinear finite element operators, based on Enzyme or dual numbers AD at
|
||||
quadrature points. These features are part of the new mfem::future namespace
|
||||
and some of the API can change in the future. See the new dFEM minimal surface
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use.
|
||||
|
||||
- Using Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM
|
||||
built with plugin support. See INSTALL for more details.
|
||||
|
||||
- In the ParMoonolith integration, added support for variational resampling of
|
||||
H1 vector fields.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
|
||||
- Added support for higher order meshes in Mesh::MakeSimplicial and
|
||||
ParMesh::MakeSimplicial.
|
||||
|
||||
- Added a new miniapp for interpolating a surface grid of points in 3D using a
|
||||
smooth NURBS surface, that can then be sampled at arbitrary resolution while
|
||||
staying close to the original geometry. See miniapps/nurbs/nurbs_surface.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- 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).
|
||||
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
|
||||
threads, assigning one task per thread.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added miniapps to demonstrate an implementation of the absolute-value
|
||||
L(1)-Jacobi preconditioners in partially assembled operators. This includes
|
||||
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
|
||||
operators as smoothers.
|
||||
These miniapps can be found in `miniapps/diag-smoothers`.
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- mfem::internal::tensor and mfem::internal::dual have been moved to
|
||||
mfem::future::tensor and mfem::future::dual.
|
||||
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
|
||||
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added the "gpu", "raja-gpu", and "ceed-gpu" backend aliases/shortcuts which
|
||||
automatically select between CUDA or HIP.
|
||||
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
|
||||
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
|
||||
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
|
||||
MFEM build configuration.
|
||||
- Added the option to enable GPU-aware MPI in MFEM using the environment
|
||||
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
|
||||
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
|
||||
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
|
||||
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
+27
-6
@@ -18,8 +18,8 @@ message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
|
||||
"Path to optional user configuration file.")
|
||||
|
||||
# Require C++11 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use.")
|
||||
# Require C++17 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to use.")
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL
|
||||
"Force the use of the chosen C++ standard.")
|
||||
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Enable C++ standard extensions.")
|
||||
@@ -133,7 +133,6 @@ if (MFEM_USE_CUDA)
|
||||
if (NOT CMAKE_CUDA_HOST_COMPILER)
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(CUDA_FLAGS "--expt-extended-lambda")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
|
||||
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
|
||||
@@ -148,6 +147,20 @@ if (MFEM_USE_CUDA)
|
||||
endif()
|
||||
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
|
||||
enable_language(CUDA)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
# backup try to detect if this is clang or nvcc
|
||||
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
else()
|
||||
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
|
||||
"CUDA standard to use.")
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
|
||||
@@ -256,7 +269,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
|
||||
if (OPENMP_FOUND)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
if (MFEM_USE_CUDA)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
|
||||
if(MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -532,9 +549,10 @@ if (MFEM_USE_TRIBOL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enzyme
|
||||
if (MFEM_USE_ENZYME)
|
||||
find_package(ENZYME REQUIRED)
|
||||
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
|
||||
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
|
||||
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
@@ -686,6 +704,9 @@ if (MFEM_USE_MPI)
|
||||
target_link_libraries(mfem PUBLIC ${MPI_CXX_LINK_FLAGS})
|
||||
endif()
|
||||
endif()
|
||||
if (MFEM_USE_ENZYME)
|
||||
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
|
||||
endif()
|
||||
|
||||
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
|
||||
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
|
||||
|
||||
+5
-1
@@ -120,6 +120,7 @@ The MFEM source code has the following structure:
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
│ ├── eltrans
|
||||
│ ├── fe
|
||||
│ ├── gslib
|
||||
@@ -138,6 +139,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── dfem
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
@@ -549,6 +551,8 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add a short description of the example in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New miniapps:
|
||||
- [ ] All sample runs at the top of the miniapp source file work.
|
||||
- [ ] Add to internal testing repo, if sample runs should be included in nightly tests [internally](#tests-at-llnl).
|
||||
- [ ] Exclude long sample runs from automated testing, with `* ` (one space) before the command.
|
||||
- [ ] Update top-level `makefile` and `makefile` in corresponding miniapp directory.
|
||||
- [ ] Add the miniapp binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update CMake build system:
|
||||
@@ -743,7 +747,7 @@ and debug build is performed with a simple run of `ex1` to verify the executable
|
||||
- We mirror the `master` and `next` branches internally (to `gh-master` and
|
||||
`gh-next`) and run longer nightly tests via cron. On the weekends, a more
|
||||
extensive test is run which extracts and executes all the different sample
|
||||
runs from each example.
|
||||
runs from each example and most miniapps.
|
||||
|
||||
- We also mirror PRs on the LLNL GitLab instance. PR mirroring can only be
|
||||
triggered by _LLNL developers_, but test status is publicly available. Only
|
||||
|
||||
@@ -263,7 +263,7 @@ See the configuration file config/defaults.mk for the default settings.
|
||||
Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc'
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -608,11 +608,12 @@ MFEM_USE_TRIBOL = YES/NO
|
||||
|
||||
MFEM_USE_ENZYME = YES/NO
|
||||
Enables automatic differentiation support through the LLVM plugin Enzyme.
|
||||
This requires the compiler to be set to clang (>=14.0.0). We also advise to
|
||||
use the link time optimization (LTO) plugin, to enable functions that you
|
||||
define over multiple files (compilation units) and want to be differentiated
|
||||
automatically, to work. This requires to also use LLVM/LLD for linking.
|
||||
Recommended options are in config/defaults.mk.
|
||||
This requires the compiler to be set to clang (>=14.0.0). We also advise the
|
||||
use of the link time optimization (LTO) plugin, so functions defined over
|
||||
multiple files (compilation units) can be differentiated automatically. This
|
||||
requires to also use LLVM/LLD for linking. The recommended options are in
|
||||
config/defaults.mk. For more detailed instructions, see the section "Specific
|
||||
options for Enzyme" below.
|
||||
|
||||
MFEM_BUILD_TAG = (any value)
|
||||
An optional tag to characterize the build. Exported to config/config.mk.
|
||||
@@ -835,7 +836,7 @@ The specific libraries and their options are:
|
||||
|
||||
- CUDA (optional), used when MFEM_USE_CUDA = YES.
|
||||
URL: https://developer.nvidia.com/cuda-toolkit
|
||||
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
|
||||
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB, CUDA_DIR (when CUDA_CXX=clang++).
|
||||
Versions: CUDA >= 10.1.168.
|
||||
|
||||
- HIP (optional), used when MFEM_USE_HIP = YES.
|
||||
@@ -911,7 +912,7 @@ The specific libraries and their options are:
|
||||
- Enzyme, used when MFEM_USE_ENZYME = YES. Requires LLVM/Clang >= 14.0.0.
|
||||
URL: https://github.com/EnzymeAD/Enzyme
|
||||
Options: ENZYME_DIR, ENZYME_OPT, ENZYME_LIB.
|
||||
Versions: Enzyme >= v0.0.33.
|
||||
Versions: Enzyme >= v0.0.176.
|
||||
|
||||
|
||||
Building with CMake
|
||||
@@ -1190,3 +1191,73 @@ 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.
|
||||
|
||||
Specific options for Enzyme
|
||||
===========================
|
||||
To work properly, MFEM and Enzyme need to use the same LLVM/Clang configuration.
|
||||
For example, on macOS this can be done by using Homebrew: first install Enzyme,
|
||||
which in turn installs LLVM as a dependency (as of May 2025, this is LLVM 19):
|
||||
|
||||
brew install enzyme
|
||||
|
||||
In order to ensure the correct compiler choice for the MFEM makefile build, set
|
||||
|
||||
CXX = $(shell brew --prefix llvm@19)/bin/clang++
|
||||
|
||||
in the user.mk file (adapted from config/defaults.mk, see the section "Building
|
||||
with GNU make" above). With MPI, it is convenient to set
|
||||
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
for OpenMPI and
|
||||
|
||||
MPICXX = MPICH_CXX=$(CXX) mpicxx
|
||||
|
||||
for MPICH.
|
||||
|
||||
Additionally, the Enzyme directory needs to be set in user.mk as follows:
|
||||
|
||||
ENZYME_DIR = $(shell brew --prefix enzyme)
|
||||
|
||||
Specifically, a full build on a Mac can be tested by adding the following
|
||||
user.mk file in the config/ directory
|
||||
|
||||
MFEM_USE_ENZYME = YES
|
||||
ENZYME_DIR = $(shell brew --prefix enzyme)
|
||||
LLVM_DIR = $(shell brew --prefix llvm@19)
|
||||
CXX = $(LLVM_DIR)/bin/clang++
|
||||
MFEM_USE_MPI = YES
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
and running
|
||||
|
||||
make config
|
||||
make -j
|
||||
cd miniapps/dfem
|
||||
make
|
||||
./dfem-minimal-surface
|
||||
|
||||
On Linux systems, for example Ubuntu 24.04, use the package manager to install
|
||||
the Enzyme dependencies
|
||||
|
||||
sudo apt install libclang-dev libzstd-dev llvm-dev clang
|
||||
|
||||
and then clone and build Enzyme
|
||||
|
||||
cd $HOME
|
||||
git clone https://github.com/EnzymeAD/Enzyme.git
|
||||
cd Enzyme/enzyme && mkdir build && cd build
|
||||
CC=clang CXX=clang++ cmake .. -DLLVM_DIR=/usr/lib/llvm-18/lib/cmake -DCMAKE_INSTALL_PREFIX=$HOME/Enzyme/enzyme/build
|
||||
make -j
|
||||
make install
|
||||
|
||||
From here, one can proceed in the same way using the following user.mk settings
|
||||
|
||||
MFEM_USE_ENZYME = YES
|
||||
ENZYME_DIR = $(HOME)/Enzyme/enzyme/build
|
||||
CXX = clang++
|
||||
MFEM_USE_MPI = YES
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
On other Linux systems the LLVM packages may have different names, for example
|
||||
on RHEL9, one needs to "sudo yum install llvm-devel libzstd clang-devel".
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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.
|
||||
|
||||
message(STATUS "Looking for ENZYME ...")
|
||||
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
|
||||
|
||||
# Make sure the directory and version combination works. Do nothing otherwise.
|
||||
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
|
||||
# Set ENZYME_FOUND
|
||||
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
|
||||
|
||||
# Set CXX flags to accommodate the Enzyme Clang plugin
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
|
||||
set(MFEM_USE_ENZYME YES)
|
||||
else()
|
||||
|
||||
endif()
|
||||
@@ -86,8 +86,9 @@ if (HYPRE_FOUND AND HYPRE_USING_CUDA)
|
||||
mfem_culib_set_libraries(CUSPARSE cusparse)
|
||||
mfem_culib_set_libraries(CURAND curand)
|
||||
mfem_culib_set_libraries(CUBLAS cublas)
|
||||
mfem_culib_set_libraries(CUSOLVER cusolver)
|
||||
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
|
||||
${CUBLAS_LIBRARIES})
|
||||
${CUBLAS_LIBRARIES} ${CUSOLVER_LIBRARIES})
|
||||
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
|
||||
"HYPRE libraries + dependencies." FORCE)
|
||||
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
|
||||
|
||||
@@ -125,7 +125,9 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
|
||||
if (MFEM_USE_CUDA)
|
||||
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
|
||||
PROPERTIES LANGUAGE CUDA)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Actually add the executable
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ MFEM_MPIEXEC_NP = @MFEM_MPIEXEC_NP@
|
||||
MFEM_MPI_NP = @MFEM_MPI_NP@
|
||||
|
||||
# The NVCC compiler cannot link with -x=cu
|
||||
MFEM_LINK_FLAGS := $(filter-out -x=cu -xhip, $(MFEM_FLAGS))
|
||||
MFEM_LINK_FLAGS := $(filter-out -x=cu -xcuda -xhip, $(MFEM_FLAGS))
|
||||
|
||||
# Optional extra configuration
|
||||
@MFEM_CONFIG_EXTRA@
|
||||
|
||||
@@ -268,6 +268,8 @@ set(TRIBOL_DIR "${MFEM_DIR}/../tribol" CACHE PATH "Path to Tribol")
|
||||
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
|
||||
"Additional packages required by Tribol")
|
||||
|
||||
set(ENZYME_DIR "${MFEM_DIR}/../enzyme" CACHE PATH "Path to Enzyme")
|
||||
|
||||
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
|
||||
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
|
||||
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
|
||||
|
||||
+34
-29
@@ -24,7 +24,7 @@ EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
CXX = g++
|
||||
MPICXX = mpicxx
|
||||
|
||||
BASE_FLAGS = -std=c++11
|
||||
BASE_FLAGS = -std=c++17
|
||||
OPTIM_FLAGS = -O3 $(BASE_FLAGS)
|
||||
DEBUG_FLAGS = -g $(XCOMPILER)-Wall $(BASE_FLAGS)
|
||||
|
||||
@@ -43,12 +43,23 @@ SHARED = NO
|
||||
|
||||
# CUDA configuration options
|
||||
#
|
||||
# If you set MFEM_USE_ENZYME=YES, CUDA_CXX has to be configured to use cuda with
|
||||
# clang as its host compiler.
|
||||
# If you set MFEM_USE_ENZYME=YES, must use CUDA_CXX=clang++
|
||||
CUDA_CXX = nvcc
|
||||
CUDA_ARCH = sm_60
|
||||
CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
|
||||
# Prefixes for passing flags to the host compiler and linker when using CUDA_CXX
|
||||
# Base CUDA install directory, only needed if building with clang+cuda:
|
||||
# The default setting is:
|
||||
# 1. If CUDA_HOME is defined and non-empty, use that.
|
||||
# 2. If nvcc is in the path, use the directory two levels up from that.
|
||||
# 3. Use /usr/local/cuda
|
||||
CUDA_DIR = $(or $(CUDA_HOME),$(patsubst %/,%,$(dir \
|
||||
$(patsubst %/,%,$(dir $(shell command -v nvcc))))),/usr/local/cuda)
|
||||
# flags for clang+cuda
|
||||
CLANG_CUDA_FLAGS = -xcuda --cuda-path=$(CUDA_DIR) --cuda-gpu-arch=$(CUDA_ARCH)
|
||||
# flags for nvcc
|
||||
NVCC_FLAGS = -x=cu --expt-extended-lambda --expt-relaxed-constexpr \
|
||||
-arch=$(CUDA_ARCH)
|
||||
# Prefixes for passing flags to the host compiler and linker when using
|
||||
# CUDA_CXX=nvcc
|
||||
CUDA_XCOMPILER = -Xcompiler=
|
||||
CUDA_XLINKER = -Xlinker=
|
||||
|
||||
@@ -227,7 +238,7 @@ HYPRE_OPT = -I$(HYPRE_DIR)/include
|
||||
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
|
||||
ifeq (YES,$(MFEM_USE_CUDA))
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
HYPRE_LIB += -lcusparse -lcurand -lcublas
|
||||
HYPRE_LIB += -lcusolver -lcusparse -lcurand -lcublas
|
||||
endif
|
||||
ifeq (YES,$(MFEM_USE_HIP))
|
||||
# This is only necessary when hypre is built with hip:
|
||||
@@ -242,7 +253,7 @@ ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS),NONONO)
|
||||
METIS_OPT =
|
||||
METIS_LIB = -L$(METIS_DIR) -lmetis
|
||||
else
|
||||
METIS_DIR = @MFEM_DIR@/../metis-5.0
|
||||
METIS_DIR = @MFEM_DIR@/../metis-5.1.0
|
||||
METIS_OPT = -I$(METIS_DIR)/include
|
||||
METIS_LIB = -L$(METIS_DIR)/lib -lmetis
|
||||
endif
|
||||
@@ -511,6 +522,9 @@ GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
|
||||
# CUDA library configuration
|
||||
CUDA_OPT =
|
||||
CUDA_LIB = -lcusparse -lcublas
|
||||
CLANG_CUDA_LIB = -L$(CUDA_DIR)/lib64 -L$(CUDA_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(CUDA_DIR)/lib64,-rpath,$(CUDA_DIR)/lib \
|
||||
-lcudart -ldl -lrt -pthread
|
||||
|
||||
# HIP library configuration
|
||||
HIP_OPT =
|
||||
@@ -610,29 +624,20 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
# If you want to enable automatic differentiation at compile time, use the
|
||||
# options below, adapted to your configuration. To be more flexible, we
|
||||
# recommend using the Enzyme plugin during link time optimization. One option is
|
||||
# to add your options to the global compiler/linker flags like
|
||||
#
|
||||
# BASE_FLAGS += -flto
|
||||
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
|
||||
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
|
||||
#
|
||||
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
|
||||
ENZYME_VERSION ?= 14
|
||||
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
|
||||
ENZYME_LIB = ""
|
||||
|
||||
# Google Benchmark, SUNDIALS >= 6.4.0, STRUMPACK, RAJA, UMPIRE, and Tribol require C++14:
|
||||
ifneq ($(filter YES,$(MFEM_USE_BENCHMARK) $(MFEM_USE_SUNDIALS) $(MFEM_USE_STRUMPACK) $(MFEM_USE_RAJA) $(MFEM_USE_UMPIRE) $(MFEM_USE_TRIBOL)),)
|
||||
BASE_FLAGS = -std=c++14
|
||||
endif
|
||||
# Ginkgo requires C++17:
|
||||
ifeq ($(MFEM_USE_GINKGO),YES)
|
||||
BASE_FLAGS = -std=c++17
|
||||
ENZYME_DIR = @MFEM_DIR@/../enzyme
|
||||
ENZYME_PLUGIN = $(abspath $(wildcard $(subst \
|
||||
@MFEM_DIR@,$(MFEM_DIR),$(ENZYME_DIR))/lib/ClangEnzyme-*.$(SO_EXT)))
|
||||
ifeq ($(MAKECMDGOALS)-$(MFEM_USE_ENZYME),config-YES)
|
||||
ifeq ($(ENZYME_PLUGIN),)
|
||||
$(error Unable to find the Enzyme pluging! Please set ENZYME_DIR)
|
||||
endif
|
||||
ifneq ($(words $(ENZYME_PLUGIN)),1)
|
||||
$(error Multiple versions of the Enzyme pluging found! \
|
||||
Please set ENZYME_PLUGIN directly)
|
||||
endif
|
||||
endif
|
||||
ENZYME_OPT = -fplugin=$(ENZYME_PLUGIN)
|
||||
ENZYME_LIB =
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: Quadratic
|
||||
FiniteElementCollection: H1_3D_P2
|
||||
VDim: 3
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: Quadratic
|
||||
FiniteElementCollection: H1_3D_P2
|
||||
VDim: 3
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: Quadratic
|
||||
FiniteElementCollection: H1_2D_P2
|
||||
VDim: 2
|
||||
Ordering: 0
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: Quadratic
|
||||
FiniteElementCollection: H1_2D_P2
|
||||
VDim: 2
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -951,6 +951,7 @@ 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/dfem \
|
||||
@MFEM_SOURCE_DIR@/fem/eltrans \
|
||||
@MFEM_SOURCE_DIR@/fem/fe \
|
||||
@MFEM_SOURCE_DIR@/fem/gslib \
|
||||
@@ -972,6 +973,7 @@ 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/dfem \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
|
||||
+35
-76
@@ -62,14 +62,9 @@ static real_t epsilon_ = 1.0;
|
||||
static real_t sigma_ = 20.0;
|
||||
static real_t omega_ = 10.0;
|
||||
|
||||
real_t u0_real_exact(const Vector &);
|
||||
real_t u0_imag_exact(const Vector &);
|
||||
|
||||
void u1_real_exact(const Vector &, Vector &);
|
||||
void u1_imag_exact(const Vector &, Vector &);
|
||||
|
||||
void u2_real_exact(const Vector &, Vector &);
|
||||
void u2_imag_exact(const Vector &, Vector &);
|
||||
complex<real_t> u0_exact(const Vector &x);
|
||||
void u1_exact(const Vector &, ComplexVector &);
|
||||
void u2_exact(const Vector &, ComplexVector &);
|
||||
|
||||
bool check_for_inline_mesh(const char * mesh_file);
|
||||
|
||||
@@ -215,54 +210,48 @@ int main(int argc, char *argv[])
|
||||
ComplexGridFunction * u_exact = NULL;
|
||||
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
|
||||
|
||||
FunctionCoefficient u0_r(u0_real_exact);
|
||||
FunctionCoefficient u0_i(u0_imag_exact);
|
||||
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
|
||||
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
|
||||
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
|
||||
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
|
||||
ComplexFunctionCoefficient u0(u0_exact);
|
||||
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
|
||||
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
|
||||
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
ConstantCoefficient oneCoef(1.0);
|
||||
ComplexConstantCoefficient oneCoef(1.0);
|
||||
|
||||
Vector zeroVec(dim); zeroVec = 0.0;
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0_r, u0_i);
|
||||
u.ProjectBdrCoefficient(u0, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1_r, u1_i);
|
||||
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2_r, u2_i);
|
||||
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
@@ -300,27 +289,24 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient lossCoef(omega_ * sigma_);
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
|
||||
omega_ * sigma_);
|
||||
|
||||
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new MassIntegrator(massCoef),
|
||||
new MassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 1:
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 2:
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -436,29 +422,24 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (exact_sol)
|
||||
{
|
||||
real_t err_r = -1.0;
|
||||
real_t err_i = -1.0;
|
||||
real_t err_u = -1.0;
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
err_r = u.real().ComputeL2Error(u0_r);
|
||||
err_i = u.imag().ComputeL2Error(u0_i);
|
||||
err_u = u.ComputeL2Error(u0);
|
||||
break;
|
||||
case 1:
|
||||
err_r = u.real().ComputeL2Error(u1_r);
|
||||
err_i = u.imag().ComputeL2Error(u1_i);
|
||||
err_u = u.ComputeL2Error(u1);
|
||||
break;
|
||||
case 2:
|
||||
err_r = u.real().ComputeL2Error(u2_r);
|
||||
err_i = u.imag().ComputeL2Error(u2_i);
|
||||
err_u = u.ComputeL2Error(u2);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
|
||||
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
|
||||
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
@@ -564,36 +545,14 @@ complex<real_t> u0_exact(const Vector &x)
|
||||
return std::exp(-i * kappa * x[dim - 1]);
|
||||
}
|
||||
|
||||
real_t u0_real_exact(const Vector &x)
|
||||
{
|
||||
return u0_exact(x).real();
|
||||
}
|
||||
|
||||
real_t u0_imag_exact(const Vector &x)
|
||||
{
|
||||
return u0_exact(x).imag();
|
||||
}
|
||||
|
||||
void u1_real_exact(const Vector &x, Vector &v)
|
||||
void u1_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u1_imag_exact(const Vector &x, Vector &v)
|
||||
void u2_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
|
||||
}
|
||||
|
||||
void u2_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
|
||||
}
|
||||
|
||||
void u2_imag_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
|
||||
}
|
||||
|
||||
+50
-33
@@ -62,6 +62,10 @@ static real_t epsilon_ = 1.0;
|
||||
static real_t sigma_ = 20.0;
|
||||
static real_t omega_ = 10.0;
|
||||
|
||||
complex<real_t> u0_exact(const Vector &x);
|
||||
void u1_exact(const Vector &, ComplexVector &);
|
||||
void u2_exact(const Vector &, ComplexVector &);
|
||||
|
||||
real_t u0_real_exact(const Vector &);
|
||||
real_t u0_imag_exact(const Vector &);
|
||||
|
||||
@@ -244,13 +248,22 @@ int main(int argc, char *argv[])
|
||||
ParComplexGridFunction * u_exact = NULL;
|
||||
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
|
||||
|
||||
ComplexFunctionCoefficient u0(u0_exact);
|
||||
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
|
||||
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
|
||||
|
||||
ComplexConstantCoefficient oneCoef(1.0);
|
||||
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
FunctionCoefficient u0_r(u0_real_exact);
|
||||
FunctionCoefficient u0_i(u0_imag_exact);
|
||||
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
|
||||
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
|
||||
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
|
||||
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
|
||||
|
||||
/*
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
ConstantCoefficient oneCoef(1.0);
|
||||
|
||||
@@ -258,40 +271,40 @@ int main(int argc, char *argv[])
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
*/
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0_r, u0_i);
|
||||
u.ProjectBdrCoefficient(u0, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1_r, u1_i);
|
||||
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2_r, u2_i);
|
||||
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
@@ -331,27 +344,24 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient lossCoef(omega_ * sigma_);
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
|
||||
omega_ * sigma_);
|
||||
|
||||
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new MassIntegrator(massCoef),
|
||||
new MassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 1:
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 2:
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -475,22 +485,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (exact_sol)
|
||||
{
|
||||
real_t err_r = -1.0;
|
||||
real_t err_i = -1.0;
|
||||
real_t err_u = -1.0;
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
err_r = u.real().ComputeL2Error(u0_r);
|
||||
err_i = u.imag().ComputeL2Error(u0_i);
|
||||
err_u = u.ComputeL2Error(u0);
|
||||
break;
|
||||
case 1:
|
||||
err_r = u.real().ComputeL2Error(u1_r);
|
||||
err_i = u.imag().ComputeL2Error(u1_i);
|
||||
err_u = u.ComputeL2Error(u1);
|
||||
break;
|
||||
case 2:
|
||||
err_r = u.real().ComputeL2Error(u2_r);
|
||||
err_i = u.imag().ComputeL2Error(u2_i);
|
||||
err_u = u.ComputeL2Error(u2);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -498,8 +504,7 @@ int main(int argc, char *argv[])
|
||||
if ( myid == 0 )
|
||||
{
|
||||
cout << endl;
|
||||
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
|
||||
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
|
||||
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
@@ -627,6 +632,12 @@ real_t u0_imag_exact(const Vector &x)
|
||||
return u0_exact(x).imag();
|
||||
}
|
||||
|
||||
void u1_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u1_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
@@ -639,6 +650,12 @@ void u1_imag_exact(const Vector &x, Vector &v)
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
|
||||
}
|
||||
|
||||
void u2_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u2_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
@@ -59,11 +59,13 @@ set(SRCS
|
||||
integ/nonlininteg_vecconvection_pa.cpp
|
||||
integ/nonlininteg_vecconvection_mf.cpp
|
||||
coefficient.cpp
|
||||
complex_coefficient.cpp
|
||||
complex_fem.cpp
|
||||
convergence.cpp
|
||||
datacollection.cpp
|
||||
dgmassinv.cpp
|
||||
doftrans.cpp
|
||||
dfem/doperator.cpp
|
||||
eltrans.cpp
|
||||
batchitrans.cpp
|
||||
estimators.cpp
|
||||
@@ -161,6 +163,7 @@ set(SRCS
|
||||
transfer.cpp
|
||||
hyperbolic.cpp
|
||||
integrator.cpp
|
||||
bounds.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -174,12 +177,21 @@ set(HDRS
|
||||
integ/bilininteg_hcurlhdiv_kernels.hpp
|
||||
integ/bilininteg_mass_kernels.hpp
|
||||
coefficient.hpp
|
||||
complex_coefficient.hpp
|
||||
complex_fem.hpp
|
||||
convergence.hpp
|
||||
datacollection.hpp
|
||||
dgmassinv.hpp
|
||||
dgmassinv_kernels.hpp
|
||||
doftrans.hpp
|
||||
dfem/doperator.hpp
|
||||
dfem/fieldoperator.hpp
|
||||
dfem/integrate.hpp
|
||||
dfem/parameterspace.hpp
|
||||
dfem/qfunction_apply.hpp
|
||||
dfem/qfunction_transform.hpp
|
||||
dfem/tuple.hpp
|
||||
dfem/util.hpp
|
||||
eltrans.hpp
|
||||
estimators.hpp
|
||||
fe.hpp
|
||||
@@ -237,6 +249,7 @@ set(HDRS
|
||||
nonlinearform_ext.hpp
|
||||
nonlininteg.hpp
|
||||
qfunction.hpp
|
||||
qinterp/det.hpp
|
||||
qinterp/eval.hpp
|
||||
qinterp/eval_hdiv.hpp
|
||||
qinterp/grad.hpp
|
||||
@@ -263,6 +276,7 @@ set(HDRS
|
||||
transfer.hpp
|
||||
hyperbolic.hpp
|
||||
integrator.hpp
|
||||
bounds.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_SIDRE)
|
||||
|
||||
@@ -515,6 +515,7 @@ struct InvTNewtonSolver<Geometry::SEGMENT, SDim, SType, max_team_x>
|
||||
phys_tol += pptr[idx + d * npts] * pptr[idx + d * npts];
|
||||
}
|
||||
phys_tol = fmax(phys_rtol * phys_rtol, phys_tol * phys_rtol * phys_rtol);
|
||||
hit_bdr[0] = prev_hit_bdr[0] = false;
|
||||
}
|
||||
// for each iteration
|
||||
while (true)
|
||||
|
||||
+19
-33
@@ -466,7 +466,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * doftrans;
|
||||
Mesh *mesh = fes -> GetMesh();
|
||||
DenseMatrix elmat, *elmat_p;
|
||||
|
||||
@@ -503,13 +502,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
// Element-wise integration
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
// Set both doftrans (potentially needed to assemble the element
|
||||
// matrix) and vdofs, which is also needed when the element matrices
|
||||
// are pre-assembled.
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
@@ -547,10 +547,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
doftrans.TransformDual(elmat);
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (static_cond)
|
||||
@@ -628,13 +625,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
fes -> GetBdrElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
int k = 0;
|
||||
for (; k < boundary_integs.Size(); k++)
|
||||
@@ -654,10 +652,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
doftrans.TransformDual(elmat);
|
||||
elmat_p = &elmat;
|
||||
if (!static_cond)
|
||||
{
|
||||
@@ -1530,8 +1525,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes -> GetMesh();
|
||||
@@ -1554,11 +1547,12 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < test_fes -> GetNE(); i++)
|
||||
{
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
|
||||
trial_fes->GetElementVDofs (i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs (i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes -> GetElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -1574,10 +1568,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -1605,13 +1596,14 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < test_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
dom_dof_trans = trial_fes -> GetBdrElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetBdrElementVDofs (i, test_vdofs);
|
||||
trial_fes->GetBdrElementVDofs (i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetBdrElementVDofs (i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes -> GetBdrElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -1626,10 +1618,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -2407,8 +2396,6 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes->GetMesh();
|
||||
@@ -2431,11 +2418,13 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans;
|
||||
DofTransformation ran_dof_trans;
|
||||
for (int i = 0; i < test_fes->GetNE(); i++)
|
||||
{
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes->GetElementVDofs(i, trial_vdofs);
|
||||
ran_dof_trans = test_fes->GetElementVDofs(i, test_vdofs);
|
||||
trial_fes->GetElementVDofs(i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes->GetElementTransformation(i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -2451,10 +2440,7 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
}
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
mat->SetSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
+208
-170
@@ -78,7 +78,7 @@ void MFBilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict);
|
||||
if (H1elem_restrict)
|
||||
{
|
||||
H1elem_restrict->MultTransposeUnsigned(localY, y);
|
||||
H1elem_restrict->AbsMultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +456,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict);
|
||||
if (H1elem_restrict)
|
||||
{
|
||||
H1elem_restrict->MultTransposeUnsigned(localY, y);
|
||||
H1elem_restrict->AbsMultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -491,7 +491,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
assemble_diagonal_with_markers(*bdr_integs[i], bdr_markers[i],
|
||||
bdr_attributes, bdr_face_Y);
|
||||
}
|
||||
bdr_face_restrict_lex->AddMultTransposeUnsigned(bdr_face_Y, y);
|
||||
bdr_face_restrict_lex->AddAbsMultTranspose(bdr_face_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +526,8 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
A.Reset(oper); // A will own oper
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
|
||||
const bool useAbs) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
@@ -558,11 +559,13 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (integrators[i]->Patchwise())
|
||||
{
|
||||
MFEM_ASSERT(!useAbs, "AbsMult not implemented with NURBS!")
|
||||
integrators[i]->AddMultNURBSPA(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
integrators[i]->AddMultPA(x, y);
|
||||
if (useAbs) { integrators[i]->AddAbsMultPA(x, y); }
|
||||
else { integrators[i]->AddMultPA(x, y); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,14 +574,30 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
if (iSz)
|
||||
{
|
||||
Array<Array<int>*> &elem_markers = *a->GetDBFI_Marker();
|
||||
elem_restrict->Mult(x, localX);
|
||||
auto H1elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict);
|
||||
if (H1elem_restrict && useAbs)
|
||||
{
|
||||
H1elem_restrict->AbsMult(x, localX);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->Mult(x, localX);
|
||||
}
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*integrators[i], localX, elem_markers[i],
|
||||
elem_attributes, false, localY);
|
||||
elem_attributes, false, localY, useAbs);
|
||||
}
|
||||
if (H1elem_restrict && useAbs)
|
||||
{
|
||||
H1elem_restrict->AbsMultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -590,6 +609,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
MFEM_ASSERT(!useAbs, "AbsMult not implemented for face integrators!")
|
||||
// When assembling interior face integrators for DG spaces, we need to
|
||||
// exchange the face-neighbor information. This happens inside member
|
||||
// functions of the 'int_face_restrict_lex'. To avoid repeated calls to
|
||||
@@ -651,6 +671,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
const bool has_bdr_integs = (n_bdr_face_integs > 0 || n_bdr_integs > 0);
|
||||
if (bdr_face_restrict_lex && has_bdr_integs)
|
||||
{
|
||||
MFEM_ASSERT(!useAbs, "AbsMult not implemented for bdr integrators!")
|
||||
Array<Array<int>*> &bdr_markers = *a->GetBBFI_Marker();
|
||||
Array<Array<int>*> &bdr_face_markers = *a->GetBFBFI_Marker();
|
||||
bdr_face_restrict_lex->Mult(x, bdr_face_X);
|
||||
@@ -828,22 +849,39 @@ void PABilinearFormExtension::AddMultWithMarkers(
|
||||
const Array<int> *markers,
|
||||
const Array<int> &attributes,
|
||||
const bool transpose,
|
||||
Vector &y) const
|
||||
Vector &y,
|
||||
const bool useAbs) const
|
||||
{
|
||||
if (markers)
|
||||
{
|
||||
tmp_evec.SetSize(y.Size());
|
||||
tmp_evec = 0.0;
|
||||
if (transpose) { integ.AddMultTransposePA(x, tmp_evec); }
|
||||
else { integ.AddMultPA(x, tmp_evec); }
|
||||
if (useAbs)
|
||||
{
|
||||
if (transpose) { integ.AddAbsMultTransposePA(x, tmp_evec); }
|
||||
else { integ.AddAbsMultPA(x, tmp_evec); }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transpose) { integ.AddMultTransposePA(x, tmp_evec); }
|
||||
else { integ.AddMultPA(x, tmp_evec); }
|
||||
}
|
||||
const int ne = attributes.Size();
|
||||
const int nd = x.Size() / ne;
|
||||
AddWithMarkers_(ne, nd, tmp_evec, *markers, attributes, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transpose) { integ.AddMultTransposePA(x, y); }
|
||||
else { integ.AddMultPA(x, y); }
|
||||
if (useAbs)
|
||||
{
|
||||
if (transpose) { integ.AddAbsMultTransposePA(x, y); }
|
||||
else { integ.AddAbsMultPA(x, y); }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transpose) { integ.AddMultTransposePA(x, y); }
|
||||
else { integ.AddMultPA(x, y); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1010,8 +1048,13 @@ void EABilinearFormExtension::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
|
||||
const bool useTranspose,
|
||||
const bool useAbs) const
|
||||
{
|
||||
auto elemRest = dynamic_cast<const ElementRestriction*>(elem_restrict);
|
||||
MFEM_ASSERT(useAbs?(elemRest!=nullptr):true,
|
||||
"elem_restrict is not ElementRestriction*!")
|
||||
// Apply the Element Restriction
|
||||
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
|
||||
if (!useRestrict)
|
||||
@@ -1019,6 +1062,11 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
}
|
||||
else if (useAbs)
|
||||
{
|
||||
elemRest->AbsMult(x, localX);
|
||||
localY = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->Mult(x, localX);
|
||||
@@ -1026,25 +1074,55 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
// Apply the Element Matrices
|
||||
{
|
||||
Vector abs_ea_data;
|
||||
if (useAbs)
|
||||
{
|
||||
abs_ea_data = ea_data;
|
||||
abs_ea_data.Abs();
|
||||
}
|
||||
const int NDOFS = elemDofs;
|
||||
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
|
||||
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
|
||||
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
|
||||
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
auto A = Reshape(useAbs?abs_ea_data.Read():ea_data.Read(), NDOFS, NDOFS, ne);
|
||||
if (!useTranspose)
|
||||
{
|
||||
const int e = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
res += A(i, j, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
const int e = 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, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
const int e = 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, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
}
|
||||
// Apply the Element Restriction transposed
|
||||
if (useRestrict)
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
if (useAbs)
|
||||
{
|
||||
elemRest->AbsMultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,6 +1131,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
MFEM_VERIFY(!useAbs, "AbsMult not implemented with Face integrators!")
|
||||
// Apply the Interior Face Restriction
|
||||
int_face_restrict_lex->Mult(x, int_face_X);
|
||||
if (int_face_X.Size()>0)
|
||||
@@ -1064,7 +1143,65 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
auto Y = Reshape(int_face_Y.ReadWrite(), NDOFS, 2, nf_int);
|
||||
if (!factorize_face_terms)
|
||||
{
|
||||
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
Vector abs_ea_data_int(ea_data_int.Size());
|
||||
if (useAbs)
|
||||
{
|
||||
abs_ea_data_int = ea_data_int;
|
||||
abs_ea_data_int.Abs();
|
||||
}
|
||||
auto A_int = Reshape(useAbs?abs_ea_data_int.Read():ea_data_int.Read(),
|
||||
NDOFS, NDOFS, 2, nf_int);
|
||||
if (!useTranspose)
|
||||
{
|
||||
mfem::forall(nf_int*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++)
|
||||
{
|
||||
res += A_int(i, j, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(i, j, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::forall(nf_int*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++)
|
||||
{
|
||||
res += A_int(j, i, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(j, i, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
});
|
||||
}
|
||||
}
|
||||
Vector abs_ea_data_ext(ea_data_ext.Size());
|
||||
if (useAbs)
|
||||
{
|
||||
abs_ea_data_ext = ea_data_ext;
|
||||
abs_ea_data_ext.Abs();
|
||||
}
|
||||
auto A_ext = Reshape(useAbs?abs_ea_data_ext.Read():ea_data_ext.Read(),
|
||||
NDOFS, NDOFS, 2, nf_int);
|
||||
if (!useTranspose)
|
||||
{
|
||||
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
@@ -1072,35 +1209,37 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(i, j, 0, f)*X(i, 0, f);
|
||||
res += A_ext(i, j, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(i, j, 1, f)*X(i, 1, f);
|
||||
res += A_ext(i, j, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
}
|
||||
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
else
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
real_t res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
res += A_ext(i, j, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(i, j, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
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_ext(j, i, 1, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(j, i, 0, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
}
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->AddMultTransposeInPlace(int_face_Y, y);
|
||||
}
|
||||
@@ -1109,7 +1248,9 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
// Treatment of boundary faces
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
|
||||
{
|
||||
MFEM_ASSERT(!useAbs, "AbsMult not implemented with Face integrators!")
|
||||
// Apply the Boundary Face Restriction
|
||||
// TODO: AbsMult if needed
|
||||
bdr_face_restrict_lex->Mult(x, bdr_face_X);
|
||||
bdr_face_Y = 0.0;
|
||||
// Apply the boundary face matrices
|
||||
@@ -1117,141 +1258,38 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
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)
|
||||
if (!useTranspose)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Apply the Element Restriction
|
||||
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
|
||||
if (!useRestrict)
|
||||
{
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
}
|
||||
// Apply the Element Matrices transposed
|
||||
{
|
||||
const int NDOFS = elemDofs;
|
||||
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
|
||||
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
|
||||
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
|
||||
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
{
|
||||
const int e = 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, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
// Apply the Element Restriction transposed
|
||||
if (useRestrict)
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Treatment of interior faces
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
// Apply the Interior Face Restriction
|
||||
int_face_restrict_lex->Mult(x, int_face_X);
|
||||
if (int_face_X.Size()>0)
|
||||
{
|
||||
int_face_Y = 0.0;
|
||||
// Apply the interior face matrices transposed
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(int_face_X.Read(), NDOFS, 2, nf_int);
|
||||
auto Y = Reshape(int_face_Y.ReadWrite(), NDOFS, 2, nf_int);
|
||||
if (!factorize_face_terms)
|
||||
{
|
||||
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
mfem::forall(nf_int*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++)
|
||||
{
|
||||
res += A_int(j, i, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(j, i, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
});
|
||||
}
|
||||
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
|
||||
// TODO: useAbs
|
||||
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++)
|
||||
{
|
||||
res += A_ext(j, i, 1, f)*X(i, 0, f);
|
||||
res += A(i, j, f)*X(i, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
Y(j, f) += res;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: useAbs
|
||||
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++)
|
||||
{
|
||||
res += A_ext(j, i, 0, f)*X(i, 1, f);
|
||||
res += A(j, i, f)*X(i, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->AddMultTransposeInPlace(int_face_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Treatment of boundary faces
|
||||
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);
|
||||
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++)
|
||||
{
|
||||
res += A(j, i, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
// TODO: AbsMultTranspose if needed
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
}
|
||||
}
|
||||
@@ -1911,7 +1949,7 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_trial);
|
||||
if (H1elem_restrict_trial)
|
||||
{
|
||||
H1elem_restrict_trial->MultUnsigned(D, localTrial);
|
||||
H1elem_restrict_trial->AbsMult(D, localTrial);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1937,7 +1975,7 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (H1elem_restrict_test)
|
||||
{
|
||||
H1elem_restrict_test->MultTransposeUnsigned(localTest, diag);
|
||||
H1elem_restrict_test->AbsMultTranspose(localTest, diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1993,7 +2031,7 @@ void PADiscreteLinearOperatorExtension::Assemble()
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (elem_restrict)
|
||||
{
|
||||
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
|
||||
elem_restrict->AbsMultTranspose(ones, test_multiplicity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -91,12 +91,17 @@ public:
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0) override;
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x,y); }
|
||||
void AbsMult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x,y, true); }
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
void Update() override;
|
||||
|
||||
protected:
|
||||
void SetupRestrictionOperators(const L2FaceValues m);
|
||||
void MultInternal(const Vector &x, Vector &y,
|
||||
const bool useAbs = false) const;
|
||||
|
||||
/// @brief Accumulate the action (or transpose) of the integrator on @a x
|
||||
/// into @a y, taking into account the (possibly null) @a markers array.
|
||||
@@ -110,12 +115,14 @@ protected:
|
||||
/// @param attributes Array of element or boundary element attributes.
|
||||
/// @param transpose Compute the action or transpose of the integrator .
|
||||
/// @param y Output E-vector
|
||||
/// @param useAbs Apply absolute-value operator
|
||||
void AddMultWithMarkers(const BilinearFormIntegrator &integ,
|
||||
const Vector &x,
|
||||
const Array<int> *markers,
|
||||
const Array<int> &attributes,
|
||||
const bool transpose,
|
||||
Vector &y) const;
|
||||
Vector &y,
|
||||
const bool useAbs = false) const;
|
||||
|
||||
/// @brief Performs the same function as AddMultWithMarkers, but takes as
|
||||
/// input and output face normal derivatives.
|
||||
@@ -152,8 +159,15 @@ public:
|
||||
EABilinearFormExtension(BilinearForm *form);
|
||||
|
||||
void Assemble() override;
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y, false); }
|
||||
void AbsMult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y, false, true); }
|
||||
void MultTranspose(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y, true); }
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y, true, true); }
|
||||
|
||||
/// @brief Populates @a element_matrices with the element matrices.
|
||||
///
|
||||
@@ -165,6 +179,10 @@ public:
|
||||
void GetElementMatrices(DenseTensor &element_matrices,
|
||||
ElementDofOrdering ordering,
|
||||
bool add_bdr);
|
||||
|
||||
// This method needs to be public due to 'nvcc' restriction.
|
||||
void MultInternal(const Vector &x, Vector &y, const bool useTranspose,
|
||||
const bool useAbs = false) const;
|
||||
};
|
||||
|
||||
/// Data and methods for fully-assembled bilinear forms
|
||||
|
||||
@@ -121,6 +121,12 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddAbsMultPA(const Vector &, Vector &) const
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator:AddAbsMultPA:(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddMultNURBSPA(const Vector &, Vector &) const
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AddMultNURBSPA(...)\n"
|
||||
@@ -133,6 +139,13 @@ void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddAbsMultTransposePA(const Vector &,
|
||||
Vector &) const
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AddAbsMultTransposePA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleMF(...)\n"
|
||||
@@ -418,6 +431,14 @@ void SumIntegrator::AddMultPA(const Vector& x, Vector& y) const
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddAbsMultPA(const Vector& x, Vector& y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddAbsMultPA(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
@@ -426,6 +447,14 @@ void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddAbsMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddAbsMultTransposePA(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
|
||||
@@ -78,6 +78,8 @@ public:
|
||||
called. */
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
|
||||
virtual void AddAbsMultPA(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Method for partially assembled action on NURBS patches.
|
||||
virtual void AddMultNURBSPA(const Vector&x, Vector&y) const;
|
||||
|
||||
@@ -90,6 +92,8 @@ public:
|
||||
called. */
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void AddAbsMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Method defining element assembly.
|
||||
/** The result of the element assembly is added to the @a emat Vector if
|
||||
@a add is true. Otherwise, if @a add is false, we set @a emat. */
|
||||
@@ -496,8 +500,12 @@ public:
|
||||
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AddAbsMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AddMultPA(const Vector& x, Vector& y) const override;
|
||||
|
||||
void AddAbsMultPA(const Vector& x, Vector& y) const override;
|
||||
|
||||
void AssembleMF(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
@@ -2320,8 +2328,12 @@ public:
|
||||
|
||||
void AddMultPA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddAbsMultPA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddMultTransposePA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddAbsMultTransposePA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddMultNURBSPA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
|
||||
@@ -2419,8 +2431,12 @@ public:
|
||||
|
||||
void AddMultPA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddAbsMultPA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddMultTransposePA(const Vector&, Vector&) const override;
|
||||
|
||||
void AddAbsMultTransposePA(const Vector&, Vector&) const override;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans);
|
||||
@@ -2816,6 +2832,7 @@ public:
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddAbsMultPA(const Vector &x, Vector &y) const override;
|
||||
void AssembleDiagonalPA(Vector& diag) override;
|
||||
|
||||
const Coefficient *GetCoefficient() const { return Q; }
|
||||
@@ -2933,6 +2950,7 @@ public:
|
||||
void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes) override;
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddAbsMultPA(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,
|
||||
|
||||
+715
@@ -0,0 +1,715 @@
|
||||
// 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.
|
||||
|
||||
// Implementation of bounds
|
||||
|
||||
#include "bounds.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
const int b_type_i, const int cp_type_i,
|
||||
const real_t tol_i)
|
||||
{
|
||||
MFEM_VERIFY(b_type_i >= 0 && b_type_i <= 2, "Bases not supported. "
|
||||
"Please read class description to see supported types.");
|
||||
MFEM_VERIFY(cp_type_i == 0 || cp_type_i == 1,
|
||||
"Control point type not supported. Please read class "
|
||||
"description to see supported types.");
|
||||
nb = nb_i;
|
||||
ncp = ncp_i;
|
||||
b_type = b_type_i;
|
||||
cp_type = cp_type_i;
|
||||
tol = tol_i;
|
||||
lbound.SetSize(nb, ncp);
|
||||
ubound.SetSize(nb, ncp);
|
||||
nodes.SetSize(nb);
|
||||
weights.SetSize(nb);
|
||||
control_points.SetSize(ncp);
|
||||
|
||||
auto scalenodes = [](const Vector &in, const real_t a, const real_t b) -> Vector
|
||||
{
|
||||
Vector outVec(in.Size());
|
||||
real_t maxv = in.Max();
|
||||
real_t minv = in.Min();
|
||||
for (int i = 0; i < in.Size(); i++)
|
||||
{
|
||||
outVec(i) = a + (b-a)*(in(i)-minv)/(maxv-minv);
|
||||
}
|
||||
return outVec;
|
||||
};
|
||||
MFEM_VERIFY(ncp >= 2,"At least 2 control points are required.");
|
||||
|
||||
if (cp_type == 0) // GL + End Point
|
||||
{
|
||||
control_points(0) = 0.0;
|
||||
control_points(ncp-1) = 1.0;
|
||||
if (ncp > 2)
|
||||
{
|
||||
const real_t *x = poly1d.GetPoints(ncp-3, 0);
|
||||
MFEM_VERIFY(x, "Error in getting points.");
|
||||
for (int i = 0; i < ncp-2; i++)
|
||||
{
|
||||
control_points(i+1) = x[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cp_type == 1) // Chebyshev
|
||||
{
|
||||
auto GetChebyshevNodes = [](int n) -> Vector
|
||||
{
|
||||
Vector cheb(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
cheb(i) = -cos(M_PI * (static_cast<real_t>(i) / (n - 1)));
|
||||
}
|
||||
return cheb;
|
||||
};
|
||||
control_points = GetChebyshevNodes(ncp);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported interval points. Use [0,1].\n");
|
||||
}
|
||||
control_points = scalenodes(control_points, 0.0, 1.0); // rescale to [0,1]
|
||||
|
||||
Poly_1D::Basis &basis1d(poly1d.GetBasis(nb-1, b_type));
|
||||
|
||||
// Initialize bounds
|
||||
lbound = 0.0;
|
||||
ubound = 0.0;
|
||||
|
||||
Vector bmv(nb), bpv(nb), bv(nb); // basis values
|
||||
Vector bdmv(nb), bdpv(nb), bdv(nb); // basis derivative values
|
||||
Vector vals(3);
|
||||
|
||||
// See Section 3.1.1 of https://arxiv.org/pdf/2501.12349 for explanation of
|
||||
// procedure below.
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
real_t x = control_points(j);
|
||||
real_t xm = x;
|
||||
if (j != 0)
|
||||
{
|
||||
xm = 0.5*(control_points(j-1)+control_points(j));
|
||||
}
|
||||
real_t xp = x;
|
||||
if (j != ncp-1)
|
||||
{
|
||||
xp = 0.5*(control_points(j)+control_points(j+1));
|
||||
}
|
||||
basis1d.Eval(xm, bmv, bdmv);
|
||||
basis1d.Eval(xp, bpv, bdpv);
|
||||
basis1d.Eval(x, bv);
|
||||
real_t dm = x-xm;
|
||||
real_t dp = x-xp;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else if (j == ncp-1)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals(0) = bv(i);
|
||||
vals(1) = bmv(i) + dm*bdmv(i);
|
||||
vals(2) = bpv(i) + dp*bdpv(i);
|
||||
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IntegrationRule irule(nb);
|
||||
if (b_type == 0)
|
||||
{
|
||||
QuadratureFunctions1D::GaussLegendre(nb, &irule);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
weights(i) = irule.IntPoint(i).weight;
|
||||
nodes(i) = irule.IntPoint(i).x;
|
||||
}
|
||||
}
|
||||
else if (b_type == 1)
|
||||
{
|
||||
QuadratureFunctions1D::GaussLobatto(nb, &irule);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
weights(i) = irule.IntPoint(i).weight;
|
||||
nodes(i) = irule.IntPoint(i).x;
|
||||
}
|
||||
}
|
||||
else if (b_type == 2)
|
||||
{
|
||||
QuadratureFunctions1D::ClosedUniform(nb, &irule);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
weights(i) = irule.IntPoint(i).weight;
|
||||
nodes(i) = irule.IntPoint(i).x;
|
||||
}
|
||||
}
|
||||
|
||||
if (b_type == 2)
|
||||
{
|
||||
nodes_int.SetSize(nb);
|
||||
weights_int.SetSize(nb);
|
||||
IntegrationRule irule_int(nb);
|
||||
{
|
||||
QuadratureFunctions1D::GaussLobatto(nb, &irule_int);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
weights_int(i) = irule_int.IntPoint(i).weight;
|
||||
nodes_int(i) = irule_int.IntPoint(i).x;
|
||||
}
|
||||
}
|
||||
|
||||
SetupBernsteinBasisMat(basisMatNodes, nodes);
|
||||
// Setup memory for lu factors
|
||||
basisMatLU = basisMatNodes;
|
||||
lu_ip.SetSize(nb);
|
||||
// Compute lu factors
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
bool factor = lu.Factor(nb);
|
||||
MFEM_VERIFY(factor,"Failure in LU factorization in PLBound.");
|
||||
|
||||
// Setup the Bernstein basis matrix for the GLL integration points. This
|
||||
// is used to compute linear fit.
|
||||
SetupBernsteinBasisMat(basisMatInt, nodes_int);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes_int.SetDataAndSize(nodes.GetData(), nb);
|
||||
weights_int.SetDataAndSize(weights.GetData(), nb);
|
||||
}
|
||||
}
|
||||
|
||||
PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
|
||||
{
|
||||
MFEM_VERIFY(!fes->IsVariableOrder(),
|
||||
"Variable order meshes not yet supported.");
|
||||
const char *name = fes->FEColl()->Name();
|
||||
string cname = name;
|
||||
|
||||
cp_type = cp_type_i;
|
||||
b_type = BasisType::Invalid;
|
||||
nb = fes->GetMaxElementOrder()+1;
|
||||
tol = 0.0;
|
||||
|
||||
int minncp = 2;
|
||||
if (nb > 12)
|
||||
{
|
||||
minncp = 2*nb;
|
||||
}
|
||||
else if (!strncmp(name, "H1_", 3) && strncmp(name, "H1_Trace_", 9))
|
||||
{
|
||||
// H1 GLL
|
||||
b_type = BasisType::GaussLobatto;
|
||||
minncp = min_ncp_gll_x[cp_type][nb-2];
|
||||
}
|
||||
else if (!strncmp(name, "H1Pos_", 6) && strncmp(name, "H1Pos_Trace_", 12))
|
||||
{
|
||||
// H1 Positive
|
||||
b_type = BasisType::Positive;
|
||||
minncp = min_ncp_pos_x[cp_type][nb-2];
|
||||
}
|
||||
else if (!strncmp(name, "L2_", 3) && strncmp(name, "L2_T", 4))
|
||||
{
|
||||
// L2 Gauss-Legendre
|
||||
b_type = BasisType::GaussLegendre;
|
||||
minncp = min_ncp_gl_x[cp_type][nb-2];
|
||||
}
|
||||
else if (!strncmp(name, "L2_T1", 5))
|
||||
{
|
||||
// L2 GLL
|
||||
b_type = BasisType::GaussLobatto;
|
||||
minncp = min_ncp_gll_x[cp_type][nb-2];
|
||||
}
|
||||
else if (!strncmp(name, "L2_T2", 5))
|
||||
{
|
||||
// L2 Positive
|
||||
b_type = BasisType::Positive;
|
||||
minncp = min_ncp_pos_x[cp_type][nb-2];
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Only H1 GLL/Positive & L2 GL/GLL/Positive bases supported.");
|
||||
}
|
||||
|
||||
ncp = std::max(minncp, ncp_i);
|
||||
|
||||
Setup(nb, ncp, b_type, cp_type, tol);
|
||||
}
|
||||
|
||||
void PLBound::Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
real_t x,w;
|
||||
intmin.SetSize(ncp);
|
||||
intmax.SetSize(ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector coeffm(nb);
|
||||
coeffm = 0.0;
|
||||
|
||||
real_t a0 = 0.0;
|
||||
real_t a1 = 0.0;
|
||||
|
||||
Vector nodal_vals, nodal_integ_vals;
|
||||
if (b_type == 2) // compute values at equispaced nodes and GLL nodes
|
||||
{
|
||||
nodal_vals.SetSize(nb);
|
||||
nodal_integ_vals.SetSize(nb);
|
||||
Vector shape(nb);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
basisMatNodes.GetRow(i, shape);
|
||||
nodal_vals(i) = shape*coeff;
|
||||
basisMatInt.GetRow(i, shape);
|
||||
nodal_integ_vals(i) = shape*coeff;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodal_vals.SetDataAndSize(coeff.GetData(), nb);
|
||||
nodal_integ_vals.SetDataAndSize(coeff.GetData(), nb);
|
||||
}
|
||||
|
||||
// compute L2 projection for linear bases: a0 + a1*x
|
||||
if (proj)
|
||||
{
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1;
|
||||
w = 2.0*weights_int(i);
|
||||
a0 += 0.5*nodal_integ_vals(i)*w;
|
||||
a1 += 1.5*nodal_integ_vals(i)*w*x;
|
||||
}
|
||||
|
||||
// offset the linear fit from nodal values
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1;
|
||||
coeffm(i) = nodal_vals(i) - a0 - a1*x;
|
||||
}
|
||||
|
||||
// compute coefficients for Bernstein
|
||||
if (b_type == 2)
|
||||
{
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, coeffm.GetData());
|
||||
}
|
||||
|
||||
// initialize the bounds to be the linear fit
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
x = 2.0*control_points(j)-1;
|
||||
intmin(j) = a0 + a1*x;
|
||||
intmax(j) = intmin(j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coeffm.SetDataAndSize(coeff.GetData(), nb);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
real_t c = coeffm(i);
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
intmin.SetSize(ncp*ncp);
|
||||
intmax.SetSize(ncp*ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector intminT(ncp*nb);
|
||||
Vector intmaxT(ncp*nb);
|
||||
// Get bounds for each row of the solution
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
Vector solcoeff(coeff.GetData()+i*nb, nb);
|
||||
Vector intminrow(intminT.GetData()+i*ncp, ncp);
|
||||
Vector intmaxrow(intmaxT.GetData()+i*ncp, ncp);
|
||||
Get1DBounds(solcoeff, intminrow, intmaxrow);
|
||||
}
|
||||
Vector intminT2 = intminT;
|
||||
|
||||
// Compute a0 and a1 for each column of nodes
|
||||
Vector a0V(ncp), a1V(ncp);
|
||||
a0V = 0.0;
|
||||
a1V = 0.0;
|
||||
real_t x,w,t;
|
||||
if (proj)
|
||||
{
|
||||
if (b_type == 2)
|
||||
{
|
||||
// Note: DenseMatrix uses column-major ordering so we will need to
|
||||
// transpose the matrix.
|
||||
DenseMatrix intminTM(intminT.GetData(), ncp, nb),
|
||||
intmaxTM(intmaxT.GetData(), ncp, nb),
|
||||
intmeanTM(ncp, nb);
|
||||
DenseMatrix minvalsM(nb, ncp), maxvalsM(nb, ncp), meanintvalsM(nb, ncp);
|
||||
MultABt(basisMatNodes, intminTM, minvalsM);
|
||||
MultABt(basisMatNodes, intmaxTM, maxvalsM);
|
||||
intmeanTM = intminTM;
|
||||
intmeanTM += intmaxTM;
|
||||
intmeanTM *= 0.5;
|
||||
MultABt(basisMatInt, intmeanTM, meanintvalsM);
|
||||
|
||||
// Compute the linear fit along each column and then offset it from
|
||||
// the bounds on the coefficient.
|
||||
// Note: Since Bernstein bases are positive, we can use the lower
|
||||
// bounds to compute the lower bounding polynomial and subtract the
|
||||
// linear fit before finding the Bernstein coefficients corresponding
|
||||
// to the perturbation. Same for upper bounds. If the bases were not
|
||||
// always positive, it is not yet clear if the perturbation
|
||||
// coefficients will be this straightforward to compute.
|
||||
for (int j = 0; j < ncp; j++) // row of interval points
|
||||
{
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1; // x-coordinate
|
||||
w = 2.0*weights_int(i); // weight
|
||||
t = meanintvalsM(i,j);
|
||||
a0V(j) += 0.5*t*w;
|
||||
a1V(j) += 1.5*t*w*x;
|
||||
}
|
||||
// Offset linear fit
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minvalsM(i,j) -= a0V(j) + a1V(j)*x;
|
||||
maxvalsM(i,j) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minvalsM.GetColumn(j));
|
||||
lu.Solve(nb, 1, maxvalsM.GetColumn(j));
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp+j) = minvalsM(i,j);
|
||||
intmaxT(i*ncp+j) = maxvalsM(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < nb; j++) // row of nodes
|
||||
{
|
||||
x = 2.0*nodes(j)-1; // x-coordinate
|
||||
w = 2.0*weights(j); // weight
|
||||
for (int i = 0; i < ncp; i++) // column of interval points
|
||||
{
|
||||
t = 0.5*(intminT(j*ncp+i)+intmaxT(j*ncp+i));
|
||||
a0V(i) += 0.5*t*w;
|
||||
a1V(i) += 1.5*t*w*x;
|
||||
}
|
||||
}
|
||||
// offset the linear fit from nodal values
|
||||
for (int j = 0; j < nb; j++) // row of nodes
|
||||
{
|
||||
x = 2.0*nodes(j)-1; // x-coordinate
|
||||
for (int i = 0; i < ncp; i++) // column of interval points
|
||||
{
|
||||
t = a0V(i) + a1V(i)*x;
|
||||
intminT(j*ncp+i) -= t;
|
||||
intmaxT(j*ncp+i) -= t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize bounds using a0 and a1 values
|
||||
for (int j = 0; j < ncp; j++) // row j
|
||||
{
|
||||
x = 2.0*control_points(j)-1;
|
||||
for (int i = 0; i < ncp; i++) // column i
|
||||
{
|
||||
intmin(j*ncp+i) = a0V(i) + a1V(i)*x;
|
||||
intmax(j*ncp+i) = intmin(j*ncp+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute bounds
|
||||
int id1 = 0, id2 = 0;
|
||||
Vector vals(4);
|
||||
for (int j = 0; j < nb; j++)
|
||||
{
|
||||
for (int i = 0; i < ncp; i++) // ith column
|
||||
{
|
||||
real_t w0 = intminT(id1++);
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth row
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp+i) += vals.Min();
|
||||
intmax(k*ncp+i) += vals.Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
int nb2 = nb*nb,
|
||||
ncp2 = ncp*ncp,
|
||||
ncp3 = ncp*ncp*ncp;
|
||||
|
||||
intmin.SetSize(ncp3);
|
||||
intmax.SetSize(ncp3);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector intminT(ncp2*nb);
|
||||
Vector intmaxT(ncp2*nb);
|
||||
|
||||
// Get bounds for each slice of the solution
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
Vector solcoeff(coeff.GetData()+i*nb2, nb2);
|
||||
Vector intminrow(intminT.GetData()+i*ncp2, ncp2);
|
||||
Vector intmaxrow(intmaxT.GetData()+i*ncp2, ncp2);
|
||||
Get2DBounds(solcoeff, intminrow, intmaxrow);
|
||||
}
|
||||
DenseMatrix intminTM(intminT.GetData(), ncp2, nb),
|
||||
intmaxTM(intmaxT.GetData(), ncp2, nb);
|
||||
|
||||
// Compute a0 and a1 for each tower of nodes
|
||||
Vector a0V(ncp2), a1V(ncp2);
|
||||
a0V = 0.0;
|
||||
a1V = 0.0;
|
||||
real_t x,w,t;
|
||||
if (proj)
|
||||
{
|
||||
if (b_type == 2) // Bernstein bases
|
||||
{
|
||||
// Compute the mean coefficients along each tower.
|
||||
for (int j = 0; j < ncp2; j++) // slice of interval points
|
||||
{
|
||||
Vector meanBounds(nb), minBounds(nb), maxBounds(nb);
|
||||
intminTM.GetRow(j, minBounds);
|
||||
intmaxTM.GetRow(j, maxBounds);
|
||||
for (int i = 0; i < nb; i++) // column of nodes
|
||||
{
|
||||
meanBounds(i) = 0.5*(minBounds(i)+maxBounds(i));
|
||||
}
|
||||
Vector meanNodalIntVals(nb);
|
||||
Vector minNodalVals(nb);
|
||||
Vector maxNodalVals(nb);
|
||||
Vector row(nb);
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
basisMatNodes.GetRow(i, row);
|
||||
minNodalVals(i) = row*minBounds;
|
||||
maxNodalVals(i) = row*maxBounds;
|
||||
basisMatInt.GetRow(i, row);
|
||||
meanNodalIntVals(i) = row*meanBounds;
|
||||
}
|
||||
// linear fit along each tower
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1; // x-coordinate
|
||||
w = 2.0*weights_int(i); // weight
|
||||
a0V(j) += 0.5*meanNodalIntVals(i)*w;
|
||||
a1V(j) += 1.5*meanNodalIntVals(i)*w*x;
|
||||
}
|
||||
// offset the linear fit from bounding coefficients
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
maxBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minBounds.GetData());
|
||||
lu.Solve(nb, 1, maxBounds.GetData());
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp2+j) = minBounds(i);
|
||||
intmaxT(i*ncp2+j) = maxBounds(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nodal bases
|
||||
for (int j = 0; j < nb; j++) // tower of nodes
|
||||
{
|
||||
x = 2.0*nodes(j)-1; // x-coordinate
|
||||
w = 2.0*weights(j); // weight
|
||||
for (int i = 0; i < ncp2; i++) // slice of interval points
|
||||
{
|
||||
t = 0.5*(intminT(j*ncp2+i)+intmaxT(j*ncp2+i));
|
||||
a0V(i) += 0.5*t*w;
|
||||
a1V(i) += 1.5*t*w*x;
|
||||
}
|
||||
}
|
||||
// offset the linear fit from nodal values
|
||||
for (int j = 0; j < nb; j++) // row of nodes
|
||||
{
|
||||
x = 2.0*nodes(j)-1; // x-coordinate
|
||||
for (int i = 0; i < ncp2; i++) // column of interval points
|
||||
{
|
||||
t = a0V(i) + a1V(i)*x;
|
||||
intminT(j*ncp2+i) -= t;
|
||||
intmaxT(j*ncp2+i) -= t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize bounds using a0 and a1 values
|
||||
for (int j = 0; j < ncp; j++) // slice j
|
||||
{
|
||||
x = 2.0*control_points(j)-1;
|
||||
for (int i = 0; i < ncp2; i++) // tower i
|
||||
{
|
||||
intmin(j*ncp2+i) = a0V(i) + a1V(i)*x;
|
||||
intmax(j*ncp2+i) = a0V(i) + a1V(i)*x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute bounds
|
||||
int id1 = 0, id2 = 0;
|
||||
Vector vals(4);
|
||||
for (int j = 0; j < nb; j++)
|
||||
{
|
||||
for (int i = 0; i < ncp2; i++) // ith tower
|
||||
{
|
||||
real_t w0 = intminT(id1++);
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth slice
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp2+i) += vals.Min();
|
||||
intmax(k*ncp2+i) += vals.Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
if (rdim == 1)
|
||||
{
|
||||
Get1DBounds(coeff, intmin, intmax);
|
||||
}
|
||||
else if (rdim == 2)
|
||||
{
|
||||
Get2DBounds(coeff, intmin, intmax);
|
||||
}
|
||||
else if (rdim == 3)
|
||||
{
|
||||
Get3DBounds(coeff, intmin, intmax);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Currently not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
Vector &nodesBern) const
|
||||
{
|
||||
const int nbern = nodesBern.Size();
|
||||
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
|
||||
Array<int> ordering = el.GetLexicographicOrdering();
|
||||
basisMat.SetSize(nbern, nbern);
|
||||
Vector shape(nbern);
|
||||
IntegrationPoint ip;
|
||||
for (int i = 0; i < nbern; i++)
|
||||
{
|
||||
ip.x = nodesBern(i);
|
||||
el.CalcShape(ip, shape);
|
||||
basisMat.SetRow(i, shape);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int PLBound::min_ncp_gl_x[2][11];
|
||||
constexpr int PLBound::min_ncp_gll_x[2][11];
|
||||
constexpr int PLBound::min_ncp_pos_x[2][11];
|
||||
|
||||
int PLBound::GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const
|
||||
{
|
||||
MFEM_VERIFY(b_type_i >= 0 && b_type_i <= 2, "Invalid node type. Specify 0 "
|
||||
"for GL, 1 for GLL, and 2 for positive " "bases.");
|
||||
MFEM_VERIFY(cp_type_i == 0 || cp_type_i == 1, "Invalid control point type. "
|
||||
"Specify 0 for GL+end points, 1 for Chebyshev.");
|
||||
if (nb_i > 12)
|
||||
{
|
||||
MFEM_ABORT("GetMinimumPointsForGivenBases can only be used for maximum "
|
||||
"order = 11, i.e. nb=12. 2*nb points should be sufficient to "
|
||||
"bound the bases up to nb = 30.");
|
||||
}
|
||||
else if (b_type_i == 0)
|
||||
{
|
||||
return min_ncp_gl_x[cp_type_i][nb_i-2];
|
||||
}
|
||||
else if (b_type_i == 1)
|
||||
{
|
||||
return min_ncp_gll_x[cp_type_i][nb_i-2];
|
||||
}
|
||||
else if (b_type_i == 2)
|
||||
{
|
||||
return min_ncp_pos_x[cp_type_i][nb_i-2];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PLBound::Print(std::ostream &outp) const
|
||||
{
|
||||
outp << "PLBound nb: " << nb << std::endl;
|
||||
outp << "PLBound ncp: " << ncp << std::endl;
|
||||
outp << "PLBound b_type: " << b_type << std::endl;
|
||||
outp << "PLBound cp_type: " << cp_type << std::endl;
|
||||
outp << "Print nodes: " << std::endl;
|
||||
nodes.Print(outp);
|
||||
outp << "Print weights: " << std::endl;
|
||||
weights.Print(outp);
|
||||
outp << "Print control_points: " << std::endl;
|
||||
control_points.Print(outp);
|
||||
outp << "Print lower bounds: " << std::endl;
|
||||
lbound.Print(outp);
|
||||
outp << "Print upper bounds: " << std::endl;
|
||||
ubound.Print(outp);
|
||||
}
|
||||
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
// 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_BOUND
|
||||
#define MFEM_BOUND
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @name Piecewise linear bounds of bases
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
|
||||
|
||||
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
|
||||
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
2 - Positive/Bernstein bases on uniformly distributed nodes,
|
||||
|
||||
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
|
||||
|
||||
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
|
||||
1 - Chebyshev.
|
||||
|
||||
Note: @b nb and @b b_type are inferred directly from the grid-function.
|
||||
|
||||
If the user does not specify @b ncp and @b cp_type, the minimum value of
|
||||
@b ncp is used that would bound the bases for the @b cp_type. We default
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
|
||||
|
||||
Finally, only tensor-product elements are currently supported.
|
||||
|
||||
For more technical details see:
|
||||
Mittal et al., "General Field Evaluation in High-Order Meshes on GPUs" &
|
||||
Dzanic et al., "A method for bounding high-order finite element
|
||||
functions: Applications to mesh validity and bounds-preserving limiters".
|
||||
*/
|
||||
class PLBound
|
||||
{
|
||||
private:
|
||||
int nb; // #mesh nodes in 1D
|
||||
int ncp; // #control points in 1D
|
||||
int b_type; // bases type: 0 - GL, 1 - GLL, 2 - Bernstein
|
||||
int cp_type; // control points type: 0 - GL+Ends, 1 - Chebyshev
|
||||
bool proj = true; // Use linear projection to compute bounds.
|
||||
real_t tol = 0.0; // offset bounds to avoid round-off errors
|
||||
Vector nodes, weights, control_points;
|
||||
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
|
||||
// Some auxillary storage for computing the bounds with Bernstein
|
||||
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
|
||||
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
|
||||
Vector nodes_int, weights_int; // Integration nodes and weights
|
||||
DenseMatrix basisMatLU; // Used to compute LU factors for Bernstein
|
||||
mutable Array<int> lu_ip;
|
||||
|
||||
// stores min_ncp for nb = 2..12 for Lagrange interpolants on GL nodes
|
||||
// with GL+end points and Chebyshev points as control points
|
||||
static constexpr int min_ncp_gl_x[2][11]= {{3,5,6,8,9,10,11,11,12,13,14},
|
||||
{3,5,8,9,11,12,14,15,17,18,20}
|
||||
};
|
||||
|
||||
// stores min_ncp for nb = 2..12 for Lagrange interpolants on GLL nodes
|
||||
// with GL+end points and Chebyshev points as control points
|
||||
static constexpr int min_ncp_gll_x[2][11]= {{3,5,7,8,9,10,12,13,14,15,16},
|
||||
{3,5,8,10,12,13,15,17,19,21,22}
|
||||
};
|
||||
|
||||
// stores min_ncp for nb = 2..12 for Bernstein bases with GL+end points
|
||||
// and Chebyshev points as control points
|
||||
static constexpr int min_ncp_pos_x[2][11]= {{3,5,7,8,8,9,10,10,11,12,13},
|
||||
{3,5,8,9,11,12,13,13,14,15,16}
|
||||
};
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
const int cp_type_i, const real_t tol_i)
|
||||
{
|
||||
Setup(nb_i, ncp_i, b_type_i, cp_type_i, tol_i);
|
||||
}
|
||||
|
||||
// Constructor
|
||||
PLBound(FiniteElementSpace *fes, int ncp_i = -1, int cp_type_i = 0);
|
||||
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const;
|
||||
|
||||
// Print information about the bounds
|
||||
void Print(std::ostream &outp = mfem::out) const;
|
||||
|
||||
// Enable (default) or disable linear projection before bounding.
|
||||
// This projection increases the computational cost but results in tighter
|
||||
// bounds.
|
||||
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
void GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
int GetNControlPoints() const { return ncp; }
|
||||
private:
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
void Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
void Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
void Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
|
||||
|
||||
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
const int cp_type_i, const real_t tol_i);
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_BOUND
|
||||
@@ -0,0 +1,217 @@
|
||||
// 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.
|
||||
|
||||
#include "complex_fem.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
real_t
|
||||
RealPartCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_t val = complex_coef_.Eval(T, ip);
|
||||
return val.real();
|
||||
}
|
||||
|
||||
real_t
|
||||
ImagPartCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_t val = complex_coef_.Eval(T, ip);
|
||||
return val.imag();
|
||||
}
|
||||
|
||||
RealPartVectorCoefficient::RealPartVectorCoefficient(ComplexVectorCoefficient &
|
||||
complex_vcoef)
|
||||
: VectorCoefficient(complex_vcoef.GetVDim()),
|
||||
complex_vcoef_(complex_vcoef),
|
||||
val_(vdim)
|
||||
{}
|
||||
|
||||
void
|
||||
RealPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_vcoef_.Eval(val_, T, ip);
|
||||
V = val_.real();
|
||||
}
|
||||
|
||||
ImagPartVectorCoefficient::ImagPartVectorCoefficient(ComplexVectorCoefficient &
|
||||
complex_vcoef)
|
||||
: VectorCoefficient(complex_vcoef.GetVDim()),
|
||||
complex_vcoef_(complex_vcoef),
|
||||
val_(vdim)
|
||||
{}
|
||||
|
||||
void
|
||||
ImagPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_vcoef_.Eval(val_, T, ip);
|
||||
V = val_.imag();
|
||||
}
|
||||
|
||||
RealPartMatrixCoefficient::RealPartMatrixCoefficient(ComplexMatrixCoefficient &
|
||||
complex_mcoef)
|
||||
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
|
||||
complex_mcoef_(complex_mcoef),
|
||||
val_(height, width)
|
||||
{}
|
||||
|
||||
void
|
||||
RealPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_mcoef_.Eval(val_, T, ip);
|
||||
M = val_.real();
|
||||
}
|
||||
|
||||
ImagPartMatrixCoefficient::ImagPartMatrixCoefficient(ComplexMatrixCoefficient &
|
||||
complex_mcoef)
|
||||
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
|
||||
complex_mcoef_(complex_mcoef),
|
||||
val_(height, width)
|
||||
{}
|
||||
|
||||
void
|
||||
ImagPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_mcoef_.Eval(val_, T, ip);
|
||||
M = val_.imag();
|
||||
}
|
||||
|
||||
ComplexCoefficient::ComplexCoefficient()
|
||||
: time(0.),
|
||||
re_part_coef_(*this), im_part_coef_(*this),
|
||||
real_coef_(re_part_coef_), imag_coef_(im_part_coef_)
|
||||
{ }
|
||||
|
||||
ComplexCoefficient::ComplexCoefficient(Coefficient &c_r,
|
||||
Coefficient &c_i)
|
||||
: time(c_r.GetTime()),
|
||||
re_part_coef_(*this), im_part_coef_(*this),
|
||||
real_coef_(c_r), imag_coef_(c_i)
|
||||
{
|
||||
c_i.SetTime(time);
|
||||
}
|
||||
|
||||
complex_t
|
||||
ComplexCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
// Avoid circular dependency
|
||||
MFEM_VERIFY(std::addressof(real_coef_) != std::addressof(re_part_coef_) &&
|
||||
std::addressof(imag_coef_) != std::addressof(im_part_coef_),
|
||||
"Classes dervied from ComplexCoefficient must either "
|
||||
"implement an Eval method or supply Coefficients "
|
||||
"for both the real and imaginary parts of the field.");
|
||||
|
||||
return complex_t(real_coef_.Eval(T, ip), imag_coef_.Eval(T, ip));
|
||||
}
|
||||
|
||||
ComplexVectorCoefficient::ComplexVectorCoefficient(VectorCoefficient &v_r,
|
||||
VectorCoefficient &v_i)
|
||||
: vdim(v_r.GetVDim()), time(v_r.GetTime()),
|
||||
re_part_vcoef_(*this), im_part_vcoef_(*this),
|
||||
real_vcoef_(v_r), imag_vcoef_(v_i)
|
||||
{
|
||||
MFEM_ASSERT(v_r.GetVDim() == v_i.GetVDim(), "ComplexVectorCoefficient"
|
||||
" - incompatible vector dimensions of real and imaginary parts.");
|
||||
|
||||
v_i.SetTime(time);
|
||||
}
|
||||
|
||||
void ComplexVectorCoefficient::Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
// Avoid circular dependency
|
||||
MFEM_VERIFY(std::addressof(real_vcoef_) != std::addressof(re_part_vcoef_) &&
|
||||
std::addressof(imag_vcoef_) != std::addressof(im_part_vcoef_),
|
||||
"Classes dervied from ComplexVectorCoefficient must either "
|
||||
"implement an Eval method or supply VectorCoefficients "
|
||||
"for both the real and imaginary parts of the field.");
|
||||
|
||||
V_r_.SetSize(vdim);
|
||||
V_i_.SetSize(vdim);
|
||||
|
||||
real_vcoef_.Eval(V_r_, T, ip);
|
||||
imag_vcoef_.Eval(V_i_, T, ip);
|
||||
|
||||
V.Set(V_r_, V_i_);
|
||||
}
|
||||
|
||||
ComplexConstantCoefficient::ComplexConstantCoefficient(
|
||||
const complex_t z)
|
||||
: val(z), real_coef(z.real()), imag_coef(z.imag())
|
||||
{
|
||||
real_coef_ = real_coef;
|
||||
imag_coef_ = imag_coef;
|
||||
}
|
||||
|
||||
ComplexConstantCoefficient::ComplexConstantCoefficient(
|
||||
real_t z_r, real_t z_i)
|
||||
: real_coef(z_r), imag_coef(z_i)
|
||||
{
|
||||
val = complex_t(z_r, z_i);
|
||||
|
||||
real_coef_ = real_coef;
|
||||
imag_coef_ = imag_coef;
|
||||
}
|
||||
|
||||
complex_t ComplexFunctionCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
real_t x[3];
|
||||
Vector transip(x, 3);
|
||||
|
||||
T.Transform(ip, transip);
|
||||
|
||||
if (Function)
|
||||
{
|
||||
return Function(transip);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TDFunction(transip, GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexVectorFunctionCoefficient::Eval(ComplexVector &V,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
real_t x[3];
|
||||
Vector transip(x, 3);
|
||||
|
||||
T.Transform(ip, transip);
|
||||
|
||||
V.SetSize(vdim);
|
||||
if (Function)
|
||||
{
|
||||
Function(transip, V);
|
||||
}
|
||||
else
|
||||
{
|
||||
TDFunction(transip, GetTime(), V);
|
||||
}
|
||||
if (Q)
|
||||
{
|
||||
V *= Q->Eval(T, ip, GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace mfem
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
// 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_COMPLEX_COEFFICIENT
|
||||
#define MFEM_COMPLEX_COEFFICIENT
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "intrules.hpp"
|
||||
#include "eltrans.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ComplexCoefficient;
|
||||
class ComplexVectorCoefficient;
|
||||
class ComplexMatrixCoefficient;
|
||||
|
||||
/// Standard Coefficient which returns the real part of a ComplexCoefficient
|
||||
class RealPartCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
ComplexCoefficient &complex_coef_;
|
||||
|
||||
public:
|
||||
RealPartCoefficient(ComplexCoefficient & complex_coef)
|
||||
: complex_coef_(complex_coef) {}
|
||||
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Standard Coefficient which returns the imaginary part of a
|
||||
/// ComplexCoefficient
|
||||
class ImagPartCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
ComplexCoefficient &complex_coef_;
|
||||
|
||||
public:
|
||||
ImagPartCoefficient(ComplexCoefficient & complex_coef)
|
||||
: complex_coef_(complex_coef) {}
|
||||
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartCoefficient ImaginaryPartCoefficient;
|
||||
|
||||
class RealPartVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVectorCoefficient &complex_vcoef_;
|
||||
mutable ComplexVector val_;
|
||||
|
||||
public:
|
||||
RealPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class ImagPartVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVectorCoefficient &complex_vcoef_;
|
||||
mutable ComplexVector val_;
|
||||
|
||||
public:
|
||||
ImagPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartVectorCoefficient ImaginaryPartVectorCoefficient;
|
||||
|
||||
class RealPartMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexMatrixCoefficient &complex_mcoef_;
|
||||
mutable ComplexTypeDenseMatrix val_;
|
||||
|
||||
public:
|
||||
RealPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
|
||||
|
||||
void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class ImagPartMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexMatrixCoefficient &complex_mcoef_;
|
||||
mutable ComplexTypeDenseMatrix val_;
|
||||
|
||||
public:
|
||||
ImagPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
|
||||
|
||||
void Eval(DenseMatrix &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartMatrixCoefficient ImaginaryPartMatrixCoefficient;
|
||||
|
||||
/** @brief Base class ComplexCoefficients that optionally depend on space and
|
||||
time. These are used by the SesquilinearForm, ComplexLinearForm, and
|
||||
ComplexGridFunction classes to represent the physical coefficients in
|
||||
the PDEs that are being discretized. This class can also be used in a more
|
||||
general way to represent functions that don't necessarily belong to a FE
|
||||
space, e.g., to project onto ComplexGridFunctions to use as initial
|
||||
conditions, exact solutions, etc. See, e.g., ex22 for these uses. */
|
||||
class ComplexCoefficient
|
||||
{
|
||||
protected:
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartCoefficient re_part_coef_;
|
||||
ImagPartCoefficient im_part_coef_;
|
||||
|
||||
protected:
|
||||
Coefficient &real_coef_;
|
||||
Coefficient &imag_coef_;
|
||||
|
||||
public:
|
||||
|
||||
ComplexCoefficient();
|
||||
ComplexCoefficient(Coefficient &c_r, Coefficient &c_i);
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t)
|
||||
{ time = t; real_coef_.SetTime(t); imag_coef_.SetTime(t); }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip at time @a t. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip, real_t t)
|
||||
{
|
||||
SetTime(t);
|
||||
return Eval(T, ip);
|
||||
}
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual Coefficient & real() { return real_coef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual Coefficient & imag() { return imag_coef_; }
|
||||
|
||||
virtual ~ComplexCoefficient() { }
|
||||
};
|
||||
|
||||
/** @brief Base class ComplexVectorCoefficients that optionally depend
|
||||
on space and time. These are used by the SesquilinearForm,
|
||||
ComplexLinearForm, and ComplexGridFunction classes to represent
|
||||
the physical vector-valued coefficients in the PDEs that are being
|
||||
discretized. This class can also be used in a more general way to
|
||||
represent functions that don't necessarily belong to a FE space,
|
||||
e.g., to project onto ComplexGridFunctions to use as initial
|
||||
conditions, exact solutions, etc. See, e.g., ex22 for these
|
||||
uses. */
|
||||
class ComplexVectorCoefficient
|
||||
{
|
||||
protected:
|
||||
int vdim;
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartVectorCoefficient re_part_vcoef_;
|
||||
ImagPartVectorCoefficient im_part_vcoef_;
|
||||
|
||||
protected:
|
||||
VectorCoefficient &real_vcoef_;
|
||||
VectorCoefficient &imag_vcoef_;
|
||||
|
||||
mutable Vector V_r_;
|
||||
mutable Vector V_i_;
|
||||
|
||||
public:
|
||||
ComplexVectorCoefficient(int vd)
|
||||
: vdim(vd), time(0.),
|
||||
re_part_vcoef_(*this), im_part_vcoef_(*this),
|
||||
real_vcoef_(re_part_vcoef_), imag_vcoef_(im_part_vcoef_)
|
||||
{ }
|
||||
|
||||
ComplexVectorCoefficient(VectorCoefficient &v_r, VectorCoefficient &v_i);
|
||||
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t)
|
||||
{ time = t; real_vcoef_.SetTime(t); imag_vcoef_.SetTime(t); }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Returns dimension of the vector.
|
||||
int GetVDim() { return vdim; }
|
||||
|
||||
/** @brief Evaluate the vector coefficient in the element described by @a T
|
||||
at the point @a ip, storing the result in @a V. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/** @brief Evaluate the vector coefficient in the element described by @a T
|
||||
at the point @a ip at time @a t, storing the result in @a V. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip, real_t t)
|
||||
{
|
||||
SetTime(t);
|
||||
Eval(V, T, ip);
|
||||
}
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual VectorCoefficient & real() { return real_vcoef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual VectorCoefficient & imag() { return imag_vcoef_; }
|
||||
|
||||
virtual ~ComplexVectorCoefficient() { }
|
||||
};
|
||||
|
||||
/** @brief Base class ComplexMatrixCoefficients that optionally depend
|
||||
on space and time. These are used by the SesquilinearForm,
|
||||
ComplexLinearForm, and ComplexGridFunction classes to represent
|
||||
the physical matrix-valued coefficients in the PDEs that are being
|
||||
discretized. This class can also be used in a more general way to
|
||||
represent functions that don't necessarily belong to a FE space.
|
||||
See, e.g., ex22 for these uses. */
|
||||
class ComplexMatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
int height, width;
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartMatrixCoefficient re_part_mcoef_;
|
||||
ImagPartMatrixCoefficient im_part_mcoef_;
|
||||
|
||||
protected:
|
||||
MatrixCoefficient &real_mcoef_;
|
||||
MatrixCoefficient &imag_mcoef_;
|
||||
|
||||
mutable DenseMatrix M_r_;
|
||||
mutable DenseMatrix M_i_;
|
||||
|
||||
public:
|
||||
/// Construct a dim x dim matrix coefficient.
|
||||
explicit ComplexMatrixCoefficient(int dim)
|
||||
: height(dim), width(dim), time(0.),
|
||||
re_part_mcoef_(*this), im_part_mcoef_(*this),
|
||||
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
|
||||
{ }
|
||||
|
||||
/// Construct a h x w matrix coefficient.
|
||||
ComplexMatrixCoefficient(int h, int w) :
|
||||
height(h), width(w), time(0.),
|
||||
re_part_mcoef_(*this), im_part_mcoef_(*this),
|
||||
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
|
||||
{ }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Get the height of the matrix.
|
||||
int GetHeight() const { return height; }
|
||||
|
||||
/// Get the width of the matrix.
|
||||
int GetWidth() const { return width; }
|
||||
|
||||
/// For backward compatibility get the width of the matrix.
|
||||
int GetVDim() const { return width; }
|
||||
|
||||
/** @brief Evaluate the matrix coefficient in the element described by @a T
|
||||
at the point @a ip, storing the result in @a K. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual void Eval(ComplexTypeDenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) = 0;
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual MatrixCoefficient & real() { return real_mcoef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual MatrixCoefficient & imag() { return imag_mcoef_; }
|
||||
|
||||
virtual ~ComplexMatrixCoefficient() { }
|
||||
};
|
||||
|
||||
/// A complex-valued coefficient that is constant across space and time
|
||||
class ComplexConstantCoefficient : public ComplexCoefficient
|
||||
{
|
||||
private:
|
||||
complex_t val;
|
||||
|
||||
ConstantCoefficient real_coef;
|
||||
ConstantCoefficient imag_coef;
|
||||
|
||||
public:
|
||||
ComplexConstantCoefficient(const complex_t z);
|
||||
|
||||
ComplexConstantCoefficient(real_t z_r, real_t z_i = 0.);
|
||||
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) { return val; }
|
||||
};
|
||||
|
||||
/// Complex-valued vector coefficient that is constant in space and time.
|
||||
class ComplexVectorConstantCoefficient : public ComplexVectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVector vec;
|
||||
|
||||
public:
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexVectorConstantCoefficient(const ComplexVector &v)
|
||||
: ComplexVectorCoefficient(v.Size()), vec(v) { }
|
||||
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexVectorConstantCoefficient(const Vector &v)
|
||||
: ComplexVectorCoefficient(v.Size()), vec(v) { }
|
||||
|
||||
using ComplexVectorCoefficient::Eval;
|
||||
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override { V = vec; }
|
||||
|
||||
/// Return a reference to the constant vector in this class.
|
||||
const ComplexVector& GetVec() const { return vec; }
|
||||
};
|
||||
|
||||
/// Complex-valued vector coefficient that is constant in space and time.
|
||||
class ComplexMatrixConstantCoefficient : public ComplexMatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexTypeDenseMatrix mat;
|
||||
|
||||
public:
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexMatrixConstantCoefficient(const ComplexTypeDenseMatrix &m)
|
||||
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
|
||||
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexMatrixConstantCoefficient(const DenseMatrix &m)
|
||||
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
|
||||
|
||||
using ComplexMatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
void Eval(ComplexTypeDenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override { M = mat; }
|
||||
|
||||
/// Return a reference to the constant matrix in this class.
|
||||
const ComplexTypeDenseMatrix& GetMat() const { return mat; }
|
||||
};
|
||||
|
||||
/// A general complex-valued function coefficient
|
||||
class ComplexFunctionCoefficient : public ComplexCoefficient
|
||||
{
|
||||
protected:
|
||||
std::function<complex_t(const Vector &)> Function;
|
||||
std::function<complex_t(const Vector &, real_t)> TDFunction;
|
||||
|
||||
public:
|
||||
/// Define a time-independent coefficient from a std function
|
||||
/** \param F time-independent std::function */
|
||||
ComplexFunctionCoefficient(std::function<complex_t
|
||||
(const Vector &)> F)
|
||||
: Function(std::move(F))
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent coefficient from a std function
|
||||
/** \param TDF time-dependent function */
|
||||
ComplexFunctionCoefficient(std::function<complex_t
|
||||
(const Vector &, real_t)> TDF)
|
||||
: TDFunction(std::move(TDF))
|
||||
{ }
|
||||
|
||||
/// (DEPRECATED) Define a time-independent coefficient from a C-function
|
||||
/** @deprecated Use the method where the C-function, @a f, uses a const
|
||||
Vector argument instead of Vector. */
|
||||
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_t
|
||||
(*f)(Vector &))
|
||||
{
|
||||
// Cast first to (void*) to suppress a warning from newer version of
|
||||
// Clang when using -Wextra.
|
||||
Function = reinterpret_cast<complex_t(*)
|
||||
(const Vector&)>((void*)f);
|
||||
TDFunction = NULL;
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
|
||||
/** @deprecated Use the method where the C-function, @a tdf, uses a const
|
||||
Vector argument instead of Vector. */
|
||||
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_t
|
||||
(*tdf)(Vector &, real_t))
|
||||
{
|
||||
Function = NULL;
|
||||
// Cast first to (void*) to suppress a warning from newer version of
|
||||
// Clang when using -Wextra.
|
||||
TDFunction =
|
||||
reinterpret_cast<complex_t(*)(const Vector&,
|
||||
real_t)>((void*)tdf);
|
||||
}
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// A general vector function coefficient
|
||||
class ComplexVectorFunctionCoefficient : public ComplexVectorCoefficient
|
||||
{
|
||||
private:
|
||||
std::function<void(const Vector &, ComplexVector &)> Function;
|
||||
std::function<void(const Vector &, real_t, ComplexVector &)> TDFunction;
|
||||
ComplexCoefficient *Q;
|
||||
|
||||
public:
|
||||
/// Define a time-independent complex-valued vector coefficient
|
||||
/// from a std function
|
||||
/** \param dim - the size of the vector
|
||||
\param F - time-independent function
|
||||
\param q - optional scalar Coefficient to scale the vector coefficient */
|
||||
ComplexVectorFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &,
|
||||
ComplexVector &)> F,
|
||||
ComplexCoefficient *q = nullptr)
|
||||
: ComplexVectorCoefficient(dim), Function(std::move(F)), Q(q)
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent complex-valued vector coefficient from
|
||||
/// a std function
|
||||
/** \param dim - the size of the vector
|
||||
\param TDF - time-dependent function
|
||||
\param q - optional scalar ComplexCoefficient to scale the vector coefficient */
|
||||
ComplexVectorFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, real_t,
|
||||
ComplexVector &)> TDF,
|
||||
ComplexCoefficient *q = nullptr)
|
||||
: ComplexVectorCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
using ComplexVectorCoefficient::Eval;
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~ComplexVectorFunctionCoefficient() { }
|
||||
};
|
||||
|
||||
} // end namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -96,6 +96,23 @@ ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
|
||||
{
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectCoefficient(real_coeff);
|
||||
*gfi = 0.0;
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(ComplexCoefficient &coeff)
|
||||
{
|
||||
this->ProjectCoefficient(coeff.real(), coeff.imag());
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff)
|
||||
@@ -108,6 +125,23 @@ ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
|
||||
{
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectCoefficient(real_vcoeff);
|
||||
*gfi = 0.0;
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(ComplexVectorCoefficient &vcoeff)
|
||||
{
|
||||
this->ProjectCoefficient(vcoeff.real(), vcoeff.imag());
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff,
|
||||
@@ -121,6 +155,26 @@ ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
ConstantCoefficient zero_coeff(0.0);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficient(real_coeff, attr);
|
||||
gfi->ProjectBdrCoefficient(zero_coeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(ComplexCoefficient &coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficient(coeff.real(), coeff.imag(), attr);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff,
|
||||
@@ -134,6 +188,28 @@ ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
|
||||
gfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(
|
||||
ComplexVectorCoefficient &vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficientNormal(vcoeff.real(), vcoeff.imag(), attr);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -149,6 +225,80 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
|
||||
gfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(
|
||||
ComplexVectorCoefficient &vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficientTangent(vcoeff.real(), vcoeff.imag(), attr);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
|
||||
Coefficient &im_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
ConstantCoefficient zero_coef(0.0);
|
||||
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
|
||||
VectorCoefficient &im_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
Vector zero_vec(re_exsol.GetVDim()); zero_vec = 0.0;
|
||||
VectorConstantCoefficient zero_coef(zero_vec);
|
||||
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
|
||||
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *fes,
|
||||
ComplexOperator::Convention convention)
|
||||
@@ -731,6 +881,17 @@ ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
|
||||
{
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectCoefficient(real_coeff);
|
||||
*pgfi = 0.0;
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff)
|
||||
@@ -743,6 +904,17 @@ ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
|
||||
{
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectCoefficient(real_vcoeff);
|
||||
*pgfi = 0.0;
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff,
|
||||
@@ -756,6 +928,19 @@ ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
ConstantCoefficient zero_coeff(0.0);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficient(real_coeff, attr);
|
||||
pgfi->ProjectBdrCoefficient(zero_coeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -771,6 +956,21 @@ ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
|
||||
pgfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -786,6 +986,21 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
|
||||
pgfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::Distribute(const Vector *tv)
|
||||
{
|
||||
@@ -825,6 +1040,31 @@ ParComplexGridFunction::ParallelProject(Vector &tv) const
|
||||
tvi.SyncAliasMemory(tv);
|
||||
}
|
||||
|
||||
real_t
|
||||
ParComplexGridFunction::ComputeL2Error(Coefficient &exsolr,
|
||||
const IntegrationRule *irs[],
|
||||
Array<int> *elems) const
|
||||
{
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ParComplexGridFunction::ComputeL2Error(VectorCoefficient &exsolr,
|
||||
const IntegrationRule *irs[],
|
||||
Array<int> *elems) const
|
||||
{
|
||||
Vector zeroVec(exsolr.GetVDim()); zeroVec = 0.0;
|
||||
VectorConstantCoefficient zeroCoef(zeroVec);
|
||||
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
|
||||
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
|
||||
ComplexOperator::Convention
|
||||
|
||||
+1307
-21
File diff suppressed because it is too large
Load Diff
+12
-10
@@ -764,9 +764,9 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
|
||||
{
|
||||
cycle = 0;
|
||||
#ifdef MFEM_USE_ZLIB
|
||||
compression = true; // if we have zlib, enable compression
|
||||
#else
|
||||
compression = false; // otherwise, disable compression
|
||||
// If we have zlib, enable compression. Otherwise, compression is disabled in
|
||||
// the DataCollection base class constructor.
|
||||
compression = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -784,13 +784,8 @@ void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
|
||||
{
|
||||
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
|
||||
"Compression level must be between -1 and 9 (inclusive).");
|
||||
if (compression_level_ != 0) { SetCompression(true);}
|
||||
compression_level = compression_level_;
|
||||
compression = compression_level_ != 0;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
int ParaViewDataCollectionBase::GetCompressionLevel() const
|
||||
@@ -1174,7 +1169,14 @@ const char *ParaViewDataCollection::GetDataTypeString() const
|
||||
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
|
||||
const std::string &collection_name, Mesh *mesh)
|
||||
: ParaViewDataCollectionBase(collection_name, mesh)
|
||||
{ }
|
||||
{
|
||||
compression = true;
|
||||
}
|
||||
|
||||
void ParaViewHDFDataCollection::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
void ParaViewHDFDataCollection::EnsureVTKHDF()
|
||||
{
|
||||
|
||||
@@ -537,13 +537,6 @@ public:
|
||||
/// Any nonzero compression level will enable compression.
|
||||
void SetCompressionLevel(int compression_level_);
|
||||
|
||||
/// @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).
|
||||
///
|
||||
@@ -633,6 +626,12 @@ public:
|
||||
ParaViewHDFDataCollection(const std::string& collection_name,
|
||||
Mesh *mesh_ = nullptr);
|
||||
|
||||
/// @brief Enable or disable compression.
|
||||
///
|
||||
/// The compression level can be set with SetCompressionLevel()). VTKHDF
|
||||
/// compression does not require MFEM to be compiled with zlib support.
|
||||
void SetCompression(bool compression_) override;
|
||||
|
||||
/// Save the collection.
|
||||
void Save() override;
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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.
|
||||
|
||||
#include "doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
|
||||
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
|
||||
{
|
||||
MFEM_ASSERT(parameters.size() == p.size(),
|
||||
"number of parameters doesn't match descriptors");
|
||||
for (size_t i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
p[i]->Read();
|
||||
parameters_l[i] = *p[i];
|
||||
}
|
||||
}
|
||||
|
||||
DifferentiableOperator::DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh) :
|
||||
mesh(mesh),
|
||||
solutions(solutions),
|
||||
parameters(parameters)
|
||||
{
|
||||
fields.resize(solutions.size() + parameters.size());
|
||||
fields_e.resize(fields.size());
|
||||
solutions_l.resize(solutions.size());
|
||||
parameters_l.resize(parameters.size());
|
||||
|
||||
for (size_t i = 0; i < solutions.size(); i++)
|
||||
{
|
||||
fields[i] = solutions[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
fields[i + solutions.size()] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,797 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "../fespace.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
#include "interpolate.hpp"
|
||||
#include "integrate.hpp"
|
||||
#include "qfunction_apply.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Type alias for a function that computes the action of an operator
|
||||
using action_t =
|
||||
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the action of a derivative
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the sparse matrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_hypreparmatrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
|
||||
|
||||
/// @brief Type alias for a function that applies the appropriate restriction to
|
||||
/// the solution and parameters
|
||||
using restriction_callback_t =
|
||||
std::function<void(std::vector<Vector> &,
|
||||
const std::vector<Vector> &,
|
||||
std::vector<Vector> &)>;
|
||||
|
||||
/// Class representing the derivative (Jacobian) operator of a
|
||||
/// DifferentiableOperator.
|
||||
///
|
||||
/// This class implements a derivative operator that computes directional
|
||||
/// derivatives for a given set of solution and parameter fields. It supports
|
||||
/// both forward and transpose operations, as well as assembly into sparse
|
||||
/// matrices.
|
||||
///
|
||||
/// @note The derivative operator uses only forward mode differentiation in Mult
|
||||
/// and MultTranspose. It does not support reverse mode differentiation. The
|
||||
/// MultTranspose operation is achieved by using the transpose of the derivative
|
||||
/// actions on each quadrature point.
|
||||
///
|
||||
/// @see DifferentiableOperator
|
||||
class DerivativeOperator : public Operator
|
||||
{
|
||||
public:
|
||||
/// Constructor for the DerivativeOperator class.
|
||||
///
|
||||
/// This is usually not called directly from a user. A DifferentiableOperator
|
||||
/// calls this constructor when using
|
||||
/// DifferentiableOperator::GetDerivative().
|
||||
DerivativeOperator(
|
||||
const int &height,
|
||||
const int &width,
|
||||
const std::vector<derivative_action_t> &derivative_actions,
|
||||
const FieldDescriptor &direction,
|
||||
const int &daction_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_actions_transpose,
|
||||
const FieldDescriptor &transpose_direction,
|
||||
const int &daction_transpose_l_size,
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
Operator(height, width),
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
daction_l(daction_l_size),
|
||||
daction_l_size(daction_l_size),
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
{
|
||||
s_l[i] = *solutions_l[i];
|
||||
}
|
||||
|
||||
std::vector<Vector> p_l(parameters_l.size());
|
||||
for (size_t i = 0; i < p_l.size(); i++)
|
||||
{
|
||||
p_l[i] = *parameters_l[i];
|
||||
}
|
||||
|
||||
fields_e.resize(solutions_l.size() + parameters_l.size());
|
||||
restriction_callback(s_l, p_l, fields_e);
|
||||
}
|
||||
|
||||
/// @brief Compute the action of the derivative operator on a given vector.
|
||||
///
|
||||
/// @param direction_t The direction vector in which to compute the
|
||||
/// derivative. This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the action of the derivative on
|
||||
/// direction_t on T-dofs.
|
||||
void Mult(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
daction_l.SetSize(daction_l_size);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Compute the transpose of the derivative operator on a given
|
||||
/// vector.
|
||||
///
|
||||
/// This function computes the transpose of the derivative operator on a
|
||||
/// given vector by transposing the quadrature point local forward derivative
|
||||
/// action. It does not use reverse mode automatic differentiation.
|
||||
///
|
||||
/// @param direction_t The direction vector in which to compute the
|
||||
/// derivative. This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the transpose action of the derivative on
|
||||
/// direction_t on T-dofs.
|
||||
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!derivative_actions_transpose.empty(),
|
||||
"derivative can't be used to be multiplied in transpose mode");
|
||||
|
||||
daction_l.SetSize(width);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(transpose_direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions_transpose)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
///
|
||||
/// @param A The HypreParMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a matrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Derivative action callbacks. Depending on the requested derivatives in
|
||||
/// DifferentiableOperator the callbacks represent certain combinations of
|
||||
/// actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions;
|
||||
|
||||
FieldDescriptor direction;
|
||||
|
||||
mutable Vector daction_l;
|
||||
|
||||
const int daction_l_size;
|
||||
|
||||
/// Transpose Derivative action callbacks. Depending on the requested
|
||||
/// derivatives in DifferentiableOperator the callbacks represent certain
|
||||
/// combinations of actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions_transpose;
|
||||
|
||||
FieldDescriptor transpose_direction;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
|
||||
mutable Vector direction_l;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
};
|
||||
|
||||
/// Class representing a differentiable operator which acts on solution and
|
||||
/// parameter fields to compute residuals.
|
||||
///
|
||||
/// This class provides functionality to define differentiable operators by
|
||||
/// composing functions that compute values at quadrature points. It supports
|
||||
/// automatic differentiation to compute derivatives with respect to solutions
|
||||
/// (Jacobians) and parameter fields (general derivative operators).
|
||||
///
|
||||
/// The operator is constructed with solution fields that it will act on and
|
||||
/// parameter fields that define coefficients. Quadrature functions are added by
|
||||
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates f
|
||||
/// those functionas and parameters at quadrature points.
|
||||
///
|
||||
/// Derivatives can be computed by obtaining a DerivativeOperator using
|
||||
/// GetDerivative().
|
||||
///
|
||||
/// @see DerivativeOperator
|
||||
class DifferentiableOperator : public Operator
|
||||
{
|
||||
public:
|
||||
/// Constructor for the DifferentiableOperator class.
|
||||
///
|
||||
/// @param solutions The solution fields that the operator will act on.
|
||||
/// @param parameters The parameter fields that define coefficients.
|
||||
/// @param mesh The mesh on which the operator is defined.
|
||||
DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh);
|
||||
|
||||
/// @brief Compute the action of the operator on a given vector.
|
||||
///
|
||||
/// @param solutions_t The solution vector in which to compute the action.
|
||||
/// This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the action of the operator on
|
||||
/// solutions_t. The result is a T-dof vector.
|
||||
void Mult(const Vector &solutions_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
|
||||
prolongation(solutions, solutions_t, solutions_l);
|
||||
residual_l = 0.0;
|
||||
for (auto &action : action_callbacks)
|
||||
{
|
||||
action(solutions_l, parameters_l, residual_l);
|
||||
}
|
||||
prolongation_transpose(residual_l, result_t);
|
||||
}
|
||||
|
||||
/// @brief Add a domain integrator to the operator.
|
||||
///
|
||||
/// @param qfunc The quadrature function to be added.
|
||||
/// @param inputs Tuple of FieldOperators for the inputs of the quadrature
|
||||
/// function.
|
||||
/// @param outputs Tuple of FieldOperators for the outputs of the quadrature
|
||||
/// function.
|
||||
/// @param integration_rule IntegrationRule to use with this integrator.
|
||||
/// @param domain_attributes Domain attributes marker array indicating over
|
||||
/// which attributes this integrator will integrate over.
|
||||
/// @param derivative_ids Derivatives to be made available for this
|
||||
/// integrator.
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t = decltype(std::make_index_sequence<0> {})>
|
||||
void AddDomainIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
|
||||
|
||||
/// @brief Set the parameters for the operator.
|
||||
///
|
||||
/// This has to be called before using Mult() or MultTranspose().
|
||||
///
|
||||
/// @param p The parameters to be set. This should be a vector of pointers to
|
||||
/// the parameter vectors. The vectors have to be L-vectors (e.g.
|
||||
/// GridFunctions).
|
||||
void SetParameters(std::vector<Vector *> p) const;
|
||||
|
||||
/// @brief Disable the use of tensor product structure.
|
||||
///
|
||||
/// This function disables the use of tensor product structure for the
|
||||
/// operator. Usually, DifferentiableOperator creates callbacks based on
|
||||
/// heuristics that achieve good performance for each element type. Some
|
||||
/// functionality is not implemented for these performant algorithms but only
|
||||
/// for generic assembly. Therefore the user can decide to use fallback
|
||||
/// methods.
|
||||
void DisableTensorProductStructure(bool disable = true)
|
||||
{
|
||||
use_tensor_product_structure = !disable;
|
||||
}
|
||||
|
||||
/// @brief Get the derivative operator for a given derivative ID.
|
||||
///
|
||||
/// This function returns a shared pointer to a DerivativeOperator that
|
||||
/// computes the derivative of the operator with respect to the given
|
||||
/// derivative ID. The derivative ID is used to identify the specific
|
||||
/// derivative action to be performed.
|
||||
///
|
||||
/// @param derivative_id The ID of the derivative to be computed.
|
||||
/// @param sol_l The solution vectors to be used for the derivative
|
||||
/// computation. This should be a vector of pointers to the solution
|
||||
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
|
||||
/// @param par_l The parameter vectors to be used for the derivative
|
||||
/// computation. This should be a vector of pointers to the parameter
|
||||
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
|
||||
/// @return A shared pointer to the DerivativeOperator.
|
||||
std::shared_ptr<DerivativeOperator> GetDerivative(
|
||||
size_t derivative_id, std::vector<Vector *> sol_l, std::vector<Vector *> par_l)
|
||||
{
|
||||
MFEM_ASSERT(derivative_action_callbacks.find(derivative_id) !=
|
||||
derivative_action_callbacks.end(),
|
||||
"no derivative action has been found for ID " << derivative_id);
|
||||
|
||||
MFEM_ASSERT(sol_l.size() == solutions.size(),
|
||||
"wrong number of solutions");
|
||||
|
||||
MFEM_ASSERT(par_l.size() == parameters.size(),
|
||||
"wrong number of parameters");
|
||||
|
||||
const size_t derivative_idx = FindIdx(derivative_id, fields);
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
GetTrueVSize(fields[derivative_idx]),
|
||||
derivative_action_callbacks[derivative_id],
|
||||
fields[derivative_idx],
|
||||
residual_l.Size(),
|
||||
daction_transpose_callbacks[derivative_id],
|
||||
fields[test_space_field_idx],
|
||||
GetVSize(fields[test_space_field_idx]),
|
||||
sol_l,
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
}
|
||||
|
||||
private:
|
||||
const ParMesh &mesh;
|
||||
|
||||
std::vector<action_t> action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
// solutions and parameters
|
||||
std::vector<FieldDescriptor> fields;
|
||||
|
||||
mutable std::vector<Vector> solutions_l;
|
||||
mutable std::vector<Vector> parameters_l;
|
||||
mutable Vector residual_l;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
mutable Vector residual_e;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
bool use_tensor_product_structure = true;
|
||||
|
||||
size_t test_space_field_idx = SIZE_MAX;
|
||||
};
|
||||
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t>
|
||||
void DifferentiableOperator::AddDomainIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids)
|
||||
{
|
||||
using entity_t = Entity::Element;
|
||||
|
||||
static constexpr size_t num_inputs =
|
||||
tuple_size<decltype(inputs)>::value;
|
||||
|
||||
static constexpr size_t num_outputs =
|
||||
tuple_size<decltype(outputs)>::value;
|
||||
|
||||
using qf_signature =
|
||||
typename create_function_signature<decltype(&qfunc_t::operator())>::type;
|
||||
using qf_param_ts = typename qf_signature::parameter_ts;
|
||||
using qf_output_t = typename qf_signature::return_t;
|
||||
|
||||
// Consistency checks
|
||||
if constexpr (num_outputs > 1)
|
||||
{
|
||||
static_assert(dfem::always_false<qfunc_t>,
|
||||
"more than one output per quadrature functions is not supported right now");
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<qf_output_t, void>)
|
||||
{
|
||||
static_assert(dfem::always_false<qfunc_t>,
|
||||
"quadrature function has no return value");
|
||||
}
|
||||
|
||||
constexpr size_t num_qfinputs = tuple_size<qf_param_ts>::value;
|
||||
static_assert(num_qfinputs == num_inputs,
|
||||
"quadrature function inputs and descriptor inputs have to match");
|
||||
|
||||
constexpr size_t num_qf_outputs = tuple_size<qf_output_t>::value;
|
||||
static_assert(num_qf_outputs == num_outputs,
|
||||
"quadrature function outputs and descriptor outputs have to match");
|
||||
|
||||
constexpr auto inout_tuple =
|
||||
merge_mfem_tuples_as_empty_std_tuple(inputs, outputs);
|
||||
constexpr auto filtered_inout_tuple = filter_fields(inout_tuple);
|
||||
static constexpr size_t num_fields =
|
||||
count_unique_field_ids(filtered_inout_tuple);
|
||||
|
||||
MFEM_ASSERT(num_fields == solutions.size() + parameters.size(),
|
||||
"Total number of fields doesn't match sum of solutions and parameters."
|
||||
" This indicates that some fields are not used in the integrator,"
|
||||
" which currently is not supported.");
|
||||
|
||||
auto dependency_map = make_dependency_map(inputs);
|
||||
|
||||
// pretty_print(dependency_map);
|
||||
|
||||
auto input_to_field =
|
||||
create_descriptors_to_fields_map<entity_t>(fields, inputs);
|
||||
auto output_to_field =
|
||||
create_descriptors_to_fields_map<entity_t>(fields, outputs);
|
||||
|
||||
// TODO: factor out
|
||||
std::vector<int> inputs_vdim(num_inputs);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
inputs_vdim[i] = get<i>(inputs).vdim;
|
||||
});
|
||||
|
||||
|
||||
Array<int> elem_attributes;
|
||||
elem_attributes.SetSize(mesh.GetNE());
|
||||
for (int i = 0; i < mesh.GetNE(); ++i)
|
||||
{
|
||||
elem_attributes[i] = mesh.GetAttribute(i);
|
||||
}
|
||||
|
||||
const auto output_fop = get<0>(outputs);
|
||||
test_space_field_idx = FindIdx(output_fop.GetFieldId(), fields);
|
||||
|
||||
bool use_sum_factorization = false;
|
||||
auto entity_element_type =
|
||||
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
|
||||
if ((entity_element_type == Element::QUADRILATERAL ||
|
||||
entity_element_type == Element::HEXAHEDRON) &&
|
||||
use_tensor_product_structure == true)
|
||||
{
|
||||
use_sum_factorization = true;
|
||||
}
|
||||
|
||||
ElementDofOrdering element_dof_ordering = ElementDofOrdering::NATIVE;
|
||||
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::FULL;
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
doftoquad_mode = DofToQuad::Mode::TENSOR;
|
||||
}
|
||||
|
||||
auto [output_rt,
|
||||
output_e_sz] = get_restriction_transpose<entity_t>
|
||||
(fields[test_space_field_idx],
|
||||
element_dof_ordering, output_fop);
|
||||
auto &output_e_size = output_e_sz;
|
||||
|
||||
output_restriction_transpose = output_rt;
|
||||
residual_e.SetSize(output_e_size);
|
||||
|
||||
// The explicit captures are necessary to avoid dependency on
|
||||
// the specific instance of this class (this pointer).
|
||||
restriction_callback =
|
||||
[=, solutions = this->solutions, parameters = this->parameters]
|
||||
(std::vector<Vector> &sol,
|
||||
const std::vector<Vector> &par,
|
||||
std::vector<Vector> &f)
|
||||
{
|
||||
restriction<entity_t>(solutions, sol, f,
|
||||
element_dof_ordering);
|
||||
restriction<entity_t>(parameters, par, f,
|
||||
element_dof_ordering,
|
||||
solutions.size());
|
||||
};
|
||||
|
||||
prolongation_transpose = get_prolongation_transpose(
|
||||
fields[test_space_field_idx], output_fop, mesh.GetComm());
|
||||
|
||||
const int dimension = mesh.Dimension();
|
||||
[[maybe_unused]] const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
if constexpr (is_sum_fop<decltype(output_fop)>::value)
|
||||
{
|
||||
residual_l.SetSize(1);
|
||||
height = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int residual_lsize = GetVSize(fields[test_space_field_idx]);
|
||||
residual_l.SetSize(residual_lsize);
|
||||
height = GetTrueVSize(fields[test_space_field_idx]);
|
||||
}
|
||||
|
||||
// TODO: Is this a hack?
|
||||
width = GetTrueVSize(fields[0]);
|
||||
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(
|
||||
field,
|
||||
integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/dimension) + 0.5);
|
||||
|
||||
const int residual_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop,
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
|
||||
|
||||
const int test_vdim = output_fop.vdim;
|
||||
const int test_op_dim = output_fop.size_on_qp / output_fop.vdim;
|
||||
const int num_test_dof =
|
||||
num_entities ? (output_e_size / output_fop.vdim / num_entities) : 0;
|
||||
|
||||
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp =
|
||||
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
|
||||
|
||||
auto action_shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
|
||||
|
||||
Vector shmem_cache(action_shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(action_shmem_info);
|
||||
|
||||
ThreadBlocks thread_blocks;
|
||||
if (dimension == 3)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
thread_blocks.x = q1d;
|
||||
thread_blocks.y = q1d;
|
||||
thread_blocks.z = q1d;
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
thread_blocks.x = q1d;
|
||||
thread_blocks.y = q1d;
|
||||
thread_blocks.z = 1;
|
||||
}
|
||||
}
|
||||
|
||||
action_callbacks.push_back(
|
||||
// Explicitly capture everything we need, so we can make explicit choice
|
||||
// how to capture every variable, by copy or by ref.
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
residual_size_on_qp, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
action_shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
// capture by ref:
|
||||
&restriction_cb = this->restriction_callback,
|
||||
&fields_e = this->fields_e,
|
||||
&residual_e = this->residual_e,
|
||||
&output_restriction_transpose = this->output_restriction_transpose
|
||||
]
|
||||
(std::vector<Vector> &sol, const std::vector<Vector> &par, Vector &res)
|
||||
mutable // mutable: needed to modify 'shmem_cache'
|
||||
{
|
||||
restriction_cb(sol, par, fields_e);
|
||||
|
||||
residual_e = 0.0;
|
||||
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
|
||||
|
||||
auto wrapped_fields_e = wrap_fields(fields_e,
|
||||
action_shmem_info.field_sizes,
|
||||
num_entities);
|
||||
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
|
||||
residual_shmem, scratch_shmem] =
|
||||
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, num_qp, e);
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
|
||||
call_qfunction<qf_param_ts>(
|
||||
qfunc, input_shmem, residual_shmem,
|
||||
residual_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, action_shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
output_restriction_transpose(residual_e, res);
|
||||
});
|
||||
|
||||
// Without this compile-time check, some valid instantiations of this method
|
||||
// will fail.
|
||||
if constexpr (derivative_ids_t::size() != 0)
|
||||
{
|
||||
// Create the action of the derivatives
|
||||
for_constexpr([&, &or_transpose =
|
||||
this->output_restriction_transpose](const std::size_t derivative_id)
|
||||
{
|
||||
const size_t d_field_idx = FindIdx(derivative_id, fields);
|
||||
const auto direction = fields[d_field_idx];
|
||||
const int da_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
|
||||
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
|
||||
num_qp, input_size_on_qp, residual_size_on_qp,
|
||||
element_dof_ordering, d_field_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
Vector direction_e;
|
||||
Vector derivative_action_e(output_e_size);
|
||||
derivative_action_e = 0.0;
|
||||
|
||||
// Lookup the derivative_id key in the dependency map
|
||||
auto it = dependency_map.find(derivative_id);
|
||||
if (it == dependency_map.end())
|
||||
{
|
||||
MFEM_ABORT("Derivative ID not found in dependency map");
|
||||
}
|
||||
const auto input_is_dependent = it->second;
|
||||
|
||||
derivative_action_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction, // FieldDescriptor
|
||||
direction_e, // Vector
|
||||
derivative_action_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
da_size_on_qp, // int
|
||||
|
||||
// capture by ref:
|
||||
&or_transpose
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_l,
|
||||
Vector &der_action_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, dir_l, direction_e,
|
||||
element_dof_ordering);
|
||||
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof,
|
||||
test_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
|
||||
derivative_action_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
|
||||
inputs, ir_weights, scratch_shmem, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
// TODO: Probably redundant
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
|
||||
ir_weights, scratch_shmem, input_is_dependent, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
call_qfunction_derivative_action<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem,
|
||||
da_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim,
|
||||
test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Base class for FieldOperators.
|
||||
///
|
||||
/// This class serves as a base for different FieldOperator types which can be
|
||||
/// applied to fields that are used with inputs to a quadrature point function.
|
||||
/// See DifferentialOperator.
|
||||
template <int FIELD_ID = -1>
|
||||
class FieldOperator
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for the FieldOperator.
|
||||
///
|
||||
/// This constructor initializes the FieldOperator with it's size on
|
||||
/// quadrature points. The size on quadrature points has to be determined by
|
||||
/// the FieldOperator type, the dimension and the vector dimension (number
|
||||
/// of components). See the following examples
|
||||
///
|
||||
/// Scalar FiniteElementSpace with Value FieldOperator:
|
||||
/// size = vdim x dim x 1 = 1 x dim x 1 = dim
|
||||
///
|
||||
/// Vector FiniteElementSpace with Gradient FieldOperator:
|
||||
/// size = vdim x dim x dim = vdim x dim x dim = vdim * dim^2
|
||||
///
|
||||
/// ParameterSpace with Identity FieldOperator:
|
||||
/// size = vdim = vdim
|
||||
constexpr FieldOperator(int size_on_qp = 0) :
|
||||
size_on_qp(size_on_qp) {};
|
||||
|
||||
/// @brief Get the field id this FieldOperator is attached to.
|
||||
static constexpr int GetFieldId() { return FIELD_ID; }
|
||||
|
||||
/// @brief Get the size on quadrature point for this FieldOperator.
|
||||
int size_on_qp = -1;
|
||||
|
||||
/// @brief Get the dimension of the FieldOperator.
|
||||
int dim = -1;
|
||||
|
||||
/// @brief Get the vector dimension (number of components)
|
||||
/// of the FieldOperator.
|
||||
int vdim = -1;
|
||||
};
|
||||
|
||||
/// @brief Identity FieldOperator.
|
||||
///
|
||||
/// This FieldOperator does nothing to the field. The field (usually a
|
||||
/// ParametricFunction) transfers the values to the quadrature point data and
|
||||
/// Identity can be viewed as an identity operation.
|
||||
template <int FIELD_ID = -1>
|
||||
class Identity : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Identity() : FieldOperator<FIELD_ID>() {}
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_identity_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_identity_fop<Identity<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Weight FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that this field contains the quadrature
|
||||
/// point weights.
|
||||
class Weight : public FieldOperator<-1>
|
||||
{
|
||||
public:
|
||||
constexpr Weight() : FieldOperator<-1>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_weight_fop : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_weight_fop<Weight> : std::true_type {};
|
||||
|
||||
/// @brief Value FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that the field contains the
|
||||
/// interpolated values of the degrees of freedom at the quadrature points.
|
||||
template <int FIELD_ID = -1>
|
||||
class Value : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Value() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_value_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_value_fop<Value<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Gradient FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that the field contains the
|
||||
/// interpolated gradients of the degrees of freedom at the quadrature points.
|
||||
template <int FIELD_ID = -1>
|
||||
class Gradient : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_gradient_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_gradient_fop<Gradient<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Sum FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is commonly used to signal that an output of a quadrature
|
||||
/// function should be summed.
|
||||
template <int FIELD_ID = -1>
|
||||
class Sum : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Sum() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_sum_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_sum_fop<Sum<FIELD_ID>> : std::true_type {};
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,450 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_impl(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
// assuming the quadrature point residual has to "play nice with
|
||||
// the test function"
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, cdim, num_dof] = B.GetShape();
|
||||
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * f(vd, 0, qp);
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += G(qp, d, dof) * f(vd, d, qp);
|
||||
}
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
// This is the "integral over all quadrature points type" applying
|
||||
// B = 1 s.t. B^T * C \in R^1.
|
||||
const auto [num_qp, unused, unused1] = B.GetShape();
|
||||
auto cc = Reshape(&f(0, 0, 0), num_qp);
|
||||
for (int i = 0; i < num_qp; i++)
|
||||
{
|
||||
y(0, 0) += cc(i);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const auto vdim = output.vdim;
|
||||
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
|
||||
auto yy = Reshape(&y(0, 0), num_qp * vdim);
|
||||
for (int i = 0; i < num_qp * vdim; i++)
|
||||
{
|
||||
yy(i) = cc(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qy, dx) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
yd(dx, dy, vd) += acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
uv[0] += fqp(vd, 0, qx, qy) * G(qx, 0, dx);
|
||||
uv[1] += fqp(vd, 1, qx, qy) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qy, dx) = uv[0];
|
||||
s1(qy, dx) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
uv[0] += s0(qy, dx) * B(qy, 0, dy);
|
||||
uv[1] += s1(qy, dx) * G(qy, 0, dy);
|
||||
}
|
||||
yd(dx, dy, vd) += uv[0] + uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
|
||||
// // TODO: Check if this is the right fix for all cases
|
||||
// auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d);
|
||||
// auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d);
|
||||
// for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
// {
|
||||
// MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
// {
|
||||
// yqp(sq, qx) = fqp(sq, qx);
|
||||
// }
|
||||
// MFEM_SYNC_THREAD;
|
||||
// }
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy) = fqp(sq, qx, qy);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(qz, dy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
acc += s1(qz, dy, dx) * B(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
|
||||
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
|
||||
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
|
||||
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = uvw[0];
|
||||
s1(qz, qy, dx) = uvw[1];
|
||||
s2(qz, qy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
|
||||
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s3(qz, dy, dx) = uvw[0];
|
||||
s4(qz, dy, dx) = uvw[1];
|
||||
s5(qz, dy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_2d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_3d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("dimension not supported"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
map_quadrature_data_to_fields_impl(y, f, output, dtq);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,573 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product_3d(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
|
||||
}
|
||||
s0(dz, dy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dz, dy, qx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(dz, qy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
acc += s1(dz, qy, qx) * B(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, qx, qy, qz) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const int dim = input.dim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
const real_t f = field(dx, dy, dz, vd);
|
||||
uv[0] += f * B(qx, 0, dx);
|
||||
uv[1] += f * G(qx, 0, dx);
|
||||
}
|
||||
s0(dz, dy, qx) = uv[0];
|
||||
s1(dz, dy, qx) = uv[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
const real_t s0i = s0(dz, dy, qx);
|
||||
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
|
||||
uvw[1] += s0i * G(qy, 0, dy);
|
||||
uvw[2] += s0i * B(qy, 0, dy);
|
||||
}
|
||||
s2(dz, qy, qx) = uvw[0];
|
||||
s3(dz, qy, qx) = uvw[1];
|
||||
s4(dz, qy, qx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, 0, qx, qy, qz) = uvw[0];
|
||||
fqp(vd, 1, qx, qy, qz) = uvw[1];
|
||||
fqp(vd, 2, qx, qy, qz) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
// TODO: eeek
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
f(qx, qy, qz) = w(qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product_2d(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d);
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, vd);
|
||||
}
|
||||
s0(dy, qx) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dy, qx) * B(qy, 0, dy);
|
||||
}
|
||||
fqp(vd, qx, qy) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const int dim = input.dim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
const real_t f = field(dx, dy, vd);
|
||||
uv[0] += f * B(qx, 0, dx);
|
||||
uv[1] += f * G(qx, 0, dx);
|
||||
}
|
||||
s0(dy, qx) = uv[0];
|
||||
s1(dy, qx) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
const real_t s0i = s0(dy, qx);
|
||||
uv[0] += s1(dy, qx) * B(qy, 0, dy);
|
||||
uv[1] += s0i * G(qy, 0, dy);
|
||||
}
|
||||
fqp(vd, 0, qx, qy) = uv[0];
|
||||
fqp(vd, 1, qx, qy) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
// TODO: eeek
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
f(qx, qy) = w(qx, qy);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data(
|
||||
DeviceTensor<2> field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
if constexpr (is_value_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, dim, num_dof] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * field(dof, vd);
|
||||
}
|
||||
field_qp(vd, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<field_operator_t>::value)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += G(qp, d, dof) * field(dof, vd);
|
||||
}
|
||||
f(vd, d, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<field_operator_t, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
auto f = Reshape(&field_qp[0], num_qp);
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
f(qp) = integration_weights(qp);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const int size_on_qp = input.size_on_qp;
|
||||
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
|
||||
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
|
||||
for (int i = 0; i < size_on_qp * num_qp; i++)
|
||||
{
|
||||
f(i) = field(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<field_operator_t>,
|
||||
"can't map field to quadrature data");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<int, num_inputs> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
// When the input_to_field map returns -1, this means the requested input
|
||||
// is the integration weight. Weights don't have a user defined field
|
||||
// attached to them and we create a dummy field which is not accessed
|
||||
// inside the functions it is passed to.
|
||||
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data_conditional(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const DofToQuadMap &dtqmap,
|
||||
field_operator_t &fop,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const bool &condition,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t num_fields, size_t num_inputs, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_fields_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1, const real_t>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
map_field_to_quadrature_data_conditional(
|
||||
fields_qp[i], fields_e[i], dtqmaps[i], get<i>(fops), integration_weights,
|
||||
scratch_mem, conditions[i], use_sum_factorization);
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t num_inputs, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_direction_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
|
||||
const DeviceTensor<1> &direction_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "../fe/fe_base.hpp"
|
||||
#include "../../fem/fespace.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// Base class for parametric spaces
|
||||
class ParameterSpace
|
||||
{
|
||||
public:
|
||||
ParameterSpace(int vdim = 1) : vdim(vdim) {}
|
||||
|
||||
/// @brief Get vector dimension at each point
|
||||
///
|
||||
/// This is the number of components at each point in the parametric space.
|
||||
int GetVDim() const { return vdim; }
|
||||
|
||||
/// Get DofToQuad information
|
||||
const DofToQuad& GetDofToQuad() const { return dtq; }
|
||||
|
||||
/// Get total size of the space (T-vector size)
|
||||
///
|
||||
/// returns the true size vsize of the space
|
||||
virtual int GetTrueVSize() const = 0;
|
||||
|
||||
/// Get local vector size (L-vector size)
|
||||
///
|
||||
/// returns the local size of the space
|
||||
virtual int GetVSize() const = 0;
|
||||
|
||||
/// Get spatial dimension
|
||||
///
|
||||
/// returns always 1.
|
||||
int Dimension() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// @brief Get T-vector to L-vector transformation
|
||||
///
|
||||
/// returns identity by default that is lazy evaluated.
|
||||
virtual const Operator* GetProlongationMatrix() const
|
||||
{
|
||||
if (!prolongation)
|
||||
{
|
||||
prolongation.reset(new IdentityOperator(GetTrueVSize()));
|
||||
}
|
||||
return prolongation.get();
|
||||
}
|
||||
|
||||
/// @brief Get L-vector to E-vector transformation
|
||||
/// @note This is a mock call to replicate interface of FiniteElementSpace.
|
||||
/// It should not be used by a user.
|
||||
///
|
||||
/// returns identity by default that is lazy evaluated.
|
||||
virtual const Operator* GetElementRestriction(ElementDofOrdering o) const
|
||||
{
|
||||
if (!elem_restr)
|
||||
{
|
||||
elem_restr.reset(new IdentityOperator(GetVSize()));
|
||||
}
|
||||
return elem_restr.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
int vdim;
|
||||
DofToQuad dtq;
|
||||
mutable std::unique_ptr<Operator> prolongation;
|
||||
mutable std::unique_ptr<Operator> elem_restr;
|
||||
};
|
||||
|
||||
/// @brief Uniform parameter space
|
||||
class UniformParameterSpace : public ParameterSpace
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for a uniform parameter space
|
||||
///
|
||||
/// @param mesh The mesh to determine dimension and number of elements.
|
||||
/// @param ir The integration rule to determine the number of quadrature points.
|
||||
/// @param vdim The vector dimension at each point.
|
||||
/// @param used_in_tensor_product If true, the number of quadrature points is
|
||||
/// calculated as the nth root of the number of points in the integration rule,
|
||||
/// where n is the mesh dimension. If false, the number of quadrature points is
|
||||
/// taken directly from the integration rule.
|
||||
UniformParameterSpace(Mesh &mesh, const IntegrationRule &ir, int vdim,
|
||||
bool used_in_tensor_product = true) :
|
||||
ParameterSpace(vdim)
|
||||
{
|
||||
// Setup DofToQuad information
|
||||
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
|
||||
dtq.ndof = dtq.nqpt;
|
||||
dtq.mode = used_in_tensor_product ? DofToQuad::TENSOR : DofToQuad::FULL;
|
||||
|
||||
// Calculate sizes
|
||||
const int num_qp = used_in_tensor_product ?
|
||||
static_cast<int>(std::pow(dtq.nqpt, mesh.Dimension())) :
|
||||
ir.GetNPoints();
|
||||
|
||||
tsize = vdim * num_qp * mesh.GetNE();
|
||||
lsize = tsize;
|
||||
}
|
||||
|
||||
int GetTrueVSize() const override
|
||||
{
|
||||
return tsize;
|
||||
}
|
||||
|
||||
int GetVSize() const override
|
||||
{
|
||||
return lsize;
|
||||
}
|
||||
|
||||
private:
|
||||
/// T-vector size
|
||||
int tsize;
|
||||
|
||||
/// L-vector size
|
||||
int lsize;
|
||||
};
|
||||
|
||||
class ParameterFunction : public Vector
|
||||
{
|
||||
public:
|
||||
ParameterFunction(ParameterSpace &space) :
|
||||
Vector(space.GetTrueVSize()),
|
||||
space(space)
|
||||
{}
|
||||
|
||||
/// @brief Get the ParameterSpace
|
||||
const ParameterSpace& GetParameterSpace() const
|
||||
{
|
||||
return space;
|
||||
}
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
private:
|
||||
/// the parametric space
|
||||
ParameterSpace &space;
|
||||
};
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
#include "qfunction_transform.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Call a qfunction with the given parameters.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param rs_qp the size of the residual.
|
||||
/// @param num_qp the number of quadrature points.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
const int &rs_qp,
|
||||
const int &num_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension for sum factorization");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Call a qfunction with the given parameters and
|
||||
/// compute it's derivative action.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param das_qp the size of the derivative action.
|
||||
/// @param num_qp the number of quadrature points.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative_action(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
const int &das_qp,
|
||||
const int &num_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename args_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
const qfunc_t &qfunc,
|
||||
args_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
int qp)
|
||||
{
|
||||
process_qf_args(u, args, qp);
|
||||
process_qf_result(f_qp, get<0>(apply(qfunc, args)));
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_native_dual(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
const qfunc_t &qfunc,
|
||||
arg_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
const int &qp_idx)
|
||||
{
|
||||
process_qf_args(u, v, args, qp_idx);
|
||||
auto r = get<0>(apply(qfunc, args));
|
||||
process_derivative_from_native_dual(f_qp, r);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
|
||||
template <typename func_t, typename... arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto qfunction_wrapper(const func_t &f, arg_ts &&...args)
|
||||
{
|
||||
return f(args...);
|
||||
}
|
||||
|
||||
// Version for active function arguments only
|
||||
//
|
||||
// This is an Enzyme regression and can be removed in later versions.
|
||||
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<>)
|
||||
{
|
||||
using qf_return_t = typename create_function_signature<
|
||||
decltype(&qfunc_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<qf_return_t>(
|
||||
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...>, enzyme_const,
|
||||
(void *)&qfunc, enzyme_dup, &get<Is>(args)..., enzyme_interleave,
|
||||
&get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
// Interleave function arguments for enzyme
|
||||
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts, std::size_t... Js>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<Js...>)
|
||||
{
|
||||
using qf_return_t = typename create_function_signature<
|
||||
decltype(&qfunc_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<qf_return_t>(
|
||||
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...,
|
||||
decltype(get<Js>(inactive_args))...>,
|
||||
enzyme_const, (void *)&qfunc, enzyme_dup, &get<Is>(args)...,
|
||||
enzyme_const, &get<Js>(inactive_args)..., enzyme_interleave,
|
||||
&get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
inactive_arg_ts &&inactive_args)
|
||||
{
|
||||
auto arg_indices = std::make_index_sequence<
|
||||
tuple_size<std::remove_reference_t<arg_ts>>::value> {};
|
||||
|
||||
auto inactive_arg_indices = std::make_index_sequence<
|
||||
tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
|
||||
|
||||
return fwddiff_apply_enzyme_indexed(qfunc, args, shadow_args, arg_indices,
|
||||
inactive_args, inactive_arg_indices);
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_fwddiff_enzyme(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
qfunc_t &qfunc,
|
||||
arg_ts &args,
|
||||
arg_ts &shadow_args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
int qp_idx)
|
||||
{
|
||||
process_qf_args(u, args, qp_idx);
|
||||
process_qf_args(v, shadow_args, qp_idx);
|
||||
process_qf_result(f_qp,
|
||||
get<0>(fwddiff_apply_enzyme(qfunc, args, shadow_args, tuple<> {})));
|
||||
}
|
||||
#endif // MFEM_USE_ENZYME
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,338 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
#include "../../linalg/tensor.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_qf_arg(const T0 &, const T1 &, T2 &)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1, T2>,
|
||||
"process_qf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
arg.gradient = v(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
tensor<dual<T, T>, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i).value = u(i);
|
||||
arg(i).gradient = v(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
tensor<dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
arg(j, i).gradient = v((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<2> &u,
|
||||
const DeviceTensor<2> &v,
|
||||
arg_type &arg,
|
||||
const int &qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
|
||||
process_qf_arg(u_qp, v_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename qf_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
const std::array<DeviceTensor<2>, num_fields> &v,
|
||||
qf_args &args,
|
||||
const int &qp)
|
||||
{
|
||||
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
|
||||
{
|
||||
process_qf_arg(u[i], v[i], get<i>(args), qp);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).gradient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).gradient;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const T0 &, T1 &)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1>,
|
||||
"process_qf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
tensor<T> &arg)
|
||||
{
|
||||
arg(0) = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
process_qf_arg(u_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename qf_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
qf_args &args,
|
||||
const int &qp)
|
||||
{
|
||||
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
|
||||
{
|
||||
process_qf_arg(u[i], get<i>(args), qp);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
Vector process_qf_result(T0, T1)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1>,
|
||||
"process_qf_result not implemented for result type");
|
||||
return Vector{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const T &x)
|
||||
{
|
||||
r(0) = x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1> &r,
|
||||
const dual<T, T> &x)
|
||||
{
|
||||
r(0) = x.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T> &x)
|
||||
{
|
||||
r(0) = x(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,885 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
#include <utility>
|
||||
|
||||
// Define a portable unreachable macro
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#if defined(__CUDACC_VER_MAJOR__)
|
||||
#if __CUDACC_VER_MAJOR__ <= 11 && __CUDACC_VER_MINOR__ < 3
|
||||
// nvcc didn't add __builtin_unreachable() until cuda 11.3
|
||||
#define MFEM_UNREACHABLE()
|
||||
#else
|
||||
// nvcc >= 11.3
|
||||
#define MFEM_UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
#else
|
||||
// host-only version
|
||||
#define MFEM_UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#define MFEM_UNREACHABLE() __assume(0)
|
||||
#endif
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief This is a class that mimics most of std::tuple's interface,
|
||||
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
|
||||
*
|
||||
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct tuple
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
*/
|
||||
template <typename T0>
|
||||
struct tuple<T0>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1>
|
||||
struct tuple<T0, T1>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2>
|
||||
struct tuple<T0, T1, T2>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
struct tuple<T0, T1, T2, T3>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
struct tuple<T0, T1, T2, T3, T4>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
* @tparam T7 The eighth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
};
|
||||
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
T8 v8;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class template argument deduction rule for tuples
|
||||
* @tparam T The variadic template parameter for tuple types
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE
|
||||
tuple(T...) -> tuple<T...>;
|
||||
|
||||
/**
|
||||
* @brief helper function for combining a list of values into a tuple
|
||||
* @tparam T types of the values to be tuple-d
|
||||
* @param args the actual values to be put into a tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
|
||||
{
|
||||
return tuple<T...> {args...};
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size
|
||||
{
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size<tuple<Types...>> :
|
||||
std::integral_constant<std::size_t, sizeof...(Types)>
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a reference to the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a copy of the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a function intended to be used for extracting the ith type from a tuple.
|
||||
*
|
||||
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
|
||||
*
|
||||
* @tparam i the index of the tuple to query
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param values the tuple of values
|
||||
* @return a copy of the ith entry of the input
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the + operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple sum
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) + get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise sum of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return plus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the += operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be incremented
|
||||
* @param y tuple of increment values
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) += get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return plus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the -= operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be subracted from
|
||||
* @param y tuple of values to subtract from x
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) -= get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return minus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) - get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise difference of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return minus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{-get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
|
||||
{
|
||||
return unary_minus_helper(x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise division of x by y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return div_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant numerator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a / get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant denomenator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
|
||||
const real_t a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple x
|
||||
* @param a the numerator
|
||||
* @param x a tuple of denominator values
|
||||
* @brief return a tuple of values defined by division of a by the elements of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const real_t a, const tuple<T...>& x)
|
||||
{
|
||||
return div_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of numerator values
|
||||
* @param a a denominator
|
||||
* @brief return a tuple of values defined by elementwise division of x by a
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const real_t a)
|
||||
{
|
||||
return div_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise multiplication of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return mult_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const real_t a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a * get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
|
||||
const real_t a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param a a scaling factor
|
||||
* @param x the tuple object
|
||||
* @brief multiply each component of x by the value a on the left
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const real_t a, const tuple<T...>& x)
|
||||
{
|
||||
return mult_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param x the tuple object
|
||||
* @param a a scaling factor
|
||||
* @brief multiply each component of x by the value a on the right
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
|
||||
{
|
||||
return mult_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @tparam i a list of indices used to acces each element of the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief helper used to implement printing a tuple of values
|
||||
*/
|
||||
template <typename... T, std::size_t... i>
|
||||
auto& print_helper(std::ostream& out, const tuple<T...>& A,
|
||||
std::integer_sequence<size_t, i...>)
|
||||
{
|
||||
out << "tuple{";
|
||||
(..., (out << (i == 0 ? "" : ", ") << get<i>(A)));
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief print a tuple of values
|
||||
*/
|
||||
template <typename... T>
|
||||
auto& operator<<(std::ostream& out, const tuple<T...>& A)
|
||||
{
|
||||
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper to apply a lambda to a tuple
|
||||
*
|
||||
* @tparam lambda The functor type
|
||||
* @tparam T The tuple types
|
||||
* @tparam i The integer sequence to i
|
||||
* @param f The functor to apply to the tuple
|
||||
* @param args The input tuple
|
||||
* @return The functor output
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a struct used to determine the type at index I of a tuple
|
||||
*
|
||||
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
|
||||
*
|
||||
* @tparam I the index of the desired type
|
||||
* @tparam T a tuple of different types
|
||||
*/
|
||||
template <size_t I, class T>
|
||||
struct tuple_element;
|
||||
|
||||
// recursive case
|
||||
/// @overload
|
||||
template <size_t I, class Head, class... Tail>
|
||||
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
|
||||
tuple<Tail...>>
|
||||
{
|
||||
};
|
||||
|
||||
// base case
|
||||
/// @overload
|
||||
template <class Head, class... Tail>
|
||||
struct tuple_element<0, tuple<Head, Tail...>>
|
||||
{
|
||||
using type = Head; ///< the type at the specified index
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type is a @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/// @overload
|
||||
template <typename... T>
|
||||
struct is_tuple<tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple_of_tuples : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct is_tuple_of_tuples<tuple<T...>>
|
||||
{
|
||||
static constexpr bool value = (is_tuple<T>::value &&
|
||||
...); ///< true/false result of type check
|
||||
};
|
||||
|
||||
/** @brief Auxiliary template function that merges (concatenates) two
|
||||
mfem::future::tuple types into a single std::tuple that is empty, i.e. it is
|
||||
value initialized. */
|
||||
template <typename... T1s, typename... T2s>
|
||||
constexpr auto merge_mfem_tuples_as_empty_std_tuple(
|
||||
const mfem::future::tuple<T1s...> &,
|
||||
const mfem::future::tuple<T2s...> &)
|
||||
{
|
||||
return std::tuple<T1s..., T2s...> {};
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
+2257
File diff suppressed because it is too large
Load Diff
+16
-24
@@ -16,9 +16,7 @@ namespace mfem
|
||||
|
||||
void DofTransformation::TransformPrimal(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -48,9 +46,7 @@ void DofTransformation::TransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Height();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -80,9 +76,7 @@ void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::TransformDual(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -112,9 +106,7 @@ void DofTransformation::TransformDual(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformDual(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -142,33 +134,33 @@ void DofTransformation::InvTransformDual(real_t *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
ran_dof_trans.TransformPrimalCols(elmat);
|
||||
}
|
||||
if (dom_dof_trans)
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
ran_dof_trans.TransformDualCols(elmat);
|
||||
}
|
||||
if (dom_dof_trans)
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -201,19 +201,19 @@ public:
|
||||
inline int NumRows() const { return dof_trans_->NumRows(); }
|
||||
inline int Width() const { return dof_trans_->Width(); }
|
||||
inline int NumCols() const { return dof_trans_->NumCols(); }
|
||||
inline bool IsIdentity() const { return dof_trans_->IsIdentity(); }
|
||||
inline bool IsIdentity() const { return !dof_trans_ || dof_trans_->IsIdentity(); }
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
GridFunction object. */
|
||||
void TransformPrimal(real_t *v) const;
|
||||
inline void TransformPrimal(Vector &v) const
|
||||
{ TransformPrimal(v.GetData()); }
|
||||
inline void TransformPrimal(Vector &v) const { TransformPrimal(v.GetData()); }
|
||||
|
||||
/// Transform groups of DoFs stored as dense matrices
|
||||
inline void TransformPrimalCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformPrimal(V.GetColumn(c));
|
||||
@@ -251,6 +251,7 @@ public:
|
||||
/// Transform rows of a dense matrix containing dual DoFs
|
||||
inline void TransformDualRows(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
Vector row;
|
||||
for (int r=0; r<V.Height(); r++)
|
||||
{
|
||||
@@ -263,6 +264,7 @@ public:
|
||||
/// Transform columns of a dense matrix containing dual DoFs
|
||||
inline void TransformDualCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformDual(V.GetColumn(c));
|
||||
@@ -274,16 +276,16 @@ public:
|
||||
computed by a DiscreteInterpolator before copying into a
|
||||
DiscreteLinearOperator.
|
||||
*/
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Transform a matrix of dual DoFs entries from different finite element spaces
|
||||
as computed by a BilinearFormIntegrator before summing into a
|
||||
MixedBilinearForm object.
|
||||
*/
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
|
||||
@@ -20,6 +20,16 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
DofToQuad DofToQuad::Abs() const
|
||||
{
|
||||
DofToQuad d2q(*this);
|
||||
d2q.B.Abs();
|
||||
d2q.Bt.Abs();
|
||||
d2q.G.Abs();
|
||||
d2q.Gt.Abs();
|
||||
return d2q;
|
||||
}
|
||||
|
||||
FiniteElement::FiniteElement(int D, Geometry::Type G,
|
||||
int Do, int O, int F)
|
||||
: Nodes(Do)
|
||||
|
||||
@@ -219,6 +219,9 @@ public:
|
||||
- #ndof x #nqpt, for H(div) vector elements, or
|
||||
- #ndof x #nqpt x cdim, for H(curl) vector elements. */
|
||||
Array<real_t> Gt;
|
||||
|
||||
/// Returns absolute value of the maps
|
||||
DofToQuad Abs() const;
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
|
||||
+30
-24
@@ -1891,31 +1891,38 @@ L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
|
||||
|
||||
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;
|
||||
if (p == 0)
|
||||
{
|
||||
dof_map[idx(0,0,0,0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set3(0.375, 0.375, 0.25);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 = 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);
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -2197,7 +2204,6 @@ void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
{
|
||||
dshape(it.second, d) = m_dshape(it.first, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
#include "lor/lor.hpp"
|
||||
#include "dgmassinv.hpp"
|
||||
#include "hyperbolic.hpp"
|
||||
#include "bounds.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
|
||||
+16
-17
@@ -331,7 +331,6 @@ void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -347,7 +346,6 @@ void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -1936,6 +1934,7 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -1956,10 +1955,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
subY.SetSize(lP.Height());
|
||||
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, dofs);
|
||||
fespace->GetElementDofs(k, dofs, doftrans);
|
||||
old_elem_dof->GetRow(emb.parent, old_dofs);
|
||||
|
||||
if (!doftrans)
|
||||
if (doftrans.IsIdentity())
|
||||
{
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
@@ -1979,7 +1978,7 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
doftrans.SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
@@ -1990,10 +1989,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
old_DoFTrans.InvTransformPrimal(subX);
|
||||
lP.Mult(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
doftrans.TransformPrimal(subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2020,6 +2019,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
const FiniteElement *fe = nullptr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -2040,10 +2040,10 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
const DenseMatrix &lP = (fespace->IsVariableOrder()) ? eP : localP[geom](
|
||||
emb.matrix);
|
||||
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
fespace->GetElementDofs(k, f_dofs, doftrans);
|
||||
old_elem_dof->GetRow(emb.parent, c_dofs);
|
||||
|
||||
if (!doftrans)
|
||||
if (doftrans.IsIdentity())
|
||||
{
|
||||
subY.SetSize(lP.Width());
|
||||
|
||||
@@ -2074,7 +2074,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
doftrans.SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
@@ -2083,7 +2083,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
doftrans->InvTransformDual(subX);
|
||||
doftrans.InvTransformDual(subX);
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
@@ -2095,7 +2095,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.TransformDual(subYt);
|
||||
y.AddElementVector(c_vdofs, subYt);
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
@@ -3407,6 +3407,8 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(elem, dofs);
|
||||
@@ -3513,7 +3515,6 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetElementDofs(int elem,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementDofs(elem, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -3523,6 +3524,8 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(bel, dofs);
|
||||
@@ -3617,7 +3620,6 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementDofs(bel, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -4276,9 +4278,6 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
void FiniteElementSpace::PRefineAndUpdate(const Array<pRefinement> & refs,
|
||||
bool want_transfer)
|
||||
{
|
||||
MFEM_VERIFY(PRefinementSupported(),
|
||||
"p-refinement is not supported in this space");
|
||||
|
||||
if (want_transfer)
|
||||
{
|
||||
fesPrev.reset(new FiniteElementSpace(mesh, fec, vdim, ordering));
|
||||
|
||||
+42
-30
@@ -946,8 +946,8 @@ public:
|
||||
/// could be used to produce the appropriate offsets from these local dofs.
|
||||
///@{
|
||||
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'.
|
||||
/// The returned indices are offsets into an @ref ldof vector. See also
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'. The
|
||||
/// returned indices are offsets into an @ref ldof vector. See also
|
||||
/// GetElementVDofs().
|
||||
///
|
||||
/// @note In many cases the returned DofTransformation object will be NULL.
|
||||
@@ -957,15 +957,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
virtual void GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -980,15 +983,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetBdrElementDofs(int bel, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
virtual void GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1192,15 +1198,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
void GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1216,15 +1225,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
void GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
|
||||
+234
-174
@@ -288,8 +288,6 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
GridFunction &u = *this;
|
||||
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation *udoftrans;
|
||||
DofTransformation *fdoftrans;
|
||||
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
@@ -302,6 +300,7 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
flux = 0.0;
|
||||
count = 0;
|
||||
|
||||
DofTransformation udoftrans, fdoftrans;
|
||||
for (int i = 0; i < nfe; i++)
|
||||
{
|
||||
if (subdomain >= 0 && ufes->GetAttribute(i) != subdomain)
|
||||
@@ -309,23 +308,17 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
continue;
|
||||
}
|
||||
|
||||
udoftrans = ufes->GetElementVDofs(i, udofs);
|
||||
fdoftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
ufes->GetElementVDofs(i, udofs, udoftrans);
|
||||
ffes->GetElementVDofs(i, fdofs, fdoftrans);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
if (udoftrans)
|
||||
{
|
||||
udoftrans->InvTransformPrimal(ul);
|
||||
}
|
||||
udoftrans.InvTransformPrimal(ul);
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
*ffes->GetFE(i), fl, wcoef);
|
||||
|
||||
if (fdoftrans)
|
||||
{
|
||||
fdoftrans->TransformPrimal(fl);
|
||||
}
|
||||
fdoftrans.TransformPrimal(fl);
|
||||
flux.AddElementVector(fdofs, fl);
|
||||
|
||||
FiniteElementSpace::AdjustVDofs(fdofs);
|
||||
@@ -353,12 +346,23 @@ void GridFunction::ComputeFlux(BilinearFormIntegrator &blfi,
|
||||
|
||||
int GridFunction::VectorDim() const
|
||||
{
|
||||
return fes->GetVectorDim();
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
}
|
||||
|
||||
int GridFunction::CurlDim() const
|
||||
{
|
||||
return fes->GetCurlDim();
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return 2 * fes->GetMesh()->SpaceDimension() - 3;
|
||||
}
|
||||
return fes->GetVDim()*fe->GetCurlDim();
|
||||
}
|
||||
|
||||
void GridFunction::GetTrueDofs(Vector &tv) const
|
||||
@@ -394,7 +398,8 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
{
|
||||
Array<int> vdofs;
|
||||
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
const IntegrationRule *ElemVert =
|
||||
Geometries.GetVertices(FElem->GetGeomType());
|
||||
@@ -404,10 +409,7 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
vdim--;
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
@@ -448,7 +450,8 @@ real_t GridFunction::GetValue(int i, const IntegrationPoint &ip, int vdim)
|
||||
const
|
||||
{
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -463,10 +466,7 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
return (DofVal * LocVec);
|
||||
}
|
||||
@@ -477,13 +477,11 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -517,22 +515,19 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
}
|
||||
|
||||
void GridFunction::GetValues(int i, const IntegrationRule &ir, Vector &vals,
|
||||
int vdim)
|
||||
const
|
||||
int vdim) const
|
||||
{
|
||||
Array<int> dofs;
|
||||
int n = ir.GetNPoints();
|
||||
vals.SetSize(n);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Vector DofVal(dof), loc_data(dof);
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
for (int k = 0; k < n; k++)
|
||||
@@ -865,12 +860,12 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
|
||||
Array<int> vdofs;
|
||||
const FiniteElement *fe = NULL;
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
fe = fes->GetFE(T.ElementNo);
|
||||
break;
|
||||
case ElementTransformation::EDGE:
|
||||
@@ -960,10 +955,7 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
int dof = fe->GetDof();
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -1007,13 +999,11 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
|
||||
int dof = FElem->GetDof();
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
int nip = ir.GetNPoints();
|
||||
|
||||
@@ -1104,23 +1094,19 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
DofTransformation * doftrans;
|
||||
DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, orig_loc_values;
|
||||
int i, j, d, ne, dof, odof, vdim;
|
||||
|
||||
ne = fes->GetNE();
|
||||
vdim = fes->GetVDim();
|
||||
DofTransformation doftrans, orig_doftrans;
|
||||
for (i = 0; i < ne; i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
orig_doftrans = orig_fes->GetElementVDofs(i, orig_vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
orig_fes->GetElementVDofs(i, orig_vdofs, orig_doftrans);
|
||||
orig_func.GetSubVector(orig_vdofs, orig_loc_values);
|
||||
if (orig_doftrans)
|
||||
{
|
||||
orig_doftrans->InvTransformPrimal(orig_loc_values);
|
||||
}
|
||||
orig_doftrans.InvTransformPrimal(orig_loc_values);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const FiniteElement *orig_fe = orig_fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
@@ -1137,10 +1123,7 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
loc_values(d*dof+j) = shape * (&orig_loc_values[d * odof]);
|
||||
}
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(loc_values);
|
||||
}
|
||||
doftrans.TransformPrimal(loc_values);
|
||||
SetSubVector(vdofs, loc_values);
|
||||
}
|
||||
}
|
||||
@@ -1150,8 +1133,6 @@ void GridFunction::GetBdrValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
// DofTransformation * doftrans;
|
||||
// DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, loc_values_t, orig_loc_values, orig_loc_values_t;
|
||||
int i, j, d, nbe, dof, odof, vdim;
|
||||
@@ -1191,7 +1172,8 @@ void GridFunction::GetVectorFieldValues(
|
||||
ElementTransformation *transf;
|
||||
|
||||
const int n = ir.GetNPoints();
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const int dof = fe->GetDof();
|
||||
const int sdim = fes->GetMesh()->SpaceDimension();
|
||||
@@ -1203,10 +1185,7 @@ void GridFunction::GetVectorFieldValues(
|
||||
DenseMatrix vshape(dof, vdim);
|
||||
Vector loc_data, val(vdim);
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(k);
|
||||
@@ -1397,6 +1376,7 @@ void GridFunction::GetVectorGradientHat(
|
||||
|
||||
real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1424,13 +1404,10 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
// Assuming RT-type space
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
Vector loc_data, divshape(fe->GetDof());
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
fe->CalcDivShape(T.GetIntPoint(), divshape);
|
||||
return (loc_data * divshape) / T.Weight();
|
||||
}
|
||||
@@ -1483,6 +1460,7 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
|
||||
void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1517,13 +1495,10 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
// Assuming ND-type space
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
DenseMatrix curl_shape(fe->GetDof(), fe->GetCurlDim());
|
||||
curl.SetSize(curl_shape.Width());
|
||||
fe->CalcPhysCurlShape(T, curl_shape);
|
||||
@@ -1724,11 +1699,10 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
MassIntegrator Mi;
|
||||
DenseMatrix loc_mass;
|
||||
DofTransformation * te_doftrans;
|
||||
DofTransformation * tr_doftrans;
|
||||
Array<int> te_dofs, tr_dofs;
|
||||
Vector loc_avgs, loc_this;
|
||||
Vector int_psi(avgs.Size());
|
||||
DofTransformation tr_doftrans, te_doftrans;
|
||||
|
||||
avgs = 0.0;
|
||||
int_psi = 0.0;
|
||||
@@ -1736,19 +1710,13 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
Mi.AssembleElementMatrix2(*fes->GetFE(i), *avgs.FESpace()->GetFE(i),
|
||||
*fes->GetElementTransformation(i), loc_mass);
|
||||
tr_doftrans = fes->GetElementDofs(i, tr_dofs);
|
||||
te_doftrans = avgs.FESpace()->GetElementDofs(i, te_dofs);
|
||||
fes->GetElementDofs(i, tr_dofs, tr_doftrans);
|
||||
avgs.FESpace()->GetElementDofs(i, te_dofs, te_doftrans);
|
||||
GetSubVector(tr_dofs, loc_this);
|
||||
if (tr_doftrans)
|
||||
{
|
||||
tr_doftrans->InvTransformPrimal(loc_this);
|
||||
}
|
||||
tr_doftrans.InvTransformPrimal(loc_this);
|
||||
loc_avgs.SetSize(te_dofs.Size());
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
if (te_doftrans)
|
||||
{
|
||||
te_doftrans->TransformPrimal(loc_avgs);
|
||||
}
|
||||
te_doftrans.TransformPrimal(loc_avgs);
|
||||
avgs.AddElementVector(te_dofs, loc_avgs);
|
||||
loc_this = 1.0; // assume the local basis for 'this' sums to 1
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
@@ -1763,12 +1731,10 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
Array<int> dof_idx;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(el, dof_idx);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(el, dof_idx, doftrans);
|
||||
GetSubVector(dof_idx, dof_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(dof_vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(dof_vals);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
@@ -1793,6 +1759,7 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
Array<int> src_vdofs, dest_vdofs;
|
||||
Vector src_lvec, dest_lvec(vdim*P.Height());
|
||||
|
||||
DofTransformation src_doftrans, doftrans;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
// Assuming the projection matrix P depends only on the element geometry
|
||||
@@ -1804,21 +1771,15 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
cached_geom = geom;
|
||||
}
|
||||
|
||||
DofTransformation * src_doftrans = src.fes->GetElementVDofs(i, src_vdofs);
|
||||
src.fes->GetElementVDofs(i, src_vdofs, src_doftrans);
|
||||
src.GetSubVector(src_vdofs, src_lvec);
|
||||
if (src_doftrans)
|
||||
{
|
||||
src_doftrans->InvTransformPrimal(src_lvec);
|
||||
}
|
||||
src_doftrans.InvTransformPrimal(src_lvec);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
P.Mult(&src_lvec[vd*P.Width()], &dest_lvec[vd*P.Height()]);
|
||||
}
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, dest_vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(dest_lvec);
|
||||
}
|
||||
fes->GetElementVDofs(i, dest_vdofs, doftrans);
|
||||
doftrans.TransformPrimal(dest_lvec);
|
||||
SetSubVector(dest_vdofs, dest_lvec);
|
||||
}
|
||||
}
|
||||
@@ -1827,15 +1788,13 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
const Vector &lo_, const Vector &hi_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
|
||||
MFEM_ASSERT(weights.Size() == size, "Different # of weights and dofs.");
|
||||
MFEM_ASSERT(lo_.Size() == size, "Different # of lower bounds and dofs.");
|
||||
@@ -1852,10 +1811,7 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
slbqp.SetPrintLevel(0); // print messages only if not converged
|
||||
slbqp.Mult(vals, new_vals);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
SetSubVector(vdofs, new_vals);
|
||||
}
|
||||
|
||||
@@ -1863,14 +1819,12 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
real_t min_, real_t max_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
|
||||
real_t max_val = vals.Max();
|
||||
real_t min_val = vals.Min();
|
||||
@@ -1878,10 +1832,7 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
if (max_val <= min_)
|
||||
{
|
||||
new_vals = min_;
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
SetSubVector(vdofs, new_vals);
|
||||
return;
|
||||
}
|
||||
@@ -1913,7 +1864,6 @@ void GridFunction::RestrictConforming()
|
||||
|
||||
void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
{
|
||||
int i, j;
|
||||
Array<int> vertices;
|
||||
Array<real_t> values;
|
||||
Array<int> overlap(fes->GetNV());
|
||||
@@ -1921,17 +1871,17 @@ void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
nval = 0.0;
|
||||
overlap = 0;
|
||||
nval.HostReadWrite();
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVertices(i, vertices);
|
||||
GetNodalValues(i, values, vdim);
|
||||
for (j = 0; j < vertices.Size(); j++)
|
||||
for (int j = 0; j < vertices.Size(); j++)
|
||||
{
|
||||
nval(vertices[j]) += values[j];
|
||||
overlap[vertices[j]]++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
nval(i) /= overlap[i];
|
||||
}
|
||||
@@ -2211,6 +2161,7 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector lvec;
|
||||
DofTransformation dof_tr;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
@@ -2225,10 +2176,10 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
}
|
||||
fe = fes->GetBE(i);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
DofTransformation *dof_tr = fes->GetBdrElementDofs(i, dofs);
|
||||
fes->GetBdrElementDofs(i, dofs, dof_tr);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
if (dof_tr) { dof_tr->TransformPrimal(lvec); }
|
||||
dof_tr.TransformPrimal(lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
|
||||
@@ -2333,6 +2284,8 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
DenseMatrix loc_mass;
|
||||
Array<int> vdofs, vertices;
|
||||
Vector vals, loc_mass_vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
mesh->GetElementVertices(i, vertices);
|
||||
@@ -2344,11 +2297,8 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
loc_mass);
|
||||
vals.SetSize(fe->GetDof());
|
||||
fe->ProjectDelta(j, vals);
|
||||
const DofTransformation* const doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
loc_mass_vals.SetSize(vals.Size());
|
||||
loc_mass.Mult(vals, loc_mass_vals);
|
||||
@@ -2361,7 +2311,7 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
@@ -2372,13 +2322,10 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2445,23 +2392,19 @@ void GridFunction::ProjectCoefficient(
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2528,8 +2471,7 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
@@ -2538,13 +2480,10 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
continue;
|
||||
}
|
||||
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2555,7 +2494,6 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
real_t val;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
// DofTransformation * doftrans;
|
||||
Array<int> vdofs;
|
||||
|
||||
vdim = fes->GetVDim();
|
||||
@@ -2738,6 +2676,7 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -2757,11 +2696,8 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
const DofTransformation* const doftrans = fes->GetBdrElementDofs(i, dofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(lvec);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
@@ -4059,6 +3995,7 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans, ftrans;
|
||||
|
||||
int dim = ufes->GetMesh()->Dimension();
|
||||
int nfe = ufes->GetNE();
|
||||
@@ -4090,19 +4027,13 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
{
|
||||
if (with_subdomains && ufes->GetAttribute(i) != s) { continue; }
|
||||
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(i, udofs);
|
||||
const DofTransformation* const ftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
ufes->GetElementVDofs(i, udofs, utrans);
|
||||
ffes->GetElementVDofs(i, fdofs, ftrans);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
flux.GetSubVector(fdofs, fla);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
if (ftrans)
|
||||
{
|
||||
ftrans->InvTransformPrimal(fla);
|
||||
}
|
||||
utrans.InvTransformPrimal(ul);
|
||||
ftrans.InvTransformPrimal(fla);
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
@@ -4317,6 +4248,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
MFEM_VERIFY(tichonov_coeff >= 0.0, "tichonov_coeff cannot be negative");
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans;
|
||||
|
||||
Mesh *mesh = ufes->GetMesh();
|
||||
int dim = mesh->Dimension();
|
||||
@@ -4398,14 +4330,11 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
flux_order));
|
||||
int num_integration_pts = ir->GetNPoints();
|
||||
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(ielem, udofs);
|
||||
ufes->GetElementVDofs(ielem, udofs, utrans);
|
||||
u.GetSubVector(udofs, ul);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
utrans.InvTransformPrimal(ul);
|
||||
Transf = ufes->GetElementTransformation(ielem);
|
||||
FiniteElement *dummy = nullptr;
|
||||
const auto *dummy = ufes->GetFE(ielem);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
|
||||
*dummy, fl, with_coeff, ir);
|
||||
|
||||
@@ -4634,4 +4563,135 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
return sol2d;
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int fes_dim = fes->GetVDim();
|
||||
int rdim = fe->GetDim();
|
||||
|
||||
const TensorBasisElement *tbe =
|
||||
dynamic_cast<const TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
|
||||
const Array<int> &dof_map = tbe->GetDofMap();
|
||||
|
||||
Vector loc_data;
|
||||
Array<int> dof_idx;
|
||||
fes->GetElementDofs(elem, dof_idx);
|
||||
int ndofs = dof_idx.Size();
|
||||
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
lower.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
upper.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Array<int> dof_idx_c = dof_idx;
|
||||
Vector lowerT(lower, d_off*n_c_pts, n_c_pts);
|
||||
Vector upperT(upper, d_off*n_c_pts, n_c_pts);
|
||||
fes->DofsToVDofs(vdim > 0 ? vdim-1 : d, dof_idx_c);
|
||||
GetSubVector(dof_idx_c, loc_data);
|
||||
Vector nodal_data;
|
||||
if (dof_map.Size() == 0)
|
||||
{
|
||||
nodal_data.SetDataAndSize(loc_data.GetData(), ndofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodal_data.SetSize(ndofs);
|
||||
for (int j = 0; j < ndofs; j++)
|
||||
{
|
||||
nodal_data(j) = loc_data(dof_map[j]);
|
||||
}
|
||||
}
|
||||
plb.GetNDBounds(rdim, nodal_data, lowerT, upperT);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
Vector lowerC, upperC;
|
||||
GetElementBoundsAtControlPoints(elem, plb, lowerC, upperC, vdim);
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int rdim = fe->GetDim();
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
upper.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Vector lowerT(lowerC, d_off*n_c_pts, n_c_pts);
|
||||
Vector upperT(upperC, d_off*n_c_pts, n_c_pts);
|
||||
lower(d_off) = lowerT.Min();
|
||||
upper(d_off) = upperT.Max();
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
int nel = fes->GetNE();
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize(nel*(vdim > 0 ? 1 :fes_dim));
|
||||
upper.SetSize(nel*(vdim > 0 ? 1 :fes_dim));
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
Vector lt, ut;
|
||||
GetElementBounds(e, plb, lt, ut, vdim);
|
||||
for (int d = 0; d < fes_dim ; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
lower(e + d_off*nel) = lt(d_off);
|
||||
upper(e + d_off*nel) = ut(d_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PLBound GridFunction::GetElementBounds(Vector &lower,
|
||||
Vector &upper,
|
||||
const int ref_factor,
|
||||
const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
GetElementBounds(plb, lower, upper, vdim);
|
||||
return plb;
|
||||
}
|
||||
|
||||
PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor, const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
Vector lel, uel;
|
||||
GetElementBounds(plb, lel, uel, vdim);
|
||||
|
||||
int nel = fes->GetNE();
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize(vdim > 0 ? 1 : fes_dim);
|
||||
upper.SetSize(vdim > 0 ? 1 : fes_dim);
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Vector lelt(lel, d_off*nel, nel);
|
||||
Vector uelt(uel, d_off*nel, nel);
|
||||
lower(d_off) = lelt.Min();
|
||||
upper(d_off) = uelt.Max();
|
||||
}
|
||||
return plb;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+47
-1
@@ -16,6 +16,7 @@
|
||||
#include "fespace.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "bilininteg.hpp"
|
||||
#include "bounds.hpp"
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
#include "../general/adios2stream.hpp"
|
||||
#endif
|
||||
@@ -1561,11 +1562,56 @@ public:
|
||||
must be 2 and that quad elements will be broken into two triangles.*/
|
||||
void SaveSTL(std::ostream &out, int TimesToRefine = 1);
|
||||
|
||||
/** @name Methods to compute bounds on the grid function
|
||||
\brief See bounds.hpp for \ref PLBound that constructs piecewise linear
|
||||
bounds for a given set of bases. These piecewise bounds can be used to compute bounds on a grid function. Currently tensor-product elements are
|
||||
supported with Lagrange interpolants on Gauss Legendre nodes and Gauss Lobatto Legendre nodes, and Bernstein bases.
|
||||
*/
|
||||
///@{
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in @b lower and @b upper. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// Note: For most cases, this method/interface will be sufficient.
|
||||
virtual PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1);
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byVDim:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
PLBound GetElementBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1);
|
||||
|
||||
/// Compute piecewise linear bounds on the given element at the grid of
|
||||
/// [plb.ncp x plb.ncp x plb.ncp] control points for each of the vdim
|
||||
/// components of the gridfunction.
|
||||
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1);
|
||||
|
||||
/// Compute bounds on the grid function for the given element.
|
||||
/// The bounds are stored in @b lower and @b upper.
|
||||
void GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1);
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byVDim:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
const int vdim=-1);
|
||||
///@}
|
||||
|
||||
/// Destroys grid function.
|
||||
virtual ~GridFunction() { Destroy(); }
|
||||
};
|
||||
|
||||
|
||||
/** Overload operator<< for std::ostream and GridFunction; valid also for the
|
||||
derived class ParGridFunction */
|
||||
std::ostream &operator<<(std::ostream &out, const GridFunction &sol);
|
||||
|
||||
+3
-1
@@ -30,7 +30,9 @@ namespace mfem
|
||||
{
|
||||
|
||||
/** \brief FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary
|
||||
* collection of points.
|
||||
* collection of points. See Mittal et al., "General Field Evaluation in
|
||||
* High-Order Meshes on GPUs". (2025). Computers & Fluids. for technical
|
||||
* details.
|
||||
*
|
||||
* There are three key functions in FindPointsGSLIB:
|
||||
*
|
||||
|
||||
@@ -202,4 +202,68 @@ void CurlCurlIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void CurlCurlIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
auto absO = mapsO->Abs();
|
||||
auto absC = mapsC->Abs();
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
const int ID = (dofs1D << 4) | quad1D;
|
||||
switch (ID)
|
||||
{
|
||||
case 0x23:
|
||||
return internal::SmemPACurlCurlApply3D<2,3>(
|
||||
dofs1D, quad1D,
|
||||
symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
case 0x34:
|
||||
return internal::SmemPACurlCurlApply3D<3,4>(
|
||||
dofs1D, quad1D,
|
||||
symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
case 0x45:
|
||||
return internal::SmemPACurlCurlApply3D<4,5>(
|
||||
dofs1D, quad1D,
|
||||
symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
case 0x56:
|
||||
return internal::SmemPACurlCurlApply3D<5,6>(
|
||||
dofs1D, quad1D,
|
||||
symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
default:
|
||||
return internal::SmemPACurlCurlApply3D<0,0>(
|
||||
dofs1D, quad1D, symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
internal::PACurlCurlApply3D<0,0>(
|
||||
dofs1D, quad1D, symmetric, ne,
|
||||
absO.B, absC.B, absO.Bt, absC.Bt, absC.G, absC.Gt,
|
||||
abs_pa_data, x, y, true);
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
internal::PACurlCurlApply2D(dofs1D, quad1D, ne, absO.B, absO.Bt,
|
||||
absC.G, absC.Gt, abs_pa_data, x, y, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension!");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -200,7 +200,7 @@ void PADiffusionSetup2D<3>(const int Q1D,
|
||||
const real_t E = J11*J11 + J21*J21 + J31*J31;
|
||||
const real_t G = J12*J12 + J22*J22 + J32*J32;
|
||||
const real_t F = J11*J12 + J21*J22 + J31*J32;
|
||||
const real_t iw = 1.0 / sqrt(E*G - F*F);
|
||||
const real_t iw = 1.0 / std::sqrt(E*G - F*F);
|
||||
const real_t coeff = const_c ? C(0,0,0) : C(qx,qy,e);
|
||||
const real_t alpha = wq * coeff * iw;
|
||||
D(qx,qy,0,e) = alpha * G; // 1,1
|
||||
|
||||
@@ -483,19 +483,6 @@ inline void SmemPADiffusionDiagonal3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
void PADiffusionApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symm,
|
||||
const Array<real_t> &B,
|
||||
const Array<real_t> &G,
|
||||
const Array<real_t> &Bt,
|
||||
const Array<real_t> &Gt,
|
||||
const Vector &D,
|
||||
const Vector &X,
|
||||
Vector &Y);
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
// OCCA PA Diffusion Apply 2D kernel
|
||||
void OccaPADiffusionApply2D(const int D1D,
|
||||
@@ -1022,6 +1009,7 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
@@ -1051,11 +1039,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
real_t (*QDD0)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+0);
|
||||
real_t (*QDD1)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+1);
|
||||
real_t (*QDD2)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+2);
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
@@ -1063,9 +1051,9 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
B[qx][dy] = b(qx,dy);
|
||||
G[qx][dy] = g(qx,dy);
|
||||
@@ -1073,11 +1061,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1093,11 +1081,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1114,11 +1102,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1149,9 +1137,9 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
MFEM_SYNC_THREAD;
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
Bt[dy][qx] = b(qx,dy);
|
||||
Gt[dy][qx] = g(qx,dy);
|
||||
@@ -1159,11 +1147,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -1180,11 +1168,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(Q1D)
|
||||
@@ -1201,11 +1189,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
|
||||
@@ -164,6 +164,36 @@ void DiffusionIntegrator::AssemblePatchPA(const int patch,
|
||||
SetupPatchPA(patch, mesh); // For full quadrature, unitWeights = false
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
MFEM_ABORT("Ceed AbsMult not implemented yet");
|
||||
}
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
auto abs_maps = maps->Abs();
|
||||
|
||||
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
|
||||
abs_maps.B, abs_maps.G, abs_maps.Bt, abs_maps.Gt,
|
||||
abs_pa_data, x, y, dofs1D, quad1D);
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AddAbsMultTransposePA(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
if (symmetric)
|
||||
{
|
||||
AddAbsMultPA(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("DiffusionIntegrator::AddAbsMultTransposePA only implemented "
|
||||
"in the symmetric case.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This version uses full 1D quadrature rules, taking into account the
|
||||
// minimum interaction between basis functions and integration points.
|
||||
void DiffusionIntegrator::AddMultPatchPA(const int patch, const Vector &x,
|
||||
|
||||
@@ -147,6 +147,11 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
const GeometricFactors &geom, const DofToQuad &maps, const Vector &x,
|
||||
QuadratureFunction &QVec, Vector &y)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
static_assert((i_block < 0) == (j_block < 0),
|
||||
"i_block and j_block must both be non-negative or strictly negative.");
|
||||
static constexpr int d = dim;
|
||||
@@ -207,7 +212,7 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
const int iIndex = isComponent ? 0 : i;
|
||||
div += gradx(iIndex,i);
|
||||
}
|
||||
const real_t w = ipWeights[p] /det(invJ);
|
||||
const real_t w = ipWeights[p]/det(invJ);
|
||||
for (int m = 0; m < d; m++)
|
||||
{
|
||||
for (int q = qLower; q < qUpper; q++)
|
||||
@@ -221,8 +226,8 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
{
|
||||
for (int a = 0; a < d; a++)
|
||||
{
|
||||
contraction += 2*((a == q)*invJ(m,j_block) + (j_block==q)*invJ(m,a))*(gradx(0,
|
||||
a));
|
||||
contraction += 2*((a == q)*invJ(m,j_block)
|
||||
+ (j_block==q)*invJ(m,a))*(gradx(0, a));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -231,7 +236,7 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
{
|
||||
for (int b = 0; b < d; b++)
|
||||
{
|
||||
contraction += ((a == q)*invJ(m,b) + (b==q)*invJ(m,a))
|
||||
contraction += ((a == q)*invJ(m,b) + (b == q)*invJ(m,a))
|
||||
*(gradx(a,b) + gradx(b, a));
|
||||
}
|
||||
}
|
||||
@@ -239,7 +244,8 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
|
||||
// contraction = 4*sym(grad(u))sym(grad(v))
|
||||
const int qIndex = isComponent ? 0 : q;
|
||||
Q(p,m,qIndex,e) = w*(lamDev(p, e)*invJ(m,q)*div + 0.5*muDev(p, e)*contraction);
|
||||
Q(p,m,qIndex,e) = w*(lamDev(p, e)*invJ(m,q)*div
|
||||
+ 0.5*muDev(p, e)*contraction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,6 +283,11 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
// Assuming all elements are the same
|
||||
const auto &ir = QVec.GetIntRule(0);
|
||||
static constexpr int d = dim;
|
||||
@@ -361,6 +372,11 @@ void ElasticityAssembleEA_(const int i_block,
|
||||
const DofToQuad &maps,
|
||||
Vector &emat)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
// Assuming all elements are the same
|
||||
static constexpr int d = dim;
|
||||
const int numPoints = ir.GetNPoints();
|
||||
|
||||
@@ -662,7 +662,8 @@ void PACurlCurlApply2D(const int D1D,
|
||||
const Array<real_t> &gct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
Vector &y,
|
||||
const bool useAbs)
|
||||
{
|
||||
|
||||
auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
|
||||
@@ -717,7 +718,8 @@ void PACurlCurlApply2D(const int D1D,
|
||||
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t wy = (c == 0) ? -Gc(qy,dy) : Bo(qy,dy);
|
||||
const int sign = useAbs ? 1 : -1;
|
||||
const real_t wy = (c == 0) ? (sign*Gc(qy,dy)) : Bo(qy,dy);
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
curl[qy][qx] += gradX[qx] * wy;
|
||||
@@ -760,7 +762,8 @@ void PACurlCurlApply2D(const int D1D,
|
||||
}
|
||||
for (int dy = 0; dy < D1Dy; ++dy)
|
||||
{
|
||||
const real_t wy = (c == 0) ? -Gct(dy,qy) : Bot(dy,qy);
|
||||
const int sign = useAbs ? 1 : -1;
|
||||
const real_t wy = (c == 0) ? (sign*Gct(dy,qy)) : Bot(dy,qy);
|
||||
|
||||
for (int dx = 0; dx < D1Dx; ++dx)
|
||||
{
|
||||
|
||||
@@ -828,7 +828,7 @@ inline void SmemPACurlCurlAssembleDiagonal3D(const int d1d,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
// PA H(curl) curl-curl Apply 2D kernel
|
||||
// PA H(curl) curl-curl Apply/AbsApply 2D kernel
|
||||
void PACurlCurlApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
@@ -838,9 +838,10 @@ void PACurlCurlApply2D(const int D1D,
|
||||
const Array<real_t> &gct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y);
|
||||
Vector &y,
|
||||
const bool useAbs = false);
|
||||
|
||||
// PA H(curl) curl-curl Apply 3D kernel
|
||||
// PA H(curl) curl-curl Apply/AbsApply 3D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void PACurlCurlApply3D(const int d1d,
|
||||
const int q1d,
|
||||
@@ -854,7 +855,8 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
const Array<real_t> &gct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
Vector &y,
|
||||
const bool useAbs = false)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
|
||||
"Error: d1d > HCURL_MAX_D1D");
|
||||
@@ -970,7 +972,16 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
{
|
||||
// \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
|
||||
curl[qz][qy][qx][1] += gradXY[qy][qx][1] * wDz; // (u_0)_{x_2}
|
||||
curl[qz][qy][qx][2] -= gradXY[qy][qx][0] * wz; // -(u_0)_{x_1}
|
||||
if (useAbs)
|
||||
{
|
||||
// +(u_0)_{x_1}
|
||||
curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz;
|
||||
}
|
||||
else
|
||||
{
|
||||
// -(u_0)_{x_1}
|
||||
curl[qz][qy][qx][2] -= gradXY[qy][qx][0] * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1038,7 +1049,16 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
// \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
|
||||
curl[qz][qy][qx][0] -= gradXY[qy][qx][1] * wDz; // -(u_1)_{x_2}
|
||||
if (useAbs)
|
||||
{
|
||||
// +(u_1)_{x_2}
|
||||
curl[qz][qy][qx][0] += gradXY[qy][qx][1] * wDz;
|
||||
}
|
||||
else
|
||||
{
|
||||
// -(u_1)_{x_2}
|
||||
curl[qz][qy][qx][0] -= gradXY[qy][qx][1] * wDz;
|
||||
}
|
||||
curl[qz][qy][qx][2] += gradXY[qy][qx][0] * wz; // (u_1)_{x_0}
|
||||
}
|
||||
}
|
||||
@@ -1109,7 +1129,16 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
{
|
||||
// \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
|
||||
curl[qz][qy][qx][0] += gradYZ[qz][qy][1] * wx; // (u_2)_{x_1}
|
||||
curl[qz][qy][qx][1] -= gradYZ[qz][qy][0] * wDx; // -(u_2)_{x_0}
|
||||
if (useAbs)
|
||||
{
|
||||
// +(u_2)_{x_0}
|
||||
curl[qz][qy][qx][1] += gradYZ[qz][qy][0] * wDx;
|
||||
}
|
||||
else
|
||||
{
|
||||
// -(u_2)_{x_0}
|
||||
curl[qz][qy][qx][1] -= gradYZ[qz][qy][0] * wDx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,9 +1238,21 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
for (int dx = 0; dx < D1Dx; ++dx)
|
||||
{
|
||||
// \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
|
||||
// (u_0)_{x_2} * (op * curl)_1 - (u_0)_{x_1} * (op * curl)_2
|
||||
Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
|
||||
e) += (gradXY21[dy][dx] * wDz) - (gradXY12[dy][dx] * wz);
|
||||
const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
|
||||
if (useAbs)
|
||||
{
|
||||
// (u_0)_{x_2} * (op * curl)_1 +
|
||||
// (u_0)_{x_1} * (op * curl)_2
|
||||
Y(idx, e) += (gradXY21[dy][dx] * wDz) +
|
||||
(gradXY12[dy][dx] * wz);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (u_0)_{x_2} * (op * curl)_1 -
|
||||
// (u_0)_{x_1} * (op * curl)_2
|
||||
Y(idx, e) += (gradXY21[dy][dx] * wDz) -
|
||||
(gradXY12[dy][dx] * wz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1278,10 +1319,22 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
{
|
||||
for (int dx = 0; dx < D1Dx; ++dx)
|
||||
{
|
||||
const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
|
||||
// \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
|
||||
// -(u_1)_{x_2} * (op * curl)_0 + (u_1)_{x_0} * (op * curl)_2
|
||||
Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
|
||||
e) += (-gradXY20[dy][dx] * wDz) + (gradXY02[dy][dx] * wz);
|
||||
if (useAbs)
|
||||
{
|
||||
// +(u_1)_{x_2} * (op * curl)_0 +
|
||||
// (u_1)_{x_0} * (op * curl)_2
|
||||
Y(idx, e) += (gradXY20[dy][dx] * wDz) +
|
||||
(gradXY02[dy][dx] * wz);
|
||||
}
|
||||
else
|
||||
{
|
||||
// -(u_1)_{x_2} * (op * curl)_0 +
|
||||
// (u_1)_{x_0} * (op * curl)_2
|
||||
Y(idx, e) += (-gradXY20[dy][dx] * wDz) +
|
||||
(gradXY02[dy][dx] * wz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1351,10 +1404,22 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
{
|
||||
for (int dz = 0; dz < D1Dz; ++dz)
|
||||
{
|
||||
const int idx = dx + ((dy + (dz * D1Dy)) * D1Dx) + osc;
|
||||
// \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
|
||||
// (u_2)_{x_1} * (op * curl)_0 - (u_2)_{x_0} * (op * curl)_1
|
||||
Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc,
|
||||
e) += (gradYZ10[dz][dy] * wx) - (gradYZ01[dz][dy] * wDx);
|
||||
if (useAbs)
|
||||
{
|
||||
// (u_2)_{x_1} * (op * curl)_0 +
|
||||
// (u_2)_{x_0} * (op * curl)_1
|
||||
Y(idx, e) += (gradYZ10[dz][dy] * wx) +
|
||||
(gradYZ01[dz][dy] * wDx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (u_2)_{x_1} * (op * curl)_0 -
|
||||
// (u_2)_{x_0} * (op * curl)_1
|
||||
Y(idx, e) += (gradYZ10[dz][dy] * wx) -
|
||||
(gradYZ01[dz][dy] * wDx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1363,7 +1428,7 @@ inline void PACurlCurlApply3D(const int d1d,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
// Shared memory PA H(curl) curl-curl Apply 3D kernel
|
||||
// Shared memory PA H(curl) curl-curl Apply/AbsApply 3D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
const int q1d,
|
||||
@@ -1377,7 +1442,8 @@ inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
const Array<real_t> &gct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
Vector &y,
|
||||
const bool useAbs = false)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
|
||||
"Error: d1d > HCURL_MAX_D1D");
|
||||
@@ -1531,7 +1597,8 @@ inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
}
|
||||
|
||||
curl[qy][qx][1] += v; // (u_0)_{x_2}
|
||||
curl[qy][qx][2] -= u; // -(u_0)_{x_1}
|
||||
if (useAbs) { curl[qy][qx][2] += u; } // +(u_0)_{x_1}
|
||||
else { curl[qy][qx][2] -= u; } // -(u_0)_{x_1}
|
||||
}
|
||||
else if (c == 1) // y component
|
||||
{
|
||||
@@ -1558,7 +1625,8 @@ inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
}
|
||||
}
|
||||
|
||||
curl[qy][qx][0] -= v; // -(u_1)_{x_2}
|
||||
if (useAbs) { curl[qy][qx][0] += v; } // +(u_1)_{x_2}
|
||||
else { curl[qy][qx][0] -= v; } // -(u_1)_{x_2}
|
||||
curl[qy][qx][2] += u; // (u_1)_{x_0}
|
||||
}
|
||||
else // z component
|
||||
@@ -1587,7 +1655,8 @@ inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
}
|
||||
|
||||
curl[qy][qx][0] += v; // (u_2)_{x_1}
|
||||
curl[qy][qx][1] -= u; // -(u_2)_{x_0}
|
||||
if (useAbs) { curl[qy][qx][1] += u; }// +(u_2)_{x_0}
|
||||
else { curl[qy][qx][1] -= u; } // -(u_2)_{x_0}
|
||||
}
|
||||
} // qx
|
||||
} // qy
|
||||
@@ -1642,18 +1711,54 @@ inline void SmemPACurlCurlApply3D(const int d1d,
|
||||
if (dx < D1D-1)
|
||||
{
|
||||
// \hat{\nabla}\times\hat{u} is [0, (u_0)_{x_2}, -(u_0)_{x_1}]
|
||||
// (u_0)_{x_2} * (op * curl)_1 - (u_0)_{x_1} * (op * curl)_2
|
||||
const real_t wx = sBo[dx][qx];
|
||||
dxyz1 += (wx * c2 * wcy * wcDz) - (wx * c3 * wcDy * wcz);
|
||||
if (useAbs)
|
||||
{
|
||||
// (u_0)_{x_2} * (op * curl)_1 +
|
||||
// (u_0)_{x_1} * (op * curl)_2
|
||||
dxyz1 += (wx * c2 * wcy * wcDz) +
|
||||
(wx * c3 * wcDy * wcz);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (u_0)_{x_2} * (op * curl)_1 -
|
||||
// (u_0)_{x_1} * (op * curl)_2
|
||||
dxyz1 += (wx * c2 * wcy * wcDz) -
|
||||
(wx * c3 * wcDy * wcz);
|
||||
}
|
||||
}
|
||||
|
||||
// \hat{\nabla}\times\hat{u} is [-(u_1)_{x_2}, 0, (u_1)_{x_0}]
|
||||
// -(u_1)_{x_2} * (op * curl)_0 + (u_1)_{x_0} * (op * curl)_2
|
||||
dxyz2 += (-wy * c1 * wcx * wcDz) + (wy * c3 * wDx * wcz);
|
||||
if (useAbs)
|
||||
{
|
||||
// +(u_1)_{x_2} * (op * curl)_0 +
|
||||
// (u_1)_{x_0} * (op * curl)_2
|
||||
dxyz2 += (wy * c1 * wcx * wcDz) +
|
||||
(wy * c3 * wDx * wcz);
|
||||
}
|
||||
else
|
||||
{
|
||||
// -(u_1)_{x_2} * (op * curl)_0 +
|
||||
// (u_1)_{x_0} * (op * curl)_2
|
||||
dxyz2 += (-wy * c1 * wcx * wcDz) +
|
||||
(wy * c3 * wDx * wcz);
|
||||
}
|
||||
|
||||
// \hat{\nabla}\times\hat{u} is [(u_2)_{x_1}, -(u_2)_{x_0}, 0]
|
||||
// (u_2)_{x_1} * (op * curl)_0 - (u_2)_{x_0} * (op * curl)_1
|
||||
dxyz3 += (wcDy * wz * c1 * wcx) - (wcy * wz * c2 * wDx);
|
||||
if (useAbs)
|
||||
{
|
||||
// (u_2)_{x_1} * (op * curl)_0 +
|
||||
// (u_2)_{x_0} * (op * curl)_1
|
||||
dxyz3 += (wcDy * wz * c1 * wcx) +
|
||||
(wcy * wz * c2 * wDx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (u_2)_{x_1} * (op * curl)_0 -
|
||||
// (u_2)_{x_0} * (op * curl)_1
|
||||
dxyz3 += (wcDy * wz * c1 * wcx) -
|
||||
(wcy * wz * c2 * wDx);
|
||||
}
|
||||
} // qx
|
||||
} // qy
|
||||
} // dx
|
||||
|
||||
@@ -62,7 +62,7 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
|
||||
const int NE = ne;
|
||||
const int Q1D = quad1D;
|
||||
const int NQ = pow(Q1D, dim);
|
||||
const int NQ = static_cast<int>(std::pow(Q1D, dim));
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool by_val = map_type == FiniteElement::VALUE;
|
||||
const auto W = Reshape(ir->GetWeights().Read(), NQ);
|
||||
@@ -199,10 +199,37 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void MassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
MFEM_ABORT("AddAbsMultPA not implemented with CEED!");
|
||||
ceedOp->AddMult(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
Array<real_t> absB(maps->B);
|
||||
Array<real_t> absBt(maps->Bt);
|
||||
absB.Abs();
|
||||
absBt.Abs();
|
||||
|
||||
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, absB, absBt, abs_pa_data,
|
||||
x, y, dofs1D, quad1D);
|
||||
}
|
||||
}
|
||||
|
||||
void MassIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Mass integrator is symmetric
|
||||
AddMultPA(x, y);
|
||||
}
|
||||
|
||||
void MassIntegrator::AddAbsMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Mass integrator is symmetric
|
||||
AddAbsMultPA(x, y);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -313,6 +313,129 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void VectorFEMassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
|
||||
const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
|
||||
const bool test_curl = (test_fetype == mfem::FiniteElement::CURL);
|
||||
const bool test_div = (test_fetype == mfem::FiniteElement::DIV);
|
||||
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
|
||||
Array<real_t> absBo(mapsO->B);
|
||||
Array<real_t> absBc(mapsC->B);
|
||||
Array<real_t> absBto(mapsO->Bt);
|
||||
Array<real_t> absBtc(mapsC->Bt);
|
||||
Array<real_t> absBto_t(mapsOtest->Bt);
|
||||
Array<real_t> absBtc_t(mapsCtest->Bt);
|
||||
|
||||
absBo.Abs();
|
||||
absBc.Abs();
|
||||
absBto.Abs();
|
||||
absBtc.Abs();
|
||||
absBto_t.Abs();
|
||||
absBtc_t.Abs();
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
if (trial_curl && test_curl)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
const int ID = (dofs1D << 4) | quad1D;
|
||||
switch (ID)
|
||||
{
|
||||
case 0x23:
|
||||
return internal::SmemPAHcurlMassApply3D<2,3>(
|
||||
dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
case 0x34:
|
||||
return internal::SmemPAHcurlMassApply3D<3,4>(
|
||||
dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
case 0x45:
|
||||
return internal::SmemPAHcurlMassApply3D<4,5>(
|
||||
dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
case 0x56:
|
||||
return internal::SmemPAHcurlMassApply3D<5,6>(
|
||||
dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
default:
|
||||
return internal::SmemPAHcurlMassApply3D(
|
||||
dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
internal::PAHcurlMassApply3D(dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
}
|
||||
else if (trial_div && test_div)
|
||||
{
|
||||
internal::PAHdivMassApply(3, dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else if (trial_curl && test_div)
|
||||
{
|
||||
const bool scalarCoeff = !(DQ || MQ);
|
||||
internal::PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
scalarCoeff, true, false,
|
||||
absBo, absBc, absBto_t, absBtc_t,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else if (trial_div && test_curl)
|
||||
{
|
||||
const bool scalarCoeff = !(DQ || MQ);
|
||||
internal::PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
scalarCoeff, false, false,
|
||||
absBo, absBc, absBto_t, absBtc_t,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
}
|
||||
else // 2D
|
||||
{
|
||||
if (trial_curl && test_curl)
|
||||
{
|
||||
internal::PAHcurlMassApply2D(dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else if (trial_div && test_div)
|
||||
{
|
||||
internal::PAHdivMassApply(2, dofs1D, quad1D, ne, symmetric,
|
||||
absBo, absBc, absBto, absBtc,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else if ((trial_curl && test_div) || (trial_div && test_curl))
|
||||
{
|
||||
const bool scalarCoeff = !(DQ || MQ);
|
||||
internal::PAHcurlHdivMassApply2D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
scalarCoeff, trial_curl, false,
|
||||
absBo, absBc, absBto_t, absBtc_t,
|
||||
abs_pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VectorFEMassIntegrator::AddMultTransposePA(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
|
||||
+6
-12
@@ -173,7 +173,6 @@ void LinearForm::Assemble()
|
||||
{
|
||||
Array<int> vdofs;
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation *doftrans;
|
||||
Vector elemvect;
|
||||
|
||||
Vector::operator=(0.0);
|
||||
@@ -198,6 +197,7 @@ void LinearForm::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
@@ -207,14 +207,11 @@ void LinearForm::Assemble()
|
||||
if (markers) { markers->HostRead(); }
|
||||
if ( markers == NULL || (*markers)[elem_attr-1] == 1 )
|
||||
{
|
||||
doftrans = fes -> GetElementVDofs (i, vdofs);
|
||||
fes -> GetElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetElementTransformation (i);
|
||||
domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i),
|
||||
*eltrans, elemvect);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -247,11 +244,12 @@ void LinearForm::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
fes -> GetBdrElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
for (int k=0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
@@ -260,11 +258,7 @@ void LinearForm::Assemble()
|
||||
|
||||
boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i),
|
||||
*eltrans, elemvect);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -673,7 +673,7 @@ public:
|
||||
int myid;
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
|
||||
int seed = (seed_ > 0) ? seed_ + myid : (int)time(0) + myid;
|
||||
int seed = (seed_ > 0) ? seed_ + myid : time(nullptr) + myid;
|
||||
SetSeed(seed);
|
||||
}
|
||||
#else
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
|
||||
for (int i=0; i<integrators->Size(); ++i)
|
||||
{
|
||||
BilinearFormIntegrator *integrator = (*integrators)[i];
|
||||
if (*markers[i])
|
||||
if (markers[i] != nullptr)
|
||||
{
|
||||
(a_to.*add_integrator_marker)(integrator, *markers[i]);
|
||||
}
|
||||
|
||||
+10
-3
@@ -485,10 +485,17 @@ void BatchedLORAssembly::Assemble(
|
||||
#endif
|
||||
|
||||
AssembleWithoutBC(a, A);
|
||||
SparseMatrix *A_mat = A.As<SparseMatrix>();
|
||||
|
||||
A_mat->EliminateBC(ess_dofs,
|
||||
Operator::DiagonalPolicy::DIAG_KEEP);
|
||||
const SparseMatrix *P = fes_ho.GetConformingProlongation();
|
||||
if (P)
|
||||
{
|
||||
std::unique_ptr<SparseMatrix> R(Transpose(*P));
|
||||
std::unique_ptr<SparseMatrix> RA(mfem::Mult(*R, *A.As<SparseMatrix>()));
|
||||
A.Reset(mfem::Mult(*RA, *P));
|
||||
}
|
||||
|
||||
A.As<SparseMatrix>()->EliminateBC(ess_dofs,
|
||||
Operator::DiagonalPolicy::DIAG_KEEP);
|
||||
}
|
||||
|
||||
BatchedLORAssembly::BatchedLORAssembly(FiniteElementSpace &fes_ho_)
|
||||
|
||||
+43
-44
@@ -96,7 +96,6 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
Vector el_x;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
real_t energy = 0.0;
|
||||
|
||||
@@ -123,16 +122,17 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
x.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
@@ -166,16 +166,17 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
x.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
@@ -240,7 +241,6 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
Vector el_x, el_y;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
|
||||
py = 0.0;
|
||||
@@ -268,23 +268,24 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
doftrans.TransformDual(el_y);
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
}
|
||||
}
|
||||
@@ -313,23 +314,25 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
(*bnfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
doftrans.TransformDual(el_y);
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
}
|
||||
}
|
||||
@@ -453,7 +456,6 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
DenseMatrix elmat;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
const Vector &px = Prolongate(x);
|
||||
|
||||
@@ -489,23 +491,24 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
doftrans.TransformDual(elmat);
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
// Grad->AddSubMatrix(vdofs, vdofs, elmat, 1);
|
||||
}
|
||||
@@ -535,23 +538,24 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
(*bnfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
doftrans.TransformDual(elmat);
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -806,7 +810,6 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
Array<const Vector *> el_x_const(fes.Size());
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
real_t energy = 0.0;
|
||||
|
||||
@@ -839,6 +842,7 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes[0]->GetNE(); ++i)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
@@ -848,9 +852,9 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s], doftrans);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans.InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -886,6 +890,7 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
@@ -895,9 +900,9 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
doftrans = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans.InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -947,7 +952,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
Array<const FiniteElement *> fe2(fes.Size());
|
||||
ElementTransformation *T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
std::vector<DofTransformation> doftrans(fes.Size());
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
by.UseDevice(true);
|
||||
@@ -992,10 +997,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -1009,7 +1014,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
if (el_y[s]->Size() == 0) { continue; }
|
||||
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
|
||||
doftrans[s].TransformDual(*el_y[s]);
|
||||
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
|
||||
}
|
||||
}
|
||||
@@ -1047,10 +1052,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
T = fes[0]->GetBdrElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
doftrans[s] = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -1063,7 +1068,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
if (el_y[s]->Size() == 0) { continue; }
|
||||
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
|
||||
doftrans[s].TransformDual(*el_y[s]);
|
||||
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
|
||||
}
|
||||
}
|
||||
@@ -1231,7 +1236,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
Array<const FiniteElement *>fe(fes.Size());
|
||||
Array<const FiniteElement *>fe2(fes.Size());
|
||||
ElementTransformation * T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
std::vector<DofTransformation> doftrans(fes.Size());
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
@@ -1293,9 +1298,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s], doftrans[s]);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -1310,10 +1315,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
if (elmats(j,l)->Height() == 0) { continue; }
|
||||
if (doftrans[j] || doftrans[l])
|
||||
{
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
}
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
|
||||
*elmats(j,l), skip_zeros);
|
||||
}
|
||||
@@ -1354,9 +1356,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
doftrans[s] = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -1371,10 +1373,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
if (elmats(j,l)->Height() == 0) { continue; }
|
||||
if (doftrans[j] || doftrans[l])
|
||||
{
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
}
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
|
||||
*elmats(j,l), skip_zeros);
|
||||
}
|
||||
|
||||
+8
-3
@@ -561,6 +561,8 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(i, dofs);
|
||||
@@ -586,6 +588,8 @@ void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
void ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(i, dofs);
|
||||
@@ -1687,6 +1691,8 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs, DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
face_nbr_element_dof.GetRow(i, vdofs);
|
||||
|
||||
if (DoFTransArray[GetFaceNbrFE(i)->GetGeomType()])
|
||||
@@ -1703,7 +1709,6 @@ void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetFaceNbrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -5254,7 +5259,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
gc.GetNeighborLTDofTable(nbr_ltdof);
|
||||
const int nb_connections = nbr_ltdof.Size_of_connections();
|
||||
shr_ltdof.SetSize(nb_connections);
|
||||
shr_ltdof.CopyFrom(nbr_ltdof.GetJ());
|
||||
if (nb_connections > 0) { shr_ltdof.CopyFrom(nbr_ltdof.GetJ()); }
|
||||
shr_buf.SetSize(nb_connections);
|
||||
shr_buf.UseDevice(true);
|
||||
shr_buf_offsets = nbr_ltdof.GetIMemory();
|
||||
@@ -5283,7 +5288,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
gc.GetNeighborLDofTable(nbr_ldof);
|
||||
const int nb_connections = nbr_ldof.Size_of_connections();
|
||||
ext_ldof.SetSize(nb_connections);
|
||||
ext_ldof.CopyFrom(nbr_ldof.GetJ());
|
||||
if (nb_connections > 0) { ext_ldof.CopyFrom(nbr_ldof.GetJ()); }
|
||||
ext_ldof.GetMemory().UseDevice(true);
|
||||
ext_buf.SetSize(nb_connections);
|
||||
ext_buf.UseDevice(true);
|
||||
|
||||
@@ -577,7 +577,13 @@ public:
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AbsMult(const Vector &x, Vector &y) const override
|
||||
{ Mult(x,y); }
|
||||
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override
|
||||
{ MultTranspose(x,y); }
|
||||
};
|
||||
|
||||
/// Auxiliary device class used by ParFiniteElementSpace.
|
||||
@@ -628,7 +634,13 @@ public:
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AbsMult(const Vector &x, Vector &y) const override
|
||||
{ Mult(x,y); }
|
||||
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override
|
||||
{ MultTranspose(x,y); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+36
-50
@@ -55,22 +55,16 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, const GridFunction *gf,
|
||||
int element_counter = 0;
|
||||
const int MyRank = pfes->GetMyRank();
|
||||
const int glob_ne = glob_fes->GetNE();
|
||||
DofTransformation ltrans, gtrans;
|
||||
for (int i = 0; i < glob_ne; i++)
|
||||
{
|
||||
if (partitioning[i] == MyRank)
|
||||
{
|
||||
const DofTransformation* const ltrans = pfes->GetElementVDofs(element_counter,
|
||||
lvdofs);
|
||||
const DofTransformation* const gtrans = glob_fes->GetElementVDofs(i, gvdofs);
|
||||
pfes->GetElementVDofs(element_counter, lvdofs, ltrans);
|
||||
glob_fes->GetElementVDofs(i, gvdofs, gtrans);
|
||||
gf->GetSubVector(gvdofs, lnodes);
|
||||
if (gtrans)
|
||||
{
|
||||
gtrans->InvTransformPrimal(lnodes);
|
||||
}
|
||||
if (ltrans)
|
||||
{
|
||||
ltrans->TransformPrimal(lnodes);
|
||||
}
|
||||
gtrans.InvTransformPrimal(lnodes);
|
||||
ltrans.TransformPrimal(lnodes);
|
||||
SetSubVector(lvdofs, lnodes);
|
||||
element_counter++;
|
||||
}
|
||||
@@ -279,11 +273,11 @@ const
|
||||
Array<int> dofs;
|
||||
Vector DofVal, LocVec;
|
||||
const int nbr_el_no = i - pfes->GetParMesh()->GetNE();
|
||||
DofTransformation doftrans;
|
||||
if (nbr_el_no >= 0)
|
||||
{
|
||||
int fes_vdim = pfes->GetVDim();
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no, dofs);
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
// Choose fe to be of the order whose number of DOFs matches dofs.Size(),
|
||||
// in the variable order case.
|
||||
const int ndofs = pfes->IsVariableOrder() ? dofs.Size() : 0;
|
||||
@@ -302,10 +296,7 @@ const
|
||||
face_nbr_data.GetSubVector(dofs, LocVec);
|
||||
DofVal.SetSize(dofs.Size());
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
if (fe->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
@@ -321,7 +312,7 @@ const
|
||||
}
|
||||
else
|
||||
{
|
||||
const DofTransformation* const doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
DofVal.SetSize(dofs.Size());
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -336,10 +327,7 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
}
|
||||
|
||||
return (DofVal * LocVec);
|
||||
@@ -352,15 +340,11 @@ void ParGridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
if (nbr_el_no >= 0)
|
||||
{
|
||||
Array<int> dofs;
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no,
|
||||
dofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
const FiniteElement *FElem = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
int dof = FElem->GetDof();
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
@@ -428,8 +412,8 @@ real_t ParGridFunction::GetValue(ElementTransformation &T,
|
||||
|
||||
Array<int> dofs;
|
||||
const FiniteElement * fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no, dofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
|
||||
pfes->DofsToVDofs(comp-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
@@ -442,10 +426,7 @@ real_t ParGridFunction::GetValue(ElementTransformation &T,
|
||||
fe->CalcPhysShape(T, DofVal);
|
||||
}
|
||||
face_nbr_data.GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
|
||||
return (DofVal * LocVec);
|
||||
@@ -476,13 +457,11 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
const int dof = fe->GetDof();
|
||||
@@ -1335,25 +1314,20 @@ real_t L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
|
||||
ParFiniteElementSpace *xfes = x.ParFESpace();
|
||||
Array<int> xdofs, fdofs;
|
||||
Vector el_x, el_f;
|
||||
DofTransformation xtrans, ftrans;
|
||||
|
||||
for (int i = 0; i < xfes->GetNE(); i++)
|
||||
{
|
||||
const DofTransformation* const xtrans = xfes->GetElementVDofs(i, xdofs);
|
||||
xfes->GetElementVDofs(i, xdofs, xtrans);
|
||||
x.GetSubVector(xdofs, el_x);
|
||||
if (xtrans)
|
||||
{
|
||||
xtrans->InvTransformPrimal(el_x);
|
||||
}
|
||||
xtrans.InvTransformPrimal(el_x);
|
||||
|
||||
ElementTransformation *Transf = xfes->GetElementTransformation(i);
|
||||
flux_integrator.ComputeElementFlux(*xfes->GetFE(i), *Transf, el_x,
|
||||
*flux_fes.GetFE(i), el_f, false);
|
||||
|
||||
const DofTransformation* const ftrans = flux_fes.GetElementVDofs(i, fdofs);
|
||||
if (ftrans)
|
||||
{
|
||||
ftrans->TransformPrimal(el_f);
|
||||
}
|
||||
flux_fes.GetElementVDofs(i, fdofs, ftrans);
|
||||
ftrans.TransformPrimal(el_f);
|
||||
flux.SetSubVector(fdofs, el_f);
|
||||
}
|
||||
|
||||
@@ -1432,6 +1406,18 @@ real_t L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
|
||||
return pow(glob_error, 1.0/norm_p);
|
||||
}
|
||||
|
||||
PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor, const int vdim)
|
||||
{
|
||||
PLBound plb = GridFunction::GetBounds(lower, upper, ref_factor, vdim);
|
||||
int siz = vdim > 0 ? 1 : fes->GetVDim();
|
||||
MPI_Allreduce(MPI_IN_PLACE, lower.HostReadWrite(), siz,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, upper.HostReadWrite(), siz,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
return plb;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
@@ -300,12 +300,16 @@ public:
|
||||
real_t ComputeL1Error(Coefficient *exsol[],
|
||||
const IntegrationRule *irs[] = NULL) const override
|
||||
{
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
real_t glb_err = GlobalLpNorm(1.0,
|
||||
GridFunction::ComputeL1Error(exsol, irs),
|
||||
pfes->GetComm());
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return glb_err;
|
||||
}
|
||||
|
||||
@@ -577,6 +581,14 @@ public:
|
||||
GridFunction &flux,
|
||||
bool wcoef = true, int subdomain = -1) override;
|
||||
|
||||
/// Computes the PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each
|
||||
/// vdim across all elements in @b lower and @b upper. We also return the
|
||||
/// PLBound object used to compute the bounds. Note: if vdim < 1, we compute
|
||||
/// the bounds for each vector dimension.
|
||||
PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) override;
|
||||
|
||||
/** Save the local portion of the ParGridFunction. This differs from the
|
||||
serial GridFunction::Save in that it takes into account the signs of
|
||||
the local dofs. */
|
||||
|
||||
+3
-280
@@ -9,278 +9,16 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../quadinterpolator.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../fem/kernels.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
#include "det.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
namespace quadrature_interpolator
|
||||
{
|
||||
|
||||
static void Det1D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
const auto G = Reshape(g, q1d, d1d);
|
||||
const auto X = Reshape(x, d1d, NE);
|
||||
|
||||
auto Y = Reshape(y, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < q1d; q++)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int d = 0; d < d1d; d++)
|
||||
{
|
||||
u += G(q, d) * X(d, e);
|
||||
}
|
||||
Y(q, e) = u;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
static constexpr int SDIM = 2;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
|
||||
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
|
||||
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[4];
|
||||
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
|
||||
Y(qx,qy,e) = kernels::Det<2>(J);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void Det2DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
static constexpr int SDIM = 3;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
// Load XYZ components
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
|
||||
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
|
||||
|
||||
// x contraction
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
|
||||
u += xval * G_mat(dx,qx);
|
||||
v += xval * B_mat(dx,qx);
|
||||
}
|
||||
DQ[d][tidz][dy + qx*D1D] = u;
|
||||
DQ[3 + d][tidz][dy + qx*D1D] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// y contraction and determinant computation
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
|
||||
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
|
||||
}
|
||||
}
|
||||
DeviceTensor<2> J(J_, 3, 2);
|
||||
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
Y(qx,qy,e) = sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
|
||||
static void Det3D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr) // used only with SMEM = false
|
||||
{
|
||||
constexpr int DIM = 3;
|
||||
static constexpr int GRID = SMEM ? 0 : 128;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
|
||||
|
||||
real_t *GM = nullptr;
|
||||
if (!SMEM)
|
||||
{
|
||||
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
|
||||
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
|
||||
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
|
||||
const int max_qd = std::max(max_q1d, max_d1d);
|
||||
const int mem_size = max_qd * max_qd * max_qd * 9;
|
||||
d_buff->SetSize(2*mem_size*GRID);
|
||||
GM = d_buff->Write();
|
||||
}
|
||||
|
||||
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
static constexpr int MQ1 = T_Q1D ? T_Q1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
|
||||
static constexpr int MD1 = T_D1D ? T_D1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
|
||||
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
|
||||
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
|
||||
|
||||
const int bid = MFEM_BLOCK_ID(x);
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
|
||||
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
|
||||
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
|
||||
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
|
||||
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
|
||||
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
|
||||
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
|
||||
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
|
||||
|
||||
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
|
||||
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
|
||||
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[9];
|
||||
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
|
||||
Y(qx,qy,qz,e) = kernels::Det<3>(J);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InitDetKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::DetKernels;
|
||||
@@ -302,27 +40,12 @@ void InitDetKernels()
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
namespace
|
||||
{
|
||||
using DetKernel = QuadratureInterpolator::DetKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
DetKernel QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
DetKernel QuadratureInterpolator::DetKernels::Fallback(
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Fallback(
|
||||
int DIM, int SDIM, int D1D, int Q1D)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
// 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_QUADINTERP_DET_HPP
|
||||
#define MFEM_QUADINTERP_DET_HPP
|
||||
|
||||
#include "../quadinterpolator.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../fem/kernels.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
namespace quadrature_interpolator
|
||||
{
|
||||
|
||||
inline void Det1D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
const auto G = Reshape(g, q1d, d1d);
|
||||
const auto X = Reshape(x, d1d, NE);
|
||||
|
||||
auto Y = Reshape(y, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < q1d; q++)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int d = 0; d < d1d; d++)
|
||||
{
|
||||
u += G(q, d) * X(d, e);
|
||||
}
|
||||
Y(q, e) = u;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
static constexpr int SDIM = 2;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
|
||||
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
|
||||
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[4];
|
||||
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
|
||||
Y(qx,qy,e) = kernels::Det<2>(J);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
static constexpr int SDIM = 3;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
// Load XYZ components
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
|
||||
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
|
||||
|
||||
// x contraction
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
|
||||
u += xval * G_mat(dx,qx);
|
||||
v += xval * B_mat(dx,qx);
|
||||
}
|
||||
DQ[d][tidz][dy + qx*D1D] = u;
|
||||
DQ[3 + d][tidz][dy + qx*D1D] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// y contraction and determinant computation
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
|
||||
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
|
||||
}
|
||||
}
|
||||
DeviceTensor<2> J(J_, 3, 2);
|
||||
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
Y(qx,qy,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
|
||||
inline void Det3D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr) // used only with SMEM = false
|
||||
{
|
||||
constexpr int DIM = 3;
|
||||
static constexpr int GRID = SMEM ? 0 : 128;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
|
||||
|
||||
real_t *GM = nullptr;
|
||||
if (!SMEM)
|
||||
{
|
||||
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
|
||||
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
|
||||
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
|
||||
const int max_qd = std::max(max_q1d, max_d1d);
|
||||
const int mem_size = max_qd * max_qd * max_qd * 9;
|
||||
d_buff->SetSize(2*mem_size*GRID);
|
||||
GM = d_buff->Write();
|
||||
}
|
||||
|
||||
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
static constexpr int MQ1 = T_Q1D ? T_Q1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
|
||||
static constexpr int MD1 = T_D1D ? T_D1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
|
||||
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
|
||||
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
|
||||
|
||||
const int bid = MFEM_BLOCK_ID(x);
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
|
||||
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
|
||||
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
|
||||
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
|
||||
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
|
||||
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
|
||||
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
|
||||
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
|
||||
|
||||
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
|
||||
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
|
||||
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[9];
|
||||
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
|
||||
Y(qx,qy,qz,e) = kernels::Det<3>(J);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_QUADINTERP_DET_HPP
|
||||
@@ -326,7 +326,7 @@ static void Eval2D(const int NE,
|
||||
const double E = j(0,0)*j(0,0) + j(1,0)*j(1,0) + j(2,0)*j(2,0);
|
||||
const double F = j(0,0)*j(0,1) + j(1,0)*j(1,1) + j(2,0)*j(2,1);
|
||||
const double G = j(0,1)*j(0,1) + j(1,1)*j(1,1) + j(2,1)*j(2,1);
|
||||
det(q,e) = sqrt(E*G - F*F);
|
||||
det(q,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,8 +566,8 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
|
||||
}
|
||||
else // use_tensor_eval == false
|
||||
{
|
||||
EvalKernels::Run(dim, vdim, maps.ndof, maps.nqpt, ne,vdim,q_layout,
|
||||
geom, maps,e_vec, q_val,q_der,q_det,eval_flags);
|
||||
EvalKernels::Run(dim, vdim, maps.ndof, maps.nqpt, ne,vdim, q_layout,
|
||||
geom, maps, e_vec, q_val, q_der, q_det, eval_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -128,7 +128,7 @@ void ElementRestriction::Mult(const Vector& x, Vector& y) const
|
||||
});
|
||||
}
|
||||
|
||||
void ElementRestriction::MultUnsigned(const Vector& x, Vector& y) const
|
||||
void ElementRestriction::AbsMult(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
@@ -193,7 +193,7 @@ void ElementRestriction::AddMultTranspose(const Vector& x, Vector& y,
|
||||
TAddMultTranspose<ADD>(x, y);
|
||||
}
|
||||
|
||||
void ElementRestriction::MultTransposeUnsigned(const Vector& x, Vector& y) const
|
||||
void ElementRestriction::AbsMultTranspose(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
@@ -653,7 +653,8 @@ ConformingFaceRestriction::ConformingFaceRestriction(
|
||||
: ConformingFaceRestriction(fes, f_ordering, type, true)
|
||||
{ }
|
||||
|
||||
void ConformingFaceRestriction::Mult(const Vector& x, Vector& y) const
|
||||
void ConformingFaceRestriction::MultInternal(const Vector& x, Vector& y,
|
||||
const bool useAbs) const
|
||||
{
|
||||
if (nf==0) { return; }
|
||||
// Assumes all elements have the same number of dofs
|
||||
@@ -666,7 +667,7 @@ void ConformingFaceRestriction::Mult(const Vector& x, Vector& y) const
|
||||
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int s_idx = d_indices[i];
|
||||
const int sgn = (s_idx >= 0) ? 1 : -1;
|
||||
const int sgn = (useAbs || s_idx >= 0) ? 1 : -1;
|
||||
const int idx = (s_idx >= 0) ? s_idx : -1 - s_idx;
|
||||
const int dof = i % nface_dofs;
|
||||
const int face = i / nface_dofs;
|
||||
@@ -724,7 +725,7 @@ void ConformingFaceRestriction::AddMultTranspose(
|
||||
true, a);
|
||||
}
|
||||
|
||||
void ConformingFaceRestriction::AddMultTransposeUnsigned(
|
||||
void ConformingFaceRestriction::AddAbsMultTranspose(
|
||||
const Vector& x, Vector& y, const real_t a) const
|
||||
{
|
||||
ConformingFaceRestriction_AddMultTranspose(
|
||||
|
||||
+55
-7
@@ -59,9 +59,18 @@ public:
|
||||
const real_t a = 1.0) const override;
|
||||
|
||||
/// Compute Mult without applying signs based on DOF orientations.
|
||||
void MultUnsigned(const Vector &x, Vector &y) const;
|
||||
void AbsMult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/// Compute MultTranspose without applying signs based on DOF orientations.
|
||||
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
/// @deprecated Use AbsMult() instead.
|
||||
MFEM_DEPRECATED void MultUnsigned(const Vector &x, Vector &y) const
|
||||
{ AbsMult(x, y); }
|
||||
|
||||
/// @deprecated Use AbsMultTranspose() instead.
|
||||
MFEM_DEPRECATED void MultTransposeUnsigned(const Vector &x, Vector &y) const
|
||||
{ AbsMultTranspose(x, y); }
|
||||
|
||||
/// Compute MultTranspose by setting (rather than adding) element
|
||||
/// contributions; this is a left inverse of the Mult() operation
|
||||
@@ -184,12 +193,19 @@ public:
|
||||
|
||||
/** @brief Add the face degrees of freedom @a x to the element degrees of
|
||||
freedom @a y ignoring the signs from DOF orientation. */
|
||||
virtual void AddMultTransposeUnsigned(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const
|
||||
virtual void AddAbsMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const
|
||||
{
|
||||
AddMultTranspose(x, y, a);
|
||||
}
|
||||
|
||||
/// @deprecated Use AddAbsMultTranspose() instead.
|
||||
MFEM_DEPRECATED void AddMultTransposeUnsigned(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const
|
||||
{
|
||||
AddAbsMultTranspose(x, y, a);
|
||||
}
|
||||
|
||||
/** @brief Add the face degrees of freedom @a x to the element degrees of
|
||||
freedom @a y. Perform the same computation as AddMultTranspose, but
|
||||
@a x is invalid after calling this method.
|
||||
@@ -219,6 +235,12 @@ public:
|
||||
AddMultTranspose(x, y);
|
||||
}
|
||||
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override
|
||||
{
|
||||
y = 0.0;
|
||||
AddAbsMultTranspose(x, y);
|
||||
}
|
||||
|
||||
/** @brief For each face, sets @a y to the partial derivative of @a x with
|
||||
respect to the reference coordinate whose direction is
|
||||
perpendicular to the face on the reference element.
|
||||
@@ -319,7 +341,16 @@ public:
|
||||
requested by @a type in the constructor.
|
||||
The face_dofs are ordered according to the given
|
||||
ElementDofOrdering. */
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y); }
|
||||
|
||||
/// Compute Mult without applying signs based on DOF orientations.
|
||||
void AbsMult(const Vector &x, Vector &y) const override
|
||||
{ MultInternal(x, y, true); }
|
||||
|
||||
/// @deprecated Use AbsMult() instead.
|
||||
MFEM_DEPRECATED void MultUnsigned(const Vector &x, Vector &y) const
|
||||
{ AbsMult(x, y); }
|
||||
|
||||
using FaceRestriction::AddMultTransposeInPlace;
|
||||
|
||||
@@ -341,8 +372,20 @@ public:
|
||||
L-Vector @b not taking into account signs from DOF orientations.
|
||||
|
||||
@sa AddMultTranspose(). */
|
||||
void AddMultTransposeUnsigned(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
void AddAbsMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
|
||||
/// @deprecated Use AddAbsMultTranspose() instead.
|
||||
MFEM_DEPRECATED void AddMultTransposeUnsigned(const Vector &x, Vector &y) const
|
||||
{
|
||||
AddAbsMultTranspose(x, y);
|
||||
}
|
||||
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override
|
||||
{
|
||||
y = 0.0;
|
||||
AddAbsMultTranspose(x, y);
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief Compute the scatter indices: L-vector to E-vector, and the offsets
|
||||
@@ -395,6 +438,11 @@ protected:
|
||||
void SetFaceDofsGatherIndices(const Mesh::FaceInformation &face,
|
||||
const int face_index,
|
||||
const ElementDofOrdering f_ordering);
|
||||
|
||||
public:
|
||||
// This method needs to be public due to 'nvcc' restriction.
|
||||
void MultInternal(const Vector &x, Vector &y,
|
||||
const bool useAbs = false) const;
|
||||
};
|
||||
|
||||
/// @brief Alias for ConformingFaceRestriction, for backwards compatibility and
|
||||
|
||||
+11
-29
@@ -22,9 +22,9 @@ namespace mfem
|
||||
/* AD related definitions below ========================================*/
|
||||
|
||||
/// MFEM native AD-type for first derivatives
|
||||
using AD1Type = internal::dual<real_t, real_t>;
|
||||
using AD1Type = future::dual<real_t, real_t>;
|
||||
/// MFEM native AD-type for second derivatives
|
||||
using AD2Type = internal::dual<AD1Type, AD1Type>;
|
||||
using AD2Type = future::dual<AD1Type, AD1Type>;
|
||||
|
||||
/*
|
||||
Functions for 2x2 DenseMatrix cast as std::vector<type>, assuming column-major storage
|
||||
@@ -5122,33 +5122,32 @@ real_t TMOP_Integrator::GetSurfaceFittingWeight()
|
||||
|
||||
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
|
||||
{
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal, surf_fit_normal);
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
|
||||
metric_normal = 1.0 / metric_normal;
|
||||
lim_normal = 1.0 / lim_normal;
|
||||
//if (surf_fit_gf) { surf_fit_normal = 1.0 / surf_fit_normal; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
|
||||
{
|
||||
real_t loc[3];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]);
|
||||
real_t rdc[3];
|
||||
MPI_Allreduce(loc, rdc, 3, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
real_t loc[2];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1]);
|
||||
real_t rdc[2];
|
||||
MPI_Allreduce(loc, rdc, 2, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
x.ParFESpace()->GetComm());
|
||||
metric_normal = 1.0 / rdc[0];
|
||||
lim_normal = 1.0 / rdc[1];
|
||||
// if (surf_fit_gf) { surf_fit_normal = 1.0 / rdc[2]; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
real_t &metric_energy,
|
||||
real_t &lim_energy,
|
||||
real_t &surf_fit_gf_energy)
|
||||
real_t &lim_energy)
|
||||
{
|
||||
metric_energy = 0.0;
|
||||
lim_energy = 0.0;
|
||||
if (PA.enabled)
|
||||
{
|
||||
MFEM_VERIFY(PA.E.Size() > 0, "Must be called after AssemblePA!");
|
||||
@@ -5191,9 +5190,6 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
Jpr.SetSize(dim);
|
||||
Jpt.SetSize(dim);
|
||||
|
||||
metric_energy = 0.0;
|
||||
lim_energy = 0.0;
|
||||
surf_fit_gf_energy = 0.0;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -5225,21 +5221,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
lim_energy += weight;
|
||||
}
|
||||
|
||||
// Normalization of the surface fitting term.
|
||||
if (surf_fit_gf)
|
||||
{
|
||||
Array<int> dofs;
|
||||
Vector sigma_e;
|
||||
surf_fit_gf->FESpace()->GetElementDofs(i, dofs);
|
||||
surf_fit_gf->GetSubVector(dofs, sigma_e);
|
||||
for (int s = 0; s < dofs.Size(); s++)
|
||||
{
|
||||
if ((*surf_fit_marker)[dofs[s]] == true)
|
||||
{
|
||||
surf_fit_gf_energy += sigma_e(s) * sigma_e(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Normalization of the surface fitting term.
|
||||
}
|
||||
|
||||
// Cases when integration is not over the target element, or when the
|
||||
|
||||
+1
-2
@@ -2038,8 +2038,7 @@ protected:
|
||||
} PA;
|
||||
|
||||
void ComputeNormalizationEnergies(const GridFunction &x,
|
||||
real_t &metric_energy, real_t &lim_energy,
|
||||
real_t &surf_fit_gf_energy);
|
||||
real_t &metric_energy, real_t &lim_energy);
|
||||
|
||||
void AssembleElementVectorExact(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
|
||||
+11
-20
@@ -2100,10 +2100,11 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
|
||||
y = 0.0;
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom || isvar_order)
|
||||
@@ -2123,15 +2124,9 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
h_dofs.Copy(h_vdofs);
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
x.GetSubVector(l_vdofs, subX);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->InvTransformPrimal(subX);
|
||||
}
|
||||
doftrans_l.InvTransformPrimal(subX);
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->TransformPrimal(subY);
|
||||
}
|
||||
doftrans_h.TransformPrimal(subY);
|
||||
y.SetSubVector(h_vdofs, subY);
|
||||
}
|
||||
}
|
||||
@@ -2157,10 +2152,12 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
|
||||
int vdim = lFESpace.GetVDim();
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom || isvar_order)
|
||||
@@ -2182,10 +2179,7 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
|
||||
x.GetSubVector(h_vdofs, subX);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->InvTransformDual(subX);
|
||||
}
|
||||
doftrans_h.InvTransformDual(subX);
|
||||
for (int p = 0; p < h_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[lFESpace.DecodeDof(h_dofs[p])])
|
||||
@@ -2195,10 +2189,7 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
}
|
||||
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->TransformDual(subY);
|
||||
}
|
||||
doftrans_l.TransformDual(subY);
|
||||
y.AddElementVector(l_vdofs, subY);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ list(APPEND HDRS
|
||||
arrays_by_name.hpp
|
||||
backends.hpp
|
||||
binaryio.hpp
|
||||
complex_type.hpp
|
||||
cuda.hpp
|
||||
device.hpp
|
||||
error.hpp
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "array.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include <fstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -110,6 +111,19 @@ void Array<T>::PartialSum()
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Abs()
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "Use with arithmetic types!");
|
||||
const bool useDevice = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(useDevice);
|
||||
mfem::forall_switch(useDevice, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = std::abs(y[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// Sum
|
||||
template <class T>
|
||||
T Array<T>::Sum() const
|
||||
|
||||
+15
-1
@@ -56,6 +56,10 @@ protected:
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
|
||||
public:
|
||||
using value_type = T; ///< Type alias for stl.
|
||||
using reference = T&; ///< Type alias for stl.
|
||||
using const_reference = const T&; ///< Type alias for stl.
|
||||
|
||||
friend void Swap<T>(Array<T> &, Array<T> &);
|
||||
|
||||
/// Creates an empty array
|
||||
@@ -174,6 +178,9 @@ public:
|
||||
/// Append element 'el' to array, resize if necessary.
|
||||
inline int Append(const T & el);
|
||||
|
||||
/// STL-like push_back. Append element 'el' to array, resize if necessary.
|
||||
void push_back(const T &el) { Append(el); }
|
||||
|
||||
/// Append another array to this array, resize if necessary.
|
||||
inline int Append(const T *els, int nels);
|
||||
|
||||
@@ -298,6 +305,9 @@ public:
|
||||
/// Fill the entries of the array with the cumulative sum of the entries.
|
||||
void PartialSum();
|
||||
|
||||
/// Replace each entry of the array with its absolute value.
|
||||
void Abs();
|
||||
|
||||
/// Return the sum of all the array entries using the '+'' operator for class 'T'.
|
||||
T Sum() const;
|
||||
|
||||
@@ -316,7 +326,11 @@ public:
|
||||
the Size to match this Capacity after this.*/
|
||||
template <typename U>
|
||||
inline void CopyFrom(const U *src)
|
||||
{ std::memcpy(begin(), src, MemoryUsage()); }
|
||||
{
|
||||
if (!begin() || size == 0) { return; }
|
||||
MFEM_ASSERT(begin() && src, "Error in Array::CopyFrom");
|
||||
std::memcpy(begin(), src, MemoryUsage());
|
||||
}
|
||||
|
||||
/// STL-like begin. Returns pointer to the first element of the array.
|
||||
inline T* begin() { return data; }
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#define MFEM_THREAD_ID(k) 0
|
||||
#define MFEM_THREAD_SIZE(k) 1
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=0; i<N; i++)
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) MFEM_FOREACH_THREAD(i,k,N)
|
||||
#endif
|
||||
|
||||
// 'double' and 'float' atomicAdd implementation for previous versions of CUDA
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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_COMPLEX_TYPE
|
||||
#define MFEM_COMPLEX_TYPE
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
#include <complex>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
#include <cuComplex.h>
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_HIP)
|
||||
#include <hip/hip_complex.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// @brief Complex number type for device.
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
|
||||
#define zAbs std::abs
|
||||
#define zExp std::exp
|
||||
#define zNorm std::norm
|
||||
using complex_t = std::complex<real_t>;
|
||||
|
||||
#else // CUDA or HIP
|
||||
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
using DoubleComplex_t = cuDoubleComplex;
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_HIP)
|
||||
using DoubleComplex_t = hipDoubleComplex;
|
||||
#endif
|
||||
|
||||
struct Complex : public DoubleComplex_t
|
||||
{
|
||||
MFEM_HOST_DEVICE Complex() = default;
|
||||
MFEM_HOST_DEVICE Complex(real_t r) { x = r, y = 0.0; }
|
||||
MFEM_HOST_DEVICE Complex(real_t r, real_t i) { x = r, y = i; }
|
||||
MFEM_HOST_DEVICE real_t real() const { return x; }
|
||||
MFEM_HOST_DEVICE void real(real_t r) { x = r; }
|
||||
MFEM_HOST_DEVICE real_t imag() const { return y; }
|
||||
MFEM_HOST_DEVICE void imag(real_t i) { y = i; }
|
||||
|
||||
template <typename U>
|
||||
MFEM_HOST_DEVICE inline Complex &operator*=(const U &z)
|
||||
{
|
||||
return *this = *this * z, *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
MFEM_HOST_DEVICE inline Complex &operator/=(const U &z)
|
||||
{
|
||||
return *this = *this / z, *this;
|
||||
}
|
||||
};
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const Complex &x, const real_t &y)
|
||||
{
|
||||
return Complex(x.real() * y, x.imag() * y);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator+(const Complex &a, const Complex &b)
|
||||
{
|
||||
return Complex(a.real() + b.real(), a.imag() + b.imag());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const real_t d, const Complex &z)
|
||||
{
|
||||
return Complex(z.real() * d, z.imag() * d);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const Complex &a, const Complex &b)
|
||||
{
|
||||
return Complex(a.real() * b.real() - a.imag() * b.imag(),
|
||||
a.real() * b.imag() + a.imag() * b.real());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator/(const Complex &z, const real_t &d)
|
||||
{
|
||||
return Complex(z.real() / d, z.imag() / d);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline real_t zAbs(const Complex &z)
|
||||
{
|
||||
return std::hypot(z.real(), z.imag());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex zExp(const Complex &q)
|
||||
{
|
||||
Complex z;
|
||||
real_t s, c, e = std::exp(q.real());
|
||||
sincos(q.imag(), &s, &c);
|
||||
z.real(c * e), z.imag(s * e);
|
||||
return z;
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline real_t zNorm(const Complex &z)
|
||||
{
|
||||
return z.real() * z.real() + z.imag() * z.imag();
|
||||
}
|
||||
|
||||
using complex_t = Complex;
|
||||
#endif // MFEM_USE_CUDA || MFEM_USE_HIP
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_COMPLEX_TYPE
|
||||
@@ -47,6 +47,7 @@
|
||||
#define MFEM_THREAD_ID(k) threadIdx.k
|
||||
#define MFEM_THREAD_SIZE(k) blockDim.k
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=threadIdx.k; i<N; i+=blockDim.k)
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) if(const int i=threadIdx.k; i<N)
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user