Compare commits
541
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6744b7f895 | ||
|
|
51d1876427 | ||
|
|
cf18624920 | ||
|
|
ac5d8d5993 | ||
|
|
fcf50aae53 | ||
|
|
e2636e7492 | ||
|
|
d70bf954ae | ||
|
|
74ea5db428 | ||
|
|
6f780be8b6 | ||
|
|
af81ad2656 | ||
|
|
9ef3e3533a | ||
|
|
e4d4a84ac8 | ||
|
|
dbdb234d1d | ||
|
|
457d39c643 | ||
|
|
b3a37687a0 | ||
|
|
be0c57cc73 | ||
|
|
351a5a00f7 | ||
|
|
9592ee5ab6 | ||
|
|
d2764aa218 | ||
|
|
5f38694913 | ||
|
|
967a024dbe | ||
|
|
5e235154a5 | ||
|
|
6979e3ae40 | ||
|
|
f2f489a36e | ||
|
|
e3775d570f | ||
|
|
a4acfbb6bd | ||
|
|
9c2d3f81d0 | ||
|
|
3ef72174c8 | ||
|
|
c8242b8e98 | ||
|
|
c91875c5a9 | ||
|
|
94112d7a7a | ||
|
|
c275856ced | ||
|
|
670b7b1ae3 | ||
|
|
ad58472eb5 | ||
|
|
041ffbd32f | ||
|
|
72683f55ca | ||
|
|
2c061c0366 | ||
|
|
154073caf3 | ||
|
|
baa438344f | ||
|
|
b77b608d21 | ||
|
|
40af7f79ad | ||
|
|
6ec94abe05 | ||
|
|
7bfe22ad28 | ||
|
|
855d610ba1 | ||
|
|
e29260f2df | ||
|
|
3f0fe1b4eb | ||
|
|
dfd3bbe1b4 | ||
|
|
e81c4b86c3 | ||
|
|
13543703bd | ||
|
|
9bfaf169ed | ||
|
|
6652c061d5 | ||
|
|
65b0d86736 | ||
|
|
bcc694aa41 | ||
|
|
4884063dab | ||
|
|
d4d002aca7 | ||
|
|
773ea0cc5d | ||
|
|
e9acd4c084 | ||
|
|
9dc2495906 | ||
|
|
8692b9ab56 | ||
|
|
5dcf7e17d8 | ||
|
|
e69d5bd59c | ||
|
|
b02bfbc799 | ||
|
|
edbe8ee14b | ||
|
|
2a033c3298 | ||
|
|
3d7b3b18fb | ||
|
|
47fe9738ec | ||
|
|
69e9674c06 | ||
|
|
972e13a595 | ||
|
|
2208fc9aa8 | ||
|
|
e6c39635c9 | ||
|
|
5fa59a2f66 | ||
|
|
8b41d1dd05 | ||
|
|
e7485db5eb | ||
|
|
4614a69b51 | ||
|
|
c11576a686 | ||
|
|
c93c185315 | ||
|
|
fe55085199 | ||
|
|
9f13caf64e | ||
|
|
fc1a8776ba | ||
|
|
01f981d705 | ||
|
|
c225923cce | ||
|
|
8f90dc5c03 | ||
|
|
a46cf91039 | ||
|
|
0aaf837463 | ||
|
|
ef53cb55fd | ||
|
|
9de9bdaa64 | ||
|
|
2bb7d8fe72 | ||
|
|
0d6809927f | ||
|
|
023d6e3276 | ||
|
|
3dd5cbbe07 | ||
|
|
cdc8c9634f | ||
|
|
94578246c6 | ||
|
|
66849d50a8 | ||
|
|
a3dc3fa717 | ||
|
|
33fc6fa644 | ||
|
|
c0fbb01a73 | ||
|
|
a3429fa4ab | ||
|
|
1b812b4a36 | ||
|
|
96af414790 | ||
|
|
e327e111ad | ||
|
|
413fb4e44b | ||
|
|
7e44926d03 | ||
|
|
e5f2c551bf | ||
|
|
c251fa7d78 | ||
|
|
9495bf51dd | ||
|
|
29b8f10fdb | ||
|
|
ef98bba35c | ||
|
|
295d5a0ac9 | ||
|
|
b81fa9f479 | ||
|
|
c81506c238 | ||
|
|
a2c73fab47 | ||
|
|
9332beb87a | ||
|
|
6d5403daab | ||
|
|
1c7164a5b1 | ||
|
|
941262e9d3 | ||
|
|
5cfeb44ea2 | ||
|
|
513b4433b2 | ||
|
|
542bd7ecd8 | ||
|
|
2dd937d526 | ||
|
|
c03554d7b0 | ||
|
|
ddd5f51c29 | ||
|
|
df8b66826b | ||
|
|
ea45cda9bb | ||
|
|
2f198f7e2d | ||
|
|
8a88a7c7a4 | ||
|
|
7a9462b41e | ||
|
|
0ba5fd7e8a | ||
|
|
065b5f9fde | ||
|
|
f04fdefd1a | ||
|
|
28c512a2b9 | ||
|
|
841fe38186 | ||
|
|
63b969754f | ||
|
|
2b21ebc361 | ||
|
|
b9afa1fe99 | ||
|
|
664dc801d3 | ||
|
|
5275b4926d | ||
|
|
91c73f9481 | ||
|
|
e19ef66f80 | ||
|
|
94e763c272 | ||
|
|
b3128b0897 | ||
|
|
fb7fdecb9f | ||
|
|
6ed5dfd1e6 | ||
|
|
45d5bd70e1 | ||
|
|
3b1a806bc0 | ||
|
|
67e0beb41d | ||
|
|
ae2b918ff6 | ||
|
|
ed8e6fc20d | ||
|
|
be121033ed | ||
|
|
2d7938ed3e | ||
|
|
28bdb1a235 | ||
|
|
c78e984d69 | ||
|
|
26a18096d3 | ||
|
|
ab64e55247 | ||
|
|
aee9379f34 | ||
|
|
3e40a30e5d | ||
|
|
24b66d11cc | ||
|
|
f40aa8985b | ||
|
|
0917cebd10 | ||
|
|
5ac499ff34 | ||
|
|
38c60734b6 | ||
|
|
7d40cb822c | ||
|
|
3ed49a8ccf | ||
|
|
d7fbcda0fa | ||
|
|
45ee759dd7 | ||
|
|
6fee4044ef | ||
|
|
c6d8097500 | ||
|
|
b13e1afad0 | ||
|
|
9de38b3f2e | ||
|
|
3f3398f6de | ||
|
|
6287639acc | ||
|
|
91e6559d36 | ||
|
|
c6d1f20249 | ||
|
|
a3d2b38f31 | ||
|
|
4de70fd432 | ||
|
|
71c167805d | ||
|
|
6ef17443a4 | ||
|
|
67300f588d | ||
|
|
26f37ac152 | ||
|
|
9fd2fe536d | ||
|
|
777e352a76 | ||
|
|
4ea8883b23 | ||
|
|
23a8ffe384 | ||
|
|
5d110683f6 | ||
|
|
7b36ff88a1 | ||
|
|
255302eae6 | ||
|
|
07f8b8f525 | ||
|
|
baea0cf188 | ||
|
|
aefc9068e0 | ||
|
|
52137ea095 | ||
|
|
e131e0f3a6 | ||
|
|
33cdfdcd6e | ||
|
|
21dc848651 | ||
|
|
1df33bfceb | ||
|
|
26cc3465e2 | ||
|
|
b4787f3fff | ||
|
|
48f4efb5a5 | ||
|
|
5218ea2649 | ||
|
|
976d64ff95 | ||
|
|
cc86955b73 | ||
|
|
f4ce842065 | ||
|
|
ae27de9af2 | ||
|
|
78cf39781d | ||
|
|
fdc0ed7cd7 | ||
|
|
46a5b8cd81 | ||
|
|
b2a7499cbf | ||
|
|
38d771af7c | ||
|
|
881d0be80a | ||
|
|
15de7d1352 | ||
|
|
b8b0645195 | ||
|
|
47b42e9f7d | ||
|
|
b7bd26fe50 | ||
|
|
c3d2bdaf19 | ||
|
|
8f328237ab | ||
|
|
6872f7bee6 | ||
|
|
eed10fe35b | ||
|
|
cdcc3339d6 | ||
|
|
c911247ee3 | ||
|
|
bc546dc598 | ||
|
|
3916f7dabf | ||
|
|
e91c582d03 | ||
|
|
d539dc6fb3 | ||
|
|
cb0d8a7af6 | ||
|
|
e25eb36d29 | ||
|
|
863638bb47 | ||
|
|
c9685df178 | ||
|
|
0999fddcab | ||
|
|
d1b4d38e5d | ||
|
|
178ceeeaa3 | ||
|
|
d1cf744f06 | ||
|
|
4c7b9989d9 | ||
|
|
d59281d801 | ||
|
|
043b338fee | ||
|
|
83074c4c0a | ||
|
|
9d204c8d81 | ||
|
|
e237f942e8 | ||
|
|
32f4c4f88e | ||
|
|
185f63bdc5 | ||
|
|
c9ddefd68a | ||
|
|
0b7980091e | ||
|
|
6bb1aaba5d | ||
|
|
079f9cce33 | ||
|
|
244719c39c | ||
|
|
0de5f1f70b | ||
|
|
a1c905b6ee | ||
|
|
18e7b9552c | ||
|
|
fe300a678a | ||
|
|
54c1073f4c | ||
|
|
78458fed0a | ||
|
|
8497117c61 | ||
|
|
d2753a59b0 | ||
|
|
ee5cc572ce | ||
|
|
6e16d5b7bf | ||
|
|
f273e9b04f | ||
|
|
cb731d4f1a | ||
|
|
7d1b925438 | ||
|
|
1314d0b7d3 | ||
|
|
95be3d9c12 | ||
|
|
3d6f1694b3 | ||
|
|
6e82a1953b | ||
|
|
c454ddeb1c | ||
|
|
eb0882ed94 | ||
|
|
0a051d9428 | ||
|
|
4104e010d5 | ||
|
|
d9d01bdf5b | ||
|
|
beaaa373ee | ||
|
|
80f8a57a43 | ||
|
|
c7df56044c | ||
|
|
4ffd22a8ec | ||
|
|
65f3507406 | ||
|
|
cd161542b3 | ||
|
|
17ef5ae043 | ||
|
|
654a070e12 | ||
|
|
979954402b | ||
|
|
bc5fbe767f | ||
|
|
333e7dc512 | ||
|
|
878bfef86b | ||
|
|
bb0b0132b1 | ||
|
|
f04b6a467e | ||
|
|
f3fe49a07c | ||
|
|
7f1e9aca34 | ||
|
|
634519cc89 | ||
|
|
7d8877de64 | ||
|
|
7947c5f2de | ||
|
|
b58a6b599f | ||
|
|
0b653bc3cd | ||
|
|
623fc516f0 | ||
|
|
24e142b6b4 | ||
|
|
4e75435e20 | ||
|
|
364bc0faa4 | ||
|
|
03f1f5e252 | ||
|
|
6b69120856 | ||
|
|
e591dbe8b0 | ||
|
|
edd4bae030 | ||
|
|
6ea11d6e65 | ||
|
|
6e2d4bd6b8 | ||
|
|
6b40a55988 | ||
|
|
ecf3c10c08 | ||
|
|
e3297d9e91 | ||
|
|
7931e85395 | ||
|
|
1190685292 | ||
|
|
b0828aa597 | ||
|
|
caa6085362 | ||
|
|
cef5e71448 | ||
|
|
078603885e | ||
|
|
8123a2e211 | ||
|
|
63e3496131 | ||
|
|
dddef6be52 | ||
|
|
107b5c236e | ||
|
|
7f58074e97 | ||
|
|
81178ac5fd | ||
|
|
021e39e537 | ||
|
|
3456950640 | ||
|
|
79e352c460 | ||
|
|
3c6210d83a | ||
|
|
fbf563955d | ||
|
|
9a72bced50 | ||
|
|
b5025ea8b0 | ||
|
|
d1a5ffa822 | ||
|
|
82b32e9a30 | ||
|
|
a6cd361884 | ||
|
|
15c481f52e | ||
|
|
3404d4c938 | ||
|
|
29d1803362 | ||
|
|
e5bcaaffb7 | ||
|
|
41e90576de | ||
|
|
9c4021e35b | ||
|
|
2530418b8c | ||
|
|
cd5c8571af | ||
|
|
0eb2d04854 | ||
|
|
81c951a8b8 | ||
|
|
8b8141020d | ||
|
|
c9f5103796 | ||
|
|
5b96b41815 | ||
|
|
482a7f84ea | ||
|
|
f188b9eebc | ||
|
|
80ddc79123 | ||
|
|
31005dda60 | ||
|
|
bd7b0a2c06 | ||
|
|
4e1e6531f3 | ||
|
|
6223000e9a | ||
|
|
ec689e5d33 | ||
|
|
f1c472dc20 | ||
|
|
5fe0d860d7 | ||
|
|
accae8301b | ||
|
|
f4578552dc | ||
|
|
485f9b07d7 | ||
|
|
7f9eb72e74 | ||
|
|
02e4cbe75b | ||
|
|
d81a2728b4 | ||
|
|
5316e51c4f | ||
|
|
b2825c9625 | ||
|
|
1ca041cabc | ||
|
|
41123cdb19 | ||
|
|
35d9a405a6 | ||
|
|
8c76b75573 | ||
|
|
9d06122995 | ||
|
|
73f4c3298e | ||
|
|
2af6d555a3 | ||
|
|
d413251dcb | ||
|
|
8bdfcd231e | ||
|
|
5af0b1cf8a | ||
|
|
eba4c64e15 | ||
|
|
5ed43cb1b4 | ||
|
|
c9a8df1e0d | ||
|
|
6de42881be | ||
|
|
db592e1f2a | ||
|
|
3c7241e735 | ||
|
|
d1dd6c9546 | ||
|
|
4564b6dd5d | ||
|
|
7f7394c991 | ||
|
|
e4bd8dd615 | ||
|
|
00f23ff6c7 | ||
|
|
9c6553ce2e | ||
|
|
a008739b3d | ||
|
|
673bb91447 | ||
|
|
d322228504 | ||
|
|
77ef3a2e92 | ||
|
|
2cc8c18250 | ||
|
|
48c54c0bbc | ||
|
|
57c99323e7 | ||
|
|
7bf7c35218 | ||
|
|
8da89221b3 | ||
|
|
24824722f2 | ||
|
|
1b69590800 | ||
|
|
fec64571ac | ||
|
|
aa9a4887f7 | ||
|
|
757369beb3 | ||
|
|
a7a8e61bb3 | ||
|
|
a26a3dde23 | ||
|
|
e404adf317 | ||
|
|
c7a94e8f8b | ||
|
|
b3e6b22b71 | ||
|
|
73cb59919e | ||
|
|
463b57205d | ||
|
|
0e9e25f893 | ||
|
|
6598a8d90b | ||
|
|
91fabccd8b | ||
|
|
950fb937c7 | ||
|
|
ac9b14e8d7 | ||
|
|
b58ac482d3 | ||
|
|
1ebd603e62 | ||
|
|
6967342813 | ||
|
|
fe16207d6a | ||
|
|
ba94547c4d | ||
|
|
02ef0f7f04 | ||
|
|
fa3a262608 | ||
|
|
0f5b0eb57e | ||
|
|
8abd6992d8 | ||
|
|
7349ece185 | ||
|
|
58f7aacce9 | ||
|
|
af871ffa7b | ||
|
|
369d55c7b2 | ||
|
|
65d63395c0 | ||
|
|
9fd331714d | ||
|
|
3876d111cf | ||
|
|
e3085a1182 | ||
|
|
ad0a5e0474 | ||
|
|
3b0618405b | ||
|
|
05e264c53e | ||
|
|
cd03b3fd74 | ||
|
|
bc6d4e68b4 | ||
|
|
66e0c79b3f | ||
|
|
4beda68a36 | ||
|
|
6a79901fa7 | ||
|
|
8b5f9f7720 | ||
|
|
98739f8c09 | ||
|
|
84129a1f83 | ||
|
|
b6ea025795 | ||
|
|
d528e9d63e | ||
|
|
d18f11e6c7 | ||
|
|
efeb400b27 | ||
|
|
1451c3c483 | ||
|
|
f12d46bdf9 | ||
|
|
6dfb809f97 | ||
|
|
ef8785b852 | ||
|
|
efd182ec4c | ||
|
|
e7856b982a | ||
|
|
4f4d1f9d2d | ||
|
|
1a89d5af02 | ||
|
|
c71b02a915 | ||
|
|
5576f3653e | ||
|
|
7f316dd07b | ||
|
|
19b5ae6b9a | ||
|
|
94cd8859b2 | ||
|
|
8ea8b97fc8 | ||
|
|
f9abc77026 | ||
|
|
68e3f9aeb9 | ||
|
|
08f018c299 | ||
|
|
69e57546d4 | ||
|
|
433a29c9e3 | ||
|
|
92ee7099a4 | ||
|
|
808c9cd2c4 | ||
|
|
f3cd0a10e7 | ||
|
|
7de0cfe77d | ||
|
|
441b503593 | ||
|
|
f1614c5185 | ||
|
|
e6bb8c8976 | ||
|
|
4718577810 | ||
|
|
9e520183c8 | ||
|
|
95a40d7377 | ||
|
|
036846bcd6 | ||
|
|
6c2968156f | ||
|
|
9ba650f2fd | ||
|
|
ee6cb39ee0 | ||
|
|
1701a6e84c | ||
|
|
f500e5220a | ||
|
|
e078288ecb | ||
|
|
e46c7ac0e7 | ||
|
|
c2b133f3c8 | ||
|
|
373379fbdd | ||
|
|
bfc3835a1d | ||
|
|
ab8578a728 | ||
|
|
fdaaacfdf3 | ||
|
|
5d7ebbb51a | ||
|
|
fe0821413a | ||
|
|
695a5adcb2 | ||
|
|
b2dfc76f1a | ||
|
|
76c700b41e | ||
|
|
dc3f10fa63 | ||
|
|
420bb73d6c | ||
|
|
13648768ae | ||
|
|
7bfda36bca | ||
|
|
46fc3ce9c2 | ||
|
|
b3086ce754 | ||
|
|
d1312d354a | ||
|
|
16fcc3f970 | ||
|
|
a6caab3bed | ||
|
|
172b5bdcc9 | ||
|
|
72f83c132d | ||
|
|
911e394d80 | ||
|
|
4ff29e40c9 | ||
|
|
dac7808875 | ||
|
|
246fd8e6fe | ||
|
|
5e243e01f8 | ||
|
|
56b83ea538 | ||
|
|
cd7371896a | ||
|
|
3fdd3e2450 | ||
|
|
303ccefd83 | ||
|
|
48bcc332f7 | ||
|
|
0c23d23874 | ||
|
|
e76cef225d | ||
|
|
6f49201e62 | ||
|
|
1cb75f7730 | ||
|
|
ef14d682c5 | ||
|
|
54eb78e0df | ||
|
|
cd1a3a4fc7 | ||
|
|
ffb50c2416 | ||
|
|
f568ca3c6e | ||
|
|
c7fb058051 | ||
|
|
a4d87d936b | ||
|
|
f26bd4bea1 | ||
|
|
6c001c3f9a | ||
|
|
82573af316 | ||
|
|
4bfb2d62d9 | ||
|
|
0bf7a715db | ||
|
|
ad68c8f245 | ||
|
|
0b430fa5f6 | ||
|
|
238d1921c7 | ||
|
|
a25783e3b4 | ||
|
|
3aa8d49ca8 | ||
|
|
4ed6a4a014 | ||
|
|
08f910343b | ||
|
|
9fb0340206 | ||
|
|
db40e8125e | ||
|
|
3e46fce65f | ||
|
|
f649ed6b62 | ||
|
|
a1a3a51d86 | ||
|
|
6728fc7eda | ||
|
|
71e9d4e1d9 | ||
|
|
7feb560f9b | ||
|
|
aa9eadfcdf | ||
|
|
82122f05cf | ||
|
|
2d64fa2162 | ||
|
|
8041734755 | ||
|
|
523891f05f | ||
|
|
b3fc46b6f2 | ||
|
|
9271494723 | ||
|
|
f2dbcecb92 | ||
|
|
f2af9748c4 | ||
|
|
9813dd7722 | ||
|
|
3806e68ed1 |
@@ -20,6 +20,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository == 'mfem/mfem' # Don't run in forks
|
||||
permissions:
|
||||
packages: write
|
||||
strategy:
|
||||
@@ -27,7 +28,8 @@ jobs:
|
||||
matrix:
|
||||
|
||||
# Dockerfiles to build, a matrix supports future expanded builds
|
||||
container: [["config/docker/Dockerfile", "ghcr.io/mfem/mfem-ubuntu-base"]]
|
||||
container: [["config/docker/Dockerfile.base", "ghcr.io/mfem/mfem-ubuntu-base"],
|
||||
["config/docker/Dockerfile", "ghcr.io/mfem/mfem-ubuntu"]]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
name: Build
|
||||
|
||||
@@ -205,6 +205,10 @@ jobs:
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
run: |
|
||||
$PortFile = 'C:\vcpkg\ports\metis\portfile.cmake'
|
||||
$OriginalURL = 'http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-${METIS_VERSION}.tar.gz'
|
||||
$NewURL = 'https://github.com/mfem/tpls/raw/gh-pages/metis-5.1.0.tar.gz'
|
||||
(Get-Content $PortFile).replace($OriginalURL, $NewURL) | Set-Content $PortFile
|
||||
vcpkg install metis --triplet=x64-windows-static
|
||||
|
||||
# MFEM build and test
|
||||
|
||||
@@ -307,6 +307,8 @@ miniapps/solvers/sol.*
|
||||
miniapps/parelag/MultilevelHcurlHdivSolver
|
||||
miniapps/parelag/*.mesh
|
||||
|
||||
miniapps/hooke/hooke
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
|
||||
@@ -10,20 +10,23 @@
|
||||
|
||||
Version 4.4.1 (development)
|
||||
===========================
|
||||
- Added example for body-fitted volumetric and shape integration using the
|
||||
Algoim library.
|
||||
|
||||
- Added WhiteGaussianNoiseDomainLFIntegrator: a LinearFormIntegrator class for
|
||||
spatial Gaussian white noise.
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added support for mixed meshes and pyramids in GSLIB-FindPoints.
|
||||
|
||||
- Added a new Zienkiewicz-Zhu patch recovery-based a posteriori error estimator.
|
||||
See fem/estimators.hpp.
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added support for assembling low-order-refined matrices using a GPU-enabled
|
||||
"batched" algorithm. The lor_solvers and plor_solvers now fully support GPU
|
||||
acceleration.
|
||||
|
||||
- Added support for ParMoonolith, https://bitbucket.org/zulianp/par_moonolith,
|
||||
which provides parallel non-conforming, non-matching, variational, volumetric
|
||||
mesh information transfer. With ParMortarAssember, fields can be exchanged
|
||||
between arbitrarily distributed and unrelated finite element meshes in a
|
||||
variationally consistent way.
|
||||
- Added support for partial assembly and fully matrix-free operators on mixed
|
||||
meshes (different element types and p-adaptivity) through libCEED, including
|
||||
device acceleration, e.g. with NVIDIA and AMD GPUs. The p-adaptivity is
|
||||
currently limited by MFEM capabilities, i.e. 2D serial meshes. All mixed
|
||||
element topologies are supported in serial and parallel: segment, triangle,
|
||||
square, tetrahedron, cube, prism, and pyramid.
|
||||
|
||||
- Added full assembly and device support for several LinearForm integrators:
|
||||
* DomainLF: (f, v)
|
||||
@@ -31,18 +34,59 @@ Version 4.4.1 (development)
|
||||
* DomainLFGrad: (f, grad(v))
|
||||
* VectorDomainLFGrad: ((f1x,f1y,f1z,...,fnx,fny,fnz), grad(v1,...,vn))
|
||||
|
||||
- Added WhiteGaussianNoiseDomainLFIntegrator: a LinearFormIntegrator class for
|
||||
spatial Gaussian white noise.
|
||||
|
||||
- Added a new Zienkiewicz-Zhu patch recovery-based a posteriori error estimator.
|
||||
See fem/estimators.hpp.
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new elasticity miniapp, Hooke, that showcases a low-level approach of
|
||||
using MFEM to solve a nonlinear elasticity problem based on the fundamental
|
||||
finite element operator decomposition. The miniapp also integrates with
|
||||
automatic differentiation tools like a native dual number implementation or a
|
||||
third party library such as Enzyme. See miniapps/elasticity for more details.
|
||||
|
||||
- Add a new example code, Example 33/33p, to demonstrate the solution of
|
||||
spectral fractional PDEs with MFEM.
|
||||
|
||||
Integrations, testing and documentation
|
||||
---------------------------------------
|
||||
- Added a Dockerfile for a simple MFEM container, see config/docker/README.md.
|
||||
|
||||
- Added support for assembling low-order-refined matrices using a GPU-enabled
|
||||
"batched" algorithm. The lor_solvers and plor_solvers now fully support GPU
|
||||
acceleration.
|
||||
- Added support for ParMoonolith, https://bitbucket.org/zulianp/par_moonolith,
|
||||
which provides parallel non-conforming, non-matching, variational, volumetric
|
||||
mesh information transfer. With ParMortarAssember, fields can be exchanged
|
||||
between arbitrarily distributed and unrelated finite element meshes in a
|
||||
variationally consistent way.
|
||||
|
||||
- Added support for the LLVM-based automatic differentiation tool Enzyme, see
|
||||
https://github.com/EnzymeAD/Enzyme. Build system flags and a convenience
|
||||
header are provided. The functionality and interaction are demonstrated in a
|
||||
new miniapp in miniapps/elasticity.
|
||||
|
||||
- Added example for body-fitted volumetric and shape integration using the
|
||||
Algoim library.
|
||||
|
||||
- Added Windows 2022 CI testing with GitHub actions.
|
||||
|
||||
- Added support for mixed meshes and pyramids in GSLIB-FindPoints.
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
|
||||
- Added boundary elimination with device support for `SparseMatrix` and
|
||||
`HypreParMatrix`.
|
||||
|
||||
- When using `AssemblyLevel::FULL`, `FABilinearFormExtension::FormSystemMatrix`
|
||||
outputs an `OperatorHandle` containing a `SparseMatrix` in serial, and an
|
||||
`HypreParMatrix` in parallel (instead of a `ConstrainedOperator`).
|
||||
|
||||
- Added TMOP metrics for mesh untangling and worst-case quality improvement.
|
||||
|
||||
Version 4.4, released on March 21, 2022
|
||||
=======================================
|
||||
@@ -75,6 +119,11 @@ Meshing improvements
|
||||
- Added a simpler interface to access mesh face information, see FaceInformation
|
||||
and GetFaceInformation in the Mesh class.
|
||||
|
||||
- Added the method ParMesh::GetSerialMesh() that reconstructs a partitioned
|
||||
parallel mesh on a given single rank. Also, added the method
|
||||
ParMesh::PrintAsSerial() that saves the reconstructed serial mesh to a C++
|
||||
stream on rank 0.
|
||||
|
||||
- Gmsh meshes where all elements have zero physical tag (the default Gmsh output
|
||||
format if no physical groups are defined) are now successfully loaded, and
|
||||
elements are reassigned attribute number 1.
|
||||
@@ -177,6 +226,13 @@ Miscellaneous
|
||||
|
||||
- Fixed several MinGW build issues on Windows.
|
||||
|
||||
- In various places in the library, replace the use of 'long' with 'long long'
|
||||
to better support Win64 builds where 'long' is 32-bit and 'long long' is
|
||||
64-bit. On Linux and MacOS, both types are typically 64-bit.
|
||||
|
||||
- Update various "MemoryUsage" methods to return 'std::size_t' instead of 'long'
|
||||
since the latter is 32-bit in Win64 builds.
|
||||
|
||||
- Added 'double' atomicAdd implementation for previous versions of CUDA.
|
||||
|
||||
- HypreParVector and Vector now support C++ move semantics, and the copy
|
||||
|
||||
+11
-4
@@ -136,6 +136,8 @@ if (MFEM_USE_CUDA)
|
||||
"CUDA flags set for MFEM" FORCE)
|
||||
set(CUSPARSE_FOUND TRUE)
|
||||
set(CUSPARSE_LIBRARIES "cusparse")
|
||||
set(CUBLAS_FOUND TRUE)
|
||||
set(CUSBLAS_LIBRARIES "cublas")
|
||||
endif()
|
||||
|
||||
if (XSDK_ENABLE_C)
|
||||
@@ -452,6 +454,11 @@ if (MFEM_USE_PARELAG)
|
||||
find_package(PARELAG REQUIRED)
|
||||
endif()
|
||||
|
||||
# Enzyme
|
||||
if (MFEM_USE_ENZYME)
|
||||
find_package(ENZYME REQUIRED)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
if (NOT DEFINED MFEM_TIMER_TYPE)
|
||||
if (APPLE)
|
||||
@@ -478,8 +485,8 @@ endif()
|
||||
set(MFEM_TPLS OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS
|
||||
PETSC SLEPC MESQUITE MUMPS STRUMPACK AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 CUSPARSE MKL_CPARDISO AMGX CALIPER CODIPACK BENCHMARK PARELAG
|
||||
MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM)
|
||||
ADIOS2 CUBLAS CUSPARSE MKL_CPARDISO AMGX CALIPER CODIPACK BENCHMARK PARELAG
|
||||
MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM ENZYME)
|
||||
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
@@ -556,9 +563,9 @@ endif()
|
||||
message(STATUS "TPL_INCLUDE_DIRS = ${TPL_INCLUDE_DIRS}")
|
||||
target_include_directories(mfem
|
||||
PUBLIC
|
||||
${TPL_INCLUDE_DIRS}
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
${TPL_INCLUDE_DIRS})
|
||||
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
|
||||
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── common
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
│ ├── hooke
|
||||
│ ├── meshing
|
||||
│ ├── mtop
|
||||
│ ├── navier
|
||||
|
||||
@@ -558,6 +558,14 @@ MFEM_USE_PARELAG = YES/NO
|
||||
use ParELAG. In fact, ParELAG is dependent on MFEM. Therefore, this option
|
||||
currently only concerns the miniapps.
|
||||
|
||||
MFEM_USE_ENZYME = YES/NO
|
||||
Enables automatic differentiation support through the LLVM plugin Enzyme.
|
||||
This requires the compiler to be set to clang (>=14.0.0). We also advise to
|
||||
use the link time optimization (LTO) plugin, to enable functions that you
|
||||
define over multiple files (compilation units) and want to be differentiated
|
||||
automatically, to work. This requires to also use LLVM/LLD for linking.
|
||||
Recommended options are in config/defaults.mk.
|
||||
|
||||
MFEM_BUILD_TAG = (any value)
|
||||
An optional tag to characterize the build. Exported to config/config.mk.
|
||||
Can be used to identify the MFEM build from other makefiles.
|
||||
@@ -760,8 +768,6 @@ The specific libraries and their options are:
|
||||
Options: BLITZ_OPT, BLITZ_LIB
|
||||
Versions: BLITZ = 1.0.2
|
||||
|
||||
|
||||
|
||||
- MKL CPardiso (optional), used when MFEM_USE_MKL_CPARDISO = YES.
|
||||
URL: https://software.intel.com/content/www/us/en/develop/tools/math-kernel-library.html
|
||||
Options: MKL_CPARDISO_OPT, MKL_CPARDISO_LIB.
|
||||
@@ -838,6 +844,12 @@ The specific libraries and their options are:
|
||||
URL: https://github.com/LLNL/parelag
|
||||
Options: PARELAG_DIR, PARELAG_OPT, PARELAG_LIB.
|
||||
|
||||
- Enzyme, used when MFEM_USE_ENZYME = YES. Requires LLVM/Clang >= 14.0.0.
|
||||
URL: https://github.com/EnzymeAD/Enzyme
|
||||
Options: ENZYME_DIR, ENZYME_OPT, ENZYME_LIB.
|
||||
Versions: Enzyme >= v0.0.33.
|
||||
|
||||
|
||||
Building with CMake
|
||||
===================
|
||||
The MFEM build system consists of two steps: configuration and compilation.
|
||||
@@ -976,6 +988,7 @@ MFEM_USE_CALIPER
|
||||
MFEM_USE_FMS
|
||||
MFEM_USE_BENCHMARK
|
||||
MFEM_USE_PARELAG
|
||||
MFEM_USE_ENZYME
|
||||
|
||||
The following options are CMake specific:
|
||||
|
||||
@@ -1035,6 +1048,7 @@ The CMake build system adds auto-detection for the following packages/libraries:
|
||||
- FMS
|
||||
- BENCHMARK
|
||||
- ParELAG
|
||||
- Enzyme
|
||||
|
||||
The following built-in CMake packages are also used:
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
|
||||
set(MFEM_USE_ALGOIM @MFEM_USE_ALGOIM@)
|
||||
set(MFEM_USE_BENCHMARK @MFEM_USE_BENCHMARK@)
|
||||
set(MFEM_USE_PARELAG @MFEM_USE_PARELAG@)
|
||||
set(MFEM_USE_ENZYME @MFEM_USE_ENZYME@)
|
||||
|
||||
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
|
||||
set(MFEM_CXX_FLAGS "@CMAKE_CXX_FLAGS@")
|
||||
|
||||
@@ -190,4 +190,7 @@
|
||||
// Enable MFEM functionality based on the Google Benchmark library.
|
||||
#cmakedefine MFEM_USE_BENCHMARK
|
||||
|
||||
// Enable Enzyme for AD
|
||||
#cmakedefine MFEM_USE_ENZYME
|
||||
|
||||
#endif // MFEM_CONFIG_HEADER
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2010-2022, 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 accomodate 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()
|
||||
@@ -894,7 +894,7 @@ function(mfem_export_mk_files)
|
||||
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_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
|
||||
MFEM_USE_MOONOLITH MFEM_USE_ALGOIM)
|
||||
MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
|
||||
@@ -195,4 +195,7 @@
|
||||
// Enable functionality based on the Google Benchmark library.
|
||||
// #define MFEM_USE_BENCHMARK
|
||||
|
||||
// Enable the Enzyme LLVM plugin
|
||||
// #define MFEM_USE_ENZYME
|
||||
|
||||
#endif // MFEM_CONFIG_HEADER
|
||||
|
||||
@@ -63,6 +63,7 @@ MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
|
||||
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
|
||||
MFEM_USE_BENCHMARK = @MFEM_USE_BENCHMARK@
|
||||
MFEM_USE_PARELAG = @MFEM_USE_PARELAG@
|
||||
MFEM_USE_ENZYME = @MFEM_USE_ENZYME@
|
||||
|
||||
# Compiler, compile options, and link options
|
||||
MFEM_CXX = @MFEM_CXX@
|
||||
|
||||
@@ -64,6 +64,7 @@ option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
|
||||
option(MFEM_USE_CODIPACK "Enable automatic differentiation (AD) using CoDiPack" OFF)
|
||||
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
|
||||
option(MFEM_USE_PARELAG "Enable ParELAG" OFF)
|
||||
option(MFEM_USE_ENZYME "Enable Enzyme" OFF)
|
||||
|
||||
# Optional overrides for autodetected MPIEXEC and MPIEXEC_NUMPROC_FLAG
|
||||
# set(MFEM_MPIEXEC "mpirun" CACHE STRING "Command for running MPI tests")
|
||||
|
||||
+21
-1
@@ -42,6 +42,9 @@ STATIC = YES
|
||||
SHARED = NO
|
||||
|
||||
# CUDA configuration options
|
||||
#
|
||||
# If you set MFEM_USE_ENZYME=YES, CUDA_CXX has to be configured to use cuda with
|
||||
# clang as its host compiler.
|
||||
CUDA_CXX = nvcc
|
||||
CUDA_ARCH = sm_60
|
||||
CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
|
||||
@@ -163,6 +166,7 @@ MFEM_USE_ADFORWARD = NO
|
||||
MFEM_USE_CODIPACK = NO
|
||||
MFEM_USE_BENCHMARK = NO
|
||||
MFEM_USE_PARELAG = NO
|
||||
MFEM_USE_ENZYME = NO
|
||||
|
||||
# MPI library compile and link flags
|
||||
# These settings are used only when building MFEM with MPI + HIP
|
||||
@@ -203,7 +207,7 @@ HYPRE_OPT = -I$(HYPRE_DIR)/include
|
||||
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
|
||||
ifeq (YES,$(MFEM_USE_CUDA))
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
HYPRE_LIB += -lcusparse -lcurand
|
||||
HYPRE_LIB += -lcusparse -lcurand -lcublas
|
||||
endif
|
||||
ifeq (YES,$(MFEM_USE_HIP))
|
||||
# This is only necessary when hypre is built with hip:
|
||||
@@ -520,6 +524,22 @@ PARELAG_DIR = @MFEM_DIR@/../parelag
|
||||
PARELAG_OPT = -I$(PARELAG_DIR)/src -I$(PARELAG_DIR)/build/src
|
||||
PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
# If you want to enable automatic differentiation at compile time, use the
|
||||
# options below, adapted to your configuration. To be more flexible, we
|
||||
# recommend using the Enzyme plugin during link time optimization. One option is
|
||||
# to add your options to the global compiler/linker flags like
|
||||
#
|
||||
# BASE_FLAGS += -flto
|
||||
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
|
||||
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
|
||||
#
|
||||
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
|
||||
ENZYME_VERSION ?= 14
|
||||
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
|
||||
ENZYME_LIB = ""
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
|
||||
+19
-22
@@ -1,30 +1,27 @@
|
||||
FROM ghcr.io/rse-ops/cuda-ubuntu-20.04:cuda-11.0.3
|
||||
FROM ghcr.io/mfem/mfem-ubuntu-base:latest as builder
|
||||
|
||||
# docker build -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
# docker build -t ghcr.io/mfem/mfem-ubuntu .
|
||||
|
||||
COPY ./config/docker/spack.yaml /opt/mfem-env/spack.yaml
|
||||
RUN apt-get install -y python3 && \
|
||||
cd /opt/mfem-env && \
|
||||
. /opt/spack/share/spack/setup-env.sh && \
|
||||
spack env activate . && \
|
||||
spack env view regenerate
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
COPY --from=builder /opt/view /opt/view
|
||||
COPY --from=builder /opt/mfem-view /opt/mfem-view
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y unzip gfortran && \
|
||||
spack compiler find && \
|
||||
apt-get install -y libcurl4-openssl-dev libssl-dev
|
||||
|
||||
# /code is the working directory for code
|
||||
WORKDIR /code
|
||||
COPY . /code
|
||||
|
||||
# This is for a spack environment/view to install from there
|
||||
WORKDIR /opt/mfem-env
|
||||
RUN . /opt/spack/share/spack/setup-env.sh && \
|
||||
spack env create -d . && \
|
||||
echo " concretization: together" >> spack.yaml && \
|
||||
spack env activate . && \
|
||||
spack develop --path /code mfem@master+examples+miniapps && \
|
||||
spack add mfem@master+examples+miniapps && \
|
||||
spack install
|
||||
|
||||
# ensure mfem always on various paths
|
||||
RUN cd /opt/mfem-env && \
|
||||
spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
|
||||
ENV PATH=$PATH:/opt/mfem-view/bin
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/mfem-view/lib:/opt/mfem-view/lib64
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# The user will see the view on shell into the container
|
||||
WORKDIR /opt/mfem-env/.spack-env/view/
|
||||
ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
|
||||
WORKDIR /opt/mfem-view
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
FROM ghcr.io/rse-ops/cuda-ubuntu-20.04:cuda-11.0.3
|
||||
|
||||
# docker build -f Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y unzip gfortran && \
|
||||
spack compiler find && \
|
||||
apt-get install -y libcurl4-openssl-dev libssl-dev
|
||||
|
||||
# /code is the working directory for code
|
||||
WORKDIR /code
|
||||
COPY . /code
|
||||
|
||||
# This is for a spack environment/view to install from there
|
||||
RUN mkdir -p /opt/mfem-env \
|
||||
&& (echo "spack:" \
|
||||
&& echo " view:" \
|
||||
&& echo " mfem:" \
|
||||
&& echo " root: /opt/mfem-view" \
|
||||
&& echo " link_type: copy" \
|
||||
&& echo " packages:" \
|
||||
&& echo " all:" \
|
||||
&& echo " target:" \
|
||||
&& echo " - x86_64_v3" \
|
||||
&& echo " config:" \
|
||||
&& echo " concretizer: clingo" \
|
||||
&& echo " compiler:" \
|
||||
&& echo " target:" \
|
||||
&& echo " - x86_64_v3" \
|
||||
&& echo " install_missing_compilers: true" \
|
||||
&& echo " concretization: together") > /opt/mfem-env/spack.yaml
|
||||
|
||||
RUN cd /opt/mfem-env && \
|
||||
. /opt/spack/share/spack/setup-env.sh && \
|
||||
spack env activate . && \
|
||||
spack develop --path /code mfem@master+examples+miniapps && \
|
||||
spack add mfem@master+examples+miniapps # && \
|
||||
# spack install
|
||||
|
||||
# ensure mfem always on various paths
|
||||
#RUN cd /opt/mfem-env && \
|
||||
# spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
|
||||
|
||||
# Present the software install when we shell in
|
||||
# The view is at /opt/mfem-env/.spack-env/view
|
||||
#WORKDIR /opt/software
|
||||
#ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
|
||||
+24
-7
@@ -1,7 +1,8 @@
|
||||
# mfem Docker
|
||||
|
||||
We provide a [Dockerfile](Dockerfile) to build an ubuntu base image. You can use
|
||||
this image for a demo of using mfem! 🎉️
|
||||
We provide a [Dockerfile.base](Dockerfile.base) to build an ubuntu base image,
|
||||
and a [Dockerfile](Dockerfile) to build a smaller one with a multi-stage build.
|
||||
You can use this image for a demo of using mfem! 🎉️
|
||||
|
||||
Updated containers are built and deployed on merges to the main branch and releases.
|
||||
If you want to request a build on demand, you can [manually run the workflow](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) thanks to the workflow dispatch event.
|
||||
@@ -14,18 +15,33 @@ is the [GitHub packages](https://github.com/features/packages) registry that sup
|
||||
Docker images and other OCI artifacts. From the root of the repository:
|
||||
|
||||
```bash
|
||||
$ docker build -f config/docker/Dockerfile -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
$ docker build -f config/docker/Dockerfile -t ghcr.io/mfem/mfem-ubuntu .
|
||||
$ docker build -f config/docker/Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
```
|
||||
|
||||
or this directory:
|
||||
### Shell Ubuntu
|
||||
|
||||
To shell into the container:
|
||||
|
||||
```bash
|
||||
$ docker build -f Dockerfile -t ghcr.io/mfem/mfem-ubuntu-base ../../
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu
|
||||
```
|
||||
|
||||
### Shell
|
||||
This smaller image has a view where everything is installed.
|
||||
|
||||
To shell into a container (here is an example with ubuntu):
|
||||
```bash
|
||||
$ ls
|
||||
bin etc include lib libexec sbin share var
|
||||
```
|
||||
|
||||
- Examples are in share/mfem/examples
|
||||
- Examples are in share/mfem/miniapps
|
||||
|
||||
You can read more about interaction with these examples and miniapps below.
|
||||
|
||||
### Shell Ubuntu Base
|
||||
|
||||
To shell into the container:
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base bash
|
||||
@@ -128,3 +144,4 @@ $ docker run -it ghcr.io/mfem/mfem-ubuntu-base -v $PWD:/src bash
|
||||
In the above, we can pretend your project is in the present working directory (PWD) and we are
|
||||
binding to source. You can then use the mfem in the container for development, and if you
|
||||
want to distribute your library or app in a container, you can use the mfem container as the base.
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
spack:
|
||||
specs: [mfem@master+examples+miniapps]
|
||||
view:
|
||||
mfem:
|
||||
root: /opt/mfem-view
|
||||
link_type: copy
|
||||
concretization: together
|
||||
develop:
|
||||
mfem:
|
||||
path: /code
|
||||
spec: mfem@master+examples+miniapps
|
||||
@@ -1,86 +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
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
8
|
||||
1 5 0 1 10 9 3 4 13 12
|
||||
1 5 1 2 11 10 4 5 14 13
|
||||
1 5 3 4 13 12 6 7 16 15
|
||||
1 5 4 5 14 13 7 8 17 16
|
||||
1 5 9 10 19 18 12 13 22 21
|
||||
1 5 10 11 20 19 13 14 23 22
|
||||
1 5 12 13 22 21 15 16 25 24
|
||||
1 5 13 14 23 22 16 17 26 25
|
||||
|
||||
#
|
||||
|
||||
boundary
|
||||
24
|
||||
1 3 1 0 9 10
|
||||
1 3 2 1 10 11
|
||||
1 3 10 9 18 19
|
||||
1 3 11 10 19 20
|
||||
2 3 0 3 12 9
|
||||
2 3 9 12 21 18
|
||||
2 3 3 6 15 12
|
||||
2 3 12 15 24 21
|
||||
3 3 0 1 4 3
|
||||
3 3 1 2 5 4
|
||||
3 3 3 4 7 6
|
||||
3 3 4 5 8 7
|
||||
4 3 6 7 16 15
|
||||
4 3 7 8 17 16
|
||||
4 3 15 16 25 24
|
||||
4 3 16 17 26 25
|
||||
5 3 18 21 22 19
|
||||
5 3 19 22 23 20
|
||||
5 3 21 24 25 22
|
||||
5 3 22 25 26 23
|
||||
6 3 2 11 14 5
|
||||
6 3 11 20 23 14
|
||||
6 3 5 14 17 8
|
||||
6 3 14 23 26 17
|
||||
|
||||
vertices
|
||||
27
|
||||
3
|
||||
0.0 0.0 0.0
|
||||
0.5 0.0 0.0
|
||||
1.0 0.0 0.0
|
||||
0.0 0.0 0.5
|
||||
0.5 0.0 0.5
|
||||
1.0 0.0 0.5
|
||||
0.0 0.0 1.0
|
||||
0.5 0.0 1.0
|
||||
1.0 0.0 1.0
|
||||
0.0 0.5 0.0
|
||||
0.5 0.5 0.0
|
||||
1.0 0.5 0.0
|
||||
0.0 0.5 0.5
|
||||
0.5 0.5 0.5
|
||||
1.0 0.5 0.5
|
||||
0.0 0.5 1.0
|
||||
0.5 0.5 1.0
|
||||
1.0 0.5 1.0
|
||||
0.0 1.0 0.0
|
||||
0.5 1.0 0.0
|
||||
1.0 1.0 0.0
|
||||
0.0 1.0 0.5
|
||||
0.5 1.0 0.5
|
||||
1.0 1.0 0.5
|
||||
0.0 1.0 1.0
|
||||
0.5 1.0 1.0
|
||||
1.0 1.0 1.0
|
||||
@@ -1,84 +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
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
8
|
||||
1 5 0 1 4 3 9 10 13 12
|
||||
1 5 1 2 5 4 10 11 14 13
|
||||
1 5 9 10 13 12 18 19 22 21
|
||||
1 5 10 11 14 13 19 20 23 22
|
||||
1 5 3 4 7 6 12 13 16 15
|
||||
1 5 4 5 8 7 13 14 17 16
|
||||
1 5 12 13 16 15 21 22 25 24
|
||||
1 5 13 14 17 16 22 23 26 25
|
||||
|
||||
boundary
|
||||
24
|
||||
1 3 0 1 10 9
|
||||
1 3 1 2 11 10
|
||||
1 3 9 10 19 18
|
||||
1 3 10 11 20 19
|
||||
3 3 0 3 4 1
|
||||
3 3 1 4 5 2
|
||||
3 3 3 6 7 4
|
||||
3 3 4 7 8 5
|
||||
3 3 18 19 22 21
|
||||
3 3 19 20 23 22
|
||||
3 3 21 22 25 24
|
||||
3 3 22 23 26 25
|
||||
3 3 2 5 14 11
|
||||
3 3 11 14 23 20
|
||||
3 3 5 8 17 14
|
||||
3 3 14 17 26 23
|
||||
3 3 0 9 12 3
|
||||
3 3 9 18 21 12
|
||||
3 3 3 12 15 6
|
||||
3 3 12 21 24 15
|
||||
2 3 6 15 16 7
|
||||
2 3 7 16 17 8
|
||||
2 3 15 24 25 16
|
||||
2 3 16 25 26 17
|
||||
|
||||
vertices
|
||||
27
|
||||
3
|
||||
0.0 0.0 0.0
|
||||
0.5 0.0 0.0
|
||||
1.0 0.0 0.0
|
||||
0.0 0.0 0.5
|
||||
0.5 0.0 0.5
|
||||
1.0 0.0 0.5
|
||||
0.0 0.0 1.0
|
||||
0.5 0.0 1.0
|
||||
1.0 0.0 1.0
|
||||
0.0 0.5 0.0
|
||||
0.5 0.5 0.0
|
||||
1.0 0.5 0.0
|
||||
0.0 0.5 0.5
|
||||
0.5 0.5 0.5
|
||||
1.0 0.5 0.5
|
||||
0.0 0.5 1.0
|
||||
0.5 0.5 1.0
|
||||
1.0 0.5 1.0
|
||||
0.0 1.0 0.0
|
||||
0.5 1.0 0.0
|
||||
1.0 1.0 0.0
|
||||
0.0 1.0 0.5
|
||||
0.5 1.0 0.5
|
||||
1.0 1.0 0.5
|
||||
0.0 1.0 1.0
|
||||
0.5 1.0 1.0
|
||||
1.0 1.0 1.0
|
||||
@@ -0,0 +1,310 @@
|
||||
// Example run: ./FOSLS2D_maxwell -ref 4 -o 3 -sol 1 -k 3.0
|
||||
|
||||
// ∇ × E - ω H = 0
|
||||
// -ω E + ∇ × H = J
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// | | E | H | RHS |
|
||||
// --------------------------------------------------------------------------
|
||||
// | F | (∇ × E,∇ × F)+ ω^2 (E,F) | - ω (∇ × H,F) - ω (H,curF) | - ω (J,F) |
|
||||
// | | | | |
|
||||
// | G |-ω (E,∇ × G)-ω (∇ × E,G) | (∇ × H,∇ × G)+ ω^2(H,G) | (J,∇ × G) |
|
||||
|
||||
// for E in H1 (scalar) we have ∇ × E = [0 1;-1 0] ∇ E
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Define exact solution
|
||||
double E_exact(const Vector &x);
|
||||
void H_exact(const Vector &x, Vector &H);
|
||||
double frhs(const Vector &x);
|
||||
void fvrhs(const Vector &x, Vector &f);
|
||||
void get_maxwell_solution(const Vector &x, double & E, Vector & curlE, double & curl2E);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
int isol = 0;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
StopWatch chrono;
|
||||
|
||||
// 1. Parse command-line options.
|
||||
// geometry file
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
// finite element order of approximation
|
||||
int order = 1;
|
||||
// visualization flag
|
||||
bool visualization = 1;
|
||||
int ref = 1;
|
||||
// number of wavelengths
|
||||
double k = 0.6;
|
||||
// optional command line inputs
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&ref, "-ref", "--init-refinements",
|
||||
"Number of initial mesh refinements");
|
||||
args.AddOption(&k, "-k", "--wavelengths",
|
||||
"Number of wavelengths.");
|
||||
args.AddOption(&isol, "-sol", "--solution",
|
||||
"Exact Solution: 0) Polynomial, 1) Sinusoidal.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
omega = 2.0 * M_PI * k;
|
||||
|
||||
// Mesh mesh(1, 1, Element::QUADRILATERAL, true, 1.0, 1.0, false);
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
dim = mesh.Dimension();
|
||||
if (dim == 3) {MFEM_ABORT("This is 2D Maxwell")};
|
||||
|
||||
for (int i = 0; i < ref; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
H1_FECollection H1fec(order,dim);
|
||||
FiniteElementSpace H1fes(&mesh, &H1fec);
|
||||
|
||||
ND_FECollection NDfec(order, dim);
|
||||
FiniteElementSpace NDfes(&mesh, &NDfec);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
// Essential BC on E. Nothing on H
|
||||
H1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
Array<int> block_offsets(3);
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = H1fes.GetVSize();
|
||||
block_offsets[2] = NDfes.GetVSize();
|
||||
block_offsets.PartialSum();
|
||||
|
||||
BlockVector x(block_offsets), b(block_offsets);
|
||||
x = 0.0;
|
||||
b = 0.0;
|
||||
|
||||
FunctionCoefficient Eex(E_exact);
|
||||
VectorFunctionCoefficient Hex(dim, H_exact);
|
||||
GridFunction E_gf;
|
||||
GridFunction H_gf;
|
||||
E_gf.MakeRef(&H1fes, x.GetBlock(0));
|
||||
E_gf.ProjectBdrCoefficient(Eex,ess_bdr);
|
||||
H_gf.MakeRef(&NDfes, x.GetBlock(1));
|
||||
|
||||
FunctionCoefficient f(frhs);
|
||||
ProductCoefficient f_E(-omega, f);
|
||||
VectorFunctionCoefficient f_H(1,fvrhs);
|
||||
LinearForm b_E;
|
||||
b_E.Update(&H1fes, b.GetBlock(0), 0);
|
||||
b_E.AddDomainIntegrator(new DomainLFIntegrator(f_E));
|
||||
b_E.Assemble();
|
||||
|
||||
LinearForm b_H;
|
||||
b_H.Update(&NDfes, b.GetBlock(1), 0);
|
||||
b_H.AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(f_H));
|
||||
b_H.Assemble();
|
||||
|
||||
// 7. Bilinear form a(.,.) on the finite element space
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient omeg2(pow(omega, 2));
|
||||
ConstantCoefficient negomega(-(omega));
|
||||
DenseMatrix mat(2);
|
||||
mat(0,0) = 0.; mat(0,1) = 1.;
|
||||
mat(1,0) = -1.; mat(1,1) = 0.;
|
||||
MatrixConstantCoefficient rot(mat);
|
||||
|
||||
BilinearForm a_EE(&H1fes);
|
||||
a_EE.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a_EE.AddDomainIntegrator(new MassIntegrator(omeg2));
|
||||
a_EE.Assemble();
|
||||
a_EE.EliminateEssentialBC(ess_bdr, x.GetBlock(0), b.GetBlock(0));
|
||||
a_EE.Finalize();
|
||||
SparseMatrix &A_EE = a_EE.SpMat();
|
||||
|
||||
ScalarMatrixProductCoefficient c1(-omega, rot);
|
||||
MixedBilinearForm a_EH(&H1fes,&NDfes);
|
||||
// - omega (rot grad E, G) - (omega E, curl G)
|
||||
a_EH.AddDomainIntegrator(new MixedVectorGradientIntegrator(c1));
|
||||
a_EH.AddDomainIntegrator(new MixedScalarWeakCurlIntegrator(negomega));
|
||||
a_EH.Assemble();
|
||||
a_EH.EliminateTrialDofs(ess_bdr, x.GetBlock(0), b.GetBlock(1));
|
||||
a_EH.Finalize();
|
||||
SparseMatrix &A_EH = a_EH.SpMat();
|
||||
SparseMatrix * A_HE = Transpose(A_EH);
|
||||
|
||||
BilinearForm a_HH(&NDfes);
|
||||
a_HH.AddDomainIntegrator(new CurlCurlIntegrator(one)); // one is the coeff
|
||||
a_HH.AddDomainIntegrator(new VectorFEMassIntegrator(omeg2)); // one is the coeff
|
||||
a_HH.Assemble();
|
||||
a_HH.Finalize();
|
||||
SparseMatrix &A_HH = a_HH.SpMat();
|
||||
|
||||
BlockMatrix LS_Maxwellop(block_offsets);
|
||||
LS_Maxwellop.SetBlock(0, 0, &A_EE);
|
||||
LS_Maxwellop.SetBlock(0, 1, A_HE);
|
||||
LS_Maxwellop.SetBlock(1, 0, &A_EH);
|
||||
LS_Maxwellop.SetBlock(1, 1, &A_HH);
|
||||
|
||||
UMFPackSolver invE;
|
||||
invE.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
invE.SetOperator(LS_Maxwellop.GetBlock(0,0));
|
||||
|
||||
UMFPackSolver invH;
|
||||
invH.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
invH.SetOperator(LS_Maxwellop.GetBlock(1,1));
|
||||
|
||||
BlockDiagonalPreconditioner prec(block_offsets);
|
||||
prec.SetDiagonalBlock(0, &invE);
|
||||
prec.SetDiagonalBlock(1, &invH);
|
||||
|
||||
int maxit(5000);
|
||||
double rtol(1.e-16);
|
||||
double atol(0.0);
|
||||
|
||||
CGSolver pcg;
|
||||
pcg.SetAbsTol(atol);
|
||||
pcg.SetRelTol(rtol);
|
||||
pcg.SetMaxIter(maxit);
|
||||
pcg.SetOperator(LS_Maxwellop);
|
||||
pcg.SetPreconditioner(prec);
|
||||
pcg.SetPrintLevel(3);
|
||||
pcg.Mult(b, x);
|
||||
|
||||
int order_quad = max(2, 2 * order + 1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i = 0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
double Error_E = E_gf.ComputeL2Error(Eex, irs);
|
||||
double Error_H = H_gf.ComputeL2Error(Hex, irs);
|
||||
|
||||
cout << "|| E_h - E || = " << Error_E << "\n";
|
||||
cout << "|| H_h - H || = " << Error_H << "\n";
|
||||
cout << "Total error = " << sqrt(Error_H*Error_H+Error_E*Error_E) << "\n";
|
||||
|
||||
GridFunction E_exgf(&H1fes);
|
||||
E_exgf.ProjectCoefficient(Eex);
|
||||
|
||||
GridFunction H_exgf(&NDfes);
|
||||
H_exgf.ProjectCoefficient(Hex);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
socketstream ex_sock(vishost, visport);
|
||||
ex_sock.precision(8);
|
||||
socketstream sol_sockH(vishost, visport);
|
||||
sol_sockH.precision(8);
|
||||
socketstream ex_sockH(vishost, visport);
|
||||
ex_sockH.precision(8);
|
||||
sol_sock << "solution\n"
|
||||
<< mesh << E_gf << "window_title 'Numerical E'" << "keys rRljc\n"
|
||||
<< flush;
|
||||
ex_sock << "solution\n"
|
||||
<< mesh << E_exgf << "window_title 'Exact E'" << "keys rRljc\n"
|
||||
<< flush;
|
||||
sol_sockH << "solution\n"
|
||||
<< mesh << H_gf << "window_title 'Numerical H'" << "keys rRljc\n"
|
||||
<< flush;
|
||||
ex_sockH << "solution\n"
|
||||
<< mesh << H_exgf << "window_title 'Exact H'" << "keys rRljc\n"
|
||||
<< flush;
|
||||
}
|
||||
delete A_HE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
double E_exact(const Vector &x)
|
||||
{
|
||||
double E, curl2E;
|
||||
Vector curlE(2);
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
return E; //Scalar
|
||||
}
|
||||
|
||||
//define exact solution
|
||||
void H_exact(const Vector &x, Vector &H)
|
||||
{
|
||||
double E, curl2E;
|
||||
Vector curlE(2);
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
H[0] = curlE[0]/omega;
|
||||
H[1] = curlE[1]/omega;
|
||||
}
|
||||
|
||||
double frhs(const Vector &x)
|
||||
{
|
||||
double E, curl2E;
|
||||
Vector curlE(2);
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
|
||||
// - omega E + curl H = f
|
||||
// - omega E + curl (curl E) / omega = f
|
||||
double f = - omega * E + curl2E / omega;
|
||||
return f;
|
||||
}
|
||||
|
||||
void fvrhs(const Vector &x, Vector &f)
|
||||
{
|
||||
double E, curl2E;
|
||||
Vector curlE(2);
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
f[0] = - omega * E + curl2E / omega;
|
||||
}
|
||||
|
||||
|
||||
void get_maxwell_solution(const Vector &X, double & E, Vector & curlE, double & curl2E)
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double Ex, Ey, Exx, Eyy;
|
||||
if (isol == 0) // polynomial
|
||||
{
|
||||
E = x * (1.0 - x) * y * (1.0 - y);
|
||||
Ex = (1.0 - 2.0 * x) * y * (1.0 - y);
|
||||
Ey = x * (1.0 - x) * (1.0 - 2.0 * y);
|
||||
|
||||
Exx = -2.0 * y * (1.0 - y);
|
||||
Eyy = -2.0 * x * (1.0 - x);
|
||||
}
|
||||
else
|
||||
{
|
||||
double s = omega * (y+x);
|
||||
E = cos(s);
|
||||
Ex = -omega * sin(s);
|
||||
Ey = Ex;
|
||||
Exx = - omega * omega * E;
|
||||
Eyy = Exx;
|
||||
}
|
||||
curlE[0] = Ey;
|
||||
curlE[1] = -Ex;
|
||||
curl2E = -Exx - Eyy;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/EM-diffusion,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = primal_dpg
|
||||
|
||||
PAR_EXAMPLES =
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
rm -rf ParaView
|
||||
|
||||
clean-exec:
|
||||
@@ -0,0 +1,201 @@
|
||||
// MFEM primal_dpg example
|
||||
//
|
||||
// Compile with: make primal_dpg
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
void E_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
int dim;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command line options
|
||||
const char *mesh_file = "../../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
int ref = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
|
||||
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(&ref, "-ref", "--refinements",
|
||||
"Number of refinements.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 2. Read the mesh from the given mesh file, and refine once uniformly.
|
||||
Mesh mesh(mesh_file);
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
// 3. Define a finite element space on the mesh. Here we use H1 continuous
|
||||
// high-order Lagrange finite elements of the given order.
|
||||
ND_FECollection fec(order, mesh.Dimension());
|
||||
FiniteElementSpace NDfes(&mesh, &fec);
|
||||
|
||||
FiniteElementCollection * trace_fec = nullptr;
|
||||
if (dim == 3)
|
||||
{
|
||||
trace_fec = new ND_Trace_FECollection(order,mesh.Dimension());
|
||||
}
|
||||
else
|
||||
{
|
||||
trace_fec = new H1_Trace_FECollection(order,mesh.Dimension());
|
||||
}
|
||||
FiniteElementSpace trace_fes(&mesh, trace_fec);
|
||||
|
||||
int test_order = order+1;
|
||||
|
||||
ND_FECollection test_fec(test_order,mesh.Dimension());
|
||||
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fecs;
|
||||
|
||||
trial_fes.Append(&NDfes);
|
||||
trial_fes.Append(&trace_fes);
|
||||
test_fecs.Append(&test_fec);
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
|
||||
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
a->AddTrialIntegrator(new CurlCurlIntegrator(one),0,0);
|
||||
a->AddTrialIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
a->AddTrialIntegrator(new TangentTraceIntegrator,1,0);
|
||||
|
||||
a->AddTestIntegrator(new CurlCurlIntegrator(one),0,0);
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f),0);
|
||||
|
||||
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
NDfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
|
||||
Vector X,B;
|
||||
OperatorPtr Ah;
|
||||
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = NDfes.GetVSize();
|
||||
offsets[2] = trace_fes.GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets);
|
||||
x = 0.;
|
||||
|
||||
|
||||
GridFunction E_gf(&NDfes);
|
||||
E_gf.MakeRef(&NDfes,x.GetBlock(0));
|
||||
E_gf.ProjectBdrCoefficientTangent(E,ess_bdr);
|
||||
E_gf.ProjectCoefficient(E);
|
||||
|
||||
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
|
||||
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
|
||||
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
GMRESSolver cg;
|
||||
cg.SetRelTol(1e-8);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
E_gf.MakeRef(&NDfes,x.GetData());
|
||||
|
||||
double L2Error = E_gf.ComputeL2Error(E);
|
||||
mfem::out << "L2_error = " << L2Error << endl;
|
||||
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << E_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
delete trace_fec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
// MFEM Ultraweak DPG acoustics example
|
||||
//
|
||||
// Compile with: make uw_dpg
|
||||
//
|
||||
|
||||
// - Δ p - ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ p + i ω u = 0, in Ω
|
||||
// ∇⋅u + i ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/(i ω)
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
|
||||
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
|
||||
// -(p, ∇⋅v) + i ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
|
||||
// -(u , ∇ q) + i ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
|
||||
// p̂ = p_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// p̂ := p on Γ_h (skeleton)
|
||||
// û := u on Γ_h
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | p | u | p̂ | û | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v | -(p, ∇⋅v) | i ω (u,v) | < p̂, v⋅n> | | |
|
||||
// | | | | | | |
|
||||
// | q | i ω (p,q) |-(u , ∇ q) | | < û,q > | (f,q) |
|
||||
|
||||
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void acoustics_solution(const Vector & X, complex<double> & p,
|
||||
vector<complex<double>> &dp, complex<double> & d2p);
|
||||
|
||||
void acoustics_solution_r(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p);
|
||||
|
||||
void acoustics_solution_i(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p);
|
||||
|
||||
double p_exact_r(const Vector &x);
|
||||
double p_exact_i(const Vector &x);
|
||||
void u_exact_r(const Vector &x, Vector & u);
|
||||
void u_exact_i(const Vector &x, Vector & u);
|
||||
double rhs_func_r(const Vector &x);
|
||||
double rhs_func_i(const Vector &x);
|
||||
void gradp_exact_r(const Vector &x, Vector &gradu);
|
||||
void gradp_exact_i(const Vector &x, Vector &gradu);
|
||||
double divu_exact_r(const Vector &x);
|
||||
double divu_exact_i(const Vector &x);
|
||||
double d2_exact_r(const Vector &x);
|
||||
double d2_exact_i(const Vector &x);
|
||||
double hatp_exact_r(const Vector & X);
|
||||
double hatp_exact_i(const Vector & X);
|
||||
void hatu_exact(const Vector & X, Vector & hatu);
|
||||
void hatu_exact_r(const Vector & X, Vector & hatu);
|
||||
void hatu_exact_i(const Vector & X, Vector & hatu);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
plane_wave,
|
||||
gaussian_beam
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int ref = 1;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool static_cond = false;
|
||||
int iprob = 0;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: plane wave, 1: Gaussian beam");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&ref, "-ref", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 1) { iprob = 0; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
omega = 2.*M_PI*rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
|
||||
|
||||
// Define spaces
|
||||
// L2 space for p
|
||||
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *p_fes = new FiniteElementSpace(&mesh,p_fec);
|
||||
|
||||
// Vector L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec, dim);
|
||||
|
||||
// H^1/2 space for p̂
|
||||
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
|
||||
FiniteElementSpace *hatp_fes = new FiniteElementSpace(&mesh,hatp_fec);
|
||||
|
||||
// H^-1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
|
||||
mfem::out << "p_fes space true dofs = " << p_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "u_fes space true dofs = " << u_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "hatp_fes space true dofs = " << hatp_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "hatu_fes space true dofs = " << hatu_fes->GetTrueVSize() << endl;
|
||||
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
Vector vec0(dim); vec0 = 0.;
|
||||
VectorConstantCoefficient vzero(vec0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient omeg2(omega*omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(p_fes);
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(hatp_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
|
||||
test_fec.Append(q_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
ComplexNormalEquations * a = new ComplexNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices();
|
||||
|
||||
// i ω (p,q)
|
||||
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omeg),0,0);
|
||||
|
||||
// -(u , ∇ q)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(negone)),nullptr,1,0);
|
||||
|
||||
// -(p, ∇⋅v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),nullptr,0,1);
|
||||
|
||||
// i ω (u,v)
|
||||
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(omeg)),1,1);
|
||||
|
||||
// < p̂, v⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,nullptr,2,1);
|
||||
|
||||
// < û,q >
|
||||
a->AddTrialIntegrator(new TraceIntegrator,nullptr,3,0);
|
||||
|
||||
// for impedence condition (only on the boundary)
|
||||
// TODO
|
||||
// a->AddTrialIntegrator(new TraceIntegrator,nullptr,2,0);
|
||||
|
||||
|
||||
// test integrators
|
||||
|
||||
//space-induced norm for H(div) × H1
|
||||
// (∇q,∇δq)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
|
||||
// (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
|
||||
// (∇⋅v,∇⋅δv)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),nullptr,1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
|
||||
|
||||
// additional integrators for the adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -i ω (∇q,δv)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negomeg),0,1);
|
||||
// i ω (v,∇ δq)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorWeakDivergenceIntegrator(negomeg),1,0);
|
||||
// ω^2 (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),nullptr,1,1);
|
||||
|
||||
// - i ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(nullptr,new VectorFEDivergenceIntegrator(negomeg),1,0);
|
||||
// i ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(nullptr,new MixedScalarWeakGradientIntegrator(negomeg),0,1);
|
||||
// ω^2 (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(omeg2),nullptr,0,0);
|
||||
}
|
||||
|
||||
// RHS
|
||||
FunctionCoefficient f_rhs_r(rhs_func_r);
|
||||
FunctionCoefficient f_rhs_i(rhs_func_i);
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs_r),new DomainLFIntegrator(f_rhs_i),0);
|
||||
|
||||
|
||||
FunctionCoefficient hatpex_r(hatp_exact_r);
|
||||
FunctionCoefficient hatpex_i(hatp_exact_i);
|
||||
VectorFunctionCoefficient hatuex_r(dim,hatu_exact_r);
|
||||
VectorFunctionCoefficient hatuex_i(dim,hatu_exact_i);
|
||||
Array<int> elements_to_refine;
|
||||
|
||||
socketstream p_out_r;
|
||||
socketstream p_out_i;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
p_out_r.open(vishost, visport);
|
||||
p_out_i.open(vishost, visport);
|
||||
}
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
mfem::out << " Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------" << endl;
|
||||
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
// ess_bdr[1] = 0;
|
||||
// ess_bdr[2] = 1;
|
||||
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
// hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int j = 0; j < ess_tdof_list.Size(); j++)
|
||||
{
|
||||
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
|
||||
// + hatp_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = p_fes->GetVSize();
|
||||
offsets[2] = u_fes->GetVSize();
|
||||
offsets[3] = hatp_fes->GetVSize();
|
||||
offsets[4] = hatu_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
Vector x(2*offsets.Last());
|
||||
x = 0.;
|
||||
double * xdata = x.GetData();
|
||||
|
||||
ComplexGridFunction hatp_gf(hatp_fes);
|
||||
hatp_gf.real().MakeRef(hatp_fes,&xdata[offsets[2]]);
|
||||
hatp_gf.imag().MakeRef(hatp_fes,&xdata[offsets.Last()+ offsets[2]]);
|
||||
hatp_gf.ProjectBdrCoefficient(hatpex_r,hatpex_i, ess_bdr);
|
||||
|
||||
// ComplexGridFunction hatu_gf(hatu_fes);
|
||||
// hatu_gf.real().MakeRef(hatu_fes,&xdata[offsets[3]]);
|
||||
// hatu_gf.imag().MakeRef(hatu_fes,&xdata[offsets.Last()+ offsets[3]]);
|
||||
// hatu_gf.ProjectBdrCoefficientNormal(hatuex_r,hatuex_i, ess_bdr);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
|
||||
|
||||
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
|
||||
|
||||
SparseMatrix * Ar = dynamic_cast<BlockMatrix *>(&Ahc->real())->CreateMonolithic();
|
||||
SparseMatrix * Ai = dynamic_cast<BlockMatrix *>(&Ahc->imag())->CreateMonolithic();
|
||||
|
||||
ComplexSparseMatrix Ac(Ar,Ai,true,true);
|
||||
SparseMatrix * A = Ac.GetSystemMatrix();
|
||||
|
||||
mfem::out << "Size of the linear system: " << A->Height() << std::endl;
|
||||
|
||||
|
||||
UMFPackSolver umf(*A);
|
||||
umf.Mult(B,X);
|
||||
|
||||
delete A;
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
double max_resid = residuals.Max();
|
||||
for (int iel = 0; iel<mesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * max_resid)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ComplexGridFunction p(p_fes);
|
||||
p.real().MakeRef(p_fes,x.GetData());
|
||||
p.imag().MakeRef(p_fes,&x.GetData()[offsets.Last()]);
|
||||
|
||||
ComplexGridFunction pgf_ex(p_fes);
|
||||
FunctionCoefficient p_ex_r(p_exact_r);
|
||||
FunctionCoefficient p_ex_i(p_exact_i);
|
||||
pgf_ex.ProjectCoefficient(p_ex_r, p_ex_i);
|
||||
|
||||
int dofs = X.Size()/2;
|
||||
|
||||
double p_err_r = p.real().ComputeL2Error(p_ex_r);
|
||||
double p_err_i = p.imag().ComputeL2Error(p_ex_i);
|
||||
|
||||
double L2Error = sqrt(p_err_r*p_err_r + p_err_i*p_err_i);
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = L2Error;
|
||||
res0 = residual;
|
||||
dof0 = dofs;
|
||||
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << 0.0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::resetiosflags(std::ios::showbase)
|
||||
<< std::setw(10) << std::scientific
|
||||
<< std::endl;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
p_out_r.precision(8);
|
||||
p_out_r << "solution\n" << mesh << p.real() <<
|
||||
"window_title 'Real Numerical presure' "
|
||||
<< flush;
|
||||
|
||||
p_out_i.precision(8);
|
||||
p_out_i << "solution\n" << mesh << p.imag() <<
|
||||
"window_title 'Imag Numerical presure' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
if (i == ref)
|
||||
break;
|
||||
|
||||
mesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete q_fec;
|
||||
delete v_fec;
|
||||
delete hatp_fes;
|
||||
delete hatp_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete u_fec;
|
||||
delete p_fec;
|
||||
delete u_fes;
|
||||
delete p_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double p_exact_r(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_r(x,p,dp,d2p);
|
||||
return p;
|
||||
}
|
||||
|
||||
double p_exact_i(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_i(x,p,dp,d2p);
|
||||
return p;
|
||||
}
|
||||
|
||||
double hatp_exact_r(const Vector & X)
|
||||
{
|
||||
return p_exact_r(X);
|
||||
}
|
||||
|
||||
double hatp_exact_i(const Vector & X)
|
||||
{
|
||||
return p_exact_i(X);
|
||||
}
|
||||
|
||||
void gradp_exact_r(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
double p,d2p;
|
||||
acoustics_solution_r(x,p,grad,d2p);
|
||||
}
|
||||
|
||||
void gradp_exact_i(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
double p,d2p;
|
||||
acoustics_solution_i(x,p,grad,d2p);
|
||||
}
|
||||
|
||||
double d2_exact_r(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_r(x,p,dp,d2p);
|
||||
return d2p;
|
||||
}
|
||||
|
||||
double d2_exact_i(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_i(x,p,dp,d2p);
|
||||
return d2p;
|
||||
}
|
||||
|
||||
// u = - ∇ p / (i ω )
|
||||
// = i (∇ p_r + i * ∇ p_i) / ω
|
||||
// = - ∇ p_i / ω + i ∇ p_r / ω
|
||||
void u_exact_r(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact_i(x,u);
|
||||
u *= -1./omega;
|
||||
}
|
||||
|
||||
void u_exact_i(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact_r(x,u);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
void hatu_exact_r(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact_r(X,hatu);
|
||||
}
|
||||
void hatu_exact_i(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact_i(X,hatu);
|
||||
}
|
||||
|
||||
// ∇⋅u = i Δ p / ω
|
||||
// = i (Δ p_r + i * Δ p_i) / ω
|
||||
// = - Δ p_i / ω + i Δ p_r / ω
|
||||
|
||||
double divu_exact_r(const Vector &x)
|
||||
{
|
||||
return -d2_exact_i(x)/omega;
|
||||
}
|
||||
|
||||
double divu_exact_i(const Vector &x)
|
||||
{
|
||||
return d2_exact_r(x)/omega;
|
||||
}
|
||||
|
||||
// f = ∇⋅u + i ω p
|
||||
// f_r = ∇⋅u_r - ω p_i
|
||||
double rhs_func_r(const Vector &x)
|
||||
{
|
||||
double p = p_exact_i(x);
|
||||
double divu = divu_exact_r(x);
|
||||
return divu - omega * p;
|
||||
}
|
||||
|
||||
// f_i = ∇⋅u_i + ω p_r
|
||||
double rhs_func_i(const Vector &x)
|
||||
{
|
||||
double p = p_exact_r(x);
|
||||
double divu = divu_exact_i(x);
|
||||
return divu + omega * p;
|
||||
}
|
||||
|
||||
|
||||
void acoustics_solution_r(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p)
|
||||
{
|
||||
complex<double> zp, d2zp;
|
||||
vector<complex<double>> dzp;
|
||||
acoustics_solution(X,zp,dzp,d2zp);
|
||||
p = zp.real();
|
||||
d2p = d2zp.real();
|
||||
dp.SetSize(X.Size());
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = dzp[i].real();
|
||||
}
|
||||
}
|
||||
|
||||
void acoustics_solution_i(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p)
|
||||
{
|
||||
complex<double> zp, d2zp;
|
||||
vector<complex<double>> dzp;
|
||||
acoustics_solution(X,zp,dzp,d2zp);
|
||||
p = zp.imag();
|
||||
d2p = d2zp.imag();
|
||||
dp.SetSize(X.Size());
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = dzp[i].imag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void acoustics_solution(const Vector & X, complex<double> & p, vector<complex<double>> & dp,
|
||||
complex<double> & d2p)
|
||||
{
|
||||
dp.resize(X.Size());
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
switch (prob)
|
||||
{
|
||||
case plane_wave:
|
||||
{
|
||||
double beta = omega/std::sqrt((double)X.Size());
|
||||
complex<double> alpha = beta * zi * X.Sum();
|
||||
p = exp(-alpha);
|
||||
d2p = - dim * beta * beta * p;
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = - zi * beta * p;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double rk = omega;
|
||||
double alpha = 45 * M_PI/180.;
|
||||
double sina = sin(alpha);
|
||||
double cosa = cos(alpha);
|
||||
// shift the origin
|
||||
double xprim=X(0) + 0.1;
|
||||
double yprim=X(1) + 0.1;
|
||||
|
||||
double x = xprim*sina - yprim*cosa;
|
||||
double y = xprim*cosa + yprim*sina;
|
||||
double dxdxprim = sina, dxdyprim = -cosa;
|
||||
double dydxprim = cosa, dydyprim = sina;
|
||||
//wavelength
|
||||
double rl = 2.*M_PI/rk;
|
||||
|
||||
// beam waist radius
|
||||
double w0 = 0.05;
|
||||
|
||||
// function w
|
||||
double fact = rl/M_PI/(w0*w0);
|
||||
double aux = 1. + (fact*y)*(fact*y);
|
||||
|
||||
double w = w0*sqrt(aux);
|
||||
double dwdy = w0*fact*fact*y/sqrt(aux);
|
||||
double d2wdydy = w0*fact*fact*(1. - (fact*y)*(fact*y)/aux)/sqrt(aux);
|
||||
|
||||
double phi0 = atan(fact*y);
|
||||
double dphi0dy = cos(phi0)*cos(phi0)*fact;
|
||||
double d2phi0dydy = -2.*cos(phi0)*sin(phi0)*fact*dphi0dy;
|
||||
|
||||
double r = y + 1./y/(fact*fact);
|
||||
double drdy = 1. - 1./(y*y)/(fact*fact);
|
||||
double d2rdydy = 2./(y*y*y)/(fact*fact);
|
||||
|
||||
// pressure
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r + zi*phi0/2.;
|
||||
|
||||
complex<double> zdedx = -2.*x/(w*w) - 2.*zi*M_PI*x/rl/r;
|
||||
complex<double> zdedy = 2.*x*x/(w*w*w)*dwdy - zi*rk + zi*M_PI*x*x/rl/(r*r)*drdy + zi*dphi0dy/2.;
|
||||
complex<double> zd2edxdx = -2./(w*w) - 2.*zi*M_PI/rl/r;
|
||||
complex<double> zd2edxdy = 4.*x/(w*w*w)*dwdy + 2.*zi*M_PI*x/rl/(r*r)*drdy;
|
||||
complex<double> zd2edydx = zd2edxdy;
|
||||
complex<double> zd2edydy = -6.*x*x/(w*w*w*w)*dwdy*dwdy + 2.*x*x/(w*w*w)*d2wdydy - 2.*zi*M_PI*x*x/rl/(r*r*r)*drdy*drdy
|
||||
+ zi*M_PI*x*x/rl/(r*r)*d2rdydy + zi/2.*d2phi0dydy;
|
||||
|
||||
double pf = pow(2.0/M_PI/(w*w),0.25);
|
||||
double dpfdy = -pow(2./M_PI/(w*w),-0.75)/M_PI/(w*w*w)*dwdy;
|
||||
double d2pfdydy = -1./M_PI*pow(2./M_PI,-0.75)*(-1.5*pow(w,-2.5)
|
||||
*dwdy*dwdy + pow(w,-1.5)*d2wdydy);
|
||||
|
||||
|
||||
complex<double> zp = pf*exp(ze);
|
||||
complex<double> zdpdx = zp*zdedx;
|
||||
complex<double> zdpdy = dpfdy*exp(ze)+zp*zdedy;
|
||||
complex<double> zd2pdxdx = zdpdx*zdedx + zp*zd2edxdx;
|
||||
complex<double> zd2pdxdy = zdpdy*zdedx + zp*zd2edxdy;
|
||||
complex<double> zd2pdydx = dpfdy*exp(ze)*zdedx + zdpdx*zdedy + zp*zd2edydx;
|
||||
complex<double> zd2pdydy = d2pfdydy*exp(ze) + dpfdy*exp(ze)*zdedy + zdpdy*zdedy + zp*zd2edydy;
|
||||
|
||||
p = zp;
|
||||
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim);
|
||||
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim);
|
||||
|
||||
d2p = (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
|
||||
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
// MFEM FOSLS acoustics Example
|
||||
//
|
||||
// Compile with: make fosls
|
||||
//
|
||||
// Definite/Indefinite Helmholtz
|
||||
|
||||
// - Δ p ± ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ p - ω u = 0, in Ω
|
||||
// - ∇⋅u ± ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/ω
|
||||
|
||||
// FOSLS:
|
||||
// minimize 1/2(||∇p - ω u||^2 + ||-∇⋅u ± ω p - f||^2)
|
||||
|
||||
// (p,u) ∈ H^1(Ω) × H(div,Ω)
|
||||
// -------------------------------------------------------------------
|
||||
// | | p | u | RHS |
|
||||
// -------------------------------------------------------------------
|
||||
// | q | (∇ p,∇ q) + ω^2(p,q) | ∓ ω (∇⋅u,q) - ω (u, ∇ q) | ± ω(f,q) |
|
||||
// | | | | |
|
||||
// | v | ∓ ω (p,∇⋅v) - ω (∇ p,v)| (∇⋅u,∇⋅v) + ω^2 (u,v) | -(f,∇⋅v) |
|
||||
|
||||
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// #define DEFINITE
|
||||
|
||||
double p_exact(const Vector &x);
|
||||
void u_exact(const Vector &x, Vector & u);
|
||||
double rhs_func(const Vector &x);
|
||||
void gradp_exact(const Vector &x, Vector &gradu);
|
||||
double divu_exact(const Vector &x);
|
||||
double d2_exact(const Vector &x);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int sr = 1;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&sr, "-sr", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
omega = 2.0 * M_PI * rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
|
||||
for (int i = 0; i < sr; i++ )
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
|
||||
FiniteElementCollection *RTfec = new RT_FECollection(order-1, dim);
|
||||
FiniteElementSpace * H1fes = new FiniteElementSpace(&mesh, H1fec);
|
||||
FiniteElementSpace * RTfes = new FiniteElementSpace(&mesh, RTfec);
|
||||
|
||||
Array<FiniteElementSpace *> fespaces(2);
|
||||
fespaces[0] = H1fes;
|
||||
fespaces[1] = RTfes;
|
||||
|
||||
Array<int> ess_bdr;
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespaces[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
BlockBilinearForm a(fespaces);
|
||||
a.SetDiagonalPolicy(mfem::Operator::DIAG_KEEP);
|
||||
cout << "H1 fespace = " << H1fes->GetTrueVSize() << endl;
|
||||
cout << "RT fespace = " << RTfes->GetTrueVSize() << endl;
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
ConstantCoefficient omeg2(omega*omega);
|
||||
|
||||
|
||||
Array2D<BilinearFormIntegrator * > blfi(2,2);
|
||||
|
||||
// blfi(0,0) = (∇ p,∇ q) + ω^2(p,q)
|
||||
SumIntegrator * integ00 = new SumIntegrator();
|
||||
integ00->AddIntegrator(new DiffusionIntegrator(one));
|
||||
integ00->AddIntegrator(new MassIntegrator(omeg2));
|
||||
blfi(0,0) = integ00;
|
||||
|
||||
// blfi(0,1) = ∓ ω (∇⋅u,q) - ω (u, ∇ q)
|
||||
SumIntegrator * integ01 = new SumIntegrator();
|
||||
#ifdef DEFINITE
|
||||
// -ω (∇⋅u,q)
|
||||
integ01->AddIntegrator(new MixedScalarDivergenceIntegrator(negomeg));
|
||||
#else
|
||||
// ω (∇⋅u,q)
|
||||
integ01->AddIntegrator(new MixedScalarDivergenceIntegrator(omeg));
|
||||
#endif
|
||||
// - ω (u, ∇ q)
|
||||
integ01->AddIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg));
|
||||
blfi(0,1) = integ01;
|
||||
|
||||
// blfi(1,0) = ∓ ω (p,∇⋅v) - ω (∇ p,v)
|
||||
SumIntegrator * integ10 = new SumIntegrator();
|
||||
#ifdef DEFINITE
|
||||
// - ω (p,∇⋅v)
|
||||
integ10->AddIntegrator(new MixedScalarWeakGradientIntegrator(omeg));
|
||||
#else
|
||||
// ω (p,∇⋅v)
|
||||
integ10->AddIntegrator(new MixedScalarWeakGradientIntegrator(negomeg));
|
||||
#endif
|
||||
// - ω (∇ p,v)
|
||||
integ10->AddIntegrator(new MixedVectorGradientIntegrator(negomeg));
|
||||
blfi(1,0) = integ10;
|
||||
|
||||
// blfi(1,1) = (∇⋅u,∇⋅v) + ω^2 (u,v)
|
||||
SumIntegrator * integ11 = new SumIntegrator();
|
||||
integ11->AddIntegrator(new DivDivIntegrator(one));
|
||||
integ11->AddIntegrator(new VectorFEMassIntegrator(omeg2));
|
||||
blfi(1,1) = integ11;
|
||||
|
||||
|
||||
BlockLinearForm b(fespaces);
|
||||
Array<LinearFormIntegrator * > lfi(2);
|
||||
// ± ω (f,q)
|
||||
FunctionCoefficient f_rhs(rhs_func);
|
||||
#ifdef DEFINITE
|
||||
ProductCoefficient w_f(omeg,f_rhs);
|
||||
#else
|
||||
ProductCoefficient w_f(negomeg,f_rhs);
|
||||
#endif
|
||||
// lfi[0] = new DomainLFIntegrator(w_f);
|
||||
lfi[0] = new DomainLFIntegrator(w_f);
|
||||
|
||||
|
||||
// -(f,∇⋅v)
|
||||
ProductCoefficient neg_f(negone,f_rhs);
|
||||
// lfi[1] = new VectorFEDomainLFDivIntegrator(f_rhs);
|
||||
lfi[1] = new VectorFEDomainLFDivIntegrator(neg_f);
|
||||
|
||||
TestBlockBilinearFormIntegrator * integ = new TestBlockBilinearFormIntegrator();
|
||||
integ->SetIntegrators(blfi);
|
||||
a.AddDomainIntegrator(integ);
|
||||
a.Assemble();
|
||||
|
||||
TestBlockLinearFormIntegrator * lininteg = new TestBlockLinearFormIntegrator();
|
||||
lininteg->SetIntegrators(lfi);
|
||||
b.AddDomainIntegrator(lininteg);
|
||||
b.Assemble();
|
||||
|
||||
int size = 0;
|
||||
for (int i = 0; i<fespaces.Size(); i++)
|
||||
{
|
||||
size += fespaces[i]->GetVSize();
|
||||
}
|
||||
|
||||
Vector x(size);
|
||||
x = 0.0;
|
||||
FunctionCoefficient p_ex(p_exact);
|
||||
VectorFunctionCoefficient gradp_ex(dim,gradp_exact);
|
||||
VectorFunctionCoefficient u_ex(dim,u_exact);
|
||||
FunctionCoefficient divu_ex(divu_exact);
|
||||
GridFunction p_gf, u_gf;
|
||||
GridFunction pex_gf(H1fes);
|
||||
|
||||
p_gf.MakeRef(H1fes,x,0);
|
||||
// p_gf.ProjectBdrCoefficient(p_ex,ess_bdr);
|
||||
p_gf.ProjectCoefficient(p_ex);
|
||||
pex_gf.ProjectCoefficient(p_ex);
|
||||
|
||||
u_gf.MakeRef(RTfes,x,H1fes->GetVSize());
|
||||
u_gf = 0.;
|
||||
|
||||
|
||||
OperatorPtr A;
|
||||
Vector X,B;
|
||||
a.FormLinearSystem(ess_tdof_list,x,b,A,X,B);
|
||||
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-10);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
|
||||
a.RecoverFEMSolution(X,b,x);
|
||||
|
||||
p_gf.MakeRef(H1fes,x,0);
|
||||
u_gf.MakeRef(RTfes,x,H1fes->GetVSize());
|
||||
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << p_gf <<
|
||||
"window_title 'Numerical p' "
|
||||
<< flush;
|
||||
// socketstream sols_sock(vishost, visport);
|
||||
// sols_sock.precision(8);
|
||||
// sols_sock << "solution\n" << mesh << u_gf <<
|
||||
// "window_title 'Numerical sigma' "
|
||||
// << flush;
|
||||
|
||||
socketstream solex_sock(vishost, visport);
|
||||
solex_sock.precision(8);
|
||||
solex_sock << "solution\n" << mesh << pex_gf <<
|
||||
"window_title 'Exact p' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double rhs_func(const Vector &x)
|
||||
{
|
||||
double p = p_exact(x);
|
||||
double divu = divu_exact(x);
|
||||
// f = - ∇⋅u ± ω p,
|
||||
#ifdef DEFINITE
|
||||
return -divu + omega * p;
|
||||
#else
|
||||
return -divu - omega * p;
|
||||
#endif
|
||||
}
|
||||
|
||||
double p_exact(const Vector &x)
|
||||
{
|
||||
return sin(omega*x.Sum());
|
||||
}
|
||||
|
||||
void gradp_exact(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
grad = omega * cos(omega * x.Sum());
|
||||
}
|
||||
|
||||
void u_exact(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact(x,u);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
double divu_exact(const Vector &x)
|
||||
{
|
||||
return d2_exact(x)/omega;
|
||||
}
|
||||
|
||||
double d2_exact(const Vector &x)
|
||||
{
|
||||
return -dim * omega * omega * sin(omega*x.Sum());
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/acoustics,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = fosls uw_dpg strong_dpg complex_uw_dpg
|
||||
PAR_EXAMPLES = uw_dpgp
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@@ -0,0 +1,837 @@
|
||||
// MFEM Ultraweak DPG acoustics example
|
||||
//
|
||||
// Compile with: make pcomplex_uw_dpg
|
||||
//
|
||||
// sample runs
|
||||
// ./pcomplex_uw_dpg -o 3 -m ../../../data/inline-quad.mesh -sref 2 -pref 3 -rnum 4.1 -prob 0 -sc -graph-norm
|
||||
|
||||
// - Δ p - ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ p + i ω u = 0, in Ω
|
||||
// ∇⋅u + i ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/(i ω)
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
|
||||
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
|
||||
// -(p, ∇⋅v) + i ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
|
||||
// -(u , ∇ q) + i ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
|
||||
// p̂ = p_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// p̂ := p on Γ_h (skeleton)
|
||||
// û := u on Γ_h
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | p | u | p̂ | û | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v | -(p, ∇⋅v) | i ω (u,v) | < p̂, v⋅n> | | |
|
||||
// | | | | | | |
|
||||
// | q | i ω (p,q) |-(u , ∇ q) | | < û,q > | (f,q) |
|
||||
|
||||
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void acoustics_solution(const Vector & X, complex<double> & p,
|
||||
vector<complex<double>> &dp, complex<double> & d2p);
|
||||
|
||||
void acoustics_solution_r(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p);
|
||||
|
||||
void acoustics_solution_i(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p);
|
||||
|
||||
double p_exact_r(const Vector &x);
|
||||
double p_exact_i(const Vector &x);
|
||||
void u_exact_r(const Vector &x, Vector & u);
|
||||
void u_exact_i(const Vector &x, Vector & u);
|
||||
double rhs_func_r(const Vector &x);
|
||||
double rhs_func_i(const Vector &x);
|
||||
void gradp_exact_r(const Vector &x, Vector &gradu);
|
||||
void gradp_exact_i(const Vector &x, Vector &gradu);
|
||||
double divu_exact_r(const Vector &x);
|
||||
double divu_exact_i(const Vector &x);
|
||||
double d2_exact_r(const Vector &x);
|
||||
double d2_exact_i(const Vector &x);
|
||||
double hatp_exact_r(const Vector & X);
|
||||
double hatp_exact_i(const Vector & X);
|
||||
void hatu_exact(const Vector & X, Vector & hatu);
|
||||
void hatu_exact_r(const Vector & X, Vector & hatu);
|
||||
void hatu_exact_i(const Vector & X, Vector & hatu);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
plane_wave,
|
||||
gaussian_beam
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool static_cond = false;
|
||||
int iprob = 0;
|
||||
int sr = 0;
|
||||
int pr = 1;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: plane wave, 1: Gaussian beam");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&sr, "-sref", "--serial_ref",
|
||||
"Number of parallel refinements.");
|
||||
args.AddOption(&pr, "-pref", "--parallel_ref",
|
||||
"Number of parallel refinements.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
|
||||
if (iprob > 1) { iprob = 0; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
omega = 2.*M_PI*rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
for (int i = 0; i<sr; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
dim = mesh.Dimension();
|
||||
mesh.EnsureNCMesh();
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// Define spaces
|
||||
// L2 space for p
|
||||
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *p_fes = new ParFiniteElementSpace(&pmesh,p_fec);
|
||||
|
||||
// Vector L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec, dim);
|
||||
|
||||
// H^1/2 space for p̂
|
||||
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
|
||||
ParFiniteElementSpace *hatp_fes = new ParFiniteElementSpace(&pmesh,hatp_fec);
|
||||
|
||||
// H^-1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
|
||||
// if (myid == 0)
|
||||
// {
|
||||
// mfem::out << "p_fes space true dofs = " << p_fes->GetTrueVSize() << endl;
|
||||
// mfem::out << "u_fes space true dofs = " << u_fes->GetTrueVSize() << endl;
|
||||
// mfem::out << "hatp_fes space true dofs = " << hatp_fes->GetTrueVSize() << endl;
|
||||
// mfem::out << "hatu_fes space true dofs = " << hatu_fes->GetTrueVSize() << endl;
|
||||
// }
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
Vector vec0(dim); vec0 = 0.;
|
||||
VectorConstantCoefficient vzero(vec0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient omeg2(omega*omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<ParFiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(p_fes);
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(hatp_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
|
||||
test_fec.Append(q_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
ComplexParNormalEquations * a = new ComplexParNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices();
|
||||
// i ω (p,q)
|
||||
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omeg),0,0);
|
||||
|
||||
// -(u , ∇ q)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(negone)),nullptr,1,0);
|
||||
|
||||
// -(p, ∇⋅v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),nullptr,0,1);
|
||||
|
||||
// i ω (u,v)
|
||||
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(omeg)),1,1);
|
||||
|
||||
// < p̂, v⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,nullptr,2,1);
|
||||
|
||||
// < û,q >
|
||||
a->AddTrialIntegrator(new TraceIntegrator,nullptr,3,0);
|
||||
|
||||
// test integrators
|
||||
//space-induced norm for H(div) × H1
|
||||
// (∇q,∇δq)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
|
||||
// (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
|
||||
// (∇⋅v,∇⋅δv)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),nullptr,1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
|
||||
|
||||
// additional integrators for the adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -i ω (∇q,δv)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negomeg),0,1);
|
||||
// i ω (v,∇ δq)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorWeakDivergenceIntegrator(negomeg),1,0);
|
||||
// ω^2 (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),nullptr,1,1);
|
||||
|
||||
// - i ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(nullptr,new VectorFEDivergenceIntegrator(negomeg),1,0);
|
||||
// i ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(nullptr,new MixedScalarWeakGradientIntegrator(negomeg),0,1);
|
||||
// ω^2 (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(omeg2),nullptr,0,0);
|
||||
}
|
||||
|
||||
// RHS
|
||||
FunctionCoefficient f_rhs_r(rhs_func_r);
|
||||
FunctionCoefficient f_rhs_i(rhs_func_i);
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs_r),new DomainLFIntegrator(f_rhs_i),0);
|
||||
|
||||
|
||||
FunctionCoefficient hatpex_r(hatp_exact_r);
|
||||
FunctionCoefficient hatpex_i(hatp_exact_i);
|
||||
|
||||
VectorFunctionCoefficient hatuex_r(dim,hatu_exact_r);
|
||||
VectorFunctionCoefficient hatuex_i(dim,hatu_exact_i);
|
||||
Array<int> elements_to_refine;
|
||||
|
||||
socketstream p_out_r;
|
||||
socketstream p_out_i;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
p_out_r.open(vishost, visport);
|
||||
p_out_i.open(vishost, visport);
|
||||
}
|
||||
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Ref |"
|
||||
<< " Mesh |"
|
||||
<< " Dofs |"
|
||||
<< " ω |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |"
|
||||
<< " PCG it |"
|
||||
<< " PCG time |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "---------------------"
|
||||
<< "---------------------"
|
||||
<< "---------------------"
|
||||
<< "---------------------"
|
||||
<< "-------------------" << endl;
|
||||
}
|
||||
|
||||
|
||||
for (int it = 0; it<pr; it++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
// ess_bdr[1] = 0;
|
||||
// ess_bdr[2] = 0;
|
||||
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
// hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int j = 0; j < ess_tdof_list.Size(); j++)
|
||||
{
|
||||
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
|
||||
// + hatp_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = p_fes->GetVSize();
|
||||
offsets[2] = u_fes->GetVSize();
|
||||
offsets[3] = hatp_fes->GetVSize();
|
||||
offsets[4] = hatu_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
Vector x(2*offsets.Last());
|
||||
x = 0.;
|
||||
double * xdata = x.GetData();
|
||||
|
||||
ParComplexGridFunction hatp_gf(hatp_fes);
|
||||
hatp_gf.real().MakeRef(hatp_fes,&xdata[offsets[2]]);
|
||||
hatp_gf.imag().MakeRef(hatp_fes,&xdata[offsets.Last()+ offsets[2]]);
|
||||
hatp_gf.ProjectBdrCoefficient(hatpex_r,hatpex_i, ess_bdr);
|
||||
// ParComplexGridFunction hatu_gf(hatu_fes);
|
||||
// hatu_gf.real().MakeRef(hatu_fes,&xdata[offsets[3]]);
|
||||
// hatu_gf.imag().MakeRef(hatu_fes,&xdata[offsets.Last()+ offsets[3]]);
|
||||
// hatu_gf.ProjectCoefficientNormal(hatuex_r,hatuex_i, ess_bdr);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
|
||||
|
||||
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
|
||||
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
|
||||
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
|
||||
|
||||
int num_blocks = BlockA_r->NumRowBlocks();
|
||||
Array<int> tdof_offsets(2*num_blocks+1);
|
||||
|
||||
tdof_offsets[0] = 0;
|
||||
int skip = (static_cond) ? 0 : 2;
|
||||
int k = (static_cond) ? 2 : 0;
|
||||
for (int i=0; i<num_blocks;i++)
|
||||
{
|
||||
tdof_offsets[i+1] = trial_fes[i+k]->GetTrueVSize();
|
||||
tdof_offsets[num_blocks+i+1] = trial_fes[i+k]->GetTrueVSize();
|
||||
}
|
||||
tdof_offsets.PartialSum();
|
||||
|
||||
BlockOperator blockA(tdof_offsets);
|
||||
for (int i = 0; i<num_blocks; i++)
|
||||
{
|
||||
for (int j = 0; j<num_blocks; j++)
|
||||
{
|
||||
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
|
||||
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
|
||||
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
|
||||
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
X = 0.;
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(tdof_offsets);
|
||||
|
||||
|
||||
if (!static_cond)
|
||||
{
|
||||
HypreBoomerAMG * solver_p = new HypreBoomerAMG((HypreParMatrix &)BlockA_r->GetBlock(0,0));
|
||||
solver_p->SetPrintLevel(0);
|
||||
solver_p->SetSystemsOptions(dim);
|
||||
HypreBoomerAMG * solver_u = new HypreBoomerAMG((HypreParMatrix &)BlockA_r->GetBlock(1,1));
|
||||
solver_u->SetPrintLevel(0);
|
||||
solver_u->SetSystemsOptions(dim);
|
||||
M->SetDiagonalBlock(0,solver_p);
|
||||
M->SetDiagonalBlock(1,solver_u);
|
||||
M->SetDiagonalBlock(num_blocks,solver_p);
|
||||
M->SetDiagonalBlock(num_blocks+1,solver_u);
|
||||
}
|
||||
|
||||
|
||||
HypreBoomerAMG * solver_hatp = new HypreBoomerAMG((HypreParMatrix &)BlockA_r->GetBlock(skip,skip));
|
||||
// amg->SetCycleNumSweeps(5, 5);
|
||||
solver_hatp->SetPrintLevel(0);
|
||||
|
||||
HypreSolver * solver_hatu = nullptr;
|
||||
if (dim == 2)
|
||||
{
|
||||
solver_hatu = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1),hatu_fes);
|
||||
dynamic_cast<HypreAMS*>(solver_hatu)->SetPrintLevel(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
solver_hatu = new HypreADS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1), hatu_fes);
|
||||
dynamic_cast<HypreAMS*>(solver_hatu)->SetPrintLevel(0);
|
||||
}
|
||||
|
||||
|
||||
M->SetDiagonalBlock(skip,solver_hatp);
|
||||
M->SetDiagonalBlock(skip+1,solver_hatu);
|
||||
M->SetDiagonalBlock(skip+num_blocks,solver_hatp);
|
||||
M->SetDiagonalBlock(skip+num_blocks+1,solver_hatu);
|
||||
|
||||
StopWatch chrono;
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-7);
|
||||
cg.SetAbsTol(1e-7);
|
||||
cg.SetMaxIter(10000);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(blockA);
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
cg.Mult(B, X);
|
||||
chrono.Stop();
|
||||
delete M;
|
||||
|
||||
int ne = pmesh.GetNE();
|
||||
MPI_Allreduce(MPI_IN_PLACE,&ne,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
|
||||
int ne_x = (dim == 2) ? (int)sqrt(ne) : (int)cbrt(ne);
|
||||
ostringstream oss;
|
||||
double pcg_time = chrono.RealTime();
|
||||
if (myid == 0)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
oss << ne_x << " x " << ne_x ;
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << ne_x << " x " << ne_x << " x " << ne_x ;
|
||||
}
|
||||
}
|
||||
|
||||
int num_iter = cg.GetNumIterations();
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
double maxresidual = residuals.Max();
|
||||
double globalresidual = residual * residual;
|
||||
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
|
||||
|
||||
globalresidual = sqrt(globalresidual);
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
for (int iel = 0; iel<pmesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * maxresidual)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
ParComplexGridFunction p(p_fes);
|
||||
p.real().MakeRef(p_fes,x.GetData());
|
||||
p.imag().MakeRef(p_fes,&x.GetData()[offsets.Last()]);
|
||||
|
||||
ParComplexGridFunction u(u_fes);
|
||||
u.real().MakeRef(u_fes,&x.GetData()[offsets[1]]);
|
||||
u.imag().MakeRef(u_fes,&x.GetData()[offsets.Last()+offsets[1]]);
|
||||
|
||||
|
||||
// Error in pressure
|
||||
ParComplexGridFunction pgf_ex(p_fes);
|
||||
FunctionCoefficient p_ex_r(p_exact_r);
|
||||
FunctionCoefficient p_ex_i(p_exact_i);
|
||||
pgf_ex.ProjectCoefficient(p_ex_r, p_ex_i);
|
||||
|
||||
double p_err_r = p.real().ComputeL2Error(p_ex_r);
|
||||
double p_err_i = p.imag().ComputeL2Error(p_ex_i);
|
||||
double p_error = sqrt(p_err_r*p_err_r + p_err_i*p_err_i);
|
||||
double p_norm_r = pgf_ex.real().ComputeL2Error(zero);
|
||||
double p_norm_i = pgf_ex.imag().ComputeL2Error(zero);
|
||||
double p_norm = sqrt(p_norm_r*p_norm_r + p_norm_i*p_norm_i);
|
||||
|
||||
// Error in velocity
|
||||
ParComplexGridFunction ugf_ex(u_fes);
|
||||
VectorFunctionCoefficient u_ex_r(dim,u_exact_r);
|
||||
VectorFunctionCoefficient u_ex_i(dim,u_exact_i);
|
||||
|
||||
double u_err_r = u.real().ComputeL2Error(u_ex_r);
|
||||
double u_err_i = u.imag().ComputeL2Error(u_ex_i);
|
||||
double u_error = sqrt(u_err_r*u_err_r + u_err_i*u_err_i);
|
||||
double u_norm_r = pgf_ex.real().ComputeL2Error(vzero);
|
||||
double u_norm_i = pgf_ex.imag().ComputeL2Error(vzero);
|
||||
double u_norm = sqrt(u_norm_r*u_norm_r + u_norm_i*u_norm_i);
|
||||
|
||||
|
||||
double L2Error = sqrt(p_error*p_error + u_error*u_error);
|
||||
double L2norm = sqrt(p_norm*p_norm + u_norm*u_norm);
|
||||
|
||||
double rel_err = L2Error/L2norm;
|
||||
|
||||
int dofs = p_fes->GlobalTrueVSize()
|
||||
+ u_fes->GlobalTrueVSize()
|
||||
+ hatp_fes->GlobalTrueVSize()
|
||||
+ hatu_fes->GlobalTrueVSize();
|
||||
|
||||
|
||||
double rate_err = (it) ? dim*log(err0/rel_err)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (it) ? dim*log(res0/globalresidual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = rel_err;
|
||||
res0 = globalresidual;
|
||||
dof0 = dofs;
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << std::right << std::setw(5) << it << " | "
|
||||
<< std::setw(16) << oss.str() << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(0) << std::fixed
|
||||
<< std::setw(2) << 2*rnum << " π | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << rel_err * 100. << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::setw(6) << std::fixed << num_iter << " | "
|
||||
<< std::setprecision(5)
|
||||
<< std::setw(8) << std::fixed << pcg_time << " | "
|
||||
<< std::scientific
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
p_out_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
p_out_r.precision(8);
|
||||
p_out_r << "solution\n" << pmesh << p.real() <<
|
||||
"window_title 'Real Numerical presure' "
|
||||
<< flush;
|
||||
|
||||
p_out_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
p_out_i.precision(8);
|
||||
p_out_i << "solution\n" << pmesh << p.imag() <<
|
||||
"window_title 'Imag Numerical presure' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
if (it == pr)
|
||||
break;
|
||||
|
||||
pmesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete q_fec;
|
||||
delete v_fec;
|
||||
delete hatp_fes;
|
||||
delete hatp_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete u_fec;
|
||||
delete p_fec;
|
||||
delete u_fes;
|
||||
delete p_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double p_exact_r(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_r(x,p,dp,d2p);
|
||||
return p;
|
||||
}
|
||||
|
||||
double p_exact_i(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_i(x,p,dp,d2p);
|
||||
return p;
|
||||
}
|
||||
|
||||
double hatp_exact_r(const Vector & X)
|
||||
{
|
||||
return p_exact_r(X);
|
||||
}
|
||||
|
||||
double hatp_exact_i(const Vector & X)
|
||||
{
|
||||
return p_exact_i(X);
|
||||
}
|
||||
|
||||
void gradp_exact_r(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
double p,d2p;
|
||||
acoustics_solution_r(x,p,grad,d2p);
|
||||
}
|
||||
|
||||
void gradp_exact_i(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
double p,d2p;
|
||||
acoustics_solution_i(x,p,grad,d2p);
|
||||
}
|
||||
|
||||
double d2_exact_r(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_r(x,p,dp,d2p);
|
||||
return d2p;
|
||||
}
|
||||
|
||||
double d2_exact_i(const Vector &x)
|
||||
{
|
||||
double p,d2p;
|
||||
Vector dp;
|
||||
acoustics_solution_i(x,p,dp,d2p);
|
||||
return d2p;
|
||||
}
|
||||
|
||||
// u = - ∇ p / (i ω )
|
||||
// = i (∇ p_r + i * ∇ p_i) / ω
|
||||
// = - ∇ p_i / ω + i ∇ p_r / ω
|
||||
void u_exact_r(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact_i(x,u);
|
||||
u *= -1./omega;
|
||||
}
|
||||
|
||||
void u_exact_i(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact_r(x,u);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
void hatu_exact_r(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact_r(X,hatu);
|
||||
}
|
||||
void hatu_exact_i(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact_i(X,hatu);
|
||||
}
|
||||
|
||||
// ∇⋅u = i Δ p / ω
|
||||
// = i (Δ p_r + i * Δ p_i) / ω
|
||||
// = - Δ p_i / ω + i Δ p_r / ω
|
||||
|
||||
double divu_exact_r(const Vector &x)
|
||||
{
|
||||
return -d2_exact_i(x)/omega;
|
||||
}
|
||||
|
||||
double divu_exact_i(const Vector &x)
|
||||
{
|
||||
return d2_exact_r(x)/omega;
|
||||
}
|
||||
|
||||
// f = ∇⋅u + i ω p
|
||||
// f_r = ∇⋅u_r - ω p_i
|
||||
double rhs_func_r(const Vector &x)
|
||||
{
|
||||
double p = p_exact_i(x);
|
||||
double divu = divu_exact_r(x);
|
||||
return divu - omega * p;
|
||||
}
|
||||
|
||||
// f_i = ∇⋅u_i + ω p_r
|
||||
double rhs_func_i(const Vector &x)
|
||||
{
|
||||
double p = p_exact_r(x);
|
||||
double divu = divu_exact_i(x);
|
||||
return divu + omega * p;
|
||||
}
|
||||
|
||||
|
||||
void acoustics_solution_r(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p)
|
||||
{
|
||||
complex<double> zp, d2zp;
|
||||
vector<complex<double>> dzp;
|
||||
acoustics_solution(X,zp,dzp,d2zp);
|
||||
p = zp.real();
|
||||
d2p = d2zp.real();
|
||||
dp.SetSize(X.Size());
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = dzp[i].real();
|
||||
}
|
||||
}
|
||||
|
||||
void acoustics_solution_i(const Vector & X, double & p,
|
||||
Vector &dp, double & d2p)
|
||||
{
|
||||
complex<double> zp, d2zp;
|
||||
vector<complex<double>> dzp;
|
||||
acoustics_solution(X,zp,dzp,d2zp);
|
||||
p = zp.imag();
|
||||
d2p = d2zp.imag();
|
||||
dp.SetSize(X.Size());
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = dzp[i].imag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void acoustics_solution(const Vector & X, complex<double> & p, vector<complex<double>> & dp,
|
||||
complex<double> & d2p)
|
||||
{
|
||||
dp.resize(X.Size());
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
switch (prob)
|
||||
{
|
||||
case plane_wave:
|
||||
{
|
||||
double beta = omega/std::sqrt((double)X.Size());
|
||||
complex<double> alpha = beta * zi * X.Sum();
|
||||
p = exp(-alpha);
|
||||
d2p = - dim * beta * beta * p;
|
||||
for (int i = 0; i<X.Size(); i++)
|
||||
{
|
||||
dp[i] = - zi * beta * p;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double rk = omega;
|
||||
double alpha = 45 * M_PI/180.;
|
||||
double sina = sin(alpha);
|
||||
double cosa = cos(alpha);
|
||||
// shift the origin
|
||||
double xprim=X(0) + 0.1;
|
||||
double yprim=X(1) + 0.1;
|
||||
|
||||
double x = xprim*sina - yprim*cosa;
|
||||
double y = xprim*cosa + yprim*sina;
|
||||
double dxdxprim = sina, dxdyprim = -cosa;
|
||||
double dydxprim = cosa, dydyprim = sina;
|
||||
//wavelength
|
||||
double rl = 2.*M_PI/rk;
|
||||
|
||||
// beam waist radius
|
||||
double w0 = 0.05;
|
||||
|
||||
// function w
|
||||
double fact = rl/M_PI/(w0*w0);
|
||||
double aux = 1. + (fact*y)*(fact*y);
|
||||
|
||||
double w = w0*sqrt(aux);
|
||||
double dwdy = w0*fact*fact*y/sqrt(aux);
|
||||
double d2wdydy = w0*fact*fact*(1. - (fact*y)*(fact*y)/aux)/sqrt(aux);
|
||||
|
||||
double phi0 = atan(fact*y);
|
||||
double dphi0dy = cos(phi0)*cos(phi0)*fact;
|
||||
double d2phi0dydy = -2.*cos(phi0)*sin(phi0)*fact*dphi0dy;
|
||||
|
||||
double r = y + 1./y/(fact*fact);
|
||||
double drdy = 1. - 1./(y*y)/(fact*fact);
|
||||
double d2rdydy = 2./(y*y*y)/(fact*fact);
|
||||
|
||||
// pressure
|
||||
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r + zi*phi0/2.;
|
||||
|
||||
complex<double> zdedx = -2.*x/(w*w) - 2.*zi*M_PI*x/rl/r;
|
||||
complex<double> zdedy = 2.*x*x/(w*w*w)*dwdy - zi*rk + zi*M_PI*x*x/rl/(r*r)*drdy + zi*dphi0dy/2.;
|
||||
complex<double> zd2edxdx = -2./(w*w) - 2.*zi*M_PI/rl/r;
|
||||
complex<double> zd2edxdy = 4.*x/(w*w*w)*dwdy + 2.*zi*M_PI*x/rl/(r*r)*drdy;
|
||||
complex<double> zd2edydx = zd2edxdy;
|
||||
complex<double> zd2edydy = -6.*x*x/(w*w*w*w)*dwdy*dwdy + 2.*x*x/(w*w*w)*d2wdydy - 2.*zi*M_PI*x*x/rl/(r*r*r)*drdy*drdy
|
||||
+ zi*M_PI*x*x/rl/(r*r)*d2rdydy + zi/2.*d2phi0dydy;
|
||||
|
||||
double pf = pow(2.0/M_PI/(w*w),0.25);
|
||||
double dpfdy = -pow(2./M_PI/(w*w),-0.75)/M_PI/(w*w*w)*dwdy;
|
||||
double d2pfdydy = -1./M_PI*pow(2./M_PI,-0.75)*(-1.5*pow(w,-2.5)
|
||||
*dwdy*dwdy + pow(w,-1.5)*d2wdydy);
|
||||
|
||||
|
||||
complex<double> zp = pf*exp(ze);
|
||||
complex<double> zdpdx = zp*zdedx;
|
||||
complex<double> zdpdy = dpfdy*exp(ze)+zp*zdedy;
|
||||
complex<double> zd2pdxdx = zdpdx*zdedx + zp*zd2edxdx;
|
||||
complex<double> zd2pdxdy = zdpdy*zdedx + zp*zd2edxdy;
|
||||
complex<double> zd2pdydx = dpfdy*exp(ze)*zdedx + zdpdx*zdedy + zp*zd2edydx;
|
||||
complex<double> zd2pdydy = d2pfdydy*exp(ze) + dpfdy*exp(ze)*zdedy + zdpdy*zdedy + zp*zd2edydy;
|
||||
|
||||
p = zp;
|
||||
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim);
|
||||
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim);
|
||||
|
||||
d2p = (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
|
||||
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// MFEM DPG_strong acoustics Example
|
||||
//
|
||||
// Compile with: make strong_dpg
|
||||
//
|
||||
// Definite/Indefinite Helmholtz
|
||||
|
||||
// - Δ p ± ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ p - ω u = 0, in Ω
|
||||
// - ∇⋅u ± ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/ω
|
||||
|
||||
// Strong DPG formulation
|
||||
// (p,u) ∈ H^1(Ω) × H(div,Ω)
|
||||
//
|
||||
// (∇ p, v) - ω (u,v) = 0, in Ω, ∀ v ∈ (L^2)^dim
|
||||
// -(∇⋅u, q) ± ω (p,q) = (f,q), in Ω, ∀ q ∈ L^2
|
||||
// p = p_0, in ∂Ω
|
||||
//
|
||||
// ------------------------------------
|
||||
// | | p | u | RHS |
|
||||
// ------------------------------------
|
||||
// | q | ± ω (p,q) | -(∇⋅u,q) | (f,q) |
|
||||
// | | | | |
|
||||
// | v | (∇ p, v) | -ω (u,v) | |
|
||||
|
||||
// where (q,v) ∈ L^2 × (L^2)^dim
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// #define DEFINITE
|
||||
|
||||
double p_exact(const Vector &x);
|
||||
void u_exact(const Vector &x, Vector & u);
|
||||
double rhs_func(const Vector &x);
|
||||
void gradp_exact(const Vector &x, Vector &gradu);
|
||||
double divu_exact(const Vector &x);
|
||||
double d2_exact(const Vector &x);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int ref = 1;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&ref, "-ref", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
omega = 2.0 * M_PI * rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
|
||||
|
||||
for (int i = 0; i < ref; i++ )
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// Define spaces
|
||||
// H1 space for p
|
||||
FiniteElementCollection *p_fec = new H1_FECollection(order, dim);
|
||||
FiniteElementSpace * p_fes = new FiniteElementSpace(&mesh, p_fec);
|
||||
|
||||
// H(div) for u
|
||||
FiniteElementCollection *u_fec = new RT_FECollection(order-1, dim);
|
||||
FiniteElementSpace * u_fes = new FiniteElementSpace(&mesh, u_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * q_fec = new L2_FECollection(test_order-1, dim);
|
||||
FiniteElementCollection * v_fec = new L2_FECollection(test_order-1, dim);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(p_fes);
|
||||
trial_fes.Append(u_fes);
|
||||
test_fec.Append(q_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
|
||||
a->SetTestFECollVdim(1,dim);
|
||||
|
||||
a->StoreMatrices(true);
|
||||
|
||||
// ± ω (p, q)
|
||||
#ifdef DEFINITE
|
||||
// ω (p, q)
|
||||
a->AddTrialIntegrator(new MassIntegrator(omeg),0,0);
|
||||
#else
|
||||
// -ω (p, q)
|
||||
a->AddTrialIntegrator(new MassIntegrator(negomeg),0,0);
|
||||
#endif
|
||||
|
||||
// -(∇⋅u, q)
|
||||
a->AddTrialIntegrator(new MixedScalarDivergenceIntegrator(negone),1,0);
|
||||
|
||||
// -ω (u,v)
|
||||
a->AddTrialIntegrator(new VectorFEMassIntegrator(negomeg),1,1);
|
||||
|
||||
// (∇ p, v)
|
||||
a->AddTrialIntegrator(new GradientIntegrator(one),0,1);
|
||||
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new VectorMassIntegrator(one),1,1);
|
||||
|
||||
// (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
|
||||
FunctionCoefficient f_rhs(rhs_func);
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
p_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
FunctionCoefficient p_ex(p_exact);
|
||||
VectorFunctionCoefficient gradp_ex(dim,gradp_exact);
|
||||
VectorFunctionCoefficient u_ex(dim,u_exact);
|
||||
FunctionCoefficient divu_ex(divu_exact);
|
||||
GridFunction p_gf, u_gf;
|
||||
GridFunction pex_gf(p_fes);
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = p_fes->GetVSize();
|
||||
offsets[2] = u_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
|
||||
p_gf.MakeRef(p_fes,x.GetBlock(0));
|
||||
p_gf.ProjectBdrCoefficient(p_ex,ess_bdr);
|
||||
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(1));
|
||||
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = Ah.As<BlockMatrix>();
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream p_out;
|
||||
socketstream u_out;
|
||||
p_out.open(vishost, visport);
|
||||
u_out.open(vishost, visport);
|
||||
p_out.precision(8);
|
||||
p_out << "solution\n" << mesh << p_gf <<
|
||||
"window_title 'Numerical p' "
|
||||
<< flush;
|
||||
|
||||
u_out.precision(8);
|
||||
u_out << "solution\n" << mesh << u_gf <<
|
||||
"window_title 'Numerical flux' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double rhs_func(const Vector &x)
|
||||
{
|
||||
double p = p_exact(x);
|
||||
double divu = divu_exact(x);
|
||||
// f = - ∇⋅u ± ω p,
|
||||
#ifdef DEFINITE
|
||||
return -divu + omega * p;
|
||||
#else
|
||||
return -divu - omega * p;
|
||||
#endif
|
||||
}
|
||||
|
||||
double p_exact(const Vector &x)
|
||||
{
|
||||
return sin(omega*x.Sum());
|
||||
}
|
||||
|
||||
void gradp_exact(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
grad = omega * cos(omega * x.Sum());
|
||||
}
|
||||
|
||||
void u_exact(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact(x,u);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
double divu_exact(const Vector &x)
|
||||
{
|
||||
return d2_exact(x)/omega;
|
||||
}
|
||||
|
||||
double d2_exact(const Vector &x)
|
||||
{
|
||||
return -dim * omega * omega * sin(omega*x.Sum());
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
// MFEM Ultraweak DPG acoustics example
|
||||
//
|
||||
// Compile with: make uw_dpg
|
||||
//
|
||||
// ./uw_dpg -m ../../../data/inline-quad.mesh -rnum 40 -theta 0.7 -prob 1 -graph-norm -ref 40 -o 3
|
||||
|
||||
// - Δ p ± ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ p - ω u = 0, in Ω
|
||||
// - ∇⋅u ± ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/ω
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
|
||||
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
|
||||
// -(p, ∇⋅v) - ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
|
||||
// (u , ∇ q) ± ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
|
||||
// p̂ = p_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// p̂ := p on Γ_h (skeleton)
|
||||
// û := -u on Γ_h
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | p | u | p̂ | û | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v | -(p, ∇⋅v) | - ω (u,v) | < p̂, v⋅n> | | |
|
||||
// | | | | | | |
|
||||
// | q | ± ω (p,q) | (u , ∇ q) | | < û,q > | (f,q) |
|
||||
|
||||
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// #define DEFINITE
|
||||
void acoustics_solution(const Vector & X, double & p, Vector & dp, double & d2p);
|
||||
double p_exact(const Vector &x);
|
||||
void u_exact(const Vector &x, Vector & u);
|
||||
double rhs_func(const Vector &x);
|
||||
double divu_exact(const Vector &x);
|
||||
double hatp_exact(const Vector & X);
|
||||
void hatu_exact(const Vector & X, Vector & hatu);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
plane_wave,
|
||||
gaussian_beam
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int ref = 1;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
int iprob = 0;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: plane wave, 1: Gaussian beam");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&ref, "-ref", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 1) { iprob = 0; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
|
||||
omega = 2.0 * M_PI * rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
|
||||
|
||||
// Define spaces
|
||||
// L2 space for p
|
||||
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *p_fes = new FiniteElementSpace(&mesh,p_fec);
|
||||
|
||||
// Vector L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec, dim);
|
||||
|
||||
// H^1/2 space for p̂
|
||||
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
|
||||
FiniteElementSpace *hatp_fes = new FiniteElementSpace(&mesh,hatp_fec);
|
||||
|
||||
// H^-1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
Vector vec0(dim); vec0 = 0.;
|
||||
VectorConstantCoefficient vzero(vec0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient omeg2(omega*omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(p_fes);
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(hatp_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
|
||||
test_fec.Append(q_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
|
||||
// ± ω (p,q)
|
||||
#ifdef DEFINITE
|
||||
a->AddTrialIntegrator(new MixedScalarMassIntegrator(omeg),0,0);
|
||||
#else
|
||||
a->AddTrialIntegrator(new MixedScalarMassIntegrator(negomeg),0,0);
|
||||
#endif
|
||||
|
||||
// (u , ∇ q)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
|
||||
|
||||
// -(p, ∇⋅v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,1);
|
||||
|
||||
// - ω (u,v)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negomeg)),1,1);
|
||||
|
||||
// < p̂, v⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
|
||||
|
||||
// < û,q >
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,0);
|
||||
|
||||
|
||||
// test integrators
|
||||
|
||||
//space-induced norm for H(div) × H1
|
||||
// (∇q,∇δq)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅v,∇⋅δv)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
|
||||
// additional integrators for the adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -ω (∇q,δv)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negomeg),0,1);
|
||||
// -ω (v,δq)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg),1,0);
|
||||
// ω^2 (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),1,1);
|
||||
|
||||
#ifdef DEFINITE
|
||||
// - ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(negomeg),1,0);
|
||||
// - ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(omeg),0,1);
|
||||
#else
|
||||
// ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(omeg),1,0);
|
||||
// ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(negomeg),0,1);
|
||||
#endif
|
||||
// ω^2 (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(omeg2),0,0);
|
||||
}
|
||||
|
||||
// RHS
|
||||
FunctionCoefficient f_rhs(rhs_func);
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
|
||||
|
||||
|
||||
FunctionCoefficient hatpex(hatp_exact);
|
||||
FunctionCoefficient pex(p_exact);
|
||||
VectorFunctionCoefficient uex(dim,u_exact);
|
||||
Array<int> elements_to_refine;
|
||||
GridFunction hatp_gf;
|
||||
|
||||
|
||||
socketstream p_out;
|
||||
// socketstream u_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
p_out.open(vishost, visport);
|
||||
// u_out.open(vishost, visport);
|
||||
}
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
mfem::out << " Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------" << endl;
|
||||
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
ess_tdof_list[i] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = p_fes->GetVSize();
|
||||
offsets[2] = u_fes->GetVSize();
|
||||
offsets[3] = hatp_fes->GetVSize();
|
||||
offsets[4] = hatu_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
hatp_gf.MakeRef(hatp_fes,x.GetBlock(2));
|
||||
hatp_gf.ProjectBdrCoefficient(hatpex,ess_bdr);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = Ah.As<BlockMatrix>();
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-8);
|
||||
cg.SetMaxIter(20000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
double max_resid = residuals.Max();
|
||||
for (int iel = 0; iel<mesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * max_resid)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
GridFunction p_gf;
|
||||
p_gf.MakeRef(p_fes,x.GetBlock(0));
|
||||
|
||||
GridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(1));
|
||||
|
||||
GridFunction pex_gf(p_fes);
|
||||
GridFunction uex_gf(u_fes);
|
||||
pex_gf.ProjectCoefficient(pex);
|
||||
uex_gf.ProjectCoefficient(uex);
|
||||
|
||||
|
||||
// Error
|
||||
int dofs = X.Size();
|
||||
double p_err = p_gf.ComputeL2Error(pex);
|
||||
double p_norm = pex_gf.ComputeL2Error(zero);
|
||||
double u_err = u_gf.ComputeL2Error(uex);
|
||||
double u_norm = uex_gf.ComputeL2Error(vzero);
|
||||
|
||||
double L2Error = sqrt(p_err*p_err + u_err*u_err);
|
||||
double L2norm = sqrt(p_norm * p_norm + u_norm * u_norm);
|
||||
|
||||
double rel_error = L2Error/L2norm;
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = L2Error;
|
||||
res0 = residual;
|
||||
dof0 = dofs;
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::resetiosflags(std::ios::showbase)
|
||||
<< std::endl;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
p_out.precision(8);
|
||||
p_out << "solution\n" << mesh << p_gf <<
|
||||
"window_title 'Numerical presure' "
|
||||
<< flush;
|
||||
|
||||
// u_out.precision(8);
|
||||
// u_out << "solution\n" << mesh << u_gf <<
|
||||
// "window_title 'Numerical velocity' "
|
||||
// << flush;
|
||||
}
|
||||
|
||||
if (i == ref)
|
||||
break;
|
||||
|
||||
mesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete q_fec;
|
||||
delete v_fec;
|
||||
delete hatp_fes;
|
||||
delete hatp_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete u_fec;
|
||||
delete p_fec;
|
||||
delete u_fes;
|
||||
delete p_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double rhs_func(const Vector &x)
|
||||
{
|
||||
double p = p_exact(x);
|
||||
double divu = divu_exact(x);
|
||||
// f = - ∇⋅u ± ω p,
|
||||
#ifdef DEFINITE
|
||||
return -divu + omega * p;
|
||||
#else
|
||||
return -divu - omega * p;
|
||||
#endif
|
||||
}
|
||||
|
||||
double p_exact(const Vector &x)
|
||||
{
|
||||
double p, d2p;
|
||||
Vector dp;
|
||||
acoustics_solution(x,p,dp,d2p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void u_exact(const Vector &x, Vector & u)
|
||||
{
|
||||
double p, d2p;
|
||||
acoustics_solution(x,p,u,d2p);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
double divu_exact(const Vector &x)
|
||||
{
|
||||
double p, d2p;
|
||||
Vector dp;
|
||||
acoustics_solution(x,p,dp,d2p);
|
||||
return d2p/omega;
|
||||
}
|
||||
|
||||
double hatp_exact(const Vector & X)
|
||||
{
|
||||
return p_exact(X);
|
||||
}
|
||||
|
||||
void hatu_exact(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact(X,hatu);
|
||||
hatu *= -1.;
|
||||
}
|
||||
|
||||
void acoustics_solution(const Vector & X, double & p, Vector & dp, double & d2p)
|
||||
{
|
||||
dp.SetSize(X.Size());
|
||||
switch (prob)
|
||||
{
|
||||
case plane_wave:
|
||||
{
|
||||
p = sin(omega*X.Sum());
|
||||
dp = omega * cos(omega * X.Sum());
|
||||
d2p = -dim * omega * omega * sin(omega*X.Sum());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double rk = omega;
|
||||
double alpha = 45 * M_PI/180.;
|
||||
double sina = sin(alpha);
|
||||
double cosa = cos(alpha);
|
||||
// shift the origin
|
||||
double xprim=X(0) + 0.1;
|
||||
double yprim=X(1) + 0.1;
|
||||
|
||||
double x = xprim*sina - yprim*cosa;
|
||||
double y = xprim*cosa + yprim*sina;
|
||||
double dxdxprim = sina, dxdyprim = -cosa;
|
||||
double dydxprim = cosa, dydyprim = sina;
|
||||
//wavelength
|
||||
double rl = 2.*M_PI/rk;
|
||||
|
||||
// beam waist radius
|
||||
double w0 = 0.05;
|
||||
|
||||
// function w
|
||||
double fact = rl/M_PI/(w0*w0);
|
||||
double aux = 1. + (fact*y)*(fact*y);
|
||||
|
||||
double w = w0*sqrt(aux);
|
||||
double dwdy = w0*fact*fact*y/sqrt(aux);
|
||||
double d2wdydy = w0*fact*fact*(1. - (fact*y)*(fact*y)/aux)/sqrt(aux);
|
||||
|
||||
double phi0 = atan(fact*y);
|
||||
double dphi0dy = cos(phi0)*cos(phi0)*fact;
|
||||
double d2phi0dydy = -2.*cos(phi0)*sin(phi0)*fact*dphi0dy;
|
||||
|
||||
double r = y + 1./y/(fact*fact);
|
||||
double drdy = 1. - 1./(y*y)/(fact*fact);
|
||||
double d2rdydy = 2./(y*y*y)/(fact*fact);
|
||||
|
||||
// pressure
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r + zi*phi0/2.;
|
||||
|
||||
complex<double> zdedx = -2.*x/(w*w) - 2.*zi*M_PI*x/rl/r;
|
||||
complex<double> zdedy = 2.*x*x/(w*w*w)*dwdy - zi*rk + zi*M_PI*x*x/rl/(r*r)*drdy + zi*dphi0dy/2.;
|
||||
complex<double> zd2edxdx = -2./(w*w) - 2.*zi*M_PI/rl/r;
|
||||
complex<double> zd2edxdy = 4.*x/(w*w*w)*dwdy + 2.*zi*M_PI*x/rl/(r*r)*drdy;
|
||||
complex<double> zd2edydx = zd2edxdy;
|
||||
complex<double> zd2edydy = -6.*x*x/(w*w*w*w)*dwdy*dwdy + 2.*x*x/(w*w*w)*d2wdydy - 2.*zi*M_PI*x*x/rl/(r*r*r)*drdy*drdy
|
||||
+ zi*M_PI*x*x/rl/(r*r)*d2rdydy + zi/2.*d2phi0dydy;
|
||||
|
||||
double pf = pow(2.0/M_PI/(w*w),0.25);
|
||||
double dpfdy = -pow(2./M_PI/(w*w),-0.75)/M_PI/(w*w*w)*dwdy;
|
||||
double d2pfdydy = -1./M_PI*pow(2./M_PI,-0.75)*(-1.5*pow(w,-2.5)
|
||||
*dwdy*dwdy + pow(w,-1.5)*d2wdydy);
|
||||
|
||||
|
||||
complex<double> zp = pf*exp(ze);
|
||||
complex<double> zdpdx = zp*zdedx;
|
||||
complex<double> zdpdy = dpfdy*exp(ze)+zp*zdedy;
|
||||
complex<double> zd2pdxdx = zdpdx*zdedx + zp*zd2edxdx;
|
||||
complex<double> zd2pdxdy = zdpdy*zdedx + zp*zd2edxdy;
|
||||
complex<double> zd2pdydx = dpfdy*exp(ze)*zdedx + zdpdx*zdedy + zp*zd2edydx;
|
||||
complex<double> zd2pdydy = d2pfdydy*exp(ze) + dpfdy*exp(ze)*zdedy + zdpdy*zdedy + zp*zd2edydy;
|
||||
|
||||
p = zp.real();
|
||||
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim).real();
|
||||
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim).real();
|
||||
|
||||
d2p = ( (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
|
||||
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim ).real();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
// MFEM Ultraweak DPG MPI acoustics (Helmholtz) example
|
||||
//
|
||||
// Compile with: make uw_dpgp
|
||||
//
|
||||
// - Δ p ± ω^2 p = f̃ , in Ω
|
||||
// p = p_0, on ∂Ω
|
||||
//
|
||||
// First Order System
|
||||
|
||||
// ∇ p - ω u = 0, in Ω
|
||||
// - ∇⋅u ± ω p = f, in Ω
|
||||
// p = p_0, in ∂Ω
|
||||
// where f:=f̃/ω
|
||||
//
|
||||
// UW-DPG:
|
||||
//
|
||||
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
|
||||
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
|
||||
// -(p, ∇⋅v) - ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
|
||||
// (u , ∇ q) ± ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
|
||||
// p̂ = p_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// p̂ := p on Γ_h (skeleton)
|
||||
// û := -u on Γ_h
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | p | u | p̂ | û | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v | -(p, ∇⋅v) | - ω (u,v) | < p̂, v⋅n> | | |
|
||||
// | | | | | | |
|
||||
// | q | ± ω (p,q) | (u , ∇ q) | | < û,q > | (f,q) |
|
||||
|
||||
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// #define DEFINITE
|
||||
|
||||
double p_exact(const Vector &x);
|
||||
void u_exact(const Vector &x, Vector & u);
|
||||
double rhs_func(const Vector &x);
|
||||
void gradp_exact(const Vector &x, Vector &gradu);
|
||||
double divu_exact(const Vector &x);
|
||||
double d2_exact(const Vector &x);
|
||||
double hatp_exact(const Vector & X);
|
||||
void hatu_exact(const Vector & X, Vector & hatu);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int sr = 0;
|
||||
int pr = 1;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool static_cond = false;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&sr, "-sref", "--serial_ref",
|
||||
"Number of parallel refinements.");
|
||||
args.AddOption(&pr, "-pref", "--parallel_ref",
|
||||
"Number of parallel refinements.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
omega = 2.0 * M_PI * rnum;
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
for (int i = 0; i<sr; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
dim = mesh.Dimension();
|
||||
|
||||
mesh.EnsureNCMesh();
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// Define spaces
|
||||
// L2 space for p
|
||||
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *p_fes = new ParFiniteElementSpace(&pmesh,p_fec);
|
||||
|
||||
// Vector L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec, dim);
|
||||
|
||||
// H^1/2 space for p̂
|
||||
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
|
||||
ParFiniteElementSpace *hatp_fes = new ParFiniteElementSpace(&pmesh,hatp_fec);
|
||||
|
||||
// H^-1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
|
||||
Array<ParFiniteElementSpace * > trial_fes;
|
||||
trial_fes.Append(p_fes);
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(hatp_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
test_fec.Append(q_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
Vector vec0(dim); vec0 = 0.;
|
||||
VectorConstantCoefficient vzero(vec0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient omeg(omega);
|
||||
ConstantCoefficient omeg2(omega*omega);
|
||||
ConstantCoefficient negomeg(-omega);
|
||||
|
||||
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
|
||||
// Integrators
|
||||
|
||||
// ± ω (p,q)
|
||||
#ifdef DEFINITE
|
||||
a->AddTrialIntegrator(new MixedScalarMassIntegrator(omeg),0,0);
|
||||
#else
|
||||
a->AddTrialIntegrator(new MixedScalarMassIntegrator(negomeg),0,0);
|
||||
#endif
|
||||
|
||||
// (u , ∇ q)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
|
||||
|
||||
// -(p, ∇⋅v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,1);
|
||||
|
||||
// - ω (u,v)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negomeg)),1,1);
|
||||
|
||||
// < p̂, v⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
|
||||
|
||||
// < û,q >
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,0);
|
||||
|
||||
|
||||
// test integrators
|
||||
|
||||
//space-induced norm for H(div) × H1
|
||||
// (∇q,∇δq)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅v,∇⋅δv)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
|
||||
// additional integrators for the adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -ω (∇q,δv)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negomeg),0,1);
|
||||
// -ω (v,δq)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg),1,0);
|
||||
// ω^2 (v,δv)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),1,1);
|
||||
|
||||
#ifdef DEFINITE
|
||||
// - ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(negomeg),1,0);
|
||||
// - ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(omeg),0,1);
|
||||
#else
|
||||
// ω (∇⋅v,δq)
|
||||
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(omeg),1,0);
|
||||
// ω (q,∇⋅v)
|
||||
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(negomeg),0,1);
|
||||
#endif
|
||||
// ω^2 (q,δq)
|
||||
a->AddTestIntegrator(new MassIntegrator(omeg2),0,0);
|
||||
}
|
||||
|
||||
// RHS
|
||||
FunctionCoefficient f_rhs(rhs_func);
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
|
||||
|
||||
|
||||
FunctionCoefficient hatpex(hatp_exact);
|
||||
FunctionCoefficient pex(p_exact);
|
||||
VectorFunctionCoefficient uex(dim,u_exact);
|
||||
Array<int> elements_to_refine;
|
||||
ParGridFunction hatp_gf;
|
||||
|
||||
|
||||
|
||||
|
||||
socketstream p_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
p_out.open(vishost, visport);
|
||||
}
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " ω |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |"
|
||||
<< " PCG it |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "---------------------"
|
||||
<< "---------------------"
|
||||
<< "---------------------"
|
||||
<< "----------------" << endl;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i<pr; i++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int j = 0; j < ess_tdof_list.Size(); j++)
|
||||
{
|
||||
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = p_fes->GetVSize();
|
||||
offsets[2] = u_fes->GetVSize();
|
||||
offsets[3] = hatp_fes->GetVSize();
|
||||
offsets[4] = hatu_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
hatp_gf.MakeRef(hatp_fes,x.GetBlock(2));
|
||||
hatp_gf.ProjectBdrCoefficient(hatpex,ess_bdr);
|
||||
|
||||
Vector X,B;
|
||||
OperatorPtr Ah;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockOperator * A = Ah.As<BlockOperator>();
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
|
||||
int skip = 0;
|
||||
if (!static_cond)
|
||||
{
|
||||
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
|
||||
HypreBoomerAMG * amg1 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
|
||||
amg0->SetPrintLevel(0);
|
||||
amg1->SetPrintLevel(0);
|
||||
// amg0->SetRelaxType(16);
|
||||
// amg1->SetRelaxType(16);
|
||||
M->SetDiagonalBlock(0,amg0);
|
||||
M->SetDiagonalBlock(1,amg1);
|
||||
skip = 2;
|
||||
}
|
||||
|
||||
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,skip));
|
||||
amg2->SetPrintLevel(0);
|
||||
// amg2->SetRelaxType(16);
|
||||
M->SetDiagonalBlock(skip,amg2);
|
||||
|
||||
HypreSolver * prec;
|
||||
if (dim == 2)
|
||||
{
|
||||
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatu_fes);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatu_fes);
|
||||
}
|
||||
M->SetDiagonalBlock(skip+1,prec);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-7);
|
||||
cg.SetMaxIter(20000);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
int num_iter = cg.GetNumIterations();
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
double maxresidual = residuals.Max();
|
||||
double globalresidual = residual * residual;
|
||||
|
||||
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
|
||||
|
||||
globalresidual = sqrt(globalresidual);
|
||||
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
for (int iel = 0; iel<pmesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * maxresidual)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ParGridFunction p_gf;
|
||||
p_gf.MakeRef(p_fes,x.GetBlock(0));
|
||||
|
||||
ParGridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(1));
|
||||
|
||||
|
||||
ParGridFunction pex_gf(p_fes);
|
||||
ParGridFunction uex_gf(u_fes);
|
||||
pex_gf.ProjectCoefficient(pex);
|
||||
uex_gf.ProjectCoefficient(uex);
|
||||
|
||||
int dofs = p_fes->GlobalTrueVSize()
|
||||
+ u_fes->GlobalTrueVSize()
|
||||
+ hatp_fes->GlobalTrueVSize()
|
||||
+ hatu_fes->GlobalTrueVSize();
|
||||
|
||||
double p_err = p_gf.ComputeL2Error(pex);
|
||||
double p_norm = pex_gf.ComputeL2Error(zero);
|
||||
double u_err = u_gf.ComputeL2Error(uex);
|
||||
double u_norm = uex_gf.ComputeL2Error(vzero);
|
||||
|
||||
double L2Error = sqrt(p_err*p_err + u_err*u_err);
|
||||
double L2norm = sqrt(p_norm * p_norm + u_norm * u_norm);
|
||||
|
||||
double rel_error = L2Error/L2norm;
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/globalresidual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = L2Error;
|
||||
res0 = globalresidual;
|
||||
dof0 = dofs;
|
||||
|
||||
std::ios oldState(nullptr);
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(0) << std::fixed
|
||||
<< std::setw(2) << 2*rnum << " π | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::setw(6) << std::fixed << num_iter << " | "
|
||||
<< std::setprecision(5)
|
||||
<< std::scientific
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
p_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
p_out.precision(8);
|
||||
p_out << "solution\n" << pmesh << p_gf <<
|
||||
"window_title 'Numerical pressure' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
if (i == pr)
|
||||
break;
|
||||
|
||||
pmesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete q_fec;
|
||||
delete v_fec;
|
||||
delete hatp_fes;
|
||||
delete hatp_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete u_fec;
|
||||
delete p_fec;
|
||||
delete u_fes;
|
||||
delete p_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double rhs_func(const Vector &x)
|
||||
{
|
||||
double p = p_exact(x);
|
||||
double divu = divu_exact(x);
|
||||
// f = - ∇⋅u ± ω p,
|
||||
#ifdef DEFINITE
|
||||
return -divu + omega * p;
|
||||
#else
|
||||
return -divu - omega * p;
|
||||
#endif
|
||||
}
|
||||
|
||||
double p_exact(const Vector &x)
|
||||
{
|
||||
return sin(omega*x.Sum());
|
||||
}
|
||||
|
||||
void gradp_exact(const Vector &x, Vector &grad)
|
||||
{
|
||||
grad.SetSize(x.Size());
|
||||
grad = omega * cos(omega * x.Sum());
|
||||
}
|
||||
|
||||
void u_exact(const Vector &x, Vector & u)
|
||||
{
|
||||
gradp_exact(x,u);
|
||||
u *= 1./omega;
|
||||
}
|
||||
|
||||
double divu_exact(const Vector &x)
|
||||
{
|
||||
return d2_exact(x)/omega;
|
||||
}
|
||||
|
||||
double d2_exact(const Vector &x)
|
||||
{
|
||||
return -dim * omega * omega * sin(omega*x.Sum());
|
||||
}
|
||||
|
||||
double hatp_exact(const Vector & X)
|
||||
{
|
||||
return p_exact(X);
|
||||
}
|
||||
|
||||
void hatu_exact(const Vector & X, Vector & hatu)
|
||||
{
|
||||
u_exact(X,hatu);
|
||||
hatu *= -1.;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/convection-diffusion,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = uw_dpg
|
||||
PAR_EXAMPLES = uw_dpgp
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@@ -0,0 +1,652 @@
|
||||
// MFEM Ultraweak DPG example
|
||||
//
|
||||
// Compile with: make uw_dpg
|
||||
//
|
||||
// sample runs
|
||||
// ./uw_dpg -m ../../../data/inline-quad.mesh -o 3 -ref 10 -test-norm 2 -do 1 -prob 1 -eps 1e-4
|
||||
// - εΔu + ∇⋅(βu) = f, in Ω
|
||||
// u = u_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// - ∇⋅σ + ∇⋅(βu) = f, in Ω
|
||||
// 1/ε σ - ∇u = 0, in Ω
|
||||
// u = u_0, on ∂Ω
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
|
||||
// û ∈ H^1/2, σ̂ ∈ H^-1/2
|
||||
// -(βu , ∇v) + (σ , ∇v) + < f̂ , v > = (f,v), ∀ v ∈ H^1(Ω)
|
||||
// (u , ∇⋅τ) + 1/ε (σ , τ) + < û , τ⋅n > = 0, ∀ τ ∈ H(div,Ω)
|
||||
// û = u_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// f̂ := βu - σ
|
||||
// û := -u
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | u | σ | û | f̂ | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v |-(βu , ∇v) | (σ , ∇v) | | < f̂ ,v > | (f,v) |
|
||||
// | | | | | | |
|
||||
// | τ | (u ,∇⋅τ) | 1/ε(σ , τ)| <û,τ⋅n> | | 0 |
|
||||
|
||||
// where (v,τ) ∈ H^1(Ω_h) × H(div,Ω_h)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
polynomial,
|
||||
EJ,
|
||||
general
|
||||
};
|
||||
|
||||
enum test_norm_type
|
||||
{
|
||||
standard,
|
||||
adjoint_graph,
|
||||
robust
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
test_norm_type test_norm;
|
||||
Vector beta;
|
||||
double epsilon;
|
||||
// Function returns the solution u, and gradient du and the Laplacian d2u
|
||||
void solution(const Vector & x, double & u, Vector & du, double & d2u);
|
||||
double exact_u(const Vector & X);
|
||||
void exact_sigma(const Vector & X, Vector & sigma);
|
||||
double exact_hatu(const Vector & X);
|
||||
void exact_hatf(const Vector & X, Vector & hatf);
|
||||
double f_exact(const Vector & X);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
int ref = 1;
|
||||
bool visualization = true;
|
||||
int iprob = 0;
|
||||
int itest_norm = 0;
|
||||
double theta = 0.7;
|
||||
epsilon = 1e0;
|
||||
|
||||
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(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&epsilon, "-eps", "--epsilon",
|
||||
"Epsilon coefficient");
|
||||
args.AddOption(&ref, "-ref", "--num_refinements",
|
||||
"Number of uniform refinements");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: polynomial, 1: EJ ,2: General");
|
||||
args.AddOption(&itest_norm, "-test-norm", "--test-norm", "Choice of test norm"
|
||||
" 0: Standard, 1: Adjoint Graph, 2: Robust");
|
||||
args.AddOption(&beta, "-beta", "--beta",
|
||||
"Vector Coefficient beta");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 2) { iprob = 2; }
|
||||
prob = (prob_type)iprob;
|
||||
test_norm = (test_norm_type)itest_norm;
|
||||
|
||||
if (prob == prob_type::EJ)
|
||||
{
|
||||
mesh_file = "../../../data/inline-quad.mesh";
|
||||
}
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
if (beta.Size() == 0)
|
||||
{
|
||||
beta.SetSize(dim);
|
||||
beta[0] = 1.;
|
||||
beta[1] = 0.;
|
||||
}
|
||||
|
||||
// Define spaces
|
||||
// L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec);
|
||||
|
||||
// Vector L2 space for σ
|
||||
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *sigma_fes = new FiniteElementSpace(&mesh,sigma_fec, dim);
|
||||
|
||||
// H^1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
|
||||
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
|
||||
|
||||
// H^-1/2 space for σ̂
|
||||
FiniteElementCollection * hatf_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
FiniteElementSpace *hatf_fes = new FiniteElementSpace(&mesh,hatf_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient eps(epsilon);
|
||||
ConstantCoefficient eps1(1./epsilon);
|
||||
ConstantCoefficient negeps1(-1./epsilon);
|
||||
ConstantCoefficient eps2(1/(epsilon*epsilon));
|
||||
|
||||
ConstantCoefficient negeps(-epsilon);
|
||||
VectorConstantCoefficient betacoeff(beta);
|
||||
Vector negbeta = beta;
|
||||
negbeta.Neg();
|
||||
|
||||
ConstantCoefficient zero(0.0);
|
||||
Vector vec0(dim); vec0 = 0.;
|
||||
VectorConstantCoefficient vzero(vec0);
|
||||
|
||||
|
||||
DenseMatrix bbt(beta.Size());
|
||||
MultVVt(beta, bbt);
|
||||
MatrixConstantCoefficient bbtcoeff(bbt);
|
||||
|
||||
|
||||
VectorConstantCoefficient negbetacoeff(negbeta);
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(sigma_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
trial_fes.Append(hatf_fes);
|
||||
test_fec.Append(v_fec);
|
||||
test_fec.Append(tau_fec);
|
||||
|
||||
|
||||
FiniteElementCollection *coeff_fec = new L2_FECollection(0,dim);
|
||||
FiniteElementSpace *coeff_fes = new FiniteElementSpace(&mesh,coeff_fec);
|
||||
GridFunction c1_gf, c2_gf;
|
||||
GridFunctionCoefficient c1_coeff(&c1_gf);
|
||||
GridFunctionCoefficient c2_coeff(&c2_gf);
|
||||
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
//-(βu , ∇v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakDivergenceIntegrator(betacoeff),0,0);
|
||||
|
||||
// (σ,∇ v)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
|
||||
|
||||
// (u ,∇⋅τ)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(negone),0,1);
|
||||
|
||||
// 1/ε (σ,τ)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(eps1)),1,1);
|
||||
|
||||
// <û,τ⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
|
||||
|
||||
// <f̂ ,v>
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,0);
|
||||
|
||||
|
||||
switch (test_norm)
|
||||
{
|
||||
case standard:
|
||||
{
|
||||
// (∇v,∇δv)
|
||||
mfem::out << "\n Test norm: Standard" << endl;
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
}
|
||||
break;
|
||||
case adjoint_graph:
|
||||
{
|
||||
mfem::out << "\n Test norm: Adjoint Graph" << endl;
|
||||
// (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (β⋅∇v, β⋅∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
// 1/ε^2 (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2),1,1);
|
||||
// 1/ε (∇v, δτ)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(eps1),0,1);
|
||||
// - (β ⋅ ∇v,∇⋅δτ)
|
||||
a->AddTestIntegrator(new MixedGradDivIntegrator(betacoeff),0,1);
|
||||
// 1/ε (τ,∇δv)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(negeps1),1,0);
|
||||
// -(β ∇⋅τ ,∇⋅δv)
|
||||
a->AddTestIntegrator(new MixedDivGradIntegrator(betacoeff),1,0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
mfem::out << "\n Test norm: Robust" << endl;
|
||||
c1_gf.SetSpace(coeff_fes);
|
||||
c2_gf.SetSpace(coeff_fes);
|
||||
Array<int> dofs;
|
||||
for (int i =0; i < mesh.GetNE(); i++)
|
||||
{
|
||||
double volume = mesh.GetElementVolume(i);
|
||||
double c1 = min(epsilon/volume, 1.);
|
||||
double c2 = min(1./epsilon, 1./volume);
|
||||
// double c2 = 1.;
|
||||
coeff_fes->GetElementDofs(i,dofs);
|
||||
c1_gf.SetSubVector(dofs,c1);
|
||||
c2_gf.SetSubVector(dofs,c2);
|
||||
}
|
||||
// c1 (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(c1_coeff),0,0);
|
||||
// ε (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(eps),0,0);
|
||||
// (β⋅∇v, β⋅∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
|
||||
// c2 (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(c2_coeff),1,1);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
FunctionCoefficient f(f_exact);
|
||||
// if (prob != prob_type::EJ)
|
||||
// {
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),0);
|
||||
// }
|
||||
|
||||
FunctionCoefficient hatuex(exact_hatu);
|
||||
VectorFunctionCoefficient hatfex(dim,exact_hatf);
|
||||
Array<int> elements_to_refine;
|
||||
FunctionCoefficient uex(exact_u);
|
||||
VectorFunctionCoefficient sigmaex(dim,exact_sigma);
|
||||
GridFunction hatu_gf;
|
||||
GridFunction hatf_gf;
|
||||
|
||||
// socketstream uex_out;
|
||||
socketstream u_out;
|
||||
// socketstream sigma_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
u_out.open(vishost, visport);
|
||||
// uex_out.open(vishost, visport);
|
||||
// sigma_out.open(vishost, visport);
|
||||
}
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
mfem::out << " Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------" << endl;
|
||||
|
||||
|
||||
for (int i = 0; i<=ref; i++)
|
||||
{
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list_uhat;
|
||||
Array<int> ess_tdof_list_fhat;
|
||||
Array<int> ess_bdr_uhat;
|
||||
Array<int> ess_bdr_fhat;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr_uhat.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr_fhat.SetSize(mesh.bdr_attributes.Max());
|
||||
// ess_bdr_uhat = 1;
|
||||
// ess_bdr_fhat = 0;
|
||||
ess_bdr_uhat = 0;
|
||||
ess_bdr_fhat = 1;
|
||||
ess_bdr_uhat[1] = 1;
|
||||
ess_bdr_fhat[1] = 0;
|
||||
hatu_fes->GetEssentialTrueDofs(ess_bdr_uhat, ess_tdof_list_uhat);
|
||||
hatf_fes->GetEssentialTrueDofs(ess_bdr_fhat, ess_tdof_list_fhat);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
int n = ess_tdof_list_uhat.Size();
|
||||
int m = ess_tdof_list_fhat.Size();
|
||||
Array<int> ess_tdof_list(n+m);
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
ess_tdof_list[j] = ess_tdof_list_uhat[j]
|
||||
+ u_fes->GetTrueVSize()
|
||||
+ sigma_fes->GetTrueVSize();
|
||||
}
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
ess_tdof_list[j+n] = ess_tdof_list_fhat[j]
|
||||
+ u_fes->GetTrueVSize()
|
||||
+ sigma_fes->GetTrueVSize()
|
||||
+ hatu_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = u_fes->GetVSize();
|
||||
offsets[2] = sigma_fes->GetVSize();
|
||||
offsets[3] = hatu_fes->GetVSize();
|
||||
offsets[4] = hatf_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
|
||||
|
||||
hatf_gf.MakeRef(hatf_fes,x.GetBlock(3));
|
||||
|
||||
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr_uhat);
|
||||
hatf_gf.ProjectBdrCoefficientNormal(hatfex,ess_bdr_fhat);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = Ah.As<BlockMatrix>();
|
||||
|
||||
// BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
// M->owns_blocks = 1;
|
||||
// for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
// {
|
||||
// M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
// }
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(200000);
|
||||
cg.SetPrintLevel(0);
|
||||
// cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
// delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
double max_resid = residuals.Max();
|
||||
for (int iel = 0; iel<mesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * max_resid)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
GridFunction uex_gf(u_fes);
|
||||
uex_gf.ProjectCoefficient(uex);
|
||||
|
||||
GridFunction sigmaex_gf(sigma_fes);
|
||||
sigmaex_gf.ProjectCoefficient(sigmaex);
|
||||
|
||||
GridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(0));
|
||||
|
||||
GridFunction sigma_gf;
|
||||
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
|
||||
|
||||
int dofs = X.Size();
|
||||
double u_err = u_gf.ComputeL2Error(uex);
|
||||
double u_norm = uex_gf.ComputeL2Error(zero);
|
||||
// mfem::out << "u_err = " << u_err << endl;
|
||||
double sigma_err = sigma_gf.ComputeL2Error(sigmaex);
|
||||
double sigma_norm = sigmaex_gf.ComputeL2Error(vzero);
|
||||
// mfem::out << "sigma_err = " << sigma_err << endl;
|
||||
double L2Error = sqrt(u_err*u_err + sigma_err*sigma_err);
|
||||
double L2norm = sqrt(u_norm * u_norm + sigma_norm * sigma_norm);
|
||||
|
||||
double rel_error = L2Error/L2norm;
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = L2Error;
|
||||
res0 = residual;
|
||||
dof0 = dofs;
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::resetiosflags(std::ios::showbase)
|
||||
<< std::endl;
|
||||
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
// uex_out.precision(8);
|
||||
// uex_out << "solution\n" << mesh << uex_gf <<
|
||||
// "window_title 'Exact u' "
|
||||
// << flush;
|
||||
u_out.precision(8);
|
||||
u_out << "solution\n" << mesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
// sigma_out.precision(8);
|
||||
// sigma_out << "solution\n" << mesh << sigma_gf <<
|
||||
// "window_title 'Numerical flux' "
|
||||
// << flush;
|
||||
}
|
||||
|
||||
if (i == ref)
|
||||
break;
|
||||
|
||||
mesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
|
||||
if (test_norm == test_norm_type::robust)
|
||||
{
|
||||
coeff_fes->Update();
|
||||
c1_gf.Update();
|
||||
c2_gf.Update();
|
||||
Array<int> dofs;
|
||||
for (int i = 0; i < mesh.GetNE(); i++)
|
||||
{
|
||||
double volume = mesh.GetElementVolume(i);
|
||||
double c1 = min(epsilon/volume, 1.);
|
||||
double c2 = min(1./epsilon, 1./volume);
|
||||
// double c2 = 1.;
|
||||
coeff_fes->GetElementDofs(i,dofs);
|
||||
c1_gf.SetSubVector(dofs,c1);
|
||||
c2_gf.SetSubVector(dofs,c2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete coeff_fes;
|
||||
delete coeff_fec;
|
||||
delete a;
|
||||
delete tau_fec;
|
||||
delete v_fec;
|
||||
delete hatf_fes;
|
||||
delete hatf_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete sigma_fes;
|
||||
delete sigma_fec;
|
||||
delete u_fec;
|
||||
delete u_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void solution(const Vector & X, double & u, Vector & du, double & d2u)
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double z = 0.;
|
||||
if (X.Size() == 3) z = X[2];
|
||||
du.SetSize(X.Size());
|
||||
du = 0.;
|
||||
d2u = 0.;
|
||||
|
||||
switch(prob)
|
||||
{
|
||||
case polynomial:
|
||||
{
|
||||
int n=2;
|
||||
int m=2;
|
||||
u = pow(x,n)*pow(y,m);
|
||||
du[0] = n * pow(x,n-1) * pow(y,m);
|
||||
du[1] = m * pow(x,n) * pow(y,m-1);
|
||||
d2u = n * (n-1) * pow(x,n-2) * pow(y,m)
|
||||
+ m * (m-1) * pow(x,n) * pow(y,m-2);
|
||||
}
|
||||
break;
|
||||
case EJ:
|
||||
{
|
||||
double alpha = sqrt(1. + 4. * epsilon * epsilon * M_PI * M_PI);
|
||||
double r1 = (1. + alpha) / (2.*epsilon);
|
||||
double r2 = (1. - alpha) / (2.*epsilon);
|
||||
double denom = exp(-r2) - exp(-r1);
|
||||
|
||||
|
||||
double g1 = exp(r2*(x-1.));
|
||||
double g1_x = r2*g1;
|
||||
double g1_xx = r2*g1_x;
|
||||
double g2 = exp(r1*(x-1.));
|
||||
double g2_x = r1*g2;
|
||||
double g2_xx = r1*g2_x;
|
||||
double g = g1-g2;
|
||||
double g_x = g1_x - g2_x;
|
||||
double g_xx = g1_xx - g2_xx;
|
||||
|
||||
|
||||
u = g * cos(M_PI * y)/denom;
|
||||
double u_x = g_x * cos(M_PI * y)/denom;
|
||||
double u_xx = g_xx * cos(M_PI * y)/denom;
|
||||
double u_y = -M_PI * g * sin(M_PI*y)/denom;
|
||||
double u_yy = -M_PI * M_PI * u;
|
||||
du[0] = u_x;
|
||||
du[1] = u_y;
|
||||
d2u = u_xx + u_yy;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double alpha = M_PI * (x + y + z);
|
||||
u = sin(alpha);
|
||||
du.SetSize(X.Size());
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
du[i] = M_PI * cos(alpha);
|
||||
}
|
||||
d2u = - M_PI*M_PI * u * du.Size();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double exact_u(const Vector & X)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
return u;
|
||||
}
|
||||
|
||||
void exact_sigma(const Vector & X, Vector & sigma)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
// σ = ε ∇ u
|
||||
sigma = du;
|
||||
sigma *= epsilon;
|
||||
}
|
||||
|
||||
double exact_hatu(const Vector & X)
|
||||
{
|
||||
return -exact_u(X);
|
||||
}
|
||||
|
||||
void exact_hatf(const Vector & X, Vector & hatf)
|
||||
{
|
||||
Vector sigma;
|
||||
exact_sigma(X,sigma);
|
||||
double u = exact_u(X);
|
||||
hatf.SetSize(X.Size());
|
||||
for (int i = 0; i<hatf.Size(); i++)
|
||||
{
|
||||
hatf[i] = beta[i] * u - sigma[i];
|
||||
}
|
||||
}
|
||||
|
||||
double f_exact(const Vector & X)
|
||||
{
|
||||
// f = - εΔu + ∇⋅(βu)
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
|
||||
double s = 0;
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
s += beta[i] * du[i];
|
||||
}
|
||||
return -epsilon * d2u + s;
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
// MFEM Ultraweak DPG example
|
||||
//
|
||||
// Compile with: make uw_dpgp
|
||||
//
|
||||
// sample runs
|
||||
// mpirun -np 6 ./uw_dpgp -m ../../../data/inline-quad.mesh -o 3 -ref 10 -test-norm 2 -do 1 -prob 1 -eps 1e-4
|
||||
// - εΔu + ∇⋅(βu) = f, in Ω
|
||||
// u = u_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// - ∇⋅σ + ∇⋅(βu) = f, in Ω
|
||||
// 1/ε σ - ∇u = 0, in Ω
|
||||
// u = u_0, on ∂Ω
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
|
||||
// û ∈ H^1/2, f̂ ∈ H^-1/2
|
||||
// -(βu , ∇v) + (σ , ∇v) + < f̂ , v > = (f,v), ∀ v ∈ H^1(Ω)
|
||||
// (u , ∇⋅τ) + 1/ε (σ , τ) + < û , τ⋅n > = 0, ∀ τ ∈ H(div,Ω)
|
||||
// û = u_0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// f̂ := βu - σ
|
||||
// û := -u
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | u | σ | û | f̂ | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | v |-(βu , ∇v) | (σ , ∇v) | | < f̂ ,v > | (f,v) |
|
||||
// | | | | | | |
|
||||
// | τ | (u ,∇⋅τ) | 1/ε(σ , τ)| <û,τ⋅n> | | 0 |
|
||||
|
||||
// where (v,τ) ∈ H^1(Ω_h) × H(div,Ω_h)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
polynomial,
|
||||
EJ,
|
||||
general
|
||||
};
|
||||
|
||||
enum test_norm_type
|
||||
{
|
||||
standard,
|
||||
adjoint_graph,
|
||||
robust
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
test_norm_type test_norm;
|
||||
Vector beta;
|
||||
double epsilon;
|
||||
// Function returns the solution u, and gradient du and the Laplacian d2u
|
||||
void solution(const Vector & x, double & u, Vector & du, double & d2u);
|
||||
double exact_u(const Vector & X);
|
||||
void exact_sigma(const Vector & X, Vector & sigma);
|
||||
double exact_hatu(const Vector & X);
|
||||
void exact_hatf(const Vector & X, Vector & hatf);
|
||||
double f_exact(const Vector & X);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
int ref = 1;
|
||||
bool visualization = true;
|
||||
int iprob = 0;
|
||||
int itest_norm = 0;
|
||||
double theta = 0.7;
|
||||
bool static_cond = false;
|
||||
epsilon = 1e0;
|
||||
|
||||
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(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&epsilon, "-eps", "--epsilon",
|
||||
"Epsilon coefficient");
|
||||
args.AddOption(&ref, "-ref", "--num_refinements",
|
||||
"Number of uniform refinements");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: lshape, 1: General");
|
||||
args.AddOption(&itest_norm, "-test-norm", "--test-norm", "Choice of test norm"
|
||||
" 0: Standard, 1: Adjoint Graph, 2: Robust");
|
||||
args.AddOption(&beta, "-beta", "--beta",
|
||||
"Vector Coefficient beta");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (iprob > 2) { iprob = 2; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
test_norm = (test_norm_type)itest_norm;
|
||||
|
||||
if (prob == prob_type::EJ)
|
||||
{
|
||||
mesh_file = "../../../data/inline-quad.mesh";
|
||||
}
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
if (beta.Size() == 0)
|
||||
{
|
||||
beta.SetSize(dim);
|
||||
beta[0] = 1.;
|
||||
beta[1] = 0.;
|
||||
}
|
||||
|
||||
mesh.EnsureNCMesh();
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// Define spaces
|
||||
// L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec);
|
||||
|
||||
// Vector L2 space for σ
|
||||
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *sigma_fes = new ParFiniteElementSpace(&pmesh,sigma_fec, dim);
|
||||
|
||||
// H^1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
|
||||
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
|
||||
|
||||
// H^-1/2 space for σ̂
|
||||
FiniteElementCollection * hatf_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *hatf_fes = new ParFiniteElementSpace(&pmesh,hatf_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
|
||||
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
ConstantCoefficient eps(epsilon);
|
||||
ConstantCoefficient eps1(1./epsilon);
|
||||
ConstantCoefficient negeps1(-1./epsilon);
|
||||
ConstantCoefficient eps2(1/(epsilon*epsilon));
|
||||
|
||||
ConstantCoefficient negeps(-epsilon);
|
||||
VectorConstantCoefficient betacoeff(beta);
|
||||
Vector negbeta = beta;
|
||||
negbeta.Neg();
|
||||
|
||||
DenseMatrix bbt(beta.Size());
|
||||
MultVVt(beta, bbt);
|
||||
MatrixConstantCoefficient bbtcoeff(bbt);
|
||||
|
||||
|
||||
VectorConstantCoefficient negbetacoeff(negbeta);
|
||||
// Normal equation weak formulation
|
||||
Array<ParFiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(sigma_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
trial_fes.Append(hatf_fes);
|
||||
test_fec.Append(v_fec);
|
||||
test_fec.Append(tau_fec);
|
||||
|
||||
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
//-(βu , ∇v)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakDivergenceIntegrator(betacoeff),0,0);
|
||||
|
||||
// (σ,∇ v)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
|
||||
|
||||
// (u ,∇⋅τ)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(negone),0,1);
|
||||
|
||||
// 1/ε (σ,τ)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(eps1)),1,1);
|
||||
|
||||
// <û,τ⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
|
||||
|
||||
// <f̂ ,v>
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,0);
|
||||
|
||||
|
||||
FiniteElementCollection *coeff_fec = new L2_FECollection(0,dim);
|
||||
ParFiniteElementSpace *coeff_fes = new ParFiniteElementSpace(&pmesh,coeff_fec);
|
||||
ParGridFunction c1_gf, c2_gf;
|
||||
GridFunctionCoefficient c1_coeff(&c1_gf);
|
||||
GridFunctionCoefficient c2_coeff(&c2_gf);
|
||||
|
||||
switch (test_norm)
|
||||
{
|
||||
case standard:
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Test norm: Standard" << endl;
|
||||
}
|
||||
// (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
}
|
||||
break;
|
||||
case adjoint_graph:
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Test norm: Adjoint Graph" << endl;
|
||||
}
|
||||
// (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
// (β⋅∇v, β⋅∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),0,0);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
|
||||
// 1/ε^2 (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2),1,1);
|
||||
// 1/ε (∇v, δτ)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(eps1),0,1);
|
||||
// - (β ⋅ ∇v,∇⋅δτ)
|
||||
a->AddTestIntegrator(new MixedGradDivIntegrator(betacoeff),0,1);
|
||||
// 1/ε (τ,∇δv)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(negeps1),1,0);
|
||||
// -(β ∇⋅τ ,∇⋅δv)
|
||||
a->AddTestIntegrator(new MixedDivGradIntegrator(betacoeff),1,0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Test norm: Robust" << endl;
|
||||
}
|
||||
c1_gf.SetSpace(coeff_fes);
|
||||
c2_gf.SetSpace(coeff_fes);
|
||||
Array<int> dofs;
|
||||
for (int i =0; i < pmesh.GetNE(); i++)
|
||||
{
|
||||
double volume = pmesh.GetElementVolume(i);
|
||||
double c1 = min(epsilon/volume, 1.);
|
||||
double c2 = min(1./epsilon, 1./volume);
|
||||
coeff_fes->GetElementDofs(i,dofs);
|
||||
c1_gf.SetSubVector(dofs,c1);
|
||||
c2_gf.SetSubVector(dofs,c2);
|
||||
}
|
||||
// c1 (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(c1_coeff),0,0);
|
||||
// ε (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(eps),0,0);
|
||||
// (β⋅∇v, β⋅∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
|
||||
// c2 (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(c2_coeff),1,1);
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
FunctionCoefficient f(f_exact);
|
||||
// if (prob != prob_type::EJ)
|
||||
// {
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),0);
|
||||
// }
|
||||
|
||||
FunctionCoefficient hatuex(exact_hatu);
|
||||
VectorFunctionCoefficient hatfex(dim,exact_hatf);
|
||||
Array<int> elements_to_refine;
|
||||
FunctionCoefficient uex(exact_u);
|
||||
VectorFunctionCoefficient sigmaex(dim,exact_sigma);
|
||||
|
||||
ParGridFunction hatu_gf;
|
||||
ParGridFunction hatf_gf;
|
||||
|
||||
// socketstream uex_out;
|
||||
socketstream u_out;
|
||||
// socketstream sigma_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
u_out.open(vishost, visport);
|
||||
// uex_out.open(vishost, visport);
|
||||
// sigma_out.open(vishost, visport);
|
||||
}
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << " Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |"
|
||||
<< " CG iter |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------" << endl;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list_uhat;
|
||||
Array<int> ess_tdof_list_fhat;
|
||||
Array<int> ess_bdr_uhat;
|
||||
Array<int> ess_bdr_fhat;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr_uhat.SetSize(pmesh.bdr_attributes.Max());
|
||||
ess_bdr_fhat.SetSize(pmesh.bdr_attributes.Max());
|
||||
// ess_bdr_uhat = 1;
|
||||
// ess_bdr_fhat = 0;
|
||||
ess_bdr_uhat = 0;
|
||||
ess_bdr_fhat = 1;
|
||||
ess_bdr_uhat[1] = 1;
|
||||
ess_bdr_fhat[1] = 0;
|
||||
hatu_fes->GetEssentialTrueDofs(ess_bdr_uhat, ess_tdof_list_uhat);
|
||||
hatf_fes->GetEssentialTrueDofs(ess_bdr_fhat, ess_tdof_list_fhat);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
int n = ess_tdof_list_uhat.Size();
|
||||
int m = ess_tdof_list_fhat.Size();
|
||||
Array<int> ess_tdof_list(n+m);
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
ess_tdof_list[j] = ess_tdof_list_uhat[j]
|
||||
+ u_fes->GetTrueVSize()
|
||||
+ sigma_fes->GetTrueVSize();
|
||||
}
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
ess_tdof_list[j+n] = ess_tdof_list_fhat[j]
|
||||
+ u_fes->GetTrueVSize()
|
||||
+ sigma_fes->GetTrueVSize()
|
||||
+ hatu_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = u_fes->GetVSize();
|
||||
offsets[2] = sigma_fes->GetVSize();
|
||||
offsets[3] = hatu_fes->GetVSize();
|
||||
offsets[4] = hatf_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
|
||||
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr_uhat);
|
||||
|
||||
hatf_gf.MakeRef(hatf_fes,x.GetBlock(3));
|
||||
hatf_gf.ProjectBdrCoefficientNormal(hatfex,ess_bdr_fhat);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockOperator * A = Ah.As<BlockOperator>();
|
||||
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
int skip = 0;
|
||||
if (!static_cond)
|
||||
{
|
||||
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
|
||||
HypreBoomerAMG * amg1 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
|
||||
amg0->SetPrintLevel(0);
|
||||
amg1->SetPrintLevel(0);
|
||||
M->SetDiagonalBlock(0,amg0);
|
||||
M->SetDiagonalBlock(1,amg1);
|
||||
skip = 2;
|
||||
}
|
||||
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,skip));
|
||||
amg2->SetPrintLevel(0);
|
||||
M->SetDiagonalBlock(skip,amg2);
|
||||
|
||||
HypreSolver * prec;
|
||||
if (dim == 2)
|
||||
{
|
||||
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
|
||||
}
|
||||
M->SetDiagonalBlock(skip+1,prec);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-6);
|
||||
cg.SetMaxIter(200000);
|
||||
cg.SetPrintLevel(-1);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
int num_iter = cg.GetNumIterations();
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
double maxresidual = residuals.Max();
|
||||
|
||||
double gresidual = residual * residual;
|
||||
|
||||
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE,&gresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
|
||||
|
||||
gresidual = sqrt(gresidual);
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
for (int iel = 0; iel<pmesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * maxresidual)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
ParGridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(0));
|
||||
|
||||
ParGridFunction sigma_gf;
|
||||
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
|
||||
|
||||
int dofs = u_fes->GlobalTrueVSize()
|
||||
+ sigma_fes->GlobalTrueVSize()
|
||||
+ hatu_fes->GlobalTrueVSize()
|
||||
+ hatf_fes->GlobalTrueVSize();
|
||||
|
||||
double u_err = u_gf.ComputeL2Error(uex);
|
||||
double sigma_err = sigma_gf.ComputeL2Error(sigmaex);
|
||||
double L2Error = sqrt(u_err*u_err + sigma_err*sigma_err);
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/gresidual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = L2Error;
|
||||
res0 = gresidual;
|
||||
dof0 = dofs;
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::setw(6) << std::fixed << num_iter << " | "
|
||||
<< std::resetiosflags(std::ios::showbase)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
// uex_out.precision(8);
|
||||
// uex_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
// uex_out << "solution\n" << pmesh << uex_gf <<
|
||||
// "window_title 'Exact u' "
|
||||
// << flush;
|
||||
|
||||
u_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
u_out.precision(8);
|
||||
u_out << "solution\n" << pmesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
|
||||
// sigma_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
// sigma_out.precision(8);
|
||||
// sigma_out << "solution\n" << pmesh << sigma_gf <<
|
||||
// "window_title 'Numerical flux' "
|
||||
// << flush;
|
||||
}
|
||||
|
||||
if (i == ref-1)
|
||||
break;
|
||||
|
||||
pmesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
|
||||
if (test_norm == test_norm_type::robust)
|
||||
{
|
||||
coeff_fes->Update();
|
||||
c1_gf.Update();
|
||||
c2_gf.Update();
|
||||
Array<int> edofs;
|
||||
for (int i = 0; i < pmesh.GetNE(); i++)
|
||||
{
|
||||
double volume = pmesh.GetElementVolume(i);
|
||||
double c1 = min(epsilon/volume, 1.);
|
||||
double c2 = min(1./epsilon, 1./volume);
|
||||
coeff_fes->GetElementDofs(i,edofs);
|
||||
c1_gf.SetSubVector(edofs,c1);
|
||||
c2_gf.SetSubVector(edofs,c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete coeff_fes;
|
||||
delete coeff_fec;
|
||||
delete a;
|
||||
delete tau_fec;
|
||||
delete v_fec;
|
||||
delete hatf_fes;
|
||||
delete hatf_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete sigma_fec;
|
||||
delete sigma_fes;
|
||||
delete u_fec;
|
||||
delete u_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void solution(const Vector & X, double & u, Vector & du, double & d2u)
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double z = 0.;
|
||||
if (X.Size() == 3) z = X[2];
|
||||
du.SetSize(X.Size());
|
||||
du = 0.;
|
||||
d2u = 0.;
|
||||
|
||||
switch(prob)
|
||||
{
|
||||
case polynomial:
|
||||
{
|
||||
int n=2;
|
||||
int m=2;
|
||||
u = pow(x,n)*pow(y,m);
|
||||
du[0] = n * pow(x,n-1) * pow(y,m);
|
||||
du[1] = m * pow(x,n) * pow(y,m-1);
|
||||
d2u = n * (n-1) * pow(x,n-2) * pow(y,m)
|
||||
+ m * (m-1) * pow(x,n) * pow(y,m-2);
|
||||
}
|
||||
break;
|
||||
case EJ:
|
||||
{
|
||||
double alpha = sqrt(1. + 4. * epsilon * epsilon * M_PI * M_PI);
|
||||
double r1 = (1. + alpha) / (2.*epsilon);
|
||||
double r2 = (1. - alpha) / (2.*epsilon);
|
||||
double denom = exp(-r2) - exp(-r1);
|
||||
|
||||
|
||||
double g1 = exp(r2*(x-1.));
|
||||
double g1_x = r2*g1;
|
||||
double g1_xx = r2*g1_x;
|
||||
double g2 = exp(r1*(x-1.));
|
||||
double g2_x = r1*g2;
|
||||
double g2_xx = r1*g2_x;
|
||||
double g = g1-g2;
|
||||
double g_x = g1_x - g2_x;
|
||||
double g_xx = g1_xx - g2_xx;
|
||||
|
||||
|
||||
u = g * cos(M_PI * y)/denom;
|
||||
double u_x = g_x * cos(M_PI * y)/denom;
|
||||
double u_xx = g_xx * cos(M_PI * y)/denom;
|
||||
double u_y = -M_PI * g * sin(M_PI*y)/denom;
|
||||
double u_yy = -M_PI * M_PI * u;
|
||||
du[0] = u_x;
|
||||
du[1] = u_y;
|
||||
d2u = u_xx + u_yy;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double alpha = M_PI * (x + y + z);
|
||||
u = sin(alpha);
|
||||
du.SetSize(X.Size());
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
du[i] = M_PI * cos(alpha);
|
||||
}
|
||||
d2u = - M_PI*M_PI * u * du.Size();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double exact_u(const Vector & X)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
return u;
|
||||
}
|
||||
|
||||
void exact_sigma(const Vector & X, Vector & sigma)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
// σ = ε ∇ u
|
||||
sigma = du;
|
||||
sigma *= epsilon;
|
||||
}
|
||||
|
||||
double exact_hatu(const Vector & X)
|
||||
{
|
||||
return -exact_u(X);
|
||||
}
|
||||
|
||||
void exact_hatf(const Vector & X, Vector & hatf)
|
||||
{
|
||||
Vector sigma;
|
||||
exact_sigma(X,sigma);
|
||||
double u = exact_u(X);
|
||||
hatf.SetSize(X.Size());
|
||||
for (int i = 0; i<hatf.Size(); i++)
|
||||
{
|
||||
hatf[i] = beta[i] * u - sigma[i];
|
||||
}
|
||||
}
|
||||
|
||||
double f_exact(const Vector & X)
|
||||
{
|
||||
// f = - εΔu + ∇⋅(βu)
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
|
||||
double s = 0;
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
s += beta[i] * du[i];
|
||||
}
|
||||
return -epsilon * d2u + s;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
// MFEM Fosls 1
|
||||
//
|
||||
// Compile with: make blkfosls
|
||||
//
|
||||
// - Δ u = f, in Ω
|
||||
// u = 0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ u - σ = 0, in Ω
|
||||
// - ∇⋅σ = f, in Ω
|
||||
// u = 0, in ∂Ω
|
||||
|
||||
// FOSLS:
|
||||
// minimize 1/2(||∇u - σ||^2 + ||∇ ⋅ σ - f||^2)
|
||||
|
||||
|
||||
// -------------------------------------------------
|
||||
// | | u | σ | RHS |
|
||||
// -------------------------------------------------
|
||||
// | v | (∇u,∇v) | -(σ,∇v) | 0 |
|
||||
// | | | | |
|
||||
// | τ | -(∇u,τ) | (∇⋅σ, ∇⋅τ) + (σ,τ) | -(f,∇⋅τ ) |
|
||||
|
||||
// where (u,τ) ∈ H^1(Ω) × H(div,Ω)
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec0 = new H1_FECollection(order, dim);
|
||||
FiniteElementCollection *fec1 = new RT_FECollection(order-1, dim);
|
||||
FiniteElementSpace fespace0(&mesh, fec0);
|
||||
FiniteElementSpace fespace1(&mesh, fec1);
|
||||
|
||||
Array<FiniteElementSpace *> fespaces(2);
|
||||
fespaces[0] = &fespace0;
|
||||
fespaces[1] = &fespace1;
|
||||
|
||||
Array<int> ess_bdr;
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespaces[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
BlockBilinearForm a(fespaces);
|
||||
a.SetDiagonalPolicy(mfem::Operator::DIAG_KEEP);
|
||||
|
||||
cout << "H1 fespace = " << fespace0.GetVSize() << endl;
|
||||
cout << "RT fespace = " << fespace1.GetVSize() << endl;
|
||||
|
||||
FiniteElementCollection *fec2 = new RT_Trace_FECollection(order-1, dim);
|
||||
FiniteElementSpace RT_trace_fes(&mesh, fec2);
|
||||
cout << "RT trace = " << RT_trace_fes.GetVSize() << endl;
|
||||
|
||||
// for (int i = 0; i<mesh.GetNE(); i++)
|
||||
// {
|
||||
// // const FiniteElement * fe = fespace1.GetFE(i);
|
||||
// // fespace1.GetTraceElement()
|
||||
// Array<int> faces, ori;
|
||||
// mesh.GetElementEdges(i, faces, ori);
|
||||
// for (int f = 0; f<faces.Size(); f++)
|
||||
// {
|
||||
// const FiniteElement * fe_trace = RT_trace_fes.GetFaceElement(faces[f]);
|
||||
// cout << fe_trace->GetDof() << endl;
|
||||
// Array<int> face_dofs;
|
||||
// RT_trace_fes.GetFaceDofs(faces[f],face_dofs);
|
||||
// cout << "face dofs = " << endl;
|
||||
// face_dofs.Print();
|
||||
// }
|
||||
|
||||
// // cout << fe->GetGeomType() << endl;
|
||||
// Array<int> vdofs;
|
||||
// RT_trace_fes.GetElementVDofs(i, vdofs);
|
||||
// cout << "trace dofs = " << endl;
|
||||
// vdofs.Print();
|
||||
// fespace1.GetElementVDofs(i, vdofs);
|
||||
// cout << "elem dofs = " << endl;
|
||||
// vdofs.Print();
|
||||
// cin.get();
|
||||
// }
|
||||
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
|
||||
Array2D<BilinearFormIntegrator * > blfi(2,2);
|
||||
blfi(0,0) = new DiffusionIntegrator(one);
|
||||
blfi(0,1) = new MixedVectorWeakDivergenceIntegrator(one);
|
||||
blfi(1,0) = new MixedVectorGradientIntegrator(negone);
|
||||
|
||||
BilinearFormIntegrator * divdiv = new DivDivIntegrator(one);
|
||||
BilinearFormIntegrator * mass = new VectorFEMassIntegrator(one);
|
||||
SumIntegrator * suminteg = new SumIntegrator();
|
||||
suminteg->AddIntegrator(divdiv);
|
||||
suminteg->AddIntegrator(mass);
|
||||
blfi(1,1) = suminteg;
|
||||
|
||||
TestBlockBilinearFormIntegrator * integ = new TestBlockBilinearFormIntegrator();
|
||||
integ->SetIntegrators(blfi);
|
||||
a.AddDomainIntegrator(integ);
|
||||
a.Assemble();
|
||||
|
||||
|
||||
BlockLinearForm b(fespaces);
|
||||
|
||||
TestBlockLinearFormIntegrator * lininteg = new TestBlockLinearFormIntegrator();
|
||||
Array<LinearFormIntegrator * > lfi(2);
|
||||
lfi[0] = nullptr;
|
||||
lfi[1] = new VectorFEDomainLFDivIntegrator(negone);
|
||||
lininteg->SetIntegrators(lfi);
|
||||
b.AddDomainIntegrator(lininteg);
|
||||
b.Assemble();
|
||||
|
||||
|
||||
// need to implement blkgridfunction later but for now Vector would do
|
||||
int size = 0;
|
||||
for (int i = 0; i<fespaces.Size(); i++)
|
||||
{
|
||||
size += fespaces[i]->GetVSize();
|
||||
}
|
||||
|
||||
Vector x(size);
|
||||
x = 0.0;
|
||||
|
||||
OperatorPtr A;
|
||||
Vector X,B;
|
||||
a.FormLinearSystem(ess_tdof_list,x,b,A,X,B);
|
||||
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-6);
|
||||
cg.SetMaxIter(200);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
|
||||
a.RecoverFEMSolution(X,b,x);
|
||||
|
||||
GridFunction u_gf, sigma_gf;
|
||||
double *data = x.GetData();
|
||||
u_gf.MakeRef(fespaces[0],&data[0]);
|
||||
sigma_gf.MakeRef(fespaces[1],&data[fespaces[0]->GetVSize()]);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
socketstream sols_sock(vishost, visport);
|
||||
sols_sock.precision(8);
|
||||
sols_sock << "solution\n" << mesh << sigma_gf <<
|
||||
"window_title 'Numerical sigma' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
delete fec0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
// MFEM Fosls example
|
||||
//
|
||||
// Compile with: make fosls
|
||||
//
|
||||
// - Δ u = f, in Ω
|
||||
// u = 0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ u - σ = 0, in Ω
|
||||
// - ∇⋅σ = f, in Ω
|
||||
// u = 0, in ∂Ω
|
||||
|
||||
// FOSLS:
|
||||
// minimize 1/2(||∇u - σ||^2 + ||∇ ⋅ σ - f||^2)
|
||||
|
||||
|
||||
// -------------------------------------------------
|
||||
// | | u | σ | RHS |
|
||||
// -------------------------------------------------
|
||||
// | v | (∇u,∇v) | -(σ,∇v) | 0 |
|
||||
// | | | | |
|
||||
// | τ | -(∇u,τ) | (∇⋅σ, ∇⋅τ) + (σ,τ) | -(f,∇⋅τ ) |
|
||||
|
||||
// where (u,τ) ∈ H^1(Ω) × H(div,Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
FiniteElementCollection *H1fec = new H1_FECollection(order,dim);
|
||||
FiniteElementSpace *H1fes = new FiniteElementSpace(&mesh, H1fec);
|
||||
|
||||
FiniteElementCollection *RTfec = new RT_FECollection(order-1,dim);
|
||||
FiniteElementSpace *RTfes = new FiniteElementSpace(&mesh, RTfec);
|
||||
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
|
||||
// Linear forms
|
||||
LinearForm b_0(H1fes);
|
||||
// (f,∇⋅τ )
|
||||
LinearForm b_1(RTfes);
|
||||
b_1.AddDomainIntegrator(new VectorFEDomainLFDivIntegrator(negone));
|
||||
|
||||
// Bilinear forms
|
||||
// (∇u,∇v)
|
||||
BilinearForm a_00(H1fes);
|
||||
a_00.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// -(σ,∇v)
|
||||
MixedBilinearForm a_01(RTfes, H1fes);
|
||||
a_01.AddDomainIntegrator(new MixedVectorWeakDivergenceIntegrator(
|
||||
one)); // (-1 is included)
|
||||
|
||||
// // -(∇u,τ)
|
||||
// MixedBilinearForm()
|
||||
MixedBilinearForm a_10(H1fes, RTfes);
|
||||
a_10.AddDomainIntegrator(new MixedVectorGradientIntegrator(negone));
|
||||
|
||||
// (∇⋅σ, ∇⋅τ) + (σ,τ)
|
||||
|
||||
BilinearForm a_11(RTfes);
|
||||
a_11.AddDomainIntegrator(new DivDivIntegrator(one));
|
||||
a_11.AddDomainIntegrator(new VectorFEMassIntegrator(one));
|
||||
|
||||
|
||||
Array<int> ess_bdr;
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
H1fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
Array<int> block_Toffsets(3);
|
||||
block_Toffsets[0] = 0;
|
||||
block_Toffsets[1] = H1fes->GetTrueVSize();
|
||||
block_Toffsets[2] = RTfes->GetTrueVSize();
|
||||
block_Toffsets.PartialSum();
|
||||
|
||||
Vector rhs_H1(H1fes->GetVSize()); rhs_H1 = 0.;
|
||||
Vector rhs_RT(RTfes->GetVSize()); rhs_RT = 0.;
|
||||
|
||||
Vector x_H1(H1fes->GetVSize()); x_H1 = 0.;
|
||||
Vector x_RT(RTfes->GetVSize()); x_RT = 0.;
|
||||
|
||||
|
||||
Vector RHS_H1(H1fes->GetTrueVSize()); RHS_H1 = 0.0;
|
||||
Vector RHS_RT(RTfes->GetTrueVSize()); RHS_RT = 0.0;
|
||||
|
||||
Vector X_H1(H1fes->GetTrueVSize()); X_H1 = 0.0;
|
||||
Vector X_RT(RTfes->GetTrueVSize()); X_RT = 0.0;
|
||||
|
||||
|
||||
b_0.Update(H1fes,rhs_H1,0);
|
||||
b_0.Assemble();
|
||||
|
||||
b_1.Update(RTfes,rhs_RT,0);
|
||||
b_1.Assemble();
|
||||
|
||||
|
||||
// Assembly and BC
|
||||
a_00.Assemble();
|
||||
SparseMatrix A_00;
|
||||
a_00.FormLinearSystem(ess_tdof_list,x_H1,rhs_H1,
|
||||
A_00,X_H1,RHS_H1);
|
||||
|
||||
a_01.Assemble();
|
||||
SparseMatrix A_01;
|
||||
Array<int> empty;
|
||||
a_01.FormRectangularSystemMatrix(empty, ess_tdof_list,A_01);
|
||||
|
||||
|
||||
a_10.Assemble();
|
||||
SparseMatrix A_10;
|
||||
|
||||
a_10.FormRectangularLinearSystem(ess_tdof_list,empty,x_H1,rhs_RT,
|
||||
A_10,X_H1,RHS_RT);
|
||||
|
||||
a_11.Assemble();
|
||||
SparseMatrix A_11;
|
||||
a_11.FormSystemMatrix(empty,A_11);
|
||||
|
||||
|
||||
BlockMatrix BlockA(block_Toffsets);
|
||||
BlockA.SetBlock(0,0,&A_00);
|
||||
BlockA.SetBlock(0,1,&A_01);
|
||||
BlockA.SetBlock(1,0,&A_10);
|
||||
BlockA.SetBlock(1,1,&A_11);
|
||||
|
||||
|
||||
BlockVector RHS(block_Toffsets);
|
||||
RHS.GetBlock(0) = RHS_H1;
|
||||
RHS.GetBlock(1) = RHS_RT;
|
||||
|
||||
BlockVector X(block_Toffsets);
|
||||
X.GetBlock(0) = X_H1;
|
||||
X.GetBlock(1) = X_RT;
|
||||
|
||||
|
||||
SparseMatrix * A = BlockA.CreateMonolithic();
|
||||
|
||||
GSSmoother M(*A);
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-6);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(RHS, X);
|
||||
|
||||
GridFunction u_gf(H1fes), sigma_gf(RTfes);
|
||||
u_gf = 0.;
|
||||
sigma_gf = 0.;
|
||||
|
||||
const SparseMatrix * P = H1fes->GetConformingProlongation();
|
||||
if (P)
|
||||
{
|
||||
a_00.RecoverFEMSolution(X.GetBlock(0),rhs_H1,u_gf);
|
||||
a_11.RecoverFEMSolution(X.GetBlock(1),rhs_RT,sigma_gf);
|
||||
}
|
||||
else
|
||||
{
|
||||
u_gf.MakeRef(X.GetBlock(0),0);
|
||||
sigma_gf.MakeRef(X.GetBlock(1),0);
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
socketstream sols_sock(vishost, visport);
|
||||
sols_sock.precision(8);
|
||||
sols_sock << "solution\n" << mesh << sigma_gf <<
|
||||
"window_title 'Numerical sigma' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/diffusion,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = blkfosls fosls primal_dpg \
|
||||
uw_dpg
|
||||
PAR_EXAMPLES = uw_dpgp
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
rm -rf ParaView
|
||||
|
||||
clean-exec:
|
||||
@@ -0,0 +1,179 @@
|
||||
// MFEM primal_dpg example
|
||||
//
|
||||
// Compile with: make primal_dpg
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command line options
|
||||
const char *mesh_file = "../../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.ParseCheck();
|
||||
|
||||
// 2. Read the mesh from the given mesh file, and refine once uniformly.
|
||||
Mesh mesh(mesh_file);
|
||||
// mesh.UniformRefinement();
|
||||
|
||||
// 3. Define a finite element space on the mesh. Here we use H1 continuous
|
||||
// high-order Lagrange finite elements of the given order.
|
||||
H1_FECollection fec(order, mesh.Dimension());
|
||||
FiniteElementSpace H1fes(&mesh, &fec);
|
||||
|
||||
RT_Trace_FECollection trace_fec(order-1, mesh.Dimension());
|
||||
FiniteElementSpace RTtrace_fes(&mesh, &trace_fec);
|
||||
|
||||
int dim = mesh.Dimension();
|
||||
int test_order = order;
|
||||
if (dim == 2 && (order%2 == 0 || (mesh.MeshGenerator() & 2 && order > 1)))
|
||||
{
|
||||
test_order++;
|
||||
}
|
||||
|
||||
test_order++;
|
||||
|
||||
H1_FECollection test_fec(test_order,mesh.Dimension());
|
||||
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fecs;
|
||||
|
||||
trial_fes.Append(&H1fes);
|
||||
trial_fes.Append(&RTtrace_fes);
|
||||
test_fecs.Append(&test_fec);
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
|
||||
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
a->AddTrialIntegrator(new DiffusionIntegrator(one),0,0);
|
||||
a->AddTrialIntegrator(new TraceIntegrator,1,0);
|
||||
|
||||
BilinearFormIntegrator * diffusion = new DiffusionIntegrator(one);
|
||||
BilinearFormIntegrator * mass = new MassIntegrator(one);
|
||||
a->AddTestIntegrator(diffusion,0,0);
|
||||
a->AddTestIntegrator(mass,0,0);
|
||||
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(one),0);
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
H1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
Vector X,B;
|
||||
OperatorPtr Ah;
|
||||
|
||||
int size = H1fes.GetVSize() + RTtrace_fes.GetVSize();
|
||||
|
||||
Vector x(size);
|
||||
x = 0.0;
|
||||
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-6);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
|
||||
delete M;
|
||||
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
GridFunction u_gf;
|
||||
double *data = x.GetData();
|
||||
u_gf.MakeRef(&H1fes,data);
|
||||
|
||||
GridFunction s_gf;
|
||||
s_gf.MakeRef(&RTtrace_fes,&data[H1fes.GetVSize()]);
|
||||
|
||||
|
||||
|
||||
RT_FECollection RTfec(order-1, mesh.Dimension());
|
||||
FiniteElementSpace RTfes(&mesh, &RTfec);
|
||||
|
||||
GridFunction sigma_gf(&RTfes);
|
||||
sigma_gf = 0.0;
|
||||
for (int i = 0; i<mesh.GetNE(); i++)
|
||||
{
|
||||
Array<int> strace_dofs;
|
||||
Array<int> trace_dofs;
|
||||
Vector dofs;
|
||||
RTtrace_fes.GetElementDofs(i,trace_dofs);
|
||||
strace_dofs.SetSize(trace_dofs.Size());
|
||||
// shift dofs;
|
||||
for (int j = 0; j< trace_dofs.Size(); j++)
|
||||
{
|
||||
int offset = trace_dofs[j] < 0 ? -H1fes.GetVSize() : H1fes.GetVSize();
|
||||
strace_dofs[j] = offset + trace_dofs[j];
|
||||
}
|
||||
x.GetSubVector(strace_dofs, dofs);
|
||||
sigma_gf.SetSubVector(trace_dofs,dofs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ParaViewDataCollection paraview_dc("DPG_example", &mesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
paraview_dc.SetCycle(0);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.SetTime(0.0); // set the time
|
||||
paraview_dc.RegisterField("field",&u_gf);
|
||||
paraview_dc.RegisterField("flux",&sigma_gf);
|
||||
// paraview_dc.RegisterField("flux",&s_gf);
|
||||
paraview_dc.Save();
|
||||
|
||||
|
||||
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
|
||||
socketstream soltrace_sock(vishost, visport);
|
||||
soltrace_sock.precision(8);
|
||||
soltrace_sock << "solution\n" << mesh << sigma_gf <<
|
||||
"window_title 'Flux sigma_n' "
|
||||
<< flush;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
// MFEM Ultraweak DPG example
|
||||
//
|
||||
// Compile with: make uw_dpg
|
||||
//
|
||||
// sample runs
|
||||
// ./uw_dpg -m ../lshape2.mesh -o 2 -ref 20 -graph-norm -do 1 -prob 0
|
||||
|
||||
// - Δ u = f, in Ω
|
||||
// u = u_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ u - σ = 0, in Ω
|
||||
// - ∇⋅σ = f, in Ω
|
||||
// u = 0, in ∂Ω
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
|
||||
// û ∈ H^1/2, σ̂ ∈ H^-1/2
|
||||
// -(u , ∇⋅τ) - (σ , τ) + < û, τ⋅n> = 0, ∀ τ ∈ H(div,Ω)
|
||||
// (σ , ∇ v) + < σ̂, v > = (f,v) ∀ v ∈ H^1(Ω)
|
||||
// û = 0 on ∂Ω
|
||||
|
||||
// Note:
|
||||
// û := u
|
||||
// σ̂ := -σ
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | u | σ | û | σ̂ | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | τ | -(u,∇⋅τ) | -(σ,τ) | < û, τ⋅n> | | 0 |
|
||||
// | | | | | | |
|
||||
// | v | | (σ,∇ v) | | <σ̂,v> | (f,v) |
|
||||
|
||||
// where (τ,v) ∈ H(div,Ω) × H^1(Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
lshape,
|
||||
general
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
void solution(const Vector & X, double & u, Vector & du, double & d2u);
|
||||
|
||||
|
||||
double exact_u(const Vector & X)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
return u;
|
||||
}
|
||||
|
||||
void exact_sigma(const Vector & X, Vector & sigma)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
// σ = ∇ u
|
||||
sigma = du;
|
||||
}
|
||||
|
||||
double exact_hatu(const Vector & X)
|
||||
{
|
||||
return exact_u(X);
|
||||
}
|
||||
|
||||
void exact_hatsigma(const Vector & X, Vector & hatsigma)
|
||||
{
|
||||
exact_sigma(X,hatsigma);
|
||||
hatsigma *= -1.;
|
||||
}
|
||||
|
||||
double f_exact(const Vector & X)
|
||||
{
|
||||
double u, d2u;
|
||||
Vector du;
|
||||
solution(X,u,du,d2u);
|
||||
return -d2u;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
int ref = 1;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool visualization = true;
|
||||
int iprob = 0;
|
||||
bool static_cond = false;
|
||||
|
||||
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(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&ref, "-ref", "--num_refinements",
|
||||
"Number of uniform refinements");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: lshape, 1: General");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 1) { iprob = 1; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
if (prob == prob_type::lshape)
|
||||
{
|
||||
mesh_file = "../lshape2.mesh";
|
||||
}
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
mesh.UniformRefinement();
|
||||
|
||||
// Define spaces
|
||||
// L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec);
|
||||
|
||||
// Vector L2 space for σ
|
||||
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *sigma_fes = new FiniteElementSpace(&mesh,sigma_fec, dim);
|
||||
|
||||
// H^1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
|
||||
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
|
||||
|
||||
// H^-1/2 space for σ̂
|
||||
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
FiniteElementSpace *hatsigma_fes = new FiniteElementSpace(&mesh,hatsigma_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
|
||||
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
|
||||
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(sigma_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
trial_fes.Append(hatsigma_fes);
|
||||
|
||||
test_fec.Append(tau_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
// -(u,∇⋅τ)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,0);
|
||||
|
||||
// -(σ,τ)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negone)),1,0);
|
||||
|
||||
// (σ,∇ v)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,1);
|
||||
|
||||
// <û,τ⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,0);
|
||||
|
||||
// <σ̂,v>
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,1);
|
||||
|
||||
// test integrators (space-induced norm for H(div) × H1)
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),0,0);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
// (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),1,1);
|
||||
|
||||
// additional terms for adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -(∇v,δτ)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negone),1,0);
|
||||
// -(τ,∇δv)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(one),0,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
}
|
||||
|
||||
// RHS
|
||||
FunctionCoefficient f(f_exact);
|
||||
if (prob == prob_type::general)
|
||||
{
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),1);
|
||||
}
|
||||
|
||||
FunctionCoefficient hatuex(exact_hatu);
|
||||
Array<int> elements_to_refine;
|
||||
GridFunction hatu_gf;
|
||||
|
||||
|
||||
socketstream u_out;
|
||||
// socketstream sigma_out;
|
||||
socketstream mesh_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
u_out.open(vishost, visport);
|
||||
// sigma_out.open(vishost, visport);
|
||||
mesh_out.open(vishost, visport);
|
||||
}
|
||||
|
||||
|
||||
for (int iref = 0; iref<ref; iref++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
ess_tdof_list[i] += u_fes->GetTrueVSize() + sigma_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = u_fes->GetVSize();
|
||||
offsets[2] = sigma_fes->GetVSize();
|
||||
offsets[3] = hatu_fes->GetVSize();
|
||||
offsets[4] = hatsigma_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
|
||||
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr);
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = Ah.As<BlockMatrix>();
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new GSSmoother(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
cout << "Residual = " << residual << endl;
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
double max_resid = residuals.Max();
|
||||
double theta = 0.7;
|
||||
for (int iel = 0; iel<mesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * max_resid)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
GridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(0));
|
||||
|
||||
GridFunction sigma_gf;
|
||||
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
u_out.precision(8);
|
||||
string keys = (iref == 0) ? "keys em\n" : "keys";
|
||||
u_out << "solution\n" << mesh << u_gf
|
||||
<< "window_title 'Numerical u' "
|
||||
<< flush;
|
||||
|
||||
// sigma_out.precision(8);
|
||||
// sigma_out << "solution\n" << mesh << sigma_gf <<
|
||||
// "window_title 'Numerical flux' "
|
||||
// << flush;
|
||||
|
||||
mesh_out.precision(8);
|
||||
mesh_out << "mesh\n" << mesh
|
||||
<< keys
|
||||
<< "window_title 'Mesh' "
|
||||
<< flush;
|
||||
|
||||
}
|
||||
|
||||
mesh.GeneralRefinement(elements_to_refine);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete tau_fec;
|
||||
delete v_fec;
|
||||
delete hatsigma_fes;
|
||||
delete hatsigma_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete sigma_fec;
|
||||
delete sigma_fes;
|
||||
delete u_fec;
|
||||
delete u_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void solution(const Vector & X, double & u, Vector & du, double & d2u)
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double z = 0.;
|
||||
if (X.Size() == 3) z = X[2];
|
||||
du.SetSize(X.Size());
|
||||
du = 0.;
|
||||
d2u = 0.;
|
||||
|
||||
switch(prob)
|
||||
{
|
||||
case lshape:
|
||||
{
|
||||
double r = sqrt(x*x + y*y);
|
||||
double alpha = 2./3.;
|
||||
double theta = atan2(y,x);
|
||||
if (theta < 0) theta += 2*M_PI;
|
||||
u = pow(r,alpha) * sin(alpha * theta);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double alpha = M_PI * (x + y + z);
|
||||
u = sin(alpha);
|
||||
du.SetSize(X.Size());
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
du[i] = M_PI * cos(alpha);
|
||||
}
|
||||
d2u = - M_PI*M_PI * u * du.Size();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
// MFEM UW DPG parallel example
|
||||
//
|
||||
// Compile with: make poisson_fosls
|
||||
//
|
||||
// - Δ u = f, in Ω
|
||||
// u = 0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// ∇ u - σ = 0, in Ω
|
||||
// - ∇⋅σ = f, in Ω
|
||||
// u = 0, in ∂Ω
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
|
||||
// û ∈ H^1/2, σ̂ ∈ H^-1/2
|
||||
// -(u , ∇⋅τ) + < û, τ⋅n> - (σ , τ) = 0, ∀ τ ∈ H(div,Ω)
|
||||
// (σ , ∇ v) - < σ̂, v > = (f,v) ∀ v ∈ H^1(Ω)
|
||||
// û = 0 on ∂Ω
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// | | u | σ | û | σ̂ | RHS |
|
||||
// -------------------------------------------------------------
|
||||
// | τ | -(u,∇⋅τ) | -(σ,τ) | < û, τ⋅n> | | 0 |
|
||||
// | | | | | | |
|
||||
// | v | | (σ,∇ v) | | -<σ̂,v> | (f,v) |
|
||||
|
||||
// where (τ,v) ∈ H(div,Ω) × H^1(Ω)
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
lshape,
|
||||
general
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
double exact(const Vector & X)
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
|
||||
double r = sqrt(x*x + y*y);
|
||||
double alpha = 2./3.;
|
||||
double theta = atan2(y,x);
|
||||
if (theta < 0) theta += 2*M_PI;
|
||||
|
||||
return pow(r,alpha) * sin(alpha * theta);
|
||||
}
|
||||
|
||||
void gradexact(const Vector & X, Vector & grad)
|
||||
{
|
||||
grad.SetSize(2);
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
|
||||
double r = sqrt(x*x + y*y);
|
||||
double alpha = 2./3.;
|
||||
double theta = atan2(y,x);
|
||||
if (theta < 0) theta += 2*M_PI;
|
||||
|
||||
double r_x = x/r;
|
||||
double r_y = y/r;
|
||||
double theta_x = - y / (r*r);
|
||||
double theta_y = x / (r*r);
|
||||
double beta = alpha * pow(r,alpha - 1.);
|
||||
grad[0] = beta*(r_x * sin(alpha*theta) + r * theta_x * cos(alpha*theta));
|
||||
grad[1] = beta*(r_y * sin(alpha*theta) + r * theta_y * cos(alpha*theta));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MPI_Session mpi;
|
||||
int num_procs = mpi.WorldSize();
|
||||
int myid = mpi.WorldRank();
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../../data/inline-quad.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
int ref = 1;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool visualization = true;
|
||||
int iprob = 0;
|
||||
bool static_cond = false;
|
||||
double theta = 0.7;
|
||||
|
||||
|
||||
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(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&ref, "-ref", "--num_refinements",
|
||||
"Number of uniform refinements");
|
||||
args.AddOption(&theta, "-theta", "--theta_factor",
|
||||
"Refinement factor");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: lshape, 1: General");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (iprob > 1) { iprob = 1; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
if (prob == prob_type::lshape)
|
||||
{
|
||||
mesh_file = "../lshape2.mesh";
|
||||
}
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
mesh.UniformRefinement();
|
||||
|
||||
mesh.EnsureNCMesh();
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// Define spaces
|
||||
// L2 space for u
|
||||
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec);
|
||||
|
||||
// Vector L2 space for σ
|
||||
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *sigma_fes = new ParFiniteElementSpace(&pmesh,sigma_fec, dim);
|
||||
|
||||
// H^1/2 space for û
|
||||
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
|
||||
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
|
||||
|
||||
// H^-1/2 space for σ̂
|
||||
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
ParFiniteElementSpace *hatsigma_fes = new ParFiniteElementSpace(&pmesh,hatsigma_fec);
|
||||
|
||||
// testspace fe collections
|
||||
int test_order = order+delta_order;
|
||||
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
|
||||
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
|
||||
|
||||
// Coefficients
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient negone(-1.0);
|
||||
|
||||
// Normal equation weak formulation
|
||||
Array<ParFiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(u_fes);
|
||||
trial_fes.Append(sigma_fes);
|
||||
trial_fes.Append(hatu_fes);
|
||||
trial_fes.Append(hatsigma_fes);
|
||||
|
||||
test_fec.Append(tau_fec);
|
||||
test_fec.Append(v_fec);
|
||||
|
||||
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices(true);
|
||||
|
||||
// -(u,∇⋅τ)
|
||||
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,0);
|
||||
|
||||
// -(σ,τ)
|
||||
TransposeIntegrator * mass = new TransposeIntegrator(new VectorFEMassIntegrator(negone));
|
||||
a->AddTrialIntegrator(mass,1,0);
|
||||
|
||||
// (σ,∇ v)
|
||||
TransposeIntegrator * grad = new TransposeIntegrator(new GradientIntegrator(one));
|
||||
a->AddTrialIntegrator(grad,1,1);
|
||||
|
||||
// <û,τ⋅n>
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,2,0);
|
||||
|
||||
// -<σ̂,v> (sign is included in σ̂)
|
||||
a->AddTrialIntegrator(new TraceIntegrator,3,1);
|
||||
|
||||
// test integrators (space-induced norm for H(div) × H1)
|
||||
// (∇⋅τ,∇⋅δτ)
|
||||
a->AddTestIntegrator(new DivDivIntegrator(one),0,0);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
// (∇v,∇δv)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),1,1);
|
||||
// (v,δv)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),1,1);
|
||||
|
||||
// additional terms for adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
// -(∇v,δτ)
|
||||
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negone),1,0);
|
||||
// -(τ,∇δv)
|
||||
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(one),0,1);
|
||||
// (τ,δτ)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
|
||||
}
|
||||
// RHS
|
||||
if (prob == prob_type::general)
|
||||
{
|
||||
a->AddDomainLFIntegrator(new DomainLFIntegrator(one),1);
|
||||
}
|
||||
|
||||
FunctionCoefficient uex(exact);
|
||||
Array<int> elements_to_refine;
|
||||
ParGridFunction hatu_gf;
|
||||
|
||||
|
||||
socketstream u_out;
|
||||
socketstream sigma_out;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
u_out.open(vishost, visport);
|
||||
sigma_out.open(vishost, visport);
|
||||
}
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
ess_tdof_list[i] += u_fes->GetTrueVSize() + sigma_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = u_fes->GetVSize();
|
||||
offsets[2] = sigma_fes->GetVSize();
|
||||
offsets[3] = hatu_fes->GetVSize();
|
||||
offsets[4] = hatsigma_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
BlockVector x(offsets);
|
||||
x = 0.0;
|
||||
if (prob == prob_type::lshape)
|
||||
{
|
||||
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
|
||||
hatu_gf.ProjectBdrCoefficient(uex,ess_bdr);
|
||||
}
|
||||
|
||||
Vector X,B;
|
||||
OperatorPtr Ah;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockOperator * A = Ah.As<BlockOperator>();
|
||||
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
int skip = 0;
|
||||
if (!static_cond)
|
||||
{
|
||||
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
|
||||
HypreBoomerAMG * amg1 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
|
||||
amg0->SetPrintLevel(0);
|
||||
amg1->SetPrintLevel(0);
|
||||
M->SetDiagonalBlock(0,amg0);
|
||||
M->SetDiagonalBlock(1,amg1);
|
||||
skip=2;
|
||||
}
|
||||
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,skip));
|
||||
amg2->SetPrintLevel(0);
|
||||
M->SetDiagonalBlock(skip,amg2);
|
||||
HypreSolver * prec;
|
||||
if (dim == 2)
|
||||
{
|
||||
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
|
||||
}
|
||||
M->SetDiagonalBlock(skip+1,prec);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
double maxresidual = residuals.Max();
|
||||
double globalresidual = residual * residual;
|
||||
|
||||
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
|
||||
|
||||
globalresidual = sqrt(globalresidual);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Global Residual = " << globalresidual << endl;
|
||||
}
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
for (int iel = 0; iel<pmesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * maxresidual)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
ParGridFunction u_gf;
|
||||
u_gf.MakeRef(u_fes,x.GetBlock(0));
|
||||
|
||||
ParGridFunction sigma_gf;
|
||||
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
u_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
u_out.precision(8);
|
||||
u_out << "solution\n" << pmesh << u_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
|
||||
sigma_out << "parallel " << num_procs << " " << myid << "\n";
|
||||
sigma_out.precision(8);
|
||||
sigma_out << "solution\n" << pmesh << sigma_gf <<
|
||||
"window_title 'Numerical flux' "
|
||||
<< flush;
|
||||
}
|
||||
|
||||
|
||||
if (i == ref-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pmesh.GeneralRefinement(elements_to_refine);
|
||||
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete tau_fec;
|
||||
delete v_fec;
|
||||
delete hatsigma_fes;
|
||||
delete hatsigma_fec;
|
||||
delete hatu_fes;
|
||||
delete hatu_fec;
|
||||
delete sigma_fec;
|
||||
delete sigma_fes;
|
||||
delete u_fec;
|
||||
delete u_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/grad-div,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = primal_dpg
|
||||
PAR_EXAMPLES =
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@@ -0,0 +1,176 @@
|
||||
// MFEM primal dpg example for grad-dic problem
|
||||
//
|
||||
// Compile with: make primal_dpg
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command line options
|
||||
const char *mesh_file = "../../../data/star.mesh";
|
||||
int order = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
|
||||
args.ParseCheck();
|
||||
|
||||
kappa = freq * M_PI;
|
||||
|
||||
|
||||
// 2. Read the mesh from the given mesh file, and refine once uniformly.
|
||||
Mesh mesh(mesh_file);
|
||||
// mesh.UniformRefinement();
|
||||
|
||||
RT_FECollection fec(order-1, mesh.Dimension());
|
||||
FiniteElementSpace RTfes(&mesh, &fec);
|
||||
|
||||
H1_Trace_FECollection trace_fec(order, mesh.Dimension());
|
||||
FiniteElementSpace H1trace_fes(&mesh, &trace_fec);
|
||||
|
||||
int dim = mesh.Dimension();
|
||||
int test_order = order;
|
||||
if (dim == 2 && (order%2 == 0 || (mesh.MeshGenerator() & 2 && order > 1)))
|
||||
{
|
||||
test_order++;
|
||||
}
|
||||
|
||||
test_order++;
|
||||
|
||||
RT_FECollection test_fec(test_order,mesh.Dimension());
|
||||
|
||||
Array<FiniteElementSpace *> trial_fes;
|
||||
Array<FiniteElementCollection * > test_fecs;
|
||||
|
||||
trial_fes.Append(&RTfes);
|
||||
trial_fes.Append(&H1trace_fes);
|
||||
test_fecs.Append(&test_fec);
|
||||
|
||||
|
||||
GridFunction rt_gf(&RTfes);
|
||||
VectorFunctionCoefficient F(dim, F_exact);
|
||||
rt_gf.ProjectCoefficient(F);
|
||||
|
||||
Vector x(RTfes.GetVSize()+H1trace_fes.GetVSize());
|
||||
x = 0.;
|
||||
x.SetVector(rt_gf,0);
|
||||
|
||||
|
||||
ConstantCoefficient alpha(1.0);
|
||||
ConstantCoefficient beta(1.0);
|
||||
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
|
||||
a->AddTrialIntegrator(new DivDivIntegrator(alpha),0,0);
|
||||
a->AddTrialIntegrator(new VectorFEMassIntegrator(beta),0,0);
|
||||
a->AddTrialIntegrator(new NormalTraceIntegrator,1,0);
|
||||
a->AddTestIntegrator(new DivDivIntegrator(alpha),0,0);
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(beta),0,0);
|
||||
|
||||
|
||||
VectorFunctionCoefficient f(dim, f_exact);
|
||||
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f),0);
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
RTfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
Vector X,B;
|
||||
|
||||
OperatorPtr Ah;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
|
||||
|
||||
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
|
||||
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
|
||||
M->owns_blocks = 1;
|
||||
for (int i=0; i<A->NumRowBlocks(); i++)
|
||||
{
|
||||
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
|
||||
}
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(*M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
|
||||
delete M;
|
||||
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
// GridFunction u_gf;
|
||||
double *data = x.GetData();
|
||||
rt_gf.MakeRef(&RTfes,data);
|
||||
|
||||
GridFunction exact_gf(&RTfes);
|
||||
exact_gf.ProjectCoefficient(F);
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream solu_sock(vishost, visport);
|
||||
solu_sock.precision(8);
|
||||
solu_sock << "solution\n" << mesh << rt_gf <<
|
||||
"window_title 'Numerical u' "
|
||||
<< flush;
|
||||
|
||||
socketstream soltrace_sock(vishost, visport);
|
||||
soltrace_sock.precision(8);
|
||||
soltrace_sock << "solution\n" << mesh << exact_gf <<
|
||||
"window_title 'Exact' "
|
||||
<< flush;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// The exact solution (for non-surface meshes)
|
||||
void F_exact(const Vector &p, Vector &F)
|
||||
{
|
||||
int dim = p.Size();
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
|
||||
|
||||
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();
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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
|
||||
2
|
||||
|
||||
elements
|
||||
3
|
||||
1 3 0 1 4 3
|
||||
1 3 3 4 7 6
|
||||
1 3 1 2 5 4
|
||||
|
||||
boundary
|
||||
8
|
||||
1 1 0 1
|
||||
1 1 1 2
|
||||
1 1 2 5
|
||||
2 1 5 4
|
||||
2 1 4 7
|
||||
1 1 7 6
|
||||
1 1 6 3
|
||||
1 1 3 0
|
||||
|
||||
vertices
|
||||
8
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_2D_P1
|
||||
VDim: 2
|
||||
Ordering: 1
|
||||
|
||||
-1 1
|
||||
-1 -0
|
||||
-1 -1
|
||||
0 1
|
||||
0 -0
|
||||
0 -1
|
||||
1 1
|
||||
1 -0
|
||||
@@ -0,0 +1,907 @@
|
||||
// MFEM Ultraweak DPG Maxwell example
|
||||
//
|
||||
// Compile with: make complex_uw_dpg
|
||||
//
|
||||
|
||||
// ∇×(1/μ ∇×E) - ω^2 ϵ E = Ĵ , in Ω
|
||||
// E×n = E_0, on ∂Ω
|
||||
|
||||
// First Order System
|
||||
|
||||
// i ω μ H + ∇ × E = 0, in Ω
|
||||
// -i ω ϵ E + ∇ × H = J, in Ω
|
||||
// E × n = E_0, on ∂Ω
|
||||
|
||||
// note: Ĵ = -iωJ
|
||||
// in 2D
|
||||
// E is vector valued and H is scalar.
|
||||
// (∇ × E, F) = (E, ∇ × F) + < n × E , F>
|
||||
// or (∇ ⋅ AE , F) = (AE, ∇ F) + < AE ⋅ n, F>
|
||||
// where A = A = [0 1; -1 0];
|
||||
|
||||
// UW-DPG:
|
||||
//
|
||||
// in 3D
|
||||
// E,H ∈ (L^2(Ω))^3
|
||||
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h), Ĥ ∈ H^-1/2(curl, Γ_h)
|
||||
// i ω μ (H,F) + (E,∇ × F) + < Ê, F × n > = 0, ∀ F ∈ H(curl,Ω)
|
||||
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
|
||||
// Ê × n = E_0 on ∂Ω
|
||||
// -------------------------------------------------------------------------
|
||||
// | | E | H | Ê | Ĥ | RHS |
|
||||
// -------------------------------------------------------------------------
|
||||
// | F | (E,∇ × F) | i ω μ (H,F) | < n × Ê, F > | | |
|
||||
// | | | | | | |
|
||||
// | G | -i ω ϵ (E,G) | (H,∇ × G) | | < n × Ĥ, G > | (J,G) |
|
||||
// where (F,G) ∈ H(curl,Ω) × H(curl,Ω)
|
||||
|
||||
// in 2D
|
||||
// E ∈ L^2(Ω)^2, H ∈ L^2(Ω)
|
||||
// Ê ∈ H^-1/2(Ω)(Γ_h), Ĥ ∈ H^1/2(Γ_h)
|
||||
// i ω μ (H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H^1
|
||||
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
|
||||
// Ê = E_0 on ∂Ω
|
||||
// -------------------------------------------------------------------------
|
||||
// | | E | H | Ê | Ĥ | RHS |
|
||||
// -------------------------------------------------------------------------
|
||||
// | F | (E,∇ × F) | i ω μ (H,F) | < Ê, F > | | |
|
||||
// | | | | | | |
|
||||
// | G | -i ω ϵ (E,G) | (H,∇ × G) | | < Ĥ, G × n > | (J,G) |
|
||||
|
||||
// where (F,G) ∈ H^1 × H(curl,Ω)
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void E_exact_r(const Vector &x, Vector & E_r);
|
||||
void E_exact_i(const Vector &x, Vector & E_i);
|
||||
|
||||
void H_exact_r(const Vector &x, Vector & H_r);
|
||||
void H_exact_i(const Vector &x, Vector & H_i);
|
||||
|
||||
|
||||
void rhs_func_r(const Vector &x, Vector & J_r);
|
||||
void rhs_func_i(const Vector &x, Vector & J_i);
|
||||
|
||||
void curlE_exact_r(const Vector &x, Vector &curlE_r);
|
||||
void curlE_exact_i(const Vector &x, Vector &curlE_i);
|
||||
void curlH_exact_r(const Vector &x,Vector &curlH_r);
|
||||
void curlH_exact_i(const Vector &x,Vector &curlH_i);
|
||||
|
||||
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r);
|
||||
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i);
|
||||
|
||||
void hatE_exact_r(const Vector & X, Vector & hatE_r);
|
||||
void hatE_exact_i(const Vector & X, Vector & hatE_i);
|
||||
|
||||
void hatH_exact_r(const Vector & X, Vector & hatH_r);
|
||||
void hatH_exact_i(const Vector & X, Vector & hatH_i);
|
||||
|
||||
double hatH_exact_scalar_r(const Vector & X);
|
||||
double hatH_exact_scalar_i(const Vector & X);
|
||||
|
||||
void maxwell_solution(const Vector & X,
|
||||
std::vector<complex<double>> &E,
|
||||
std::vector<complex<double>> &curlE,
|
||||
std::vector<complex<double>> &curlcurlE);
|
||||
|
||||
void maxwell_solution_r(const Vector & X, Vector &E_r,
|
||||
Vector &curlE_r,
|
||||
Vector &curlcurlE_r);
|
||||
|
||||
void maxwell_solution_i(const Vector & X, Vector &E_i,
|
||||
Vector &curlE_i,
|
||||
Vector &curlcurlE_i);
|
||||
|
||||
int dim;
|
||||
int dimc;
|
||||
double omega;
|
||||
double mu = 1.0;
|
||||
double epsilon = 1.0;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
polynomial,
|
||||
plane_wave,
|
||||
fichera_oven
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *mesh_file = "../../../data/inline-hex.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int ref = 1;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool static_cond = false;
|
||||
int iprob = 0;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&mu, "-mu", "--permeability",
|
||||
"Permeability of free space (or 1/(spring constant)).");
|
||||
args.AddOption(&epsilon, "-eps", "--permittivity",
|
||||
"Permittivity of free space (or mass constant).");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: polynomial, 1: plane wave, 2: Gaussian beam");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&ref, "-ref", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 2) { iprob = 0; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
omega = 2.*M_PI*rnum;
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
dimc = (dim == 3) ? 3 : 1;
|
||||
int test_order = order+delta_order;
|
||||
|
||||
// Define spaces
|
||||
// L2 space for E
|
||||
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *E_fes = new FiniteElementSpace(&mesh,E_fec,dim);
|
||||
|
||||
// Vector L2 space for H
|
||||
FiniteElementCollection *H_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *H_fes = new FiniteElementSpace(&mesh,H_fec, dimc);
|
||||
|
||||
// H^-1/2 (curl) space for Ê
|
||||
FiniteElementCollection * hatE_fec = nullptr;
|
||||
FiniteElementCollection * hatH_fec = nullptr;
|
||||
FiniteElementCollection * F_fec = nullptr;
|
||||
if (dim == 3)
|
||||
{
|
||||
hatE_fec = new ND_Trace_FECollection(order,dim);
|
||||
hatH_fec = new ND_Trace_FECollection(order,dim);
|
||||
F_fec = new ND_FECollection(test_order, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
hatE_fec = new RT_Trace_FECollection(order-1,dim);
|
||||
hatH_fec = new H1_Trace_FECollection(order,dim);
|
||||
F_fec = new H1_FECollection(test_order, dim);
|
||||
}
|
||||
FiniteElementSpace *hatE_fes = new FiniteElementSpace(&mesh,hatE_fec);
|
||||
FiniteElementSpace *hatH_fes = new FiniteElementSpace(&mesh,hatH_fec);
|
||||
|
||||
FiniteElementCollection * G_fec = new ND_FECollection(test_order, dim);
|
||||
|
||||
|
||||
mfem::out << "E_fes space true dofs = " << E_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "H_fes space true dofs = " << H_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "hatE_fes space true dofs = " << hatE_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "hatH_fes space true dofs = " << hatH_fes->GetTrueVSize() << endl;
|
||||
|
||||
|
||||
// // Coefficients
|
||||
Vector dim_zero(dim); dim_zero = 0.0;
|
||||
Vector dimc_zero(dimc); dimc_zero = 0.0;
|
||||
VectorConstantCoefficient E_zero(dim_zero);
|
||||
VectorConstantCoefficient H_zero(dimc_zero);
|
||||
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient eps2omeg2(epsilon*epsilon*omega*omega);
|
||||
ConstantCoefficient mu2omeg2(mu*mu*omega*omega);
|
||||
ConstantCoefficient muomeg(mu*omega);
|
||||
ConstantCoefficient negepsomeg(-epsilon*omega);
|
||||
ConstantCoefficient epsomeg(epsilon*omega);
|
||||
ConstantCoefficient negmuomeg(-mu*omega);
|
||||
|
||||
DenseMatrix rot_mat(2);
|
||||
rot_mat(0,0) = 0.; rot_mat(0,1) = 1.;
|
||||
rot_mat(1,0) = -1.; rot_mat(1,1) = 0.;
|
||||
MatrixConstantCoefficient rot(rot_mat);
|
||||
ScalarMatrixProductCoefficient epsrot(epsomeg,rot);
|
||||
ScalarMatrixProductCoefficient negepsrot(negepsomeg,rot);
|
||||
// Normal equation weak formulation
|
||||
Array<FiniteElementSpace * > trial_fes;
|
||||
Array<FiniteElementCollection * > test_fec;
|
||||
|
||||
trial_fes.Append(E_fes);
|
||||
trial_fes.Append(H_fes);
|
||||
trial_fes.Append(hatE_fes);
|
||||
trial_fes.Append(hatH_fes);
|
||||
|
||||
test_fec.Append(F_fec);
|
||||
test_fec.Append(G_fec);
|
||||
|
||||
ComplexNormalEquations * a = new ComplexNormalEquations(trial_fes,test_fec);
|
||||
a->StoreMatrices();
|
||||
|
||||
// (E,∇ × F)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new CurlIntegrator(one)),nullptr,0,0);
|
||||
|
||||
// -i ω ϵ (E , G)
|
||||
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(negepsomeg)),0,1);
|
||||
|
||||
// i ω μ (H, F)
|
||||
if (dim == 3)
|
||||
{
|
||||
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(muomeg)),1,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(muomeg),1,0);
|
||||
}
|
||||
// (H,∇ × G)
|
||||
a->AddTrialIntegrator(new TransposeIntegrator(new CurlIntegrator(one)),nullptr,1,1);
|
||||
|
||||
// < n×Ê,F>
|
||||
if (dim == 3)
|
||||
{
|
||||
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,2,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->AddTrialIntegrator(new TraceIntegrator,nullptr,2,0);
|
||||
}
|
||||
|
||||
// < n×Ĥ ,G>
|
||||
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,3,1);
|
||||
|
||||
|
||||
// test integrators
|
||||
|
||||
//space-induced norm for H(curl) × H(curl)
|
||||
if (dim == 3)
|
||||
{
|
||||
// (∇×F,∇×δF)
|
||||
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
|
||||
// (F,δF)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (∇F,∇δF)
|
||||
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
|
||||
// (F,δF)
|
||||
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
|
||||
}
|
||||
|
||||
// (∇×G ,∇× δG)
|
||||
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,1,1);
|
||||
// (G,δG)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
|
||||
|
||||
// additional integrators for the adjoint graph norm
|
||||
if (adjoint_graph_norm)
|
||||
{
|
||||
if(dim == 3)
|
||||
{
|
||||
// μ^2 ω^2 (F,δF)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(mu2omeg2),nullptr,0,0);
|
||||
// -i ω μ (F,∇ × δG) = (F, ω μ ∇ × δ G)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(negmuomeg),0,1);
|
||||
// -i ω ϵ (∇ × F, δG)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(negepsomeg),0,1);
|
||||
// i ω μ (∇ × G,δF)
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(epsomeg),1,0);
|
||||
// i ω ϵ (G, ∇ × δF )
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(muomeg),1,0);
|
||||
// ϵ^2 ω^2 (G,δG)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2omeg2),nullptr,1,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// μ^2 ω^2 (F,δF)
|
||||
a->AddTestIntegrator(new MassIntegrator(mu2omeg2),nullptr,0,0);
|
||||
|
||||
// -i ω μ (F,∇ × δG) = i (F, -ω μ ∇ × δ G)
|
||||
a->AddTestIntegrator(nullptr,
|
||||
new TransposeIntegrator(new CurlIntegrator(negmuomeg)),0,1);
|
||||
|
||||
// -i ω ϵ (∇ × F, δG) = i (- ω ϵ A ∇ F,δG), A = [0 1; -1; 0]
|
||||
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negepsrot),0,1);
|
||||
|
||||
// i ω μ (∇ × G,δF) = i (ω μ ∇ × G, δF )
|
||||
a->AddTestIntegrator(nullptr,new CurlIntegrator(muomeg),1,0);
|
||||
|
||||
// i ω ϵ (G, ∇ × δF ) = i (ω ϵ G, A ∇ δF) = i ( G , ω ϵ A ∇ δF)
|
||||
a->AddTestIntegrator(nullptr,
|
||||
new TransposeIntegrator(new MixedVectorGradientIntegrator(epsrot)),1,0);
|
||||
|
||||
// or i ( ω ϵ A^t G, ∇ δF) = i (- ω ϵ A G, ∇ δF)
|
||||
// a->AddTestIntegrator(nullptr,
|
||||
// new MixedVectorWeakDivergenceIntegrator(epsrot),1,0);
|
||||
// ϵ^2 ω^2 (G,δG)
|
||||
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2omeg2),nullptr,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
// RHS
|
||||
VectorFunctionCoefficient f_rhs_r(dim,rhs_func_r);
|
||||
VectorFunctionCoefficient f_rhs_i(dim,rhs_func_i);
|
||||
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f_rhs_r),
|
||||
new VectorFEDomainLFIntegrator(f_rhs_i),1);
|
||||
|
||||
|
||||
VectorFunctionCoefficient hatEex_r(dim,hatE_exact_r);
|
||||
VectorFunctionCoefficient hatEex_i(dim,hatE_exact_i);
|
||||
|
||||
VectorFunctionCoefficient hatHex_r(dimc,hatH_exact_r);
|
||||
VectorFunctionCoefficient hatHex_i(dimc,hatH_exact_i);
|
||||
|
||||
FunctionCoefficient hatH_2D_ex_r(hatH_exact_scalar_r);
|
||||
FunctionCoefficient hatH_2D_ex_i(hatH_exact_scalar_i);
|
||||
|
||||
|
||||
Array<int> elements_to_refine;
|
||||
|
||||
socketstream E_out_r;
|
||||
socketstream Eex_out_r;
|
||||
// socketstream E_out_i;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
E_out_r.open(vishost, visport);
|
||||
Eex_out_r.open(vishost, visport);
|
||||
// E_out_i.open(vishost, visport);
|
||||
}
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
mfem::out << " Refinement |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
<< " Relative % |"
|
||||
<< " Rate |"
|
||||
<< " Residual |"
|
||||
<< " Rate |" << endl;
|
||||
mfem::out << " --------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------"
|
||||
<< "-------------------" << endl;
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
hatE_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
// hatH_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// shift the ess_tdofs
|
||||
for (int j = 0; j < ess_tdof_list.Size(); j++)
|
||||
{
|
||||
ess_tdof_list[j] += E_fes->GetTrueVSize() + H_fes->GetTrueVSize();
|
||||
// + hatE_fes->GetTrueVSize();
|
||||
}
|
||||
|
||||
Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = E_fes->GetVSize();
|
||||
offsets[2] = H_fes->GetVSize();
|
||||
offsets[3] = hatE_fes->GetVSize();
|
||||
offsets[4] = hatH_fes->GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
Vector x(2*offsets.Last());
|
||||
x = 0.;
|
||||
double * xdata = x.GetData();
|
||||
|
||||
ComplexGridFunction hatE_gf(hatE_fes);
|
||||
hatE_gf.real().MakeRef(hatE_fes,&xdata[offsets[2]]);
|
||||
hatE_gf.imag().MakeRef(hatE_fes,&xdata[offsets.Last()+ offsets[2]]);
|
||||
|
||||
ComplexGridFunction hatH_gf(hatH_fes);
|
||||
hatH_gf.real().MakeRef(hatH_fes,&xdata[offsets[3]]);
|
||||
hatH_gf.imag().MakeRef(hatH_fes,&xdata[offsets.Last()+ offsets[3]]);
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
hatE_gf.ProjectBdrCoefficientTangent(hatEex_r,hatEex_i, ess_bdr);
|
||||
// hatH_gf.ProjectBdrCoefficientTangent(hatHex_r,hatHex_i, ess_bdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
hatE_gf.ProjectBdrCoefficientNormal(hatEex_r,hatEex_i, ess_bdr);
|
||||
// hatH_gf.ProjectBdrCoefficient(hatH_2D_ex_r,hatH_2D_ex_i, ess_bdr);
|
||||
|
||||
}
|
||||
OperatorPtr Ah;
|
||||
Vector X,B;
|
||||
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
|
||||
|
||||
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
|
||||
|
||||
SparseMatrix * Ar = dynamic_cast<BlockMatrix *>(&Ahc->real())->CreateMonolithic();
|
||||
SparseMatrix * Ai = dynamic_cast<BlockMatrix *>(&Ahc->imag())->CreateMonolithic();
|
||||
|
||||
ComplexSparseMatrix Ac(Ar,Ai,true,true);
|
||||
SparseMatrix * A = Ac.GetSystemMatrix();
|
||||
|
||||
UMFPackSolver umf(*A);
|
||||
umf.Mult(B,X);
|
||||
|
||||
delete A;
|
||||
a->RecoverFEMSolution(X,x);
|
||||
|
||||
Vector & residuals = a->ComputeResidual(x);
|
||||
double residual = residuals.Norml2();
|
||||
|
||||
|
||||
elements_to_refine.SetSize(0);
|
||||
double max_resid = residuals.Max();
|
||||
for (int iel = 0; iel<mesh.GetNE(); iel++)
|
||||
{
|
||||
if (residuals[iel] > theta * max_resid)
|
||||
{
|
||||
elements_to_refine.Append(iel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ComplexGridFunction E(E_fes);
|
||||
E.real().MakeRef(E_fes,x.GetData());
|
||||
E.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
|
||||
|
||||
VectorFunctionCoefficient E_ex_r(dim,E_exact_r);
|
||||
VectorFunctionCoefficient E_ex_i(dim,E_exact_i);
|
||||
|
||||
ComplexGridFunction H(H_fes);
|
||||
H.real().MakeRef(H_fes,&x.GetData()[offsets[1]]);
|
||||
H.imag().MakeRef(H_fes,&x.GetData()[offsets.Last()+offsets[1]]);
|
||||
|
||||
VectorFunctionCoefficient H_ex_r(dimc,H_exact_r);
|
||||
VectorFunctionCoefficient H_ex_i(dimc,H_exact_i);
|
||||
|
||||
|
||||
int dofs = X.Size()/2;
|
||||
|
||||
double E_err_r = E.real().ComputeL2Error(E_ex_r);
|
||||
double E_err_i = E.imag().ComputeL2Error(E_ex_i);
|
||||
double H_err_r = H.real().ComputeL2Error(H_ex_r);
|
||||
double H_err_i = H.imag().ComputeL2Error(H_ex_i);
|
||||
|
||||
double L2Error = sqrt( E_err_r*E_err_r + E_err_i*E_err_i
|
||||
+ H_err_r*H_err_r + H_err_i*H_err_i );
|
||||
|
||||
ComplexGridFunction Egf_ex(E_fes);
|
||||
ComplexGridFunction Hgf_ex(H_fes);
|
||||
Egf_ex.ProjectCoefficient(E_ex_r, E_ex_i);
|
||||
Hgf_ex.ProjectCoefficient(H_ex_r, H_ex_i);
|
||||
|
||||
|
||||
|
||||
double E_norm_r = Egf_ex.real().ComputeL2Error(E_zero);
|
||||
double E_norm_i = Egf_ex.imag().ComputeL2Error(E_zero);
|
||||
double H_norm_r = Hgf_ex.real().ComputeL2Error(H_zero);
|
||||
double H_norm_i = Hgf_ex.imag().ComputeL2Error(H_zero);
|
||||
|
||||
double L2norm = sqrt( E_norm_r*E_norm_r + E_norm_i*E_norm_i
|
||||
+ H_norm_r*H_norm_r + H_norm_i*H_norm_i );
|
||||
|
||||
double rel_err = L2Error/L2norm;
|
||||
|
||||
|
||||
double rate_err = (i) ? dim*log(err0/rel_err)/log((double)dof0/dofs) : 0.0;
|
||||
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
|
||||
|
||||
err0 = rel_err;
|
||||
res0 = residual;
|
||||
dof0 = dofs;
|
||||
|
||||
mfem::out << std::right << std::setw(11) << i << " | "
|
||||
<< std::setw(10) << dof0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << err0 << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::fixed << rel_err*100 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_err << " | "
|
||||
<< std::setprecision(3)
|
||||
<< std::setw(10) << std::scientific << res0 << " | "
|
||||
<< std::setprecision(2)
|
||||
<< std::setw(6) << std::fixed << rate_res << " | "
|
||||
<< std::resetiosflags(std::ios::showbase)
|
||||
<< std::setw(10) << std::scientific
|
||||
<< std::endl;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
E_out_r.precision(8);
|
||||
E_out_r << "solution\n" << mesh << E.real() <<
|
||||
"window_title 'Real Numerical Electric field' "
|
||||
<< flush;
|
||||
|
||||
// E_out_i.precision(8);
|
||||
// E_out_i << "solution\n" << mesh << E.imag() <<
|
||||
// "window_title 'Imag Numerical Electric field' "
|
||||
// << flush;
|
||||
|
||||
|
||||
|
||||
|
||||
Eex_out_r.precision(8);
|
||||
Eex_out_r << "solution\n" << mesh << Egf_ex.real()
|
||||
<< "window_title 'Real Exact Electric field' "
|
||||
<< flush;
|
||||
// socketstream E_i_sock(vishost, visport);
|
||||
// E_i_sock.precision(8);
|
||||
// E_i_sock << "solution\n" << mesh << Egf_ex.imag()
|
||||
// << "window_title 'Imag Exact Electric field' "
|
||||
// << flush;
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (i == ref-1)
|
||||
break;
|
||||
|
||||
mesh.GeneralRefinement(elements_to_refine,1,1);
|
||||
for (int i =0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
trial_fes[i]->Update(false);
|
||||
}
|
||||
a->Update();
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete F_fec;
|
||||
delete G_fec;
|
||||
delete hatH_fes;
|
||||
delete hatH_fec;
|
||||
delete hatE_fes;
|
||||
delete hatE_fec;
|
||||
delete H_fec;
|
||||
delete E_fec;
|
||||
delete H_fes;
|
||||
delete E_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void E_exact_r(const Vector &x, Vector & E_r)
|
||||
{
|
||||
Vector curlE_r;
|
||||
Vector curlcurlE_r;
|
||||
|
||||
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
|
||||
}
|
||||
|
||||
void E_exact_i(const Vector &x, Vector & E_i)
|
||||
{
|
||||
Vector curlE_i;
|
||||
Vector curlcurlE_i;
|
||||
|
||||
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
|
||||
}
|
||||
|
||||
void curlE_exact_r(const Vector &x, Vector &curlE_r)
|
||||
{
|
||||
Vector E_r;
|
||||
Vector curlcurlE_r;
|
||||
|
||||
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
|
||||
}
|
||||
|
||||
void curlE_exact_i(const Vector &x, Vector &curlE_i)
|
||||
{
|
||||
Vector E_i;
|
||||
Vector curlcurlE_i;
|
||||
|
||||
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
|
||||
}
|
||||
|
||||
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r)
|
||||
{
|
||||
Vector E_r;
|
||||
Vector curlE_r;
|
||||
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
|
||||
}
|
||||
|
||||
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i)
|
||||
{
|
||||
Vector E_i;
|
||||
Vector curlE_i;
|
||||
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
|
||||
}
|
||||
|
||||
|
||||
void H_exact_r(const Vector &x, Vector & H_r)
|
||||
{
|
||||
// H = i ∇ × E / ω μ
|
||||
// H_r = - ∇ × E_i / ω μ
|
||||
Vector curlE_i;
|
||||
curlE_exact_i(x,curlE_i);
|
||||
H_r.SetSize(dimc);
|
||||
for (int i = 0; i<dimc; i++)
|
||||
{
|
||||
H_r(i) = - curlE_i(i) / (omega * mu);
|
||||
}
|
||||
}
|
||||
|
||||
void H_exact_i(const Vector &x, Vector & H_i)
|
||||
{
|
||||
// H = i ∇ × E / ω μ
|
||||
// H_i = ∇ × E_r / ω μ
|
||||
Vector curlE_r;
|
||||
curlE_exact_r(x,curlE_r);
|
||||
H_i.SetSize(dimc);
|
||||
for (int i = 0; i<dimc; i++)
|
||||
{
|
||||
H_i(i) = curlE_r(i) / (omega * mu);
|
||||
}
|
||||
}
|
||||
|
||||
void curlH_exact_r(const Vector &x,Vector &curlH_r)
|
||||
{
|
||||
// ∇ × H_r = - ∇ × ∇ × E_i / ω μ
|
||||
Vector curlcurlE_i;
|
||||
curlcurlE_exact_i(x,curlcurlE_i);
|
||||
curlH_r.SetSize(dim);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
curlH_r(i) = -curlcurlE_i(i) / (omega * mu);
|
||||
}
|
||||
}
|
||||
|
||||
void curlH_exact_i(const Vector &x,Vector &curlH_i)
|
||||
{
|
||||
// ∇ × H_i = ∇ × ∇ × E_r / ω μ
|
||||
Vector curlcurlE_r;
|
||||
curlcurlE_exact_r(x,curlcurlE_r);
|
||||
curlH_i.SetSize(dim);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
curlH_i(i) = curlcurlE_r(i) / (omega * mu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hatE_exact_r(const Vector & x, Vector & hatE_r)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E_exact_r(x,hatE_r);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector E_r;
|
||||
E_exact_r(x,E_r);
|
||||
hatE_r.SetSize(hatE_r.Size());
|
||||
// rotate E_hat
|
||||
hatE_r[0] = E_r[1];
|
||||
hatE_r[1] = -E_r[0];
|
||||
}
|
||||
}
|
||||
|
||||
void hatE_exact_i(const Vector & x, Vector & hatE_i)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E_exact_i(x,hatE_i);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector E_i;
|
||||
E_exact_i(x,E_i);
|
||||
hatE_i.SetSize(hatE_i.Size());
|
||||
// rotate E_hat
|
||||
hatE_i[0] = E_i[1];
|
||||
hatE_i[1] = -E_i[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hatH_exact_r(const Vector & x, Vector & hatH_r)
|
||||
{
|
||||
H_exact_r(x,hatH_r);
|
||||
}
|
||||
|
||||
void hatH_exact_i(const Vector & x, Vector & hatH_i)
|
||||
{
|
||||
H_exact_i(x,hatH_i);
|
||||
}
|
||||
|
||||
double hatH_exact_scalar_r(const Vector & x)
|
||||
{
|
||||
Vector hatH_r;
|
||||
H_exact_r(x,hatH_r);
|
||||
return hatH_r[0];
|
||||
}
|
||||
|
||||
double hatH_exact_scalar_i(const Vector & x)
|
||||
{
|
||||
Vector hatH_i;
|
||||
H_exact_i(x,hatH_i);
|
||||
return hatH_i[0];
|
||||
}
|
||||
|
||||
// J = -i ω ϵ E + ∇ × H
|
||||
// J_r + iJ_i = -i ω ϵ (E_r + i E_i) + ∇ × (H_r + i H_i)
|
||||
void rhs_func_r(const Vector &x, Vector & J_r)
|
||||
{
|
||||
// J_r = ω ϵ E_i + ∇ × H_r
|
||||
Vector E_i, curlH_r;
|
||||
E_exact_i(x,E_i);
|
||||
curlH_exact_r(x,curlH_r);
|
||||
J_r.SetSize(dim);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
J_r(i) = omega * epsilon * E_i(i) + curlH_r(i);
|
||||
}
|
||||
}
|
||||
|
||||
void rhs_func_i(const Vector &x, Vector & J_i)
|
||||
{
|
||||
// J_i = - ω ϵ E_r + ∇ × H_i
|
||||
Vector E_r, curlH_i;
|
||||
E_exact_r(x,E_r);
|
||||
curlH_exact_i(x,curlH_i);
|
||||
J_i.SetSize(dim);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
J_i(i) = -omega * epsilon * E_r(i) + curlH_i(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E,
|
||||
std::vector<complex<double>> &curlE,
|
||||
std::vector<complex<double>> &curlcurlE)
|
||||
{
|
||||
double x = X(0);
|
||||
double y = X(1);
|
||||
double z;
|
||||
if (dim == 3) z = X(2);
|
||||
|
||||
E.resize(dim);
|
||||
curlE.resize(dimc);
|
||||
curlcurlE.resize(dim);
|
||||
switch (prob)
|
||||
{
|
||||
case prob_type::polynomial:
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
|
||||
E[2] = x * y * (1.0 - x) * (1.0 - y);
|
||||
curlE[0] = (1.0 - x) * x * (y*(2.0*z-3.0)+1.0);
|
||||
curlE[1] = 2.0*(1.0 - y)*y*(x-z);
|
||||
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
|
||||
curlcurlE[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
|
||||
curlcurlE[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
|
||||
curlcurlE[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
|
||||
}
|
||||
else
|
||||
{
|
||||
E[0] = y * (1.0 - y);
|
||||
E[1] = x * y * (1.0 - x);
|
||||
curlE[0] = y*(3.0 - 2*x) - 1.0;
|
||||
curlcurlE[0] = 3.0 - 2*x;
|
||||
curlcurlE[1] = 2.0*y;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case prob_type::plane_wave:
|
||||
{
|
||||
std::complex<double> zi(0,1);
|
||||
std::complex<double> pw = exp(-zi * omega * (X.Sum()));
|
||||
E[0] = pw;
|
||||
E[1] = 0.0;
|
||||
if (dim == 3)
|
||||
{
|
||||
E[2] = 0.0;
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = -zi * omega * pw;
|
||||
curlE[2] = zi * omega * pw;
|
||||
|
||||
curlcurlE[0] = 2.0 * omega * omega * pw;
|
||||
curlcurlE[1] = - omega * omega * pw;
|
||||
curlcurlE[2] = - omega * omega * pw;
|
||||
}
|
||||
else
|
||||
{
|
||||
curlE[0] = zi * omega * pw;
|
||||
curlcurlE[0] = omega * omega * pw;
|
||||
curlcurlE[1] = - omega * omega * pw ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Fichera 'oven' problem not implemented yet");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void maxwell_solution_r(const Vector & X, Vector &E_r,
|
||||
Vector &curlE_r,
|
||||
Vector &curlcurlE_r)
|
||||
{
|
||||
E_r.SetSize(dim);
|
||||
curlE_r.SetSize(dimc);
|
||||
curlcurlE_r.SetSize(dim);
|
||||
|
||||
std::vector<complex<double>> E;
|
||||
std::vector<complex<double>> curlE;
|
||||
std::vector<complex<double>> curlcurlE;
|
||||
|
||||
maxwell_solution(X,E,curlE,curlcurlE);
|
||||
for (int i = 0; i<dim ; i++)
|
||||
{
|
||||
E_r(i) = E[i].real();
|
||||
curlcurlE_r(i) = curlcurlE[i].real();
|
||||
}
|
||||
for (int i = 0; i<dimc; i++)
|
||||
{
|
||||
curlE_r(i) = curlE[i].real();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void maxwell_solution_i(const Vector & X, Vector &E_i,
|
||||
Vector &curlE_i,
|
||||
Vector &curlcurlE_i)
|
||||
{
|
||||
E_i.SetSize(dim);
|
||||
curlE_i.SetSize(dimc);
|
||||
curlcurlE_i.SetSize(dim);
|
||||
|
||||
std::vector<complex<double>> E;
|
||||
std::vector<complex<double>> curlE;
|
||||
std::vector<complex<double>> curlcurlE;
|
||||
|
||||
maxwell_solution(X,E,curlE,curlcurlE);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
E_i(i) = E[i].imag();
|
||||
curlcurlE_i(i) = curlcurlE[i].imag();
|
||||
}
|
||||
for (int i = 0; i<dimc; i++)
|
||||
{
|
||||
curlE_i(i) = curlE[i].imag();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../../..
|
||||
MFEM_BUILD_DIR ?= ../../..
|
||||
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/dpg_tests/acoustics,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = complex_uw_dpg complex_uw_dpg_2D
|
||||
PAR_EXAMPLES = pcomplex_uw_dpg
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
// Test integrator
|
||||
// (∇ × E, F)
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void E_exact_r(const Vector &x, Vector & E_r);
|
||||
|
||||
void curlE_exact_r(const Vector &x, Vector &curlE_r);
|
||||
|
||||
void maxwell_solution(const Vector & X,
|
||||
std::vector<complex<double>> &E,
|
||||
std::vector<complex<double>> &curlE,
|
||||
std::vector<complex<double>> &curlcurlE);
|
||||
|
||||
void maxwell_solution_r(const Vector & X, Vector &E_r,
|
||||
Vector &curlE_r,
|
||||
Vector &curlcurlE_r);
|
||||
|
||||
int dim;
|
||||
int dimc;
|
||||
double omega;
|
||||
|
||||
enum prob_type
|
||||
{
|
||||
polynomial,
|
||||
plane_wave,
|
||||
fichera_oven
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *mesh_file = "../../../data/inline-hex.mesh";
|
||||
int order = 1;
|
||||
int delta_order = 1;
|
||||
bool visualization = true;
|
||||
double rnum=1.0;
|
||||
int ref = 1;
|
||||
double theta = 0.0;
|
||||
bool adjoint_graph_norm = false;
|
||||
bool static_cond = false;
|
||||
int iprob = 0;
|
||||
|
||||
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(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
|
||||
"Number of wavelengths");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: polynomial, 1: plane wave, 2: Gaussian beam");
|
||||
args.AddOption(&delta_order, "-do", "--delta_order",
|
||||
"Order enrichment for DPG test space.");
|
||||
args.AddOption(&theta, "-theta", "--theta",
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
|
||||
"-no-graph-norm", "--no-adjoint-graph-norm",
|
||||
"Enable or disable Adjoint Graph Norm on the test space");
|
||||
args.AddOption(&ref, "-ref", "--serial_ref",
|
||||
"Number of serial refinements.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (iprob > 2) { iprob = 0; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
omega = 2.*M_PI*rnum;
|
||||
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
dim = mesh.Dimension();
|
||||
|
||||
dimc = (dim == 3) ? 3 : 1;
|
||||
|
||||
// Define spaces
|
||||
// L2 space for E
|
||||
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
|
||||
FiniteElementSpace *E_fes = new FiniteElementSpace(&mesh,E_fec);
|
||||
|
||||
|
||||
FiniteElementCollection *curlE_fec = new L2_FECollection(order-1,dim);
|
||||
FiniteElementSpace *curlE_fes = new FiniteElementSpace(&mesh,curlE_fec,dimc);
|
||||
|
||||
mfem::out << "E_fes space true dofs = " << E_fes->GetTrueVSize() << endl;
|
||||
mfem::out << "curlE_fes space true dofs = " << curlE_fes->GetTrueVSize() << endl;
|
||||
|
||||
|
||||
GridFunction E_gf(E_fes);
|
||||
VectorFunctionCoefficient E_cf(dim,E_exact_r);
|
||||
E_gf.ProjectCoefficient(E_cf);
|
||||
|
||||
GridFunction curlE_gf(curlE_fes);
|
||||
VectorFunctionCoefficient curlE_cf(dimc,curlE_exact_r);
|
||||
curlE_gf.ProjectCoefficient(curlE_cf);
|
||||
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream E_sock(vishost, visport);
|
||||
E_sock.precision(8);
|
||||
E_sock << "solution\n"
|
||||
<< mesh << E_gf
|
||||
<< "window_title 'Exact E'" << flush;
|
||||
|
||||
socketstream curlE_sock(vishost, visport);
|
||||
curlE_sock.precision(8);
|
||||
curlE_sock << "solution\n"
|
||||
<< mesh << curlE_gf
|
||||
<< "window_title 'Exact curlE'" << flush;
|
||||
|
||||
|
||||
|
||||
MixedBilinearForm a(E_fes,curlE_fes);
|
||||
a.AddDomainIntegrator(new CurlIntegrator());
|
||||
a.Assemble();
|
||||
Array<int> empty;
|
||||
SparseMatrix A;
|
||||
a.FormRectangularSystemMatrix(empty,empty,A);
|
||||
|
||||
|
||||
Vector curl_load(A.Height());
|
||||
A.Mult(E_gf,curl_load);
|
||||
BilinearForm m(curlE_fes);
|
||||
m.AddDomainIntegrator(new VectorMassIntegrator);
|
||||
m.Assemble();
|
||||
SparseMatrix M;
|
||||
m.FormSystemMatrix(empty, M);
|
||||
|
||||
|
||||
GSSmoother prec(M);
|
||||
PCG(M, prec, curl_load, curlE_gf, 1, 200, 1e-12, 0.0);
|
||||
|
||||
|
||||
socketstream curlE2_sock(vishost, visport);
|
||||
curlE2_sock.precision(8);
|
||||
curlE2_sock << "solution\n"
|
||||
<< mesh << curlE_gf
|
||||
<< "window_title 'Numerical curlE'" << flush;
|
||||
|
||||
|
||||
delete E_fec;
|
||||
delete E_fes;
|
||||
delete curlE_fec;
|
||||
delete curlE_fes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void E_exact_r(const Vector &x, Vector & E_r)
|
||||
{
|
||||
Vector curlE_r;
|
||||
Vector curlcurlE_r;
|
||||
|
||||
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
|
||||
}
|
||||
|
||||
void curlE_exact_r(const Vector &x, Vector &curlE_r)
|
||||
{
|
||||
Vector E_r;
|
||||
Vector curlcurlE_r;
|
||||
|
||||
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E,
|
||||
std::vector<complex<double>> &curlE,
|
||||
std::vector<complex<double>> &curlcurlE)
|
||||
{
|
||||
double x = X(0);
|
||||
double y = X(1);
|
||||
double z;
|
||||
if (dim == 3)
|
||||
{
|
||||
z = X(2);
|
||||
}
|
||||
|
||||
E.resize(dim);
|
||||
curlE.resize(dimc);
|
||||
curlcurlE.resize(dim);
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
|
||||
E[2] = x * y * (1.0 - x) * (1.0 - y);
|
||||
|
||||
curlE[0] = (1.0 - x) * x * (y*(2.0*z-3.0)+1.0);
|
||||
curlE[1] = 2.0*(1.0 - y)*y*(x-z);
|
||||
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
|
||||
|
||||
curlcurlE[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
|
||||
curlcurlE[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
|
||||
curlcurlE[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
double c = 2.0*M_PI;
|
||||
E[0] = sin(c * y);
|
||||
E[1] = sin(c * x);
|
||||
curlE[0] = c * (cos(c*x) - cos(c*y));
|
||||
curlcurlE[0] = c*c * sin(c*y);
|
||||
curlcurlE[1] = c*c * sin(c*x);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Dimension cannot be 1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void maxwell_solution_r(const Vector & X, Vector &E_r,
|
||||
Vector &curlE_r,
|
||||
Vector &curlcurlE_r)
|
||||
{
|
||||
E_r.SetSize(dim);
|
||||
curlE_r.SetSize(dimc);
|
||||
curlcurlE_r.SetSize(dim);
|
||||
|
||||
std::vector<complex<double>> E;
|
||||
std::vector<complex<double>> curlE;
|
||||
std::vector<complex<double>> curlcurlE;
|
||||
|
||||
maxwell_solution(X,E,curlE,curlcurlE);
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
E_r(i) = E[i].real();
|
||||
curlcurlE_r(i) = curlcurlE[i].real();
|
||||
}
|
||||
for (int i = 0; i<dimc; i++)
|
||||
{
|
||||
curlE_r(i) = curlE[i].real();
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex1 -pa -d cuda
|
||||
// ex1 -fa -d cuda
|
||||
// ex1 -pa -d raja-cuda
|
||||
// * ex1 -pa -d raja-hip
|
||||
// ex1 -pa -d occa-cuda
|
||||
@@ -37,9 +38,13 @@
|
||||
// ex1 -pa -d occa-omp
|
||||
// ex1 -pa -d ceed-cpu
|
||||
// ex1 -pa -d ceed-cpu -o 4 -a
|
||||
// ex1 -pa -d ceed-cpu -m ../data/square-mixed.mesh
|
||||
// ex1 -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
|
||||
// * ex1 -pa -d ceed-cuda
|
||||
// * ex1 -pa -d ceed-hip
|
||||
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// ex1 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
@@ -73,6 +78,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool algebraic_ceed = false;
|
||||
@@ -87,6 +93,8 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
#ifdef MFEM_USE_CEED
|
||||
@@ -184,6 +192,7 @@ int main(int argc, char *argv[])
|
||||
// domain integrator.
|
||||
BilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (fa) { a.SetAssemblyLevel(AssemblyLevel::FULL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 10. Assemble the bilinear form and the corresponding linear system,
|
||||
|
||||
@@ -30,13 +30,18 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d cuda
|
||||
// mpirun -np 4 ex1p -fa -d cuda
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -o 4 -a
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-hip
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
@@ -74,6 +79,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool algebraic_ceed = false;
|
||||
@@ -88,6 +94,8 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
#ifdef MFEM_USE_CEED
|
||||
@@ -211,6 +219,7 @@ int main(int argc, char *argv[])
|
||||
// Diffusion domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (fa) { a.SetAssemblyLevel(AssemblyLevel::FULL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
|
||||
+1
-1
@@ -182,7 +182,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
for (int level = 0; level < order_refinements; ++level)
|
||||
{
|
||||
collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
|
||||
collections.Append(new H1_FECollection((int)std::pow(2, level+1), dim));
|
||||
fespaces.AddOrderRefinedLevel(collections.Last());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -219,7 +219,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
for (int level = 0; level < order_refinements; ++level)
|
||||
{
|
||||
collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
|
||||
collections.Append(new H1_FECollection((int)std::pow(2, level+1), dim));
|
||||
fespaces->AddOrderRefinedLevel(collections.Last());
|
||||
}
|
||||
|
||||
|
||||
+280
-68
@@ -3,34 +3,63 @@
|
||||
// Compile with: make ex33
|
||||
//
|
||||
// Sample runs: ex33 -m ../data/square-disc.mesh -alpha 0.33 -o 2
|
||||
// ex33 -m ../data/square-disc.mesh -alpha 4.5 -o 3
|
||||
// ex33 -m ../data/star.mesh -alpha 1.4 -o 3
|
||||
// ex33 -m ../data/star.mesh -alpha 0.99 -o 3
|
||||
// ex33 -m ../data/inline-quad.mesh -alpha 0.5 -o 3
|
||||
// ex33 -m ../data/amr-quad.mesh -alpha 1.5 -o 3
|
||||
// ex33 -m ../data/disc-nurbs.mesh -alpha 0.33 -o 3
|
||||
// ex33 -m ../data/disc-nurbs.mesh -alpha 2.4 -o 3 -r 4
|
||||
// ex33 -m ../data/l-shape.mesh -alpha 0.33 -o 3 -r 4
|
||||
// ex33 -m ../data/l-shape.mesh -alpha 1.7 -o 3 -r 5
|
||||
//
|
||||
// Verification runs:
|
||||
// ex33 -m ../data/inline-segment.mesh -ver -alpha 1.7 -o 2 -r 2
|
||||
// ex33 -m ../data/inline-quad.mesh -ver -alpha 1.2 -o 2 -r 2
|
||||
// ex33 -m ../data/amr-quad.mesh -ver -alpha 2.6 -o 2 -r 2
|
||||
// ex33 -m ../data/inline-hex.mesh -ver -alpha 0.3 -o 2 -r 1
|
||||
//
|
||||
// Note: the analytic solution to this problem is u = ∏_{i=0}^{dim-1} sin(π x_i)
|
||||
// for all alpha.
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// In this example we solve the following fractional PDE with MFEM:
|
||||
//
|
||||
// ( - Δ )^α u = f in Ω, u = 0 on ∂Ω, 0 < α < 1,
|
||||
// ( - Δ )^α u = f in Ω, u = 0 on ∂Ω, 0 < α,
|
||||
//
|
||||
// To solve this FPDE, we rely on a rational approximation [2] of the normal
|
||||
// linear operator A^{-α}, where A = - Δ (with associated homogeneous
|
||||
// boundary conditions). Namely, we first approximate the operator
|
||||
// To solve this FPDE, we apply the operator ( - Δ )^(-N), where the integer
|
||||
// N is given by floor(α). By doing so, we obtain
|
||||
//
|
||||
// A^{-α} ≈ Σ_{i=0}^N c_i (A + d_i I)^{-1}, d_0 = 0, d_i > 0,
|
||||
// ( - Δ )^(α-N) u = ( - Δ )^(-N) f in Ω, u = 0 on ∂Ω, 0 < α.
|
||||
//
|
||||
// We first compute the right hand side by solving the integer order PDE
|
||||
//
|
||||
// ( - Δ )^N g = f in Ω, g = ( - Δ )^k g = 0 on ∂Ω, k = 1,..,N-1
|
||||
//
|
||||
// The remaining FPDE is then given by
|
||||
//
|
||||
// ( - Δ )^(α-N) u = g in Ω, u = 0 on ∂Ω.
|
||||
//
|
||||
// We rely on a rational approximation [2] of the normal linear operator
|
||||
// A^{-α + N}, where A = - Δ (with associated homogeneous boundary conditions)
|
||||
// and (a-N) in (0,1). We approximate the operator
|
||||
//
|
||||
// A^{-α+N} ≈ Σ_{i=0}^M c_i (A + d_i I)^{-1}, d_0 = 0, d_i > 0,
|
||||
//
|
||||
// where I is the L2-identity operator and the coefficients c_i and d_i
|
||||
// are generated offline to a prescribed accuracy in a pre-processing step.
|
||||
// We use the triple-A algorithm [1] to generate the rational approximation
|
||||
// that this partial fractional expansion derives from. We then solve N+1
|
||||
// that this partial fractional expansion derives from. We then solve M+1
|
||||
// independent integer-order PDEs,
|
||||
//
|
||||
// A u_i + d_i u_i = c_i f in Ω, u_i = 0 on ∂Ω, i=0,...,N,
|
||||
// A u_i + d_i u_i = c_i g in Ω, u_i = 0 on ∂Ω, i=0,...,M,
|
||||
//
|
||||
// using MFEM and sum u_i to arrive at an approximate solution of the FPDE
|
||||
//
|
||||
// u ≈ Σ_{i=0}^N u_i.
|
||||
// u ≈ Σ_{i=0}^M u_i.
|
||||
//
|
||||
// (If alpha is an integer, we stop after the first PDE was solved.)
|
||||
//
|
||||
// References:
|
||||
//
|
||||
@@ -47,6 +76,8 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
|
||||
#include "ex33.hpp"
|
||||
|
||||
@@ -59,8 +90,9 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int num_refs = 3;
|
||||
bool visualization = true;
|
||||
double alpha = 0.5;
|
||||
bool visualization = true;
|
||||
bool verification = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -75,6 +107,9 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&verification, "-ver", "--verification", "-no-ver",
|
||||
"--no-verification",
|
||||
"Use sinusoidal function (f) for analytic comparison.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -84,9 +119,31 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
|
||||
Array<double> coeffs, poles;
|
||||
int progress_steps = 1;
|
||||
|
||||
// 2. Compute the coefficients that define the integer-order PDEs.
|
||||
ComputePartialFractionApproximation(alpha,coeffs,poles);
|
||||
// 2. Compute the rational expansion coefficients that define the
|
||||
// integer-order PDEs.
|
||||
const int power_of_laplace = floor(alpha);
|
||||
double exponent_to_approximate = alpha - power_of_laplace;
|
||||
bool integer_order = false;
|
||||
// Check if alpha is an integer or not.
|
||||
if (abs(exponent_to_approximate) > 1e-12)
|
||||
{
|
||||
mfem::out << "Approximating the fractional exponent "
|
||||
<< exponent_to_approximate
|
||||
<< endl;
|
||||
ComputePartialFractionApproximation(exponent_to_approximate, coeffs,
|
||||
poles);
|
||||
|
||||
// If the example is build without LAPACK, the exponent_to_approximate
|
||||
// might be modified by the function call above.
|
||||
alpha = exponent_to_approximate + power_of_laplace;
|
||||
}
|
||||
else
|
||||
{
|
||||
integer_order = true;
|
||||
mfem::out << "Treating integer order PDE." << endl;
|
||||
}
|
||||
|
||||
// 3. Read the mesh from the given mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
@@ -99,8 +156,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh.
|
||||
FiniteElementCollection *fec = new H1_FECollection(order, dim);
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
H1_FECollection fec(order, dim);
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace.GetTrueVSize() << endl;
|
||||
|
||||
@@ -114,79 +171,234 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 7. Define diffusion coefficient, load, and solution GridFunction.
|
||||
ConstantCoefficient f(1.0);
|
||||
auto func = [&alpha](const Vector &x)
|
||||
{
|
||||
double val = 1.0;
|
||||
for (int i=0; i<x.Size(); i++)
|
||||
{
|
||||
val *= sin(M_PI*x(i));
|
||||
}
|
||||
return pow(x.Size()*pow(M_PI,2), alpha) * val;
|
||||
};
|
||||
FunctionCoefficient f(func);
|
||||
ConstantCoefficient one(1.0);
|
||||
GridFunction u(&fespace);
|
||||
u = 0.;
|
||||
GridFunction x(&fespace);
|
||||
GridFunction g(&fespace);
|
||||
u = 0.0;
|
||||
x = 0.0;
|
||||
g = 0.0;
|
||||
|
||||
// 8. Prepare for visualization.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream xout, uout;
|
||||
ostringstream oss_x, oss_u;
|
||||
if (visualization)
|
||||
|
||||
// 9. Set up the linear form b(.) for integer-order PDE solves.
|
||||
LinearForm b(&fespace);
|
||||
if (verification)
|
||||
{
|
||||
xout.open(vishost, visport);
|
||||
xout.precision(8);
|
||||
uout.open(vishost, visport);
|
||||
uout.precision(8);
|
||||
// This statement is only relevant for the verification of the code. It
|
||||
// uses a different f such that an analytic solution is known and easy
|
||||
// to compare with the numerical one. The FPDE becomes:
|
||||
// (-Δ)^α u = (2\pi ^2)^α sin(\pi x) sin(\pi y) on [0,1]^2
|
||||
// -> u(x,y) = sin(\pi x) sin(\pi y)
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(f));
|
||||
}
|
||||
|
||||
for (int i = 0; i < coeffs.Size(); i++)
|
||||
else
|
||||
{
|
||||
// 9. Set up the linear form b(.) for integer-order PDE solve.
|
||||
LinearForm b(&fespace);
|
||||
ProductCoefficient cf(coeffs[i], f);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(cf));
|
||||
b.Assemble();
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
}
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define GridFunction for integer-order PDE solve.
|
||||
GridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
// ------------------------------------------------------------------------
|
||||
// 10. Solve the PDE (-Δ)^N g = f, i.e. compute g = (-Δ)^{-1}^N f.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 11. Set up the bilinear form a(.,.) for integer-order PDE solve.
|
||||
BilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ConstantCoefficient c2(-poles[i]);
|
||||
a.AddDomainIntegrator(new MassIntegrator(c2));
|
||||
a.Assemble();
|
||||
if (power_of_laplace > 0)
|
||||
{
|
||||
// 10.1 Compute Stiffnes Matrix
|
||||
BilinearForm k(&fespace);
|
||||
k.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
k.Assemble();
|
||||
|
||||
// 12. Assemble the bilinear form and the corresponding linear system.
|
||||
OperatorPtr A;
|
||||
// 10.2 Compute Mass Matrix
|
||||
BilinearForm m(&fespace);
|
||||
m.AddDomainIntegrator(new MassIntegrator(one));
|
||||
m.Assemble();
|
||||
SparseMatrix mass;
|
||||
Array<int> empty;
|
||||
m.FormSystemMatrix(empty, mass);
|
||||
|
||||
// 10.3 Form the system of equations
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
OperatorPtr Op;
|
||||
k.FormLinearSystem(ess_tdof_list, g, b, Op, X, B);
|
||||
GSSmoother M((SparseMatrix&)(*Op));
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
|
||||
mfem::out << "\nSolving PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " f " << endl;
|
||||
PCG(*A, M, B, X, 3, 200, 1e-12, 0.0);
|
||||
|
||||
// 14. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 15. Accumulate integer-order PDE solutions.
|
||||
u+=x;
|
||||
|
||||
// 16. Send the solutions by socket to a GLVis server.
|
||||
if (visualization)
|
||||
mfem::out << "\nComputing (-Δ) ^ -" << power_of_laplace
|
||||
<< " ( f ) " << endl;
|
||||
for (int i = 0; i < power_of_laplace; i++)
|
||||
{
|
||||
oss_x.str(""); oss_x.clear();
|
||||
oss_x << "Solution of PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " f";
|
||||
xout << "solution\n" << mesh << x
|
||||
<< "window_title '" << oss_x.str() << "'" << flush;
|
||||
// 10.4 Solve the linear system Op X = B (N times).
|
||||
PCG(*Op, M, B, X, 3, 300, 1e-12, 0.0);
|
||||
|
||||
oss_u.str(""); oss_u.clear();
|
||||
oss_u << "Solution of fractional PDE -Δ^" << alpha
|
||||
<< " u = f";
|
||||
uout << "solution\n" << mesh << u
|
||||
<< "window_title '" << oss_u.str() << "'" << flush;
|
||||
// 10.5 Visualize the solution g of -Δ ^ N g = f in the last step
|
||||
if (i == power_of_laplace - 1)
|
||||
{
|
||||
// Needed for visualization and solution verification.
|
||||
k.RecoverFEMSolution(X, b, g);
|
||||
if (integer_order && verification)
|
||||
{
|
||||
// For an integer order PDE, g is also our solution u.
|
||||
u+=g;
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
socketstream fout;
|
||||
ostringstream oss_f;
|
||||
fout.open(vishost, visport);
|
||||
fout.precision(8);
|
||||
oss_f.str(""); oss_f.clear();
|
||||
oss_f << "Step " << progress_steps++ << ": Solution of PDE -Δ ^ "
|
||||
<< power_of_laplace
|
||||
<< " g = f";
|
||||
fout << "solution\n" << mesh << g
|
||||
<< "window_title '" << oss_f.str() << "'" << flush;
|
||||
}
|
||||
}
|
||||
|
||||
// 10.6 Prepare for next iteration (primal / dual space)
|
||||
mass.Mult(X, B);
|
||||
X.SetSubVectorComplement(ess_tdof_list,0.0);
|
||||
}
|
||||
|
||||
// 10.7 Extract solution for the next step. The b now corresponds to the
|
||||
// function g in the PDE.
|
||||
const SparseMatrix * R = fespace.GetRestrictionMatrix();
|
||||
if (R)
|
||||
{
|
||||
R->MultTranspose(B,b);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = B;
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete fec;
|
||||
// ------------------------------------------------------------------------
|
||||
// 11. Solve the fractional PDE by solving M integer order PDEs and adding
|
||||
// up the solutions.
|
||||
// ------------------------------------------------------------------------
|
||||
if (!integer_order)
|
||||
{
|
||||
// Setup visualization.
|
||||
socketstream xout, uout;
|
||||
ostringstream oss_x, oss_u;
|
||||
if (visualization)
|
||||
{
|
||||
xout.open(vishost, visport);
|
||||
xout.precision(8);
|
||||
uout.open(vishost, visport);
|
||||
uout.precision(8);
|
||||
}
|
||||
// Iterate over all expansion coefficient that contribute to the
|
||||
// solution.
|
||||
for (int i = 0; i < coeffs.Size(); i++)
|
||||
{
|
||||
mfem::out << "\nSolving PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " g " << endl;
|
||||
|
||||
|
||||
// 11.1 Reset GridFunction for integer-order PDE solve.
|
||||
x = 0.0;
|
||||
|
||||
// 11.2 Set up the bilinear form a(.,.) for integer-order PDE solve.
|
||||
BilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ConstantCoefficient d_i(-poles[i]);
|
||||
a.AddDomainIntegrator(new MassIntegrator(d_i));
|
||||
a.Assemble();
|
||||
|
||||
// 11.3 Assemble the bilinear form and the corresponding linear system.
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 11.4 Solve the linear system A X = B.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
|
||||
PCG(*A, M, B, X, 3, 300, 1e-12, 0.0);
|
||||
|
||||
// 11.5 Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 11.6 Accumulate integer-order PDE solutions.
|
||||
x *= coeffs[i];
|
||||
u += x;
|
||||
|
||||
// 11.7 Send fractional PDE solution to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
oss_x.str(""); oss_x.clear();
|
||||
oss_x << "Step " << progress_steps
|
||||
<< ": Solution of PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " g";
|
||||
xout << "solution\n" << mesh << x
|
||||
<< "window_title '" << oss_x.str() << "'" << flush;
|
||||
|
||||
oss_u.str(""); oss_u.clear();
|
||||
oss_u << "Step " << progress_steps + 1
|
||||
<< ": Solution of fractional PDE (-Δ)^" << alpha
|
||||
<< " u = f";
|
||||
uout << "solution\n" << mesh << u
|
||||
<< "window_title '" << oss_u.str() << "'"
|
||||
<< flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 12. (optional) Verify the solution.
|
||||
// ------------------------------------------------------------------------
|
||||
if (verification)
|
||||
{
|
||||
auto solution = [] (const Vector &x)
|
||||
{
|
||||
double val = 1.0;
|
||||
for (int i=0; i<x.Size(); i++)
|
||||
{
|
||||
val *= sin(M_PI*x(i));
|
||||
}
|
||||
return val;
|
||||
};
|
||||
FunctionCoefficient sol(solution);
|
||||
double l2_error = u.ComputeL2Error(sol);
|
||||
|
||||
string analytic_solution,expected_mesh;
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
analytic_solution = "sin(π x)";
|
||||
expected_mesh = "inline_segment.mesh";
|
||||
break;
|
||||
case 2:
|
||||
analytic_solution = "sin(π x) sin(π y)";
|
||||
expected_mesh = "inline_quad.mesh";
|
||||
break;
|
||||
default:
|
||||
analytic_solution = "sin(π x) sin(π y) sin(π z)";
|
||||
expected_mesh = "inline_hex.mesh";
|
||||
break;
|
||||
}
|
||||
|
||||
mfem::out << "\n" << string(80,'=')
|
||||
<< "\n\nSolution Verification in "<< dim << "D \n\n"
|
||||
<< "Analytic solution : " << analytic_solution << "\n"
|
||||
<< "Expected mesh : " << expected_mesh <<"\n"
|
||||
<< "Your mesh : " << mesh_file << "\n"
|
||||
<< "L2 error : " << l2_error << "\n\n"
|
||||
<< string(80,'=') << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+15
-4
@@ -32,6 +32,7 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
@@ -249,6 +250,13 @@ void PartialFractionExpansion(double scale, Array<double> & poles,
|
||||
coeffs.SetSize(psize);
|
||||
coeffs = scale;
|
||||
|
||||
// Note: C p(z)/q(z) = Σ_i c_i / (z - p_i) results in an system of equations
|
||||
// where the N unknowns are the coefficients c_i. After multiplying the
|
||||
// system with q(z), the coefficients c_i can be computed analytically by
|
||||
// choosing N values for z. Choosing z_j = = p_j diagonalizes the system and
|
||||
// one can obtain an analytic form for the c_i coefficients. The result is
|
||||
// implemented in the code block below.
|
||||
|
||||
for (int i=0; i<psize; i++)
|
||||
{
|
||||
double tmp_numer=1.0;
|
||||
@@ -305,9 +313,12 @@ void ComputePartialFractionApproximation(double & alpha,
|
||||
if (print_warning)
|
||||
{
|
||||
mfem::out
|
||||
<< "\nMFEM is compiled without LAPACK.\nUsing precomputed values for PartialFractionApproximation. \n"
|
||||
<< "Only alpha = 0.33, 0.5, and 0.99 are available.\nThe default is alpha = 0.5."
|
||||
<< std::endl;
|
||||
<< "\n" << string(80, '=')
|
||||
<< "\nMFEM is compiled without LAPACK."
|
||||
<< "\nUsing precomputed values for PartialFractionApproximation."
|
||||
<< "\nOnly alpha = 0.33, 0.5, and 0.99 are available."
|
||||
<< "\nThe default is alpha = 0.5.\n" << string(80, '=') << "\n"
|
||||
<< endl;
|
||||
}
|
||||
const double eps = std::numeric_limits<double>::epsilon();
|
||||
|
||||
@@ -351,7 +362,7 @@ void ComputePartialFractionApproximation(double & alpha,
|
||||
|
||||
if (print_warning)
|
||||
{
|
||||
mfem::out << "Using precomputed values for alpha = "
|
||||
mfem::out << "=> Using precomputed values for alpha = "
|
||||
<< alpha << "\n" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
+294
-143
@@ -3,34 +3,63 @@
|
||||
// Compile with: make ex33p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex33p -m ../data/square-disc.mesh -alpha 0.33 -o 2
|
||||
// mpirun -np 4 ex33p -m ../data/square-disc.mesh -alpha 4.5 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/star.mesh -alpha 1.4 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/star.mesh -alpha 0.99 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/inline-quad.mesh -alpha 0.5 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/disc-nurbs.mesh -alpha 0.33 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/amr-quad.mesh -alpha 1.5 -o 3
|
||||
// mpirun -np 4 ex33p -m ../data/disc-nurbs.mesh -alpha 0.33 -o 3 -r 2
|
||||
// mpirun -np 4 ex33p -m ../data/disc-nurbs.mesh -alpha 2.4 -o 3 -r 4
|
||||
// mpirun -np 4 ex33p -m ../data/l-shape.mesh -alpha 0.33 -o 3 -r 4
|
||||
// mpirun -np 4 ex33p -m ../data/l-shape.mesh -alpha 1.7 -o 3 -r 5
|
||||
//
|
||||
// Verification runs:
|
||||
// mpirun -np 4 ex33p -m ../data/inline-segment.mesh -ver -alpha 1.7 -o 2 -r 2
|
||||
// mpirun -np 4 ex33p -m ../data/inline-quad.mesh -ver -alpha 1.2 -o 2 -r 2
|
||||
// mpirun -np 4 ex33p -m ../data/amr-quad.mesh -ver -alpha 2.6 -o 2 -r 2
|
||||
// mpirun -np 4 ex33p -m ../data/inline-hex.mesh -ver -alpha 0.3 -o 2 -r 1
|
||||
|
||||
// Note: the analytic solution to this problem is u = ∏_{i=0}^{dim-1} sin(π x_i)
|
||||
// for all alpha.
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// In this example we solve the following fractional PDE with MFEM:
|
||||
//
|
||||
// ( - Δ )^α u = f in Ω, u = 0 on ∂Ω, 0 < α < 1,
|
||||
// ( - Δ )^α u = f in Ω, u = 0 on ∂Ω, 0 < α,
|
||||
//
|
||||
// To solve this FPDE, we rely on a rational approximation [2] of the normal
|
||||
// linear operator A^{-α}, where A = - Δ (with associated homogeneous
|
||||
// boundary conditions). Namely, we first approximate the operator
|
||||
// To solve this FPDE, we apply the operator ( - Δ )^(-N), where the integer
|
||||
// N is given by floor(α). By doing so, we obtain
|
||||
//
|
||||
// A^{-α} ≈ Σ_{i=0}^N c_i (A + d_i I)^{-1}, d_0 = 0, d_i > 0,
|
||||
// ( - Δ )^(α-N) u = ( - Δ )^(-N) f in Ω, u = 0 on ∂Ω, 0 < α.
|
||||
//
|
||||
// We first compute the right hand side by solving the integer order PDE
|
||||
//
|
||||
// ( - Δ )^N g = f in Ω, g = ( - Δ )^k g = 0 on ∂Ω, k = 1,..,N-1
|
||||
//
|
||||
// The remaining FPDE is then given by
|
||||
//
|
||||
// ( - Δ )^(α-N) u = g in Ω, u = 0 on ∂Ω.
|
||||
//
|
||||
// We rely on a rational approximation [2] of the normal linear operator
|
||||
// A^{-α + N}, where A = - Δ (with associated homogeneous boundary conditions)
|
||||
// and (a-N) in (0,1). We approximate the operator
|
||||
//
|
||||
// A^{-α+N} ≈ Σ_{i=0}^M c_i (A + d_i I)^{-1}, d_0 = 0, d_i > 0,
|
||||
//
|
||||
// where I is the L2-identity operator and the coefficients c_i and d_i
|
||||
// are generated offline to a prescribed accuracy in a pre-processing step.
|
||||
// We use the triple-A algorithm [1] to generate the rational approximation
|
||||
// that this partial fractional expansion derives from. We then solve N+1
|
||||
// that this partial fractional expansion derives from. We then solve M+1
|
||||
// independent integer-order PDEs,
|
||||
//
|
||||
// A u_i + d_i u_i = c_i f in Ω, u_i = 0 on ∂Ω, i=0,...,N,
|
||||
// A u_i + d_i u_i = c_i g in Ω, u_i = 0 on ∂Ω, i=0,...,M,
|
||||
//
|
||||
// using MFEM and sum u_i to arrive at an approximate solution of the FPDE
|
||||
//
|
||||
// u ≈ Σ_{i=0}^N u_i.
|
||||
// u ≈ Σ_{i=0}^M u_i.
|
||||
//
|
||||
// (If alpha is an integer, we stop after the first PDE was solved.)
|
||||
//
|
||||
// References:
|
||||
//
|
||||
@@ -47,6 +76,8 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
|
||||
#include "ex33.hpp"
|
||||
|
||||
@@ -65,9 +96,9 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int num_refs = 3;
|
||||
bool visualization = true;
|
||||
bool visualize_x = false;
|
||||
double alpha = 0.5;
|
||||
bool visualization = true;
|
||||
bool verification = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -79,12 +110,12 @@ int main(int argc, char *argv[])
|
||||
"Number of uniform refinements");
|
||||
args.AddOption(&alpha, "-alpha", "--alpha",
|
||||
"Fractional exponent");
|
||||
args.AddOption(&visualize_x, "-vis_x", "--visualize_x", "-no-vis_x",
|
||||
"--no-visualization_x",
|
||||
"Enable or disable GLVis visualization of each integer-order PDE solution.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization of the fractional PDE solution.");
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&verification, "-ver", "--verification", "-no-ver",
|
||||
"--no-verification",
|
||||
"Use sinusoidal function (f) for analytic comparison.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -97,61 +128,51 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
Array<double> coeffs, poles;
|
||||
int progress_steps = 1;
|
||||
|
||||
// 2. Compute the coefficients that define the integer-order PDEs.
|
||||
ComputePartialFractionApproximation(alpha,coeffs,poles);
|
||||
|
||||
int num_par_solves;
|
||||
int max_par_solves = max(1,num_procs/2);
|
||||
for (num_par_solves=max_par_solves; num_par_solves>0; num_par_solves--)
|
||||
// 2. Compute the rational expansion coefficients that define the
|
||||
// integer-order PDEs.
|
||||
const int power_of_laplace = floor(alpha);
|
||||
double exponent_to_approximate = alpha - power_of_laplace;
|
||||
bool integer_order = false;
|
||||
// Check if alpha is an integer or not.
|
||||
if (abs(exponent_to_approximate) > 1e-12)
|
||||
{
|
||||
if (num_procs%num_par_solves==0 && num_par_solves<coeffs.Size())
|
||||
if (Mpi::Root())
|
||||
{
|
||||
break;
|
||||
mfem::out << "Approximating the fractional exponent "
|
||||
<< exponent_to_approximate
|
||||
<< endl;
|
||||
}
|
||||
ComputePartialFractionApproximation(exponent_to_approximate, coeffs,
|
||||
poles);
|
||||
|
||||
// If the example is build without LAPACK, the exponent_to_approximate
|
||||
// might be modified by the function call above.
|
||||
alpha = exponent_to_approximate + power_of_laplace;
|
||||
}
|
||||
else
|
||||
{
|
||||
integer_order = true;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << "Treating integer order PDE." << endl;
|
||||
}
|
||||
}
|
||||
if (num_par_solves == 1) {num_par_solves = num_procs;}
|
||||
|
||||
int solver_ranks = num_procs/num_par_solves;
|
||||
|
||||
// 3. Split the MPI communicator:
|
||||
// row_comm is used for parallel partition of the mesh
|
||||
// col_comm is used for independent integer-order solves
|
||||
int row_color = myid / solver_ranks; // Determine color based on row
|
||||
int col_color = myid % solver_ranks; // Determine color based on col
|
||||
|
||||
MPI_Comm row_comm, col_comm;
|
||||
MPI_Comm_split(MPI_COMM_WORLD, row_color, myid, &row_comm);
|
||||
MPI_Comm_split(MPI_COMM_WORLD, col_color, myid, &col_comm);
|
||||
|
||||
int row_rank, row_size, col_rank, col_size;
|
||||
MPI_Comm_rank(row_comm, &row_rank);
|
||||
MPI_Comm_size(row_comm, &row_size);
|
||||
MPI_Comm_rank(col_comm, &col_rank);
|
||||
MPI_Comm_size(col_comm, &col_size);
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << "\nTotal number of MPI ranks = " << num_procs << endl;
|
||||
mfem::out << "Number of independent parallel solves = " << col_size << endl;
|
||||
mfem::out << "Number of MPI ranks within each solve = " << row_size
|
||||
<<"\n" << endl;
|
||||
}
|
||||
|
||||
// 4. Read the mesh from the given mesh file.
|
||||
// 3. Read the mesh from the given mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 5. Refine the mesh to increase the resolution.
|
||||
// 4. Refine the mesh to increase the resolution.
|
||||
for (int i = 0; i < num_refs; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
ParMesh pmesh(row_comm, mesh);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// 6. Define a finite element space on the mesh.
|
||||
// 5. Define a finite element space on the mesh.
|
||||
H1_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
if (Mpi::Root())
|
||||
@@ -160,7 +181,7 @@ int main(int argc, char *argv[])
|
||||
<< fespace.GetTrueVSize() << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
@@ -169,120 +190,250 @@ int main(int argc, char *argv[])
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 8. Define diffusion coefficient, load, and solution GridFunction.
|
||||
ConstantCoefficient f(1.0);
|
||||
// 7. Define diffusion coefficient, load, and solution GridFunction.
|
||||
auto func = [&alpha](const Vector &x)
|
||||
{
|
||||
double val = 1.0;
|
||||
for (int i=0; i<x.Size(); i++)
|
||||
{
|
||||
val *= sin(M_PI*x(i));
|
||||
}
|
||||
return pow(x.Size()*pow(M_PI,2), alpha) * val;
|
||||
};
|
||||
FunctionCoefficient f(func);
|
||||
ConstantCoefficient one(1.0);
|
||||
ParGridFunction u(&fespace);
|
||||
ParGridFunction x(&fespace);
|
||||
ParGridFunction g(&fespace);
|
||||
u = 0.0;
|
||||
x = 0.0;
|
||||
g = 0.0;
|
||||
|
||||
// 8. Prepare for visualization.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
|
||||
// 9. Set up the linear form b(.) for integer-order PDE solves.
|
||||
ParLinearForm b(&fespace);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(f));
|
||||
if (verification)
|
||||
{
|
||||
// This statement is only relevant for the verification of the code. It
|
||||
// uses a different f such that an analytic solution is known and easy
|
||||
// to compare with the numerical one. The FPDE becomes:
|
||||
// (-Δ)^α u = (2\pi ^2)^α sin(\pi x) sin(\pi y) on [0,1]^2
|
||||
// -> u(x,y) = sin(\pi x) sin(\pi y)
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(f));
|
||||
}
|
||||
else
|
||||
{
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
}
|
||||
b.Assemble();
|
||||
|
||||
int my_coeff_size = max(coeffs.Size()/col_size,1);
|
||||
int ibeg = col_rank*my_coeff_size;
|
||||
if (ibeg + 2*my_coeff_size > coeffs.Size())
|
||||
// ------------------------------------------------------------------------
|
||||
// 10. Solve the PDE (-Δ)^N g = f, i.e. compute g = (-Δ)^{-1}^N f.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
if (power_of_laplace > 0)
|
||||
{
|
||||
my_coeff_size = coeffs.Size()-col_rank*my_coeff_size;
|
||||
}
|
||||
else if (ibeg > coeffs.Size() - 1)
|
||||
{
|
||||
my_coeff_size = 0;
|
||||
}
|
||||
// 10.1 Compute Stiffnes Matrix
|
||||
ParBilinearForm k(&fespace);
|
||||
k.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
k.Assemble();
|
||||
|
||||
int iend = ibeg+my_coeff_size;
|
||||
// 10.2 Compute Mass Matrix
|
||||
ParBilinearForm m(&fespace);
|
||||
m.AddDomainIntegrator(new MassIntegrator(one));
|
||||
m.Assemble();
|
||||
HypreParMatrix mass;
|
||||
Array<int> empty;
|
||||
m.FormSystemMatrix(empty, mass);
|
||||
|
||||
|
||||
for (int i = ibeg; i < iend; i++)
|
||||
{
|
||||
// 10. Reset GridFunction for integer-order PDE solve.
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the bilinear form a(.,.) for integer-order PDE solve.
|
||||
ParBilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ConstantCoefficient d_i(-poles[i]);
|
||||
a.AddDomainIntegrator(new MassIntegrator(d_i));
|
||||
a.Assemble();
|
||||
|
||||
// 12. Assemble the bilinear form and the corresponding linear system.
|
||||
OperatorPtr A;
|
||||
// 10.3 Form the system of equations
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
HypreBoomerAMG * prec = new HypreBoomerAMG;
|
||||
prec->SetPrintLevel(-1);
|
||||
|
||||
int print_level = (col_rank==0) ? 3 : 0;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << "\nMPI rank " << myid
|
||||
<< ": Solving PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " f " << endl;
|
||||
}
|
||||
CGSolver cg(row_comm);
|
||||
OperatorPtr Op;
|
||||
k.FormLinearSystem(ess_tdof_list, g, b, Op, X, B);
|
||||
HypreBoomerAMG prec;
|
||||
prec.SetPrintLevel(-1);
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(print_level);
|
||||
cg.SetPreconditioner(*prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetOperator(*Op);
|
||||
|
||||
// 14. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 15. Accumulate integer-order PDE solutions.
|
||||
x *= coeffs[i];
|
||||
u += x;
|
||||
|
||||
// 16. Send integer-order PDE solutions to a GLVis server.
|
||||
if (visualize_x)
|
||||
if (Mpi::Root())
|
||||
{
|
||||
if (col_rank > 0 && i < iend-1)
|
||||
mfem::out << "\nComputing (-Δ) ^ -" << power_of_laplace
|
||||
<< " ( f ) " << endl;
|
||||
}
|
||||
for (int i = 0; i < power_of_laplace; i++)
|
||||
{
|
||||
// 10.4 Solve the linear system Op X = B (N times).
|
||||
cg.Mult(B, X);
|
||||
// 10.5 Visualize the solution g of -Δ ^ N g = f in the last step
|
||||
if (i == power_of_laplace - 1)
|
||||
{
|
||||
MPI_Status status;
|
||||
MPI_Recv(nullptr,0,MPI_INT, col_rank-1,0,col_comm,&status);
|
||||
// Needed for visualization and solution verification.
|
||||
k.RecoverFEMSolution(X, b, g);
|
||||
if (integer_order && verification)
|
||||
{
|
||||
// For an integer order PDE, g is also our solution u.
|
||||
u+=g;
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
socketstream fout;
|
||||
ostringstream oss_f;
|
||||
fout.open(vishost, visport);
|
||||
fout.precision(8);
|
||||
oss_f.str(""); oss_f.clear();
|
||||
oss_f << "Step " << progress_steps++ << ": Solution of PDE -Δ ^ "
|
||||
<< power_of_laplace
|
||||
<< " g = f";
|
||||
fout << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << pmesh << g
|
||||
<< "window_title '" << oss_f.str() << "'" << flush;
|
||||
}
|
||||
}
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream xout(vishost, visport);
|
||||
|
||||
// 10.6 Prepare for next iteration (primal / dual space)
|
||||
mass.Mult(X, B);
|
||||
X.SetSubVectorComplement(ess_tdof_list,0.0);
|
||||
}
|
||||
|
||||
// 10.7 Extract solution for the next step. The b now corresponds to the
|
||||
// function g in the PDE.
|
||||
const SparseMatrix* rm = fespace.GetRestrictionMatrix();
|
||||
rm->MultTranspose(B, b);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 11. Solve the fractional PDE by solving M integer order PDEs and adding
|
||||
// up the solutions.
|
||||
// ------------------------------------------------------------------------
|
||||
if (!integer_order)
|
||||
{
|
||||
// Setup visualization.
|
||||
socketstream xout, uout;
|
||||
ostringstream oss_x, oss_u;
|
||||
if (visualization)
|
||||
{
|
||||
xout.open(vishost, visport);
|
||||
xout.precision(8);
|
||||
ostringstream oss;
|
||||
oss << "Solution of PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " f" ;
|
||||
xout << "parallel " << row_size << " " << row_rank << "\n";
|
||||
xout << "solution\n" << pmesh << x
|
||||
<< "window_title '" << oss.str() << "'" << flush;
|
||||
if (col_rank < col_size-1)
|
||||
uout.open(vishost, visport);
|
||||
uout.precision(8);
|
||||
}
|
||||
// Iterate over all expansion coefficient that contribute to the
|
||||
// solution.
|
||||
for (int i = 0; i < coeffs.Size(); i++)
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
MPI_Send(nullptr,0,MPI_INT,col_rank+1,0,col_comm);
|
||||
mfem::out << "\nSolving PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " g " << endl;
|
||||
}
|
||||
|
||||
// 11.1 Reset GridFunction for integer-order PDE solve.
|
||||
x = 0.0;
|
||||
|
||||
// 11.2 Set up the bilinear form a(.,.) for integer-order PDE solve.
|
||||
ParBilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ConstantCoefficient d_i(-poles[i]);
|
||||
a.AddDomainIntegrator(new MassIntegrator(d_i));
|
||||
a.Assemble();
|
||||
|
||||
// 11.3 Assemble the bilinear form and the corresponding linear system.
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 11.4 Solve the linear system A X = B.
|
||||
HypreBoomerAMG prec;
|
||||
prec.SetPrintLevel(-1);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
|
||||
// 11.5 Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 11.6 Accumulate integer-order PDE solutions.
|
||||
x *= coeffs[i];
|
||||
u += x;
|
||||
|
||||
// 11.7 Send fractional PDE solution to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
oss_x.str(""); oss_x.clear();
|
||||
oss_x << "Step " << progress_steps
|
||||
<< ": Solution of PDE -Δ u + " << -poles[i]
|
||||
<< " u = " << coeffs[i] << " g";
|
||||
xout << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << pmesh << x
|
||||
<< "window_title '" << oss_x.str() << "'" << flush;
|
||||
|
||||
oss_u.str(""); oss_u.clear();
|
||||
oss_u << "Step " << progress_steps + 1
|
||||
<< ": Solution of fractional PDE (-Δ)^" << alpha
|
||||
<< " u = f";
|
||||
uout << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << pmesh << u
|
||||
<< "window_title '" << oss_u.str() << "'"
|
||||
<< flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Accumulate for the fractional PDE solution
|
||||
MPI_Allreduce(MPI_IN_PLACE, u.GetData(), u.Size(),
|
||||
MPI_DOUBLE, MPI_SUM,col_comm);
|
||||
|
||||
// 18. Send fractional PDE solution to a GLVis server.
|
||||
if (visualization)
|
||||
// ------------------------------------------------------------------------
|
||||
// 12. (optional) Verify the solution.
|
||||
// ------------------------------------------------------------------------
|
||||
if (verification)
|
||||
{
|
||||
if (col_rank == 0)
|
||||
auto solution = [] (const Vector &x)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream uout(vishost, visport);
|
||||
uout.precision(8);
|
||||
ostringstream oss;
|
||||
oss << "Solution of fractional PDE -Δ^" << alpha
|
||||
<< " u = f" ;
|
||||
uout << "parallel " << row_size << " " << row_rank << "\n";
|
||||
uout << "solution\n" << pmesh << u
|
||||
<< "window_title '" << oss.str() << "'" << flush;
|
||||
double val = 1.0;
|
||||
for (int i=0; i<x.Size(); i++)
|
||||
{
|
||||
val *= sin(M_PI*x(i));
|
||||
}
|
||||
return val;
|
||||
};
|
||||
FunctionCoefficient sol(solution);
|
||||
double l2_error = u.ComputeL2Error(sol);
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
string analytic_solution,expected_mesh;
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
analytic_solution = "sin(π x)";
|
||||
expected_mesh = "inline_segment.mesh";
|
||||
break;
|
||||
case 2:
|
||||
analytic_solution = "sin(π x) sin(π y)";
|
||||
expected_mesh = "inline_quad.mesh";
|
||||
break;
|
||||
default:
|
||||
analytic_solution = "sin(π x) sin(π y) sin(π z)";
|
||||
expected_mesh = "inline_hex.mesh";
|
||||
break;
|
||||
}
|
||||
|
||||
mfem::out << "\n" << string(80,'=')
|
||||
<< "\n\nSolution Verification in "<< dim << "D \n\n"
|
||||
<< "Analytic solution : " << analytic_solution << "\n"
|
||||
<< "Expected mesh : " << expected_mesh <<"\n"
|
||||
<< "Your mesh : " << mesh_file << "\n"
|
||||
<< "L2 error : " << l2_error << "\n\n"
|
||||
<< string(80,'=') << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-5
@@ -72,8 +72,8 @@ int main(int argc, char *argv[])
|
||||
// largest number that gives a final mesh with no more than 10,000
|
||||
// elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
int ref_levels = 1;
|
||||
// (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
@@ -147,6 +147,8 @@ int main(int argc, char *argv[])
|
||||
F.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
F.Assemble();
|
||||
|
||||
|
||||
|
||||
// 7. Set up the mixed bilinear form for the primal trial unknowns, B0,
|
||||
// the mixed bilinear form for the interfacial unknowns, Bhat,
|
||||
// the inverse stiffness matrix on the discontinuous test space, Sinv,
|
||||
@@ -187,10 +189,17 @@ int main(int argc, char *argv[])
|
||||
// 8. Set up the 1x2 block Least Squares DPG operator, B = [B0 Bhat],
|
||||
// the normal equation operator, A = B^t Sinv B, and
|
||||
// the normal equation right-hand-size, b = B^t Sinv F.
|
||||
BlockOperator B(offsets_test, offsets);
|
||||
// BlockOperator B(offsets_test, offsets);
|
||||
|
||||
BlockMatrix B(offsets_test, offsets);
|
||||
B.SetBlock(0,0,&matB0);
|
||||
B.SetBlock(0,1,&matBhat);
|
||||
RAPOperator A(B, matSinv, B);
|
||||
SparseMatrix * Bh = B.CreateMonolithic();
|
||||
|
||||
SparseMatrix * A = RAP(*Bh, matSinv, *Bh);
|
||||
|
||||
|
||||
// RAPOperator A(B, matSinv, B);
|
||||
{
|
||||
Vector SinvF(s_test);
|
||||
matSinv.Mult(F,SinvF);
|
||||
@@ -234,7 +243,20 @@ int main(int argc, char *argv[])
|
||||
// 10. Solve the normal equation system using the PCG iterative solver.
|
||||
// Check the weighted norm of residual for the DPG least square problem.
|
||||
// Wrap the primal variable in a GridFunction for visualization purposes.
|
||||
PCG(A, P, b, x, 1, 200, 1e-12, 0.0);
|
||||
|
||||
|
||||
// PCG(*A, P, b, x, 1, 200, 1e-12, 0.0);
|
||||
|
||||
GSSmoother M(*A);
|
||||
|
||||
CGSolver cg;
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(b, x);
|
||||
|
||||
|
||||
{
|
||||
Vector LSres(s_test);
|
||||
|
||||
+40
-4
@@ -305,38 +305,66 @@ public:
|
||||
/// Finalizes the matrix initialization.
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
/// Returns a const reference to the sparse matrix.
|
||||
/** @brief Returns a const reference to the sparse matrix: \f$ M \f$
|
||||
|
||||
This will fail if HasSpMat() is false. */
|
||||
const SparseMatrix &SpMat() const
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix: \f$ M \f$
|
||||
/** @brief Returns a reference to the sparse matrix: \f$ M \f$
|
||||
|
||||
This will fail if HasSpMat() is false. */
|
||||
SparseMatrix &SpMat()
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/** @brief Returns true if the sparse matrix is not null, false otherwise.
|
||||
|
||||
@sa SpMat(). */
|
||||
bool HasSpMat()
|
||||
{
|
||||
return mat != nullptr;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Nullifies the internal matrix \f$ M \f$ and returns a pointer
|
||||
to it. Used for transfering ownership. */
|
||||
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
|
||||
|
||||
/// Returns a const reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
/** @brief Returns a const reference to the sparse matrix of eliminated b.c.:
|
||||
\f$ M_e \f$
|
||||
|
||||
This will fail if HasSpMatElim() is false. */
|
||||
const SparseMatrix &SpMatElim() const
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
return *mat_e;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
/** @brief Returns a reference to the sparse matrix of eliminated b.c.:
|
||||
\f$ M_e \f$
|
||||
|
||||
This will fail if HasSpMatElim() is false. */
|
||||
SparseMatrix &SpMatElim()
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
return *mat_e;
|
||||
}
|
||||
|
||||
/** @brief Returns true if the sparse matrix of eliminated b.c.s is not null,
|
||||
false otherwise.
|
||||
|
||||
@sa SpMatElim(). */
|
||||
bool HasSpMatElim()
|
||||
{
|
||||
return mat_e != nullptr;
|
||||
}
|
||||
|
||||
/// Adds new Domain Integrator. Assumes ownership of @a bfi.
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi);
|
||||
/// Adds new Domain Integrator restricted to certain elements specified by
|
||||
@@ -410,6 +438,14 @@ public:
|
||||
virtual const Operator *GetOutputRestriction() const
|
||||
{ return GetRestriction(); }
|
||||
|
||||
/// @brief Compute serial RAP operator and store it in @a A as a SparseMatrix.
|
||||
void SerialRAP(OperatorHandle &A)
|
||||
{
|
||||
MFEM_ASSERT(mat, "SerialRAP requires the SparseMatrix to be assembled.");
|
||||
ConformingAssemble();
|
||||
A.Reset(mat, false);
|
||||
}
|
||||
|
||||
/** @brief Form the linear system A X = B, corresponding to this bilinear
|
||||
form and the linear form @a b(.). */
|
||||
/** This method applies any necessary transformations to the linear system
|
||||
|
||||
@@ -251,6 +251,7 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
|
||||
|
||||
void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
{
|
||||
if ( Device::Allows(Backend::CEED_MASK) ) { return; }
|
||||
ElementDofOrdering ordering = UsesTensorBasis(*a->FESpace())?
|
||||
ElementDofOrdering::LEXICOGRAPHIC:
|
||||
ElementDofOrdering::NATIVE;
|
||||
@@ -956,6 +957,57 @@ void FABilinearFormExtension::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FABilinearFormExtension::RAP(OperatorHandle &A)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if ( auto pa = dynamic_cast<ParBilinearForm*>(a) )
|
||||
{
|
||||
pa->ParallelRAP(*pa->mat, A);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
a->SerialRAP(A);
|
||||
}
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::EliminateBC(const Array<int> &ess_dofs,
|
||||
OperatorHandle &A)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if ( dynamic_cast<ParBilinearForm*>(a) )
|
||||
{
|
||||
A.As<HypreParMatrix>()->EliminateBC(ess_dofs,
|
||||
DiagonalPolicy::DIAG_ONE);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
A.As<SparseMatrix>()->EliminateBC(ess_dofs,
|
||||
DiagonalPolicy::DIAG_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::FormSystemMatrix(const Array<int> &ess_dofs,
|
||||
OperatorHandle &A)
|
||||
{
|
||||
RAP(A);
|
||||
EliminateBC(ess_dofs, A);
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A,
|
||||
Vector &X, Vector &B,
|
||||
int copy_interior)
|
||||
{
|
||||
Operator *A_out;
|
||||
Operator::FormLinearSystem(ess_tdof_list, x, b, A_out, X, B, copy_interior);
|
||||
delete A_out;
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::DGMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
@@ -125,6 +125,15 @@ public:
|
||||
FABilinearFormExtension(BilinearForm *form);
|
||||
|
||||
void Assemble();
|
||||
void RAP(OperatorHandle &A);
|
||||
/** @note Always does `DIAG_ONE` policy to be consistent with
|
||||
`Operator::FormConstrainedSystemOperator`. */
|
||||
void EliminateBC(const Array<int> &ess_dofs, OperatorHandle &A);
|
||||
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
|
||||
void FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0);
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
|
||||
|
||||
+423
-2
@@ -144,6 +144,14 @@ void BilinearFormIntegrator::AssembleFaceMatrix (
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe, const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleTraceFaceMatrix(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
|
||||
const FiniteElement &test_fe2, FaceElementTransformations &Trans,
|
||||
@@ -793,6 +801,84 @@ const IntegrationRule &GradientIntegrator::GetRule(const FiniteElement
|
||||
}
|
||||
|
||||
|
||||
void CurlIntegrator::AssembleElementMatrix2(
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
int dim = trial_fe.GetDim();
|
||||
int trial_dof = trial_fe.GetDof();
|
||||
int test_dof = test_fe.GetDof();
|
||||
int dimc = (dim == 3) ? 3 : 1;
|
||||
|
||||
MFEM_ASSERT(trial_fe.GetMapType() == mfem::FiniteElement::H_CURL ||
|
||||
dim == 2 && trial_fe.GetMapType() == mfem::FiniteElement::VALUE,
|
||||
"Trial finite element must be either 2D/3D H(Curl) or 2D H1");
|
||||
MFEM_ASSERT(test_fe.GetMapType() == mfem::FiniteElement::VALUE ||
|
||||
test_fe.GetMapType() == mfem::FiniteElement::INTEGRAL,
|
||||
"Test finite element must be in H1/L2");
|
||||
|
||||
bool spaceH1 = (trial_fe.GetMapType() == mfem::FiniteElement::VALUE);
|
||||
|
||||
if (spaceH1)
|
||||
{
|
||||
dshape.SetSize(trial_dof,dim);
|
||||
curlshape.SetSize(dim*trial_dof,1);
|
||||
dimc = dim;
|
||||
}
|
||||
else
|
||||
{
|
||||
curlshape.SetSize(trial_dof,dimc);
|
||||
elmat_comp.SetSize(test_dof, trial_dof);
|
||||
}
|
||||
elmat.SetSize(dimc * test_dof, trial_dof);
|
||||
shape.SetSize(test_dof);
|
||||
elmat = 0.0;
|
||||
|
||||
double c;
|
||||
Vector d_col;
|
||||
const IntegrationRule *ir = IntRule;
|
||||
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderJ();
|
||||
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Trans.SetIntPoint(&ip);
|
||||
if (spaceH1)
|
||||
{
|
||||
trial_fe.CalcPhysDShape(Trans, dshape);
|
||||
dshape.GradToCurl(curlshape);
|
||||
}
|
||||
else
|
||||
{
|
||||
trial_fe.CalcPhysCurlShape(Trans, curlshape);
|
||||
}
|
||||
test_fe.CalcPhysShape(Trans, shape);
|
||||
c = ip.weight*Trans.Weight();
|
||||
if (Q)
|
||||
{
|
||||
c *= Q->Eval(Trans, ip);
|
||||
}
|
||||
shape *= c;
|
||||
|
||||
for (int d = 0; d < dimc; ++d)
|
||||
{
|
||||
double * curldata = &(curlshape.GetData())[d*trial_dof];
|
||||
for (int jj = 0; jj < trial_dof; ++jj)
|
||||
{
|
||||
for (int ii = 0; ii < test_dof; ++ii)
|
||||
{
|
||||
elmat(d * test_dof + ii, jj) += shape(ii) * curldata[jj];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AssembleElementMatrix
|
||||
( const FiniteElement &el, ElementTransformation &Trans,
|
||||
DenseMatrix &elmat )
|
||||
@@ -2003,6 +2089,84 @@ void CurlCurlIntegrator::AssembleElementMatrix
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CurlCurlIntegrator::AssembleElementMatrix2(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int tr_nd = trial_fe.GetDof();
|
||||
int te_nd = test_fe.GetDof();
|
||||
dim = trial_fe.GetDim();
|
||||
int dimc = trial_fe.GetCurlDim();
|
||||
double w;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector D;
|
||||
DenseMatrix curlshape(tr_nd,dimc), curlshape_dFt(tr_nd,dimc), M;
|
||||
DenseMatrix te_curlshape(te_nd,dimc), te_curlshape_dFt(te_nd,dimc), M;
|
||||
#else
|
||||
curlshape.SetSize(tr_nd,dimc);
|
||||
curlshape_dFt.SetSize(tr_nd,dimc);
|
||||
te_curlshape.SetSize(te_nd,dimc);
|
||||
te_curlshape_dFt.SetSize(te_nd,dimc);
|
||||
#endif
|
||||
elmat.SetSize(te_nd, tr_nd);
|
||||
|
||||
if (MQ) { M.SetSize(dimc); }
|
||||
if (DQ) { D.SetSize(dimc); }
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order = test_fe.GetOrder() + trial_fe.GetOrder() - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
order = test_fe.GetOrder() + trial_fe.GetOrder() + trial_fe.GetDim() - 1;
|
||||
}
|
||||
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
|
||||
w = ip.weight * Trans.Weight();
|
||||
trial_fe.CalcPhysCurlShape(Trans, curlshape_dFt);
|
||||
test_fe.CalcPhysCurlShape(Trans, te_curlshape_dFt);
|
||||
|
||||
if (MQ)
|
||||
{
|
||||
MQ->Eval(M, Trans, ip);
|
||||
M *= w;
|
||||
Mult(te_curlshape_dFt, M, te_curlshape);
|
||||
AddMultABt(te_curlshape, curlshape_dFt, elmat);
|
||||
}
|
||||
else if (DQ)
|
||||
{
|
||||
DQ->Eval(D, Trans, ip);
|
||||
D *= w;
|
||||
AddMultADBt(te_curlshape_dFt,D,curlshape_dFt,elmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Q)
|
||||
{
|
||||
w *= Q->Eval(Trans, ip);
|
||||
}
|
||||
curlshape_dFt *= w;
|
||||
AddMultABt(te_curlshape_dFt, curlshape_dFt, elmat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurlCurlIntegrator
|
||||
::ComputeElementFlux(const FiniteElement &el, ElementTransformation &Trans,
|
||||
Vector &u, const FiniteElement &fluxelem, Vector &flux,
|
||||
@@ -2586,6 +2750,55 @@ void DivDivIntegrator::AssembleElementMatrix(
|
||||
}
|
||||
}
|
||||
|
||||
void DivDivIntegrator::AssembleElementMatrix2(
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int tr_nd = trial_fe.GetDof();
|
||||
int te_nd = test_fe.GetDof();
|
||||
double c;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector divshape(tr_nd);
|
||||
Vector te_divshape(te_nd);
|
||||
#else
|
||||
divshape.SetSize(tr_nd);
|
||||
te_divshape.SetSize(te_nd);
|
||||
#endif
|
||||
elmat.SetSize(te_nd,tr_nd);
|
||||
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order = 2 * max(test_fe.GetOrder(),
|
||||
trial_fe.GetOrder()) - 2; // <--- OK for RTk
|
||||
ir = &IntRules.Get(test_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
|
||||
for (int i = 0; i < ir -> GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
trial_fe.CalcDivShape(ip,divshape);
|
||||
test_fe.CalcDivShape(ip,te_divshape);
|
||||
|
||||
Trans.SetIntPoint (&ip);
|
||||
c = ip.weight / Trans.Weight();
|
||||
|
||||
if (Q)
|
||||
{
|
||||
c *= Q -> Eval (Trans, ip);
|
||||
}
|
||||
|
||||
te_divshape *= c;
|
||||
AddMultVWt(te_divshape, divshape, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void VectorDiffusionIntegrator::AssembleElementMatrix(
|
||||
const FiniteElement &el,
|
||||
@@ -3780,7 +3993,7 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
|
||||
for (i = 0; i < ndof1; i++)
|
||||
for (j = 0; j < face_ndof; j++)
|
||||
{
|
||||
elmat(i, j) -= shape1_n(i) * face_shape(j);
|
||||
elmat(i, j) += shape1_n(i) * face_shape(j);
|
||||
}
|
||||
if (ndof2)
|
||||
{
|
||||
@@ -3788,12 +4001,220 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
|
||||
for (i = 0; i < ndof2; i++)
|
||||
for (j = 0; j < face_ndof; j++)
|
||||
{
|
||||
elmat(ndof1+i, j) += shape2_n(i) * face_shape(j);
|
||||
elmat(ndof1+i, j) -= shape2_n(i) * face_shape(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceIntegrator::AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations & Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int i, j, face_ndof, ndof;
|
||||
int order;
|
||||
|
||||
face_ndof = trial_face_fe.GetDof();
|
||||
ndof = test_fe.GetDof();
|
||||
|
||||
face_shape.SetSize(face_ndof);
|
||||
shape.SetSize(ndof);
|
||||
|
||||
elmat.SetSize(ndof, face_ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
order = test_fe.GetOrder();
|
||||
order += trial_face_fe.GetOrder();
|
||||
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
order += Trans.OrderW();
|
||||
}
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
int iel = Trans.Elem1->ElementNo;
|
||||
if (iel != elem)
|
||||
{
|
||||
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
|
||||
}
|
||||
|
||||
double scale = 1.0;
|
||||
if (iel != elem) { scale = -1.; }
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
|
||||
// Set the integration point in the face and the neighboring elements
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
// Trace finite element shape function
|
||||
trial_face_fe.CalcPhysShape(Trans,face_shape);
|
||||
|
||||
// Finite element shape function
|
||||
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
|
||||
test_fe.CalcPhysShape(*eltrans, shape);
|
||||
|
||||
face_shape *= Trans.Weight()*ip.weight;
|
||||
for (i = 0; i < ndof; i++)
|
||||
{
|
||||
for (j = 0; j < face_ndof; j++)
|
||||
{
|
||||
elmat(i, j) += scale * shape(i) * face_shape(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NormalTraceIntegrator::AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int i, j, face_ndof, ndof, dim;
|
||||
int order;
|
||||
|
||||
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE, "");
|
||||
|
||||
face_ndof = trial_face_fe.GetDof();
|
||||
ndof = test_fe.GetDof();
|
||||
dim = test_fe.GetDim();
|
||||
|
||||
face_shape.SetSize(face_ndof);
|
||||
normal.SetSize(dim);
|
||||
shape.SetSize(ndof,dim);
|
||||
shape_n.SetSize(ndof);
|
||||
|
||||
elmat.SetSize(ndof, face_ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
order = test_fe.GetOrder();
|
||||
order += trial_face_fe.GetOrder();
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
int iel = Trans.Elem1->ElementNo;
|
||||
if (iel != elem)
|
||||
{
|
||||
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
|
||||
}
|
||||
|
||||
double scale = 1.0;
|
||||
if (iel != elem) { scale = -1.; }
|
||||
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
trial_face_fe.CalcPhysShape(Trans, face_shape);
|
||||
CalcOrtho(Trans.Jacobian(),normal);
|
||||
ElementTransformation * etrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
|
||||
test_fe.CalcVShape(*etrans, shape);
|
||||
shape.Mult(normal, shape_n);
|
||||
face_shape *= ip.weight;
|
||||
|
||||
for (i = 0; i < ndof; i++)
|
||||
{
|
||||
for (j = 0; j < face_ndof; j++)
|
||||
{
|
||||
elmat(i, j) += scale * shape_n(i) * face_shape(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TangentTraceIntegrator::AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations & Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
|
||||
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::H_CURL, "");
|
||||
|
||||
int face_ndof, ndof, dim;
|
||||
int order;
|
||||
dim = test_fe.GetDim();
|
||||
if (dim == 3)
|
||||
{
|
||||
std::string msg =
|
||||
"Trial space should be ND face trace and test space should be a ND vector field in 3D ";
|
||||
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::H_CURL &&
|
||||
trial_face_fe.GetDim() == 2 && test_fe.GetDim() == 3, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string msg =
|
||||
"Trial space should be H1 edge trace and test space should be a ND vector field in 2D";
|
||||
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE &&
|
||||
trial_face_fe.GetDim() == 1 && test_fe.GetDim() == 2, msg);
|
||||
}
|
||||
face_ndof = trial_face_fe.GetDof();
|
||||
ndof = test_fe.GetDof();
|
||||
|
||||
int dimc = (dim == 3) ? 3 : 1;
|
||||
|
||||
face_shape.SetSize(face_ndof,dimc);
|
||||
shape_n.SetSize(ndof,dimc);
|
||||
shape.SetSize(ndof,dim);
|
||||
normal.SetSize(dim);
|
||||
DenseMatrix face_shape_n(face_ndof,dimc);
|
||||
|
||||
elmat.SetSize(ndof, face_ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
order = test_fe.GetOrder();
|
||||
order += trial_face_fe.GetOrder();
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
int iel = Trans.Elem1->ElementNo;
|
||||
if (iel != elem)
|
||||
{
|
||||
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
|
||||
}
|
||||
|
||||
double scale = 1.0;
|
||||
if (iel != elem) { scale = -1.; }
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
// Set the integration point in the face and the neighboring elements
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
// Trace finite element shape function
|
||||
if (dim == 3)
|
||||
{
|
||||
trial_face_fe.CalcVShape(Trans,face_shape);
|
||||
}
|
||||
else
|
||||
{
|
||||
face_shape.GetColumnReference(0,temp);
|
||||
trial_face_fe.CalcPhysShape(Trans,temp);
|
||||
}
|
||||
CalcOrtho(Trans.Jacobian(),normal);
|
||||
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
|
||||
test_fe.CalcVShape(*eltrans, shape);
|
||||
|
||||
// rotate
|
||||
cross_product(normal, shape, shape_n);
|
||||
|
||||
const double w = scale*ip.weight;
|
||||
AddMult_a_ABt(w,shape_n, face_shape, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void NormalInterpolator::AssembleElementMatrix2(
|
||||
const FiniteElement &dom_fe, const FiniteElement &ran_fe,
|
||||
|
||||
+126
-2
@@ -151,6 +151,12 @@ public:
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Abstract method used for assembling TraceFaceIntegrators in a
|
||||
MixedBilinearForm. */
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &trial_face_fe,
|
||||
@@ -2069,6 +2075,31 @@ public:
|
||||
ElementTransformation &Trans);
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (Q curl u, v) where Q is a
|
||||
scalar coefficient, and v is a vector with components v_i in the L2 or H1 space.
|
||||
u can be in H(curl) (2D or 3D) or it can be a scalar H1.
|
||||
Note: If u is scalar H1 then curl u = [0 1; -1 0] grad u */
|
||||
class CurlIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
Vector shape;
|
||||
DenseMatrix dshape;
|
||||
DenseMatrix curlshape;
|
||||
DenseMatrix elmat_comp;
|
||||
public:
|
||||
CurlIntegrator() : Q{NULL} { }
|
||||
CurlIntegrator(Coefficient *q_) : Q{q_} { }
|
||||
CurlIntegrator(Coefficient &q) : Q{&q} { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (Q grad u, grad v) where Q
|
||||
can be a scalar or a matrix coefficient. */
|
||||
class DiffusionIntegrator: public BilinearFormIntegrator
|
||||
@@ -2524,6 +2555,7 @@ private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector D;
|
||||
DenseMatrix curlshape, curlshape_dFt, M;
|
||||
DenseMatrix te_curlshape, te_curlshape_dFt;
|
||||
DenseMatrix vshape, projcurl;
|
||||
#endif
|
||||
|
||||
@@ -2557,6 +2589,11 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void ComputeElementFlux(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
Vector &u, const FiniteElement &fluxelem,
|
||||
@@ -2725,7 +2762,7 @@ protected:
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape;
|
||||
Vector divshape, te_divshape;
|
||||
#endif
|
||||
|
||||
// PA extension
|
||||
@@ -2737,11 +2774,16 @@ private:
|
||||
|
||||
public:
|
||||
DivDivIntegrator() { Q = NULL; }
|
||||
DivDivIntegrator(Coefficient &q) : Q(&q) { }
|
||||
DivDivIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), Q(&q) { }
|
||||
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
const Coefficient *GetCoefficient() const { return Q; }
|
||||
};
|
||||
|
||||
@@ -3258,6 +3300,88 @@ public:
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
|
||||
/** Integrator for the DPG form: < v, w > over a face (the interface) where
|
||||
the trial variable v is defined on the interface
|
||||
(H^-1/2 i.e., v:=u⋅n normal trace of H(div))
|
||||
and the test variable w is in an H1-conforming space. */
|
||||
class TraceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
Vector face_shape, shape;
|
||||
public:
|
||||
TraceIntegrator() { }
|
||||
void AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Integrator for the form: < v, w.n > over a face (the interface) where
|
||||
the trial variable v is defined on the interface (H^1/2, i.e., trace of H1)
|
||||
and the test variable w is in an H(div)-conforming space. */
|
||||
class NormalTraceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
Vector face_shape, normal, shape_n;
|
||||
DenseMatrix shape;
|
||||
|
||||
public:
|
||||
NormalTraceIntegrator() { }
|
||||
virtual void AssembleTraceFaceMatrix(int ielem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
|
||||
/** Integrator for the form: < v, w × n > over a face (the interface)
|
||||
* In 3D the trial variable v is defined on the interface (H^-1/2(curl), trace of H(curl))
|
||||
* In 2D it's defined on the interface (H^1/2, trace of H1)
|
||||
* The test variable w is in an H(curl)-conforming space. */
|
||||
class TangentTraceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
DenseMatrix face_shape, shape, shape_n;
|
||||
Vector normal;
|
||||
Vector temp;
|
||||
|
||||
void cross_product(const Vector & x, const DenseMatrix & Y, DenseMatrix & Z)
|
||||
{
|
||||
int dim = x.Size();
|
||||
MFEM_VERIFY(Y.Width() == dim, "Size missmatch");
|
||||
int dimc = dim == 3 ? dim : 1;
|
||||
int h = Y.Height();
|
||||
Z.SetSize(h,dimc);
|
||||
if (dim == 3)
|
||||
{
|
||||
for (int i = 0; i<h; i++)
|
||||
{
|
||||
Z(i,0) = x(2) * Y(i,1) - x(1) * Y(i,2);
|
||||
Z(i,1) = x(0) * Y(i,2) - x(2) * Y(i,0);
|
||||
Z(i,2) = x(1) * Y(i,0) - x(0) * Y(i,1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i<h; i++)
|
||||
{
|
||||
Z(i,0) = x(1) * Y(i,0) - x(0) * Y(i,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
TangentTraceIntegrator() { }
|
||||
void AssembleTraceFaceMatrix(int elem,
|
||||
const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Abstract class to serve as a base for local interpolators to be used in the
|
||||
DiscreteLinearOperator class. */
|
||||
class DiscreteInterpolator : public BilinearFormIntegrator { };
|
||||
|
||||
@@ -30,7 +30,16 @@ void ConvectionIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::MFConvectionIntegrator(fes, *ir, Q, alpha);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedMFConvectionIntegrator(*this, fes, Q, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::MFConvectionIntegrator(fes, *ir, Q, alpha);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("Error: ConvectionIntegrator::AssembleMF only implemented with"
|
||||
|
||||
@@ -1386,7 +1386,16 @@ void ConvectionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::PAConvectionIntegrator(fes, *ir, Q, alpha);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedPAConvectionIntegrator(*this, fes, Q, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::PAConvectionIntegrator(fes, *ir, Q, alpha);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const int dims = el.GetDim();
|
||||
@@ -1497,6 +1506,7 @@ static void PAConvectionApply(const int dim,
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPAConvectionApply3D<2,2>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x23: return SmemPAConvectionApply3D<2,3>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x24: return SmemPAConvectionApply3D<2,4>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x26: return SmemPAConvectionApply3D<2,6>(NE,B,G,Bt,Gt,op,x,y);
|
||||
@@ -1548,6 +1558,7 @@ static void PAConvectionApplyT(const int dim,
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPAConvectionApplyT3D<2,2>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x23: return SmemPAConvectionApplyT3D<2,3>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x24: return SmemPAConvectionApplyT3D<2,4>(NE,B,G,Bt,Gt,op,x,y);
|
||||
case 0x26: return SmemPAConvectionApplyT3D<2,6>(NE,B,G,Bt,Gt,op,x,y);
|
||||
|
||||
@@ -136,6 +136,9 @@ static void PADGTraceSetup(const int dim,
|
||||
|
||||
void DGTraceIntegrator::SetupPA(const FiniteElementSpace &fes, FaceType type)
|
||||
{
|
||||
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
|
||||
Device::GetDeviceMemoryType() : pa_mt;
|
||||
|
||||
nf = fes.GetNFbyType(type);
|
||||
if (nf==0) { return; }
|
||||
// Assumes tensor-product elements
|
||||
@@ -153,7 +156,7 @@ void DGTraceIntegrator::SetupPA(const FiniteElementSpace &fes, FaceType type)
|
||||
geom = mesh->GetFaceGeometricFactors(
|
||||
*ir,
|
||||
FaceGeometricFactors::DETERMINANTS |
|
||||
FaceGeometricFactors::NORMALS, type);
|
||||
FaceGeometricFactors::NORMALS, type, mt);
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
@@ -695,6 +698,7 @@ static void PADGTraceApply(const int dim,
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPADGTraceApply3D<2,2,1>(NF,B,Bt,op,x,y);
|
||||
case 0x23: return SmemPADGTraceApply3D<2,3,1>(NF,B,Bt,op,x,y);
|
||||
case 0x34: return SmemPADGTraceApply3D<3,4,2>(NF,B,Bt,op,x,y);
|
||||
case 0x45: return SmemPADGTraceApply3D<4,5,2>(NF,B,Bt,op,x,y);
|
||||
@@ -1124,6 +1128,7 @@ static void PADGTraceApplyTranspose(const int dim,
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPADGTraceApplyTranspose3D<2,2>(NF,B,Bt,op,x,y);
|
||||
case 0x23: return SmemPADGTraceApplyTranspose3D<2,3>(NF,B,Bt,op,x,y);
|
||||
case 0x34: return SmemPADGTraceApplyTranspose3D<3,4>(NF,B,Bt,op,x,y);
|
||||
case 0x45: return SmemPADGTraceApplyTranspose3D<4,5>(NF,B,Bt,op,x,y);
|
||||
|
||||
@@ -33,7 +33,16 @@ void DiffusionIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
MFEM_VERIFY(!VQ && !MQ,
|
||||
"Only scalar coefficient supported for DiffusionIntegrator"
|
||||
" with libCEED");
|
||||
ceedOp = new ceed::MFDiffusionIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedMFDiffusionIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::MFDiffusionIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("Error: DiffusionIntegrator::AssembleMF only implemented with"
|
||||
|
||||
@@ -271,18 +271,21 @@ void PADiffusionSetup3D(const int Q1D,
|
||||
D(qx,qy,qz,1,e) = D12; // 1,2
|
||||
D(qx,qy,qz,2,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
|
||||
|
||||
const double D21 = w_detJ * (A21*R11 + A22*R21 + A23*R31);
|
||||
const double D22 = w_detJ * (A21*R12 + A22*R22 + A23*R32);
|
||||
const double D23 = w_detJ * (A21*R13 + A22*R23 + A23*R33);
|
||||
|
||||
const double D33 = w_detJ * (A31*R13 + A32*R23 + A33*R33);
|
||||
|
||||
D(qx,qy,qz,3,e) = symmetric ? D22 : D21; // 2,2 or 2,1
|
||||
D(qx,qy,qz,4,e) = symmetric ? D23 : D22; // 2,3 or 2,2
|
||||
D(qx,qy,qz,5,e) = symmetric ? D33 : D23; // 3,3 or 2,3
|
||||
|
||||
if (!symmetric)
|
||||
if (symmetric)
|
||||
{
|
||||
D(qx,qy,qz,3,e) = D22; // 2,2
|
||||
}
|
||||
else
|
||||
{
|
||||
D(qx,qy,qz,3,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
|
||||
D(qx,qy,qz,6,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
|
||||
D(qx,qy,qz,7,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
|
||||
D(qx,qy,qz,8,e) = D33; // 3,3
|
||||
@@ -365,7 +368,16 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
MFEM_VERIFY(!VQ && !MQ,
|
||||
"Only scalar coefficient supported for DiffusionIntegrator"
|
||||
" with libCEED");
|
||||
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const int dims = el.GetDim();
|
||||
|
||||
+626
-31
@@ -24,18 +24,20 @@ namespace mfem
|
||||
|
||||
// PA H(div) Mass Assemble 2D kernel
|
||||
void PAHdivSetup2D(const int Q1D,
|
||||
const int coeffDim,
|
||||
const int NE,
|
||||
const Array<double> &w,
|
||||
const Vector &j,
|
||||
Vector &coeff_,
|
||||
Vector &op)
|
||||
{
|
||||
const bool symmetric = (coeffDim != 4);
|
||||
const int NQ = Q1D*Q1D;
|
||||
auto W = w.Read();
|
||||
|
||||
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
|
||||
auto coeff = Reshape(coeff_.Read(), NQ, NE);
|
||||
auto y = Reshape(op.Write(), NQ, 3, NE);
|
||||
auto C = Reshape(coeff_.Read(), coeffDim, NQ, NE);
|
||||
auto y = Reshape(op.Write(), NQ, symmetric ? 3 : 4, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
@@ -45,28 +47,60 @@ void PAHdivSetup2D(const int Q1D,
|
||||
const double J21 = J(q,1,0,e);
|
||||
const double J12 = J(q,0,1,e);
|
||||
const double J22 = J(q,1,1,e);
|
||||
const double c_detJ = W[q] * coeff(q, e) / ((J11*J22)-(J21*J12));
|
||||
// (c/detJ) J^T J
|
||||
y(q,0,e) = c_detJ * (J11*J11 + J21*J21); // 1,1
|
||||
y(q,1,e) = c_detJ * (J11*J12 + J21*J22); // 1,2
|
||||
y(q,2,e) = c_detJ * (J12*J12 + J22*J22); // 2,2
|
||||
const double c_detJ = W[q] / ((J11*J22)-(J21*J12));
|
||||
|
||||
// (1/detJ) J^T C J
|
||||
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
|
||||
{
|
||||
const double C11 = C(0,q,e);
|
||||
const double C12 = C(1,q,e);
|
||||
const double C21 = symmetric ? C12 : C(2,q,e);
|
||||
const double C22 = symmetric ? C(2,q,e) : C(3,q,e);
|
||||
const double R11 = C11*J11 + C12*J21;
|
||||
const double R21 = C21*J11 + C22*J21;
|
||||
const double R12 = C11*J12 + C12*J22;
|
||||
const double R22 = C21*J12 + C22*J22;
|
||||
|
||||
y(q,0,e) = c_detJ * (J11*R11 + J21*R21); // 1,1
|
||||
y(q,1,e) = c_detJ * (J11*R12 + J21*R22); // 1,2
|
||||
|
||||
if (symmetric)
|
||||
{
|
||||
y(q,2,e) = c_detJ * (J12*R12 + J22*R22); // 2,2
|
||||
}
|
||||
else
|
||||
{
|
||||
y(q,2,e) = c_detJ * (J12*R11 + J22*R21); // 2,1
|
||||
y(q,3,e) = c_detJ * (J12*R12 + J22*R22); // 2,2
|
||||
}
|
||||
}
|
||||
else // Vector or scalar coefficient
|
||||
{
|
||||
const double C1 = C(0,q,e);
|
||||
const double C2 = (coeffDim == 2 ? C(1,q,e) : C1);
|
||||
y(q,0,e) = c_detJ * (J11*C1*J11 + J21*C2*J21); // 1,1
|
||||
y(q,1,e) = c_detJ * (J11*C1*J12 + J21*C2*J22); // 1,2
|
||||
y(q,2,e) = c_detJ * (J12*C1*J12 + J22*C2*J22); // 2,2
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PA H(div) Mass Assemble 3D kernel
|
||||
void PAHdivSetup3D(const int Q1D,
|
||||
const int coeffDim,
|
||||
const int NE,
|
||||
const Array<double> &w,
|
||||
const Vector &j,
|
||||
Vector &coeff_,
|
||||
Vector &op)
|
||||
{
|
||||
const bool symmetric = (coeffDim != 9);
|
||||
const int NQ = Q1D*Q1D*Q1D;
|
||||
auto W = w.Read();
|
||||
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
|
||||
auto coeff = Reshape(coeff_.Read(), NQ, NE);
|
||||
auto y = Reshape(op.Write(), NQ, 6, NE);
|
||||
auto C = Reshape(coeff_.Read(), coeffDim, NQ, NE);
|
||||
auto y = Reshape(op.Write(), NQ, symmetric ? 6 : 9, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
@@ -84,14 +118,58 @@ void PAHdivSetup3D(const int Q1D,
|
||||
const double detJ = J11 * (J22 * J33 - J32 * J23) -
|
||||
/* */ J21 * (J12 * J33 - J32 * J13) +
|
||||
/* */ J31 * (J12 * J23 - J22 * J13);
|
||||
const double c_detJ = W[q] * coeff(q, e) / detJ;
|
||||
// (c/detJ) J^T J
|
||||
y(q,0,e) = c_detJ * (J11*J11 + J21*J21 + J31*J31); // 1,1
|
||||
y(q,1,e) = c_detJ * (J12*J11 + J22*J21 + J32*J31); // 2,1
|
||||
y(q,2,e) = c_detJ * (J13*J11 + J23*J21 + J33*J31); // 3,1
|
||||
y(q,3,e) = c_detJ * (J12*J12 + J22*J22 + J32*J32); // 2,2
|
||||
y(q,4,e) = c_detJ * (J13*J12 + J23*J22 + J33*J32); // 3,2
|
||||
y(q,5,e) = c_detJ * (J13*J13 + J23*J23 + J33*J33); // 3,3
|
||||
const double c_detJ = W[q] / detJ;
|
||||
|
||||
// (1/detJ) J^T C J
|
||||
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
|
||||
{
|
||||
double M[3][3];
|
||||
M[0][0] = C(0, q, e);
|
||||
M[0][1] = C(1, q, e);
|
||||
M[0][2] = C(2, q, e);
|
||||
M[1][0] = (!symmetric) ? C(3, q, e) : M[0][1];
|
||||
M[1][1] = (!symmetric) ? C(4, q, e) : C(3, q, e);
|
||||
M[1][2] = (!symmetric) ? C(5, q, e) : C(4, q, e);
|
||||
M[2][0] = (!symmetric) ? C(6, q, e) : M[0][2];
|
||||
M[2][1] = (!symmetric) ? C(7, q, e) : M[1][2];
|
||||
M[2][2] = (!symmetric) ? C(8, q, e) : C(5, q, e);
|
||||
|
||||
int idx = 0;
|
||||
for (int i=0; i<3; ++i)
|
||||
for (int j = (symmetric ? i : 0); j<3; ++j)
|
||||
{
|
||||
y(q,idx,e) = 0.0;
|
||||
for (int k=0; k<3; ++k)
|
||||
{
|
||||
double MJ_kj = 0.0;
|
||||
for (int l=0; l<3; ++l)
|
||||
{
|
||||
MJ_kj += M[k][l] * J(q,l,j,e);
|
||||
}
|
||||
|
||||
y(q,idx,e) += J(q,k,i,e) * MJ_kj;
|
||||
}
|
||||
|
||||
y(q,idx,e) *= c_detJ;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
else // Vector or scalar coefficient version
|
||||
{
|
||||
int idx = 0;
|
||||
for (int i=0; i<3; ++i)
|
||||
for (int j=i; j<3; ++j)
|
||||
{
|
||||
y(q,idx,e) = 0.0;
|
||||
for (int k=0; k<3; ++k)
|
||||
{
|
||||
y(q,idx,e) += J(q,k,i,e) * C(coeffDim == 3 ? k : 0, q, e) * J(q,k,j,e);
|
||||
}
|
||||
|
||||
y(q,idx,e) *= c_detJ;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -99,6 +177,7 @@ void PAHdivSetup3D(const int Q1D,
|
||||
void PAHdivMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
@@ -115,7 +194,7 @@ void PAHdivMassApply2D(const int D1D,
|
||||
auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
auto Bot = Reshape(Bot_.Read(), D1D-1, Q1D);
|
||||
auto Bct = Reshape(Bct_.Read(), D1D, Q1D);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, 3, NE);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, symmetric ? 3 : 4, NE);
|
||||
auto x = Reshape(x_.Read(), 2*(D1D-1)*D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), 2*(D1D-1)*D1D, NE);
|
||||
|
||||
@@ -178,11 +257,12 @@ void PAHdivMassApply2D(const int D1D,
|
||||
{
|
||||
const double O11 = op(qx,qy,0,e);
|
||||
const double O12 = op(qx,qy,1,e);
|
||||
const double O22 = op(qx,qy,2,e);
|
||||
const double O21 = symmetric ? O12 : op(qx,qy,2,e);
|
||||
const double O22 = symmetric ? op(qx,qy,2,e) : op(qx,qy,3,e);
|
||||
const double massX = mass[qy][qx][0];
|
||||
const double massY = mass[qy][qx][1];
|
||||
mass[qy][qx][0] = (O11*massX)+(O12*massY);
|
||||
mass[qy][qx][1] = (O12*massX)+(O22*massY);
|
||||
mass[qy][qx][1] = (O21*massX)+(O22*massY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +305,179 @@ void PAHdivMassApply2D(const int D1D,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
void SmemPAHdivMassApply2D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
const Array<double> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(Bot_);
|
||||
MFEM_CONTRACT_VAR(Bct_);
|
||||
|
||||
static constexpr int VDIM = 2;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto bo = Reshape(Bo_.Read(), Q1D, D1D-1);
|
||||
const auto bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
const auto D = Reshape(op_.Read(), Q1D, Q1D, symmetric ? 3 : 4, NE);
|
||||
const auto x = Reshape(x_.Read(), D1D*(D1D-1), VDIM, NE);
|
||||
auto y = y_.ReadWrite();
|
||||
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, VDIM,
|
||||
{
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : HDIV_MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : HDIV_MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
|
||||
MFEM_SHARED double smo[MQ1*(MD1-1)];
|
||||
DeviceMatrix Bo(smo, D1D-1, Q1D);
|
||||
|
||||
MFEM_SHARED double smc[MQ1*MD1];
|
||||
DeviceMatrix Bc(smc, D1D, Q1D);
|
||||
|
||||
MFEM_SHARED double sm0[VDIM*MDQ*MDQ];
|
||||
MFEM_SHARED double sm1[VDIM*MDQ*MDQ];
|
||||
DeviceMatrix X(sm0, D1D*(D1D-1), VDIM);
|
||||
DeviceCube QD(sm1, Q1D, D1D, VDIM);
|
||||
DeviceCube QQ(sm0, Q1D, Q1D, VDIM);
|
||||
|
||||
// Load X, Bo and Bc into shared memory
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
if (qx < D1D && dy < (D1D-1)) { X(qx + dy*D1D,vd) = x(qx+dy*D1D,vd,e); }
|
||||
if (tidz == 0)
|
||||
{
|
||||
if (dy < (D1D-1)) { Bo(dy,qx) = bo(qx,dy); }
|
||||
Bc(dy,qx) = bc(qx,dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply B operator
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
DeviceCube Xxy(X, nx, ny, VDIM);
|
||||
DeviceMatrix Bx = (vd == 0) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double dq = 0.0;
|
||||
for (int dx = 0; dx < nx; ++dx)
|
||||
{
|
||||
dq += Xxy(dx,dy,vd) * Bx(dx,qx);
|
||||
}
|
||||
QD(qx,dy,vd) = dq;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
DeviceMatrix By = (vd == 1) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double qq = 0.0;
|
||||
for (int dy = 0; dy < ny; ++dy)
|
||||
{
|
||||
qq += QD(qx,dy,vd) * By(dy,qy);
|
||||
}
|
||||
QQ(qx,qy,vd) = qq;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply D operator
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
const double Qx = QQ(qx,qy,0);
|
||||
const double Qy = QQ(qx,qy,1);
|
||||
|
||||
const double D11 = D(qx,qy,0,e);
|
||||
const double D12 = D(qx,qy,1,e);
|
||||
const double D21 = symmetric ? D12 : D(qx,qy,2,e);
|
||||
const double D22 = symmetric ? D(qx,qy,2,e) : D(qx,qy,3,e);
|
||||
|
||||
QQ(qx,qy,0) = D11*Qx + D12*Qy;
|
||||
QQ(qx,qy,1) = D21*Qx + D22*Qy;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply Bt operator
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
double qd = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
qd += QQ(qx,qy,vd) * Btx(dx,qx);
|
||||
}
|
||||
QD(dx,qy,vd) = qd;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bc : Bo;
|
||||
DeviceTensor<4> Yxy(y, nx, ny, VDIM, NE);
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
double dd = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
dd += QD(dx,qy,vd) * Bty(dy,qy);
|
||||
}
|
||||
Yxy(dx,dy,vd,e) += dd;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Vector &op_,
|
||||
@@ -238,7 +488,7 @@ void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
|
||||
auto Bo = Reshape(Bo_.Read(), Q1D, D1D-1);
|
||||
auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, 3, NE);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, symmetric ? 3 : 4, NE);
|
||||
auto diag = Reshape(diag_.ReadWrite(), 2*(D1D-1)*D1D, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
@@ -259,7 +509,7 @@ void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const double wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
|
||||
mass[qx] += wy*wy*((c == 0) ? op(qx,qy,0,e) : op(qx,qy,2,e));
|
||||
mass[qx] += wy*wy*((c == 0) ? op(qx,qy,0,e) : op(qx,qy,symmetric ? 2 : 3,e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +533,7 @@ void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Vector &op_,
|
||||
@@ -294,7 +545,7 @@ void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
|
||||
auto Bo = Reshape(Bo_.Read(), Q1D, D1D-1);
|
||||
auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 6, NE);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto diag = Reshape(diag_.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
@@ -307,7 +558,8 @@ void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
const int D1Dy = (c == 1) ? D1D : D1D - 1;
|
||||
const int D1Dx = (c == 0) ? D1D : D1D - 1;
|
||||
|
||||
const int opc = (c == 0) ? 0 : ((c == 1) ? 3 : 5);
|
||||
const int opc = (c == 0) ? 0 : ((c == 1) ? (symmetric ? 3 : 4) :
|
||||
(symmetric ? 5 : 8));
|
||||
|
||||
double mass[HDIV_MAX_Q1D];
|
||||
|
||||
@@ -350,6 +602,7 @@ void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
void PAHdivMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
@@ -366,7 +619,7 @@ void PAHdivMassApply3D(const int D1D,
|
||||
auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
auto Bot = Reshape(Bot_.Read(), D1D-1, Q1D);
|
||||
auto Bct = Reshape(Bct_.Read(), D1D, Q1D);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 6, NE);
|
||||
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), 3*(D1D-1)*(D1D-1)*D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
|
||||
|
||||
@@ -461,15 +714,19 @@ void PAHdivMassApply3D(const int D1D,
|
||||
const double O11 = op(qx,qy,qz,0,e);
|
||||
const double O12 = op(qx,qy,qz,1,e);
|
||||
const double O13 = op(qx,qy,qz,2,e);
|
||||
const double O22 = op(qx,qy,qz,3,e);
|
||||
const double O23 = op(qx,qy,qz,4,e);
|
||||
const double O33 = op(qx,qy,qz,5,e);
|
||||
const double O21 = symmetric ? O12 : op(qx,qy,qz,3,e);
|
||||
const double O22 = symmetric ? op(qx,qy,qz,3,e) : op(qx,qy,qz,4,e);
|
||||
const double O23 = symmetric ? op(qx,qy,qz,4,e) : op(qx,qy,qz,5,e);
|
||||
const double O31 = symmetric ? O13 : op(qx,qy,qz,6,e);
|
||||
const double O32 = symmetric ? O23 : op(qx,qy,qz,7,e);
|
||||
const double O33 = symmetric ? op(qx,qy,qz,5,e) : op(qx,qy,qz,8,e);
|
||||
|
||||
const double massX = mass[qz][qy][qx][0];
|
||||
const double massY = mass[qz][qy][qx][1];
|
||||
const double massZ = mass[qz][qy][qx][2];
|
||||
mass[qz][qy][qx][0] = (O11*massX)+(O12*massY)+(O13*massZ);
|
||||
mass[qz][qy][qx][1] = (O12*massX)+(O22*massY)+(O23*massZ);
|
||||
mass[qz][qy][qx][2] = (O13*massX)+(O23*massY)+(O33*massZ);
|
||||
mass[qz][qy][qx][1] = (O21*massX)+(O22*massY)+(O23*massZ);
|
||||
mass[qz][qy][qx][2] = (O31*massX)+(O32*massY)+(O33*massZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,6 +794,337 @@ void PAHdivMassApply3D(const int D1D,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
void SmemPAHdivMassApply3D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
const Array<double> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(Bot_);
|
||||
MFEM_CONTRACT_VAR(Bct_);
|
||||
|
||||
static constexpr int VDIM = 3;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto bo = Reshape(Bo_.Read(), Q1D, D1D-1);
|
||||
const auto bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
const auto D = Reshape(op_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
const auto x = Reshape(x_.Read(), D1D*(D1D-1)*(D1D-1), VDIM, NE);
|
||||
auto y = y_.ReadWrite();
|
||||
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, VDIM,
|
||||
{
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : HDIV_MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : HDIV_MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
|
||||
MFEM_SHARED double smo[MQ1*(MD1-1)];
|
||||
DeviceMatrix Bo(smo, D1D-1, Q1D);
|
||||
|
||||
MFEM_SHARED double smc[MQ1*MD1];
|
||||
DeviceMatrix Bc(smc, D1D, Q1D);
|
||||
|
||||
MFEM_SHARED double sm0[VDIM*MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED double sm1[VDIM*MDQ*MDQ*MDQ];
|
||||
DeviceMatrix X(sm0, D1D*(D1D-1)*(D1D-1), VDIM);
|
||||
DeviceTensor<4> QDD(sm1, Q1D, D1D, D1D, VDIM);
|
||||
DeviceTensor<4> QQD(sm0, Q1D, Q1D, D1D, VDIM);
|
||||
DeviceTensor<4> QQQ(sm1, Q1D, Q1D, Q1D, VDIM);
|
||||
DeviceTensor<4> DQQ(sm0, D1D, Q1D, Q1D, VDIM);
|
||||
DeviceTensor<4> DDQ(sm1, D1D, D1D, Q1D, VDIM);
|
||||
|
||||
// Load X into shared memory
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz,y,D1D-1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,x,D1D-1)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
X(dx+(dy+dz*(D1D-1))*D1D,vd) = x(dx+(dy+dz*(D1D-1))*D1D,vd,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Load Bo and Bc into shared memory
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D-1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
Bo(d,q) = bo(q,d);
|
||||
}
|
||||
}
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
Bc(d,q) = bc(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply B operator
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
const int nz = (vd == 2) ? D1D : D1D-1;
|
||||
DeviceTensor<4> Xxyz(X, nx, ny, nz, VDIM);
|
||||
DeviceMatrix Bx = (vd == 0) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dx = 0; dx < nx; ++dx)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += Xxyz(dx,dy,dz,vd) * Bx(dx,qx);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { QDD(qx,dy,dz,vd) = u[dz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
const int nz = (vd == 2) ? D1D : D1D-1;
|
||||
DeviceMatrix By = (vd == 1) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dy = 0; dy < ny; ++dy)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += QDD(qx,dy,dz,vd) * By(dy,qy);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { QQD(qx,qy,dz,vd) = u[dz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nz = (vd == 2) ? D1D : D1D-1;
|
||||
DeviceMatrix Bz = (vd == 2) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u[qz] += QQD(qx,qy,dz,vd) * Bz(dz,qz);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { QQQ(qx,qy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply D operator
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
const double Qx = QQQ(qx,qy,qz,0);
|
||||
const double Qy = QQQ(qx,qy,qz,1);
|
||||
const double Qz = QQQ(qx,qy,qz,2);
|
||||
|
||||
const double D11 = D(qx,qy,qz,0,e);
|
||||
const double D12 = D(qx,qy,qz,1,e);
|
||||
const double D13 = D(qx,qy,qz,2,e);
|
||||
const double D21 = symmetric ? D12 : D(qx,qy,qz,3,e);
|
||||
const double D22 = symmetric ? D(qx,qy,qz,3,e) : D(qx,qy,qz,4,e);
|
||||
const double D23 = symmetric ? D(qx,qy,qz,4,e) : D(qx,qy,qz,5,e);
|
||||
const double D31 = symmetric ? D13 : D(qx,qy,qz,6,e);
|
||||
const double D32 = symmetric ? D23 : D(qx,qy,qz,7,e);
|
||||
const double D33 = symmetric ? D(qx,qy,qz,5,e) : D(qx,qy,qz,8,e);
|
||||
|
||||
QQQ(qx,qy,qz,0) = D11*Qx + D12*Qy + D13*Qz;
|
||||
QQQ(qx,qy,qz,1) = D21*Qx + D22*Qy + D23*Qz;
|
||||
QQQ(qx,qy,qz,2) = D31*Qx + D32*Qy + D33*Qz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply Bt operator
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
double u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u[qz] += QQQ(qx,qy,qz,vd) * Btx(dx,qx);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { DQQ(dx,qy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
double u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u[qz] += DQQ(dx,qy,qz,vd) * Bty(dy,qy);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { DDQ(dx,dy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,VDIM)
|
||||
{
|
||||
const int nx = (vd == 0) ? D1D : D1D-1;
|
||||
const int ny = (vd == 1) ? D1D : D1D-1;
|
||||
const int nz = (vd == 2) ? D1D : D1D-1;
|
||||
DeviceTensor<5> Yxyz(y, nx, ny, nz, VDIM, NE);
|
||||
DeviceMatrix Btz = (vd == 2) ? Bc : Bo;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
double u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += DDQ(dx,dy,qz,vd) * Btz(dz,qz);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < nz; ++dz) { Yxyz(dx,dy,dz,vd,e) += u[dz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
void PAHdivMassApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo,
|
||||
const Array<double> &Bc,
|
||||
const Array<double> &Bot,
|
||||
const Array<double> &Bct,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
const int id = (D1D << 4) | Q1D;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x22: return SmemPAHdivMassApply2D<2,2>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x33: return SmemPAHdivMassApply2D<3,3>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x44: return SmemPAHdivMassApply2D<4,4>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x55: return SmemPAHdivMassApply2D<5,5>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
default: // fallback
|
||||
return PAHdivMassApply2D(D1D,Q1D,NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x23: return SmemPAHdivMassApply3D<2,3>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x34: return SmemPAHdivMassApply3D<3,4>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x45: return SmemPAHdivMassApply3D<4,5>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x56: return SmemPAHdivMassApply3D<5,6>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x67: return SmemPAHdivMassApply3D<6,7>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x78: return SmemPAHdivMassApply3D<7,8>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
default: // fallback
|
||||
return PAHdivMassApply3D(D1D,Q1D,NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PA H(div) div-div assemble 2D kernel
|
||||
// NOTE: this is identical to PACurlCurlSetup3D
|
||||
static void PADivDivSetup2D(const int Q1D,
|
||||
@@ -626,7 +1214,7 @@ static void PADivDivApply2D(const int D1D,
|
||||
{
|
||||
double div[MAX_Q1D][MAX_Q1D];
|
||||
|
||||
// div[qy][qx] will be computed as du_x/dx + duy_/dy
|
||||
// div[qy][qx] will be computed as du_x/dx + du_y/dy
|
||||
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
@@ -1209,6 +1797,13 @@ VectorFEDivergenceIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
}
|
||||
}
|
||||
|
||||
if (test_el->GetMapType() == FiniteElement::INTEGRAL)
|
||||
{
|
||||
const GeometricFactors *geom =
|
||||
mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS);
|
||||
coeff /= geom->detJ;
|
||||
}
|
||||
|
||||
if (trial_el->GetDerivType() == mfem::FiniteElement::DIV && dim == 3)
|
||||
{
|
||||
PADivL2Setup3D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
|
||||
|
||||
@@ -31,7 +31,16 @@ void MassIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::MFMassIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedMFMassIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::MFMassIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("Error: MassIntegrator::AssembleMF only implemented with"
|
||||
|
||||
@@ -38,7 +38,16 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedPAMassIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int map_type = el.GetMapType();
|
||||
|
||||
@@ -149,7 +149,16 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const int dims = el.GetDim();
|
||||
|
||||
@@ -30,7 +30,19 @@ void VectorDiffusionIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::MFDiffusionIntegrator(fes, *ir, Q);
|
||||
MFEM_VERIFY(!VQ && !MQ,
|
||||
"Only scalar coefficient supported for DiffusionIntegrator"
|
||||
" with libCEED");
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedMFDiffusionIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::MFDiffusionIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("Error: VectorDiffusionIntegrator::AssembleMF only implemented"
|
||||
|
||||
@@ -34,7 +34,16 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedPAMassIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dim = mesh->Dimension();
|
||||
|
||||
@@ -34,7 +34,16 @@ void VectorMassIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
ceedOp = new ceed::MFMassIntegrator(fes, *ir, Q);
|
||||
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
|
||||
fes.IsVariableOrder();
|
||||
if (mixed)
|
||||
{
|
||||
ceedOp = new ceed::MixedMFMassIntegrator(*this, fes, Q);
|
||||
}
|
||||
else
|
||||
{
|
||||
ceedOp = new ceed::MFMassIntegrator(fes, *ir, Q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("Error: VectorMassIntegrator::AssembleMF only implemented with"
|
||||
|
||||
+98
-86
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../general/forall.hpp"
|
||||
#include "bilininteg.hpp"
|
||||
#include "gridfunc.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -89,6 +90,7 @@ void SmemPAHcurlMassApply3D(const int D1D,
|
||||
Vector &y);
|
||||
|
||||
void PAHdivSetup2D(const int Q1D,
|
||||
const int coeffDim,
|
||||
const int NE,
|
||||
const Array<double> &w,
|
||||
const Vector &j,
|
||||
@@ -96,6 +98,7 @@ void PAHdivSetup2D(const int Q1D,
|
||||
Vector &op);
|
||||
|
||||
void PAHdivSetup3D(const int Q1D,
|
||||
const int coeffDim,
|
||||
const int NE,
|
||||
const Array<double> &w,
|
||||
const Vector &j,
|
||||
@@ -149,6 +152,7 @@ void PAHcurlH1ApplyTranspose3D(const int D1D,
|
||||
void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Vector &op_,
|
||||
@@ -157,32 +161,24 @@ void PAHdivMassAssembleDiagonal2D(const int D1D,
|
||||
void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Vector &op_,
|
||||
Vector &diag_);
|
||||
|
||||
void PAHdivMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
const Array<double> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
|
||||
void PAHdivMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const Array<double> &Bo_,
|
||||
const Array<double> &Bc_,
|
||||
const Array<double> &Bot_,
|
||||
const Array<double> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
void PAHdivMassApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<double> &Bo,
|
||||
const Array<double> &Bc,
|
||||
const Array<double> &Bot,
|
||||
const Array<double> &Bct,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y);
|
||||
|
||||
void PAHcurlL2Setup(const int NQ,
|
||||
const int coeffDim,
|
||||
@@ -818,68 +814,79 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
pa_data.SetSize((symmetric ? symmDims : MQfullDim) * nq * ne,
|
||||
Device::GetMemoryType());
|
||||
|
||||
Vector coeff(coeffDim * ne * nq);
|
||||
coeff = 1.0;
|
||||
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
|
||||
if (Q || DQ || MQ)
|
||||
Vector coeff;
|
||||
|
||||
auto *qf_c = dynamic_cast<QuadratureFunctionCoefficient*>(Q);
|
||||
if (qf_c)
|
||||
{
|
||||
Vector DM(DQ ? coeffDim : 0);
|
||||
DenseMatrix M;
|
||||
DenseSymmetricMatrix SM;
|
||||
const QuadratureFunction &qf = qf_c->GetQuadFunction();
|
||||
qf.Read();
|
||||
coeff.MakeRef(const_cast<QuadratureFunction&>(qf), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff.SetSize(coeffDim * ne * nq);
|
||||
coeff = 1.0;
|
||||
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
|
||||
if (Q || DQ || MQ)
|
||||
{
|
||||
Vector DM(DQ ? coeffDim : 0);
|
||||
DenseMatrix M;
|
||||
DenseSymmetricMatrix SM;
|
||||
|
||||
if (DQ)
|
||||
{
|
||||
MFEM_VERIFY(coeffDim == dim, "");
|
||||
}
|
||||
if (SMQ)
|
||||
{
|
||||
MFEM_VERIFY(SMQ->GetSize() == dim, "");
|
||||
SM.SetSize(dim);
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
MFEM_VERIFY(coeffDim == MQdim, "");
|
||||
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
|
||||
M.SetSize(dim);
|
||||
}
|
||||
|
||||
|
||||
for (int e=0; e<ne; ++e)
|
||||
{
|
||||
ElementTransformation *tr = mesh->GetElementTransformation(e);
|
||||
for (int p=0; p<nq; ++p)
|
||||
if (DQ)
|
||||
{
|
||||
if (SMQ)
|
||||
{
|
||||
SMQ->Eval(SM, *tr, ir->IntPoint(p));
|
||||
int cnt = 0;
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=i; j<dim; ++j, ++cnt)
|
||||
{
|
||||
coeffh(cnt, p, e) = SM(i,j);
|
||||
}
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
MFEM_VERIFY(coeffDim == dim, "");
|
||||
}
|
||||
if (SMQ)
|
||||
{
|
||||
MFEM_VERIFY(SMQ->GetSize() == dim, "");
|
||||
SM.SetSize(dim);
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
MFEM_VERIFY(coeffDim == MQdim, "");
|
||||
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
|
||||
M.SetSize(dim);
|
||||
}
|
||||
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
coeffh(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
else if (DQ)
|
||||
for (int e=0; e<ne; ++e)
|
||||
{
|
||||
ElementTransformation *tr = mesh->GetElementTransformation(e);
|
||||
for (int p=0; p<nq; ++p)
|
||||
{
|
||||
DQ->Eval(DM, *tr, ir->IntPoint(p));
|
||||
for (int i=0; i<coeffDim; ++i)
|
||||
if (SMQ)
|
||||
{
|
||||
coeffh(i, p, e) = DM[i];
|
||||
SMQ->Eval(SM, *tr, ir->IntPoint(p));
|
||||
int cnt = 0;
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=i; j<dim; ++j, ++cnt)
|
||||
{
|
||||
coeffh(cnt, p, e) = SM(i,j);
|
||||
}
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
coeffh(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
else if (DQ)
|
||||
{
|
||||
DQ->Eval(DM, *tr, ir->IntPoint(p));
|
||||
for (int i=0; i<coeffDim; ++i)
|
||||
{
|
||||
coeffh(i, p, e) = DM[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coeffh(0, p, e) = Q->Eval(*tr, ir->IntPoint(p));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coeffh(0, p, e) = Q->Eval(*tr, ir->IntPoint(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -897,12 +904,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
}
|
||||
else if (trial_div && test_div && dim == 3)
|
||||
{
|
||||
PAHdivSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
|
||||
PAHdivSetup3D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
|
||||
coeff, pa_data);
|
||||
}
|
||||
else if (trial_div && test_div && dim == 2)
|
||||
{
|
||||
PAHdivSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
|
||||
PAHdivSetup2D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
|
||||
coeff, pa_data);
|
||||
}
|
||||
else if (((trial_curl && test_div) || (trial_div && test_curl)) &&
|
||||
@@ -963,7 +970,7 @@ void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
|
||||
else if (trial_fetype == mfem::FiniteElement::DIV &&
|
||||
test_fetype == trial_fetype)
|
||||
{
|
||||
PAHdivMassAssembleDiagonal3D(dofs1D, quad1D, ne,
|
||||
PAHdivMassAssembleDiagonal3D(dofs1D, quad1D, ne, symmetric,
|
||||
mapsO->B, mapsC->B, pa_data, diag);
|
||||
}
|
||||
else
|
||||
@@ -971,7 +978,7 @@ void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
}
|
||||
else
|
||||
else // 2D
|
||||
{
|
||||
if (trial_fetype == mfem::FiniteElement::CURL && test_fetype == trial_fetype)
|
||||
{
|
||||
@@ -981,7 +988,7 @@ void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
|
||||
else if (trial_fetype == mfem::FiniteElement::DIV &&
|
||||
test_fetype == trial_fetype)
|
||||
{
|
||||
PAHdivMassAssembleDiagonal2D(dofs1D, quad1D, ne,
|
||||
PAHdivMassAssembleDiagonal2D(dofs1D, quad1D, ne, symmetric,
|
||||
mapsO->B, mapsC->B, pa_data, diag);
|
||||
}
|
||||
else
|
||||
@@ -1034,8 +1041,8 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else if (trial_div && test_div)
|
||||
{
|
||||
PAHdivMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
|
||||
mapsC->Bt, pa_data, x, y);
|
||||
PAHdivMassApply(3, dofs1D, quad1D, ne, symmetric, mapsO->B, mapsC->B, mapsO->Bt,
|
||||
mapsC->Bt, pa_data, x, y);
|
||||
}
|
||||
else if (trial_curl && test_div)
|
||||
{
|
||||
@@ -1056,7 +1063,7 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
}
|
||||
else
|
||||
else // 2D
|
||||
{
|
||||
if (trial_curl && test_curl)
|
||||
{
|
||||
@@ -1065,8 +1072,8 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else if (trial_div && test_div)
|
||||
{
|
||||
PAHdivMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
|
||||
mapsC->Bt, pa_data, x, y);
|
||||
PAHdivMassApply(2, dofs1D, quad1D, ne, symmetric, mapsO->B, mapsC->B, mapsO->Bt,
|
||||
mapsC->Bt, pa_data, x, y);
|
||||
}
|
||||
else if ((trial_curl && test_div) || (trial_div && test_curl))
|
||||
{
|
||||
@@ -1111,6 +1118,11 @@ void VectorFEMassIntegrator::AddMultTransposePA(const Vector &x,
|
||||
|
||||
if (symmetricSpaces)
|
||||
{
|
||||
if (MQ && dynamic_cast<SymmetricMatrixCoefficient*>(MQ) == NULL)
|
||||
{
|
||||
MFEM_ABORT("VectorFEMassIntegrator transpose not implemented for asymmetric MatrixCoefficient");
|
||||
}
|
||||
|
||||
this->AddMultPA(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,512 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "fem.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
BlockBilinearForm::BlockBilinearForm(Array<FiniteElementSpace *> & fespaces_) :
|
||||
Matrix(0), fespaces(fespaces_)
|
||||
{
|
||||
height = 0;
|
||||
nblocks = fespaces.Size();
|
||||
dof_offsets.SetSize(nblocks+1);
|
||||
tdof_offsets.SetSize(nblocks+1);
|
||||
dof_offsets[0] = 0;
|
||||
tdof_offsets[0] = 0;
|
||||
for (int i =0; i<nblocks; i++)
|
||||
{
|
||||
dof_offsets[i+1] = fespaces[i]->GetVSize();
|
||||
tdof_offsets[i+1] = fespaces[i]->GetTrueVSize();
|
||||
}
|
||||
dof_offsets.PartialSum();
|
||||
tdof_offsets.PartialSum();
|
||||
height = dof_offsets[nblocks];
|
||||
width = height;
|
||||
mat = mat_e = NULL;
|
||||
extern_bfs = 0;
|
||||
element_matrices = NULL;
|
||||
diag_policy = DIAG_KEEP;
|
||||
}
|
||||
|
||||
|
||||
// Allocate appropriate SparseMatrix and assign it to mat
|
||||
void BlockBilinearForm::AllocMat()
|
||||
{
|
||||
mat = new SparseMatrix(height);
|
||||
}
|
||||
|
||||
void BlockBilinearForm::BuildProlongation()
|
||||
{
|
||||
P = new BlockMatrix(dof_offsets, tdof_offsets);
|
||||
R = new BlockMatrix(tdof_offsets, dof_offsets);
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
const SparseMatrix *P_ = fespaces[i]->GetConformingProlongation();
|
||||
const SparseMatrix *R_ = fespaces[i]->GetRestrictionMatrix();
|
||||
P->SetBlock(i,i,const_cast<SparseMatrix*>(P_));
|
||||
R->SetBlock(i,i,const_cast<SparseMatrix*>(R_));
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::ConformingAssemble()
|
||||
{
|
||||
Finalize(0);
|
||||
MFEM_ASSERT(mat, "the BilinearForm is not assembled");
|
||||
|
||||
if (!P) { BuildProlongation(); }
|
||||
|
||||
SparseMatrix * Pm = P->CreateMonolithic();
|
||||
|
||||
SparseMatrix *Pt = Transpose(*Pm);
|
||||
|
||||
SparseMatrix *PtA = mfem::Mult(*Pt, *mat);
|
||||
delete mat;
|
||||
if (mat_e)
|
||||
{
|
||||
SparseMatrix *PtAe = mfem::Mult(*Pt, *mat_e);
|
||||
delete mat_e;
|
||||
mat_e = PtAe;
|
||||
}
|
||||
delete Pt;
|
||||
mat = mfem::Mult(*PtA, *Pm);
|
||||
delete PtA;
|
||||
if (mat_e)
|
||||
{
|
||||
SparseMatrix *PtAeP = mfem::Mult(*mat_e, *Pm);
|
||||
delete mat_e;
|
||||
mat_e = PtAeP;
|
||||
}
|
||||
delete Pm;
|
||||
height = mat->Height();
|
||||
width = mat->Width();
|
||||
}
|
||||
|
||||
void BlockBilinearForm::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
double& BlockBilinearForm::Elem (int i, int j)
|
||||
{
|
||||
return mat -> Elem(i,j);
|
||||
}
|
||||
|
||||
const double& BlockBilinearForm::Elem (int i, int j) const
|
||||
{
|
||||
return mat -> Elem(i,j);
|
||||
}
|
||||
|
||||
MatrixInverse * BlockBilinearForm::Inverse() const
|
||||
{
|
||||
return mat -> Inverse();
|
||||
}
|
||||
|
||||
void BlockBilinearForm::Finalize(int skip_zeros)
|
||||
{
|
||||
mat->Finalize(skip_zeros);
|
||||
if (mat_e) { mat_e->Finalize(skip_zeros); }
|
||||
}
|
||||
|
||||
/// Adds new Block Domain Integrator. Assumes ownership of @a bfi.
|
||||
void BlockBilinearForm::AddDomainIntegrator(BlockBilinearFormIntegrator *bfi)
|
||||
{
|
||||
domain_integs.Append(bfi);
|
||||
}
|
||||
|
||||
/// Assembles the form i.e. sums over all domain integrators.
|
||||
void BlockBilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * doftrans_j, *doftrans_k;
|
||||
Mesh *mesh = fespaces[0] -> GetMesh();
|
||||
DenseMatrix elmat, *elmat_p;
|
||||
int nblocks = fespaces.Size();
|
||||
Array<const FiniteElement *> fe(nblocks);
|
||||
Array<int> vdofs_j, vdofs_k;
|
||||
Array<int> offsetvdofs_j;
|
||||
Array<int> elementblockoffsets(nblocks+1);
|
||||
elementblockoffsets[0] = 0;
|
||||
Array<int> blockoffsets(nblocks+1);
|
||||
blockoffsets[0] = 0;
|
||||
for (int i =0; i<nblocks; i++)
|
||||
{
|
||||
blockoffsets[i+1] = fespaces[i]->GetVSize();
|
||||
}
|
||||
blockoffsets.PartialSum();
|
||||
// mfem::out << "blockoffsets = " ; blockoffsets.Print();
|
||||
|
||||
if (mat == NULL)
|
||||
{
|
||||
AllocMat();
|
||||
}
|
||||
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
// loop through elements
|
||||
for (int i = 0; i < mesh -> GetNE(); i++)
|
||||
{
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
elmat.SetSize(0);
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
fe[j] = fespaces[j]->GetFE(i);
|
||||
elementblockoffsets[j+1] = fe[j]->GetDof();
|
||||
}
|
||||
elementblockoffsets.PartialSum();
|
||||
eltrans = mesh->GetElementTransformation(i);
|
||||
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
if (elmat.Size() == 0)
|
||||
{
|
||||
elmat = elemmat;
|
||||
}
|
||||
else
|
||||
{
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elmat.Size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
vdofs.SetSize(0);
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
doftrans_j = fespaces[j]->GetElementVDofs(i, vdofs_j);
|
||||
int jbeg = elementblockoffsets[j];
|
||||
int jend = elementblockoffsets[j+1]-1;
|
||||
int offset_j = blockoffsets[j];
|
||||
offsetvdofs_j.SetSize(vdofs_j.Size());
|
||||
|
||||
for (int l = 0; l<vdofs_j.Size(); l++)
|
||||
{
|
||||
offsetvdofs_j[l] = vdofs_j[l]<0 ? -offset_j + vdofs_j[l]
|
||||
: offset_j + vdofs_j[l];
|
||||
}
|
||||
vdofs.Append(offsetvdofs_j);
|
||||
for (int k = 0; k<nblocks; k++)
|
||||
{
|
||||
doftrans_k = fespaces[k]->GetElementVDofs(i, vdofs_k);
|
||||
if (doftrans_k || doftrans_j)
|
||||
{
|
||||
int kbeg = elementblockoffsets[k];
|
||||
int kend = elementblockoffsets[k+1]-1;
|
||||
DenseMatrix A;
|
||||
elmat_p->GetSubMatrix(jbeg,jend,kbeg, kend, A);
|
||||
TransformDual(doftrans_j, doftrans_k, A);
|
||||
elmat_p->SetSubMatrix(jbeg,kbeg,A);
|
||||
}
|
||||
}
|
||||
}
|
||||
mat->AddSubMatrix(vdofs,vdofs,*elmat_p, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BlockBilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
if (!P)
|
||||
{
|
||||
EliminateVDofsInRHS(ess_tdof_list, x, b);
|
||||
X.MakeRef(x, 0, x.Size());
|
||||
B.MakeRef(b, 0, b.Size());
|
||||
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
|
||||
}
|
||||
else // non conforming space
|
||||
{
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(b, B);
|
||||
X.SetSize(R->Height());
|
||||
|
||||
mfem::out << "R height, width = " << R->Height() <<" x "<< R->Width() <<
|
||||
std::endl;
|
||||
|
||||
R->Mult(x, X);
|
||||
EliminateVDofsInRHS(ess_tdof_list, X, B);
|
||||
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BlockBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
OperatorHandle &A)
|
||||
{
|
||||
if (!mat_e)
|
||||
{
|
||||
const SparseMatrix *P_ = fespaces[0]->GetConformingProlongation();
|
||||
if (P_) { ConformingAssemble(); }
|
||||
EliminateVDofs(ess_tdof_list, diag_policy);
|
||||
const int remove_zeros = 0;
|
||||
Finalize(remove_zeros);
|
||||
}
|
||||
A.Reset(mat, false);
|
||||
}
|
||||
|
||||
void BlockBilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
|
||||
Vector &x)
|
||||
{
|
||||
if (!P)
|
||||
{
|
||||
x.SyncMemory(X);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply conforming prolongation
|
||||
x.SetSize(P->Height());
|
||||
P->Mult(X, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BlockBilinearForm::ComputeElementMatrices()
|
||||
{
|
||||
MFEM_ABORT("BlockBilinearForm::ComputeElementMatrices:not implemented yet")
|
||||
}
|
||||
|
||||
void BlockBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
{
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat.SetSize(element_matrices->SizeI(), element_matrices->SizeJ());
|
||||
elmat = element_matrices->GetData(i);
|
||||
return;
|
||||
}
|
||||
|
||||
int nblocks = fespaces.Size();
|
||||
Array<const FiniteElement *> fe(nblocks);
|
||||
ElementTransformation *eltrans;
|
||||
|
||||
elmat.SetSize(0);
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
fe[j] = fespaces[j]->GetFE(i);
|
||||
}
|
||||
eltrans = fespaces[0]->GetElementTransformation(i);
|
||||
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
|
||||
for (int k = 1; k < domain_integs.Size(); k++)
|
||||
{
|
||||
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int matsize = 0;
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
matsize += fespaces[j]->GetFE(i)->GetDof();
|
||||
}
|
||||
elmat.SetSize(matsize);
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBC: not implemented yet");
|
||||
// Array<int> ess_dofs, conf_ess_dofs;
|
||||
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
|
||||
|
||||
// if (fes->GetVSize() == height)
|
||||
// {
|
||||
// EliminateEssentialBCFromDofs(ess_dofs, sol, rhs, dpolicy);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
|
||||
// EliminateEssentialBCFromDofs(conf_ess_dofs, sol, rhs, dpolicy);
|
||||
// }
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBC: not implemented yet");
|
||||
// Array<int> ess_dofs, conf_ess_dofs;
|
||||
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
|
||||
|
||||
// if (fes->GetVSize() == height)
|
||||
// {
|
||||
// EliminateEssentialBCFromDofs(ess_dofs, dpolicy);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
|
||||
// EliminateEssentialBCFromDofs(conf_ess_dofs, dpolicy);
|
||||
// }
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBCDiag (const Array<int>
|
||||
&bdr_attr_is_ess,
|
||||
double value)
|
||||
{
|
||||
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBCDiag: not implemented yet");
|
||||
// Array<int> ess_dofs, conf_ess_dofs;
|
||||
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
|
||||
|
||||
// if (fes->GetVSize() == height)
|
||||
// {
|
||||
// EliminateEssentialBCFromDofsDiag(ess_dofs, value);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
|
||||
// EliminateEssentialBCFromDofsDiag(conf_ess_dofs, value);
|
||||
// }
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateVDofs(const Array<int> &vdofs,
|
||||
const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
vdofs.HostRead();
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
int vdof = vdofs[i];
|
||||
if ( vdof >= 0 )
|
||||
{
|
||||
mat -> EliminateRowCol (vdof, sol(vdof), rhs, dpolicy);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat -> EliminateRowCol (-1-vdof, sol(-1-vdof), rhs, dpolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateVDofs(const Array<int> &vdofs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
if (mat_e == NULL)
|
||||
{
|
||||
mat_e = new SparseMatrix(height);
|
||||
}
|
||||
|
||||
// mat -> EliminateCols(vdofs, *mat_e,)
|
||||
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
int vdof = vdofs[i];
|
||||
if ( vdof >= 0 )
|
||||
{
|
||||
mat -> EliminateRowCol (vdof, *mat_e, dpolicy);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat -> EliminateRowCol (-1-vdof, *mat_e, dpolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBCFromDofs(
|
||||
const Array<int> &ess_dofs, const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
|
||||
MFEM_ASSERT(sol.Size() == height, "incorrect sol Vector size");
|
||||
MFEM_ASSERT(rhs.Size() == height, "incorrect rhs Vector size");
|
||||
|
||||
for (int i = 0; i < ess_dofs.Size(); i++)
|
||||
{
|
||||
if (ess_dofs[i] < 0)
|
||||
{
|
||||
mat -> EliminateRowCol (i, sol(i), rhs, dpolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBCFromDofs (const Array<int>
|
||||
&ess_dofs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
|
||||
|
||||
for (int i = 0; i < ess_dofs.Size(); i++)
|
||||
{
|
||||
if (ess_dofs[i] < 0)
|
||||
{
|
||||
mat -> EliminateRowCol (i, dpolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateEssentialBCFromDofsDiag (
|
||||
const Array<int> &ess_dofs,
|
||||
double value)
|
||||
{
|
||||
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
|
||||
|
||||
for (int i = 0; i < ess_dofs.Size(); i++)
|
||||
{
|
||||
if (ess_dofs[i] < 0)
|
||||
{
|
||||
mat -> EliminateRowColDiag (i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockBilinearForm::EliminateVDofsInRHS(
|
||||
const Array<int> &vdofs, const Vector &x, Vector &b)
|
||||
{
|
||||
mat_e->AddMult(x, b, -1.);
|
||||
mat->PartMult(vdofs, x, b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BlockBilinearForm::~BlockBilinearForm()
|
||||
{
|
||||
delete mat_e;
|
||||
delete mat;
|
||||
delete element_matrices;
|
||||
|
||||
for (int k=0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
delete domain_integs[k];
|
||||
}
|
||||
for (int k=0; k < trace_integs.Size(); k++)
|
||||
{
|
||||
delete trace_integs[k];
|
||||
}
|
||||
delete P;
|
||||
delete R;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BLOCKBILINEARFORM
|
||||
#define MFEM_BLOCKBILINEARFORM
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief A "square matrix" operator for the associated FE space and
|
||||
BLFIntegrators The sum of all the BLFIntegrators can be used form the matrix
|
||||
M. */
|
||||
class BlockBilinearForm : public Matrix
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
int nblocks;
|
||||
Array<int> dof_offsets;
|
||||
Array<int> tdof_offsets;
|
||||
|
||||
/// Sparse matrix \f$ M \f$ to be associated with the form. Owned.
|
||||
SparseMatrix *mat;
|
||||
|
||||
/** @brief Sparse Matrix \f$ M_e \f$ used to store the eliminations
|
||||
from the b.c. Owned.
|
||||
\f$ M + M_e = M_{original} \f$ */
|
||||
SparseMatrix *mat_e;
|
||||
|
||||
/// FE spaces on which the block form lives. Not owned.
|
||||
Array<FiniteElementSpace * > fespaces;
|
||||
|
||||
/** @brief Indicates the Mesh::sequence corresponding to the current state of
|
||||
the BilinearForm. */
|
||||
long sequence;
|
||||
|
||||
/** @brief Indicates the BlockBilinearFormIntegrator%s stored in #domain_integs,
|
||||
are owned by another BlockBilinearForm. */
|
||||
int extern_bfs;
|
||||
|
||||
/// Set of Domain Integrators to be applied.
|
||||
Array<BlockBilinearFormIntegrator * > domain_integs;
|
||||
|
||||
/// Trace integrators.
|
||||
Array<BlockBilinearFormIntegrator * > trace_integs;
|
||||
|
||||
DenseMatrix elemmat;
|
||||
Array<int> vdofs;
|
||||
|
||||
DenseTensor *element_matrices; ///< Owned.
|
||||
|
||||
BlockMatrix * P = nullptr; // Block Prolongation
|
||||
BlockMatrix * R = nullptr; // Block Restriction
|
||||
|
||||
/** This data member allows one to specify what should be done to the
|
||||
diagonal matrix entries and corresponding RHS values upon elimination of
|
||||
the constrained DoFs. */
|
||||
DiagonalPolicy diag_policy;
|
||||
|
||||
// Allocate appropriate SparseMatrix and assign it to mat
|
||||
void AllocMat();
|
||||
|
||||
void ConformingAssemble();
|
||||
|
||||
void BuildProlongation();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
public:
|
||||
|
||||
/// Creates bilinear form associated with FE spaces @a *fespaces.
|
||||
BlockBilinearForm(Array<FiniteElementSpace * > & fespaces_);
|
||||
|
||||
/// Get the size of the BilinearForm as a square matrix.
|
||||
int Size() const { return height; }
|
||||
|
||||
|
||||
/// Pre-allocate the internal SparseMatrix before assembly.
|
||||
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
const double &operator()(int i, int j) { return (*mat)(i,j); }
|
||||
|
||||
|
||||
/// Matrix vector multiplication: \f$ y = M x \f$
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/** @brief Matrix vector multiplication with the original uneliminated
|
||||
matrix. The original matrix is \f$ M + M_e \f$ so we have:
|
||||
\f$ y = M x + M_e x \f$ */
|
||||
void FullMult(const Vector &x, Vector &y) const
|
||||
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
|
||||
|
||||
virtual double &Elem(int i, int j);
|
||||
virtual const double &Elem(int i, int j) const;
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/// Finalizes the matrix initialization.
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
/// Returns a const reference to the sparse matrix.
|
||||
const SparseMatrix &SpMat() const
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix: \f$ M \f$
|
||||
SparseMatrix &SpMat()
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
const SparseMatrix &SpMatElim() const
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
return *mat_e;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
SparseMatrix &SpMatElim()
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
return *mat_e;
|
||||
}
|
||||
|
||||
/// Adds new Domain Integrator. Assumes ownership of @a bfi.
|
||||
void AddDomainIntegrator(BlockBilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds new Trace Integrator. Assumes ownership of @a bfi.
|
||||
void AddTraceIntegrator(BlockBilinearFormIntegrator *bfi);
|
||||
|
||||
/// Sets all sparse values of \f$ M \f$ and \f$ M_e \f$ to 'a'.
|
||||
void operator=(const double a)
|
||||
{
|
||||
if (mat != NULL) { *mat = a; }
|
||||
if (mat_e != NULL) { *mat_e = a; }
|
||||
}
|
||||
|
||||
/// Assembles the form i.e. sums over all domain integrators.
|
||||
void Assemble(int skip_zeros = 1);
|
||||
|
||||
|
||||
virtual void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior = 0);
|
||||
|
||||
/** @brief Form the linear system A X = B, corresponding to this bilinear
|
||||
form and the linear form @a b(.). */
|
||||
/** Version of the method FormLinearSystem() where the system matrix is
|
||||
returned in the variable @a A, of type OpType, holding a *reference* to
|
||||
the system matrix (created with the method OpType::MakeRef()). The
|
||||
reference will be invalidated when SetOperatorType(), Update(), or the
|
||||
destructor is called. */
|
||||
template <typename OpType>
|
||||
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
|
||||
OpType &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0)
|
||||
{
|
||||
OperatorHandle Ah;
|
||||
FormLinearSystem(ess_tdof_list, x, b, Ah, X, B, copy_interior);
|
||||
OpType *A_ptr = Ah.Is<OpType>();
|
||||
MFEM_VERIFY(A_ptr, "invalid OpType used");
|
||||
A.MakeRef(*A_ptr);
|
||||
}
|
||||
|
||||
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
OperatorHandle &A);
|
||||
|
||||
/// Form the linear system matrix A, see FormLinearSystem() for details.
|
||||
/** Version of the method FormSystemMatrix() where the system matrix is
|
||||
returned in the variable @a A, of type OpType, holding a *reference* to
|
||||
the system matrix (created with the method OpType::MakeRef()). The
|
||||
reference will be invalidated when SetOperatorType(), Update(), or the
|
||||
destructor is called. */
|
||||
template <typename OpType>
|
||||
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
|
||||
{
|
||||
OperatorHandle Ah;
|
||||
FormSystemMatrix(ess_tdof_list, Ah);
|
||||
OpType *A_ptr = Ah.Is<OpType>();
|
||||
MFEM_VERIFY(A_ptr, "invalid OpType used");
|
||||
A.MakeRef(*A_ptr);
|
||||
}
|
||||
|
||||
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x);
|
||||
|
||||
|
||||
void ComputeElementMatrices();
|
||||
|
||||
/// Free the memory used by the element matrices.
|
||||
void FreeElementMatrices()
|
||||
{ delete element_matrices; element_matrices = NULL; }
|
||||
|
||||
/// Compute the element matrix of the given element
|
||||
/** The element matrix is computed by calling the domain integrators
|
||||
or the one stored internally by a prior call of ComputeElementMatrices()
|
||||
is returned when available.
|
||||
*/
|
||||
void ComputeElementMatrix(int i, DenseMatrix &elmat);
|
||||
|
||||
/// Eliminate essential boundary DOFs from the system.
|
||||
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
|
||||
the essential part of the boundary. By default, the diagonal at the
|
||||
essential DOFs is set to 1.0. This behavior is controlled by the argument
|
||||
@a dpolicy. */
|
||||
void EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/// Eliminate essential boundary DOFs from the system matrix.
|
||||
void EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
/// Perform elimination and set the diagonal entry to the given value
|
||||
void EliminateEssentialBCDiag(const Array<int> &bdr_attr_is_ess,
|
||||
double value);
|
||||
|
||||
/// Eliminate the given @a vdofs.
|
||||
/** NOTE: here, @a vdofs is a list of DOFs from all the fespaces
|
||||
In this case the eliminations are applied to the internal \f$ M \f$
|
||||
and @a rhs without storing the elimination matrix \f$ M_e \f$. */
|
||||
void EliminateVDofs(const Array<int> &vdofs, const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/// Eliminate the given @a vdofs (all the fespaces), storing the eliminated part internally in \f$ M_e \f$.
|
||||
/** This method works in conjunction with EliminateVDofsInRHS() and allows
|
||||
elimination of boundary conditions in multiple right-hand sides. In this
|
||||
method, @a vdofs is a list of DOFs. */
|
||||
void EliminateVDofs(const Array<int> &vdofs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/** @brief Similar to
|
||||
EliminateVDofs(const Array<int> &, const Vector &, Vector &, DiagonalPolicy)
|
||||
but here @a ess_dofs is a marker (boolean) array on all vector-dofs
|
||||
(@a ess_dofs[i] < 0 is true). */
|
||||
void EliminateEssentialBCFromDofs(const Array<int> &ess_dofs, const Vector &sol,
|
||||
Vector &rhs, DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/** @brief Similar to EliminateVDofs(const Array<int> &, DiagonalPolicy) but
|
||||
here @a ess_dofs is a marker (boolean) array on all vector-dofs
|
||||
(@a ess_dofs[i] < 0 is true). */
|
||||
void EliminateEssentialBCFromDofs(const Array<int> &ess_dofs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
/// Perform elimination and set the diagonal entry to the given value
|
||||
void EliminateEssentialBCFromDofsDiag(const Array<int> &ess_dofs,
|
||||
double value);
|
||||
|
||||
/** @brief Use the stored eliminated part of the matrix (see
|
||||
EliminateVDofs(const Array<int> &, DiagonalPolicy)) to modify the r.h.s.
|
||||
@a b; @a vdofs is a list of DOFs (non-directional, i.e. >= 0). */
|
||||
void EliminateVDofsInRHS(const Array<int> &vdofs, const Vector &x,
|
||||
Vector &b);
|
||||
|
||||
|
||||
/// Sets diagonal policy used upon construction of the linear system.
|
||||
/** Policies include:
|
||||
|
||||
- DIAG_ZERO (Set the diagonal values to zero)
|
||||
- DIAG_ONE (Set the diagonal values to one)
|
||||
- DIAG_KEEP (Keep the diagonal values)
|
||||
*/
|
||||
void SetDiagonalPolicy(DiagonalPolicy policy)
|
||||
{
|
||||
diag_policy = policy;
|
||||
}
|
||||
|
||||
/// Destroys bilinear form.
|
||||
virtual ~BlockBilinearForm();
|
||||
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "fem.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void BlockBilinearFormIntegrator::AssembleElementMatrix(
|
||||
const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
mfem_error ("BlockBilinearFormIntegrator::AssembleElementMatrix\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BlockLinearFormIntegrator::AssembleRHSElementVect(
|
||||
const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvect)
|
||||
{
|
||||
mfem_error ("BlockLinearFormIntegrator::AssembleElementVector\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
/** Given a particular Finite Element computes the element vector */
|
||||
void TestBlockBilinearFormIntegrator::AssembleElementMatrix
|
||||
(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int nd = 0;
|
||||
int nblocks = el.Size();
|
||||
Array<int> offsets(nblocks+1);
|
||||
offsets[0] = 0;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
nd += el[i]->GetDof();
|
||||
offsets[i+1] = el[i]->GetDof();
|
||||
}
|
||||
offsets.PartialSum();
|
||||
elmat.SetSize(nd);
|
||||
elmat = 0.0;
|
||||
DenseMatrix dmat;
|
||||
|
||||
if (blfis.NumRows())
|
||||
{
|
||||
// Get the matrices directly from the existing BilinearFormIntegrators
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
// mfem::out << "i = " << i << std::endl;
|
||||
int offset_i = offsets[i];
|
||||
const FiniteElement * fe_i = el[i];
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
// mfem::out << "j = " << j << std::endl;
|
||||
BilinearFormIntegrator * blfi = blfis(i,j);
|
||||
if (!blfi) { continue; }
|
||||
if (j == i)
|
||||
{
|
||||
blfi->AssembleElementMatrix(*fe_i,Trans,dmat);
|
||||
// mfem::out << "j 1 = " << j << std::endl;
|
||||
elmat.SetSubMatrix(offset_i,dmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
const FiniteElement * fe_j = el[j];
|
||||
blfi->AssembleElementMatrix2(*fe_j,*fe_i,Trans,dmat);
|
||||
// mfem::out << "j 2 = " << j << std::endl;
|
||||
int offset_j = offsets[j];
|
||||
elmat.SetSubMatrix(offset_i,offset_j,dmat);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// else compute the matrices
|
||||
elmat = 25.0;
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
/** Given a particular Finite Element computes the element vector */
|
||||
void TestBlockLinearFormIntegrator::AssembleRHSElementVect
|
||||
(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvector)
|
||||
{
|
||||
int nd = 0;
|
||||
int nblocks = el.Size();
|
||||
Array<int> offsets(nblocks+1);
|
||||
offsets[0] = 0;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
nd += el[i]->GetDof();
|
||||
offsets[i+1] = el[i]->GetDof();
|
||||
}
|
||||
offsets.PartialSum();
|
||||
elvector.SetSize(nd);
|
||||
elvector = 0.0;
|
||||
Vector subvector;
|
||||
|
||||
if (lfis.Size())
|
||||
{
|
||||
// Get the matrices directly from the existing BilinearFormIntegrators
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
int offset = offsets[i];
|
||||
const FiniteElement * fe_i = el[i];
|
||||
LinearFormIntegrator * lfi = lfis[i];
|
||||
if (!lfi)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
lfi->AssembleRHSElementVect(*fe_i,Trans,subvector);
|
||||
elvector.SetVector(subvector,offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// else, compute the block linear form integrator
|
||||
// elvector = 1.0;
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BLOCKINTEG
|
||||
#define MFEM_BLOCKINTEG
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fe.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/** The abstract base class BlockBilinearFormIntegrator is
|
||||
a generalization of the BilinearFormIntegrator class suitable
|
||||
for block formulations. */
|
||||
class BlockBilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
const IntegrationRule *IntRule;
|
||||
BlockBilinearFormIntegrator(const IntegrationRule *ir = NULL)
|
||||
: IntRule(ir) { }
|
||||
public:
|
||||
|
||||
|
||||
/// Given a particular Finite Element computes the element matrix elmat.
|
||||
virtual void AssembleElementMatrix(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual ~BlockBilinearFormIntegrator() { }
|
||||
};
|
||||
|
||||
/** The abstract base class BlockBilinearFormIntegrator is
|
||||
a generalization of the BilinearFormIntegrator class suitable
|
||||
for block formulations. */
|
||||
class BlockLinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
const IntegrationRule *IntRule;
|
||||
BlockLinearFormIntegrator(const IntegrationRule *ir = NULL)
|
||||
: IntRule(ir) { }
|
||||
|
||||
public:
|
||||
/// Given a particular Finite Element computes the element matrix elmat.
|
||||
virtual void AssembleRHSElementVect(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvect);
|
||||
|
||||
virtual ~BlockLinearFormIntegrator() { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class TestBlockBilinearFormIntegrator: public BlockBilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
Array<const FiniteElementSpace * > fespaces;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
|
||||
Array2D<BilinearFormIntegrator *> blfis;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
TestBlockBilinearFormIntegrator(const IntegrationRule *ir = NULL)
|
||||
: BlockBilinearFormIntegrator(ir), Q(NULL), maps(NULL), geom(NULL) { }
|
||||
|
||||
/// Construct a mass integrator with coefficient q
|
||||
TestBlockBilinearFormIntegrator(Coefficient &q,
|
||||
const IntegrationRule *ir = NULL)
|
||||
: BlockBilinearFormIntegrator(ir), Q(&q), maps(NULL), geom(NULL) { }
|
||||
|
||||
|
||||
TestBlockBilinearFormIntegrator(Array2D<BilinearFormIntegrator *> blfis_)
|
||||
: BlockBilinearFormIntegrator(NULL), blfis(blfis_) { }
|
||||
|
||||
void SetIntegrators(Array2D<BilinearFormIntegrator *> blfis_)
|
||||
{
|
||||
blfis = blfis_;
|
||||
}
|
||||
|
||||
|
||||
/** Given a particular Finite Element computes the element matrix
|
||||
elmat. */
|
||||
virtual void AssembleElementMatrix(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual ~TestBlockBilinearFormIntegrator() { }
|
||||
|
||||
};
|
||||
|
||||
/** Class for local vector assembly */
|
||||
class TestBlockLinearFormIntegrator: public BlockLinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
Array<const FiniteElementSpace * > fespaces;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
Array<LinearFormIntegrator *> lfis;
|
||||
|
||||
public:
|
||||
|
||||
TestBlockLinearFormIntegrator(const IntegrationRule *ir = NULL)
|
||||
: BlockLinearFormIntegrator(ir), Q(NULL), maps(NULL), geom(NULL) { }
|
||||
|
||||
/// Construct a test linear integrator with coefficient q
|
||||
TestBlockLinearFormIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
|
||||
: BlockLinearFormIntegrator(ir), Q(&q), maps(NULL), geom(NULL) { }
|
||||
|
||||
|
||||
TestBlockLinearFormIntegrator(Array<LinearFormIntegrator *> lfis_)
|
||||
: BlockLinearFormIntegrator(NULL), lfis(lfis_) { }
|
||||
|
||||
void SetIntegrators(Array<LinearFormIntegrator *> lfis_)
|
||||
{
|
||||
lfis = lfis_;
|
||||
}
|
||||
|
||||
/** Given a particular Finite Element computes the element vector */
|
||||
virtual void AssembleRHSElementVect(const Array<const FiniteElement *> &el,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvector);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "fem.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
BlockLinearForm::BlockLinearForm(Array<FiniteElementSpace * > & fespaces_) :
|
||||
Vector(0), fespaces(fespaces_)
|
||||
{
|
||||
int s = 0;
|
||||
int nblocks = fespaces.Size();
|
||||
for (int i =0; i<nblocks; i++)
|
||||
{
|
||||
s += fespaces[i]->GetVSize();
|
||||
}
|
||||
// mfem::out << "size = " << size << std::endl;
|
||||
|
||||
SetSize(s);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void BlockLinearForm::AddDomainIntegrator(BlockLinearFormIntegrator *lfi)
|
||||
{
|
||||
domain_integs.Append(lfi);
|
||||
}
|
||||
|
||||
void BlockLinearForm::Assemble()
|
||||
{
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fespaces[0] -> GetMesh();
|
||||
Vector subvect,elvect, *elvect_p;
|
||||
|
||||
int nblocks = fespaces.Size();
|
||||
Array<const FiniteElement *> fe(nblocks);
|
||||
Array<int> offsetvdofs;
|
||||
Array<int> elementblockoffsets(nblocks+1);
|
||||
elementblockoffsets[0] = 0;
|
||||
Array<int> blockoffsets(nblocks+1);
|
||||
blockoffsets[0] = 0;
|
||||
for (int i =0; i<nblocks; i++)
|
||||
{
|
||||
blockoffsets[i+1] = fespaces[i]->GetVSize();
|
||||
}
|
||||
blockoffsets.PartialSum();
|
||||
|
||||
Vector::operator=(0.0);
|
||||
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
// loop through elements
|
||||
for (int i = 0; i < mesh -> GetNE(); i++)
|
||||
{
|
||||
elvect.SetSize(0);
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
fe[j] = fespaces[j]->GetFE(i);
|
||||
elementblockoffsets[j+1] = fe[j]->GetDof();
|
||||
}
|
||||
elementblockoffsets.PartialSum();
|
||||
eltrans = mesh->GetElementTransformation(i);
|
||||
|
||||
domain_integs[k]->AssembleRHSElementVect(fe, *eltrans, elemvect);
|
||||
if (elvect.Size() == 0)
|
||||
{
|
||||
elvect = elemvect;
|
||||
}
|
||||
else
|
||||
{
|
||||
elvect += elemvect;
|
||||
}
|
||||
}
|
||||
if (elvect.Size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
elvect_p = &elvect;
|
||||
}
|
||||
|
||||
double *data = elvect_p->GetData();
|
||||
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
doftrans = fespaces[j]->GetElementVDofs(i, vdofs);
|
||||
int offset = blockoffsets[j];
|
||||
offsetvdofs.SetSize(vdofs.Size());
|
||||
for (int l = 0; l<vdofs.Size(); l++)
|
||||
{
|
||||
offsetvdofs[l] = vdofs[l]<0 ? -offset + vdofs[l]
|
||||
: offset + vdofs[l];
|
||||
}
|
||||
int jbeg = elementblockoffsets[j];
|
||||
int jend = elementblockoffsets[j+1]-1;
|
||||
subvect.SetSize(jend-jbeg+1);
|
||||
subvect.SetData(&data[jbeg]);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(subvect);
|
||||
}
|
||||
AddElementVector(offsetvdofs,subvect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // name space mfem
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BLOCKLINEARFORM
|
||||
#define MFEM_BLOCKLINEARFORM
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
class BlockLinearForm : public Vector
|
||||
{
|
||||
protected:
|
||||
/// FE spaces on which the LinearForm lives. Not owned.
|
||||
Array<FiniteElementSpace * > fespaces;
|
||||
|
||||
/// Set of Domain Integrators to be applied.
|
||||
Array<BlockLinearFormIntegrator*> domain_integs;
|
||||
|
||||
Vector elemvect;
|
||||
Array<int> vdofs;
|
||||
|
||||
public:
|
||||
BlockLinearForm(Array<FiniteElementSpace * > & fespaces_);
|
||||
|
||||
/// Adds new Domain Integrator. Assumes ownership of @a lfi.
|
||||
void AddDomainIntegrator(BlockLinearFormIntegrator *lfi);
|
||||
|
||||
/// Assembles the block linear form i.e. sums over all domain integrators.
|
||||
void Assemble();
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,969 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "blockstaticcond.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
BlockStaticCondensation::BlockStaticCondensation(Array<FiniteElementSpace *> &
|
||||
fes_)
|
||||
{
|
||||
SetSpaces(fes_);
|
||||
|
||||
Array<int> rvdofs;
|
||||
Array<int> vdofs;
|
||||
Array<int> rdof_edof0;
|
||||
for (int k = 0; k<nblocks; k++)
|
||||
{
|
||||
if (!tr_fes[k]) { continue; }
|
||||
rdof_edof0.SetSize(tr_fes[k]->GetVSize());
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
fes[k]->GetElementVDofs(i, vdofs);
|
||||
tr_fes[k]->GetElementVDofs(i, rvdofs);
|
||||
const int vdim = fes[k]->GetVDim();
|
||||
const int nsd = vdofs.Size()/vdim;
|
||||
const int nsrd = rvdofs.Size()/vdim;
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int j = 0; j < nsrd; j++)
|
||||
{
|
||||
int rvdof = rvdofs[j+nsrd*vd];
|
||||
int vdof = vdofs[j+nsd*vd];
|
||||
if (rvdof < 0)
|
||||
{
|
||||
rvdof = -1-rvdof;
|
||||
vdof = -1-vdof;
|
||||
}
|
||||
MFEM_ASSERT(vdof >= 0, "incompatible volume and trace FE spaces");
|
||||
rdof_edof0[rvdof] = vdof + dof_offsets[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
rdof_edof.Append(rdof_edof0);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> & fes_)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh *pmesh = nullptr;
|
||||
parallel = false;
|
||||
if (dynamic_cast<ParFiniteElementSpace *>(fes_[0]))
|
||||
{
|
||||
parallel = true;
|
||||
}
|
||||
#else
|
||||
parallel = false;
|
||||
#endif
|
||||
fes=fes_;
|
||||
nblocks = fes.Size();
|
||||
rblocks = 0;
|
||||
tr_fes.SetSize(nblocks);
|
||||
mesh = fes[0]->GetMesh();
|
||||
|
||||
IsTraceSpace.SetSize(nblocks);
|
||||
const FiniteElementCollection * fec;
|
||||
for (int i = 0; i < nblocks; i++)
|
||||
{
|
||||
fec = fes[i]->FEColl();
|
||||
IsTraceSpace[i] =
|
||||
(dynamic_cast<const H1_Trace_FECollection*>(fec) ||
|
||||
dynamic_cast<const ND_Trace_FECollection*>(fec) ||
|
||||
dynamic_cast<const RT_Trace_FECollection*>(fec));
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
pmesh = dynamic_cast<ParMesh *>(mesh);
|
||||
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
|
||||
nullptr : (IsTraceSpace[i]) ? fes[i] :
|
||||
new ParFiniteElementSpace(pmesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
|
||||
fes[i]->GetOrdering());
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
|
||||
nullptr : (IsTraceSpace[i]) ? fes[i] :
|
||||
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
|
||||
fes[i]->GetOrdering());
|
||||
}
|
||||
#else
|
||||
// skip if it's an L2 space (no trace space to construct)
|
||||
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
|
||||
nullptr : (IsTraceSpace[i]) ? fes[i] :
|
||||
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
|
||||
fes[i]->GetOrdering());
|
||||
#endif
|
||||
if (tr_fes[i]) { rblocks++; }
|
||||
}
|
||||
if (parallel)
|
||||
{
|
||||
ess_tdofs.SetSize(rblocks);
|
||||
for (int i = 0; i<rblocks; i++)
|
||||
{
|
||||
ess_tdofs[i] = new Array<int>();
|
||||
}
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::ComputeOffsets()
|
||||
{
|
||||
dof_offsets.SetSize(nblocks+1);
|
||||
tdof_offsets.SetSize(nblocks+1);
|
||||
dof_offsets[0] = 0;
|
||||
tdof_offsets[0] = 0;
|
||||
|
||||
rdof_offsets.SetSize(rblocks+1);
|
||||
rtdof_offsets.SetSize(rblocks+1);
|
||||
rdof_offsets[0] = 0;
|
||||
rtdof_offsets[0] = 0;
|
||||
|
||||
int j=0;
|
||||
for (int i =0; i<nblocks; i++)
|
||||
{
|
||||
dof_offsets[i+1] = fes[i]->GetVSize();
|
||||
tdof_offsets[i+1] = fes[i]->GetTrueVSize();
|
||||
if (tr_fes[i])
|
||||
{
|
||||
rdof_offsets[j+1] = tr_fes[i]->GetVSize();
|
||||
rtdof_offsets[j+1] = tr_fes[i]->GetTrueVSize();
|
||||
j++;
|
||||
}
|
||||
}
|
||||
rdof_offsets.PartialSum();
|
||||
rtdof_offsets.PartialSum();
|
||||
dof_offsets.PartialSum();
|
||||
tdof_offsets.PartialSum();
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::Init()
|
||||
{
|
||||
lmat.SetSize(mesh->GetNE());
|
||||
lvec.SetSize(mesh->GetNE());
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
lmat[i] = nullptr;
|
||||
lvec[i] = nullptr;
|
||||
}
|
||||
|
||||
ComputeOffsets();
|
||||
|
||||
S = new BlockMatrix(rdof_offsets);
|
||||
S->owns_blocks = 1;
|
||||
|
||||
for (int i = 0; i<S->NumRowBlocks(); i++)
|
||||
{
|
||||
int h = rdof_offsets[i+1] - rdof_offsets[i];
|
||||
for (int j = 0; j<S->NumColBlocks(); j++)
|
||||
{
|
||||
int w = rdof_offsets[j+1] - rdof_offsets[j];
|
||||
S->SetBlock(i,j,new SparseMatrix(h, w));
|
||||
}
|
||||
}
|
||||
y = new BlockVector(rdof_offsets);
|
||||
*y = 0.;
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::GetReduceElementIndicesAndOffsets(int el,
|
||||
Array<int> & trace_ldofs,
|
||||
Array<int> & interior_ldofs,
|
||||
Array<int> & offsets) const
|
||||
{
|
||||
int dim = mesh->Dimension();
|
||||
offsets.SetSize(tr_fes.Size()+1); offsets = 0;
|
||||
Array<int> dofs;
|
||||
Array<int> faces, ori;
|
||||
if (dim == 1)
|
||||
{
|
||||
mesh->GetElementVertices(el, faces);
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
mesh->GetElementEdges(el, faces, ori);
|
||||
}
|
||||
else //dim = 3
|
||||
{
|
||||
mesh->GetElementFaces(el,faces,ori);
|
||||
}
|
||||
int numfaces = faces.Size();
|
||||
|
||||
trace_ldofs.SetSize(0);
|
||||
interior_ldofs.SetSize(0);
|
||||
// construct Array of bubble dofs to be extracted
|
||||
int skip=0;
|
||||
Array<int> tr_dofs;
|
||||
Array<int> int_dofs;
|
||||
for (int i = 0; i<tr_fes.Size(); i++)
|
||||
{
|
||||
int td = 0;
|
||||
int ndof;
|
||||
// if it's an L2 space (bubbles)
|
||||
if (!tr_fes[i])
|
||||
{
|
||||
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
|
||||
td = 0;
|
||||
}
|
||||
else if (IsTraceSpace[i])
|
||||
{
|
||||
for (int iface = 0; iface < numfaces; iface++)
|
||||
{
|
||||
td += fes[i]->GetVDim()*fes[i]->GetFaceElement(faces[iface])->GetDof();
|
||||
}
|
||||
ndof = td;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<int> trace_dofs;
|
||||
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
|
||||
tr_fes[i]->GetElementVDofs(el, trace_dofs);
|
||||
td = trace_dofs.Size(); // number of trace dofs
|
||||
}
|
||||
offsets[i+1] = td;
|
||||
tr_dofs.SetSize(td);
|
||||
int_dofs.SetSize(ndof - td);
|
||||
for (int j = 0; j<td; j++)
|
||||
{
|
||||
tr_dofs[j] = skip + j;
|
||||
}
|
||||
for (int j = 0; j<ndof-td; j++)
|
||||
{
|
||||
int_dofs[j] = skip + td + j;
|
||||
}
|
||||
skip+=ndof;
|
||||
|
||||
trace_ldofs.Append(tr_dofs);
|
||||
interior_ldofs.Append(int_dofs);
|
||||
}
|
||||
offsets.PartialSum();
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::GetReduceElementVDofs(int el,
|
||||
Array<int> & rdofs) const
|
||||
{
|
||||
Array<int> faces, ori;
|
||||
int dim = mesh->Dimension();
|
||||
if (dim == 1)
|
||||
{
|
||||
mesh->GetElementVertices(el, faces);
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
mesh->GetElementEdges(el, faces, ori);
|
||||
}
|
||||
else //dim = 3
|
||||
{
|
||||
mesh->GetElementFaces(el,faces,ori);
|
||||
}
|
||||
int numfaces = faces.Size();
|
||||
rdofs.SetSize(0);
|
||||
int skip = 0;
|
||||
for (int i = 0; i<tr_fes.Size(); i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
Array<int> vdofs;
|
||||
if (IsTraceSpace[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
for (int k = 0; k < numfaces; k++)
|
||||
{
|
||||
int iface = faces[k];
|
||||
tr_fes[i]->GetFaceVDofs(iface, face_vdofs);
|
||||
vdofs.Append(face_vdofs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_fes[i]->GetElementVDofs(el, vdofs);
|
||||
}
|
||||
for (int j=0; j<vdofs.Size(); j++)
|
||||
{
|
||||
vdofs[j] = (vdofs[j]>=0) ? vdofs[j]+rdof_offsets[skip] :
|
||||
vdofs[j]-rdof_offsets[skip];
|
||||
}
|
||||
skip++;
|
||||
rdofs.Append(vdofs);
|
||||
}
|
||||
}
|
||||
void BlockStaticCondensation::GetElementVDofs(int el, Array<int> & vdofs) const
|
||||
{
|
||||
Array<int> faces, ori;
|
||||
int dim = mesh->Dimension();
|
||||
if (dim == 1)
|
||||
{
|
||||
mesh->GetElementVertices(el, faces);
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
mesh->GetElementEdges(el, faces, ori);
|
||||
}
|
||||
else //dim = 3
|
||||
{
|
||||
mesh->GetElementFaces(el,faces,ori);
|
||||
}
|
||||
int numfaces = faces.Size();
|
||||
vdofs.SetSize(0);
|
||||
for (int i = 0; i<tr_fes.Size(); i++)
|
||||
{
|
||||
Array<int> dofs;
|
||||
if (IsTraceSpace[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
for (int k = 0; k < numfaces; k++)
|
||||
{
|
||||
int iface = faces[k];
|
||||
fes[i]->GetFaceVDofs(iface, face_vdofs);
|
||||
dofs.Append(face_vdofs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fes[i]->GetElementVDofs(el, dofs);
|
||||
}
|
||||
for (int j=0; j<dofs.Size(); j++)
|
||||
{
|
||||
dofs[j] = (dofs[j]>=0) ? dofs[j]+dof_offsets[i] :
|
||||
dofs[j]-dof_offsets[i];
|
||||
}
|
||||
vdofs.Append(dofs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::GetLocalShurComplement(int el,
|
||||
const Array<int> & tr_idx, const Array<int> & int_idx,
|
||||
const DenseMatrix & elmat, const Vector & elvect,
|
||||
DenseMatrix & rmat, Vector & rvect)
|
||||
{
|
||||
int rdofs = tr_idx.Size();
|
||||
int idofs = int_idx.Size();
|
||||
MFEM_VERIFY(idofs != 0, "Number of interior dofs is zero");
|
||||
MFEM_VERIFY(rdofs != 0, "Number of interface dofs is zero");
|
||||
|
||||
rmat.SetSize(rdofs);
|
||||
rvect.SetSize(rdofs);
|
||||
|
||||
DenseMatrix A_tt, A_ti, A_it, A_ii;
|
||||
Vector y_t, y_i;
|
||||
|
||||
elmat.GetSubMatrix(tr_idx,A_tt);
|
||||
elmat.GetSubMatrix(tr_idx,int_idx, A_ti);
|
||||
elmat.GetSubMatrix(int_idx, tr_idx, A_it);
|
||||
elmat.GetSubMatrix(int_idx, A_ii);
|
||||
|
||||
elvect.GetSubVector(tr_idx, y_t);
|
||||
elvect.GetSubVector(int_idx, y_i);
|
||||
|
||||
DenseMatrixInverse lu(A_ii);
|
||||
lu.Factor();
|
||||
lmat[el] = new DenseMatrix(idofs,rdofs);
|
||||
lvec[el] = new Vector(idofs);
|
||||
|
||||
lu.Mult(A_it,*lmat[el]);
|
||||
lu.Mult(y_i,*lvec[el]);
|
||||
|
||||
// LHS
|
||||
mfem::Mult(A_ti,*lmat[el],rmat);
|
||||
|
||||
rmat.Neg();
|
||||
rmat.Add(1., A_tt);
|
||||
|
||||
// RHS
|
||||
A_ti.Mult(*lvec[el], rvect);
|
||||
rvect.Neg();
|
||||
rvect.Add(1., y_t);
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
DenseMatrix &elmat,
|
||||
Vector & elvect)
|
||||
{
|
||||
// Get Shur Complement
|
||||
Array<int> tr_idx, int_idx;
|
||||
Array<int> offsets;
|
||||
// Get local element idx and offsets for global assembly
|
||||
GetReduceElementIndicesAndOffsets(el, tr_idx,int_idx, offsets);
|
||||
|
||||
DenseMatrix rmat, *rmatptr;
|
||||
Vector rvec, *rvecptr;
|
||||
// Extract the reduced matrices based on tr_idx and int_idx
|
||||
if (int_idx.Size()!=0)
|
||||
{
|
||||
GetLocalShurComplement(el,tr_idx,int_idx, elmat, elvect, rmat, rvec);
|
||||
rmatptr = &rmat;
|
||||
rvecptr = &rvec;
|
||||
}
|
||||
else
|
||||
{
|
||||
rmatptr = &elmat;
|
||||
rvecptr = &elvect;
|
||||
}
|
||||
|
||||
// Assemble global mat and rhs
|
||||
DofTransformation * doftrans_i, *doftrans_j;
|
||||
|
||||
|
||||
Array<int> faces, ori;
|
||||
int dim = mesh->Dimension();
|
||||
if (dim == 1)
|
||||
{
|
||||
mesh->GetElementVertices(el, faces);
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
mesh->GetElementEdges(el, faces, ori);
|
||||
}
|
||||
else //dim = 3
|
||||
{
|
||||
mesh->GetElementFaces(el,faces,ori);
|
||||
}
|
||||
int numfaces = faces.Size();
|
||||
|
||||
int skip_i=0;
|
||||
for (int i = 0; i<tr_fes.Size(); i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
Array<int> vdofs_i;
|
||||
doftrans_i = nullptr;
|
||||
if (IsTraceSpace[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
for (int k = 0; k < numfaces; k++)
|
||||
{
|
||||
int iface = faces[k];
|
||||
tr_fes[i]->GetFaceVDofs(iface, face_vdofs);
|
||||
vdofs_i.Append(face_vdofs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_i = tr_fes[i]->GetElementVDofs(el, vdofs_i);
|
||||
}
|
||||
int skip_j=0;
|
||||
for (int j = 0; j<tr_fes.Size(); j++)
|
||||
{
|
||||
if (!tr_fes[j]) { continue; }
|
||||
Array<int> vdofs_j;
|
||||
doftrans_j = nullptr;
|
||||
|
||||
if (IsTraceSpace[j])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
for (int k = 0; k < numfaces; k++)
|
||||
{
|
||||
int iface = faces[k];
|
||||
tr_fes[j]->GetFaceVDofs(iface, face_vdofs);
|
||||
vdofs_j.Append(face_vdofs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_j = tr_fes[j]->GetElementVDofs(el, vdofs_j);
|
||||
}
|
||||
|
||||
DenseMatrix Ae;
|
||||
rmatptr->GetSubMatrix(offsets[i],offsets[i+1],
|
||||
offsets[j],offsets[j+1], Ae);
|
||||
if (doftrans_i || doftrans_j)
|
||||
{
|
||||
TransformDual(doftrans_i, doftrans_j, Ae);
|
||||
}
|
||||
S->GetBlock(skip_i,skip_j).AddSubMatrix(vdofs_i,vdofs_j, Ae);
|
||||
skip_j++;
|
||||
}
|
||||
|
||||
// assemble rhs
|
||||
double * data = rvecptr->GetData();
|
||||
Vector vec1;
|
||||
// ref subvector
|
||||
vec1.SetDataAndSize(&data[offsets[i]],
|
||||
offsets[i+1]-offsets[i]);
|
||||
if (doftrans_i)
|
||||
{
|
||||
doftrans_i->TransformDual(vec1);
|
||||
}
|
||||
y->GetBlock(skip_i).AddElementVector(vdofs_i,vec1);
|
||||
skip_i++;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::BuildProlongation()
|
||||
{
|
||||
P = new BlockMatrix(rdof_offsets, rtdof_offsets);
|
||||
R = new BlockMatrix(rtdof_offsets, rdof_offsets);
|
||||
P->owns_blocks = 0;
|
||||
R->owns_blocks = 0;
|
||||
int skip = 0;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
const SparseMatrix *P_ = tr_fes[i]->GetConformingProlongation();
|
||||
if (P_)
|
||||
{
|
||||
const SparseMatrix *R_ = tr_fes[i]->GetRestrictionMatrix();
|
||||
P->SetBlock(skip,skip,const_cast<SparseMatrix*>(P_));
|
||||
R->SetBlock(skip,skip,const_cast<SparseMatrix*>(R_));
|
||||
}
|
||||
skip++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void BlockStaticCondensation::BuildParallelProlongation()
|
||||
{
|
||||
MFEM_VERIFY(parallel, "BuildParallelProlongation: wrong code path");
|
||||
pP = new BlockOperator(rdof_offsets, rtdof_offsets);
|
||||
R = new BlockMatrix(rtdof_offsets, rdof_offsets);
|
||||
pP->owns_blocks = 0;
|
||||
R->owns_blocks = 0;
|
||||
int skip = 0;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
const HypreParMatrix *P_ =
|
||||
dynamic_cast<ParFiniteElementSpace *>(tr_fes[i])->Dof_TrueDof_Matrix();
|
||||
if (P_)
|
||||
{
|
||||
const SparseMatrix *R_ = tr_fes[i]->GetRestrictionMatrix();
|
||||
pP->SetBlock(skip,skip,const_cast<HypreParMatrix*>(P_));
|
||||
R->SetBlock(skip,skip,const_cast<SparseMatrix*>(R_));
|
||||
}
|
||||
skip++;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::ParallelAssemble(BlockMatrix *m)
|
||||
{
|
||||
if (!pP) { BuildParallelProlongation(); }
|
||||
|
||||
pS = new BlockOperator(rtdof_offsets);
|
||||
pS_e = new BlockOperator(rtdof_offsets);
|
||||
pS->owns_blocks = 1;
|
||||
pS_e->owns_blocks = 1;
|
||||
HypreParMatrix * A = nullptr;
|
||||
HypreParMatrix * PtAP = nullptr;
|
||||
int skip_i=0;
|
||||
ParFiniteElementSpace * pfes_i = nullptr;
|
||||
ParFiniteElementSpace * pfes_j = nullptr;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
pfes_i = dynamic_cast<ParFiniteElementSpace*>(fes[i]);
|
||||
HypreParMatrix * Pi = (HypreParMatrix*)(&pP->GetBlock(skip_i,skip_i));
|
||||
int skip_j=0;
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
if (!tr_fes[j]) { continue; }
|
||||
if (m->IsZeroBlock(skip_i,skip_j)) { continue; }
|
||||
if (skip_i == skip_j)
|
||||
{
|
||||
// Make block diagonal square hypre matrix
|
||||
A = new HypreParMatrix(pfes_i->GetComm(), pfes_i->GlobalVSize(),
|
||||
pfes_i->GetDofOffsets(),&m->GetBlock(skip_i,skip_i));
|
||||
PtAP = RAP(A,Pi);
|
||||
delete A;
|
||||
pS_e->SetBlock(skip_i,skip_i,PtAP->EliminateRowsCols(*ess_tdofs[skip_i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
pfes_j = dynamic_cast<ParFiniteElementSpace*>(fes[j]);
|
||||
HypreParMatrix * Pj = (HypreParMatrix*)(&pP->GetBlock(skip_j,skip_j));
|
||||
A = new HypreParMatrix(pfes_i->GetComm(), pfes_i->GlobalVSize(),
|
||||
pfes_j->GlobalVSize(), pfes_i->GetDofOffsets(),
|
||||
pfes_j->GetDofOffsets(), &m->GetBlock(skip_i,skip_j));
|
||||
PtAP = RAP(Pi,A,Pj);
|
||||
delete A;
|
||||
pS_e->SetBlock(skip_i,skip_j,PtAP->EliminateCols(*ess_tdofs[skip_j]));
|
||||
PtAP->EliminateRows(*ess_tdofs[skip_i]);
|
||||
}
|
||||
pS->SetBlock(skip_i,skip_j,PtAP);
|
||||
skip_j++;
|
||||
}
|
||||
skip_i++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void BlockStaticCondensation::ConformingAssemble(int skip_zeros)
|
||||
{
|
||||
Finalize(0);
|
||||
if (!P) { BuildProlongation(); }
|
||||
|
||||
BlockMatrix * Pt = Transpose(*P);
|
||||
BlockMatrix * PtA = mfem::Mult(*Pt, *S);
|
||||
delete S;
|
||||
if (S_e)
|
||||
{
|
||||
BlockMatrix *PtAe = mfem::Mult(*Pt, *S_e);
|
||||
delete S_e;
|
||||
S_e = PtAe;
|
||||
}
|
||||
delete Pt;
|
||||
S = mfem::Mult(*PtA, *P);
|
||||
delete PtA;
|
||||
|
||||
if (S_e)
|
||||
{
|
||||
BlockMatrix *PtAeP = mfem::Mult(*S_e, *P);
|
||||
S_e = PtAeP;
|
||||
}
|
||||
height = S->Height();
|
||||
width = S->Width();
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::Finalize(int skip_zeros)
|
||||
{
|
||||
if (S) { S->Finalize(skip_zeros); }
|
||||
if (S_e) { S_e->Finalize(skip_zeros); }
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::FormSystemMatrix(Operator::DiagonalPolicy
|
||||
diag_policy)
|
||||
{
|
||||
if (parallel)
|
||||
{
|
||||
FillEssTdofLists(ess_rtdof_list);
|
||||
if (S)
|
||||
{
|
||||
const int remove_zeros = 0;
|
||||
Finalize(remove_zeros);
|
||||
ParallelAssemble(S);
|
||||
delete S;
|
||||
S=nullptr;
|
||||
delete S_e;
|
||||
S_e = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!S_e)
|
||||
{
|
||||
bool conforming = true;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
const SparseMatrix *P_ = tr_fes[i]->GetConformingProlongation();
|
||||
if (P_)
|
||||
{
|
||||
conforming = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!conforming) { ConformingAssemble(0); }
|
||||
const int remove_zeros = 0;
|
||||
EliminateReducedTrueDofs(ess_rtdof_list, diag_policy);
|
||||
Finalize(remove_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::ConvertMarkerToReducedTrueDofs(
|
||||
Array<int> & tdof_marker,
|
||||
Array<int> & rtdof_marker)
|
||||
{
|
||||
// convert tdof_marker to dof_marker
|
||||
rtdof_marker.SetSize(0);
|
||||
Array<int> tdof_marker0;
|
||||
Array<int> dof_marker0;
|
||||
Array<int> dof_marker;
|
||||
int * data = tdof_marker.GetData();
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
tdof_marker0.MakeRef(&data[tdof_offsets[i]],tdof_offsets[i+1]-tdof_offsets[i]);
|
||||
const SparseMatrix * R = fes[i]->GetRestrictionMatrix();
|
||||
if (!R)
|
||||
{
|
||||
dof_marker0.MakeRef(tdof_marker0);
|
||||
}
|
||||
else
|
||||
{
|
||||
dof_marker0.SetSize(fes[i]->GetVSize());
|
||||
R->BooleanMultTranspose(tdof_marker0, dof_marker0);
|
||||
}
|
||||
dof_marker.Append(dof_marker0);
|
||||
}
|
||||
|
||||
int rdofs = rdof_edof.Size();
|
||||
Array<int> rdof_marker(rdofs);
|
||||
|
||||
for (int i = 0; i < rdofs; i++)
|
||||
{
|
||||
rdof_marker[i] = dof_marker[rdof_edof[i]];
|
||||
}
|
||||
|
||||
// convert rdof_marker to rtdof_marker
|
||||
Array<int> rtdof_marker0;
|
||||
Array<int> rdof_marker0;
|
||||
int * rdata = rdof_marker.GetData();
|
||||
int k=0;
|
||||
for (int i = 0; i<nblocks; i++)
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
rdof_marker0.MakeRef(&rdata[rdof_offsets[k]],rdof_offsets[k+1]-rdof_offsets[k]);
|
||||
const SparseMatrix *tr_R = tr_fes[i]->GetRestrictionMatrix();
|
||||
if (!tr_R)
|
||||
{
|
||||
rtdof_marker0.MakeRef(rdof_marker0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtdof_marker0.SetSize(tr_fes[i]->GetTrueVSize());
|
||||
tr_R->BooleanMult(rdof_marker0, rtdof_marker0);
|
||||
}
|
||||
rtdof_marker.Append(rtdof_marker0);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::FillEssTdofLists(const Array<int> & ess_tdof_list)
|
||||
{
|
||||
int j;
|
||||
for (int i = 0; i<ess_tdof_list.Size(); i++)
|
||||
{
|
||||
int tdof = ess_tdof_list[i];
|
||||
for (j = 0; j < rblocks; j++)
|
||||
{
|
||||
if (rtdof_offsets[j+1] > tdof) { break; }
|
||||
}
|
||||
ess_tdofs[j]->Append(tdof-rtdof_offsets[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::SetEssentialTrueDofs(const Array<int>
|
||||
&ess_tdof_list)
|
||||
{
|
||||
Array<int> tdof_marker;
|
||||
Array<int> rtdof_marker;
|
||||
FiniteElementSpace::ListToMarker(ess_tdof_list,tdof_offsets.Last(),tdof_marker);
|
||||
ConvertMarkerToReducedTrueDofs(tdof_marker, rtdof_marker);
|
||||
FiniteElementSpace::MarkerToList(rtdof_marker,ess_rtdof_list);
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::EliminateReducedTrueDofs(const Array<int>
|
||||
&ess_rtdof_list,
|
||||
Matrix::DiagonalPolicy dpolicy)
|
||||
{
|
||||
|
||||
MFEM_VERIFY(!parallel, "EliminateReducedTrueDofs::Wrong Code path");
|
||||
|
||||
if (S_e == NULL)
|
||||
{
|
||||
Array<int> offsets;
|
||||
|
||||
offsets.MakeRef( (P) ? rtdof_offsets : rdof_offsets);
|
||||
|
||||
S_e = new BlockMatrix(offsets);
|
||||
S_e->owns_blocks = 1;
|
||||
for (int i = 0; i<S_e->NumRowBlocks(); i++)
|
||||
{
|
||||
int h = offsets[i+1] - offsets[i];
|
||||
for (int j = 0; j<S_e->NumColBlocks(); j++)
|
||||
{
|
||||
int w = offsets[j+1] - offsets[j];
|
||||
S_e->SetBlock(i,j,new SparseMatrix(h, w));
|
||||
}
|
||||
}
|
||||
}
|
||||
S->EliminateRowCols(ess_rtdof_list,S_e,dpolicy);
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::EliminateReducedTrueDofs(Matrix::DiagonalPolicy
|
||||
dpolicy)
|
||||
{
|
||||
EliminateReducedTrueDofs(ess_rtdof_list, dpolicy);
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::ReduceSolution(const Vector &sol,
|
||||
Vector &sc_sol) const
|
||||
{
|
||||
MFEM_ASSERT(sol.Size() == dof_offsets.Last(), "'sol' has incorrect size");
|
||||
const int nrdofs = rdof_offsets.Last();
|
||||
Vector sol_r;
|
||||
if (!R)
|
||||
{
|
||||
sc_sol.SetSize(nrdofs);
|
||||
sol_r.SetDataAndSize(sc_sol.GetData(), sc_sol.Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
sol_r.SetSize(nrdofs);
|
||||
}
|
||||
for (int i = 0; i < nrdofs; i++)
|
||||
{
|
||||
sol_r(i) = sol(rdof_edof[i]);
|
||||
}
|
||||
if (R)
|
||||
{
|
||||
// wrap vector into a block vector
|
||||
BlockVector blsol_r(sol_r,rdof_offsets);
|
||||
sc_sol.SetSize(R->Height());
|
||||
R->Mult(blsol_r, sc_sol);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockStaticCondensation::ReduceSystem(Vector &x, Vector &X,
|
||||
Vector &B,
|
||||
int copy_interior) const
|
||||
{
|
||||
ReduceSolution(x, X);
|
||||
if (parallel)
|
||||
{
|
||||
B.SetSize(pP->Width());
|
||||
pP->MultTranspose(*y,B);
|
||||
|
||||
Vector tmp(B.Size());
|
||||
pS_e->Mult(X,tmp);
|
||||
B-=tmp;
|
||||
for (int j = 0; j<rblocks; j++)
|
||||
{
|
||||
if (!ess_tdofs[j]->Size()) { continue; }
|
||||
HypreParMatrix *Ah = (HypreParMatrix *)(&pS->GetBlock(j,j));
|
||||
Vector diag;
|
||||
Ah->GetDiag(diag);
|
||||
for (int i = 0; i < ess_tdofs[j]->Size(); i++)
|
||||
{
|
||||
int tdof = (*ess_tdofs[j])[i];
|
||||
int gdof = tdof + rtdof_offsets[j];
|
||||
B(gdof) = diag(tdof)*X(gdof);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!P)
|
||||
{
|
||||
S_e->AddMult(X,*y,-1.);
|
||||
S->PartMult(ess_rtdof_list,X,*y);
|
||||
B.MakeRef(*y, 0, y->Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(*y, B);
|
||||
S_e->AddMult(X,B,-1.);
|
||||
S->PartMult(ess_rtdof_list,X,B);
|
||||
}
|
||||
}
|
||||
if (!copy_interior) { X.SetSubVectorComplement(ess_rtdof_list, 0.0); }
|
||||
}
|
||||
|
||||
|
||||
void BlockStaticCondensation::ComputeSolution(const Vector &sc_sol,
|
||||
Vector &sol) const
|
||||
{
|
||||
|
||||
const int nrdofs = rdof_offsets.Last();
|
||||
const int nrtdofs = rtdof_offsets.Last();
|
||||
MFEM_VERIFY(sc_sol.Size() == nrtdofs, "'sc_sol' has incorrect size");
|
||||
|
||||
Vector sol_r;
|
||||
if (parallel)
|
||||
{
|
||||
sol_r.SetSize(nrdofs);
|
||||
pP->Mult(sc_sol, sol_r);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!P)
|
||||
{
|
||||
sol_r.SetDataAndSize(sc_sol.GetData(), sc_sol.Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
sol_r.SetSize(nrdofs);
|
||||
P->Mult(sc_sol, sol_r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (rdof_offsets.Last() == dof_offsets.Last())
|
||||
{
|
||||
sol = sol_r;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
sol.SetSize(dof_offsets.Last());
|
||||
}
|
||||
|
||||
Vector lsr; // element (local) sc solution vector
|
||||
Vector lsi; // element (local) interior solution vector
|
||||
const int NE = mesh->GetNE();
|
||||
|
||||
Array<int> trace_vdofs;
|
||||
Array<int> vdofs;
|
||||
Array<int> tr_offsets;
|
||||
Vector lsol;
|
||||
for (int iel = 0; iel < NE; iel++)
|
||||
{
|
||||
lsol.SetSize(lmat[iel]->Width() + lmat[iel]->Height());
|
||||
// GetReduceElementIndicesAndOffsets(iel, trace_ldofs, interior_ldofs, tr_offsets);
|
||||
GetReduceElementVDofs(iel, trace_vdofs);
|
||||
|
||||
lsr.SetSize(trace_vdofs.Size());
|
||||
sol_r.GetSubVector(trace_vdofs, lsr);
|
||||
// complete the interior dofs
|
||||
|
||||
lsi.SetSize(lmat[iel]->Height());
|
||||
lmat[iel]->Mult(lsr,lsi);
|
||||
lsi.Neg();
|
||||
lsi+=*lvec[iel];
|
||||
|
||||
Array<int> tr_idx,int_idx,idx_offs;
|
||||
GetReduceElementIndicesAndOffsets(iel,tr_idx, int_idx, idx_offs);
|
||||
lsol.SetSubVector(tr_idx,lsr);
|
||||
|
||||
lsol.SetSubVector(int_idx,lsi);
|
||||
|
||||
GetElementVDofs(iel, vdofs);
|
||||
sol.SetSubVector(vdofs,lsol);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BlockStaticCondensation::~BlockStaticCondensation()
|
||||
{
|
||||
delete S_e; S_e = nullptr;
|
||||
delete S; S=nullptr;
|
||||
delete y; y=nullptr;
|
||||
|
||||
if (P) { delete P; } P=nullptr;
|
||||
if (R) { delete R; } R=nullptr;
|
||||
|
||||
if (parallel)
|
||||
{
|
||||
delete pS; pS=nullptr;
|
||||
delete pS_e; pS_e=nullptr;
|
||||
for (int i = 0; i<rblocks; i++)
|
||||
{
|
||||
delete ess_tdofs[i];
|
||||
}
|
||||
delete pP; pP=nullptr;
|
||||
}
|
||||
|
||||
for (int i=0; i<lmat.Size(); i++)
|
||||
{
|
||||
delete lmat[i]; lmat[i] = nullptr;
|
||||
delete lvec[i]; lvec[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BLOCK_STATIC_CONDENSATION
|
||||
#define MFEM_BLOCK_STATIC_CONDENSATION
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
class BlockStaticCondensation
|
||||
{
|
||||
int height, width;
|
||||
int nblocks; // original number of blocks
|
||||
int rblocks; // reduces number of blocks
|
||||
Mesh * mesh = nullptr;
|
||||
bool parallel = false;
|
||||
// original set of Finite Element Spaces
|
||||
Array<FiniteElementSpace *> fes;
|
||||
// indicates if the original space is already a trace space
|
||||
Array<bool> IsTraceSpace;
|
||||
|
||||
// New set of "reduced" Finite Element Spaces
|
||||
// (after static condensation)
|
||||
Array<FiniteElementSpace *> tr_fes;
|
||||
|
||||
Array<int> dof_offsets;
|
||||
Array<int> tdof_offsets;
|
||||
|
||||
Array<int> rdof_offsets;
|
||||
Array<int> rtdof_offsets;
|
||||
|
||||
// Schur complement matrix
|
||||
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
|
||||
BlockMatrix * S = nullptr;
|
||||
BlockMatrix * S_e = nullptr;
|
||||
|
||||
BlockVector * y = nullptr;
|
||||
|
||||
Array<DenseMatrix * > lmat;
|
||||
Array<Vector * > lvec;
|
||||
|
||||
Array<int> rdof_edof; // Map from reduced dofs to exposed dofs
|
||||
Array<int> ess_rtdof_list;
|
||||
|
||||
BlockMatrix * P = nullptr; // Block Prolongation
|
||||
BlockMatrix * R = nullptr; // Block Restriction
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
BlockOperator * pS = nullptr;
|
||||
BlockOperator * pS_e = nullptr;
|
||||
// Block HypreParMatrix for Prolongation
|
||||
BlockOperator * pP = nullptr;
|
||||
#endif
|
||||
|
||||
bool Parallel() const { return parallel; }
|
||||
|
||||
|
||||
// tr_idx (trace dofs indices)
|
||||
// int_idx (interior dof indices)
|
||||
void GetReduceElementIndicesAndOffsets(int el, Array<int> & tr_idx,
|
||||
Array<int> & int_idx,
|
||||
Array<int> & offsets) const;
|
||||
|
||||
void GetReduceElementVDofs(int el, Array<int> & rdofs) const;
|
||||
void GetElementVDofs(int el, Array<int> & vdofs) const;
|
||||
|
||||
|
||||
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
|
||||
// y = y_i - A_ib (A_bb)^{-1} y_b
|
||||
void GetLocalShurComplement(int el, const Array<int> & tr_idx,
|
||||
const Array<int> & int_idx,
|
||||
const DenseMatrix & elmat, const Vector & elvect,
|
||||
DenseMatrix & rmat, Vector & rvect);
|
||||
|
||||
void ComputeOffsets();
|
||||
|
||||
void BuildProlongation();
|
||||
#ifdef MFEM_USE_MPI
|
||||
void BuildParallelProlongation();
|
||||
#endif
|
||||
|
||||
// ess_tdof list for each space
|
||||
Array<Array<int> *> ess_tdofs;
|
||||
void FillEssTdofLists(const Array<int> & ess_tdof_list);
|
||||
|
||||
void ConformingAssemble(int skip_zeros);
|
||||
|
||||
/** Restrict a marker Array on the true FE spaces dofs to a marker Array on
|
||||
the reduced/trace true FE spaces dofs. */
|
||||
void ConvertMarkerToReducedTrueDofs(Array<int> & tdof_marker,
|
||||
Array<int> & rtdof_marker);
|
||||
public:
|
||||
|
||||
BlockStaticCondensation(Array<FiniteElementSpace *> & fes_);
|
||||
|
||||
~BlockStaticCondensation();
|
||||
|
||||
void SetSpaces(Array<FiniteElementSpace*> & fes_);
|
||||
|
||||
void Init();
|
||||
|
||||
/** Assemble the contribution to the Schur complement from the given
|
||||
element matrix 'elmat'; save the other blocks internally: A_bb_inv, A_bi,
|
||||
and A_bi. */
|
||||
|
||||
void AssembleReducedSystem(int el, DenseMatrix &elmat,
|
||||
Vector & elvect);
|
||||
|
||||
/// Finalize the construction of the Schur complement matrix.
|
||||
void Finalize(int skip_zeros = 0);
|
||||
|
||||
/// Determine and save internally essential reduced true dofs.
|
||||
void SetEssentialTrueDofs(const Array<int> &ess_tdof_list);
|
||||
|
||||
/// Eliminate the given reduced true dofs from the Schur complement matrix S.
|
||||
void EliminateReducedTrueDofs(const Array<int> &ess_rtdof_list,
|
||||
Matrix::DiagonalPolicy dpolicy);
|
||||
|
||||
void EliminateReducedTrueDofs(Matrix::DiagonalPolicy dpolicy);
|
||||
|
||||
bool HasEliminatedBC() const
|
||||
{
|
||||
#ifndef MFEM_USE_MPI
|
||||
return S_e;
|
||||
#else
|
||||
return S_e || pS_e;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// Return the serial Schur complement matrix.
|
||||
BlockMatrix &GetMatrix() { return *S; }
|
||||
|
||||
/// Return the eliminated part of the serial Schur complement matrix.
|
||||
BlockMatrix &GetMatrixElim() { return *S_e; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Return the parallel Schur complement matrix.
|
||||
BlockOperator &GetParallelMatrix() { return *pS; }
|
||||
|
||||
/// Return the eliminated part of the parallel Schur complement matrix.
|
||||
BlockOperator &GetParallelMatrixElim() { return *pS_e; }
|
||||
|
||||
void ParallelAssemble(BlockMatrix *m);
|
||||
#endif
|
||||
|
||||
void FormSystemMatrix(Operator::DiagonalPolicy diag_policy);
|
||||
|
||||
/** Restrict a solution vector on the full FE space dofs to a vector on the
|
||||
reduced/trace true FE space dofs. */
|
||||
void ReduceSolution(const Vector &sol, Vector &sc_sol) const;
|
||||
|
||||
/** @brief Set the reduced solution `X` and r.h.s `B` vectors from the full
|
||||
linear system solution `x` and r.h.s. `b` vectors.
|
||||
|
||||
This method should be called after the internal reduced essential dofs
|
||||
have been set using SetEssentialTrueDofs() and both the Schur complement
|
||||
and its eliminated part have been finalized. */
|
||||
void ReduceSystem(Vector &x, Vector &X, Vector &B,
|
||||
int copy_interior = 0) const;
|
||||
|
||||
/** Restrict a list of true FE space dofs to a list of reduced/trace true FE
|
||||
space dofs. */
|
||||
void ConvertListToReducedTrueDofs(const Array<int> &ess_tdof_list,
|
||||
Array<int> &ess_rtdof_list) const;
|
||||
|
||||
/** Given a solution of the reduced system 'sc_sol' and the RHS 'b' for the
|
||||
full linear system, compute the solution of the full system 'sol'. */
|
||||
void ComputeSolution(const Vector &sc_sol, Vector &sol) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -62,6 +62,20 @@ PAConvectionIntegrator::PAConvectionIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPAConvectionIntegrator::MixedPAConvectionIntegrator(
|
||||
const ConvectionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
ConvectionOperatorInfo info(fes.GetMesh()->Dimension(), alpha);
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MFConvectionIntegrator::MFConvectionIntegrator(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
@@ -77,6 +91,20 @@ MFConvectionIntegrator::MFConvectionIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFConvectionIntegrator::MixedMFConvectionIntegrator(
|
||||
const ConvectionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
ConvectionOperatorInfo info(fes.GetMesh()->Dimension(), alpha);
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_LIBCEED_CONV_HPP
|
||||
|
||||
#include "../../interface/integrator.hpp"
|
||||
#include "../../interface/mixed_integrator.hpp"
|
||||
#include "../../../fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -26,21 +27,39 @@ class PAConvectionIntegrator : public PAIntegrator
|
||||
{
|
||||
public:
|
||||
PAConvectionIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha);
|
||||
};
|
||||
|
||||
class MixedPAConvectionIntegrator : public MixedIntegrator<PAIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedPAConvectionIntegrator(const ConvectionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha);
|
||||
};
|
||||
|
||||
/// Represent a ConvectionIntegrator with AssemblyLevel::None using libCEED.
|
||||
class MFConvectionIntegrator : public MFIntegrator
|
||||
{
|
||||
public:
|
||||
MFConvectionIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha);
|
||||
};
|
||||
|
||||
class MixedMFConvectionIntegrator : public MixedIntegrator<MFIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedMFConvectionIntegrator(const ConvectionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::VectorCoefficient *Q,
|
||||
const double alpha);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,32 @@ PADiffusionIntegrator::PADiffusionIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPADiffusionIntegrator::MixedPADiffusionIntegrator(
|
||||
const DiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
DiffusionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPADiffusionIntegrator::MixedPADiffusionIntegrator(
|
||||
const VectorDiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
DiffusionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MFDiffusionIntegrator::MFDiffusionIntegrator(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
@@ -74,6 +100,32 @@ MFDiffusionIntegrator::MFDiffusionIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFDiffusionIntegrator::MixedMFDiffusionIntegrator(
|
||||
const DiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
DiffusionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFDiffusionIntegrator::MixedMFDiffusionIntegrator(
|
||||
const VectorDiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
DiffusionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_LIBCEED_DIFF_HPP
|
||||
|
||||
#include "../../interface/integrator.hpp"
|
||||
#include "../../interface/mixed_integrator.hpp"
|
||||
#include "../../../fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -26,19 +27,43 @@ class PADiffusionIntegrator : public PAIntegrator
|
||||
{
|
||||
public:
|
||||
PADiffusionIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
class MixedPADiffusionIntegrator : public MixedIntegrator<PAIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedPADiffusionIntegrator(const DiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
|
||||
MixedPADiffusionIntegrator(const VectorDiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
/// Represent a DiffusionIntegrator with AssemblyLevel::None using libCEED.
|
||||
class MFDiffusionIntegrator : public MFIntegrator
|
||||
{
|
||||
public:
|
||||
MFDiffusionIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
class MixedMFDiffusionIntegrator : public MixedIntegrator<MFIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedMFDiffusionIntegrator(const DiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
|
||||
MixedMFDiffusionIntegrator(const VectorDiffusionIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,30 @@ PAMassIntegrator::PAMassIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPAMassIntegrator::MixedPAMassIntegrator(const MassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
MassOperatorInfo info;
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPAMassIntegrator::MixedPAMassIntegrator(const VectorMassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
MassOperatorInfo info;
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MFMassIntegrator::MFMassIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
mfem::Coefficient *Q)
|
||||
@@ -72,6 +96,30 @@ MFMassIntegrator::MFMassIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFMassIntegrator::MixedMFMassIntegrator(const MassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
MassOperatorInfo info;
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFMassIntegrator::MixedMFMassIntegrator(const VectorMassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
MassOperatorInfo info;
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_LIBCEED_MASS_HPP
|
||||
|
||||
#include "../../interface/integrator.hpp"
|
||||
#include "../../interface/mixed_integrator.hpp"
|
||||
#include "../../../fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -26,19 +27,43 @@ class PAMassIntegrator : public PAIntegrator
|
||||
{
|
||||
public:
|
||||
PAMassIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
class MixedPAMassIntegrator : public MixedIntegrator<PAIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedPAMassIntegrator(const MassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
|
||||
MixedPAMassIntegrator(const VectorMassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
/// Represent a MassIntegrator with AssemblyLevel::None using libCEED.
|
||||
class MFMassIntegrator : public MFIntegrator
|
||||
{
|
||||
public:
|
||||
MFMassIntegrator(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
class MixedMFMassIntegrator : public MixedIntegrator<MFIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedMFMassIntegrator(const MassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
|
||||
MixedMFMassIntegrator(const VectorMassIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,19 @@ PAVectorConvectionNLFIntegrator::PAVectorConvectionNLFIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedPAVectorConvectionNLIntegrator::MixedPAVectorConvectionNLIntegrator(
|
||||
const VectorConvectionNLFIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
NLConvectionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
MFVectorConvectionNLFIntegrator::MFVectorConvectionNLFIntegrator(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
@@ -74,6 +87,19 @@ MFVectorConvectionNLFIntegrator::MFVectorConvectionNLFIntegrator(
|
||||
#endif
|
||||
}
|
||||
|
||||
MixedMFVectorConvectionNLIntegrator::MixedMFVectorConvectionNLIntegrator(
|
||||
const VectorConvectionNLFIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q)
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
NLConvectionOperatorInfo info(fes.GetMesh()->Dimension());
|
||||
Assemble(integ, info, fes, Q);
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_LIBCEED_NLCONV_HPP
|
||||
|
||||
#include "../../interface/integrator.hpp"
|
||||
#include "../../interface/mixed_integrator.hpp"
|
||||
#include "../../../fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -31,6 +32,15 @@ public:
|
||||
mfem::Coefficient *coeff);
|
||||
};
|
||||
|
||||
class MixedPAVectorConvectionNLIntegrator : public MixedIntegrator<PAIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedPAVectorConvectionNLIntegrator(
|
||||
const VectorConvectionNLFIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
/** Represent a VectorConvectionNLFIntegrator with AssemblyLevel::None
|
||||
using libCEED. */
|
||||
class MFVectorConvectionNLFIntegrator : public MFIntegrator
|
||||
@@ -41,6 +51,15 @@ public:
|
||||
mfem::Coefficient *coeff);
|
||||
};
|
||||
|
||||
class MixedMFVectorConvectionNLIntegrator : public MixedIntegrator<MFIntegrator>
|
||||
{
|
||||
public:
|
||||
MixedMFVectorConvectionNLIntegrator(
|
||||
const VectorConvectionNLFIntegrator &integ,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
mfem::Coefficient *Q);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -327,13 +327,13 @@ CEED_QFUNCTION(f_apply_conv_mf_const)(void *ctx, CeedInt Q,
|
||||
const CeedScalar A33 = J11 * J22 - J12 * J21;
|
||||
const CeedScalar w = qw[i] * coeff;
|
||||
const CeedScalar qd00 = w * A11;
|
||||
const CeedScalar qd01 = w * A21;
|
||||
const CeedScalar qd02 = w * A31;
|
||||
const CeedScalar qd10 = w * A12;
|
||||
const CeedScalar qd10 = w * A21;
|
||||
const CeedScalar qd20 = w * A31;
|
||||
const CeedScalar qd01 = w * A12;
|
||||
const CeedScalar qd11 = w * A22;
|
||||
const CeedScalar qd12 = w * A32;
|
||||
const CeedScalar qd20 = w * A13;
|
||||
const CeedScalar qd21 = w * A23;
|
||||
const CeedScalar qd21 = w * A32;
|
||||
const CeedScalar qd02 = w * A13;
|
||||
const CeedScalar qd12 = w * A23;
|
||||
const CeedScalar qd22 = w * A33;
|
||||
const CeedScalar u0 = u[i + Q * 0];
|
||||
const CeedScalar u1 = u[i + Q * 1];
|
||||
@@ -440,13 +440,13 @@ CEED_QFUNCTION(f_apply_conv_mf_quad)(void *ctx, CeedInt Q,
|
||||
const CeedScalar A33 = J11 * J22 - J12 * J21;
|
||||
const CeedScalar w = qw[i] * c[i];
|
||||
const CeedScalar qd00 = w * A11;
|
||||
const CeedScalar qd01 = w * A21;
|
||||
const CeedScalar qd02 = w * A31;
|
||||
const CeedScalar qd10 = w * A12;
|
||||
const CeedScalar qd10 = w * A21;
|
||||
const CeedScalar qd20 = w * A31;
|
||||
const CeedScalar qd01 = w * A12;
|
||||
const CeedScalar qd11 = w * A22;
|
||||
const CeedScalar qd12 = w * A32;
|
||||
const CeedScalar qd20 = w * A13;
|
||||
const CeedScalar qd21 = w * A23;
|
||||
const CeedScalar qd21 = w * A32;
|
||||
const CeedScalar qd02 = w * A13;
|
||||
const CeedScalar qd12 = w * A23;
|
||||
const CeedScalar qd22 = w * A33;
|
||||
const CeedScalar u0 = u[i + Q * 0];
|
||||
const CeedScalar u1 = u[i + Q * 1];
|
||||
|
||||
@@ -36,6 +36,8 @@ static CeedElemTopology GetCeedTopology(Geometry::Type geom)
|
||||
return CEED_TOPOLOGY_HEX;
|
||||
case Geometry::PRISM:
|
||||
return CEED_TOPOLOGY_PRISM;
|
||||
case Geometry::PYRAMID:
|
||||
return CEED_TOPOLOGY_PYRAMID;
|
||||
default:
|
||||
MFEM_ABORT("This type of element is not supported");
|
||||
return CEED_TOPOLOGY_PRISM; // Silence warning
|
||||
@@ -43,11 +45,11 @@ static CeedElemTopology GetCeedTopology(Geometry::Type geom)
|
||||
}
|
||||
|
||||
static void InitNonTensorBasis(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::FiniteElement &fe,
|
||||
const mfem::IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
{
|
||||
const mfem::DofToQuad &maps = fes.GetFE(0)->
|
||||
GetDofToQuad(ir,mfem::DofToQuad::FULL);
|
||||
const mfem::DofToQuad &maps = fe.GetDofToQuad(ir, mfem::DofToQuad::FULL);
|
||||
mfem::Mesh *mesh = fes.GetMesh();
|
||||
const int dim = mesh->Dimension();
|
||||
const int ndofs = maps.ndof;
|
||||
@@ -62,18 +64,18 @@ static void InitNonTensorBasis(const mfem::FiniteElementSpace &fes,
|
||||
if (dim>2) { qX(2,i) = ip.z; }
|
||||
qW(i) = ip.weight;
|
||||
}
|
||||
CeedBasisCreateH1(ceed, GetCeedTopology(fes.GetFE(0)->GetGeomType()),
|
||||
CeedBasisCreateH1(ceed, GetCeedTopology(fe.GetGeomType()),
|
||||
fes.GetVDim(), ndofs, nqpts,
|
||||
maps.Bt.GetData(), maps.Gt.GetData(),
|
||||
qX.GetData(), qW.GetData(), basis);
|
||||
}
|
||||
|
||||
static void InitTensorBasis(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::FiniteElement &fe,
|
||||
const mfem::IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
{
|
||||
const mfem::DofToQuad &maps =
|
||||
fes.GetFE(0)->GetDofToQuad(ir, mfem::DofToQuad::TENSOR);
|
||||
const mfem::DofToQuad &maps = fe.GetDofToQuad(ir, mfem::DofToQuad::TENSOR);
|
||||
mfem::Mesh *mesh = fes.GetMesh();
|
||||
const int ndofs = maps.ndof;
|
||||
const int nqpts = maps.nqpt;
|
||||
@@ -96,28 +98,30 @@ static void InitTensorBasis(const mfem::FiniteElementSpace &fes,
|
||||
qW.GetData(), basis);
|
||||
}
|
||||
|
||||
void InitBasis(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &irm,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
static void InitBasisImpl(const FiniteElementSpace &fes,
|
||||
const FiniteElement &fe,
|
||||
const IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
{
|
||||
// Check for FES -> basis, restriction in hash tables
|
||||
const mfem::FiniteElement *fe = fes.GetFE(0);
|
||||
const int P = fe->GetDof();
|
||||
const int Q = irm.GetNPoints();
|
||||
const int P = fe.GetDof();
|
||||
const int Q = ir.GetNPoints();
|
||||
const int ncomp = fes.GetVDim();
|
||||
BasisKey basis_key(&fes, &irm, ncomp, P, Q);
|
||||
BasisKey basis_key(&fes, &ir, ncomp, P, Q);
|
||||
auto basis_itr = mfem::internal::ceed_basis_map.find(basis_key);
|
||||
const bool tensor = dynamic_cast<const mfem::TensorBasisElement *>
|
||||
(&fe) != nullptr;
|
||||
|
||||
// Init or retreive key values
|
||||
if (basis_itr == mfem::internal::ceed_basis_map.end())
|
||||
{
|
||||
if (UsesTensorBasis(fes))
|
||||
if ( tensor )
|
||||
{
|
||||
InitTensorBasis(fes, irm, ceed, basis);
|
||||
InitTensorBasis(fes, fe, ir, ceed, basis);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitNonTensorBasis(fes, irm, ceed, basis);
|
||||
InitNonTensorBasis(fes, fe, ir, ceed, basis);
|
||||
}
|
||||
mfem::internal::ceed_basis_map[basis_key] = *basis;
|
||||
}
|
||||
@@ -127,6 +131,24 @@ void InitBasis(const FiniteElementSpace &fes,
|
||||
}
|
||||
}
|
||||
|
||||
void InitBasis(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
{
|
||||
const mfem::FiniteElement &fe = *fes.GetFE(0);
|
||||
InitBasisImpl(fes, fe, ir, ceed, basis);
|
||||
}
|
||||
|
||||
void InitBasisWithIndices(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedBasis *basis)
|
||||
{
|
||||
const mfem::FiniteElement &fe = *fes.GetFE(indices[0]);
|
||||
InitBasisImpl(fes, fe, ir, ceed, basis);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
@@ -22,17 +22,32 @@ namespace ceed
|
||||
|
||||
#ifdef MFEM_USE_CEED
|
||||
|
||||
/** @brief Initialize a CeedBasis.
|
||||
/** @brief Initialize a CeedBasis for non-mixed meshes.
|
||||
|
||||
@param[in] fes Input finite element space.
|
||||
@param[in] irm Input integration rule.
|
||||
@param[in] ir Input integration rule.
|
||||
@param[in] ceed Input Ceed object.
|
||||
@param[out] basis The address of the initialized CeedBasis object.
|
||||
*/
|
||||
void InitBasis(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &irm,
|
||||
const IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis);
|
||||
|
||||
/** @brief Initialize a CeedBasis for mixed meshes.
|
||||
|
||||
@param[in] fes The finite element space.
|
||||
@param[in] ir is the integration rule for the operator.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`.
|
||||
@param[in] ceed The Ceed object.
|
||||
@param[out] basis The `CeedBasis` to initialize. */
|
||||
void InitBasisWithIndices(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedBasis *basis);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#ifdef MFEM_USE_CEED
|
||||
|
||||
#include "../../../general/forall.hpp"
|
||||
#include "../../../config/config.hpp"
|
||||
#include "../../../linalg/vector.hpp"
|
||||
#include "../../../linalg/dtensor.hpp"
|
||||
@@ -77,7 +78,14 @@ struct QuadCoefficient : VariableCoefficient
|
||||
|
||||
/** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an
|
||||
mfem::Coefficient @a Q, an mfem::Mesh @a mesh, and an mfem::IntegrationRule
|
||||
@a ir. */
|
||||
@a ir.
|
||||
|
||||
@param[in] Q is the coefficient from the `Integrator`.
|
||||
@param[in] mesh is the mesh.
|
||||
@param[in] ir is the integration rule.
|
||||
@param[out] coeff_ptr is the structure to store the coefficient for the
|
||||
`CeedOperator`.
|
||||
@param[out] ctx is the Context associated to the QFunction. */
|
||||
template <typename Context>
|
||||
void InitCoefficient(mfem::Coefficient *Q, mfem::Mesh &mesh,
|
||||
const mfem::IntegrationRule &ir,
|
||||
@@ -143,8 +151,15 @@ void InitCoefficient(mfem::Coefficient *Q, mfem::Mesh &mesh,
|
||||
|
||||
|
||||
/** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an
|
||||
mfem::VectorCoefficient @a Q, an mfem::Mesh @a mesh, and an
|
||||
mfem::IntegrationRule @a ir. */
|
||||
mfem::VectorCoefficient @a VQ, an mfem::Mesh @a mesh, and an
|
||||
mfem::IntegrationRule @a ir.
|
||||
|
||||
@param[in] VQ is the vector coefficient from the `Integrator`.
|
||||
@param[in] mesh is the mesh.
|
||||
@param[in] ir is the integration rule.
|
||||
@param[out] coeff_ptr is the structure to store the coefficient for the
|
||||
`CeedOperator`.
|
||||
@param[out] ctx is the Context associated to the QFunction. */
|
||||
template <typename Context>
|
||||
void InitCoefficient(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh,
|
||||
const mfem::IntegrationRule &ir,
|
||||
@@ -214,6 +229,209 @@ void InitCoefficient(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an
|
||||
mfem::Coefficient @a Q, an mfem::Mesh @a mesh, and an mfem::IntegrationRule
|
||||
@a ir for the elements given by the indices @a indices.
|
||||
|
||||
@param[in] Q is the coefficient from the `Integrator`.
|
||||
@param[in] mesh is the mesh.
|
||||
@param[in] ir is the integration rule.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`.
|
||||
@param[out] coeff_ptr is the structure to store the coefficient for the
|
||||
`CeedOperator`.
|
||||
@param[out] ctx is the Context associated to the QFunction. */
|
||||
template <typename Context>
|
||||
void InitCoefficientWithIndices(mfem::Coefficient *Q, mfem::Mesh &mesh,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Coefficient*& coeff_ptr, Context &ctx)
|
||||
{
|
||||
if ( Q == nullptr )
|
||||
{
|
||||
Coefficient *ceedCoeff = new Coefficient(1);
|
||||
ctx.coeff = 1.0;
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else if (ConstantCoefficient *const_coeff =
|
||||
dynamic_cast<ConstantCoefficient*>(Q))
|
||||
{
|
||||
Coefficient *ceedCoeff = new Coefficient(1);
|
||||
ctx.coeff = const_coeff->constant;
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else if (GridFunctionCoefficient* gf_coeff =
|
||||
dynamic_cast<GridFunctionCoefficient*>(Q))
|
||||
{
|
||||
GridCoefficient *ceedCoeff =
|
||||
new GridCoefficient(*gf_coeff->GetGridFunction());
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else if (QuadratureFunctionCoefficient *cQ =
|
||||
dynamic_cast<QuadratureFunctionCoefficient*>(Q))
|
||||
{
|
||||
QuadCoefficient *ceedCoeff = new QuadCoefficient(1);
|
||||
const int ne = mesh.GetNE();
|
||||
const int nq = ir.GetNPoints();
|
||||
const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction();
|
||||
MFEM_VERIFY(qFun.Size() == nq * ne,
|
||||
"Incompatible QuadratureFunction dimension \n");
|
||||
|
||||
MFEM_VERIFY(&ir == &qFun.GetSpace()->GetElementIntRule(0),
|
||||
"IntegrationRule used within integrator and in"
|
||||
" QuadratureFunction appear to be different");
|
||||
ceedCoeff->coeff.SetSize(nq * nelem);
|
||||
Memory<int> m_indices((int*)indices, nelem, false);
|
||||
auto in = Reshape(qFun.Read(), nq, ne);
|
||||
auto d_indices = Read(m_indices, nelem);
|
||||
auto out = Reshape(ceedCoeff->coeff.Write(), nq, nelem);
|
||||
MFEM_FORALL(i, nelem * nq,
|
||||
{
|
||||
const int q = i%nq;
|
||||
const int sub_e = i/nq;
|
||||
const int e = d_indices[sub_e];
|
||||
out(q, sub_e) = in(q, e);
|
||||
});
|
||||
m_indices.DeleteDevice();
|
||||
InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector);
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else
|
||||
{
|
||||
QuadCoefficient *ceedCoeff = new QuadCoefficient(1);
|
||||
const int nq = ir.GetNPoints();
|
||||
ceedCoeff->coeff.SetSize(nq * nelem);
|
||||
auto C = Reshape(ceedCoeff->coeff.HostWrite(), nq, nelem);
|
||||
for (int i = 0; i < nelem; ++i)
|
||||
{
|
||||
const int e = indices[i];
|
||||
mfem::ElementTransformation &T = *mesh.GetElementTransformation(e);
|
||||
for (int q = 0; q < nq; ++q)
|
||||
{
|
||||
C(q, i) = Q->Eval(T, ir.IntPoint(q));
|
||||
}
|
||||
}
|
||||
InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector);
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an
|
||||
mfem::VectorCoefficient @a Q, an mfem::Mesh @a mesh, and an
|
||||
mfem::IntegrationRule @a ir for the elements given by the indices @a indices.
|
||||
|
||||
@param[in] VQ is the vector coefficient from the `Integrator`.
|
||||
@param[in] mesh is the mesh.
|
||||
@param[in] ir is the integration rule.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`.
|
||||
@param[out] coeff_ptr is the structure to store the coefficient for the
|
||||
`CeedOperator`.
|
||||
@param[out] ctx is the Context associated to the QFunction. */
|
||||
template <typename Context>
|
||||
void InitCoefficientWithIndices(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Coefficient *&coeff_ptr, Context &ctx)
|
||||
{
|
||||
if (VectorConstantCoefficient *const_coeff =
|
||||
dynamic_cast<VectorConstantCoefficient*>(VQ))
|
||||
{
|
||||
const int vdim = const_coeff->GetVDim();
|
||||
const mfem::Vector &val = const_coeff->GetVec();
|
||||
Coefficient *ceedCoeff = new Coefficient(vdim);
|
||||
for (int i = 0; i < vdim; i++)
|
||||
{
|
||||
ctx.coeff[i] = val[i];
|
||||
}
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else if (VectorGridFunctionCoefficient* vgf_coeff =
|
||||
dynamic_cast<VectorGridFunctionCoefficient*>(VQ))
|
||||
{
|
||||
GridCoefficient *ceedCoeff =
|
||||
new GridCoefficient(*vgf_coeff->GetGridFunction());
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else if (VectorQuadratureFunctionCoefficient *cQ =
|
||||
dynamic_cast<VectorQuadratureFunctionCoefficient*>(VQ))
|
||||
{
|
||||
QuadCoefficient *ceedCoeff = new QuadCoefficient(cQ->GetVDim());
|
||||
const int dim = mesh.Dimension();
|
||||
const int ne = mesh.GetNE();
|
||||
const int nq = ir.GetNPoints();
|
||||
const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction();
|
||||
MFEM_VERIFY(qFun.Size() == dim * nq * ne,
|
||||
"Incompatible QuadratureFunction dimension \n");
|
||||
|
||||
MFEM_VERIFY(&ir == &qFun.GetSpace()->GetElementIntRule(0),
|
||||
"IntegrationRule used within integrator and in"
|
||||
" QuadratureFunction appear to be different");
|
||||
ceedCoeff->coeff.SetSize(dim * nq * nelem);
|
||||
Memory<int> m_indices((int*)indices, nelem, false);
|
||||
auto in = Reshape(qFun.Read(), dim, nq, ne);
|
||||
auto d_indices = Read(m_indices, nelem);
|
||||
auto out = Reshape(ceedCoeff->coeff.Write(), dim, nq, nelem);
|
||||
MFEM_FORALL(i, nelem * nq,
|
||||
{
|
||||
const int q = i%nq;
|
||||
const int sub_e = i/nq;
|
||||
const int e = d_indices[sub_e];
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
out(d, q, sub_e) = in(d, q, e);
|
||||
}
|
||||
});
|
||||
m_indices.DeleteDevice();
|
||||
InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector);
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int dim = mesh.Dimension();
|
||||
QuadCoefficient *ceedCoeff = new QuadCoefficient(dim);
|
||||
const int nq = ir.GetNPoints();
|
||||
ceedCoeff->coeff.SetSize(dim * nq * nelem);
|
||||
auto C = Reshape(ceedCoeff->coeff.HostWrite(), dim, nq, nelem);
|
||||
mfem::DenseMatrix Q_ir;
|
||||
for (int i = 0; i < nelem; ++i)
|
||||
{
|
||||
const int e = indices[i];
|
||||
mfem::ElementTransformation &T = *mesh.GetElementTransformation(e);
|
||||
VQ->Eval(Q_ir, T, ir);
|
||||
for (int q = 0; q < nq; ++q)
|
||||
{
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
C(d, q, i) = Q_ir(d, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector);
|
||||
coeff_ptr = ceedCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Coeff, typename Context>
|
||||
void InitCoefficient(Coeff *Q, mfem::Mesh &mesh,
|
||||
const mfem::IntegrationRule &ir, int nelem,
|
||||
const int* indices, Coefficient *&coeff_ptr, Context &ctx)
|
||||
{
|
||||
if (indices)
|
||||
{
|
||||
InitCoefficientWithIndices(Q, mesh, ir, nelem, indices, coeff_ptr, ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitCoefficient(Q, mesh, ir, coeff_ptr, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "operator.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "restriction.hpp"
|
||||
#include "util.hpp"
|
||||
#include "ceed.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -86,6 +87,7 @@ protected:
|
||||
CeedQFunctionContext build_ctx;
|
||||
CeedOperator build_oper;
|
||||
|
||||
public:
|
||||
PAIntegrator()
|
||||
: Operator(),
|
||||
trial_basis(nullptr), test_basis(nullptr), mesh_basis(nullptr),
|
||||
@@ -95,23 +97,51 @@ protected:
|
||||
qdata(nullptr), coeff(nullptr), build_ctx(nullptr), build_oper(nullptr)
|
||||
{ }
|
||||
|
||||
public:
|
||||
/** This method assembles the PAIntegrator.
|
||||
/** @brief This method assembles the `PAIntegrator` with the given
|
||||
`CeedOperatorInfo` @a info, an `mfem::FiniteElementSpace` @a fes, an
|
||||
`mfem::IntegrationRule` @a ir, and `mfem::Coefficient` or
|
||||
`mfem::VectorCoefficient` @a Q.
|
||||
The `CeedOperatorInfo` type is expected to inherit from `OperatorInfo`,
|
||||
and contain a `Context` type relevant to the qFunctions.
|
||||
|
||||
@param[in] info the `CeedOperatorInfo` describing the `CeedOperator`,
|
||||
the `CeedOperatorInfo` type is expected to inherit from
|
||||
`OperatorInfo` and contain a `Context` type relevant to
|
||||
the qFunctions.
|
||||
@param[in] fes the `FiniteElementSpace` for the form,
|
||||
@param[in] ir the `IntegrationRule` for the numerical integration,
|
||||
@param[in] Q `Coefficient` or `VectorCoefficient`. */
|
||||
@param[in] info is the structure describing the CeedOperator to assemble.
|
||||
@param[in] fes is the finite element space.
|
||||
@param[in] ir is the integration rule for the operator.
|
||||
@param[in] Q is the coefficient from the `Integrator`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, fes, fes, irm, Q);
|
||||
Assemble(info, fes, ir, fes.GetNE(), nullptr, Q);
|
||||
}
|
||||
|
||||
/** @brief This method assembles the `PAIntegrator` with the given
|
||||
`CeedOperatorInfo` @a info, an `mfem::FiniteElementSpace` @a fes, an
|
||||
`mfem::IntegrationRule` @a ir, and `mfem::Coefficient` or
|
||||
`mfem::VectorCoefficient` @a Q for the elements given by the indices
|
||||
@a indices.
|
||||
The `CeedOperatorInfo` type is expected to inherit from `OperatorInfo`,
|
||||
and contain a `Context` type relevant to the qFunctions.
|
||||
|
||||
@param[in] info is the structure describing the CeedOperator to assemble.
|
||||
@param[in] fes is the finite element space.
|
||||
@param[in] ir is the integration rule for the operator.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`. If `indices == nullptr`, assumes
|
||||
that the `FiniteElementSpace` is not mixed.
|
||||
@param[in] Q is the coefficient from the `Integrator`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, fes, fes, ir, nelem, indices, Q);
|
||||
}
|
||||
|
||||
/** This method assembles the PAIntegrator for mixed forms.
|
||||
@@ -128,12 +158,40 @@ public:
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &trial_fes,
|
||||
const mfem::FiniteElementSpace &test_fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, trial_fes, test_fes, ir, trial_fes.GetNE(), nullptr, Q);
|
||||
}
|
||||
|
||||
/** This method assembles the PAIntegrator for mixed forms on mixed meshes.
|
||||
|
||||
@param[in] info the `CeedOperatorInfo` describing the `CeedOperator`,
|
||||
the `CeedOperatorInfo` type is expected to inherit from
|
||||
`OperatorInfo` and contain a `Context` type relevant to
|
||||
the qFunctions.
|
||||
@param[in] trial_fes the trial `FiniteElementSpace` for the form,
|
||||
@param[in] test_fes the test `FiniteElementSpace` for the form,
|
||||
@param[in] ir the `IntegrationRule` for the numerical integration,
|
||||
@param[in] nelem The number of elements,
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`. If `indices == nullptr`, assumes
|
||||
that the `FiniteElementSpace` is not mixed,
|
||||
@param[in] Q `Coefficient` or `VectorCoefficient`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &trial_fes,
|
||||
const mfem::FiniteElementSpace &test_fes,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Ceed ceed(internal::ceed);
|
||||
mfem::Mesh &mesh = *trial_fes.GetMesh();
|
||||
InitCoefficient(Q, mesh, irm, coeff, info.ctx);
|
||||
MFEM_VERIFY(!(!indices && mesh.GetNumGeometries(mesh.Dimension()) > 1),
|
||||
"Use ceed::MixedIntegrator on mixed meshes.");
|
||||
InitCoefficient(Q, mesh, ir, nelem, indices, coeff, info.ctx);
|
||||
bool const_coeff = coeff->IsConstant();
|
||||
std::string build_func = const_coeff ? info.build_func_const
|
||||
: info.build_func_quad;
|
||||
@@ -145,7 +203,6 @@ public:
|
||||
info.trial_op,
|
||||
info.test_op
|
||||
};
|
||||
CeedInt nqpts, nelem = mesh.GetNE();
|
||||
CeedInt dim = mesh.SpaceDimension();
|
||||
CeedInt trial_vdim = trial_fes.GetVDim();
|
||||
CeedInt test_vdim = test_fes.GetVDim();
|
||||
@@ -153,23 +210,23 @@ public:
|
||||
mesh.EnsureNodes();
|
||||
if ( &trial_fes == &test_fes )
|
||||
{
|
||||
InitBasisAndRestriction(trial_fes, irm, ceed,
|
||||
&trial_basis, &trial_restr);
|
||||
InitBasisAndRestriction(trial_fes, ir, nelem, indices,
|
||||
ceed, &trial_basis, &trial_restr);
|
||||
test_basis = trial_basis;
|
||||
test_restr = trial_restr;
|
||||
}
|
||||
else
|
||||
{
|
||||
InitBasisAndRestriction(trial_fes, irm, ceed,
|
||||
&trial_basis, &trial_restr);
|
||||
InitBasisAndRestriction(test_fes, irm, ceed,
|
||||
&test_basis, &test_restr);
|
||||
InitBasisAndRestriction(trial_fes, ir, nelem, indices,
|
||||
ceed, &trial_basis, &trial_restr);
|
||||
InitBasisAndRestriction(test_fes, ir, nelem, indices,
|
||||
ceed, &test_basis, &test_restr);
|
||||
}
|
||||
|
||||
const mfem::FiniteElementSpace *mesh_fes = mesh.GetNodalFESpace();
|
||||
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
|
||||
InitBasisAndRestriction(*mesh_fes, irm, ceed, &mesh_basis,
|
||||
&mesh_restr);
|
||||
InitBasisAndRestriction(*mesh_fes, ir, nelem, indices,
|
||||
ceed, &mesh_basis, &mesh_restr);
|
||||
|
||||
CeedInt trial_nqpts, test_nqpts;
|
||||
CeedBasisGetNumQuadraturePoints(trial_basis, &trial_nqpts);
|
||||
@@ -177,7 +234,7 @@ public:
|
||||
MFEM_VERIFY(trial_nqpts == test_nqpts,
|
||||
"Trial and test basis must have the same number of quadrature"
|
||||
" points.");
|
||||
nqpts = trial_nqpts;
|
||||
CeedInt nqpts = trial_nqpts;
|
||||
|
||||
const int qdatasize = op.qdatasize;
|
||||
InitStridedRestriction(*mesh_fes, nelem, nqpts, qdatasize,
|
||||
@@ -221,8 +278,10 @@ public:
|
||||
CeedOperatorCreate(ceed, build_qfunc, NULL, NULL, &build_oper);
|
||||
if (GridCoefficient *gridCoeff = dynamic_cast<GridCoefficient*>(coeff))
|
||||
{
|
||||
InitBasisAndRestriction(*gridCoeff->gf.FESpace(), irm, ceed,
|
||||
&gridCoeff->basis, &gridCoeff->restr);
|
||||
InitBasisAndRestriction(*gridCoeff->gf.FESpace(), ir,
|
||||
nelem, indices, ceed,
|
||||
&gridCoeff->basis,
|
||||
&gridCoeff->restr);
|
||||
CeedOperatorSetField(build_oper, "coeff", gridCoeff->restr,
|
||||
gridCoeff->basis, gridCoeff->coeffVector);
|
||||
}
|
||||
@@ -231,7 +290,8 @@ public:
|
||||
{
|
||||
const int ncomp = quadCoeff->ncomp;
|
||||
CeedInt strides[3] = {ncomp, 1, ncomp*nqpts};
|
||||
InitStridedRestriction(*mesh_fes, nelem, nqpts, ncomp, strides,
|
||||
InitStridedRestriction(*mesh.GetNodalFESpace(),
|
||||
nelem, nqpts, ncomp, strides,
|
||||
&quadCoeff->restr);
|
||||
CeedOperatorSetField(build_oper, "coeff", quadCoeff->restr,
|
||||
CEED_BASIS_COLLOCATED, quadCoeff->coeffVector);
|
||||
@@ -254,22 +314,17 @@ public:
|
||||
switch (op.trial_op)
|
||||
{
|
||||
case EvalMode::None:
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim,
|
||||
CEED_EVAL_NONE);
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim, CEED_EVAL_NONE);
|
||||
break;
|
||||
case EvalMode::Interp:
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim,
|
||||
CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim, CEED_EVAL_INTERP);
|
||||
break;
|
||||
case EvalMode::Grad:
|
||||
CeedQFunctionAddInput(apply_qfunc, "gu", trial_vdim*dim,
|
||||
CEED_EVAL_GRAD);
|
||||
CeedQFunctionAddInput(apply_qfunc, "gu", trial_vdim*dim, CEED_EVAL_GRAD);
|
||||
break;
|
||||
case EvalMode::InterpAndGrad:
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim,
|
||||
CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddInput(apply_qfunc, "gu", trial_vdim*dim,
|
||||
CEED_EVAL_GRAD);
|
||||
CeedQFunctionAddInput(apply_qfunc, "u", trial_vdim, CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddInput(apply_qfunc, "gu", trial_vdim*dim, CEED_EVAL_GRAD);
|
||||
break;
|
||||
}
|
||||
// qdata
|
||||
@@ -278,22 +333,17 @@ public:
|
||||
switch (op.test_op)
|
||||
{
|
||||
case EvalMode::None:
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim,
|
||||
CEED_EVAL_NONE);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim, CEED_EVAL_NONE);
|
||||
break;
|
||||
case EvalMode::Interp:
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim,
|
||||
CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim, CEED_EVAL_INTERP);
|
||||
break;
|
||||
case EvalMode::Grad:
|
||||
CeedQFunctionAddOutput(apply_qfunc, "gv", test_vdim*dim,
|
||||
CEED_EVAL_GRAD);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "gv", test_vdim*dim, CEED_EVAL_GRAD);
|
||||
break;
|
||||
case EvalMode::InterpAndGrad:
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim,
|
||||
CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "gv", test_vdim*dim,
|
||||
CEED_EVAL_GRAD);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "v", test_vdim, CEED_EVAL_INTERP);
|
||||
CeedQFunctionAddOutput(apply_qfunc, "gv", test_vdim*dim, CEED_EVAL_GRAD);
|
||||
break;
|
||||
}
|
||||
CeedQFunctionSetContext(apply_qfunc, build_ctx);
|
||||
@@ -308,18 +358,14 @@ public:
|
||||
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::Interp:
|
||||
CeedOperatorSetField(oper, "u", trial_restr, trial_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "u", trial_restr, trial_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::Grad:
|
||||
CeedOperatorSetField(oper, "gu", trial_restr, trial_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gu", trial_restr, trial_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::InterpAndGrad:
|
||||
CeedOperatorSetField(oper, "u", trial_restr, trial_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gu", trial_restr, trial_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "u", trial_restr, trial_basis, CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gu", trial_restr, trial_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
}
|
||||
// qdata
|
||||
@@ -333,18 +379,14 @@ public:
|
||||
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::Interp:
|
||||
CeedOperatorSetField(oper, "v", test_restr, test_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::Grad:
|
||||
CeedOperatorSetField(oper, "gv", test_restr, test_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gv", test_restr, test_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
case EvalMode::InterpAndGrad:
|
||||
CeedOperatorSetField(oper, "v", test_restr, test_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gv", test_restr, test_basis,
|
||||
CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE);
|
||||
CeedOperatorSetField(oper, "gv", test_restr, test_basis, CEED_VECTOR_ACTIVE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -402,6 +444,7 @@ protected:
|
||||
Coefficient *coeff;
|
||||
CeedQFunctionContext build_ctx;
|
||||
|
||||
public:
|
||||
MFIntegrator()
|
||||
: Operator(),
|
||||
trial_basis(nullptr), test_basis(nullptr), mesh_basis(nullptr),
|
||||
@@ -410,23 +453,51 @@ protected:
|
||||
apply_qfunc(nullptr), node_coords(nullptr),
|
||||
qdata(nullptr), coeff(nullptr), build_ctx(nullptr) { }
|
||||
|
||||
public:
|
||||
/** This method assembles the MFIntegrator.
|
||||
/** @brief This method assembles the `MFIntegrator` with the given
|
||||
`CeedOperatorInfo` @a info, an `mfem::FiniteElementSpace` @a fes, an
|
||||
`mfem::IntegrationRule` @a ir, and `mfem::Coefficient` or
|
||||
`mfem::VectorCoefficient` @a Q.
|
||||
The `CeedOperatorInfo` type is expected to inherit from `OperatorInfo`,
|
||||
and contain a `Context` type relevant to the qFunctions.
|
||||
|
||||
@param[in] info the `CeedOperatorInfo` describing the `CeedOperator`,
|
||||
the `CeedOperatorInfo` type is expected to inherit from
|
||||
`OperatorInfo` and contain a `Context` type relevant to
|
||||
the qFunctions.
|
||||
@param[in] fes the `FiniteElementSpace` for the form,
|
||||
@param[in] ir the `IntegrationRule` for the numerical integration,
|
||||
@param[in] Q `Coefficient` or `VectorCoefficient`. */
|
||||
@param[in] info is the structure describing the CeedOperator to assemble.
|
||||
@param[in] fes is the finite element space.
|
||||
@param[in] ir is the integration rule for the operator.
|
||||
@param[in] Q is the coefficient from the `Integrator`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, fes, fes, irm, Q);
|
||||
Assemble(info, fes, ir, fes.GetNE(), nullptr, Q);
|
||||
}
|
||||
|
||||
/** @brief This method assembles the `MFIntegrator` with the given
|
||||
`CeedOperatorInfo` @a info, an `mfem::FiniteElementSpace` @a fes, an
|
||||
`mfem::IntegrationRule` @a ir, and `mfem::Coefficient` or
|
||||
`mfem::VectorCoefficient` @a Q for the elements given by the indices
|
||||
@a indices.
|
||||
The `CeedOperatorInfo` type is expected to inherit from `OperatorInfo`,
|
||||
and contain a `Context` type relevant to the qFunctions.
|
||||
|
||||
@param[in] info is the structure describing the CeedOperator to assemble.
|
||||
@param[in] fes is the finite element space.
|
||||
@param[in] ir is the integration rule for the operator.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`. If `indices == nullptr`, assumes
|
||||
that the `FiniteElementSpace` is not mixed.
|
||||
@param[in] Q is the coefficient from the `Integrator`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, fes, fes, ir, nelem, indices, Q);
|
||||
}
|
||||
|
||||
/** This method assembles the MFIntegrator for mixed forms.
|
||||
@@ -443,12 +514,40 @@ public:
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &trial_fes,
|
||||
const mfem::FiniteElementSpace &test_fes,
|
||||
const mfem::IntegrationRule &irm,
|
||||
const mfem::IntegrationRule &ir,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Assemble(info, trial_fes, test_fes, ir, trial_fes.GetNE(), nullptr, Q);
|
||||
}
|
||||
|
||||
/** This method assembles the MFIntegrator for mixed forms.
|
||||
|
||||
@param[in] info the `CeedOperatorInfo` describing the `CeedOperator`,
|
||||
the `CeedOperatorInfo` type is expected to inherit from
|
||||
`OperatorInfo` and contain a `Context` type relevant to
|
||||
the qFunctions.
|
||||
@param[in] trial_fes the trial `FiniteElementSpace` for the form,
|
||||
@param[in] test_fes the test `FiniteElementSpace` for the form,
|
||||
@param[in] ir the `IntegrationRule` for the numerical integration,
|
||||
@param[in] nelem The number of elements,
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`. If `indices == nullptr`, assumes
|
||||
that the `FiniteElementSpace` is not mixed,
|
||||
@param[in] Q `Coefficient` or `VectorCoefficient`. */
|
||||
template <typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &trial_fes,
|
||||
const mfem::FiniteElementSpace &test_fes,
|
||||
const mfem::IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
CoeffType *Q)
|
||||
{
|
||||
Ceed ceed(internal::ceed);
|
||||
Mesh &mesh = *trial_fes.GetMesh();
|
||||
InitCoefficient(Q, mesh, irm, coeff, info.ctx);
|
||||
MFEM_VERIFY(!(!indices && mesh.GetNumGeometries(mesh.Dimension()) > 1),
|
||||
"Use ceed::MixedIntegrator on mixed meshes.");
|
||||
InitCoefficient(Q, mesh, ir, nelem, indices, coeff, info.ctx);
|
||||
bool const_coeff = coeff->IsConstant();
|
||||
std::string apply_func = const_coeff ? info.apply_func_mf_const
|
||||
: info.apply_func_mf_quad;
|
||||
@@ -459,7 +558,7 @@ public:
|
||||
info.trial_op,
|
||||
info.test_op
|
||||
};
|
||||
CeedInt nqpts, nelem = mesh.GetNE();
|
||||
|
||||
CeedInt dim = mesh.SpaceDimension();
|
||||
CeedInt trial_vdim = trial_fes.GetVDim();
|
||||
CeedInt test_vdim = test_fes.GetVDim();
|
||||
@@ -467,22 +566,22 @@ public:
|
||||
mesh.EnsureNodes();
|
||||
if ( &trial_fes == &test_fes )
|
||||
{
|
||||
InitBasisAndRestriction(trial_fes, irm, ceed,
|
||||
InitBasisAndRestriction(trial_fes, ir, nelem, indices, ceed,
|
||||
&trial_basis, &trial_restr);
|
||||
test_basis = trial_basis;
|
||||
test_restr = trial_restr;
|
||||
}
|
||||
else
|
||||
{
|
||||
InitBasisAndRestriction(trial_fes, irm, ceed,
|
||||
InitBasisAndRestriction(trial_fes, ir, nelem, indices, ceed,
|
||||
&trial_basis, &trial_restr);
|
||||
InitBasisAndRestriction(test_fes, irm, ceed,
|
||||
InitBasisAndRestriction(test_fes, ir, nelem, indices, ceed,
|
||||
&test_basis, &test_restr);
|
||||
}
|
||||
|
||||
const mfem::FiniteElementSpace *mesh_fes = mesh.GetNodalFESpace();
|
||||
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
|
||||
InitBasisAndRestriction(*mesh_fes, irm, ceed, &mesh_basis,
|
||||
InitBasisAndRestriction(*mesh_fes, ir, nelem, indices, ceed, &mesh_basis,
|
||||
&mesh_restr);
|
||||
|
||||
CeedInt trial_nqpts, test_nqpts;
|
||||
@@ -491,7 +590,7 @@ public:
|
||||
MFEM_VERIFY(trial_nqpts == test_nqpts,
|
||||
"Trial and test basis must have the same number of quadrature"
|
||||
" points.");
|
||||
nqpts = trial_nqpts;
|
||||
CeedInt nqpts = trial_nqpts;
|
||||
|
||||
InitVector(*mesh.GetNodes(), node_coords);
|
||||
|
||||
@@ -572,8 +671,8 @@ public:
|
||||
// coefficient
|
||||
if (GridCoefficient *gridCoeff = dynamic_cast<GridCoefficient*>(coeff))
|
||||
{
|
||||
InitBasisAndRestriction(*gridCoeff->gf.FESpace(), irm, ceed,
|
||||
&gridCoeff->basis, &gridCoeff->restr);
|
||||
InitBasisAndRestriction(*gridCoeff->gf.FESpace(), ir, nelem, indices,
|
||||
ceed, &gridCoeff->basis, &gridCoeff->restr);
|
||||
CeedOperatorSetField(oper, "coeff", gridCoeff->restr,
|
||||
gridCoeff->basis, gridCoeff->coeffVector);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "coefficient.hpp"
|
||||
// PA or MF Operator using libCEED.
|
||||
#include "integrator.hpp"
|
||||
// PA Operator supporting mixed finite element spaces.
|
||||
#include "mixed_integrator.hpp"
|
||||
// Utility functions
|
||||
#include "util.hpp"
|
||||
// Wrapper to include <ceed.h>
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_LIBCEED_MIXED_INTEGRATOR
|
||||
#define MFEM_LIBCEED_MIXED_INTEGRATOR
|
||||
|
||||
#include "ceed.hpp"
|
||||
#include "integrator.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace ceed
|
||||
{
|
||||
|
||||
/** @brief This class wraps a `ceed::PAIntegrator` or `ceed::MFIntegrator` to
|
||||
support mixed finite element spaces. */
|
||||
template <typename CeedInteg>
|
||||
class MixedIntegrator : public ceed::Operator
|
||||
{
|
||||
#ifdef MFEM_USE_CEED
|
||||
using ElementKey = std::pair<int, int>; //< Element::Type, Order >
|
||||
struct key_hash
|
||||
{
|
||||
std::size_t operator()(const ElementKey& k) const
|
||||
{
|
||||
return k.first + 2 * k.second;
|
||||
}
|
||||
};
|
||||
using ElementsMap = std::unordered_map<const ElementKey, int*, key_hash>;
|
||||
std::vector<CeedInteg*> sub_ops;
|
||||
|
||||
public:
|
||||
template <typename Integrator, typename CeedOperatorInfo, typename CoeffType>
|
||||
void Assemble(const Integrator &integ,
|
||||
CeedOperatorInfo &info,
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
CoeffType *Q)
|
||||
{
|
||||
ElementsMap count;
|
||||
ElementsMap element_indices;
|
||||
ElementsMap offsets;
|
||||
|
||||
// Count the number of elements of each type
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
{
|
||||
ElementKey key(fes.GetElementType(i), fes.GetElementOrder(i));
|
||||
auto value = count.find(key);
|
||||
if (value == count.end())
|
||||
{
|
||||
count[key] = new int(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*value->second)++;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization of the arrays
|
||||
for ( const auto& value : count )
|
||||
{
|
||||
element_indices[value.first] = new int[*value.second];
|
||||
offsets[value.first] = new int(0);
|
||||
}
|
||||
|
||||
// Populates the indices arrays for each element type
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
{
|
||||
ElementKey key(fes.GetElementType(i), fes.GetElementOrder(i));
|
||||
int &offset = *(offsets[key]);
|
||||
int* indices_array = element_indices[key];
|
||||
indices_array[offset] = i;
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Create composite CeedOperator
|
||||
CeedCompositeOperatorCreate(internal::ceed, &oper);
|
||||
|
||||
// Create each sub-CeedOperator
|
||||
sub_ops.reserve(element_indices.size());
|
||||
for (const auto& value : element_indices)
|
||||
{
|
||||
const int* indices = value.second;
|
||||
const int first_index = indices[0];
|
||||
const mfem::FiniteElement &el = *fes.GetFE(first_index);
|
||||
auto &T = *fes.GetMesh()->GetElementTransformation(first_index);
|
||||
MFEM_ASSERT(!integ.GetIntegrationRule(),
|
||||
"Mixed mesh integrators should not have an"
|
||||
" IntegrationRule.");
|
||||
const IntegrationRule &ir = GetRule(integ, el, el, T);
|
||||
auto sub_op = new CeedInteg();
|
||||
int nelem = *count[value.first];
|
||||
sub_op->Assemble(info, fes, ir, nelem, indices, Q);
|
||||
sub_ops.push_back(sub_op);
|
||||
CeedCompositeOperatorAddSub(oper, sub_op->GetCeedOperator());
|
||||
}
|
||||
|
||||
const int ndofs = fes.GetVDim() * fes.GetNDofs();
|
||||
CeedVectorCreate(internal::ceed, ndofs, &u);
|
||||
CeedVectorCreate(internal::ceed, ndofs, &v);
|
||||
}
|
||||
|
||||
virtual ~MixedIntegrator()
|
||||
{
|
||||
for (auto sub_op : sub_ops)
|
||||
{
|
||||
delete sub_op;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ceed
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_LIBCEED_MIXED_INTEGRATOR
|
||||
@@ -20,8 +20,8 @@ namespace ceed
|
||||
|
||||
#ifdef MFEM_USE_CEED
|
||||
|
||||
static void InitNonTensorRestriction(const mfem::FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
static void InitNativeRestr(const mfem::FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(0);
|
||||
const int P = fe->GetDof();
|
||||
@@ -31,77 +31,173 @@ static void InitNonTensorRestriction(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::TensorBasisElement * tfe =
|
||||
dynamic_cast<const mfem::TensorBasisElement *>(fe);
|
||||
const int stride = compstride == 1 ? fes.GetVDim() : 1;
|
||||
if (tfe) // Lexicographic ordering using dof_map
|
||||
const mfem::Array<int>& dof_map = tfe->GetDofMap();
|
||||
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
{
|
||||
const mfem::Array<int>& dof_map = tfe->GetDofMap();
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
const int el_offset = P * i;
|
||||
for (int j = 0; j < P; j++)
|
||||
{
|
||||
const int el_offset = P * i;
|
||||
for (int j = 0; j < P; j++)
|
||||
{
|
||||
tp_el_dof[j+el_offset] = stride*el_dof.GetJ()[dof_map[j]+el_offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Native ordering
|
||||
{
|
||||
for (int e = 0; e < fes.GetNE(); e++)
|
||||
{
|
||||
for (int i = 0; i < P; i++)
|
||||
{
|
||||
tp_el_dof[i + e*P] = stride*el_dof.GetJ()[i + e*P];
|
||||
}
|
||||
tp_el_dof[j+el_offset] = stride*el_dof.GetJ()[dof_map[j]+el_offset];
|
||||
}
|
||||
}
|
||||
|
||||
CeedElemRestrictionCreate(ceed, fes.GetNE(), P, fes.GetVDim(),
|
||||
compstride, (fes.GetVDim())*(fes.GetNDofs()),
|
||||
CEED_MEM_HOST, CEED_COPY_VALUES,
|
||||
tp_el_dof.GetData(), restr);
|
||||
}
|
||||
|
||||
// TODO fuse Tensor and NonTensor Restriction
|
||||
void InitTensorRestriction(const mfem::FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
static void InitLexicoRestr(const mfem::FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(0);
|
||||
const int P = fe->GetDof();
|
||||
CeedInt compstride = fes.GetOrdering()==Ordering::byVDIM ? 1 : fes.GetNDofs();
|
||||
const mfem::Table &el_dof = fes.GetElementToDofTable();
|
||||
mfem::Array<int> tp_el_dof(el_dof.Size_of_connections());
|
||||
const int stride = compstride == 1 ? fes.GetVDim() : 1;
|
||||
|
||||
for (int e = 0; e < fes.GetNE(); e++)
|
||||
{
|
||||
for (int i = 0; i < P; i++)
|
||||
{
|
||||
tp_el_dof[i + e*P] = stride*el_dof.GetJ()[i + e*P];
|
||||
}
|
||||
}
|
||||
|
||||
CeedElemRestrictionCreate(ceed, fes.GetNE(), P, fes.GetVDim(),
|
||||
compstride, (fes.GetVDim())*(fes.GetNDofs()),
|
||||
CEED_MEM_HOST, CEED_COPY_VALUES,
|
||||
tp_el_dof.GetData(), restr);
|
||||
}
|
||||
|
||||
static void InitRestrictionImpl(const mfem::FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(0);
|
||||
const mfem::TensorBasisElement * tfe =
|
||||
dynamic_cast<const mfem::TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tfe, "invalid FE");
|
||||
if ( tfe && tfe->GetDofMap().Size()>0 ) // Native ordering using dof_map
|
||||
{
|
||||
InitNativeRestr(fes, ceed, restr);
|
||||
}
|
||||
else // Lexicographic ordering
|
||||
{
|
||||
InitLexicoRestr(fes, ceed, restr);
|
||||
}
|
||||
}
|
||||
|
||||
static void InitNativeRestrWithIndices(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(indices[0]);
|
||||
const int P = fe->GetDof();
|
||||
CeedInt compstride = fes.GetOrdering()==Ordering::byVDIM ? 1 : fes.GetNDofs();
|
||||
mfem::Array<int> tp_el_dof(nelem*P);
|
||||
const mfem::TensorBasisElement * tfe =
|
||||
dynamic_cast<const mfem::TensorBasisElement *>(fe);
|
||||
Array<int> dofs;
|
||||
const int stride = compstride == 1 ? fes.GetVDim() : 1;
|
||||
const mfem::Array<int>& dof_map = tfe->GetDofMap();
|
||||
|
||||
CeedInt compstride = fes.GetOrdering()==Ordering::byVDIM ? 1 : fes.GetNDofs();
|
||||
const mfem::Table &el_dof = fes.GetElementToDofTable();
|
||||
mfem::Array<int> tp_el_dof(el_dof.Size_of_connections());
|
||||
const int dof = fe->GetDof();
|
||||
const int stride = compstride == 1 ? fes.GetVDim() : 1;
|
||||
if (dof_map.Size()>0)
|
||||
for (int i = 0; i < nelem; i++)
|
||||
{
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
const int elem_index = indices[i];
|
||||
fes.GetElementDofs(elem_index, dofs);
|
||||
const int el_offset = P * i;
|
||||
for (int j = 0; j < P; j++)
|
||||
{
|
||||
const int el_offset = dof * i;
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
tp_el_dof[j+el_offset] = stride*el_dof.GetJ()[dof_map[j]+el_offset];
|
||||
}
|
||||
tp_el_dof[j + el_offset] = stride*dofs[dof_map[j]];
|
||||
}
|
||||
}
|
||||
else // dof_map.Size == 0, means dof_map[j]==j;
|
||||
{
|
||||
for (int i = 0; i < fes.GetNE(); i++)
|
||||
{
|
||||
const int el_offset = dof * i;
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
tp_el_dof[j+el_offset] = stride*el_dof.GetJ()[j+el_offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
CeedElemRestrictionCreate(ceed, fes.GetNE(), dof, fes.GetVDim(),
|
||||
|
||||
CeedElemRestrictionCreate(ceed, nelem, P, fes.GetVDim(),
|
||||
compstride, (fes.GetVDim())*(fes.GetNDofs()),
|
||||
CEED_MEM_HOST, CEED_COPY_VALUES,
|
||||
tp_el_dof.GetData(), restr);
|
||||
}
|
||||
|
||||
static void InitLexicoRestrWithIndices(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(indices[0]);
|
||||
const int P = fe->GetDof();
|
||||
CeedInt compstride = fes.GetOrdering()==Ordering::byVDIM ? 1 : fes.GetNDofs();
|
||||
mfem::Array<int> tp_el_dof(nelem*P);
|
||||
Array<int> dofs;
|
||||
const int stride = compstride == 1 ? fes.GetVDim() : 1;
|
||||
|
||||
for (int i = 0; i < nelem; i++)
|
||||
{
|
||||
const int elem_index = indices[i];
|
||||
fes.GetElementDofs(elem_index, dofs);
|
||||
const int el_offset = P * i;
|
||||
for (int j = 0; j < P; j++)
|
||||
{
|
||||
tp_el_dof[j + el_offset] = stride*dofs[j];
|
||||
}
|
||||
}
|
||||
|
||||
CeedElemRestrictionCreate(ceed, nelem, P, fes.GetVDim(),
|
||||
compstride, (fes.GetVDim())*(fes.GetNDofs()),
|
||||
CEED_MEM_HOST, CEED_COPY_VALUES,
|
||||
tp_el_dof.GetData(), restr);
|
||||
}
|
||||
|
||||
static void InitRestrictionWithIndicesImpl(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedElemRestriction *restr)
|
||||
{
|
||||
const mfem::FiniteElement *fe = fes.GetFE(indices[0]);
|
||||
const mfem::TensorBasisElement * tfe =
|
||||
dynamic_cast<const mfem::TensorBasisElement *>(fe);
|
||||
if ( tfe && tfe->GetDofMap().Size()>0 ) // Native ordering using dof_map
|
||||
{
|
||||
InitNativeRestrWithIndices(fes, nelem, indices, ceed, restr);
|
||||
}
|
||||
else // Lexicographic ordering
|
||||
{
|
||||
InitLexicoRestrWithIndices(fes, nelem, indices, ceed, restr);
|
||||
}
|
||||
}
|
||||
|
||||
static void InitCoeffRestrictionWithIndicesImpl(
|
||||
const mfem::FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
int nquads,
|
||||
int ncomp,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr)
|
||||
{
|
||||
mfem::Array<int> tp_el_dof(nelem*nquads);
|
||||
const int stride_quad = ncomp;
|
||||
const int stride_elem = ncomp*nquads;
|
||||
// TODO generalize to support different #quads
|
||||
for (int i = 0; i < nelem; i++)
|
||||
{
|
||||
const int elem_index = indices[i];
|
||||
const int el_offset = elem_index * stride_elem;
|
||||
for (int j = 0; j < nquads; j++)
|
||||
{
|
||||
tp_el_dof[j + nquads * i] = j * stride_quad + el_offset;
|
||||
}
|
||||
}
|
||||
CeedElemRestrictionCreate(ceed, nelem, nquads, ncomp, 1,
|
||||
ncomp*fes.GetNE()*nquads,
|
||||
CEED_MEM_HOST, CEED_COPY_VALUES,
|
||||
tp_el_dof.GetData(), restr);
|
||||
}
|
||||
|
||||
void InitStridedRestriction(const mfem::FiniteElementSpace &fes,
|
||||
CeedInt nelem, CeedInt nqpts, CeedInt qdatasize,
|
||||
const CeedInt *strides,
|
||||
@@ -139,14 +235,57 @@ void InitRestriction(const FiniteElementSpace &fes,
|
||||
// Init or retreive key values
|
||||
if (restr_itr == mfem::internal::ceed_restr_map.end())
|
||||
{
|
||||
if (UsesTensorBasis(fes))
|
||||
{
|
||||
InitTensorRestriction(fes, ceed, restr);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitNonTensorRestriction(fes, ceed, restr);
|
||||
}
|
||||
InitRestrictionImpl(fes, ceed, restr);
|
||||
mfem::internal::ceed_restr_map[restr_key] = *restr;
|
||||
}
|
||||
else
|
||||
{
|
||||
*restr = restr_itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
void InitRestrictionWithIndices(const FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr)
|
||||
{
|
||||
// Check for FES -> basis, restriction in hash tables
|
||||
const mfem::FiniteElement *fe = fes.GetFE(indices[0]);
|
||||
const int P = fe->GetDof();
|
||||
const int ncomp = fes.GetVDim();
|
||||
RestrKey restr_key(&fes, nelem, P, ncomp, restr_type::Standard);
|
||||
auto restr_itr = mfem::internal::ceed_restr_map.find(restr_key);
|
||||
|
||||
// Init or retreive key values
|
||||
if (restr_itr == mfem::internal::ceed_restr_map.end())
|
||||
{
|
||||
InitRestrictionWithIndicesImpl(fes, nelem, indices, ceed, restr);
|
||||
mfem::internal::ceed_restr_map[restr_key] = *restr;
|
||||
}
|
||||
else
|
||||
{
|
||||
*restr = restr_itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
void InitCoeffRestrictionWithIndices(const FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
int nquads,
|
||||
int ncomp,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr)
|
||||
{
|
||||
// Check for FES -> basis, restriction in hash tables
|
||||
RestrKey restr_key(&fes, nelem, nquads, ncomp, restr_type::Coeff);
|
||||
auto restr_itr = mfem::internal::ceed_restr_map.find(restr_key);
|
||||
|
||||
// Init or retreive key values
|
||||
if (restr_itr == mfem::internal::ceed_restr_map.end())
|
||||
{
|
||||
InitCoeffRestrictionWithIndicesImpl(fes, nelem, indices, nquads, ncomp,
|
||||
ceed, restr);
|
||||
mfem::internal::ceed_restr_map[restr_key] = *restr;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -21,37 +21,63 @@ namespace ceed
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_CEED
|
||||
/// @brief Initialize a strided CeedElemRestriction
|
||||
/** @a nelem is the number of elements,
|
||||
@a nqpts is the total number of quadrature points
|
||||
@a qdatasize is the number of data per quadrature point
|
||||
@a strides Array for strides between [nodes, components, elements].
|
||||
Data for node i, component j, element k can be found in the L-vector at
|
||||
index i*strides[0] + j*strides[1] + k*strides[2]. CEED_STRIDES_BACKEND may
|
||||
be used with vectors created by a Ceed backend. */
|
||||
void InitStridedRestriction(const mfem::FiniteElementSpace &fes,
|
||||
CeedInt nelem, CeedInt nqpts, CeedInt qdatasize,
|
||||
const CeedInt *strides,
|
||||
CeedElemRestriction *restr);
|
||||
/** @brief Initialize a CeedElemRestriction for non-mixed meshes.
|
||||
|
||||
/** @brief Initialize a CeedElemRestriction.
|
||||
*
|
||||
* @param[in] fes Input finite element space.
|
||||
* @param[in] ceed Input Ceed object.
|
||||
@param[out] restr The address of the initialized CeedElemRestriction object.
|
||||
@param[in] fes Input finite element space.
|
||||
@param[in] ceed Input Ceed object.
|
||||
@param[out] restr The address of the initialized CeedElemRestriction object.
|
||||
*/
|
||||
void InitRestriction(const FiniteElementSpace &fes,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
/** @brief Initialize a CeedElemRestriction.
|
||||
*
|
||||
* @param[in] fes Input finite element space.
|
||||
* @param[in] ceed Input Ceed object.
|
||||
@param[out] restr The address of the initialized CeedElemRestriction object.
|
||||
*/
|
||||
void InitTensorRestriction(const FiniteElementSpace &fes,
|
||||
Ceed ceed, CeedElemRestriction *restr);
|
||||
/** @brief Initialize a CeedElemRestriction for mixed meshes.
|
||||
|
||||
@param[in] fes The finite element space.
|
||||
@param[in] ceed The Ceed object.
|
||||
@param[in] nelem The number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`.
|
||||
@param[out] restr The `CeedElemRestriction` to initialize. */
|
||||
void InitRestrictionWithIndices(const FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
/** @brief Initialize a strided CeedElemRestriction
|
||||
|
||||
@param[in] nelem is the number of elements.
|
||||
@param[in] nqpts is the total number of quadrature points.
|
||||
@param[in] qdatasize is the number of data per quadrature point.
|
||||
@param[in] strides Array for strides between [nodes, components, elements].
|
||||
Data for node i, component j, element k can be found in the L-vector at
|
||||
index i*strides[0] + j*strides[1] + k*strides[2]. CEED_STRIDES_BACKEND may
|
||||
be used with vectors created by a Ceed backend.
|
||||
@param[out] restr The `CeedElemRestriction` to initialize. */
|
||||
void InitStridedRestriction(const mfem::FiniteElementSpace &fes,
|
||||
CeedInt nelem, CeedInt nqpts, CeedInt qdatasize,
|
||||
const CeedInt *strides,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
/** @brief Initialize a CeedElemRestriction for a mfem::Coefficient on a mixed
|
||||
mesh.
|
||||
|
||||
@param[in] fes The finite element space.
|
||||
@param[in] nelem is the number of elements.
|
||||
@param[in] indices The indices of the elements of same type in the
|
||||
`FiniteElementSpace`.
|
||||
@param[in] nquads is the total number of quadrature points
|
||||
@param[in] ncomp is the number of data per quadrature point
|
||||
@param[in] ceed The Ceed object.
|
||||
@param[out] restr The `CeedElemRestriction` to initialize. */
|
||||
void InitCoeffRestrictionWithIndices(const FiniteElementSpace &fes,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
int nquads,
|
||||
int ncomp,
|
||||
Ceed ceed,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -99,6 +99,34 @@ void InitBasisAndRestriction(const FiniteElementSpace &fes,
|
||||
InitRestriction(fes, ceed, restr);
|
||||
}
|
||||
|
||||
void InitBasisAndRestrictionWithIndices(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &irm,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedBasis *basis,
|
||||
CeedElemRestriction *restr)
|
||||
{
|
||||
InitBasisWithIndices(fes, irm, nelem, indices, ceed, basis);
|
||||
InitRestrictionWithIndices(fes, nelem, indices, ceed, restr);
|
||||
}
|
||||
|
||||
void InitBasisAndRestriction(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &irm,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedBasis *basis,
|
||||
CeedElemRestriction *restr)
|
||||
{
|
||||
if (indices)
|
||||
{
|
||||
InitBasisAndRestrictionWithIndices(fes,irm,nelem,indices,ceed,basis,restr);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitBasisAndRestriction(fes,irm,ceed,basis,restr);
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes a tensor-product operator with one active field
|
||||
int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field)
|
||||
{
|
||||
@@ -158,6 +186,66 @@ int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field)
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<MassIntegrator>(
|
||||
const MassIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return MassIntegrator::GetRule(trial_fe, test_fe, trans);
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<VectorMassIntegrator>(
|
||||
const VectorMassIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return MassIntegrator::GetRule(trial_fe, test_fe, trans);
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<ConvectionIntegrator>(
|
||||
const ConvectionIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return ConvectionIntegrator::GetRule(trial_fe, test_fe, trans);
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<VectorConvectionNLFIntegrator>(
|
||||
const VectorConvectionNLFIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return VectorConvectionNLFIntegrator::GetRule(trial_fe, trans);
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<DiffusionIntegrator>(
|
||||
const DiffusionIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return DiffusionIntegrator::GetRule(trial_fe, test_fe);
|
||||
}
|
||||
|
||||
template <>
|
||||
const IntegrationRule & GetRule<VectorDiffusionIntegrator>(
|
||||
const VectorDiffusionIntegrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &trans)
|
||||
{
|
||||
return DiffusionIntegrator::GetRule(trial_fe, test_fe);
|
||||
}
|
||||
|
||||
std::string ceed_path;
|
||||
|
||||
const std::string &GetCeedPath()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user