Compare commits
490
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 | ||
|
|
01f981d705 | ||
|
|
c225923cce | ||
|
|
8f90dc5c03 | ||
|
|
a46cf91039 | ||
|
|
0aaf837463 | ||
|
|
ef53cb55fd | ||
|
|
9de9bdaa64 | ||
|
|
2bb7d8fe72 | ||
|
|
0d6809927f | ||
|
|
023d6e3276 | ||
|
|
cdc8c9634f | ||
|
|
94578246c6 | ||
|
|
66849d50a8 | ||
|
|
a3dc3fa717 | ||
|
|
33fc6fa644 | ||
|
|
c0fbb01a73 | ||
|
|
a3429fa4ab | ||
|
|
1b812b4a36 | ||
|
|
7e44926d03 | ||
|
|
e5f2c551bf | ||
|
|
c251fa7d78 | ||
|
|
9495bf51dd | ||
|
|
295d5a0ac9 | ||
|
|
b81fa9f479 | ||
|
|
c81506c238 | ||
|
|
9332beb87a | ||
|
|
6d5403daab | ||
|
|
1c7164a5b1 | ||
|
|
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 | ||
|
|
67e0beb41d | ||
|
|
ed8e6fc20d | ||
|
|
be121033ed | ||
|
|
2d7938ed3e | ||
|
|
28bdb1a235 | ||
|
|
c78e984d69 | ||
|
|
ab64e55247 | ||
|
|
aee9379f34 | ||
|
|
3e40a30e5d | ||
|
|
24b66d11cc | ||
|
|
5ac499ff34 | ||
|
|
7d40cb822c | ||
|
|
3ed49a8ccf | ||
|
|
d7fbcda0fa | ||
|
|
45ee759dd7 | ||
|
|
b13e1afad0 | ||
|
|
9de38b3f2e | ||
|
|
3f3398f6de | ||
|
|
6287639acc | ||
|
|
91e6559d36 | ||
|
|
c6d1f20249 | ||
|
|
a3d2b38f31 | ||
|
|
4de70fd432 | ||
|
|
71c167805d | ||
|
|
6ef17443a4 | ||
|
|
67300f588d | ||
|
|
26f37ac152 | ||
|
|
777e352a76 | ||
|
|
23a8ffe384 | ||
|
|
5d110683f6 | ||
|
|
255302eae6 | ||
|
|
07f8b8f525 | ||
|
|
baea0cf188 | ||
|
|
aefc9068e0 | ||
|
|
52137ea095 | ||
|
|
e131e0f3a6 | ||
|
|
21dc848651 | ||
|
|
48f4efb5a5 | ||
|
|
5218ea2649 | ||
|
|
976d64ff95 | ||
|
|
f4ce842065 | ||
|
|
ae27de9af2 | ||
|
|
78cf39781d | ||
|
|
881d0be80a | ||
|
|
47b42e9f7d | ||
|
|
8f328237ab | ||
|
|
6872f7bee6 | ||
|
|
eed10fe35b | ||
|
|
cdcc3339d6 | ||
|
|
c911247ee3 | ||
|
|
bc546dc598 | ||
|
|
3916f7dabf | ||
|
|
d539dc6fb3 | ||
|
|
cb0d8a7af6 | ||
|
|
e25eb36d29 | ||
|
|
178ceeeaa3 | ||
|
|
d1cf744f06 | ||
|
|
4c7b9989d9 | ||
|
|
32f4c4f88e | ||
|
|
c9ddefd68a | ||
|
|
0b7980091e | ||
|
|
6bb1aaba5d | ||
|
|
079f9cce33 | ||
|
|
244719c39c | ||
|
|
0de5f1f70b | ||
|
|
18e7b9552c | ||
|
|
fe300a678a | ||
|
|
54c1073f4c | ||
|
|
78458fed0a | ||
|
|
8497117c61 | ||
|
|
d2753a59b0 | ||
|
|
ee5cc572ce | ||
|
|
6e16d5b7bf | ||
|
|
f273e9b04f | ||
|
|
cb731d4f1a | ||
|
|
7d1b925438 | ||
|
|
1314d0b7d3 | ||
|
|
95be3d9c12 | ||
|
|
3d6f1694b3 | ||
|
|
6e82a1953b | ||
|
|
c454ddeb1c | ||
|
|
eb0882ed94 | ||
|
|
0a051d9428 | ||
|
|
4104e010d5 | ||
|
|
beaaa373ee | ||
|
|
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 | ||
|
|
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 |
@@ -28,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
|
||||
|
||||
+9
-2
@@ -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 "")
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
+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);
|
||||
|
||||
@@ -438,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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class FiniteElement;
|
||||
class FiniteElementSpace;
|
||||
class ElementTransformation;
|
||||
class IntegrationRule;
|
||||
class Vector;
|
||||
|
||||
@@ -55,15 +57,51 @@ void RemoveBasisAndRestriction(const mfem::FiniteElementSpace *fes);
|
||||
/// Initialize a CeedVector from an mfem::Vector
|
||||
void InitVector(const mfem::Vector &v, CeedVector &cv);
|
||||
|
||||
/** Initialize a CeedBasis and a CeedElemRestriction based on an
|
||||
mfem::FiniteElementSpace @a fes, and an mfem::IntegrationRule @a ir. */
|
||||
/** @brief Initialize a CeedBasis and a CeedElemRestriction based on an
|
||||
mfem::FiniteElementSpace @a fes, and an mfem::IntegrationRule @a ir.
|
||||
|
||||
@param[in] fes The finite element space.
|
||||
@param[in] ir The integration rule.
|
||||
@param[in] ceed The Ceed object.
|
||||
@param[out] basis The `CeedBasis` to initialize.
|
||||
@param[out] restr The `CeedElemRestriction` to initialize.
|
||||
|
||||
@warning Only for non-mixed finite element spaces. */
|
||||
void InitBasisAndRestriction(const mfem::FiniteElementSpace &fes,
|
||||
const mfem::IntegrationRule &ir,
|
||||
Ceed ceed, CeedBasis *basis,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
/** @brief Initialize a CeedBasis and a CeedElemRestriction based on an
|
||||
mfem::FiniteElementSpace @a fes, and an mfem::IntegrationRule @a ir,
|
||||
and a list of @a nelem elements of indices @a indices.
|
||||
|
||||
@param[in] fes The finite element space.
|
||||
@param[in] ir The integration rule.
|
||||
@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] ceed The Ceed object.
|
||||
@param[out] basis The `CeedBasis` to initialize.
|
||||
@param[out] restr The `CeedElemRestriction` to initialize. */
|
||||
void InitBasisAndRestriction(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir,
|
||||
int nelem,
|
||||
const int* indices,
|
||||
Ceed ceed, CeedBasis *basis,
|
||||
CeedElemRestriction *restr);
|
||||
|
||||
int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field);
|
||||
|
||||
|
||||
template <typename Integrator>
|
||||
const IntegrationRule & GetRule(
|
||||
const Integrator &integ,
|
||||
const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans);
|
||||
|
||||
/// Return the path to the libCEED q-function headers.
|
||||
const std::string &GetCeedPath();
|
||||
|
||||
@@ -87,7 +125,7 @@ struct BasisHash
|
||||
};
|
||||
using BasisMap = std::unordered_map<const BasisKey, CeedBasis, BasisHash>;
|
||||
|
||||
enum restr_type {Standard, Strided};
|
||||
enum restr_type {Standard, Strided, Coeff};
|
||||
|
||||
// Hash table for CeedElemRestriction
|
||||
using RestrKey =
|
||||
@@ -117,6 +155,8 @@ namespace internal
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_CEED
|
||||
/** @warning These maps have a tendency to create bugs when adding new "types"
|
||||
of CeedBasis and CeedElemRestriction. */
|
||||
extern ceed::BasisMap ceed_basis_map;
|
||||
extern ceed::RestrMap ceed_restr_map;
|
||||
#endif
|
||||
|
||||
@@ -633,7 +633,7 @@ AlgebraicSpaceHierarchy::AlgebraicSpaceHierarchy(FiniteElementSpace &fes)
|
||||
current_order = order;
|
||||
|
||||
Ceed ceed = internal::ceed;
|
||||
InitTensorRestriction(fes, ceed, &fine_er);
|
||||
InitRestriction(fes, ceed, &fine_er);
|
||||
CeedElemRestriction er = fine_er;
|
||||
|
||||
int dim = fes.GetMesh()->Dimension();
|
||||
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
}
|
||||
~AlgebraicSpaceHierarchy()
|
||||
{
|
||||
CeedElemRestrictionDestroy(&fine_er);
|
||||
for (int i=0; i<R_tr.Size(); ++i)
|
||||
{
|
||||
delete R_tr[i];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,220 @@
|
||||
// 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_COMPLEX_BLOCK_STATIC_CONDENSATION
|
||||
#define MFEM_COMPLEX_BLOCK_STATIC_CONDENSATION
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
class ComplexBlockStaticCondensation
|
||||
{
|
||||
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_r = nullptr;
|
||||
BlockMatrix * S_i = nullptr;
|
||||
BlockMatrix * S_e_r = nullptr;
|
||||
BlockMatrix * S_e_i = nullptr;
|
||||
ComplexOperator * S = nullptr;
|
||||
|
||||
BlockVector * y_r = nullptr;
|
||||
BlockVector * y_i = nullptr;
|
||||
Vector * y = nullptr;
|
||||
|
||||
Array<ComplexDenseMatrix * > 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_r = nullptr;
|
||||
BlockOperator * pS_e_r = nullptr;
|
||||
BlockOperator * pS_i = nullptr;
|
||||
BlockOperator * pS_e_i = 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
|
||||
ComplexDenseMatrix * GetLocalShurComplement(int el, const Array<int> & tr_idx,
|
||||
const Array<int> & int_idx,
|
||||
const ComplexDenseMatrix & elmat,
|
||||
const Vector & elvect_r,
|
||||
const Vector & elvect_i,
|
||||
Vector & rvect_r,
|
||||
Vector & rvect_i);
|
||||
|
||||
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:
|
||||
|
||||
ComplexBlockStaticCondensation(Array<FiniteElementSpace *> & fes_);
|
||||
|
||||
~ComplexBlockStaticCondensation();
|
||||
|
||||
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, ComplexDenseMatrix &elmat,
|
||||
Vector & elvect_r, Vector & elvect_i);
|
||||
|
||||
/// 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_r;
|
||||
#else
|
||||
return S_e_r || pS_e_r;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// Return the serial Schur complement matrix.
|
||||
BlockMatrix &GetMatrix_r() { return *S_r; }
|
||||
BlockMatrix &GetMatrix_i() { return *S_i; }
|
||||
ComplexOperator &GetComplexOperator()
|
||||
{
|
||||
if (!S)
|
||||
{
|
||||
if (parallel)
|
||||
{
|
||||
S = new ComplexOperator(pS_r,pS_i,false,false);
|
||||
}
|
||||
else
|
||||
{
|
||||
S = new ComplexOperator(S_r,S_i,false,false);
|
||||
}
|
||||
}
|
||||
return *S;
|
||||
}
|
||||
|
||||
/// Return the eliminated part of the serial Schur complement matrix.
|
||||
BlockMatrix &GetMatrixElim_r() { return *S_e_r; }
|
||||
BlockMatrix &GetMatrixElim_i() { return *S_e_i; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Return the parallel Schur complement matrix.
|
||||
BlockOperator &GetParallelMatrix_r() { return *pS_r; }
|
||||
BlockOperator &GetParallelMatrix_i() { return *pS_i; }
|
||||
|
||||
/// Return the eliminated part of the parallel Schur complement matrix.
|
||||
BlockOperator &GetParallelMatrixElim_r() { return *pS_e_r; }
|
||||
BlockOperator &GetParallelMatrixElim_i() { return *pS_e_i; }
|
||||
|
||||
void ParallelAssemble(BlockMatrix *m_r, BlockMatrix*m_i);
|
||||
#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
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user