Compare commits
480
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1ea8aefb7 | ||
|
|
04444dc37e | ||
|
|
6f204dde67 | ||
|
|
f04f98752b | ||
|
|
9b31004ce5 | ||
|
|
04571c5ddd | ||
|
|
e448b71831 | ||
|
|
48e415e17e | ||
|
|
8b249e8004 | ||
|
|
9c17c781e1 | ||
|
|
f72b0151e2 | ||
|
|
1585e7d9f1 | ||
|
|
ae4a2ee9a6 | ||
|
|
14e8c6ce45 | ||
|
|
05f5967267 | ||
|
|
d9e0018e98 | ||
|
|
c8ef9676e8 | ||
|
|
b1def1e3fa | ||
|
|
f08dba7b84 | ||
|
|
7e75c7f6fb | ||
|
|
a1962fa492 | ||
|
|
ce7fefb6eb | ||
|
|
503b286f98 | ||
|
|
cd0616fae0 | ||
|
|
891b675c7f | ||
|
|
951cf8886b | ||
|
|
1ff144a358 | ||
|
|
f0f1052c99 | ||
|
|
2c02d1b3cb | ||
|
|
9a87b34c47 | ||
|
|
80e40ace14 | ||
|
|
f5d71a2798 | ||
|
|
9981355ba2 | ||
|
|
f77dc8d7e9 | ||
|
|
1416665dc3 | ||
|
|
c23b850b77 | ||
|
|
ebda52d76c | ||
|
|
f0d4d84a91 | ||
|
|
c8ab3cbf69 | ||
|
|
d7c781a6f9 | ||
|
|
c9b2ed7a65 | ||
|
|
9eaa3cdf0a | ||
|
|
17d1afc3b7 | ||
|
|
0eaa3b521a | ||
|
|
46d05563d9 | ||
|
|
27c1ea66a4 | ||
|
|
7b69944246 | ||
|
|
fa13c848c9 | ||
|
|
8d09be7080 | ||
|
|
7f71dacae6 | ||
|
|
a7b74155f7 | ||
|
|
0c20eef8fe | ||
|
|
842514bde2 | ||
|
|
ee498a19f7 | ||
|
|
bd5f9d80b4 | ||
|
|
6ee3bbde89 | ||
|
|
92f4fe3bd0 | ||
|
|
0171b4b02d | ||
|
|
68e3a929c2 | ||
|
|
06d18956f8 | ||
|
|
60c2ac77d1 | ||
|
|
ee23534091 | ||
|
|
c0da3d6aa9 | ||
|
|
fef38a9fd2 | ||
|
|
01aa047be1 | ||
|
|
8c68e8402f | ||
|
|
a24e3f6dfb | ||
|
|
950f406803 | ||
|
|
ade7aedfc6 | ||
|
|
79bca13634 | ||
|
|
5048b1a219 | ||
|
|
b84988d5f1 | ||
|
|
55aa12823c | ||
|
|
80555fa132 | ||
|
|
9a98c2be01 | ||
|
|
268231dd09 | ||
|
|
10880e5ad2 | ||
|
|
3df2f14eb9 | ||
|
|
21f1404580 | ||
|
|
63a38bed84 | ||
|
|
867c0a0e7e | ||
|
|
270c4d5175 | ||
|
|
0779fbbc72 | ||
|
|
16f9cb63a1 | ||
|
|
58da879f06 | ||
|
|
09b9e1c775 | ||
|
|
1ac86956d1 | ||
|
|
48622d3f1e | ||
|
|
5cd7f88da0 | ||
|
|
cc6a34b575 | ||
|
|
9cf0b8cb08 | ||
|
|
4843835f98 | ||
|
|
161630bf12 | ||
|
|
fa9fdc6576 | ||
|
|
03c99b8dfe | ||
|
|
b65e7ee791 | ||
|
|
473084c8c5 | ||
|
|
e25f778f7b | ||
|
|
2f0bbc6fc4 | ||
|
|
b5baebd0b3 | ||
|
|
c9ecc5b466 | ||
|
|
f38de5059e | ||
|
|
0ff0e1c76b | ||
|
|
c7cba857af | ||
|
|
85a16c2e43 | ||
|
|
3c4d103982 | ||
|
|
fcca0eaa71 | ||
|
|
0bfa06f87d | ||
|
|
3aed584bf7 | ||
|
|
c4d944cfad | ||
|
|
c05114555f | ||
|
|
dfc15d77e5 | ||
|
|
25e5ac7db0 | ||
|
|
d0533903f3 | ||
|
|
38ed1e049b | ||
|
|
48e258a60c | ||
|
|
e1b9c355e6 | ||
|
|
c3db6ed873 | ||
|
|
56b3e1ecc6 | ||
|
|
5385e090f7 | ||
|
|
ecb7767b70 | ||
|
|
42bf39cd8c | ||
|
|
716f92d636 | ||
|
|
efdc17ce07 | ||
|
|
ffab9d82fa | ||
|
|
891eaf2829 | ||
|
|
053804f759 | ||
|
|
a8af93c4ec | ||
|
|
ac7fa02927 | ||
|
|
eeca0b4cd5 | ||
|
|
ad18d18be1 | ||
|
|
00a1f5ed64 | ||
|
|
a76b0dbc0e | ||
|
|
59ce107b33 | ||
|
|
ce1036ccc8 | ||
|
|
b6861ed26f | ||
|
|
8522cceb89 | ||
|
|
dec9509a5d | ||
|
|
5f02c6f64b | ||
|
|
8a69b52f48 | ||
|
|
51a0f8accb | ||
|
|
eb4fa33a7b | ||
|
|
42d7d20e43 | ||
|
|
75f8ca8cd4 | ||
|
|
36e915f226 | ||
|
|
f236d70a19 | ||
|
|
85e33c6645 | ||
|
|
7b62f035a5 | ||
|
|
81fb02389f | ||
|
|
23666fd4d8 | ||
|
|
f3a60e2f08 | ||
|
|
8777bc0810 | ||
|
|
de97775bd0 | ||
|
|
ad453255a5 | ||
|
|
2143ed5ca8 | ||
|
|
2f21794999 | ||
|
|
2aa8372cdc | ||
|
|
0176c7664c | ||
|
|
e1c0705785 | ||
|
|
50b8f67bd1 | ||
|
|
a4d114ad6e | ||
|
|
1e5609f6c9 | ||
|
|
c21d90589b | ||
|
|
bef7183ce7 | ||
|
|
db7da59b03 | ||
|
|
28194a6736 | ||
|
|
f6eda1fce6 | ||
|
|
a1bcd7443d | ||
|
|
9c4d742db8 | ||
|
|
16ac6c40a3 | ||
|
|
ee2484383c | ||
|
|
1863dafeca | ||
|
|
a4c7828ac2 | ||
|
|
0a02f5737a | ||
|
|
154a1840d0 | ||
|
|
13e83cbd14 | ||
|
|
f64405ebb6 | ||
|
|
92f2057650 | ||
|
|
f3558cb78c | ||
|
|
d5ddb83c9d | ||
|
|
869b0e48a6 | ||
|
|
75180606f8 | ||
|
|
3cac4326e4 | ||
|
|
da5e8b5844 | ||
|
|
081a90d1fa | ||
|
|
d06a3eec7f | ||
|
|
2766e56f2d | ||
|
|
0c781120a2 | ||
|
|
449c9f5903 | ||
|
|
6f793edfb7 | ||
|
|
6455a0c1fc | ||
|
|
b19c3c7e01 | ||
|
|
fb7c8af59e | ||
|
|
f6ea1ea0da | ||
|
|
7a6313d725 | ||
|
|
84bbead832 | ||
|
|
ef6510b42e | ||
|
|
27ce32de07 | ||
|
|
341f6ec683 | ||
|
|
8c88e1e26a | ||
|
|
cabdb42bb4 | ||
|
|
e4a69c6245 | ||
|
|
dbb751d7fb | ||
|
|
e447f090cc | ||
|
|
c3096d08bf | ||
|
|
67b906ea3b | ||
|
|
9167fc0c58 | ||
|
|
e48ffe3579 | ||
|
|
a7697c1db1 | ||
|
|
36c1cce132 | ||
|
|
38f257cf87 | ||
|
|
a0022e0330 | ||
|
|
3a2f286e2d | ||
|
|
58e6c4fc6d | ||
|
|
7031f7cb80 | ||
|
|
afaced7cd6 | ||
|
|
d671ca4712 | ||
|
|
1095aee346 | ||
|
|
ba8764300c | ||
|
|
510477a204 | ||
|
|
0b9ae2202c | ||
|
|
60641259b2 | ||
|
|
99e2b5023d | ||
|
|
4c59f6cfe6 | ||
|
|
670dfb2d98 | ||
|
|
4167f0027c | ||
|
|
b59ccd206d | ||
|
|
cee913cc04 | ||
|
|
76b033ad36 | ||
|
|
0cd0a98198 | ||
|
|
bd5d928084 | ||
|
|
5e62d62c3d | ||
|
|
5faba43544 | ||
|
|
20805d87b4 | ||
|
|
0119d25dfc | ||
|
|
22c15267f9 | ||
|
|
6c83fec2da | ||
|
|
411a361ebf | ||
|
|
bc845844ea | ||
|
|
35ceed5193 | ||
|
|
ce52e1f51f | ||
|
|
16a21b366e | ||
|
|
d540fa5a12 | ||
|
|
1221dea58e | ||
|
|
a5b9a7948f | ||
|
|
386e7e8c6a | ||
|
|
26077ab9aa | ||
|
|
0ee57ac12d | ||
|
|
87bdb90bcc | ||
|
|
848b54d13a | ||
|
|
62214f61ae | ||
|
|
dee971c308 | ||
|
|
9b93f1c1e1 | ||
|
|
be03c9703f | ||
|
|
b885fdc50f | ||
|
|
e4c0069ad9 | ||
|
|
9fe53c2403 | ||
|
|
bb12710561 | ||
|
|
f3276a0d5d | ||
|
|
00b9678b1d | ||
|
|
426b5dc6cd | ||
|
|
5e15a2e29b | ||
|
|
d168f5b489 | ||
|
|
7a5d13e86a | ||
|
|
2e0d1697b9 | ||
|
|
87168f51ab | ||
|
|
2558e32ee0 | ||
|
|
7521efeff9 | ||
|
|
3b23fd4941 | ||
|
|
d6a084322b | ||
|
|
d2a38dd3d4 | ||
|
|
17aab43082 | ||
|
|
e5c3cf4ee3 | ||
|
|
b09cc88bab | ||
|
|
620904124e | ||
|
|
6e55cfdc39 | ||
|
|
bef23c5770 | ||
|
|
a6fcf162a6 | ||
|
|
ba9af41877 | ||
|
|
2762b9dbfc | ||
|
|
d75a153df8 | ||
|
|
0dd81462c0 | ||
|
|
25514d6e8e | ||
|
|
9f01e61a57 | ||
|
|
90fdd7e762 | ||
|
|
cfa6594977 | ||
|
|
b170c6ae54 | ||
|
|
c4b4ad3224 | ||
|
|
cecd75aff6 | ||
|
|
50b197e754 | ||
|
|
69dc2b5142 | ||
|
|
fc26f0a773 | ||
|
|
d160b88706 | ||
|
|
007d5e3e43 | ||
|
|
fd78d48dfb | ||
|
|
c1d1df3d80 | ||
|
|
c0d32918b0 | ||
|
|
03937d0dc2 | ||
|
|
63e24c2a25 | ||
|
|
c202f9244a | ||
|
|
76cf9a2d00 | ||
|
|
4f796b3708 | ||
|
|
2399e5e294 | ||
|
|
205b694162 | ||
|
|
f2b661ca05 | ||
|
|
c8ef971f03 | ||
|
|
10aa1133b2 | ||
|
|
05c3a2c83f | ||
|
|
d194a26542 | ||
|
|
a3a368dfd9 | ||
|
|
e8f39487b9 | ||
|
|
ec6e2a7c74 | ||
|
|
cb1f54ed2b | ||
|
|
9a18da5aaa | ||
|
|
02822e620a | ||
|
|
4e31111827 | ||
|
|
0cbf5b5562 | ||
|
|
d81bbc1214 | ||
|
|
41255f308e | ||
|
|
96be0cafdf | ||
|
|
55fc2f806c | ||
|
|
25a26b5e6f | ||
|
|
d6aacb95cf | ||
|
|
0466624b21 | ||
|
|
42e2d0b9a4 | ||
|
|
08bf7f991b | ||
|
|
0503cbd41c | ||
|
|
74d9671ec8 | ||
|
|
9917fa8306 | ||
|
|
74f64934e8 | ||
|
|
8ff5affe45 | ||
|
|
2e7a6d745c | ||
|
|
eb95b46fad | ||
|
|
a0778c759e | ||
|
|
a922c1f2c3 | ||
|
|
17600f59f2 | ||
|
|
6cfec88f47 | ||
|
|
f11009d674 | ||
|
|
5df384f6ef | ||
|
|
daa555b68d | ||
|
|
76cfafe700 | ||
|
|
58976c6f41 | ||
|
|
aa4f1bc8e4 | ||
|
|
fcc4b2dade | ||
|
|
3c137f36cb | ||
|
|
9211b97eb0 | ||
|
|
9cb3c1e1a6 | ||
|
|
1ed3b48c2e | ||
|
|
fbd9189e7b | ||
|
|
1dd889cb16 | ||
|
|
2e8fbd661a | ||
|
|
6e424dba6e | ||
|
|
0f449eb906 | ||
|
|
cdca79060b | ||
|
|
0114739e5e | ||
|
|
708b642be2 | ||
|
|
d78357494d | ||
|
|
9398b1a6e0 | ||
|
|
b23a087f42 | ||
|
|
3505a5a354 | ||
|
|
72203ebb5c | ||
|
|
d62ffe149a | ||
|
|
86b2ad3a61 | ||
|
|
35ba876ecf | ||
|
|
2886c4b211 | ||
|
|
9455ed83b2 | ||
|
|
cc46bfdfcc | ||
|
|
010e96b6f0 | ||
|
|
edf551c40a | ||
|
|
fd36e1b177 | ||
|
|
d390f9a7d1 | ||
|
|
128f650dd3 | ||
|
|
2bbb317831 | ||
|
|
36835e62e0 | ||
|
|
70867ab87b | ||
|
|
4b59c90b08 | ||
|
|
b538e9d34c | ||
|
|
79733572d7 | ||
|
|
6b5d1e55de | ||
|
|
6b5eb2f92f | ||
|
|
49c346233e | ||
|
|
505661eb5f | ||
|
|
e81352715e | ||
|
|
159f1873d6 | ||
|
|
fa07a503dd | ||
|
|
d65fcc5d8c | ||
|
|
1696197f54 | ||
|
|
19089ac132 | ||
|
|
5e9d32a0f7 | ||
|
|
724866141a | ||
|
|
f17c9eef12 | ||
|
|
69c1d0d15b | ||
|
|
5f7d138aef | ||
|
|
4659602e63 | ||
|
|
1771a26426 | ||
|
|
b42a383201 | ||
|
|
deae7e4997 | ||
|
|
6142015168 | ||
|
|
305a7f1e02 | ||
|
|
0bff07026c | ||
|
|
32c3e5dfd8 | ||
|
|
ee78679277 | ||
|
|
185f8b099d | ||
|
|
89a0f18b30 | ||
|
|
4cbc345ba6 | ||
|
|
abe3843712 | ||
|
|
3c2d1e4814 | ||
|
|
a8c4ed3c79 | ||
|
|
0aa73fa285 | ||
|
|
f1652b5ba0 | ||
|
|
fed0baf6b6 | ||
|
|
49a8ee5b54 | ||
|
|
c9569a0629 | ||
|
|
a4e83b5836 | ||
|
|
8b285047bd | ||
|
|
f999be0372 | ||
|
|
e639cfd75b | ||
|
|
2753692303 | ||
|
|
2722e979fc | ||
|
|
144c0ce106 | ||
|
|
b64172e12e | ||
|
|
c0072fcb84 | ||
|
|
37cd34a0e1 | ||
|
|
005be84af4 | ||
|
|
0ef682c156 | ||
|
|
3faa838774 | ||
|
|
f10c2793c9 | ||
|
|
9f4b83362a | ||
|
|
8ef423ca65 | ||
|
|
aff5173656 | ||
|
|
2164f07f01 | ||
|
|
2b3657c76b | ||
|
|
63d45eb194 | ||
|
|
fc0037da31 | ||
|
|
795a1a29a3 | ||
|
|
476d9c5111 | ||
|
|
64625f7333 | ||
|
|
4ae8c1bc2b | ||
|
|
4ae97f1834 | ||
|
|
816e8e4bea | ||
|
|
c14d630f14 | ||
|
|
899433f79f | ||
|
|
ff9294e5b0 | ||
|
|
16db883427 | ||
|
|
1834268091 | ||
|
|
947e25f769 | ||
|
|
9fbe90527a | ||
|
|
076f1450e5 | ||
|
|
bfa672c644 | ||
|
|
30ac5d8d38 | ||
|
|
5a2b05cc35 | ||
|
|
afe388a573 | ||
|
|
50a9cce9f4 | ||
|
|
8ee2fdbfd9 | ||
|
|
d6b4594150 | ||
|
|
771e263db8 | ||
|
|
ee13fec158 | ||
|
|
0162a6727d | ||
|
|
31fae9d195 | ||
|
|
6ce5fe8fac | ||
|
|
1739cebfdb | ||
|
|
9e5b93a532 | ||
|
|
a7ca72c59f | ||
|
|
8267c7ea64 | ||
|
|
d2cf8fef73 | ||
|
|
2df3fc8ddb | ||
|
|
59ee0d93c6 | ||
|
|
d8164fd158 | ||
|
|
23ce5f08e7 | ||
|
|
e059253549 | ||
|
|
c89807da1e | ||
|
|
53de3bde2c | ||
|
|
56414350e2 | ||
|
|
e5111df7c6 | ||
|
|
4fe80816d2 | ||
|
|
0839f915b4 | ||
|
|
23b4a5a5b8 | ||
|
|
41d15a3a0a | ||
|
|
daca70bf80 | ||
|
|
0e02aa947a |
@@ -94,6 +94,16 @@ inputs:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
# Unfortunately, "uses:" fields cannot have references to variables like
|
||||
# ${{env.MFEM_ACTIONS_VERSION}}, so the branch/tag name has to be hard coded.
|
||||
# Therefore, in the future, when updating the version of the
|
||||
# mfem/github-actions to use, we'll have to replace:
|
||||
# - all definitions of MFEM_ACTIONS_VERSION and
|
||||
# - all "uses:" fields that refer to mfem/github-actions.
|
||||
MFEM_ACTIONS_VERSION:
|
||||
description: Version (branch or tag) of the mfem/github-actions to use.
|
||||
default: v2.7
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -118,6 +128,7 @@ runs:
|
||||
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MFEM_ACTIONS_VERSION=${{inputs.MFEM_ACTIONS_VERSION}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (dir)
|
||||
|
||||
@@ -53,7 +53,7 @@ runs:
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- uses: mfem/github-actions/build-mfem@v2.6
|
||||
- uses: mfem/github-actions/build-mfem@v2.7
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
env:
|
||||
CXXFLAGS: ${{env.CXXFLAGS}}
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
name: 'Install MPI'
|
||||
description: 'Installs MPI and set up its environment variables'
|
||||
|
||||
inputs:
|
||||
NO_FLAGS:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -27,6 +32,7 @@ runs:
|
||||
shell: bash
|
||||
|
||||
- name: Env (bis)
|
||||
if: ${{ inputs.NO_FLAGS != 'true' }}
|
||||
run: |
|
||||
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
|
||||
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
|
||||
|
||||
@@ -37,14 +37,14 @@ runs:
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{env.MFEM_ACTIONS_VERSION}}
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
|
||||
- name: Hypre/Metis links
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
@@ -29,16 +29,12 @@ Runs a number of static repository-level sanity checks.
|
||||
|
||||
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
|
||||
|
||||
## `mfem-analysis.yml` (`build-analysis`)
|
||||
|
||||
Checks if the code builds and satisfies minimal requirements.
|
||||
|
||||
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
|
||||
|
||||
## `builds-and-tests.yml`
|
||||
|
||||
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
|
||||
|
||||
One matrix job runs `tests/scripts/gitignore` after `make test-noclean` to check generated artifacts against `.gitignore`.
|
||||
|
||||
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
|
||||
|
||||
- `mfem/github-actions/build-hypre`
|
||||
|
||||
@@ -40,6 +40,7 @@ env:
|
||||
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
MFEM_TOP_DIR: mfem
|
||||
MFEM_ACTIONS_VERSION: v2.7
|
||||
|
||||
# Note for future improvements:
|
||||
#
|
||||
@@ -110,6 +111,7 @@ jobs:
|
||||
build-system: make
|
||||
hypre-target: int64
|
||||
precision: fp64
|
||||
gitignore-check: YES
|
||||
- os: ubuntu-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
@@ -170,20 +172,6 @@ jobs:
|
||||
env
|
||||
shell: bash
|
||||
|
||||
# For info on Xcode see:
|
||||
# - https://github.com/actions/runner-images/issues/12541
|
||||
# - https://github.com/actions/runner-images/blob/releases/macos-15-arm64/20250811/images/macos/macos-15-arm64-Readme.md#xcode
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
XCODE_PATH="/Applications/Xcode_16.4.app"
|
||||
echo "> sudo xcode-select -s ${XCODE_PATH}"
|
||||
sudo xcode-select -s ${XCODE_PATH}
|
||||
echo "> g++ -v"
|
||||
g++ -v
|
||||
echo "> clang++ -v"
|
||||
clang++ -v
|
||||
|
||||
# Only get MPI if defined for the job.
|
||||
# TODO: It would be nice to have only one step, e.g. with a dedicated
|
||||
# action, but I (@adrienbernede) don't see how at the moment.
|
||||
@@ -228,11 +216,11 @@ jobs:
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -242,7 +230,7 @@ jobs:
|
||||
|
||||
- name: get hypre (Windows)
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -258,11 +246,11 @@ jobs:
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
with:
|
||||
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
@@ -304,7 +292,7 @@ jobs:
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.6
|
||||
uses: mfem/github-actions/build-mfem@v2.7
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
with:
|
||||
@@ -330,7 +318,13 @@ jobs:
|
||||
- name: tests
|
||||
if: matrix.build-system == 'make' && (matrix.target == 'opt' || matrix.os == 'ubuntu-latest')
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make test
|
||||
cd ${{ env.MFEM_TOP_DIR }}
|
||||
if [[ "${{ matrix.gitignore-check }}" == "YES" ]]; then
|
||||
make test-noclean
|
||||
else
|
||||
make test
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: cmake checks
|
||||
if: matrix.build-system == 'cmake' && matrix.target == 'dbg'
|
||||
@@ -375,10 +369,16 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.6
|
||||
uses: mfem/github-actions/upload-coverage@v2.7
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: gitignore
|
||||
if: matrix.gitignore-check == 'YES'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }}/tests/scripts
|
||||
./runtest gitignore
|
||||
|
||||
@@ -14,9 +14,19 @@ name: "Static Analysis"
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "next"]
|
||||
paths-ignore: &docs-only-paths
|
||||
- "**/*.md"
|
||||
- "doc/**"
|
||||
- ".binder/**"
|
||||
- "CITATION.cff"
|
||||
- "LICENSE"
|
||||
- "NOTICE"
|
||||
- "CHANGELOG"
|
||||
- "INSTALL"
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
paths-ignore: *docs-only-paths
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
name: "Build Analysis"
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HYPRE_ARCHIVE: v2.19.0.tar.gz
|
||||
HYPRE_TOP_DIR: hypre-2.19.0
|
||||
METIS_ARCHIVE: metis-4.0.3.tar.gz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
COVERAGE_ENV: mfem-coverage
|
||||
|
||||
jobs:
|
||||
gitignore:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout MFEM
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
- name: Get MPI (Linux)
|
||||
run: |
|
||||
sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
export OMPI_MCA_rmaps_base_oversubscribe=1
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: int32
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@v2.6
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
mfem-dir: mfem
|
||||
|
||||
- name: test (no clean)
|
||||
run: |
|
||||
cd mfem && make test-noclean
|
||||
|
||||
- name: gitignore
|
||||
run: |
|
||||
cd mfem/tests/scripts
|
||||
./runtest gitignore
|
||||
@@ -19,18 +19,22 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
with:
|
||||
archive: ${{env.HYPRE_TGZ}}
|
||||
dir: ${{env.HYPRE_DIR}}
|
||||
|
||||
@@ -19,18 +19,22 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
with:
|
||||
archive: ${{env.METIS_TGZ}}
|
||||
dir: ${{env.METIS_DIR}}
|
||||
|
||||
@@ -17,7 +17,17 @@ permissions:
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "next"]
|
||||
paths-ignore: &docs-only-paths
|
||||
- "**/*.md"
|
||||
- "doc/**"
|
||||
- ".binder/**"
|
||||
- "CITATION.cff"
|
||||
- "LICENSE"
|
||||
- "NOTICE"
|
||||
- "CHANGELOG"
|
||||
- "INSTALL"
|
||||
pull_request:
|
||||
paths-ignore: *docs-only-paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
||||
@@ -11,35 +11,49 @@
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
- Policy for AI-assisted contribution added to CONTRIBUTING.md
|
||||
- Added policy for AI-assisted contribution to CONTRIBUTING.md.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
- Improved FindPointsGSLIB surface mesh capability with support for simplices
|
||||
and an option to specify axis-aligned bounding box padding for near-surface
|
||||
point queries.
|
||||
|
||||
- Added GPU-enabled partial assembly for simplicial Bernstein H1 basis based on
|
||||
ragged tensor algorithms (see DOI: 10.1137/11082539X) for mass and diffusion
|
||||
integrators.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive weight rules
|
||||
for triangles (orders 0-25) and tetrahedra (orders 0-20). These rules
|
||||
guarantee all-positive weights and interior quadrature points, improving
|
||||
numerical stability. Higher orders fall back to Grundmann-Moller.
|
||||
* Triangle rules: Witherden and Vincent, DOI: 10.1016/j.camwa.2015.03.017
|
||||
* Tet rules (d=1-13): Witherden and Vincent (same as above)
|
||||
* Tet rules (d=14-20): Chuluunbaatar et al., DOI: 10.1016/j.camwa.2022.08.016
|
||||
|
||||
- Added support for general 1D Gauss-Jacobi quadrature rules and Stroud conical
|
||||
quadrature rules on triangles and tetrahedra.
|
||||
|
||||
- Improved the GridFunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behavior has not changed.
|
||||
|
||||
- Added GridFunction projection methods for trace spaces, i.e., project
|
||||
coefficients on the mesh skeleton.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds plus
|
||||
recursive subdivision.
|
||||
|
||||
- Extend FindPointsGSLIB to support surface meshes.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive-weight
|
||||
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
|
||||
rules guarantee all-positive weights and interior quadrature points,
|
||||
improving numerical stability. Higher orders fall back to Grundmann-Moller.
|
||||
Triangle rules: Witherden & Vincent, Comput. Math. Appl. 69(10):1232-1241,
|
||||
2015.
|
||||
Tet rules (d=1-13): Witherden & Vincent (ibid).
|
||||
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
|
||||
2022.
|
||||
|
||||
- Improved the gridfunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds +
|
||||
recursive subdivision.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added option to guarantee mesh validity during TMOP-based r-adaptivity, using
|
||||
bounds on the determinant of the mesh transformation Jacobian.
|
||||
|
||||
- Added PA support for TMOP's adaptive limiting functionality. Multiple
|
||||
GridFunctions and Coefficients can be combined to form a composite term.
|
||||
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
@@ -48,22 +62,35 @@ Meshing improvements
|
||||
parallel visualization, e.g. with GLVis. This is supported by both the Print
|
||||
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added support for trace spaces in PRefinementTransferOperator. This is used in
|
||||
PRefinement multigrid methods for problems posed on trace spaces (see e.g. the
|
||||
DPG miniapps).
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added device assembly support for 3D H(curl) VectorFEDomainLFIntegrator.
|
||||
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
|
||||
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- The Lorentz miniapp (in miniapps/electromagnetics) has been updated to
|
||||
leverage the ParticleSet capability.
|
||||
|
||||
- Added (Complex)PRefinementMultigrid solver option in the DPG miniapps.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Fixed signed DOF handling in parallel grid-function reading (read constructor)
|
||||
and saving via ParGridFunction::SaveAsOne(). Simplified the process of
|
||||
applying the DOF signs by using the new method ApplyDofSigns() in class
|
||||
ParFiniteElementSpace -- the method will return immediately if no sign flips
|
||||
are needed.
|
||||
- Fixed signed DOF handling in ParGridFunction reading (read constructor) and
|
||||
saving via SaveAsOne(). Simplified the process of applying the DOF signs by
|
||||
using the new method ApplyDofSigns() in class ParFiniteElementSpace: the
|
||||
method will return immediately if no sign flips are needed.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
|
||||
@@ -18,19 +18,17 @@
|
||||
|
||||
if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(GSLIB_FETCH_VERSION 1.0.9)
|
||||
set(GSLIB_C_FLAGS ${CMAKE_C_FLAGS_${BUILD_TYPE}})
|
||||
if (CMAKE_C_FLAGS)
|
||||
set(GSLIB_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
endif()
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(GSLIB_C_FLAGS "${GSLIB_C_FLAGS} -fPIC")
|
||||
endif()
|
||||
add_library(GSLIB STATIC IMPORTED)
|
||||
# set options (technically flags because GSLIB does not use cmake)
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(GSLIB_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(GSLIB_FLAGS "${GSLIB_FLAGS} -fPIC")
|
||||
endif()
|
||||
# define external project and create future include directory so it is present
|
||||
# to pass CMake checks at end of MFEM configuration step
|
||||
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_C_FLAGS}")
|
||||
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_FLAGS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/gslib)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(gslib
|
||||
@@ -40,7 +38,7 @@ if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS= ${GSLIB_C_FLAGS}"
|
||||
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS=${GSLIB_FLAGS}"
|
||||
INSTALL_COMMAND "")
|
||||
file(MAKE_DIRECTORY ${PREFIX}/include)
|
||||
# set imported library target properties
|
||||
|
||||
@@ -44,6 +44,9 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
# set options and associated dependencies
|
||||
set(HYPRE_CMAKE_OPTIONS "")
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (BUILD_SHARED_LIBS)
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON)
|
||||
endif()
|
||||
# collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
|
||||
get_cmake_property(all_vars VARIABLES)
|
||||
foreach(var ${all_vars})
|
||||
@@ -95,7 +98,6 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
SOURCE_SUBDIR src
|
||||
PREFIX ${HYPRE_INSTALL}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . -- -j${CMAKE_BUILD_PARALLEL_LEVEL}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${HYPRE_INSTALL} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${HYPRE_CMAKE_OPTIONS})
|
||||
file(MAKE_DIRECTORY ${HYPRE_INSTALL}/include)
|
||||
# set imported library target properties
|
||||
|
||||
@@ -19,10 +19,18 @@
|
||||
# - METIS_VERSION_5 (cache variable)
|
||||
|
||||
if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
set(METIS_FETCH_VERSION 4.0.3)
|
||||
add_library(METIS STATIC IMPORTED)
|
||||
# set options (technically flags because METIS does not use cmake)
|
||||
set(METIS_FLAGS "-Wno-implicit-int -Wno-incompatible-pointer-types")
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} -fPIC")
|
||||
endif()
|
||||
# define external project
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with ${METIS_FLAGS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(metis
|
||||
@@ -32,7 +40,7 @@ if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
|
||||
BUILD_COMMAND $(MAKE) COPTIONS=-Wno-incompatible-pointer-types
|
||||
BUILD_COMMAND $(MAKE) clean && $(MAKE) "OPTFLAGS=${METIS_FLAGS}"
|
||||
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
|
||||
# set imported library target properties
|
||||
add_dependencies(METIS metis)
|
||||
|
||||
@@ -22,15 +22,15 @@ include(MfemCmakeUtilities)
|
||||
mfem_find_package(SuiteSparse SuiteSparse SuiteSparse_DIR "" "" "" ""
|
||||
"Paths to headers required by SuiteSparse."
|
||||
"Libraries required by SuiteSparse."
|
||||
ADD_COMPONENT "UMFPACK" "include;suitesparse" umfpack.h "lib" umfpack
|
||||
ADD_COMPONENT "KLU" "include;suitesparse" klu.h "lib" klu
|
||||
ADD_COMPONENT "AMD" "include;suitesparse" amd.h "lib" amd
|
||||
ADD_COMPONENT "BTF" "include;suitesparse" btf.h "lib" btf
|
||||
ADD_COMPONENT "CHOLMOD" "include;suitesparse" cholmod.h "lib" cholmod
|
||||
ADD_COMPONENT "COLAMD" "include;suitesparse" colamd.h "lib" colamd
|
||||
ADD_COMPONENT "CAMD" "include;suitesparse" camd.h "lib" camd
|
||||
ADD_COMPONENT "CCOLAMD" "include;suitesparse" ccolamd.h "lib" ccolamd
|
||||
ADD_COMPONENT "config" "include;suitesparse" SuiteSparse_config.h "lib"
|
||||
ADD_COMPONENT "UMFPACK" "include;include/suitesparse;suitesparse" umfpack.h "lib" umfpack
|
||||
ADD_COMPONENT "KLU" "include;include/suitesparse;suitesparse" klu.h "lib" klu
|
||||
ADD_COMPONENT "AMD" "include;include/suitesparse;suitesparse" amd.h "lib" amd
|
||||
ADD_COMPONENT "BTF" "include;include/suitesparse;suitesparse" btf.h "lib" btf
|
||||
ADD_COMPONENT "CHOLMOD" "include;include/suitesparse;suitesparse" cholmod.h "lib" cholmod
|
||||
ADD_COMPONENT "COLAMD" "include;include/suitesparse;suitesparse" colamd.h "lib" colamd
|
||||
ADD_COMPONENT "CAMD" "include;include/suitesparse;suitesparse" camd.h "lib" camd
|
||||
ADD_COMPONENT "CCOLAMD" "include;include/suitesparse;suitesparse" ccolamd.h "lib" ccolamd
|
||||
ADD_COMPONENT "config" "include;include/suitesparse;suitesparse" SuiteSparse_config.h "lib"
|
||||
suitesparseconfig)
|
||||
|
||||
if (SuiteSparse_FOUND AND METIS_VERSION_5)
|
||||
|
||||
+13
-12
@@ -50,6 +50,10 @@
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// ex1 -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// ex1 -pa -d gpu -m ../data/inline-tri.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
@@ -138,25 +142,25 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
auto basis_type = (pa && mesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
}
|
||||
else if (mesh.GetNodes())
|
||||
{
|
||||
fec = mesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
}
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
@@ -292,10 +296,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
if (order > 0) { delete fec; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+14
-13
@@ -42,7 +42,11 @@
|
||||
// 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
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/beam-tet.mesh
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tri.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
@@ -165,19 +169,20 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// use continuous Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
auto basis_type = (pa && pmesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
@@ -185,8 +190,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
@@ -333,10 +337,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
if (order > 0) { delete fec; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-1
@@ -133,7 +133,7 @@ set(SRCS
|
||||
tmop/assemble/diag2.cpp
|
||||
tmop/assemble/grad2_limit.cpp
|
||||
tmop/assemble/grad2.cpp
|
||||
tmop/assemble/diag3_limit.cpp
|
||||
tmop/assemble/diag3_limit.cpp
|
||||
tmop/assemble/diag3.cpp
|
||||
tmop/assemble/grad3_limit.cpp
|
||||
tmop/assemble/grad3.cpp
|
||||
@@ -195,12 +195,14 @@ set(HDRS
|
||||
integ/bilininteg_dgtrace_kernels.hpp
|
||||
integ/bilininteg_vecdiffusion_kernels.hpp
|
||||
integ/bilininteg_convection_kernels.hpp
|
||||
integ/bilininteg_diffusion_pa_simplices.hpp
|
||||
integ/bilininteg_diffusion_kernels.hpp
|
||||
integ/bilininteg_elasticity_kernels.hpp
|
||||
integ/bilininteg_hcurl_kernels.hpp
|
||||
integ/bilininteg_hdiv_kernels.hpp
|
||||
integ/bilininteg_hcurlhdiv_kernels.hpp
|
||||
integ/bilininteg_mass_kernels.hpp
|
||||
integ/bilininteg_mass_pa_simplices.hpp
|
||||
integ/bilininteg_vecdiffusion_pa.hpp
|
||||
integ/bilininteg_vecmass_pa.hpp
|
||||
coefficient.hpp
|
||||
@@ -309,6 +311,7 @@ set(HDRS
|
||||
tmop_tools.hpp
|
||||
tmop_amr.hpp
|
||||
gslib.hpp
|
||||
gslib/gslib_kernel_helpers.hpp
|
||||
transfer.hpp
|
||||
hyperbolic.hpp
|
||||
integrator.hpp
|
||||
|
||||
+22
-4
@@ -1345,7 +1345,8 @@ real_t DiffusionIntegrator::ComputeFluxEnergy
|
||||
}
|
||||
|
||||
const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe)
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe,
|
||||
const bool stroud)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
@@ -1362,7 +1363,15 @@ const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
MassIntegrator::MassIntegrator(const IntegrationRule *ir)
|
||||
@@ -1449,7 +1458,8 @@ void MassIntegrator::AssembleElementMatrix2(
|
||||
|
||||
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans)
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud)
|
||||
{
|
||||
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
|
||||
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
|
||||
@@ -1458,7 +1468,15 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+40
-2
@@ -2184,11 +2184,22 @@ public:
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const bool, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const bool, const Array<real_t>&,
|
||||
const Array<real_t>&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2341,7 +2352,8 @@ public:
|
||||
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe);
|
||||
const FiniteElement &test_fe,
|
||||
const bool stroud = false);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2352,6 +2364,13 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
protected:
|
||||
const IntegrationRule* GetDefaultIntegrationRule(
|
||||
@@ -2388,11 +2407,22 @@ public:
|
||||
const Array<real_t>&, const Vector&,
|
||||
const Vector&, Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const Array<real_t>&,
|
||||
const Vector&, Vector&, const int,
|
||||
const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2441,7 +2471,8 @@ public:
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans);
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud = false);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2452,6 +2483,13 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -54,6 +54,8 @@ void Coefficient::Project(QuadratureFunction &qf)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
Vector values;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -327,6 +329,8 @@ void VectorCoefficient::Project(QuadratureFunction &qf)
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values;
|
||||
Vector col;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -695,6 +699,8 @@ void MatrixCoefficient::Project(QuadratureFunction &qf, bool transpose)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values, matrix;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
|
||||
@@ -237,6 +237,81 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeLpError(const real_t p,
|
||||
Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
Coefficient *weight,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t error = 0.0;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Vector valsr;
|
||||
Vector valsi;
|
||||
|
||||
const GridFunction& gf_r = real();
|
||||
const GridFunction& gf_i = imag();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
if (elems != NULL && (*elems)[i] == 0) { continue; }
|
||||
fe = fes->GetFE(i);
|
||||
const IntegrationRule *ir;
|
||||
if (irs)
|
||||
{
|
||||
ir = irs[fe->GetGeomType()];
|
||||
}
|
||||
else
|
||||
{
|
||||
int intorder = 2*fe->GetOrder() + 3;
|
||||
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
|
||||
}
|
||||
real_t elem_error = 0.0;
|
||||
gf_r.GetValues(i, *ir, valsr);
|
||||
gf_i.GetValues(i, *ir, valsi);
|
||||
T = fes->GetElementTransformation(i);
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
real_t diffr = valsr(j) - exsolr.Eval(*T, ip);
|
||||
real_t diffi = valsi(j) - exsoli.Eval(*T, ip);
|
||||
real_t diff = hypot(diffr, diffi);
|
||||
if (p < infinity())
|
||||
{
|
||||
diff = pow(diff, p);
|
||||
if (weight)
|
||||
{
|
||||
diff *= weight->Eval(*T, ip);
|
||||
}
|
||||
elem_error += ip.weight * T->Weight() * diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight)
|
||||
{
|
||||
diff *= weight->Eval(*T, ip);
|
||||
}
|
||||
error = std::max(error, diff);
|
||||
}
|
||||
}
|
||||
if (p < infinity())
|
||||
{
|
||||
// negative quadrature weights may cause the error to be negative
|
||||
error += fabs(elem_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (p < infinity())
|
||||
{
|
||||
error = pow(error, 1./p);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void ComplexGridFunction::Save(std::ostream &os) const
|
||||
{
|
||||
os << "ComplexGridFunction\n";
|
||||
|
||||
@@ -166,6 +166,75 @@ public:
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
/// @brief Returns Max|u_ex - u_h| error for complex-valued H1 or L2 elements
|
||||
///
|
||||
/// Compute the $L_\infty$ error across the entire domain.
|
||||
///
|
||||
/// @param[in] exsolr Coefficient object reproducing the real part of the
|
||||
/// anticipated values of the scalar field, Re(u_ex).
|
||||
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
|
||||
/// the anticipated values of the scalar field, Im(u_ex).
|
||||
/// @param[in] irs Optional pointer to an array of custom integration
|
||||
/// rules e.g. higher order than the default rules. If
|
||||
/// present the array will be indexed by
|
||||
/// Geometry::Type.
|
||||
///
|
||||
/// @note Uses ComputeLpError internally. See the ComputeLpError
|
||||
/// documentation for generalizations of this error computation.
|
||||
///
|
||||
/// @note If an array of integration rules is provided through @a irs, be
|
||||
/// sure to include valid rules for each element type that may occur
|
||||
/// in the list of elements.
|
||||
///
|
||||
virtual real_t ComputeMaxError(Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL) const
|
||||
{
|
||||
return ComputeLpError(infinity(), exsolr, exsoli, NULL, irs);
|
||||
}
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_Lp for complex-valued H1 or L2 elements
|
||||
///
|
||||
/// Computes:
|
||||
/// $$(\sum_{elems} \int_{elem} w \, |u_{ex} - u_h|^p)^{1/p}$$
|
||||
/// Where:
|
||||
/// $$|u_{ex} - u_h| = \sqrt{Re(u_{ex} - u_h)^2 + Im(u_{ex} - u_h)^2}$$
|
||||
///
|
||||
/// @param[in] p Real value indicating the exponent of the $L^p$ norm.
|
||||
/// To avoid domain errors p should have a positive value,
|
||||
/// either finite or infinite.
|
||||
/// @param[in] exsolr Coefficient object reproducing the real part of the
|
||||
/// anticipated values of the scalar field, Re(u_ex).
|
||||
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
|
||||
/// the anticipated values of the scalar field, Im(u_ex).
|
||||
/// @param[in] weight Optional pointer to a Coefficient object reproducing
|
||||
/// a weighting function, w.
|
||||
/// @param[in] irs Optional pointer to an array of custom integration
|
||||
/// rules e.g. higher order than the default rules. If
|
||||
/// present the array will be indexed by Geometry::Type.
|
||||
/// @param[in] elems Optional pointer to a marker array, with a length
|
||||
/// equal to the number of local elements, indicating
|
||||
/// which elements to integrate over. Only those elements
|
||||
/// corresponding to non-zero entries in @a elems will
|
||||
/// contribute to the computed L2 error.
|
||||
///
|
||||
/// @note If an array of integration rules is provided through @a irs, be
|
||||
/// sure to include valid rules for each element type that may occur
|
||||
/// in the list of elements.
|
||||
///
|
||||
/// @note Quadratures with negative weights (as in some simplex integration
|
||||
/// rules in MFEM) can produce negative integrals even with
|
||||
/// non-negative integrands. To avoid returning negative errors this
|
||||
/// function uses the absolute values of the element-wise integrals.
|
||||
/// This may lead to results which are not entirely consistent with
|
||||
/// such integration rules.
|
||||
virtual real_t ComputeLpError(const real_t p,
|
||||
Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
Coefficient *weight = NULL,
|
||||
const IntegrationRule *irs[] = NULL,
|
||||
const Array<int> *elems = NULL) const;
|
||||
|
||||
/// Save the ComplexGridFunction to an output stream.
|
||||
virtual void Save(std::ostream &out) const;
|
||||
|
||||
|
||||
@@ -1181,12 +1181,14 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
|
||||
DenseMatrix vval, pmat;
|
||||
std::vector<char> buf;
|
||||
int vec_dim = it->second->VectorDim();
|
||||
int map_type = it->second->FESpace()->GetTypicalFE()->GetMapType();
|
||||
os << "<DataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << it->first
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
|
||||
<< VTKComponentLabels(vec_dim) << " "
|
||||
<< "format=\"" << GetDataFormatString() << "\" >" << '\n';
|
||||
if (vec_dim == 1)
|
||||
if (vec_dim == 1 && (map_type == FiniteElement::VALUE ||
|
||||
map_type == FiniteElement::INTEGRAL))
|
||||
{
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
|
||||
+79
-30
@@ -25,21 +25,35 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Lightweight adaptor over an std::map from strings to pointer to T
|
||||
template<typename T>
|
||||
class NamedFieldsMap
|
||||
/// Lightweight adaptor over an std::map from type K to type to V
|
||||
template<typename K, typename V,
|
||||
typename = typename std::enable_if<std::is_default_constructible<V>::value>::type>
|
||||
class GenericFieldMap
|
||||
{
|
||||
private:
|
||||
static constexpr bool ValueIsPointer = std::is_pointer<V>::value;
|
||||
|
||||
public:
|
||||
typedef std::map<std::string, T*> MapType;
|
||||
typedef std::map<K, V> MapType;
|
||||
typedef typename MapType::iterator iterator;
|
||||
typedef typename MapType::const_iterator const_iterator;
|
||||
|
||||
/// Register field @a field with name @a fname
|
||||
/** Replace existing field associated with @a fname (and optionally
|
||||
delete associated pointer if @a own_data is true) */
|
||||
void Register(const std::string& fname, T* field, bool own_data)
|
||||
/// Register field @a field with name @a key
|
||||
/// Only enabled if the template parameter V is not a pointer
|
||||
template<typename = std::enable_if<!ValueIsPointer, bool>>
|
||||
void Register(const K& key, V field)
|
||||
{
|
||||
T*& ref = field_map[fname];
|
||||
field_map[key] = field;
|
||||
}
|
||||
|
||||
/// Register field @a field with name @a key
|
||||
/** Replace existing field associated with @a key (and optionally
|
||||
delete associated pointer if @a own_data is true).
|
||||
Only enabled if the template parameter V is a pointer*/
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
void Register(const K& key, V field, bool own_data)
|
||||
{
|
||||
V& ref = field_map[key];
|
||||
if (own_data)
|
||||
{
|
||||
delete ref; // if newly allocated -> ref is null -> OK
|
||||
@@ -47,23 +61,40 @@ public:
|
||||
ref = field;
|
||||
}
|
||||
|
||||
/// Unregister association between field @a field and name @a fname
|
||||
/** Optionally delete associated pointer if @a own_data is true */
|
||||
void Deregister(const std::string& fname, bool own_data)
|
||||
/// Unregister association between field @a field and name @a key
|
||||
/// Only enabled if the template parameter V is not a pointer
|
||||
template<typename = std::enable_if<!ValueIsPointer, bool>>
|
||||
void Deregister(const K& key)
|
||||
{
|
||||
iterator it = field_map.find(fname);
|
||||
iterator it = field_map.find(key);
|
||||
if ( it != field_map.end() )
|
||||
{
|
||||
field_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/// Unregister association between field @a field and name @a key
|
||||
/** Optionally delete associated pointer if @a own_data is true.
|
||||
Only enabled if the template parameter V is a pointer */
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
void Deregister(const K& key, bool own_data)
|
||||
{
|
||||
iterator it = field_map.find(key);
|
||||
if ( it != field_map.end() )
|
||||
{
|
||||
if (own_data)
|
||||
{
|
||||
delete it->second;
|
||||
it->second = nullptr;
|
||||
}
|
||||
field_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all associations between names and fields
|
||||
/** Delete associated pointers when @a own_data is true */
|
||||
/** Delete associated pointers when @a own_data is true.
|
||||
Only enabled if the template parameter V is a pointer */
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
void DeleteData(bool own_data)
|
||||
{
|
||||
for (iterator it = field_map.begin(); it != field_map.end(); ++it)
|
||||
@@ -76,22 +107,37 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Predicate to check if a field is associated with name @a fname
|
||||
bool Has(const std::string& fname) const
|
||||
/// Predicate to check if a field is associated with name @a key
|
||||
bool Has(const K& key) const
|
||||
{
|
||||
return field_map.find(fname) != field_map.end();
|
||||
return field_map.find(key) != field_map.end();
|
||||
}
|
||||
|
||||
/// Get a pointer to the field associated with name @a fname
|
||||
/** @return Pointer to field associated with @a fname or NULL */
|
||||
T* Get(const std::string& fname) const
|
||||
/// Get a pointer to the field associated with name @a key
|
||||
/** @return Field associated with @a key or NULL,
|
||||
if value is pointer and key not found */
|
||||
V Get(const K& key) const
|
||||
{
|
||||
const_iterator it = field_map.find(fname);
|
||||
return it != field_map.end() ? it->second : NULL;
|
||||
const_iterator it = field_map.find(key);
|
||||
if (it != field_map.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (ValueIsPointer)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return V(); // Return default-constructed value for non-pointer types
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a const reference to the underlying map
|
||||
const MapType& GetMap() const { return field_map; }
|
||||
const MapType &GetMap() const { return field_map; }
|
||||
|
||||
/// Returns the number of registered fields
|
||||
int NumFields() const { return field_map.size(); }
|
||||
@@ -106,21 +152,24 @@ public:
|
||||
/// Returns an end const iterator to the registered fields
|
||||
const_iterator end() const { return field_map.end(); }
|
||||
|
||||
/// Returns an iterator to the field @a fname
|
||||
iterator find(const std::string& fname)
|
||||
{ return field_map.find(fname); }
|
||||
/// Returns an iterator to the field @a key
|
||||
iterator find(const K& key)
|
||||
{ return field_map.find(key); }
|
||||
|
||||
/// Returns a const iterator to the field @a fname
|
||||
const_iterator find(const std::string& fname) const
|
||||
{ return field_map.find(fname); }
|
||||
/// Returns a const iterator to the field @a key
|
||||
const_iterator find(const K& key) const
|
||||
{ return field_map.find(key); }
|
||||
|
||||
/// Clears the map of registered fields without reclaiming memory
|
||||
/// Clears the map of registered fields
|
||||
void clear() { field_map.clear(); }
|
||||
|
||||
protected:
|
||||
MapType field_map;
|
||||
};
|
||||
|
||||
/// Lightweight adaptor over an std::map from strings to pointer to T
|
||||
template<typename T>
|
||||
using NamedFieldsMap = GenericFieldMap<std::string, T*>;
|
||||
|
||||
/** A class for collecting finite element data that is part of the same
|
||||
simulation. Currently, this class groups together grid functions (fields),
|
||||
|
||||
+16
-2
@@ -671,6 +671,20 @@ public:
|
||||
MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
Operator& GetGradient(const Vector &x0) const override
|
||||
{
|
||||
x = x0;
|
||||
f.UseDevice(x.UseDevice());
|
||||
xpev.UseDevice(x.UseDevice());
|
||||
|
||||
op.Mult(x, f);
|
||||
const real_t xnorm_local = x.Norml2();
|
||||
MPI_Allreduce(&xnorm_local, &xnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
MPI_COMM_WORLD);
|
||||
|
||||
return const_cast<FDJacobian&>(*this);
|
||||
}
|
||||
|
||||
void Mult(const Vector &v, Vector &y) const override
|
||||
{
|
||||
// See [1] for choice of eps.
|
||||
@@ -725,11 +739,11 @@ public:
|
||||
|
||||
private:
|
||||
const Operator &op;
|
||||
Vector x, f;
|
||||
mutable Vector x, f;
|
||||
mutable Vector xpev;
|
||||
real_t lambda = 1.0e-6;
|
||||
real_t fixed_eps;
|
||||
real_t xnorm;
|
||||
mutable real_t xnorm;
|
||||
};
|
||||
|
||||
/// @brief Find the index of a field descriptor in a vector of field descriptors.
|
||||
|
||||
+42
-1
@@ -167,7 +167,15 @@ public:
|
||||
/** @brief Full multidimensional representation which does not use tensor
|
||||
product structure. The ordering of the degrees of freedom is the
|
||||
same as TENSOR, but the sizes of B and G are the same as FULL.*/
|
||||
LEXICOGRAPHIC_FULL
|
||||
LEXICOGRAPHIC_FULL,
|
||||
|
||||
/** @brief Ragged tensor product representation using 1D matrices/tensors
|
||||
with dimensions using 1D number of quadrature points and ragged tensor degrees of
|
||||
freedom. */
|
||||
/** Used only for partial assembly of the H1 positive basis. The
|
||||
size of B is d1d x qnpt x dim. Since different Gauss-Jacobi quadrature rules
|
||||
are employed in each dimension, we need to store dim arrays. */
|
||||
RAGGED_TENSOR
|
||||
};
|
||||
|
||||
/// Describes the contents of the #B, #Bt, #G, and #Gt arrays, see #Mode.
|
||||
@@ -228,6 +236,39 @@ public:
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode);
|
||||
|
||||
virtual ~DofToQuad() = default;
|
||||
};
|
||||
|
||||
/** @brief Structure representing the matrices/tensors needed to evaluate (in
|
||||
reference space) the values, gradients, divergences, or curls of a positive
|
||||
FiniteElement on simplices at the quadrature points of Stroud conical quadrature. */
|
||||
class RaggedDofToQuad : public DofToQuad
|
||||
{
|
||||
public:
|
||||
/** @brief Special basis function structures for positive (Bernstein) basis with
|
||||
partial assembly. The storage layout of Ba1 is ndof x nqpt for scalar elements.
|
||||
The storage layout of Ba2 is ndof x ndof x nqpt. In particular, we have
|
||||
Ba2(iqpt, a1, a2) = B^{p-a1}_{a2}(x_{iqpt}). */
|
||||
Array<real_t> Ba1, Ba2, Ba3;
|
||||
Array<real_t> Ba1t, Ba2t, Ba3t;
|
||||
|
||||
/** @brief Special structures for gradients of positive basis with partial assembly.
|
||||
The gradient arrays exploit properties of the Bernstein basis which allow grad(B^p_alpha)
|
||||
to be expressed as the sum of products of B^{p-1}_alpha and the barycentric coordinates.
|
||||
Thus, Ga1 and Ga2 simply contain the ragged tensor product components of B^{p-1}_alpha */
|
||||
Array<real_t> Ga1, Ga2, Ga3;
|
||||
Array<real_t> Ga1t, Ga2t, Ga3t;
|
||||
|
||||
/** @brief Mapping from the Bernstein multi-index (a_1, ..., a_d) to the lexicographic
|
||||
dof index. */
|
||||
Array<int> lex_map;
|
||||
|
||||
Array<int> forward_map2d_diff, forward_map3d_diff;
|
||||
Array<int> inverse_map2d_diff, inverse_map3d_diff;
|
||||
|
||||
Array<int> forward_map2d_mass, forward_map3d_mass;
|
||||
Array<int> inverse_map2d_mass, inverse_map3d_mass;
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
|
||||
@@ -557,6 +557,101 @@ H1Pos_TriangleElement::H1Pos_TriangleElement(const int p)
|
||||
}
|
||||
}
|
||||
|
||||
const DofToQuad &H1Pos_TriangleElement::GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array)
|
||||
{
|
||||
DofToQuad *d2q = nullptr;
|
||||
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new RaggedDofToQuad;
|
||||
const int ndof = fe.GetOrder() + 1; // verify
|
||||
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
|
||||
d2q->FE = &fe;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = ndof;
|
||||
d2q->nqpt = nqpt;
|
||||
|
||||
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
|
||||
rd2q->Ba1.SetSize(nqpt*ndof);
|
||||
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba2.SetSize((int)nqpt*ndof*ndof);
|
||||
rd2q->Ba1t.SetSize(nqpt*ndof);
|
||||
rd2q->Ba2t.SetSize((int)nqpt*ndof*ndof);
|
||||
// stores first component of ragged tensor basis with order p-1, for gradients only
|
||||
rd2q->Ga1.SetSize(nqpt*(ndof -1));
|
||||
// stores second component of ragged tensor basis with order p-1
|
||||
rd2q->Ga2.SetSize(nqpt*(ndof-1)*(ndof -1));
|
||||
rd2q->Ga1t.SetSize(nqpt*(ndof -1));
|
||||
rd2q->Ga2t.SetSize(nqpt*(ndof-1)*(ndof -1));
|
||||
rd2q->lex_map.SetSize(ndof * ndof);
|
||||
Vector shape_a1(ndof), shape_a2(ndof * ndof);
|
||||
Vector shape_Ga1(ndof-1), shape_Ga2((ndof-1) * (ndof-1));
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
|
||||
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
|
||||
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
|
||||
// Gauss-Jacobi rule). Additionally, the Bernstein PA algorithms expect evaluation of the
|
||||
// component 1D bases at the Stroud nodes pulled back to the unit square, so perform the pullback
|
||||
// on the fly.
|
||||
const real_t x = ir.IntPoint(i).x;
|
||||
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
|
||||
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
|
||||
if (j < ndof-1)
|
||||
{
|
||||
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
|
||||
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
|
||||
for (int k = 0; k < ndof-j; k++)
|
||||
{
|
||||
rd2q->Ba2t[i + nqpt*(j + ndof*k)] = rd2q->Ba2[k + ndof*(j + ndof*i)] = shape_a2(
|
||||
k);
|
||||
if (j < ndof-1 && k < ndof-j-1)
|
||||
{
|
||||
rd2q->Ga2t[i + nqpt*(j + (ndof-1)*k)] = rd2q->Ga2[k + (ndof-1)*(j +
|
||||
(ndof-1)*i)] = shape_Ga2(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stores the mapping from 2D Bernstein multi-index (i,j,p-i-j) to the
|
||||
// lexicographic DOF ordering
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i; j++)
|
||||
{
|
||||
int idx = ((2 * (ndof-1) + 3) - j) * j / 2 + i;
|
||||
rd2q->lex_map[j + ndof*i] = idx;
|
||||
}
|
||||
}
|
||||
dof2quad_array.Append(d2q);
|
||||
}
|
||||
}
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_TriangleElement::CalcShape(
|
||||
const int p, const real_t l1, const real_t l2, real_t *shape)
|
||||
@@ -749,6 +844,213 @@ H1Pos_TetrahedronElement::H1Pos_TetrahedronElement(const int p)
|
||||
}
|
||||
}
|
||||
|
||||
const DofToQuad &H1Pos_TetrahedronElement::GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array)
|
||||
{
|
||||
DofToQuad *d2q = nullptr;
|
||||
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new RaggedDofToQuad;
|
||||
const int ndof = fe.GetOrder() + 1; // verify
|
||||
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
|
||||
const int basis_dim2d = ndof*(ndof+1) / 2;
|
||||
const int basis_dim3d = ndof*(ndof+1)*(ndof+2) / 6;
|
||||
const int basis_dim2d_diff = (ndof-1)*(ndof) / 2;
|
||||
const int basis_dim3d_diff = (ndof-1)*(ndof)*(ndof+1) / 6;
|
||||
d2q->FE = &fe;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = ndof;
|
||||
d2q->nqpt = nqpt;
|
||||
|
||||
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
|
||||
rd2q->Ba1.SetSize(nqpt * ndof);
|
||||
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba2.SetSize(nqpt * basis_dim2d);
|
||||
// third component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba3.SetSize(nqpt * basis_dim3d);
|
||||
rd2q->Ba1t.SetSize(nqpt * ndof);
|
||||
rd2q->Ba2t.SetSize(nqpt * basis_dim2d);
|
||||
rd2q->Ba3t.SetSize(nqpt * basis_dim3d);
|
||||
// stores first component of ragged tensor basis with order p-1, for gradients only
|
||||
rd2q->Ga1.SetSize(nqpt * (ndof-1));
|
||||
// stores second component of ragged tensor basis with order p-1
|
||||
rd2q->Ga2.SetSize(nqpt * basis_dim2d_diff);
|
||||
// stores third component of ragged tensor basis with order p-1
|
||||
rd2q->Ga3.SetSize(nqpt * basis_dim3d_diff);
|
||||
rd2q->Ga1t.SetSize(nqpt * (ndof-1));
|
||||
rd2q->Ga2t.SetSize(nqpt * basis_dim2d_diff);
|
||||
rd2q->Ga3t.SetSize(nqpt * basis_dim3d_diff);
|
||||
rd2q->lex_map.SetSize(ndof * ndof * ndof);
|
||||
|
||||
rd2q->forward_map2d_diff.SetSize((ndof-1) * (ndof-1));
|
||||
rd2q->forward_map3d_diff.SetSize((ndof-1) * (ndof-1) * (ndof-1));
|
||||
rd2q->inverse_map2d_diff.SetSize(2 * basis_dim2d_diff);
|
||||
rd2q->inverse_map3d_diff.SetSize(3 * basis_dim3d_diff);
|
||||
|
||||
rd2q->forward_map2d_mass.SetSize(ndof * ndof);
|
||||
rd2q->forward_map3d_mass.SetSize(ndof * ndof * ndof);
|
||||
rd2q->inverse_map2d_mass.SetSize(2 * basis_dim2d);
|
||||
rd2q->inverse_map3d_mass.SetSize(2 * basis_dim3d);
|
||||
|
||||
// forward and inverse maps for multi-index to collpased 1d index for diffusion, can combine
|
||||
// these four loops, but need four idx's and clause for shorter diff loops
|
||||
int idx = 0;
|
||||
for (int i = 0; i < ndof-1; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i-1; j++)
|
||||
{
|
||||
rd2q->forward_map2d_diff[j + (ndof-1)*i] = idx;
|
||||
rd2q->inverse_map2d_diff[2*idx] = i;
|
||||
rd2q->inverse_map2d_diff[1 + 2*idx] = j;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
for (int k = 0; k < ndof-1; k++)
|
||||
{
|
||||
for (int j = 0; j < ndof-k-1; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-k-j-1; i++)
|
||||
{
|
||||
rd2q->forward_map3d_diff[k + (ndof-1)*(j + (ndof-1)*i)] = idx;
|
||||
rd2q->inverse_map3d_diff[3*idx] = i;
|
||||
rd2q->inverse_map3d_diff[1 + 3*idx] = j;
|
||||
rd2q->inverse_map3d_diff[2 + 3*idx] = k;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// forward and inverse maps for multi-index to collpased 1d index for mass
|
||||
idx = 0;
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-j; i++)
|
||||
{
|
||||
rd2q->forward_map2d_mass[j + ndof*i] = idx;
|
||||
rd2q->inverse_map2d_mass[2*idx] = i;
|
||||
rd2q->inverse_map2d_mass[1 + 2*idx] = j;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
for (int k = 0; k < ndof; k++)
|
||||
{
|
||||
for (int j = 0; j < ndof-k; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-k-j; i++)
|
||||
{
|
||||
rd2q->forward_map3d_mass[k + ndof*(j + ndof*i)] = idx;
|
||||
rd2q->inverse_map3d_mass[2*idx] = i;
|
||||
rd2q->inverse_map3d_mass[1 + 2*idx] = j;
|
||||
// d2q->inverse_map3d_mass[2 + 3*idx] = k;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector shape_a1(ndof), shape_a2(ndof * ndof), shape_a3(ndof * ndof * ndof);
|
||||
Vector shape_Ga1(ndof-1), shape_Ga2(ndof-1), shape_Ga3(ndof-1);
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
|
||||
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
|
||||
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
|
||||
// Gauss-Jacobi rule). The first 'nqpt' points in the third dimension have the same z-coordinates
|
||||
// as those of the 1D rule for the third dimension (i.e. Gauss-Legendre rule). Additionally,
|
||||
// the Bernstein PA algorithms expect evaluation of the component 1D bases at the Stroud nodes
|
||||
// pulled back to the unit cube, so perform the pullback on the fly.
|
||||
const real_t x = ir.IntPoint(i).x;
|
||||
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
|
||||
const real_t z = ir.IntPoint(nqpt*nqpt*i).z / (1.0 - ir.IntPoint(
|
||||
nqpt*nqpt*i).x - ir.IntPoint(nqpt*nqpt*i).y);
|
||||
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
|
||||
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
|
||||
if (j < ndof-1)
|
||||
{
|
||||
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
|
||||
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
|
||||
for (int k = 0; k < ndof-j; k++)
|
||||
{
|
||||
const int a_2d_mass = rd2q->forward_map2d_mass[k + ndof*j];
|
||||
rd2q->Ba2t[i + nqpt*a_2d_mass] = rd2q->Ba2[a_2d_mass + basis_dim2d*i] =
|
||||
shape_a2(
|
||||
k);
|
||||
if (j < ndof-1 && k < ndof-j-1)
|
||||
{
|
||||
const int a_2d_diff = rd2q->forward_map2d_diff[k + (ndof-1)*j];
|
||||
rd2q->Ga2t[i + nqpt*a_2d_diff] = rd2q->Ga2[a_2d_diff + basis_dim2d_diff*i] =
|
||||
shape_Ga2(k);
|
||||
Poly_1D::CalcBernstein(ndof-2-j-k, z, shape_Ga3);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j-k, z, shape_a3);
|
||||
for (int m = 0; m < ndof-j-k; m++)
|
||||
{
|
||||
const int a_3d_mass = rd2q->forward_map3d_mass[m + ndof*(k + ndof*j)];
|
||||
rd2q->Ba3t[i + nqpt*a_3d_mass] = rd2q->Ba3[a_3d_mass + basis_dim3d*i] =
|
||||
shape_a3(
|
||||
m);
|
||||
if (j < ndof-1 && k < ndof-j-1 && m < ndof-j-k-1)
|
||||
{
|
||||
// // collapsed 1D access
|
||||
// d2q->Ga3[i + nqpt*(m + d2q->offset3d[k + (ndof-1)*j])] = shape_Ga3(m);
|
||||
// collapsed 1D access with forward mapping
|
||||
const int a_3d_diff = rd2q->forward_map3d_diff[m + (ndof-1)*(k + (ndof-1)*j)];
|
||||
rd2q->Ga3t[i + nqpt*a_3d_diff] = rd2q->Ga3[a_3d_diff + basis_dim3d_diff*i] =
|
||||
shape_Ga3(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stores the mapping from 3D Bernstein multi-index (i,j,k,p-i-j-k) to the
|
||||
// lexicographic DOF ordering
|
||||
int p = ndof - 1;
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i; j++)
|
||||
{
|
||||
for (int k = 0; k < ndof-i-j; k++)
|
||||
{
|
||||
int dof = (p+1)*(p+2)*(p+3) / 6;
|
||||
int tet = (p-k)*(p-k+1)*(p-k+2) / 6;
|
||||
int tri = (p+1-k-j)*(p+2-k-j)/2;
|
||||
int multi_idx = dof - tet - tri + i;
|
||||
rd2q->lex_map[k + ndof*(j + ndof*i)] = multi_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q);
|
||||
}
|
||||
}
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_TetrahedronElement::CalcShape(
|
||||
const int p, const real_t l1, const real_t l2, const real_t l3,
|
||||
|
||||
@@ -191,6 +191,21 @@ public:
|
||||
/// Construct the H1Pos_TriangleElement of order @a p
|
||||
H1Pos_TriangleElement(const int p);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const override
|
||||
{
|
||||
return (mode == DofToQuad::RAGGED_TENSOR) ?
|
||||
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
|
||||
FiniteElement::GetDofToQuad(ir, mode);
|
||||
}
|
||||
|
||||
static const DofToQuad &GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array);
|
||||
|
||||
const Array<int> &GetDofMap() const { return dof_map; }
|
||||
|
||||
// The size of shape is (p+1)(p+2)/2 (dof).
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
real_t *shape);
|
||||
@@ -220,6 +235,21 @@ public:
|
||||
/// Construct the H1Pos_TetrahedronElement of order @a p
|
||||
H1Pos_TetrahedronElement(const int p);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const override
|
||||
{
|
||||
return (mode == DofToQuad::RAGGED_TENSOR) ?
|
||||
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
|
||||
FiniteElement::GetDofToQuad(ir, mode);
|
||||
}
|
||||
|
||||
static const DofToQuad &GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array);
|
||||
|
||||
const Array<int> &GetDofMap() const { return dof_map; }
|
||||
|
||||
// The size of shape is (p+1)(p+2)(p+3)/6 (dof).
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *shape);
|
||||
|
||||
@@ -250,6 +250,14 @@ public:
|
||||
its GetOrder() method. */
|
||||
virtual FiniteElementCollection *Clone(int p) const;
|
||||
|
||||
/** @brief Return the order parameter used to construct this collection.
|
||||
* This differs from GetOrder() depending on the collection type. */
|
||||
virtual int GetConstructorOrder() const
|
||||
{
|
||||
MFEM_ABORT("Collection " << Name() << " does not support GetConstructorOrder");
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int base_p; ///< Order as returned by GetOrder().
|
||||
|
||||
@@ -314,6 +322,9 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new H1_FECollection(p, dim, b_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p; }
|
||||
|
||||
virtual ~H1_FECollection();
|
||||
};
|
||||
|
||||
@@ -343,6 +354,10 @@ class H1_Trace_FECollection : public H1_FECollection
|
||||
public:
|
||||
H1_Trace_FECollection(const int p, const int dim,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new H1_Trace_FECollection(p, dim+1, b_type); }
|
||||
|
||||
};
|
||||
|
||||
/// Arbitrary order "L2-conforming" discontinuous finite elements.
|
||||
@@ -396,6 +411,9 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new L2_FECollection(p, dim, b_type, m_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p; }
|
||||
|
||||
virtual ~L2_FECollection();
|
||||
};
|
||||
|
||||
@@ -456,6 +474,9 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new RT_FECollection(p, dim, cb_type, ob_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p-1; }
|
||||
|
||||
virtual ~RT_FECollection();
|
||||
};
|
||||
|
||||
@@ -536,6 +557,9 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new ND_FECollection(p, dim, cb_type, ob_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return dim>1 ? base_p : base_p+1; }
|
||||
|
||||
virtual ~ND_FECollection();
|
||||
};
|
||||
|
||||
@@ -548,6 +572,9 @@ public:
|
||||
ND_Trace_FECollection(const int p, const int dim,
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new ND_Trace_FECollection(p, dim+1, cb_type, ob_type); }
|
||||
};
|
||||
|
||||
/// Arbitrary order 3D H(curl)-conforming Nedelec finite elements in 1D.
|
||||
|
||||
+1
-2
@@ -4631,9 +4631,8 @@ FiniteElementCollection *FiniteElementSpace::Load(Mesh *m, std::istream &input)
|
||||
|
||||
ElementDofOrdering GetEVectorOrdering(const FiniteElementSpace& fes)
|
||||
{
|
||||
return UsesTensorBasis(fes)?
|
||||
return (UsesTensorBasis(fes) || fes.UsesRaggedTensorBasis()) ?
|
||||
ElementDofOrdering::LEXICOGRAPHIC:
|
||||
ElementDofOrdering::NATIVE;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -1514,6 +1514,18 @@ public:
|
||||
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
|
||||
}
|
||||
|
||||
/// @brief Return true if the mesh contains only one topology, the elements are
|
||||
/// all triangles or tetrahedrons, and the elements are ragged tensor elements
|
||||
/// i.e. Bernstein/positive basis.
|
||||
bool UsesRaggedTensorBasis() const
|
||||
{
|
||||
bool simplex = this->GetMesh()->IsSimplexMesh();
|
||||
bool positive =
|
||||
dynamic_cast<const mfem::H1Pos_TriangleElement *>(this->GetTypicalFE()) ||
|
||||
dynamic_cast<const mfem::H1Pos_TetrahedronElement *>(this->GetTypicalFE());
|
||||
return simplex && positive;
|
||||
}
|
||||
|
||||
/** In variable-order spaces on nonconforming (NC) meshes, this function
|
||||
controls whether strict conformity is enforced in cases where coarse
|
||||
edges/faces have higher polynomial order than their fine NC neighbors.
|
||||
|
||||
@@ -2256,6 +2256,104 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::AccumulateAndCountTraceValues(
|
||||
Coefficient *coeff[], VectorCoefficient *vcoeff,
|
||||
Array<int> &values_counter)
|
||||
{
|
||||
if (vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == vcoeff->GetVDim(),
|
||||
"vcoeff vdim != fes VDim");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetMapType() ==
|
||||
FiniteElement::VALUE &&
|
||||
fes->GetTypicalTraceElement()->GetRangeType() ==
|
||||
FiniteElement::SCALAR,
|
||||
"Can only call ProjectTraceCoefficient on scalar value-type "
|
||||
"trace elements. "
|
||||
"Use ProjectTraceCoefficientNormal for RT and "
|
||||
"ProjectTraceCoefficientTangent for ND finite elements.");
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
Vector vc;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
|
||||
const int vdim = fes->GetVDim();
|
||||
HostReadWrite();
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
|
||||
const FiniteElement *fe = fes->GetFaceElement(i);
|
||||
const int fdof = fe->GetDof();
|
||||
ElementTransformation *transf = fes->GetMesh()->GetFaceTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
fes->GetFaceVDofs(i, vdofs);
|
||||
|
||||
for (int j = 0; j < fdof; j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
if (vcoeff) { vcoeff->Eval(vc, *transf, ip); }
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
if (!vcoeff && !coeff[d]) { continue; }
|
||||
|
||||
real_t val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
|
||||
int ind = vdofs[fdof*d+j];
|
||||
if ( ind < 0 )
|
||||
{
|
||||
val = -val, ind = -1-ind;
|
||||
}
|
||||
if (++values_counter[ind] == 1)
|
||||
{
|
||||
(*this)(ind) = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*this)(ind) += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::AccumulateAndCountTraceTangentValues(
|
||||
VectorCoefficient &vcoeff, Array<int> &values_counter)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()
|
||||
->GetRangeType() == FiniteElement::VECTOR &&
|
||||
fes->GetTypicalTraceElement()
|
||||
->GetMapType() == FiniteElement::H_CURL,
|
||||
"Not an ND FE space!");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetPhysRangeDim(
|
||||
fes->GetMesh()->SpaceDimension()) == vcoeff.GetVDim(),
|
||||
"vcoeff vdim != PhysRangeDim");
|
||||
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector lvec;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
|
||||
HostReadWrite();
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
fe = fes->GetFaceElement(i);
|
||||
T = fes->GetMesh()->GetFaceTransformation(i);
|
||||
fes->GetFaceVDofs(i, dofs);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ComputeMeans(AvgType type, Array<int> &zones_per_vdof)
|
||||
{
|
||||
switch (type)
|
||||
@@ -2698,6 +2796,74 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(Coefficient *coeff[])
|
||||
{
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceValues(coeff, NULL, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(Coefficient &coeff)
|
||||
{
|
||||
MFEM_VERIFY(FESpace()->GetVDim() == 1, "ProjectTraceCoefficient(Coefficient&)"
|
||||
"is only valid for scalar GridFunction");
|
||||
Coefficient *coeff_p = &coeff;
|
||||
ProjectTraceCoefficient(&coeff_p);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(FESpace()->GetVDim() == vcoeff.GetVDim(),
|
||||
"Incompatible vcoeff vdim and fes vdim");
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceValues(NULL, &vcoeff, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetRangeType() ==
|
||||
FiniteElement::SCALAR &&
|
||||
fes->GetTypicalTraceElement()->GetMapType() ==
|
||||
FiniteElement::INTEGRAL, "Not an RT FE space!");
|
||||
MFEM_VERIFY(vcoeff.GetVDim() == fes->GetMesh()->SpaceDimension(),
|
||||
"vcoeff vdim (" << vcoeff.GetVDim()
|
||||
<< ") != SpaceDimension ("
|
||||
<< fes->GetMesh()->SpaceDimension() << ")");
|
||||
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
fe = fes->GetFaceElement(i);
|
||||
T = fes->GetMesh()->GetFaceTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
lvec.SetSize(fe->GetDof());
|
||||
for (int j = 0; j < ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
fes->GetFaceVDofs(i, dofs);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff)
|
||||
{
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceTangentValues(vcoeff, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
@@ -5286,6 +5452,7 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
|
||||
Vector lel, uel;
|
||||
GetElementBounds(plb, lel, uel, vdim);
|
||||
|
||||
|
||||
@@ -578,6 +578,13 @@ protected:
|
||||
const Array<int> &bdr_attr,
|
||||
Array<int> &values_counter);
|
||||
|
||||
void AccumulateAndCountTraceValues(Coefficient *coeff[],
|
||||
VectorCoefficient *vcoeff,
|
||||
Array<int> &values_counter);
|
||||
|
||||
void AccumulateAndCountTraceTangentValues(VectorCoefficient &vcoeff,
|
||||
Array<int> &values_counter);
|
||||
|
||||
// Complete the computation of averages; called e.g. after
|
||||
// AccumulateAndCountZones().
|
||||
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
|
||||
@@ -663,6 +670,23 @@ public:
|
||||
ProjectBdrCoefficient(&coeff_p, attr);
|
||||
}
|
||||
|
||||
/// Project a Coefficient on a GridFunction defined on H1 trace space
|
||||
void ProjectTraceCoefficient(Coefficient *coeff[]);
|
||||
void ProjectTraceCoefficient(Coefficient &coeff);
|
||||
|
||||
/** @brief Project a VectorCoefficient @a vcoeff on a GridFunction
|
||||
defined on a Vector H1 trace space. Note that this also works
|
||||
for a scalar H1 trace space, where only the first component of
|
||||
@a vcoeff is used. */
|
||||
void ProjectTraceCoefficient(VectorCoefficient &vcoeff);
|
||||
/** @brief Project a VectorCoefficient on a GridFunction
|
||||
defined on an RT trace space */
|
||||
void ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff);
|
||||
/** @brief Project a VectorCoefficient on a GridFunction
|
||||
defined on an ND trace space */
|
||||
void ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff);
|
||||
|
||||
|
||||
/** @brief Project a VectorCoefficient on the GridFunction, modifying only
|
||||
DOFs on the boundary associated with the boundary attributes marked in
|
||||
the @a attr array. */
|
||||
|
||||
+1234
-726
File diff suppressed because it is too large
Load Diff
+166
-51
@@ -12,6 +12,9 @@
|
||||
#ifndef MFEM_GSLIB
|
||||
#define MFEM_GSLIB
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pgridfunc.hpp"
|
||||
@@ -119,6 +122,11 @@ protected:
|
||||
// IntegrationRules for simplex->Quad/Hex and to project to p_max in-case of
|
||||
// p-refinement.
|
||||
Array<IntegrationRule *> ir_split;
|
||||
/// Integration rules built at the field polynomial order (only for surface
|
||||
/// meshes when mesh order is not the same as gridfunction order).
|
||||
Array<IntegrationRule *> ir_split_sol;
|
||||
/// Order at which #ir_split_sol was built; -1 means not built.
|
||||
int ir_split_sol_order = -1;
|
||||
Array<FiniteElementSpace *> fes_rst_map; //FESpaces to map Quad/Hex->Simplex
|
||||
Array<GridFunction *> gf_rst_map; // GridFunctions to map Quad/Hex->Simplex
|
||||
FiniteElementCollection *fec_map_lin;
|
||||
@@ -134,6 +142,8 @@ protected:
|
||||
AvgType avgtype; // average type used for L2 functions
|
||||
Array<int> split_element_map;
|
||||
Array<int> split_element_index;
|
||||
// Geometry::Type (as int) of the original element for each split quad.
|
||||
Array<int> split_element_geom;
|
||||
int NE_split_total; // total number of elements after mesh splitting
|
||||
int mesh_points_cnt; // number of mesh nodes
|
||||
// Tolerance to ignore points found beyond the mesh boundary.
|
||||
@@ -141,6 +151,12 @@ protected:
|
||||
double bdr_tol;
|
||||
// Use CPU functions for Mesh/GridFunction on device for gslib1.0.7
|
||||
bool gpu_to_cpu_fallback = false;
|
||||
// Check if a point is inside the oriented bounding box of an
|
||||
// element before the Newton iteration.
|
||||
// Note: only used in MFEM implementation (not in gslib) which currently
|
||||
// supports GPU kernels for area meshes in 2D, volume meshes in 3D,
|
||||
// and surface meshes in 1D/2D/3D.
|
||||
bool obb_check = true;
|
||||
|
||||
// Device specific data used for FindPoints
|
||||
struct DEV_STRUCT
|
||||
@@ -162,11 +178,16 @@ protected:
|
||||
mutable double surf_dist_tol;
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
// Helper function to setup and free gslib's crystal router.
|
||||
void SetupCrystal(); // Called inside Setup and SetupSurf_base
|
||||
void FreeCrystal(); // Called inside FreeData
|
||||
|
||||
/// Use GSLIB for communication and interpolation. Updates field_out on
|
||||
/// host.
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/// Uses GSLIB Crystal Router for communication followed by MFEM's
|
||||
/// interpolation functions
|
||||
/// interpolation functions. Updates field_out on host.
|
||||
virtual void InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
@@ -181,12 +202,26 @@ protected:
|
||||
IntegrationRule *irule,
|
||||
int order);
|
||||
|
||||
/** @brief Build integration rules at the given @a order for each split mesh
|
||||
* and store them in @a ir_out. Requires that \ref SetupSplitMeshes has
|
||||
* already been called. */
|
||||
virtual void SetupIntegrationRules(const int order,
|
||||
Array<IntegrationRule *> &ir_out);
|
||||
|
||||
/** @brief Helper function that calls \ref SetupSplitMeshes and
|
||||
* \ref SetupIntegrationRuleForSplitMesh. */
|
||||
* \ref SetupIntegrationRules. */
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
/** @brief Get GridFunction value at the points expected by GSLIB.
|
||||
* @param[in] gf_in Grid function to evaluate.
|
||||
* @param[out] node_vals Output values.
|
||||
* @param[in] ir_in If non-null, use these rules instead of #ir_split.
|
||||
* @param[in] by_element If true, output has element-major layout
|
||||
* [nel][vdim][ndofs]; otherwise component-major
|
||||
* layout [vdim][total_pts]. */
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals,
|
||||
const Array<IntegrationRule *> *ir_in = nullptr,
|
||||
bool by_element = false) const;
|
||||
|
||||
/** @brief Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For
|
||||
* simplices, find the original element number (that was split into
|
||||
@@ -291,29 +326,60 @@ protected:
|
||||
void findptsedge_setup_2(DEV_STRUCT &devs,
|
||||
const double *const elx[2],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned int nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size);
|
||||
const double bbox_rel_size_inc,
|
||||
const unsigned int local_hash_size,
|
||||
const unsigned int global_hash_size,
|
||||
const Vector *aabb_sz_inc);
|
||||
|
||||
/// Preprocess 3D surface mesh needed for FindPoints.
|
||||
void findptssurf_setup_3(DEV_STRUCT &devs,
|
||||
const double *const elx[3],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned int nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size,
|
||||
const int rD);
|
||||
const double bbox_rel_size_inc,
|
||||
const unsigned int local_hash_size,
|
||||
const unsigned int global_hash_size,
|
||||
const int rD,
|
||||
const Vector *aabb_sz_inc);
|
||||
|
||||
/** @brief Shared implementation for the public surface-setup methods.
|
||||
*
|
||||
* @details Initializes the surface-search data structures, builds the
|
||||
* split-element representation expected by gslib, and constructs the
|
||||
* element bounding boxes used by the MFEM surface kernels.
|
||||
*
|
||||
* If @a aabb_sz_inc is null, the setup stores the default oriented
|
||||
* bounding boxes and uses @a bbox_rel_size_inc as their relative size
|
||||
* increase factor.
|
||||
*
|
||||
* If @a aabb_sz_inc is non-null, the setup stores axis-aligned bounding
|
||||
* boxes only, applies the requested absolute AABB expansion in each
|
||||
* physical direction, and adjusts the tolerance @a bdr_tol so points
|
||||
* found in the expanded region are classified as border points.
|
||||
*
|
||||
* @param[in] m Input surface mesh.
|
||||
* @param[in] bbox_rel_size_inc Relative size increase applied when
|
||||
* expanding each element bounding box during
|
||||
* setup.
|
||||
* @param[in] aabb_sz_inc Optional total absolute AABB expansion
|
||||
* applied to the stored axis-aligned
|
||||
* bounding boxes after construction.
|
||||
* @param[in] newt_tol Newton tolerance for the point-search
|
||||
* kernels.
|
||||
*/
|
||||
void SetupSurf_Base(Mesh &m,
|
||||
const double bbox_rel_size_inc,
|
||||
const Vector *aabb_sz_inc,
|
||||
const double newt_tol);
|
||||
public:
|
||||
/// Serial constructor
|
||||
FindPointsGSLIB();
|
||||
|
||||
/// Serial constructor + setup with given Mesh (see \ref Setup)
|
||||
FindPointsGSLIB(Mesh &mesh_in, const double bb_t = 0.1,
|
||||
FindPointsGSLIB(Mesh &mesh_in, const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
@@ -322,7 +388,7 @@ public:
|
||||
FindPointsGSLIB(MPI_Comm comm_);
|
||||
|
||||
/// Constructor + setup with given ParMesh (see \ref Setup)
|
||||
FindPointsGSLIB(ParMesh &mesh_in, const double bb_t = 0.1,
|
||||
FindPointsGSLIB(ParMesh &mesh_in, const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
#endif
|
||||
@@ -338,23 +404,59 @@ public:
|
||||
Note: not tested with periodic (L2).
|
||||
Note: the input mesh \p m must have Nodes set.
|
||||
|
||||
@param[in] m Input mesh.
|
||||
@param[in] bb_t (Optional) Relative size of bounding box around
|
||||
each element.
|
||||
@param[in] newt_tol (Optional) Newton tolerance for the gslib
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for simultaneous
|
||||
iteration. This alters performance and
|
||||
memory footprint.
|
||||
@param[in] m Input mesh.
|
||||
@param[in] bbox_rel_size_inc (Optional) Relative size increase applied
|
||||
when expanding each element bounding box.
|
||||
@param[in] newt_tol (Optional) Newton tolerance for the gslib
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for
|
||||
simultaneous iteration. This alters
|
||||
performance and memory footprint.
|
||||
*/
|
||||
void Setup(Mesh &m, const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
void Setup(Mesh &m, const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/// Preprocess the surface mesh to compute data for FindPoints.
|
||||
void SetupSurf(Mesh &m,
|
||||
const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12);
|
||||
|
||||
/** @brief Preprocess the surface mesh to compute data for FindPoints using
|
||||
* absolute AABB expansion.
|
||||
*
|
||||
* @details This method computes only axis-aligned bounding boxes and
|
||||
* increases their total length by a user-specified amount in each
|
||||
* physical direction. The absolute AABB expansion is applied
|
||||
* symmetrically to the lower and upper bounds.
|
||||
*
|
||||
* The size of @a aabb_sz_inc determines how the expansion values are
|
||||
* interpreted:
|
||||
* - `1`: one expansion value used in every direction for every element
|
||||
* - `NElements`: one expansion value per element, reused in x/y/z
|
||||
* directions
|
||||
* - `SpaceDim`: one expansion value per physical direction, reused for
|
||||
* every element
|
||||
* - `NElements*SpaceDim`: one expansion value per element and direction,
|
||||
* ordered as `(dx1,dy1,dz1, ... dxN,dyN,dzN)`
|
||||
*
|
||||
* This method disables the oriented bounding-box precheck because the
|
||||
* stored boxes are modified only in their axis-aligned representation.
|
||||
*
|
||||
* @param[in] m Input surface mesh.
|
||||
* @param[in] aabb_sz_inc Total absolute AABB expansion applied in
|
||||
* each physical direction to the stored
|
||||
* axis-aligned bounding boxes.
|
||||
* @param[in] newt_tol Newton tolerance for the point-search
|
||||
* kernels.
|
||||
*
|
||||
* @note We disable the oriented bounding box check with this setup.
|
||||
* @a bdr_tol is also adjusted so that all points in the AABBs can
|
||||
* be found.
|
||||
*/
|
||||
void SetupSurfWithAABBExpansion(Mesh &m, const Vector &aabb_sz_inc,
|
||||
const double newt_tol = 1.0e-12);
|
||||
|
||||
|
||||
/** @brief Searches positions given in physical space by \p point_pos.
|
||||
|
||||
@@ -401,7 +503,8 @@ public:
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
@@ -413,7 +516,11 @@ public:
|
||||
mesh that was given to Setup().
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
The output ordering is determined from field_in.
|
||||
|
||||
@note: field_out is moved to device if field_in is on device. Otherwise,
|
||||
field_out memory allocation is not changed.
|
||||
*/
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
@@ -468,7 +575,12 @@ public:
|
||||
* @details When using FindPoints, gslib may return points as found on the
|
||||
* boundary even when they are slightly outside the domain. This tolerance
|
||||
* is used to filter such points based on the distance^2 value and mark them
|
||||
* as not found.*/
|
||||
* as not found.
|
||||
*
|
||||
* @note When the SetupSurfWithAABBExpansion method is used for surface
|
||||
* meshes, this tolerance is automatically computed based on the size of
|
||||
* expanded AABBs. Using this method will override that computed tolerance.
|
||||
* */
|
||||
virtual void SetDistanceToleranceForPointsFoundOnBoundary(double bdr_tol_)
|
||||
{
|
||||
bdr_tol = bdr_tol_;
|
||||
@@ -603,25 +715,28 @@ public:
|
||||
Note: not tested with periodic meshes (L2).
|
||||
Note: the input mesh \p m must have Nodes set.
|
||||
|
||||
@param[in] m Input mesh.
|
||||
@param[in] meshid A unique # for each overlapping mesh. This id is
|
||||
used to make sure that points being searched are not
|
||||
looked for in the mesh that they belong to.
|
||||
@param[in] gfmax (Optional) GridFunction in H1 that is used as a
|
||||
discriminator when one point is located in multiple
|
||||
meshes. The mesh that maximizes gfmax is chosen.
|
||||
For example, using the distance field based on the
|
||||
overlapping boundaries is helpful for convergence
|
||||
during Schwarz iterations.
|
||||
@param[in] bb_t (Optional) Relative size of bounding box around
|
||||
each element.
|
||||
@param[in] newt_tol (Optional) Newton tolerance for the gslib
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for simultaneous
|
||||
iteration. This alters performance and
|
||||
memory footprint.*/
|
||||
void Setup(Mesh &m, const int meshid, GridFunction *gfmax = NULL,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
@param[in] m Input mesh.
|
||||
@param[in] meshid A unique # for each overlapping mesh.
|
||||
This id is used to make sure that points
|
||||
being searched are not looked for in the
|
||||
mesh that they belong to.
|
||||
@param[in] gfmax (Optional) GridFunction in H1 that is used
|
||||
as a discriminator when one point is
|
||||
located in multiple meshes. The mesh that
|
||||
maximizes gfmax is chosen. For example,
|
||||
using the distance field based on the
|
||||
overlapping boundaries is helpful for
|
||||
convergence during Schwarz iterations.
|
||||
@param[in] bbox_rel_size_inc (Optional) Relative size increase applied
|
||||
when expanding each element bounding box.
|
||||
@param[in] newt_tol (Optional) Newton tolerance for the gslib
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for
|
||||
simultaneous iteration. This alters
|
||||
performance and memory footprint.*/
|
||||
void Setup(Mesh &m, const int meshid, GridFunction *gfmax = nullptr,
|
||||
const double bbox_rel_size_inc = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** Searches positions given in physical space by \p point_pos. All output
|
||||
@@ -677,7 +792,7 @@ class GSOPGSLIB
|
||||
protected:
|
||||
struct gslib::crystal *cr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm; // gslib's internal data
|
||||
struct gslib::gs_data *gsl_data = NULL;
|
||||
struct gslib::gs_data *gsl_data = nullptr;
|
||||
int num_ids;
|
||||
|
||||
public:
|
||||
|
||||
+64
-170
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
@@ -54,127 +52,14 @@ struct findptsElementGPT_t
|
||||
double x[DIM], jac[DIM * DIM], hes[4];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[DIM], A[DIM * DIM];
|
||||
dbl_range_t x[DIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[DIM];
|
||||
double fac[DIM];
|
||||
unsigned int *offset;
|
||||
int max;
|
||||
};
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first derivative at x.
|
||||
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x - z[j]);
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p0[pN+i] = 2.0 * lCoeff[i] * u1;
|
||||
}
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first and second derivative at x.
|
||||
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x - z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p0[pN+i] = 2.0 * lCoeff[i] * u1;
|
||||
p0[2*pN+i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
// Axis-aligned bounding box test.
|
||||
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
|
||||
const double x[2])
|
||||
{
|
||||
double test = 1;
|
||||
for (int d = 0; d < 2; ++d)
|
||||
{
|
||||
double b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
test = test < 0 ? test : b_d;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
// Axis-aligned bounding box test followed by oriented bounding-box test.
|
||||
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
|
||||
const double x[2])
|
||||
{
|
||||
const double bxyz = AABB_test(b, x);
|
||||
if (bxyz < 0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[2];
|
||||
for (int d = 0; d < 2; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
double test = 1;
|
||||
for (int d = 0; d < 2; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e = 0; e < 2; ++e)
|
||||
{
|
||||
rst += b->A[d * 2 + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst + 1) * (1 - rst);
|
||||
test = test < 0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
// Element index corresponding to hash mesh that the point is located in.
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[2])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d = 2 - 1; d >= 0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i < 0 ? 0 : (n - 1 < i ? n - 1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
using dbl_range_t = gslib::dbl_range_t;
|
||||
using obbox_t = gslib::obbox_t<DIM>;
|
||||
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<DIM>;
|
||||
using gslib::bbox_test;
|
||||
using gslib::hash_index;
|
||||
using gslib::l2norm2;
|
||||
using gslib::lag_eval_first_der;
|
||||
using gslib::lag_eval_second_der;
|
||||
|
||||
/*Solve Ax=y. A is row-major */
|
||||
static MFEM_HOST_DEVICE inline void lin_solve_2(double x[2], const double A[4],
|
||||
@@ -185,12 +70,6 @@ static MFEM_HOST_DEVICE inline void lin_solve_2(double x[2], const double A[4],
|
||||
x[1] = idet*(A[0]*y[1] - A[2]*y[0]);
|
||||
}
|
||||
|
||||
/* L2 norm squared. */
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
|
||||
{
|
||||
return x[0] * x[0] + x[1] * x[1];
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CSSRR
|
||||
the C bit --- 1<<4 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
@@ -352,7 +231,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *res,
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double dist2 = l2norm2<2>(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d = 0; d < 2; ++d)
|
||||
@@ -695,25 +574,25 @@ static MFEM_HOST_DEVICE double tensor_ig2_j(double *g_partials,
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsLocal2D_Kernel(const int npt,
|
||||
const double tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
static void FindPointsLocal2DKernel(const int npt,
|
||||
const double tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
@@ -1175,30 +1054,45 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
FindPointsLocal2DKernel<2>(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 3:
|
||||
return FindPointsLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
FindPointsLocal2DKernel<3>(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 4:
|
||||
return FindPointsLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
FindPointsLocal2DKernel<4>(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 5:
|
||||
return FindPointsLocal2D_Kernel<5>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
FindPointsLocal2DKernel<5>(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
default:
|
||||
return FindPointsLocal2D_Kernel(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx,
|
||||
plhm, plhf, plho, pcode, pelem,
|
||||
pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
FindPointsLocal2DKernel(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc, DEV.dof1d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef DIM2
|
||||
|
||||
+29
-157
@@ -11,9 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
#include <climits>
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -59,128 +57,15 @@ struct findptsElemPt
|
||||
double x[DIM], jac[DIM * DIM], hes[18];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[DIM], A[DIM * DIM];
|
||||
dbl_range_t x[DIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[DIM];
|
||||
double fac[DIM];
|
||||
unsigned int *offset;
|
||||
// int max;
|
||||
};
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first derivative at x.
|
||||
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2*(x-z[j]);
|
||||
u1 = d_j*u1+u0;
|
||||
u0 = d_j*u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i]*u0;
|
||||
p0[pN+i] = 2.0*lCoeff[i]*u1;
|
||||
}
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first and second derivative at x.
|
||||
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2*(x-z[j]);
|
||||
u2 = d_j*u2+u1;
|
||||
u1 = d_j*u1+u0;
|
||||
u0 = d_j*u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i]*u0;
|
||||
p0[pN+i] = 2.0*lCoeff[i]*u1;
|
||||
p0[2*pN+i] = 8.0*lCoeff[i]*u2;
|
||||
}
|
||||
|
||||
// Axis-aligned bounding box test.
|
||||
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
|
||||
const double x[3])
|
||||
{
|
||||
double b_d;
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
b_d = (x[d]-b->x[d].min)*(b->x[d].max-x[d]);
|
||||
if (b_d < 0) { return b_d; }
|
||||
}
|
||||
return b_d;
|
||||
}
|
||||
|
||||
// Axis-aligned bounding box test followed by oriented bounding-box test.
|
||||
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
|
||||
const double x[3])
|
||||
{
|
||||
const double bxyz = AABB_test(b, x);
|
||||
if (bxyz < 0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[3];
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
dxyz[d] = x[d]-b->c0[d];
|
||||
}
|
||||
double test = 1;
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e = 0; e < 3; ++e)
|
||||
{
|
||||
rst += b->A[d*3+e]*dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test < 0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
// Element index corresponding to hash mesh that the point is located in.
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[3])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d = 3-1; d >= 0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d]-p->bnd[d].min)*p->fac[d]);
|
||||
sum += i < 0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
using dbl_range_t = gslib::dbl_range_t;
|
||||
using obbox_t = gslib::obbox_t<DIM>;
|
||||
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<DIM>;
|
||||
using gslib::bbox_test;
|
||||
using gslib::hash_index;
|
||||
using gslib::l2norm2;
|
||||
using gslib::lag_eval_first_der;
|
||||
using gslib::lag_eval_second_der;
|
||||
using gslib::lin_solve_sym_2;
|
||||
|
||||
// Solve Ax=y. A is row-major.
|
||||
static MFEM_HOST_DEVICE inline void lin_solve_3(double x[3], const double A[9],
|
||||
@@ -199,22 +84,6 @@ static MFEM_HOST_DEVICE inline void lin_solve_3(double x[3], const double A[9],
|
||||
x[2] = idet*(inv6*y[0]+inv7*y[1]+inv8*y[2]);
|
||||
}
|
||||
|
||||
// Solve Ax=y. A is a symmetric 2x2 matrix.
|
||||
static MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
|
||||
const double A[3],
|
||||
const double y[2])
|
||||
{
|
||||
const double idet = 1 / (A[0]*A[2]-A[1]*A[1]);
|
||||
x[0] = idet*(A[2]*y[0]-A[1]*y[1]);
|
||||
x[1] = idet*(A[0]*y[1]-A[1]*y[0]);
|
||||
}
|
||||
|
||||
// L2 norm.
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[3])
|
||||
{
|
||||
return x[0]*x[0]+x[1]*x[1]+x[2]*x[2];
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CTTSSRR
|
||||
the C bit --- 1<<6 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
@@ -459,7 +328,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsPt *res,
|
||||
const findptsPt *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double dist2 = l2norm2<3>(resid);
|
||||
const double decr = p->dist2-dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d = 0; d < 3; ++d)
|
||||
@@ -1809,33 +1678,36 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
{
|
||||
case 2:
|
||||
FindPointsLocal3DKernel<2>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
pgslm, NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
break;
|
||||
case 3:
|
||||
FindPointsLocal3DKernel<3>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
pgslm, NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
break;
|
||||
case 4:
|
||||
FindPointsLocal3DKernel<4>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
pgslm, NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
break;
|
||||
case 5:
|
||||
FindPointsLocal3DKernel<5>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
pgslm, NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
break;
|
||||
default:
|
||||
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc,
|
||||
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist, pgll1d, plc,
|
||||
DEV.dof1d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef pMax
|
||||
|
||||
+107
-176
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -52,113 +53,14 @@ struct findptsElementGPT_t
|
||||
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*rDIM];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside
|
||||
}
|
||||
|
||||
/* positive when given point is possibly inside given obbox b */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b,x);
|
||||
if (bxyz<0) // test if point is in AABB
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else // test OBB only if inside AABB
|
||||
{
|
||||
double dxyz[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*2 + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[2])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
|
||||
{
|
||||
return x[0] * x[0] + x[1] * x[1];
|
||||
}
|
||||
using dbl_range_t = gslib::dbl_range_t;
|
||||
using obbox_t = gslib::obbox_t<sDIM>;
|
||||
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
|
||||
using gslib::AABB_test;
|
||||
using gslib::bbox_test;
|
||||
using gslib::hash_index;
|
||||
using gslib::l2norm2;
|
||||
using gslib::lag_eval_second_der;
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
@@ -187,29 +89,29 @@ static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
|
||||
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
|
||||
const double resid[2],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double dist2 = l2norm2<2>(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
out->x[0] = p->x[0];
|
||||
out->x[1] = p->x[1];
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
out_pt->x[0] = p->x[0];
|
||||
out_pt->x[1] = p->x[1];
|
||||
out_pt->oldr = p->r;
|
||||
out_pt->dist2 = dist2;
|
||||
if (decr >= 0.01*pred)
|
||||
{
|
||||
if (decr >= 0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = p->tr*2;
|
||||
out_pt->tr = p->tr*2;
|
||||
}
|
||||
else // somewhat good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
out_pt->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -220,21 +122,21 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
out_pt->tr = v0/4.0;
|
||||
out_pt->dist2 = p->dist2;
|
||||
out_pt->r = p->oldr;
|
||||
out_pt->flags = p->flags>>3;
|
||||
out_pt->dist2p = -HUGE_VAL;
|
||||
if (pred < dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
out_pt->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge( findptsElementPoint_t *const
|
||||
out,
|
||||
out_pt,
|
||||
const double jac[2],
|
||||
const double rhess,
|
||||
const double resid[2],
|
||||
@@ -304,9 +206,9 @@ newton_edge_fin:
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = newr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
out_pt->r = newr;
|
||||
out_pt->dist2p = -v;
|
||||
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
|
||||
@@ -332,26 +234,27 @@ static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal2D_Kernel( const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0 )
|
||||
static void FindPointsEdgeLocal2DKernel( const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const bool obb_check,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0 )
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
@@ -412,22 +315,34 @@ static void FindPointsEdgeLocal2D_Kernel( const int npt,
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
|
||||
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
|
||||
bool pass_bb = true;
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
if (obb_check)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
pass_bb = (bbox_test(&box, x_i) >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int d = 0; d < sDIM; ++d)
|
||||
{
|
||||
box.x[d].min = boxinfo[n_box_ents*el + d];
|
||||
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
|
||||
}
|
||||
pass_bb = (AABB_test(&box, x_i) >= 0);
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (obbox_test(&box,x_i)>=0)
|
||||
if (pass_bb)
|
||||
{
|
||||
//------------ findpts_local ------------------
|
||||
{
|
||||
@@ -516,11 +431,14 @@ static void FindPointsEdgeLocal2D_Kernel( const int npt,
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
@@ -681,28 +599,41 @@ void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
const bool obb_chk = obb_check;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal2DKernel<2>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 3:
|
||||
return FindPointsEdgeLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal2DKernel<3>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 4:
|
||||
return FindPointsEdgeLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal2DKernel<4>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
default:
|
||||
return FindPointsEdgeLocal2D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
FindPointsEdgeLocal2DKernel(npt, DEV.newt_tol, dist2tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc, DEV.dof1d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef sDIM
|
||||
|
||||
+109
-181
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -54,117 +55,14 @@ struct findptsElementGPT_t
|
||||
double x[sDIM], jac[sDIM], hes[sDIM*(1+1)];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j=0; j<pN; ++j)
|
||||
{
|
||||
if (i!=j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside in all dimensions
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b, x);
|
||||
if (bxyz<0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[3];
|
||||
// dxyz: distance of the point from the center of the OBB
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
// transform dxyz to the local coordinate system of the OBB,
|
||||
// and check if the point is inside the OBB [-1,1]^sDIM
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*sDIM + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
|
||||
{
|
||||
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
|
||||
}
|
||||
using dbl_range_t = gslib::dbl_range_t;
|
||||
using obbox_t = gslib::obbox_t<sDIM>;
|
||||
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
|
||||
using gslib::AABB_test;
|
||||
using gslib::bbox_test;
|
||||
using gslib::hash_index;
|
||||
using gslib::l2norm2;
|
||||
using gslib::lag_eval_second_der;
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
@@ -175,47 +73,46 @@ static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
|
||||
#define CONVERGED_FLAG (1u<<2)
|
||||
#define FLAG_MASK 0x07u
|
||||
|
||||
/* returns the number of constrained reference coordinates, max 2
|
||||
/* returns the number of constrained reference coordinates, max 1
|
||||
*/
|
||||
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
|
||||
{
|
||||
const int y = (flags | flags>>1);
|
||||
return (y & 1u) + (y>>2 & 1u);
|
||||
return ((flags | flags>>1) & 1u);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
{
|
||||
return ((x>>1)&1u) | ((x>>2)&2u);
|
||||
return ((x>>1)&1u);
|
||||
}
|
||||
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
|
||||
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
|
||||
const double resid[3],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = norm2(resid);
|
||||
const double dist2 = l2norm2<sDIM>(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
out->x[d] = p->x[d];
|
||||
out_pt->x[d] = p->x[d];
|
||||
}
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
out_pt->oldr = p->r;
|
||||
out_pt->dist2 = dist2;
|
||||
if (decr>=0.01*pred)
|
||||
{
|
||||
if (decr>=0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = 2*p->tr;
|
||||
out_pt->tr = 2*p->tr;
|
||||
}
|
||||
else // good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
out_pt->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -226,21 +123,21 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
out_pt->tr = v0/4.0;
|
||||
out_pt->dist2 = p->dist2;
|
||||
out_pt->r = p->oldr;
|
||||
out_pt->flags = p->flags>>3;
|
||||
out_pt->dist2p = -HUGE_VAL;
|
||||
if (pred<dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
out_pt->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
|
||||
out,
|
||||
out_pt,
|
||||
const double jac[sDIM*rDIM],
|
||||
const double rhes,
|
||||
const double resid[sDIM],
|
||||
@@ -314,9 +211,9 @@ newton_edge_fin:
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = nr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
out_pt->r = nr;
|
||||
out_pt->dist2p = -v;
|
||||
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
@@ -338,31 +235,32 @@ static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
|
||||
{
|
||||
dx[d] = x[d] - elx[d][ir];
|
||||
}
|
||||
dist2[ir] = norm2(dx);;
|
||||
dist2[ir] = l2norm2(dx);
|
||||
r[ir] = z[ir];
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal3D_Kernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
static void FindPointsEdgeLocal3DKernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const bool obb_check,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
@@ -419,21 +317,35 @@ static void FindPointsEdgeLocal3D_Kernel(const int npt,
|
||||
for (; elp!=ele; ++elp)
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
|
||||
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
|
||||
bool pass_bb = true;
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
if (obb_check)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
pass_bb = (bbox_test(&box, x_i) >= 0);
|
||||
}
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
else
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
for (int d = 0; d < sDIM; ++d)
|
||||
{
|
||||
box.x[d].min = boxinfo[n_box_ents*el + d];
|
||||
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
|
||||
}
|
||||
pass_bb = (AABB_test(&box, x_i) >= 0);
|
||||
}
|
||||
|
||||
if (obbox_test(&box, x_i)>=0)
|
||||
if (pass_bb)
|
||||
{
|
||||
//// findpts_local ////
|
||||
{
|
||||
@@ -521,11 +433,14 @@ static void FindPointsEdgeLocal3D_Kernel(const int npt,
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
@@ -688,28 +603,41 @@ void FindPointsGSLIB::FindPointsEdgeLocal3(const Vector &point_pos,
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
const bool obb_chk = obb_check;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal3D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal3DKernel<2>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 3:
|
||||
return FindPointsEdgeLocal3D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal3DKernel<3>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 4:
|
||||
return FindPointsEdgeLocal3D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsEdgeLocal3DKernel<4>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
default:
|
||||
return FindPointsEdgeLocal3D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
FindPointsEdgeLocal3DKernel(npt, DEV.newt_tol, dist2tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc, DEV.dof1d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef rDIM2
|
||||
|
||||
+131
-206
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
@@ -51,124 +52,15 @@ struct findptsElementGPT_t
|
||||
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*(rDIM+1)];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x - z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p0[pN+i] = 2.0 * lCoeff[i] * u1;
|
||||
p0[2*pN+i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside in all dimensions
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = AABB_test(b, x);
|
||||
if (bxyz<0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[3];
|
||||
// dxyz: distance of the point from the center of the OBB
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
// tranform dxyz to the local coordinate system of the OBB,
|
||||
// and check if the point is inside the OBB [-1,1]^sDIM
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*sDIM + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
|
||||
const double A[3],
|
||||
const double y[2])
|
||||
{
|
||||
const double idet = 1 / (A[0] * A[2] - A[1] * A[1]);
|
||||
x[0] = idet * (A[2] * y[0] - A[1] * y[1]);
|
||||
x[1] = idet * (A[0] * y[1] - A[1] * y[0]);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[sDIM])
|
||||
{
|
||||
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);
|
||||
}
|
||||
using dbl_range_t = gslib::dbl_range_t;
|
||||
using obbox_t = gslib::obbox_t<sDIM>;
|
||||
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
|
||||
using gslib::AABB_test;
|
||||
using gslib::bbox_test;
|
||||
using gslib::hash_index;
|
||||
using gslib::l2norm2;
|
||||
using gslib::lag_eval_second_der;
|
||||
using gslib::lin_solve_sym_2;
|
||||
|
||||
/* the bit structure of flags is CSSRR
|
||||
the C bit --- 1<<4 --- is set when the point is converged
|
||||
@@ -219,18 +111,10 @@ static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
return ((x>>1)&1u) | ((x>>2)&2u);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline findptsElementGEdge_t
|
||||
static MFEM_HOST_DEVICE inline void
|
||||
get_edge(const double *elx[3], const double *wtend, int ei,
|
||||
double *workspace, int &side_init, int jidx, int pN)
|
||||
int &side_init, int jidx, int pN, findptsElementGEdge_t &edge)
|
||||
{
|
||||
findptsElementGEdge_t edge;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = workspace + d*pN;
|
||||
edge.dxdn[d] = workspace + sDIM*pN + d*pN;
|
||||
edge.d2xdn[d] = workspace + 2*sDIM*pN + d*pN;
|
||||
}
|
||||
|
||||
// given edge index, compute normal and tangential directions
|
||||
const int dn = ei>>1, //0 for rmin/rmax, 1 for smin/smax
|
||||
de = plus_1_mod_2(dn); // 1 for rmin/rmax, 0 for smin/smax
|
||||
@@ -256,7 +140,6 @@ get_edge(const double *elx[3], const double *wtend, int ei,
|
||||
edge.d2xdn[dd][jj] = sums_k[1];
|
||||
#undef ELX
|
||||
}
|
||||
return edge;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[3],
|
||||
@@ -312,34 +195,34 @@ static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[3],
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
|
||||
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
|
||||
const double resid[3],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double dist2 = l2norm2<sDIM>(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
out->x[d] = p->x[d];
|
||||
out_pt->x[d] = p->x[d];
|
||||
}
|
||||
for (int d=0; d<rDIM; ++d)
|
||||
{
|
||||
out->oldr[d] = p->r[d];
|
||||
out_pt->oldr[d] = p->r[d];
|
||||
}
|
||||
out->dist2 = dist2;
|
||||
out_pt->dist2 = dist2;
|
||||
if (decr>=0.01*pred)
|
||||
{
|
||||
if (decr>=0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = 2*p->tr;
|
||||
out_pt->tr = 2*p->tr;
|
||||
}
|
||||
else // good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
out_pt->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -351,17 +234,17 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r[0] - p->oldr[0]),
|
||||
v1 = fabs(p->r[1] - p->oldr[1]);
|
||||
out->tr = ( v0>v1 ? v0 : v1 )/4;
|
||||
out->dist2 = p->dist2;
|
||||
out->flags = p->flags >> 5;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
out_pt->tr = ( v0>v1 ? v0 : v1 )/4;
|
||||
out_pt->dist2 = p->dist2;
|
||||
out_pt->flags = p->flags >> 5;
|
||||
out_pt->dist2p = -HUGE_VAL;
|
||||
for (int d=0; d<rDIM; ++d)
|
||||
{
|
||||
out->r[d] = p->oldr[d];
|
||||
out_pt->r[d] = p->oldr[d];
|
||||
}
|
||||
if (pred<dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
out_pt->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -369,7 +252,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
|
||||
/* minimize ||resid - jac * dr||_2, with |dr| <= tr, |r0+dr|<=1
|
||||
(exact solution of trust region problem) */
|
||||
static MFEM_HOST_DEVICE void newton_face( findptsElementPoint_t *const out,
|
||||
static MFEM_HOST_DEVICE void newton_face( findptsElementPoint_t *const out_pt,
|
||||
const double jac[sDIM*rDIM],
|
||||
const double rhes[3],
|
||||
const double resid[sDIM],
|
||||
@@ -540,19 +423,19 @@ newton_face_constrained:
|
||||
}
|
||||
|
||||
newton_face_fin:
|
||||
out->dist2p = -2*v;
|
||||
out_pt->dist2p = -2*v;
|
||||
dr[0] = r[0] - p->r[0];
|
||||
dr[1] = r[1] - p->r[1];
|
||||
if ( fabs(dr[0])+fabs(dr[1]) < tol)
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r[0] = r[0], out->r[1] = r[1];
|
||||
out->flags = new_flags | ((p->flags & FLAG_MASK)<<5);
|
||||
out_pt->r[0] = r[0], out_pt->r[1] = r[1];
|
||||
out_pt->flags = new_flags | ((p->flags & FLAG_MASK)<<5);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
|
||||
out,
|
||||
out_pt,
|
||||
const double jac[sDIM*rDIM],
|
||||
const double rhes,
|
||||
const double resid[sDIM],
|
||||
@@ -637,10 +520,10 @@ newton_edge_fin:
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r[de] = nr;
|
||||
out->r[dn] = p->r[dn];
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<5);
|
||||
out_pt->r[de] = nr;
|
||||
out_pt->r[dn] = p->r[dn];
|
||||
out_pt->dist2p = -v;
|
||||
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<5);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
@@ -676,26 +559,27 @@ static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
|
||||
// global memory access of element coordinates.
|
||||
// Are the structs being stored in "local memory" or registers?
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsSurfLocal3D_Kernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
static void FindPointsSurfLocal3DKernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const bool obb_check,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
@@ -753,22 +637,36 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
|
||||
// construct obbox on the fly
|
||||
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
|
||||
bool pass_bb = true;
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
if (obb_check)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
// construct obbox on the fly
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
pass_bb = (bbox_test(&box, x_i) >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int d = 0; d < sDIM; ++d)
|
||||
{
|
||||
box.x[d].min = boxinfo[n_box_ents*el + d];
|
||||
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
|
||||
}
|
||||
pass_bb = (AABB_test(&box, x_i) >= 0);
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (bbox_test(&box, x_i) < 0) { continue; }
|
||||
if (!pass_bb) { continue; }
|
||||
|
||||
//// findpts_local ////
|
||||
{
|
||||
@@ -968,13 +866,19 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
|
||||
double *hes_T = jac + sDIM*rDIM;
|
||||
double *hes = hes_T + hes_count*sDIM;
|
||||
findptsElementGEdge_t edge;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.dxdn[d] = constraint_workspace + d*D1D
|
||||
+ sDIM*D1D;
|
||||
edge.d2xdn[d] = constraint_workspace + d*D1D
|
||||
+ 2*sDIM*D1D;
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
|
||||
{
|
||||
// utilized first D1D threads
|
||||
edge = get_edge(elx, wtend, ei,
|
||||
constraint_workspace, edge_init, j,
|
||||
D1D);
|
||||
// One thread per physical component and edge DOF.
|
||||
get_edge(elx, wtend, ei, edge_init, j, D1D, edge);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
@@ -1045,7 +949,15 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
|
||||
steep *= tmp->r[dn];
|
||||
if (steep<0)
|
||||
{
|
||||
newton_face( fpt,jac,hes,resid,tmp->flags&CONVERGED_FLAG,tmp,tol);
|
||||
double face_hes[3] =
|
||||
{
|
||||
dn == 0 ? hes[2] : hes[0],
|
||||
hes[1],
|
||||
dn == 0 ? hes[0] : hes[2]
|
||||
};
|
||||
newton_face(fpt, jac, face_hes, resid,
|
||||
tmp->flags & CONVERGED_FLAG,
|
||||
tmp, tol);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1211,29 +1123,42 @@ void FindPointsGSLIB::FindPointsSurfLocal3(const Vector &point_pos,
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
const bool obb_chk = obb_check;
|
||||
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsSurfLocal3D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsSurfLocal3DKernel<2>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 3:
|
||||
return FindPointsSurfLocal3D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsSurfLocal3DKernel<3>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
case 4:
|
||||
return FindPointsSurfLocal3D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
FindPointsSurfLocal3DKernel<4>(npt, DEV.newt_tol, dist2tol,
|
||||
pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
break;
|
||||
default:
|
||||
return FindPointsSurfLocal3D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
FindPointsSurfLocal3DKernel(npt, DEV.newt_tol, dist2tol, pp,
|
||||
point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, obb_chk,
|
||||
DEV.lh_nx, plhm, plhf, plho,
|
||||
pcode, pelem, pref, pdist,
|
||||
pgll1d, plc, DEV.dof1d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifndef MFEM_GSLIB_KERNEL_HELPERS_HPP
|
||||
#define MFEM_GSLIB_KERNEL_HELPERS_HPP
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace gslib
|
||||
{
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
template <int SDIM>
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[SDIM], A[SDIM * SDIM];
|
||||
dbl_range_t x[SDIM];
|
||||
};
|
||||
|
||||
template <int SDIM>
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[SDIM];
|
||||
double fac[SDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
// Eval the ith Lagrange interpolant at x.
|
||||
MFEM_HOST_DEVICE inline void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j = 0; j < p_Nq; ++j)
|
||||
{
|
||||
const double d_j = x - z[j];
|
||||
p_i *= j == i ? 1 : d_j;
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first derivative at x.
|
||||
MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
const double d_j = 2 * (x - z[j]);
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p0[pN + i] = 2.0 * lCoeff[i] * u1;
|
||||
}
|
||||
|
||||
// Eval the ith Lagrange interpolant and its first and second derivative at x.
|
||||
MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
const double d_j = 2 * (x - z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p0[pN + i] = 2.0 * lCoeff[i] * u1;
|
||||
p0[2 * pN + i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
// Solve Ax=y where A is a symmetric 2x2 matrix packed as {a00, a01, a11}.
|
||||
MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
|
||||
const double A[3],
|
||||
const double y[2])
|
||||
{
|
||||
const double idet = 1 / (A[0] * A[2] - A[1] * A[1]);
|
||||
x[0] = idet * (A[2] * y[0] - A[1] * y[1]);
|
||||
x[1] = idet * (A[0] * y[1] - A[1] * y[0]);
|
||||
}
|
||||
|
||||
// Positive when the point is inside the axis-aligned bounding box.
|
||||
template <int SDIM>
|
||||
MFEM_HOST_DEVICE inline double AABB_test(const obbox_t<SDIM> *const b,
|
||||
const double (&x)[SDIM])
|
||||
{
|
||||
double test = 1.0;
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
const double b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
test = test < 0.0 ? test : b_d;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
// Positive when the point is inside the oriented bounding box.
|
||||
template <int SDIM>
|
||||
MFEM_HOST_DEVICE inline double bbox_test(const obbox_t<SDIM> *const b,
|
||||
const double (&x)[SDIM])
|
||||
{
|
||||
const double bxyz = AABB_test(b, x);
|
||||
if (bxyz < 0.0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
|
||||
double dxyz[SDIM];
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
|
||||
double test = 1.0;
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
double rst = 0.0;
|
||||
for (int e = 0; e < SDIM; ++e)
|
||||
{
|
||||
rst += b->A[d * SDIM + e] * dxyz[e];
|
||||
}
|
||||
const double brst = (rst + 1.0) * (1.0 - rst);
|
||||
test = test < 0.0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
// Hash index in the hash table for the point x.
|
||||
template <int SDIM>
|
||||
MFEM_HOST_DEVICE inline int hash_index(
|
||||
const findptsLocalHashData_t<SDIM> *const p,
|
||||
const double (&x)[SDIM])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d = SDIM - 1; d >= 0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
const int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i < 0 ? 0 : (n - 1 < i ? n - 1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Squared Euclidean norm.
|
||||
template <int SDIM>
|
||||
MFEM_HOST_DEVICE inline double l2norm2(const double (&x)[SDIM])
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
sum += x[d] * x[d];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <int SDIM>
|
||||
MFEM_HOST_DEVICE inline double l2norm2(const double *x)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
sum += x[d] * x[d];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
} // namespace gslib
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -33,17 +33,7 @@ namespace mfem
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
|
||||
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j=0; j<p_Nq; ++j)
|
||||
{
|
||||
p_i *= j==i ? 1 : x-z[j];
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
using gslib::lagrange_eval;
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void InterpolateLocal1DKernel(const double *const gf_in,
|
||||
@@ -123,21 +113,26 @@ void FindPointsGSLIB::InterpolateLocal1( const Vector &field_in,
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
case 2:
|
||||
InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 3:
|
||||
InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 4:
|
||||
InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 5:
|
||||
InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
default:
|
||||
InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf, dof1Dsol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef CODE_INTERNAL
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -32,18 +33,7 @@ namespace mfem
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
|
||||
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j = 0; j < p_Nq; ++j)
|
||||
{
|
||||
double d_j = x - z[j];
|
||||
p_i *= j == i ? 1 : d_j;
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
using gslib::lagrange_eval;
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
@@ -132,21 +122,26 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
case 2:
|
||||
InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 3:
|
||||
InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 4:
|
||||
InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 5:
|
||||
InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
default:
|
||||
InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf, dof1Dsol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "gslib_kernel_helpers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -32,18 +33,7 @@ namespace mfem
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
|
||||
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j = 0; j < p_Nq; ++j)
|
||||
{
|
||||
double d_j = x - z[j];
|
||||
p_i *= j == i ? 1 : d_j;
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
using gslib::lagrange_eval;
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void InterpolateLocal3DKernel(const double *const gf_in,
|
||||
@@ -135,21 +125,26 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
case 2:
|
||||
InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 3:
|
||||
InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 4:
|
||||
InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
case 5:
|
||||
InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf);
|
||||
break;
|
||||
default:
|
||||
InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, pgll, plcf, dof1Dsol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp" // IWYU pragma: keep
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -19,6 +20,13 @@ namespace mfem
|
||||
DiffusionIntegrator::Kernels::Kernels()
|
||||
{
|
||||
// 2D
|
||||
// Q = P, only for simplex
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,2,1>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,3,2>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,4,3>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,5,4>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,6,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,7,6>();
|
||||
// Q = P+1
|
||||
DiffusionIntegrator::AddSpecialization<2,1,1>();
|
||||
DiffusionIntegrator::AddSpecialization<2,2,2>();
|
||||
@@ -40,7 +48,18 @@ DiffusionIntegrator::Kernels::Kernels()
|
||||
DiffusionIntegrator::AddSpecialization<2,8,9>();
|
||||
DiffusionIntegrator::AddSpecialization<2,9,10>();
|
||||
// others
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,2,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,3,6>();
|
||||
|
||||
// 3D
|
||||
// Q = P, only for simplex
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,2,1>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,3,2>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,4,3>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,5,4>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,6,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,7,6>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,8,7>();
|
||||
// Q = P+1
|
||||
DiffusionIntegrator::AddSpecialization<3,1,1>();
|
||||
DiffusionIntegrator::AddSpecialization<3,2,2>();
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#ifndef MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
|
||||
#define MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
|
||||
|
||||
#include "../kernel_dispatch.hpp"
|
||||
#include "../../config/config.hpp"
|
||||
#include "../../general/array.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -20,6 +19,8 @@
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -637,8 +638,8 @@ inline void SmemPADiffusionApply2D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &b_,
|
||||
const Array<real_t> &g_,
|
||||
const Array<real_t> &bt_,
|
||||
const Array<real_t> >_,
|
||||
const Array<real_t> &,
|
||||
const Array<real_t> &,
|
||||
const Vector &d_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
@@ -1218,43 +1219,47 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
namespace
|
||||
{
|
||||
using ApplyKernelType = DiffusionIntegrator::ApplyKernelType;
|
||||
using ApplySimplexKernelType = DiffusionIntegrator::ApplySimplexKernelType;
|
||||
using DiagonalKernelType = DiffusionIntegrator::DiagonalKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int)
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int dim, int, int)
|
||||
{
|
||||
if (DIM == 2) { return internal::PADiffusionApply2D; }
|
||||
else if (DIM == 3) { return internal::PADiffusionApply3D; }
|
||||
if (dim == 2) { return internal::PADiffusionApply2D; }
|
||||
else if (dim == 3) { return internal::PADiffusionApply3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
DiagonalKernelType DiffusionIntegrator::DiagonalPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D,Q1D>; }
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionDiagonal3D<D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline DiagonalKernelType
|
||||
DiffusionIntegrator::DiagonalPAKernels::Fallback(int DIM, int, int)
|
||||
DiffusionIntegrator::DiagonalPAKernels::Fallback(int dim, int, int)
|
||||
{
|
||||
if (DIM == 2) { return internal::PADiffusionDiagonal2D; }
|
||||
else if (DIM == 3) { return internal::PADiffusionDiagonal3D; }
|
||||
if (dim == 2) { return internal::PADiffusionDiagonal2D; }
|
||||
else if (dim == 3) { return internal::PADiffusionDiagonal3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../../mesh/nurbs.hpp"
|
||||
#include "../ceed/integrators/diffusion/diffusion.hpp"
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -68,6 +69,24 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
|
||||
if (fespace->UsesRaggedTensorBasis())
|
||||
{
|
||||
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
|
||||
return ApplySimplexPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
|
||||
rmaps->lex_map,
|
||||
rmaps->forward_map2d_diff,
|
||||
rmaps->inverse_map2d_diff,
|
||||
rmaps->forward_map3d_diff,
|
||||
rmaps->inverse_map3d_diff,
|
||||
rmaps->Ga1,
|
||||
rmaps->Ga2,
|
||||
rmaps->Ga3,
|
||||
rmaps->Ga1t,
|
||||
rmaps->Ga2t,
|
||||
rmaps->Ga3t,
|
||||
Dv, x, y, dofs1D, quad1D);
|
||||
}
|
||||
|
||||
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
|
||||
Gt, Dv, x, y, dofs1D, quad1D);
|
||||
}
|
||||
@@ -94,7 +113,8 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
const FiniteElement &el = *fes.GetTypicalFE();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
|
||||
const bool stroud = fes.UsesRaggedTensorBasis();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, stroud);
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
@@ -119,13 +139,22 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
dim = mesh->Dimension();
|
||||
ne = fes.GetNE();
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
|
||||
if (stroud)
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
}
|
||||
const int sdim = mesh->SpaceDimension();
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(qs, CoefficientStorage::COMPRESSED);
|
||||
// QuadratureSpace expects ir defined in reference simplex for Bernstein
|
||||
// elements with partial assembly
|
||||
|
||||
if (MQ) { coeff.ProjectTranspose(*MQ); }
|
||||
else if (VQ) { coeff.Project(*VQ); }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "bilininteg_mass_kernels.hpp"
|
||||
#include "bilininteg_mass_pa_simplices.hpp" // IWYU pragma: keep
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -39,8 +40,10 @@ MassIntegrator::Kernels::Kernels()
|
||||
MassIntegrator::AddSpecialization<2,9,10>();
|
||||
// others
|
||||
MassIntegrator::AddSpecialization<2,2,4>();
|
||||
MassIntegrator::AddSpecialization<2,2,5>();
|
||||
MassIntegrator::AddSpecialization<2,3,6>();
|
||||
MassIntegrator::AddSpecialization<2,4,6>();
|
||||
|
||||
// 3D
|
||||
// Q=P+1
|
||||
MassIntegrator::AddSpecialization<3,1,1>();
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
|
||||
#include "bilininteg_mass_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -1408,51 +1410,57 @@ using ApplyKernelType = MassIntegrator::ApplyKernelType;
|
||||
using DiagonalKernelType = MassIntegrator::DiagonalKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
constexpr int MDQ = T_D1D >= T_Q1D ? T_D1D : T_Q1D;
|
||||
constexpr int MDQ = D1D >= Q1D ? D1D : Q1D;
|
||||
// max 64 threads in z limit in cuda and hip
|
||||
if constexpr (MDQ > 0)
|
||||
{
|
||||
return internal::SmemPAMassApply3D<T_D1D, T_Q1D,
|
||||
return internal::SmemPAMassApply3D<D1D, Q1D,
|
||||
internal::mass::NBZ3D(MDQ)>;
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("");
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline ApplyKernelType MassIntegrator::ApplyPAKernels::Fallback(
|
||||
int DIM, int, int)
|
||||
int dim, int, int)
|
||||
{
|
||||
if (DIM == 1) { return internal::PAMassApply1D; }
|
||||
else if (DIM == 2) { return internal::PAMassApply2D; }
|
||||
else if (DIM == 3) { return internal::PAMassApply3D; }
|
||||
if (dim == 1) { return internal::PAMassApply1D; }
|
||||
else if (dim == 2) { return internal::PAMassApply2D; }
|
||||
else if (dim == 3) { return internal::PAMassApply3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
DiagonalKernelType MassIntegrator::DiagonalPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline DiagonalKernelType MassIntegrator::DiagonalPAKernels::Fallback(
|
||||
int DIM, int, int)
|
||||
int dim, int, int)
|
||||
{
|
||||
if (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if (DIM == 2) { return internal::PAMassAssembleDiagonal2D; }
|
||||
else if (DIM == 3) { return internal::PAMassAssembleDiagonal3D; }
|
||||
if (dim == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if (dim == 2) { return internal::PAMassAssembleDiagonal2D; }
|
||||
else if (dim == 3) { return internal::PAMassAssembleDiagonal3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../qfunction.hpp"
|
||||
#include "../ceed/integrators/mass/mass.hpp"
|
||||
#include "bilininteg_mass_kernels.hpp"
|
||||
#include "bilininteg_mass_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -29,9 +30,11 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
// Assuming the same element type
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
dim = mesh->Dimension();
|
||||
const FiniteElement &el = *fes.GetTypicalFE();
|
||||
ElementTransformation *T0 = mesh->GetTypicalElementTransformation();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0);
|
||||
const bool stroud = fes.UsesRaggedTensorBasis();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0, stroud);
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
@@ -48,17 +51,25 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
return;
|
||||
}
|
||||
int map_type = el.GetMapType();
|
||||
dim = mesh->Dimension();
|
||||
ne = fes.GetMesh()->GetNE();
|
||||
nq = ir->GetNPoints();
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS, mt);
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
if (stroud)
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
}
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
pa_data.SetSize(ne*nq, mt);
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
|
||||
// QuadratureSpace expects ir defined in reference simplex for Bernstein
|
||||
// elements with partial assembly
|
||||
{
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
@@ -147,9 +158,10 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int D1D = dofs1D;
|
||||
const int Q1D = quad1D;
|
||||
const Vector &D = pa_data;
|
||||
const Array<real_t> &B = maps->B;
|
||||
const Array<real_t> &Bt = maps->Bt;
|
||||
const Vector &D = pa_data;
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
if (DeviceCanUseOcca())
|
||||
{
|
||||
@@ -164,7 +176,31 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
|
||||
|
||||
if (fespace->UsesRaggedTensorBasis())
|
||||
{
|
||||
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
|
||||
|
||||
const Array<real_t> &Ba1 = rmaps->Ba1;
|
||||
const Array<real_t> &Ba2 = rmaps->Ba2;
|
||||
const Array<real_t> &Ba3 = rmaps->Ba3;
|
||||
const Array<real_t> &Ba1t = rmaps->Ba1t;
|
||||
const Array<real_t> &Ba2t = rmaps->Ba2t;
|
||||
const Array<real_t> &Ba3t = rmaps->Ba3t;
|
||||
const Array<int> &lex_map = rmaps->lex_map;
|
||||
const Array<int> &forward_map2d = rmaps->forward_map2d_mass;
|
||||
const Array<int> &inverse_map2d = rmaps->inverse_map2d_mass;
|
||||
const Array<int> &forward_map3d = rmaps->forward_map3d_mass;
|
||||
const Array<int> &inverse_map3d = rmaps->inverse_map3d_mass;
|
||||
ApplySimplexPAKernels::Run(dim, D1D, Q1D, ne, lex_map, forward_map2d,
|
||||
inverse_map2d,
|
||||
forward_map3d, inverse_map3d, Ba1, Ba2, Ba3, Ba1t, Ba2t, Ba3t,
|
||||
D, x, y, D1D, Q1D);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +213,8 @@ void MassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(!fespace->UsesRaggedTensorBasis(),
|
||||
"AbsMultPA not implemented for ragged tensor basis");
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
Array<real_t> absB(maps->B);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -307,6 +307,506 @@ DomainLFIntegrator::AssembleKernels::Kernel()
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
template <int T_D1D = 0, int T_Q1D = 0>
|
||||
static void HdivDLFAssemble2D(const int ne, const Array<int> &markers,
|
||||
const Vector &jac, const Array<real_t> &weights,
|
||||
const Array<real_t> &testBO,
|
||||
const Array<real_t> &testBC, const Vector &coeff,
|
||||
Vector &y, const int d, const int q)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(y.Size() == 2 * (d - 1) * d * ne, "");
|
||||
|
||||
constexpr int vdim = 2;
|
||||
const auto F = coeff.Read();
|
||||
const auto M = markers.Read();
|
||||
const auto BO = Reshape(testBO.Read(), q, d-1);
|
||||
const auto BC = Reshape(testBC.Read(), q, d);
|
||||
const auto J = Reshape(jac.Read(), q, q, vdim, vdim, ne);
|
||||
const auto W = Reshape(weights.Read(), q, q);
|
||||
const bool cst = coeff.Size() == vdim;
|
||||
const auto C = cst ? Reshape(F,vdim,1,1,1) : Reshape(F,vdim,q,q,ne);
|
||||
auto Y = y.ReadWrite();
|
||||
|
||||
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int vdim = 2;
|
||||
if (M[e] == 0) { return; } // ignore
|
||||
|
||||
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
|
||||
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t sBot[Q*D];
|
||||
MFEM_SHARED real_t sBct[Q*D];
|
||||
MFEM_SHARED real_t sQQ[vdim*Q*Q];
|
||||
MFEM_SHARED real_t sQD[vdim*Q*D];
|
||||
|
||||
// Bo and Bc into shared memory
|
||||
const DeviceMatrix Bot(sBot, d-1, q);
|
||||
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
|
||||
const DeviceMatrix Bct(sBct, d, q);
|
||||
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
|
||||
|
||||
const DeviceCube QQ(sQQ, q, q, vdim);
|
||||
const DeviceCube QD(sQD, q, d, vdim);
|
||||
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const real_t cst_val_0 = C(0,0,0,0);
|
||||
const real_t cst_val_1 = C(1,0,0,0);
|
||||
MFEM_FOREACH_THREAD(y,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(x,x,q)
|
||||
{
|
||||
const real_t J0 = J(x,y,0,vd,e);
|
||||
const real_t J1 = J(x,y,1,vd,e);
|
||||
const real_t C0 = cst ? cst_val_0 : C(0,x,y,e);
|
||||
const real_t C1 = cst ? cst_val_1 : C(1,x,y,e);
|
||||
QQ(x,y,vd) = W(x,y)*(J0*C0 + J1*C1);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(qy,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t qd = 0.0;
|
||||
for (int qx = 0; qx < q; ++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) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
|
||||
DeviceTensor<4> Yxy(Y, nx, ny, vdim, ne);
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t dd = 0.0;
|
||||
for (int qy = 0; qy < q; ++qy)
|
||||
{
|
||||
dd += QD(dx,qy,vd) * Bty(dy,qy);
|
||||
}
|
||||
Yxy(dx,dy,vd,e) += dd;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_D1D = 0, int T_Q1D = 0>
|
||||
static void HdivDLFAssemble3D(const int ne, const Array<int> &markers,
|
||||
const Vector &jac, const Array<real_t> &weights,
|
||||
const Array<real_t> &testBO,
|
||||
const Array<real_t> &testBC, const Vector &coeff,
|
||||
Vector &y, const int d, const int q)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(y.Size() == 3 * (d - 1) * (d - 1) * d * ne, "y wrong length");
|
||||
|
||||
constexpr int vdim = 3;
|
||||
const auto F = coeff.Read();
|
||||
const auto M = markers.Read();
|
||||
const auto BO = Reshape(testBO.Read(), q, d-1);
|
||||
const auto BC = Reshape(testBC.Read(), q, d);
|
||||
const auto J = Reshape(jac.Read(), q, q, q, vdim, vdim, ne);
|
||||
const auto W = Reshape(weights.Read(), q, q, q);
|
||||
const bool cst = coeff.Size() == vdim;
|
||||
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
|
||||
auto Y = y.ReadWrite();
|
||||
|
||||
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int vdim = 3;
|
||||
if (M[e] == 0) { return; } // ignore
|
||||
|
||||
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
|
||||
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t sBot[Q*D];
|
||||
MFEM_SHARED real_t sBct[Q*D];
|
||||
|
||||
// Bo and Bc into shared memory
|
||||
const DeviceMatrix Bot(sBot, d-1, q);
|
||||
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
|
||||
const DeviceMatrix Bct(sBct, d, q);
|
||||
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
|
||||
|
||||
MFEM_SHARED real_t sm0[vdim*Q*Q*Q];
|
||||
MFEM_SHARED real_t sm1[vdim*Q*Q*Q];
|
||||
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
|
||||
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
|
||||
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
|
||||
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const real_t cst_val_0 = C(0,0,0,0,0);
|
||||
const real_t cst_val_1 = C(1,0,0,0,0);
|
||||
const real_t cst_val_2 = C(2,0,0,0,0);
|
||||
MFEM_FOREACH_THREAD(y,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(x,x,q)
|
||||
{
|
||||
for (int z = 0; z < q; ++z)
|
||||
{
|
||||
const real_t J0 = J(x,y,z,0,vd,e);
|
||||
const real_t J1 = J(x,y,z,1,vd,e);
|
||||
const real_t J2 = J(x,y,z,2,vd,e);
|
||||
const real_t C0 = cst ? cst_val_0 : C(0,x,y,z,e);
|
||||
const real_t C1 = cst ? cst_val_1 : C(1,x,y,z,e);
|
||||
const real_t C2 = cst ? cst_val_2 : C(2,x,y,z,e);
|
||||
QQQ(x,y,z,vd) = W(x,y,z)*(J0*C0 + J1*C1 + J2*C2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply Bt operator
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(qy,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qx = 0; qx < q; ++qx)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += QQQ(qx,qy,qz,vd) * Btx(dx,qx);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { DQQ(dx,qy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qy = 0; qy < q; ++qy)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += DQQ(dx,qy,qz,vd) * Bty(dy,qy);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { DDQ(dx,dy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
const int nz = (vd == 2) ? d : d-1;
|
||||
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
|
||||
DeviceMatrix Btz = (vd == 2) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[D];
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += DDQ(dx,dy,qz,vd) * Btz(dz,qz);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz) { Yxyz(dx,dy,dz,vd,e) += u[dz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
/// @param ne number of elements
|
||||
/// @param markers array where entry markers[e] == 0 to skip assembly over
|
||||
/// element e element
|
||||
/// @param jac Spatial Jacobians evaluated at all quadrature points
|
||||
/// @param weights 1D quadrature weights
|
||||
/// @param testBO 1D open basis test functions
|
||||
/// @param testBC 1D closed basis test functions
|
||||
/// @param coeff coefficient values evaluated at quadrature points, possibly
|
||||
/// compressed.
|
||||
/// @param d number of 1D closed dofs
|
||||
/// @param q number of 1D quadrature points
|
||||
/// @tparam T_D1D maximum number of dofs along any direction, or 0
|
||||
/// @tparam T_Q1D maximum number of quadrature points along any direction, or 0
|
||||
template <int T_D1D = 0, int T_Q1D = 0>
|
||||
static void HcurlDLFAssemble3D(const int ne, const Array<int> &markers,
|
||||
const Vector &jac, const Array<real_t> &weights,
|
||||
const Array<real_t> &testBO,
|
||||
const Array<real_t> &testBC, const Vector &coeff,
|
||||
Vector &y, const int d, const int q)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(y.Size() == 3 * (d - 1) * d * d * ne, "y wrong length");
|
||||
|
||||
constexpr int vdim = 3;
|
||||
const auto F = coeff.Read();
|
||||
const auto M = markers.Read();
|
||||
const auto BO = Reshape(testBO.Read(), q, d-1);
|
||||
const auto BC = Reshape(testBC.Read(), q, d);
|
||||
const auto J = Reshape(jac.Read(), q, q, q, vdim, vdim, ne);
|
||||
const auto W = Reshape(weights.Read(), q, q, q);
|
||||
const bool cst = coeff.Size() == vdim;
|
||||
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
|
||||
auto Y = y.ReadWrite();
|
||||
|
||||
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
if (M[e] == 0)
|
||||
{
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int vdim = 3;
|
||||
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
|
||||
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t sBot[Q * D];
|
||||
MFEM_SHARED real_t sBct[Q * D];
|
||||
|
||||
// Bo and Bc into shared memory
|
||||
const DeviceMatrix Bot(sBot, d - 1, q);
|
||||
kernels::internal::LoadB<D, Q>(d - 1, q, BO, sBot);
|
||||
const DeviceMatrix Bct(sBct, d, q);
|
||||
kernels::internal::LoadB<D, Q>(d, q, BC, sBct);
|
||||
|
||||
MFEM_SHARED real_t sm0[vdim * Q * Q * Q];
|
||||
MFEM_SHARED real_t sm1[vdim * Q * Q * Q];
|
||||
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
|
||||
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
|
||||
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
|
||||
|
||||
const real_t cst_val_0 = C(0, 0, 0, 0, 0);
|
||||
const real_t cst_val_1 = C(1, 0, 0, 0, 0);
|
||||
const real_t cst_val_2 = C(2, 0, 0, 0, 0);
|
||||
|
||||
MFEM_FOREACH_THREAD(vd, z, vdim)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(y, y, q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(x, x, q)
|
||||
{
|
||||
for (int z = 0; z < q; ++z)
|
||||
{
|
||||
real_t curr[3];
|
||||
curr[0] = cst ? cst_val_0 : C(0, x, y, z, e);
|
||||
curr[1] = cst ? cst_val_1 : C(1, x, y, z, e);
|
||||
curr[2] = cst ? cst_val_2 : C(2, x, y, z, e);
|
||||
|
||||
const real_t J11 = J(x, y, z, 0, 0, e);
|
||||
const real_t J21 = J(x, y, z, 1, 0, e);
|
||||
const real_t J31 = J(x, y, z, 2, 0, e);
|
||||
const real_t J12 = J(x, y, z, 0, 1, e);
|
||||
const real_t J22 = J(x, y, z, 1, 1, e);
|
||||
const real_t J32 = J(x, y, z, 2, 1, e);
|
||||
const real_t J13 = J(x, y, z, 0, 2, e);
|
||||
const real_t J23 = J(x, y, z, 1, 2, e);
|
||||
const real_t J33 = J(x, y, z, 2, 2, e);
|
||||
// adj(J)
|
||||
const real_t A11 = (J22 * J33) - (J23 * J32);
|
||||
const real_t A12 = (J32 * J13) - (J12 * J33);
|
||||
const real_t A13 = (J12 * J23) - (J22 * J13);
|
||||
const real_t A21 = (J31 * J23) - (J21 * J33);
|
||||
const real_t A22 = (J11 * J33) - (J13 * J31);
|
||||
const real_t A23 = (J21 * J13) - (J11 * J23);
|
||||
const real_t A31 = (J21 * J32) - (J31 * J22);
|
||||
const real_t A32 = (J31 * J12) - (J11 * J32);
|
||||
const real_t A33 = (J11 * J22) - (J12 * J21);
|
||||
const real_t A[9] = {A11, A12, A13, A21, A22,
|
||||
A23, A31, A32, A33
|
||||
};
|
||||
QQQ(x, y, z, vd) = W(x, y, z) * (A[vd * vdim] * curr[0] +
|
||||
A[vd * vdim + 1] * curr[1] +
|
||||
A[vd * vdim + 2] * curr[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply Bt operator
|
||||
MFEM_FOREACH_THREAD(vd, z, vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d - 1 : d;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bot : Bct;
|
||||
MFEM_FOREACH_THREAD(qy, y, q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] = 0.0;
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qx = 0; qx < q; ++qx)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += QQQ(qx, qy, qz, vd) * Btx(dx, qx);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
DQQ(dx, qy, qz, vd) = u[qz];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd, z, vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d - 1 : d;
|
||||
const int ny = (vd == 1) ? d - 1 : d;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bot : Bct;
|
||||
MFEM_FOREACH_THREAD(dy, y, ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] = 0.0;
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qy = 0; qy < q; ++qy)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += DQQ(dx, qy, qz, vd) * Bty(dy, qy);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
DDQ(dx, dy, qz, vd) = u[qz];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd, z, vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d - 1 : d;
|
||||
const int ny = (vd == 1) ? d - 1 : d;
|
||||
const int nz = (vd == 2) ? d - 1 : d;
|
||||
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
|
||||
DeviceMatrix Btz = (vd == 2) ? Bot : Bct;
|
||||
MFEM_FOREACH_THREAD(dy, y, ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, nx)
|
||||
{
|
||||
real_t u[D];
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] = 0.0;
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += DDQ(dx, dy, qz, vd) * Btz(dz, qz);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
Yxyz(dx, dy, dz, vd, e) += u[dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
template <FiniteElement::DerivType TestType, int DIM, int TEST_D1D, int Q1D>
|
||||
VectorFEDomainLFIntegrator::AssembleKernelType
|
||||
VectorFEDomainLFIntegrator::AssembleKernels::Kernel()
|
||||
{
|
||||
if constexpr (TestType == FiniteElement::DIV)
|
||||
{
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return HdivDLFAssemble2D<TEST_D1D, Q1D>;
|
||||
}
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
return HdivDLFAssemble3D<TEST_D1D, Q1D>;
|
||||
}
|
||||
}
|
||||
if constexpr (TestType == FiniteElement::CURL)
|
||||
{
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
return HcurlDLFAssemble3D<TEST_D1D, Q1D>;
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,317 +13,76 @@
|
||||
#include "../../fem/kernels.hpp"
|
||||
#include "../fem.hpp"
|
||||
|
||||
#include "lininteg_domain_kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void HdivDLFAssemble2D(
|
||||
const int ne, const int d, const int q, const int *markers, const real_t *bo,
|
||||
const real_t *bc, const real_t *j, const real_t *weights,
|
||||
const Vector &coeff, real_t *y)
|
||||
VectorFEDomainLFIntegrator::Kernels::Kernels()
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
|
||||
"Problem size too large.");
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 1, 1>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 2, 2>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 3, 3>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 4, 4>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 5, 5>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 6, 6>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 7, 7>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 8, 8>();
|
||||
|
||||
static constexpr int vdim = 2;
|
||||
const auto F = coeff.Read();
|
||||
const auto M = Reshape(markers, ne);
|
||||
const auto BO = Reshape(bo, q, d-1);
|
||||
const auto BC = Reshape(bc, q, d);
|
||||
const auto J = Reshape(j, q, q, vdim, vdim, ne);
|
||||
const auto W = Reshape(weights, q, q);
|
||||
const bool cst = coeff.Size() == vdim;
|
||||
const auto C = cst ? Reshape(F,vdim,1,1,1) : Reshape(F,vdim,q,q,ne);
|
||||
auto Y = Reshape(y, 2*(d-1)*d, ne);
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 1, 1>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 2, 2>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 3, 3>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 4, 4>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 5, 5>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 6, 6>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 7, 7>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 8, 8>();
|
||||
|
||||
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
if (M(e) == 0) { return; } // ignore
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 1, 1>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 2, 2>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 3, 3>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 4, 4>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 5, 5>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 6, 6>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 7, 7>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 8, 8>();
|
||||
|
||||
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
|
||||
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t sBot[Q*D];
|
||||
MFEM_SHARED real_t sBct[Q*D];
|
||||
MFEM_SHARED real_t sQQ[vdim*Q*Q];
|
||||
MFEM_SHARED real_t sQD[vdim*Q*D];
|
||||
|
||||
// Bo and Bc into shared memory
|
||||
const DeviceMatrix Bot(sBot, d-1, q);
|
||||
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
|
||||
const DeviceMatrix Bct(sBct, d, q);
|
||||
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
|
||||
|
||||
const DeviceCube QQ(sQQ, q, q, vdim);
|
||||
const DeviceCube QD(sQD, q, d, vdim);
|
||||
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const real_t cst_val_0 = C(0,0,0,0);
|
||||
const real_t cst_val_1 = C(1,0,0,0);
|
||||
MFEM_FOREACH_THREAD(y,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(x,x,q)
|
||||
{
|
||||
const real_t J0 = J(x,y,0,vd,e);
|
||||
const real_t J1 = J(x,y,1,vd,e);
|
||||
const real_t C0 = cst ? cst_val_0 : C(0,x,y,e);
|
||||
const real_t C1 = cst ? cst_val_1 : C(1,x,y,e);
|
||||
QQ(x,y,vd) = W(x,y)*(J0*C0 + J1*C1);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(qy,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t qd = 0.0;
|
||||
for (int qx = 0; qx < q; ++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) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
|
||||
DeviceTensor<4> Yxy(Y, nx, ny, vdim, ne);
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t dd = 0.0;
|
||||
for (int qy = 0; qy < q; ++qy)
|
||||
{
|
||||
dd += QD(dx,qy,vd) * Bty(dy,qy);
|
||||
}
|
||||
Yxy(dx,dy,vd,e) += dd;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 1, 2>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 2, 3>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 3, 4>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 4, 5>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 5, 6>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 6, 7>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 7, 8>();
|
||||
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 8, 9>();
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void HdivDLFAssemble3D(
|
||||
const int ne, const int d, const int q, const int *markers, const real_t *bo,
|
||||
const real_t *bc, const real_t *j, const real_t *weights,
|
||||
const Vector &coeff, real_t *y)
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
VectorFEDomainLFIntegrator::AssembleKernelType
|
||||
VectorFEDomainLFIntegrator::AssembleKernels::Fallback(
|
||||
FiniteElement::DerivType TestType, int DIM, int, int)
|
||||
{
|
||||
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
|
||||
"Problem size too large.");
|
||||
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
|
||||
"Problem size too large.");
|
||||
|
||||
static constexpr int vdim = 3;
|
||||
const auto F = coeff.Read();
|
||||
const auto M = Reshape(markers, ne);
|
||||
const auto BO = Reshape(bo, q, d-1);
|
||||
const auto BC = Reshape(bc, q, d);
|
||||
const auto J = Reshape(j, q, q, q, vdim, vdim, ne);
|
||||
const auto W = Reshape(weights, q, q, q);
|
||||
const bool cst = coeff.Size() == vdim;
|
||||
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
|
||||
auto Y = Reshape(y, 2*(d-1)*(d-1)*d, ne);
|
||||
|
||||
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
|
||||
if (TestType == FiniteElement::DIV)
|
||||
{
|
||||
if (M(e) == 0) { return; } // ignore
|
||||
|
||||
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
|
||||
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t sBot[Q*D];
|
||||
MFEM_SHARED real_t sBct[Q*D];
|
||||
|
||||
// Bo and Bc into shared memory
|
||||
const DeviceMatrix Bot(sBot, d-1, q);
|
||||
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
|
||||
const DeviceMatrix Bct(sBct, d, q);
|
||||
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
|
||||
|
||||
MFEM_SHARED real_t sm0[vdim*Q*Q*Q];
|
||||
MFEM_SHARED real_t sm1[vdim*Q*Q*Q];
|
||||
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
|
||||
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
|
||||
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
|
||||
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
if (DIM == 2)
|
||||
{
|
||||
const real_t cst_val_0 = C(0,0,0,0,0);
|
||||
const real_t cst_val_1 = C(1,0,0,0,0);
|
||||
const real_t cst_val_2 = C(2,0,0,0,0);
|
||||
MFEM_FOREACH_THREAD(y,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(x,x,q)
|
||||
{
|
||||
for (int z = 0; z < q; ++z)
|
||||
{
|
||||
const real_t J0 = J(x,y,z,0,vd,e);
|
||||
const real_t J1 = J(x,y,z,1,vd,e);
|
||||
const real_t J2 = J(x,y,z,2,vd,e);
|
||||
const real_t C0 = cst ? cst_val_0 : C(0,x,y,z,e);
|
||||
const real_t C1 = cst ? cst_val_1 : C(1,x,y,z,e);
|
||||
const real_t C2 = cst ? cst_val_2 : C(2,x,y,z,e);
|
||||
QQQ(x,y,z,vd) = W(x,y,z)*(J0*C0 + J1*C1 + J2*C2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return HdivDLFAssemble2D<0, 0>;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// Apply Bt operator
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
if (DIM == 3)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(qy,y,q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qx = 0; qx < q; ++qx)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += QQQ(qx,qy,qz,vd) * Btx(dx,qx);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { DQQ(dx,qy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
return HdivDLFAssemble3D<0, 0>;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[Q];
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qy = 0; qy < q; ++qy)
|
||||
{
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
u[qz] += DQQ(dx,qy,qz,vd) * Bty(dy,qy);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz) { DDQ(dx,dy,qz,vd) = u[qz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(vd,z,vdim)
|
||||
{
|
||||
const int nx = (vd == 0) ? d : d-1;
|
||||
const int ny = (vd == 1) ? d : d-1;
|
||||
const int nz = (vd == 2) ? d : d-1;
|
||||
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
|
||||
DeviceMatrix Btz = (vd == 2) ? Bct : Bot;
|
||||
MFEM_FOREACH_THREAD(dy,y,ny)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,nx)
|
||||
{
|
||||
real_t u[D];
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
|
||||
MFEM_UNROLL(Q)
|
||||
for (int qz = 0; qz < q; ++qz)
|
||||
{
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz)
|
||||
{
|
||||
u[dz] += DDQ(dx,dy,qz,vd) * Btz(dz,qz);
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(D)
|
||||
for (int dz = 0; dz < nz; ++dz) { Yxyz(dx,dy,dz,vd,e) += u[dz]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
static void HdivDLFAssemble(const FiniteElementSpace &fes,
|
||||
const IntegrationRule *ir,
|
||||
const Array<int> &markers,
|
||||
const Vector &coeff,
|
||||
Vector &y)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
const int dim = mesh.Dimension();
|
||||
const FiniteElement *el = fes.GetTypicalFE();
|
||||
const auto *vel = dynamic_cast<const VectorTensorFiniteElement *>(el);
|
||||
MFEM_VERIFY(vel != nullptr, "Must be VectorTensorFiniteElement");
|
||||
const MemoryType mt = Device::GetDeviceMemoryType();
|
||||
const DofToQuad &maps_o = vel->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
|
||||
const DofToQuad &maps_c = vel->GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
const int d = maps_c.ndof, q = maps_c.nqpt;
|
||||
constexpr int flags = GeometricFactors::JACOBIANS;
|
||||
const GeometricFactors *geom = mesh.GetGeometricFactors(*ir, flags, mt);
|
||||
decltype(&HdivDLFAssemble2D<>) ker =
|
||||
dim == 2 ? HdivDLFAssemble2D<> : HdivDLFAssemble3D<>;
|
||||
|
||||
if (dim==2)
|
||||
{
|
||||
if (d==1 && q==1) { ker=HdivDLFAssemble2D<1,1>; }
|
||||
if (d==2 && q==2) { ker=HdivDLFAssemble2D<2,2>; }
|
||||
if (d==3 && q==3) { ker=HdivDLFAssemble2D<3,3>; }
|
||||
if (d==4 && q==4) { ker=HdivDLFAssemble2D<4,4>; }
|
||||
if (d==5 && q==5) { ker=HdivDLFAssemble2D<5,5>; }
|
||||
if (d==6 && q==6) { ker=HdivDLFAssemble2D<6,6>; }
|
||||
if (d==7 && q==7) { ker=HdivDLFAssemble2D<7,7>; }
|
||||
if (d==8 && q==8) { ker=HdivDLFAssemble2D<8,8>; }
|
||||
}
|
||||
|
||||
if (dim==3)
|
||||
else if (TestType == FiniteElement::CURL)
|
||||
{
|
||||
if (d==2 && q==2) { ker=HdivDLFAssemble3D<2,2>; }
|
||||
if (d==3 && q==3) { ker=HdivDLFAssemble3D<3,3>; }
|
||||
if (d==4 && q==4) { ker=HdivDLFAssemble3D<4,4>; }
|
||||
if (d==5 && q==5) { ker=HdivDLFAssemble3D<5,5>; }
|
||||
if (d==6 && q==6) { ker=HdivDLFAssemble3D<6,6>; }
|
||||
if (d==7 && q==7) { ker=HdivDLFAssemble3D<7,7>; }
|
||||
if (d==8 && q==8) { ker=HdivDLFAssemble3D<8,8>; }
|
||||
if (DIM == 3)
|
||||
{
|
||||
return HcurlDLFAssemble3D<0, 0>;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(ker, "No kernel ndof " << d << " nqpt " << q);
|
||||
|
||||
const int ne = mesh.GetNE();
|
||||
const int *M = markers.Read();
|
||||
const real_t *Bo = maps_o.B.Read();
|
||||
const real_t *Bc = maps_c.B.Read();
|
||||
const real_t *J = geom->J.Read();
|
||||
const real_t *W = ir->GetWeights().Read();
|
||||
real_t *Y = y.ReadWrite();
|
||||
ker(ne, d, q, M, Bo, Bc, J, W, coeff, Y);
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
void VectorFEDomainLFIntegrator::AssembleDevice(const FiniteElementSpace &fes,
|
||||
const Array<int> &markers,
|
||||
@@ -337,15 +96,23 @@ void VectorFEDomainLFIntegrator::AssembleDevice(const FiniteElementSpace &fes,
|
||||
QuadratureSpace qs(*fes.GetMesh(), *ir);
|
||||
CoefficientVector coeff(QF, qs, CoefficientStorage::COMPRESSED);
|
||||
|
||||
const int fe_type = fe.GetDerivType();
|
||||
if (fe_type == FiniteElement::DIV)
|
||||
{
|
||||
HdivDLFAssemble(fes, ir, markers, coeff, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Not implemented.");
|
||||
}
|
||||
const FiniteElement::DerivType fe_type =
|
||||
static_cast<FiniteElement::DerivType>(fe.GetDerivType());
|
||||
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
const int dim = mesh.Dimension();
|
||||
const FiniteElement *el = fes.GetTypicalFE();
|
||||
const auto *vel = dynamic_cast<const VectorTensorFiniteElement *>(el);
|
||||
MFEM_VERIFY(vel != nullptr, "Must be VectorTensorFiniteElement");
|
||||
const MemoryType mt = Device::GetDeviceMemoryType();
|
||||
const DofToQuad &maps_o = vel->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
|
||||
const DofToQuad &maps_c = vel->GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
const int d = maps_c.ndof, q = maps_c.nqpt;
|
||||
constexpr int flags = GeometricFactors::JACOBIANS;
|
||||
const GeometricFactors *geom = mesh.GetGeometricFactors(*ir, flags, mt);
|
||||
|
||||
AssembleKernels::Run(fe_type, dim, d, q, mesh.GetNE(), markers, geom->J,
|
||||
ir->GetWeights(), maps_o.B, maps_c.B, coeff, b, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -236,6 +236,58 @@ IntegrationRule::ApplyToKnotIntervals(KnotVector const& kv) const
|
||||
return kvir;
|
||||
}
|
||||
|
||||
IntegrationRule IntegrationRule::Reorder(const Array<int> &ordering) const
|
||||
{
|
||||
const int np = GetNPoints();
|
||||
MFEM_VERIFY(np == ordering.Size(), "Invalid permutation size");
|
||||
IntegrationRule ir(np);
|
||||
ir.SetOrder(GetOrder());
|
||||
|
||||
for (int i = 0; i < np; i++)
|
||||
{
|
||||
IntegrationPoint &ip_new = ir.IntPoint(i);
|
||||
const IntegrationPoint &ip_old = IntPoint(ordering[i]);
|
||||
ip_new.Set(ip_old.x, ip_old.y, ip_old.z, ip_old.weight);
|
||||
}
|
||||
|
||||
return ir;
|
||||
}
|
||||
|
||||
IntegrationRule DuffyTrans(const IntegrationRule &ir, int dim)
|
||||
{
|
||||
IntegrationRule ir_mapped(ir.GetNPoints());
|
||||
ir_mapped.SetOrder(ir.GetOrder());
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
IntegrationPoint &ip_mapped = ir_mapped.IntPoint(i);
|
||||
ip_mapped.y = ir.IntPoint(i).y * (1 - ir.IntPoint(i).x);
|
||||
ip_mapped.x = ir.IntPoint(i).x;
|
||||
ip_mapped.weight = ir.IntPoint(i).weight;
|
||||
}
|
||||
return ir_mapped;
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
IntegrationPoint &ip_mapped = ir_mapped.IntPoint(i);
|
||||
ip_mapped.z = ir.IntPoint(i).z * (1 - ir.IntPoint(i).x) * (1 - ir.IntPoint(
|
||||
i).y);
|
||||
ip_mapped.y = ir.IntPoint(i).y * (1 - ir.IntPoint(i).x);
|
||||
ip_mapped.x = ir.IntPoint(i).x;
|
||||
ip_mapped.weight = ir.IntPoint(i).weight;
|
||||
}
|
||||
return ir_mapped;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Duffy transformation not implemented for this dimension!");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPFR
|
||||
|
||||
// Class for computing hi-precision (HP) quadrature in 1D
|
||||
@@ -433,6 +485,142 @@ public:
|
||||
#endif // MFEM_USE_MPFR
|
||||
|
||||
|
||||
void QuadratureFunctions1D::GaussJacobi(const int np, const real_t alpha,
|
||||
const real_t beta, IntegrationRule* ir)
|
||||
{
|
||||
/* The np-point Gauss-Jacobi quadrature rule is exact for polynomials of
|
||||
degree 2np - 1 with weight function w(x) = (1-x)^alpha * x^beta. The
|
||||
nodes are the zeros of the Jacobi polynomial P_{np}^{alpha,beta} and
|
||||
the weights are
|
||||
|
||||
w_i = C / [(1 - x_i^2) * P'_{np}^{alpha,beta}(x_i)^2]
|
||||
C = 2^{alpha + beta + 1} * Gamma(np + alpha + 1) * Gamma(np + beta + 1)
|
||||
/ [Gamma(np + alpha + beta + 1) * Gamma(np + 1)].
|
||||
|
||||
The nodes are computed via nonlinear solve (Newton's method) with an
|
||||
initial guess corresponding to Gatteschi's asymptotic expansions of the
|
||||
Jacobi polynomial roots [1].
|
||||
|
||||
The current initial guess has been tested and performs well for
|
||||
np <= 200 and -1 <= alpha, beta <= 4. For larger np, it may be necessary
|
||||
utilize different initial guesses in the vicinity of x = -1,+1 [2].
|
||||
|
||||
[1] Gautschi, W., & Giordano, C. (2008). Luigi Gatteschi’s work on
|
||||
asymptotics of special functions and their zeros. Numerical Algorithms,
|
||||
49, 11-31.
|
||||
[2] Hale, N., & Townsend, A. (2013). Fast and accurate computation of
|
||||
Gauss--Legendre and Gauss--Jacobi quadrature nodes and weights.
|
||||
SIAM Journal on Scientific Computing, 35(2), A652-A674.
|
||||
*/
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
ir->SetOrder(2*np - 1);
|
||||
|
||||
if (alpha <= -1.0 || beta <= -1.0)
|
||||
{
|
||||
MFEM_ABORT("Gauss-Jacobi quadrature only defined for alpha > -1 and beta > -1");
|
||||
}
|
||||
// Jacobi weight function is undefined whenever alpha <= -1 or beta <= -1
|
||||
|
||||
if (alpha > 4.0 || beta > 4.0)
|
||||
{
|
||||
MFEM_ABORT("Current Gauss-Jacobi quadrature implementation only tested for alpha <= 4 and beta <= 4");
|
||||
}
|
||||
// current asymptotic expansions for initial guess may perform poorly for large alpha, beta
|
||||
|
||||
switch (np)
|
||||
{
|
||||
case 1:
|
||||
real_t x = (beta - alpha) / (alpha + beta + 2);
|
||||
real_t w = pow(2, alpha + beta + 1) * tgamma(alpha + 2) * tgamma(
|
||||
beta + 2) / (tgamma(alpha + beta + 2));
|
||||
w = 0.5 * w / pow(2, alpha + beta);
|
||||
// map weight to to [0,1], with additional 1/(2^(alpha + beta)) factor coming from mapping
|
||||
// the weight (1-x)^alpha * (1+x)^beta to [0,1] as well.
|
||||
ir->IntPoint(0).Set1w(0.5 * x + 0.5,
|
||||
4.0 * w / ((1.0 - x*x) * (alpha + beta + 2) * (alpha + beta + 2)));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef MFEM_USE_MPFR
|
||||
|
||||
const int n = np;
|
||||
// common constants for Jacobi polynomials
|
||||
real_t ab = alpha + beta;
|
||||
real_t a2_minus_b2 = (alpha - beta) * (alpha + beta);
|
||||
|
||||
// roots of P^(alpha,beta)_n in the interval [-1,1]
|
||||
for (int i = 1; i <= n; i++)
|
||||
{
|
||||
// rather than using Chebyshev points for initial guess, use Gatteschi's asymptotic expansion for roots of Jacobi
|
||||
// polynomials
|
||||
real_t n_ab_plus_1 = 2 * n + alpha + beta + 1;
|
||||
real_t v = (2 * i + alpha - 0.5) * M_PI / n_ab_plus_1;
|
||||
real_t theta = v + 1.0 / (n_ab_plus_1*n_ab_plus_1) * ((0.25 - alpha*alpha) *
|
||||
1.0/tan(0.5*v) - (0.25 - beta*beta) * tan(0.5*v));
|
||||
real_t z = cos(theta);
|
||||
|
||||
real_t pp, p1, dz, xi = 0.;
|
||||
bool done = false;
|
||||
while (1)
|
||||
{
|
||||
real_t p2 = 1;
|
||||
p1 = ((alpha-beta) + (alpha + beta + 2) * z) / 2;
|
||||
for (int j = 1; j <= n-1; j++)
|
||||
{
|
||||
real_t p3 = p2;
|
||||
p2 = p1;
|
||||
|
||||
real_t jx2_ab = 2 * j + ab;
|
||||
real_t an = (jx2_ab) * (jx2_ab + 2);
|
||||
real_t bn = a2_minus_b2;
|
||||
real_t cn = 2 * (j + alpha) * (j + beta) * (jx2_ab + 2) / (jx2_ab + 1);
|
||||
|
||||
real_t D = (jx2_ab + 1) / (2 * (j + 1) * (j + ab + 1) * (jx2_ab));
|
||||
p1 = ((an * z + bn) * p2 - cn * p3) * D;
|
||||
}
|
||||
// p1 is Jacobi polynomial
|
||||
pp = n * (alpha - beta - (2 * n + ab) * z) * p1 + 2 * (n + alpha) *
|
||||
(n + beta) * p2;
|
||||
pp = pp / ((2 * n + ab) * (1 - z*z));
|
||||
// derivative of the Jacobi polynomial
|
||||
if (done) { break; }
|
||||
|
||||
dz = p1/pp;
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
if (std::abs(dz) < 1e-7)
|
||||
#elif defined MFEM_USE_DOUBLE
|
||||
if (std::abs(dz) < std::numeric_limits<real_t>::epsilon())
|
||||
// this seems to cause trouble if we try std::abs(dz) < 1e-16
|
||||
#else
|
||||
MFEM_ABORT("Floating point type undefined");
|
||||
// if (std::abs(dz) < 1e-16)
|
||||
#endif
|
||||
{
|
||||
done = true;
|
||||
xi = z - dz;
|
||||
}
|
||||
z -= dz;
|
||||
}
|
||||
real_t c0 = exp(lgamma(n + alpha + 1) - lgamma(n + ab + 1)) * exp(lgamma(
|
||||
n + beta + 1) - lgamma(n + 1));
|
||||
// ratio of gamma functions prone to overflow for large n, so compute logarithms
|
||||
// of Gamma function instead, i.e. Gamma(a)/Gamma(b) = exp(lgamma(a) - lgamma(b))
|
||||
ir->IntPoint(n-i).x = 0.5 * xi + 0.5;
|
||||
ir->IntPoint(n-i).weight = 0.5 * c0 * pow(2.0,
|
||||
ab + 1) / ((1.0 - xi*xi)*pp*pp) / pow(2, ab);
|
||||
// map nodes and weights to the interval [0,1]
|
||||
}
|
||||
|
||||
#else // MFEM_USE_MPFR is defined
|
||||
|
||||
MFEM_ABORT("MPFR implementation of Gauss-Jacobi quadrature not defined yet");
|
||||
|
||||
#endif // MFEM_USE_MPFR
|
||||
|
||||
}
|
||||
|
||||
|
||||
void QuadratureFunctions1D::GaussLegendre(const int np, IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
@@ -2362,6 +2550,194 @@ IntegrationRule *IntegrationRules::CubeIntegrationRule(int Order)
|
||||
return CubeIntRules[Order];
|
||||
}
|
||||
|
||||
StroudIntegrationRules StroudIntRules;
|
||||
|
||||
StroudIntegrationRules::StroudIntegrationRules()
|
||||
{
|
||||
const MemoryType h_mt = MemoryType::HOST;
|
||||
SquareStroudIntRules.SetSize(32, h_mt);
|
||||
SquareStroudIntRules = NULL;
|
||||
|
||||
TriangleStroudIntRules.SetSize(32, h_mt);
|
||||
TriangleStroudIntRules = NULL;
|
||||
|
||||
CubeStroudIntRules.SetSize(32, h_mt);
|
||||
CubeStroudIntRules = NULL;
|
||||
|
||||
TetrahedronStroudIntRules.SetSize(32, h_mt);
|
||||
TetrahedronStroudIntRules = NULL;
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
IntRuleLocks.SetSize(Geometry::NUM_GEOMETRIES, h_mt);
|
||||
for (int i = 0; i < Geometry::NUM_GEOMETRIES; i++)
|
||||
{
|
||||
omp_init_lock(&IntRuleLocks[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const IntegrationRule &StroudIntegrationRules::Get(int GeomType, int Order)
|
||||
{
|
||||
Array<IntegrationRule *> *ir_array = NULL;
|
||||
|
||||
switch (GeomType)
|
||||
{
|
||||
case Geometry::TRIANGLE: ir_array = &TriangleStroudIntRules; break;
|
||||
case Geometry::TETRAHEDRON: ir_array = &TetrahedronStroudIntRules; break;
|
||||
case Geometry::INVALID:
|
||||
case Geometry::NUM_GEOMETRIES:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
default:
|
||||
MFEM_ABORT("Stroud rules only valid for triangular and tetrahedral elements!");
|
||||
}
|
||||
|
||||
if (Order < 0)
|
||||
{
|
||||
Order = 0;
|
||||
}
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
omp_set_lock(&IntRuleLocks[GeomType]);
|
||||
#endif
|
||||
|
||||
if (!HaveIntRule(*ir_array, Order))
|
||||
{
|
||||
IntegrationRule *ir = GenerateIntegrationRule(GeomType, Order);
|
||||
#ifdef MFEM_DEBUG
|
||||
int RealOrder = Order;
|
||||
while (RealOrder+1 < ir_array->Size() && (*ir_array)[RealOrder+1] == ir)
|
||||
{
|
||||
RealOrder++;
|
||||
}
|
||||
MFEM_VERIFY(RealOrder == ir->GetOrder(), "internal error");
|
||||
#else
|
||||
MFEM_CONTRACT_VAR(ir);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
omp_unset_lock(&IntRuleLocks[GeomType]);
|
||||
#endif
|
||||
|
||||
return *(*ir_array)[Order];
|
||||
}
|
||||
|
||||
void StroudIntegrationRules::DeleteIntRuleArray(
|
||||
Array<IntegrationRule *> &ir_array) const
|
||||
{
|
||||
// Many of the intrules have multiple contiguous copies in the ir_array
|
||||
// so we have to be careful to not delete them twice.
|
||||
IntegrationRule *ir = NULL;
|
||||
for (int i = 0; i < ir_array.Size(); i++)
|
||||
{
|
||||
if (ir_array[i] != NULL && ir_array[i] != ir)
|
||||
{
|
||||
ir = ir_array[i];
|
||||
delete ir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StroudIntegrationRules::~StroudIntegrationRules()
|
||||
{
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
for (int i = 0; i < Geometry::NUM_GEOMETRIES; i++)
|
||||
{
|
||||
omp_destroy_lock(&IntRuleLocks[i]);
|
||||
}
|
||||
#endif
|
||||
DeleteIntRuleArray(SquareStroudIntRules);
|
||||
DeleteIntRuleArray(TriangleStroudIntRules);
|
||||
DeleteIntRuleArray(CubeStroudIntRules);
|
||||
DeleteIntRuleArray(TetrahedronStroudIntRules);
|
||||
}
|
||||
|
||||
|
||||
IntegrationRule *StroudIntegrationRules::GenerateIntegrationRule(int GeomType,
|
||||
int Order)
|
||||
{
|
||||
switch (GeomType)
|
||||
{
|
||||
case Geometry::TRIANGLE:
|
||||
return TriangleStroudIntegrationRule(Order);
|
||||
case Geometry::TETRAHEDRON:
|
||||
return TetrahedronStroudIntegrationRule(Order);
|
||||
case Geometry::INVALID:
|
||||
case Geometry::NUM_GEOMETRIES:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
default:
|
||||
MFEM_ABORT("Stroud rules only valid for triangular and tetrahedral elements!");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Integration rule in reference triangle according to tensor product Gauss-Jacobi rule.
|
||||
The nodes and weights are used in the original form defined on the reference
|
||||
square to evaluate the component 1D basis functions. Mapping to the reference
|
||||
triangle via IntegrationRule::DuffyTrans() occurs only in evaluation of coefficient
|
||||
vectors, see e.g. MassIntegrator::AssemblePASimplex. */
|
||||
IntegrationRule *StroudIntegrationRules::TriangleStroudIntegrationRule(
|
||||
int Order)
|
||||
{
|
||||
int RealOrder = GetSegmentRealOrder(Order);
|
||||
// Order is one of {RealOrder-1,RealOrder}
|
||||
// if (!HaveIntRule(SegmentIntRules, RealOrder))
|
||||
// {
|
||||
// SegmentIntegrationRule(RealOrder);
|
||||
// }
|
||||
IntegrationRule ir_0_0;
|
||||
// Gauss-Jacobi is exact for 2*n-1
|
||||
int n = RealOrder/2 + 1;
|
||||
QuadratureFunctions1D::GaussJacobi(n, 0.0, 0.0, &ir_0_0);
|
||||
|
||||
IntegrationRule ir_1_0;
|
||||
QuadratureFunctions1D::GaussJacobi(n, 1.0, 0.0, &ir_1_0);
|
||||
|
||||
AllocIntRule(TriangleStroudIntRules, RealOrder); // RealOrder >= Order
|
||||
// create rule in unit square
|
||||
TriangleStroudIntRules[RealOrder-1] =
|
||||
TriangleStroudIntRules[RealOrder] =
|
||||
new IntegrationRule(ir_1_0, ir_0_0);
|
||||
// map rule to reference triangle
|
||||
// TriangleStroudIntRules[RealOrder-1]->DuffyTrans(2);
|
||||
*TriangleStroudIntRules[RealOrder-1] =
|
||||
DuffyTrans(*TriangleStroudIntRules[RealOrder-1], 2);
|
||||
return TriangleStroudIntRules[Order];
|
||||
}
|
||||
|
||||
/* Integration rule in reference tetrahedron according to tensor product Gauss-Jacobi rule.
|
||||
The nodes and weights are used in the original form defined on the reference
|
||||
square to evaluate the component 1D basis functions. Mapping to the reference
|
||||
triangle via IntegrationRule::DuffyTrans() occurs only in evaluation of coefficient
|
||||
vectors, see e.g. MassIntegrator::AssemblePASimplex. */
|
||||
IntegrationRule *StroudIntegrationRules::TetrahedronStroudIntegrationRule(
|
||||
int Order)
|
||||
{
|
||||
int RealOrder = GetSegmentRealOrder(Order);
|
||||
// Order is one of {RealOrder-1,RealOrder}
|
||||
|
||||
IntegrationRule ir_0_0;
|
||||
int n = RealOrder/2 + 1;
|
||||
QuadratureFunctions1D::GaussJacobi(n, 0.0, 0.0, &ir_0_0);
|
||||
|
||||
IntegrationRule ir_1_0;
|
||||
QuadratureFunctions1D::GaussJacobi(n, 1.0, 0.0, &ir_1_0);
|
||||
|
||||
IntegrationRule ir_2_0;
|
||||
QuadratureFunctions1D::GaussJacobi(n, 2.0, 0.0, &ir_2_0);
|
||||
|
||||
AllocIntRule(TetrahedronStroudIntRules, RealOrder); // RealOrder >= Order
|
||||
// create rule in unit cube
|
||||
TetrahedronStroudIntRules[RealOrder-1] =
|
||||
TetrahedronStroudIntRules[RealOrder] =
|
||||
new IntegrationRule(ir_2_0, ir_1_0, ir_0_0);
|
||||
// map rule to reference tetrahedron
|
||||
// TetrahedronStroudIntRules[RealOrder-1]->DuffyTrans(3);
|
||||
*TetrahedronStroudIntRules[RealOrder-1] =
|
||||
DuffyTrans(*TetrahedronStroudIntRules[RealOrder-1], 3);
|
||||
return TetrahedronStroudIntRules[Order];
|
||||
}
|
||||
|
||||
IntegrationRule& NURBSMeshRules::GetElementRule(const int elem,
|
||||
const int patch, const int *ijk,
|
||||
Array<const KnotVector*> const& kv) const
|
||||
|
||||
@@ -269,6 +269,13 @@ public:
|
||||
/// applying this rule on each knot interval.
|
||||
IntegrationRule* ApplyToKnotIntervals(KnotVector const& kv) const;
|
||||
|
||||
/** @brief Returns an integration rule such that the new IntegrationPoints
|
||||
* are re-ordered based on @a ordering.
|
||||
*
|
||||
* @details In the new integration rule, ip_new[i] = ip_old[ordering[i]]
|
||||
*/
|
||||
IntegrationRule Reorder(const Array<int> &ordering) const;
|
||||
|
||||
/// Destroys an IntegrationRule object
|
||||
~IntegrationRule() { }
|
||||
};
|
||||
@@ -378,6 +385,8 @@ public:
|
||||
These methods calculate the actual points and weights for the different
|
||||
types of quadrature rules. */
|
||||
///@{
|
||||
static void GaussJacobi(const int np, const real_t alpha, const real_t beta,
|
||||
IntegrationRule* ir);
|
||||
static void GaussLegendre(const int np, IntegrationRule* ir);
|
||||
static void GaussLobatto(const int np, IntegrationRule *ir);
|
||||
static void OpenUniform(const int np, IntegrationRule *ir);
|
||||
@@ -487,12 +496,71 @@ public:
|
||||
~IntegrationRules();
|
||||
};
|
||||
|
||||
/// Container class for integration rules
|
||||
class StroudIntegrationRules
|
||||
{
|
||||
private:
|
||||
Array<IntegrationRule *> SquareStroudIntRules;
|
||||
Array<IntegrationRule *> TriangleStroudIntRules;
|
||||
Array<IntegrationRule *> CubeStroudIntRules;
|
||||
Array<IntegrationRule *> TetrahedronStroudIntRules;
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
Array<omp_lock_t> IntRuleLocks;
|
||||
#endif
|
||||
|
||||
void AllocIntRule(Array<IntegrationRule *> &ir_array, int Order) const
|
||||
{
|
||||
if (ir_array.Size() <= Order)
|
||||
{
|
||||
ir_array.SetSize(Order + 1, NULL);
|
||||
}
|
||||
}
|
||||
bool HaveIntRule(Array<IntegrationRule *> &ir_array, int Order) const
|
||||
{
|
||||
return (ir_array.Size() > Order && ir_array[Order] != NULL);
|
||||
}
|
||||
int GetSegmentRealOrder(int Order) const
|
||||
{
|
||||
return Order | 1; // valid for all quad_type's
|
||||
}
|
||||
void DeleteIntRuleArray(Array<IntegrationRule *> &ir_array) const;
|
||||
|
||||
/// The following methods allocate new IntegrationRule objects without
|
||||
/// checking if they already exist. To avoid memory leaks use
|
||||
/// IntegrationRules::Get(int GeomType, int Order) instead.
|
||||
IntegrationRule *GenerateIntegrationRule(int GeomType, int Order);
|
||||
IntegrationRule *TriangleStroudIntegrationRule(int Order);
|
||||
IntegrationRule *TetrahedronStroudIntegrationRule(int Order);
|
||||
|
||||
public:
|
||||
/// Sets initial sizes for the integration rule arrays, but rules
|
||||
/// are defined the first time they are requested with the Get method.
|
||||
explicit StroudIntegrationRules();
|
||||
|
||||
/// Returns a Stroud integration rule for given GeomType and Order.
|
||||
const IntegrationRule &Get(int GeomType, int Order);
|
||||
|
||||
/// Destroys an StroudIntegrationRules object
|
||||
~StroudIntegrationRules();
|
||||
};
|
||||
|
||||
/// A global object with all integration rules (defined in intrules.cpp)
|
||||
extern MFEM_EXPORT IntegrationRules IntRules;
|
||||
|
||||
/// A global object with all refined integration rules
|
||||
extern MFEM_EXPORT IntegrationRules RefinedIntRules;
|
||||
|
||||
/// A global object with all Stroud integration rules (defined in intrules.cpp)
|
||||
extern MFEM_EXPORT StroudIntegrationRules StroudIntRules;
|
||||
|
||||
/// Duffy Transformation of 2D and 3D tensor product rules of the form
|
||||
/// $X(t) = \sum_{i=1}^{d+1} \lambda_i(t) * x_i$, where $x_i$ are the vertices
|
||||
/// of the simplex and $\lambda_i = t_i * (1-\lambda_1-...-\lambda_{i-1})$, with
|
||||
/// $t$ being the coordinates in the unit square/cube. This function is used only
|
||||
/// in the partial assembly of Bernstein elements on simplices and does NOT
|
||||
/// modify the quadrature weights.
|
||||
IntegrationRule DuffyTrans(const IntegrationRule &ir, int dim);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -471,6 +471,13 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
VectorFEDomainLFIntegrator::VectorFEDomainLFIntegrator(
|
||||
VectorCoefficient &F, const IntegrationRule *ir)
|
||||
: DeltaLFIntegrator(F, ir), QF(F)
|
||||
{
|
||||
static Kernels kernels{};
|
||||
}
|
||||
|
||||
void VectorFEDomainLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
|
||||
+36
-2
@@ -369,8 +369,8 @@ private:
|
||||
Vector vec;
|
||||
|
||||
public:
|
||||
VectorFEDomainLFIntegrator(VectorCoefficient &F)
|
||||
: DeltaLFIntegrator(F), QF(F) { }
|
||||
VectorFEDomainLFIntegrator(VectorCoefficient &F,
|
||||
const IntegrationRule *ir = nullptr);
|
||||
|
||||
void AssembleRHSElementVect(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
@@ -387,6 +387,40 @@ public:
|
||||
Vector &b) override;
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
|
||||
/// @param ne number of elements
|
||||
/// @param markers array where entry markers[e] == 0 to skip assembly over
|
||||
/// element e element
|
||||
/// @param jac Spatial Jacobians evaluated at all quadrature points
|
||||
/// @param weights 1D quadrature weights
|
||||
/// @param testBO 1D open basis test functions
|
||||
/// @param testBC 1D closed basis test functions
|
||||
/// @param coeff coefficient values evaluated at quadrature points, possibly
|
||||
/// compressed.
|
||||
/// @param d number of 1D closed dofs
|
||||
/// @param q number of 1D quadrature points
|
||||
using AssembleKernelType = void (*)(const int NE, const Array<int> &markers,
|
||||
const Vector &jac,
|
||||
const Array<real_t> &weights,
|
||||
const Array<real_t> &testBO,
|
||||
const Array<real_t> &testBC,
|
||||
const Vector &coeff, Vector &y,
|
||||
const int testd1d, const int q1d);
|
||||
|
||||
/// parameters: test_fetype, ndims, test_d1d, q1d
|
||||
MFEM_REGISTER_KERNELS(AssembleKernels, AssembleKernelType,
|
||||
(FiniteElement::DerivType, int, int, int));
|
||||
|
||||
struct Kernels
|
||||
{
|
||||
Kernels();
|
||||
};
|
||||
|
||||
template <FiniteElement::DerivType TestType, int DIM, int TEST_D1D, int Q1D>
|
||||
static void AddSpecialization()
|
||||
{
|
||||
AssembleKernels::Specialization<TestType, DIM, TEST_D1D, Q1D>::Add();
|
||||
}
|
||||
};
|
||||
|
||||
/// $ (Q, \mathrm{curl}(v))_{\Omega} $ for Nedelec Elements
|
||||
|
||||
@@ -224,6 +224,9 @@ public:
|
||||
/** @see GetGradient(const Vector &) */
|
||||
Operator &GetGradient(const Vector &x, bool finalize) const;
|
||||
|
||||
/// Suppress a warning about hiding overloaded virtual function.
|
||||
using Operator::GetGradient;
|
||||
|
||||
/// Update the NonlinearForm to propagate updates of the associated FE space.
|
||||
/** After calling this method, the essential boundary conditions need to be
|
||||
set again. */
|
||||
|
||||
+354
-63
@@ -10,6 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "particleset.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
|
||||
@@ -225,6 +226,7 @@ void ParticleSet::AddParticles(const Array<IDType> &new_ids,
|
||||
}
|
||||
}
|
||||
// Add new ids
|
||||
ids.HostReadWrite();
|
||||
ids.Append(new_ids);
|
||||
|
||||
// Update data
|
||||
@@ -244,6 +246,102 @@ void ParticleSet::AddParticles(const Array<IDType> &new_ids,
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
// Static helper: gather selected particle-vector entries into a compact buffer.
|
||||
// nvcc does not allow extended host/device lambdas in non-public members.
|
||||
static void GatherParticleVectorDevice(const ParticleVector &pv,
|
||||
const Array<int> &send_idxs,
|
||||
Vector &send_data,
|
||||
int nsend)
|
||||
{
|
||||
const int vdim = pv.GetVDim();
|
||||
const int ordering = pv.GetOrdering();
|
||||
const int num_particles = pv.GetNumParticles();
|
||||
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
|
||||
send_data.SetSize(nsend*vdim);
|
||||
real_t *d_send_data =
|
||||
send_data.GetMemory().Write(device_mc, send_data.Size());
|
||||
const real_t *d_src = pv.GetMemory().Read(device_mc, pv.Size());
|
||||
const int *d_send_idxs = send_idxs.GetMemory().Read(device_mc, nsend);
|
||||
|
||||
mfem::forall(nsend, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int p = d_send_idxs[i];
|
||||
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
|
||||
const int stride = (ordering == Ordering::byVDIM) ? 1 : num_particles;
|
||||
|
||||
for (int c = 0; c < vdim; c++)
|
||||
{
|
||||
d_send_data[i*vdim + c] = d_src[offset + c*stride];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Static helper: gather selected tag values into a compact buffer.
|
||||
// nvcc does not allow extended host/device lambdas in non-public members.
|
||||
static void GatherParticleTagsDevice(const Array<int> &tag,
|
||||
const Array<int> &send_idxs,
|
||||
Array<int> &send_tag,
|
||||
int nsend)
|
||||
{
|
||||
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
|
||||
send_tag.SetSize(nsend);
|
||||
int *d_send_tag = send_tag.GetMemory().Write(device_mc, nsend);
|
||||
const int *d_tag = tag.GetMemory().Read(device_mc, tag.Size());
|
||||
const int *d_send_idxs = send_idxs.GetMemory().Read(device_mc, nsend);
|
||||
|
||||
mfem::forall(nsend, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
d_send_tag[i] = d_tag[d_send_idxs[i]];
|
||||
});
|
||||
}
|
||||
|
||||
// Static helper: scatter compact particle-vector entries to particle storage.
|
||||
// nvcc does not allow extended host/device lambdas in non-public members.
|
||||
static void ScatterParticleVectorDevice(ParticleVector &pv,
|
||||
const Vector &recv_data,
|
||||
const Array<int> &recv_locs,
|
||||
int nrecv)
|
||||
{
|
||||
const int vdim = pv.GetVDim();
|
||||
const int ordering = pv.GetOrdering();
|
||||
const int num_particles = pv.GetNumParticles();
|
||||
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
|
||||
const real_t *d_recv_data =
|
||||
recv_data.GetMemory().Read(device_mc, recv_data.Size());
|
||||
const int *d_recv_locs = recv_locs.GetMemory().Read(device_mc, nrecv);
|
||||
real_t *d_dst = pv.GetMemory().ReadWrite(device_mc, pv.Size());
|
||||
|
||||
mfem::forall(nrecv, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int p = d_recv_locs[i];
|
||||
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
|
||||
const int stride = (ordering == Ordering::byVDIM) ? 1 : num_particles;
|
||||
|
||||
for (int c = 0; c < vdim; c++)
|
||||
{
|
||||
d_dst[offset + c*stride] = d_recv_data[i*vdim + c];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Static helper: scatter compact tag values to particle storage.
|
||||
// nvcc does not allow extended host/device lambdas in non-public members.
|
||||
static void ScatterParticleTagsDevice(Array<int> &tag,
|
||||
const Array<int> &recv_tag,
|
||||
const Array<int> &recv_locs,
|
||||
int nrecv)
|
||||
{
|
||||
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
|
||||
const int *d_recv_tag = recv_tag.GetMemory().Read(device_mc, nrecv);
|
||||
const int *d_recv_locs = recv_locs.GetMemory().Read(device_mc, nrecv);
|
||||
int *d_tag = tag.GetMemory().ReadWrite(device_mc, tag.Size());
|
||||
|
||||
mfem::forall(nrecv, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
d_tag[d_recv_locs[i]] = d_recv_tag[i];
|
||||
});
|
||||
}
|
||||
|
||||
template<size_t NBytes>
|
||||
void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
|
||||
const Array<int> &send_idxs,
|
||||
@@ -266,37 +364,108 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
|
||||
array_init(parr_t, &gsl_arr, send_idxs.Size());
|
||||
pdata_arr = (parr_t*) gsl_arr.ptr;
|
||||
|
||||
int nparticles = pset.GetNParticles();
|
||||
int nsend = send_idxs.Size();
|
||||
gsl_arr.n = send_idxs.Size();
|
||||
|
||||
const int *h_send_idxs_initial = send_idxs.HostRead();
|
||||
const IDType *h_ids = pset.GetIDs().HostRead();
|
||||
for (int i = 0; i < send_idxs.Size(); i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
pdata.id = pset.GetIDs()[send_idxs[i]];
|
||||
pdata.id = h_ids[h_send_idxs_initial[i]];
|
||||
}
|
||||
|
||||
// Copy particle data directly into pdata
|
||||
size_t counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
// Pack coords and fields into the GSLIB send buffer. Device-resident data
|
||||
// is first gathered into a compact device buffer so that only selected
|
||||
// particles are copied back to host. Host-resident data is packed directly.
|
||||
int max_vdim = pset.Coords().GetVDim();
|
||||
for (int f = 0; f < pset.GetNFields(); f++)
|
||||
{
|
||||
int f_vdim = pset.Field(f).GetVDim();
|
||||
if (f_vdim > max_vdim) { max_vdim = f_vdim; }
|
||||
}
|
||||
Vector send_data;
|
||||
Array<int> send_tag;
|
||||
if (Device::IsEnabled())
|
||||
{
|
||||
send_data.SetSize(nsend * max_vdim); // allocate max size over all fields
|
||||
send_tag.SetSize(nsend);
|
||||
}
|
||||
|
||||
size_t counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
{
|
||||
const ParticleVector &pv = f == -1 ? pset.Coords() : pset.Field(f);
|
||||
const int vdim = pv.GetVDim();
|
||||
const int ordering = pv.GetOrdering();
|
||||
const int num_particles = pv.GetNumParticles();
|
||||
const bool use_dev = Device::IsEnabled() && pv.UseDevice();
|
||||
|
||||
if (use_dev)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
GatherParticleVectorDevice(pv, send_idxs, send_data, nsend);
|
||||
|
||||
const real_t *h_send_data = send_data.HostRead();
|
||||
for (int i = 0; i < nsend; i++)
|
||||
{
|
||||
std::memcpy(pdata.data.data() + counter, &pv(send_idxs[i], c),
|
||||
sizeof(real_t));
|
||||
counter += sizeof(real_t);
|
||||
std::memcpy(pdata_arr[i].data.data() + counter,
|
||||
h_send_data + i*vdim, vdim * sizeof(real_t));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const real_t *h_src = pv.HostRead();
|
||||
const int *h_send_idxs = send_idxs.HostRead();
|
||||
for (int i = 0; i < nsend; i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
const int p = h_send_idxs[i];
|
||||
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
|
||||
const int stride = (ordering == Ordering::byVDIM) ? 1 :
|
||||
num_particles;
|
||||
|
||||
for (int c = 0; c < vdim; c++)
|
||||
{
|
||||
std::memcpy(pdata.data.data() + counter + c*sizeof(real_t),
|
||||
h_src + offset + c*stride, sizeof(real_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy tags
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
Array<int> &tag_arr = pset.Tag(t);
|
||||
std::memcpy(pdata.data.data() + counter, &tag_arr[send_idxs[i]],
|
||||
sizeof(int));
|
||||
counter += sizeof(int);
|
||||
}
|
||||
counter += vdim*sizeof(real_t);
|
||||
}
|
||||
|
||||
int nparticles = pset.GetNParticles();
|
||||
int nsend = send_idxs.Size();
|
||||
// Pack tags after all real_t data. Each tag uses the same selective
|
||||
// device gather path when its Array is device-resident.
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
const Array<int> &tag = pset.Tag(t);
|
||||
const size_t tag_counter = counter + t*sizeof(int);
|
||||
const bool use_dev = Device::IsEnabled() && tag.UseDevice();
|
||||
|
||||
if (use_dev)
|
||||
{
|
||||
GatherParticleTagsDevice(tag, send_idxs, send_tag, nsend);
|
||||
|
||||
const int *h_send_tag = send_tag.HostRead();
|
||||
for (int i = 0; i < nsend; i++)
|
||||
{
|
||||
std::memcpy(pdata_arr[i].data.data() + tag_counter,
|
||||
h_send_tag + i, sizeof(int));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int *h_tag = tag.HostRead();
|
||||
const int *h_send_idxs = send_idxs.HostRead();
|
||||
for (int i = 0; i < nsend; i++)
|
||||
{
|
||||
std::memcpy(pdata_arr[i].data.data() + tag_counter,
|
||||
h_tag + h_send_idxs[i], sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer particles
|
||||
sarray_transfer_ext(parr_t, &gsl_arr, send_ranks.GetData(),
|
||||
@@ -304,11 +473,20 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
|
||||
|
||||
// Make sure we have enough space for received particles
|
||||
int nrecv = (int) gsl_arr.n;
|
||||
|
||||
Vector recv_data;
|
||||
Array<int> recv_tag;
|
||||
if (Device::IsEnabled())
|
||||
{
|
||||
recv_data.SetSize(nrecv * max_vdim);
|
||||
recv_tag.SetSize(nrecv);
|
||||
}
|
||||
|
||||
int ndelete = nsend - nrecv;
|
||||
if (ndelete > 0)
|
||||
{
|
||||
// Remove unneeded particles
|
||||
auto datap = const_cast<int*>(send_idxs.GetData());
|
||||
auto datap = const_cast<int*>(send_idxs.HostRead());
|
||||
Array<int> delete_idxs(datap + nrecv, ndelete);
|
||||
pset.RemoveParticles(delete_idxs);
|
||||
}
|
||||
@@ -319,47 +497,133 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
|
||||
|
||||
pdata_arr = (parr_t*) gsl_arr.ptr;
|
||||
|
||||
// Add newly-recvd data directly to active state
|
||||
// Make a list of new IDs to add
|
||||
int num_new = nrecv > nsend ? nrecv - nsend : 0;
|
||||
Array<IDType> new_ids(num_new);
|
||||
for (int i = 0; i < num_new; i++)
|
||||
{
|
||||
new_ids[i] = pdata_arr[nsend + i].id;
|
||||
}
|
||||
|
||||
// Add particles in batch
|
||||
Array<int> new_indices;
|
||||
if (num_new > 0)
|
||||
{
|
||||
pset.AddParticles(new_ids, &new_indices);
|
||||
}
|
||||
|
||||
// Map each received packet to the local particle slot it updates.
|
||||
Array<int> recv_locs(nrecv);
|
||||
int *h_recv_locs = recv_locs.HostWrite();
|
||||
const int *h_send_idxs_recv = send_idxs.HostRead();
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
IDType id = pdata.id;
|
||||
|
||||
int new_loc_idx;
|
||||
if (i < nsend) // update existing particle
|
||||
{
|
||||
new_loc_idx = send_idxs[i];
|
||||
pset.UpdateID(new_loc_idx, id);
|
||||
h_recv_locs[i] = h_send_idxs_recv[i];
|
||||
pset.UpdateID(h_recv_locs[i], pdata.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new particle
|
||||
Array<int> idx_temp;
|
||||
pset.AddParticles(Array<IDType>({id}), &idx_temp);
|
||||
new_loc_idx = idx_temp[0]; // Get index of newly-added particle
|
||||
h_recv_locs[i] = new_indices[i - nsend];
|
||||
}
|
||||
}
|
||||
|
||||
size_t counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
// Unpack coords and fields from GSLIB host packets. Device-resident
|
||||
// destinations use a compact host buffer followed by a device scatter.
|
||||
size_t recv_counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
const int vdim = pv.GetVDim();
|
||||
const int ordering = pv.GetOrdering();
|
||||
const int num_particles = pv.GetNumParticles();
|
||||
const bool use_dev = Device::IsEnabled() && pv.UseDevice();
|
||||
|
||||
if (use_dev)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
recv_data.SetSize(nrecv*vdim);
|
||||
real_t *h_recv_data = recv_data.HostWrite();
|
||||
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
real_t& val = pv(new_loc_idx, c);
|
||||
std::memcpy(&val, pdata.data.data() + counter, sizeof(real_t));
|
||||
counter += sizeof(real_t);
|
||||
std::memcpy(h_recv_data + i*vdim,
|
||||
pdata_arr[i].data.data() + recv_counter,
|
||||
vdim*sizeof(real_t));
|
||||
}
|
||||
|
||||
ScatterParticleVectorDevice(pv, recv_data, recv_locs, nrecv);
|
||||
}
|
||||
else
|
||||
{
|
||||
real_t *h_dst = pv.HostReadWrite();
|
||||
const int *h_recv_locs_read = recv_locs.HostRead();
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
const int p = h_recv_locs_read[i];
|
||||
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
|
||||
const int stride = (ordering == Ordering::byVDIM) ? 1 :
|
||||
num_particles;
|
||||
|
||||
for (int c = 0; c < vdim; c++)
|
||||
{
|
||||
std::memcpy(h_dst + offset + c*stride,
|
||||
pdata.data.data() + recv_counter + c*sizeof(real_t),
|
||||
sizeof(real_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
recv_counter += vdim*sizeof(real_t);
|
||||
}
|
||||
|
||||
// Unpack tags after all real_t data, using the same compact scatter path
|
||||
// for device-resident tag arrays.
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
Array<int> &tag = pset.Tag(t);
|
||||
const size_t tag_counter = recv_counter + t*sizeof(int);
|
||||
const bool use_dev = Device::IsEnabled() && tag.UseDevice();
|
||||
|
||||
if (use_dev)
|
||||
{
|
||||
Array<int> &tag_arr = pset.Tag(t);
|
||||
std::memcpy(&tag_arr[new_loc_idx],
|
||||
pdata.data.data() + counter, sizeof(int));
|
||||
counter += sizeof(int);
|
||||
recv_tag.SetSize(nrecv);
|
||||
int *h_recv_tag = recv_tag.HostWrite();
|
||||
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
std::memcpy(h_recv_tag + i,
|
||||
pdata_arr[i].data.data() + tag_counter, sizeof(int));
|
||||
}
|
||||
|
||||
ScatterParticleTagsDevice(tag, recv_tag, recv_locs, nrecv);
|
||||
}
|
||||
else
|
||||
{
|
||||
int *h_tag = tag.HostReadWrite();
|
||||
const int *h_recv_locs_read = recv_locs.HostRead();
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
std::memcpy(h_tag + h_recv_locs_read[i],
|
||||
pdata_arr[i].data.data() + tag_counter, sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
||||
array_free(&gsl_arr);
|
||||
|
||||
// Restore Device validity if needed
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
pv.ReadWrite(pv.UseDevice());
|
||||
}
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
Array<int> &tag_arr = pset.Tag(t);
|
||||
if (tag_arr.UseDevice()) { tag_arr.ReadWrite(true); }
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t NBytes>
|
||||
@@ -526,11 +790,14 @@ ParticleSet::ParticleSet(int id_stride_, IDType id_counter_, int num_particles,
|
||||
int dim, Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
const Array<const char*> &tag_names_,
|
||||
bool use_device)
|
||||
: id_stride(id_stride_),
|
||||
id_counter(id_counter_),
|
||||
coords(dim, coords_ordering)
|
||||
{
|
||||
if (use_device) { coords.UseDevice(true); }
|
||||
|
||||
// Initialize fields
|
||||
for (int f = 0; f < field_vdims.Size(); f++)
|
||||
{
|
||||
@@ -580,21 +847,22 @@ bool ParticleSet::IsValidParticle(const Particle &p) const
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
Ordering::Type coords_ordering)
|
||||
Ordering::Type coords_ordering,
|
||||
bool use_device)
|
||||
: ParticleSet(1, 0, num_particles, dim, coords_ordering, Array<int>(),
|
||||
Array<Ordering::Type>(), Array<const char*>(), 0,
|
||||
Array<const char*>())
|
||||
Array<const char*>(), use_device)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering)
|
||||
Ordering::Type all_ordering, bool use_device)
|
||||
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
GetEmptyNameArray(field_vdims.Size()), num_tags,
|
||||
GetEmptyNameArray(num_tags))
|
||||
GetEmptyNameArray(num_tags), use_device)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -602,11 +870,11 @@ ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
const Array<int> &field_vdims, const Array<const
|
||||
char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering)
|
||||
Ordering::Type all_ordering, bool use_device)
|
||||
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
field_names_, num_tags,
|
||||
tag_names_)
|
||||
tag_names_, use_device)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -616,9 +884,9 @@ ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
const Array<const char*> &tag_names_, bool use_device)
|
||||
: ParticleSet(1, 0, num_particles, dim, coords_ordering, field_vdims,
|
||||
field_orderings, field_names_, num_tags, tag_names_)
|
||||
field_orderings, field_names_, num_tags, tag_names_, use_device)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -627,21 +895,21 @@ ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering)
|
||||
Ordering::Type coords_ordering, bool use_device)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, coords_ordering, Array<int>(),
|
||||
Array<Ordering::Type>(), Array<const char*>(), 0,
|
||||
Array<const char*>())
|
||||
Array<const char*>(), use_device)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering)
|
||||
Ordering::Type all_ordering, bool use_device)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
GetEmptyNameArray(field_vdims.Size()), num_tags,
|
||||
GetEmptyNameArray(num_tags))
|
||||
GetEmptyNameArray(num_tags), use_device)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -650,11 +918,11 @@ ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, const Array<const
|
||||
char*> &field_names_,
|
||||
int num_tags, const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering)
|
||||
Ordering::Type all_ordering, bool use_device)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
field_names_, num_tags,
|
||||
tag_names_)
|
||||
tag_names_, use_device)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -664,7 +932,7 @@ ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
const Array<const char*> &tag_names_, bool use_device)
|
||||
: ParticleSet(GetSize(comm_), (IDType)GetRank(comm_),
|
||||
rank_num_particles,
|
||||
dim,
|
||||
@@ -673,7 +941,7 @@ ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
field_orderings,
|
||||
field_names_,
|
||||
num_tags,
|
||||
tag_names_)
|
||||
tag_names_, use_device)
|
||||
{
|
||||
comm = comm_;
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
@@ -705,6 +973,7 @@ int ParticleSet::AddField(int vdim, Ordering::Type field_ordering,
|
||||
}
|
||||
fields.emplace_back(std::make_unique<ParticleVector>(vdim, field_ordering,
|
||||
GetNParticles()));
|
||||
if (coords.UseDevice()) { fields.back()->UseDevice(true); }
|
||||
field_names.emplace_back(field_name_str);
|
||||
|
||||
return GetNFields() - 1;
|
||||
@@ -718,6 +987,7 @@ int ParticleSet::AddTag(const char* tag_name)
|
||||
tag_name_str = GetDefaultTagName(tag_names.size());
|
||||
}
|
||||
tags.emplace_back(std::make_unique<Array<int>>(GetNParticles()));
|
||||
if (coords.UseDevice()) { tags.back()->GetMemory().UseDevice(true); }
|
||||
tag_names.emplace_back(tag_name_str);
|
||||
|
||||
return GetNTags() - 1;
|
||||
@@ -782,7 +1052,7 @@ Particle ParticleSet::GetParticle(int i) const
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
p.Tag(t) = Tag(t)[i];
|
||||
p.Tag(t) = Tag(t).HostRead()[i];
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -790,13 +1060,21 @@ Particle ParticleSet::GetParticle(int i) const
|
||||
|
||||
bool ParticleSet::IsParticleRefValid() const
|
||||
{
|
||||
if (coords.GetOrdering() == Ordering::byNODES)
|
||||
if (coords.GetOrdering() == Ordering::byNODES || coords.UseDevice())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
if (fields[f]->GetOrdering() == Ordering::byNODES)
|
||||
if (fields[f]->GetOrdering() == Ordering::byNODES ||
|
||||
fields[f]->UseDevice())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
if (tags[t]->UseDevice())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -806,6 +1084,10 @@ bool ParticleSet::IsParticleRefValid() const
|
||||
|
||||
Particle ParticleSet::GetParticleRef(int i)
|
||||
{
|
||||
MFEM_ASSERT(IsParticleRefValid(),
|
||||
"GetParticleRef is only valid when coordinates and fields are "
|
||||
"ordered byVDIM and particle data is host-resident.");
|
||||
|
||||
Particle p = CreateParticle();
|
||||
|
||||
Coords().GetValuesRef(i, p.Coords());
|
||||
@@ -839,7 +1121,7 @@ void ParticleSet::SetParticle(int i, const Particle &p)
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
Tag(t)[i] = p.Tag(t);
|
||||
Tag(t).HostReadWrite()[i] = p.Tag(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,6 +1182,15 @@ void ParticleSet::PrintCSV(const char *fname, const Array<int> &field_idxs,
|
||||
#ifdef MFEM_USE_MPI
|
||||
int rank = GetRank(comm);
|
||||
#endif // MFEM_USE_MPI
|
||||
// make sure we can read tag data on host. fields and coords will be read as
|
||||
// needed in the loop below, so we don't need to pre-read them here.
|
||||
for (int i = 0; i < GetNTags(); i++)
|
||||
{
|
||||
tags[i]->HostRead();
|
||||
}
|
||||
ids.HostRead();
|
||||
|
||||
// Write particle data
|
||||
for (int i = 0; i < GetNParticles(); i++)
|
||||
{
|
||||
ss_data << ids[i];
|
||||
|
||||
+49
-12
@@ -211,6 +211,12 @@ public:
|
||||
* byVDIM). The unique_ptrs to all the ParticleVectors are stored in the
|
||||
* std::vector \ref fields.
|
||||
*
|
||||
* @par Device Behavior:
|
||||
* When a ParticleSet is constructed with \p use_device=true, \ref coords and
|
||||
* all ParticleVector fields are marked to use device memory. Fields added
|
||||
* later through \ref AddField inherit the current device mode (through
|
||||
* \ref coords).
|
||||
*
|
||||
* @par Tags:
|
||||
* Tags represent integers associated with each particle. For a given tag,
|
||||
* all particle data are stored in a single Array<int>. The unique_ptrs to all
|
||||
@@ -369,7 +375,10 @@ protected:
|
||||
* ID of a particle.
|
||||
*/
|
||||
void UpdateID(int local_idx, IDType new_global_id)
|
||||
{ ids[local_idx] = new_global_id; }
|
||||
{
|
||||
ids.HostReadWrite();
|
||||
ids[local_idx] = new_global_id;
|
||||
}
|
||||
|
||||
/** @brief Create a Particle object with the same spatial dimension,
|
||||
* number of fields and field vdims, and number of tags as this ParticleSet.
|
||||
@@ -399,12 +408,14 @@ protected:
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] use_device Use device memory for particle fields.
|
||||
*/
|
||||
ParticleSet(int id_stride_, IDType id_counter_, int num_particles, int dim,
|
||||
Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
const Array<const char*> &tag_names_,
|
||||
bool use_device);
|
||||
|
||||
public:
|
||||
|
||||
@@ -413,9 +424,12 @@ public:
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering Ordering of coordinates.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim,
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM);
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Construct a serial ParticleSet with specified fields and tags at
|
||||
* construction.
|
||||
@@ -426,9 +440,12 @@ public:
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
|
||||
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Construct a serial ParticleSet with specified fields and tags at
|
||||
* construction, with names.
|
||||
@@ -441,11 +458,14 @@ public:
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
Ordering::Type all_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Comprehensive serial constructor of ParticleSet.
|
||||
*
|
||||
@@ -457,12 +477,15 @@ public:
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, Ordering::Type coords_ordering,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
const Array<const char*> &tag_names_,
|
||||
bool use_device=false);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/** @brief Construct a parallel ParticleSet.
|
||||
@@ -471,9 +494,12 @@ public:
|
||||
* @param[in] rank_num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering (Optional) Ordering of coordinates.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM);
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Construct a parallel ParticleSet with specified fields and tags
|
||||
* at construction.
|
||||
@@ -485,10 +511,13 @@ public:
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
Ordering::Type all_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Construct a parallel ParticleSet with specified fields and tags
|
||||
* at construction, with names (for PrintCSV()).
|
||||
@@ -502,12 +531,15 @@ public:
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<const char*> &field_names_,
|
||||
int num_tags, const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
Ordering::Type all_ordering=Ordering::byVDIM,
|
||||
bool use_device=false);
|
||||
|
||||
/** @brief Comprehensive parallel constructor of ParticleSet.
|
||||
*
|
||||
@@ -520,12 +552,15 @@ public:
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] use_device (Optional) Use device memory for particle
|
||||
* fields.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
const Array<const char*> &tag_names_,
|
||||
bool use_device=false);
|
||||
|
||||
/// Get the MPI communicator for this ParticleSet.
|
||||
MPI_Comm GetComm() const { return comm; };
|
||||
@@ -545,6 +580,8 @@ public:
|
||||
* @param[in] field_ordering (Optional) Ordering::Type of the field.
|
||||
* @param[in] field_name (Optional) Name of the field.
|
||||
*
|
||||
* @note New fields inherit the current device mode of \ref coords.
|
||||
*
|
||||
* @return Index of the newly-added field.
|
||||
*/
|
||||
int AddField(int vdim, Ordering::Type field_ordering=Ordering::byVDIM,
|
||||
@@ -637,8 +674,8 @@ public:
|
||||
|
||||
/** @brief Determine if GetParticleRef is valid.
|
||||
*
|
||||
* If coordinates and all fields are ordered byVDIM, then returns true.
|
||||
* Otherwise, false.
|
||||
* Returns true when coordinates and all fields are ordered byVDIM and
|
||||
* particle data is host-resident. Otherwise, false.
|
||||
*/
|
||||
bool IsParticleRefValid() const;
|
||||
|
||||
|
||||
+9
-2
@@ -50,14 +50,21 @@ ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
|
||||
const FiniteElement *fe = fes.GetFE(e);
|
||||
auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
|
||||
auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
|
||||
if (el_t || el_n) { continue; }
|
||||
auto el_p = dynamic_cast<const H1Pos_TriangleElement*>(fe) ||
|
||||
dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
|
||||
if (el_t || el_n || el_p) { continue; }
|
||||
MFEM_ABORT("Finite element not suitable for lexicographic ordering");
|
||||
}
|
||||
const FiniteElement *fe = fes.GetTypicalFE();
|
||||
auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
|
||||
auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
|
||||
auto el_p_tri = dynamic_cast<const H1Pos_TriangleElement*>(fe);
|
||||
auto el_p_tet = dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
|
||||
const Array<int> &fe_dof_map =
|
||||
(el_t) ? el_t->GetDofMap() : el_n->GetLexicographicOrdering();
|
||||
el_n ? el_n->GetLexicographicOrdering() :
|
||||
el_t ? el_t->GetDofMap() :
|
||||
el_p_tri ? el_p_tri->GetDofMap() :
|
||||
el_p_tet->GetDofMap();
|
||||
MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
|
||||
dof_map = fe_dof_map.HostRead();
|
||||
}
|
||||
|
||||
+303
-113
@@ -3758,7 +3758,8 @@ void TMOP_Integrator::SetInitialMeshPos(const GridFunction *x0)
|
||||
TMOP_Integrator::~TMOP_Integrator()
|
||||
{
|
||||
delete lim_func;
|
||||
delete adapt_lim_gf;
|
||||
for (int i = 0; i < adapt_lim_gf.Size(); i++) { delete adapt_lim_gf[i]; }
|
||||
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { delete adapt_lim_gf0[i]; }
|
||||
delete surf_fit_gf;
|
||||
delete surf_fit_limiter;
|
||||
delete surf_fit_grad;
|
||||
@@ -3800,20 +3801,13 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const GridFunction &z0,
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(delta_max > 0.0,
|
||||
"EnableAdaptiveLimiting requires delta_max > 0.0.");
|
||||
|
||||
adapt_lim_gf0 = &z0;
|
||||
delete adapt_lim_gf;
|
||||
adapt_lim_gf = new GridFunction(z0);
|
||||
adapt_lim_coeff = &coeff;
|
||||
adapt_lim_eval = &ae;
|
||||
adapt_lim_delta_max = delta_max;
|
||||
|
||||
adapt_lim_eval->SetSerialMetaInfo(*z0.FESpace()->GetMesh(),
|
||||
*z0.FESpace());
|
||||
adapt_lim_eval->SetInitialField
|
||||
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
|
||||
Array<const GridFunction *> z0_arr(1);
|
||||
Array<Coefficient *> c_arr(1);
|
||||
Array<real_t> d_arr(1);
|
||||
z0_arr[0] = &z0;
|
||||
c_arr[0] = &coeff;
|
||||
d_arr[0] = delta_max;
|
||||
EnableAdaptiveLimiting(z0_arr, c_arr, ae, d_arr);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
@@ -3822,21 +3816,111 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(delta_max > 0.0,
|
||||
"EnableAdaptiveLimiting requires delta_max > 0.0.");
|
||||
Array<const ParGridFunction *> z0_arr(1);
|
||||
Array<Coefficient *> c_arr(1);
|
||||
Array<real_t> d_arr(1);
|
||||
z0_arr[0] = &z0;
|
||||
c_arr[0] = &coeff;
|
||||
d_arr[0] = delta_max;
|
||||
EnableAdaptiveLimiting(z0_arr, c_arr, ae, d_arr);
|
||||
}
|
||||
#endif
|
||||
|
||||
adapt_lim_gf0 = &z0;
|
||||
adapt_lim_pgf0 = &z0;
|
||||
delete adapt_lim_gf;
|
||||
adapt_lim_gf = new GridFunction(z0);
|
||||
adapt_lim_coeff = &coeff;
|
||||
void TMOP_Integrator::
|
||||
EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
|
||||
{
|
||||
MFEM_VERIFY(z0.Size() > 0, "Requires at least one field.");
|
||||
MFEM_VERIFY(z0.Size() == coeff.Size(), "Requires one Coefficient per field.");
|
||||
MFEM_VERIFY(z0.Size() == delta_max.Size(), "Requires one delta_max per field.");
|
||||
for (int i = 0; i < delta_max.Size(); i++)
|
||||
{
|
||||
MFEM_VERIFY(delta_max[i] > 0.0, "Requires delta_max > 0.0.");
|
||||
}
|
||||
|
||||
// Verify compatibility of input fields.
|
||||
const FiniteElementSpace *sfes = z0[0]->FESpace();
|
||||
MFEM_VERIFY(sfes->GetVDim() == 1, "Expects scalar input GridFunctions.");
|
||||
const int ndofs = sfes->GetVSize();
|
||||
Mesh *mesh = sfes->GetMesh();
|
||||
MFEM_VERIFY(mesh->GetNodes(), "EnableAdaptiveLimiting requires mesh Nodes.");
|
||||
for (int i = 0; i < z0.Size(); i++)
|
||||
{
|
||||
MFEM_VERIFY(z0[i], "NULL GridFunction pointer.");
|
||||
const FiniteElementSpace *fes_i = z0[i]->FESpace();
|
||||
MFEM_VERIFY(fes_i->GetVDim() == 1, "Expects scalar input GridFunctions.");
|
||||
MFEM_VERIFY(fes_i->GetVSize() == ndofs,
|
||||
"All fields must be on the same FE space.");
|
||||
MFEM_VERIFY(fes_i->GetMesh() == mesh,
|
||||
"All fields must be on the same Mesh.");
|
||||
MFEM_VERIFY(coeff[i], "NULL Coefficient pointer.");
|
||||
}
|
||||
|
||||
// Delete previous adaptive limiting data.
|
||||
for (int i = 0; i < adapt_lim_gf.Size(); i++) { delete adapt_lim_gf[i]; }
|
||||
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { delete adapt_lim_gf0[i]; }
|
||||
|
||||
adapt_lim_coeff.SetSize(coeff.Size());
|
||||
for (int i = 0; i < coeff.Size(); i++) { adapt_lim_coeff[i] = coeff[i]; }
|
||||
adapt_lim_eval = &ae;
|
||||
adapt_lim_delta_max = delta_max;
|
||||
adapt_lim_init_nodes = *mesh->GetNodes();
|
||||
|
||||
adapt_lim_eval->SetParMetaInfo(*z0.ParFESpace()->GetParMesh(),
|
||||
*z0.ParFESpace());
|
||||
adapt_lim_eval->SetInitialField
|
||||
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
|
||||
// Use one internal vector field (vdim = #fields) so remapping can be done in
|
||||
// one call and incremental remap state (when provided by the evaluator) is
|
||||
// preserved across TMOP iterations.
|
||||
//
|
||||
// Use Ordering::byNODES for the packed vector field so packing / unpacking
|
||||
// can be done with contiguous sub-vector copies (device-friendly).
|
||||
const int nal = z0.Size();
|
||||
const Ordering::Type packed_ord = Ordering::byNODES;
|
||||
|
||||
// Setup the evaluator.
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (auto pfes = dynamic_cast<const ParFiniteElementSpace *>(sfes))
|
||||
{
|
||||
auto *pm = pfes->GetParMesh();
|
||||
MFEM_VERIFY(pm, "Invalid ParMesh.");
|
||||
ParFiniteElementSpace vfes(pm, pfes->FEColl(), nal, packed_ord);
|
||||
adapt_lim_eval->SetParMetaInfo(*pm, vfes);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
FiniteElementSpace vfes(mesh, sfes->FEColl(), nal, packed_ord);
|
||||
adapt_lim_eval->SetSerialMetaInfo(*mesh, vfes);
|
||||
}
|
||||
|
||||
// Copy the initial fields; remapped fields are initialized to the same data.
|
||||
adapt_lim_gf0.SetSize(z0.Size());
|
||||
adapt_lim_gf.SetSize(z0.Size());
|
||||
for (int i = 0; i < z0.Size(); i++)
|
||||
{
|
||||
adapt_lim_gf0[i] = new GridFunction(*z0[i]);
|
||||
adapt_lim_gf[i] = new GridFunction(*z0[i]);
|
||||
}
|
||||
|
||||
// Initialize the evaluator with the packed vector field.
|
||||
Vector init_field_vec;
|
||||
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
|
||||
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
|
||||
}
|
||||
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::
|
||||
EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
|
||||
{
|
||||
Array<const GridFunction *> z0_base(z0.Size());
|
||||
for (int i = 0; i < z0.Size(); i++) { z0_base[i] = z0[i]; }
|
||||
EnableAdaptiveLimiting(z0_base, coeff, ae, delta_max);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4157,26 +4241,61 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
|
||||
|
||||
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
|
||||
{
|
||||
if (adapt_lim_gf)
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{
|
||||
adapt_lim_gf->Update();
|
||||
adapt_lim_eval->SetSerialMetaInfo(*adapt_lim_gf->FESpace()->GetMesh(),
|
||||
*adapt_lim_gf->FESpace());
|
||||
adapt_lim_eval->SetInitialField
|
||||
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
|
||||
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { adapt_lim_gf0[i]->Update(); }
|
||||
for (int i = 0; i < adapt_lim_gf.Size(); i++) { adapt_lim_gf[i]->Update(); }
|
||||
|
||||
Mesh *mesh = adapt_lim_gf[0]->FESpace()->GetMesh();
|
||||
|
||||
// Same setup as in EnableAdaptiveLimiting().
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
const Ordering::Type packed_ord = Ordering::byNODES;
|
||||
FiniteElementSpace vfes(mesh, adapt_lim_gf[0]->FESpace()->FEColl(), nal,
|
||||
packed_ord);
|
||||
adapt_lim_eval->SetSerialMetaInfo(*mesh, vfes);
|
||||
|
||||
adapt_lim_init_nodes = *mesh->GetNodes();
|
||||
const int ndofs = adapt_lim_gf0[0]->Size();
|
||||
Vector init_field_vec;
|
||||
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
|
||||
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
|
||||
}
|
||||
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
|
||||
{
|
||||
if (adapt_lim_gf)
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{
|
||||
adapt_lim_gf->Update();
|
||||
adapt_lim_eval->SetParMetaInfo(*adapt_lim_pgf0->ParFESpace()->GetParMesh(),
|
||||
*adapt_lim_pgf0->ParFESpace());
|
||||
adapt_lim_eval->SetInitialField
|
||||
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
|
||||
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { adapt_lim_gf0[i]->Update(); }
|
||||
for (int i = 0; i < adapt_lim_gf.Size(); i++) { adapt_lim_gf[i]->Update(); }
|
||||
|
||||
// Same setup as in EnableAdaptiveLimiting().
|
||||
auto *pfes = dynamic_cast<ParFiniteElementSpace *>(adapt_lim_gf[0]->FESpace());
|
||||
MFEM_VERIFY(pfes, "internal error");
|
||||
ParMesh *pmesh = pfes->GetParMesh();
|
||||
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
const Ordering::Type packed_ord = Ordering::byNODES;
|
||||
ParFiniteElementSpace vfes(pmesh, pfes->FEColl(), nal, packed_ord);
|
||||
adapt_lim_eval->SetParMetaInfo(*pmesh, vfes);
|
||||
|
||||
adapt_lim_init_nodes = *pmesh->GetNodes();
|
||||
const int ndofs = adapt_lim_gf0[0]->Size();
|
||||
Vector init_field_vec;
|
||||
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
|
||||
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
|
||||
}
|
||||
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4208,7 +4327,8 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// No adaptive limiting / surface fitting terms if the function is called
|
||||
// as part of a FD derivative computation (because we include the exact
|
||||
// derivatives of these terms in FD computations).
|
||||
const bool adaptive_limiting = (adapt_lim_gf && fd_call_flag == false);
|
||||
const bool adaptive_limiting = (adapt_lim_gf.Size() > 0 &&
|
||||
fd_call_flag == false);
|
||||
const bool surface_fit = (surf_fit_marker && fd_call_flag == false);
|
||||
|
||||
DSh.SetSize(dof, dim);
|
||||
@@ -4271,11 +4391,21 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
|
||||
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
|
||||
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
const int nqp = ir.GetNPoints();
|
||||
Vector adapt_lim_gf_q, adapt_lim_gf0_q;
|
||||
if (adaptive_limiting)
|
||||
{
|
||||
adapt_lim_gf->GetValues(el_id, ir, adapt_lim_gf_q);
|
||||
adapt_lim_gf0->GetValues(el_id, ir, adapt_lim_gf0_q);
|
||||
adapt_lim_gf_q.SetSize(nal * nqp);
|
||||
adapt_lim_gf0_q.SetSize(nal * nqp);
|
||||
Vector zc, z0c;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
zc.MakeRef(adapt_lim_gf_q, c * nqp, nqp);
|
||||
z0c.MakeRef(adapt_lim_gf0_q, c * nqp, nqp);
|
||||
adapt_lim_gf[c]->GetValues(el_id, ir, zc);
|
||||
adapt_lim_gf0[c]->GetValues(el_id, ir, z0c);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
@@ -4307,9 +4437,13 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// Contribution from the adaptive limiting term.
|
||||
if (adaptive_limiting)
|
||||
{
|
||||
const real_t diff = (adapt_lim_gf_q(i) - adapt_lim_gf0_q(i)) /
|
||||
adapt_lim_delta_max;
|
||||
val += adapt_lim_coeff->Eval(*Tpr, ip) * lim_normal * diff * diff;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const int idx = c * nqp + i;
|
||||
const real_t diff = (adapt_lim_gf_q(idx) - adapt_lim_gf0_q(idx)) /
|
||||
adapt_lim_delta_max[c];
|
||||
val += adapt_lim_coeff[c]->Eval(*Tpr, ip) * lim_normal * diff * diff;
|
||||
}
|
||||
}
|
||||
|
||||
energy += weight * val;
|
||||
@@ -4602,7 +4736,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf ||
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf.Size() > 0 ||
|
||||
surf_fit_gf || surf_fit_pos || exact_action)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
@@ -4700,7 +4834,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
|
||||
|
||||
delete Tpr;
|
||||
@@ -4774,7 +4908,8 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf.Size() > 0 ||
|
||||
surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
@@ -4829,7 +4964,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, *Tpr, elmat);}
|
||||
|
||||
delete Tpr;
|
||||
@@ -4842,34 +4977,42 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
|
||||
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q, adapt_lim_gf0_q(nqp);
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
|
||||
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q(nqp), adapt_lim_gf0_q(nqp);
|
||||
Array<int> dofs;
|
||||
adapt_lim_gf->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
adapt_lim_gf->GetSubVector(dofs, adapt_lim_gf_e);
|
||||
adapt_lim_gf->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
|
||||
adapt_lim_gf0->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
|
||||
adapt_lim_gf[0]->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
|
||||
// Project the gradient of adapt_lim_gf in the same space.
|
||||
// The FE coefficients of the gradient go in adapt_lim_gf_grad_e.
|
||||
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
el.ProjectGrad(el, Tpr, grad_phys);
|
||||
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
|
||||
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
|
||||
|
||||
Vector adapt_lim_gf_grad_q(dim);
|
||||
for (int q = 0; q < nqp; q++)
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
el.CalcShape(ip, shape);
|
||||
const real_t delta2 = adapt_lim_delta_max[c] * adapt_lim_delta_max[c];
|
||||
adapt_lim_gf[c]->GetSubVector(dofs, adapt_lim_gf_e);
|
||||
adapt_lim_gf[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
|
||||
adapt_lim_gf0[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
|
||||
|
||||
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
|
||||
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) /
|
||||
adapt_lim_delta_max / adapt_lim_delta_max;
|
||||
adapt_lim_gf_grad_q *= weights(q) * lim_normal * adapt_lim_coeff->Eval(Tpr, ip);
|
||||
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
|
||||
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
|
||||
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
|
||||
|
||||
AddMultVWt(shape, adapt_lim_gf_grad_q, mat);
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
el.CalcShape(ip, shape);
|
||||
|
||||
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
|
||||
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) /
|
||||
delta2;
|
||||
adapt_lim_gf_grad_q *=
|
||||
weights(q) * lim_normal * adapt_lim_coeff[c]->Eval(Tpr, ip);
|
||||
|
||||
AddMultVWt(shape, adapt_lim_gf_grad_q, mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4880,60 +5023,66 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
|
||||
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q, adapt_lim_gf0_q(nqp);
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
|
||||
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q(nqp), adapt_lim_gf0_q(nqp);
|
||||
Array<int> dofs;
|
||||
adapt_lim_gf->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
adapt_lim_gf->GetSubVector(dofs, adapt_lim_gf_e);
|
||||
adapt_lim_gf->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
|
||||
adapt_lim_gf0->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
|
||||
adapt_lim_gf[0]->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
|
||||
// Project the gradient of adapt_lim_gf in the same space.
|
||||
// The FE coefficients of the gradient go in adapt_lim_gf_grad_e.
|
||||
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
el.ProjectGrad(el, Tpr, grad_phys);
|
||||
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
|
||||
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
|
||||
|
||||
// Project the gradient of each gradient of adapt_lim_gf in the same space.
|
||||
// The FE coefficients of the second derivatives go in adapt_lim_gf_hess_e.
|
||||
DenseMatrix adapt_lim_gf_hess_e(dof*dim, dim);
|
||||
Mult(grad_phys, adapt_lim_gf_grad_e, adapt_lim_gf_hess_e);
|
||||
// Reshape to be more convenient later (no change in the data).
|
||||
adapt_lim_gf_hess_e.SetSize(dof, dim*dim);
|
||||
|
||||
Vector adapt_lim_gf_grad_q(dim);
|
||||
DenseMatrix adapt_lim_gf_hess_q(dim, dim);
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
el.CalcShape(ip, shape);
|
||||
const real_t delta2 = adapt_lim_delta_max[c] * adapt_lim_delta_max[c];
|
||||
adapt_lim_gf[c]->GetSubVector(dofs, adapt_lim_gf_e);
|
||||
adapt_lim_gf[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
|
||||
adapt_lim_gf0[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
|
||||
|
||||
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
|
||||
Vector gg_ptr(adapt_lim_gf_hess_q.GetData(), dim*dim);
|
||||
adapt_lim_gf_hess_e.MultTranspose(shape, gg_ptr);
|
||||
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
|
||||
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
|
||||
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
|
||||
|
||||
const real_t coeff = adapt_lim_coeff->Eval(Tpr, ip);
|
||||
const real_t factor =
|
||||
weights(q) * lim_normal * coeff * 2.0 /
|
||||
(adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
// Project the gradient of each gradient of adapt_lim_gf in the same space.
|
||||
// The FE coefficients of the second derivatives go in adapt_lim_gf_hess_e.
|
||||
DenseMatrix adapt_lim_gf_hess_e(dof*dim, dim);
|
||||
Mult(grad_phys, adapt_lim_gf_grad_e, adapt_lim_gf_hess_e);
|
||||
// Reshape to be more convenient later (no change in the data).
|
||||
adapt_lim_gf_hess_e.SetSize(dof, dim*dim);
|
||||
|
||||
for (int i = 0; i < dof * dim; i++)
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const int idof = i % dof, idim = i / dof;
|
||||
for (int j = 0; j <= i; j++)
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
el.CalcShape(ip, shape);
|
||||
|
||||
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
|
||||
Vector gg_ptr(adapt_lim_gf_hess_q.GetData(), dim*dim);
|
||||
adapt_lim_gf_hess_e.MultTranspose(shape, gg_ptr);
|
||||
|
||||
const real_t coeff_q = adapt_lim_coeff[c]->Eval(Tpr, ip);
|
||||
const real_t factor =
|
||||
weights(q) * lim_normal * coeff_q * 2.0 /
|
||||
delta2;
|
||||
|
||||
for (int i = 0; i < dof * dim; i++)
|
||||
{
|
||||
const int jdof = j % dof, jdim = j / dof;
|
||||
const real_t entry =
|
||||
factor *
|
||||
(adapt_lim_gf_grad_q(idim) * shape(idof) *
|
||||
adapt_lim_gf_grad_q(jdim) * shape(jdof) +
|
||||
(adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
|
||||
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
|
||||
mat(i, j) += entry;
|
||||
if (i != j) { mat(j, i) += entry; }
|
||||
const int idof = i % dof, idim = i / dof;
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
const int jdof = j % dof, jdim = j / dof;
|
||||
const real_t entry =
|
||||
factor *
|
||||
(adapt_lim_gf_grad_q(idim) * shape(idof) *
|
||||
adapt_lim_gf_grad_q(jdim) * shape(jdof) +
|
||||
(adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
|
||||
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
|
||||
mat(i, j) += entry;
|
||||
if (i != j) { mat(j, i) += entry; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5206,7 +5355,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting, surface fitting (exact derivatives).
|
||||
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
if (adapt_lim_gf.Size() > 0 || surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
const IntegrationRule &ir = ActionIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -5230,7 +5379,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
}
|
||||
|
||||
PMatO.UseExternalData(elvect.GetData(), dof, dim);
|
||||
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
|
||||
}
|
||||
}
|
||||
@@ -5316,7 +5465,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting.
|
||||
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
if (adapt_lim_gf.Size() > 0 || surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
const IntegrationRule &ir = GradientIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -5339,7 +5488,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
ir.IntPoint(q).weight;
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, Tpr, elmat); }
|
||||
}
|
||||
}
|
||||
@@ -5686,9 +5835,22 @@ UpdateAfterMeshPositionChange(const Vector &d, const FiniteElementSpace &d_fes)
|
||||
}
|
||||
|
||||
// Update adapt_lim_gf if adaptive limiting is enabled.
|
||||
if (adapt_lim_gf)
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{
|
||||
adapt_lim_eval->ComputeAtNewPosition(x_loc, *adapt_lim_gf, ordering);
|
||||
// All adapt_lim_gf are remapped as a multi-component vector.
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
const int ndofs = adapt_lim_gf0[0]->Size();
|
||||
Vector new_field_vec;
|
||||
new_field_vec.SetSize(nal * ndofs, *adapt_lim_gf[0]);
|
||||
new_field_vec.UseDevice(adapt_lim_gf[0]->UseDevice());
|
||||
adapt_lim_eval->ComputeAtNewPosition(x_loc, new_field_vec, ordering);
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t *src = new_field_vec.Read() + c * ndofs;
|
||||
real_t *dst = adapt_lim_gf[c]->Write();
|
||||
internal::device_copy(dst, src, ndofs);
|
||||
}
|
||||
|
||||
if (PA.enabled)
|
||||
{
|
||||
PA.AL_grads_assembled = false;
|
||||
@@ -5698,9 +5860,17 @@ UpdateAfterMeshPositionChange(const Vector &d, const FiniteElementSpace &d_fes)
|
||||
|
||||
// Refresh PA.ALF from the updated adapt_lim_gf.
|
||||
const ElementDofOrdering ord = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
const Operator *alf_R =
|
||||
adapt_lim_gf->FESpace()->GetElementRestriction(ord);
|
||||
alf_R->Mult(*adapt_lim_gf, PA.ALF);
|
||||
const FiniteElementSpace *alfes = adapt_lim_gf[0]->FESpace();
|
||||
const Operator *alf_R = alfes->GetElementRestriction(ord);
|
||||
|
||||
const int Esize = alf_R->Height();
|
||||
Vector ALFc;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
MFEM_VERIFY(adapt_lim_gf[c]->Size() == ndofs, "internal error");
|
||||
ALFc.MakeRef(PA.ALF, c * Esize, Esize);
|
||||
alf_R->Mult(*adapt_lim_gf[c], ALFc);
|
||||
}
|
||||
|
||||
// Step 2 of PA.ALFmF0 update: add the new ALF.
|
||||
PA.ALFmF0 += PA.ALF;
|
||||
@@ -5917,7 +6087,7 @@ ComputeUntangleMetricQuantiles(const Vector &d, const FiniteElementSpace &fes)
|
||||
dynamic_cast<const ParFiniteElementSpace *>(&fes);
|
||||
#endif
|
||||
|
||||
if (wcuo && wcuo->GetBarrierType() ==
|
||||
if (wcuo->GetBarrierType() ==
|
||||
TMOP_WorstCaseUntangleOptimizer_Metric::BarrierType::Shifted)
|
||||
{
|
||||
real_t min_detT = ComputeMinDetT(x_loc, fes);
|
||||
@@ -5929,7 +6099,7 @@ ComputeUntangleMetricQuantiles(const Vector &d, const FiniteElementSpace &fes)
|
||||
MPITypeMap<real_t>::mpi_type, MPI_MIN, pfes->GetComm());
|
||||
}
|
||||
#endif
|
||||
if (wcuo) { wcuo->SetMinDetT(min_detT_all); }
|
||||
wcuo->SetMinDetT(min_detT_all);
|
||||
}
|
||||
|
||||
real_t max_muT = ComputeUntanglerMaxMuBarrier(x_loc, fes);
|
||||
@@ -5975,6 +6145,16 @@ void TMOPComboIntegrator::EnableAdaptiveLimiting(const GridFunction &z0,
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
|
||||
void TMOPComboIntegrator::
|
||||
EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
|
||||
{
|
||||
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
|
||||
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOPComboIntegrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
Coefficient &coeff,
|
||||
@@ -5985,6 +6165,16 @@ void TMOPComboIntegrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
|
||||
void TMOPComboIntegrator::
|
||||
EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
|
||||
{
|
||||
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
|
||||
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOPComboIntegrator::SetLimitingNodes(const GridFunction &n0)
|
||||
|
||||
+34
-11
@@ -2038,14 +2038,17 @@ protected:
|
||||
real_t lim_normal;
|
||||
|
||||
// Adaptive limiting.
|
||||
const GridFunction *adapt_lim_gf0; // Not owned.
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParGridFunction *adapt_lim_pgf0;
|
||||
#endif
|
||||
GridFunction *adapt_lim_gf; // Owned. Updated by adapt_lim_eval.
|
||||
Coefficient *adapt_lim_coeff; // Not owned.
|
||||
AdaptivityEvaluator *adapt_lim_eval; // Not owned.
|
||||
real_t adapt_lim_delta_max = 1.0;
|
||||
// Adaptive limiting fields. Each field adds a term to the integral:
|
||||
// int [ c_k (z_k(x) - z_k0(x0))^2 / delta_max_k^2 ] dx
|
||||
// with one Coefficient per field. The fields z_k(x) are remapped from their
|
||||
// initial values z_k0(x0) through a single AdaptivityEvaluator instance.
|
||||
// All GridFunctions must use the same FE space.
|
||||
Array<GridFunction *> adapt_lim_gf0; // Owned. Initial fields z_k0(x0).
|
||||
Array<GridFunction *> adapt_lim_gf; // Owned. Remapped fields z_k(x).
|
||||
Vector adapt_lim_init_nodes; // Owned. Initial mesh nodes (ldofs).
|
||||
Array<Coefficient *> adapt_lim_coeff; // Not owned, one per field.
|
||||
AdaptivityEvaluator *adapt_lim_eval; // Not owned. Used for all fields.
|
||||
Array<real_t> adapt_lim_delta_max; // Per-field delta_max_k (>0).
|
||||
|
||||
// Surface fitting.
|
||||
const Array<bool> *surf_fit_marker; // Not owned. Nodes to fit.
|
||||
@@ -2141,13 +2144,13 @@ protected:
|
||||
{
|
||||
bool enabled;
|
||||
int dim, ne, nq;
|
||||
int nal = 0; // number of adaptive limiting fields
|
||||
mutable DenseTensor Jtr;
|
||||
mutable bool Jtr_needs_update;
|
||||
mutable bool Jtr_debug_grad;
|
||||
mutable Vector E, O, X0, XL, H, C0, LD, H0, MC, ALC,
|
||||
ALF, ALFmF0, ALFG, ALFH;
|
||||
ALF, ALFmF0, ALFG, ALFH, ALD;
|
||||
mutable bool AL_grads_assembled;
|
||||
real_t al_delta;
|
||||
const DofToQuad *maps;
|
||||
const DofToQuad *maps_lim = nullptr;
|
||||
const DofToQuad *maps_nodes = nullptr;
|
||||
@@ -2314,7 +2317,6 @@ public:
|
||||
integ_order(-1), metric_coeff(NULL), metric_normal(1.0),
|
||||
lim_nodes0(NULL), lim_coeff(NULL),
|
||||
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
|
||||
adapt_lim_gf0(NULL), adapt_lim_gf(NULL), adapt_lim_coeff(NULL),
|
||||
adapt_lim_eval(NULL),
|
||||
surf_fit_marker(NULL), surf_fit_coeff(NULL),
|
||||
surf_fit_gf(NULL), surf_fit_eval(NULL),
|
||||
@@ -2403,10 +2405,21 @@ public:
|
||||
Smaller values activate the term faster. */
|
||||
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
/// Multi-field adaptive limiting with per-field delta_max values. All
|
||||
/// GridFunctions must be on the same FiniteElementSpace.
|
||||
void EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
const Array<real_t> &delta_max);
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for adaptive limiting.
|
||||
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
/// Multi-field parallel adaptive limiting with per-field delta_max values.
|
||||
void EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
const Array<real_t> &delta_max);
|
||||
#endif
|
||||
|
||||
/** @brief Fitting of certain DOFs to the zero level set of a function.
|
||||
@@ -2632,10 +2645,20 @@ public:
|
||||
/// Adds the adaptive limiting term to the first integrator.
|
||||
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
/// Multi-field adaptive limiting with per-field delta_max values.
|
||||
void EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
const Array<real_t> &delta_max);
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for adaptive limiting.
|
||||
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
/// Multi-field parallel adaptive limiting with per-field delta_max values.
|
||||
void EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
|
||||
const Array<Coefficient *> &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
const Array<real_t> &delta_max);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -119,15 +119,14 @@ void TMOP_AssembleDiagPA_AdaptLim_2D(const real_t lim_normal,
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, e);
|
||||
const real_t detJtr = kernels::Det<2>(Jtr);
|
||||
const real_t weight = W(qx, qy) * detJtr;
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
|
||||
const real_t coeff = const_coeff ? ALC(0,0,0) : ALC(qx, qy, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
|
||||
const real_t diff = alf_quad(qy, qx);
|
||||
const real_t grad_v = ALF_grad(v, qx, qy, e);
|
||||
const real_t hess_vv = ALF_hess(v, v, qx, qy, e);
|
||||
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
|
||||
|
||||
QD(qx, dy) += bb * hdiag;
|
||||
QD(qx, dy) += bb * factor * (grad_v*grad_v + diff * hess_vv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,27 +175,45 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim2D);
|
||||
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_2D(Vector &diagonal) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
|
||||
auto D = Reshape(diagonal.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d;
|
||||
const int nqp_el = q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 2 * nqp_el * NE;
|
||||
const int ALFH_stride = 2 * 2 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
const real_t *ALFH_all = PA.ALFH.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -183,15 +183,15 @@ void TMOP_AssembleDiagPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t coeff =
|
||||
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
|
||||
const real_t diff = alf_quad(qz, qy, qx);
|
||||
const real_t grad_v = ALF_grad(v, qx, qy, qz, e);
|
||||
const real_t hess_vv = ALF_hess(v, v, qx, qy, qz, e);
|
||||
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
|
||||
|
||||
u += bb * hdiag;
|
||||
u += bb * factor * (grad_v * grad_v + diff * hess_vv);
|
||||
}
|
||||
r0[dz][qy][qx] = u;
|
||||
}
|
||||
@@ -265,26 +265,45 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim3D);
|
||||
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_3D(Vector &diagonal) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
|
||||
auto D = Reshape(diagonal.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d * d;
|
||||
const int nqp_el = q * q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 3 * nqp_el * NE;
|
||||
const int ALFH_stride = 3 * 3 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
const real_t *ALFH_all = PA.ALFH.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -113,7 +113,7 @@ void TMOP_AssembleGradPA_C0_2D(const real_t lim_normal,
|
||||
});
|
||||
}
|
||||
|
||||
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (2D)
|
||||
// Assemble gradient and Hessian of ALF field at quad points for AdaptLim (2D).
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleGradPA_AdaptLim_2D(const int NE,
|
||||
const real_t *B_nodes,
|
||||
@@ -185,7 +185,7 @@ void TMOP_AssembleGradPA_AdaptLim_2D(const int NE,
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Compute/interpolate gradient and Hessian one vector component at a time.
|
||||
// Compute/interpolate gradient and Hessian, one component at a time.
|
||||
for (int c = 0; c < 2; c++)
|
||||
{
|
||||
kernels::internal::s_regs2d_t<MD1> rgrad_nodes, ddalf_dx_n, ddalf_dy_n;
|
||||
@@ -326,16 +326,31 @@ void TMOP_Integrator::AssembleGradPA_AdaptLim_2D(const Vector &x) const
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
|
||||
const auto *B_nodes = PA.maps_nodes->B.Read(),
|
||||
*G_nodes = PA.maps_nodes->G.Read();
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto X = Reshape(x.Read(), d, d, 2, NE);
|
||||
const auto ALF = Reshape(PA.ALF.Read(), d, d, NE);
|
||||
auto ALF_grad = Reshape(PA.ALFG.Write(), 2, q, q, NE);
|
||||
auto ALF_hess = Reshape(PA.ALFH.Write(), 2, 2, q, q, NE);
|
||||
const int ndof_el = d * d;
|
||||
const int nqp_el = q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 2 * nqp_el * NE;
|
||||
const int ALFH_stride = 2 * 2 * nqp_el * NE;
|
||||
|
||||
TMOPAssembleGradAdaptLim2D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
const real_t *ALF_all = PA.ALF.Read();
|
||||
real_t *ALFG_all = PA.ALFG.Write();
|
||||
real_t *ALFH_all = PA.ALFH.Write();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const auto ALF = Reshape(ALF_all + c * ALF_stride, d, d, NE);
|
||||
auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
|
||||
auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
|
||||
|
||||
TMOPAssembleGradAdaptLim2D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
}
|
||||
PA.AL_grads_assembled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ void TMOP_Integrator::AssembleGradPA_C0_3D(const Vector &x) const
|
||||
J, W, b, bld, XL, X, H0, exp_lim, d, q);
|
||||
}
|
||||
|
||||
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (3D)
|
||||
// Assemble gradient and Hessian of ALF field at quadr points for AdaptLim (3D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
|
||||
const real_t *B_nodes,
|
||||
@@ -202,7 +202,8 @@ void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
|
||||
kernels::internal::Grad3d(D1D, D1D, smem.d, sB_nodes, sG_nodes, r_X, r_J);
|
||||
|
||||
// Compute the reference derivatives of ALF at DOF nodes.
|
||||
kernels::internal::s_regs3d_t<MD1> alf_n, dalf_dxi_n, dalf_deta_n, dalf_dzeta_n;
|
||||
kernels::internal::s_regs3d_t<MD1> alf_n;
|
||||
kernels::internal::s_regs3d_t<MD1> dalf_dxi_n, dalf_deta_n, dalf_dzeta_n;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sG_nodes, sB_nodes, sB_nodes,
|
||||
@@ -222,7 +223,8 @@ void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
|
||||
// Compute/interpolate gradient and Hessian one vector component at a time.
|
||||
for (int c = 0; c < 3; c++)
|
||||
{
|
||||
kernels::internal::s_regs3d_t<MD1> rgrad_nodes, dd_dxi_n, dd_deta_n, dd_dzeta_n;
|
||||
kernels::internal::s_regs3d_t<MD1> rgrad_nodes;
|
||||
kernels::internal::s_regs3d_t<MD1> dd_dxi_n, dd_deta_n, dd_dzeta_n;
|
||||
|
||||
// Precompute the inverse of the physical Jacobian.
|
||||
kernels::internal::vd_regs3d_t<3, 3, MD1> Jpr_inv;
|
||||
@@ -399,16 +401,31 @@ void TMOP_Integrator::AssembleGradPA_AdaptLim_3D(const Vector &x) const
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
|
||||
const auto *B_nodes = PA.maps_nodes->B.Read(),
|
||||
*G_nodes = PA.maps_nodes->G.Read();
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto X = Reshape(x.Read(), d, d, d, 3, NE);
|
||||
const auto ALF = Reshape(PA.ALF.Read(), d, d, d, NE);
|
||||
auto ALF_grad = Reshape(PA.ALFG.Write(), 3, q, q, q, NE);
|
||||
auto ALF_hess = Reshape(PA.ALFH.Write(), 3, 3, q, q, q, NE);
|
||||
const int ndof_el = d * d * d;
|
||||
const int nqp_el = q * q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 3 * nqp_el * NE;
|
||||
const int ALFH_stride = 3 * 3 * nqp_el * NE;
|
||||
|
||||
TMOPAssembleGradAdaptLim3D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
const real_t *ALF_all = PA.ALF.Read();
|
||||
real_t *ALFG_all = PA.ALFG.Write();
|
||||
real_t *ALFH_all = PA.ALFH.Write();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const auto ALF = Reshape(ALF_all + c * ALF_stride, d, d, d, NE);
|
||||
auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
|
||||
auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
|
||||
|
||||
TMOPAssembleGradAdaptLim3D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
}
|
||||
PA.AL_grads_assembled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,27 +182,46 @@ void TMOP_Integrator::AddMultGradPA_AdaptLim_2D(const Vector &R,
|
||||
Vector &C) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto RR = Reshape(R.Read(), d, d, 2, NE);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
|
||||
auto Y = Reshape(C.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPMultGradAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
|
||||
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d;
|
||||
const int nqp_el = q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 2 * nqp_el * NE;
|
||||
const int ALFH_stride = 2 * 2 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
const real_t *ALFH_all = PA.ALFH.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
|
||||
|
||||
TMOPMultGradAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
|
||||
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -169,10 +169,12 @@ void TMOP_AddMultGradPA_AdaptLim_3D(const real_t lim_normal,
|
||||
|
||||
// Hessian action:
|
||||
// H = factor * (grad x grad + (gf - gf0) * hess)
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t coeff =
|
||||
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
const real_t grad_dot_R =
|
||||
grad_alf[0] * R_q[0] + grad_alf[1] * R_q[1] + grad_alf[2] * R_q[2];
|
||||
const real_t grad_dot_R = grad_alf[0] * R_q[0] +
|
||||
grad_alf[1] * R_q[1] +
|
||||
grad_alf[2] * R_q[2];
|
||||
real_t hess_R[3];
|
||||
hess_R[0] =
|
||||
ALF_hess(0, 0, qx, qy, qz, e) * R_q[0] +
|
||||
@@ -187,9 +189,12 @@ void TMOP_AddMultGradPA_AdaptLim_3D(const real_t lim_normal,
|
||||
ALF_hess(2, 1, qx, qy, qz, e) * R_q[1] +
|
||||
ALF_hess(2, 2, qx, qy, qz, e) * R_q[2];
|
||||
|
||||
r00(0, qz, qy, qx) = factor * (grad_alf[0] * grad_dot_R + diff * hess_R[0]);
|
||||
r00(1, qz, qy, qx) = factor * (grad_alf[1] * grad_dot_R + diff * hess_R[1]);
|
||||
r00(2, qz, qy, qx) = factor * (grad_alf[2] * grad_dot_R + diff * hess_R[2]);
|
||||
r00(0, qz, qy, qx) = factor * (grad_alf[0] * grad_dot_R +
|
||||
diff * hess_R[0]);
|
||||
r00(1, qz, qy, qx) = factor * (grad_alf[1] * grad_dot_R +
|
||||
diff * hess_R[1]);
|
||||
r00(2, qz, qy, qx) = factor * (grad_alf[2] * grad_dot_R +
|
||||
diff * hess_R[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,27 +211,46 @@ void TMOP_Integrator::AddMultGradPA_AdaptLim_3D(const Vector &R,
|
||||
Vector &C) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto RR = Reshape(R.Read(), d, d, d, 3, NE);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
|
||||
auto Y = Reshape(C.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPMultGradAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
|
||||
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d * d;
|
||||
const int nqp_el = q * q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 3 * nqp_el * NE;
|
||||
const int ALFH_stride = 3 * 3 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
const real_t *ALFH_all = PA.ALFH.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
|
||||
|
||||
TMOPMultGradAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J,
|
||||
W, B, RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -205,25 +205,41 @@ void TMOP_Integrator::AddMultPA_AdaptLim_2D([[maybe_unused]] const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPMultAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d;
|
||||
const int nqp_el = q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 2 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
|
||||
TMOPMultAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -76,7 +76,7 @@ void TMOP_AddMultPA_C0_3D(const real_t lim_normal,
|
||||
r11(1, qz, qy, qx),
|
||||
r11(2, qz, qy, qx)
|
||||
};
|
||||
const real_t coeff0 = const_c0 ? C0(0, 0, 0, 0) : C0(qx, qy, qz, e);
|
||||
const real_t coeff0 = const_c0 ? C0(0,0,0,0) : C0(qx, qy, qz, e);
|
||||
|
||||
real_t d1[3];
|
||||
// Eval_d1 (Quadratic Limiter)
|
||||
@@ -193,7 +193,8 @@ void TMOP_AddMultPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t coeff =
|
||||
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq *
|
||||
alf_quad(qz, qy, qx);
|
||||
|
||||
@@ -217,26 +218,41 @@ void TMOP_Integrator::AddMultPA_AdaptLim_3D([[maybe_unused]] const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPMultAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
const real_t *ALD = PA.ALD.HostRead();
|
||||
|
||||
const int ndof_el = d * d * d;
|
||||
const int nqp_el = q * q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
const int ALFG_stride = 3 * nqp_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
const real_t *ALFG_all = PA.ALFG.Read();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = ALD[c];
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
|
||||
TMOPMultAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+99
-41
@@ -46,14 +46,14 @@ void TMOP_Integrator::AssembleGradPA(const Vector &de,
|
||||
{
|
||||
AssembleGradPA_2D(xe);
|
||||
if (lim_coeff) { AssembleGradPA_C0_2D(xe); }
|
||||
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_2D(xe); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleGradPA_AdaptLim_2D(xe); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AssembleGradPA_3D(xe);
|
||||
if (lim_coeff) { AssembleGradPA_C0_3D(xe); }
|
||||
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_3D(xe); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleGradPA_AdaptLim_3D(xe); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,12 +201,15 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
|
||||
|
||||
|
||||
// All are constant or not specified.
|
||||
if (PA.MC.Size() == 1 && PA.C0.Size() <= 1 && PA.ALC.Size() <= 1) { return; }
|
||||
const int nal = PA.nal;
|
||||
const bool alc_is_qvec =
|
||||
(nal > 0) ? (PA.ALC.Size() == nal * PA.nq * PA.ne) : false;
|
||||
if (PA.MC.Size() == 1 && PA.C0.Size() <= 1 && !alc_is_qvec) { return; }
|
||||
|
||||
// Coefficients are always evaluated on the CPU for now.
|
||||
PA.MC.HostWrite();
|
||||
PA.C0.HostWrite();
|
||||
PA.ALC.HostWrite();
|
||||
if (alc_is_qvec) { PA.ALC.HostWrite(); }
|
||||
|
||||
const IntegrationRule &ir = *PA.ir;
|
||||
auto T = new IsoparametricTransformation;
|
||||
@@ -231,11 +234,17 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
|
||||
}
|
||||
}
|
||||
|
||||
if (PA.ALC.Size() > 1)
|
||||
if (alc_is_qvec)
|
||||
{
|
||||
for (int q = 0; q < PA.nq; ++q)
|
||||
MFEM_VERIFY(nal == adapt_lim_coeff.Size(), "internal error");
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
PA.ALC(q + e * PA.nq) = adapt_lim_coeff->Eval(*T, ir.IntPoint(q));
|
||||
real_t *ALC_c = PA.ALC.HostWrite() + c * PA.nq * PA.ne;
|
||||
for (int q = 0; q < PA.nq; ++q)
|
||||
{
|
||||
ALC_c[q + e * PA.nq] =
|
||||
adapt_lim_coeff[c]->Eval(*T, ir.IntPoint(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,37 +345,67 @@ void TMOP_Integrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
if (lim_coeff) { AssemblePA_Limiting(); }
|
||||
// Adaptive limiting: adapt_lim_coeff -> PA.ALC, adapt_lim_gf -> PA.ALF,
|
||||
// adapt_lim_gf0 -> PA.ALF0, adapt_lim_delta_max -> PA.ALD
|
||||
if (adapt_lim_gf) { AssemblePA_AdaptLim(); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssemblePA_AdaptLim(); }
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssemblePA_AdaptLim()
|
||||
{
|
||||
const FiniteElementSpace *alfes = adapt_lim_gf->FESpace();
|
||||
const int nal = adapt_lim_coeff.Size();
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
|
||||
MFEM_VERIFY(adapt_lim_gf.Size() == nal && adapt_lim_gf0.Size() == nal,
|
||||
"internal error");
|
||||
const FiniteElementSpace *alfes = adapt_lim_gf[0]->FESpace();
|
||||
MFEM_VERIFY(alfes && alfes->GetVDim() == 1, "internal error");
|
||||
|
||||
MFEM_VERIFY(strcmp(alfes->FEColl()->Name(), PA.fes->FEColl()->Name()) == 0 &&
|
||||
alfes->FEColl()->GetOrder() == PA.fes->FEColl()->GetOrder(),
|
||||
"The PA code assumes the same FE spaces for mesh and limiting.");
|
||||
|
||||
PA.AL_grads_assembled = false;
|
||||
PA.nal = nal;
|
||||
|
||||
// adapt_lim_coeff -> PA.ALC (Q-vector).
|
||||
PA.ALC.UseDevice(true);
|
||||
if (auto *cQ = dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff))
|
||||
// adapt_lim_coeff -> PA.ALC
|
||||
// Keep the ConstantCoefficient fast-path: when all coefficients are
|
||||
// constant, store one scalar per adaptive-limiting term.
|
||||
bool all_const = true;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
PA.ALC.SetSize(1, Device::GetMemoryType());
|
||||
PA.ALC.HostWrite();
|
||||
PA.ALC(0) = cQ->constant;
|
||||
if (!dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff[c]))
|
||||
{
|
||||
all_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PA.ALC.UseDevice(true);
|
||||
if (all_const)
|
||||
{
|
||||
PA.ALC.SetSize(nal, Device::GetMemoryType());
|
||||
real_t *ALC_all = PA.ALC.HostWrite();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
auto *cc = dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff[c]);
|
||||
MFEM_VERIFY(cc, "internal error");
|
||||
ALC_all[c] = cc->constant;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PA.ALC.SetSize(PA.nq * PA.ne, Device::GetMemoryType());
|
||||
auto ALC = Reshape(PA.ALC.HostWrite(), PA.nq, PA.ne);
|
||||
for (int e = 0; e < PA.ne; ++e)
|
||||
// If one Coefficient is not constant, we allocate the full size for
|
||||
// all Coefficients. Could be optimized in the future.
|
||||
PA.ALC.SetSize(nal * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
real_t *ALC_all = PA.ALC.HostWrite();
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
ElementTransformation &T = *PA.fes->GetElementTransformation(e);
|
||||
for (int q = 0; q < PA.ir->GetNPoints(); ++q)
|
||||
real_t *ALC_c = ALC_all + c * PA.nq * PA.ne;
|
||||
for (int e = 0; e < PA.ne; ++e)
|
||||
{
|
||||
ALC(q, e) = adapt_lim_coeff->Eval(T, PA.ir->IntPoint(q));
|
||||
ElementTransformation &T = *PA.fes->GetElementTransformation(e);
|
||||
for (int q = 0; q < PA.ir->GetNPoints(); ++q)
|
||||
{
|
||||
ALC_c[q + e * PA.nq] =
|
||||
adapt_lim_coeff[c]->Eval(T, PA.ir->IntPoint(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,29 +435,46 @@ void TMOP_Integrator::AssemblePA_AdaptLim()
|
||||
PA.maps_nodes = &fe_n->GetDofToQuad(lex_nodes, DofToQuad::TENSOR);
|
||||
}
|
||||
|
||||
// adapt_lim_gf -> PA.ALF (E-vector, same pattern as LD).
|
||||
const FiniteElement &fe = *alfes->GetTypicalFE();
|
||||
PA.ALF.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
|
||||
PA.ALF.UseDevice(true);
|
||||
// Restrict each adaptive limiting field into separate contiguous E-vectors
|
||||
// (one block per adaptive limiting term).
|
||||
const Operator *alf_R = alfes->GetElementRestriction(ordering);
|
||||
alf_R->Mult(*adapt_lim_gf, PA.ALF);
|
||||
// adapt_lim_gf - adapt_lim_gf0 -> PA.ALFmF0
|
||||
PA.ALFmF0.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
|
||||
const int ndofs = alfes->GetVSize();
|
||||
|
||||
const int Esize = alf_R->Height();
|
||||
PA.ALF.SetSize(nal * Esize, Device::GetMemoryType());
|
||||
PA.ALF.UseDevice(true);
|
||||
PA.ALFmF0.SetSize(nal * Esize, Device::GetMemoryType());
|
||||
PA.ALFmF0.UseDevice(true);
|
||||
alf_R->Mult(*adapt_lim_gf0, PA.ALFmF0);
|
||||
|
||||
Vector ALFc, ALF0c;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
ALFc.MakeRef(PA.ALF, c * Esize, Esize);
|
||||
ALF0c.MakeRef(PA.ALFmF0, c * Esize, Esize);
|
||||
|
||||
MFEM_VERIFY(adapt_lim_gf[c]->Size() == ndofs, "internal error");
|
||||
MFEM_VERIFY(adapt_lim_gf0[c]->Size() == ndofs, "internal error");
|
||||
alf_R->Mult(*adapt_lim_gf[c], ALFc);
|
||||
alf_R->Mult(*adapt_lim_gf0[c], ALF0c);
|
||||
}
|
||||
|
||||
// Build differences in-place: ALFmF0 = ALF - ALF0.
|
||||
PA.ALFmF0 *= -1.0;
|
||||
PA.ALFmF0 += PA.ALF;
|
||||
|
||||
// adapt_lim_delta_max -> PA.al_delta.
|
||||
PA.al_delta = adapt_lim_delta_max;
|
||||
// Per-field delta_max values.
|
||||
MFEM_VERIFY(adapt_lim_delta_max.Size() == nal, "internal error");
|
||||
PA.ALD.SetSize(nal);
|
||||
PA.ALD.HostWrite();
|
||||
for (int c = 0; c < nal; c++) { PA.ALD(c) = adapt_lim_delta_max[c]; }
|
||||
|
||||
// Allocate storage for gradient and Hessian of ALF at quadrature points
|
||||
// These will be filled during AssembleGradPA
|
||||
const int dim = PA.dim;
|
||||
PA.ALFG.UseDevice(true);
|
||||
PA.ALFG.SetSize(dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
PA.ALFG.SetSize(nal * dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
PA.ALFH.UseDevice(true);
|
||||
PA.ALFH.SetSize(dim * dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
PA.ALFH.SetSize(nal * dim * dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
|
||||
}
|
||||
|
||||
@@ -439,14 +495,14 @@ void TMOP_Integrator::AssembleGradDiagonalPA(Vector &de) const
|
||||
{
|
||||
AssembleDiagonalPA_2D(de);
|
||||
if (lim_coeff) { AssembleDiagonalPA_C0_2D(de); }
|
||||
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_2D(de); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleDiagonalPA_AdaptLim_2D(de); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AssembleDiagonalPA_3D(de);
|
||||
if (lim_coeff) { AssembleDiagonalPA_C0_3D(de); }
|
||||
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_3D(de); }
|
||||
if (adapt_lim_gf.Size() > 0) { AssembleDiagonalPA_AdaptLim_3D(de); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +529,7 @@ void TMOP_Integrator::AddMultPA(const Vector &de, Vector &ye) const
|
||||
{
|
||||
AddMultPA_2D(xe, ye);
|
||||
if (lim_coeff) { AddMultPA_C0_2D(xe, ye); }
|
||||
if (adapt_lim_gf)
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{
|
||||
// AddMultPA_AdaptLim_2D uses the precomputed AdaptLim field gradient
|
||||
// at quadrature points (PA.ALFG). Ensure it is up-to-date for the
|
||||
@@ -488,7 +544,7 @@ void TMOP_Integrator::AddMultPA(const Vector &de, Vector &ye) const
|
||||
{
|
||||
AddMultPA_3D(xe, ye);
|
||||
if (lim_coeff) { AddMultPA_C0_3D(xe, ye); }
|
||||
if (adapt_lim_gf)
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{
|
||||
AssembleGradPA_AdaptLim_3D(xe);
|
||||
AddMultPA_AdaptLim_3D(xe, ye);
|
||||
@@ -513,14 +569,14 @@ void TMOP_Integrator::AddMultGradPA(const Vector &re, Vector &ce) const
|
||||
{
|
||||
AddMultGradPA_2D(re, ce);
|
||||
if (lim_coeff) { AddMultGradPA_C0_2D(re, ce); }
|
||||
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_2D(re, ce); }
|
||||
if (adapt_lim_gf.Size() > 0) { AddMultGradPA_AdaptLim_2D(re, ce); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AddMultGradPA_3D(re, ce);
|
||||
if (lim_coeff) { AddMultGradPA_C0_3D(re, ce); }
|
||||
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_3D(re, ce); }
|
||||
if (adapt_lim_gf.Size() > 0) { AddMultGradPA_AdaptLim_3D(re, ce); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,14 +605,16 @@ real_t TMOP_Integrator::GetLocalStateEnergyPA(const Vector &de) const
|
||||
{
|
||||
GetLocalStateEnergyPA_2D(xe, energy);
|
||||
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_2D(xe); }
|
||||
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_2D(); }
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{ energy += GetLocalStateEnergyPA_AdaptLim_2D(); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
GetLocalStateEnergyPA_3D(xe, energy);
|
||||
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_3D(xe); }
|
||||
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_3D(); }
|
||||
if (adapt_lim_gf.Size() > 0)
|
||||
{ energy += GetLocalStateEnergyPA_AdaptLim_3D(); }
|
||||
}
|
||||
|
||||
return energy;
|
||||
|
||||
@@ -182,26 +182,43 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim2D);
|
||||
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_2D() const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *b = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
auto E = Reshape(PA.E.Write(), q, q, NE);
|
||||
|
||||
TMOPEnergyAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
MFEM_VERIFY(PA.ALD.Size() == nal, "internal error");
|
||||
PA.ALD.HostRead();
|
||||
|
||||
return PA.E * PA.O;
|
||||
const int ndof_el = d * d;
|
||||
const int nqp_el = q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
real_t energy = 0.0;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = PA.ALD(c);
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
|
||||
TMOPEnergyAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
energy += PA.E * PA.O;
|
||||
}
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -198,26 +198,43 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim3D);
|
||||
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_3D() const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *b = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
auto E = Reshape(PA.E.Write(), q, q, q, NE);
|
||||
|
||||
TMOPEnergyAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
const int nal = PA.nal;
|
||||
MFEM_VERIFY(nal > 0, "internal error");
|
||||
MFEM_VERIFY(PA.ALD.Size() == nal, "internal error");
|
||||
PA.ALD.HostRead();
|
||||
|
||||
return PA.E * PA.O;
|
||||
const int ndof_el = d * d * d;
|
||||
const int nqp_el = q * q * q;
|
||||
const int ALF_stride = ndof_el * NE;
|
||||
|
||||
const bool const_coeff = (PA.ALC.Size() == nal);
|
||||
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
|
||||
const real_t *ALC_all = PA.ALC.Read();
|
||||
const real_t *ALFmF0_all = PA.ALFmF0.Read();
|
||||
real_t energy = 0.0;
|
||||
for (int c = 0; c < nal; c++)
|
||||
{
|
||||
const real_t delta_max = PA.ALD(c);
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(ALC_all + c, 1, 1, 1, 1)
|
||||
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
|
||||
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
|
||||
TMOPEnergyAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
energy += PA.E * PA.O;
|
||||
}
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -759,6 +759,7 @@ void TMOPHRSolver::Update()
|
||||
gridfuncarr[i]->SetTrueVector();
|
||||
gridfuncarr[i]->SetFromTrueVector();
|
||||
}
|
||||
tmopns->UpdateDeterminantBoundGridFunction();
|
||||
|
||||
// Update Discrete Indicator for all the TMOP_Integrators in NonLinearForm
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
@@ -806,6 +807,7 @@ void TMOPHRSolver::ParUpdate()
|
||||
pgridfuncarr[i]->SetTrueVector();
|
||||
pgridfuncarr[i]->SetFromTrueVector();
|
||||
}
|
||||
tmopns->UpdateDeterminantBoundGridFunction();
|
||||
|
||||
// Update Discrete Indicator
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
|
||||
+105
-7
@@ -68,6 +68,9 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_mesh_nodes,
|
||||
}
|
||||
}
|
||||
|
||||
// Without this, the next remap would start from the initial mesh, i.e.,
|
||||
// every consecutive remap would be more expensive, as it would have to
|
||||
// transport the solution through bigger displacements.
|
||||
field0 = new_field;
|
||||
nodes0 = new_mesh_nodes;
|
||||
}
|
||||
@@ -305,8 +308,14 @@ void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
|
||||
M.BilinearForm::operator=(0.0);
|
||||
M.Assemble();
|
||||
|
||||
HypreParVector *RHS = rhs.ParallelAssemble();
|
||||
HypreParVector X(K.ParFESpace());
|
||||
Vector RHS;
|
||||
RHS.SetSize(M.ParFESpace()->GetTrueVSize(), ind);
|
||||
RHS.UseDevice(ind.UseDevice());
|
||||
rhs.ParallelAssemble(RHS);
|
||||
|
||||
Vector X;
|
||||
X.SetSize(M.ParFESpace()->GetTrueVSize(), ind);
|
||||
X.UseDevice(ind.UseDevice());
|
||||
X = 0.0;
|
||||
|
||||
OperatorHandle Mop;
|
||||
@@ -335,10 +344,8 @@ void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
|
||||
lin_solver.SetRelTol(rtol); lin_solver.SetAbsTol(0.0);
|
||||
lin_solver.SetMaxIter(100);
|
||||
lin_solver.SetPrintLevel(0);
|
||||
lin_solver.Mult(*RHS, X);
|
||||
lin_solver.Mult(RHS, X);
|
||||
K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
|
||||
|
||||
delete RHS;
|
||||
delete prec;
|
||||
}
|
||||
#endif
|
||||
@@ -493,7 +500,10 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &d_in,
|
||||
|
||||
// Check if the starting mesh (given by x) is inverted. Note that x hasn't
|
||||
// been modified by the Newton update yet.
|
||||
const real_t min_detT_in = ComputeMinDet(d_loc, *fes);
|
||||
const real_t min_detT_in =
|
||||
detJpr_pos_bound ? ComputeDetJptLowerBound(d_loc, *fes)
|
||||
/* */ : ComputeMinDet(d_loc, *fes);
|
||||
|
||||
const bool untangling = (min_detT_in <= 0.0) ? true : false;
|
||||
const real_t untangle_factor = 1.5;
|
||||
if (untangling)
|
||||
@@ -544,7 +554,10 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &d_in,
|
||||
#endif
|
||||
|
||||
// Check the changes in detJ.
|
||||
min_detT_out = ComputeMinDet(d_loc, *fes);
|
||||
min_detT_out =
|
||||
detJpr_pos_bound ? ComputeDetJptLowerBound(d_loc, *fes)
|
||||
/* */ : ComputeMinDet(d_loc, *fes);
|
||||
|
||||
if (untangling == false && min_detT_out <= min_detJ_limit)
|
||||
{
|
||||
// No untangling, and detJ got negative (or small) -- no good.
|
||||
@@ -969,6 +982,37 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &dx) const
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPNewtonSolver::EnsurePositiveDeterminantBound(
|
||||
Mesh &mesh, int ref_factor, int max_recursion_depth)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ParMesh *pmesh = dynamic_cast<ParMesh *>(&mesh))
|
||||
{
|
||||
det_gf = pmesh->GetJacobianDeterminantGF();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
det_gf = mesh.GetJacobianDeterminantGF();
|
||||
}
|
||||
|
||||
// setup the PLBound object for estimating the minima.
|
||||
// note: this must be updated if the mesh is p-refined.
|
||||
int max_order = det_gf->FESpace()->GetMaxElementOrder();
|
||||
det_plb = std::make_unique<PLBound>(det_gf->FESpace(),
|
||||
ref_factor*(max_order+1));
|
||||
plb_rec_depth = max_recursion_depth;
|
||||
detJpr_pos_bound = true;
|
||||
}
|
||||
|
||||
void TMOPNewtonSolver::UpdateDeterminantBoundGridFunction()
|
||||
{
|
||||
if (!det_gf) { return; }
|
||||
|
||||
det_gf->FESpace()->Update();
|
||||
det_gf->Update();
|
||||
}
|
||||
|
||||
real_t TMOPNewtonSolver::ComputeMinDet(const Vector &d_loc,
|
||||
const FiniteElementSpace &fes) const
|
||||
{
|
||||
@@ -1028,6 +1072,60 @@ real_t TMOPNewtonSolver::ComputeMinDet(const Vector &d_loc,
|
||||
return min_detJ;
|
||||
}
|
||||
|
||||
real_t TMOPNewtonSolver::ComputeDetJptLowerBound(const Vector &d_loc,
|
||||
const FiniteElementSpace &fes) const
|
||||
{
|
||||
MFEM_VERIFY(det_gf != nullptr && det_plb != nullptr,
|
||||
"Determinant bounding has not been setup.");
|
||||
FiniteElementSpace *det_fes = det_gf->FESpace();
|
||||
MFEM_VERIFY(!det_fes->IsVariableOrder() && UsesTensorBasis(*det_fes),
|
||||
"Determinant lower bounds require a fixed-order tensor-product "
|
||||
"determinant space.");
|
||||
Array<int> dofs, xdofs;
|
||||
DenseMatrix dshape, Jpr, pos;
|
||||
Vector d_loc_el, detvals;
|
||||
|
||||
for (int e = 0; e < fes.GetNE(); e++)
|
||||
{
|
||||
const FiniteElement *fe = fes.GetFE(e);
|
||||
const int dof = fe->GetDof(), dim = fe->GetDim();
|
||||
dshape.SetSize(dof, dim);
|
||||
Jpr.SetSize(dim);
|
||||
pos.SetSize(dof, dim);
|
||||
Vector posV(pos.Data(), dof * dim);
|
||||
|
||||
x_0.GetElementDofValues(e, posV);
|
||||
if (periodic)
|
||||
{
|
||||
auto n_el = dynamic_cast<const NodalFiniteElement *>(fe);
|
||||
n_el->ReorderLexToNative(dim, posV);
|
||||
}
|
||||
|
||||
fes.GetElementVDofs(e, xdofs);
|
||||
d_loc.GetSubVector(xdofs, d_loc_el);
|
||||
posV += d_loc_el;
|
||||
|
||||
const IntegrationRule &irule = det_fes->GetFE(e)->GetNodes();
|
||||
const int nsp = irule.GetNPoints();
|
||||
detvals.SetSize(nsp);
|
||||
det_fes->GetElementDofs(e, dofs);
|
||||
for (int q = 0; q < nsp; q++)
|
||||
{
|
||||
fe->CalcDShape(irule.IntPoint(q), dshape);
|
||||
MultAtB(pos, dshape, Jpr);
|
||||
detvals(q) = Jpr.Det();
|
||||
}
|
||||
det_gf->SetSubVector(dofs, detvals);
|
||||
}
|
||||
|
||||
auto minbounds = det_gf->EstimateFunctionMinimum(0, *det_plb,
|
||||
plb_rec_depth, 1e-5);
|
||||
|
||||
const DenseMatrix &Wideal =
|
||||
Geometries.GetGeomToPerfGeomJac(fes.GetMesh()->GetTypicalElementGeometry());
|
||||
return minbounds.first/Wideal.Det();
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Metric values are visualized by creating an L2 finite element functions and
|
||||
// computing the metric values at the nodes.
|
||||
|
||||
@@ -204,6 +204,11 @@ protected:
|
||||
// These fields are relevant for mixed meshes.
|
||||
IntegrationRules *IntegRules;
|
||||
int integ_order;
|
||||
// Determinant lower-bound data used by the line search.
|
||||
bool detJpr_pos_bound = false;
|
||||
std::unique_ptr<GridFunction> det_gf;
|
||||
std::unique_ptr<PLBound> det_plb;
|
||||
int plb_rec_depth = 0;
|
||||
|
||||
MemoryType temp_mt = MemoryType::DEFAULT;
|
||||
|
||||
@@ -216,9 +221,16 @@ protected:
|
||||
return ir;
|
||||
}
|
||||
|
||||
/// Compute the minimum det(Jpt) of the trial mesh at quadrature points
|
||||
/// (computes det(Jpr) and scales by the det of ideal target element).
|
||||
real_t ComputeMinDet(const Vector &d_loc,
|
||||
const FiniteElementSpace &fes) const;
|
||||
|
||||
/// Compute a lower bound for det(Jpt) of the trial mesh,
|
||||
/// (computes det(Jpr) and scales by the det of ideal target element).
|
||||
real_t ComputeDetJptLowerBound(const Vector &d_loc,
|
||||
const FiniteElementSpace &fes) const;
|
||||
|
||||
real_t MinDetJpr_2D(const FiniteElementSpace *, const Vector &) const;
|
||||
real_t MinDetJpr_3D(const FiniteElementSpace *, const Vector &) const;
|
||||
|
||||
@@ -261,6 +273,26 @@ public:
|
||||
|
||||
void SetMinDetPtr(real_t *md_ptr) { min_det_ptr = md_ptr; }
|
||||
|
||||
/** @brief Ensure a positive lower bound for the Jacobian determinant in
|
||||
tensor-product elements during line-search.
|
||||
@note The solver creates and updates its own determinant GridFunction
|
||||
from @a mesh while testing trial mesh positions. When @a mesh is a
|
||||
ParMesh, the internal determinant field is a ParGridFunction. The
|
||||
@a ref_factor controls the number of control points used by the PLBound
|
||||
object, and @a max_recursion_depth controls the depth used by the
|
||||
minimum-value estimator.
|
||||
|
||||
The determinant is represented by a high-order GridFunction computed
|
||||
at the mesh nodes. The order is chosen s.t. interpolating the det at
|
||||
some quad point would be equivalent to computing the det directly at the
|
||||
same quad point using the mesh positions.
|
||||
*/
|
||||
void EnsurePositiveDeterminantBound(Mesh &mesh, int ref_factor,
|
||||
int max_recursion_depth = 0);
|
||||
|
||||
/// Update internal determinant GridFunction after a mesh topology change.
|
||||
void UpdateDeterminantBoundGridFunction();
|
||||
|
||||
/// Set the memory type for temporary memory allocations.
|
||||
void SetTempMemoryType(MemoryType mt) { temp_mt = mt; }
|
||||
|
||||
|
||||
+282
-16
@@ -2057,6 +2057,10 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
|
||||
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize())
|
||||
{
|
||||
bool isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
|
||||
bool is_trace_space =
|
||||
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace_.FEColl()) ||
|
||||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace_.FEColl()) ||
|
||||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace_.FEColl()));
|
||||
if (lFESpace_.FEColl() == hFESpace_.FEColl() && !isvar_order)
|
||||
{
|
||||
OperatorPtr P(Operator::ANY_TYPE);
|
||||
@@ -2066,6 +2070,7 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
|
||||
}
|
||||
else if (lFESpace_.GetVDim() == 1
|
||||
&& hFESpace_.GetVDim() == 1
|
||||
&& !is_trace_space
|
||||
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetTypicalFE())
|
||||
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetTypicalFE())
|
||||
&& !isvar_order
|
||||
@@ -2096,15 +2101,245 @@ void TransferOperator::MultTranspose(const Vector& x, Vector& y) const
|
||||
|
||||
|
||||
PRefinementTransferOperator::PRefinementTransferOperator(
|
||||
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_)
|
||||
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_,
|
||||
bool assemble_matrix)
|
||||
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
|
||||
hFESpace(hFESpace_)
|
||||
{
|
||||
isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
|
||||
|
||||
MFEM_VERIFY(lFESpace.FEColl()->GetContType() ==
|
||||
hFESpace.FEColl()->GetContType(),
|
||||
"Incompatible finite element space continuity types.");
|
||||
|
||||
is_trace_space =
|
||||
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace.FEColl()) ||
|
||||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace.FEColl()) ||
|
||||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace.FEColl()));
|
||||
|
||||
if (assemble_matrix) { AssembleMatrix(); }
|
||||
|
||||
}
|
||||
|
||||
void PRefinementTransferOperator::AssembleMatrix()
|
||||
{
|
||||
Mesh* mesh = hFESpace.GetMesh();
|
||||
const int nL = lFESpace.GetVSize();
|
||||
const int nH = hFESpace.GetVSize();
|
||||
|
||||
P.reset(new SparseMatrix(nH, nL));
|
||||
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
|
||||
DenseMatrix loc_prol;
|
||||
|
||||
Geometry::Type cached_geom = Geometry::INVALID;
|
||||
const FiniteElement* h_fe = nullptr;
|
||||
const FiniteElement* l_fe = nullptr;
|
||||
IsoparametricTransformation T;
|
||||
|
||||
int vdim = lFESpace.GetVDim();
|
||||
|
||||
const int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
Vector w(nH); w = 0.0;
|
||||
|
||||
for (int i = 0; i < iend; i++)
|
||||
{
|
||||
if (is_trace_space)
|
||||
{
|
||||
hFESpace.GetFaceDofs(i, h_dofs);
|
||||
lFESpace.GetFaceDofs(i, l_dofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
}
|
||||
|
||||
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
|
||||
: mesh->GetElementBaseGeometry(i);
|
||||
|
||||
if (geom != cached_geom || isvar_order)
|
||||
{
|
||||
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
|
||||
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
|
||||
T.SetIdentityTransformation(h_fe->GetGeomType());
|
||||
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
|
||||
cached_geom = geom;
|
||||
}
|
||||
|
||||
DenseMatrix Aeff(loc_prol);
|
||||
TransformPrimal(doftrans_h, doftrans_l, Aeff);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
DenseMatrix temp_Aeff(Aeff);
|
||||
|
||||
l_dofs.Copy(l_vdofs);
|
||||
lFESpace.DofsToVDofs(vd, l_vdofs);
|
||||
|
||||
h_dofs.Copy(h_vdofs);
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
|
||||
temp_Aeff.AdjustDofDirection(h_vdofs, l_vdofs);
|
||||
|
||||
P->AddSubMatrix(h_vdofs, l_vdofs, temp_Aeff);
|
||||
|
||||
for (int rr = 0; rr < h_vdofs.Size(); rr++)
|
||||
{
|
||||
w(h_vdofs[rr]) += 1.0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
P->Finalize();
|
||||
|
||||
Vector inv_w(nH);
|
||||
for (int i = 0; i < nH; i++)
|
||||
{
|
||||
inv_w(i) = (w(i) > 0.0) ? (1.0 / w(i)) : 1.0;
|
||||
}
|
||||
|
||||
P->ScaleRows(inv_w);
|
||||
|
||||
assembled = true;
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<SparseMatrix>
|
||||
PRefinementTransferOperator::BuildConformingTransferMatrix() const
|
||||
{
|
||||
MFEM_VERIFY(assembled && P, "Matrix path requires assembled P.");
|
||||
|
||||
const SparseMatrix *Pl = lFESpace.GetConformingProlongation();
|
||||
const SparseMatrix *Rh = hFESpace.GetRestrictionMatrix();
|
||||
|
||||
if (Pl && Rh)
|
||||
{
|
||||
SparseMatrix *RhP = mfem::Mult(*Rh, *P);
|
||||
SparseMatrix *RhPPl = mfem::Mult(*RhP, *Pl);
|
||||
delete RhP;
|
||||
return std::unique_ptr<SparseMatrix>(RhPPl);
|
||||
}
|
||||
else if (Pl)
|
||||
{
|
||||
return std::unique_ptr<SparseMatrix>(mfem::Mult(*P, *Pl));
|
||||
}
|
||||
else if (Rh)
|
||||
{
|
||||
return std::unique_ptr<SparseMatrix>(mfem::Mult(*Rh, *P));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_unique<SparseMatrix>(*P);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Operator>
|
||||
PRefinementTransferOperator::BuildConformingTransferOperator() const
|
||||
{
|
||||
const Operator *Pl = lFESpace.GetProlongationMatrix();
|
||||
const Operator *Rh = hFESpace.GetRestrictionOperator();
|
||||
|
||||
if (Pl && Rh)
|
||||
{
|
||||
return std::make_unique<TripleProductOperator>(Rh,
|
||||
const_cast<PRefinementTransferOperator*>(this), Pl,
|
||||
false, false, false);
|
||||
}
|
||||
else if (Pl)
|
||||
{
|
||||
return std::make_unique<ProductOperator>
|
||||
(const_cast<PRefinementTransferOperator*>(this), Pl,
|
||||
false, false);
|
||||
}
|
||||
else if (Rh)
|
||||
{
|
||||
return std::make_unique<ProductOperator>(Rh,
|
||||
const_cast<PRefinementTransferOperator*>(this),
|
||||
false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// return nullptr to mean "identity/no-op wrapper", i.e. use `this`
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Operator *
|
||||
PRefinementTransferOperator::GetTrueTransferOperator()
|
||||
{
|
||||
if (tP) { return tP.get(); }
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParFiniteElementSpace* lpfes = dynamic_cast<const ParFiniteElementSpace*>
|
||||
(&lFESpace);
|
||||
const ParFiniteElementSpace* hpfes = dynamic_cast<const ParFiniteElementSpace*>
|
||||
(&hFESpace);
|
||||
bool parallel = (lpfes) && (hpfes);
|
||||
|
||||
if (parallel)
|
||||
{
|
||||
if (assembled)
|
||||
{
|
||||
HypreParMatrix * Pl = lpfes->Dof_TrueDof_Matrix();
|
||||
const SparseMatrix * Rh = hpfes->GetRestrictionMatrix();
|
||||
// Rh * P
|
||||
SparseMatrix * RhP = mfem::Mult(*Rh, *P);
|
||||
HypreParMatrix * RhPh = new HypreParMatrix(hpfes->GetComm(),
|
||||
hpfes->GlobalTrueVSize(), lpfes->GlobalVSize(),
|
||||
hpfes->GetTrueDofOffsets(), lpfes->GetDofOffsets(), RhP);
|
||||
HypreStealOwnership(*RhPh, *RhP);
|
||||
delete RhP;
|
||||
HypreParMatrix * tmp = ParMult(RhPh, Pl, true);
|
||||
delete RhPh;
|
||||
tP.reset(tmp);
|
||||
return tP.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto Pl = lpfes->GetProlongationMatrix();
|
||||
auto Rh = hpfes->GetRestrictionOperator();
|
||||
tP = std::make_unique<TripleProductOperator>(Rh, this, Pl, false, false, false);
|
||||
return tP.get();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assembled)
|
||||
{
|
||||
auto M = BuildConformingTransferMatrix();
|
||||
tP.reset(M.release());
|
||||
return tP.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
tP = BuildConformingTransferOperator();
|
||||
return tP ? tP.get() : this;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
if (assembled)
|
||||
{
|
||||
auto M = BuildConformingTransferMatrix();
|
||||
tP.reset(M.release());
|
||||
return tP.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
tP = BuildConformingTransferOperator();
|
||||
return tP ? tP.get() : this;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
{
|
||||
y = 0.0;
|
||||
|
||||
if (assembled) { P->Mult(x, y); return; }
|
||||
|
||||
Mesh* mesh = hFESpace.GetMesh();
|
||||
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
|
||||
DenseMatrix loc_prol;
|
||||
@@ -2117,19 +2352,31 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
|
||||
int vdim = lFESpace.GetVDim();
|
||||
|
||||
y = 0.0;
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
const int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
|
||||
|
||||
for (int i = 0; i < iend; i++)
|
||||
{
|
||||
if (is_trace_space)
|
||||
{
|
||||
hFESpace.GetFaceDofs(i, h_dofs);
|
||||
lFESpace.GetFaceDofs(i, l_dofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
}
|
||||
|
||||
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
|
||||
: mesh->GetElementBaseGeometry(i);
|
||||
|
||||
if (geom != cached_geom || isvar_order)
|
||||
{
|
||||
h_fe = hFESpace.GetFE(i);
|
||||
l_fe = lFESpace.GetFE(i);
|
||||
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
|
||||
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
|
||||
T.SetIdentityTransformation(h_fe->GetGeomType());
|
||||
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
|
||||
subY.SetSize(loc_prol.Height());
|
||||
@@ -2144,6 +2391,7 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
x.GetSubVector(l_vdofs, subX);
|
||||
doftrans_l.InvTransformPrimal(subX);
|
||||
|
||||
loc_prol.Mult(subX, subY);
|
||||
doftrans_h.TransformPrimal(subY);
|
||||
y.SetSubVector(h_vdofs, subY);
|
||||
@@ -2156,6 +2404,12 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
{
|
||||
y = 0.0;
|
||||
|
||||
if (assembled)
|
||||
{
|
||||
P->MultTranspose(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh* mesh = hFESpace.GetMesh();
|
||||
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
|
||||
DenseMatrix loc_prol;
|
||||
@@ -2173,16 +2427,28 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
|
||||
|
||||
for (int i = 0; i < iend; i++)
|
||||
{
|
||||
if (is_trace_space)
|
||||
{
|
||||
hFESpace.GetFaceDofs(i, h_dofs);
|
||||
lFESpace.GetFaceDofs(i, l_dofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
}
|
||||
|
||||
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
|
||||
: mesh->GetElementBaseGeometry(i);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom || isvar_order)
|
||||
{
|
||||
h_fe = hFESpace.GetFE(i);
|
||||
l_fe = lFESpace.GetFE(i);
|
||||
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
|
||||
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
|
||||
T.SetIdentityTransformation(h_fe->GetGeomType());
|
||||
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
|
||||
loc_prol.Transpose();
|
||||
|
||||
+27
-4
@@ -569,15 +569,38 @@ private:
|
||||
const FiniteElementSpace& lFESpace;
|
||||
const FiniteElementSpace& hFESpace;
|
||||
bool isvar_order;
|
||||
bool is_trace_space;
|
||||
bool assembled = false;
|
||||
std::unique_ptr<SparseMatrix> P;
|
||||
std::unique_ptr<Operator> tP;
|
||||
|
||||
std::unique_ptr<SparseMatrix> BuildConformingTransferMatrix() const;
|
||||
std::unique_ptr<Operator> BuildConformingTransferOperator() const;
|
||||
|
||||
void AssembleMatrix();
|
||||
|
||||
public:
|
||||
/// @brief Constructs a transfer operator from \p lFESpace to \p hFESpace
|
||||
/// which have different FE collections.
|
||||
/** No matrices are assembled, only the action to a vector is being computed.
|
||||
The underlying finite elements need to implement the GetTransferMatrix
|
||||
methods. */
|
||||
/** By default no matrices are assembled, only the action to a vector is
|
||||
being computed. The underlying finite elements need to implement
|
||||
the GetTransferMatrix methods. */
|
||||
PRefinementTransferOperator(const FiniteElementSpace& lFESpace_,
|
||||
const FiniteElementSpace& hFESpace_);
|
||||
const FiniteElementSpace& hFESpace_,
|
||||
bool assemble_matrix = false);
|
||||
|
||||
/** @brief Return the true-dof transfer operator.
|
||||
|
||||
The returned pointer is non-owning; the operator is either this object
|
||||
or a cached operator owned by this PRefinementTransferOperator. The
|
||||
pointer remains valid until this PRefinementTransferOperator is
|
||||
destroyed and must not be deleted by the caller. */
|
||||
Operator * GetTrueTransferOperator();
|
||||
const Operator * GetTrueTransferOperator() const
|
||||
{
|
||||
return const_cast<PRefinementTransferOperator*>(this)
|
||||
->GetTrueTransferOperator();
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~PRefinementTransferOperator() { }
|
||||
|
||||
@@ -38,15 +38,6 @@
|
||||
#define CUB_IGNORE_DEPRECATED_CPP_DIALECT
|
||||
#define THRUST_IGNORE_DEPRECATED_CPP_DIALECT
|
||||
|
||||
// MFEM only supports using RAJA/CAMP backends in default stream mode because
|
||||
// memory calls are performed outside of the RAJA ecosystem
|
||||
#ifndef CAMP_USE_PLATFORM_DEFAULT_STREAM
|
||||
#define CAMP_USE_PLATFORM_DEFAULT_STREAM 1
|
||||
#else
|
||||
#if !CAMP_USE_PLATFORM_DEFAULT_STREAM
|
||||
#error "MFEM only supports RAJA/CAMP with the default platform stream."
|
||||
#endif
|
||||
#endif
|
||||
#include "RAJA/RAJA.hpp"
|
||||
#if defined(RAJA_ENABLE_CUDA) && !defined(MFEM_USE_CUDA)
|
||||
#error When RAJA is built with CUDA, MFEM_USE_CUDA=YES is required
|
||||
|
||||
+3
-1
@@ -581,7 +581,9 @@ void Device::Setup(const std::string &device_option, const int device_id)
|
||||
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
|
||||
if (Allows(Backend::HIP)) { HipDeviceSetup(dev, ngpu); }
|
||||
if (Allows(Backend::RAJA_CUDA) || Allows(Backend::RAJA_HIP))
|
||||
{ RajaDeviceSetup(dev, ngpu); }
|
||||
{
|
||||
RajaDeviceSetup(dev, ngpu);
|
||||
}
|
||||
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
|
||||
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
|
||||
if (Allows(Backend::CEED_MASK))
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
#include "globals.hpp"
|
||||
#include "mem_manager.hpp"
|
||||
|
||||
#ifdef MFEM_USE_RAJA
|
||||
#include "RAJA/RAJA.hpp"
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace mfem
|
||||
@@ -266,6 +271,18 @@ public:
|
||||
static inline bool Allows(unsigned long b_mask)
|
||||
{ return Get().backends & b_mask; }
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && \
|
||||
(defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP))
|
||||
static inline auto GetRajaResource()
|
||||
{
|
||||
#if defined(RAJA_ENABLE_CUDA)
|
||||
return RAJA::resources::Cuda::CudaFromStream(0, Get().GetId());
|
||||
#elif defined(RAJA_ENABLE_HIP)
|
||||
return RAJA::resources::Hip::HipFromStream(0, Get().GetId());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @brief Get the current Host MemoryType. This is the MemoryType used by
|
||||
most MFEM classes when allocating memory used on the host.
|
||||
*/
|
||||
|
||||
+42
-38
@@ -46,6 +46,8 @@ struct DofQuadLimits_CUDA
|
||||
{
|
||||
static constexpr int MAX_D1D = 14;
|
||||
static constexpr int MAX_Q1D = 14;
|
||||
static constexpr int MAX_D1D_SIMPLEX = 14;
|
||||
static constexpr int MAX_Q1D_SIMPLEX = 14;
|
||||
static constexpr int MAX_T1D = 32;
|
||||
static constexpr int HCURL_MAX_D1D = 5;
|
||||
static constexpr int HCURL_MAX_Q1D = 6;
|
||||
@@ -59,6 +61,8 @@ struct DofQuadLimits_HIP
|
||||
{
|
||||
static constexpr int MAX_D1D = 10;
|
||||
static constexpr int MAX_Q1D = 10;
|
||||
static constexpr int MAX_D1D_SIMPLEX = 9;
|
||||
static constexpr int MAX_Q1D_SIMPLEX = 9;
|
||||
static constexpr int MAX_T1D = 32;
|
||||
static constexpr int HCURL_MAX_D1D = 5;
|
||||
static constexpr int HCURL_MAX_Q1D = 5;
|
||||
@@ -73,9 +77,13 @@ struct DofQuadLimits_CPU
|
||||
#ifndef _WIN32
|
||||
static constexpr int MAX_D1D = 24;
|
||||
static constexpr int MAX_Q1D = 24;
|
||||
static constexpr int MAX_D1D_SIMPLEX = 24;
|
||||
static constexpr int MAX_Q1D_SIMPLEX = 24;
|
||||
#else
|
||||
static constexpr int MAX_D1D = 14;
|
||||
static constexpr int MAX_Q1D = 14;
|
||||
static constexpr int MAX_D1D_SIMPLEX = 14;
|
||||
static constexpr int MAX_Q1D_SIMPLEX = 14;
|
||||
#endif
|
||||
static constexpr int MAX_T1D = 32;
|
||||
static constexpr int HCURL_MAX_D1D = 10;
|
||||
@@ -117,6 +125,8 @@ struct DeviceDofQuadLimits
|
||||
{
|
||||
int MAX_D1D; ///< Maximum number of 1D nodal points.
|
||||
int MAX_Q1D; ///< Maximum number of 1D quadrature points.
|
||||
int MAX_D1D_SIMPLEX; ///< Maximum number of 1D nodal points for simplices.
|
||||
int MAX_Q1D_SIMPLEX; ///< Maximum number of 1D quadrature points for simplices.
|
||||
int HCURL_MAX_D1D; ///< Maximum number of 1D nodal points for H(curl).
|
||||
int HCURL_MAX_Q1D; ///< Maximum number of 1D quadrature points for H(curl).
|
||||
int HDIV_MAX_D1D; ///< Maximum number of 1D nodal points for H(div).
|
||||
@@ -148,6 +158,8 @@ private:
|
||||
{
|
||||
MAX_D1D = T::MAX_D1D;
|
||||
MAX_Q1D = T::MAX_Q1D;
|
||||
MAX_D1D_SIMPLEX = T::MAX_D1D_SIMPLEX;
|
||||
MAX_Q1D_SIMPLEX = T::MAX_Q1D_SIMPLEX;
|
||||
HCURL_MAX_D1D = T::HCURL_MAX_D1D;
|
||||
HCURL_MAX_Q1D = T::HCURL_MAX_Q1D;
|
||||
HDIV_MAX_D1D = T::HDIV_MAX_D1D;
|
||||
@@ -305,8 +317,8 @@ template <typename DBODY>
|
||||
void RajaCuWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS,true>>(RAJA::RangeSegment(0,N),
|
||||
d_body);
|
||||
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS, true> >(
|
||||
Device::GetRajaResource(), RAJA::RangeSegment(0, N), d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
@@ -319,9 +331,9 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_policy>
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
launch<cuda_launch_policy>(Device::GetRajaResource(),
|
||||
LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
@@ -337,7 +349,6 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
@@ -353,9 +364,9 @@ void RajaCuWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
launch<cuda_launch_bounds_policy<LB> >(
|
||||
Device::GetRajaResource(), LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
@@ -378,13 +389,12 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_policy>
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
launch<cuda_launch_policy>(Device::GetRajaResource(),
|
||||
LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
@@ -398,12 +408,10 @@ void RajaCuWrap3DLaunchBounds(const int N, DBODY &&d_body,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
});
|
||||
launch<cuda_launch_bounds_policy<LB> >(
|
||||
Device::GetRajaResource(), LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{ loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body); });
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
@@ -472,8 +480,8 @@ template <typename DBODY>
|
||||
void RajaHipWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true>>(RAJA::RangeSegment(0,N),
|
||||
d_body);
|
||||
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true> >(RAJA::RangeSegment(0,N),
|
||||
d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
@@ -486,9 +494,9 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_policy>
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
launch<hip_launch_policy>(Device::GetRajaResource(),
|
||||
LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
@@ -504,7 +512,6 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
@@ -520,9 +527,9 @@ void RajaHipWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
launch<hip_launch_bounds_policy<LB> >(
|
||||
Device::GetRajaResource(), LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
@@ -545,13 +552,12 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_policy>
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
launch<hip_launch_policy>(Device::GetRajaResource(),
|
||||
LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
@@ -565,12 +571,10 @@ void RajaHipWrap3DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
});
|
||||
launch<hip_launch_bounds_policy<LB> >(
|
||||
Device::GetRajaResource(), LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{ loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body); });
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ list(APPEND SRCS
|
||||
handle.cpp
|
||||
matrix.cpp
|
||||
mma.cpp
|
||||
multivector.cpp
|
||||
ode.cpp
|
||||
operator.cpp
|
||||
ordering.cpp
|
||||
@@ -63,6 +64,7 @@ list(APPEND HDRS
|
||||
linalg.hpp
|
||||
matrix.hpp
|
||||
mma.hpp
|
||||
multivector.hpp
|
||||
ode.hpp
|
||||
operator.hpp
|
||||
ordering.hpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "blockvector.hpp"
|
||||
#include "blockoperator.hpp"
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -129,6 +130,33 @@ void BlockOperator::MultTranspose(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
HypreParMatrix * BlockOperator::GetMonolithicHypreParMatrix() const
|
||||
{
|
||||
Array2D<const HypreParMatrix*> blocks(nRowBlocks, nColBlocks);
|
||||
for (int i = 0; i < nRowBlocks; ++i)
|
||||
{
|
||||
for (int j = 0; j < nColBlocks; ++j)
|
||||
{
|
||||
if (IsZeroBlock(i, j))
|
||||
{
|
||||
blocks(i, j) = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto mat = dynamic_cast<const HypreParMatrix*>(&GetBlock(i, j));
|
||||
MFEM_VERIFY(mat,"BlockOperator block (" << i << "," << j
|
||||
<< ") is not a HypreParMatrix.");
|
||||
blocks(i, j) = mat;
|
||||
}
|
||||
}
|
||||
}
|
||||
Array2D<real_t> coef_mut = coef; // make a non-const copy
|
||||
return HypreParMatrixFromBlocks(blocks, &coef_mut);
|
||||
}
|
||||
#endif
|
||||
|
||||
BlockOperator::~BlockOperator()
|
||||
{
|
||||
if (owns_blocks)
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include "../general/array.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "blockvector.hpp"
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "hypre.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -105,6 +108,15 @@ public:
|
||||
/// Action of the transpose operator
|
||||
void MultTranspose (const Vector & x, Vector & y) const override;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/** @brief Returns a monolithic HypreParMatrix formed by merging the blocks of
|
||||
this BlockOperator, assuming every block is a HypreParMatrix.
|
||||
|
||||
The returned matrix is newly allocated and owned by the caller, who is
|
||||
responsible for deleting it. */
|
||||
HypreParMatrix * GetMonolithicHypreParMatrix() const;
|
||||
#endif
|
||||
|
||||
~BlockOperator();
|
||||
|
||||
//! Controls the ownership of the blocks: if nonzero, BlockOperator will
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "complex_operator.hpp"
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "blockoperator.hpp"
|
||||
#endif
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
@@ -164,6 +167,51 @@ void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ComplexHypreParMatrix * ComplexOperator::AsComplexHypreParMatrix() const
|
||||
{
|
||||
HypreParMatrix *Ar = nullptr;
|
||||
HypreParMatrix *Ai = nullptr;
|
||||
bool own_r = false;
|
||||
bool own_i = false;
|
||||
|
||||
if (auto *Ahr = dynamic_cast<const HypreParMatrix*>(&real()))
|
||||
{
|
||||
Ar = const_cast<HypreParMatrix*>(Ahr);
|
||||
}
|
||||
else if (auto *Br = dynamic_cast<const BlockOperator*>(&real()))
|
||||
{
|
||||
Ar = Br->GetMonolithicHypreParMatrix();
|
||||
own_r = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Real part is neither HypreParMatrix nor BlockOperator.");
|
||||
}
|
||||
|
||||
if (auto *Ahi = dynamic_cast<const HypreParMatrix*>(&imag()))
|
||||
{
|
||||
Ai = const_cast<HypreParMatrix*>(Ahi);
|
||||
}
|
||||
else if (auto *Bi = dynamic_cast<const BlockOperator*>(&imag()))
|
||||
{
|
||||
Ai = Bi->GetMonolithicHypreParMatrix();
|
||||
own_i = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Imag part is neither HypreParMatrix nor BlockOperator.");
|
||||
}
|
||||
|
||||
return new ComplexHypreParMatrix(Ar, Ai, own_r, own_i, GetConvention());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
SparseMatrix & ComplexSparseMatrix::real()
|
||||
{
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
class ComplexHypreParMatrix; // forward declaration
|
||||
#endif
|
||||
|
||||
/** @brief Mimic the action of a complex operator using two real operators.
|
||||
|
||||
@@ -118,6 +121,20 @@ public:
|
||||
|
||||
Convention GetConvention() const { return convention_; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/** @brief Return a newly allocated ComplexHypreParMatrix representation.
|
||||
|
||||
If the real and imaginary parts are HypreParMatrix objects, the returned
|
||||
object borrows them and they must outlive the returned
|
||||
ComplexHypreParMatrix. If they are BlockOperator objects with
|
||||
HypreParMatrix blocks, they are first merged into monolithic matrices
|
||||
owned by the returned ComplexHypreParMatrix.
|
||||
|
||||
The returned ComplexHypreParMatrix is owned by the caller, who is
|
||||
responsible for deleting it. */
|
||||
ComplexHypreParMatrix *AsComplexHypreParMatrix() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Let this be hidden from the public interface since the implementation
|
||||
// depends on internal members
|
||||
|
||||
+40
-9
@@ -15,10 +15,20 @@
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
|
||||
#if CUDSS_VERSION >= 800
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
#define CUDA_REAL_T CUDA_R_32F
|
||||
#define CUDSS_REAL_T CUDSS_R_32F
|
||||
#else
|
||||
#define CUDA_REAL_T CUDA_R_64F
|
||||
#define CUDSS_REAL_T CUDSS_R_64F
|
||||
#endif
|
||||
#define CUDSS_INT_T CUDSS_R_32I
|
||||
#else
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
#define CUDSS_REAL_T CUDA_R_32F
|
||||
#else
|
||||
#define CUDSS_REAL_T CUDA_R_64F
|
||||
#endif
|
||||
#define CUDSS_INT_T CUDA_R_32I
|
||||
#endif
|
||||
|
||||
// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
|
||||
@@ -65,8 +75,13 @@ CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
|
||||
#endif
|
||||
MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
|
||||
|
||||
#if CUDSS_VERSION >= 800
|
||||
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM_HOST,
|
||||
&mpi_comm, sizeof(MPI_Comm *)));
|
||||
#else
|
||||
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
|
||||
&mpi_comm, sizeof(MPI_Comm *)));
|
||||
#endif
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
@@ -257,11 +272,19 @@ void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
|
||||
CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
|
||||
CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
|
||||
|
||||
#if CUDSS_VERSION >= 800
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
|
||||
csr_columns_d, csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
|
||||
mat_type, mview, CUDSS_BASE_ZERO));
|
||||
#else
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
|
||||
csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
|
||||
mat_type, mview, CUDSS_BASE_ZERO));
|
||||
#endif
|
||||
}
|
||||
else // !reorder_reuse
|
||||
{
|
||||
@@ -269,11 +292,19 @@ void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
|
||||
}
|
||||
#if CUDSS_VERSION >= 800
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL, csr_columns,
|
||||
csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL,
|
||||
csr_columns, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
|
||||
mat_type, mview, CUDSS_BASE_ZERO));
|
||||
#else
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL,
|
||||
csr_columns, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
|
||||
mat_type, mview, CUDSS_BASE_ZERO));
|
||||
#endif
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::IsInitialized())
|
||||
@@ -334,10 +365,10 @@ void CuDSSSolver::SetNumRHS(int nrhs_) const
|
||||
}
|
||||
// Create empty RHS and solution vectors
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
|
||||
|
||||
+41
-2
@@ -2158,7 +2158,7 @@ void DenseMatrix::GetFromVector(int offset, const Vector &v)
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
|
||||
void DenseMatrix::AdjustDofDirection(const Array<int> &dofs)
|
||||
{
|
||||
const int n = Height();
|
||||
|
||||
@@ -2169,7 +2169,7 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
|
||||
}
|
||||
#endif
|
||||
|
||||
int *dof = dofs;
|
||||
const int *dof = dofs;
|
||||
for (int i = 0; i < n-1; i++)
|
||||
{
|
||||
const int s = (dof[i] < 0) ? (-1) : (1);
|
||||
@@ -2185,6 +2185,45 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::AdjustDofDirection(Array<int> &row_dofs,
|
||||
Array<int> &col_dofs)
|
||||
{
|
||||
const int nr = row_dofs.Size();
|
||||
const int nc = col_dofs.Size();
|
||||
|
||||
MFEM_VERIFY(Height() == nr && Width() == nc,
|
||||
"DenseMatrix::AdjustDofDirection: size mismatch.");
|
||||
|
||||
// Extract signs and convert to unsigned indices
|
||||
Vector rsign(nr), csign(nc);
|
||||
|
||||
for (int i = 0; i < nr; i++)
|
||||
{
|
||||
const int d = row_dofs[i];
|
||||
if (d >= 0) { rsign(i) = 1.0; }
|
||||
else { rsign(i) = -1.0; row_dofs[i] = -d - 1; continue; }
|
||||
row_dofs[i] = d;
|
||||
}
|
||||
|
||||
for (int j = 0; j < nc; j++)
|
||||
{
|
||||
const int d = col_dofs[j];
|
||||
if (d >= 0) { csign(j) = 1.0; }
|
||||
else { csign(j) = -1.0; col_dofs[j] = -d - 1; continue; }
|
||||
col_dofs[j] = d;
|
||||
}
|
||||
|
||||
// Apply row/column signs
|
||||
for (int i = 0; i < nr; i++)
|
||||
{
|
||||
const real_t rs = rsign(i);
|
||||
for (int j = 0; j < nc; j++)
|
||||
{
|
||||
(*this)(i,j) *= rs * csign(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::SetRow(int row, real_t value)
|
||||
{
|
||||
for (int j = 0; j < Width(); j++)
|
||||
|
||||
+7
-1
@@ -474,7 +474,13 @@ public:
|
||||
void GetFromVector(int offset, const Vector &v);
|
||||
/** If (dofs[i] < 0 and dofs[j] >= 0) or (dofs[i] >= 0 and dofs[j] < 0)
|
||||
then (*this)(i,j) = -(*this)(i,j). */
|
||||
void AdjustDofDirection(Array<int> &dofs);
|
||||
void AdjustDofDirection(const Array<int> &dofs);
|
||||
|
||||
/** If (row_dofs[i] < 0) xor (col_dofs[j] < 0) then
|
||||
(*this)(i,j) = -(*this)(i,j). This method also converts
|
||||
row_dofs/col_dofs to unsigned indices (d -> -d-1). */
|
||||
void AdjustDofDirection(Array<int> &row_dofs,
|
||||
Array<int> &col_dofs);
|
||||
|
||||
/// Replace small entries, abs(a_ij) <= eps, with zero.
|
||||
void Threshold(real_t eps);
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Ginkgo
|
||||
{
|
||||
|
||||
template <typename T> using gko_array = gko::array<T>;
|
||||
#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
|
||||
// for inter-operability with hypre integer types
|
||||
using gko_hypre_int =
|
||||
std::conditional_t<sizeof(HYPRE_Int) == sizeof(std::int32_t), std::int32_t,
|
||||
@@ -50,6 +51,7 @@ static_assert(!std::is_void_v<gko_hypre_int>,
|
||||
"HYPRE_Int type is incompatible with Ginkgo");
|
||||
static_assert(!std::is_void_v<gko_hypre_bigint>,
|
||||
"HYPRE_BigInt type is incompatible with Ginkgo");
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Helper class for a case where a wrapped MFEM Vector
|
||||
|
||||
+29
-3
@@ -2872,8 +2872,8 @@ void HypreParMatrix::Destroy()
|
||||
if (HypreUsingGPU() && ParCSROwner && (diagOwner < 0 || offdOwner < 0))
|
||||
{
|
||||
// Put the "host" or "hypre" pointers in {i,j,data} of A->{diag,offd}, so
|
||||
// that they can be destroyed by hypre when hypre_ParCSRMatrixDestroy(A)
|
||||
// is called below.
|
||||
// that they can be destroyed by mfem_hypre_TFree_host() or hypre when
|
||||
// hypre_ParCSRMatrixDestroy(A) is called below, respectively.
|
||||
|
||||
// Check that if both diagOwner and offdOwner are negative then they have
|
||||
// the same value.
|
||||
@@ -2882,7 +2882,33 @@ void HypreParMatrix::Destroy()
|
||||
|
||||
MemoryClass mc = (diagOwner == -1 || offdOwner == -1) ?
|
||||
Device::GetHostMemoryClass() : GetHypreMemoryClass();
|
||||
Write(mc, diagOwner < 0, offdOwner <0);
|
||||
Write(mc, diagOwner < 0, offdOwner < 0);
|
||||
if (diagOwner == -1)
|
||||
{
|
||||
// Note: mfem_hypre_TFree_host() sets the pointer to NULL.
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixI(A->diag));
|
||||
if (hypre_CSRMatrixOwnsData(A->diag))
|
||||
{
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixJ(A->diag));
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixData(A->diag));
|
||||
}
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
hypre_CSRMatrixMemoryLocation(A->diag) = GetHypreMemoryLocation();
|
||||
#endif
|
||||
}
|
||||
if (offdOwner == -1)
|
||||
{
|
||||
// Note: mfem_hypre_TFree_host() sets the pointer to NULL.
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixI(A->offd));
|
||||
if (hypre_CSRMatrixOwnsData(A->offd))
|
||||
{
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixJ(A->offd));
|
||||
mfem_hypre_TFree_host(hypre_CSRMatrixData(A->offd));
|
||||
}
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
hypre_CSRMatrixMemoryLocation(A->offd) = GetHypreMemoryLocation();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+5
-4
@@ -432,10 +432,11 @@ private:
|
||||
// and A->col_map_offd.
|
||||
// The possible values for diagOwner are:
|
||||
// -1: no special treatment of A->diag (default)
|
||||
// when hypre is built with CUDA support, A->diag owns the "host"
|
||||
// pointers (according to A->diag->owns_data)
|
||||
// -2: used when hypre is built with CUDA support, A->diag owns the "hypre"
|
||||
// pointers (according to A->diag->owns_data)
|
||||
// when hypre is using GPU, A->diag owns the "host" pointers (according
|
||||
// to A->diag->owns_data); these host pointers are freed by MFEM using
|
||||
// hypre's host deallocation macros
|
||||
// -2: used when hypre is using GPU, A->diag owns the "hypre" pointers
|
||||
// (according to A->diag->owns_data)
|
||||
// 0: prevent hypre from destroying A->diag->{i,j,data}
|
||||
// 1: same as 0, plus own the "host" A->diag->{i,j}
|
||||
// 2: same as 0, plus own the "host" A->diag->data
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Linear algebra header file
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "multivector.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "matrix.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "multivector.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
MultiVector::MultiVector(const Array<int> &vector_sizes)
|
||||
{
|
||||
SetSizes(vector_sizes);
|
||||
}
|
||||
|
||||
MultiVector::MultiVector(const Array<int> &vector_sizes, MemoryType mt)
|
||||
{
|
||||
SetSizes(vector_sizes, mt);
|
||||
}
|
||||
|
||||
MultiVector::MultiVector(Vector &base, const Array<int> &vector_sizes)
|
||||
{
|
||||
MakeRef(base, vector_sizes);
|
||||
}
|
||||
|
||||
void MultiVector::SetSizes(const Array<int> &vector_sizes)
|
||||
{
|
||||
blocks.resize(vector_sizes.Size());
|
||||
for (int i = 0; i < vector_sizes.Size(); i++)
|
||||
{
|
||||
operator[](i).SetSize(vector_sizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiVector::SetSizes(const Array<int> &vector_sizes, MemoryType mt)
|
||||
{
|
||||
blocks.resize(vector_sizes.Size());
|
||||
for (int i = 0; i < vector_sizes.Size(); i++)
|
||||
{
|
||||
operator[](i).SetSize(vector_sizes[i], mt);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiVector::MakeRef(Vector &base, const Array<int> &vector_sizes)
|
||||
{
|
||||
blocks.resize(vector_sizes.Size());
|
||||
for (int offset = 0, i = 0; i < vector_sizes.Size(); i++)
|
||||
{
|
||||
blocks[i].emplace<0>(base, offset, vector_sizes[i]);
|
||||
offset += vector_sizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user