Compare commits
682
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
653a610455 | ||
|
|
304dac15c2 | ||
|
|
061a92067f | ||
|
|
f0cb31088c | ||
|
|
0ddb02c7e7 | ||
|
|
f407ca7756 | ||
|
|
ab00472c5d | ||
|
|
c4a3d31289 | ||
|
|
427406d1b8 | ||
|
|
5026d6ca8a | ||
|
|
04d7e8a62f | ||
|
|
20e77be88a | ||
|
|
97a568af13 | ||
|
|
cd2715680f | ||
|
|
62997e9dc7 | ||
|
|
915853cee0 | ||
|
|
a52599d4cc | ||
|
|
0d5b13c4aa | ||
|
|
dda6b0dbe1 | ||
|
|
91cdbeee7d | ||
|
|
0466ee74f2 | ||
|
|
50b8873f20 | ||
|
|
cd7dfe2188 | ||
|
|
1bf00d3761 | ||
|
|
b2a0f48001 | ||
|
|
fb200e4b98 | ||
|
|
da852fd0e4 | ||
|
|
c2657d20a8 | ||
|
|
47e9b29c0f | ||
|
|
cbcae09fe8 | ||
|
|
b75c6e96e6 | ||
|
|
a54c5d2105 | ||
|
|
cd5d098c95 | ||
|
|
30506b52d9 | ||
|
|
3f78374d2a | ||
|
|
bd7281bdfa | ||
|
|
1299d05d21 | ||
|
|
39944d8dfb | ||
|
|
6df8ae80cb | ||
|
|
0730fa304a | ||
|
|
6dfbe25c07 | ||
|
|
a8e251c28e | ||
|
|
9df402b565 | ||
|
|
5ddbaed1f4 | ||
|
|
02edb95a31 | ||
|
|
2d6cb3f0e5 | ||
|
|
d06f52461e | ||
|
|
3853cb6860 | ||
|
|
f5d0db2a33 | ||
|
|
a102daf621 | ||
|
|
597cba8d37 | ||
|
|
075a160187 | ||
|
|
6d8a96a8fb | ||
|
|
8d9579057f | ||
|
|
76d65f5866 | ||
|
|
720fb72372 | ||
|
|
bbde976378 | ||
|
|
1efc5e78e5 | ||
|
|
a553c2dba8 | ||
|
|
58ad1c79cd | ||
|
|
b6f755925c | ||
|
|
447cd0f500 | ||
|
|
2d68b877f2 | ||
|
|
cce301f92b | ||
|
|
46ccaee493 | ||
|
|
25bd2f9596 | ||
|
|
516f709061 | ||
|
|
fa89692e57 | ||
|
|
1b6d878189 | ||
|
|
4cc9b5c6f6 | ||
|
|
f73f41fc82 | ||
|
|
9b1b56a155 | ||
|
|
f95b18b457 | ||
|
|
af6d0d7479 | ||
|
|
7aa327174a | ||
|
|
8c861b4602 | ||
|
|
267082d6f2 | ||
|
|
dd6c207534 | ||
|
|
71af00ab6d | ||
|
|
a901754de5 | ||
|
|
03da41c0f5 | ||
|
|
2d5f00635f | ||
|
|
c6ec74db41 | ||
|
|
8854247f86 | ||
|
|
755e4501e1 | ||
|
|
1ec73c3bf6 | ||
|
|
3a15fe3d96 | ||
|
|
3f447cb11c | ||
|
|
e629502dca | ||
|
|
8b000dd222 | ||
|
|
a4d6889332 | ||
|
|
e0fbc5e3aa | ||
|
|
03da9d7789 | ||
|
|
9c8874a38c | ||
|
|
ce434a9c82 | ||
|
|
48dd78b154 | ||
|
|
bb2460cbd0 | ||
|
|
0648e50e70 | ||
|
|
45e8125fd6 | ||
|
|
dc08fc65ce | ||
|
|
3276cb955f | ||
|
|
25056defeb | ||
|
|
20cd965ed8 | ||
|
|
1fda9c2391 | ||
|
|
2cec0353b1 | ||
|
|
9d22775395 | ||
|
|
94625fad8f | ||
|
|
c9a9c71ff5 | ||
|
|
764d9919b5 | ||
|
|
8efbd4e46f | ||
|
|
bcdbda1597 | ||
|
|
b94ac358e4 | ||
|
|
d4d4b79522 | ||
|
|
120f4cb043 | ||
|
|
4f1597c1bc | ||
|
|
56beedbdcb | ||
|
|
0239b86304 | ||
|
|
520a9c5125 | ||
|
|
ed521022cd | ||
|
|
7b35a47626 | ||
|
|
def35c8a15 | ||
|
|
c53a016d06 | ||
|
|
ea2b42c49d | ||
|
|
4e35b3d8f1 | ||
|
|
c7e066a0d3 | ||
|
|
29681677a1 | ||
|
|
9d72af995f | ||
|
|
b099252dcf | ||
|
|
aca8d51f95 | ||
|
|
d696fc2cea | ||
|
|
8dcd0d6349 | ||
|
|
2951d5f98e | ||
|
|
4b27589abf | ||
|
|
8338aa85e6 | ||
|
|
0a5730eac3 | ||
|
|
368724d456 | ||
|
|
e20bb381ca | ||
|
|
a99efcdb63 | ||
|
|
89e23a93f5 | ||
|
|
4816fa0849 | ||
|
|
6fc7a8ca5a | ||
|
|
22c8b607dc | ||
|
|
91deab3c75 | ||
|
|
87cee25894 | ||
|
|
1bcddddfdb | ||
|
|
da68672955 | ||
|
|
a5dd4b862b | ||
|
|
a3fcb89049 | ||
|
|
bd6f3d51c8 | ||
|
|
8d95f71305 | ||
|
|
aee0cb1dc6 | ||
|
|
7dded1fdcf | ||
|
|
477c475c1a | ||
|
|
34fdcddb8d | ||
|
|
91fa9936ef | ||
|
|
d95e6e0adc | ||
|
|
b5a7cf6470 | ||
|
|
cc0dcaad09 | ||
|
|
82d3b8b92e | ||
|
|
960379a43d | ||
|
|
3782ece6b3 | ||
|
|
823c7a952d | ||
|
|
2bc553633e | ||
|
|
8be00d1115 | ||
|
|
bdd2bfbb79 | ||
|
|
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 | ||
|
|
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 | ||
|
|
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 | ||
|
|
a617205ee1 | ||
|
|
620f906765 | ||
|
|
2620effa65 | ||
|
|
3da9bdc39a | ||
|
|
2ab00899fd | ||
|
|
7009449ef5 | ||
|
|
34482860b0 | ||
|
|
8f4aafdebc | ||
|
|
26eea4f2fd | ||
|
|
313f6856e8 | ||
|
|
bf97e92be2 | ||
|
|
d7a04ca4ac | ||
|
|
28b1ac0c9d | ||
|
|
4f3c73b3eb | ||
|
|
0eb2d21602 | ||
|
|
efcf608a5d | ||
|
|
f1d56c4068 | ||
|
|
94698f27d7 | ||
|
|
30dac8986c | ||
|
|
0691354c84 | ||
|
|
5c0b2a6b62 | ||
|
|
c25b84fbd8 | ||
|
|
8be13975b0 | ||
|
|
7fce328f12 | ||
|
|
d6c1edc9a3 | ||
|
|
56b1a1715a | ||
|
|
3514c0f0d4 | ||
|
|
1ac7837dc0 | ||
|
|
e6fc7893be | ||
|
|
74498373c9 | ||
|
|
1cc3d81866 | ||
|
|
233316269a | ||
|
|
e34c6b6013 | ||
|
|
ea593def25 | ||
|
|
5791aa4629 | ||
|
|
18cff41dac | ||
|
|
f316ec7d5e | ||
|
|
b5366e3ad3 | ||
|
|
800be7971a | ||
|
|
ee06c0eb44 | ||
|
|
fb9449c47d | ||
|
|
1555bfe3a5 | ||
|
|
a3b12b6f97 | ||
|
|
f0e8e6e19a | ||
|
|
c82d9bd8a0 | ||
|
|
1e6ee60790 | ||
|
|
b7ff8749a3 | ||
|
|
78df8dffb2 | ||
|
|
ff11a6b572 | ||
|
|
d0f5cb73e7 | ||
|
|
a72d85fa6c | ||
|
|
0fd9dcceb3 | ||
|
|
056152aaa6 | ||
|
|
db55be84ad | ||
|
|
a8bbdf4fd4 | ||
|
|
b66d56ee9b | ||
|
|
9852e93449 | ||
|
|
2421b48f56 | ||
|
|
2cd2d11215 | ||
|
|
27db3a4121 | ||
|
|
e11d19e3a9 | ||
|
|
b0c30784b6 | ||
|
|
a35ef68c2a | ||
|
|
2fca844393 | ||
|
|
a7aa6c5a7c | ||
|
|
698183a8db | ||
|
|
f6c2f10dee | ||
|
|
fa61508248 | ||
|
|
d2baadad26 | ||
|
|
02424a41a9 | ||
|
|
0aa68f431f | ||
|
|
d2288ef6fd | ||
|
|
091c6272fc | ||
|
|
6f882ed87e | ||
|
|
813a7de323 | ||
|
|
cc4f5625e2 | ||
|
|
5dcf6f7ead | ||
|
|
f4baef8b5d | ||
|
|
0e4b0f7bda | ||
|
|
7317d4d139 | ||
|
|
e8ed1a4c02 | ||
|
|
e9e684599f | ||
|
|
6c994fea99 | ||
|
|
3b05995fd1 | ||
|
|
3cfca882af | ||
|
|
5e99ffc9a3 | ||
|
|
bcbb24dce0 | ||
|
|
797113ff71 | ||
|
|
41af83b10c | ||
|
|
4da2ea6f08 | ||
|
|
9eb0f5c0c3 | ||
|
|
4a1a5dfa55 | ||
|
|
59edd7255c | ||
|
|
6d01e152de | ||
|
|
70cb8fcc04 | ||
|
|
0366ad2468 | ||
|
|
0e22b182a6 | ||
|
|
49eb2715ba | ||
|
|
5d3be16590 | ||
|
|
899a2fc7cb | ||
|
|
bd0fa51539 | ||
|
|
b151c909f3 | ||
|
|
ce7f94ec1f | ||
|
|
ee6b9fdc2f | ||
|
|
c418868c83 | ||
|
|
7a0344a6bb | ||
|
|
51812480bd | ||
|
|
61cc19ca22 | ||
|
|
5e48080f3d | ||
|
|
af73851cb2 | ||
|
|
338e4288ca | ||
|
|
dca4cd510a | ||
|
|
2b23f35ec7 | ||
|
|
ea569c5806 | ||
|
|
a355f28eae | ||
|
|
e2efc259b6 | ||
|
|
a75b1ca9c0 | ||
|
|
8ff6d69f74 | ||
|
|
101af72047 | ||
|
|
b1468c14fd | ||
|
|
d3281a8a86 | ||
|
|
a7d59d35e0 | ||
|
|
70680d6187 | ||
|
|
35aeecb5c0 | ||
|
|
44f2a63f16 | ||
|
|
948462da6e | ||
|
|
fddefa7838 | ||
|
|
62a01ee670 | ||
|
|
e23768f5f7 | ||
|
|
53d4f78fdb | ||
|
|
eeb71eee37 | ||
|
|
3630a8f8a1 | ||
|
|
048904b731 | ||
|
|
2496b33699 | ||
|
|
01990d4605 | ||
|
|
4a3cd44695 | ||
|
|
3b059c3995 | ||
|
|
89dc5ce5c0 | ||
|
|
eb205caaa2 | ||
|
|
f50601f1aa | ||
|
|
529d6e3bc2 | ||
|
|
b289def58a | ||
|
|
eb9022540e | ||
|
|
ef29b1dd30 | ||
|
|
33b3ef6045 | ||
|
|
657f6ef5fe | ||
|
|
96cfef783c | ||
|
|
603f43e775 | ||
|
|
1ff8b6811d | ||
|
|
cf269700a8 | ||
|
|
cf127c8b14 | ||
|
|
932508ad1b | ||
|
|
0e30ee1e15 | ||
|
|
cb0c205bd1 | ||
|
|
2ed6fdc85a | ||
|
|
dd236b8796 | ||
|
|
15b35a01ba | ||
|
|
9a47f4391d | ||
|
|
ff746a8af6 | ||
|
|
df36d0f352 | ||
|
|
765ebcecaa | ||
|
|
ccc006febf | ||
|
|
b3a08b91d6 | ||
|
|
726b5f99ff | ||
|
|
a943683063 | ||
|
|
b6fb45f384 | ||
|
|
d119fa7636 | ||
|
|
4a7c643f99 | ||
|
|
41ea219782 | ||
|
|
2dfd2ccfc5 | ||
|
|
ae9a8b2897 | ||
|
|
a2e33109eb | ||
|
|
b54ee3537f | ||
|
|
2d3aba5d87 | ||
|
|
f4c66c56d6 | ||
|
|
3c3face72c | ||
|
|
b277a0cb2d | ||
|
|
f42334cb4d | ||
|
|
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 | ||
|
|
ac26026ff1 | ||
|
|
5044d9cd45 | ||
|
|
2edddb700e | ||
|
|
1d79ab00ba | ||
|
|
85e08b67a0 | ||
|
|
e3c3150958 | ||
|
|
c513bb1276 | ||
|
|
7559524573 | ||
|
|
7ac34aab22 | ||
|
|
17edacb630 | ||
|
|
703ba47151 | ||
|
|
57caab145a | ||
|
|
911fbfbe82 | ||
|
|
0b3b21dbb2 | ||
|
|
823fd86a87 | ||
|
|
2d3c1bc79a | ||
|
|
1d556b93e7 | ||
|
|
00fcb1b37f | ||
|
|
9445358bc9 | ||
|
|
d439781751 | ||
|
|
16c9123f98 | ||
|
|
cc86e59167 | ||
|
|
52eaf0deb7 | ||
|
|
e73ea1fb77 | ||
|
|
ea6c104475 | ||
|
|
e7762d126e | ||
|
|
c61d0e07d3 | ||
|
|
e4becc6e02 | ||
|
|
b294248b06 | ||
|
|
9b1c3b718d | ||
|
|
e9826f9c69 | ||
|
|
720051d641 | ||
|
|
5c25fa49a7 | ||
|
|
d9ef117ee9 | ||
|
|
b7a8cf6290 | ||
|
|
bed1823d72 | ||
|
|
93a3648712 | ||
|
|
83ca977d71 | ||
|
|
6739574668 | ||
|
|
01eeccee7f | ||
|
|
39b6112e69 | ||
|
|
c70d2b7309 | ||
|
|
c98e8bfe07 | ||
|
|
ef9137bd1f | ||
|
|
9f153f9fcb | ||
|
|
8e0c42a132 | ||
|
|
d755037904 | ||
|
|
b577d2322e | ||
|
|
6f99204d37 | ||
|
|
c6e93f5474 | ||
|
|
00d35316b9 | ||
|
|
84a3fe000b | ||
|
|
7354e1ce6c | ||
|
|
d5089996cc | ||
|
|
9787ad0d3b | ||
|
|
26fb49960e | ||
|
|
5cb2c82d59 | ||
|
|
9e7f8ce838 | ||
|
|
21b6ade630 | ||
|
|
41e4360d2e | ||
|
|
263dd0f019 | ||
|
|
2e8685d80a | ||
|
|
433f9a4c51 | ||
|
|
08856158ce | ||
|
|
6d8c32db80 | ||
|
|
b21bf9a2d9 | ||
|
|
8d705f4230 | ||
|
|
0a6c72c52d | ||
|
|
4adb51df50 | ||
|
|
6cec49e220 | ||
|
|
8cefee8799 | ||
|
|
e9b631116f | ||
|
|
9006171327 | ||
|
|
6468880b2a | ||
|
|
89a0e08f29 | ||
|
|
7471a22505 | ||
|
|
779ce337f4 | ||
|
|
54b24da610 | ||
|
|
1a332939a0 | ||
|
|
9e10cd87b1 | ||
|
|
3f7c551d66 | ||
|
|
a7a5ce5cb1 | ||
|
|
4218c11b41 | ||
|
|
44f58a169c | ||
|
|
3c4a860f3a | ||
|
|
bbd26e1835 | ||
|
|
e8842c506a | ||
|
|
214313291b | ||
|
|
783136234a | ||
|
|
e64300f84b | ||
|
|
3854184620 | ||
|
|
dd85ae3384 | ||
|
|
4a09b7a1fc | ||
|
|
1ea8e59f69 | ||
|
|
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 | ||
|
|
b6aa248fc9 | ||
|
|
6857e4b277 | ||
|
|
639a0347fb | ||
|
|
a02f7165d8 | ||
|
|
4fb48b4e41 | ||
|
|
751dbd0ed9 | ||
|
|
c2c6a94d42 | ||
|
|
088ec0f461 | ||
|
|
7e1be9da75 | ||
|
|
be29d4a3d4 | ||
|
|
be34c5a6bd | ||
|
|
8bd3569f6d | ||
|
|
e8908ad646 | ||
|
|
6c7673df1c | ||
|
|
1f7f394f4e | ||
|
|
bfc6b0708d | ||
|
|
3d5c6d58e8 | ||
|
|
6efbd4cc27 | ||
|
|
369b9365ad | ||
|
|
b35ff2270a | ||
|
|
7a54a543b0 | ||
|
|
15420f7049 | ||
|
|
dd20722733 | ||
|
|
5577edf446 | ||
|
|
230a345ef3 | ||
|
|
89f1dce705 | ||
|
|
7f17c1f049 | ||
|
|
7e6edcddec | ||
|
|
d89b01e24c | ||
|
|
33f1150c34 | ||
|
|
c6a05d371b | ||
|
|
f97a29f6e8 | ||
|
|
85ae72fada | ||
|
|
27a5bd71ee | ||
|
|
643db22bef | ||
|
|
97fb6be029 | ||
|
|
9587ef212d | ||
|
|
35462b8645 | ||
|
|
4f0f353720 | ||
|
|
a3156023d0 | ||
|
|
7794c291be | ||
|
|
7ca49cb200 | ||
|
|
e48d41c9dc | ||
|
|
0ead1d9c77 | ||
|
|
e2168f9348 | ||
|
|
65516eca23 | ||
|
|
79d9314560 | ||
|
|
d91d39bc00 |
@@ -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.
|
||||
|
||||
@@ -168,10 +168,13 @@ jobs:
|
||||
env
|
||||
shell: bash
|
||||
|
||||
# For info on Xcode see:
|
||||
# - https://github.com/actions/runner-images/issues/12541
|
||||
# - https://github.com/actions/runner-images/blob/releases/macos-15-arm64/20250811/images/macos/macos-15-arm64-Readme.md#xcode
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
XCODE_PATH="/Applications/Xcode_15.3.app"
|
||||
XCODE_PATH="/Applications/Xcode_16.4.app"
|
||||
echo "> sudo xcode-select -s ${XCODE_PATH}"
|
||||
sudo xcode-select -s ${XCODE_PATH}
|
||||
echo "> g++ -v"
|
||||
@@ -289,10 +292,10 @@ jobs:
|
||||
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
|
||||
brew install llvm@20 enzyme
|
||||
echo "LLVM_PREFIX=$(brew --prefix llvm@20)" >> $GITHUB_ENV
|
||||
echo "OMPI_CC=$(brew --prefix llvm@20)/bin/clang" >> $GITHUB_ENV
|
||||
echo "OMPI_CXX=$(brew --prefix llvm@20)/bin/clang++" >> $GITHUB_ENV
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
|
||||
@@ -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++17
|
||||
-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
|
||||
+15
-6
@@ -211,7 +211,7 @@ miniapps/electromagnetics/joule
|
||||
miniapps/electromagnetics/Volta-AMR*
|
||||
miniapps/electromagnetics/Tesla-AMR*
|
||||
miniapps/electromagnetics/Maxwell-Parallel*
|
||||
miniapps/electromagnetics/Joule_*
|
||||
miniapps/electromagnetics/Joule_[0-9]*
|
||||
|
||||
miniapps/gslib/field-diff
|
||||
miniapps/gslib/field-interp
|
||||
@@ -232,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
|
||||
@@ -262,11 +263,13 @@ 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*
|
||||
miniapps/mtop/ParHeat/*
|
||||
miniapps/mtop/seqheat
|
||||
miniapps/mtop/SeqHeat*
|
||||
miniapps/mtop/SeqHeat/*
|
||||
|
||||
miniapps/autodiff/paradiff
|
||||
miniapps/autodiff/seqadiff
|
||||
@@ -274,7 +277,7 @@ miniapps/autodiff/seqtest
|
||||
miniapps/autodiff/par_example
|
||||
miniapps/autodiff/seq_example
|
||||
miniapps/autodiff/seq_test
|
||||
miniapps/autodiff/Exampl*
|
||||
miniapps/autodiff/Example/*
|
||||
|
||||
miniapps/navier/navier_mms
|
||||
miniapps/navier/navier_kovasznay
|
||||
@@ -297,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
|
||||
@@ -315,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
|
||||
@@ -336,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
|
||||
@@ -402,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_*
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ include:
|
||||
# the "needs" keyword and express the DAG of jobs for more efficiency.
|
||||
# - We use setup and setup_baseline phases to download content outside of mfem
|
||||
# directory.
|
||||
# - Allocate/Release is where ruby resource are allocated/released once for all.
|
||||
# - Allocate/Release is where Dane resource are allocated/released once for all.
|
||||
# - Build and Test is where we build and MFEM for multiple toolchains.
|
||||
# - Baseline_checks gathers baseline-type test suites execution
|
||||
# - Baseline_publish, only available on master, allows to update baseline
|
||||
@@ -53,7 +53,7 @@ variables:
|
||||
AUTOTEST_COMMIT: "YES"
|
||||
|
||||
# Trigger subpipelines:
|
||||
ruby-build-and-test:
|
||||
dane-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
@@ -61,10 +61,10 @@ ruby-build-and-test:
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/ruby-build-and-test.yml
|
||||
include: .gitlab/dane-build-and-test.yml
|
||||
strategy: depend
|
||||
|
||||
ruby-baseline:
|
||||
dane-baseline:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
@@ -73,7 +73,7 @@ ruby-baseline:
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/ruby-baseline.yml
|
||||
include: .gitlab/dane-baseline.yml
|
||||
strategy: depend
|
||||
|
||||
lassen-build-and-test:
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ and `test type`.
|
||||
|
||||
Machines typically include:
|
||||
|
||||
* Ruby: 2nd Gen Intel Xeon (Cascade Lake)
|
||||
* Dane: Intel Sapphire Rapids
|
||||
* Lassen: Power9 + Nvidia GPU
|
||||
* Corona: AMD GPU
|
||||
|
||||
@@ -76,13 +76,13 @@ with a spack spec of MFEM, within the limits permitted by the MFEM spack
|
||||
package.
|
||||
|
||||
In any build-and-test sub-pipeline a job basically consists in defining the
|
||||
spack spec to use. Adding a job on ruby for example resumes to:
|
||||
spack spec to use. Adding a job on Dane for example resumes to:
|
||||
|
||||
```yaml
|
||||
<job_name>:
|
||||
variables:
|
||||
SPEC: "<spack_spec>"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
```
|
||||
|
||||
The remaining and non trivial work is to make sure this spec is working. To
|
||||
|
||||
@@ -24,7 +24,7 @@ variables:
|
||||
# TODO: add a clean-up mechanism
|
||||
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
|
||||
|
||||
# On LLNL's ruby, there is only one allocation shared among jobs in order to
|
||||
# On LLNL's Dane, there is only one allocation shared among jobs in order to
|
||||
# save time and resource. This allocation has to be uniquely named so that we
|
||||
# are sure to retrieve it.
|
||||
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# GitLab pipelines configurations for the Ruby machine at LLNL
|
||||
# GitLab pipelines configurations for the Dane machine at LLNL
|
||||
variables:
|
||||
MACHINE_NAME: ruby
|
||||
MACHINE_NAME: dane
|
||||
|
||||
.on_ruby:
|
||||
.on_dane:
|
||||
tags:
|
||||
- shell
|
||||
- ruby
|
||||
- dane
|
||||
rules:
|
||||
# Don't run ruby jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_RUBY == "OFF"'
|
||||
# Don't run dane jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_DANE == "OFF"'
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
|
||||
@@ -40,16 +40,17 @@ variables:
|
||||
- when: on_success
|
||||
|
||||
# Spack helped builds
|
||||
# Generic ruby build job, extending build script
|
||||
.build_and_test_on_ruby:
|
||||
extends: [.on_ruby]
|
||||
# Generic dane build job, extending build script
|
||||
.build_and_test_on_dane:
|
||||
extends: [.on_dane]
|
||||
stage: build_and_test
|
||||
script:
|
||||
# THREADS is used by 'tests/gitlab/build_and_test', run below
|
||||
- export THREADS=16
|
||||
# Dane has 224 threads/node and we run 7 separate jobs: 224=7*32
|
||||
- export THREADS=28
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 60 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
@@ -18,7 +18,7 @@
|
||||
setup_baseline:
|
||||
tags:
|
||||
- shell
|
||||
- ruby
|
||||
- dane
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
setup:
|
||||
tags:
|
||||
- shell
|
||||
- ruby
|
||||
- dane
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -19,8 +19,8 @@ stages:
|
||||
- cleanup
|
||||
- baseline_publish
|
||||
|
||||
baselinecheck_mfem_intel_ruby:
|
||||
extends: [.on_ruby]
|
||||
baselinecheck_mfem_intel_dane:
|
||||
extends: [.on_dane]
|
||||
stage: baseline_check
|
||||
variables:
|
||||
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
|
||||
@@ -31,8 +31,8 @@ baselinecheck_mfem_intel_ruby:
|
||||
script:
|
||||
- echo ${BUILD_ROOT}
|
||||
- echo ${TPLS_DIR}
|
||||
# Used by the tests in MFEM/tests:
|
||||
- export MFEM_TEST_NP=48
|
||||
# Used by the tests in MFEM/tests, dane has 224 threads/node:
|
||||
- export MFEM_TEST_NP=192
|
||||
# The next script uses the following environment variables:
|
||||
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
|
||||
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
|
||||
@@ -44,7 +44,7 @@ baselinecheck_mfem_intel_ruby:
|
||||
allow_failure: true
|
||||
|
||||
cleanup:
|
||||
extends: .on_ruby
|
||||
extends: .on_dane
|
||||
stage: cleanup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
@@ -53,7 +53,7 @@ cleanup:
|
||||
- rm -rf "${BUILD_ROOT}" || true
|
||||
|
||||
report_baseline:
|
||||
extends: [.on_ruby]
|
||||
extends: [.on_dane]
|
||||
stage: baseline_report
|
||||
script:
|
||||
- echo ${MACHINE_NAME}
|
||||
@@ -113,8 +113,8 @@ report_baseline:
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
baselinepublish_mfem_ruby:
|
||||
extends: [.on_ruby]
|
||||
baselinepublish_mfem_dane:
|
||||
extends: [.on_dane]
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
@@ -129,5 +129,5 @@ baselinepublish_mfem_ruby:
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/ruby-config.yml
|
||||
- local: .gitlab/configs/dane-config.yml
|
||||
- local: .gitlab/configs/setup-baseline.yml
|
||||
@@ -19,54 +19,54 @@ stages:
|
||||
allocate_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_ruby
|
||||
extends: .on_dane
|
||||
stage: allocate_resource
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
- salloc --exclusive --nodes=1 --reservation=ci --time=60 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
|
||||
# GitLab jobs for the Ruby machine at LLNL
|
||||
# GitLab jobs for the Dane machine at LLNL
|
||||
debug_ser_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +debug~mpi"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
debug_par_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +debug+mpi"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
opt_ser_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 ~mpi"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
opt_par_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
opt_par_gcc_10_sundials:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +sundials"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
opt_par_gcc_10_petsc:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
opt_par_gcc_10_pumi:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +pumi"
|
||||
extends: .build_and_test_on_ruby
|
||||
extends: .build_and_test_on_dane
|
||||
|
||||
# Release
|
||||
release_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_ruby
|
||||
extends: .on_dane
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
@@ -78,17 +78,17 @@ release_resource:
|
||||
report_job_success:
|
||||
stage: release_resource_and_report
|
||||
extends:
|
||||
- .on_ruby
|
||||
- .on_dane
|
||||
- .report_job_success
|
||||
|
||||
report_job_failure:
|
||||
stage: release_resource_and_report
|
||||
extends:
|
||||
- .on_ruby
|
||||
- .on_dane
|
||||
- .report_job_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/ruby-config.yml
|
||||
- local: .gitlab/configs/dane-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
- local: .gitlab/configs/report-build-and-test.yml
|
||||
@@ -14,7 +14,7 @@
|
||||
# locals
|
||||
glob_err=${BASELINE_TEST}.err
|
||||
base=${BASELINE_TEST}-${SYS_TYPE}
|
||||
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
base="${BASELINE_TEST}-${MACHINE_NAME}"
|
||||
fi
|
||||
base_diff=${base}.diff
|
||||
@@ -31,7 +31,7 @@ cd tests
|
||||
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# There will be collision between corona and ruby baselines.
|
||||
# There will be collision between corona and dane baselines.
|
||||
# Once the corresponding files have been generated, we can switch to machine
|
||||
# specific ref.
|
||||
ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}
|
||||
@@ -21,7 +21,7 @@ PATCH_FILE=${ARTIFACT_PATH}.patch
|
||||
FULL_FILE=${ARTIFACT_PATH}.out
|
||||
DIFF_FILE=${ARTIFACT_PATH}.diff
|
||||
|
||||
# There will be collision between corona and ruby baselines.
|
||||
# There will be collision between corona and dane baselines.
|
||||
# Once the corresponding files have been generated, we can switch to machine
|
||||
# specific ref.
|
||||
SAVED_NAME=baseline-${SYS_TYPE}.saved
|
||||
|
||||
@@ -29,9 +29,14 @@ Discretization improvements
|
||||
|
||||
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
|
||||
@@ -39,12 +44,42 @@ GPU computing
|
||||
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.
|
||||
- Implemented a GPU-accelerated matrix-free AMR derefinement `GridFunction`
|
||||
update operator. This supports mixed geometry meshes and variable order
|
||||
spaces, and is the default derefinement operator constructed by
|
||||
`FiniteElementSpace::Update` and `ParFiniteElementSpace::Update`.
|
||||
The operator requires `FiniteElementSpace::Nonconforming() == true`.
|
||||
|
||||
API changes:
|
||||
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
|
||||
====================================
|
||||
|
||||
+14
-5
@@ -598,14 +598,20 @@ set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME)
|
||||
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
# Add all created targets and *_FOUND libraries in the variables TPL_TARGETS and
|
||||
# TPL_LIBRARIES, respectively.
|
||||
set(TPL_TARGETS)
|
||||
set(TPL_LIBRARIES "")
|
||||
set(TPL_INCLUDE_DIRS "")
|
||||
foreach(TPL IN LISTS MFEM_TPLS)
|
||||
if (${TPL}_FOUND)
|
||||
if (${TPL}_FOUND OR TARGET ${TPL})
|
||||
message(STATUS "MFEM: using package ${TPL}")
|
||||
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
|
||||
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
|
||||
if (TARGET ${TPL})
|
||||
list(APPEND TPL_TARGETS ${TPL})
|
||||
else()
|
||||
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
|
||||
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endforeach(TPL)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
@@ -680,7 +686,10 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
# Declaring the library
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
|
||||
if (TPL_TARGETS)
|
||||
add_dependencies(mfem ${TPL_TARGETS})
|
||||
endif()
|
||||
if (MINGW)
|
||||
target_link_libraries(mfem PRIVATE ws2_32)
|
||||
endif()
|
||||
|
||||
@@ -121,6 +121,11 @@ Parallel build:
|
||||
make -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
Parallel build with fetching of hypre and METIS:
|
||||
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DFETCH_TPLS=YES
|
||||
make -j 4
|
||||
|
||||
CUDA build:
|
||||
(this build requires CMake 3.17 or newer)
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
@@ -663,6 +668,7 @@ The specific libraries and their options are:
|
||||
- OpenMP (optional), usually part of compiler, used when either MFEM_USE_OPENMP
|
||||
or MFEM_USE_LEGACY_OPENMP is set to YES.
|
||||
Options: OPENMP_OPT, OPENMP_LIB.
|
||||
Versions: OpenMP >= 3.1 when MFEM_USE_OPENMP=YES.
|
||||
|
||||
- High-resolution POSIX clocks: when using MFEM_TIMER_TYPE = 2, it may be
|
||||
necessary to link with a system library (e.g. librt.so).
|
||||
@@ -842,6 +848,7 @@ The specific libraries and their options are:
|
||||
- HIP (optional), used when MFEM_USE_HIP = YES.
|
||||
URL: https://rocmdocs.amd.com
|
||||
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
|
||||
Versions: ROCm >= 5.6.1.
|
||||
|
||||
- OCCA (optional), used when MFEM_USE_OCCA = YES.
|
||||
URL: https://libocca.org
|
||||
@@ -1074,6 +1081,9 @@ The following options are CMake specific:
|
||||
MFEM_ENABLE_TESTING - Enable the ctest framework for testing.
|
||||
MFEM_ENABLE_EXAMPLES - Build all of the examples by default.
|
||||
MFEM_ENABLE_MINIAPPS - Build all of the miniapps by default.
|
||||
FETCH_TPLS - Enable fetching of all supported third-party libraries.
|
||||
HYPRE_FETCH - Enable fetching of hypre.
|
||||
METIS_FETCH - Enable fetching of metis.
|
||||
|
||||
External libraries (CMake):
|
||||
---------------------------
|
||||
@@ -1135,6 +1145,12 @@ The following built-in CMake packages are also used:
|
||||
set the <LIBNAME>_LIBRARIES option directly; the configuration option
|
||||
<LIBNAME>_DIR is not supported.
|
||||
|
||||
The MFEM CMake build system also provides fetching (automated building) for the
|
||||
packages/libraries listed below. Note that when fetching is enabled, any related
|
||||
auto-detection functionality is disabled.
|
||||
|
||||
- HYPRE
|
||||
- METIS
|
||||
|
||||
Building without GNU make or CMake
|
||||
==================================
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Defines the following variables:
|
||||
# Defines the following variables if fetching of TPLs is disabled (default):
|
||||
# - HYPRE_FOUND
|
||||
# - HYPRE_LIBRARIES
|
||||
# - HYPRE_INCLUDE_DIRS
|
||||
# - HYPRE_VERSION
|
||||
# - HYPRE_USING_CUDA (internal)
|
||||
# - HYPRE_USING_HIP (internal)
|
||||
# otherwise, the following are defined:
|
||||
# - HYPRE (imported library target)
|
||||
# - HYPRE_VERSION (cache variable)
|
||||
|
||||
if (HYPRE_FOUND)
|
||||
if (HYPRE_FOUND OR TARGET HYPRE)
|
||||
if (HYPRE_USING_CUDA)
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
endif()
|
||||
@@ -33,6 +36,55 @@ if (HYPRE_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (HYPRE_FETCH OR FETCH_TPLS)
|
||||
set(HYPRE_FETCH_VERSION 2.33.0)
|
||||
add_library(HYPRE STATIC IMPORTED)
|
||||
# set options and associated dependencies
|
||||
set(CMAKE_OPTIONS)
|
||||
list(APPEND CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (MFEM_USE_CUDA)
|
||||
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_CUDA:BOOL=ON)
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
target_link_libraries(HYPRE INTERFACE CUDA::cusparse CUDA::curand CUDA::cublas)
|
||||
elseif (MFEM_USE_HIP)
|
||||
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_HIP:BOOL=ON)
|
||||
find_package(rocsparse REQUIRED)
|
||||
find_package(rocrand REQUIRED)
|
||||
target_link_libraries(HYPRE INTERFACE rocsparse rocrand)
|
||||
endif()
|
||||
if (MFEM_USE_SINGLE)
|
||||
list(APPEND CMAKE_OPTIONS -DHYPRE_ENABLE_SINGLE:BOOL=ON)
|
||||
endif()
|
||||
# define external project and create future include directory so it is present
|
||||
# to pass CMake checks at end of MFEM configuration step
|
||||
message(STATUS "Will fetch HYPRE ${HYPRE_FETCH_VERSION} to be built with ${CMAKE_OPTIONS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/hypre)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(hypre
|
||||
GIT_REPOSITORY https://github.com/hypre-space/hypre.git
|
||||
GIT_TAG v${HYPRE_FETCH_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
SOURCE_SUBDIR src
|
||||
PREFIX ${PREFIX}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${PREFIX} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${CMAKE_OPTIONS})
|
||||
file(MAKE_DIRECTORY ${PREFIX}/include)
|
||||
# set imported library target properties
|
||||
add_dependencies(HYPRE hypre)
|
||||
set_target_properties(HYPRE PROPERTIES
|
||||
IMPORTED_LOCATION ${PREFIX}/lib/libHYPRE.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${PREFIX}/include)
|
||||
# convert HYPRE version to integer
|
||||
string(REGEX MATCHALL "[0-9]+" HYPRE_SPLIT_VERSION ${HYPRE_FETCH_VERSION})
|
||||
list(GET HYPRE_SPLIT_VERSION 0 HYPRE_MAJOR_VERSION)
|
||||
list(GET HYPRE_SPLIT_VERSION 1 HYPRE_MINOR_VERSION)
|
||||
list(GET HYPRE_SPLIT_VERSION 2 HYPRE_PATCH_VERSION)
|
||||
math(EXPR HYPRE_VERSION "10000*${HYPRE_MAJOR_VERSION} + 100*${HYPRE_MINOR_VERSION} + ${HYPRE_PATCH_VERSION}")
|
||||
# set cache variables that would otherwise be set after mfem_find_package call
|
||||
set(HYPRE_VERSION ${HYPRE_VERSION} CACHE STRING "HYPRE version." FORCE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(HYPRE HYPRE HYPRE_DIR "include" "HYPRE.h" "lib" "HYPRE"
|
||||
"Paths to headers required by HYPRE." "Libraries required by HYPRE."
|
||||
|
||||
@@ -9,10 +9,38 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Defines the following variables:
|
||||
# Defines the following variables if fetching of TPLs is disabled (default):
|
||||
# - METIS_FOUND
|
||||
# - METIS_LIBRARIES
|
||||
# - METIS_INCLUDE_DIRS
|
||||
# - METIS_VERSION_5
|
||||
# otherwise, the following are defined:
|
||||
# - METIS (imported library target)
|
||||
# - METIS_VERSION_5 (cache variable)
|
||||
|
||||
if (METIS_FETCH OR FETCH_TPLS)
|
||||
set(METIS_FETCH_VERSION 4.0.3)
|
||||
add_library(METIS STATIC IMPORTED)
|
||||
# define external project
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(metis
|
||||
GIT_REPOSITORY https://github.com/mfem/tpls
|
||||
GIT_TAG b60352fbe9675d374b00828055e55be4584c7995 # tag from 1/16/25
|
||||
GIT_SHALLOW TRUE
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
|
||||
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
|
||||
# set imported library target properties
|
||||
add_dependencies(METIS metis)
|
||||
set_target_properties(METIS PROPERTIES
|
||||
IMPORTED_LOCATION ${PREFIX}/lib/libmetis.a)
|
||||
# set cache variables that would otherwise be set after mfem_find_package call
|
||||
set(METIS_VERSION_5 FALSE CACHE BOOL "Is METIS version 5?")
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
|
||||
|
||||
+4
-1
@@ -23,11 +23,14 @@
|
||||
#include "_config.hpp"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
#if (defined(MFEM_USE_CUDA) && defined(__CUDACC__)) || \
|
||||
(defined(MFEM_USE_HIP) && defined(__HIPCC__))
|
||||
(defined(MFEM_USE_HIP) && defined(__HIP__))
|
||||
#define MFEM_HOST_DEVICE __host__ __device__
|
||||
#else
|
||||
#define MFEM_HOST_DEVICE
|
||||
|
||||
@@ -89,6 +89,12 @@ option(MFEM_ENABLE_EXAMPLES "Build all of the examples" OFF)
|
||||
option(MFEM_ENABLE_MINIAPPS "Build all of the miniapps" OFF)
|
||||
option(MFEM_ENABLE_BENCHMARKS "Build all of the benchmarks" OFF)
|
||||
|
||||
# Allow a user to specify fetching of certain third-party libraries instead of
|
||||
# searching for existing installations.
|
||||
option(FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
|
||||
option(HYPRE_FETCH "Enable fetching of hypre" OFF)
|
||||
option(METIS_FETCH "Enable fetching of METIS" OFF)
|
||||
|
||||
# Setting CXX/MPICXX on the command line or in user.cmake will overwrite the
|
||||
# autodetected C++ compiler.
|
||||
# set(CXX g++)
|
||||
|
||||
+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
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ set(SRCS
|
||||
fe/fe_ser.cpp
|
||||
fe_coll.cpp
|
||||
fespace.cpp
|
||||
derefmat_op.cpp
|
||||
pderefmat_op.cpp
|
||||
geom.cpp
|
||||
gridfunc.cpp
|
||||
hybridization.cpp
|
||||
@@ -162,6 +164,7 @@ set(SRCS
|
||||
transfer.cpp
|
||||
hyperbolic.cpp
|
||||
integrator.cpp
|
||||
bounds.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -246,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
|
||||
@@ -272,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)
|
||||
|
||||
@@ -1275,6 +1275,22 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
|
||||
height = width = fes->GetVSize();
|
||||
|
||||
if (ext) { ext->Update(); }
|
||||
for (int k = 0; k < domain_integs.Size(); ++k)
|
||||
{
|
||||
domain_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < interior_face_integs.Size(); ++k)
|
||||
{
|
||||
interior_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_face_integs[k]->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::SetDiagonalPolicy(DiagonalPolicy policy)
|
||||
@@ -2337,6 +2353,31 @@ void MixedBilinearForm::Update()
|
||||
height = test_fes->GetVSize();
|
||||
width = trial_fes->GetVSize();
|
||||
if (ext) { ext->Update(); }
|
||||
|
||||
for (int k = 0; k < domain_integs.Size(); ++k)
|
||||
{
|
||||
domain_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < interior_face_integs.Size(); ++k)
|
||||
{
|
||||
interior_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < trace_face_integs.Size(); ++k)
|
||||
{
|
||||
trace_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_trace_face_integs.Size(); ++k)
|
||||
{
|
||||
boundary_trace_face_integs[k]->Update();
|
||||
}
|
||||
}
|
||||
|
||||
MixedBilinearForm::~MixedBilinearForm()
|
||||
|
||||
+233
-233
@@ -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
|
||||
{
|
||||
@@ -266,11 +266,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
|
||||
// Gather the attributes on the host from all the elements
|
||||
const Mesh &mesh = *trial_fes->GetMesh();
|
||||
elem_attributes.SetSize(mesh.GetNE());
|
||||
for (int i = 0; i < mesh.GetNE(); ++i)
|
||||
{
|
||||
elem_attributes[i] = mesh.GetAttribute(i);
|
||||
}
|
||||
elem_attributes = &mesh.GetElementAttributes();
|
||||
}
|
||||
|
||||
// Construct face restriction operators only if the bilinear form has
|
||||
@@ -329,45 +325,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
bdr_face_dYdn.SetSize(bdr_face_restrict_lex->Height());
|
||||
}
|
||||
|
||||
const Mesh &mesh = *trial_fes->GetMesh();
|
||||
// See LinearFormExtension::Update for explanation of f_to_be logic.
|
||||
std::unordered_map<int,int> f_to_be;
|
||||
for (int i = 0; i < mesh.GetNBE(); ++i)
|
||||
{
|
||||
const int f = mesh.GetBdrElementFaceIndex(i);
|
||||
f_to_be[f] = i;
|
||||
}
|
||||
const int nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
|
||||
bdr_attributes.SetSize(nf_bdr);
|
||||
int f_ind = 0;
|
||||
int missing_bdr_elems = 0;
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
{
|
||||
if (!mesh.GetFaceInformation(f).IsOfFaceType(FaceType::Boundary))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int attribute = 1; // default value
|
||||
if (f_to_be.find(f) != f_to_be.end())
|
||||
{
|
||||
const int be = f_to_be[f];
|
||||
attribute = mesh.GetBdrAttribute(be);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a boundary face does not correspond to the a boundary element,
|
||||
// we assign it the default attribute of 1. We also generate a
|
||||
// warning at runtime with the number of such missing elements.
|
||||
++missing_bdr_elems;
|
||||
}
|
||||
bdr_attributes[f_ind] = attribute;
|
||||
++f_ind;
|
||||
}
|
||||
if (missing_bdr_elems)
|
||||
{
|
||||
MFEM_WARNING("Missing " << missing_bdr_elems << " boundary elements "
|
||||
"for boundary faces.");
|
||||
}
|
||||
bdr_face_attributes = &trial_fes->GetMesh()->GetBdrFaceAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +387,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int attr = d_attr[e];
|
||||
if (d_m[attr - 1] == 0)
|
||||
if (attr <= 0 || d_m[attr - 1] == 0)
|
||||
{
|
||||
for (int i = 0; i < nd; ++i)
|
||||
{
|
||||
@@ -450,13 +408,13 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
|
||||
elem_attributes, localY);
|
||||
*elem_attributes, localY);
|
||||
}
|
||||
const ElementRestriction* H1elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict);
|
||||
if (H1elem_restrict)
|
||||
{
|
||||
H1elem_restrict->MultTransposeUnsigned(localY, y);
|
||||
H1elem_restrict->AbsMultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -476,7 +434,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
|
||||
elem_attributes, y);
|
||||
*elem_attributes, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,9 +447,9 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
for (int i = 0; i < n_bdr_integs; ++i)
|
||||
{
|
||||
assemble_diagonal_with_markers(*bdr_integs[i], bdr_markers[i],
|
||||
bdr_attributes, bdr_face_Y);
|
||||
*bdr_face_attributes, bdr_face_Y);
|
||||
}
|
||||
bdr_face_restrict_lex->AddMultTransposeUnsigned(bdr_face_Y, y);
|
||||
bdr_face_restrict_lex->AddAbsMultTranspose(bdr_face_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +484,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 +517,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 +532,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 +567,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 +629,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);
|
||||
@@ -669,8 +648,8 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
for (int i = 0; i < n_bdr_integs; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
|
||||
false, bdr_face_Y);
|
||||
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
|
||||
*bdr_face_attributes, false, bdr_face_Y);
|
||||
}
|
||||
for (int i = 0; i < n_bdr_face_integs; ++i)
|
||||
{
|
||||
@@ -678,12 +657,14 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
AddMultNormalDerivativesWithMarkers(
|
||||
*bdr_face_integs[i], bdr_face_X, bdr_face_dXdn,
|
||||
bdr_face_markers[i], bdr_attributes, bdr_face_Y, bdr_face_dYdn);
|
||||
bdr_face_markers[i], *bdr_face_attributes, bdr_face_Y,
|
||||
bdr_face_dYdn);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
|
||||
bdr_attributes, false, bdr_face_Y);
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
|
||||
bdr_face_markers[i], *bdr_face_attributes, false,
|
||||
bdr_face_Y);
|
||||
}
|
||||
}
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
@@ -706,7 +687,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], elem_attributes,
|
||||
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], *elem_attributes,
|
||||
true, localY);
|
||||
}
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
@@ -753,13 +734,14 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
bdr_face_Y = 0.0;
|
||||
for (int i = 0; i < n_bdr_integs; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
|
||||
true, bdr_face_Y);
|
||||
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
|
||||
*bdr_face_attributes, true, bdr_face_Y);
|
||||
}
|
||||
for (int i = 0; i < n_bdr_face_integs; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
|
||||
bdr_attributes, true, bdr_face_Y);
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
|
||||
bdr_face_markers[i], *bdr_face_attributes, true,
|
||||
bdr_face_Y);
|
||||
}
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
}
|
||||
@@ -783,7 +765,7 @@ static void AddWithMarkers_(
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int attr = d_attr[e];
|
||||
if (d_m[attr - 1] == 0) { return; }
|
||||
if (attr <= 0 || d_m[attr - 1] == 0) { return; }
|
||||
for (int i = 0; i < nd; ++i)
|
||||
{
|
||||
d_y(i, e) += d_x(i, e);
|
||||
@@ -828,22 +810,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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +881,8 @@ void EABilinearFormExtension::Assemble()
|
||||
{
|
||||
const int i = idx % sz;
|
||||
const int e = idx / sz;
|
||||
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
|
||||
const real_t val =
|
||||
d_a[e] > 0 ? (d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0) : 0;
|
||||
if (add)
|
||||
{
|
||||
d_ea_2(i, e) += val;
|
||||
@@ -915,7 +915,7 @@ void EABilinearFormExtension::Assemble()
|
||||
ea_data_tmp.SetSize(ea_data.Size());
|
||||
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
|
||||
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
|
||||
elem_attributes, add);
|
||||
*elem_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -944,7 +944,7 @@ void EABilinearFormExtension::Assemble()
|
||||
ea_data_tmp.SetSize(ea_data_bdr.Size());
|
||||
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
|
||||
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
|
||||
bdr_attributes, add);
|
||||
*bdr_face_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -993,7 +993,7 @@ void EABilinearFormExtension::Assemble()
|
||||
ea_data_tmp,
|
||||
add);
|
||||
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
|
||||
bdr_attributes, add);
|
||||
*bdr_face_attributes, add);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1010,8 +1010,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 +1024,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 +1036,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 +1093,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 +1105,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 +1171,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 +1210,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 +1220,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 +1911,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 +1937,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 +1993,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
|
||||
{
|
||||
|
||||
@@ -69,7 +69,8 @@ class PABilinearFormExtension : public BilinearFormExtension
|
||||
protected:
|
||||
const FiniteElementSpace *trial_fes, *test_fes; // Not owned
|
||||
/// Attributes of all mesh elements.
|
||||
Array<int> elem_attributes, bdr_attributes;
|
||||
const Array<int> *elem_attributes; // Not owned
|
||||
const Array<int> *bdr_face_attributes; // Not owned
|
||||
mutable Vector tmp_evec; // Work array
|
||||
mutable Vector localX, localY;
|
||||
mutable Vector int_face_X, int_face_Y;
|
||||
@@ -91,12 +92,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 +116,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 +160,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 +180,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
|
||||
|
||||
@@ -21,6 +21,11 @@ using namespace std;
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void BilinearFormIntegrator::Update()
|
||||
{
|
||||
// default no-op
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssemblePA(fes)\n"
|
||||
@@ -121,6 +126,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 +144,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 +436,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 +452,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++)
|
||||
@@ -3431,6 +3465,12 @@ real_t ElasticityIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
return energy;
|
||||
}
|
||||
|
||||
void DGTraceIntegrator::Update()
|
||||
{
|
||||
qspace[0].reset();
|
||||
qspace[1].reset();
|
||||
}
|
||||
|
||||
void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
|
||||
+37
-10
@@ -23,6 +23,8 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
class QuadratureSpace;
|
||||
class FaceQuadratureSpace;
|
||||
|
||||
/// Abstract base class BilinearFormIntegrator
|
||||
class BilinearFormIntegrator : public NonlinearFormIntegrator
|
||||
@@ -44,6 +46,10 @@ public:
|
||||
// make sense for the action of the nonlinear operator (but they all make
|
||||
// sense for its Jacobian).
|
||||
|
||||
/// Signal this integrator that something about either the trial or test space has changed.
|
||||
virtual void Update();
|
||||
|
||||
|
||||
/// Method defining partial assembly.
|
||||
/** The result of the partial assembly is stored internally so that it can be
|
||||
used later in the methods AddMultPA() and AddMultTransposePA(). */
|
||||
@@ -78,6 +84,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 +98,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 +506,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;
|
||||
@@ -804,7 +818,7 @@ protected:
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetDim() == 1 && test_fe.GetDim() == 1 &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
|
||||
}
|
||||
|
||||
@@ -876,7 +890,7 @@ protected:
|
||||
const FiniteElement & trial_fe,
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
|
||||
}
|
||||
|
||||
@@ -911,7 +925,7 @@ protected:
|
||||
const FiniteElement & trial_fe,
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
|
||||
}
|
||||
|
||||
@@ -1592,7 +1606,7 @@ public:
|
||||
{
|
||||
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
|
||||
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
|
||||
}
|
||||
|
||||
@@ -1627,7 +1641,7 @@ public:
|
||||
{
|
||||
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
|
||||
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
|
||||
}
|
||||
|
||||
@@ -1661,7 +1675,7 @@ public:
|
||||
{
|
||||
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
|
||||
trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
|
||||
}
|
||||
|
||||
@@ -1752,7 +1766,7 @@ public:
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
|
||||
}
|
||||
|
||||
@@ -1785,7 +1799,7 @@ public:
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
|
||||
test_fe.GetDerivType() == mfem::FiniteElement::DIV );
|
||||
}
|
||||
@@ -1824,7 +1838,7 @@ public:
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
|
||||
test_fe.GetDerivType() == mfem::FiniteElement::GRAD
|
||||
);
|
||||
@@ -1965,7 +1979,7 @@ protected:
|
||||
const FiniteElement & test_fe) const override
|
||||
{
|
||||
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
|
||||
}
|
||||
|
||||
@@ -2320,8 +2334,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 +2437,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 +2838,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 +2956,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,
|
||||
@@ -3293,6 +3317,7 @@ protected:
|
||||
VectorCoefficient *u;
|
||||
real_t alpha, beta;
|
||||
// PA extension
|
||||
std::unique_ptr<FaceQuadratureSpace> qspace[2];
|
||||
Vector pa_data;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const FaceGeometricFactors *geom; ///< Not owned
|
||||
@@ -3315,6 +3340,8 @@ public:
|
||||
real_t a, real_t b)
|
||||
{ rho = &rho_; u = &u_; alpha = a; beta = b; }
|
||||
|
||||
void Update() override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
|
||||
+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
|
||||
@@ -912,7 +912,7 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
|
||||
|
||||
if (vdim == 1) // scalar case
|
||||
{
|
||||
n_field["values"].set_external(gf->GetData(),
|
||||
n_field["values"].set_external(const_cast<real_t *>(gf->HostRead()),
|
||||
ndofs);
|
||||
}
|
||||
else // vector case
|
||||
@@ -925,18 +925,18 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
|
||||
int vdim_stride = (ordering == Ordering::byNODES ? ndofs : 1);
|
||||
|
||||
index_t offset = 0;
|
||||
index_t stride = sizeof(double) * entry_stride;
|
||||
index_t stride = sizeof(real_t) * entry_stride;
|
||||
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "v" << d;
|
||||
std::string comp_name = oss.str();
|
||||
n_field["values"][comp_name].set_external(gf->GetData(),
|
||||
n_field["values"][comp_name].set_external(const_cast<real_t *>(gf->HostRead()),
|
||||
ndofs,
|
||||
offset,
|
||||
stride);
|
||||
offset += sizeof(double) * vdim_stride;
|
||||
offset += sizeof(real_t) * vdim_stride;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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,266 @@
|
||||
// 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 "derefmat_op.hpp"
|
||||
#include "fes_kernels.hpp"
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
template <Ordering::Type Order, bool Atomic>
|
||||
static void DerefMultKernelImpl(const DerefineMatrixOp &op, const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
DerefineMatrixOpMultFunctor<Order, Atomic> func;
|
||||
func.xptr = x.Read();
|
||||
y.UseDevice();
|
||||
y = 0.;
|
||||
func.yptr = y.ReadWrite();
|
||||
func.bsptr = op.block_storage.Read();
|
||||
func.boptr = op.block_offsets.Read();
|
||||
func.brptr = op.block_row_idcs_offsets.Read();
|
||||
func.bcptr = op.block_col_idcs_offsets.Read();
|
||||
func.rptr = op.row_idcs.Read();
|
||||
func.cptr = op.col_idcs.Read();
|
||||
func.vdims = op.fespace->GetVDim();
|
||||
func.nblocks = op.block_offsets.Size();
|
||||
func.width = op.Width() / func.vdims;
|
||||
func.height = op.Height() / func.vdims;
|
||||
func.Run(op.max_rows);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
DerefineMatrixOp::DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
|
||||
const Table *old_elem_dof,
|
||||
const Table *old_elem_fos)
|
||||
: Operator(fespace_.GetVSize(), old_ndofs * fespace_.GetVDim()),
|
||||
fespace(&fespace_)
|
||||
{
|
||||
static Kernels kernels;
|
||||
constexpr int max_team_size = 256;
|
||||
/// TODO: Implement DofTransformation support
|
||||
|
||||
MFEM_VERIFY(fespace->Nonconforming(),
|
||||
"Not implemented for conforming meshes.");
|
||||
MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
|
||||
MFEM_VERIFY(fespace->GetNDofs() <= old_ndofs,
|
||||
"Previous space is not finer.");
|
||||
|
||||
const CoarseFineTransformations &dtrans =
|
||||
fespace->GetMesh()->ncmesh->GetDerefinementTransforms();
|
||||
|
||||
MFEM_ASSERT(dtrans.embeddings.Size() == old_elem_dof->Size(), "");
|
||||
|
||||
const bool is_dg = fespace->FEColl()->GetContType()
|
||||
== FiniteElementCollection::DISCONTINUOUS;
|
||||
DenseMatrix localRVO; // for variable-order only
|
||||
|
||||
DenseTensor localR[Geometry::NumGeom];
|
||||
int total_rows = 0;
|
||||
int total_cols = 0;
|
||||
block_offsets.SetSize(dtrans.embeddings.Size());
|
||||
block_offsets.HostWrite();
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
// TODO: any potential for some compression here?
|
||||
// determine storage size and offsets
|
||||
block_offsets[0] = 0;
|
||||
int total_size = 0;
|
||||
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
const FiniteElement *fe = fespace->GetFE(emb.parent);
|
||||
const int ldof = fe->GetDof();
|
||||
if (k + 1 < dtrans.embeddings.Size())
|
||||
{
|
||||
block_offsets[k + 1] = block_offsets[k] + ldof * ldof;
|
||||
}
|
||||
total_rows += ldof;
|
||||
total_cols += ldof;
|
||||
total_size += ldof * ldof;
|
||||
}
|
||||
block_storage.SetSize(total_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// compression scheme:
|
||||
// block_offsets is the start of each block, potentially repeated
|
||||
// only need to store localR for used shapes
|
||||
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
|
||||
|
||||
int geom_offsets[Geometry::NumGeom];
|
||||
{
|
||||
int size = 0;
|
||||
for (int i = 0; i < elem_geoms.Size(); ++i)
|
||||
{
|
||||
fespace->GetLocalDerefinementMatrices(elem_geoms[i],
|
||||
localR[elem_geoms[i]]);
|
||||
geom_offsets[elem_geoms[i]] = size;
|
||||
size += localR[elem_geoms[i]].TotalSize();
|
||||
}
|
||||
block_storage.SetSize(size);
|
||||
// copy blocks into block_storage
|
||||
auto bs_ptr = block_storage.HostWrite();
|
||||
for (int i = 0; i < elem_geoms.Size(); ++i)
|
||||
{
|
||||
std::copy(localR[elem_geoms[i]].Data(),
|
||||
localR[elem_geoms[i]].Data()
|
||||
+ localR[elem_geoms[i]].TotalSize(),
|
||||
bs_ptr);
|
||||
bs_ptr += localR[elem_geoms[i]].TotalSize();
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
Geometry::Type geom =
|
||||
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
|
||||
|
||||
auto size = localR[geom].SizeI() * localR[geom].SizeJ();
|
||||
total_rows += localR[geom].SizeI();
|
||||
total_cols += localR[geom].SizeJ();
|
||||
// set block offsets and sizes
|
||||
block_offsets[k] = geom_offsets[geom] + size * emb.matrix;
|
||||
}
|
||||
}
|
||||
row_idcs.SetSize(total_rows);
|
||||
row_idcs.HostWrite();
|
||||
col_idcs.SetSize(total_cols);
|
||||
col_idcs.HostWrite();
|
||||
block_row_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
|
||||
block_row_idcs_offsets.HostWrite();
|
||||
block_col_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
|
||||
block_col_idcs_offsets.HostWrite();
|
||||
block_row_idcs_offsets[0] = 0;
|
||||
block_col_idcs_offsets[0] = 0;
|
||||
|
||||
// compute index information
|
||||
Array<int> dofs, old_dofs;
|
||||
max_rows = 1;
|
||||
|
||||
{
|
||||
Array<int> mark(fespace->GetNDofs());
|
||||
mark = 0;
|
||||
auto bs_ptr = block_storage.HostWrite();
|
||||
int ridx = 0;
|
||||
int cidx = 0;
|
||||
int num_marked = 0;
|
||||
for (int k = 0; k < dtrans.embeddings.Size(); k++)
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
Geometry::Type geom =
|
||||
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
|
||||
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
const FiniteElement *fe = fespace->GetFE(emb.parent);
|
||||
const DenseTensor &pmats = dtrans.point_matrices[geom];
|
||||
const int ldof = fe->GetDof();
|
||||
|
||||
IsoparametricTransformation isotr;
|
||||
isotr.SetIdentityTransformation(geom);
|
||||
|
||||
localRVO.SetSize(ldof, ldof);
|
||||
isotr.SetPointMat(pmats(emb.matrix));
|
||||
// Local restriction is size ldofxldof assuming that the parent
|
||||
// and child are of same polynomial order.
|
||||
fe->GetLocalRestriction(isotr, localRVO);
|
||||
// copy block
|
||||
auto size = localRVO.Height() * localRVO.Width();
|
||||
std::copy(localRVO.Data(), localRVO.Data() + size, bs_ptr);
|
||||
bs_ptr += size;
|
||||
}
|
||||
DenseMatrix &lR =
|
||||
fespace->IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
|
||||
block_row_idcs_offsets[k + 1] =
|
||||
block_row_idcs_offsets[k] + lR.Height();
|
||||
block_col_idcs_offsets[k + 1] = block_col_idcs_offsets[k] + lR.Width();
|
||||
max_rows = std::max(lR.Height(), max_rows);
|
||||
// index information
|
||||
fespace->elem_dof->GetRow(emb.parent, dofs);
|
||||
old_elem_dof->GetRow(k, old_dofs);
|
||||
MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
|
||||
"Parent and child must have same #dofs.");
|
||||
for (int i = 0; i < lR.Height(); ++i, ++ridx)
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0)))
|
||||
{
|
||||
row_idcs[ridx] = INT_MAX;
|
||||
continue;
|
||||
}
|
||||
int r = dofs[i];
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
row_idcs[ridx] = r;
|
||||
mark[m] = 1;
|
||||
++num_marked;
|
||||
}
|
||||
else
|
||||
{
|
||||
row_idcs[ridx] = INT_MAX;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < lR.Width(); ++i, ++cidx)
|
||||
{
|
||||
col_idcs[cidx] = old_dofs[i];
|
||||
}
|
||||
}
|
||||
if (!is_dg && !fespace->IsVariableOrder())
|
||||
{
|
||||
MFEM_VERIFY(num_marked * fespace->GetVDim() == Height(),
|
||||
"internal error: not all rows were set.");
|
||||
}
|
||||
}
|
||||
// if not using GPU, set max_rows/max_cols to zero
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
max_rows = std::min(max_rows, max_team_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_rows = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void DerefineMatrixOp::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
const bool is_dg = fespace->FEColl()->GetContType()
|
||||
== FiniteElementCollection::DISCONTINUOUS;
|
||||
// DG needs atomic summation
|
||||
MultKernel::Run(fespace->GetOrdering(), is_dg, *this, x, y);
|
||||
}
|
||||
|
||||
DerefineMatrixOp::Kernels::Kernels()
|
||||
{
|
||||
MultKernel::Specialization<Ordering::byNODES, false>::Add();
|
||||
MultKernel::Specialization<Ordering::byVDIM, false>::Add();
|
||||
MultKernel::Specialization<Ordering::byNODES, true>::Add();
|
||||
MultKernel::Specialization<Ordering::byVDIM, true>::Add();
|
||||
}
|
||||
|
||||
template <Ordering::Type Order, bool Atomic>
|
||||
DerefineMatrixOp::MultKernelType DerefineMatrixOp::MultKernel::Kernel()
|
||||
{
|
||||
return internal::DerefMultKernelImpl<Order, Atomic>;
|
||||
}
|
||||
|
||||
DerefineMatrixOp::MultKernelType
|
||||
DerefineMatrixOp::MultKernel::Fallback(Ordering::Type, bool)
|
||||
{
|
||||
MFEM_ABORT("invalid MultKernel parameters");
|
||||
}
|
||||
} // namespace mfem
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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_DEREFMAT_OP
|
||||
#define MFEM_DEREFMAT_OP
|
||||
|
||||
#include "fespace.hpp"
|
||||
|
||||
#include "kernel_dispatch.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
|
||||
struct DerefineMatrixOp : public Operator
|
||||
{
|
||||
FiniteElementSpace *fespace;
|
||||
/// offsets into block_storage
|
||||
Array<int> block_offsets;
|
||||
/// offsets into row_idcs
|
||||
Array<int> block_row_idcs_offsets;
|
||||
/// offsets into col_idcs
|
||||
Array<int> block_col_idcs_offsets;
|
||||
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
|
||||
/// negative means the row data should be negated.
|
||||
Array<int> row_idcs;
|
||||
/// mapping for col dofs, negative means the col data should be negated.
|
||||
Array<int> col_idcs;
|
||||
/// dense block matrices which can be reused to construct the full matrix
|
||||
/// operation. These are stored contiguously and blocks have no restrictions
|
||||
/// on shape (can be rectangle and differ from block to block).
|
||||
Vector block_storage;
|
||||
/// maximum height of any block in block_storage for GPU
|
||||
/// parallelization, or 1 for CPU runs.
|
||||
int max_rows;
|
||||
|
||||
using MultKernelType = void (*)(const DerefineMatrixOp &, const Vector &,
|
||||
Vector &);
|
||||
/// template args: ordering, atomic
|
||||
MFEM_REGISTER_KERNELS(MultKernel, MultKernelType, (Ordering::Type, bool));
|
||||
|
||||
struct Kernels
|
||||
{
|
||||
Kernels();
|
||||
};
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
|
||||
const Table *old_elem_dof, const Table *old_elem_fos);
|
||||
};
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
#endif
|
||||
@@ -241,6 +241,7 @@ public:
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -101,10 +101,11 @@ public:
|
||||
// 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 ?
|
||||
std::pow(dtq.nqpt, mesh.Dimension()) :
|
||||
static_cast<int>(std::pow(dtq.nqpt, mesh.Dimension())) :
|
||||
ir.GetNPoints();
|
||||
|
||||
tsize = vdim * num_qp * mesh.GetNE();
|
||||
|
||||
+13
-12
@@ -327,8 +327,8 @@ void print_mpi_sync(const std::string& msg)
|
||||
// First gather string lengths
|
||||
size_t msg_len = msg.length();
|
||||
std::vector<size_t> lengths(nranks);
|
||||
MPI_Gather(&msg_len, 1, MPI_INT,
|
||||
lengths.data(), 1, MPI_INT,
|
||||
MPI_Gather(&msg_len, 1, MPITypeMap<size_t>::mpi_type,
|
||||
lengths.data(), 1, MPITypeMap<size_t>::mpi_type,
|
||||
0, MPI_COMM_WORLD);
|
||||
|
||||
if (myrank == 0)
|
||||
@@ -568,7 +568,7 @@ struct ThreadBlocks
|
||||
int z = 1;
|
||||
};
|
||||
|
||||
#if (defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
#if defined(MFEM_USE_CUDA_OR_HIP)
|
||||
template <typename func_t>
|
||||
__global__ void forall_kernel_shmem(func_t f, int n)
|
||||
{
|
||||
@@ -591,7 +591,7 @@ void forall(func_t f,
|
||||
if (Device::Allows(Backend::CUDA_MASK) ||
|
||||
Device::Allows(Backend::HIP_MASK))
|
||||
{
|
||||
#if (defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
#if defined(MFEM_USE_CUDA_OR_HIP)
|
||||
// int gridsize = (N + Z - 1) / Z;
|
||||
int num_bytes = num_shmem * sizeof(decltype(shmem));
|
||||
dim3 block_size(blocks.x, blocks.y, blocks.z);
|
||||
@@ -987,7 +987,7 @@ get_restriction_transpose(
|
||||
{
|
||||
auto RT = [=](const Vector &v_e, Vector &v_l)
|
||||
{
|
||||
v_l = v_e;
|
||||
v_l += v_e;
|
||||
};
|
||||
return std::make_tuple(RT, 1);
|
||||
}
|
||||
@@ -996,7 +996,7 @@ get_restriction_transpose(
|
||||
const Operator *R = get_restriction<entity_t>(f, o);
|
||||
std::function<void(const Vector&, Vector&)> RT = [=](const Vector &x, Vector &y)
|
||||
{
|
||||
R->MultTranspose(x, y);
|
||||
R->AddMultTranspose(x, y);
|
||||
};
|
||||
return std::make_tuple(RT, R->Height());
|
||||
}
|
||||
@@ -1702,12 +1702,13 @@ std::array<DofToQuadMap, N> load_dtq_mem(
|
||||
std::array<DofToQuadMap, N> f;
|
||||
for (std::size_t i = 0; i < N; i++)
|
||||
{
|
||||
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
|
||||
const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
|
||||
auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
|
||||
ndof_b);
|
||||
if (dtq[i].which_input != -1)
|
||||
{
|
||||
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
|
||||
const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
|
||||
auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
|
||||
ndof_b);
|
||||
|
||||
MFEM_FOREACH_THREAD(q, x, nqp_b)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d, y, ndof_b)
|
||||
@@ -2158,7 +2159,7 @@ template <
|
||||
std::size_t... Is>
|
||||
std::array<DofToQuadMap, N> create_dtq_maps_impl(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> dtqs,
|
||||
std::vector<const DofToQuad*> &dtqs,
|
||||
const std::array<int, N> &field_map,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
@@ -2243,7 +2244,7 @@ template <
|
||||
std::size_t num_fields>
|
||||
std::array<DofToQuadMap, num_fields> create_dtq_maps(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> dtqmaps,
|
||||
std::vector<const DofToQuad*> &dtqmaps,
|
||||
const std::array<int, num_fields> &to_field_map)
|
||||
{
|
||||
return create_dtq_maps_impl<entity_t>(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "lor/lor.hpp"
|
||||
#include "dgmassinv.hpp"
|
||||
#include "hyperbolic.hpp"
|
||||
#include "bounds.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
// 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_FES_KERNELS_HPP
|
||||
#define MFEM_FES_KERNELS_HPP
|
||||
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
namespace internal
|
||||
{
|
||||
|
||||
///
|
||||
/// Implements matrix-vector multiply $y = A x$ for a sparse matrix composed of
|
||||
/// a sum of smaller dense blocks. There is additional permutation/sign
|
||||
/// information associated with each block. The base class only implements
|
||||
/// helper routines such as computing block widths, index into x, index into y,
|
||||
/// and column in A given sub-block information.
|
||||
/// @sa DerefineMatrixOpMultFunctor
|
||||
///
|
||||
/// @tparam Order vdim ordering for x and y. Note that for Diag = false this is
|
||||
/// ignored for x as x has a special interleaved order.
|
||||
/// @tparam Base used for the curious recurring template pattern (CRTP) so the
|
||||
/// base class can access child class fields without virtual functions
|
||||
/// @tparam Diag true if this corresponds to the diagonal block (coarse element
|
||||
/// and fine element are on our rank), false otherwise (coarse element is on our
|
||||
/// rank, fine element is on a different rank).
|
||||
///
|
||||
template <Ordering::Type Order, class Base, bool Diag = true>
|
||||
struct DerefineMatrixOpFunctorBase;
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, true>
|
||||
{
|
||||
/// block column indices offsets
|
||||
const int *bcptr;
|
||||
/// column indices
|
||||
const int *cptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const
|
||||
{
|
||||
return bcptr[k + 1] - bcptr[k];
|
||||
}
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = cptr[bcptr[k] + j];
|
||||
if (col < 0)
|
||||
{
|
||||
col = -1 - col;
|
||||
sign = -sign;
|
||||
}
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
|
||||
{
|
||||
return col + vdim * static_cast<const Base *>(this)->width;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return row + vdim * static_cast<const Base *>(this)->height;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, true>
|
||||
{
|
||||
/// block column indices offsets
|
||||
const int *bcptr;
|
||||
/// column indices
|
||||
const int *cptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const
|
||||
{
|
||||
return bcptr[k + 1] - bcptr[k];
|
||||
}
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = cptr[bcptr[k] + j];
|
||||
if (col < 0)
|
||||
{
|
||||
col = -1 - col;
|
||||
sign = -sign;
|
||||
}
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
|
||||
{
|
||||
return vdim + col * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return vdim + row * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, false>
|
||||
{
|
||||
/// receive segment offsets
|
||||
const int *segptr;
|
||||
/// receive segment index
|
||||
const int *rsptr;
|
||||
/// off-diagonal block column offsets
|
||||
const int *coptr;
|
||||
/// off-diagonal block widths
|
||||
const int *bwptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = coptr[k] + j;
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
|
||||
{
|
||||
int tmp = rsptr[k];
|
||||
int segwidth = segptr[tmp + 1] - segptr[tmp];
|
||||
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
|
||||
vdim * segwidth;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return row + vdim * static_cast<const Base *>(this)->height;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, false>
|
||||
{
|
||||
/// receive segment offsets
|
||||
const int *segptr;
|
||||
/// receive segment index
|
||||
const int *rsptr;
|
||||
/// off-diagonal block column offsets
|
||||
const int *coptr;
|
||||
/// off-diagonal block widths
|
||||
const int *bwptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = coptr[k] + j;
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
|
||||
{
|
||||
int tmp = rsptr[k];
|
||||
int segwidth = segptr[tmp + 1] - segptr[tmp];
|
||||
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
|
||||
vdim * segwidth;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return vdim + row * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
};
|
||||
|
||||
/// internally used to implement the derefinement operator Mult diagonal
|
||||
/// block
|
||||
template <Ordering::Type Order, bool Atomic, bool Diag = true>
|
||||
struct DerefineMatrixOpMultFunctor
|
||||
: public DerefineMatrixOpFunctorBase<
|
||||
Order, DerefineMatrixOpMultFunctor<Order, Atomic, Diag>, Diag>
|
||||
{
|
||||
const real_t *xptr;
|
||||
real_t *yptr;
|
||||
/// block storage
|
||||
const real_t *bsptr;
|
||||
/// block offsets
|
||||
const int *boptr;
|
||||
/// block row index offsets
|
||||
const int *brptr;
|
||||
/// row indices
|
||||
const int *rptr;
|
||||
|
||||
// number of blocks
|
||||
int nblocks;
|
||||
// number of components
|
||||
int vdims;
|
||||
/// overall operator height (for vdim = 1)
|
||||
int height;
|
||||
/// overall operator width (for vdim = 1)
|
||||
int width;
|
||||
void MFEM_HOST_DEVICE operator()(int kidx) const
|
||||
{
|
||||
int k = kidx % nblocks;
|
||||
int vdim = kidx / nblocks;
|
||||
|
||||
int block_height = brptr[k + 1] - brptr[k];
|
||||
int block_width = this->BlockWidth(k);
|
||||
MFEM_FOREACH_THREAD(i, x, block_height)
|
||||
{
|
||||
int row = rptr[brptr[k] + i];
|
||||
int rsign = 1;
|
||||
if (row < 0)
|
||||
{
|
||||
row = -1 - row;
|
||||
rsign = -1;
|
||||
}
|
||||
if (row < INT_MAX)
|
||||
{
|
||||
// row not marked as unused
|
||||
real_t sum = 0;
|
||||
for (int j = 0; j < block_width; ++j)
|
||||
{
|
||||
int col, sign = rsign;
|
||||
this->Col(j, k, col, sign);
|
||||
sum += sign * bsptr[boptr[k] + i + j * block_height] *
|
||||
xptr[this->IndexX(col, vdim, k)];
|
||||
}
|
||||
#if defined(__CUDA_ARCH__) or defined(__HIP_DEVICE_COMPILE__)
|
||||
if (Atomic)
|
||||
{
|
||||
atomicAdd(yptr + this->IndexY(row, vdim), sum);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
yptr[this->IndexY(row, vdim)] += sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// N is the max block row size (doesn't have to be a power of 2)
|
||||
void Run(int N) const { forall_2D(nblocks * vdims, N, 1, *this); }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+13
-9
@@ -17,6 +17,9 @@
|
||||
#include "fem.hpp"
|
||||
#include "ceed/interface/util.hpp"
|
||||
|
||||
#include "derefmat_op.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
|
||||
@@ -24,9 +27,9 @@ using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <> void Ordering::
|
||||
DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim, Array<int> &dofs)
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
@@ -40,8 +43,9 @@ DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim, Array<int> &dofs)
|
||||
}
|
||||
}
|
||||
|
||||
template <> void Ordering::
|
||||
DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
@@ -55,7 +59,6 @@ DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FiniteElementSpace::FiniteElementSpace()
|
||||
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
|
||||
ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
|
||||
@@ -4244,7 +4247,11 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
case Mesh::DEREFINE:
|
||||
{
|
||||
BuildConformingInterpolation();
|
||||
#if 0
|
||||
Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
|
||||
#else
|
||||
Th.Reset(new DerefineMatrixOp(*this, old_ndofs, old_elem_dof, old_elem_fos));
|
||||
#endif
|
||||
if (IsVariableOrder())
|
||||
{
|
||||
if (cP && cR_hp)
|
||||
@@ -4278,9 +4285,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));
|
||||
|
||||
+2
-1
@@ -113,7 +113,7 @@ class QuadratureSpace;
|
||||
class QuadratureInterpolator;
|
||||
class FaceQuadratureInterpolator;
|
||||
class PRefinementTransferOperator;
|
||||
|
||||
struct DerefineMatrixOp;
|
||||
|
||||
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
|
||||
mesh, mainly managing the set of degrees of freedom.
|
||||
@@ -246,6 +246,7 @@ class FiniteElementSpace
|
||||
friend class PRefinementTransferOperator;
|
||||
friend void Mesh::Swap(Mesh &, bool);
|
||||
friend class LORBase;
|
||||
friend struct DerefineMatrixOp;
|
||||
|
||||
protected:
|
||||
/// The mesh that FE space lives on (not owned).
|
||||
|
||||
+132
-1
@@ -4334,7 +4334,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
u.GetSubVector(udofs, 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);
|
||||
|
||||
@@ -4563,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
|
||||
|
||||
@@ -139,8 +139,6 @@ void DGTraceIntegrator::SetupPA(const FiniteElementSpace &fes, FaceType type)
|
||||
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
|
||||
Device::GetDeviceMemoryType() : pa_mt;
|
||||
|
||||
nf = fes.GetNFbyType(type);
|
||||
if (nf==0) { return; }
|
||||
// Assumes tensor-product elements
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
const FiniteElement &el = *fes.GetTypicalTraceElement();
|
||||
@@ -148,6 +146,17 @@ void DGTraceIntegrator::SetupPA(const FiniteElementSpace &fes, FaceType type)
|
||||
IntRule:
|
||||
&GetRule(el.GetGeomType(), el.GetOrder(),
|
||||
*mesh->GetTypicalElementTransformation());
|
||||
|
||||
if (!qspace[static_cast<int>(type)])
|
||||
{
|
||||
qspace[static_cast<int>(type)].reset(
|
||||
new FaceQuadratureSpace(*mesh, *ir, type));
|
||||
}
|
||||
|
||||
FaceQuadratureSpace& qs = *qspace[static_cast<int>(type)];
|
||||
nf = qs.GetNumFaces();
|
||||
if (nf==0) { return; }
|
||||
|
||||
const int symmDims = 4;
|
||||
nq = ir->GetNPoints();
|
||||
dim = mesh->Dimension();
|
||||
@@ -159,8 +168,6 @@ void DGTraceIntegrator::SetupPA(const FiniteElementSpace &fes, FaceType type)
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
pa_data.SetSize(symmDims * nq * nf, Device::GetMemoryType());
|
||||
|
||||
FaceQuadratureSpace qs(*mesh, *ir, type);
|
||||
CoefficientVector vel(*u, qs, CoefficientStorage::COMPRESSED);
|
||||
|
||||
CoefficientVector r(qs, CoefficientStorage::COMPRESSED);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -212,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++)
|
||||
@@ -226,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
|
||||
@@ -236,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));
|
||||
}
|
||||
}
|
||||
@@ -244,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,26 +59,23 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
|
||||
|
||||
const int NE = ne;
|
||||
const int Q1D = quad1D;
|
||||
const int NQ = 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);
|
||||
const auto J = Reshape(geom->detJ.Read(), NQ, NE);
|
||||
const auto C = const_c ? Reshape(coeff.Read(), 1, 1) :
|
||||
Reshape(coeff.Read(), NQ,NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ, NE);
|
||||
mfem::forall_2D(NE, NQ, 1, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i, x, NQ)
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool by_val = map_type == FiniteElement::VALUE;
|
||||
const auto W = Reshape(ir->GetWeights().Read(), NQ);
|
||||
const auto J = Reshape(geom->detJ.Read(), NQ, NE);
|
||||
const auto C =
|
||||
const_c ? Reshape(coeff.Read(), 1, 1) : Reshape(coeff.Read(), NQ, NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ, NE);
|
||||
mfem::forall(NQ, NE, [=] MFEM_HOST_DEVICE(int q, int e)
|
||||
{
|
||||
const real_t detJ = J(i,e);
|
||||
const real_t coeff = const_c ? C(0,0) : C(i,e);
|
||||
v(i,e) = W(i) * coeff * (by_val ? detJ : 1.0/detJ);
|
||||
}
|
||||
});
|
||||
const real_t detJ = J(q, e);
|
||||
const real_t coeff = const_c ? C(0, 0) : C(q, e);
|
||||
v(q, e) = W(q) * coeff * (by_val ? detJ : 1.0 / detJ);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
|
||||
@@ -109,50 +106,22 @@ void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
|
||||
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
|
||||
|
||||
const int NE = ne;
|
||||
const int Q1D = quad1D;
|
||||
const int NQ = nq;
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool by_val = map_type == FiniteElement::VALUE;
|
||||
if (dim==1)
|
||||
{
|
||||
const auto W = Reshape(ir->GetWeights().Read(), Q1D);
|
||||
const auto J = Reshape(face_geom->detJ.Read(), Q1D, NE);
|
||||
const auto C = const_c ? Reshape(coeff.Read(), 1, 1) :
|
||||
Reshape(coeff.Read(), Q1D, NE);
|
||||
auto v = Reshape(pa_data.Write(), Q1D, NE);
|
||||
mfem::forall_2D(NE, Q1D, 1, [=] MFEM_HOST_DEVICE (int e)
|
||||
const auto W = Reshape(ir->GetWeights().Read(), NQ);
|
||||
const auto J = Reshape(face_geom->detJ.Read(), NQ, NE);
|
||||
const auto C = const_c ? Reshape(coeff.Read(), 1, 1)
|
||||
: Reshape(coeff.Read(), NQ, NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ, NE);
|
||||
mfem::forall(NQ, NE, [=] MFEM_HOST_DEVICE(int q, int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
const real_t detJ = J(qx,e);
|
||||
const real_t coeff = const_c ? C(0,0) : C(qx,e);
|
||||
v(qx,e) = W(qx) * coeff * (by_val ? detJ : 1.0/detJ);
|
||||
}
|
||||
const real_t detJ = J(q, e);
|
||||
const real_t coeff = const_c ? C(0, 0) : C(q, e);
|
||||
v(q, e) = W(q) * coeff * (by_val ? detJ : 1.0 / detJ);
|
||||
});
|
||||
}
|
||||
else if (dim==2)
|
||||
{
|
||||
const auto W = Reshape(ir->GetWeights().Read(), Q1D,Q1D);
|
||||
const auto J = Reshape(face_geom->detJ.Read(), Q1D,Q1D,NE);
|
||||
const auto C = const_c ? Reshape(coeff.Read(), 1,1,1) :
|
||||
Reshape(coeff.Read(), Q1D,Q1D,NE);
|
||||
auto v = Reshape(pa_data.Write(), Q1D,Q1D, NE);
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
const real_t detJ = J(qx,qy,e);
|
||||
const real_t coeff = const_c ? C(0,0,0) : C(qx,qy,e);
|
||||
v(qx,qy,e) = W(qx,qy) * coeff * (by_val ? detJ : 1.0/detJ);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
void MassIntegrator::AssembleDiagonalPA(Vector &diag)
|
||||
@@ -199,10 +168,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
|
||||
{
|
||||
|
||||
+29
-29
@@ -346,13 +346,13 @@ private:
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[2], u3);
|
||||
const int el_order = el->GetOrder();
|
||||
std::vector<T> u1(el_order+1);
|
||||
std::vector<T> u2(el_order+1);
|
||||
std::vector<T> u3(el_order+1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[2], u3.data());
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
@@ -370,17 +370,17 @@ private:
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,3> grad(const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
T d3[el_order+1];
|
||||
const int el_order = el->GetOrder();
|
||||
std::vector<T> u1(el_order+1);
|
||||
std::vector<T> u2(el_order+1);
|
||||
std::vector<T> u3(el_order+1);
|
||||
std::vector<T> d1(el_order+1);
|
||||
std::vector<T> d2(el_order+1);
|
||||
std::vector<T> d3(el_order+1);
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[2], u3, d3);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1.data(), d1.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2.data(), d2.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[2], u3.data(), d3.data());
|
||||
|
||||
blitz::TinyVector<T,3> res(T(0.0),T(0.0),T(0.0));
|
||||
|
||||
@@ -415,11 +415,11 @@ private:
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
const int el_order = el->GetOrder();
|
||||
std::vector<T> u1(el_order+1);
|
||||
std::vector<T> u2(el_order+1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2.data());
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
@@ -437,14 +437,14 @@ private:
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,2> grad(const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
const int el_order = el->GetOrder();
|
||||
std::vector<T> u1(el_order+1);
|
||||
std::vector<T> u2(el_order+1);
|
||||
std::vector<T> d1(el_order+1);
|
||||
std::vector<T> d2(el_order+1);
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1.data(), d1.data());
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2.data(), d2.data());
|
||||
|
||||
blitz::TinyVector<T,2> res(T(0.0),T(0.0));
|
||||
|
||||
|
||||
+10
-32
@@ -51,7 +51,7 @@ void LinearFormExtension::Assemble()
|
||||
{
|
||||
// scan the attributes to set the markers to 0 or 1
|
||||
const int NE = fes.GetNE();
|
||||
const auto attr = attributes.Read();
|
||||
const auto attr = attributes->Read();
|
||||
const auto dimk = domain_integs_marker_k->Read();
|
||||
auto markers_w = markers.Write();
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
@@ -93,13 +93,14 @@ void LinearFormExtension::Assemble()
|
||||
else
|
||||
{
|
||||
// scan the attributes to set the markers to 0 or 1
|
||||
const int NBE = bdr_attributes.Size();
|
||||
const auto attr = bdr_attributes.Read();
|
||||
const int NBE = bdr_face_attributes->Size();
|
||||
const auto attr = bdr_face_attributes->Read();
|
||||
const auto attr_markers = boundary_integs_marker_k->Read();
|
||||
auto markers_w = bdr_markers.Write();
|
||||
mfem::forall(NBE, [=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall(NBE, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
markers_w[e] = attr_markers[attr[e]-1] == 1;
|
||||
markers_w[e] =
|
||||
attr[e] > 0 ? (attr_markers[attr[e] - 1] == 1) : false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -125,8 +126,7 @@ void LinearFormExtension::Update()
|
||||
//markers.UseDevice(true);
|
||||
|
||||
// Gather the attributes on the host from all the elements
|
||||
attributes.SetSize(NE);
|
||||
for (int i = 0; i < NE; ++i) { attributes[i] = mesh.GetAttribute(i); }
|
||||
attributes = &mesh.GetElementAttributes();
|
||||
|
||||
elem_restrict_lex = fes.GetElementRestriction(ordering);
|
||||
MFEM_VERIFY(elem_restrict_lex, "Element restriction not available");
|
||||
@@ -136,34 +136,12 @@ void LinearFormExtension::Update()
|
||||
|
||||
if (lf->boundary_integs.Size() > 0)
|
||||
{
|
||||
const int nf_bdr = fes.GetNFbyType(FaceType::Boundary);
|
||||
bdr_face_attributes = &mesh.GetBdrFaceAttributes();
|
||||
|
||||
const int nf_bdr = bdr_face_attributes->Size();
|
||||
bdr_markers.SetSize(nf_bdr);
|
||||
// bdr_markers.UseDevice(true);
|
||||
|
||||
// The face restriction will give us "face E-vectors" on the boundary that
|
||||
// are numbered in the order of the faces of mesh. This numbering will be
|
||||
// different than the numbering of the boundary elements. We compute
|
||||
// mappings so that the array `bdr_attributes[i]` gives the boundary
|
||||
// attribute of the `i`th boundary face in the mesh face order.
|
||||
std::unordered_map<int,int> f_to_be;
|
||||
for (int i = 0; i < mesh.GetNBE(); ++i)
|
||||
{
|
||||
const int f = mesh.GetBdrElementFaceIndex(i);
|
||||
f_to_be[f] = i;
|
||||
}
|
||||
MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
|
||||
bdr_attributes.SetSize(nf_bdr);
|
||||
int f_ind = 0;
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
{
|
||||
if (f_to_be.find(f) != f_to_be.end())
|
||||
{
|
||||
const int be = f_to_be[f];
|
||||
bdr_attributes[f_ind] = mesh.GetBdrAttribute(be);
|
||||
++f_ind;
|
||||
}
|
||||
}
|
||||
|
||||
bdr_restrict_lex =
|
||||
dynamic_cast<const FaceRestriction*>(
|
||||
fes.GetFaceRestriction(ordering, FaceType::Boundary,
|
||||
|
||||
@@ -25,7 +25,8 @@ class LinearForm;
|
||||
class LinearFormExtension
|
||||
{
|
||||
/// Attributes of all mesh elements.
|
||||
Array<int> attributes, bdr_attributes;
|
||||
const Array<int> *attributes; // Not owned
|
||||
const Array<int> *bdr_face_attributes; // Not owned
|
||||
|
||||
/// Temporary markers for device kernels.
|
||||
Array<int> markers, bdr_markers;
|
||||
|
||||
+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
|
||||
|
||||
@@ -0,0 +1,591 @@
|
||||
// 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 "pderefmat_op.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "fes_kernels.hpp"
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
namespace mfem
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
template <Ordering::Type Order, bool Atomic>
|
||||
static void ParDerefMultKernelImpl(const ParDerefineMatrixOp &op,
|
||||
const Vector &x, Vector &y)
|
||||
{
|
||||
// pack sends
|
||||
if (op.xghost_send.Size())
|
||||
{
|
||||
auto src = x.Read();
|
||||
auto idcs = op.send_permutations.Read();
|
||||
auto dst = Device::GetGPUAwareMPI() ? op.xghost_send.Write()
|
||||
: op.xghost_send.HostWrite();
|
||||
auto vdims = op.fespace->GetVDim();
|
||||
auto sptr = op.send_segment_idcs.Read();
|
||||
auto lptr = op.send_segments.Read();
|
||||
auto old_ndofs = x.Size() / vdims;
|
||||
|
||||
forall(op.send_permutations.Size(), [=] MFEM_HOST_DEVICE(int i)
|
||||
{
|
||||
int seg = sptr[i];
|
||||
int width = lptr[seg + 1] - lptr[seg];
|
||||
auto tdst = dst + i + lptr[seg] * vdims;
|
||||
int sign = 1;
|
||||
int col = idcs[i];
|
||||
if (col < 0)
|
||||
{
|
||||
sign = -1;
|
||||
col = -1 - col;
|
||||
}
|
||||
for (int vdim = 0; vdim < vdims; ++vdim)
|
||||
{
|
||||
tdst[vdim * width] =
|
||||
sign
|
||||
* src[Order == Ordering::byNODES ? (col + vdim * old_ndofs)
|
||||
: (col * vdims + vdim)];
|
||||
}
|
||||
});
|
||||
// TODO: is this needed so we can send the packed data correctly?
|
||||
// unclear for GPU-aware MPI, definitely required otherwise
|
||||
MFEM_DEVICE_SYNC;
|
||||
}
|
||||
// initialize off-diagonal receive and send
|
||||
op.requests.clear();
|
||||
if (op.xghost_recv.Size())
|
||||
{
|
||||
auto vdims = op.fespace->GetVDim();
|
||||
auto rcv = Device::GetGPUAwareMPI() ? op.xghost_recv.Write()
|
||||
: op.xghost_recv.HostWrite();
|
||||
for (int i = 0; i < op.recv_ranks.Size(); ++i)
|
||||
{
|
||||
op.requests.emplace_back();
|
||||
MPI_Irecv(rcv + op.recv_segments[i] * vdims,
|
||||
(op.recv_segments[i + 1] - op.recv_segments[i]) * vdims,
|
||||
MPITypeMap<real_t>::mpi_type, op.recv_ranks[i],
|
||||
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA,
|
||||
op.fespace->GetComm(), &op.requests.back());
|
||||
}
|
||||
}
|
||||
if (op.xghost_send.Size())
|
||||
{
|
||||
auto vdims = op.fespace->GetVDim();
|
||||
// only is a GPU mem ptr if GPU-aware MPI is enabled
|
||||
auto dst = Device::GetGPUAwareMPI() ? op.xghost_send.Write()
|
||||
: op.xghost_send.HostWrite();
|
||||
for (int i = 0; i < op.send_ranks.Size(); ++i)
|
||||
{
|
||||
op.requests.emplace_back();
|
||||
MPI_Isend(dst + op.send_segments[i] * vdims,
|
||||
(op.send_segments[i + 1] - op.send_segments[i]) * vdims,
|
||||
MPITypeMap<real_t>::mpi_type, op.send_ranks[i],
|
||||
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA,
|
||||
op.fespace->GetComm(), &op.requests.back());
|
||||
}
|
||||
}
|
||||
{
|
||||
// diagonal
|
||||
DerefineMatrixOpMultFunctor<Order, Atomic, true> func;
|
||||
func.xptr = x.Read();
|
||||
y.UseDevice();
|
||||
y = 0.;
|
||||
func.yptr = y.ReadWrite();
|
||||
func.bsptr = op.block_storage.Read();
|
||||
func.boptr = op.block_offsets.Read();
|
||||
func.brptr = op.block_row_idcs_offsets.Read();
|
||||
func.bcptr = op.block_col_idcs_offsets.Read();
|
||||
func.rptr = op.row_idcs.Read();
|
||||
func.cptr = op.col_idcs.Read();
|
||||
func.vdims = op.fespace->GetVDim();
|
||||
func.nblocks = op.block_offsets.Size();
|
||||
func.width = op.Width() / func.vdims;
|
||||
func.height = op.Height() / func.vdims;
|
||||
func.Run(op.max_rows);
|
||||
}
|
||||
// wait for comm to finish, if any
|
||||
if (op.requests.size())
|
||||
{
|
||||
MPI_Waitall(op.requests.size(), op.requests.data(), MPI_STATUSES_IGNORE);
|
||||
if (op.xghost_recv.Size())
|
||||
{
|
||||
// off-diagonal kernel
|
||||
DerefineMatrixOpMultFunctor<Order, Atomic, false> func;
|
||||
// directly read from host-pinned memory if not using GPU-aware MPI
|
||||
func.xptr = Device::GetGPUAwareMPI() ? op.xghost_recv.Read()
|
||||
: op.xghost_recv.HostRead();
|
||||
func.yptr = y.ReadWrite();
|
||||
func.bsptr = op.block_storage.Read();
|
||||
func.boptr = op.off_diag_block_offsets.Read();
|
||||
func.brptr = op.block_off_diag_row_idcs_offsets.Read();
|
||||
func.rsptr = op.recv_segment_idcs.Read();
|
||||
func.segptr = op.recv_segments.Read();
|
||||
func.coptr = op.block_off_diag_col_offsets.Read();
|
||||
func.bwptr = op.block_off_diag_widths.Read();
|
||||
func.rptr = op.row_off_diag_idcs.Read();
|
||||
func.vdims = op.fespace->GetVDim();
|
||||
func.nblocks = op.off_diag_block_offsets.Size();
|
||||
func.width = op.xghost_recv.Size() / func.vdims;
|
||||
func.height = op.Height() / func.vdims;
|
||||
func.Run(op.max_rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <Ordering::Type Order, bool Atomic>
|
||||
ParDerefineMatrixOp::MultKernelType ParDerefineMatrixOp::MultKernel::Kernel()
|
||||
{
|
||||
return internal::ParDerefMultKernelImpl<Order, Atomic>;
|
||||
}
|
||||
|
||||
ParDerefineMatrixOp::MultKernelType
|
||||
ParDerefineMatrixOp::MultKernel::Fallback(Ordering::Type, bool)
|
||||
{
|
||||
MFEM_ABORT("invalid MultKernel parameters");
|
||||
}
|
||||
|
||||
ParDerefineMatrixOp::Kernels::Kernels()
|
||||
{
|
||||
MultKernel::Specialization<Ordering::byNODES, false>::Add();
|
||||
MultKernel::Specialization<Ordering::byVDIM, false>::Add();
|
||||
MultKernel::Specialization<Ordering::byNODES, true>::Add();
|
||||
MultKernel::Specialization<Ordering::byVDIM, true>::Add();
|
||||
}
|
||||
|
||||
void ParDerefineMatrixOp::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
const bool is_dg = fespace->FEColl()->GetContType()
|
||||
== FiniteElementCollection::DISCONTINUOUS;
|
||||
// DG needs atomic summation
|
||||
MultKernel::Run(fespace->GetOrdering(), is_dg, *this, x, y);
|
||||
// use this to prevent xghost* from being re-purposed for subsequent Mult
|
||||
// calls
|
||||
MFEM_DEVICE_SYNC;
|
||||
}
|
||||
|
||||
ParDerefineMatrixOp::ParDerefineMatrixOp(ParFiniteElementSpace &fespace_,
|
||||
int old_ndofs,
|
||||
const Table *old_elem_dof,
|
||||
const Table *old_elem_fos)
|
||||
: Operator(fespace_.GetVSize(), old_ndofs * fespace_.GetVDim()),
|
||||
fespace(&fespace_)
|
||||
{
|
||||
static Kernels kernels;
|
||||
constexpr int max_team_size = 256;
|
||||
|
||||
const int NRanks = fespace->GetNRanks();
|
||||
|
||||
const int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
|
||||
|
||||
MFEM_VERIFY(fespace->Nonconforming(),
|
||||
"Not implemented for conforming meshes.");
|
||||
MFEM_VERIFY(fespace->old_dof_offsets[nrk],
|
||||
"Missing previous (finer) space.");
|
||||
|
||||
const int MyRank = fespace->GetMyRank();
|
||||
ParNCMesh *old_pncmesh = fespace->GetParMesh()->pncmesh;
|
||||
const CoarseFineTransformations &dtrans =
|
||||
old_pncmesh->GetDerefinementTransforms();
|
||||
const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
|
||||
|
||||
const bool is_dg = fespace->FEColl()->GetContType()
|
||||
== FiniteElementCollection::DISCONTINUOUS;
|
||||
DenseMatrix localRVO; // for variable-order only
|
||||
|
||||
DenseTensor localR[Geometry::NumGeom];
|
||||
int diag_rows = 0;
|
||||
int off_diag_rows = 0;
|
||||
int diag_cols = 0;
|
||||
|
||||
auto get_ldofs = [&](int k) -> int
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
const FiniteElement *fe = fespace->GetFE(emb.parent);
|
||||
return fe->GetDof();
|
||||
}
|
||||
else
|
||||
{
|
||||
Geometry::Type geom =
|
||||
fespace->GetParMesh()->GetElementBaseGeometry(emb.parent);
|
||||
return fespace->FEColl()->FiniteElementForGeometry(geom)->GetDof();
|
||||
}
|
||||
};
|
||||
Array<int> dofs, old_dofs;
|
||||
max_rows = 1;
|
||||
// first pass:
|
||||
// - determine memory block lengths
|
||||
// - identify dofs in x we need to send/receive
|
||||
// don't need to send the indices, fine rank will re-arrange and sign
|
||||
// change x before transmitting the ghost data
|
||||
|
||||
// key: coarse rank to send to
|
||||
// value: old dofs to send (with sign)
|
||||
std::map<int, std::vector<int>> to_send;
|
||||
// key: fine rank
|
||||
// value: indices into dtrans.embeddings
|
||||
std::map<int, std::vector<int>> od_ks;
|
||||
// key: fine rank
|
||||
// value: recv segment length
|
||||
std::map<int, int> od_seg_lens;
|
||||
int send_len = 0;
|
||||
int recv_len = 0;
|
||||
// size of block_storage, if fespace->IsVariableOrder()
|
||||
// otherwise unused
|
||||
int total_size = 0;
|
||||
int num_diagonal_blocks = 0;
|
||||
int num_offdiagonal_blocks = 0;
|
||||
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
int fine_rank = old_ranks[k];
|
||||
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
if (coarse_rank != MyRank && fine_rank == MyRank)
|
||||
{
|
||||
// this rank needs to send data in x to course_rank
|
||||
old_elem_dof->GetRow(k, old_dofs);
|
||||
auto &tmp = to_send[coarse_rank];
|
||||
send_len += old_dofs.Size();
|
||||
for (int i = 0; i < old_dofs.Size(); ++i)
|
||||
{
|
||||
tmp.emplace_back(old_dofs[i]);
|
||||
}
|
||||
}
|
||||
else if (coarse_rank == MyRank && fine_rank != MyRank)
|
||||
{
|
||||
// this rank needs to receive data in x from fine_rank
|
||||
MFEM_ASSERT(emb.parent >= 0, "");
|
||||
auto ldofs = get_ldofs(k);
|
||||
off_diag_rows += ldofs;
|
||||
recv_len += ldofs;
|
||||
od_ks[fine_rank].emplace_back(k);
|
||||
od_seg_lens[fine_rank] += ldofs;
|
||||
++num_offdiagonal_blocks;
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
total_size += ldofs * ldofs;
|
||||
}
|
||||
}
|
||||
else if (coarse_rank == MyRank && fine_rank == MyRank)
|
||||
{
|
||||
MFEM_ASSERT(emb.parent >= 0, "");
|
||||
// diagonal
|
||||
++num_diagonal_blocks;
|
||||
auto ldofs = get_ldofs(k);
|
||||
diag_rows += ldofs;
|
||||
diag_cols += ldofs;
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
total_size += ldofs * ldofs;
|
||||
}
|
||||
}
|
||||
}
|
||||
send_segments.SetSize(to_send.size() + 1);
|
||||
send_segments.HostWrite();
|
||||
send_ranks.SetSize(to_send.size());
|
||||
send_ranks.HostWrite();
|
||||
{
|
||||
int idx = 0;
|
||||
send_segments[0] = 0;
|
||||
for (auto &tmp : to_send)
|
||||
{
|
||||
send_ranks[idx] = tmp.first;
|
||||
send_segments[idx + 1] = send_segments[idx] + tmp.second.size();
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
recv_segment_idcs.SetSize(off_diag_rows);
|
||||
recv_segment_idcs.HostWrite();
|
||||
recv_segments.SetSize(od_ks.size() + 1);
|
||||
recv_segments.HostWrite();
|
||||
recv_ranks.SetSize(od_ks.size());
|
||||
recv_ranks.HostWrite();
|
||||
|
||||
// set sizes
|
||||
row_idcs.SetSize(diag_rows);
|
||||
row_idcs.HostWrite();
|
||||
row_off_diag_idcs.SetSize(off_diag_rows);
|
||||
row_off_diag_idcs.HostWrite();
|
||||
col_idcs.SetSize(diag_cols);
|
||||
col_idcs.HostWrite();
|
||||
block_row_idcs_offsets.SetSize(num_diagonal_blocks + 1);
|
||||
block_row_idcs_offsets.HostWrite();
|
||||
block_col_idcs_offsets.SetSize(num_diagonal_blocks + 1);
|
||||
block_col_idcs_offsets.HostWrite();
|
||||
block_off_diag_row_idcs_offsets.SetSize(num_offdiagonal_blocks + 1);
|
||||
block_off_diag_row_idcs_offsets.HostWrite();
|
||||
block_off_diag_col_offsets.SetSize(num_offdiagonal_blocks);
|
||||
block_off_diag_col_offsets.HostWrite();
|
||||
block_off_diag_widths.SetSize(num_offdiagonal_blocks);
|
||||
block_off_diag_widths.HostWrite();
|
||||
pack_col_idcs.SetSize(send_len);
|
||||
// memory manager doesn't appear to have a graceful fallback for
|
||||
// HOST_PINNED if not built with CUDA or HIP
|
||||
#if defined(MFEM_USE_CUDA) or defined(MFEM_USE_HIP)
|
||||
xghost_send.SetSize(send_len * fespace->GetVDim(),
|
||||
Device::GetGPUAwareMPI() ? MemoryType::DEFAULT
|
||||
: MemoryType::HOST_PINNED);
|
||||
xghost_recv.SetSize(recv_len * fespace->GetVDim(),
|
||||
Device::GetGPUAwareMPI() ? MemoryType::DEFAULT
|
||||
: MemoryType::HOST_PINNED);
|
||||
#else
|
||||
xghost_send.SetSize(send_len * fespace->GetVDim());
|
||||
xghost_recv.SetSize(recv_len * fespace->GetVDim());
|
||||
#endif
|
||||
send_permutations.SetSize(send_len);
|
||||
send_segment_idcs.SetSize(send_len);
|
||||
block_offsets.SetSize(num_diagonal_blocks);
|
||||
block_offsets.HostWrite();
|
||||
off_diag_block_offsets.SetSize(num_offdiagonal_blocks);
|
||||
off_diag_block_offsets.HostWrite();
|
||||
int geom_offsets[Geometry::NumGeom];
|
||||
real_t *bs_ptr;
|
||||
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
block_storage.SetSize(total_size);
|
||||
bs_ptr = block_storage.HostWrite();
|
||||
// compute block data later
|
||||
}
|
||||
else
|
||||
{
|
||||
// compression scheme:
|
||||
// block_offsets is the start of each block, potentially repeated
|
||||
// only need to store localR for used shapes
|
||||
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
|
||||
|
||||
int size = 0;
|
||||
for (int i = 0; i < elem_geoms.Size(); ++i)
|
||||
{
|
||||
fespace->GetLocalDerefinementMatrices(elem_geoms[i],
|
||||
localR[elem_geoms[i]]);
|
||||
geom_offsets[elem_geoms[i]] = size;
|
||||
size += localR[elem_geoms[i]].TotalSize();
|
||||
}
|
||||
block_storage.SetSize(size);
|
||||
bs_ptr = block_storage.HostWrite();
|
||||
// copy blocks into block_storage
|
||||
for (int i = 0; i < elem_geoms.Size(); ++i)
|
||||
{
|
||||
std::copy(localR[elem_geoms[i]].Data(),
|
||||
localR[elem_geoms[i]].Data()
|
||||
+ localR[elem_geoms[i]].TotalSize(),
|
||||
bs_ptr);
|
||||
bs_ptr += localR[elem_geoms[i]].TotalSize();
|
||||
}
|
||||
}
|
||||
|
||||
// second pass:
|
||||
// - initialize buffers
|
||||
|
||||
{
|
||||
auto ptr = send_permutations.HostWrite();
|
||||
auto ptr2 = send_segment_idcs.HostWrite();
|
||||
int i = 0;
|
||||
for (auto &v : to_send)
|
||||
{
|
||||
ptr = std::copy(v.second.begin(), v.second.end(), ptr);
|
||||
for (size_t idx = 0; idx < v.second.size(); ++idx)
|
||||
{
|
||||
*ptr2 = i;
|
||||
++ptr2;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
block_row_idcs_offsets[0] = 0;
|
||||
block_col_idcs_offsets[0] = 0;
|
||||
block_off_diag_row_idcs_offsets[0] = 0;
|
||||
Array<int> mark(fespace->GetNDofs());
|
||||
mark = 0;
|
||||
{
|
||||
int idx = 0;
|
||||
recv_segments[0] = 0;
|
||||
for (auto &v : od_seg_lens)
|
||||
{
|
||||
recv_ranks[idx] = v.first;
|
||||
recv_segments[idx + 1] = recv_segments[idx] + v.second;
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
// key: index into dtrans.embeddings
|
||||
// value: off-diagonal block offset, od_ridx, seg id
|
||||
std::unordered_map<int, std::array<int, 3>> ks_map;
|
||||
{
|
||||
int od_ridx = 0;
|
||||
int seg_id = 0;
|
||||
for (auto &v1 : od_ks)
|
||||
{
|
||||
for (auto k : v1.second)
|
||||
{
|
||||
auto &tmp = ks_map[k];
|
||||
tmp[0] = ks_map.size() - 1;
|
||||
tmp[1] = od_ridx;
|
||||
tmp[2] = seg_id;
|
||||
od_ridx += get_ldofs(k);
|
||||
}
|
||||
++seg_id;
|
||||
}
|
||||
}
|
||||
int diag_idx = 0;
|
||||
int var_offset = 0;
|
||||
int ridx = 0;
|
||||
int cidx = 0;
|
||||
// can't break this up into separate diagonals/off-diagonals loops because
|
||||
// of mark
|
||||
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
if (emb.parent < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int fine_rank = old_ranks[k];
|
||||
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
if (coarse_rank == MyRank)
|
||||
{
|
||||
// either diagonal or off-diagonal
|
||||
Geometry::Type geom =
|
||||
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
const FiniteElement *fe = fespace->GetFE(emb.parent);
|
||||
const DenseTensor &pmats = dtrans.point_matrices[geom];
|
||||
const int ldof = fe->GetDof();
|
||||
|
||||
IsoparametricTransformation isotr;
|
||||
isotr.SetIdentityTransformation(geom);
|
||||
|
||||
localRVO.SetSize(ldof, ldof);
|
||||
isotr.SetPointMat(pmats(emb.matrix));
|
||||
// Local restriction is size ldofxldof assuming that the parent
|
||||
// and child are of same polynomial order.
|
||||
fe->GetLocalRestriction(isotr, localRVO);
|
||||
// copy block
|
||||
auto s = localRVO.Height() * localRVO.Width();
|
||||
std::copy(localRVO.Data(), localRVO.Data() + s, bs_ptr);
|
||||
bs_ptr += s;
|
||||
}
|
||||
DenseMatrix &lR =
|
||||
fespace->IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
|
||||
max_rows = std::max(lR.Height(), max_rows);
|
||||
auto size = lR.Height() * lR.Width();
|
||||
fespace->elem_dof->GetRow(emb.parent, dofs);
|
||||
if (fine_rank == MyRank)
|
||||
{
|
||||
// diagonal
|
||||
old_elem_dof->GetRow(k, old_dofs);
|
||||
MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
|
||||
"Parent and child must have same #dofs.");
|
||||
block_row_idcs_offsets[diag_idx + 1] =
|
||||
block_row_idcs_offsets[diag_idx] + lR.Height();
|
||||
block_col_idcs_offsets[diag_idx + 1] =
|
||||
block_col_idcs_offsets[diag_idx] + lR.Width();
|
||||
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
block_offsets[diag_idx] = var_offset;
|
||||
var_offset += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
block_offsets[diag_idx] = geom_offsets[geom] + size * emb.matrix;
|
||||
}
|
||||
for (int i = 0; i < lR.Height(); ++i, ++ridx)
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0)))
|
||||
{
|
||||
row_idcs[ridx] = INT_MAX;
|
||||
continue;
|
||||
}
|
||||
int r = dofs[i];
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
row_idcs[ridx] = r;
|
||||
mark[m] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
row_idcs[ridx] = INT_MAX;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < lR.Width(); ++i, ++cidx)
|
||||
{
|
||||
col_idcs[cidx] = old_dofs[i];
|
||||
}
|
||||
++diag_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
// off-diagonal
|
||||
auto &tmp = ks_map.at(k);
|
||||
auto od_idx = tmp[0];
|
||||
auto od_ridx = tmp[1];
|
||||
block_off_diag_row_idcs_offsets[od_idx + 1] =
|
||||
block_off_diag_row_idcs_offsets[od_idx] + lR.Height();
|
||||
block_off_diag_col_offsets[od_idx] = od_ridx;
|
||||
block_off_diag_widths[od_idx] = lR.Width();
|
||||
recv_segment_idcs[od_idx] = tmp[2];
|
||||
|
||||
if (fespace->IsVariableOrder())
|
||||
{
|
||||
off_diag_block_offsets[od_idx] = var_offset;
|
||||
var_offset += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
off_diag_block_offsets[od_idx] =
|
||||
geom_offsets[geom] + size * emb.matrix;
|
||||
}
|
||||
for (int i = 0; i < lR.Height(); ++i, ++od_ridx)
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0)))
|
||||
{
|
||||
row_off_diag_idcs[od_ridx] = INT_MAX;
|
||||
continue;
|
||||
}
|
||||
int r = dofs[i];
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
row_off_diag_idcs[od_ridx] = r;
|
||||
mark[m] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
row_off_diag_idcs[od_ridx] = INT_MAX;
|
||||
}
|
||||
}
|
||||
++od_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if not using GPU, set max_rows/max_cols to zero
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
max_rows = std::min(max_rows, max_team_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_rows = 1;
|
||||
}
|
||||
requests.reserve(recv_ranks.Size() + send_ranks.Size());
|
||||
}
|
||||
} // namespace mfem
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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_PDEREFMAT_OP
|
||||
#define MFEM_PDEREFMAT_OP
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pfespace.hpp"
|
||||
|
||||
#include "kernel_dispatch.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
struct ParDerefineMatrixOp : public Operator
|
||||
{
|
||||
ParFiniteElementSpace *fespace;
|
||||
/// offsets into block_storage for diagonal
|
||||
Array<int> block_offsets;
|
||||
/// offsets into row_idcs for diagonal
|
||||
Array<int> block_row_idcs_offsets;
|
||||
/// offsets into col_idcs for diagonal
|
||||
Array<int> block_col_idcs_offsets;
|
||||
|
||||
/// offsets into block_storage for off-diagonal
|
||||
Array<int> off_diag_block_offsets;
|
||||
/// offsets into row_idcs for off-diagonal
|
||||
Array<int> block_off_diag_row_idcs_offsets;
|
||||
Array<int> block_off_diag_col_offsets;
|
||||
Array<int> block_off_diag_widths;
|
||||
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
|
||||
/// negative means the row data should be negated.
|
||||
/// only for diagonal blocks
|
||||
Array<int> row_idcs;
|
||||
/// mapping for col dofs, negative means the col data should be negated.
|
||||
/// only for diagonal blocks
|
||||
Array<int> col_idcs;
|
||||
|
||||
Array<int> pack_col_idcs;
|
||||
|
||||
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
|
||||
/// negative means the row data should be negated.
|
||||
/// only for off-diagonal blocks
|
||||
Array<int> row_off_diag_idcs;
|
||||
/// dense block matrices which can be reused to construct the full matrix
|
||||
/// operation. These are stored contiguously and blocks have no restrictions
|
||||
/// on shape (can be rectangle and differ from block to block).
|
||||
/// This is only for the diagonal block.
|
||||
Vector block_storage;
|
||||
/// maximum height of any block in block_storage for GPU
|
||||
/// parallelization, or 1 for CPU runs.
|
||||
int max_rows;
|
||||
|
||||
/// quasi Ordering::byNODES, broken into sections by ranks we need to send
|
||||
/// the data to
|
||||
mutable Vector xghost_send;
|
||||
/// quasi Ordering::byNODES, broken into sections by ranks we received
|
||||
/// the data from
|
||||
mutable Vector xghost_recv;
|
||||
/// maps off-diagonal k to segment
|
||||
Array<int> recv_segment_idcs;
|
||||
/// cumulative count of dofs which will be received from other ranks
|
||||
Array<int> recv_segments;
|
||||
/// Source rank of each recv segment
|
||||
Array<int> recv_ranks;
|
||||
/// What send segment each entry in send_permutations corresponds to
|
||||
Array<int> send_segment_idcs;
|
||||
/// cumulative count of dofs which will be sent to other ranks
|
||||
Array<int> send_segments;
|
||||
/// Destination rank of each send segment
|
||||
Array<int> send_ranks;
|
||||
/// how to permute/sign change values from our local x to send to other ranks
|
||||
Array<int> send_permutations;
|
||||
/// internal buffer for MPI requests
|
||||
mutable std::vector<MPI_Request> requests;
|
||||
|
||||
using MultKernelType = void (*)(const ParDerefineMatrixOp &, const Vector &,
|
||||
Vector &);
|
||||
/// template args: ordering, atomic
|
||||
MFEM_REGISTER_KERNELS(MultKernel, MultKernelType, (Ordering::Type, bool));
|
||||
|
||||
struct Kernels
|
||||
{
|
||||
Kernels();
|
||||
};
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
ParDerefineMatrixOp(ParFiniteElementSpace &fespace_, int old_ndofs,
|
||||
const Table *old_elem_dof, const Table *old_elem_fos);
|
||||
};
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+51
-33
@@ -22,12 +22,13 @@
|
||||
#include "../mesh/mesh_headers.hpp"
|
||||
#include "../general/binaryio.hpp"
|
||||
|
||||
#include "pderefmat_op.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
ParFiniteElementSpace::ParFiniteElementSpace(
|
||||
const ParFiniteElementSpace &orig, ParMesh *pmesh,
|
||||
const FiniteElementCollection *fec)
|
||||
@@ -4487,13 +4488,6 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
return M;
|
||||
}
|
||||
|
||||
|
||||
struct DerefDofMessage
|
||||
{
|
||||
std::vector<HYPRE_BigInt> dofs;
|
||||
MPI_Request request;
|
||||
};
|
||||
|
||||
HypreParMatrix*
|
||||
ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof,
|
||||
@@ -4536,7 +4530,13 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
old_pncmesh->GetDerefinementTransforms();
|
||||
const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
|
||||
|
||||
std::map<int, DerefDofMessage> messages;
|
||||
// key: other rank
|
||||
// value: send or recieve buffer
|
||||
std::map<int, std::vector<HYPRE_BigInt>> to_send;
|
||||
std::map<int, std::vector<HYPRE_BigInt>> to_recv;
|
||||
// key: index into dtrans.embeddings
|
||||
// value: [start, stop]
|
||||
std::unordered_map<int, std::array<size_t, 2>> recv_messages;
|
||||
|
||||
HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
|
||||
? old_dof_offsets[0] : old_dof_offsets[MyRank];
|
||||
@@ -4556,30 +4556,46 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
old_elem_dof->GetRow(k, dofs);
|
||||
DofsToVDofs(dofs, old_ndofs);
|
||||
|
||||
DerefDofMessage &msg = messages[k];
|
||||
msg.dofs.resize(dofs.Size());
|
||||
std::vector<HYPRE_BigInt>& send_buf = to_send[coarse_rank];
|
||||
auto pos = send_buf.size();
|
||||
send_buf.resize(pos + dofs.Size());
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
msg.dofs[i] = old_offset + dofs[i];
|
||||
send_buf[pos + i] = old_offset + dofs[i];
|
||||
}
|
||||
|
||||
MPI_Isend(&msg.dofs[0], static_cast<int>(msg.dofs.size()), HYPRE_MPI_BIG_INT,
|
||||
coarse_rank, 291, MyComm, &msg.request);
|
||||
}
|
||||
else if (coarse_rank == MyRank && fine_rank != MyRank)
|
||||
{
|
||||
MFEM_ASSERT(emb.parent >= 0, "");
|
||||
Geometry::Type geom = mesh->GetElementBaseGeometry(emb.parent);
|
||||
|
||||
DerefDofMessage &msg = messages[k];
|
||||
msg.dofs.resize(ldof[geom]*vdim);
|
||||
|
||||
MPI_Irecv(&msg.dofs[0], ldof[geom]*vdim, HYPRE_MPI_BIG_INT,
|
||||
fine_rank, 291, MyComm, &msg.request);
|
||||
std::vector<HYPRE_BigInt>& recv_buf = to_recv[fine_rank];
|
||||
auto& msg = recv_messages[k];
|
||||
msg[0] = recv_buf.size();
|
||||
recv_buf.resize(recv_buf.size() + ldof[geom] * vdim);
|
||||
msg[1] = recv_buf.size();
|
||||
}
|
||||
// TODO: coalesce Isends/Irecvs to the same rank. Typically, on uniform
|
||||
// derefinement, there should be just one send to MyRank-1 and one recv
|
||||
// from MyRank+1
|
||||
}
|
||||
|
||||
// assume embedding orders are consistent (i.e. what we expect to receive
|
||||
// first from a given rank is sent first, etc.)
|
||||
std::vector<MPI_Request> requests;
|
||||
requests.reserve(to_send.size() + to_recv.size());
|
||||
// enqueue recvs
|
||||
for (auto &v : to_recv)
|
||||
{
|
||||
requests.emplace_back();
|
||||
MPI_Irecv(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
|
||||
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA, MyComm,
|
||||
&requests.back());
|
||||
}
|
||||
// enqueue sends
|
||||
for (auto &v : to_send)
|
||||
{
|
||||
requests.emplace_back();
|
||||
MPI_Isend(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
|
||||
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA, MyComm,
|
||||
&requests.back());
|
||||
}
|
||||
|
||||
DenseTensor localR[Geometry::NumGeom];
|
||||
@@ -4637,10 +4653,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
diag->Finalize();
|
||||
|
||||
// wait for all sends/receives to complete
|
||||
for (auto it = messages.begin(); it != messages.end(); ++it)
|
||||
{
|
||||
MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
|
||||
}
|
||||
MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
|
||||
|
||||
// create the off-diagonal part of the derefinement matrix
|
||||
SparseMatrix *offd = new SparseMatrix(ndofs*vdim, 1);
|
||||
@@ -4661,13 +4674,14 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
|
||||
elem_dof->GetRow(emb.parent, dofs);
|
||||
|
||||
DerefDofMessage &msg = messages[k];
|
||||
MFEM_ASSERT(msg.dofs.size(), "");
|
||||
auto& odofs = to_recv.at(fine_rank);
|
||||
auto &msg = recv_messages[k];
|
||||
MFEM_ASSERT(msg[1] > msg[0], "");
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_ASSERT(ldof[geom], "");
|
||||
HYPRE_BigInt* remote_dofs = &msg.dofs[vd*ldof[geom]];
|
||||
HYPRE_BigInt *remote_dofs = odofs.data() + msg[0] + vd * ldof[geom];
|
||||
|
||||
for (int i = 0; i < lR.Height(); i++)
|
||||
{
|
||||
@@ -4694,7 +4708,6 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
}
|
||||
}
|
||||
|
||||
messages.clear();
|
||||
offd->Finalize(0);
|
||||
offd->SetWidth(static_cast<int>(col_map.size()));
|
||||
|
||||
@@ -4946,8 +4959,13 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
|
||||
case Mesh::DEREFINE:
|
||||
{
|
||||
#if 0
|
||||
Th.Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
|
||||
old_elem_fos));
|
||||
#else
|
||||
Th.Reset(new ParDerefineMatrixOp(*this, old_ndofs, old_elem_dof,
|
||||
old_elem_fos));
|
||||
#endif
|
||||
if (Nonconforming())
|
||||
{
|
||||
Th.SetOperatorOwner(false);
|
||||
@@ -5259,7 +5277,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();
|
||||
@@ -5288,7 +5306,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);
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
struct ParDerefineMatrixOp;
|
||||
|
||||
/// Abstract parallel finite element space.
|
||||
class ParFiniteElementSpace : public FiniteElementSpace
|
||||
{
|
||||
friend struct ParDerefineMatrixOp;
|
||||
private:
|
||||
/// MPI data.
|
||||
MPI_Comm MyComm;
|
||||
@@ -577,7 +580,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 +637,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); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1406,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
|
||||
|
||||
@@ -581,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. */
|
||||
|
||||
+10
-10
@@ -214,16 +214,16 @@ public:
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, Vector &values)
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx];
|
||||
const int sl_size = qspace->offsets[idx+1] - s_offset;
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
values.MakeRef(*this, vdim*s_offset, vdim*sl_size);
|
||||
}
|
||||
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, Vector &values) const
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx];
|
||||
const int sl_size = qspace->offsets[idx+1] - s_offset;
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
values.SetSize(vdim*sl_size);
|
||||
values.HostWrite();
|
||||
const real_t *q = HostRead() + vdim*s_offset;
|
||||
@@ -236,14 +236,14 @@ inline void QuadratureFunction::GetValues(
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, const int ip_num, Vector &values)
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx] * vdim + ip_num * vdim;
|
||||
const int s_offset = qspace->Offset(idx) * vdim + ip_num * vdim;
|
||||
values.MakeRef(*this, s_offset, vdim);
|
||||
}
|
||||
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, const int ip_num, Vector &values) const
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx] * vdim + ip_num * vdim;
|
||||
const int s_offset = qspace->Offset(idx) * vdim + ip_num * vdim;
|
||||
values.SetSize(vdim);
|
||||
values.HostWrite();
|
||||
const real_t *q = HostRead() + s_offset;
|
||||
@@ -256,8 +256,8 @@ inline void QuadratureFunction::GetValues(
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, DenseMatrix &values)
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx];
|
||||
const int sl_size = qspace->offsets[idx+1] - s_offset;
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
// Make the values matrix memory an alias of the quadrature function memory
|
||||
Memory<real_t> &values_mem = values.GetMemory();
|
||||
values_mem.Delete();
|
||||
@@ -268,8 +268,8 @@ inline void QuadratureFunction::GetValues(
|
||||
inline void QuadratureFunction::GetValues(
|
||||
int idx, DenseMatrix &values) const
|
||||
{
|
||||
const int s_offset = qspace->offsets[idx];
|
||||
const int sl_size = qspace->offsets[idx+1] - s_offset;
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
values.SetSize(vdim, sl_size);
|
||||
values.HostWrite();
|
||||
const real_t *q = HostRead() + vdim*s_offset;
|
||||
|
||||
+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) = std::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
|
||||
+77
-58
@@ -17,8 +17,9 @@ namespace mfem
|
||||
{
|
||||
|
||||
QuadratureSpaceBase::QuadratureSpaceBase(Mesh &mesh_, Geometry::Type geom,
|
||||
const IntegrationRule &ir)
|
||||
: mesh(mesh_), order(ir.GetOrder())
|
||||
const IntegrationRule &ir,
|
||||
QSpaceStorage storage)
|
||||
: mesh(mesh_), order(ir.GetOrder()), storage(storage)
|
||||
{
|
||||
for (int g = 0; g < Geometry::NumGeom; g++)
|
||||
{
|
||||
@@ -96,27 +97,37 @@ void QuadratureSpaceBase::Integrate(VectorCoefficient &coeff,
|
||||
|
||||
void QuadratureSpace::ConstructOffsets()
|
||||
{
|
||||
const int num_elem = mesh.GetNE();
|
||||
offsets.SetSize(num_elem + 1);
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_elem; i++)
|
||||
const int num_elem = ne;
|
||||
|
||||
if (storage == QSpaceStorage::COMPRESSED &&
|
||||
mesh.GetNumGeometries(mesh.Dimension()) == 1)
|
||||
{
|
||||
offsets[i] = offset;
|
||||
int geom = mesh.GetElementBaseGeometry(i);
|
||||
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule.");
|
||||
offset += int_rule[geom]->GetNPoints();
|
||||
Array<Geometry::Type> geoms;
|
||||
mesh.GetGeometries(mesh.Dimension(), geoms);
|
||||
offsets.SetSize(1);
|
||||
offsets.HostWrite();
|
||||
offsets[0] = int_rule[geoms[0]]->GetNPoints();
|
||||
size = num_elem * offsets[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
offsets.SetSize(num_elem + 1);
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_elem; i++)
|
||||
{
|
||||
offsets[i] = offset;
|
||||
const Geometry::Type geom = mesh.GetElementBaseGeometry(i);
|
||||
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule.");
|
||||
offset += int_rule[geom]->GetNPoints();
|
||||
}
|
||||
offsets[num_elem] = offset;
|
||||
size = offsets.Last();
|
||||
}
|
||||
offsets[num_elem] = size = offset;
|
||||
}
|
||||
|
||||
void QuadratureSpace::Construct()
|
||||
{
|
||||
ConstructIntRules(mesh.Dimension());
|
||||
ConstructOffsets();
|
||||
}
|
||||
|
||||
QuadratureSpace::QuadratureSpace(Mesh *mesh_, std::istream &in)
|
||||
: QuadratureSpaceBase(*mesh_)
|
||||
QuadratureSpace::QuadratureSpace(Mesh *mesh_, std::istream &in,
|
||||
QSpaceStorage storage)
|
||||
: QuadratureSpaceBase(*mesh_, 0, storage)
|
||||
{
|
||||
const char *msg = "invalid input stream";
|
||||
std::string ident;
|
||||
@@ -135,15 +146,24 @@ QuadratureSpace::QuadratureSpace(Mesh *mesh_, std::istream &in)
|
||||
return;
|
||||
}
|
||||
|
||||
Construct();
|
||||
ne = mesh.GetNE();
|
||||
ConstructIntRules(mesh.Dimension());
|
||||
}
|
||||
|
||||
QuadratureSpace::QuadratureSpace(Mesh &mesh_, const IntegrationRule &ir)
|
||||
: QuadratureSpaceBase(mesh_, mesh_.GetTypicalElementGeometry(), ir)
|
||||
QuadratureSpace::QuadratureSpace(Mesh *mesh_, int order_, QSpaceStorage storage)
|
||||
: QuadratureSpaceBase(*mesh_, order_, storage)
|
||||
{
|
||||
ne = mesh.GetNE();
|
||||
ConstructIntRules(mesh.Dimension());
|
||||
}
|
||||
|
||||
QuadratureSpace::QuadratureSpace(Mesh &mesh_, const IntegrationRule &ir,
|
||||
QSpaceStorage storage)
|
||||
: QuadratureSpaceBase(mesh_, mesh_.GetTypicalElementGeometry(), ir, storage)
|
||||
{
|
||||
MFEM_VERIFY(mesh.GetNumGeometries(mesh.Dimension()) <= 1,
|
||||
"Constructor not valid for mixed meshes");
|
||||
ConstructOffsets();
|
||||
ne = mesh.GetNE();
|
||||
}
|
||||
|
||||
void QuadratureSpace::Save(std::ostream &os) const
|
||||
@@ -165,54 +185,53 @@ const Vector &QuadratureSpace::GetGeometricFactorWeights() const
|
||||
}
|
||||
|
||||
FaceQuadratureSpace::FaceQuadratureSpace(Mesh &mesh_, int order_,
|
||||
FaceType face_type_)
|
||||
: QuadratureSpaceBase(mesh_, order_),
|
||||
face_type(face_type_),
|
||||
num_faces(mesh.GetNFbyType(face_type))
|
||||
FaceType face_type_,
|
||||
QSpaceStorage storage)
|
||||
: QuadratureSpaceBase(mesh_, order_, storage), face_type(face_type_),
|
||||
face_indices(mesh.GetFaceIndices(face_type_)),
|
||||
face_indices_inv(mesh.GetInvFaceIndices(face_type_))
|
||||
{
|
||||
Construct();
|
||||
ne = face_indices.Size();
|
||||
ConstructIntRules(mesh.Dimension() - 1);
|
||||
}
|
||||
|
||||
FaceQuadratureSpace::FaceQuadratureSpace(Mesh &mesh_, const IntegrationRule &ir,
|
||||
FaceType face_type_)
|
||||
: QuadratureSpaceBase(mesh_, mesh_.GetTypicalFaceGeometry(), ir),
|
||||
face_type(face_type_),
|
||||
num_faces(mesh.GetNFbyType(face_type))
|
||||
FaceType face_type_,
|
||||
QSpaceStorage storage)
|
||||
: QuadratureSpaceBase(mesh_, mesh_.GetTypicalFaceGeometry(), ir, storage),
|
||||
face_type(face_type_), face_indices(mesh.GetFaceIndices(face_type_)),
|
||||
face_indices_inv(mesh.GetInvFaceIndices(face_type_))
|
||||
{
|
||||
MFEM_VERIFY(mesh.GetNumGeometries(mesh.Dimension() - 1) <= 1,
|
||||
"Constructor not valid for mixed meshes");
|
||||
ConstructOffsets();
|
||||
ne = face_indices.Size();
|
||||
}
|
||||
|
||||
void FaceQuadratureSpace::ConstructOffsets()
|
||||
{
|
||||
face_indices.SetSize(num_faces);
|
||||
offsets.SetSize(num_faces + 1);
|
||||
int offset = 0;
|
||||
int f_idx = 0;
|
||||
for (int i = 0; i < mesh.GetNumFacesWithGhost(); i++)
|
||||
if (storage == QSpaceStorage::COMPRESSED &&
|
||||
mesh.GetNumGeometries(mesh.Dimension() - 1) == 1)
|
||||
{
|
||||
const Mesh::FaceInformation face = mesh.GetFaceInformation(i);
|
||||
if (face.IsNonconformingCoarse() || !face.IsOfFaceType(face_type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
face_indices[f_idx] = i;
|
||||
face_indices_inv[i] = f_idx;
|
||||
offsets[f_idx] = offset;
|
||||
Geometry::Type geom = mesh.GetFaceGeometry(i);
|
||||
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule");
|
||||
offset += int_rule[geom]->GetNPoints();
|
||||
|
||||
f_idx++;
|
||||
Array<Geometry::Type> geoms;
|
||||
mesh.GetGeometries(mesh.Dimension() - 1, geoms);
|
||||
offsets.SetSize(1);
|
||||
offsets.HostWrite();
|
||||
offsets[0] = int_rule[geoms[0]]->GetNPoints();
|
||||
size = ne * offsets[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
offsets.SetSize(face_indices.Size() + 1);
|
||||
int offset = 0;
|
||||
for (int i = 0; i < mesh.GetNFbyType(face_type); ++i)
|
||||
{
|
||||
offsets[i] = offset;
|
||||
Geometry::Type geom = mesh.GetFaceGeometry(face_indices[i]);
|
||||
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule");
|
||||
offset += int_rule[geom]->GetNPoints();
|
||||
}
|
||||
offsets[face_indices.Size()] = size = offset;
|
||||
}
|
||||
offsets[num_faces] = size = offset;
|
||||
}
|
||||
|
||||
void FaceQuadratureSpace::Construct()
|
||||
{
|
||||
ConstructIntRules(mesh.Dimension() - 1);
|
||||
ConstructOffsets();
|
||||
}
|
||||
|
||||
int FaceQuadratureSpace::GetPermutedIndex(int idx, int iq) const
|
||||
|
||||
+80
-25
@@ -19,35 +19,49 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
enum class QSpaceStorage
|
||||
{
|
||||
FULL,
|
||||
COMPRESSED
|
||||
};
|
||||
|
||||
/// Abstract base class for QuadratureSpace and FaceQuadratureSpace.
|
||||
/** This class represents the storage layout for QuadratureFunction%s, that may
|
||||
be defined either on mesh elements or mesh faces. */
|
||||
class QuadratureSpaceBase
|
||||
{
|
||||
protected:
|
||||
friend class QuadratureFunction; // Uses the offsets.
|
||||
|
||||
Mesh &mesh; ///< The underlying mesh.
|
||||
int order; ///< The order of integration rule.
|
||||
int size; ///< Total number of quadrature points.
|
||||
int size = -1; ///< Total number of quadrature points. -1 indicates
|
||||
///< offsets/size not computed yet.
|
||||
int ne; ///< Actual number of entities
|
||||
mutable Vector weights; ///< Integration weights.
|
||||
mutable long nodes_sequence = 0; ///< Nodes counter for cache invalidation.
|
||||
|
||||
/// @brief Entity quadrature point offset array, of size num_entities + 1.
|
||||
QSpaceStorage storage;
|
||||
|
||||
/// @brief Entity quadrature point offset array.
|
||||
///
|
||||
/// Supports a constant compression scheme for meshes which have a single
|
||||
/// geometry type. When compressed, will have a single value. The true offset
|
||||
/// can be computed as i * offsets[0], where i is the entity index. Otherwise
|
||||
/// has size num_entities + 1. Lazily constructed.
|
||||
///
|
||||
/// The quadrature point values for entity i are stored in the indices between
|
||||
/// offsets[i] and offsets[i+1].
|
||||
Array<int> offsets;
|
||||
/// The quadrature rules used for each geometry type.
|
||||
const IntegrationRule *int_rule[Geometry::NumGeom];
|
||||
|
||||
/// Protected constructor. Used by derived classes.
|
||||
QuadratureSpaceBase(Mesh &mesh_, int order_ = 0)
|
||||
: mesh(mesh_), order(order_) { }
|
||||
QuadratureSpaceBase(Mesh &mesh_, int order_ = 0,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED)
|
||||
: mesh(mesh_), order(order_), storage(storage)
|
||||
{}
|
||||
|
||||
/// Protected constructor. Used by derived classes.
|
||||
QuadratureSpaceBase(Mesh &mesh_, Geometry::Type geom,
|
||||
const IntegrationRule &ir);
|
||||
const IntegrationRule &ir,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// Fill the @ref int_rule array for each geometry type using @ref order.
|
||||
void ConstructIntRules(int dim);
|
||||
@@ -58,15 +72,55 @@ protected:
|
||||
/// Compute the integration weights.
|
||||
void ConstructWeights() const;
|
||||
|
||||
virtual void ConstructOffsets() = 0;
|
||||
|
||||
public:
|
||||
QSpaceStorage StorageType() const { return storage; }
|
||||
|
||||
/// @brief Gets the offset for a given entity @a idx.
|
||||
///
|
||||
/// The quadrature point values for entity i are stored in the indices
|
||||
/// between Offset(i) and Offset(i+1)
|
||||
int Offset(int idx) const
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
const_cast<QuadratureSpaceBase *>(this)->ConstructOffsets();
|
||||
}
|
||||
return (offsets.Size() == 1) ? (idx * offsets[0]) : offsets[idx];
|
||||
}
|
||||
|
||||
/// @brief Entity quadrature point offset array.
|
||||
///
|
||||
/// Supports a constant compression scheme for meshes which have a single
|
||||
/// geometry type. When compressed, will have a single value. The true offset
|
||||
/// can be computed as i * offsets[0], where i is the entity index. Otherwise
|
||||
/// has size num_entities + 1.
|
||||
///
|
||||
const Array<int> &Offsets() const
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
const_cast<QuadratureSpaceBase *>(this)->ConstructOffsets();
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
/// Return the total number of quadrature points.
|
||||
int GetSize() const { return size; }
|
||||
int GetSize() const
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
const_cast<QuadratureSpaceBase *>(this)->ConstructOffsets();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/// Return the order of the quadrature rule(s) used by all elements.
|
||||
int GetOrder() const { return order; }
|
||||
|
||||
/// Return the number of entities.
|
||||
int GetNE() const { return offsets.Size() - 1; }
|
||||
int GetNE() const { return ne; }
|
||||
|
||||
/// Returns the mesh.
|
||||
inline Mesh *GetMesh() const { return &mesh; }
|
||||
@@ -120,19 +174,20 @@ class QuadratureSpace : public QuadratureSpaceBase
|
||||
{
|
||||
protected:
|
||||
const Vector &GetGeometricFactorWeights() const override;
|
||||
void ConstructOffsets();
|
||||
void Construct();
|
||||
void ConstructOffsets() override;
|
||||
public:
|
||||
/// Create a QuadratureSpace based on the global rules from #IntRules.
|
||||
QuadratureSpace(Mesh *mesh_, int order_)
|
||||
: QuadratureSpaceBase(*mesh_, order_) { Construct(); }
|
||||
QuadratureSpace(Mesh *mesh_, int order_,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// @brief Create a QuadratureSpace with an IntegrationRule, valid only when
|
||||
/// the mesh has one element type.
|
||||
QuadratureSpace(Mesh &mesh_, const IntegrationRule &ir);
|
||||
QuadratureSpace(Mesh &mesh_, const IntegrationRule &ir,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// Read a QuadratureSpace from the stream @a in.
|
||||
QuadratureSpace(Mesh *mesh_, std::istream &in);
|
||||
QuadratureSpace(Mesh *mesh_, std::istream &in,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// Returns number of elements in the mesh.
|
||||
inline int GetNE() const { return mesh.GetNE(); }
|
||||
@@ -169,29 +224,29 @@ public:
|
||||
class FaceQuadratureSpace : public QuadratureSpaceBase
|
||||
{
|
||||
FaceType face_type; ///< Is the space defined on interior or boundary faces?
|
||||
const int num_faces; ///< Number of faces.
|
||||
|
||||
/// Map from boundary or interior face indices to mesh face indices.
|
||||
Array<int> face_indices;
|
||||
const Array<int> &face_indices;
|
||||
|
||||
/// Inverse of the map @a face_indices.
|
||||
std::unordered_map<int,int> face_indices_inv;
|
||||
const std::unordered_map<int,int> &face_indices_inv;
|
||||
|
||||
const Vector &GetGeometricFactorWeights() const override;
|
||||
void ConstructOffsets();
|
||||
void Construct();
|
||||
void ConstructOffsets() override;
|
||||
|
||||
public:
|
||||
/// Create a FaceQuadratureSpace based on the global rules from #IntRules.
|
||||
FaceQuadratureSpace(Mesh &mesh_, int order_, FaceType face_type_);
|
||||
FaceQuadratureSpace(Mesh &mesh_, int order_, FaceType face_type_,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// @brief Create a FaceQuadratureSpace with an IntegrationRule, valid only
|
||||
/// when the mesh has one type of face geometry.
|
||||
FaceQuadratureSpace(Mesh &mesh_, const IntegrationRule &ir,
|
||||
FaceType face_type_);
|
||||
FaceType face_type_,
|
||||
QSpaceStorage storage = QSpaceStorage::COMPRESSED);
|
||||
|
||||
/// Returns number of faces in the mesh.
|
||||
inline int GetNumFaces() const { return num_faces; }
|
||||
inline int GetNumFaces() const { return face_indices.Size(); }
|
||||
|
||||
/// Returns the face type (boundary or interior).
|
||||
FaceType GetFaceType() const { return face_type; }
|
||||
|
||||
@@ -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
|
||||
|
||||
+9
-27
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
+35
-1
@@ -211,6 +211,9 @@ public:
|
||||
/// Delete the first entry with value == 'el'.
|
||||
inline void DeleteFirst(const T &el);
|
||||
|
||||
/// Delete entries at @a indices, and resize.
|
||||
inline void DeleteAt(const Array<int> &indices);
|
||||
|
||||
/// Delete the whole array.
|
||||
inline void DeleteAll();
|
||||
|
||||
@@ -305,6 +308,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;
|
||||
|
||||
@@ -323,7 +329,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; }
|
||||
@@ -928,6 +938,30 @@ inline void Array<T>::DeleteFirst(const T &el)
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::DeleteAt(const Array<int> &indices)
|
||||
{
|
||||
// Make a copy of the indices, sorted.
|
||||
Array<int> sorted_indices(indices);
|
||||
sorted_indices.Sort();
|
||||
|
||||
int rm_count = 0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (rm_count < sorted_indices.Size() && i == sorted_indices[rm_count])
|
||||
{
|
||||
rm_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data[i-rm_count] = data[i]; // shift data rm_count
|
||||
}
|
||||
}
|
||||
|
||||
// Resize to remove tail
|
||||
size -= rm_count;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::DeleteAll()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
#include <cusparse.h>
|
||||
#include <library_types.h>
|
||||
#include <cuda_runtime.h>
|
||||
@@ -22,7 +22,7 @@
|
||||
#endif
|
||||
#include "cuda.hpp"
|
||||
|
||||
#ifdef MFEM_USE_HIP
|
||||
#if defined(MFEM_USE_HIP) && defined(__HIP__)
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
#include "hip.hpp"
|
||||
@@ -43,7 +43,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
#if !defined(MFEM_USE_CUDA_OR_HIP)
|
||||
#define MFEM_DEVICE
|
||||
#define MFEM_HOST
|
||||
#define MFEM_LAMBDA
|
||||
@@ -55,17 +55,18 @@
|
||||
#endif
|
||||
|
||||
#if !((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
|
||||
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
|
||||
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
|
||||
#define MFEM_SHARED
|
||||
#define MFEM_SYNC_THREAD
|
||||
#define MFEM_BLOCK_ID(k) 0
|
||||
#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
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 600
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 600)
|
||||
MFEM_DEVICE inline mfem::real_t atomicAdd(mfem::real_t *add, mfem::real_t val)
|
||||
{
|
||||
unsigned long long int *ptr = (unsigned long long int *) add;
|
||||
@@ -93,7 +94,7 @@ template <typename T>
|
||||
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
|
||||
{
|
||||
#if ((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
|
||||
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
|
||||
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
|
||||
return atomicAdd(&add,val);
|
||||
#else
|
||||
T old = add;
|
||||
|
||||
@@ -347,13 +347,23 @@ void GroupTopology::Swap(GroupTopology &other)
|
||||
mfem::Swap(group_mgroup, other.group_mgroup);
|
||||
}
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
// Initialize the static mpi_type for the specializations of MPITypeMap:
|
||||
const MPI_Datatype MPITypeMap<bool>::mpi_type = MFEM_MPI_CXX_BOOL;
|
||||
const MPI_Datatype MPITypeMap<char>::mpi_type = MPI_CHAR;
|
||||
const MPI_Datatype MPITypeMap<unsigned char>::mpi_type = MPI_UNSIGNED_CHAR;
|
||||
const MPI_Datatype MPITypeMap<short>::mpi_type = MPI_SHORT;
|
||||
const MPI_Datatype MPITypeMap<unsigned short>::mpi_type = MPI_UNSIGNED_SHORT;
|
||||
const MPI_Datatype MPITypeMap<int>::mpi_type = MPI_INT;
|
||||
const MPI_Datatype MPITypeMap<unsigned int>::mpi_type = MPI_UNSIGNED;
|
||||
const MPI_Datatype MPITypeMap<long>::mpi_type = MPI_LONG;
|
||||
const MPI_Datatype MPITypeMap<unsigned long>::mpi_type = MPI_UNSIGNED_LONG;
|
||||
const MPI_Datatype MPITypeMap<long long>::mpi_type = MPI_LONG_LONG;
|
||||
const MPI_Datatype MPITypeMap<unsigned long long>::mpi_type =
|
||||
MPI_UNSIGNED_LONG_LONG;
|
||||
const MPI_Datatype MPITypeMap<float>::mpi_type = MPI_FLOAT;
|
||||
const MPI_Datatype MPITypeMap<double>::mpi_type = MPI_DOUBLE;
|
||||
const MPI_Datatype MPITypeMap<int64_t>::mpi_type = MPI_INT64_T;
|
||||
const MPI_Datatype MPITypeMap<uint64_t>::mpi_type = MPI_UINT64_T;
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
GroupCommunicator::GroupCommunicator(const GroupTopology >, Mode m)
|
||||
: gtopo(gt), mode(m)
|
||||
|
||||
+56
-17
@@ -425,16 +425,24 @@ public:
|
||||
~GroupCommunicator();
|
||||
};
|
||||
|
||||
/// General MPI message tags used by MFEM
|
||||
enum MessageTag
|
||||
{
|
||||
DEREFINEMENT_MATRIX_CONSTRUCTION_DATA =
|
||||
291, /// ParFiniteElementSpace ParallelDerefinementMatrix and
|
||||
/// ParDerefineMatrixOp
|
||||
};
|
||||
|
||||
enum VarMessageTag
|
||||
{
|
||||
NEIGHBOR_ELEMENT_RANK_VM, ///< NeighborElementRankMessage
|
||||
NEIGHBOR_ORDER_VM, ///< NeighborOrderMessage
|
||||
NEIGHBOR_DEREFINEMENT_VM, ///< NeighborDerefinementMessage
|
||||
NEIGHBOR_REFINEMENT_VM, ///< NeighborRefinementMessage
|
||||
NEIGHBOR_PREFINEMENT_VM, ///< NeighborPRefinementMessage
|
||||
NEIGHBOR_ROW_VM, ///< NeighborRowMessage
|
||||
REBALANCE_VM, ///< RebalanceMessage
|
||||
REBALANCE_DOF_VM ///< RebalanceDofMessage
|
||||
NEIGHBOR_ELEMENT_RANK_VM, ///< NeighborElementRankMessage
|
||||
NEIGHBOR_ORDER_VM, ///< NeighborOrderMessage
|
||||
NEIGHBOR_DEREFINEMENT_VM, ///< NeighborDerefinementMessage
|
||||
NEIGHBOR_REFINEMENT_VM, ///< NeighborRefinementMessage
|
||||
NEIGHBOR_PREFINEMENT_VM, ///< NeighborPRefinementMessage
|
||||
NEIGHBOR_ROW_VM, ///< NeighborRowMessage
|
||||
REBALANCE_VM, ///< RebalanceMessage
|
||||
REBALANCE_DOF_VM, ///< RebalanceDofMessage
|
||||
};
|
||||
|
||||
/// \brief Variable-length MPI message containing unspecific binary data.
|
||||
@@ -599,10 +607,50 @@ protected:
|
||||
template <typename Type> struct MPITypeMap;
|
||||
|
||||
// Specializations of MPITypeMap; mpi_type initialized in communication.cpp:
|
||||
template<> struct MPITypeMap<bool>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<char>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<unsigned char>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<short>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<unsigned short>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<int>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<unsigned int>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<long>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<unsigned long>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<long long>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<unsigned long long>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<double>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
@@ -611,15 +659,6 @@ template<> struct MPITypeMap<float>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<int64_t>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
template<> struct MPITypeMap<uint64_t>
|
||||
{
|
||||
static MFEM_EXPORT const MPI_Datatype mpi_type;
|
||||
};
|
||||
|
||||
|
||||
/** Reorder MPI ranks to follow the Z-curve within the physical machine topology
|
||||
(provided that functions to query physical node coordinates are available).
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
|
||||
const char *file, int line)
|
||||
{
|
||||
mfem::err << "\n\nCUDA error: (" << expr << ") failed with error:\n --> "
|
||||
<< cudaGetErrorString(err)
|
||||
<< cudaGetErrorString(err) << " [code: " << (int)err << ']'
|
||||
<< "\n ... in function: " << func
|
||||
<< "\n ... in file: " << file << ':' << line << '\n';
|
||||
mfem_error();
|
||||
|
||||
+6
-5
@@ -18,7 +18,7 @@
|
||||
// CUDA block size used by MFEM.
|
||||
#define MFEM_CUDA_BLOCKS 256
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
#define MFEM_USE_CUDA_OR_HIP
|
||||
#define MFEM_DEVICE __device__
|
||||
#define MFEM_HOST __host__
|
||||
@@ -37,22 +37,23 @@
|
||||
__FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif // MFEM_USE_CUDA
|
||||
|
||||
// Define the MFEM inner threading macros
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)
|
||||
#if defined(__CUDA_ARCH__)
|
||||
#define MFEM_SHARED __shared__
|
||||
#define MFEM_SYNC_THREAD __syncthreads()
|
||||
#define MFEM_BLOCK_ID(k) blockIdx.k
|
||||
#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)
|
||||
#endif
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) if(const int i=threadIdx.k; i<N)
|
||||
#endif // defined(__CUDA_ARCH__)
|
||||
#endif // defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
// Function used by the macro MFEM_GPU_CHECK.
|
||||
void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
|
||||
const char *file, int line);
|
||||
|
||||
+37
-1
@@ -16,6 +16,7 @@
|
||||
#include "../fem/ceed/interface/util.hpp"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "communication.hpp"
|
||||
#include "../linalg/hypre.hpp"
|
||||
#endif
|
||||
|
||||
@@ -145,6 +146,11 @@ Device::Device()
|
||||
Configure(device);
|
||||
device_env = true;
|
||||
}
|
||||
|
||||
if (GetEnv("MFEM_GPU_AWARE_MPI"))
|
||||
{
|
||||
SetGPUAwareMPI(true);
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
@@ -196,6 +202,29 @@ void Device::Configure(const std::string &device, const int device_id)
|
||||
{
|
||||
bmap[internal::backend_name[i]] = internal::backend_list[i];
|
||||
}
|
||||
// auto-detect GPU configurations
|
||||
// assumes only one of HIP or CUDA are available
|
||||
#ifdef MFEM_USE_HIP
|
||||
bmap["gpu"] = Backend::HIP;
|
||||
#ifdef MFEM_USE_RAJA
|
||||
bmap["raja-gpu"] = Backend::RAJA_HIP;
|
||||
#endif
|
||||
#ifdef MFEM_USE_CEED
|
||||
bmap["ceed-gpu"] = Backend::CEED_HIP;
|
||||
#endif
|
||||
// no OCCA+HIP?
|
||||
#elif defined(MFEM_USE_CUDA)
|
||||
bmap["gpu"] = Backend::CUDA;
|
||||
#ifdef MFEM_USE_RAJA
|
||||
bmap["raja-gpu"] = Backend::RAJA_CUDA;
|
||||
#endif
|
||||
#ifdef MFEM_USE_CEED
|
||||
bmap["ceed-gpu"] = Backend::CEED_CUDA;
|
||||
#endif
|
||||
#ifdef MFEM_USE_OCCA
|
||||
bmap["occa-gpu"] = Backend::OCCA_CUDA;
|
||||
#endif
|
||||
#endif
|
||||
std::string device_option;
|
||||
std::string::size_type beg = 0, end;
|
||||
while (1)
|
||||
@@ -313,6 +342,13 @@ void Device::Print(std::ostream &os)
|
||||
{
|
||||
os << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Allows(Backend::DEVICE_MASK) &&
|
||||
Mpi::IsInitialized() && !Mpi::IsFinalized())
|
||||
{
|
||||
os << "\nUse GPU-aware MPI: " << (GetGPUAwareMPI() ? "yes" : "no");
|
||||
}
|
||||
#endif
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
@@ -579,7 +615,7 @@ void Device::Setup(const std::string &device_option, const int device_id)
|
||||
if (Allows(Backend::DEBUG_DEVICE)) { ngpu = 1; }
|
||||
}
|
||||
|
||||
MemoryType Device::QueryMemoryType(void *ptr)
|
||||
MemoryType Device::QueryMemoryType(const void* ptr)
|
||||
{
|
||||
// from HYPRE's hypre_GetPointerLocation
|
||||
MemoryType res = MemoryType::HOST;
|
||||
|
||||
+7
-3
@@ -198,6 +198,10 @@ public:
|
||||
'ceed-hip', 'hip', 'debug',
|
||||
'occa-omp', 'raja-omp', 'omp',
|
||||
'ceed-cpu', 'occa-cpu', 'raja-cpu', 'cpu'.
|
||||
- The following backend aliases are also available: 'ceed-gpu',
|
||||
'occa-gpu', 'raja-gpu', and 'gpu' where they alias their respective
|
||||
'*-cuda' or '*-hip' backends depending on the MFEM build-time
|
||||
configuration.
|
||||
- Multiple backends can be configured at the same time.
|
||||
- Only one 'occa-*' backend can be configured at a time.
|
||||
- The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
|
||||
@@ -293,9 +297,9 @@ public:
|
||||
/// Get the status of GPU-aware MPI flag.
|
||||
static bool GetGPUAwareMPI() { return Get().mpi_gpu_aware; }
|
||||
|
||||
/** @brief Query the device driver for what memory type a given @a ptr is
|
||||
allocated with. */
|
||||
static MemoryType QueryMemoryType(void *ptr);
|
||||
/** Query the device driver for what memory type a given @a ptr is allocated
|
||||
* with. */
|
||||
static MemoryType QueryMemoryType(const void* ptr);
|
||||
|
||||
/** @brief The number of hardware compute units/streaming multiprocessors
|
||||
available on a given compute device @a device_id. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user