Compare commits
608
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d31dfa15fa | ||
|
|
e94b8b6c89 | ||
|
|
3f8348d04b | ||
|
|
e30f7e5aa9 | ||
|
|
047943cfda | ||
|
|
4e6c38cf6d | ||
|
|
c981633846 | ||
|
|
ac2a5107da | ||
|
|
e6d733aa95 | ||
|
|
c6ee709eef | ||
|
|
0274b67ff6 | ||
|
|
38da503958 | ||
|
|
6c4f9c69a9 | ||
|
|
0702eb2fb2 | ||
|
|
7a62a7fd4a | ||
|
|
0f94d484a4 | ||
|
|
4b5798f905 | ||
|
|
87240f5619 | ||
|
|
3ccaa48cd4 | ||
|
|
a08928dd97 | ||
|
|
a0694d5825 | ||
|
|
a3ecbef0ec | ||
|
|
db5bc1725e | ||
|
|
6d50ebc9c3 | ||
|
|
e713913177 | ||
|
|
dd9898af3b | ||
|
|
0435ef5dac | ||
|
|
79e5020a18 | ||
|
|
adc81c7f5d | ||
|
|
18676c61b7 | ||
|
|
8f8deab121 | ||
|
|
8b0c779320 | ||
|
|
a012769434 | ||
|
|
ce5517b9af | ||
|
|
70ae37d5f0 | ||
|
|
feac718e95 | ||
|
|
ac2a21516c | ||
|
|
8ba104788f | ||
|
|
a454a5407c | ||
|
|
689b46e3d6 | ||
|
|
85ccdf210a | ||
|
|
9be8c15cf8 | ||
|
|
08ba45fca3 | ||
|
|
3aacfbfab0 | ||
|
|
2a60b998c7 | ||
|
|
91a168929f | ||
|
|
1369f5e189 | ||
|
|
993e4fbbe1 | ||
|
|
07d8a17abe | ||
|
|
5531b82dbc | ||
|
|
d9f60f401b | ||
|
|
fb876ba3a1 | ||
|
|
32a94b438f | ||
|
|
917978d310 | ||
|
|
5f373e8c2a | ||
|
|
0fdb8a2709 | ||
|
|
0303669e9a | ||
|
|
37d19f99de | ||
|
|
fa10d89676 | ||
|
|
7534d86172 | ||
|
|
dc13eac9e6 | ||
|
|
993f478c54 | ||
|
|
cdf9cfe1b6 | ||
|
|
4e30030ab7 | ||
|
|
22878ee681 | ||
|
|
aba85bf679 | ||
|
|
3a59281601 | ||
|
|
2360809938 | ||
|
|
69b26cb722 | ||
|
|
f734b1bd92 | ||
|
|
14c31edb4b | ||
|
|
bbc9af2619 | ||
|
|
2a1cce4663 | ||
|
|
80399de548 | ||
|
|
473bd4177e | ||
|
|
184c3cbbb8 | ||
|
|
29730813cb | ||
|
|
cbbfaede8c | ||
|
|
bb8a5b88da | ||
|
|
057a5a43b0 | ||
|
|
867a26ae4b | ||
|
|
a62302b4cb | ||
|
|
acc8ba9df3 | ||
|
|
73ee69da91 | ||
|
|
bfb3eb786b | ||
|
|
f5d2b82839 | ||
|
|
1382f8aa1f | ||
|
|
f76a884d15 | ||
|
|
5c56659e46 | ||
|
|
4bcd4586ba | ||
|
|
083d42c6ce | ||
|
|
dbc3458db0 | ||
|
|
4f694287ae | ||
|
|
5d8fbfee93 | ||
|
|
bc729465d6 | ||
|
|
c690c25058 | ||
|
|
4f629c150d | ||
|
|
7b35f3cfde | ||
|
|
9d78e8cc23 | ||
|
|
1c252d79d8 | ||
|
|
7799753053 | ||
|
|
fe8562e6e3 | ||
|
|
ce5b362077 | ||
|
|
fd6e0e1659 | ||
|
|
73f84b48f8 | ||
|
|
cb6db58ad3 | ||
|
|
5da2bfc23d | ||
|
|
466a771ab0 | ||
|
|
0952809e5e | ||
|
|
b9a1f8689e | ||
|
|
b479f97b54 | ||
|
|
5202ab09c5 | ||
|
|
5fc313d2f2 | ||
|
|
299a8d6c14 | ||
|
|
5683932a61 | ||
|
|
2432eefc23 | ||
|
|
8030334a97 | ||
|
|
e13989a293 | ||
|
|
7f47e0b7ac | ||
|
|
e3ec06fbb8 | ||
|
|
2dd9c9ca65 | ||
|
|
ab503a3fd0 | ||
|
|
46fdcd696f | ||
|
|
9f42d495ca | ||
|
|
6be7547229 | ||
|
|
63da26c00f | ||
|
|
713f86c134 | ||
|
|
708c477714 | ||
|
|
96382fe2a6 | ||
|
|
a317e1a17d | ||
|
|
5a38a2e712 | ||
|
|
5af524009e | ||
|
|
2137ce1f4f | ||
|
|
c32d62f83c | ||
|
|
8aa00234d8 | ||
|
|
fbc4083001 | ||
|
|
fcbde98cb6 | ||
|
|
ffa4f84108 | ||
|
|
988439f60a | ||
|
|
09d03b715e | ||
|
|
5398491cd4 | ||
|
|
0ff7174de2 | ||
|
|
0108a83a43 | ||
|
|
151828f435 | ||
|
|
4d1564ff64 | ||
|
|
ac906c8827 | ||
|
|
1710d10dbe | ||
|
|
5292971b7e | ||
|
|
9997d1b718 | ||
|
|
967e0f5bac | ||
|
|
194debfcc0 | ||
|
|
eae459cb15 | ||
|
|
580578aa04 | ||
|
|
82ad111db5 | ||
|
|
f575e45593 | ||
|
|
e5c31240d0 | ||
|
|
70f2f6677f | ||
|
|
31202eb904 | ||
|
|
bf79ef7f90 | ||
|
|
d81bab442d | ||
|
|
9343ffad7b | ||
|
|
e2a8206d35 | ||
|
|
62049a7990 | ||
|
|
d44ff2d39e | ||
|
|
f5219a2484 | ||
|
|
6d2dec6361 | ||
|
|
ce18700b07 | ||
|
|
36d28c6e3e | ||
|
|
098be49296 | ||
|
|
b5b4b5da7d | ||
|
|
24e2d0f959 | ||
|
|
3d80323862 | ||
|
|
7f1eb85c45 | ||
|
|
878f94f998 | ||
|
|
0b75377438 | ||
|
|
74e538404e | ||
|
|
a042d2db0d | ||
|
|
01819638f7 | ||
|
|
acb5f5acf7 | ||
|
|
5425dbeb5b | ||
|
|
fe918fcdd4 | ||
|
|
c240df5fbe | ||
|
|
950198a3f2 | ||
|
|
d808463114 | ||
|
|
5447bcf8a9 | ||
|
|
ef6d80189f | ||
|
|
7ace2dedf1 | ||
|
|
36dea0cf38 | ||
|
|
94ca7d26e8 | ||
|
|
46e35d0bce | ||
|
|
d582c31370 | ||
|
|
8876a84dd4 | ||
|
|
8153d11274 | ||
|
|
827ed64113 | ||
|
|
ef9f02ba53 | ||
|
|
e9a0b0620a | ||
|
|
4d9d444248 | ||
|
|
6e2badecca | ||
|
|
99b45fcb02 | ||
|
|
003dc46a84 | ||
|
|
7fac0fbd07 | ||
|
|
9bae3b25ab | ||
|
|
c28fd71214 | ||
|
|
9a327eeca6 | ||
|
|
086f6c9847 | ||
|
|
80cb02328c | ||
|
|
480e90b41b | ||
|
|
1847e460cf | ||
|
|
bc6ba0252a | ||
|
|
262fa6173d | ||
|
|
e4b8584a16 | ||
|
|
7912d6915d | ||
|
|
293d9009ae | ||
|
|
51bde67bb1 | ||
|
|
9caa48d5c8 | ||
|
|
f4d286b4b7 | ||
|
|
45e2636921 | ||
|
|
57876fbfb0 | ||
|
|
1cd46aa768 | ||
|
|
11edee7aca | ||
|
|
28115e5de2 | ||
|
|
20954328c3 | ||
|
|
2b9f428909 | ||
|
|
b230e5f594 | ||
|
|
3e8d7f21a2 | ||
|
|
47b519047a | ||
|
|
35225e045e | ||
|
|
22c1087503 | ||
|
|
a74663d634 | ||
|
|
469096892f | ||
|
|
cd5d2f7c04 | ||
|
|
7fc2ce350d | ||
|
|
4314dc64db | ||
|
|
cbae29ad06 | ||
|
|
4c1d842c72 | ||
|
|
4aa0ed52af | ||
|
|
8712d02570 | ||
|
|
e5fec6279b | ||
|
|
e90e96f9a5 | ||
|
|
cc00ef7d90 | ||
|
|
2c0346bc36 | ||
|
|
56186d8770 | ||
|
|
1645b854a4 | ||
|
|
69fd2f9051 | ||
|
|
ba4b627e68 | ||
|
|
62c535d0ee | ||
|
|
17829d1c38 | ||
|
|
dd198ce3f9 | ||
|
|
b9f36468ba | ||
|
|
76bcd044d0 | ||
|
|
0dff351b2e | ||
|
|
55a914321d | ||
|
|
1410aef639 | ||
|
|
e1ac8ca08c | ||
|
|
28916b23a4 | ||
|
|
7476c00f2b | ||
|
|
7a0137c496 | ||
|
|
4267b2af05 | ||
|
|
e37daad5eb | ||
|
|
a461f25b4a | ||
|
|
20b4b72071 | ||
|
|
ddf80492c5 | ||
|
|
db8b4c9f20 | ||
|
|
d4137f9c7a | ||
|
|
20b52574c6 | ||
|
|
e33344f539 | ||
|
|
712ae82026 | ||
|
|
606439cfb8 | ||
|
|
147cbc014a | ||
|
|
aa07a1b175 | ||
|
|
53171de727 | ||
|
|
53dd97e0d8 | ||
|
|
0c413570c4 | ||
|
|
d6bfc6370e | ||
|
|
f8d18cd4be | ||
|
|
994d83dd80 | ||
|
|
f218efae09 | ||
|
|
43bb865c26 | ||
|
|
dd9b723cfd | ||
|
|
87362ca1ca | ||
|
|
a6afefc6a5 | ||
|
|
e1dc4680d3 | ||
|
|
44985dacc0 | ||
|
|
9c77f6b407 | ||
|
|
8df0341e11 | ||
|
|
41e92219ee | ||
|
|
af24eaea27 | ||
|
|
f8c6512cf9 | ||
|
|
4385e6d568 | ||
|
|
171346b20f | ||
|
|
6f7320c240 | ||
|
|
e6ce6e7532 | ||
|
|
7338e797bb | ||
|
|
31d931a99c | ||
|
|
3ae930c93b | ||
|
|
36f882257e | ||
|
|
1508ae0886 | ||
|
|
8adb7461b0 | ||
|
|
716e370d35 | ||
|
|
37c0768fe3 | ||
|
|
d395caad9b | ||
|
|
3e5e18797c | ||
|
|
aab273b303 | ||
|
|
e4d1a861c9 | ||
|
|
217b77d5f0 | ||
|
|
bb67d6cb98 | ||
|
|
cb6b94d9d9 | ||
|
|
7f118b0793 | ||
|
|
a6f0a23998 | ||
|
|
9eb4e5f947 | ||
|
|
6a48f8e165 | ||
|
|
a0c3620618 | ||
|
|
9c791bed5a | ||
|
|
696cbd05e8 | ||
|
|
6c8a4188a1 | ||
|
|
42d0fc17a1 | ||
|
|
2d2da417bb | ||
|
|
829b123641 | ||
|
|
bd52201add | ||
|
|
812ecce84a | ||
|
|
172c38b675 | ||
|
|
2e86ccb948 | ||
|
|
875b5f3f52 | ||
|
|
be1f36a523 | ||
|
|
d4c7dd3490 | ||
|
|
e55fb21538 | ||
|
|
a966b0502f | ||
|
|
1bac61ad1c | ||
|
|
c7451115d8 | ||
|
|
72ae003a00 | ||
|
|
d67098b8f8 | ||
|
|
0e6dbaf050 | ||
|
|
32afc8565c | ||
|
|
48ace60875 | ||
|
|
9e75f9e19d | ||
|
|
9b6ee6fcad | ||
|
|
1a1639b87e | ||
|
|
3735aa504b | ||
|
|
367dda6794 | ||
|
|
e4a85f79cd | ||
|
|
22ec7e7ada | ||
|
|
678101938b | ||
|
|
9a3aa18c62 | ||
|
|
5a3ba1424a | ||
|
|
3bb8419a96 | ||
|
|
ce29282f63 | ||
|
|
9575299ae3 | ||
|
|
995ceca6c2 | ||
|
|
012aa50cd3 | ||
|
|
d0193919c4 | ||
|
|
422ca290b5 | ||
|
|
59e1d7bf27 | ||
|
|
09dd9656c8 | ||
|
|
e9afca2cd6 | ||
|
|
6abd0e6002 | ||
|
|
656e3062b4 | ||
|
|
9c9c519175 | ||
|
|
8300809562 | ||
|
|
cce7296ffe | ||
|
|
a445ad00da | ||
|
|
4db7e1a107 | ||
|
|
c51a1c4aa9 | ||
|
|
d489908e50 | ||
|
|
c4eda188d5 | ||
|
|
677eb4c876 | ||
|
|
69a4aa70b9 | ||
|
|
f07c2f460d | ||
|
|
c7fe1ff1f4 | ||
|
|
09c557bdd7 | ||
|
|
e487da01c5 | ||
|
|
b6b6843ad2 | ||
|
|
011f7b0350 | ||
|
|
41eb57cee3 | ||
|
|
c4b389a4c6 | ||
|
|
6e6eeccf61 | ||
|
|
a82ec2a298 | ||
|
|
ac956e53da | ||
|
|
7332f65373 | ||
|
|
5229753c6f | ||
|
|
5cefe337dd | ||
|
|
292051c8e3 | ||
|
|
68ccd510c2 | ||
|
|
3204614d51 | ||
|
|
bd95160e67 | ||
|
|
bbf6f013af | ||
|
|
a30390c306 | ||
|
|
8e5de72407 | ||
|
|
5691f60988 | ||
|
|
9f9ccdcc55 | ||
|
|
0b10bcbba5 | ||
|
|
a4731e0031 | ||
|
|
e94d3b1628 | ||
|
|
ecdf4717a9 | ||
|
|
c181e720c9 | ||
|
|
fe8cac7082 | ||
|
|
b02f1e492f | ||
|
|
ad1bbd318a | ||
|
|
136eddd3ab | ||
|
|
e6725d8f5c | ||
|
|
484a27c13d | ||
|
|
7b67a8bf15 | ||
|
|
916c13a2ef | ||
|
|
5b4deb99b0 | ||
|
|
6e5a42c921 | ||
|
|
8478f268ac | ||
|
|
66a1379947 | ||
|
|
546ba6c11e | ||
|
|
67ba63c2f4 | ||
|
|
01b25a54c9 | ||
|
|
51dd386f0e | ||
|
|
f0d24c1ffd | ||
|
|
6174655771 | ||
|
|
d91656c768 | ||
|
|
89d95adb09 | ||
|
|
639ae13d24 | ||
|
|
1df6c37ded | ||
|
|
08406c8410 | ||
|
|
1be4bc267f | ||
|
|
4e61084655 | ||
|
|
05e8ef16b8 | ||
|
|
df59f3856a | ||
|
|
4d5a99f3de | ||
|
|
5c6917697a | ||
|
|
459d4a4940 | ||
|
|
76a71f69bc | ||
|
|
51279cb47f | ||
|
|
f9845fe3fc | ||
|
|
802d249684 | ||
|
|
6f86a4241b | ||
|
|
08ef67eafa | ||
|
|
5103b31c6a | ||
|
|
ce810be429 | ||
|
|
287e4835f0 | ||
|
|
261af4c74d | ||
|
|
3d3e0f991d | ||
|
|
d37bbb7d93 | ||
|
|
8f0b439eb8 | ||
|
|
5f4fc3acb8 | ||
|
|
6b744436fb | ||
|
|
bdfd326e8c | ||
|
|
7676d7fff6 | ||
|
|
1fdd927821 | ||
|
|
2374ae0588 | ||
|
|
deec36125c | ||
|
|
abbf08a0f8 | ||
|
|
2473af39c3 | ||
|
|
d3a2aa17b8 | ||
|
|
7b5f9a4157 | ||
|
|
178ae5ea9d | ||
|
|
b9e4187a71 | ||
|
|
053b114172 | ||
|
|
c8dcee1065 | ||
|
|
99c2967920 | ||
|
|
3c561a774d | ||
|
|
6679963094 | ||
|
|
eda46fef52 | ||
|
|
0efb8c8c2d | ||
|
|
ad90bb1acf | ||
|
|
7e5fed72cf | ||
|
|
6d81cb7748 | ||
|
|
ca92a847f2 | ||
|
|
c8d4285c94 | ||
|
|
323db614e1 | ||
|
|
9a2c0ce611 | ||
|
|
2b024ff6b3 | ||
|
|
38e37437bd | ||
|
|
5e0c5469ec | ||
|
|
a095387aa2 | ||
|
|
c084361b5c | ||
|
|
2dc419f1ae | ||
|
|
450d6cea6d | ||
|
|
75df4ad3e6 | ||
|
|
4048d46443 | ||
|
|
381cf25cbd | ||
|
|
556b577818 | ||
|
|
a5ca1b6a32 | ||
|
|
1b7f20af7f | ||
|
|
a65f6064ba | ||
|
|
993d5cb831 | ||
|
|
458447caf0 | ||
|
|
a6138169fa | ||
|
|
d65033932e | ||
|
|
d49629b916 | ||
|
|
a642f36524 | ||
|
|
bbd4edce83 | ||
|
|
1742616cac | ||
|
|
2c64bbab79 | ||
|
|
785fa7adc2 | ||
|
|
0248c58591 | ||
|
|
160e783638 | ||
|
|
ddd2500a9c | ||
|
|
2b5dee2b95 | ||
|
|
26393f230f | ||
|
|
8e9948d729 | ||
|
|
9bbbd8c324 | ||
|
|
a19e7cb38e | ||
|
|
3a2912bc0b | ||
|
|
5cfd284cb8 | ||
|
|
eeae538115 | ||
|
|
8a98c0332f | ||
|
|
84d44db3a7 | ||
|
|
c97af2f3dc | ||
|
|
c4ca3bfc5f | ||
|
|
b03cf507be | ||
|
|
066dc9b078 | ||
|
|
70854254e7 | ||
|
|
646df28ac8 | ||
|
|
73d4f987e4 | ||
|
|
c59d519c89 | ||
|
|
1ec2cba9e8 | ||
|
|
26cc1f8387 | ||
|
|
85fe35bec2 | ||
|
|
b57fa2b127 | ||
|
|
596909138a | ||
|
|
c11a76f2c1 | ||
|
|
51d32ad293 | ||
|
|
a433e9e0b4 | ||
|
|
abac61f5b5 | ||
|
|
a121a9d186 | ||
|
|
58ecbf6150 | ||
|
|
bad5ae41d1 | ||
|
|
e773e07373 | ||
|
|
c3ded3c003 | ||
|
|
e706802ed5 | ||
|
|
9e97ad8bb3 | ||
|
|
76e04c4606 | ||
|
|
83ccf77d2f | ||
|
|
a0d53975d8 | ||
|
|
1252c0fbb9 | ||
|
|
04acf613ae | ||
|
|
c8bddb8035 | ||
|
|
467e83da31 | ||
|
|
8b29ef1335 | ||
|
|
1076700714 | ||
|
|
978f1155c5 | ||
|
|
3096d9d9cb | ||
|
|
ffea75abb2 | ||
|
|
a44a8b8789 | ||
|
|
05106096c3 | ||
|
|
ba7fd7a9a9 | ||
|
|
9b0e4e0085 | ||
|
|
3cb64f7f7e | ||
|
|
6642857437 | ||
|
|
91f00d643a | ||
|
|
ce8b62cfe7 | ||
|
|
4064bda60d | ||
|
|
4a21554986 | ||
|
|
165968dc26 | ||
|
|
9eb70f7be0 | ||
|
|
8797a9cb00 | ||
|
|
8366a5a6d6 | ||
|
|
63a9d5749b | ||
|
|
0e6aa41245 | ||
|
|
bbcb054814 | ||
|
|
2e5a86db7a | ||
|
|
057b15cefb | ||
|
|
7c06741f36 | ||
|
|
3a0c42aea5 | ||
|
|
75d5555a5f | ||
|
|
10c1ac9a66 | ||
|
|
692e15c088 | ||
|
|
6be9665bfb | ||
|
|
9be617d754 | ||
|
|
ec4f37fe25 | ||
|
|
6bb4ae9d50 | ||
|
|
c7a3f188c0 | ||
|
|
8e44509585 | ||
|
|
787954715b | ||
|
|
9f03260dd2 | ||
|
|
5c3a3f7fdf | ||
|
|
6693b22c83 | ||
|
|
49c7f60a57 | ||
|
|
ace4608f10 | ||
|
|
3899dfcc64 | ||
|
|
2dc98e9153 | ||
|
|
90f8a2409f | ||
|
|
92dc0db889 | ||
|
|
d8fc48e608 | ||
|
|
611802f990 | ||
|
|
606df86b0a | ||
|
|
2f7d38e6f6 | ||
|
|
b90d665ced | ||
|
|
50dd77ffd4 | ||
|
|
5e5b79783c | ||
|
|
c815114661 | ||
|
|
93f6a53201 | ||
|
|
a3ee0cfe79 | ||
|
|
95b0178514 | ||
|
|
afacf3db45 | ||
|
|
4a80321420 | ||
|
|
9295c69249 | ||
|
|
893f04967c | ||
|
|
fa410a6e02 | ||
|
|
aed2687743 | ||
|
|
978c0d10bc | ||
|
|
25b540e804 | ||
|
|
9aa58cd5c2 | ||
|
|
691be01bcc | ||
|
|
402ed45ee4 | ||
|
|
07dfcd83b9 | ||
|
|
9845dfda2c | ||
|
|
da5ee77e61 | ||
|
|
e1d2966e42 | ||
|
|
17428ce198 | ||
|
|
e87e790215 | ||
|
|
a6d4e17911 | ||
|
|
bde7846b5a | ||
|
|
01283767a6 |
+11
@@ -272,16 +272,27 @@ miniapps/navier/*_output
|
||||
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex3
|
||||
miniapps/nurbs/nurbs_ex5
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_ex24
|
||||
miniapps/nurbs/nurbs_solenoidal
|
||||
miniapps/nurbs/nurbs_printfunc
|
||||
miniapps/nurbs/nurbs_patch_ex1
|
||||
miniapps/nurbs/nurbs_curveint
|
||||
miniapps/nurbs/refined.mesh
|
||||
miniapps/nurbs/mesh.*
|
||||
miniapps/nurbs/sol_?.gf
|
||||
miniapps/nurbs/sol.*
|
||||
miniapps/nurbs/mode_*
|
||||
miniapps/nurbs/Example1*
|
||||
miniapps/nurbs/Example3*
|
||||
miniapps/nurbs/Example5*
|
||||
miniapps/nurbs/Solenoidal*
|
||||
miniapps/nurbs/ParaView
|
||||
miniapps/nurbs/sin-fit.mesh
|
||||
miniapps/nurbs/ex5.mesh
|
||||
miniapps/nurbs/exsol.mesh
|
||||
miniapps/nurbs/CurveInt
|
||||
miniapps/nurbs/nurbs_naca_cmesh
|
||||
miniapps/nurbs/naca-cmesh.mesh
|
||||
|
||||
+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 quartz resource are allocated/released once for all.
|
||||
# - Allocate/Release is where ruby 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:
|
||||
quartz-build-and-test:
|
||||
ruby-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 @@ quartz-build-and-test:
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/quartz-build-and-test.yml
|
||||
include: .gitlab/ruby-build-and-test.yml
|
||||
strategy: depend
|
||||
|
||||
quartz-baseline:
|
||||
ruby-baseline:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
@@ -73,7 +73,7 @@ quartz-baseline:
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/quartz-baseline.yml
|
||||
include: .gitlab/ruby-baseline.yml
|
||||
strategy: depend
|
||||
|
||||
lassen-build-and-test:
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ and `test type`.
|
||||
|
||||
Machines typically include:
|
||||
|
||||
* Quartz: Intel bi-socket x86
|
||||
* Ruby: 2nd Gen Intel Xeon (Cascade Lake)
|
||||
* 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 quartz for example resumes to:
|
||||
spack spec to use. Adding a job on ruby for example resumes to:
|
||||
|
||||
```yaml
|
||||
<job_name>:
|
||||
variables:
|
||||
SPEC: "<spack_spec>"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
```
|
||||
|
||||
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 quartz, there is only one allocation shared among jobs in order to
|
||||
# On LLNL's ruby, 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 Quartz machine at LLNL
|
||||
# GitLab pipelines configurations for the Ruby machine at LLNL
|
||||
variables:
|
||||
MACHINE_NAME: quartz
|
||||
MACHINE_NAME: ruby
|
||||
|
||||
.on_quartz:
|
||||
.on_ruby:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
- ruby
|
||||
rules:
|
||||
# Don't run quartz jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
|
||||
# Don't run ruby jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_RUBY == "OFF"'
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
|
||||
@@ -40,13 +40,13 @@ variables:
|
||||
- when: on_success
|
||||
|
||||
# Spack helped builds
|
||||
# Generic quartz build job, extending build script
|
||||
.build_and_test_on_quartz:
|
||||
extends: [.on_quartz]
|
||||
# Generic ruby build job, extending build script
|
||||
.build_and_test_on_ruby:
|
||||
extends: [.on_ruby]
|
||||
stage: build_and_test
|
||||
script:
|
||||
# THREADS is used by 'tests/gitlab/build_and_test', run below
|
||||
- export THREADS=12
|
||||
- export THREADS=16
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
@@ -18,7 +18,7 @@
|
||||
setup_baseline:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
- ruby
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
setup:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
- ruby
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -19,8 +19,8 @@ stages:
|
||||
- cleanup
|
||||
- baseline_publish
|
||||
|
||||
baselinecheck_mfem_intel_quartz:
|
||||
extends: [.on_quartz]
|
||||
baselinecheck_mfem_intel_ruby:
|
||||
extends: [.on_ruby]
|
||||
stage: baseline_check
|
||||
variables:
|
||||
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
|
||||
@@ -32,7 +32,7 @@ baselinecheck_mfem_intel_quartz:
|
||||
- echo ${BUILD_ROOT}
|
||||
- echo ${TPLS_DIR}
|
||||
# Used by the tests in MFEM/tests:
|
||||
- export MFEM_TEST_NP=32
|
||||
- export MFEM_TEST_NP=48
|
||||
# The next script uses the following environment variables:
|
||||
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
|
||||
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
|
||||
@@ -44,18 +44,16 @@ baselinecheck_mfem_intel_quartz:
|
||||
allow_failure: true
|
||||
|
||||
cleanup:
|
||||
extends: .on_quartz
|
||||
extends: .on_ruby
|
||||
stage: cleanup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- echo "BUILD_ROOT=${BUILD_ROOT}"
|
||||
- rm -rf "${BUILD_ROOT}" || true
|
||||
- echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}"
|
||||
- make -C "${CI_PROJECT_DIR}" distclean
|
||||
|
||||
report_baseline:
|
||||
extends: [.on_quartz]
|
||||
extends: [.on_ruby]
|
||||
stage: baseline_report
|
||||
script:
|
||||
- echo ${MACHINE_NAME}
|
||||
@@ -115,8 +113,8 @@ report_baseline:
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
baselinepublish_mfem_quartz:
|
||||
extends: [.on_quartz]
|
||||
baselinepublish_mfem_ruby:
|
||||
extends: [.on_ruby]
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
@@ -131,5 +129,5 @@ baselinepublish_mfem_quartz:
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/quartz-config.yml
|
||||
- local: .gitlab/configs/ruby-config.yml
|
||||
- local: .gitlab/configs/setup-baseline.yml
|
||||
@@ -19,54 +19,54 @@ stages:
|
||||
allocate_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
extends: .on_ruby
|
||||
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 Quartz machine at LLNL
|
||||
# GitLab jobs for the Ruby machine at LLNL
|
||||
debug_ser_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +debug~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
debug_par_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +debug+mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
opt_ser_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 ~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
opt_par_gcc_10:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
opt_par_gcc_10_sundials:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +sundials"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
opt_par_gcc_10_petsc:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
opt_par_gcc_10_pumi:
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +pumi"
|
||||
extends: .build_and_test_on_quartz
|
||||
extends: .build_and_test_on_ruby
|
||||
|
||||
# Release
|
||||
release_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
extends: .on_ruby
|
||||
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_quartz
|
||||
- .on_ruby
|
||||
- .report_job_success
|
||||
|
||||
report_job_failure:
|
||||
stage: release_resource_and_report
|
||||
extends:
|
||||
- .on_quartz
|
||||
- .on_ruby
|
||||
- .report_job_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/quartz-config.yml
|
||||
- local: .gitlab/configs/ruby-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}" == "quartz" ]]; then
|
||||
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
base="${BASELINE_TEST}-${MACHINE_NAME}"
|
||||
fi
|
||||
base_diff=${base}.diff
|
||||
@@ -31,8 +31,8 @@ cd tests
|
||||
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "quartz" || "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
salloc --nodes=1 --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
if [[ "${MACHINE_NAME}" == "ruby" ]]; 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}"
|
||||
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
|
||||
@@ -41,11 +41,11 @@ else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
status="$?"
|
||||
|
||||
# post
|
||||
mkdir ${artifacts_path}
|
||||
|
||||
status=0
|
||||
if [[ -f ${BASELINE_TEST}.out ]]; then
|
||||
cp ${BASELINE_TEST}.out ${artifacts_path}
|
||||
fi
|
||||
|
||||
@@ -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 quartz baselines.
|
||||
# There will be collision between corona and ruby 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 quartz baselines.
|
||||
# There will be collision between corona and ruby baselines.
|
||||
# Once the corresponding files have been generated, we can switch to machine
|
||||
# specific ref.
|
||||
SAVED_NAME=baseline-${SYS_TYPE}.saved
|
||||
|
||||
@@ -11,9 +11,46 @@
|
||||
Version 4.7.1 (development)
|
||||
===========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Only on single
|
||||
patch meshes. Only implemented for serial computations.
|
||||
|
||||
- Added support for boundary constraints to the hybridization class.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
|
||||
are also supported.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
|
||||
|
||||
- Added an MFEM example for the eikonal equation. This new solver is based on
|
||||
the proximal Galerkin method introduced by Keith and Surowiec.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
|
||||
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
|
||||
`TimeDependentOperator::Mult` only when the associated ODE operator is
|
||||
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
|
||||
otherwise `TimeDependentOperator::ExplicitMult` is used. A check has been
|
||||
added to `ARKStepSolver` to verify that the associated ODE operator is not in
|
||||
explicit form when a mass matrix solver is enabled via a call to either the
|
||||
`UseMFEMMassLinearSolver` or `UseSundialsMassLinearSolver` methods. This is
|
||||
because enabling a mass matrix solver assumes that F(u,k,t) = M k in the
|
||||
associated ODE operator.
|
||||
|
||||
- Added support for custom interpolation procedure in FindPointsGSLIB.
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
|
||||
|
||||
|
||||
@@ -40,6 +77,9 @@ Meshing improvements
|
||||
|
||||
- Added support for internal boundary elements in nonconforming meshes.
|
||||
|
||||
- Added ExodusII output capability. The writer can handle first-order (Pyramid5,
|
||||
Wedge6, Hex8, Tet4) and second-order FE types (Pyramid14, Wedge18, Hex27, Tet10).
|
||||
|
||||
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
|
||||
|
||||
Discretization improvements
|
||||
|
||||
+21
-5
@@ -146,7 +146,9 @@ if (MFEM_USE_CUDA)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
set(CUSPARSE_FOUND TRUE)
|
||||
set(CUBLAS_FOUND TRUE)
|
||||
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
|
||||
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
|
||||
endif()
|
||||
|
||||
if (XSDK_ENABLE_C)
|
||||
@@ -231,6 +233,7 @@ if (MFEM_USE_HIP)
|
||||
list(INSERT CMAKE_PREFIX_PATH 0 ${ROCM_PATH})
|
||||
endif()
|
||||
find_package(HIP REQUIRED)
|
||||
find_package(HIPBLAS REQUIRED)
|
||||
find_package(HIPSPARSE REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -396,6 +399,10 @@ if (MFEM_USE_AMGX)
|
||||
find_package(AMGX REQUIRED)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_MAGMA)
|
||||
find_package(MAGMA REQUIRED)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CONDUIT)
|
||||
find_package(Conduit REQUIRED conduit relay blueprint)
|
||||
endif()
|
||||
@@ -515,7 +522,10 @@ endif()
|
||||
|
||||
# Enzyme
|
||||
if (MFEM_USE_ENZYME)
|
||||
find_package(ENZYME REQUIRED)
|
||||
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
|
||||
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
|
||||
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
|
||||
set(ENZYME_FOUND 1)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
@@ -557,8 +567,9 @@ find_package(Threads REQUIRED)
|
||||
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 CUSPARSE MKL_CPARDISO MKL_PARDISO AMGX CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM ENZYME)
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME)
|
||||
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
@@ -621,6 +632,11 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
|
||||
|
||||
if (MFEM_USE_ENZYME)
|
||||
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
|
||||
endif()
|
||||
|
||||
if (MINGW)
|
||||
target_link_libraries(mfem PRIVATE ws2_32)
|
||||
endif()
|
||||
@@ -673,7 +689,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
|
||||
")
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_BINARY_DIR}/${Header}.tmp"
|
||||
"${PROJECT_BINARY_DIR}/${Header}"
|
||||
)
|
||||
@@ -687,7 +703,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
#include \"mfem/${Header}\"
|
||||
")
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
|
||||
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
|
||||
)
|
||||
|
||||
@@ -273,7 +273,13 @@ Installation options:
|
||||
PREFIX - Specify the installation directory. The library (libmfem.a) will be
|
||||
installed in $(PREFIX)/lib, the headers in $(PREFIX)/include, and
|
||||
the configuration makefile (config.mk) in $(PREFIX)/share/mfem.
|
||||
INSTALL - Specify the install program, e.g /usr/bin/install
|
||||
INSTALL - Specify the install program, default = /usr/bin/install
|
||||
INSTALL_DEF_PERM - Specify the default install permissions. This affects
|
||||
headers and configuration makefiles, default = 644
|
||||
INSTALL_BIN_PERM - Specify the install permissions for binaries. This only
|
||||
affects the shared version of the library, default = 755
|
||||
INSTALL_DIR_PERM - Specify the install permissions for directories and,
|
||||
on macOS/BSD, for symlinks as well, default = 755
|
||||
|
||||
MFEM library features/options (GNU make)
|
||||
----------------------------------------
|
||||
@@ -388,6 +394,11 @@ MFEM_USE_AMGX = YES/NO
|
||||
Allows the user to use SparseMatrices and HypreParMatrices to solve linear
|
||||
systems with the routines from the AmgX library.
|
||||
|
||||
MFEM_USE_MAGMA = YES/NO
|
||||
Enable MFEM functionality based on the MAGMA high-performance linear algebra
|
||||
library. The MAGMA library provides a BLAS/LAPACK interface, with
|
||||
implementations that have been optimized for Nvidia and AMD GPUs.
|
||||
|
||||
MFEM_USE_GNUTLS = YES/NO
|
||||
Enable secure socket support in class socketstream, using the auxiliary
|
||||
GnuTLS_* classes, based on the GnuTLS library. This option may be useful in
|
||||
@@ -699,6 +710,11 @@ The specific libraries and their options are:
|
||||
Options: AMGX_OPT, AMGX_LIB.
|
||||
Versions: AmgX >= 2.1, older versions may work too.
|
||||
|
||||
- MAGMA (optional), used with MFEM_USE_MAGMA = YES.
|
||||
URL: https://icl.utk.edu/magma/
|
||||
Options: MAGMA_OPT, MAGMA_LIB
|
||||
Versions: MAGMA >= 2.8.0
|
||||
|
||||
- GnuTLS (optional), used when MFEM_USE_GNUTLS = YES. On most Linux systems,
|
||||
GnuTLS is available as a development package, e.g. gnutls-devel. On Mac OS X,
|
||||
one can get the library through the Homebrew package manager (http://brew.sh).
|
||||
|
||||
@@ -37,6 +37,7 @@ set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
|
||||
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
|
||||
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
|
||||
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
|
||||
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
set(MFEM_USE_HIOP @MFEM_USE_HIOP@)
|
||||
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
|
||||
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
|
||||
|
||||
@@ -114,6 +114,9 @@
|
||||
// Enable MFEM functionality based on the AmgX library.
|
||||
#cmakedefine MFEM_USE_AMGX
|
||||
|
||||
// Enable MFEM functionality based on the MAGMA library.
|
||||
#cmakedefine MFEM_USE_MAGMA
|
||||
|
||||
// Enable secure socket streams based on the GNUTLS library.
|
||||
#cmakedefine MFEM_USE_GNUTLS
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
message(STATUS "Looking for ENZYME ...")
|
||||
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
|
||||
|
||||
# Make sure the directory and version combination works. Do nothing otherwise.
|
||||
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
|
||||
# Set ENZYME_FOUND
|
||||
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
|
||||
|
||||
# Set CXX flags to accommodate the Enzyme Clang plugin
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
|
||||
set(MFEM_USE_ENZYME YES)
|
||||
else()
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright (c) 2010-2024, 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.
|
||||
|
||||
# Defines the following variables:
|
||||
# - MAGMA_FOUND
|
||||
# - MAGMA_LIBRARIES
|
||||
# - MAGMA_INCLUDE_DIRS
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(MAGMA MAGMA MAGMA_DIR "include" "magma.h" "lib" "magma"
|
||||
"Paths to headers required by MAGMA." "Libraries required by MAGMA.")
|
||||
|
||||
if (MAGMA_FOUND AND MFEM_USE_CUDA)
|
||||
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
|
||||
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
|
||||
list(APPEND MAGMA_LIBRARIES ${CUSPARSE_LIBRARIES} ${CUBLAS_LIBRARIES})
|
||||
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
|
||||
"MAGMA libraries + dependencies." FORCE)
|
||||
message(STATUS "Updated MAGMA_LIBRARIES: ${MAGMA_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
if (MAGMA_FOUND AND MFEM_USE_HIP)
|
||||
find_package(HIPBLAS REQUIRED)
|
||||
find_package(HIPSPARSE REQUIRED)
|
||||
list(APPEND MAGMA_LIBRARIES ${HIPBLAS_LIBRARIES} ${HIPSPARSE_LIBRARIES})
|
||||
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
|
||||
"MAGMA libraries + dependencies." FORCE)
|
||||
message(STATUS "Updated MAGMA_LIBRARIES: ${MAGMA_LIBRARIES}")
|
||||
endif()
|
||||
@@ -846,14 +846,14 @@ function(mfem_export_mk_files)
|
||||
MFEM_USE_ZLIB MFEM_USE_LIBUNWIND MFEM_USE_LAPACK MFEM_THREAD_SAFE
|
||||
MFEM_USE_LEGACY_OPENMP MFEM_USE_OPENMP MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS
|
||||
MFEM_USE_SUITESPARSE MFEM_USE_SUPERLU MFEM_USE_SUPERLU5 MFEM_USE_MUMPS
|
||||
MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX MFEM_USE_GNUTLS
|
||||
MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE
|
||||
MFEM_USE_FMS MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB
|
||||
MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_RAJA MFEM_USE_OCCA MFEM_USE_CEED
|
||||
MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2
|
||||
MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_ADFORWARD
|
||||
MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL
|
||||
MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
|
||||
MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX MFEM_USE_MAGMA
|
||||
MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC
|
||||
MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS MFEM_USE_CONDUIT MFEM_USE_PUMI
|
||||
MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_RAJA
|
||||
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
|
||||
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
|
||||
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
|
||||
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
|
||||
@@ -114,6 +114,9 @@
|
||||
// Enable MFEM functionality based on the AmgX library.
|
||||
// #define MFEM_USE_AMGX
|
||||
|
||||
// Enable MFEM functionality based on the MAGMA library.
|
||||
// #define MFEM_USE_MAGMA
|
||||
|
||||
// Enable secure socket streams based on the GNUTLS library.
|
||||
// #define MFEM_USE_GNUTLS
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
|
||||
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
|
||||
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
|
||||
MFEM_USE_PETSC = @MFEM_USE_PETSC@
|
||||
|
||||
@@ -40,6 +40,7 @@ option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
|
||||
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
|
||||
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
|
||||
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
|
||||
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
|
||||
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
|
||||
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
|
||||
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
|
||||
@@ -183,6 +184,10 @@ set(Ginkgo_DIR "${MFEM_DIR}/../ginkgo" CACHE PATH "Path to the Ginkgo library.")
|
||||
|
||||
set(AMGX_DIR "${MFEM_DIR}/../amgx" CACHE PATH "Path to AmgX")
|
||||
|
||||
set(MAGMA_DIR "${MFEM_DIR}/../magma" CACHE PATH "Path to MAGMA")
|
||||
set(MAGMA_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
|
||||
"Additional packages required by MAGMA.")
|
||||
|
||||
set(GNUTLS_DIR "" CACHE PATH "Path to the GnuTLS library.")
|
||||
|
||||
set(GSLIB_DIR "" CACHE PATH "Path to the GSLIB library.")
|
||||
@@ -259,7 +264,7 @@ set(PARELAG_LIBRARIES "${PARELAG_DIR}/build/src/libParELAG.a" CACHE STRING
|
||||
"The ParELAG library.")
|
||||
|
||||
set(TRIBOL_DIR "${MFEM_DIR}/../tribol" CACHE PATH "Path to Tribol")
|
||||
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
|
||||
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
|
||||
"Additional packages required by Tribol")
|
||||
|
||||
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
|
||||
|
||||
+12
-2
@@ -95,6 +95,10 @@ else
|
||||
# Silence unused command line argument warnings when generating dependencies
|
||||
# with mpicxx and clang
|
||||
DEP_FLAGS := -Wno-unused-command-line-argument $(DEP_FLAGS)
|
||||
# Silence "ignoring duplicate libraries" warnings on new (Xcode 15) linker
|
||||
ifneq (,$(findstring PROJECT:dyld,$(shell ld -v 2>&1)))
|
||||
LDFLAGS_INTERNAL = -Xlinker -no_warn_duplicate_libraries
|
||||
endif
|
||||
endif
|
||||
|
||||
# Set CXXFLAGS to overwrite the default selection of DEBUG_FLAGS/OPTIM_FLAGS
|
||||
@@ -139,6 +143,7 @@ MFEM_USE_MUMPS = NO
|
||||
MFEM_USE_STRUMPACK = NO
|
||||
MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
MFEM_USE_GNUTLS = NO
|
||||
MFEM_USE_NETCDF = NO
|
||||
MFEM_USE_PETSC = NO
|
||||
@@ -390,6 +395,11 @@ AMGX_DIR = @MFEM_DIR@/../amgx
|
||||
AMGX_OPT = -I$(AMGX_DIR)/include
|
||||
AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
|
||||
|
||||
# MAGMA library configuration
|
||||
MAGMA_DIR = @MFEM_DIR@/../magma
|
||||
MAGMA_OPT = -I$(MAGMA_DIR)/include
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
GNUTLS_LIB = -lgnutls
|
||||
@@ -497,11 +507,11 @@ GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
|
||||
|
||||
# CUDA library configuration
|
||||
CUDA_OPT =
|
||||
CUDA_LIB = -lcusparse
|
||||
CUDA_LIB = -lcusparse -lcublas
|
||||
|
||||
# HIP library configuration
|
||||
HIP_OPT =
|
||||
HIP_LIB = -L$(HIP_DIR)/lib $(XLINKER)-rpath,$(HIP_DIR)/lib -lhipsparse
|
||||
HIP_LIB = -L$(HIP_DIR)/lib $(XLINKER)-rpath,$(HIP_DIR)/lib -lhipsparse -lhipblas
|
||||
|
||||
# OCCA library configuration
|
||||
OCCA_DIR = @MFEM_DIR@/../occa
|
||||
|
||||
+83
-13
@@ -32,7 +32,7 @@ groups_serial=(
|
||||
'"examples"
|
||||
"Examples:"
|
||||
"examples"
|
||||
"ex{,1,2,3}[0-9].cpp"'
|
||||
"ex{,[1-9]}[0-9].cpp"'
|
||||
# "ex1.cpp"'
|
||||
'"sundials"
|
||||
"SUNDIALS examples:"
|
||||
@@ -58,6 +58,10 @@ groups_serial=(
|
||||
"HiOp examples:"
|
||||
"examples/hiop"
|
||||
"ex9.cpp"'
|
||||
'"moonolith"
|
||||
"Moonolith examples:"
|
||||
"examples/moonolith"
|
||||
"ex1.cpp"'
|
||||
'"pumi"
|
||||
"PUMI examples:"
|
||||
"examples/pumi"
|
||||
@@ -66,25 +70,38 @@ groups_serial=(
|
||||
'"meshing"
|
||||
"Meshing miniapps:"
|
||||
"miniapps/meshing"
|
||||
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
|
||||
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp mesh-quality.cpp
|
||||
polar-nc.cpp reflector.cpp shaper.cpp trimmer.cpp twist.cpp
|
||||
mesh-optimizer.cpp minimal-surface.cpp"'
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
"cvsRoberts_ASAi_dns.cpp"'
|
||||
'"autodiff"
|
||||
"Autodiff miniapps:"
|
||||
"miniapps/autodiff"
|
||||
"seq_example.cpp seq_test.cpp"' # 'seq_test.cpp' has no sample runs
|
||||
'"dpg"
|
||||
"DPG miniapps:"
|
||||
"miniapps/dpg"
|
||||
"{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
|
||||
'"gslib"
|
||||
"GSLIB miniapps:"
|
||||
"miniapps/gslib"
|
||||
"field-diff.cpp field-interp.cpp findpts.cpp schwarz_ex1.cpp "'
|
||||
# todo: miniapps/mtop
|
||||
'"nurbs"
|
||||
"NURBS miniapps:"
|
||||
"miniapps/nurbs"
|
||||
"nurbs_ex1.cpp"'
|
||||
# todo: add other nurbs miniapps
|
||||
# todo: miniapps/solvers (serial)
|
||||
'"tools"
|
||||
"Tools miniapps:"
|
||||
"miniapps/tools"
|
||||
"convert-dc.cpp display-basis.cpp get-values.cpp load-dc.cpp
|
||||
lor-transfer.cpp"'
|
||||
# todo: add other tools miniapps
|
||||
'"toys"
|
||||
"Toys miniapps:"
|
||||
"miniapps/toys"
|
||||
@@ -100,7 +117,7 @@ groups_parallel=(
|
||||
'"examples"
|
||||
"Examples:"
|
||||
"examples"
|
||||
"ex{,1,2,3}[0-9]p.cpp"'
|
||||
"ex{,[1-9]}[0-9]p.cpp"'
|
||||
# "ex1p.cpp"'
|
||||
'"sundials"
|
||||
"SUNDIALS examples:"
|
||||
@@ -126,6 +143,10 @@ groups_parallel=(
|
||||
"HiOp examples:"
|
||||
"examples/hiop"
|
||||
"ex9p.cpp"'
|
||||
'"moonolith"
|
||||
"Moonolith examples:"
|
||||
"examples/moonolith"
|
||||
"ex{1,2}p.cpp"'
|
||||
'"pumi"
|
||||
"PUMI examples:"
|
||||
"examples/pumi"
|
||||
@@ -138,24 +159,41 @@ groups_parallel=(
|
||||
'"meshing"
|
||||
"Meshing miniapps:"
|
||||
"miniapps/meshing"
|
||||
"pmesh-optimizer.cpp pmesh-fitting.cpp pminimal-surface.cpp"'
|
||||
"pmesh-optimizer.cpp pmesh-fitting.cpp pminimal-surface.cpp
|
||||
fit-node-position.cpp"'
|
||||
'"electromagnetics"
|
||||
"Electromagnetics miniapps:"
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
"adjoint_advection_diffusion.cpp"'
|
||||
'"autodiff"
|
||||
"Autodiff miniapps:"
|
||||
"miniapps/autodiff"
|
||||
"par_example.cpp"'
|
||||
'"dpg"
|
||||
"DPG miniapps:"
|
||||
"miniapps/dpg"
|
||||
"p{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
|
||||
'"gslib"
|
||||
"GSLIB miniapps:"
|
||||
"miniapps/gslib"
|
||||
"pfindpts.cpp schwarz_ex1p.cpp"'
|
||||
'"hdiv-linear-solver"
|
||||
"H(div) linear solver miniapps:"
|
||||
"miniapps/hdiv-linear-solver"
|
||||
"grad_div.cpp darcy.cpp"'
|
||||
# 'miniapps/hooke/hooke.cpp' has no sample runs
|
||||
# todo: miniapps/mtop
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
"NURBS miniapps:"
|
||||
"miniapps/nurbs"
|
||||
@@ -164,14 +202,18 @@ groups_parallel=(
|
||||
"Shifted miniapps:"
|
||||
"miniapps/shifted"
|
||||
"distance.cpp"'
|
||||
# todo: add other shifted miniapps
|
||||
'"solvers"
|
||||
"Solvers miniapps:"
|
||||
"miniapps/solvers"
|
||||
"block-solvers.cpp"'
|
||||
# todo: add other solvers miniapps
|
||||
# todo: miniapps/spde
|
||||
'"tools"
|
||||
"Tools miniapps:"
|
||||
"miniapps/tools"
|
||||
"convert-cd.cpp get-values.cpp load-dc.cpp"'
|
||||
"convert-dc.cpp get-values.cpp load-dc.cpp"'
|
||||
# todo: add other tools miniapps
|
||||
'"convergence"
|
||||
"Convergence tests:"
|
||||
"tests/convergence"
|
||||
@@ -186,7 +228,7 @@ groups_all=(
|
||||
'"examples"
|
||||
"Examples:"
|
||||
"examples"
|
||||
"ex\"{,1,2,3}[0-9]\"{,p}.cpp"'
|
||||
"ex\"{,[1-9]}[0-9]\"{,p}.cpp"'
|
||||
'"sundials"
|
||||
"SUNDIALS examples:"
|
||||
"examples/sundials"
|
||||
@@ -215,10 +257,14 @@ groups_all=(
|
||||
"HiOp examples:"
|
||||
"examples/hiop"
|
||||
"ex9.cpp ex9p.cpp"'
|
||||
'"moonolith"
|
||||
"Moonolith examples:"
|
||||
"examples/moonolith"
|
||||
"ex1.cpp ex{1,2}p.cpp"'
|
||||
'"pumi"
|
||||
"PUMI examples:"
|
||||
"examples/pumi"
|
||||
"ex1.cpp ex1p.cpp ex2.cpp ex6p.cpp"'
|
||||
"ex1.cpp ex2.cpp ex1p.cpp ex6p.cpp"'
|
||||
'"superlu"
|
||||
"Superlu examples:"
|
||||
"examples/superlu"
|
||||
@@ -226,43 +272,67 @@ groups_all=(
|
||||
'"meshing"
|
||||
"Meshing miniapps:"
|
||||
"miniapps/meshing"
|
||||
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
|
||||
{,p}mesh-optimizer.cpp pmesh-fitting.cpp {,p}minimal-surface.cpp"'
|
||||
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp mesh-quality.cpp
|
||||
polar-nc.cpp reflector.cpp shaper.cpp trimmer.cpp twist.cpp
|
||||
{,p}mesh-optimizer.cpp pmesh-fitting.cpp {,p}minimal-surface.cpp
|
||||
fit-node-position.cpp"'
|
||||
'"electromagnetics"
|
||||
"Electromagnetics miniapps:"
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
"adjoint_advection_diffusion.cpp cvsRoberts_ASAi_dns.cpp"'
|
||||
"cvsRoberts_ASAi_dns.cpp adjoint_advection_diffusion.cpp"'
|
||||
'"autodiff"
|
||||
"Autodiff miniapps:"
|
||||
"miniapps/autodiff"
|
||||
"seq_example.cpp seq_test.cpp par_example.cpp"'
|
||||
# 'seq_test.cpp' has no sample runs
|
||||
'"dpg"
|
||||
"DPG miniapps:"
|
||||
"miniapps/dpg"
|
||||
"{,p}{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
|
||||
'"gslib"
|
||||
"GSLIB miniapps:"
|
||||
"miniapps/gslib"
|
||||
"field-diff.cpp field-interp.cpp findpts.cpp schwarz_ex1.cpp pfindpts.cpp
|
||||
schwarz_ex1p.cpp"'
|
||||
'"hdiv-linear-solver"
|
||||
"H(div) linear solver miniapps:"
|
||||
"miniapps/hdiv-linear-solver"
|
||||
"grad_div.cpp darcy.cpp"'
|
||||
# 'miniapps/hooke/hooke.cpp' has no sample runs
|
||||
# todo: miniapps/mtop
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
"NURBS miniapps:"
|
||||
"miniapps/nurbs"
|
||||
"nurbs_ex1.cpp nurbs_ex1p.cpp nurbs_ex11p.cpp"'
|
||||
# todo: add other nurbs miniapps
|
||||
'"shifted"
|
||||
"Shifted miniapps:"
|
||||
"miniapps/shifted"
|
||||
"distance.cpp"'
|
||||
# todo: add other shifted miniapps
|
||||
'"solvers"
|
||||
"Solvers miniapps:"
|
||||
"miniapps/solvers"
|
||||
"block-solvers.cpp"'
|
||||
# todo: add other solvers miniapps
|
||||
# todo: miniapps/spde
|
||||
'"tools"
|
||||
"Tools miniapps:"
|
||||
"miniapps/tools"
|
||||
"convert-dc.cpp display-basis.cpp get-values.cpp load-dc.cpp
|
||||
lor-transfer.cpp"'
|
||||
# todo: add other tools miniapps
|
||||
'"toys"
|
||||
"Toys miniapps:"
|
||||
"miniapps/toys"
|
||||
@@ -386,7 +456,7 @@ function help_message()
|
||||
mfem_config [${mfem_config}]
|
||||
Set MFEM configuration options
|
||||
make [${make}], mpiexec [${mpiexec}], mpiexec_np [${mpiexec_np}]
|
||||
Their values can also set using the respective uppercase environment
|
||||
Their values can also be set using the respective uppercase environment
|
||||
variable
|
||||
mfem_build_dir [${mfem_build_dir}]
|
||||
Same as '-d': set this variable to something different from <mfem_dir>
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
dimension
|
||||
4
|
||||
|
||||
elements
|
||||
24
|
||||
1 7 4 5 8 11 13
|
||||
1 7 1 4 5 7 8
|
||||
1 7 1 4 5 8 11
|
||||
1 7 1 5 8 11 13
|
||||
1 7 1 5 7 8 13
|
||||
1 7 4 5 7 8 13
|
||||
1 7 1 3 4 8 11
|
||||
1 7 1 3 4 5 11
|
||||
1 7 1 5 10 11 13
|
||||
1 7 1 8 10 11 13
|
||||
1 7 1 3 5 10 11
|
||||
1 7 1 2 3 5 10
|
||||
1 7 0 1 3 4 8
|
||||
1 7 0 1 4 7 8
|
||||
1 7 1 5 6 7 13
|
||||
1 7 1 6 7 8 13
|
||||
1 7 6 7 8 13 15
|
||||
1 7 4 7 8 13 15
|
||||
1 7 4 8 12 13 15
|
||||
1 7 4 8 11 12 13
|
||||
1 7 6 8 13 14 15
|
||||
1 7 1 6 8 13 14
|
||||
1 7 1 8 9 10 13
|
||||
1 7 1 8 9 13 14
|
||||
|
||||
boundary
|
||||
48
|
||||
1 4 0 1 3 4
|
||||
1 4 0 1 3 8
|
||||
1 4 0 3 4 8
|
||||
1 4 0 1 4 7
|
||||
1 4 0 1 7 8
|
||||
1 4 0 4 7 8
|
||||
1 4 1 4 5 7
|
||||
1 4 1 3 8 11
|
||||
1 4 1 3 4 5
|
||||
1 4 1 5 10 13
|
||||
1 4 1 8 10 11
|
||||
1 4 1 3 10 11
|
||||
1 4 1 2 3 5
|
||||
1 4 1 2 3 10
|
||||
1 4 1 2 5 10
|
||||
1 4 1 5 6 7
|
||||
1 4 1 5 6 13
|
||||
1 4 1 6 7 8
|
||||
1 4 1 6 8 14
|
||||
1 4 1 6 13 14
|
||||
1 4 1 8 9 10
|
||||
1 4 1 9 10 13
|
||||
1 4 1 8 9 14
|
||||
1 4 1 9 13 14
|
||||
1 4 2 3 5 10
|
||||
1 4 3 4 8 11
|
||||
1 4 3 4 5 11
|
||||
1 4 3 5 10 11
|
||||
1 4 4 5 11 13
|
||||
1 4 4 5 7 13
|
||||
1 4 4 7 8 15
|
||||
1 4 4 7 13 15
|
||||
1 4 4 8 12 15
|
||||
1 4 4 12 13 15
|
||||
1 4 4 8 11 12
|
||||
1 4 4 11 12 13
|
||||
1 4 5 10 11 13
|
||||
1 4 5 6 7 13
|
||||
1 4 6 7 8 15
|
||||
1 4 6 7 13 15
|
||||
1 4 6 8 14 15
|
||||
1 4 6 13 14 15
|
||||
2 4 8 10 11 13
|
||||
2 4 8 12 13 15
|
||||
2 4 8 11 12 13
|
||||
2 4 8 13 14 15
|
||||
2 4 8 9 10 13
|
||||
2 4 8 9 13 14
|
||||
|
||||
vertices
|
||||
16
|
||||
4
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
@@ -1,102 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
dimension
|
||||
4
|
||||
|
||||
elements
|
||||
24
|
||||
17 8 4 5 8 11 13
|
||||
11 8 1 4 5 7 8
|
||||
15 8 1 4 5 8 11
|
||||
16 8 1 5 8 11 13
|
||||
12 8 1 5 7 8 13
|
||||
13 8 4 5 7 8 13
|
||||
7 8 1 3 4 8 11
|
||||
2 8 1 3 4 5 11
|
||||
4 8 1 5 10 11 13
|
||||
10 8 1 8 10 11 13
|
||||
3 8 1 3 5 10 11
|
||||
1 8 1 2 3 5 10
|
||||
6 8 0 1 3 4 8
|
||||
8 8 0 1 4 7 8
|
||||
5 8 1 5 6 7 13
|
||||
20 8 1 6 7 8 13
|
||||
19 8 6 7 8 13 15
|
||||
14 8 4 7 8 13 15
|
||||
21 8 4 8 12 13 15
|
||||
22 8 4 8 11 12 13
|
||||
23 8 6 8 13 14 15
|
||||
24 8 1 6 8 13 14
|
||||
9 8 1 8 9 10 13
|
||||
18 8 1 8 9 13 14
|
||||
|
||||
boundary
|
||||
48
|
||||
1 4 0 1 3 4
|
||||
3 4 0 1 3 8
|
||||
3 4 0 3 4 8
|
||||
1 4 0 1 4 7
|
||||
3 4 0 1 7 8
|
||||
3 4 0 4 7 8
|
||||
1 4 1 4 5 7
|
||||
3 4 1 3 8 11
|
||||
1 4 1 3 4 5
|
||||
3 4 1 5 10 13
|
||||
3 4 1 8 10 11
|
||||
3 4 1 3 10 11
|
||||
1 4 1 2 3 5
|
||||
3 4 1 2 3 10
|
||||
3 4 1 2 5 10
|
||||
1 4 1 5 6 7
|
||||
3 4 1 5 6 13
|
||||
3 4 1 6 7 8
|
||||
3 4 1 6 8 14
|
||||
3 4 1 6 13 14
|
||||
3 4 1 8 9 10
|
||||
3 4 1 9 10 13
|
||||
3 4 1 8 9 14
|
||||
3 4 1 9 13 14
|
||||
3 4 2 3 5 10
|
||||
3 4 3 4 8 11
|
||||
3 4 3 4 5 11
|
||||
3 4 3 5 10 11
|
||||
3 4 4 5 11 13
|
||||
3 4 4 5 7 13
|
||||
3 4 4 7 8 15
|
||||
3 4 4 7 13 15
|
||||
3 4 4 8 12 15
|
||||
3 4 4 12 13 15
|
||||
3 4 4 8 11 12
|
||||
3 4 4 11 12 13
|
||||
3 4 5 10 11 13
|
||||
3 4 5 6 7 13
|
||||
3 4 6 7 8 15
|
||||
3 4 6 7 13 15
|
||||
3 4 6 8 14 15
|
||||
3 4 6 13 14 15
|
||||
5 4 8 10 11 13
|
||||
5 4 8 12 13 15
|
||||
5 4 8 11 12 13
|
||||
5 4 8 13 14 15
|
||||
5 4 8 9 10 13
|
||||
5 4 8 9 13 14
|
||||
|
||||
vertices
|
||||
16
|
||||
4
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
@@ -1,231 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
dimension
|
||||
4
|
||||
|
||||
elements
|
||||
96
|
||||
1 8 0 1 7 8 9
|
||||
1 8 1 6 7 8 9
|
||||
1 8 4 5 6 8 9
|
||||
1 8 4 6 7 8 9
|
||||
1 8 0 1 3 8 9
|
||||
1 8 1 2 3 8 9
|
||||
1 8 0 4 7 8 9
|
||||
1 8 0 3 4 8 9
|
||||
1 8 1 2 6 8 9
|
||||
1 8 2 5 6 8 9
|
||||
1 8 3 4 5 8 9
|
||||
1 8 2 3 5 8 9
|
||||
1 8 9 10 11 17 18
|
||||
1 8 9 11 16 17 18
|
||||
1 8 9 14 15 16 18
|
||||
1 8 9 14 16 17 18
|
||||
1 8 9 10 11 13 18
|
||||
1 8 9 11 12 13 18
|
||||
1 8 9 10 14 17 18
|
||||
1 8 9 10 13 14 18
|
||||
1 8 9 11 12 16 18
|
||||
1 8 9 12 15 16 18
|
||||
1 8 9 13 14 15 18
|
||||
1 8 9 12 13 15 18
|
||||
1 8 9 12 15 16 19
|
||||
1 8 9 11 12 16 19
|
||||
1 8 1 6 9 11 19
|
||||
1 8 6 9 11 16 19
|
||||
1 8 1 2 6 9 19
|
||||
1 8 2 5 6 9 19
|
||||
1 8 2 5 9 15 19
|
||||
1 8 2 9 12 15 19
|
||||
1 8 5 6 9 16 19
|
||||
1 8 5 9 15 16 19
|
||||
1 8 1 9 11 12 19
|
||||
1 8 1 2 9 12 19
|
||||
1 8 9 10 13 14 20
|
||||
1 8 9 10 14 17 20
|
||||
1 8 0 9 10 17 20
|
||||
1 8 0 7 9 17 20
|
||||
1 8 0 3 4 9 20
|
||||
1 8 0 4 7 9 20
|
||||
1 8 3 4 9 13 20
|
||||
1 8 4 9 13 14 20
|
||||
1 8 4 7 9 14 20
|
||||
1 8 7 9 14 17 20
|
||||
1 8 0 3 9 10 20
|
||||
1 8 3 9 10 13 20
|
||||
1 8 2 5 9 15 21
|
||||
1 8 2 9 12 15 21
|
||||
1 8 2 3 5 9 21
|
||||
1 8 3 4 5 9 21
|
||||
1 8 9 13 14 15 21
|
||||
1 8 9 12 13 15 21
|
||||
1 8 4 5 9 14 21
|
||||
1 8 5 9 14 15 21
|
||||
1 8 2 3 9 12 21
|
||||
1 8 3 9 12 13 21
|
||||
1 8 3 4 9 13 21
|
||||
1 8 4 9 13 14 21
|
||||
1 8 1 6 9 11 22
|
||||
1 8 6 9 11 16 22
|
||||
1 8 6 7 9 16 22
|
||||
1 8 7 9 16 17 22
|
||||
1 8 0 7 9 17 22
|
||||
1 8 0 9 10 17 22
|
||||
1 8 0 1 9 10 22
|
||||
1 8 1 9 10 11 22
|
||||
1 8 0 1 7 9 22
|
||||
1 8 1 6 7 9 22
|
||||
1 8 9 10 11 17 22
|
||||
1 8 9 11 16 17 22
|
||||
1 8 5 9 15 16 23
|
||||
1 8 5 6 9 16 23
|
||||
1 8 6 7 9 16 23
|
||||
1 8 7 9 16 17 23
|
||||
1 8 7 9 14 17 23
|
||||
1 8 4 7 9 14 23
|
||||
1 8 4 5 9 14 23
|
||||
1 8 5 9 14 15 23
|
||||
1 8 4 6 7 9 23
|
||||
1 8 4 5 6 9 23
|
||||
1 8 9 14 15 16 23
|
||||
1 8 9 14 16 17 23
|
||||
1 8 1 2 9 12 24
|
||||
1 8 1 9 11 12 24
|
||||
1 8 0 1 9 10 24
|
||||
1 8 1 9 10 11 24
|
||||
1 8 0 3 9 10 24
|
||||
1 8 3 9 10 13 24
|
||||
1 8 2 3 9 12 24
|
||||
1 8 3 9 12 13 24
|
||||
1 8 1 2 3 9 24
|
||||
1 8 0 1 3 9 24
|
||||
1 8 9 10 11 13 24
|
||||
1 8 9 11 12 13 24
|
||||
|
||||
boundary
|
||||
96
|
||||
1 4 0 1 7 8
|
||||
1 4 0 1 3 8
|
||||
1 4 0 4 7 8
|
||||
1 4 0 3 4 8
|
||||
2 4 0 10 17 20
|
||||
2 4 0 7 17 20
|
||||
2 4 0 3 4 20
|
||||
2 4 0 4 7 20
|
||||
2 4 0 3 10 20
|
||||
2 4 0 7 17 22
|
||||
2 4 0 10 17 22
|
||||
2 4 0 1 10 22
|
||||
2 4 0 1 7 22
|
||||
2 4 0 1 10 24
|
||||
2 4 0 3 10 24
|
||||
2 4 0 1 3 24
|
||||
1 4 1 6 7 8
|
||||
1 4 1 2 3 8
|
||||
1 4 1 2 6 8
|
||||
2 4 1 6 11 19
|
||||
2 4 1 2 6 19
|
||||
2 4 1 11 12 19
|
||||
2 4 1 2 12 19
|
||||
2 4 1 6 11 22
|
||||
2 4 1 10 11 22
|
||||
2 4 1 6 7 22
|
||||
2 4 1 2 12 24
|
||||
2 4 1 11 12 24
|
||||
2 4 1 10 11 24
|
||||
2 4 1 2 3 24
|
||||
1 4 2 5 6 8
|
||||
1 4 2 3 5 8
|
||||
2 4 2 5 6 19
|
||||
2 4 2 5 15 19
|
||||
2 4 2 12 15 19
|
||||
2 4 2 5 15 21
|
||||
2 4 2 12 15 21
|
||||
2 4 2 3 5 21
|
||||
2 4 2 3 12 21
|
||||
2 4 2 3 12 24
|
||||
1 4 3 4 5 8
|
||||
2 4 3 4 13 20
|
||||
2 4 3 10 13 20
|
||||
2 4 3 4 5 21
|
||||
2 4 3 12 13 21
|
||||
2 4 3 4 13 21
|
||||
2 4 3 10 13 24
|
||||
2 4 3 12 13 24
|
||||
1 4 4 5 6 8
|
||||
1 4 4 6 7 8
|
||||
2 4 4 13 14 20
|
||||
2 4 4 7 14 20
|
||||
2 4 4 5 14 21
|
||||
2 4 4 13 14 21
|
||||
2 4 4 7 14 23
|
||||
2 4 4 5 14 23
|
||||
2 4 4 6 7 23
|
||||
2 4 4 5 6 23
|
||||
2 4 5 6 16 19
|
||||
2 4 5 15 16 19
|
||||
2 4 5 14 15 21
|
||||
2 4 5 15 16 23
|
||||
2 4 5 6 16 23
|
||||
2 4 5 14 15 23
|
||||
2 4 6 11 16 19
|
||||
2 4 6 11 16 22
|
||||
2 4 6 7 16 22
|
||||
2 4 6 7 16 23
|
||||
2 4 7 14 17 20
|
||||
2 4 7 16 17 22
|
||||
2 4 7 16 17 23
|
||||
2 4 7 14 17 23
|
||||
3 4 10 11 17 18
|
||||
3 4 10 11 13 18
|
||||
3 4 10 14 17 18
|
||||
3 4 10 13 14 18
|
||||
2 4 10 13 14 20
|
||||
2 4 10 14 17 20
|
||||
2 4 10 11 17 22
|
||||
2 4 10 11 13 24
|
||||
3 4 11 16 17 18
|
||||
3 4 11 12 13 18
|
||||
3 4 11 12 16 18
|
||||
2 4 11 12 16 19
|
||||
2 4 11 16 17 22
|
||||
2 4 11 12 13 24
|
||||
3 4 12 15 16 18
|
||||
3 4 12 13 15 18
|
||||
2 4 12 15 16 19
|
||||
2 4 12 13 15 21
|
||||
3 4 13 14 15 18
|
||||
2 4 13 14 15 21
|
||||
3 4 14 15 16 18
|
||||
3 4 14 16 17 18
|
||||
2 4 14 15 16 23
|
||||
2 4 14 16 17 23
|
||||
|
||||
vertices
|
||||
25
|
||||
4
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000
|
||||
0.5000000000000000 0.5000000000000000 0.5000000000000000 0.0000000000000000
|
||||
0.5000000000000000 0.5000000000000000 0.5000000000000000 0.5000000000000000
|
||||
0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 0.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000
|
||||
0.5000000000000000 0.5000000000000000 0.5000000000000000 1.0000000000000000
|
||||
1.0000000000000000 0.5000000000000000 0.5000000000000000 0.5000000000000000
|
||||
0.0000000000000000 0.5000000000000000 0.5000000000000000 0.5000000000000000
|
||||
0.5000000000000000 1.0000000000000000 0.5000000000000000 0.5000000000000000
|
||||
0.5000000000000000 0.0000000000000000 0.5000000000000000 0.5000000000000000
|
||||
0.5000000000000000 0.5000000000000000 1.0000000000000000 0.5000000000000000
|
||||
0.5000000000000000 0.5000000000000000 0.0000000000000000 0.5000000000000000
|
||||
@@ -18,9 +18,9 @@ elements
|
||||
boundary
|
||||
4
|
||||
1 1 0 1
|
||||
1 1 2 3
|
||||
1 1 3 0
|
||||
1 1 1 2
|
||||
2 1 2 3
|
||||
3 1 3 0
|
||||
4 1 1 2
|
||||
|
||||
edges
|
||||
4
|
||||
|
||||
@@ -16,21 +16,20 @@ dimension
|
||||
2
|
||||
|
||||
elements
|
||||
2
|
||||
1 2 2 0 1
|
||||
1 2 0 2 3
|
||||
1
|
||||
1 3 0 1 2 3
|
||||
|
||||
boundary
|
||||
4
|
||||
1 1 0 1
|
||||
1 1 1 2
|
||||
2 1 2 3
|
||||
1 1 3 0
|
||||
2 1 1 2
|
||||
3 1 2 3
|
||||
4 1 3 0
|
||||
|
||||
vertices
|
||||
4
|
||||
2
|
||||
0 0
|
||||
1 0
|
||||
1 1
|
||||
0 1
|
||||
1 0.3
|
||||
1.4 1.2
|
||||
0.25 1.34
|
||||
@@ -1,52 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
6
|
||||
1 4 3 1 7 5
|
||||
1 4 1 6 7 4
|
||||
1 4 6 1 0 2
|
||||
1 4 1 6 4 2
|
||||
1 4 6 1 3 0
|
||||
1 4 1 6 3 7
|
||||
|
||||
boundary
|
||||
12
|
||||
1 2 6 0 3
|
||||
1 2 0 6 2
|
||||
1 2 1 3 0
|
||||
1 2 3 1 5
|
||||
1 2 3 7 6
|
||||
1 2 7 3 5
|
||||
1 2 4 6 7
|
||||
1 2 6 4 2
|
||||
2 2 1 7 5
|
||||
2 2 7 1 4
|
||||
1 2 1 2 4
|
||||
1 2 2 1 0
|
||||
|
||||
vertices
|
||||
8
|
||||
3
|
||||
0 0 0
|
||||
0 0 1
|
||||
1 0 0
|
||||
0 1 0
|
||||
1 0 1
|
||||
0 1 1
|
||||
1 1 0
|
||||
1 1 1
|
||||
@@ -938,6 +938,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/config \
|
||||
@MFEM_SOURCE_DIR@/general \
|
||||
@MFEM_SOURCE_DIR@/linalg \
|
||||
@MFEM_SOURCE_DIR@/linalg/batched \
|
||||
@MFEM_SOURCE_DIR@/linalg/simd \
|
||||
@MFEM_SOURCE_DIR@/mesh \
|
||||
@MFEM_SOURCE_DIR@/mesh/submesh \
|
||||
@@ -1049,7 +1050,8 @@ RECURSIVE = NO
|
||||
EXCLUDE = @MFEM_SOURCE_DIR@/config/_config.hpp \
|
||||
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp \
|
||||
@MFEM_SOURCE_DIR@/general/tinyxml2.h \
|
||||
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp
|
||||
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp \
|
||||
@MFEM_SOURCE_DIR@/linalg/lapack.hpp
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -1208,13 +1210,13 @@ STRIP_CODE_COMMENTS = NO
|
||||
# entity all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
|
||||
# If the REFERENCES_RELATION tag is set to YES then for each documented function
|
||||
# all documented entities called/used by that function will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_RELATION = NO
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
|
||||
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
|
||||
|
||||
@@ -182,6 +182,21 @@ namespace mfem {
|
||||
* <a class="el" href="examples_2superlu_2ex1p_8cpp_source.html">1p</a>,
|
||||
* demonstrating the use of MFEM's \link superlu.hpp SuperLU integration\endlink.
|
||||
*
|
||||
* <H4>NURBS Examples</H4>
|
||||
* - Variants of Examples
|
||||
* <a class="el" href="nurbs__ex1_8cpp_source.html">1</a>,
|
||||
* <a class="el" href="nurbs__ex1p_8cpp_source.html">1p</a>,
|
||||
* <a class="el" href="nurbs__ex3_8cpp_source.html">3</a>,
|
||||
* <a class="el" href="nurbs__ex5_8cpp_source.html">5</a>,
|
||||
* <a class="el" href="nurbs__ex11p_8cpp_source.html">11p</a>, and
|
||||
* <a class="el" href="nurbs__ex24_8cpp_source.html">24</a>,
|
||||
* demonstrating howto perform NURBS-based Isogeometric Analysis.
|
||||
* - Variant of Example <a class="el" href="nurbs__patch__ex1_8cpp_source.html">1</a>: demonstrates the use of patch integration
|
||||
* - <a class="el" href="nurbs__solenoidal_8cpp_source.html">NURBS Divergence-free</a>: solve a solenoidal vector projection with NURBS-based H(div) elements
|
||||
* - <a class="el" href="nurbs__curveint_8cpp_source.html">NURBS Interpolation</a>: NURBS interpolation of given geometry
|
||||
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
|
||||
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
|
||||
*
|
||||
* <H3>Miniapps</H3>
|
||||
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
|
||||
* - <a class="el" href="tesla_8cpp_source.html">Tesla</a>: simple magnetostatics simulation code
|
||||
|
||||
+33
-3
@@ -50,6 +50,28 @@ list(APPEND ALL_EXE_SRCS
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
list(APPEND ALL_EXE_SRCS
|
||||
dfem_poisson.cpp
|
||||
dfem_stokes.cpp
|
||||
enzyme_interface_smoketest.cpp
|
||||
test_dfem_dual.cpp
|
||||
test_dfem.cpp
|
||||
dfem_laghos.cpp
|
||||
dfem_minimal_example.cpp
|
||||
dfem_test_diffusion_2d.cpp
|
||||
dfem_test_diffusion_3d.cpp
|
||||
dfem_test_diffusion_3d_refactor.cpp
|
||||
dfem_test_ordering.cpp
|
||||
dfem_test_vector_diffusion.cpp
|
||||
dfem_test_elasticity.cpp
|
||||
dfem_test_nonlinear_elasticity_3d.cpp
|
||||
dfem_test_nonlinear_diffusion_3d.cpp
|
||||
dfem_test_interpolate_linear_scalar.cpp
|
||||
dfem_test_interpolate_linear_scalar_3d.cpp
|
||||
dfem_test_interpolate_gradient_linear_scalar_3d.cpp
|
||||
dfem_test_mass_scalar_3d.cpp
|
||||
dfem_test_mass_scalar_2d.cpp
|
||||
dfem_test_interpolate_linear_vector.cpp
|
||||
dfem_test_interpolate_linear_vector_3d.cpp
|
||||
ex0p.cpp
|
||||
ex1p.cpp
|
||||
ex2p.cpp
|
||||
@@ -73,9 +95,6 @@ if (MFEM_USE_MPI)
|
||||
ex20p.cpp
|
||||
ex21p.cpp
|
||||
ex22p.cpp
|
||||
ex1p_4d.cpp
|
||||
ex3p_4d.cpp
|
||||
ex4D_DivSkew.cpp
|
||||
ex24p.cpp
|
||||
ex25p.cpp
|
||||
ex26p.cpp
|
||||
@@ -113,6 +132,17 @@ include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
# Add one executable per cpp file
|
||||
add_mfem_examples(ALL_EXE_SRCS)
|
||||
|
||||
target_link_libraries(dfem_poisson ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_stokes ClangEnzymeFlags)
|
||||
target_link_libraries(enzyme_interface_smoketest ClangEnzymeFlags)
|
||||
target_link_libraries(test_dfem ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_laghos ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_minimal_example ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_test_diffusion_3d ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_test_diffusion_3d_refactor ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_test_nonlinear_diffusion_3d ClangEnzymeFlags)
|
||||
target_link_libraries(dfem_test_nonlinear_elasticity_3d ClangEnzymeFlags)
|
||||
|
||||
# Add a test for each example
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/amgx/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/caliper,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 André L. Maravilha
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CXX_TIMER_HPP
|
||||
#define CXX_TIMER_HPP
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace cxxtimer {
|
||||
|
||||
/**
|
||||
* This class works as a stopwatch.
|
||||
*/
|
||||
class Timer {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param start
|
||||
* If true, the timer is started just after construction.
|
||||
* Otherwise, it will not be automatically started.
|
||||
*/
|
||||
Timer(bool start = false);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* @param other
|
||||
* The object to be copied.
|
||||
*/
|
||||
Timer(const Timer& other) = default;
|
||||
|
||||
/**
|
||||
* Transfer constructor.
|
||||
*
|
||||
* @param other
|
||||
* The object to be transferred.
|
||||
*/
|
||||
Timer(Timer&& other) = default;
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Timer() = default;
|
||||
|
||||
/**
|
||||
* Assignment operator by copy.
|
||||
*
|
||||
* @param other
|
||||
* The object to be copied.
|
||||
*
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
Timer& operator=(const Timer& other) = default;
|
||||
|
||||
/**
|
||||
* Assignment operator by transfer.
|
||||
*
|
||||
* @param other
|
||||
* The object to be transferred.
|
||||
*
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
Timer& operator=(Timer&& other) = default;
|
||||
|
||||
/**
|
||||
* Start/resume the timer.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop/pause the timer.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Reset the timer.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Return the elapsed time.
|
||||
*
|
||||
* @param duration_t
|
||||
* The duration type used to return the time elapsed. If not
|
||||
* specified, it returns the time as represented by
|
||||
* std::chrono::milliseconds.
|
||||
*
|
||||
* @return The elapsed time.
|
||||
*/
|
||||
template <class duration_t = std::chrono::milliseconds>
|
||||
typename duration_t::rep count() const;
|
||||
|
||||
private:
|
||||
|
||||
bool started_;
|
||||
bool paused_;
|
||||
std::chrono::steady_clock::time_point reference_;
|
||||
std::chrono::duration<long double> accumulated_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline cxxtimer::Timer::Timer(bool start) :
|
||||
started_(false), paused_(false),
|
||||
reference_(std::chrono::steady_clock::now()),
|
||||
accumulated_(std::chrono::duration<long double>(0)) {
|
||||
if (start) {
|
||||
this->start();
|
||||
}
|
||||
}
|
||||
|
||||
inline void cxxtimer::Timer::start() {
|
||||
if (!started_) {
|
||||
started_ = true;
|
||||
paused_ = false;
|
||||
accumulated_ = std::chrono::duration<long double>(0);
|
||||
reference_ = std::chrono::steady_clock::now();
|
||||
} else if (paused_) {
|
||||
reference_ = std::chrono::steady_clock::now();
|
||||
paused_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void cxxtimer::Timer::stop() {
|
||||
if (started_ && !paused_) {
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
accumulated_ = accumulated_ + std::chrono::duration_cast< std::chrono::duration<long double> >(now - reference_);
|
||||
paused_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void cxxtimer::Timer::reset() {
|
||||
if (started_) {
|
||||
started_ = false;
|
||||
paused_ = false;
|
||||
reference_ = std::chrono::steady_clock::now();
|
||||
accumulated_ = std::chrono::duration<long double>(0);
|
||||
}
|
||||
}
|
||||
|
||||
template <class duration_t>
|
||||
typename duration_t::rep cxxtimer::Timer::count() const {
|
||||
if (started_) {
|
||||
if (paused_) {
|
||||
return std::chrono::duration_cast<duration_t>(accumulated_).count();
|
||||
} else {
|
||||
return std::chrono::duration_cast<duration_t>(
|
||||
accumulated_ + (std::chrono::steady_clock::now() - reference_)).count();
|
||||
}
|
||||
} else {
|
||||
return duration_t(0).count();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_differentiable_operator.hpp"
|
||||
#include "dfem_element_operator.hpp"
|
||||
@@ -0,0 +1,232 @@
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels>::Action::create_action_callback(
|
||||
kernel_t kernel,
|
||||
mult_func_t &func)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(op.mesh);
|
||||
const int num_qp = op.integration_rule.GetNPoints();
|
||||
|
||||
// All solutions T-vector sizes make up the width of the operator, since
|
||||
// they are explicitly provided in Mult() for example.
|
||||
|
||||
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
|
||||
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
op.height = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op.height = op.residual_lsize;
|
||||
}
|
||||
|
||||
residual_l.SetSize(op.residual_lsize);
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
|
||||
|
||||
residual_e.SetSize(R->Height());
|
||||
|
||||
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(kernel.outputs),
|
||||
op.fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
auto input_fops = create_bare_fops(kernel.inputs);
|
||||
auto output_fops = create_bare_fops(kernel.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
|
||||
num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
op.fields,
|
||||
num_entities,
|
||||
kernel.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
residual_size_on_qp);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
print_shared_memory_info(shmem_info);
|
||||
|
||||
func = [=](Vector &ye_mem) mutable
|
||||
{
|
||||
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
|
||||
op.element_dof_ordering);
|
||||
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
|
||||
op.element_dof_ordering,
|
||||
op.solutions.size());
|
||||
|
||||
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
// printf("\ne: %d\n", e);
|
||||
// tic();
|
||||
auto input_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
|
||||
shmem_info.input_dtq_sizes,
|
||||
input_dtq_maps);
|
||||
|
||||
auto output_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
|
||||
shmem_info.output_dtq_sizes,
|
||||
output_dtq_maps);
|
||||
|
||||
auto fields_shmem = load_field_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::FIELD],
|
||||
shmem_info.field_sizes,
|
||||
kinput_to_field,
|
||||
wrapped_fields_e,
|
||||
e);
|
||||
|
||||
// These methods don't copy, they simply create a `DeviceTensor` object
|
||||
// that points to correct chunks of the shared memory pool.
|
||||
auto input_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto residual_shmem = load_residual_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT],
|
||||
shmem_info.residual_size,
|
||||
num_qp);
|
||||
|
||||
auto scratch_mem = load_scratch_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::TEMP],
|
||||
shmem_info.temp_sizes);
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
|
||||
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(output_fops),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_mem);
|
||||
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None>)
|
||||
{
|
||||
residual_l = ye_mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
R->MultTranspose(ye_mem, residual_l);
|
||||
}
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
y = r_local;
|
||||
};
|
||||
}
|
||||
else if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
double local_sum = r_local.Sum();
|
||||
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
op.mesh.GetComm());
|
||||
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(op.fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels
|
||||
>
|
||||
template <
|
||||
size_t derivative_idx
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels>::Derivative<derivative_idx>::assemble_hypreparmatrix_impl(
|
||||
kernel_t kernel, HypreParMatrix &A)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs,
|
||||
std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
auto output_fop = std::get<0>(kernel.outputs);
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
|
||||
int num_qp = op.integration_rule.GetNPoints();;
|
||||
int num_el = 0;
|
||||
int dimension = 0;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
{
|
||||
num_el = op.mesh.GetNE();
|
||||
dimension = op.dim;
|
||||
}
|
||||
else if (std::is_same_v<entity_t, Entity::Face>)
|
||||
{
|
||||
num_el = op.mesh.GetNumFacesWithGhost();
|
||||
dimension = op.dim - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false<entity_t>, "not implemented");
|
||||
}
|
||||
|
||||
std::vector<const DofToQuad*> dtqmaps;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
|
||||
// Allocate memory for fields on quadrature points
|
||||
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
for (auto &d_qp_mem : directions_qp_mem)
|
||||
{
|
||||
d_qp_mem = 0.0;
|
||||
}
|
||||
|
||||
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
|
||||
bool no_kinput_is_dependent = true;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_to_field[i] == derivative_idx)
|
||||
{
|
||||
no_kinput_is_dependent = false;
|
||||
kinput_is_dependent[i] = true;
|
||||
// out << "function input " << i << " is dependent on "
|
||||
// << op.fields[kinput_to_field[i]].field_label << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
kinput_is_dependent[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_kinput_is_dependent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
|
||||
DeviceTensor<1, const double> integration_weights(
|
||||
this->op.integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
Vector zero;
|
||||
GeometricFactorMaps geometric_factors
|
||||
{
|
||||
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
|
||||
};
|
||||
|
||||
// fields interpolated to the quadrature points in the order of
|
||||
// kernel function arguments
|
||||
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
|
||||
kinput_to_field);
|
||||
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
|
||||
kernel.inputs,
|
||||
dtqmaps,
|
||||
kinput_to_field,
|
||||
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
|
||||
koutput_to_field);
|
||||
|
||||
constexpr int fixed_output_idx = 0;
|
||||
auto Bv = output_dtq_ops[fixed_output_idx];
|
||||
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
|
||||
const int test_vdim = std::get<0>(kernel.outputs).vdim;
|
||||
|
||||
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
|
||||
int trial_vdim = 0;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_is_dependent[i])
|
||||
{
|
||||
trial_vdim = GetVDim(op.fields[kinput_to_field[i]]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All trial operators dimensions accumulated
|
||||
int total_trial_op_dim = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
|
||||
}
|
||||
|
||||
Vector a_qp_mem(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim *
|
||||
num_qp *
|
||||
num_el);
|
||||
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp,
|
||||
num_el);
|
||||
|
||||
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim * num_el);
|
||||
Ae_mem = 0.0;
|
||||
|
||||
auto A_e = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim, num_el);
|
||||
|
||||
for (int e = 0; e < num_el; e++)
|
||||
{
|
||||
map_fields_to_quadrature_data(
|
||||
input_qp, e, this->fields_e,
|
||||
kinput_to_field, input_dtq_ops,
|
||||
integration_weights, geometric_factors, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
size_t m_offset = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
auto Bu = dependent_input_dtq_ops[s];
|
||||
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
|
||||
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
|
||||
trial_op_dim, num_qp);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
d_qp(j, m, q) = 1.0;
|
||||
Vector f_qp = apply_kernel_fwddiff_enzyme(
|
||||
kernel.func,
|
||||
kernel_args,
|
||||
input_qp,
|
||||
kernel_shadow_args,
|
||||
directions_qp,
|
||||
q);
|
||||
// Vector f_qp = apply_kernel_fwddiff_dual(
|
||||
// kernel.func,
|
||||
// kernel_args,
|
||||
// input_qp,
|
||||
// directions_qp,
|
||||
// q);
|
||||
d_qp(j, m, q) = 0.0;
|
||||
|
||||
auto f = Reshape(f_qp.Read(), test_vdim, test_op_dim);
|
||||
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
a_qp(i, k, j, m + m_offset, q, e) = f(i, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector fhat_mem(test_op_dim * num_qp * dimension);
|
||||
auto fhat = Reshape(fhat_mem.ReadWrite(), test_vdim, test_op_dim, num_qp);
|
||||
for (int J = 0; J < num_trial_dof; J++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
fhat_mem = 0.0;
|
||||
size_t m_offset = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
auto Bu = dependent_input_dtq_ops[s];
|
||||
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
fhat(i, k, q) += a_qp(i, k, j, m + m_offset, q, e) * Bu(q, m, J);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
|
||||
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(bvtfhat, fhat, output_fop,
|
||||
output_dtq_ops[hardcoded_output_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool same_test_and_trial = false;
|
||||
if (koutput_to_field[0] ==
|
||||
kinput_to_field[dependent_input_dtq_ops[0].which_input])
|
||||
{
|
||||
same_test_and_trial = true;
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]].data);
|
||||
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&op.fields[koutput_to_field[0]].data);
|
||||
|
||||
SparseMatrix mat(test_fes->GlobalVSize(), trial_fes->GlobalVSize());
|
||||
|
||||
if (test_fes == nullptr)
|
||||
{
|
||||
MFEM_ABORT("error");
|
||||
}
|
||||
|
||||
for (int e = 0; e < num_el; e++)
|
||||
{
|
||||
auto tmp = Reshape(Ae_mem.ReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim,
|
||||
num_el);
|
||||
DenseMatrix A_e(&tmp(0, 0, e), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim);
|
||||
Array<int> test_vdofs, trial_vdofs;
|
||||
test_fes->GetElementVDofs(e, test_vdofs);
|
||||
GetElementVDofs(
|
||||
op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]], e,
|
||||
trial_vdofs);
|
||||
mat.AddSubMatrix(test_vdofs, trial_vdofs, A_e, 1);
|
||||
}
|
||||
mat.Finalize();
|
||||
|
||||
if (same_test_and_trial)
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
&mat);
|
||||
|
||||
A = *RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
|
||||
A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
trial_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
trial_fes->GetDofOffsets(),
|
||||
&mat);
|
||||
|
||||
A = *RAP(test_fes->Dof_TrueDof_Matrix(), &tmp, trial_fes->Dof_TrueDof_Matrix());
|
||||
// A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels
|
||||
>
|
||||
template <
|
||||
size_t derivative_idx
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels>::Derivative<derivative_idx>::assemble_vector_impl(
|
||||
kernel_t kernel, Vector &v)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs,
|
||||
std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
auto output_fop = std::get<0>(kernel.outputs);
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
|
||||
int num_qp = op.integration_rule.GetNPoints();;
|
||||
int num_el = 0;
|
||||
int dimension = 0;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
{
|
||||
num_el = op.mesh.GetNE();
|
||||
dimension = op.dim;
|
||||
}
|
||||
else if (std::is_same_v<entity_t, Entity::Face>)
|
||||
{
|
||||
num_el = op.mesh.GetNumFacesWithGhost();
|
||||
dimension = op.dim - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false<entity_t>, "not implemented");
|
||||
}
|
||||
|
||||
std::vector<const DofToQuad*> dtqmaps;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
|
||||
// Allocate memory for fields on quadrature points
|
||||
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
for (auto &d_qp_mem : directions_qp_mem)
|
||||
{
|
||||
d_qp_mem = 0.0;
|
||||
}
|
||||
|
||||
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
|
||||
bool no_kinput_is_dependent = true;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_to_field[i] == derivative_idx)
|
||||
{
|
||||
no_kinput_is_dependent = false;
|
||||
kinput_is_dependent[i] = true;
|
||||
// out << "function input " << i << " is dependent on "
|
||||
// << op.fields[kinput_to_field[i]].field_label << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
kinput_is_dependent[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_kinput_is_dependent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
|
||||
DeviceTensor<1, const double> integration_weights(
|
||||
this->op.integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
Vector zero;
|
||||
GeometricFactorMaps geometric_factors
|
||||
{
|
||||
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
|
||||
};
|
||||
|
||||
// fields interpolated to the quadrature points in the order of
|
||||
// kernel function arguments
|
||||
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
|
||||
kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
|
||||
kinput_to_field);
|
||||
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
|
||||
kernel.inputs,
|
||||
dtqmaps,
|
||||
kinput_to_field,
|
||||
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
|
||||
koutput_to_field);
|
||||
|
||||
constexpr int fixed_output_idx = 0;
|
||||
auto Bv = output_dtq_ops[fixed_output_idx];
|
||||
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
|
||||
const int test_vdim = std::get<0>(kernel.outputs).vdim;
|
||||
|
||||
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
|
||||
int trial_vdim = 0;
|
||||
int dependent_field_idx = -1;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_is_dependent[i])
|
||||
{
|
||||
dependent_field_idx = kinput_to_field[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trial_vdim = GetVDim(op.fields[dependent_field_idx]);
|
||||
|
||||
// All trial operators dimensions accumulated
|
||||
int total_trial_op_dim = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
|
||||
}
|
||||
|
||||
Vector a_qp_mem(trial_vdim * total_trial_op_dim * num_qp * num_el);
|
||||
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), trial_vdim,
|
||||
total_trial_op_dim, num_qp, num_el);
|
||||
Vector ve_mem(num_trial_dof * trial_vdim * num_el);
|
||||
ve_mem = 0.0;
|
||||
|
||||
for (int e = 0; e < num_el; e++)
|
||||
{
|
||||
map_fields_to_quadrature_data(
|
||||
input_qp, e, this->fields_e,
|
||||
kinput_to_field, input_dtq_ops,
|
||||
integration_weights, geometric_factors, kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
size_t m_offset = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
auto Bu = dependent_input_dtq_ops[s];
|
||||
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
|
||||
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
|
||||
trial_op_dim, num_qp);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
d_qp(j, m, q) = 1.0;
|
||||
// Vector f_qp = apply_kernel_fwddiff_dual(
|
||||
// kernel.func,
|
||||
// kernel_args,
|
||||
// input_qp,
|
||||
// directions_qp,
|
||||
// q);
|
||||
Vector f_qp = apply_kernel_fwddiff_enzyme(
|
||||
kernel.func,
|
||||
kernel_args,
|
||||
input_qp,
|
||||
kernel_shadow_args,
|
||||
directions_qp,
|
||||
q);
|
||||
d_qp(j, m, q) = 0.0;
|
||||
|
||||
auto f = Reshape(f_qp.Read(), test_vdim);
|
||||
a_qp(j, m + m_offset, q, e) = f(0);
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto shat = Reshape(ve_mem.ReadWrite(), num_trial_dof, trial_vdim, num_el);
|
||||
for (int J = 0; J < num_trial_dof; J++)
|
||||
{
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
size_t m_offset = 0;
|
||||
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
|
||||
{
|
||||
auto Bu = dependent_input_dtq_ops[s];
|
||||
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
|
||||
for (int q = 0; q < num_qp; q++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
shat(J, j, e) += a_qp(j, m + m_offset, q, e) * Bu(q, m, J);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto R = get_element_restriction(op.fields[dependent_field_idx],
|
||||
element_dof_ordering);
|
||||
Vector ve(R->Width());
|
||||
R->MultTranspose(ve_mem, ve);
|
||||
|
||||
get_prolongation(op.fields[dependent_field_idx])->MultTranspose(ve, v);
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels
|
||||
>
|
||||
template <
|
||||
size_t derivative_idx
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
|
||||
mult_func_t &func)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(op.mesh);
|
||||
const int num_qp = op.integration_rule.GetNPoints();
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = dtq[0]->nqpt;
|
||||
|
||||
derivative_action_e.SetSize(R->Height());
|
||||
|
||||
const int da_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(kernel.outputs),
|
||||
op.fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
auto input_fops = create_bare_fops(kernel.inputs);
|
||||
auto output_fops = create_bare_fops(kernel.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
|
||||
num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// Check which qf inputs are dependent on the dependent variable
|
||||
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
|
||||
bool no_kinput_is_dependent = true;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_to_field[i] == derivative_idx)
|
||||
{
|
||||
no_kinput_is_dependent = false;
|
||||
kinput_is_dependent[i] = true;
|
||||
// out << "function input " << i << " is dependent on "
|
||||
// << op.fields[kinput_to_field[i]].field_label << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
kinput_is_dependent[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool with_derivatives = true;
|
||||
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
op.fields,
|
||||
num_entities,
|
||||
kernel.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
da_size_on_qp,
|
||||
derivative_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
print_shared_memory_info(shmem_info);
|
||||
|
||||
func = [=](Vector &ye_mem) mutable
|
||||
{
|
||||
if (no_kinput_is_dependent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
restriction<entity_t>(direction, direction_l, direction_e,
|
||||
op.element_dof_ordering);
|
||||
|
||||
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.Read(), shmem_info.direction_size, num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
|
||||
{
|
||||
auto input_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
|
||||
shmem_info.input_dtq_sizes,
|
||||
input_dtq_maps);
|
||||
|
||||
auto output_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
|
||||
shmem_info.output_dtq_sizes,
|
||||
output_dtq_maps);
|
||||
|
||||
auto fields_shmem = load_field_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::FIELD],
|
||||
shmem_info.field_sizes,
|
||||
kinput_to_field,
|
||||
wrapped_fields_e,
|
||||
e);
|
||||
|
||||
auto direction_shmem = load_direction_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::DIRECTION],
|
||||
shmem_info.direction_size,
|
||||
wrapped_direction_e,
|
||||
e);
|
||||
|
||||
// These methods don't copy, they simply create a `DeviceTensor` object
|
||||
// that points to correct chunks of the shared memory pool.
|
||||
auto input_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto shadow_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::SHADOW],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto residual_shmem = load_residual_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT],
|
||||
shmem_info.residual_size,
|
||||
num_qp);
|
||||
|
||||
auto scratch_mem = load_scratch_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::TEMP],
|
||||
shmem_info.temp_sizes);
|
||||
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
zero_all(shadow_shmem);
|
||||
map_direction_to_quadrature_data_conditional<TensorProduct>(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
|
||||
scratch_mem, kinput_is_dependent,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
|
||||
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
|
||||
apply_kernel_fwddiff_enzyme(
|
||||
r,
|
||||
kernel.func,
|
||||
kernel_args,
|
||||
input_shmem,
|
||||
kernel_shadow_args,
|
||||
shadow_shmem,
|
||||
q);
|
||||
// printf(">>>>> WARNING: AD DISABLED\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(output_fops),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_mem);
|
||||
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
R->MultTranspose(ye_mem, derivative_action_l);
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
double local_sum = r_local.Sum();
|
||||
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
op.mesh.GetComm());
|
||||
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(op.fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,820 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <mfem.hpp>
|
||||
#include <type_traits>
|
||||
#include "dfem_fieldoperator.hpp"
|
||||
#include "dfem_parametricspace.hpp"
|
||||
#include "general/tic_toc.hpp"
|
||||
#include "tuple.hpp"
|
||||
#include <linalg/tensor.hpp>
|
||||
#include <enzyme/utils>
|
||||
#include <enzyme/enzyme>
|
||||
#include "dfem_util.hpp"
|
||||
#include "dfem_interpolate.hpp"
|
||||
#include "dfem_qfunction.hpp"
|
||||
#include "dfem_qfunction_dual.hpp"
|
||||
#include "dfem_integrate.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using mult_func_t = std::function<void(Vector &)>;
|
||||
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields = num_solutions + num_parameters,
|
||||
size_t num_kernels = mfem::tuple_size<kernels_tuple>::value,
|
||||
typename autodiff_t = AutoDiff::NativeDualNumber
|
||||
>
|
||||
class DifferentiableOperator : public Operator
|
||||
{
|
||||
public:
|
||||
DifferentiableOperator(DifferentiableOperator&) = delete;
|
||||
DifferentiableOperator(DifferentiableOperator&&) = delete;
|
||||
|
||||
class Action : public Operator
|
||||
{
|
||||
public:
|
||||
template <typename kernel_t>
|
||||
void create_action_callback(kernel_t kernel, mult_func_t &func);
|
||||
|
||||
template<std::size_t... idx>
|
||||
void materialize_callbacks(kernels_tuple &ks,
|
||||
std::array<mult_func_t, num_kernels>,
|
||||
std::index_sequence<idx...> const&)
|
||||
{
|
||||
(create_action_callback(mfem::get<idx>(ks), funcs[idx]), ...);
|
||||
}
|
||||
|
||||
Action(DifferentiableOperator &op, kernels_tuple &ks) : op(op)
|
||||
{
|
||||
materialize_callbacks(ks, funcs,
|
||||
std::make_index_sequence<mfem::tuple_size<kernels_tuple>::value>());
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
prolongation(op.solutions, x, solutions_l);
|
||||
|
||||
residual_e = 0.0;
|
||||
for (const auto &f : funcs)
|
||||
{
|
||||
f(residual_e);
|
||||
}
|
||||
|
||||
prolongation_transpose(residual_l, y);
|
||||
|
||||
y.SetSubVector(op.ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
void SetParameters(std::vector<Vector *> p) const
|
||||
{
|
||||
MFEM_ASSERT(num_parameters == p.size(),
|
||||
"number of parameters doesn't match descriptors");
|
||||
for (int i = 0; i < num_parameters; i++)
|
||||
{
|
||||
p[i]->Read();
|
||||
parameters_l[i] = *p[i];
|
||||
// parameters_l[i].MakeRef(p[i], 0, p[i]->Size());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
DifferentiableOperator &op;
|
||||
std::array<mult_func_t, num_kernels> funcs;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
mutable std::array<Vector, num_solutions> solutions_l;
|
||||
mutable std::array<Vector, num_parameters> parameters_l;
|
||||
mutable Vector residual_l;
|
||||
|
||||
mutable std::array<Vector, num_fields> fields_e;
|
||||
mutable Vector residual_e;
|
||||
};
|
||||
|
||||
template <size_t derivative_idx>
|
||||
class Derivative : public Operator
|
||||
{
|
||||
public:
|
||||
template <typename kernel_t>
|
||||
void create_callback(kernel_t kernel, mult_func_t &func);
|
||||
|
||||
template<std::size_t... idx>
|
||||
void materialize_callbacks(kernels_tuple &ks,
|
||||
std::array<mult_func_t, num_kernels>,
|
||||
std::index_sequence<idx...> const&)
|
||||
{
|
||||
(create_callback(mfem::get<idx>(ks), funcs[idx]), ...);
|
||||
}
|
||||
|
||||
Derivative(
|
||||
DifferentiableOperator &op,
|
||||
std::array<Vector *, num_solutions> &solutions,
|
||||
std::array<Vector *, num_parameters> ¶meters,
|
||||
kernels_tuple &ks) : op(op), ks(ks)
|
||||
{
|
||||
for (int i = 0; i < num_solutions; i++)
|
||||
{
|
||||
solutions_l[i] = *solutions[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_parameters; i++)
|
||||
{
|
||||
parameters_l[i] = *parameters[i];
|
||||
}
|
||||
|
||||
// G
|
||||
// if constexpr (std::is_same_v<OperatesOn, OperatesOnElement>)
|
||||
// {
|
||||
element_restriction(op.solutions, solutions_l, fields_e,
|
||||
op.element_dof_ordering);
|
||||
element_restriction(op.parameters, parameters_l, fields_e,
|
||||
op.element_dof_ordering,
|
||||
op.solutions.size());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// MFEM_ABORT("restriction not implemented for OperatesOn");
|
||||
// }
|
||||
direction = op.fields[derivative_idx];
|
||||
|
||||
size_t derivative_action_l_size = 0;
|
||||
for (auto &s : op.solutions)
|
||||
{
|
||||
derivative_action_l_size += GetVSize(s);
|
||||
this->width += GetTrueVSize(s);
|
||||
}
|
||||
this->height = derivative_action_l_size;
|
||||
derivative_action_l.SetSize(derivative_action_l_size);
|
||||
|
||||
materialize_callbacks(ks, funcs,
|
||||
std::make_index_sequence<num_kernels>());
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
current_direction_t = x;
|
||||
current_direction_t.SetSubVector(op.ess_tdof_list, 0.0);
|
||||
|
||||
prolongation(direction, current_direction_t, direction_l);
|
||||
|
||||
derivative_action_e = 0.0;
|
||||
for (const auto &f : funcs)
|
||||
{
|
||||
f(derivative_action_e);
|
||||
}
|
||||
|
||||
prolongation_transpose(derivative_action_l, y);
|
||||
|
||||
y.SetSubVector(op.ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
template <typename kernel_t>
|
||||
void assemble_vector_impl(kernel_t kernel, Vector &v);
|
||||
|
||||
template<std::size_t... idx>
|
||||
void assemble_vector(
|
||||
kernels_tuple &ks,
|
||||
Vector &v,
|
||||
std::index_sequence<idx...> const&)
|
||||
{
|
||||
(assemble_vector_impl(mfem::get<idx>(ks), v), ...);
|
||||
}
|
||||
|
||||
void Assemble(Vector &v)
|
||||
{
|
||||
assemble_vector(ks, v, std::make_index_sequence<num_kernels>());
|
||||
}
|
||||
|
||||
template <typename kernel_t>
|
||||
void assemble_hypreparmatrix_impl(kernel_t kernel, HypreParMatrix &A);
|
||||
|
||||
template<std::size_t... idx>
|
||||
void assemble_hypreparmatrix(
|
||||
kernels_tuple &ks,
|
||||
HypreParMatrix &A,
|
||||
std::index_sequence<idx...> const&)
|
||||
{
|
||||
(assemble_hypreparmatrix_impl(mfem::get<idx>(ks), A), ...);
|
||||
}
|
||||
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
assemble_hypreparmatrix(ks, A, std::make_index_sequence<num_kernels>());
|
||||
}
|
||||
|
||||
void AssembleDiagonal(Vector &d) const override {}
|
||||
|
||||
protected:
|
||||
DifferentiableOperator &op;
|
||||
kernels_tuple &ks;
|
||||
std::array<mult_func_t, num_kernels> funcs;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
FieldDescriptor direction;
|
||||
|
||||
std::array<Vector, num_solutions> solutions_l;
|
||||
std::array<Vector, num_parameters> parameters_l;
|
||||
mutable Vector direction_l;
|
||||
mutable Vector derivative_action_l;
|
||||
|
||||
mutable std::array<Vector, num_fields> fields_e;
|
||||
mutable Vector direction_e;
|
||||
mutable Vector derivative_action_e;
|
||||
|
||||
mutable Vector current_direction_t;
|
||||
};
|
||||
|
||||
DifferentiableOperator(std::array<FieldDescriptor, num_solutions> s,
|
||||
std::array<FieldDescriptor, num_parameters> p,
|
||||
kernels_tuple ks,
|
||||
ParMesh &m,
|
||||
autodiff_t ad = AutoDiff::NativeDualNumber{}) :
|
||||
kernels(ks),
|
||||
mesh(m),
|
||||
dim(mesh.Dimension()),
|
||||
solutions(s),
|
||||
parameters(p)
|
||||
{
|
||||
for (int i = 0; i < num_solutions; i++)
|
||||
{
|
||||
fields[i] = solutions[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_parameters; i++)
|
||||
{
|
||||
fields[i + num_solutions] = parameters[i];
|
||||
}
|
||||
|
||||
residual.reset(new Action(*this, kernels));
|
||||
}
|
||||
|
||||
void SetParameters(std::vector<Vector *> p) const
|
||||
{
|
||||
residual->SetParameters(p);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
residual->Mult(x, y);
|
||||
}
|
||||
|
||||
template <int derivative_idx>
|
||||
std::shared_ptr<Derivative<derivative_idx>>
|
||||
GetDerivativeWrt(std::array<Vector *, num_solutions> solutions,
|
||||
std::array<Vector *, num_parameters> parameters)
|
||||
{
|
||||
return std::shared_ptr<Derivative<derivative_idx>>(
|
||||
new Derivative<derivative_idx>(*this, solutions, parameters, kernels));
|
||||
}
|
||||
|
||||
void SetEssentialTrueDofs(const Array<int> &l)
|
||||
{
|
||||
l.Copy(ess_tdof_list);
|
||||
}
|
||||
|
||||
kernels_tuple kernels;
|
||||
ParMesh &mesh;
|
||||
const int dim;
|
||||
|
||||
std::array<FieldDescriptor, num_solutions> solutions;
|
||||
std::array<FieldDescriptor, num_parameters> parameters;
|
||||
// solutions and parameters
|
||||
std::array<FieldDescriptor, num_fields> fields;
|
||||
|
||||
int residual_lsize = 0;
|
||||
|
||||
mutable std::array<Vector, num_solutions> current_state_l;
|
||||
mutable Vector direction_l;
|
||||
|
||||
mutable Vector current_direction_t;
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
static constexpr ElementDofOrdering element_dof_ordering =
|
||||
ElementDofOrdering::LEXICOGRAPHIC;
|
||||
|
||||
static constexpr DofToQuad::Mode doftoquad_mode =
|
||||
DofToQuad::Mode::TENSOR;
|
||||
|
||||
// static constexpr ElementDofOrdering element_dof_ordering =
|
||||
// ElementDofOrdering::NATIVE;
|
||||
|
||||
// static constexpr DofToQuad::Mode doftoquad_mode =
|
||||
// DofToQuad::Mode::FULL;
|
||||
|
||||
std::shared_ptr<Action> residual;
|
||||
};
|
||||
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels,
|
||||
typename autodiff_t
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels,
|
||||
autodiff_t>::Action::create_action_callback(
|
||||
kernel_t kernel,
|
||||
mult_func_t &func)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(op.mesh);
|
||||
const int num_qp = kernel.integration_rule.GetNPoints();
|
||||
|
||||
// All solutions T-vector sizes make up the width of the operator, since
|
||||
// they are explicitly provided in Mult() for example.
|
||||
|
||||
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
|
||||
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
op.height = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op.height = op.residual_lsize;
|
||||
}
|
||||
|
||||
residual_l.SetSize(op.residual_lsize);
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
|
||||
|
||||
residual_e.SetSize(R->Height());
|
||||
|
||||
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(kernel.outputs),
|
||||
op.fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
auto input_fops = create_bare_fops(kernel.inputs);
|
||||
auto output_fops = create_bare_fops(kernel.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
op.fields,
|
||||
num_entities,
|
||||
kernel.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
residual_size_on_qp);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
func = [=](Vector &ye_mem) mutable
|
||||
{
|
||||
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
|
||||
op.element_dof_ordering);
|
||||
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
|
||||
op.element_dof_ordering,
|
||||
op.solutions.size());
|
||||
|
||||
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
// printf("\ne: %d\n", e);
|
||||
// tic();
|
||||
auto input_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
|
||||
shmem_info.input_dtq_sizes,
|
||||
input_dtq_maps);
|
||||
|
||||
auto output_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
|
||||
shmem_info.output_dtq_sizes,
|
||||
output_dtq_maps);
|
||||
|
||||
auto fields_shmem = load_field_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::FIELD],
|
||||
shmem_info.field_sizes,
|
||||
kinput_to_field,
|
||||
input_fops,
|
||||
wrapped_fields_e,
|
||||
e,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// These functions don't copy, they simply create a `DeviceTensor` object
|
||||
// that points to correct chunks of the shared memory pool.
|
||||
auto input_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto residual_shmem = load_residual_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT],
|
||||
shmem_info.residual_size,
|
||||
num_qp);
|
||||
|
||||
auto scratch_mem = load_scratch_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::TEMP],
|
||||
shmem_info.temp_sizes);
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
|
||||
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// tic();
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(output_fops),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_mem);
|
||||
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None>)
|
||||
{
|
||||
residual_l = ye_mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
R->MultTranspose(ye_mem, residual_l);
|
||||
}
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
y = r_local;
|
||||
};
|
||||
}
|
||||
else if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
double local_sum = r_local.Sum();
|
||||
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
op.mesh.GetComm());
|
||||
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(op.fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename kernels_tuple,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t num_fields,
|
||||
size_t num_kernels,
|
||||
typename autodiff_t
|
||||
>
|
||||
template <
|
||||
size_t derivative_idx
|
||||
>
|
||||
template <
|
||||
typename kernel_t
|
||||
>
|
||||
void DifferentiableOperator<kernels_tuple,
|
||||
num_solutions,
|
||||
num_parameters,
|
||||
num_fields,
|
||||
num_kernels,
|
||||
autodiff_t>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
|
||||
mult_func_t &func)
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(op.mesh);
|
||||
const int num_qp = kernel.integration_rule.GetNPoints();
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = dtq[0]->nqpt;
|
||||
|
||||
derivative_action_e.SetSize(R->Height());
|
||||
|
||||
const int da_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(kernel.outputs),
|
||||
op.fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
auto input_fops = create_bare_fops(kernel.inputs);
|
||||
auto output_fops = create_bare_fops(kernel.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// Check which qf inputs are dependent on the dependent variable
|
||||
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
|
||||
bool no_kinput_is_dependent = true;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_to_field[i] == derivative_idx)
|
||||
{
|
||||
no_kinput_is_dependent = false;
|
||||
kinput_is_dependent[i] = true;
|
||||
// out << "function input " << i << " is dependent on "
|
||||
// << op.fields[kinput_to_field[i]].field_label << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
kinput_is_dependent[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool with_derivatives = true;
|
||||
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
op.fields,
|
||||
num_entities,
|
||||
kernel.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
da_size_on_qp,
|
||||
derivative_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
func = [=](Vector &ye_mem) mutable
|
||||
{
|
||||
if (no_kinput_is_dependent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
restriction<entity_t>(direction, direction_l, direction_e,
|
||||
op.element_dof_ordering);
|
||||
|
||||
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
|
||||
{
|
||||
auto input_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
|
||||
shmem_info.input_dtq_sizes,
|
||||
input_dtq_maps);
|
||||
|
||||
auto output_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
|
||||
shmem_info.output_dtq_sizes,
|
||||
output_dtq_maps);
|
||||
|
||||
auto fields_shmem = load_field_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::FIELD],
|
||||
shmem_info.field_sizes,
|
||||
kinput_to_field,
|
||||
input_fops,
|
||||
wrapped_fields_e,
|
||||
e,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto direction_shmem = load_direction_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::DIRECTION],
|
||||
shmem_info.direction_size,
|
||||
wrapped_direction_e,
|
||||
e);
|
||||
|
||||
// These methods don't copy, they simply create a `DeviceTensor` object
|
||||
// that points to correct chunks of the shared memory pool.
|
||||
auto input_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto shadow_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::SHADOW],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto residual_shmem = load_residual_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT],
|
||||
shmem_info.residual_size,
|
||||
num_qp);
|
||||
|
||||
auto scratch_mem = load_scratch_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::TEMP],
|
||||
shmem_info.temp_sizes);
|
||||
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
zero_all(shadow_shmem);
|
||||
map_direction_to_quadrature_data_conditional<TensorProduct>(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
|
||||
scratch_mem, kinput_is_dependent,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
|
||||
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
|
||||
if constexpr (std::is_same_v<autodiff_t, AutoDiff::EnzymeForward>)
|
||||
{
|
||||
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(
|
||||
r,
|
||||
kernel.func,
|
||||
kernel_args,
|
||||
kernel_shadow_args,
|
||||
input_shmem,
|
||||
shadow_shmem,
|
||||
q);
|
||||
}
|
||||
else if constexpr (std::is_same_v<autodiff_t, AutoDiff::NativeDualNumber>)
|
||||
{
|
||||
apply_kernel_native_dual(
|
||||
r,
|
||||
kernel.func,
|
||||
kernel_args,
|
||||
input_shmem,
|
||||
shadow_shmem,
|
||||
q);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false<autodiff_t>, "unknown autodiff type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(output_fops),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_mem);
|
||||
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
R->MultTranspose(ye_mem, derivative_action_l);
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
double local_sum = r_local.Sum();
|
||||
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
op.mesh.GetComm());
|
||||
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(op.fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "dfem_util.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename func_t, typename input_t, typename output_t, typename dependency_map_t>
|
||||
struct ElementOperator;
|
||||
|
||||
template <typename func_t, typename... input_ts, typename... output_ts, typename dependency_map_t>
|
||||
struct ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>, dependency_map_t>
|
||||
{
|
||||
using entity_t = Entity::Element;
|
||||
|
||||
func_t qfunc;
|
||||
|
||||
mfem::tuple<input_ts...> inputs;
|
||||
mfem::tuple<output_ts...> outputs;
|
||||
|
||||
dependency_map_t dependency_map;
|
||||
|
||||
using qf_param_ts = typename create_function_signature<
|
||||
decltype(&func_t::operator())>::type::parameter_ts;
|
||||
using qf_output_t = typename create_function_signature<
|
||||
decltype(&func_t::operator())>::type::return_t;
|
||||
|
||||
static constexpr size_t num_inputs =
|
||||
mfem::tuple_size<decltype(inputs)>::value;
|
||||
static constexpr size_t num_outputs =
|
||||
mfem::tuple_size<decltype(outputs)>::value;
|
||||
|
||||
ElementOperator(func_t qfunc,
|
||||
mfem::tuple<input_ts...> inputs,
|
||||
mfem::tuple<output_ts...> outputs)
|
||||
: qfunc(qfunc), inputs(inputs), outputs(outputs),
|
||||
dependency_map(make_dependency_map(inputs))
|
||||
{
|
||||
// Consistency checks
|
||||
if constexpr (num_outputs > 1)
|
||||
{
|
||||
static_assert(always_false<func_t>,
|
||||
"more than one output per kernel is not supported right now");
|
||||
}
|
||||
|
||||
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
|
||||
static_assert(num_qfinputs == num_inputs,
|
||||
"kernel function inputs and descriptor inputs have to match");
|
||||
|
||||
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
|
||||
static_assert(num_qf_outputs == num_qf_outputs,
|
||||
"kernel function outputs and descriptor outputs have to match");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename func_t, typename... input_ts, typename... output_ts>
|
||||
ElementOperator(func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>)
|
||||
-> ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>,
|
||||
decltype(make_dependency_map(std::declval<mfem::tuple<input_ts...>>()))>;
|
||||
|
||||
// template <typename func_t, typename input_t, typename output_t>
|
||||
// struct BoundaryElementOperator : public
|
||||
// ElementOperator<func_t, input_t, output_t>
|
||||
// {
|
||||
// public:
|
||||
// using entity_t = Entity::BoundaryElement;
|
||||
// BoundaryElementOperator(func_t func, input_t inputs, output_t outputs)
|
||||
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
|
||||
// };
|
||||
|
||||
// template <typename func_t, typename input_t, typename output_t>
|
||||
// struct FaceOperator : public
|
||||
// ElementOperator<func_t, input_t, output_t>
|
||||
// {
|
||||
// public:
|
||||
// using entity_t = Entity::Face;
|
||||
// FaceOperator(func_t func, input_t inputs, output_t outputs)
|
||||
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
|
||||
// };
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <int FIELD_ID = -1>
|
||||
class FieldOperator
|
||||
{
|
||||
public:
|
||||
constexpr FieldOperator(int size_on_qp = 0) :
|
||||
size_on_qp(size_on_qp) {};
|
||||
|
||||
static constexpr int GetFieldId() { return FIELD_ID; }
|
||||
|
||||
int size_on_qp = -1;
|
||||
|
||||
int dim = -1;
|
||||
|
||||
int vdim = -1;
|
||||
};
|
||||
|
||||
template <int FIELD_ID = -1>
|
||||
class None : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr None() : FieldOperator<FIELD_ID>() {}
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_none_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_none_fop<None<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DisableAD
|
||||
{
|
||||
T& operator()() const { return fop; }
|
||||
T fop;
|
||||
};
|
||||
|
||||
class Weight : public FieldOperator<-1>
|
||||
{
|
||||
public:
|
||||
constexpr Weight() : FieldOperator<-1>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_weight_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_weight_fop<Weight>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <int FIELD_ID = -1>
|
||||
class Value : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Value() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_value_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_value_fop<Value<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_value_fop<DisableAD<T>>
|
||||
{
|
||||
static const bool value = is_value_fop<T>::value;
|
||||
};
|
||||
|
||||
template <int FIELD_ID = -1>
|
||||
class Gradient : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_gradient_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_gradient_fop<Gradient<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
// class FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// FieldOperator(std::string field_label = "", int size_on_qp = 0) :
|
||||
// field_label(field_label),
|
||||
// size_on_qp(size_on_qp) {};
|
||||
|
||||
// std::string field_label;
|
||||
|
||||
// int size_on_qp = -1;
|
||||
|
||||
// int dim = -1;
|
||||
|
||||
// int vdim = -1;
|
||||
// };
|
||||
|
||||
// class None : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// None(std::string field_label) :
|
||||
// FieldOperator(field_label) {}
|
||||
// };
|
||||
|
||||
// class Weight : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// Weight() : FieldOperator("quadrature_weights") {};
|
||||
// };
|
||||
|
||||
// class Value : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// Value(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class Gradient : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// Gradient(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class Curl : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// Curl(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class Div : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// Div(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class FaceValueLeft : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// FaceValueLeft(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class FaceValueRight : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// FaceValueRight(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class FaceNormal : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// FaceNormal(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// class One : public FieldOperator
|
||||
// {
|
||||
// public:
|
||||
// One(std::string field_label) : FieldOperator(field_label) {};
|
||||
// };
|
||||
|
||||
// namespace BareFieldOperator
|
||||
// {
|
||||
|
||||
// struct Base
|
||||
// {
|
||||
// Base(FieldOperator &o)
|
||||
// {
|
||||
// size_on_qp = o.size_on_qp;
|
||||
// dim = o.dim;
|
||||
// vdim = o.vdim;
|
||||
// };
|
||||
// int size_on_qp = -1;
|
||||
// int dim = -1;
|
||||
// int vdim = -1;
|
||||
// };
|
||||
|
||||
// struct None : Base
|
||||
// {
|
||||
// None(FieldOperator &o) : Base(o) {}
|
||||
// };
|
||||
|
||||
// struct Weight : Base
|
||||
// {
|
||||
// Weight(FieldOperator &o) : Base(o) {}
|
||||
// };
|
||||
|
||||
// struct Value : Base
|
||||
// {
|
||||
// Value(FieldOperator &o) : Base(o) {}
|
||||
// };
|
||||
|
||||
// struct Gradient : Base
|
||||
// {
|
||||
// Gradient(FieldOperator &o) : Base(o) {}
|
||||
// };
|
||||
|
||||
// }
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,290 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_util.hpp"
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_impl(DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq)
|
||||
{
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
// assuming the quadrature point residual has to "play nice with
|
||||
// the test function"
|
||||
if constexpr (std::is_same_v<std::decay_t<output_t>, Value<>>)
|
||||
{
|
||||
const auto [num_qp, cdim, num_dof] = B.GetShape();
|
||||
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * f(vd, 0, qp);
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<output_t>, Gradient<>>)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += G(qp, d, dof) * f(vd, d, qp);
|
||||
}
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else if constexpr (std::is_same_v<std::decay_t<output_t>, One>)
|
||||
// {
|
||||
// // This is the "integral over all quadrature points type" applying
|
||||
// // B = 1 s.t. B^T * C \in R^1.
|
||||
// const auto [a, b, num_qp] = B.GetShape();
|
||||
// auto cc = Reshape(&c(0, 0, 0), num_qp);
|
||||
// for (int i = 0; i < num_qp; i++)
|
||||
// {
|
||||
// y(0, 0) += cc(i);
|
||||
// }
|
||||
// }
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<output_t>, None<>>)
|
||||
{
|
||||
const auto [vdim, dim, num_qp] = G.GetShape();
|
||||
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
|
||||
auto yy = Reshape(&y(0, 0), num_qp * vdim);
|
||||
for (int i = 0; i < num_qp * vdim; i++)
|
||||
{
|
||||
yy(i) = cc(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_tensor_impl(DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(qz, dy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
acc += s1(qz, dy, dx) * B(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
|
||||
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
|
||||
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
|
||||
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = uvw[0];
|
||||
s1(qz, qy, dx) = uvw[1];
|
||||
s2(qz, qy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
|
||||
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s3(qz, dy, dx) = uvw[0];
|
||||
s4(qz, dy, dx) = uvw[1];
|
||||
s5(qz, dy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_none_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = NonTensorProduct, typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields(DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, NonTensorProduct>)
|
||||
{
|
||||
map_quadrature_data_to_fields_impl(y, f, output, dtq);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TensorProduct>)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_util.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
|
||||
}
|
||||
s0(dz, dy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dz, dy, qx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(dz, qy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
acc += s1(dz, qy, qx) * B(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, qx, qy, qz) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const int dim = input.dim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
const real_t f = field(dx, dy, dz, vd);
|
||||
uv[0] += f * B(qx, 0, dx);
|
||||
uv[1] += f * G(qx, 0, dx);
|
||||
}
|
||||
s0(dz, dy, qx) = uv[0];
|
||||
s1(dz, dy, qx) = uv[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
const real_t s0i = s0(dz, dy, qx);
|
||||
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
|
||||
uvw[1] += s0i * G(qy, 0, dy);
|
||||
uvw[2] += s0i * B(qy, 0, dy);
|
||||
}
|
||||
s2(dz, qy, qx) = uvw[0];
|
||||
s3(dz, qy, qx) = uvw[1];
|
||||
s4(dz, qy, qx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, 0, qx, qy, qz) = uvw[0];
|
||||
fqp(vd, 1, qx, qy, qz) = uvw[1];
|
||||
fqp(vd, 2, qx, qy, qz) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
// TODO: eeek
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
f(qx, qy, qz) = w(qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_none_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data(
|
||||
DeviceTensor<2> field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1, const double> &field_e,
|
||||
field_operator_t &input,
|
||||
DeviceTensor<1, const double> integration_weights)
|
||||
{
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
if constexpr (is_value_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, dim, num_dof] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * field(dof, vd);
|
||||
}
|
||||
field_qp(vd, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<field_operator_t>::value)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
double acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += G(qp, d, dof) * field(dof, vd);
|
||||
}
|
||||
f(vd, d, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// else if constexpr (std::is_same_v<field_operator_t, FaceNormal>)
|
||||
// {
|
||||
// auto normal = geometric_factors.normal;
|
||||
// auto [num_qp, dim, num_entities] = normal.GetShape();
|
||||
// auto f = Reshape(&field_qp[0], dim, num_qp);
|
||||
// for (int qp = 0; qp < num_qp; qp++)
|
||||
// {
|
||||
// for (int d = 0; d < dim; d++)
|
||||
// {
|
||||
// f(d, qp) = normal(qp, d, entity_idx);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (std::is_same_v<field_operator_t, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
auto f = Reshape(&field_qp[0], num_qp);
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
f(qp) = integration_weights(qp);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_none_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const int size_on_qp = input.size_on_qp;
|
||||
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
|
||||
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
|
||||
for (int i = 0; i < size_on_qp * num_qp; i++)
|
||||
{
|
||||
f(i) = field(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false<field_operator_t>,
|
||||
"can't map field to quadrature data");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = NonTensorProduct, typename field_operator_ts, size_t num_inputs, size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<int, num_inputs> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, TensorProduct>)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product(
|
||||
fields_qp[i],
|
||||
dtqmaps[i],
|
||||
fields_e[input_to_field[i]],
|
||||
mfem::get<i>(fops),
|
||||
integration_weights,
|
||||
scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
fields_qp[i],
|
||||
dtqmaps[i],
|
||||
fields_e[i],
|
||||
mfem::get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data_conditional(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const DofToQuadMap &dtqmap,
|
||||
field_operator_t &fop,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const bool &condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, TensorProduct>)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product(field_qp, dtqmap,
|
||||
field_e, fop,
|
||||
integration_weights,
|
||||
scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(field_qp, dtqmap, field_e, fop,
|
||||
integration_weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = NonTensorProduct, size_t num_fields, size_t num_kinputs, typename field_operator_ts, std::size_t... i>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_fields_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_kinputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1, const double>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_kinputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_kinputs> &conditions,
|
||||
std::index_sequence<i...>)
|
||||
{
|
||||
(map_field_to_quadrature_data_conditional<T>(fields_qp[i],
|
||||
fields_e[i],
|
||||
dtqmaps[i],
|
||||
mfem::get<i>(fops),
|
||||
integration_weights,
|
||||
scratch_mem,
|
||||
conditions[i]),
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename T = NonTensorProduct, size_t num_inputs, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_direction_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
|
||||
const DeviceTensor<1> &direction_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
map_field_to_quadrature_data_conditional<T>(directions_qp[i],
|
||||
direction_e,
|
||||
dtqmaps[i],
|
||||
mfem::get<i>(fops),
|
||||
integration_weights,
|
||||
scratch_mem,
|
||||
conditions[i]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ParametricSpace
|
||||
{
|
||||
|
||||
public:
|
||||
ParametricSpace(int spatial_dim, int local_size, int element_size,
|
||||
int total_size) :
|
||||
spatial_dim(spatial_dim),
|
||||
local_size(local_size),
|
||||
element_size(element_size),
|
||||
total_size(total_size),
|
||||
identity(total_size)
|
||||
{
|
||||
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
|
||||
dtq.nqpt = dtq.ndof;
|
||||
}
|
||||
|
||||
ParametricSpace(int local_size) :
|
||||
local_size(local_size),
|
||||
element_size(local_size),
|
||||
total_size(local_size),
|
||||
identity(local_size)
|
||||
{
|
||||
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
|
||||
dtq.nqpt = dtq.ndof;
|
||||
}
|
||||
|
||||
int Dimension() const
|
||||
{
|
||||
return spatial_dim;
|
||||
}
|
||||
|
||||
int GetLocalSize() const
|
||||
{
|
||||
return local_size;
|
||||
}
|
||||
|
||||
int GetElementSize() const
|
||||
{
|
||||
return element_size;
|
||||
}
|
||||
|
||||
int GetTotalSize() const
|
||||
{
|
||||
return total_size;
|
||||
}
|
||||
|
||||
const DofToQuad &GetDofToQuad() const
|
||||
{
|
||||
return dtq;
|
||||
}
|
||||
|
||||
const Operator *GetProlongation() const
|
||||
{
|
||||
return &identity;
|
||||
}
|
||||
|
||||
const Operator *GetRestriction() const
|
||||
{
|
||||
return &identity;
|
||||
}
|
||||
|
||||
private:
|
||||
int spatial_dim;
|
||||
|
||||
// Hint for the local dimension. E.g. the size on the quadrature point or vdim.
|
||||
int local_size;
|
||||
|
||||
// Size of the data on an element
|
||||
int element_size;
|
||||
|
||||
int total_size;
|
||||
|
||||
IdentityOperator identity;
|
||||
|
||||
DofToQuad dtq;
|
||||
};
|
||||
|
||||
class ParametricFunction : public Vector
|
||||
{
|
||||
public:
|
||||
ParametricFunction(ParametricSpace &space) :
|
||||
Vector(space.GetTotalSize()),
|
||||
space(space)
|
||||
{}
|
||||
|
||||
ParametricSpace &space;
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
#pragma once
|
||||
#include "dfem_util.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
#include <enzyme/utils>
|
||||
#include <enzyme/enzyme>
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(const T0 &, T1 &)
|
||||
{
|
||||
static_assert(always_false<T0, T1>,
|
||||
"process_kf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
internal::tensor<T> &arg)
|
||||
{
|
||||
arg(0) = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
process_kf_arg(u_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename kf_args, std::size_t... i>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
kf_args &args,
|
||||
const int &qp,
|
||||
std::index_sequence<i...>)
|
||||
{
|
||||
(process_kf_arg(u[i], mfem::get<i>(args), qp), ...);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1> inline
|
||||
Vector process_kf_result(T0, T1)
|
||||
{
|
||||
static_assert(always_false<T0, T1>,
|
||||
"process_kf_result not implemented for result type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const double &x)
|
||||
{
|
||||
r(0) = x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T> &x)
|
||||
{
|
||||
r(0) = x(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
double &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
internal::tensor<double, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename kernel_func_t, typename kernel_args_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel(
|
||||
DeviceTensor<1, double> &f_qp,
|
||||
const kernel_func_t &kf,
|
||||
kernel_args_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
int qp)
|
||||
{
|
||||
process_kf_args(u, args, qp,
|
||||
std::make_index_sequence<mfem::tuple_size<kernel_args_ts>::value> {});
|
||||
|
||||
process_kf_result(f_qp, mfem::get<0>(mfem::apply(kf, args)));
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
// Version for active function arguments only
|
||||
//
|
||||
// This is an Enzyme regression and can be removed in later versions.
|
||||
template <typename kernel_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<>)
|
||||
{
|
||||
using kf_return_t = typename create_function_signature<
|
||||
decltype(&kernel_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<kf_return_t>(
|
||||
+kernel, enzyme_dup, &mfem::get<Is>(args)..., enzyme_interleave,
|
||||
&mfem::get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
// Interleave function arguments for enzyme
|
||||
template <typename kernel_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts, std::size_t... Js>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<Js...>)
|
||||
{
|
||||
using kf_return_t = typename create_function_signature<
|
||||
decltype(&kernel_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<kf_return_t>(
|
||||
+kernel, enzyme_dup, &std::get<Is>(args)..., enzyme_const,
|
||||
&mfem::get<Js>(inactive_args)..., enzyme_interleave,
|
||||
&mfem::get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
template <typename kernel_t, typename arg_ts, typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
inactive_arg_ts &&inactive_args)
|
||||
{
|
||||
auto arg_indices = std::make_index_sequence<
|
||||
mfem::tuple_size<std::remove_reference_t<arg_ts>>::value> {};
|
||||
|
||||
auto inactive_arg_indices = std::make_index_sequence<
|
||||
mfem::tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
|
||||
|
||||
return fwddiff_apply_enzyme_indexed(kernel, args, shadow_args, arg_indices,
|
||||
inactive_args, inactive_arg_indices);
|
||||
}
|
||||
|
||||
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_fwddiff_enzyme(
|
||||
DeviceTensor<1, double> &f_qp,
|
||||
const kf_t &kf,
|
||||
kernel_arg_ts &args,
|
||||
kernel_arg_ts &shadow_args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
int qp_idx)
|
||||
{
|
||||
process_kf_args(u, args, qp_idx,
|
||||
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
|
||||
|
||||
process_kf_args(v, shadow_args, qp_idx,
|
||||
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
|
||||
|
||||
process_kf_result(f_qp,
|
||||
mfem::get<0>(fwddiff_apply_enzyme(kf, args, shadow_args, mfem::tuple<> {})));
|
||||
}
|
||||
#endif // MFEM_USE_ENZYME
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
#include "dfem_util.hpp"
|
||||
#include "dfem_qfunction.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
MFEM_HOST_DEVICE
|
||||
template <typename T0, typename T1, typename T2>
|
||||
void process_kf_arg(const T0 &, const T1 &, T2 &)
|
||||
{
|
||||
static_assert(always_false<T0, T1, T2>,
|
||||
"process_kf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::tensor<internal::dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
internal::dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
arg.gradient = v(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
internal::tensor<internal::dual<T, T>, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i).value = u(i);
|
||||
arg(i).gradient = v(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
internal::tensor<internal::dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
arg(j, i).gradient = v((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<internal::dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<internal::dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<2> &u,
|
||||
const DeviceTensor<2> &v,
|
||||
arg_type &arg,
|
||||
const int &qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
|
||||
process_kf_arg(u_qp, v_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_args, typename kf_args, std::size_t... Is>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_args(
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
kf_args &args,
|
||||
const int &qp,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
(process_kf_arg(u[Is], v[Is], mfem::get<Is>(args), qp), ...);
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<internal::dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).gradient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<internal::dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).gradient;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_native_dual(
|
||||
DeviceTensor<1, double> &f_qp,
|
||||
const kf_t &kf,
|
||||
kernel_arg_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
const int &qp_idx)
|
||||
{
|
||||
process_kf_args(u, v, args, qp_idx,
|
||||
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
|
||||
auto r = mfem::get<0>(mfem::apply(kf, args));
|
||||
process_derivative_from_native_dual(f_qp, r);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,530 @@
|
||||
#pragma once
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <utility>
|
||||
#include "dfem_interpolate.hpp"
|
||||
#include "dfem_integrate.hpp"
|
||||
#include "dfem_qfunction.hpp"
|
||||
#include "dfem_qfunction_dual.hpp"
|
||||
#include "examples/dfem/dfem_util.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class DerivativeOperator : public Operator
|
||||
{
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
using restriction_callback_t =
|
||||
std::function<void(std::vector<Vector> &,
|
||||
const std::vector<Vector> &,
|
||||
std::vector<Vector> &)>;
|
||||
|
||||
public:
|
||||
DerivativeOperator(
|
||||
const std::vector<derivative_action_t> &derivative_actions,
|
||||
const FieldDescriptor &direction,
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const std::vector<restriction_callback_t> &restriction_callbacks,
|
||||
const std::function<void(Vector &, Vector &)> prolongation_transpose) :
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
restriction_callbacks(restriction_callbacks),
|
||||
derivative_action_l(GetVSize(direction)),
|
||||
prolongation_transpose(prolongation_transpose)
|
||||
{
|
||||
MFEM_ASSERT(derivative_actions.size() == restriction_callbacks.size(),
|
||||
"internal error");
|
||||
|
||||
derivative_action_l = 0.0;
|
||||
this->solutions_l.resize(solutions_l.size());
|
||||
this->parameters_l.resize(parameters_l.size());
|
||||
|
||||
for (int i = 0; i < solutions_l.size(); i++)
|
||||
{
|
||||
this->solutions_l[i] = *solutions_l[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters_l.size(); i++)
|
||||
{
|
||||
this->parameters_l[i] = *parameters_l[i];
|
||||
}
|
||||
|
||||
fields_e.resize(solutions_l.size() + parameters_l.size());
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
direction_t = x;
|
||||
direction_t.SetSubVector(ess_tdof_list, 0.0);
|
||||
|
||||
prolongation(direction, direction_t, direction_l);
|
||||
for (int i = 0; i < derivative_actions.size(); i++)
|
||||
{
|
||||
restriction_callbacks[i](solutions_l, parameters_l, fields_e);
|
||||
derivative_actions[i](fields_e, direction_l, derivative_action_l);
|
||||
}
|
||||
prolongation_transpose(derivative_action_l, y);
|
||||
|
||||
y.SetSubVector(ess_tdof_list, 0.0);
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<derivative_action_t> derivative_actions;
|
||||
|
||||
mutable std::vector<Vector> solutions_l;
|
||||
std::vector<Vector> parameters_l;
|
||||
|
||||
FieldDescriptor direction;
|
||||
mutable Vector direction_t;
|
||||
mutable Vector direction_e;
|
||||
mutable Vector direction_l;
|
||||
|
||||
mutable Vector derivative_action_e;
|
||||
mutable Vector derivative_action_l;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
std::vector<restriction_callback_t> restriction_callbacks;
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
};
|
||||
|
||||
class DifferentiableOperator : public Operator
|
||||
{
|
||||
using action_t =
|
||||
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
|
||||
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
using restriction_callback_t =
|
||||
std::function<void(std::vector<Vector> &,
|
||||
const std::vector<Vector> &,
|
||||
std::vector<Vector> &)>;
|
||||
|
||||
public:
|
||||
DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
|
||||
prolongation(solutions, x, solutions_l);
|
||||
for (auto &action : action_callbacks)
|
||||
{
|
||||
action(solutions_l, parameters_l, residual_l);
|
||||
}
|
||||
prolongation_transpose(residual_l, y);
|
||||
|
||||
y.SetSubVector(ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
template <
|
||||
typename func_t,
|
||||
typename... input_ts,
|
||||
typename... output_ts,
|
||||
typename derivative_indices_t>
|
||||
void AddDomainIntegrator(
|
||||
func_t qfunc,
|
||||
mfem::tuple<input_ts...> inputs,
|
||||
mfem::tuple<output_ts...> outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const derivative_indices_t derivative_indices = {});
|
||||
|
||||
void SetParameters(std::vector<Vector *> p) const;
|
||||
|
||||
std::shared_ptr<DerivativeOperator> GetDerivative(
|
||||
size_t derivative_idx,
|
||||
std::vector<Vector *> solutions_l,
|
||||
std::vector<Vector *> parameters_l)
|
||||
{
|
||||
MFEM_ASSERT(derivative_action_callbacks.find(derivative_idx) !=
|
||||
derivative_action_callbacks.end(),
|
||||
"no derivative action has been found for index " << derivative_idx);
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
derivative_action_callbacks[derivative_idx],
|
||||
fields[derivative_idx],
|
||||
solutions_l,
|
||||
parameters_l,
|
||||
restriction_callbacks,
|
||||
prolongation_transpose);
|
||||
}
|
||||
|
||||
private:
|
||||
const ParMesh &mesh;
|
||||
|
||||
std::vector<action_t> action_callbacks;
|
||||
std::map<size_t, std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
// solutions and parameters
|
||||
std::vector<FieldDescriptor> fields;
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
mutable std::vector<Vector> solutions_l;
|
||||
mutable std::vector<Vector> parameters_l;
|
||||
mutable Vector residual_l;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
mutable Vector residual_e;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
std::vector<restriction_callback_t> restriction_callbacks;
|
||||
};
|
||||
|
||||
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
|
||||
{
|
||||
MFEM_ASSERT(parameters.size() == p.size(),
|
||||
"number of parameters doesn't match descriptors");
|
||||
for (int i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
p[i]->Read();
|
||||
parameters_l[i] = *p[i];
|
||||
}
|
||||
}
|
||||
|
||||
DifferentiableOperator::DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh) :
|
||||
mesh(mesh),
|
||||
solutions(solutions),
|
||||
parameters(parameters)
|
||||
{
|
||||
fields.resize(solutions.size() + parameters.size());
|
||||
fields_e.resize(fields.size());
|
||||
solutions_l.resize(solutions.size());
|
||||
parameters_l.resize(parameters.size());
|
||||
|
||||
for (int i = 0; i < solutions.size(); i++)
|
||||
{
|
||||
fields[i] = solutions[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
fields[i + solutions.size()] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename func_t,
|
||||
typename... input_ts,
|
||||
typename... output_ts,
|
||||
typename derivative_indices_t = std::make_index_sequence<0>>
|
||||
void DifferentiableOperator::AddDomainIntegrator(
|
||||
func_t qfunc,
|
||||
mfem::tuple<input_ts...> inputs,
|
||||
mfem::tuple<output_ts...> outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const derivative_indices_t derivative_indices)
|
||||
{
|
||||
using entity_t = Entity::Element;
|
||||
|
||||
static constexpr size_t num_inputs =
|
||||
mfem::tuple_size<decltype(inputs)>::value;
|
||||
|
||||
static constexpr size_t num_outputs =
|
||||
mfem::tuple_size<decltype(outputs)>::value;
|
||||
|
||||
using qf_param_ts = typename create_function_signature<
|
||||
decltype(&func_t::operator())>::type::parameter_ts;
|
||||
|
||||
using qf_output_t = typename create_function_signature<
|
||||
decltype(&func_t::operator())>::type::return_t;
|
||||
|
||||
// Consistency checks
|
||||
if constexpr (num_outputs > 1)
|
||||
{
|
||||
static_assert(always_false<func_t>,
|
||||
"more than one output per kernel is not supported right now");
|
||||
}
|
||||
|
||||
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
|
||||
static_assert(num_qfinputs == num_inputs,
|
||||
"kernel function inputs and descriptor inputs have to match");
|
||||
|
||||
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
|
||||
static_assert(num_qf_outputs == num_qf_outputs,
|
||||
"kernel function outputs and descriptor outputs have to match");
|
||||
|
||||
constexpr auto field_tuple = std::tuple_cat(std::tuple<input_ts...> {},
|
||||
std::tuple<output_ts...> {});
|
||||
constexpr auto filtered_field_tuple = filter_fields(field_tuple);
|
||||
constexpr size_t num_fields = count_unique_field_ids(filtered_field_tuple);
|
||||
|
||||
constexpr auto dependency_map = make_dependency_map(mfem::tuple<input_ts...> {});
|
||||
|
||||
// Create the action callback
|
||||
auto input_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
inputs,
|
||||
std::make_index_sequence<num_inputs> {});
|
||||
|
||||
auto output_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
outputs,
|
||||
std::make_index_sequence<num_outputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = output_to_field[hardcoded_output_idx];
|
||||
|
||||
ElementDofOrdering element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::TENSOR;
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
// The explicit captures are necessary to avoid dependency on
|
||||
// the specific instance of this class (this pointer).
|
||||
auto restriction_callback =
|
||||
[=, solutions = this->solutions, parameters = this->parameters]
|
||||
(std::vector<Vector> &solutions_l,
|
||||
const std::vector<Vector> ¶meters_l,
|
||||
std::vector<Vector> &fields_e)
|
||||
{
|
||||
restriction<entity_t>(solutions, solutions_l, fields_e,
|
||||
element_dof_ordering);
|
||||
restriction<entity_t>(parameters, parameters_l, fields_e,
|
||||
element_dof_ordering,
|
||||
solutions.size());
|
||||
};
|
||||
restriction_callbacks.push_back(restriction_callback);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(outputs);
|
||||
|
||||
if constexpr (is_none_fop<decltype(output_fop)>::value)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
y = r_local;
|
||||
};
|
||||
}
|
||||
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
// {
|
||||
// prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
// {
|
||||
// double local_sum = r_local.Sum();
|
||||
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
// op.mesh.GetComm());
|
||||
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
// };
|
||||
// }
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
};
|
||||
}
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
|
||||
|
||||
// if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
// {
|
||||
// this->width = 1;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
width = residual_lsize;
|
||||
}
|
||||
|
||||
residual_l.SetSize(residual_lsize);
|
||||
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(
|
||||
field,
|
||||
integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
|
||||
|
||||
residual_e.SetSize(R->Height());
|
||||
|
||||
const int residual_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(mfem::get<hardcoded_output_idx>(outputs),
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps =
|
||||
create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
|
||||
auto output_dtq_maps =
|
||||
create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(outputs).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(inputs).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(outputs).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(outputs).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp =
|
||||
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
|
||||
|
||||
auto action_shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp);
|
||||
|
||||
Vector shmem_cache(action_shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(action_shmem_info);
|
||||
|
||||
action_callbacks.push_back(
|
||||
[=](std::vector<Vector> &solutions_l,
|
||||
const std::vector<Vector> ¶meters_l,
|
||||
Vector &residual_l) mutable
|
||||
{
|
||||
restriction_callback(solutions_l, parameters_l, fields_e);
|
||||
|
||||
residual_e = 0.0;
|
||||
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
|
||||
|
||||
auto wrapped_fields_e = wrap_fields(fields_e,
|
||||
action_shmem_info.field_sizes,
|
||||
num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
|
||||
residual_shmem, scratch_shmem] =
|
||||
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, num_qp, e);
|
||||
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
|
||||
scratch_shmem);
|
||||
|
||||
call_qfunction<TensorProduct, qf_param_ts>(
|
||||
qfunc, input_shmem, residual_shmem,
|
||||
residual_size_on_qp, num_qp, q1d);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(outputs),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_shmem);
|
||||
}, num_entities, q1d, q1d, q1d, action_shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
if constexpr (is_none_fop<decltype(output_fop)>::value)
|
||||
{
|
||||
residual_l = residual_e;
|
||||
}
|
||||
else
|
||||
{
|
||||
R->MultTranspose(residual_e, residual_l);
|
||||
}
|
||||
});
|
||||
|
||||
for_constexpr([&](auto derivative_idx)
|
||||
{
|
||||
// bool is_dependent = false;
|
||||
// for_constexpr<num_inputs>([&](auto input_idx)
|
||||
// {
|
||||
// constexpr auto input_is_dependent_on_field_idx =
|
||||
// std::get<derivative_idx>(std::get<input_idx>(dependency_map));
|
||||
|
||||
// if constexpr (input_is_dependent_on_field_idx == 1)
|
||||
// {
|
||||
// is_dependent = true;
|
||||
// }
|
||||
// });
|
||||
|
||||
// if (!is_dependent)
|
||||
// {
|
||||
// derivative_action_callbacks[derivative_idx].push_back(
|
||||
// [=](const Vector &direction_l, Vector &y) mutable
|
||||
// {
|
||||
// y += 0.0;
|
||||
// });
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
auto direction = fields[derivative_idx];
|
||||
size_t derivative_action_l_size = GetVSize(direction);
|
||||
|
||||
const int da_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(outputs),
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, derivative_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
Vector direction_e;
|
||||
Vector derivative_action_e(R->Height());
|
||||
derivative_action_e = 0.0;
|
||||
|
||||
auto input_is_dependent = get_array_from_tuple(std::get<derivative_idx>
|
||||
(dependency_map));
|
||||
|
||||
derivative_action_callbacks[derivative_idx].push_back(
|
||||
[=](std::vector<Vector> &fields_e, const Vector &direction_l,
|
||||
Vector &derivative_action_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, direction_l, direction_e, element_dof_ordering);
|
||||
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof, test_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(fields_e, shmem_info.field_sizes, num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
|
||||
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
|
||||
{
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, direction_shmem,
|
||||
input_shmem, shadow_shmem, residual_shmem, scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps,
|
||||
output_dtq_maps, wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
|
||||
scratch_shmem);
|
||||
|
||||
zero_all(shadow_shmem);
|
||||
map_direction_to_quadrature_data_conditional<TensorProduct>(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, inputs, ir_weights,
|
||||
scratch_shmem, input_is_dependent);
|
||||
|
||||
call_qfunction_derivative_action<TensorProduct, qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem,
|
||||
da_size_on_qp, num_qp, q1d);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(outputs),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_shmem);
|
||||
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
R->MultTranspose(derivative_action_e, derivative_action_l);
|
||||
});
|
||||
}, derivative_indices);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
// #include "dfem_refactor_action.hpp"
|
||||
// #include "dfem_refactor_derivatives.hpp"
|
||||
@@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_refactor.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename element_operator_t, size_t num_fields>
|
||||
void DifferentiableOperator::instantiate_action(
|
||||
element_operator_t element_operator, action_t &action)
|
||||
{
|
||||
using entity_t = typename element_operator_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
element_operator.inputs,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
element_operator.outputs,
|
||||
std::make_index_sequence<element_operator.num_outputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
this->width = GetTrueVSize(fields[test_space_field_idx]);
|
||||
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
|
||||
|
||||
// if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
// {
|
||||
// this->width = 1;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
this->width = residual_lsize;
|
||||
}
|
||||
|
||||
residual_l.SetSize(residual_lsize);
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(
|
||||
field,
|
||||
integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
|
||||
|
||||
residual_e.SetSize(R->Height());
|
||||
|
||||
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs),
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
// auto input_fops = create_bare_fops(element_operator.inputs);
|
||||
// auto output_fops = create_bare_fops(element_operator.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>
|
||||
(element_operator.outputs).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(
|
||||
element_operator.inputs,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
|
||||
(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
fields,
|
||||
num_entities,
|
||||
element_operator.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
residual_size_on_qp);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
print_shared_memory_info(shmem_info);
|
||||
|
||||
action = [=](const Vector &x, Vector &y) mutable
|
||||
{
|
||||
prolongation(solutions, x, solutions_l);
|
||||
|
||||
restriction<entity_t>(solutions, solutions_l, this->fields_e,
|
||||
element_dof_ordering);
|
||||
restriction<entity_t>(parameters, parameters_l, this->fields_e,
|
||||
element_dof_ordering,
|
||||
solutions.size());
|
||||
|
||||
residual_e = 0.0;
|
||||
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof,
|
||||
num_entities);
|
||||
|
||||
auto wrapped_fields_e = wrap_fields(this->fields_e,
|
||||
shmem_info.field_sizes,
|
||||
num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
// printf("\ne: %d\n", e);
|
||||
// tic();
|
||||
auto input_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
|
||||
shmem_info.input_dtq_sizes,
|
||||
input_dtq_maps);
|
||||
|
||||
auto output_dtq_shmem = load_dtq_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
|
||||
shmem_info.output_dtq_sizes,
|
||||
output_dtq_maps);
|
||||
|
||||
auto fields_shmem = load_field_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::FIELD],
|
||||
shmem_info.field_sizes,
|
||||
kinput_to_field,
|
||||
element_operator.inputs,
|
||||
wrapped_fields_e,
|
||||
e,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
|
||||
// These functions don't copy, they simply create a `DeviceTensor` object
|
||||
// that points to correct chunks of the shared memory pool.
|
||||
auto input_shmem = load_input_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::INPUT],
|
||||
shmem_info.input_sizes,
|
||||
num_qp);
|
||||
|
||||
auto residual_shmem = load_residual_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::OUTPUT],
|
||||
shmem_info.residual_size,
|
||||
num_qp);
|
||||
|
||||
auto scratch_mem = load_scratch_mem(
|
||||
shmem,
|
||||
shmem_info.offsets[SharedMemory::Index::TEMP],
|
||||
shmem_info.temp_sizes);
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
// // printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// // tic();
|
||||
map_fields_to_quadrature_data<TensorProduct>(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, element_operator.inputs, ir_weights,
|
||||
scratch_mem,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// // tic();
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto qf_args = decay_tuple<typename element_operator_t::qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
|
||||
apply_kernel(r, element_operator.qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// // printf("qf elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
// // tic();
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
|
||||
mfem::get<0>(element_operator.outputs),
|
||||
output_dtq_shmem[hardcoded_output_idx],
|
||||
scratch_mem);
|
||||
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
|
||||
|
||||
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
|
||||
{
|
||||
residual_l = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
R->MultTranspose(residual_e, residual_l);
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
|
||||
{
|
||||
y = residual_l;
|
||||
}
|
||||
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
// {
|
||||
// double local_sum = residual_l.Sum();
|
||||
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
|
||||
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
// }
|
||||
else
|
||||
{
|
||||
get_prolongation(fields[test_space_field_idx])->MultTranspose(residual_l, y);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_refactor.hpp"
|
||||
|
||||
template<typename T, T... Ints>
|
||||
void print_sequence(std::integer_sequence<T, Ints...>)
|
||||
{
|
||||
((std::cout << Ints << " "), ...);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <
|
||||
typename element_operator_t,
|
||||
size_t num_solutions,
|
||||
size_t num_parameters,
|
||||
size_t derivative_idx>
|
||||
DerivativeOperator::DerivativeOperator(
|
||||
element_operator_t element_operator,
|
||||
const std::array<FieldDescriptor, num_solutions> &solutions,
|
||||
const std::array<FieldDescriptor, num_parameters> ¶meters,
|
||||
const std::vector<FieldDescriptor> &fields,
|
||||
ParMesh &mesh,
|
||||
const IntegrationRule &integration_rule,
|
||||
const ElementDofOrdering &element_dof_ordering,
|
||||
const DofToQuad::Mode &doftoquad_mode,
|
||||
std::integral_constant<size_t, derivative_idx>)
|
||||
{
|
||||
direction = fields[derivative_idx];
|
||||
|
||||
size_t derivative_action_l_size = 0;
|
||||
for (auto &s : solutions)
|
||||
{
|
||||
derivative_action_l_size += GetVSize(s);
|
||||
this->width += GetTrueVSize(s);
|
||||
}
|
||||
this->height = derivative_action_l_size;
|
||||
derivative_action_l.SetSize(derivative_action_l_size);
|
||||
|
||||
constexpr size_t num_fields = num_solutions + num_parameters;
|
||||
using entity_t = typename element_operator_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
element_operator.inputs,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
|
||||
fields,
|
||||
element_operator.outputs,
|
||||
std::make_index_sequence<element_operator.num_outputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(
|
||||
field,
|
||||
integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
|
||||
|
||||
derivative_action_e.SetSize(R->Height());
|
||||
|
||||
const int da_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs),
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>
|
||||
(element_operator.outputs).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(
|
||||
element_operator.inputs,
|
||||
std::make_index_sequence<element_operator.num_inputs> {});
|
||||
|
||||
auto input_is_dependent = std::get<derivative_idx>
|
||||
(element_operator.dependency_map);
|
||||
|
||||
constexpr bool with_derivatives = true;
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
|
||||
(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
fields,
|
||||
num_entities,
|
||||
element_operator.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
da_size_on_qp,
|
||||
derivative_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
print_shared_memory_info(shmem_info);
|
||||
|
||||
action_callback = [=](const Vector &x, Vector &y) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, direction_l, direction_e,
|
||||
element_dof_ordering);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
class SharedMemoryManager
|
||||
{
|
||||
private:
|
||||
struct MemoryBlock
|
||||
{
|
||||
char* ptr;
|
||||
int size;
|
||||
bool used;
|
||||
};
|
||||
|
||||
MFEM_HOST_DEVICE static const int MAX_BLOCKS = 16;
|
||||
MFEM_HOST_DEVICE static MemoryBlock blocks[MAX_BLOCKS];
|
||||
MFEM_HOST_DEVICE static int num_blocks;
|
||||
MFEM_HOST_DEVICE static char* base_ptr;
|
||||
|
||||
public:
|
||||
MFEM_HOST_DEVICE static void init(void* shmem, int total_size)
|
||||
{
|
||||
base_ptr = static_cast<char*>(shmem);
|
||||
num_blocks = 1;
|
||||
blocks[0] = {base_ptr, total_size, false};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MFEM_HOST_DEVICE static T* reserve(int n)
|
||||
{
|
||||
int size_bytes = n * sizeof(T);
|
||||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
if (!blocks[i].used && blocks[i].size >= size_bytes)
|
||||
{
|
||||
blocks[i].used = true;
|
||||
if (blocks[i].size > size_bytes)
|
||||
{
|
||||
// Split block
|
||||
if (num_blocks < MAX_BLOCKS)
|
||||
{
|
||||
blocks[num_blocks] = {blocks[i].ptr + size_bytes, blocks[i].size - size_bytes, false};
|
||||
++num_blocks;
|
||||
blocks[i].size = size_bytes;
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<T*>(blocks[i].ptr);
|
||||
}
|
||||
}
|
||||
return nullptr; // Allocation failed
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE static void release(void* ptr)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
if (blocks[i].ptr == ptr)
|
||||
{
|
||||
blocks[i].used = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE static void release_and_try_merge(void* ptr)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
if (blocks[i].ptr == ptr)
|
||||
{
|
||||
blocks[i].used = false;
|
||||
merge_adjacent_free_blocks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MFEM_HOST_DEVICE static void merge_adjacent_free_blocks()
|
||||
{
|
||||
// Simple bubble sort for simplicity (can be optimized)
|
||||
for (int i = 0; i < num_blocks - 1; ++i)
|
||||
{
|
||||
for (int j = 0; j < num_blocks - i - 1; ++j)
|
||||
{
|
||||
if (blocks[j].ptr > blocks[j + 1].ptr)
|
||||
{
|
||||
MemoryBlock temp = blocks[j];
|
||||
blocks[j] = blocks[j + 1];
|
||||
blocks[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_blocks - 1; ++i)
|
||||
{
|
||||
if (!blocks[i].used && !blocks[i + 1].used)
|
||||
{
|
||||
blocks[i].size += blocks[i + 1].size;
|
||||
for (int j = i + 1; j < num_blocks - 1; ++j)
|
||||
{
|
||||
blocks[j] = blocks[j + 1];
|
||||
}
|
||||
--num_blocks;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MFEM_HOST_DEVICE SharedMemoryManager::MemoryBlock
|
||||
SharedMemoryManager::blocks[SharedMemoryManager::MAX_BLOCKS];
|
||||
|
||||
MFEM_HOST_DEVICE int SharedMemoryManager::num_blocks;
|
||||
|
||||
MFEM_HOST_DEVICE char* SharedMemoryManager::base_ptr;
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "dfem_refactor.hpp"
|
||||
|
||||
#define DFEM_TEST_MAIN(function) \
|
||||
int main(int argc, char* argv[]) \
|
||||
{ \
|
||||
Mpi::Init(); \
|
||||
\
|
||||
const char* device_config = "cpu"; \
|
||||
const char* mesh_file = "../data/ref-square.mesh"; \
|
||||
int polynomial_order = 1; \
|
||||
int ir_order = 2; \
|
||||
int refinements = 0; \
|
||||
\
|
||||
OptionsParser args(argc, argv); \
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); \
|
||||
args.AddOption(&polynomial_order, "-o", "--order", ""); \
|
||||
args.AddOption(&refinements, "-r", "--r", ""); \
|
||||
args.AddOption(&ir_order, "-iro", "--iro", ""); \
|
||||
args.AddOption(&device_config, "-d", "--device", \
|
||||
"Device configuration string, see Device::Configure()."); \
|
||||
args.ParseCheck(); \
|
||||
\
|
||||
Device device(device_config); \
|
||||
if (Mpi::Root() == 0) \
|
||||
{ \
|
||||
device.Print(); \
|
||||
} \
|
||||
\
|
||||
out << std::setprecision(12); \
|
||||
\
|
||||
int ret; \
|
||||
\
|
||||
ret = function(mesh_file, refinements, polynomial_order); \
|
||||
out << #function; \
|
||||
ret ? out << " FAILURE\n" : out << " OK\n"; \
|
||||
\
|
||||
return ret; \
|
||||
}\
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
// SPDX-ArtifactOfProjectName: noisy
|
||||
// SPDX-ArtifactOfProjectHomePage: https://github.com/VincentZalzal/noisy
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Vincent Zalzal
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
namespace vz {
|
||||
|
||||
struct Counters {
|
||||
unsigned m_def_ctor = 0;
|
||||
unsigned m_copy_ctor = 0;
|
||||
unsigned m_move_ctor = 0;
|
||||
unsigned m_copy_assign = 0;
|
||||
unsigned m_move_assign = 0;
|
||||
unsigned m_dtor = 0;
|
||||
|
||||
void reset() {
|
||||
*this = {};
|
||||
}
|
||||
|
||||
bool leaks() const {
|
||||
return m_def_ctor + m_copy_ctor + m_move_ctor != m_dtor;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Counters& c) {
|
||||
stream_counter(os, "Default constructor count: ", c.m_def_ctor );
|
||||
stream_counter(os, "Copy constructor count: ", c.m_copy_ctor );
|
||||
stream_counter(os, "Move constructor count: ", c.m_move_ctor );
|
||||
stream_counter(os, "Copy assignment count: ", c.m_copy_assign);
|
||||
stream_counter(os, "Move assignment count: ", c.m_move_assign);
|
||||
stream_counter(os, "Destructor count: ", c.m_dtor );
|
||||
return os;
|
||||
}
|
||||
|
||||
friend bool operator==(const Counters& lhs, const Counters& rhs) {
|
||||
return
|
||||
lhs.m_def_ctor == rhs.m_def_ctor &&
|
||||
lhs.m_copy_ctor == rhs.m_copy_ctor &&
|
||||
lhs.m_move_ctor == rhs.m_move_ctor &&
|
||||
lhs.m_copy_assign == rhs.m_copy_assign &&
|
||||
lhs.m_move_assign == rhs.m_move_assign &&
|
||||
lhs.m_dtor == rhs.m_dtor ;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Counters& lhs, const Counters& rhs) { return !(lhs == rhs); }
|
||||
|
||||
private:
|
||||
static void stream_counter(std::ostream& os, const char* msg, unsigned value) {
|
||||
if (value != 0)
|
||||
os << msg << std::setw(2) << value << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct Globals {
|
||||
~Globals() {
|
||||
if (m_verbose)
|
||||
std::cout << "\n===== Noisy counters =====\n" << m_counters;
|
||||
}
|
||||
|
||||
Counters m_counters;
|
||||
unsigned m_next_id = 0;
|
||||
bool m_verbose = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class Noisy {
|
||||
private:
|
||||
static detail::Globals& globals() {
|
||||
static detail::Globals s_globals;
|
||||
return s_globals;
|
||||
}
|
||||
|
||||
public:
|
||||
static Counters& counters() { return globals().m_counters; }
|
||||
static void set_verbose(bool verbose) { globals().m_verbose = verbose; }
|
||||
|
||||
Noisy() {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": default constructor\n";
|
||||
globals().m_counters.m_def_ctor++;
|
||||
}
|
||||
|
||||
Noisy(const Noisy& other) {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": copy constructor from " << other << '\n';
|
||||
globals().m_counters.m_copy_ctor++;
|
||||
}
|
||||
|
||||
Noisy(Noisy&& other) noexcept {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": move constructor from " << other << '\n';
|
||||
globals().m_counters.m_move_ctor++;
|
||||
}
|
||||
|
||||
~Noisy() {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": destructor\n";
|
||||
globals().m_counters.m_dtor++;
|
||||
}
|
||||
|
||||
Noisy& operator=(const Noisy& other) {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": copy assignment from " << other << '\n';
|
||||
globals().m_counters.m_copy_assign++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Noisy& operator=(Noisy&& other) noexcept {
|
||||
if (globals().m_verbose)
|
||||
std::cout << *this << ": move assignment from " << other << '\n';
|
||||
globals().m_counters.m_move_assign++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
unsigned id() const { return m_id; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Noisy& noisy) { return os << "Noisy(" << std::setw(2) << noisy.m_id << ')'; }
|
||||
|
||||
private:
|
||||
unsigned m_id = globals().m_next_id++;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
{
|
||||
using entity_t = typename kernel_t::entity_t;
|
||||
|
||||
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
|
||||
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
|
||||
|
||||
constexpr int hardcoded_output_idx = 0;
|
||||
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
|
||||
|
||||
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
|
||||
element_dof_ordering);
|
||||
|
||||
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
|
||||
|
||||
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(op.mesh);
|
||||
const int num_qp = op.integration_rule.GetNPoints();
|
||||
|
||||
// assume only a single element type for now
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : op.fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = dtq[0]->nqpt;
|
||||
|
||||
derivative_action_e.SetSize(R->Height());
|
||||
|
||||
const int da_size_on_qp = GetSizeOnQP<entity_t>(
|
||||
mfem::get<hardcoded_output_idx>(kernel.outputs),
|
||||
op.fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
|
||||
kinput_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
|
||||
koutput_to_field);
|
||||
|
||||
auto input_fops = create_bare_fops(kernel.inputs);
|
||||
auto output_fops = create_bare_fops(kernel.outputs);
|
||||
|
||||
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int test_op_dim =
|
||||
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim;
|
||||
const int num_test_dof = R->Height() /
|
||||
mfem::get<hardcoded_output_idx>(output_fops).vdim /
|
||||
num_entities;
|
||||
|
||||
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
|
||||
num_qp);
|
||||
|
||||
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
|
||||
std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
|
||||
output_dtq_maps,
|
||||
op.fields,
|
||||
num_entities,
|
||||
kernel.inputs,
|
||||
num_qp,
|
||||
input_size_on_qp,
|
||||
da_size_on_qp);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
func = [=](Vector &ye_mem) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, direction_l, direction_e,
|
||||
op.element_dof_ordering, derivative_idx);
|
||||
|
||||
// Check which qf inputs are dependent on the dependent variable
|
||||
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
|
||||
bool no_qfinput_is_dependent = true;
|
||||
for (int i = 0; i < kinput_is_dependent.size(); i++)
|
||||
{
|
||||
if (kinput_to_field[i] == derivative_idx)
|
||||
{
|
||||
no_qfinput_is_dependent = false;
|
||||
kinput_is_dependent[i] = true;
|
||||
// out << "function input " << i << " is dependent on "
|
||||
// << op.fields[kinput_to_field[i]].field_label << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
kinput_is_dependent[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_qfinput_is_dependent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
// auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
|
||||
|
||||
// DeviceTensor<1, const double> integration_weights(
|
||||
// this->op.integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
// Vector zero;
|
||||
// GeometricFactorMaps geometric_factors
|
||||
// {
|
||||
// DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
|
||||
// };
|
||||
|
||||
// // Fields interpolated to the quadrature points in the order of
|
||||
// // kernel function arguments
|
||||
// auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
|
||||
// kernel.inputs,
|
||||
// std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
|
||||
// kernel.inputs,
|
||||
// std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// constexpr int fixed_output_idx = 0;
|
||||
// auto Bv = output_dtq_maps[fixed_output_idx];
|
||||
// auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
|
||||
// const int test_vdim = mfem::get<0>(kernel.outputs).vdim;
|
||||
// DeviceTensor<3> ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
|
||||
{
|
||||
// map_fields_to_quadrature_data(
|
||||
// input_qp, e, this->fields_e,
|
||||
// kinput_to_field, input_dtq_maps,
|
||||
// integration_weights, geometric_factors, kernel.inputs,
|
||||
// std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// map_fields_to_quadrature_data_conditional(
|
||||
// directions_qp, e,
|
||||
// directions_e, kinput_to_field,
|
||||
// input_dtq_maps,
|
||||
// integration_weights,
|
||||
// geometric_factors,
|
||||
// kinput_is_dependent,
|
||||
// kernel.inputs,
|
||||
// std::make_index_sequence<kernel.num_kinputs> {});
|
||||
|
||||
// for (int qp = 0; qp < num_qp; qp++)
|
||||
// {
|
||||
// auto f_qp = apply_kernel_fwddiff_enzyme(
|
||||
// kernel.func,
|
||||
// kernel_args,
|
||||
// input_qp,
|
||||
// kernel_shadow_args,
|
||||
// directions_qp,
|
||||
// qp);
|
||||
|
||||
// auto r_qp = Reshape(&da_qp(0, qp, e), da_size_on_qp);
|
||||
// for (int i = 0; i < da_size_on_qp; i++)
|
||||
// {
|
||||
// r_qp(i) = f_qp(i);
|
||||
// }
|
||||
// }
|
||||
|
||||
// DeviceTensor<3> fhat = Reshape(&da_qp(0, 0, e), test_vdim, test_op_dim, num_qp);
|
||||
// DeviceTensor<2> y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
// map_quadrature_data_to_fields(y, fhat,
|
||||
// output_fop,
|
||||
// output_dtq_maps[hardcoded_output_idx]);
|
||||
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.GetData());
|
||||
|
||||
R->MultTranspose(ye_mem, derivative_action_l);
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<decltype(output_fop), One>)
|
||||
{
|
||||
prolongation_transpose = [&](Vector &r_local, Vector &y)
|
||||
{
|
||||
double local_sum = r_local.Sum();
|
||||
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
|
||||
op.mesh.GetComm());
|
||||
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto P = get_prolongation(op.fields[test_space_field_idx]);
|
||||
prolongation_transpose = [P](Vector &r_l, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_l, y);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
* Calculate shared memory requirements
|
||||
* Interpolation and integration
|
||||
---
|
||||
* If grad involved, need B and G
|
||||
* Fit largest field, depends on polynomial order (#dofs)
|
||||
-> vdim is irrelevant
|
||||
* Temporaries for each sum
|
||||
- DDQ (d1d x d1d x q1d) x 2 -> DDQ0, DDQ1
|
||||
- DQQ (d1d x q1d x q1d) x 3 -> DQQ0, DQQ1, DQQ2
|
||||
- QQQ (q1d x q1d x q1d) x 3 -> QQQ0, QQQ1, QQQ2
|
||||
|
||||
We need the following combinations at the same time
|
||||
(1) DDQ0 + DDQ1 + DQQ0 + DQQ1 + DQQ2
|
||||
(2) DQQ0 + DQQ1 + DQQ2 + QQQ0 + QQQ1 + QQQ2
|
||||
(3) QQQ0 + QQQ1 + QQQ2 + QQD0 + QQD1 + QQD2
|
||||
(4) QQD0 + QQD1 + QQD2 + QDD0 + QDD1 + QDD2
|
||||
|
||||
Allocate largest memory footprint from 2, 3 or 4 and
|
||||
add memory footprint of fields and B/G.
|
||||
|
||||
Annotations with NR and R mean "not reusable" and
|
||||
"reusable", respectively. This means the memory location is
|
||||
reused for _all_ e.g. interpolation of a value etc.
|
||||
|
||||
----
|
||||
For the action of nonlinear diffusion in 2D we have
|
||||
(rho * |u|^2 \nabla u, \nabla v)
|
||||
|
||||
* Load
|
||||
RHO (D x D) | R (after interpolation)
|
||||
U (D x D x VDIM) | R (after interpolation)
|
||||
B (Q x D) | NR
|
||||
G (Q x D) | NR
|
||||
|
||||
* Interpolate Value
|
||||
Temporary (Q x D) | R
|
||||
R (Q x Q) | NR
|
||||
U (Q x Q x VDIM) | NR
|
||||
|
||||
* Interpolate Grad
|
||||
Temporaries (Q x D) + (Q x D) | R
|
||||
U (Q x Q x DIM x VDIM) | NR
|
||||
|
||||
Quadrature point function
|
||||
-> purely thread local
|
||||
|
||||
* Integrate Grad
|
||||
R | temp from Interpolation
|
||||
R | U from Load
|
||||
@@ -0,0 +1,845 @@
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "general/backends.hpp"
|
||||
#include <utility>
|
||||
#include <mfem.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief This is a class that mimics most of std::tuple's interface,
|
||||
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
|
||||
*
|
||||
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct tuple
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
*/
|
||||
template <typename T0>
|
||||
struct tuple<T0>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1>
|
||||
struct tuple<T0, T1>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2>
|
||||
struct tuple<T0, T1, T2>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
struct tuple<T0, T1, T2, T3>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
struct tuple<T0, T1, T2, T3, T4>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
* @tparam T7 The eighth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
};
|
||||
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
T8 v8;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class template argument deduction rule for tuples
|
||||
* @tparam T The variadic template parameter for tuple types
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE
|
||||
tuple(T...) -> tuple<T...>;
|
||||
|
||||
/**
|
||||
* @brief helper function for combining a list of values into a tuple
|
||||
* @tparam T types of the values to be tuple-d
|
||||
* @param args the actual values to be put into a tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
|
||||
{
|
||||
return tuple<T...> {args...};
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size
|
||||
{
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size<mfem::tuple<Types...>> :
|
||||
std::integral_constant<std::size_t, sizeof...(Types)>
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a reference to the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a copy of the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a function intended to be used for extracting the ith type from a tuple.
|
||||
*
|
||||
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
|
||||
*
|
||||
* @tparam i the index of the tuple to query
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param values the tuple of values
|
||||
* @return a copy of the ith entry of the input
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the + operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple sum
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) + get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise sum of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return plus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the += operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be incremented
|
||||
* @param y tuple of increment values
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) += get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return plus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the -= operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be subracted from
|
||||
* @param y tuple of values to subtract from x
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) -= get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return minus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) - get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise difference of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return minus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{-get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
|
||||
{
|
||||
return unary_minus_helper(x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise division of x by y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return div_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant numerator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const double a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a / get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant denomenator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
|
||||
const double a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple x
|
||||
* @param a the numerator
|
||||
* @param x a tuple of denominator values
|
||||
* @brief return a tuple of values defined by division of a by the elements of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const double a, const tuple<T...>& x)
|
||||
{
|
||||
return div_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of numerator values
|
||||
* @param a a denominator
|
||||
* @brief return a tuple of values defined by elementwise division of x by a
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const double a)
|
||||
{
|
||||
return div_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise multiplication of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return mult_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const double a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a * get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
|
||||
const double a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param a a scaling factor
|
||||
* @param x the tuple object
|
||||
* @brief multiply each component of x by the value a on the left
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const double a, const tuple<T...>& x)
|
||||
{
|
||||
return mult_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param x the tuple object
|
||||
* @param a a scaling factor
|
||||
* @brief multiply each component of x by the value a on the right
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const double a)
|
||||
{
|
||||
return mult_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @tparam i a list of indices used to acces each element of the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief helper used to implement printing a tuple of values
|
||||
*/
|
||||
template <typename... T, std::size_t... i>
|
||||
auto& print_helper(std::ostream& out, const mfem::tuple<T...>& A,
|
||||
std::integer_sequence<size_t, i...>)
|
||||
{
|
||||
out << "tuple{";
|
||||
(..., (out << (i == 0 ? "" : ", ") << mfem::get<i>(A)));
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief print a tuple of values
|
||||
*/
|
||||
template <typename... T>
|
||||
auto& operator<<(std::ostream& out, const mfem::tuple<T...>& A)
|
||||
{
|
||||
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper to apply a lambda to a tuple
|
||||
*
|
||||
* @tparam lambda The functor type
|
||||
* @tparam T The tuple types
|
||||
* @tparam i The integer sequence to i
|
||||
* @param f The functor to apply to the tuple
|
||||
* @param args The input tuple
|
||||
* @return The functor output
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a struct used to determine the type at index I of a tuple
|
||||
*
|
||||
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
|
||||
*
|
||||
* @tparam I the index of the desired type
|
||||
* @tparam T a tuple of different types
|
||||
*/
|
||||
template <size_t I, class T>
|
||||
struct tuple_element;
|
||||
|
||||
// recursive case
|
||||
/// @overload
|
||||
template <size_t I, class Head, class... Tail>
|
||||
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
|
||||
tuple<Tail...>>
|
||||
{
|
||||
};
|
||||
|
||||
// base case
|
||||
/// @overload
|
||||
template <class Head, class... Tail>
|
||||
struct tuple_element<0, tuple<Head, Tail...>>
|
||||
{
|
||||
using type = Head; ///< the type at the specified index
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type is a @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/// @overload
|
||||
template <typename... T>
|
||||
struct is_tuple<mfem::tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple_of_tuples : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct is_tuple_of_tuples<mfem::tuple<T...>>
|
||||
{
|
||||
static constexpr bool value = (is_tuple<T>::value &&
|
||||
...); ///< true/false result of type check
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "dfem/dfem_refactor.hpp"
|
||||
#include "fem/bilininteg.hpp"
|
||||
#include "fem/coefficient.hpp"
|
||||
#include "linalg/auxiliary.hpp"
|
||||
#include "linalg/hypre.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *mesh_file = "../data/ref-square.mesh";
|
||||
int polynomial_order = 1;
|
||||
int ir_order = 2;
|
||||
int refinements = 1;
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
L2_FECollection fec(polynomial_order, dim, BasisType::GaussLobatto);
|
||||
ParFiniteElementSpace fes(&mesh, &fec);
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(fes.GetFE(0)->GetGeomType(),
|
||||
ir_order * fec.GetOrder());
|
||||
const IntegrationRule &ir_face = IntRules.Get(
|
||||
fes.GetTraceElement(0, fes.GetMesh()->GetFaceGeometry(0))->GetGeomType(),
|
||||
ir_order * fec.GetOrder());
|
||||
ParGridFunction u(&fes);
|
||||
|
||||
// // -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
|
||||
// auto advection_kernel = [](const tensor<double, 2> &dudxi,
|
||||
// const tensor<double, 2, 2> &J,
|
||||
// const double &w)
|
||||
// {
|
||||
// constexpr tensor<double, 2> b{1.0, 1.0};
|
||||
// return std::tuple{dot(b, dudxi * inv(J)) * det(J) * w};
|
||||
// };
|
||||
|
||||
// std::tuple argument_operators_0{Gradient{"quantity"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
|
||||
// std::tuple output_operator_0{Value{"quantity"}};
|
||||
// ElementOperator op_0{advection_kernel, argument_operators_0, output_operator_0};
|
||||
|
||||
// std::array solutions{FieldDescriptor{&fes, "quantity"}};
|
||||
// std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
// DifferentiableOperator advection_op{solutions, parameters, std::tuple{op_0}, mesh, ir};
|
||||
|
||||
// auto adv_du = advection_op.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
|
||||
// HypreParMatrix A;
|
||||
// adv_du->Assemble(A);
|
||||
|
||||
// std::ofstream mmatofs("dfem_mat.dat");
|
||||
// A.PrintMatlab(mmatofs);
|
||||
// mmatofs.close();
|
||||
|
||||
auto trace_kernel = [](const double &uL, const double &uR, const double &J,
|
||||
const double &w)
|
||||
{
|
||||
return std::tuple{1.0 / J * w};
|
||||
};
|
||||
|
||||
std::tuple argument_operators_0
|
||||
{
|
||||
FaceValueLeft{"quantity"},
|
||||
FaceValueRight{"quantity"},
|
||||
Gradient{"coordinates"},
|
||||
Weight{"integration_weights"}
|
||||
};
|
||||
std::tuple output_operator_0{Value{"quantity"}};
|
||||
FaceElementOperator op_0{trace_kernel, argument_operators_0, output_operator_0};
|
||||
|
||||
std::array solutions{FieldDescriptor{&fes, "quantity"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator trace_op{solutions, parameters, std::tuple{op_0}, mesh, ir_face};
|
||||
|
||||
auto vector_func = [](const Vector &, Vector &u)
|
||||
{
|
||||
u = 1.0;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient vel_coeff(dim, vector_func);
|
||||
|
||||
ParBilinearForm adv_form(&fes);
|
||||
constexpr double alpha = 1.0;
|
||||
auto integ = new ConvectionIntegrator(vel_coeff, alpha);
|
||||
integ->SetIntRule(&ir);
|
||||
adv_form.AddInteriorFaceIntegrator(
|
||||
new NonconservativeDGTraceIntegrator(vel_coeff, alpha));
|
||||
// adv_form.AddDomainIntegrator(integ);
|
||||
adv_form.Assemble();
|
||||
adv_form.Finalize();
|
||||
|
||||
auto K = adv_form.ParallelAssemble();
|
||||
std::ofstream kmatofs("mfem_mat.dat");
|
||||
K->PrintMatlab(kmatofs);
|
||||
kmatofs.close();
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << u << std::flush;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include "dfem.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
|
||||
std::cout << std::setprecision(9);
|
||||
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int polynomial_order = 1;
|
||||
int refinements = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&polynomial_order, "-o", "--order", "");
|
||||
args.AddOption(&refinements, "-r", "--r", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh_serial(mesh_file, 1, 1);
|
||||
mesh_serial.SetCurvature(1);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
const int dim = mesh_serial.Dimension();
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh_serial.Clear();
|
||||
|
||||
constexpr int vdim = 2;
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
|
||||
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto exact_solution = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = x*x + y;
|
||||
u(1) = x + 0.5*y*y;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
|
||||
|
||||
auto elasticity_kernel = [](tensor<double, 2, 2> &dudxi,
|
||||
tensor<double, 2, 2> &J,
|
||||
double &w)
|
||||
{
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::IsotropicIdentity;
|
||||
|
||||
double lambda, mu;
|
||||
{
|
||||
lambda = 1.0;
|
||||
mu = 1.0;
|
||||
}
|
||||
static constexpr auto I = IsotropicIdentity<2>();
|
||||
auto eps = sym(dudxi * inv(J));
|
||||
auto JxW = transpose(inv(J)) * det(J) * w;
|
||||
auto r = (lambda * tr(eps) * I + 2.0 * mu * eps) * JxW;
|
||||
return r;
|
||||
};
|
||||
|
||||
tensor<double, 2, 2> dudxi, s_dudxi, J;
|
||||
double w = 1.0;
|
||||
|
||||
enzyme::get<0>
|
||||
(enzyme::autodiff<enzyme::Forward,
|
||||
enzyme::DuplicatedNoNeed<tensor<double, 2, 2>>>
|
||||
(+elasticity_kernel,
|
||||
enzyme::Duplicated<tensor<double, 2, 2> *>(&dudxi, &s_dudxi),
|
||||
enzyme::Const<tensor<double, 2, 2>*>(&J),
|
||||
enzyme::Const<double*>(&w)));
|
||||
|
||||
// std::tuple input_descriptors = {Gradient{"displacement"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
|
||||
// std::tuple output_descriptors = {Gradient{"displacement"}};
|
||||
// ElementOperator qf {elasticity_kernel, input_descriptors, output_descriptors};
|
||||
|
||||
// ElementOperator forcing_qf
|
||||
// {
|
||||
// [](tensor<double, 2> x, tensor<double, 2, 2> J, double w)
|
||||
// {
|
||||
// double lambda, mu;
|
||||
// {
|
||||
// lambda = 1.0;
|
||||
// mu = 1.0;
|
||||
// }
|
||||
// auto f = x;
|
||||
// f(0) = 4.0*mu + 2.0*lambda;
|
||||
// f(1) = 2.0*mu + lambda;
|
||||
// return f * det(J) * w;
|
||||
// },
|
||||
// // inputs
|
||||
// std::tuple{
|
||||
// Value{"coordinates"},
|
||||
// Gradient{"coordinates"},
|
||||
// Weight{"integration_weight"}},
|
||||
// // outputs
|
||||
// std::tuple{
|
||||
// Value{"displacement"}}
|
||||
// };
|
||||
|
||||
// std::vector<Field> solutions{{&u, "displacement"}};
|
||||
// std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
|
||||
// std::vector<Field> dependent_fields{{&u, "displacement"}};
|
||||
// DifferentiableForm dop(solutions, parameters, dependent_fields, mesh);
|
||||
|
||||
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
|
||||
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
|
||||
// dop.SetEssentialTrueDofs(ess_tdof_list);
|
||||
|
||||
// GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
// gmres.SetRelTol(1e-12);
|
||||
// gmres.SetMaxIter(5000);
|
||||
// gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
||||
|
||||
// NewtonSolver newton(MPI_COMM_WORLD);
|
||||
// newton.SetSolver(gmres);
|
||||
// newton.SetOperator(dop);
|
||||
// newton.SetRelTol(1e-12);
|
||||
// newton.SetMaxIter(100);
|
||||
// newton.SetPrintLevel(1);
|
||||
|
||||
// u = 1e-6;
|
||||
// u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
|
||||
// Vector x;
|
||||
// u.GetTrueDofs(x);
|
||||
|
||||
// Vector zero;
|
||||
// newton.Mult(zero, x);
|
||||
|
||||
// u.Distribute(x);
|
||||
|
||||
// std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <iostream>
|
||||
#include <enzyme/enzyme>
|
||||
|
||||
template <typename T>
|
||||
constexpr auto get_type_name() -> std::string_view
|
||||
{
|
||||
#if defined(__clang__)
|
||||
constexpr auto prefix = std::string_view {"[T = "};
|
||||
constexpr auto suffix = "]";
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(__GNUC__)
|
||||
constexpr auto prefix = std::string_view {"with T = "};
|
||||
constexpr auto suffix = "; ";
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(_MSC_VER)
|
||||
constexpr auto prefix = std::string_view {"get_type_name<"};
|
||||
constexpr auto suffix = ">(void)";
|
||||
constexpr auto function = std::string_view{__FUNCSIG__};
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
|
||||
const auto start = function.find(prefix) + prefix.size();
|
||||
const auto end = function.find(suffix);
|
||||
const auto size = end - start;
|
||||
|
||||
return function.substr(start, size);
|
||||
}
|
||||
|
||||
template <typename ... Ts>
|
||||
constexpr auto decay_types(std::tuple<Ts...> const &)
|
||||
-> std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
|
||||
|
||||
template <typename T>
|
||||
using decay_tuple = decltype(decay_types(std::declval<T>()));
|
||||
|
||||
template <class F> struct FunctionSignature;
|
||||
|
||||
template <typename output_t, typename... input_ts>
|
||||
struct FunctionSignature<output_t(input_ts...)>
|
||||
{
|
||||
using return_t = output_t;
|
||||
using parameter_ts = std::tuple<input_ts...>;
|
||||
};
|
||||
|
||||
template <class T> struct create_function_signature;
|
||||
|
||||
template <typename output_t, typename T, typename... input_ts>
|
||||
struct create_function_signature<output_t (T::*)(input_ts...) const>
|
||||
{
|
||||
using type = FunctionSignature<output_t(input_ts...)>;
|
||||
};
|
||||
|
||||
template <typename arg_ts, std::size_t... Is>
|
||||
auto create_enzyme_args(arg_ts &args,
|
||||
arg_ts &shadow_args,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
((std::cout << std::get<Is>(shadow_args) << "\n"), ...);
|
||||
return std::tuple<enzyme::Duplicated<decltype(std::get<Is>(args))>...>
|
||||
{
|
||||
{ std::get<Is>(args), std::get<Is>(shadow_args) }...
|
||||
};
|
||||
}
|
||||
|
||||
template <typename kernel_t, typename arg_ts>
|
||||
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args, arg_ts &&shadow_args)
|
||||
{
|
||||
auto arg_indices =
|
||||
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<arg_ts>>> {};
|
||||
|
||||
auto enzyme_args = create_enzyme_args(args, shadow_args, arg_indices);
|
||||
|
||||
using kf_return_t = typename create_function_signature<
|
||||
decltype(&kernel_t::operator())>::type::return_t;
|
||||
|
||||
std::cout << "args is " << get_type_name<decltype(args)>() << "\n\n";
|
||||
std::cout << "enzyme_args type is " << get_type_name<decltype(enzyme_args)>() <<
|
||||
"\n\n";
|
||||
std::cout << "return type is " << get_type_name<decltype(kf_return_t{})>() <<
|
||||
"\n\n";
|
||||
|
||||
return std::apply([&](auto &&...args)
|
||||
{
|
||||
return enzyme::get<0>(
|
||||
enzyme::autodiff<enzyme::Forward>
|
||||
(+kernel, args...));
|
||||
},
|
||||
enzyme_args);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
auto func = [](const double &x)
|
||||
{
|
||||
return x*x;
|
||||
};
|
||||
|
||||
using kf_param_ts = typename create_function_signature<
|
||||
decltype(&decltype(func)::operator())>::type::parameter_ts;
|
||||
using kf_output_t = typename create_function_signature<
|
||||
decltype(&decltype(func)::operator())>::type::return_t;
|
||||
auto kernel_args = decay_tuple<kf_param_ts> {};
|
||||
auto kernel_shadow_args = decay_tuple<kf_param_ts> {};
|
||||
|
||||
std::get<0>(kernel_args) = 3;
|
||||
std::get<0>(kernel_shadow_args) = 1;
|
||||
const auto res = fwddiff_apply_enzyme(func, kernel_args, kernel_shadow_args);
|
||||
std::cout << res << " == 6\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "dfem.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
|
||||
std::cout << std::setprecision(9);
|
||||
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int polynomial_order = 1;
|
||||
int refinements = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&polynomial_order, "-o", "--order", "");
|
||||
args.AddOption(&refinements, "-r", "--r", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh_serial(mesh_file, 1, 1);
|
||||
mesh_serial.SetCurvature(1);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
const int dim = mesh_serial.Dimension();
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh_serial.Clear();
|
||||
|
||||
constexpr int vdim = 2;
|
||||
|
||||
// test_partial_assembly_setup_qf(mesh, 1, polynomial_order);
|
||||
// exit(0);
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
|
||||
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
ParGridFunction g(&h1fes);
|
||||
ParGridFunction rho(&h1fes);
|
||||
|
||||
auto exact_solution = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = x*x + y;
|
||||
u(1) = x + 0.5*y*y;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
|
||||
|
||||
auto objective = [](tensor<double, 2> u, double rho,
|
||||
tensor<double, 2, 2> J,
|
||||
double w)
|
||||
{
|
||||
return sqnorm(u) * det(J) * w;
|
||||
};
|
||||
|
||||
std::tuple inputs{Value{"displacement"}, Value{"density"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
|
||||
std::tuple outputs{ One{"integral"} };
|
||||
ElementOperator objective_eop { objective, inputs, outputs };
|
||||
|
||||
std::vector<Field> solution_fields{{&u, "displacement"}};
|
||||
std::vector<Field> parameter_fields{{mesh.GetNodes(), "coordinates"}, {&rho, "density"}};
|
||||
std::vector<Field> dependent_variables{{&u, "displacement"}};
|
||||
DifferentiableForm dop(solution_fields, parameter_fields, dependent_variables,
|
||||
mesh);
|
||||
|
||||
dop.AddElementOperator(objective_eop, ir);
|
||||
|
||||
u.ProjectCoefficient(exact_solution_coeff);
|
||||
Vector zero;
|
||||
|
||||
Vector y(1);
|
||||
Vector utdof;
|
||||
u.GetTrueDofs(utdof);
|
||||
dop.Mult(utdof, y);
|
||||
|
||||
// finite difference test
|
||||
Vector dgdu(u.Size());
|
||||
Vector fx(y);
|
||||
out << "g: ";
|
||||
print_vector(fx);
|
||||
out << "\n";
|
||||
|
||||
for (int i = 0; i < u.Size(); i++)
|
||||
{
|
||||
double h = 1e-6;
|
||||
u(i) += h;
|
||||
dop.Mult(u, y);
|
||||
u(i) -= h;
|
||||
y -= fx;
|
||||
y /= h;
|
||||
dgdu(i) = y(0);
|
||||
}
|
||||
|
||||
out << "dgdu: ";
|
||||
print_vector(dgdu);
|
||||
|
||||
// Vector dgdu = dop.GetGradientWrt({&u, "displacement"});
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "dfem.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
|
||||
std::cout << std::setprecision(9);
|
||||
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int polynomial_order = 1;
|
||||
int refinements = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&polynomial_order, "-o", "--order", "");
|
||||
args.AddOption(&refinements, "-r", "--r", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh_serial(mesh_file, 1, 1);
|
||||
mesh_serial.SetCurvature(1);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
const int dim = mesh_serial.Dimension();
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh_serial.Clear();
|
||||
|
||||
constexpr int vdim = 1;
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
|
||||
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto exact_solution = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
// PRESENT
|
||||
return pow(x,2) + 0.5*x*pow(y,2);
|
||||
};
|
||||
|
||||
FunctionCoefficient exact_solution_coeff(exact_solution);
|
||||
|
||||
auto plaplacian = [](double u,
|
||||
tensor<double, 2> dudxi,
|
||||
tensor<double, 2, 2> J,
|
||||
double w)
|
||||
{
|
||||
using mfem::internal::tensor;
|
||||
auto dudx = dudxi * inv(J);
|
||||
auto JxW = transpose(inv(J)) * det(J) * w;
|
||||
// PRESENT: Implement (1+u^2) * ∇u
|
||||
return (1.0 + u*u) * dudx * JxW;
|
||||
};
|
||||
|
||||
// PRESENT: Implement descriptors
|
||||
std::tuple input_descriptors = {Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
|
||||
// PRESENT: Implement descriptors
|
||||
std::tuple output_descriptors = {Gradient{"potential"}};
|
||||
|
||||
ElementOperator qf {plaplacian, input_descriptors, output_descriptors};
|
||||
|
||||
ElementOperator forcing_qf
|
||||
{
|
||||
[](tensor<double, 2> coords, tensor<double, 2, 2> J, double w)
|
||||
{
|
||||
int p = 2;
|
||||
double x = coords(0);
|
||||
double y = coords(1);
|
||||
// *INDENT-OFF*
|
||||
double mathematica_please_help_me = 2.*pow(x,2)*pow(y,2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*pow(2*x + 0.5*pow(y,2),2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2)) + 1.*x*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2));
|
||||
return mathematica_please_help_me * det(J) * w;
|
||||
// *INDENT-ON*
|
||||
},
|
||||
// inputs
|
||||
std::tuple{
|
||||
Value{"coordinates"},
|
||||
Gradient{"coordinates"},
|
||||
Weight{"integration_weight"}},
|
||||
// outputs
|
||||
std::tuple{
|
||||
Value{"potential"}}
|
||||
};
|
||||
|
||||
std::tuple list_of_qfs{qf_1, qf_2, qf_n};
|
||||
|
||||
std::vector<Field> solutions{{&u, "potential"}};
|
||||
std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
|
||||
DifferentiableForm dop(solutions, parameters, mesh);
|
||||
dop.SetEssentialTrueDofs(ess_tdof_list);
|
||||
|
||||
auto R = dop.GetResidual(list_of_qfs, ir);
|
||||
auto Jacobian_aka_dRdu = dop.GetDerivative<0>(list_of_qfs, ir);
|
||||
|
||||
// R(u) = (\grad u, \grad v) + (f, v)
|
||||
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
|
||||
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
|
||||
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetRelTol(1e-12);
|
||||
gmres.SetMaxIter(5000);
|
||||
gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
||||
|
||||
NewtonSolver newton(MPI_COMM_WORLD);
|
||||
newton.SetSolver(gmres);
|
||||
newton.SetOperator(dop);
|
||||
newton.SetRelTol(1e-12);
|
||||
newton.SetMaxIter(100);
|
||||
newton.SetPrintLevel(1);
|
||||
|
||||
u = 1e-6;
|
||||
u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
|
||||
Vector x;
|
||||
u.GetTrueDofs(x);
|
||||
|
||||
Vector zero;
|
||||
newton.Mult(zero, x);
|
||||
|
||||
u.Distribute(x);
|
||||
|
||||
std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
#include "dfem/dfem_refactor.hpp"
|
||||
#include "linalg/hypre.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
template <typename diffusion_t, typename force_t>
|
||||
class DiffusionOperator : public Operator
|
||||
{
|
||||
template <typename diffusion_du_t>
|
||||
class DiffusionJacobianOperator : public Operator
|
||||
{
|
||||
public:
|
||||
DiffusionJacobianOperator(const DiffusionOperator *diffusion,
|
||||
std::shared_ptr<diffusion_du_t> diff_du) :
|
||||
Operator(diffusion->Height()), s(diffusion)
|
||||
{
|
||||
diff_du->Assemble(A);
|
||||
A.EliminateBC(s->ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
A.Mult(x, y);
|
||||
}
|
||||
|
||||
const DiffusionOperator *s;
|
||||
HypreParMatrix A;
|
||||
};
|
||||
|
||||
public:
|
||||
DiffusionOperator(diffusion_t &diffusion, force_t &force,
|
||||
Array<int> &ess_tdofs) :
|
||||
Operator(diffusion.Height()), diffusion(diffusion),
|
||||
force(force), ess_tdofs(ess_tdofs), f(force.Height()) {}
|
||||
|
||||
void SetParameters(ParGridFunction &mesh_nodes)
|
||||
{
|
||||
diffusion.SetParameters({&mesh_nodes});
|
||||
force.SetParameters({&mesh_nodes});
|
||||
|
||||
Vector zero;
|
||||
|
||||
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
|
||||
this->mesh_nodes = mesh_nodes;
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &r) const override
|
||||
{
|
||||
diffusion.Mult(x, r);
|
||||
force.Mult(x, f);
|
||||
r -= f;
|
||||
r.SetSubVector(ess_tdofs, 0.0);
|
||||
}
|
||||
|
||||
Operator &GetGradient(const Vector &x) const override
|
||||
{
|
||||
ParGridFunction u(const_cast<ParFiniteElementSpace *>
|
||||
(*std::get_if<const ParFiniteElementSpace *>
|
||||
(&diffusion.solutions[0].data)));
|
||||
u.SetFromTrueDofs(x);
|
||||
|
||||
auto dfdu = diffusion.template GetDerivativeWrt<0>({&u}, {&mesh_nodes});
|
||||
dfdu->Assemble(A);
|
||||
A.EliminateBC(ess_tdofs, DiagonalPolicy::DIAG_ONE);
|
||||
return A;
|
||||
// delete jacobian_operator;
|
||||
// jacobian_operator = new
|
||||
// DiffusionJacobianOperator<typename std::remove_pointer<decltype(dfdu.get())>::type>
|
||||
// (this, dfdu);
|
||||
// return *jacobian_operator;
|
||||
}
|
||||
|
||||
diffusion_t &diffusion;
|
||||
force_t &force;
|
||||
|
||||
const Array<int> ess_tdofs;
|
||||
mutable Vector f;
|
||||
|
||||
mutable ParGridFunction mesh_nodes;
|
||||
|
||||
mutable Operator *jacobian_operator = nullptr;
|
||||
mutable HypreParMatrix A;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *mesh_file = "../data/ref-square.mesh";
|
||||
int polynomial_order = 2;
|
||||
int ir_order = 2;
|
||||
int refinements = 4;
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection potential_fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace potential_fes(&mesh, &potential_fec);
|
||||
|
||||
const IntegrationRule &potential_ir =
|
||||
IntRules.Get(potential_fes.GetFE(0)->GetGeomType(),
|
||||
ir_order * potential_fec.GetOrder());
|
||||
|
||||
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
|
||||
bdr_attr_is_ess = 1;
|
||||
Array<int> ess_tdofs;
|
||||
potential_fes.GetEssentialTrueDofs(bdr_attr_is_ess, ess_tdofs);
|
||||
|
||||
ParGridFunction u(&potential_fes);
|
||||
u = 0.0;
|
||||
|
||||
auto diffusion_kernel = [](const internal::dual<double, double> &u,
|
||||
const tensor<internal::dual<double, double>, 2> &dudxi,
|
||||
const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
auto dudx = dudxi * invJ;
|
||||
return std::tuple{(1.0 + u * u) * dudx * det(J) * w * transpose(invJ)};
|
||||
};
|
||||
|
||||
std::tuple argument_operators_0{Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
|
||||
std::tuple output_operator_0{Gradient{"potential"}};
|
||||
ElementOperator op_0{diffusion_kernel, argument_operators_0, output_operator_0};
|
||||
|
||||
auto force_kernel = [](const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
return std::tuple{1.0 * det(J) * w};
|
||||
};
|
||||
std::tuple argument_operators_1{Gradient{"coordinates"}, Weight{"integration_weights"}};
|
||||
std::tuple output_operator_1{Value{"potential"}};
|
||||
ElementOperator op_1{force_kernel, argument_operators_1, output_operator_1};
|
||||
|
||||
std::array solutions{FieldDescriptor{&potential_fes, "potential"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator diffusion_op{solutions, parameters, std::tuple{op_0}, mesh, potential_ir};
|
||||
DifferentiableOperator force_op{solutions, parameters, std::tuple{op_1}, mesh, potential_ir};
|
||||
|
||||
DiffusionOperator diffusion(diffusion_op, force_op, ess_tdofs);
|
||||
|
||||
diffusion.SetParameters({*mesh_nodes});
|
||||
|
||||
HypreBoomerAMG amg;
|
||||
amg.SetPrintLevel(0);
|
||||
|
||||
CGSolver solver(MPI_COMM_WORLD);
|
||||
solver.SetAbsTol(1e-12);
|
||||
solver.SetRelTol(1e-12);
|
||||
solver.SetMaxIter(500);
|
||||
solver.SetPrintLevel(2);
|
||||
solver.SetPreconditioner(amg);
|
||||
|
||||
NewtonSolver newton(MPI_COMM_WORLD);
|
||||
newton.SetOperator(diffusion);
|
||||
newton.SetSolver(solver);
|
||||
newton.SetRelTol(1e-8);
|
||||
newton.SetMaxIter(10);
|
||||
newton.SetPrintLevel(1);
|
||||
|
||||
Vector zero;
|
||||
Vector x(potential_fes.GetTrueVSize());
|
||||
u.ParallelProject(x);
|
||||
newton.Mult(zero, x);
|
||||
|
||||
u.SetFromTrueDofs(x);
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << u << std::flush;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "mfem.hpp"
|
||||
#include "dfem/dfem_refactor.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
auto main(int argc, char *argv[]) -> int
|
||||
{
|
||||
Mpi::Init();
|
||||
|
||||
std::cout << std::setprecision(9);
|
||||
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int polynomial_order = 1;
|
||||
int refinements = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&polynomial_order, "-o", "--order", "");
|
||||
args.AddOption(&refinements, "-r", "--r", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh_serial(mesh_file, 1, 1);
|
||||
mesh_serial.SetCurvature(1);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
const int dim = mesh_serial.Dimension();
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
constexpr int vdim = 1;
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
|
||||
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto exact_solution = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
return 2.345 + x + y;
|
||||
};
|
||||
|
||||
FunctionCoefficient exact_solution_coeff(exact_solution);
|
||||
|
||||
u.ProjectCoefficient(exact_solution_coeff);
|
||||
|
||||
auto domain_qf = [](const double &u,
|
||||
const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
out << u << "\n" << J << "\n" << w << "\n\n";
|
||||
return std::tuple{u * det(J) * w};
|
||||
};
|
||||
|
||||
std::tuple input_descriptors = {Value{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
|
||||
std::tuple output_descriptors = {Value{"potential"}};
|
||||
ElementOperator eop{domain_qf, input_descriptors, output_descriptors};
|
||||
|
||||
auto ops = std::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
DifferentiableOperator dop{solutions, parameters, ops, mesh, ir};
|
||||
|
||||
Vector x(h1fes.GetTrueVSize()), y(h1fes.GetTrueVSize());
|
||||
|
||||
u.GetTrueDofs(x);
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
// Derivative wrt "potential", indicated by the index 0 of the set {solutions} \cup {parameters}
|
||||
auto dFd0 = dop.GetDerivativeWrt<0>({&u}, {mesh_nodes});
|
||||
dFd0->Mult(x, y);
|
||||
|
||||
Vector dFd0_vec;
|
||||
dFd0->Assemble(dFd0_vec);
|
||||
|
||||
// Derivative wrt "coordinates", indicated by the index 1 of the set {solutions} \cup {parameters}
|
||||
auto dFd1 = dop.GetDerivativeWrt<1>({&u}, {mesh_nodes});
|
||||
dFd1->Mult(x, y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
template <typename momentum_t, typename mass_conservation_t>
|
||||
class NavierStokesOperator : public Operator
|
||||
{
|
||||
template <typename momentum_du_t, typename momentum_dp_t>
|
||||
class NavierStokesJacobianOperator : public Operator
|
||||
{
|
||||
public:
|
||||
NavierStokesJacobianOperator(const NavierStokesOperator *ns,
|
||||
std::shared_ptr<momentum_du_t> mom_du,
|
||||
std::shared_ptr<momentum_dp_t> mom_dp) :
|
||||
Operator(ns->Height()), ns(ns), block_op(ns->block_offsets)
|
||||
{
|
||||
mom_du->Assemble(A);
|
||||
A.EliminateBC(ns->vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
|
||||
|
||||
mom_dp->Assemble(D);
|
||||
D.EliminateRows(ns->vel_ess_tdofs);
|
||||
|
||||
Dt = new TransposeOperator(D);
|
||||
|
||||
block_op.SetBlock(0, 0, &A);
|
||||
block_op.SetBlock(0, 1, &D);
|
||||
block_op.SetBlock(1, 0, Dt);
|
||||
// std::ofstream amatofs("dfem_mat.dat");
|
||||
// block_op.PrintMatlab(amatofs);
|
||||
// amatofs.close();
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
block_op.Mult(x, y);
|
||||
}
|
||||
|
||||
~NavierStokesJacobianOperator()
|
||||
{
|
||||
delete Dt;
|
||||
}
|
||||
|
||||
const NavierStokesOperator *ns = nullptr;
|
||||
HypreParMatrix A, D;
|
||||
TransposeOperator *Dt = nullptr;
|
||||
BlockOperator block_op;
|
||||
};
|
||||
|
||||
public:
|
||||
NavierStokesOperator(momentum_t &momentum,
|
||||
mass_conservation_t &mass_conservation,
|
||||
Array<int> &offsets, Array<int> &vel_ess_tdofs) :
|
||||
Operator(offsets.Last()), momentum(momentum),
|
||||
mass_conservation(mass_conservation),
|
||||
block_offsets(offsets), vel_ess_tdofs(vel_ess_tdofs) {}
|
||||
|
||||
void SetParameters(ParGridFunction &mesh_nodes)
|
||||
{
|
||||
momentum.SetParameters({&mesh_nodes});
|
||||
mass_conservation.SetParameters({&mesh_nodes});
|
||||
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
|
||||
this->mesh_nodes = mesh_nodes;
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &r) const override
|
||||
{
|
||||
Vector ru(r.ReadWrite() + block_offsets[0],
|
||||
block_offsets[1] - block_offsets[0]);
|
||||
Vector rp(r.ReadWrite() + block_offsets[1],
|
||||
block_offsets[2] - block_offsets[1]);
|
||||
|
||||
momentum.Mult(x, ru);
|
||||
|
||||
mass_conservation.Mult(x, rp);
|
||||
|
||||
ru.SetSubVector(vel_ess_tdofs, 0.0);
|
||||
}
|
||||
|
||||
Operator &GetGradient(const Vector &x) const override
|
||||
{
|
||||
xtmp = x;
|
||||
BlockVector xb(xtmp.ReadWrite(), block_offsets);
|
||||
|
||||
ParGridFunction u(const_cast<ParFiniteElementSpace *>
|
||||
(*std::get_if<const ParFiniteElementSpace *>
|
||||
(&momentum.solutions[0].data)));
|
||||
ParGridFunction p(const_cast<ParFiniteElementSpace *>
|
||||
(*std::get_if<const ParFiniteElementSpace *>
|
||||
(&momentum.solutions[1].data)));
|
||||
u.SetFromTrueDofs(xb.GetBlock(0));
|
||||
p.SetFromTrueDofs(xb.GetBlock(1));
|
||||
auto mom_du = momentum.template GetDerivativeWrt<0>({&u, &p}, {&mesh_nodes});
|
||||
auto mom_dp = momentum.template GetDerivativeWrt<1>({&u, &p}, {&mesh_nodes});
|
||||
delete jacobian_operator;
|
||||
jacobian_operator = new NavierStokesJacobianOperator<
|
||||
typename std::remove_pointer<decltype(mom_du.get())>::type,
|
||||
typename std::remove_pointer<decltype(mom_dp.get())>::type>(this, mom_du,
|
||||
mom_dp);
|
||||
return *jacobian_operator;
|
||||
}
|
||||
|
||||
momentum_t &momentum;
|
||||
mass_conservation_t &mass_conservation;
|
||||
|
||||
const Array<int> block_offsets;
|
||||
const Array<int> vel_ess_tdofs;
|
||||
mutable Vector xtmp;
|
||||
|
||||
mutable ParGridFunction mesh_nodes;
|
||||
|
||||
mutable Operator *jacobian_operator = nullptr;
|
||||
};
|
||||
|
||||
double reynolds = 10.0;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
constexpr int vdim = dim;
|
||||
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *mesh_file = "../data/ref-cube.mesh";
|
||||
int polynomial_order = 2;
|
||||
int ir_order = 2;
|
||||
int refinements = 2;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&refinements, "-r", "--refinements", "");
|
||||
args.AddOption(&reynolds, "-rey", "--reynolds", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection velocity_fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim);
|
||||
|
||||
H1_FECollection pressure_fec(polynomial_order - 1, dim);
|
||||
ParFiniteElementSpace pressure_fes(&mesh, &pressure_fec);
|
||||
|
||||
const IntegrationRule &velocity_ir =
|
||||
IntRules.Get(velocity_fes.GetFE(0)->GetGeomType(),
|
||||
ir_order * velocity_fec.GetOrder());
|
||||
|
||||
const IntegrationRule &pressure_ir =
|
||||
IntRules.Get(pressure_fes.GetFE(0)->GetGeomType(),
|
||||
ir_order * pressure_fec.GetOrder());
|
||||
|
||||
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
|
||||
bdr_attr_is_ess = 1;
|
||||
Array<int> vel_ess_tdofs;
|
||||
velocity_fes.GetEssentialTrueDofs(bdr_attr_is_ess, vel_ess_tdofs);
|
||||
|
||||
ParGridFunction u(&velocity_fes);
|
||||
ParGridFunction p(&pressure_fes);
|
||||
|
||||
auto u_f = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double z = coords(2);
|
||||
if (z >= 1.0)
|
||||
{
|
||||
u(0) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
u(0) = 0.0;
|
||||
}
|
||||
u(1) = 0.0;
|
||||
u(2) = 0.0;
|
||||
};
|
||||
auto u_coef = VectorFunctionCoefficient(dim, u_f);
|
||||
|
||||
u.ProjectCoefficient(u_coef);
|
||||
p = 0.0;
|
||||
|
||||
// -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
|
||||
auto momentum_kernel = [](const tensor<double, dim> &u,
|
||||
const tensor<double, dim, dim> &dudxi,
|
||||
const double &p,
|
||||
const tensor<double, dim, dim> &J,
|
||||
const double &w)
|
||||
{
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
auto invJ = inv(J);
|
||||
auto dudx = dudxi * invJ;
|
||||
double Re = reynolds;
|
||||
return mfem::tuple{(outer(u, u) - 1.0 / Re * dudx + p * I) * det(J) * w * transpose(invJ)};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators_0{Value{"velocity"}, Gradient{"velocity"}, Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator_0{Gradient{"velocity"}};
|
||||
ElementOperator op_0{momentum_kernel, argument_operators_0, output_operator_0};
|
||||
|
||||
// (\nabla \cdot u, q)
|
||||
auto mass_conservation_kernel = [](const tensor<double, dim, dim> &dudxi,
|
||||
const tensor<double, dim, dim> &J,
|
||||
const double &w)
|
||||
{
|
||||
return mfem::tuple{tr(dudxi * inv(J)) * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators_1{Gradient{"velocity"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator_1{Value{"pressure"}};
|
||||
ElementOperator op_1{mass_conservation_kernel, argument_operators_1, output_operator_1};
|
||||
|
||||
std::array solutions{FieldDescriptor{&velocity_fes, "velocity"}, FieldDescriptor{&pressure_fes, "pressure"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator momentum_op{solutions, parameters, mfem::tuple{op_0}, mesh, velocity_ir};
|
||||
DifferentiableOperator mass_conservation_op{solutions, parameters, mfem::tuple{op_1}, mesh, pressure_ir};
|
||||
|
||||
// Preconditioner form
|
||||
auto pressure_mass_kernel = [](const double &p,
|
||||
const tensor<double, dim, dim> &J,
|
||||
const double &w)
|
||||
{
|
||||
return mfem::tuple{p * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple pms_args{Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple pms_outs{Value{"pressure"}};
|
||||
ElementOperator pressure_mass{pressure_mass_kernel, pms_args, pms_outs};
|
||||
std::array pms_sols{FieldDescriptor{&pressure_fes, "pressure"}};
|
||||
std::array pms_params{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
DifferentiableOperator pressure_mass_op{pms_sols, pms_params, mfem::tuple{pressure_mass}, mesh, pressure_ir};
|
||||
|
||||
Array<int> block_offsets(3);
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = velocity_fes.GetTrueVSize();
|
||||
block_offsets[2] = pressure_fes.GetTrueVSize();
|
||||
block_offsets.PartialSum();
|
||||
|
||||
NavierStokesOperator navierstokes(momentum_op, mass_conservation_op,
|
||||
block_offsets,
|
||||
vel_ess_tdofs);
|
||||
|
||||
BlockVector x(block_offsets), y(block_offsets);
|
||||
u.ParallelProject(x.GetBlock(0));
|
||||
// p.ParallelProject(x.GetBlock(1));
|
||||
navierstokes.SetParameters(*mesh_nodes);
|
||||
|
||||
HypreParMatrix A;
|
||||
momentum_op.template GetDerivativeWrt<0>({&u, &p}, {mesh_nodes})->Assemble(A);
|
||||
A.EliminateBC(vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
|
||||
HypreBoomerAMG amg(A);
|
||||
amg.SetMaxLevels(50);
|
||||
amg.SetPrintLevel(0);
|
||||
|
||||
HypreParMatrix Mp;
|
||||
pressure_mass_op.template GetDerivativeWrt<0>({&p}, {mesh_nodes})->Assemble(Mp);
|
||||
|
||||
HypreDiagScale Mp_inv(Mp);
|
||||
|
||||
BlockDiagonalPreconditioner prec(block_offsets);
|
||||
prec.SetDiagonalBlock(0, &amg);
|
||||
prec.SetDiagonalBlock(1, &Mp_inv);
|
||||
|
||||
GMRESSolver solver(MPI_COMM_WORLD);
|
||||
solver.SetAbsTol(0.0);
|
||||
solver.SetRelTol(1e-8);
|
||||
solver.SetKDim(100);
|
||||
solver.SetMaxIter(500);
|
||||
solver.SetPrintLevel(2);
|
||||
solver.SetPreconditioner(prec);
|
||||
|
||||
NewtonSolver newton(MPI_COMM_WORLD);
|
||||
newton.SetOperator(navierstokes);
|
||||
newton.SetSolver(solver);
|
||||
newton.SetRelTol(1e-8);
|
||||
newton.SetMaxIter(50);
|
||||
newton.SetPrintLevel(1);
|
||||
|
||||
Vector zero;
|
||||
newton.Mult(zero, x);
|
||||
|
||||
u.SetFromTrueDofs(x.GetBlock(0));
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << u << std::flush;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_diffusion(
|
||||
std::string mesh_file, int refinements, int polynomial_order)
|
||||
{
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == 2, "incorrect mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
mesh_serial.Clear();
|
||||
|
||||
out << "#el: " << mesh.GetNE() << "\n";
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
|
||||
|
||||
out << "#qp: " << ir.GetNPoints() << "\n";
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
ParGridFunction rho_g(&h1fes);
|
||||
|
||||
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
|
||||
const double& w, const tensor<double, 2>& dudxi)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators =
|
||||
{
|
||||
Gradient{"coordinates"}, Weight{}, Gradient{"potential"}
|
||||
};
|
||||
mfem::tuple output_operator = {Gradient{"potential"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector& coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
return 2.345 + 0.25 * x * x * y + y * y * x;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
y.HostRead();
|
||||
|
||||
ParBilinearForm a(&h1fes);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(y2);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// // Test linearization here as well
|
||||
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
|
||||
|
||||
// if (dFdu->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdu->Mult(x, y);
|
||||
// y.HostRead();
|
||||
// a.Mult(x, y2);
|
||||
// y2.HostRead();
|
||||
|
||||
// diff = y2;
|
||||
// diff -= y;
|
||||
// if (diff.Norml2() > 1e-10)
|
||||
// {
|
||||
// print_vector(diff);
|
||||
// print_vector(y2);
|
||||
// print_vector(y);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// // fd jacobian test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
|
||||
// v *= eps;
|
||||
// xpv += v;
|
||||
// xmv -= v;
|
||||
// dop.Mult(xpv, fxpv);
|
||||
// dop.Mult(xmv, fxmv);
|
||||
// fxpv -= fxmv;
|
||||
// fxpv /= (2.0*eps);
|
||||
|
||||
// fxpv -= y;
|
||||
// if (fxpv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// f1_g.ProjectCoefficient(f1_c);
|
||||
// rho_g.ProjectCoefficient(rho_c);
|
||||
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
|
||||
// if (dFdrho->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdrho->Mult(rho_g, y);
|
||||
|
||||
// // fd test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
|
||||
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
|
||||
// dop.SetParameters({&rhopv, mesh_nodes});
|
||||
// dop.Mult(x, frhopv);
|
||||
// dop.SetParameters({&rhomv, mesh_nodes});
|
||||
// dop.Mult(x, frhomv);
|
||||
// frhopv -= frhomv;
|
||||
// frhopv /= (2.0*eps);
|
||||
|
||||
// frhopv -= y;
|
||||
// if (frhopv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_diffusion);
|
||||
@@ -0,0 +1,296 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "examples/dfem/dfem_parametricspace.hpp"
|
||||
#include "fem/bilininteg.hpp"
|
||||
#include "general/tic_toc.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::dual;
|
||||
|
||||
int test_diffusion_3d(
|
||||
std::string mesh_file, int refinements, int polynomial_order)
|
||||
{
|
||||
constexpr int num_samples = 10;
|
||||
constexpr int dim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(polynomial_order);
|
||||
mesh_serial.Clear();
|
||||
|
||||
out << "#el: " << mesh.GetNE() << "\n";
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
|
||||
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
|
||||
0)->GetDim() - 1);
|
||||
|
||||
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
|
||||
printf("#nqp = %d\n", ir.GetNPoints());
|
||||
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
|
||||
|
||||
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
|
||||
dim * dim * ir.GetNPoints() * mesh.GetNE());
|
||||
ParametricFunction qdata(qdata_space);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
ParGridFunction rho_g(&h1fes);
|
||||
|
||||
auto f1 = [](const Vector& coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + x + x*y + 1.25 * z*x;
|
||||
};
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(h1fes.GetTrueVSize());
|
||||
{
|
||||
auto diffusion_mf_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<dual<real_t, real_t>, dim>& dudxi,
|
||||
const tensor<double, dim, dim>& J,
|
||||
const double& w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator = {Gradient{"potential"}};
|
||||
|
||||
ElementOperator eop = {diffusion_mf_kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, y);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
|
||||
y.HostRead();
|
||||
}
|
||||
|
||||
{
|
||||
auto diffusion_setup_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<double, dim, dim>& J,
|
||||
const double& w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator = {None{"qdata"}};
|
||||
|
||||
ElementOperator eop = {diffusion_setup_kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array
|
||||
{
|
||||
FieldDescriptor{&mesh_fes, "coordinates"},
|
||||
FieldDescriptor{&qdata_space, "qdata"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
dop.SetParameters({mesh_nodes, &qdata});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, qdata);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
|
||||
qdata.HostRead();
|
||||
}
|
||||
|
||||
// printf("qdata: ");
|
||||
// print_vector(qdata);
|
||||
|
||||
{
|
||||
auto diffusion_apply_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<dual<real_t, real_t>, dim>& dudxi,
|
||||
const tensor<double, dim, dim>& qdata)
|
||||
{
|
||||
return mfem::tuple{dudxi * qdata};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Gradient{"potential"}, None{"qdata"}};
|
||||
mfem::tuple output_operator = {Gradient{"potential"}};
|
||||
|
||||
ElementOperator eop = {diffusion_apply_kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&qdata_space, "qdata"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
dop.SetParameters({&qdata});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, y);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
|
||||
y.HostRead();
|
||||
}
|
||||
|
||||
// printf("y: ");
|
||||
// print_vector(y);
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
{
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto diff_integ = new DiffusionIntegrator;
|
||||
diff_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(diff_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
|
||||
OperatorPtr A;
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
Array<int> empty;
|
||||
a.FormSystemMatrix(empty, A);
|
||||
sw.Stop();
|
||||
printf("mfem pa setup: %fs\n", sw.RealTime());
|
||||
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
A->Mult(x, y2);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
|
||||
y2.HostRead();
|
||||
}
|
||||
// printf("y2: ");
|
||||
// print_vector(y2);
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-15)
|
||||
{
|
||||
// printf("y ");
|
||||
// print_vector(y);
|
||||
// printf("y2: ");
|
||||
// print_vector(y2);
|
||||
// printf("diff: ");
|
||||
// print_vector(diff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test linearization here as well
|
||||
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
|
||||
|
||||
// if (dFdu->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdu->Mult(x, y);
|
||||
// y.HostRead();
|
||||
// a.Mult(x, y2);
|
||||
// y2.HostRead();
|
||||
|
||||
// diff = y2;
|
||||
// diff -= y;
|
||||
// if (diff.Norml2() > 1e-10)
|
||||
// {
|
||||
// print_vector(diff);
|
||||
// print_vector(y2);
|
||||
// print_vector(y);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// // fd jacobian test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
|
||||
// v *= eps;
|
||||
// xpv += v;
|
||||
// xmv -= v;
|
||||
// dop.Mult(xpv, fxpv);
|
||||
// dop.Mult(xmv, fxmv);
|
||||
// fxpv -= fxmv;
|
||||
// fxpv /= (2.0*eps);
|
||||
|
||||
// fxpv -= y;
|
||||
// if (fxpv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// f1_g.ProjectCoefficient(f1_c);
|
||||
// rho_g.ProjectCoefficient(rho_c);
|
||||
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
|
||||
// if (dFdrho->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdrho->Mult(rho_g, y);
|
||||
|
||||
// // fd test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
|
||||
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
|
||||
// dop.SetParameters({&rhopv, mesh_nodes});
|
||||
// dop.Mult(x, frhopv);
|
||||
// dop.SetParameters({&rhomv, mesh_nodes});
|
||||
// dop.Mult(x, frhomv);
|
||||
// frhopv -= frhomv;
|
||||
// frhopv /= (2.0*eps);
|
||||
|
||||
// frhopv -= y;
|
||||
// if (frhopv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_diffusion_3d);
|
||||
@@ -0,0 +1,309 @@
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "examples/dfem/dfem_fieldoperator.hpp"
|
||||
#include "examples/dfem/dfem_refactor.hpp"
|
||||
#include "fem/bilininteg.hpp"
|
||||
#include "general/tic_toc.hpp"
|
||||
#include <utility>
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::dual;
|
||||
|
||||
int test_diffusion_3d(
|
||||
std::string mesh_file, int refinements, int polynomial_order)
|
||||
{
|
||||
constexpr int num_samples = 100;
|
||||
constexpr int dim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(polynomial_order);
|
||||
mesh_serial.Clear();
|
||||
|
||||
out << "#el: " << mesh.GetNE() << "\n";
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
|
||||
0)->GetDim() - 1);
|
||||
|
||||
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
|
||||
printf("#nqp = %d\n", ir.GetNPoints());
|
||||
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
|
||||
|
||||
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
|
||||
dim * dim * ir.GetNPoints() * mesh.GetNE());
|
||||
ParametricFunction qdata(qdata_space);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
ParGridFunction rho_g(&h1fes);
|
||||
|
||||
auto f1 = [](const Vector& coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + x + x*y + 1.25 * z*x;
|
||||
};
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(h1fes.GetTrueVSize());
|
||||
{
|
||||
std::shared_ptr<DerivativeOperator> dpotential;
|
||||
{
|
||||
auto diffusion_mf_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<real_t, dim>& dudxi,
|
||||
const tensor<real_t, dim, dim>& J,
|
||||
const real_t& w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
constexpr int Potential = 0;
|
||||
constexpr int Coordinates = 1;
|
||||
|
||||
auto input_operators = mfem::tuple{Gradient<Potential>{}, Gradient<Coordinates>{}, Weight{}};
|
||||
auto output_operator = mfem::tuple{Gradient<Potential>{}};
|
||||
|
||||
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
|
||||
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, mesh);
|
||||
auto derivatives = std::integer_sequence<size_t, Potential> {};
|
||||
dop.AddDomainIntegrator(
|
||||
diffusion_mf_kernel, input_operators, output_operator, ir, derivatives);
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, y);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
|
||||
y.HostRead();
|
||||
|
||||
dpotential = dop.GetDerivative(Potential, {&f1_g}, {mesh_nodes});
|
||||
}
|
||||
dpotential->Mult(x, y);
|
||||
}
|
||||
|
||||
{
|
||||
auto diffusion_setup_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<double, dim, dim>& J,
|
||||
const double& w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
constexpr int Potential = 0;
|
||||
constexpr int Coordinates = 1;
|
||||
constexpr int QData = 2;
|
||||
|
||||
auto input_operators = mfem::tuple{Gradient<Coordinates>{}, Weight{}};
|
||||
auto output_operator = mfem::tuple{None<QData>{}};
|
||||
|
||||
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
|
||||
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes},
|
||||
FieldDescriptor{QData, &qdata_space}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, mesh);
|
||||
dop.AddDomainIntegrator(
|
||||
diffusion_setup_kernel, input_operators, output_operator, ir);
|
||||
|
||||
dop.SetParameters({mesh_nodes, &qdata});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, qdata);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
|
||||
qdata.HostRead();
|
||||
}
|
||||
|
||||
// printf("qdata: ");
|
||||
// print_vector(qdata);
|
||||
|
||||
{
|
||||
auto diffusion_apply_kernel =
|
||||
[] MFEM_HOST_DEVICE (
|
||||
const tensor<real_t, dim>& dudxi,
|
||||
const tensor<double, dim, dim>& qdata)
|
||||
{
|
||||
return mfem::tuple{dudxi * qdata};
|
||||
};
|
||||
|
||||
constexpr int Potential = 0;
|
||||
constexpr int QData = 1;
|
||||
|
||||
auto input_operators = mfem::tuple{Gradient<Potential>{}, None<QData>{}};
|
||||
auto output_operator = mfem::tuple{Gradient<Potential>{}};
|
||||
|
||||
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
|
||||
auto parameters = std::vector{FieldDescriptor{QData, &qdata_space}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, mesh);
|
||||
dop.AddDomainIntegrator(
|
||||
diffusion_apply_kernel, input_operators, output_operator, ir);
|
||||
|
||||
dop.SetParameters({&qdata});
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
dop.Mult(x, y);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
|
||||
y.HostRead();
|
||||
}
|
||||
|
||||
// printf("y: ");
|
||||
// print_vector(y);
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
{
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto diff_integ = new DiffusionIntegrator;
|
||||
diff_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(diff_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
|
||||
OperatorPtr A;
|
||||
StopWatch sw;
|
||||
sw.Start();
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
Array<int> empty;
|
||||
a.FormSystemMatrix(empty, A);
|
||||
sw.Stop();
|
||||
printf("mfem pa setup: %fs\n", sw.RealTime());
|
||||
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
y2 = 0.0;
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
A->Mult(x, y2);
|
||||
}
|
||||
sw.Stop();
|
||||
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
|
||||
y2.HostRead();
|
||||
}
|
||||
// printf("y2: ");
|
||||
// print_vector(y2);
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-15)
|
||||
{
|
||||
printf("y: ");
|
||||
print_vector(y);
|
||||
printf("y2: ");
|
||||
print_vector(y2);
|
||||
printf("diff: ");
|
||||
print_vector(diff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test linearization here as well
|
||||
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
|
||||
|
||||
// if (dFdu->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdu->Mult(x, y);
|
||||
// y.HostRead();
|
||||
// a.Mult(x, y2);
|
||||
// y2.HostRead();
|
||||
|
||||
// diff = y2;
|
||||
// diff -= y;
|
||||
// if (diff.Norml2() > 1e-10)
|
||||
// {
|
||||
// print_vector(diff);
|
||||
// print_vector(y2);
|
||||
// print_vector(y);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// // fd jacobian test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
|
||||
// v *= eps;
|
||||
// xpv += v;
|
||||
// xmv -= v;
|
||||
// dop.Mult(xpv, fxpv);
|
||||
// dop.Mult(xmv, fxmv);
|
||||
// fxpv -= fxmv;
|
||||
// fxpv /= (2.0*eps);
|
||||
|
||||
// fxpv -= y;
|
||||
// if (fxpv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// f1_g.ProjectCoefficient(f1_c);
|
||||
// rho_g.ProjectCoefficient(rho_c);
|
||||
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
|
||||
// if (dFdrho->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdrho->Mult(rho_g, y);
|
||||
|
||||
// // fd test
|
||||
// {
|
||||
// double eps = 1.0e-6;
|
||||
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
|
||||
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
|
||||
// dop.SetParameters({&rhopv, mesh_nodes});
|
||||
// dop.Mult(x, frhopv);
|
||||
// dop.SetParameters({&rhomv, mesh_nodes});
|
||||
// dop.Mult(x, frhomv);
|
||||
// frhopv -= frhomv;
|
||||
// frhopv /= (2.0*eps);
|
||||
|
||||
// frhopv -= y;
|
||||
// if (frhopv.Norml2() > eps)
|
||||
// {
|
||||
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_diffusion_3d);
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_elasticity(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
const int vdim = dim;
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
Array<int> ess_tdof;
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 6 * h1fec.GetOrder());
|
||||
|
||||
out << "#qp: " << ir.GetNPoints() << "\n";
|
||||
out << "#dof_el: " << h1fes.GetRestrictionMatrix()->Height() / mesh.GetNE() <<
|
||||
"\n";
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto f1 = [](const Vector& coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
|
||||
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient u_c(dim, f1);
|
||||
u.ProjectCoefficient(u_c);
|
||||
|
||||
ConstantCoefficient l_coeff(0.5), m_coeff(0.25);
|
||||
|
||||
ParBilinearForm A_form(&h1fes);
|
||||
auto A_integ = new ElasticityIntegrator(l_coeff, m_coeff);
|
||||
A_integ->SetIntegrationRule(ir);
|
||||
A_form.AddDomainIntegrator(A_integ);
|
||||
A_form.Assemble();
|
||||
A_form.Finalize();
|
||||
|
||||
auto elasticity_kernel = [](const tensor<double, 2, 2> &dudxi,
|
||||
const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
constexpr double lambda = 0.5;
|
||||
constexpr double mu = 0.25;
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
|
||||
auto invJ = inv(J);
|
||||
auto eps = sym(dudxi * invJ);
|
||||
return mfem::tuple{transpose(lambda * tr(eps) * I + 2.0 * mu * eps) * det(J) * w * transpose(invJ)};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator{Gradient{"displacement"}};
|
||||
|
||||
ElementOperator op{elasticity_kernel, argument_operators, output_operator};
|
||||
|
||||
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
|
||||
|
||||
Vector x(u), y1(h1fes.GetTrueVSize()),
|
||||
y2(h1fes.GetTrueVSize());
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y1);
|
||||
y1.HostRead();
|
||||
|
||||
A_form.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y1;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
|
||||
print_vector(diff);
|
||||
print_vector(y1);
|
||||
print_vector(y2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_elasticity);
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "examples/dfem/dfem_parametricspace.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_interpolate_gradient_linear_scalar_3d(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
// const IntegrationRule &ir =
|
||||
// IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
|
||||
const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
2 * polynomial_order - 1);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
ParametricSpace pspace(dim, dim, ir.GetNPoints(),
|
||||
dim * ir.GetNPoints() * mesh.GetNE());
|
||||
ParametricFunction qdata(pspace);
|
||||
|
||||
auto kernel = [](const tensor<double, dim> &dudxi,
|
||||
const tensor<double, dim, dim> &J)
|
||||
{
|
||||
return mfem::tuple{dudxi * inv(J)};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}};
|
||||
mfem::tuple output_operator = {None{"qdata"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array
|
||||
{
|
||||
FieldDescriptor{&mesh_fes, "coordinates"},
|
||||
FieldDescriptor{&pspace, "qdata"}
|
||||
};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + x * y * z + y * z;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize() * dim);
|
||||
dop.SetParameters({mesh_nodes, &qdata});
|
||||
dop.Mult(x, y);
|
||||
|
||||
Vector f_test(h1fes.GetElementRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC)->Height() * dim);
|
||||
for (int e = 0; e < mesh.GetNE(); e++)
|
||||
{
|
||||
ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
|
||||
for (int qp = 0; qp < ir.GetNPoints(); qp++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(qp);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
Vector g(dim);
|
||||
f1_g.GetGradient(*T, g);
|
||||
// printf("(%f, %f, %f): (%f, %f, %f)\n", ip.x, ip.y, ip.z, g(0), g(1), g(2));
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
int qpo = qp * dim;
|
||||
int eo = e * (ir.GetNPoints() * dim);
|
||||
f_test(d + qpo + eo) = g(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector diff(f_test);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(f_test);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_interpolate_gradient_linear_scalar_3d);
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_interpolate_linear_scalar(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const double &u, const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
return mfem::tuple{u};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator = {None{"potential"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
return 2.345 + x + y;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
Vector f_test(h1fes.GetElementRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC)->Height());
|
||||
for (int e = 0; e < mesh.GetNE(); e++)
|
||||
{
|
||||
ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
for (int qp = 0; qp < ir.GetNPoints(); qp++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(qp);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
|
||||
}
|
||||
}
|
||||
|
||||
Vector diff(f_test);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(f_test);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_interpolate_linear_scalar);
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_interpolate_linear_scalar_3d(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const double &u)
|
||||
{
|
||||
return mfem::tuple{u};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}};
|
||||
mfem::tuple output_operator = {None{"potential"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + x + y + 1.25 * z;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
Vector f_test(h1fes.GetElementRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC)->Height());
|
||||
for (int e = 0; e < mesh.GetNE(); e++)
|
||||
{
|
||||
ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
for (int qp = 0; qp < ir.GetNPoints(); qp++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(qp);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
|
||||
}
|
||||
}
|
||||
|
||||
Vector diff(f_test);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(f_test);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_interpolate_linear_scalar_3d);
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_interpolate_linear_vector(std::string mesh_file, int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int vdim = 2;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
QuadratureSpace qspace(mesh, ir);
|
||||
QuadratureFunction qf(&qspace, vdim);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const tensor<double, 2> &u)
|
||||
{
|
||||
return mfem::tuple{u};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}};
|
||||
mfem::tuple output_operator = {None{"potential"}};
|
||||
|
||||
ElementOperator eop{kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = 2.345 + x + y;
|
||||
u(1) = 12.345 + x + y;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient f1_c(vdim, f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(f1_g.Size());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
Vector f_test(qf.Size());
|
||||
for (int e = 0; e < mesh.GetNE(); e++)
|
||||
{
|
||||
ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
for (int qp = 0; qp < ir.GetNPoints(); qp++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(qp);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
Vector f(vdim);
|
||||
f1_g.GetVectorValue(*T, ip, f);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
int qpo = qp * vdim;
|
||||
int eo = e * (ir.GetNPoints() * vdim);
|
||||
f_test(d + qpo + eo) = f(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector diff(f_test);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(f_test);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_interpolate_linear_vector);
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_interpolate_linear_vector_3d(std::string mesh_file, int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
|
||||
constexpr int dim = 3;
|
||||
constexpr int vdim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
QuadratureSpace qspace(mesh, ir);
|
||||
QuadratureFunction qf(&qspace, vdim);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const tensor<double, vdim> &u)
|
||||
{
|
||||
return mfem::tuple{u};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}};
|
||||
mfem::tuple output_operator = {None{"potential"}};
|
||||
|
||||
ElementOperator eop{kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
u(0) = 2.345 + x + y + 3.0 * z;
|
||||
u(1) = 12.345 + x + y + 2.0 * z;
|
||||
u(2) = 5.345 + x + y + 1.0 * z;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient f1_c(vdim, f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(f1_g.Size());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
Vector f_test(qf.Size());
|
||||
for (int e = 0; e < mesh.GetNE(); e++)
|
||||
{
|
||||
ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
for (int qp = 0; qp < ir.GetNPoints(); qp++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(qp);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
Vector f(vdim);
|
||||
f1_g.GetVectorValue(*T, ip, f);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
int qpo = qp * vdim;
|
||||
int eo = e * (ir.GetNPoints() * vdim);
|
||||
f_test(d + qpo + eo) = f(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector diff(f_test);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(f_test);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_interpolate_linear_vector_3d);
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "fem/bilininteg.hpp"
|
||||
#include "fem/normal_deriv_restriction.hpp"
|
||||
#include <fstream>
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int dfem_test_mass_scalar_2d(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 2;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
|
||||
|
||||
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
|
||||
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
// 2 * polynomial_order - 1);
|
||||
|
||||
printf("#nqp = %d\n", ir.GetNPoints());
|
||||
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const double& u,
|
||||
const tensor<double, dim> x,
|
||||
const tensor<double, dim, dim> J,
|
||||
const double& w)
|
||||
{
|
||||
out << x << ": " << u << "\n";
|
||||
return mfem::tuple{u * w * det(J)};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}, Value{"coordinates"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator = {Value{"potential"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
return 2.345 + x + x*y + 1.25 * x;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector f1_g_e(f1_g.Size());
|
||||
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
|
||||
// R->Mult(f1_g, f1_g_e);
|
||||
auto r_out = std::ofstream("r_mat.mtx");
|
||||
R->PrintMatlab(r_out);
|
||||
r_out.close();
|
||||
print_vector(f1_g);
|
||||
// print_vector(f1_g_e);
|
||||
|
||||
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto mass_integ = new MassIntegrator;
|
||||
mass_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(mass_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
print_vector(diff);
|
||||
print_vector(y2);
|
||||
print_vector(y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(dfem_test_mass_scalar_2d);
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "fem/bilininteg.hpp"
|
||||
#include "fem/fe/fe_base.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int dfem_test_mass_scalar_3d(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
|
||||
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(polynomial_order);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
|
||||
0)->GetDim() - 1);
|
||||
|
||||
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
|
||||
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
// 2 * polynomial_order - 1);
|
||||
|
||||
auto dtq = h1fes.GetFE(0)->GetDofToQuad(ir, DofToQuad::TENSOR);
|
||||
// printf("\n B: ");
|
||||
// dtq.B.Print(out, dtq.B.Size());
|
||||
// printf("\n G: ");
|
||||
// dtq.G.Print(out, dtq.G.Size());
|
||||
// printf("\n w: ");
|
||||
// ir.GetWeights().Print(out, ir.GetWeights().Size());
|
||||
|
||||
// printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
|
||||
// printf("#nqp = %d\n", ir.GetNPoints());
|
||||
// printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
|
||||
|
||||
// printf("nodes: ");
|
||||
// print_vector(*mesh_nodes);
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
auto kernel = [](const double &u,
|
||||
const tensor<double, dim, dim> &J,
|
||||
const double &w)
|
||||
{
|
||||
return mfem::tuple{u * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator = {Value{"potential"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + x + x*y + 1.25 * z*x;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
// printf("\nf1_g: ");
|
||||
// print_vector(f1_g);
|
||||
|
||||
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
|
||||
// Vector f1_g_e(R->Height());
|
||||
// R->Mult(f1_g, f1_g_e);
|
||||
// printf("\nf1_g_e: ");
|
||||
// print_vector(f1_g_e);
|
||||
// auto r_out = std::ofstream("r_mat.mtx");
|
||||
// R->PrintMatlab(r_out);
|
||||
// r_out.close();
|
||||
|
||||
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto mass_integ = new MassIntegrator;
|
||||
mass_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(mass_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-15)
|
||||
{
|
||||
printf("y ");
|
||||
print_vector(y);
|
||||
printf("y2: ");
|
||||
print_vector(y2);
|
||||
printf("diff: ");
|
||||
print_vector(diff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vector y3(h1fes.TrueVSize());
|
||||
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
|
||||
|
||||
dFdu->Mult(x, y3);
|
||||
diff = y2;
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-15)
|
||||
{
|
||||
printf("y2 ");
|
||||
print_vector(y2);
|
||||
printf("y3: ");
|
||||
print_vector(y3);
|
||||
printf("diff: ");
|
||||
print_vector(diff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(dfem_test_mass_scalar_3d);
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_neo_hookean_elasticity_2d(
|
||||
std::string mesh_file, int refinements, int polynomial_order)
|
||||
{
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
MFEM_ASSERT(dim == 2, "This test is for 2D meshes only");
|
||||
mesh_serial.Clear();
|
||||
|
||||
out << "#el: " << mesh.GetNE() << "\n";
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, dim);
|
||||
|
||||
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
|
||||
|
||||
out << "#qp: " << ir.GetNPoints() << "\n";
|
||||
|
||||
ParGridFunction u_g(&h1fes);
|
||||
|
||||
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
|
||||
const double& w,
|
||||
const tensor<double, 2, 2>& dudxi)
|
||||
{
|
||||
// Neo-Hookean parameters
|
||||
const double lambda = 1.0;
|
||||
const double mu = 0.5;
|
||||
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
|
||||
auto F = I + (dudxi * inv(J));
|
||||
auto E = 0.5 * (transpose(F) * F - I);
|
||||
auto invF = inv(F);
|
||||
|
||||
// 2D plane strain formulation
|
||||
auto P = mu * (F - transpose(invF)) + lambda * log(det(F)) * transpose(invF);
|
||||
|
||||
return mfem::tuple{P * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{},
|
||||
Gradient{"displacement"}
|
||||
};
|
||||
mfem::tuple output_operator = {Gradient{"displacement"}};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array{FieldDescriptor{&h1fes, "displacement"}};
|
||||
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto displacement = [](const Vector& coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = 0.1 * x * y;
|
||||
u(1) = 0.1 * y * x;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient disp_coeff(2, displacement);
|
||||
u_g.ProjectCoefficient(disp_coeff);
|
||||
|
||||
Vector x(u_g), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
y.HostRead();
|
||||
|
||||
// Test linearization
|
||||
auto dFdu = dop.GetDerivativeWrt<0>({&u_g}, {mesh_nodes});
|
||||
dFdu->Mult(x, y);
|
||||
|
||||
// Finite difference Jacobian test
|
||||
{
|
||||
double eps = 1.0e-6;
|
||||
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
|
||||
v *= eps;
|
||||
xpv += v;
|
||||
xmv -= v;
|
||||
dop.Mult(xpv, fxpv);
|
||||
dop.Mult(xmv, fxmv);
|
||||
fxpv -= fxmv;
|
||||
fxpv /= (2.0*eps);
|
||||
|
||||
fxpv -= y;
|
||||
if (fxpv.Norml2() > eps)
|
||||
{
|
||||
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_neo_hookean_elasticity_2d);
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_nonlinear_diffusion(
|
||||
std::string mesh_file, int refinements, int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
mesh_serial.Clear();
|
||||
|
||||
out << "#el: " << mesh.GetNE() << "\n";
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec);
|
||||
|
||||
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
|
||||
0)->GetDim() - 1);
|
||||
|
||||
out << "#qp: " << ir.GetNPoints() << "\n";
|
||||
|
||||
ParGridFunction f1_g(&h1fes);
|
||||
|
||||
bool inactive_derivative = false;
|
||||
|
||||
auto kernel = [] MFEM_HOST_DEVICE(
|
||||
const tensor<double, dim, dim>& J,
|
||||
const double& w,
|
||||
const tensor<double, dim>& dudxi,
|
||||
const double& u)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
return mfem::tuple{(u * u) * dudxi * invJ * transpose(invJ) * det(J) * w};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators =
|
||||
{
|
||||
Gradient{"coordinates"},
|
||||
Weight{},
|
||||
Gradient{"potential"},
|
||||
Value{"potential"}
|
||||
};
|
||||
|
||||
mfem::tuple output_operator =
|
||||
{
|
||||
Gradient{"potential"}
|
||||
};
|
||||
|
||||
ElementOperator eop = {kernel, argument_operators, output_operator};
|
||||
auto ops = mfem::tuple{eop};
|
||||
|
||||
auto solutions = std::array
|
||||
{
|
||||
FieldDescriptor{&h1fes, "potential"}
|
||||
};
|
||||
auto parameters = std::array
|
||||
{
|
||||
FieldDescriptor{&mesh_fes, "coordinates"}
|
||||
};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
|
||||
|
||||
auto f1 = [](const Vector& coords)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
const double z = coords(2);
|
||||
return 2.345 + 0.25 * x * x * y + y * y * x + z;
|
||||
};
|
||||
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_g.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_g), y(h1fes.TrueVSize());
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y);
|
||||
y.HostRead();
|
||||
|
||||
ParBilinearForm a(&h1fes);
|
||||
GridFunctionCoefficient f1gc(&f1_g);
|
||||
TransformedCoefficient tf_c(&f1gc, [](double f) { return f * f; });
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(tf_c));
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
|
||||
Vector y2(h1fes.TrueVSize()), diff(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
diff = y2;
|
||||
diff -= y;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
|
||||
print_vector(diff);
|
||||
print_vector(y);
|
||||
print_vector(y2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test linearization here as well
|
||||
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
|
||||
dFdu->Mult(x, y);
|
||||
|
||||
// fd jacobian test
|
||||
{
|
||||
double eps = 1.0e-6;
|
||||
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
|
||||
v *= eps;
|
||||
xpv += v;
|
||||
xmv -= v;
|
||||
dop.Mult(xpv, fxpv);
|
||||
dop.Mult(xmv, fxmv);
|
||||
fxpv -= fxmv;
|
||||
fxpv /= (2.0*eps);
|
||||
|
||||
fxpv -= y;
|
||||
if (fxpv.Norml2() > eps)
|
||||
{
|
||||
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ParBilinearForm da(&h1fes);
|
||||
// TransformedCoefficient dtf_c(&f1gc, [](double f) { return 2.0 * f; });
|
||||
// da.AddDomainIntegrator(new DiffusionIntegrator(dtf_c));
|
||||
// da.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
// da.Assemble();
|
||||
// da.Finalize();
|
||||
|
||||
// if (dFdu->Height() != h1fes.GetTrueVSize())
|
||||
// {
|
||||
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// dFdu->Mult(x, y);
|
||||
// print_vector(y);
|
||||
// da.Mult(x, y2);
|
||||
// print_vector(y2);
|
||||
// y2 -= y;
|
||||
// out << "||dFdu x - A x||_l2 = " << y2.Norml2() << "\n";
|
||||
// if (y2.Norml2() > 1e-10)
|
||||
// {
|
||||
// out << "||dFdu u^* - ex||_l2 = " << y2.Norml2() << "\n";
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_nonlinear_diffusion);
|
||||
@@ -0,0 +1,267 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include <fstream>
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::dual;
|
||||
|
||||
class FDJacobian : public Operator
|
||||
{
|
||||
public:
|
||||
FDJacobian(const Operator &op, const Vector &x) :
|
||||
Operator(op.Height()),
|
||||
op(op),
|
||||
x(x)
|
||||
{
|
||||
f.SetSize(Height());
|
||||
xpev.SetSize(Height());
|
||||
op.Mult(x, f);
|
||||
xnorm = x.Norml2();
|
||||
}
|
||||
|
||||
void Mult(const Vector &v, Vector &y) const override
|
||||
{
|
||||
x.HostRead();
|
||||
|
||||
// See [1] for choice of eps.
|
||||
//
|
||||
// [1] Woodward, C.S., Gardner, D.J. and Evans, K.J., 2015. On the use of
|
||||
// finite difference matrix-vector products in Newton-Krylov solvers for
|
||||
// implicit climate dynamics with spectral elements. Procedia Computer
|
||||
// Science, 51, pp.2036-2045.
|
||||
real_t eps = lambda * (lambda + xnorm / v.Norml2());
|
||||
|
||||
for (int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
xpev(i) = x(i) + eps * v(i);
|
||||
}
|
||||
|
||||
// y = f(x + eps * v)
|
||||
op.Mult(xpev, y);
|
||||
|
||||
// y = (f(x + eps * v) - f(x)) / eps
|
||||
for (int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
y(i) = (y(i) - f(i)) / eps;
|
||||
}
|
||||
}
|
||||
|
||||
virtual MemoryClass GetMemoryClass() const override
|
||||
{
|
||||
return Device::GetDeviceMemoryClass();
|
||||
}
|
||||
|
||||
private:
|
||||
const Operator &op;
|
||||
Vector x, f;
|
||||
mutable Vector xpev;
|
||||
real_t lambda = 1.0e-6;
|
||||
real_t xnorm;
|
||||
};
|
||||
|
||||
template <typename elasticity_t>
|
||||
class ElasticityOperator : public Operator
|
||||
{
|
||||
template <typename elasticity_du_t>
|
||||
class ElasticityJacobianOperator : public Operator
|
||||
{
|
||||
public:
|
||||
ElasticityJacobianOperator(const ElasticityOperator *elasticity,
|
||||
std::shared_ptr<elasticity_du_t> dRdu) :
|
||||
Operator(elasticity->Height()),
|
||||
elasticity(elasticity),
|
||||
dRdu(dRdu),
|
||||
x_ess(dRdu->Height())
|
||||
{
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
x_ess = x;
|
||||
x_ess.SetSubVector(elasticity->ess_tdofs, 0.0);
|
||||
|
||||
dRdu->Mult(x_ess, y);
|
||||
|
||||
for (int i = 0; i < elasticity->ess_tdofs.Size(); i++)
|
||||
{
|
||||
y[elasticity->ess_tdofs[i]] = x[elasticity->ess_tdofs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
const ElasticityOperator *elasticity = nullptr;
|
||||
std::shared_ptr<elasticity_du_t> dRdu;
|
||||
mutable Vector x_ess;
|
||||
};
|
||||
|
||||
public:
|
||||
ElasticityOperator(ParFiniteElementSpace &fes, elasticity_t &elasticity,
|
||||
Array<int> &ess_tdofs) :
|
||||
Operator(fes.GetTrueVSize()),
|
||||
fes(fes),
|
||||
elasticity(elasticity),
|
||||
ess_tdofs(ess_tdofs) {}
|
||||
|
||||
void Mult(const Vector &x, Vector &r) const override
|
||||
{
|
||||
elasticity.Mult(x, r);
|
||||
r.SetSubVector(ess_tdofs, 0.0);
|
||||
}
|
||||
|
||||
Operator &GetGradient(const Vector &x) const override
|
||||
{
|
||||
ParGridFunction u(const_cast<ParFiniteElementSpace *>
|
||||
(*std::get_if<const ParFiniteElementSpace *>
|
||||
(&elasticity.solutions[0].data)));
|
||||
|
||||
u.SetFromTrueDofs(x);
|
||||
auto dRdu = elasticity.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
|
||||
|
||||
jacobian.reset(
|
||||
new ElasticityJacobianOperator<
|
||||
typename std::remove_pointer<decltype(dRdu.get())>::type> (this, dRdu));
|
||||
|
||||
// jacobian.reset(new FDJacobian(*this, x));
|
||||
|
||||
return *jacobian;
|
||||
}
|
||||
|
||||
void SetParameters(ParGridFunction &mesh_nodes)
|
||||
{
|
||||
elasticity.SetParameters({&mesh_nodes});
|
||||
this->mesh_nodes = &mesh_nodes;
|
||||
}
|
||||
|
||||
ParFiniteElementSpace &fes;
|
||||
elasticity_t &elasticity;
|
||||
Array<int> ess_tdofs;
|
||||
mutable ParGridFunction *mesh_nodes = nullptr;
|
||||
mutable std::shared_ptr<Operator> jacobian;
|
||||
};
|
||||
|
||||
int test_nonlinear_elasticity_3d(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 3;
|
||||
constexpr int vdim = dim;
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(polynomial_order);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_tdof_list, ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 0;
|
||||
ess_bdr[0] = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
const IntegrationRule& ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
|
||||
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
|
||||
0)->GetDim() - 1);
|
||||
|
||||
out << "#qp: " << ir.GetNPoints() << "\n";
|
||||
out << "#dof: " << h1fes.GetNDofs() << "\n";
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto elasticity_kernel = [] MFEM_HOST_DEVICE
|
||||
(const tensor<dual<real_t, real_t>, dim, dim> &dudxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
// shear modulus
|
||||
real_t D1{0.1e6};
|
||||
// bulk modulus
|
||||
real_t C1{1.0e6};
|
||||
constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
auto invJ = inv(J);
|
||||
auto dudx = dudxi * invJ;
|
||||
auto F = det(I + dudx);
|
||||
auto p = -2.0 * D1 * F * (F - 1);
|
||||
auto devB = dev(dudx + transpose(dudx) + dot(dudx, transpose(dudx)));
|
||||
auto sigma = -(p / F) * I + 2.0 * (C1 / pow(F, 5.0 / 3.0)) * devB;
|
||||
|
||||
return mfem::tuple{sigma * det(J) * w * transpose(invJ)};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator{Gradient{"displacement"}};
|
||||
|
||||
// B^T D(B0*dudxi, B1*J, B2*w)
|
||||
ElementOperator op(elasticity_kernel, argument_operators, output_operator, ir);
|
||||
|
||||
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop(solutions, parameters, mfem::tuple{op}, mesh,
|
||||
AutoDiff::NativeDualNumber{});
|
||||
|
||||
ElasticityOperator elasticity(h1fes, dop, ess_tdof_list);
|
||||
|
||||
VectorArrayCoefficient f(dim);
|
||||
for (int i = 0; i < dim-1; i++)
|
||||
{
|
||||
f.Set(i, new ConstantCoefficient(0.0));
|
||||
}
|
||||
{
|
||||
Vector pull_force(mesh.bdr_attributes.Max());
|
||||
pull_force = 0.0;
|
||||
pull_force(1) = -1.0e-2;
|
||||
f.Set(dim-1, new PWConstCoefficient(pull_force));
|
||||
}
|
||||
|
||||
ParLinearForm b(&h1fes);
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.UseFastAssembly(true);
|
||||
b.Assemble();
|
||||
auto B = b.ParallelAssemble();
|
||||
|
||||
Vector X = u.GetTrueVector();
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-8);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
||||
|
||||
NewtonSolver newton(MPI_COMM_WORLD);
|
||||
newton.SetSolver(cg);
|
||||
newton.SetOperator(elasticity);
|
||||
newton.SetRelTol(1e-6);
|
||||
newton.SetMaxIter(100);
|
||||
// newton.SetAdaptiveLinRtol();
|
||||
newton.SetPrintLevel(IterativeSolver::PrintLevel().Iterations());
|
||||
|
||||
elasticity.SetParameters(*mesh_nodes);
|
||||
|
||||
// Vector zero;
|
||||
newton.Mult(*B, X);
|
||||
|
||||
u.SetFromTrueDofs(X);
|
||||
|
||||
ParaViewDataCollection paraview_dc("dfem", &mesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(polynomial_order);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.SetCycle(0);
|
||||
paraview_dc.SetTime(0.0);
|
||||
paraview_dc.RegisterField("displacement", &u);
|
||||
paraview_dc.Save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_nonlinear_elasticity_3d);
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
#include "fem/coefficient.hpp"
|
||||
#include "fem/pgridfunc.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_ordering(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
constexpr int dim = 2;
|
||||
constexpr int vdim = dim;
|
||||
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(polynomial_order);
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(mesh_fes.GetFE(0)->GetGeomType(),
|
||||
2 * mesh_fes.FEColl()->GetOrder() - 1);
|
||||
|
||||
for (int q = 0; q < ir.GetNPoints(); q++)
|
||||
{
|
||||
out << "(" << ir.IntPoint(q).x << ", " << ir.IntPoint(q).y << ")\n";
|
||||
}
|
||||
|
||||
ParGridFunction u(&mesh_fes);
|
||||
auto f = [](const Vector &coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = x*x*y + 1.0;
|
||||
u(1) = y*y*x*x + 2.0;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient uc(dim, f);
|
||||
u.ProjectCoefficient(uc);
|
||||
|
||||
auto kernel = [](const tensor<double, dim> &xi,
|
||||
const tensor<double, vdim, dim> &J,
|
||||
const tensor<double, dim> &u,
|
||||
const tensor<double, vdim, dim> &dudxi)
|
||||
{
|
||||
out << "xi: " << xi << "\n";
|
||||
out << "J: " << J << "\n";
|
||||
out << "u: " << u << "\n";
|
||||
out << "dudxi: " << dudxi << "\n\n";
|
||||
return mfem::tuple{J};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"coordinates"}, Value{"potential"}, Gradient{"potential"}};
|
||||
mfem::tuple output_operator{Gradient{"potential"}};
|
||||
|
||||
ElementOperator op{kernel, argument_operators, output_operator};
|
||||
|
||||
std::array solutions{FieldDescriptor{&mesh_fes, "potential"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
|
||||
|
||||
Vector y(u);
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(u, y);
|
||||
|
||||
print_vector(y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_ordering);
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "dfem/dfem.hpp"
|
||||
#include "dfem/dfem_test_macro.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
int test_vector_diffusion(std::string mesh_file,
|
||||
int refinements,
|
||||
int polynomial_order)
|
||||
{
|
||||
Mesh mesh_serial = Mesh(mesh_file);
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh_serial.UniformRefinement();
|
||||
}
|
||||
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
mesh.SetCurvature(1);
|
||||
const int dim = mesh.Dimension();
|
||||
const int vdim = dim;
|
||||
mesh_serial.Clear();
|
||||
|
||||
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
H1_FECollection h1fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
|
||||
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
Array<int> ess_tdof;
|
||||
ess_bdr = 1;
|
||||
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
|
||||
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() - 1);
|
||||
|
||||
ParGridFunction u(&h1fes);
|
||||
|
||||
auto f1 = [](const Vector& coords, Vector &u)
|
||||
{
|
||||
const double x = coords(0);
|
||||
const double y = coords(1);
|
||||
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
|
||||
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient u_c(dim, f1);
|
||||
u.ProjectCoefficient(u_c);
|
||||
|
||||
auto vector_diffusion_kernel = [](const tensor<double, 2> &xi,
|
||||
const tensor<double, 2, 2> &dudxi,
|
||||
const tensor<double, 2, 2> &J,
|
||||
const double &w)
|
||||
{
|
||||
out << "xi: " << xi << "\n";
|
||||
out << "dudxi: " << dudxi << "\n";
|
||||
return mfem::tuple{dudxi * inv(J) * det(J) * w * transpose(inv(J))};
|
||||
// return mfem::tuple{dudxi};
|
||||
};
|
||||
|
||||
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
|
||||
mfem::tuple output_operator{Gradient{"potential"}};
|
||||
|
||||
ElementOperator op{vector_diffusion_kernel, argument_operators, output_operator};
|
||||
|
||||
std::array solutions{FieldDescriptor{&h1fes, "potential"}};
|
||||
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
|
||||
|
||||
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
|
||||
|
||||
Vector x(u), y1(h1fes.GetTrueVSize()),
|
||||
y2(h1fes.GetTrueVSize());
|
||||
|
||||
ParBilinearForm A_form(&h1fes);
|
||||
auto A_integ = new VectorDiffusionIntegrator(vdim);
|
||||
A_integ->SetIntegrationRule(ir);
|
||||
A_form.AddDomainIntegrator(A_integ);
|
||||
A_form.Assemble();
|
||||
A_form.Finalize();
|
||||
|
||||
dop.SetParameters({mesh_nodes});
|
||||
dop.Mult(x, y1);
|
||||
y1.HostRead();
|
||||
|
||||
A_form.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y1;
|
||||
if (diff.Norml2() > 1e-10)
|
||||
{
|
||||
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
|
||||
print_vector(diff);
|
||||
print_vector(y1);
|
||||
print_vector(y2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFEM_TEST_MAIN(test_vector_diffusion);
|
||||
@@ -0,0 +1,122 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <iostream>
|
||||
#include <enzyme/enzyme>
|
||||
|
||||
template <typename T>
|
||||
constexpr auto get_type_name() -> std::string_view
|
||||
{
|
||||
#if defined(__clang__)
|
||||
constexpr auto prefix = std::string_view {"[T = "};
|
||||
constexpr auto suffix = "]";
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(__GNUC__)
|
||||
constexpr auto prefix = std::string_view {"with T = "};
|
||||
constexpr auto suffix = "; ";
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(_MSC_VER)
|
||||
constexpr auto prefix = std::string_view {"get_type_name<"};
|
||||
constexpr auto suffix = ">(void)";
|
||||
constexpr auto function = std::string_view{__FUNCSIG__};
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
|
||||
const auto start = function.find(prefix) + prefix.size();
|
||||
const auto end = function.find(suffix);
|
||||
const auto size = end - start;
|
||||
|
||||
return function.substr(start, size);
|
||||
}
|
||||
|
||||
template <typename ... Ts>
|
||||
constexpr auto decay_types(std::tuple<Ts...> const &)
|
||||
-> std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
|
||||
|
||||
template <typename T>
|
||||
using decay_tuple = decltype(decay_types(std::declval<T>()));
|
||||
|
||||
template <class F> struct FunctionSignature;
|
||||
|
||||
template <typename output_t, typename... input_ts>
|
||||
struct FunctionSignature<output_t(input_ts...)>
|
||||
{
|
||||
using return_t = output_t;
|
||||
using parameter_ts = std::tuple<input_ts...>;
|
||||
};
|
||||
|
||||
template <class T> struct create_function_signature;
|
||||
|
||||
template <typename output_t, typename T, typename... input_ts>
|
||||
struct create_function_signature<output_t (T::*)(input_ts...) const>
|
||||
{
|
||||
using type = FunctionSignature<output_t(input_ts...)>;
|
||||
};
|
||||
|
||||
template <typename arg_ts, std::size_t... Is>
|
||||
auto create_enzyme_args(arg_ts &args,
|
||||
arg_ts &shadow_args,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
// (std::cout << ... << std::get<Is>(shadow_args));
|
||||
return std::tuple<enzyme::Duplicated<decltype(std::get<Is>(args))>...>
|
||||
{
|
||||
{ std::get<Is>(args), std::get<Is>(shadow_args) }...
|
||||
};
|
||||
}
|
||||
|
||||
template <typename kernel_t, typename arg_ts>
|
||||
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args, arg_ts &&shadow_args)
|
||||
{
|
||||
auto arg_indices =
|
||||
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<arg_ts>>> {};
|
||||
|
||||
auto enzyme_args = create_enzyme_args(args, shadow_args, arg_indices);
|
||||
|
||||
// using kf_return_t = typename create_function_signature<
|
||||
// decltype(&kernel_t::operator())>::type::return_t;
|
||||
|
||||
std::cout << "\n";
|
||||
std::cout << "args is " << get_type_name<decltype(args)>() << "\n\n";
|
||||
std::cout << "enzyme_args type is " << get_type_name<decltype(enzyme_args)>() <<
|
||||
"\n\n";
|
||||
// std::cout << "return type is " << get_type_name<decltype(kf_return_t{})>() <<
|
||||
// "\n\n";
|
||||
|
||||
std::cout << "args " << std::get<0>(args) << "\n";
|
||||
std::cout << "shadow args " << std::get<0>(shadow_args) << "\n";
|
||||
|
||||
return std::apply([&](auto &&...args)
|
||||
{
|
||||
// std::cout << enzyme::autodiff<enzyme::Forward>(+kernel, args...) << "\n";
|
||||
return enzyme::get<0>
|
||||
(enzyme::autodiff<enzyme::Forward>(+kernel, args...));
|
||||
},
|
||||
enzyme_args);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
auto func = [](const double &x, double &y)
|
||||
{
|
||||
std::cout << "func( x = " << x << " )\n";
|
||||
return x*x;
|
||||
};
|
||||
|
||||
using kf_param_ts = typename create_function_signature<
|
||||
decltype(&decltype(func)::operator())>::type::parameter_ts;
|
||||
using kf_output_t = typename create_function_signature<
|
||||
decltype(&decltype(func)::operator())>::type::return_t;
|
||||
auto kernel_args = decay_tuple<kf_param_ts> {};
|
||||
auto kernel_shadow_args = decay_tuple<kf_param_ts> {};
|
||||
|
||||
std::get<0>(kernel_args) = 3;
|
||||
std::get<0>(kernel_shadow_args) = 1;
|
||||
|
||||
auto dx = fwddiff_apply_enzyme(func, kernel_args, kernel_shadow_args);
|
||||
|
||||
std::cout << "dfdx = " << dx << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
+9
-27
@@ -20,7 +20,6 @@
|
||||
// ex14 -m ../data/fichera-amr.mesh
|
||||
// ex14 -pa -r 1 -o 3
|
||||
// ex14 -pa -r 1 -o 3 -m ../data/fichera.mesh
|
||||
// ex14 -m ../data/inline-tet.mesh -o 0 -nt 4 -s 1
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex14 -pa -r 2 -d cuda -o 3
|
||||
@@ -56,16 +55,10 @@ int main(int argc, char *argv[])
|
||||
bool pa = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
int nt = 0;
|
||||
double st = 1.0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&nt, "-nt", "--number-of-timeslices",
|
||||
"Number of slices through the hyper-prism in the 4th coordinate.");
|
||||
args.AddOption(&st, "-st", "--size-time",
|
||||
"Length of hyper-prims in 4th coordinate (e.g. time).");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly, -1 for auto.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
@@ -104,17 +97,8 @@ int main(int argc, char *argv[])
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
|
||||
// NURBS meshes are projected to second order meshes.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
if (dim == 3 && nt > 0)
|
||||
{
|
||||
Mesh* spat_mesh = mesh;
|
||||
mesh = new Mesh(spat_mesh, nt, Element::PENTATOPE, true, st);
|
||||
delete spat_mesh;
|
||||
|
||||
ref_levels = 0;
|
||||
dim = 4;
|
||||
}
|
||||
Mesh mesh(mesh_file);
|
||||
const int dim = mesh.Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. By default, or if ref_levels < 0,
|
||||
@@ -123,23 +107,23 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (ref_levels < 0)
|
||||
{
|
||||
ref_levels = (int)floor(log(50000./mesh->GetNE())/log(2.)/(dim < 4 ? dim : 1.));
|
||||
ref_levels = (int)floor(log(50000./mesh.GetNE())/log(2.)/dim);
|
||||
}
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
if (mesh->NURBSext)
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
mesh.SetCurvature(max(order, 1));
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use discontinuous
|
||||
// finite elements of the specified order >= 0.
|
||||
const auto bt = pa ? BasisType::GaussLobatto : BasisType::GaussLegendre;
|
||||
DG_FECollection fec(order, dim, bt);
|
||||
FiniteElementSpace fespace(mesh, &fec);
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
cout << "Number of unknowns: " << fespace.GetVSize() << endl;
|
||||
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
@@ -214,7 +198,7 @@ int main(int argc, char *argv[])
|
||||
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
mesh.Print(mesh_ofs);
|
||||
ofstream sol_ofs("sol.gf");
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
@@ -226,10 +210,8 @@ int main(int argc, char *argv[])
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
}
|
||||
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+8
-24
@@ -19,7 +19,6 @@
|
||||
// mpirun -np 4 ex14p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex14p -pa -rs 1 -rp 0 -o 3
|
||||
// mpirun -np 4 ex14p -pa -rs 1 -rp 0 -m ../data/fichera.mesh -o 3
|
||||
// mpirun -np 4 ex14p -m ../data/inline-tet.mesh -o 0 -nt 4 -s 1
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex14p -pa -rs 2 -rp 0 -d cuda -o 3
|
||||
@@ -91,16 +90,10 @@ int main(int argc, char *argv[])
|
||||
bool pa = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
int nt = 0;
|
||||
double st = 1.0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&nt, "-nt", "--number-of-timeslices",
|
||||
"Number of slices through the hyper-prism in the 4th coordinate.");
|
||||
args.AddOption(&st, "-st", "--size-time",
|
||||
"Length of hyper-prims in 4th coordinate (e.g. time).");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial,"
|
||||
" -1 for auto.");
|
||||
@@ -146,17 +139,8 @@ int main(int argc, char *argv[])
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
|
||||
// with the same code. NURBS meshes are projected to second order meshes.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
if (dim == 3 && nt > 0)
|
||||
{
|
||||
Mesh* spat_mesh = mesh;
|
||||
mesh = new Mesh(spat_mesh, nt, Element::PENTATOPE, true, st);
|
||||
delete spat_mesh;
|
||||
dim = 4;
|
||||
}
|
||||
if (dim == 4)
|
||||
ser_ref_levels = 0;
|
||||
Mesh mesh(mesh_file);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ser_ref_levels' of uniform refinement. By default,
|
||||
@@ -165,23 +149,23 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (ser_ref_levels < 0)
|
||||
{
|
||||
ser_ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
ser_ref_levels = (int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
}
|
||||
for (int l = 0; l < ser_ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
if (mesh->NURBSext)
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
mesh.SetCurvature(max(order, 1));
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
{
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "./spe10_coeff.cpp"
|
||||
|
||||
|
||||
int* LoadIterations(int NRows, int NCol)
|
||||
{
|
||||
ifstream in("iter_grad.txt");
|
||||
|
||||
//initialize
|
||||
int *iters = new int[NCol*NRows];
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
iters[row*NCol+col] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
return iters;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
if (in.eof())
|
||||
{
|
||||
in.close();
|
||||
return iters;
|
||||
}
|
||||
in >> iters[row*NCol+col];
|
||||
}
|
||||
|
||||
|
||||
in.close();
|
||||
|
||||
return iters;
|
||||
}
|
||||
|
||||
void putIterationsInArray(int iter, int row, int col, int NCol, int* iters)
|
||||
{
|
||||
iters[row*NCol+col] = iter;
|
||||
}
|
||||
|
||||
void WriteIterations(int *iters, int NRows, int NCol)
|
||||
{
|
||||
ofstream out;
|
||||
out.open("iter_grad.txt",fstream::out);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
delete[] iters;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
out << iters[row*NCol+col] << "\t";
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
out.close();
|
||||
|
||||
delete[] iters;
|
||||
}
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
double kappa = 1.0;
|
||||
|
||||
double u_exact(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
return cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*cos(M_PI*x(3));
|
||||
}
|
||||
else { return 0.0; }
|
||||
}
|
||||
|
||||
double f_exact(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
return (kappa + 4.0 * M_PI*M_PI) * cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(
|
||||
2))*cos(M_PI*x(3));
|
||||
}
|
||||
else { return 0.0; }
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
bool verbose = (myid==0);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/cube4d_96.MFEM";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
int sequ_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
double tol = 1e-6;
|
||||
bool set_bc = true;
|
||||
bool standardCG = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&sequ_ref_levels, "-sr", "--seqrefinement",
|
||||
"Number of sequential refinement steps.");
|
||||
args.AddOption(&par_ref_levels, "-pr", "--parrefinement",
|
||||
"Number of parallel refinement steps.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Polynomial order of the finite element space.");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"A parameter.");
|
||||
args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
|
||||
"Impose or not essential boundary conditions.");
|
||||
args.AddOption(&standardCG, "-sCG", "--stdCG", "-rCG", "--resCG",
|
||||
"Switch between standard PCG or recompute residuals in every step and use the residuals itself for the stopping criteria.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (verbose) { args.PrintOptions(cout); }
|
||||
|
||||
Mesh *mesh;
|
||||
ifstream imesh(mesh_file);
|
||||
if (!imesh)
|
||||
{
|
||||
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
mesh = new Mesh(imesh, 1, 1);
|
||||
imesh.close();
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// if(dim !=4 || sdim != 4)
|
||||
// {
|
||||
// MPI_Finalize();
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
for (int i=0; i<sequ_ref_levels; i++) { mesh->UniformRefinement(); }
|
||||
if (verbose) { mesh->PrintCharacteristics(); }
|
||||
|
||||
if (verbose) { cout << "now we partition the mesh..." << endl << endl; }
|
||||
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
for (int i=0; i<par_ref_levels; i++) { pmesh->UniformRefinement(); }
|
||||
|
||||
pmesh->PrintInfo(std::cout);
|
||||
if (verbose) { cout << endl; }
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
if (dim==4)
|
||||
{
|
||||
if (order==1) { fec = new LinearFECollection; }
|
||||
else { fec = new QuadraticFECollection; }
|
||||
}
|
||||
else { fec = new H1_FECollection(order, dim); }
|
||||
}
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = set_bc ? 1 : 0;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
|
||||
FunctionCoefficient uExact(u_exact);
|
||||
ParGridFunction x(fespace);
|
||||
|
||||
int NExpo =8;
|
||||
for (int expo=-NExpo; expo<=NExpo; expo++)
|
||||
{
|
||||
double weight = pow(10.0,expo);
|
||||
kappa = weight;
|
||||
|
||||
x.ProjectCoefficient(uExact);
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
FunctionCoefficient ffunc(f_exact);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(ffunc));
|
||||
b->Assemble();
|
||||
|
||||
x = 0.0;
|
||||
|
||||
// 10. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
|
||||
// std::string permFile = "spe_perm.dat";
|
||||
// InversePermeabilityFunction::ReadPermeabilityFile(permFile, MPI_COMM_WORLD);
|
||||
// FunctionCoefficient *cspe10 = new FunctionCoefficient(InversePermeabilityFunction::Norm2Permeability);
|
||||
Coefficient *beta = new ConstantCoefficient(weight);
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator);
|
||||
a->AddDomainIntegrator(new MassIntegrator(*beta));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
HypreSolver *amg = new HypreBoomerAMG(A);
|
||||
|
||||
int iter = -1;
|
||||
if (standardCG)
|
||||
{
|
||||
IterativeSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(tol);
|
||||
pcg->SetMaxIter(5000);
|
||||
pcg->SetPrintLevel(1);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
iter = pcg->GetNumIterations();
|
||||
|
||||
delete pcg;
|
||||
}
|
||||
else
|
||||
{
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(tol);
|
||||
pcg->SetMaxIter(5000);
|
||||
pcg->SetResidualConvergenceOptions(1,tol);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
pcg->GetNumIterations(iter);
|
||||
|
||||
delete pcg;
|
||||
}
|
||||
|
||||
|
||||
if (myid==0)
|
||||
{
|
||||
cout << "Weigth: " << weight << " " << iter << endl;
|
||||
|
||||
int *iters = LoadIterations(10, 2*NExpo+1);
|
||||
putIterationsInArray(iter, sequ_ref_levels+par_ref_levels, expo+NExpo,
|
||||
2*NExpo+1, iters);
|
||||
WriteIterations(iters, 10, 2*NExpo+1);
|
||||
}
|
||||
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
{
|
||||
double err = x.ComputeL2Error(uExact);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| u - u_h ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
// {
|
||||
// ostringstream mesh_name, sol_name;
|
||||
// mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
// sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
//
|
||||
// ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
// mesh_ofs.precision(8);
|
||||
// pmesh->Print(mesh_ofs);
|
||||
//
|
||||
// ofstream sol_ofs(sol_name.str().c_str());
|
||||
// sol_ofs.precision(8);
|
||||
// x.Save(sol_ofs);
|
||||
// }
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
// if (visualization)
|
||||
// {
|
||||
// char vishost[] = "localhost";
|
||||
// int visport = 19916;
|
||||
// socketstream sol_sock(vishost, visport);
|
||||
// sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
// sol_sock.precision(8);
|
||||
// sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
// }
|
||||
|
||||
delete amg;
|
||||
delete a;
|
||||
delete beta;
|
||||
delete b;
|
||||
}
|
||||
|
||||
// 16. Free the used memory.
|
||||
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
+16
-20
@@ -44,7 +44,7 @@ protected:
|
||||
BilinearForm *M;
|
||||
BilinearForm *K;
|
||||
|
||||
SparseMatrix Mmat, Kmat, Kmat0;
|
||||
SparseMatrix Mmat, Kmat;
|
||||
SparseMatrix *T; // T = M + dt K
|
||||
real_t current_dt;
|
||||
|
||||
@@ -83,25 +83,24 @@ WaveOperator::WaveOperator(FiniteElementSpace &f,
|
||||
: SecondOrderTimeDependentOperator(f.GetTrueVSize(), (real_t) 0.0),
|
||||
fespace(f), M(NULL), K(NULL), T(NULL), current_dt(0.0), z(height)
|
||||
{
|
||||
const real_t rel_tol = 1e-8;
|
||||
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// Assemble Laplace matrix
|
||||
c2 = new ConstantCoefficient(speed*speed);
|
||||
|
||||
K = new BilinearForm(&fespace);
|
||||
K->AddDomainIntegrator(new DiffusionIntegrator(*c2));
|
||||
K->Assemble();
|
||||
|
||||
Array<int> dummy;
|
||||
K->FormSystemMatrix(dummy, Kmat0);
|
||||
K->FormSystemMatrix(ess_tdof_list, Kmat);
|
||||
|
||||
// Assemble Mass matrix
|
||||
M = new BilinearForm(&fespace);
|
||||
M->AddDomainIntegrator(new MassIntegrator());
|
||||
M->Assemble();
|
||||
|
||||
// Apply Bcs
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
K->FormSystemMatrix(ess_tdof_list, Kmat);
|
||||
M->FormSystemMatrix(ess_tdof_list, Mmat);
|
||||
|
||||
// Configure preconditioner
|
||||
const real_t rel_tol = 1e-8;
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(rel_tol);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
@@ -110,14 +109,13 @@ WaveOperator::WaveOperator(FiniteElementSpace &f,
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(Mmat);
|
||||
|
||||
// Configure solver
|
||||
T_solver.iterative_mode = false;
|
||||
T_solver.SetRelTol(rel_tol);
|
||||
T_solver.SetAbsTol(0.0);
|
||||
T_solver.SetMaxIter(100);
|
||||
T_solver.SetPrintLevel(0);
|
||||
T_solver.SetPreconditioner(T_prec);
|
||||
|
||||
T = NULL;
|
||||
}
|
||||
|
||||
void WaveOperator::Mult(const Vector &u, const Vector &du_dt,
|
||||
@@ -126,9 +124,11 @@ void WaveOperator::Mult(const Vector &u, const Vector &du_dt,
|
||||
// Compute:
|
||||
// d2udt2 = M^{-1}*-K(u)
|
||||
// for d2udt2
|
||||
Kmat.Mult(u, z);
|
||||
K->FullMult(u, z);
|
||||
z.Neg(); // z = -z
|
||||
z.SetSubVector(ess_tdof_list, 0.0);
|
||||
M_solver.Mult(z, d2udt2);
|
||||
d2udt2.SetSubVector(ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
void WaveOperator::ImplicitSolve(const real_t fac0, const real_t fac1,
|
||||
@@ -142,14 +142,11 @@ void WaveOperator::ImplicitSolve(const real_t fac0, const real_t fac1,
|
||||
T = Add(1.0, Mmat, fac0, Kmat);
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
Kmat0.Mult(u, z);
|
||||
K->FullMult(u, z);
|
||||
z.Neg();
|
||||
|
||||
for (int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
z[ess_tdof_list[i]] = 0.0;
|
||||
}
|
||||
z.SetSubVector(ess_tdof_list, 0.0);
|
||||
T_solver.Mult(z, d2udt2);
|
||||
d2udt2.SetSubVector(ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
void WaveOperator::SetParameters(const Vector &u)
|
||||
@@ -314,7 +311,6 @@ int main(int argc, char *argv[])
|
||||
ess_bdr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
WaveOperator oper(fespace, ess_bdr, speed);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
|
||||
+9
-222
@@ -58,180 +58,6 @@ void f_exact(const Vector &, Vector &);
|
||||
real_t freq = 1.0, kappa;
|
||||
int dim;
|
||||
|
||||
class Curl4dPrec : public Solver
|
||||
{
|
||||
|
||||
private:
|
||||
HypreParMatrix *A;
|
||||
ParFiniteElementSpace *fespace;
|
||||
|
||||
HypreParMatrix *idMat;
|
||||
HypreParMatrix *H1VecLaplaceMat;
|
||||
HypreBoomerAMG *amgVecH1;
|
||||
|
||||
|
||||
HypreParMatrix *gradMat;
|
||||
HypreParMatrix *H1LaplaceMat;
|
||||
HypreBoomerAMG *amgH1;
|
||||
|
||||
HypreSmoother * smoother;
|
||||
CGSolver *pcgGrad;
|
||||
CGSolver *pcgH1Vec;
|
||||
|
||||
Vector *f;
|
||||
Vector *fGrad, *uGrad;
|
||||
Vector *fH1Vec, *uH1Vec;
|
||||
|
||||
bool exactSolves;
|
||||
|
||||
public:
|
||||
Curl4dPrec(HypreParMatrix *AUser, ParFiniteElementSpace *fespaceUser,
|
||||
const Array<int> &essBnd, int orderKernel=1, bool exactSolvesUser=false)
|
||||
{
|
||||
A = AUser;
|
||||
fespace = fespaceUser;
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
|
||||
exactSolves = exactSolvesUser;
|
||||
|
||||
int orderIm=1; //vecH1 --> H(curl)
|
||||
int orderKer=orderKernel; //grad V --> H(curl)
|
||||
|
||||
smoother = new HypreSmoother(*A, 16, 3);
|
||||
|
||||
// //for the pure dirichlet case
|
||||
// Array<int> essBnd(pmesh->bdr_attributes.Max()); essBnd = 1;
|
||||
|
||||
Array<int> HCurl_essDof(fespace->GetVSize()); HCurl_essDof = 0;
|
||||
fespace->GetEssentialVDofs(essBnd, HCurl_essDof);
|
||||
|
||||
//setup the H1 FESpace
|
||||
FiniteElementCollection* fecH1;
|
||||
if (orderKer==1) { fecH1 = new LinearFECollection; }
|
||||
else { fecH1 = new QuadraticFECollection; }
|
||||
|
||||
ParFiniteElementSpace *H1FESpace = new ParFiniteElementSpace(pmesh, fecH1);
|
||||
Array<int> H1_essDof(H1FESpace->GetVSize()); H1_essDof = 0;
|
||||
H1FESpace->GetEssentialVDofs(essBnd, H1_essDof);
|
||||
|
||||
//setup the discrete gradient
|
||||
ParDiscreteLinearOperator *disGrad = new ParDiscreteLinearOperator(H1FESpace,
|
||||
fespace);
|
||||
disGrad->AddDomainInterpolator(new GradientInterpolator);
|
||||
disGrad->Assemble();
|
||||
disGrad->Finalize();
|
||||
SparseMatrix* smat = &(disGrad->SpMat());
|
||||
smat->EliminateCols(H1_essDof);
|
||||
for (int dof=0; dof<HCurl_essDof.Size(); dof++) if (HCurl_essDof[dof]<0) { smat->EliminateRow(dof); }
|
||||
gradMat = disGrad->ParallelAssemble();
|
||||
delete disGrad;
|
||||
|
||||
//setup the H1 preconditioner
|
||||
ParBilinearForm* H1Varf = new ParBilinearForm(H1FESpace);
|
||||
H1Varf->AddDomainIntegrator(new DiffusionIntegrator);
|
||||
H1Varf->AddDomainIntegrator(new MassIntegrator);
|
||||
H1Varf->Assemble();
|
||||
H1Varf->Finalize();
|
||||
|
||||
SparseMatrix &matH1(H1Varf->SpMat());
|
||||
for (int dof=0; dof<H1_essDof.Size(); dof++) if (H1_essDof[dof]<0) { matH1.EliminateRowCol(dof); }
|
||||
H1LaplaceMat = H1Varf->ParallelAssemble();
|
||||
delete H1Varf;
|
||||
amgH1 = new HypreBoomerAMG(*H1LaplaceMat);
|
||||
|
||||
|
||||
//setup the H1 injection
|
||||
FiniteElementCollection* fecH1Vec;
|
||||
if (orderIm==1) { fecH1Vec = new LinearFECollection; }
|
||||
else { fecH1Vec = new QuadraticFECollection; }
|
||||
ParFiniteElementSpace *H1VecFESpace = new ParFiniteElementSpace(pmesh, fecH1Vec,
|
||||
dim, Ordering::byVDIM);
|
||||
Array<int> H1Vec_essDof(H1VecFESpace->GetVSize()); H1Vec_essDof = 0;
|
||||
H1VecFESpace->GetEssentialVDofs(essBnd, H1Vec_essDof);
|
||||
|
||||
//setup the discrete gradient
|
||||
ParDiscreteLinearOperator *disInterpol = new ParDiscreteLinearOperator(
|
||||
H1VecFESpace, fespace);
|
||||
disInterpol->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpol->Assemble();
|
||||
disInterpol->Finalize();
|
||||
SparseMatrix* smatID = &(disInterpol->SpMat());
|
||||
smatID->EliminateCols(H1Vec_essDof);
|
||||
for (int dof=0; dof<HCurl_essDof.Size(); dof++) if (HCurl_essDof[dof]<0) { smatID->EliminateRow(dof); }
|
||||
idMat = disInterpol->ParallelAssemble();
|
||||
delete disInterpol;
|
||||
|
||||
//setup the H1-vec preconditioner
|
||||
ParBilinearForm* H1VecVarf = new ParBilinearForm(H1VecFESpace);
|
||||
H1VecVarf->AddDomainIntegrator(new VectorDiffusionIntegrator);
|
||||
H1VecVarf->AddDomainIntegrator(new VectorMassIntegrator);
|
||||
H1VecVarf->Assemble();
|
||||
H1VecVarf->Finalize();
|
||||
|
||||
SparseMatrix &matH1Vec(H1VecVarf->SpMat());
|
||||
for (int dof=0; dof<H1Vec_essDof.Size(); dof++) if (H1Vec_essDof[dof]<0) { matH1Vec.EliminateRowCol(dof); }
|
||||
H1VecLaplaceMat = H1VecVarf->ParallelAssemble();
|
||||
delete H1VecVarf;
|
||||
amgVecH1 = new HypreBoomerAMG(*H1VecLaplaceMat);
|
||||
amgVecH1->SetSystemsOptions(dim);
|
||||
|
||||
|
||||
f = new Vector(fespace->GetTrueVSize());
|
||||
|
||||
fGrad = new Vector(H1FESpace->GetTrueVSize());
|
||||
uGrad = new Vector(H1FESpace->GetTrueVSize());
|
||||
|
||||
fH1Vec = new Vector(H1VecFESpace->GetTrueVSize());
|
||||
uH1Vec = new Vector(H1VecFESpace->GetTrueVSize());;
|
||||
|
||||
|
||||
amgH1->Mult(*fGrad, *uGrad);
|
||||
amgVecH1->Mult(*fH1Vec, *uH1Vec);
|
||||
|
||||
pcgGrad = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgGrad->SetOperator(*H1LaplaceMat);
|
||||
pcgGrad->SetPreconditioner(*amgH1);
|
||||
pcgGrad->SetRelTol(1e-16);
|
||||
pcgGrad->SetMaxIter(100000000);
|
||||
pcgGrad->SetPrintLevel(-2);
|
||||
|
||||
pcgH1Vec = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgH1Vec->SetOperator(*H1VecLaplaceMat);
|
||||
pcgH1Vec->SetPreconditioner(*amgVecH1);
|
||||
pcgH1Vec->SetRelTol(1e-16);
|
||||
pcgH1Vec->SetMaxIter(100000000);
|
||||
pcgH1Vec->SetPrintLevel(-2);
|
||||
|
||||
}
|
||||
|
||||
void setExactSolve(bool exSol)
|
||||
{
|
||||
exactSolves = exSol;
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
smoother->Mult(x,y);
|
||||
|
||||
idMat->MultTranspose(x,*fH1Vec);
|
||||
*uH1Vec = 0.0;
|
||||
if (exactSolves) { pcgH1Vec->Mult(*fH1Vec, *uH1Vec); }
|
||||
else { amgVecH1->Mult(*fH1Vec, *uH1Vec); }
|
||||
idMat->Mult(1.0, *uH1Vec, 1.0, y);
|
||||
|
||||
gradMat->MultTranspose(x,*fGrad);
|
||||
*uGrad = 0.0;
|
||||
if (exactSolves) { pcgGrad->Mult(*fGrad, *uGrad); }
|
||||
else { amgH1->Mult(*fGrad, *uGrad); }
|
||||
gradMat->Mult(1.0, *uGrad, 1.0, y);
|
||||
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {};
|
||||
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
@@ -336,13 +162,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec;
|
||||
if (dim==4)
|
||||
{
|
||||
if (order==1) { fec = new ND1_4DFECollection; }
|
||||
else { fec = new ND2_4DFECollection; }
|
||||
}
|
||||
else { fec = new ND_FECollection(order, dim); }
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_BigInt size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
@@ -428,29 +248,15 @@ int main(int argc, char *argv[])
|
||||
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
Solver *prec;
|
||||
HypreAMS ams(*A.As<HypreParMatrix>(), prec_fespace);
|
||||
if (dim <= 3)
|
||||
{
|
||||
prec = new HypreAMS(*A.As<HypreParMatrix>(), prec_fespace);
|
||||
}
|
||||
else if (dim == 4)
|
||||
{
|
||||
prec = new Curl4dPrec(A.As<HypreParMatrix>(), fespace, ess_bdr, order, false);
|
||||
}
|
||||
IterativeSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(*A.As<HypreParMatrix>());
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
delete pcg;
|
||||
delete prec;
|
||||
HyprePCG pcg(*A.As<HypreParMatrix>());
|
||||
pcg.SetTol(1e-12);
|
||||
pcg.SetMaxIter(500);
|
||||
pcg.SetPrintLevel(2);
|
||||
pcg.SetPreconditioner(ams);
|
||||
pcg.Mult(B, X);
|
||||
}
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
@@ -506,14 +312,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim==4)
|
||||
{
|
||||
E(0) = sin(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(1) = -cos(M_PI*x(0))*sin(M_PI*x(1))*cos(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(2) = cos(M_PI*x(0))*cos(M_PI*x(1))*sin(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(3) = -cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*sin(M_PI*x(3));
|
||||
}
|
||||
else if (dim == 3)
|
||||
if (dim == 3)
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
@@ -529,19 +328,7 @@ void E_exact(const Vector &x, Vector &E)
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
//f_exact = E + DivSkew P( curl E ), where P is the 4d permutation operator
|
||||
if (dim==4)
|
||||
{
|
||||
f(0) = (1.0+4.0*M_PI*M_PI)*sin(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(1) = -(1.0+4.0*M_PI*M_PI)*cos(M_PI*x(0))*sin(M_PI*x(1))*cos(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(2) = (1.0+4.0*M_PI*M_PI)*cos(M_PI*x(0))*cos(M_PI*x(1))*sin(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(3) = -(1.0+4.0*M_PI*M_PI)*cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*sin(
|
||||
M_PI*x(3));
|
||||
}
|
||||
else if (dim == 3)
|
||||
if (dim == 3)
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
|
||||
|
||||
@@ -1,650 +0,0 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic diffusion
|
||||
// problem corresponding to the second order definite Maxwell
|
||||
// equation curl curl E + E = f with boundary condition
|
||||
// E x n = <given tangential field>. Here, we use a given exact
|
||||
// solution E and compute the corresponding r.h.s. f.
|
||||
// We discretize with Nedelec finite elements in 2D or 3D.
|
||||
//
|
||||
// The example demonstrates the use of H(curl) finite element
|
||||
// spaces with the curl-curl and the (vector finite element) mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Static condensation is
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-2 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "./spe10_coeff.cpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
int* LoadIterations(int NRows, int NCol)
|
||||
{
|
||||
ifstream in("iter_curl.txt");
|
||||
|
||||
//initialize
|
||||
int *iters = new int[NCol*NRows];
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
iters[row*NCol+col] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
return iters;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
if (in.eof())
|
||||
{
|
||||
in.close();
|
||||
return iters;
|
||||
}
|
||||
in >> iters[row*NCol+col];
|
||||
}
|
||||
|
||||
|
||||
in.close();
|
||||
|
||||
return iters;
|
||||
}
|
||||
|
||||
void putIterationsInArray(int iter, int row, int col, int NCol, int* iters)
|
||||
{
|
||||
iters[row*NCol+col] = iter;
|
||||
}
|
||||
|
||||
void WriteIterations(int *iters, int NRows, int NCol)
|
||||
{
|
||||
ofstream out;
|
||||
out.open("iter_curl.txt",fstream::out);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
delete[] iters;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
out << iters[row*NCol+col] << "\t";
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
out.close();
|
||||
|
||||
delete[] iters;
|
||||
}
|
||||
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
void E_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa = 1.0;
|
||||
int dim;
|
||||
|
||||
double osziCoeff(const Vector &x)
|
||||
{
|
||||
return 1.0001 + sin(100*x(0))*sin(200*x(1))*sin(300*x(2))*sin(400*x(3));
|
||||
}
|
||||
|
||||
class Curl4dPrec : public Solver
|
||||
{
|
||||
|
||||
private:
|
||||
HypreParMatrix *A;
|
||||
ParFiniteElementSpace *fespace;
|
||||
Coefficient *alpha_, *beta_, *neg_beta_;
|
||||
|
||||
HypreParMatrix *idMat;
|
||||
HypreParMatrix *H1VecLaplaceMat;
|
||||
HypreBoomerAMG *amgVecH1;
|
||||
|
||||
|
||||
HypreParMatrix *gradMat;
|
||||
HypreParMatrix *H1LaplaceMat;
|
||||
HypreBoomerAMG *amgH1;
|
||||
|
||||
HypreSmoother * smoother;
|
||||
CGSolver *pcgGrad;
|
||||
CGSolver *pcgH1Vec;
|
||||
|
||||
Vector *f;
|
||||
Vector *fGrad, *uGrad;
|
||||
Vector *fH1Vec, *uH1Vec;
|
||||
|
||||
bool exactSolves;
|
||||
|
||||
public:
|
||||
~Curl4dPrec()
|
||||
{
|
||||
delete pcgH1Vec;
|
||||
delete pcgGrad;
|
||||
|
||||
delete f, fGrad, uGrad, fH1Vec, uH1Vec;
|
||||
|
||||
delete smoother;
|
||||
|
||||
delete amgVecH1, H1VecLaplaceMat;
|
||||
delete idMat;
|
||||
delete amgH1, H1LaplaceMat;
|
||||
delete gradMat;
|
||||
}
|
||||
|
||||
Curl4dPrec(HypreParMatrix *AUser, ParFiniteElementSpace *fespaceUser,
|
||||
Coefficient *alpha, Coefficient *beta, Coefficient *neg_beta,
|
||||
const Array<int> &essBnd, int orderKernel=1, bool exactSolvesUser=false)
|
||||
{
|
||||
A = AUser;
|
||||
fespace = fespaceUser;
|
||||
alpha_ = alpha;
|
||||
beta_ = beta;
|
||||
neg_beta_=neg_beta;
|
||||
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
|
||||
exactSolves = exactSolvesUser;
|
||||
|
||||
int orderIm=1; //vecH1 --> H(curl)
|
||||
int orderKer=orderKernel; //grad V --> H(curl)
|
||||
|
||||
smoother = new HypreSmoother(*A, 16, 3);
|
||||
|
||||
// //for the pure dirichlet case
|
||||
// Array<int> essBnd(pmesh->bdr_attributes.Max()); essBnd = 1;
|
||||
|
||||
Array<int> HCurl_essDof(fespace->GetVSize()); HCurl_essDof = 0;
|
||||
fespace->GetEssentialVDofs(essBnd, HCurl_essDof);
|
||||
|
||||
//setup the H1 FESpace
|
||||
FiniteElementCollection* fecH1;
|
||||
if (orderKer==1) { fecH1 = new LinearFECollection; }
|
||||
else { fecH1 = new QuadraticFECollection; }
|
||||
|
||||
ParFiniteElementSpace *H1FESpace = new ParFiniteElementSpace(pmesh, fecH1);
|
||||
Array<int> H1_essDof(H1FESpace->GetVSize()); H1_essDof = 0;
|
||||
H1FESpace->GetEssentialVDofs(essBnd, H1_essDof);
|
||||
|
||||
//setup the discrete gradient
|
||||
ParDiscreteLinearOperator *disGrad = new ParDiscreteLinearOperator(H1FESpace,
|
||||
fespace);
|
||||
disGrad->AddDomainInterpolator(new GradientInterpolator);
|
||||
disGrad->Assemble();
|
||||
disGrad->Finalize();
|
||||
SparseMatrix* smat = &(disGrad->SpMat());
|
||||
smat->EliminateCols(H1_essDof);
|
||||
for (int dof=0; dof<HCurl_essDof.Size(); dof++) if (HCurl_essDof[dof]<0) { smat->EliminateRow(dof); }
|
||||
gradMat = disGrad->ParallelAssemble();
|
||||
delete disGrad;
|
||||
|
||||
//setup the H1 preconditioner
|
||||
ParBilinearForm* H1Varf = new ParBilinearForm(H1FESpace);
|
||||
H1Varf->AddDomainIntegrator(new DiffusionIntegrator(*beta_));
|
||||
// H1Varf->AddDomainIntegrator(new MassIntegrator);
|
||||
H1Varf->Assemble();
|
||||
H1Varf->Finalize();
|
||||
|
||||
SparseMatrix &matH1(H1Varf->SpMat());
|
||||
for (int dof=0; dof<H1_essDof.Size(); dof++) if (H1_essDof[dof]<0) { matH1.EliminateRowCol(dof); }
|
||||
H1LaplaceMat = H1Varf->ParallelAssemble();
|
||||
delete H1Varf;
|
||||
amgH1 = new HypreBoomerAMG(*H1LaplaceMat);
|
||||
|
||||
|
||||
//setup the H1 injection
|
||||
FiniteElementCollection* fecH1Vec;
|
||||
if (orderIm==1) { fecH1Vec = new LinearFECollection; }
|
||||
else { fecH1Vec = new QuadraticFECollection; }
|
||||
ParFiniteElementSpace *H1VecFESpace = new ParFiniteElementSpace(pmesh, fecH1Vec,
|
||||
dim, Ordering::byVDIM);
|
||||
Array<int> H1Vec_essDof(H1VecFESpace->GetVSize()); H1Vec_essDof = 0;
|
||||
H1VecFESpace->GetEssentialVDofs(essBnd, H1Vec_essDof);
|
||||
|
||||
//setup the discrete gradient
|
||||
ParDiscreteLinearOperator *disInterpol = new ParDiscreteLinearOperator(
|
||||
H1VecFESpace, fespace);
|
||||
disInterpol->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpol->Assemble();
|
||||
disInterpol->Finalize();
|
||||
SparseMatrix* smatID = &(disInterpol->SpMat());
|
||||
smatID->EliminateCols(H1Vec_essDof);
|
||||
for (int dof=0; dof<HCurl_essDof.Size(); dof++) if (HCurl_essDof[dof]<0) { smatID->EliminateRow(dof); }
|
||||
idMat = disInterpol->ParallelAssemble();
|
||||
delete disInterpol;
|
||||
|
||||
//setup the H1-vec preconditioner
|
||||
ParBilinearForm* H1VecVarf = new ParBilinearForm(H1VecFESpace);
|
||||
H1VecVarf->AddDomainIntegrator(new VectorDiffusionIntegrator(*alpha_));
|
||||
H1VecVarf->AddDomainIntegrator(new VectorMassIntegrator(*neg_beta_));
|
||||
H1VecVarf->Assemble();
|
||||
H1VecVarf->Finalize();
|
||||
|
||||
SparseMatrix &matH1Vec(H1VecVarf->SpMat());
|
||||
for (int dof=0; dof<H1Vec_essDof.Size(); dof++) if (H1Vec_essDof[dof]<0) { matH1Vec.EliminateRowCol(dof); }
|
||||
H1VecLaplaceMat = H1VecVarf->ParallelAssemble();
|
||||
delete H1VecVarf;
|
||||
amgVecH1 = new HypreBoomerAMG(*H1VecLaplaceMat);
|
||||
amgVecH1->SetSystemsOptions(dim);
|
||||
|
||||
|
||||
f = new Vector(fespace->GetTrueVSize());
|
||||
|
||||
fGrad = new Vector(H1FESpace->GetTrueVSize());
|
||||
uGrad = new Vector(H1FESpace->GetTrueVSize());
|
||||
|
||||
fH1Vec = new Vector(H1VecFESpace->GetTrueVSize());
|
||||
uH1Vec = new Vector(H1VecFESpace->GetTrueVSize());
|
||||
|
||||
|
||||
amgH1->Mult(*fGrad, *uGrad);
|
||||
amgVecH1->Mult(*fH1Vec, *uH1Vec);
|
||||
|
||||
pcgGrad = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgGrad->SetOperator(*H1LaplaceMat);
|
||||
pcgGrad->SetPreconditioner(*amgH1);
|
||||
pcgGrad->SetRelTol(1e-16);
|
||||
pcgGrad->SetMaxIter(100000000);
|
||||
pcgGrad->SetPrintLevel(-2);
|
||||
|
||||
pcgH1Vec = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgH1Vec->SetOperator(*H1VecLaplaceMat);
|
||||
pcgH1Vec->SetPreconditioner(*amgVecH1);
|
||||
pcgH1Vec->SetRelTol(1e-16);
|
||||
pcgH1Vec->SetMaxIter(100000000);
|
||||
pcgH1Vec->SetPrintLevel(-2);
|
||||
|
||||
delete H1FESpace; delete fecH1;
|
||||
delete H1VecFESpace; delete fecH1Vec;
|
||||
|
||||
}
|
||||
|
||||
void setExactSolve(bool exSol)
|
||||
{
|
||||
exactSolves = exSol;
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
smoother->Mult(x,y);
|
||||
|
||||
idMat->MultTranspose(x,*fH1Vec);
|
||||
*uH1Vec = 0.0;
|
||||
if (exactSolves) { pcgH1Vec->Mult(*fH1Vec, *uH1Vec); }
|
||||
else { amgVecH1->Mult(*fH1Vec, *uH1Vec); }
|
||||
idMat->Mult(1.0, *uH1Vec, 1.0, y);
|
||||
|
||||
gradMat->MultTranspose(x,*fGrad);
|
||||
*uGrad = 0.0;
|
||||
if (exactSolves) { pcgGrad->Mult(*fGrad, *uGrad); }
|
||||
else { amgH1->Mult(*fGrad, *uGrad); }
|
||||
gradMat->Mult(1.0, *uGrad, 1.0, y);
|
||||
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {};
|
||||
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
bool verbose = (myid==0);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/cube4d_96.MFEM";
|
||||
int order = 1;
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
int sequ_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
double tol = 1e-6;
|
||||
double coeffWeight = 1.0;
|
||||
bool exactH1Solver = false;
|
||||
bool spe10Coeff = false;
|
||||
bool standardCG = true;
|
||||
|
||||
int NExpo = 8;
|
||||
int weightStart = -NExpo;
|
||||
int weightEnd = NExpo;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&sequ_ref_levels, "-sr", "--seqrefinement",
|
||||
"Number of sequential refinement steps.");
|
||||
args.AddOption(&par_ref_levels, "-pr", "--parrefinement",
|
||||
"Number of parallel refinement steps.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Polynomial order of the finite element space.");
|
||||
args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
|
||||
"Impose or not essential boundary conditions.");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"A parameter.");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
args.AddOption(&coeffWeight, "-c", "--coeffMass",
|
||||
"the weight for the mass term.");
|
||||
args.AddOption(&exactH1Solver, "-exH1Sol", "--exactH1Solver", "-H1prec",
|
||||
"--H1preconditioner",
|
||||
"Use exact H1 solvers for the preconditioner.");
|
||||
args.AddOption(&spe10Coeff, "-spe10", "--useSPE10Coeff", "-constCoeff",
|
||||
"--constCoeff",
|
||||
"Switch between the coefficients for the mass bilinear form.");
|
||||
args.AddOption(&standardCG, "-sCG", "--stdCG", "-rCG", "--resCG",
|
||||
"Switch between standard PCG or recompute residuals in every step and use the residuals itself for the stopping criteria.");
|
||||
args.AddOption(&weightStart, "-ws", "--weightStart",
|
||||
"the exponent for the starting weight (for the mass term).");
|
||||
args.AddOption(&weightEnd, "-we", "--weightEnd",
|
||||
"the exponent for the weight at the end (for the mass term).");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (verbose) { args.PrintOptions(cout); }
|
||||
|
||||
kappa = freq * M_PI;
|
||||
|
||||
Mesh *mesh;
|
||||
ifstream imesh(mesh_file);
|
||||
if (!imesh)
|
||||
{
|
||||
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
mesh = new Mesh(imesh, 1, 1);
|
||||
imesh.close();
|
||||
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
|
||||
if (dim !=4 || sdim != 4)
|
||||
{
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i=0; i<sequ_ref_levels; i++) { mesh->UniformRefinement(); }
|
||||
if (verbose) { mesh->PrintCharacteristics(); }
|
||||
|
||||
if (verbose) { cout << "now we partition the mesh..." << endl << endl; }
|
||||
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
for (int i=0; i<par_ref_levels; i++) { pmesh->UniformRefinement(); }
|
||||
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
pmesh->PrintInfo(std::cout);
|
||||
if (verbose) { cout << endl; }
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec;
|
||||
if (dim==4)
|
||||
{
|
||||
if (order==1) { fec = new ND1_4DFECollection; }
|
||||
else { fec = new ND2_4DFECollection; }
|
||||
}
|
||||
else { fec = new ND_FECollection(order, dim); }
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = set_bc ? 1 : 0;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
|
||||
for (int expo=weightStart; expo<=weightEnd; expo++)
|
||||
{
|
||||
double weight = pow(10.0,expo);
|
||||
kappa = weight;
|
||||
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
x.ProjectCoefficient(E);
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
// std::string permFile = "spe_perm.dat";
|
||||
// InversePermeabilityFunction::ReadPermeabilityFile(permFile, MPI_COMM_WORLD);
|
||||
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta;
|
||||
// if(spe10Coeff) beta = new FunctionCoefficient(InversePermeabilityFunction::Norm2Permeability);
|
||||
// else
|
||||
beta = new ConstantCoefficient(weight);
|
||||
Coefficient *neg_beta = new ConstantCoefficient(-weight);
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
|
||||
// preconditioner from hypre.
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
Solver *prec;
|
||||
if (dim<=3) { prec = new HypreAMS(A, prec_fespace); }
|
||||
else if (dim==4) { prec = new Curl4dPrec(&A, fespace, alpha, beta, neg_beta, ess_bdr, order, false); }
|
||||
IterativeSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(tol);
|
||||
pcg->SetMaxIter(5000);
|
||||
pcg->SetPrintLevel(1);
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
int iter = pcg->GetNumIterations();
|
||||
if (myid==0)
|
||||
{
|
||||
cout << "Weigth: " << weight << " " << iter << endl;
|
||||
|
||||
int *iters = LoadIterations(10, 2*NExpo+1);
|
||||
putIterationsInArray(iter, sequ_ref_levels+par_ref_levels, expo+NExpo,
|
||||
2*NExpo+1, iters);
|
||||
WriteIterations(iters, 10, 2*NExpo+1);
|
||||
}
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(E);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
// {
|
||||
// ostringstream mesh_name, sol_name;
|
||||
// mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
// sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
//
|
||||
// ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
// mesh_ofs.precision(8);
|
||||
// pmesh->Print(mesh_ofs);
|
||||
//
|
||||
// ofstream sol_ofs(sol_name.str().c_str());
|
||||
// sol_ofs.precision(8);
|
||||
// x.Save(sol_ofs);
|
||||
// }
|
||||
|
||||
// // 16. Send the solution by socket to a GLVis server.
|
||||
// if (visualization)
|
||||
// {
|
||||
// char vishost[] = "localhost";
|
||||
// int visport = 19916;
|
||||
// socketstream sol_sock(vishost, visport);
|
||||
// sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
// sol_sock.precision(8);
|
||||
// sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
// }
|
||||
|
||||
delete pcg;
|
||||
delete prec;
|
||||
delete a;
|
||||
delete alpha;
|
||||
delete beta;
|
||||
delete b;
|
||||
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim==4)
|
||||
{
|
||||
E(0) = sin(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(1) = -cos(M_PI*x(0))*sin(M_PI*x(1))*cos(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(2) = cos(M_PI*x(0))*cos(M_PI*x(1))*sin(M_PI*x(2))*cos(M_PI*x(3));
|
||||
E(3) = -cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*sin(M_PI*x(3));
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
E(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { E(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
//f_exact = E + DivSkew P( curl E ), where P is the 4d permutation operator
|
||||
if (dim==4)
|
||||
{
|
||||
f(0) = (kappa+4.0*M_PI*M_PI)*sin(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(1) = -(kappa+4.0*M_PI*M_PI)*cos(M_PI*x(0))*sin(M_PI*x(1))*cos(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(2) = (kappa+4.0*M_PI*M_PI)*cos(M_PI*x(0))*cos(M_PI*x(1))*sin(M_PI*x(2))*cos(
|
||||
M_PI*x(3));
|
||||
f(3) = -(kappa+4.0*M_PI*M_PI)*cos(M_PI*x(0))*cos(M_PI*x(1))*cos(M_PI*x(2))*sin(
|
||||
M_PI*x(3));
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
|
||||
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,8 @@ public:
|
||||
ZCoefficient(int vdim, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
using VectorCoefficient::Eval;
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
|
||||
@@ -67,6 +67,8 @@ public:
|
||||
ZCoefficient(int vdim, ParGridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
using VectorCoefficient::Eval;
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
|
||||
@@ -1,782 +0,0 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic diffusion
|
||||
// problem corresponding to the second order definite Maxwell
|
||||
// equation curl curl E + E = f with boundary condition
|
||||
// E x n = <given tangential field>. Here, we use a given exact
|
||||
// solution E and compute the corresponding r.h.s. f.
|
||||
// We discretize with Nedelec finite elements in 2D or 3D.
|
||||
//
|
||||
// The example demonstrates the use of H(curl) finite element
|
||||
// spaces with the curl-curl and the (vector finite element) mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Static condensation is
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-2 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "./spe10_coeff.cpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
int* LoadIterations(int NRows, int NCol)
|
||||
{
|
||||
ifstream in("iter_DivSkew.txt");
|
||||
|
||||
//initialize
|
||||
int *iters = new int[NCol*NRows];
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
iters[row*NCol+col] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
return iters;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
if (in.eof())
|
||||
{
|
||||
in.close();
|
||||
return iters;
|
||||
}
|
||||
in >> iters[row*NCol+col];
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
return iters;
|
||||
}
|
||||
|
||||
void putIterationsInArray(int iter, int row, int col, int NCol, int* iters)
|
||||
{
|
||||
iters[row*NCol+col] = iter;
|
||||
}
|
||||
|
||||
void WriteIterations(int *iters, int NRows, int NCol)
|
||||
{
|
||||
ofstream out;
|
||||
out.open("iter_DivSkew.txt",fstream::out);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
delete[] iters;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
out << iters[row*NCol+col] << "\t";
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
out.close();
|
||||
|
||||
delete[] iters;
|
||||
}
|
||||
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
void E_exact_vec(const Vector &x, Vector &E);
|
||||
void E_exact(const Vector &, DenseMatrix &);
|
||||
void f_exact(const Vector &, DenseMatrix &);
|
||||
|
||||
|
||||
class DivSkew4dPrec : public Solver
|
||||
{
|
||||
|
||||
private:
|
||||
HypreParMatrix *A;
|
||||
ParFiniteElementSpace *fespace;
|
||||
Coefficient *alpha_, *beta_;
|
||||
|
||||
//kernel operators
|
||||
HypreParMatrix *P_d_HCurl_HDivSkew;
|
||||
|
||||
|
||||
HypreParMatrix *P_H1_HCurl;
|
||||
HypreParMatrix *H1_KernelMat;
|
||||
HypreBoomerAMG *amgH1_Kernel;
|
||||
|
||||
//"image" operators
|
||||
HypreParMatrix *P_H1_HDivSkew;
|
||||
HypreParMatrix *H1_ImageMat;
|
||||
HypreBoomerAMG *amgH1_Image;
|
||||
|
||||
|
||||
HypreParMatrix *HCurlMat;
|
||||
HypreSmoother * smootherDivSkew;
|
||||
HypreSmoother * smootherCurl;
|
||||
|
||||
CGSolver *pcgKernel;
|
||||
CGSolver *pcgImage;
|
||||
|
||||
Vector *f;
|
||||
Vector *fKernel, *uKernel;
|
||||
Vector *fImage, *uImage;
|
||||
Vector *fCurl, *uCurl;
|
||||
|
||||
bool exactSolves;
|
||||
|
||||
FiniteElementCollection* fecHCurlKernel;
|
||||
ParFiniteElementSpace *HCurlKernelFESpace;
|
||||
|
||||
|
||||
public:
|
||||
~DivSkew4dPrec()
|
||||
{
|
||||
delete pcgImage, pcgKernel;
|
||||
|
||||
delete f, fKernel, uKernel, fImage, uImage, fCurl, uCurl;
|
||||
|
||||
delete smootherCurl, HCurlMat;
|
||||
|
||||
delete P_d_HCurl_HDivSkew, P_H1_HDivSkew, P_H1_HCurl;
|
||||
|
||||
delete amgH1_Image, H1_ImageMat;
|
||||
delete amgH1_Kernel, H1_KernelMat;
|
||||
|
||||
delete smootherDivSkew;
|
||||
|
||||
delete HCurlKernelFESpace, fecHCurlKernel;
|
||||
}
|
||||
DivSkew4dPrec(HypreParMatrix *AUser, ParFiniteElementSpace *fespaceUser,
|
||||
Coefficient *alpha, Coefficient *beta,
|
||||
const Array<int> &essBnd, int orderKernel=1, bool exactSolvesUser=false)
|
||||
{
|
||||
A = AUser;
|
||||
fespace = fespaceUser;
|
||||
alpha_ = alpha;
|
||||
beta_ = beta;
|
||||
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
|
||||
exactSolves = exactSolvesUser;
|
||||
|
||||
int orderIm=1; //H1 --> H(divSkew)
|
||||
int orderKer=orderKernel; //curl V --> H(divSkew)
|
||||
|
||||
smootherDivSkew = new HypreSmoother(*A, 16, 3);
|
||||
|
||||
Array<int> HDivSkew_essDof(fespace->GetVSize()); HDivSkew_essDof = 0;
|
||||
fespace->GetEssentialVDofs(essBnd, HDivSkew_essDof);
|
||||
|
||||
//setup the H1 FESpace for the kernel
|
||||
FiniteElementCollection* fecH1Kernel = new H1_FECollection(orderKer, 4);
|
||||
|
||||
ParFiniteElementSpace *H1KernelFESpace = new ParFiniteElementSpace(pmesh,
|
||||
fecH1Kernel, dim, Ordering::byVDIM);
|
||||
Array<int> H1Kernel_essDof(H1KernelFESpace->GetVSize()); H1Kernel_essDof = 0;
|
||||
H1KernelFESpace->GetEssentialVDofs(essBnd, H1Kernel_essDof);
|
||||
|
||||
|
||||
//setup the H(curl) FESpace for the kernel
|
||||
if (orderKer==1) { fecHCurlKernel = new ND1_4DFECollection; }
|
||||
else { fecHCurlKernel = new ND2_4DFECollection; }
|
||||
|
||||
HCurlKernelFESpace = new ParFiniteElementSpace(pmesh,
|
||||
fecHCurlKernel);
|
||||
Array<int> HCurlKernel_essDof(HCurlKernelFESpace->GetVSize());
|
||||
HCurlKernel_essDof = 0;
|
||||
HCurlKernelFESpace->GetEssentialVDofs(essBnd, HCurlKernel_essDof);
|
||||
|
||||
|
||||
//setup the FESpace for the H1 injection
|
||||
FiniteElementCollection* fecH1Vec;
|
||||
if (orderIm==1) { fecH1Vec = new LinearFECollection; }
|
||||
else { fecH1Vec = new QuadraticFECollection; }
|
||||
ParFiniteElementSpace *H1_ImageFESpace = new ParFiniteElementSpace(pmesh,
|
||||
fecH1Vec, 6, Ordering::byVDIM);
|
||||
Array<int> H1Image_essDof(H1_ImageFESpace->GetVSize()); H1Image_essDof = 0;
|
||||
H1_ImageFESpace->GetEssentialVDofs(essBnd, H1Image_essDof);
|
||||
|
||||
|
||||
|
||||
//setup the H1 preconditioner for the kernel
|
||||
ParBilinearForm* H1Varf = new ParBilinearForm(H1KernelFESpace);
|
||||
H1Varf->AddDomainIntegrator(new VectorDiffusionIntegrator(*beta_));
|
||||
// H1Varf->AddDomainIntegrator(new VectorMassIntegrator);
|
||||
H1Varf->Assemble();
|
||||
H1Varf->Finalize();
|
||||
SparseMatrix &matH1(H1Varf->SpMat());
|
||||
for (int dof = 0; dof < H1Kernel_essDof.Size(); dof++)
|
||||
if (H1Kernel_essDof[dof] < 0)
|
||||
{
|
||||
matH1.EliminateRowCol(dof);
|
||||
}
|
||||
H1_KernelMat = H1Varf->ParallelAssemble();
|
||||
delete H1Varf;
|
||||
amgH1_Kernel = new HypreBoomerAMG(*H1_KernelMat);
|
||||
amgH1_Kernel->SetSystemsOptions(dim);
|
||||
amgH1_Kernel->SetPrintLevel(0);
|
||||
|
||||
//setup the H1 preconditioner for the image
|
||||
ParBilinearForm* H1VecVarf = new ParBilinearForm(H1_ImageFESpace);
|
||||
VectorDiffusionIntegrator *alpha_integ = new VectorDiffusionIntegrator(*alpha_);
|
||||
alpha_integ->SetVDim(6);
|
||||
H1VecVarf->AddDomainIntegrator(alpha_integ);
|
||||
VectorMassIntegrator *beta_integ = new VectorMassIntegrator(*beta);
|
||||
beta_integ->SetVDim(6);
|
||||
H1VecVarf->AddDomainIntegrator(beta_integ);
|
||||
H1VecVarf->Assemble();
|
||||
H1VecVarf->Finalize();
|
||||
SparseMatrix &matH1Vec(H1VecVarf->SpMat());
|
||||
for (int dof=0; dof<H1Image_essDof.Size(); dof++) if (H1Image_essDof[dof]<0) { matH1Vec.EliminateRowCol(dof); }
|
||||
H1_ImageMat = H1VecVarf->ParallelAssemble();
|
||||
delete H1VecVarf;
|
||||
amgH1_Image = new HypreBoomerAMG(*H1_ImageMat);
|
||||
amgH1_Image->SetSystemsOptions(6);
|
||||
amgH1_Image->SetPrintLevel(0);
|
||||
|
||||
|
||||
//setup the injection of H1 into H(curl)
|
||||
ParDiscreteLinearOperator *disInterpol = new ParDiscreteLinearOperator(
|
||||
H1KernelFESpace, HCurlKernelFESpace);
|
||||
disInterpol->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpol->Assemble();
|
||||
disInterpol->Finalize();
|
||||
SparseMatrix* smatID = &(disInterpol->SpMat());
|
||||
smatID->EliminateCols(H1Kernel_essDof);
|
||||
for (int dof=0; dof<HCurlKernel_essDof.Size();
|
||||
dof++) if (HCurlKernel_essDof[dof]<0) { smatID->EliminateRow(dof); }
|
||||
P_H1_HCurl = disInterpol->ParallelAssemble();
|
||||
delete disInterpol;
|
||||
|
||||
//setup the injection of H1 into H(DivSkew)
|
||||
ParDiscreteLinearOperator *disInterpolIm = new ParDiscreteLinearOperator(
|
||||
H1_ImageFESpace, fespace);
|
||||
disInterpolIm->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpolIm->Assemble();
|
||||
disInterpolIm->Finalize();
|
||||
SparseMatrix* smatIDIm = &(disInterpolIm->SpMat());
|
||||
smatIDIm->EliminateCols(H1Image_essDof);
|
||||
for (int dof=0; dof<HDivSkew_essDof.Size(); dof++) if (HDivSkew_essDof[dof]<0) { smatIDIm->EliminateRow(dof); }
|
||||
P_H1_HDivSkew = disInterpolIm->ParallelAssemble();
|
||||
delete disInterpolIm;
|
||||
|
||||
|
||||
//setup the injection of the curl(H(curl)) into H(DivSkew)
|
||||
ParDiscreteLinearOperator *disCurl = new ParDiscreteLinearOperator(
|
||||
HCurlKernelFESpace, fespace);
|
||||
disCurl->AddDomainInterpolator(new CurlInterpolator);
|
||||
disCurl->Assemble();
|
||||
disCurl->Finalize();
|
||||
SparseMatrix* smatCurl = &(disCurl->SpMat());
|
||||
smatCurl->EliminateCols(HCurlKernel_essDof);
|
||||
for (int dof=0; dof<HDivSkew_essDof.Size(); dof++) if (HDivSkew_essDof[dof]<0) { smatCurl->EliminateRow(dof); }
|
||||
P_d_HCurl_HDivSkew = disCurl->ParallelAssemble();
|
||||
delete disCurl;
|
||||
|
||||
//setup the smoother for H(curl)
|
||||
// Coefficient *massC = new ConstantCoefficient(1.0);
|
||||
// Coefficient *CurlCurlC = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a_HCurl = new ParBilinearForm(HCurlKernelFESpace);
|
||||
a_HCurl->AddDomainIntegrator(new CurlCurlIntegrator(*beta_));
|
||||
// a_HCurl->AddDomainIntegrator(new CurlCurlIntegrator(*CurlCurlC));
|
||||
// a_HCurl->AddDomainIntegrator(new VectorFEMassIntegrator(*massC));
|
||||
a_HCurl->Assemble();
|
||||
a_HCurl->Finalize();
|
||||
SparseMatrix &matHCurl(a_HCurl->SpMat());
|
||||
for (int dof=0; dof<HCurlKernel_essDof.Size();
|
||||
dof++) if (HCurlKernel_essDof[dof]<0) { matHCurl.EliminateRowCol(dof); }
|
||||
HCurlMat = a_HCurl->ParallelAssemble();
|
||||
delete a_HCurl;
|
||||
smootherCurl = new HypreSmoother(*HCurlMat, 16, 3);
|
||||
|
||||
f = new Vector(fespace->GetTrueVSize());
|
||||
|
||||
fKernel = new Vector(H1KernelFESpace->GetTrueVSize());
|
||||
uKernel = new Vector(H1KernelFESpace->GetTrueVSize());
|
||||
|
||||
fImage = new Vector(H1_ImageFESpace->GetTrueVSize());
|
||||
uImage = new Vector(H1_ImageFESpace->GetTrueVSize());
|
||||
|
||||
fCurl = new Vector(HCurlKernelFESpace->GetTrueVSize());
|
||||
uCurl = new Vector(HCurlKernelFESpace->GetTrueVSize());
|
||||
|
||||
|
||||
amgH1_Kernel->Mult(*fKernel, *uKernel);
|
||||
amgH1_Image->Mult(*fImage, *uImage);
|
||||
|
||||
pcgKernel = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgKernel->SetOperator(*H1_KernelMat);
|
||||
pcgKernel->SetPreconditioner(*amgH1_Kernel);
|
||||
pcgKernel->SetRelTol(1e-16);
|
||||
pcgKernel->SetMaxIter(100000000);
|
||||
pcgKernel->SetPrintLevel(-2);
|
||||
|
||||
pcgImage = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgImage->SetOperator(*H1_ImageMat);
|
||||
pcgImage->SetPreconditioner(*amgH1_Image);
|
||||
pcgImage->SetRelTol(1e-16);
|
||||
pcgImage->SetMaxIter(100000000);
|
||||
pcgImage->SetPrintLevel(-2);
|
||||
|
||||
delete H1KernelFESpace, fecH1Kernel;
|
||||
delete H1_ImageFESpace, fecH1Vec;
|
||||
}
|
||||
|
||||
void setExactSolve(bool exSol)
|
||||
{
|
||||
exactSolves = exSol;
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
smootherDivSkew->Mult(x,y);
|
||||
|
||||
P_H1_HDivSkew->MultTranspose(x,*fImage);
|
||||
*uImage = 0.0;
|
||||
if (exactSolves) { pcgImage->Mult(*fImage, *uImage); }
|
||||
else { amgH1_Image->Mult(*fImage, *uImage); }
|
||||
P_H1_HDivSkew->Mult(1.0, *uImage, 1.0, y);
|
||||
|
||||
*uCurl = 0.0;
|
||||
P_d_HCurl_HDivSkew->MultTranspose(x,*fCurl);
|
||||
|
||||
smootherCurl->Mult(*fCurl, *uCurl);
|
||||
|
||||
P_H1_HCurl->MultTranspose(*fCurl,*fKernel);
|
||||
*uKernel = 0.0;
|
||||
if (exactSolves) { pcgKernel->Mult(*fKernel, *uKernel); }
|
||||
else { amgH1_Kernel->Mult(*fKernel, *uKernel); }
|
||||
P_H1_HCurl->Mult(1.0, *uKernel, 1.0, *uCurl);
|
||||
|
||||
P_d_HCurl_HDivSkew->Mult(1.0, *uCurl, 1.0, y);
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
bool verbose = (myid==0);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/cube4d_96.MFEM";
|
||||
int order = 1;
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
int sequ_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
double tol = 1e-6;
|
||||
double coeffWeight = 1.0;
|
||||
bool exactH1Solver = false;
|
||||
bool spe10Coeff = false;
|
||||
bool standardCG = true;
|
||||
|
||||
int NExpo = 8;
|
||||
int weightStart = -NExpo;
|
||||
int weightEnd = NExpo;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&sequ_ref_levels, "-sr", "--seqrefinement",
|
||||
"Number of sequential refinement steps.");
|
||||
args.AddOption(&par_ref_levels, "-pr", "--parrefinement",
|
||||
"Number of parallel refinement steps.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Polynomial order of the finite element space.");
|
||||
args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
|
||||
"Impose or not essential boundary conditions.");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"A parameter.");
|
||||
args.AddOption(&coeffWeight, "-c", "--coeffMass",
|
||||
"the weight for the mass term.");
|
||||
args.AddOption(&exactH1Solver, "-exH1Sol", "--exactH1Solver", "-H1prec",
|
||||
"--H1preconditioner",
|
||||
"Use exact H1 solvers for the preconditioner.");
|
||||
args.AddOption(&spe10Coeff, "-spe10", "--useSPE10Coeff", "-constCoeff",
|
||||
"--constCoeff",
|
||||
"Switch between the coefficients for the mass bilinear form.");
|
||||
args.AddOption(&standardCG, "-sCG", "--stdCG", "-rCG", "--resCG",
|
||||
"Switch between standard PCG or recompute residuals in every step and use the residuals itself for the stopping criteria.");
|
||||
args.AddOption(&weightStart, "-ws", "--weightStart",
|
||||
"the exponent for the starting weight (for the mass term).");
|
||||
args.AddOption(&weightEnd, "-we", "--weightEnd",
|
||||
"the exponent for the weight at the end (for the mass term).");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (verbose) { args.PrintOptions(cout); }
|
||||
|
||||
Mesh *mesh;
|
||||
ifstream imesh(mesh_file);
|
||||
if (!imesh)
|
||||
{
|
||||
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
mesh = new Mesh(imesh, 1, 1);
|
||||
imesh.close();
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
if (dim !=4 || sdim != 4)
|
||||
{
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i=0; i<sequ_ref_levels; i++) { mesh->UniformRefinement(); }
|
||||
if (verbose) { mesh->PrintCharacteristics(); }
|
||||
|
||||
if (verbose) { cout << "now we partition the mesh..." << endl << endl; }
|
||||
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
for (int i=0; i<par_ref_levels; i++) { pmesh->UniformRefinement(); }
|
||||
|
||||
pmesh->PrintInfo(std::cout);
|
||||
if (verbose) { cout << endl; }
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec;
|
||||
if (order==1) { fec = new DivSkew1_4DFECollection; }
|
||||
// else fec = new F2K1_4DFECollection;
|
||||
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
fespace->SetUpdateOperatorType(Operator::Hypre_ParCSR);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = set_bc ? 1 : 0;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
MatrixFunctionCoefficient f(sdim, f_exact);
|
||||
MatrixFunctionCoefficient solMat(sdim, E_exact);
|
||||
VectorFunctionCoefficient solVec(6, E_exact_vec);
|
||||
|
||||
|
||||
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
|
||||
for (int expo=weightStart; expo<=weightEnd; expo++)
|
||||
{
|
||||
double weight = pow(10.0,expo);
|
||||
|
||||
x.ProjectCoefficient(solVec);
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new MatFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// cout << x << endl;
|
||||
// x = 0.0;
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
// std::string permFile = "spe_perm.dat";
|
||||
// InversePermeabilityFunction::ReadPermeabilityFile(permFile, MPI_COMM_WORLD);
|
||||
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta;
|
||||
// if(spe10Coeff) beta = new FunctionCoefficient(InversePermeabilityFunction::Norm2Permeability);
|
||||
// else
|
||||
beta = new ConstantCoefficient(weight);
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DivSkewDivSkewIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFE_DivSkewMassIntegrator(*beta));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
//Define the preconditioner
|
||||
|
||||
if (myid == 0) { cout << "Set up the preconditioner" << endl; }
|
||||
Solver *prec;
|
||||
if (dim==4) { prec = new DivSkew4dPrec(&A, fespace, alpha, beta, ess_bdr, order, exactH1Solver); }
|
||||
|
||||
IterativeSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(tol);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(1);
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
delete prec;
|
||||
|
||||
int iter = pcg->GetNumIterations();
|
||||
if (myid==0)
|
||||
{
|
||||
cout << "Weigth: " << weight << " " << iter << endl;
|
||||
|
||||
int *iters = LoadIterations(10, 2*NExpo+1);
|
||||
putIterationsInArray(iter, sequ_ref_levels+par_ref_levels, expo+NExpo,
|
||||
2*NExpo+1, iters);
|
||||
WriteIterations(iters, 10, 2*NExpo+1);
|
||||
}
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double error = 0.0;
|
||||
for (int i = 0; i < fespace->GetNE(); i++)
|
||||
{
|
||||
const FiniteElement* fe = fespace->GetFE(i);
|
||||
int fdof = fe->GetDof();
|
||||
ElementTransformation* transf = fespace->GetElementTransformation(i);
|
||||
DenseMatrix shape(fdof,dim*dim);
|
||||
|
||||
int intorder = 2*fe->GetOrder() + 1; // <----------
|
||||
const IntegrationRule *ir;
|
||||
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
|
||||
|
||||
Vector elSol(dim*dim);
|
||||
DenseMatrix elSolMat(dim,dim);
|
||||
DenseMatrix exactSol(dim,dim);
|
||||
Vector exactSolVec(dim*dim);
|
||||
|
||||
|
||||
|
||||
Array<int> vdofs;
|
||||
fespace->GetElementVDofs(i, vdofs);
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
|
||||
fe->CalcVShape(*transf, shape);
|
||||
|
||||
elSol = 0.0;
|
||||
for (int k = 0; k < fdof; k++)
|
||||
{
|
||||
if (vdofs[k] >= 0)
|
||||
{
|
||||
for (int l=0; l<dim*dim; l++) { elSol(l) += shape(k,l)*x(vdofs[k]); }
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int l=0; l<dim*dim; l++) { elSol(l) -= shape(k,l)*x(-1-vdofs[k]); }
|
||||
}
|
||||
}
|
||||
for (int k=0; k<dim; k++)
|
||||
for (int l=0; l<dim; l++)
|
||||
{
|
||||
elSolMat(k,l) = elSol(dim*k+l);
|
||||
}
|
||||
|
||||
|
||||
solMat.Eval(exactSol,*transf, ip);
|
||||
for (int k=0; k<dim; k++)
|
||||
for (int l=0; l<dim; l++)
|
||||
{
|
||||
exactSolVec(dim*k+l) = exactSol(k,l);
|
||||
}
|
||||
elSol.Add(-1.0, exactSolVec);
|
||||
|
||||
error += ip.weight * fabs(transf->Weight()) * (elSol * elSol);
|
||||
}
|
||||
}
|
||||
double globalError = 0.0;
|
||||
MPI_Allreduce(&error, &globalError, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
|
||||
if (myid==0) { std::cout << "L2 error: " << sqrt(globalError) << std::endl; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
delete pcg;
|
||||
delete a;
|
||||
delete alpha;
|
||||
delete beta;
|
||||
|
||||
delete b;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
|
||||
|
||||
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void E_exact_vec(const Vector &x, Vector &E)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
E.SetSize(6);
|
||||
|
||||
double s0 = sin(M_PI*x(0)), s1 = sin(M_PI*x(1)), s2 = sin(M_PI*x(2)),
|
||||
s3 = sin(M_PI*x(3));
|
||||
double c0 = cos(M_PI*x(0)), c1 = cos(M_PI*x(1)), c2 = cos(M_PI*x(2)),
|
||||
c3 = cos(M_PI*x(3));
|
||||
|
||||
E(0) = c0*c1*s2*s3;
|
||||
E(1) = -c0*s1*c2*s3;
|
||||
E(2) = c0*s1*s2*c3;
|
||||
E(3) = s0*c1*c2*s3;
|
||||
E(4) = -s0*c1*s2*c3;
|
||||
E(5) = s0*s1*c2*c3;
|
||||
}
|
||||
}
|
||||
|
||||
void E_exact(const Vector &x, DenseMatrix &E)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
E.SetSize(dim*dim);
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
Vector vecE; E_exact_vec(x, vecE);
|
||||
|
||||
E = 0.0;
|
||||
|
||||
E(0,1) = vecE(0);
|
||||
E(0,2) = vecE(1);
|
||||
E(0,3) = vecE(2);
|
||||
E(1,2) = vecE(3);
|
||||
E(1,3) = vecE(4);
|
||||
E(2,3) = vecE(5);
|
||||
|
||||
E(1,0) = -E(0,1);
|
||||
E(2,0) = -E(0,2);
|
||||
E(3,0) = -E(0,3);
|
||||
E(2,1) = -E(1,2);
|
||||
E(3,1) = -E(1,3);
|
||||
E(3,2) = -E(2,3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//f_exact = E + 0.5 * P( curl DivSkew E ), where P is the 4d permutation operator
|
||||
void f_exact(const Vector &x, DenseMatrix &f)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
f.SetSize(dim,dim);
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
f = 0.0;
|
||||
|
||||
double s0 = sin(M_PI*x(0)), s1 = sin(M_PI*x(1)), s2 = sin(M_PI*x(2)),
|
||||
s3 = sin(M_PI*x(3));
|
||||
double c0 = cos(M_PI*x(0)), c1 = cos(M_PI*x(1)), c2 = cos(M_PI*x(2)),
|
||||
c3 = cos(M_PI*x(3));
|
||||
|
||||
f(0,1) = (1.0 + 1.0 * M_PI*M_PI)*c0*c1*s2*s3;
|
||||
f(0,2) = -(1.0 + 0.0 * M_PI*M_PI)*c0*s1*c2*s3;
|
||||
f(0,3) = (1.0 + 1.0 * M_PI*M_PI)*c0*s1*s2*c3;
|
||||
f(1,2) = (1.0 - 1.0 * M_PI*M_PI)*s0*c1*c2*s3;
|
||||
f(1,3) = -(1.0 + 0.0 * M_PI*M_PI)*s0*c1*s2*c3;
|
||||
f(2,3) = (1.0 + 1.0 * M_PI*M_PI)*s0*s1*c2*c3;
|
||||
|
||||
f(1,0) = -f(0,1);
|
||||
f(2,0) = -f(0,2);
|
||||
f(3,0) = -f(0,3);
|
||||
f(2,1) = -f(1,2);
|
||||
f(3,1) = -f(1,3);
|
||||
f(3,2) = -f(2,3);
|
||||
}
|
||||
}
|
||||
@@ -1,800 +0,0 @@
|
||||
// MFEM Example 4 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex4p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex4p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex-nurbs.mesh -o 3
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-square.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D H(div) diffusion
|
||||
// problem corresponding to the second order definite equation
|
||||
// -grad(alpha div F) + beta F = f with boundary condition F dot n
|
||||
// = <given normal field>. Here, we use a given exact solution F
|
||||
// and compute the corresponding r.h.s. f. We discretize with
|
||||
// Raviart-Thomas finite elements.
|
||||
//
|
||||
// The example demonstrates the use of H(div) finite element
|
||||
// spaces with the grad-div and H(div) vector finite element mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Bilinear form
|
||||
// hybridization and static condensation are also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-3 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "./spe10_coeff.cpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
int* LoadIterations(int NRows, int NCol)
|
||||
{
|
||||
ifstream in("iter_div.txt");
|
||||
|
||||
//initialize
|
||||
int *iters = new int[NCol*NRows];
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
iters[row*NCol+col] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
return iters;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
if (in.eof())
|
||||
{
|
||||
in.close();
|
||||
return iters;
|
||||
}
|
||||
in >> iters[row*NCol+col];
|
||||
}
|
||||
|
||||
|
||||
in.close();
|
||||
|
||||
return iters;
|
||||
}
|
||||
|
||||
void putIterationsInArray(int iter, int row, int col, int NCol, int* iters)
|
||||
{
|
||||
iters[row*NCol+col] = iter;
|
||||
}
|
||||
|
||||
void WriteIterations(int *iters, int NRows, int NCol)
|
||||
{
|
||||
ofstream out;
|
||||
out.open("iter_div.txt",fstream::out);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
cout << "Cannot open file.\n";
|
||||
delete[] iters;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = 0; row < NRows; row++)
|
||||
{
|
||||
for (int col = 0; col < NCol; col++)
|
||||
{
|
||||
out << iters[row*NCol+col] << "\t";
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
out.close();
|
||||
|
||||
delete[] iters;
|
||||
}
|
||||
|
||||
|
||||
// Exact solution, F, and r.h.s., f. See below for implementation.
|
||||
void F_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
|
||||
|
||||
|
||||
class div4dPrec : public Solver
|
||||
{
|
||||
|
||||
private:
|
||||
HypreParMatrix *A;
|
||||
ParFiniteElementSpace *fespace;
|
||||
|
||||
Coefficient *alpha_, *beta_;
|
||||
|
||||
//kernel operators
|
||||
HypreParMatrix *P_d_HSkewDiv_Hdiv;
|
||||
|
||||
HypreParMatrix *P_H1_HDivSkew;
|
||||
HypreParMatrix *H1_KernelMat;
|
||||
HypreBoomerAMG *amgH1_Kernel;
|
||||
|
||||
//"image" operators
|
||||
HypreParMatrix *P_H1_Hdiv;
|
||||
HypreParMatrix *H1_ImageMat;
|
||||
HypreBoomerAMG *amgH1_Image;
|
||||
|
||||
HypreParMatrix *HDivSkewMat;
|
||||
HypreSmoother * smootherdiv;
|
||||
HypreSmoother * smootherDivSkew;
|
||||
|
||||
CGSolver *pcgKernel;
|
||||
CGSolver *pcgImage;
|
||||
|
||||
Vector *f;
|
||||
Vector *fKernel, *uKernel;
|
||||
Vector *fImage, *uImage;
|
||||
Vector *fDivSkew, *uDivSkew;
|
||||
|
||||
FiniteElementCollection* fecHDivSkewKernel;
|
||||
ParFiniteElementSpace *HDivSkewKernelFESpace;
|
||||
|
||||
bool exactSolves;
|
||||
|
||||
public:
|
||||
~div4dPrec()
|
||||
{
|
||||
delete pcgImage;
|
||||
delete pcgKernel;
|
||||
|
||||
delete uDivSkew, fDivSkew, uImage, fImage, uKernel, fKernel, f;
|
||||
|
||||
delete smootherDivSkew;
|
||||
delete HDivSkewMat;
|
||||
|
||||
delete P_d_HSkewDiv_Hdiv;
|
||||
delete P_H1_Hdiv;
|
||||
delete P_H1_HDivSkew;
|
||||
|
||||
delete amgH1_Image, H1_ImageMat;
|
||||
delete amgH1_Kernel, H1_KernelMat;
|
||||
|
||||
delete smootherdiv;
|
||||
|
||||
delete HDivSkewKernelFESpace;
|
||||
delete fecHDivSkewKernel;
|
||||
}
|
||||
div4dPrec(HypreParMatrix *AUser, ParFiniteElementSpace *fespaceUser,
|
||||
Coefficient *alpha, Coefficient *beta, const Array<int> &essBnd,
|
||||
int orderKernel=1, bool exactSolvesUser=false)
|
||||
{
|
||||
A = AUser;
|
||||
fespace = fespaceUser;
|
||||
alpha_ = alpha;
|
||||
beta_ = beta;
|
||||
|
||||
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
|
||||
exactSolves = exactSolvesUser;
|
||||
|
||||
|
||||
|
||||
|
||||
int orderIm=1; //H1 --> H(div)
|
||||
int orderKer=orderKernel; //DivSkew V --> H(div)
|
||||
|
||||
|
||||
|
||||
smootherdiv = new HypreSmoother(*A, 16, 3);
|
||||
|
||||
Array<int> Hdiv_essDof(fespace->GetVSize()); Hdiv_essDof = 0;
|
||||
fespace->GetEssentialVDofs(essBnd, Hdiv_essDof);
|
||||
|
||||
|
||||
|
||||
|
||||
//setup the H1 FESpace for the kernel
|
||||
FiniteElementCollection* fecH1Kernel;
|
||||
if (orderKer==1) { fecH1Kernel = new LinearFECollection; }
|
||||
else { fecH1Kernel = new QuadraticFECollection; }
|
||||
ParFiniteElementSpace *H1KernelFESpace = new ParFiniteElementSpace(pmesh,
|
||||
fecH1Kernel, 6, Ordering::byVDIM);
|
||||
Array<int> H1Kernel_essDof(H1KernelFESpace->GetVSize()); H1Kernel_essDof = 0;
|
||||
H1KernelFESpace->GetEssentialVDofs(essBnd, H1Kernel_essDof);
|
||||
|
||||
|
||||
//setup the H(DivSkew) FESpace for the kernel
|
||||
if (orderKer==1) { fecHDivSkewKernel = new DivSkew1_4DFECollection; }
|
||||
// else fecHDivSkewKernel = new DivSkewFull1_4DFECollection;
|
||||
HDivSkewKernelFESpace = new ParFiniteElementSpace(pmesh, fecHDivSkewKernel);
|
||||
Array<int> HDivSkewKernel_essDof(HDivSkewKernelFESpace->GetVSize());
|
||||
HDivSkewKernel_essDof = 0;
|
||||
HDivSkewKernelFESpace->GetEssentialVDofs(essBnd, HDivSkewKernel_essDof);
|
||||
|
||||
|
||||
//setup the FESpace for the H1 injection
|
||||
FiniteElementCollection* fecH1Vec;
|
||||
if (orderIm==1) { fecH1Vec = new LinearFECollection; }
|
||||
else { fecH1Vec = new QuadraticFECollection; }
|
||||
ParFiniteElementSpace *H1_ImageFESpace = new ParFiniteElementSpace(pmesh,
|
||||
fecH1Vec, dim, Ordering::byVDIM);
|
||||
Array<int> H1Image_essDof(H1_ImageFESpace->GetVSize()); H1Image_essDof = 0;
|
||||
H1_ImageFESpace->GetEssentialVDofs(essBnd, H1Image_essDof);
|
||||
|
||||
|
||||
|
||||
//setup the H1 preconditioner for the kernel
|
||||
ParBilinearForm* H1Varf = new ParBilinearForm(H1KernelFESpace);
|
||||
// H1Varf->AddDomainIntegrator(new VectorDiffusionIntegrator(*alpha_, 6));
|
||||
// H1Varf->AddDomainIntegrator(new VectorMassIntegrator(6, beta_));
|
||||
|
||||
H1Varf->AddDomainIntegrator(new VectorDiffusionIntegrator(*beta_, 6));
|
||||
H1Varf->Assemble();
|
||||
H1Varf->Finalize();
|
||||
SparseMatrix &matH1(H1Varf->SpMat());
|
||||
for (int dof=0; dof<H1Kernel_essDof.Size(); dof++) if (H1Kernel_essDof[dof]<0) { matH1.EliminateRowCol(dof); }
|
||||
H1_KernelMat = H1Varf->ParallelAssemble();
|
||||
delete H1Varf;
|
||||
amgH1_Kernel = new HypreBoomerAMG(*H1_KernelMat);
|
||||
amgH1_Kernel->SetSystemsOptions(6);
|
||||
|
||||
//setup the H1 preconditioner for the image
|
||||
ParBilinearForm* H1VecVarf = new ParBilinearForm(H1_ImageFESpace);
|
||||
H1VecVarf->AddDomainIntegrator(new VectorDiffusionIntegrator(*alpha_));
|
||||
H1VecVarf->AddDomainIntegrator(new VectorMassIntegrator(-1, beta_));
|
||||
H1VecVarf->Assemble();
|
||||
H1VecVarf->Finalize();
|
||||
SparseMatrix &matH1Vec(H1VecVarf->SpMat());
|
||||
for (int dof=0; dof<H1Image_essDof.Size(); dof++) if (H1Image_essDof[dof]<0) { matH1Vec.EliminateRowCol(dof); }
|
||||
H1_ImageMat = H1VecVarf->ParallelAssemble();
|
||||
delete H1VecVarf;
|
||||
amgH1_Image = new HypreBoomerAMG(*H1_ImageMat);
|
||||
amgH1_Image->SetSystemsOptions(dim);
|
||||
|
||||
|
||||
//setup the injection of H1 into H(DivSkew)
|
||||
ParDiscreteLinearOperator *disInterpolIm = new ParDiscreteLinearOperator(
|
||||
H1KernelFESpace, HDivSkewKernelFESpace);
|
||||
disInterpolIm->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpolIm->Assemble();
|
||||
disInterpolIm->Finalize();
|
||||
SparseMatrix* smatIDIm = &(disInterpolIm->SpMat());
|
||||
smatIDIm->EliminateCols(H1Kernel_essDof);
|
||||
for (int dof=0; dof<HDivSkewKernel_essDof.Size();
|
||||
dof++) if (HDivSkewKernel_essDof[dof]<0) { smatIDIm->EliminateRow(dof); }
|
||||
P_H1_HDivSkew = disInterpolIm->ParallelAssemble();
|
||||
delete disInterpolIm;
|
||||
|
||||
//setup the injection of H1 into H(div)
|
||||
ParDiscreteLinearOperator *disInterpol = new ParDiscreteLinearOperator(
|
||||
H1_ImageFESpace, fespace);
|
||||
disInterpol->AddDomainInterpolator(new IdentityInterpolator);
|
||||
disInterpol->Assemble();
|
||||
disInterpol->Finalize();
|
||||
SparseMatrix* smatID = &(disInterpol->SpMat());
|
||||
smatID->EliminateCols(H1Image_essDof);
|
||||
for (int dof=0; dof<Hdiv_essDof.Size(); dof++) if (Hdiv_essDof[dof]<0) { smatID->EliminateRow(dof); }
|
||||
P_H1_Hdiv = disInterpol->ParallelAssemble();
|
||||
delete disInterpol;
|
||||
|
||||
|
||||
|
||||
|
||||
//setup the injection of the DivSkew(H(DivSkew)) into H(div)
|
||||
ParDiscreteLinearOperator *disDivSkew = new ParDiscreteLinearOperator(
|
||||
HDivSkewKernelFESpace, fespace);
|
||||
disDivSkew->AddDomainInterpolator(new DivSkewInterpolator);
|
||||
disDivSkew->Assemble();
|
||||
disDivSkew->Finalize();
|
||||
SparseMatrix* smatDivSkew= &(disDivSkew->SpMat());
|
||||
smatDivSkew->EliminateCols(HDivSkewKernel_essDof);
|
||||
for (int dof=0; dof<Hdiv_essDof.Size(); dof++) if (Hdiv_essDof[dof]<0) { smatDivSkew->EliminateRow(dof); }
|
||||
P_d_HSkewDiv_Hdiv = disDivSkew->ParallelAssemble();
|
||||
delete disDivSkew;
|
||||
|
||||
|
||||
//setup the smoother for H(DivSkew)
|
||||
ParBilinearForm *a_HDivSkew = new ParBilinearForm(HDivSkewKernelFESpace);
|
||||
// a_HDivSkew->AddDomainIntegrator(new DivSkewDivSkewIntegrator(*alpha_));
|
||||
// a_HDivSkew->AddDomainIntegrator(new VectorFE_DivSkewMassIntegrator(*beta_));
|
||||
|
||||
a_HDivSkew->AddDomainIntegrator(new DivSkewDivSkewIntegrator(*beta_));
|
||||
|
||||
a_HDivSkew->Assemble();
|
||||
a_HDivSkew->Finalize();
|
||||
SparseMatrix &matHDivSkew(a_HDivSkew->SpMat());
|
||||
for (int dof=0; dof<HDivSkewKernel_essDof.Size();
|
||||
dof++) if (HDivSkewKernel_essDof[dof]<0) { matHDivSkew.EliminateRowCol(dof); }
|
||||
HDivSkewMat = a_HDivSkew->ParallelAssemble();
|
||||
delete a_HDivSkew;
|
||||
smootherDivSkew = new HypreSmoother(*HDivSkewMat, 16, 3);
|
||||
|
||||
|
||||
|
||||
f = new Vector(fespace->GetTrueVSize());
|
||||
|
||||
fKernel = new Vector(H1KernelFESpace->GetTrueVSize());
|
||||
uKernel = new Vector(H1KernelFESpace->GetTrueVSize());
|
||||
|
||||
fImage = new Vector(H1_ImageFESpace->GetTrueVSize());
|
||||
uImage = new Vector(H1_ImageFESpace->GetTrueVSize());
|
||||
|
||||
fDivSkew = new Vector(HDivSkewKernelFESpace->GetTrueVSize());
|
||||
uDivSkew = new Vector(HDivSkewKernelFESpace->GetTrueVSize());
|
||||
|
||||
amgH1_Kernel->Mult(*fKernel, *uKernel);
|
||||
amgH1_Image->Mult(*fImage, *uImage);
|
||||
|
||||
pcgKernel = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgKernel->SetOperator(*H1_KernelMat);
|
||||
pcgKernel->SetPreconditioner(*amgH1_Kernel);
|
||||
pcgKernel->SetRelTol(1e-16);
|
||||
pcgKernel->SetMaxIter(100000000);
|
||||
pcgKernel->SetPrintLevel(-2);
|
||||
|
||||
pcgImage = new CGSolver(MPI_COMM_WORLD);
|
||||
pcgImage->SetOperator(*H1_ImageMat);
|
||||
pcgImage->SetPreconditioner(*amgH1_Image);
|
||||
pcgImage->SetRelTol(1e-16);
|
||||
pcgImage->SetMaxIter(100000000);
|
||||
pcgImage->SetPrintLevel(-2);
|
||||
|
||||
delete H1_ImageFESpace;
|
||||
delete H1KernelFESpace;
|
||||
delete fecH1Kernel;
|
||||
delete fecH1Vec;
|
||||
}
|
||||
|
||||
void setExactSolve(bool exSol)
|
||||
{
|
||||
exactSolves = exSol;
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
smootherdiv->Mult(x,y);
|
||||
|
||||
P_H1_Hdiv->MultTranspose(x,*fImage);
|
||||
*uImage = 0.0;
|
||||
if (exactSolves) { pcgImage->Mult(*fImage, *uImage); }
|
||||
else { amgH1_Image->Mult(*fImage, *uImage); }
|
||||
P_H1_Hdiv->Mult(1.0, *uImage, 1.0, y);
|
||||
|
||||
|
||||
*uDivSkew = 0.0;
|
||||
P_d_HSkewDiv_Hdiv->MultTranspose(x,*fDivSkew);
|
||||
|
||||
smootherDivSkew->Mult(*fDivSkew, *uDivSkew);
|
||||
|
||||
P_H1_HDivSkew->MultTranspose(*fDivSkew,*fKernel);
|
||||
*uKernel = 0.0;
|
||||
if (exactSolves) { pcgKernel->Mult(*fKernel, *uKernel); }
|
||||
else { amgH1_Kernel->Mult(*fKernel, *uKernel); }
|
||||
P_H1_HDivSkew->Mult(1.0, *uKernel, 1.0, *uDivSkew);
|
||||
|
||||
P_d_HSkewDiv_Hdiv->Mult(1.0, *uDivSkew, 1.0, y);
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
bool verbose = (myid==0);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool visualization = 1;
|
||||
int sequ_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
double tol = 1e-6;
|
||||
double coeffWeight = 1.0;
|
||||
bool spe10Coeff = false;
|
||||
bool exactH1Solver = false;
|
||||
bool standardCG = true;
|
||||
|
||||
int NExpo = 8;
|
||||
int weightStart = -NExpo;
|
||||
int weightEnd = NExpo;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&sequ_ref_levels, "-sr", "--seqrefinement",
|
||||
"Number of sequential refinement steps.");
|
||||
args.AddOption(&par_ref_levels, "-pr", "--parrefinement",
|
||||
"Number of parallel refinement steps.");
|
||||
args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
|
||||
"Impose or not essential boundary conditions.");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"A parameter.");
|
||||
args.AddOption(&coeffWeight, "-c", "--coeffMass",
|
||||
"the weight for the mass term.");
|
||||
args.AddOption(&exactH1Solver, "-exH1Sol", "--exactH1Solver", "-H1prec",
|
||||
"--H1preconditioner",
|
||||
"Use exact H1 solvers for the preconditioner.");
|
||||
args.AddOption(&spe10Coeff, "-spe10", "--useSPE10Coeff", "-constCoeff",
|
||||
"--constCoeff",
|
||||
"Switch between the coefficients for the mass bilinear form.");
|
||||
args.AddOption(&standardCG, "-sCG", "--stdCG", "-rCG", "--resCG",
|
||||
"Switch between standard PCG or recompute residuals in every step and use the residuals itself for the stopping criteria.");
|
||||
args.AddOption(&weightStart, "-ws", "--weightStart",
|
||||
"the exponent for the starting weight (for the mass term).");
|
||||
args.AddOption(&weightEnd, "-we", "--weightEnd",
|
||||
"the exponent for the weight at the end (for the mass term).");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume, as well as periodic meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
{
|
||||
for (int l = 0; l < sequ_ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them (this is needed in the ADS solver below).
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *fec;
|
||||
if (dim==4) { fec = new RT0_4DFECollection; }
|
||||
else { fec = new RT_FECollection(order-1, dim); }
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = set_bc ? 1 : 0;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary faces will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
VectorFunctionCoefficient F(sdim, F_exact);
|
||||
|
||||
for (int expo=weightStart; expo<=weightEnd; expo++)
|
||||
{
|
||||
double weight = pow(10.0,expo);
|
||||
kappa = weight;
|
||||
|
||||
x.ProjectCoefficient(F);
|
||||
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the H(div)
|
||||
// diffusion operator grad alpha div + beta I, by adding the div-div and
|
||||
// the mass domain integrators.
|
||||
|
||||
// std::string permFile = "spe_perm.dat";
|
||||
// InversePermeabilityFunction::ReadPermeabilityFile(permFile, MPI_COMM_WORLD);
|
||||
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta;
|
||||
// if(spe10Coeff) beta = new FunctionCoefficient(InversePermeabilityFunction::Norm2Permeability);
|
||||
// else
|
||||
beta = new ConstantCoefficient(weight);
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation,
|
||||
// hybridization, etc.
|
||||
FiniteElementCollection *hfec = NULL;
|
||||
ParFiniteElementSpace *hfes = NULL;
|
||||
if (static_cond)
|
||||
{
|
||||
a->EnableStaticCondensation();
|
||||
}
|
||||
else if (hybridization)
|
||||
{
|
||||
hfec = new DG_Interface_FECollection(order-1, dim);
|
||||
hfes = new ParFiniteElementSpace(pmesh, hfec);
|
||||
a->EnableHybridization(hfes, new NormalTraceJumpIntegrator(),
|
||||
ess_tdof_list);
|
||||
}
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
HYPRE_Int glob_size = A.GetGlobalNumRows();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << glob_size << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
|
||||
// the 3D ADS preconditioners from hypre. If using hybridization, the
|
||||
// system is preconditioned with hypre's BoomerAMG.
|
||||
Solver *prec = NULL;
|
||||
if (hybridization) { prec = new HypreBoomerAMG(A); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
if (dim == 2) { prec = new HypreAMS(A, prec_fespace); }
|
||||
else if (dim==3) { prec = new HypreADS(A, prec_fespace); }
|
||||
else if (dim==4) { prec = new div4dPrec(&A, fespace, alpha, beta, ess_bdr, order, exactH1Solver); }
|
||||
else { prec = NULL; }
|
||||
}
|
||||
|
||||
int iter = -1;
|
||||
if (standardCG)
|
||||
{
|
||||
IterativeSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(tol);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(1);
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
iter = pcg->GetNumIterations();
|
||||
|
||||
delete pcg;
|
||||
}
|
||||
else
|
||||
{
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(tol);
|
||||
pcg->SetMaxIter(5000);
|
||||
pcg->SetResidualConvergenceOptions(1,tol);
|
||||
pcg->SetPrintLevel(2);
|
||||
// pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
pcg->GetNumIterations(iter);
|
||||
|
||||
delete pcg;
|
||||
}
|
||||
|
||||
if (myid==0)
|
||||
{
|
||||
cout << "Weigth: " << weight << " " << iter << endl;
|
||||
|
||||
int *iters = LoadIterations(10, 2*NExpo+1);
|
||||
putIterationsInArray(iter, sequ_ref_levels+par_ref_levels, expo+NExpo,
|
||||
2*NExpo+1, iters);
|
||||
WriteIterations(iters, 10, 2*NExpo+1);
|
||||
}
|
||||
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(F);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| F_h - F ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
// {
|
||||
// ostringstream mesh_name, sol_name;
|
||||
// mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
// sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
//
|
||||
// ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
// mesh_ofs.precision(8);
|
||||
// pmesh->Print(mesh_ofs);
|
||||
//
|
||||
// ofstream sol_ofs(sol_name.str().c_str());
|
||||
// sol_ofs.precision(8);
|
||||
// x.Save(sol_ofs);
|
||||
// }
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// if (visualization)
|
||||
// {
|
||||
// char vishost[] = "localhost";
|
||||
// int visport = 19916;
|
||||
// socketstream sol_sock(vishost, visport);
|
||||
// sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
// sol_sock.precision(8);
|
||||
// sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
// }
|
||||
|
||||
if (prec!=NULL) { delete prec; }
|
||||
delete hfes;
|
||||
delete hfec;
|
||||
delete a;
|
||||
delete alpha;
|
||||
delete beta;
|
||||
|
||||
delete b;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
|
||||
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// The exact solution (for non-surface meshes)
|
||||
void F_exact(const Vector &p, Vector &F)
|
||||
{
|
||||
int dim = p.Size();
|
||||
|
||||
if (dim==4)
|
||||
{
|
||||
double s0 = sin(M_PI*p(0)), s1 = sin(M_PI*p(1)), s2 = sin(M_PI*p(2)),
|
||||
s3 = sin(M_PI*p(3));
|
||||
double c0 = cos(M_PI*p(0)), c1 = cos(M_PI*p(1)), c2 = cos(M_PI*p(2)),
|
||||
c3 = cos(M_PI*p(3));
|
||||
|
||||
F(0) = c0 * s1 * s2 * s3;
|
||||
F(1) = s0 * c1 * s2 * s3;
|
||||
F(2) = s0 * s1 * c2 * s3;
|
||||
F(3) = s0 * s1 * s2 * c3;
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
F(0) = cos(kappa*x)*sin(kappa*y);
|
||||
F(1) = cos(kappa*y)*sin(kappa*x);
|
||||
if (dim == 3)
|
||||
{
|
||||
F(2) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The right hand side
|
||||
void f_exact(const Vector &p, Vector &f)
|
||||
{
|
||||
int dim = p.Size();
|
||||
if (dim==4)
|
||||
{
|
||||
double s0 = sin(M_PI*p(0)), s1 = sin(M_PI*p(1)), s2 = sin(M_PI*p(2)),
|
||||
s3 = sin(M_PI*p(3));
|
||||
double c0 = cos(M_PI*p(0)), c1 = cos(M_PI*p(1)), c2 = cos(M_PI*p(2)),
|
||||
c3 = cos(M_PI*p(3));
|
||||
|
||||
f(0) = c0 * s1 * s2 * s3;
|
||||
f(1) = s0 * c1 * s2 * s3;
|
||||
f(2) = s0 * s1 * c2 * s3;
|
||||
f(3) = s0 * s1 * s2 * c3;
|
||||
|
||||
f *= (kappa + 4.0 * M_PI*M_PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
double temp = 1 + 2*kappa*kappa;
|
||||
|
||||
f(0) = temp*cos(kappa*x)*sin(kappa*y);
|
||||
f(1) = temp*cos(kappa*y)*sin(kappa*x);
|
||||
if (dim == 3)
|
||||
{
|
||||
f(2) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/ginkgo/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
@@ -96,6 +96,7 @@ public:
|
||||
{
|
||||
Vector w_glob(width);
|
||||
pfes.Dof_TrueDof_Matrix()->MultTranspose(w, w_glob);
|
||||
w_glob.HostReadWrite(); // read+write -> can use w_glob(i) (non-const)
|
||||
for (int i = 0; i < width; i++) { grad(0, i) = w_glob(i); }
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/hiop/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
+4
-6
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ..
|
||||
MFEM_BUILD_DIR ?= ..
|
||||
MFEM_INSTALL_DIR ?= ../mfem
|
||||
SRC = $(if $(MFEM_DIR:..=),$(MFEM_DIR)/examples/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
@@ -27,8 +26,7 @@ SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
|
||||
ex37p ex39p ex40p \
|
||||
ex1p_4d ex3p_4d ex4D_DivSkew
|
||||
ex37p ex39p ex40p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
|
||||
ex22p ex24p ex25p ex26p ex34p ex35p
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/moonolith/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user