Compare commits
331
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
199560d0fe | ||
|
|
ff43a53599 | ||
|
|
539f663fe6 | ||
|
|
104abd56e1 | ||
|
|
68b4445893 | ||
|
|
de9110300d | ||
|
|
cbff87a616 | ||
|
|
139ce00846 | ||
|
|
465459bc94 | ||
|
|
0487599b56 | ||
|
|
a85687d0cb | ||
|
|
03b9eb4b53 | ||
|
|
293acb99d9 | ||
|
|
8c5e3c4e08 | ||
|
|
620b2590a3 | ||
|
|
247818aaa6 | ||
|
|
e5e4a7e753 | ||
|
|
20cb878f5f | ||
|
|
4cc03b4238 | ||
|
|
0843a87d79 | ||
|
|
9c34c32ae6 | ||
|
|
9a12cedd5e | ||
|
|
f9f3f11818 | ||
|
|
b2df200f3d | ||
|
|
6d102971c9 | ||
|
|
ba1e213c19 | ||
|
|
acbe45ea91 | ||
|
|
0cc104331c | ||
|
|
d7e87fda54 | ||
|
|
bd1dbd3754 | ||
|
|
d6924d1b3f | ||
|
|
7a6758a53b | ||
|
|
143b0378e5 | ||
|
|
39b2f27dc2 | ||
|
|
f9e9f5b22e | ||
|
|
882883a650 | ||
|
|
2519a91d01 | ||
|
|
12cc340afb | ||
|
|
88a190e034 | ||
|
|
9f64008a55 | ||
|
|
7127265fde | ||
|
|
f6f702c522 | ||
|
|
29fc4c45b6 | ||
|
|
f6f802e0cd | ||
|
|
0ccc63af04 | ||
|
|
2ec5efc7d5 | ||
|
|
c8a8ab5cba | ||
|
|
aeac01c130 | ||
|
|
a25c9c575b | ||
|
|
2900a6ecd0 | ||
|
|
134780da77 | ||
|
|
c85f81f9b4 | ||
|
|
72331717c7 | ||
|
|
e96e88d543 | ||
|
|
a0172dfeb3 | ||
|
|
2efaaaaffa | ||
|
|
70422ccf14 | ||
|
|
716df18263 | ||
|
|
2337137f33 | ||
|
|
167ff72edc | ||
|
|
3f036b943e | ||
|
|
5565b5066f | ||
|
|
269eb6b7d1 | ||
|
|
2558593206 | ||
|
|
d09ab725b5 | ||
|
|
41bb7c5d4e | ||
|
|
a0166719e6 | ||
|
|
26200dafed | ||
|
|
b8b2d23087 | ||
|
|
ef656f03e9 | ||
|
|
c1c98ca52c | ||
|
|
405a6d2f0d | ||
|
|
ba73a40ce9 | ||
|
|
fa2e6fa2f4 | ||
|
|
a42ee48079 | ||
|
|
bfafa6c97e | ||
|
|
eeb0ede002 | ||
|
|
ef5cc5aa5d | ||
|
|
b8590d7c0f | ||
|
|
bf616ac6ec | ||
|
|
0526a67b92 | ||
|
|
59a950e1c6 | ||
|
|
26fdb711bc | ||
|
|
8d76e7462a | ||
|
|
92710559c0 | ||
|
|
17537c506f | ||
|
|
d9eec2acd8 | ||
|
|
7d5bf6e3e1 | ||
|
|
4d6c90d4fc | ||
|
|
efb97380e3 | ||
|
|
1f7e9f0f23 | ||
|
|
ca612b872f | ||
|
|
f991e44f2e | ||
|
|
3b232cd0c1 | ||
|
|
cecda6b411 | ||
|
|
8145b4c377 | ||
|
|
2c1a39031e | ||
|
|
ded707375a | ||
|
|
be505c9af0 | ||
|
|
4056c529a1 | ||
|
|
23f110f9b7 | ||
|
|
1ed1a29e68 | ||
|
|
f428d03583 | ||
|
|
ddf40b7909 | ||
|
|
21417add4b | ||
|
|
5f68f31a73 | ||
|
|
407a3321aa | ||
|
|
c76cbfa010 | ||
|
|
8a67918a24 | ||
|
|
7f0b52ccc0 | ||
|
|
6298a3ce47 | ||
|
|
000d018d1a | ||
|
|
b938ea0286 | ||
|
|
b773b012c7 | ||
|
|
f1e503e70f | ||
|
|
320bf0a980 | ||
|
|
47008f92fb | ||
|
|
d72cd3f5f7 | ||
|
|
8bb634ffcb | ||
|
|
af6426d240 | ||
|
|
15b9085ad5 | ||
|
|
cbc44b22b6 | ||
|
|
28369b9be2 | ||
|
|
31545fb3d7 | ||
|
|
c0486fec51 | ||
|
|
5df3ef4b9e | ||
|
|
d9b1f2409c | ||
|
|
1441a6e636 | ||
|
|
2fe688deea | ||
|
|
c7856f70ea | ||
|
|
ee8578e22a | ||
|
|
2888c61e59 | ||
|
|
8f59bf3005 | ||
|
|
037855173e | ||
|
|
d6a5eea4e7 | ||
|
|
092b5079fd | ||
|
|
3b325a597c | ||
|
|
8880e17cd5 | ||
|
|
22a0fa48d3 | ||
|
|
98be69f851 | ||
|
|
3f78a06411 | ||
|
|
d40305077c | ||
|
|
1e5602f139 | ||
|
|
bb085f9866 | ||
|
|
e7a7bfe3fb | ||
|
|
76617db79e | ||
|
|
f491fe30c3 | ||
|
|
a1ba9f93c4 | ||
|
|
211a221c50 | ||
|
|
a194cdacce | ||
|
|
3829693441 | ||
|
|
1e91b0bc06 | ||
|
|
e424d758be | ||
|
|
fc070abf6b | ||
|
|
7bac717364 | ||
|
|
5231ec314a | ||
|
|
a33f29549a | ||
|
|
0e48fd5f94 | ||
|
|
946ab2a893 | ||
|
|
b34831a308 | ||
|
|
c753e3bf8e | ||
|
|
f25aa615da | ||
|
|
a303bcdf0a | ||
|
|
f7b7c5388b | ||
|
|
b704fa15b1 | ||
|
|
e533a91560 | ||
|
|
b74049fff1 | ||
|
|
7e9dd26f57 | ||
|
|
621a7842b1 | ||
|
|
7cf96edc2f | ||
|
|
e04fc27673 | ||
|
|
c216a86943 | ||
|
|
3562662d93 | ||
|
|
a13a1bf95c | ||
|
|
654663a221 | ||
|
|
6708f9c19b | ||
|
|
4f0c54ff06 | ||
|
|
a53706b57c | ||
|
|
61e9c368d7 | ||
|
|
a747244a6f | ||
|
|
9b05700cb5 | ||
|
|
27dfa26f0d | ||
|
|
83c11c4ea5 | ||
|
|
09b5b1f184 | ||
|
|
7b64d8739a | ||
|
|
36e2e4d37f | ||
|
|
44d71cfdbf | ||
|
|
4806441a03 | ||
|
|
5ad64b058b | ||
|
|
dd5f50bb40 | ||
|
|
3a8908e6bf | ||
|
|
f53e14016d | ||
|
|
70bb02a0c7 | ||
|
|
091ff0c475 | ||
|
|
583024eaa7 | ||
|
|
1d5df46cdb | ||
|
|
68fabe1cfa | ||
|
|
3c82571b68 | ||
|
|
3388160761 | ||
|
|
c8f6d1ad4f | ||
|
|
7b9d5d0084 | ||
|
|
d41b7f7e83 | ||
|
|
1459c4f5a6 | ||
|
|
1fce1b6306 | ||
|
|
6b33ad4abf | ||
|
|
5f6fda1bf3 | ||
|
|
d0831c2f11 | ||
|
|
cfd1442180 | ||
|
|
0f7529465b | ||
|
|
08715a83cf | ||
|
|
4aaa441d70 | ||
|
|
bba6e27f85 | ||
|
|
4d48ddb566 | ||
|
|
6bdd079b40 | ||
|
|
71c4967e80 | ||
|
|
165f4301d8 | ||
|
|
7d309b3d9e | ||
|
|
7246251b52 | ||
|
|
4d40b7536f | ||
|
|
eb195d1cb3 | ||
|
|
ebddfa7114 | ||
|
|
a5be9f36ed | ||
|
|
1c4602cf64 | ||
|
|
2fd06ce867 | ||
|
|
435d542208 | ||
|
|
bd155a672e | ||
|
|
afacaa7cad | ||
|
|
f9811d774c | ||
|
|
3e0b76a048 | ||
|
|
9dee99fbf1 | ||
|
|
ca01ef44bf | ||
|
|
4d4cf27264 | ||
|
|
246f9bcec1 | ||
|
|
59151dbe58 | ||
|
|
002514c2e0 | ||
|
|
b133ec754e | ||
|
|
23cd7f75bb | ||
|
|
09b67ab18d | ||
|
|
6327127cdc | ||
|
|
d4c102b94c | ||
|
|
bbba4d863f | ||
|
|
9bf2e813b0 | ||
|
|
838ac0cf36 | ||
|
|
7ec56ca61f | ||
|
|
1cd7bfb28d | ||
|
|
6a63b46bd4 | ||
|
|
81aacd2c9e | ||
|
|
2077b076c8 | ||
|
|
93be96fcc0 | ||
|
|
2cb619d9cf | ||
|
|
6f2c580774 | ||
|
|
c0912fe75e | ||
|
|
5ffd03a2da | ||
|
|
ec09fe54e4 | ||
|
|
2a09088fb0 | ||
|
|
e93a3542c0 | ||
|
|
bdcbfafb42 | ||
|
|
b9eaa8c309 | ||
|
|
96a10dfa2e | ||
|
|
e4cebc0f6a | ||
|
|
865b2facc2 | ||
|
|
8877f99da8 | ||
|
|
8a61b3e27b | ||
|
|
54676a17c2 | ||
|
|
f47244859a | ||
|
|
2e6583650f | ||
|
|
2bc6f6146a | ||
|
|
d2b8fa35bb | ||
|
|
1fdfa6d39a | ||
|
|
f89d359d06 | ||
|
|
31ce22595b | ||
|
|
a6270b2582 | ||
|
|
1d84ae004c | ||
|
|
1f35f2580d | ||
|
|
86a297b34f | ||
|
|
a9d6514fb6 | ||
|
|
4d51a907cf | ||
|
|
a10fb1f421 | ||
|
|
afee0acf50 | ||
|
|
1d5ff54f4c | ||
|
|
e20ae257e5 | ||
|
|
7fcd181280 | ||
|
|
d7ae085678 | ||
|
|
e967c8a7ef | ||
|
|
1a6c24c5f2 | ||
|
|
340e3ed188 | ||
|
|
1382ed617a | ||
|
|
4d834ca852 | ||
|
|
c8a8562ea4 | ||
|
|
06032d9340 | ||
|
|
74710220a1 | ||
|
|
6e43e93144 | ||
|
|
0a9b0c681d | ||
|
|
2657585cfe | ||
|
|
c2d96bebba | ||
|
|
b69e30c2ab | ||
|
|
ec011048a0 | ||
|
|
330f67cedc | ||
|
|
6651c80b27 | ||
|
|
3ac9eff225 | ||
|
|
3d399d8eb3 | ||
|
|
216e0dfe08 | ||
|
|
a32ca0cb89 | ||
|
|
2e92b44070 | ||
|
|
2aa374efe8 | ||
|
|
476e642935 | ||
|
|
0da14179f7 | ||
|
|
ff940b8254 | ||
|
|
ec9c967b88 | ||
|
|
966071f5ce | ||
|
|
ff6dc50d7f | ||
|
|
a39f7635f5 | ||
|
|
7d2ce53a63 | ||
|
|
feca2f59dc | ||
|
|
5e296c39c6 | ||
|
|
ec306e7166 | ||
|
|
7588076853 | ||
|
|
fbcc7406a7 | ||
|
|
06b40d8f9e | ||
|
|
944de5d51c | ||
|
|
438fd06639 | ||
|
|
be26f52385 | ||
|
|
94e16907c9 | ||
|
|
dd1fe9e7c3 | ||
|
|
caeecc0e6d | ||
|
|
69f70eca5e | ||
|
|
dbbbf1f443 | ||
|
|
2d39d300d1 | ||
|
|
be3fb993f1 | ||
|
|
6e88331f87 | ||
|
|
0e195e2f87 |
@@ -82,9 +82,9 @@ jobs:
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: optim
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: parallel
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
+10
@@ -51,6 +51,8 @@ examples/ex1[04-9]
|
||||
examples/ex1[0-9]p
|
||||
examples/ex2[0-9]
|
||||
examples/ex2[0-9]p
|
||||
examples/ex30
|
||||
examples/ex30p
|
||||
|
||||
examples/refined.mesh
|
||||
examples/displaced.mesh
|
||||
@@ -223,6 +225,14 @@ miniapps/mtop/ParHeat*
|
||||
miniapps/mtop/seqheat
|
||||
miniapps/mtop/SeqHeat*
|
||||
|
||||
miniapps/autodiff/paradiff
|
||||
miniapps/autodiff/seqadiff
|
||||
miniapps/autodiff/seqtest
|
||||
miniapps/autodiff/par_example
|
||||
miniapps/autodiff/seq_example
|
||||
miniapps/autodiff/seq_test
|
||||
miniapps/autodiff/Exampl*
|
||||
|
||||
miniapps/navier/navier_mms
|
||||
miniapps/navier/navier_kovasznay
|
||||
miniapps/navier/navier_kovasznay_vs
|
||||
|
||||
+36
-4
@@ -29,12 +29,34 @@ stages:
|
||||
|
||||
variables:
|
||||
CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner"
|
||||
USER_CI_TOP_DIR: "${CUSTOM_CI_BUILDS_DIR}/${GITLAB_USER_LOGIN}"
|
||||
SHARED_REPOS_DIR: "${USER_CI_TOP_DIR}/repos"
|
||||
AUTOTEST_ROOT: "${SHARED_REPOS_DIR}"
|
||||
# MFEM_DATA_DIR is setup in '.gitlab/configs/setup-build-and-test.yml' and
|
||||
# used in '.gitlab/configs/<machine>-config.yml':
|
||||
MFEM_DATA_DIR: "${SHARED_REPOS_DIR}/mfem-data"
|
||||
|
||||
# Defines the default choice for updating the saved baseline results. By default
|
||||
# the baseline can only be updated from the master branch. This variable offers
|
||||
# the option to manually ask for rebaselining from another branch if necessary.
|
||||
REBASELINE: "NO"
|
||||
AUTOTEST: "NO"
|
||||
# AUTOTEST_COMMIT: used only when AUTOTEST is set to YES.
|
||||
# * If AUTOTEST_COMMIT is NOT set to NO, reporting jobs will commit their
|
||||
# files to the MFEM/autotest repo.
|
||||
# * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their
|
||||
# files to the MFEM/autotest repo. Instead they will just show the contents
|
||||
# of the report files and remove them.
|
||||
AUTOTEST_COMMIT: "YES"
|
||||
|
||||
# Trigger subpipelines:
|
||||
quartz-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
# pipelines manually or using scheduling
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/quartz-build-and-test.yml
|
||||
strategy: depend
|
||||
@@ -42,7 +64,11 @@ quartz-build-and-test:
|
||||
quartz-baseline:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
# pipelines manually or using scheduling
|
||||
REBASELINE: "${REBASELINE}"
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/quartz-baseline.yml
|
||||
strategy: depend
|
||||
@@ -50,7 +76,10 @@ quartz-baseline:
|
||||
lassen-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
# pipelines manually or using scheduling
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/lassen-build-and-test.yml
|
||||
strategy: depend
|
||||
@@ -58,7 +87,10 @@ lassen-build-and-test:
|
||||
corona-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
# Explicitly pass down values that we want to be able to set when triggering
|
||||
# pipelines manually or using scheduling
|
||||
AUTOTEST: "${AUTOTEST}"
|
||||
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
|
||||
trigger:
|
||||
include: .gitlab/corona-build-and-test.yml
|
||||
strategy: depend
|
||||
|
||||
@@ -18,19 +18,13 @@ variables:
|
||||
# the pipeline, preventing any form of concurrency with other pipelines. This
|
||||
# also means that the BUILD_ROOT directory will never be cleaned.
|
||||
# TODO: add a clean-up mechanism
|
||||
BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${CI_PIPELINE_ID}
|
||||
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
|
||||
|
||||
# On LLNL's quartz, there is only one allocation shared among jobs in order to
|
||||
# save time and resource. This allocation has to be uniquely named so that we
|
||||
# are sure to retrieve it.
|
||||
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
|
||||
|
||||
# Defines the default choice for updating the saved baseline results. By default
|
||||
# the baseline can only be updated from the master branch. This variable offers
|
||||
# the option to manually ask for rebaselining from another branch if necessary.
|
||||
_REBASELINE: "NO"
|
||||
_AUTOTEST: "NO"
|
||||
|
||||
# Git repositories used in the pipeline
|
||||
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
|
||||
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
|
||||
@@ -40,5 +34,3 @@ variables:
|
||||
# Directory used to place artifacts.
|
||||
ARTIFACTS_DIR: artifacts
|
||||
SLURM_OVERLAP: 1
|
||||
|
||||
|
||||
|
||||
@@ -26,17 +26,20 @@ variables:
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_cnone/ || $ON_CORONA != "ON"'
|
||||
when: never
|
||||
# Don’t run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always release resource
|
||||
- if: '$CI_JOB_NAME =~ /release_resource/'
|
||||
when: always
|
||||
# Always cleanup
|
||||
- if: '$CI_JOB_NAME =~ /cleanup/'
|
||||
when: always
|
||||
# Default is to run if previous stage succeeded
|
||||
- when: on_success
|
||||
|
||||
@@ -46,9 +49,11 @@ variables:
|
||||
extends: [.on_corona]
|
||||
stage: build_and_test
|
||||
script:
|
||||
# THREADS is used by 'tests/gitlab/build_and_test', run below
|
||||
- export THREADS=12
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
|
||||
@@ -21,14 +21,17 @@ variables:
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ...
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always cleanup
|
||||
- if: '$CI_JOB_NAME =~ /cleanup/'
|
||||
when: always
|
||||
- when: on_success
|
||||
|
||||
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
|
||||
@@ -39,5 +42,8 @@ variables:
|
||||
extends: [.on_lassen]
|
||||
stage: build_and_test
|
||||
script:
|
||||
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
# Next script uses 'THREADS': leaving it empty --> it uses 'make all -j'
|
||||
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
needs: [setup]
|
||||
|
||||
@@ -22,17 +22,20 @@ variables:
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always release resource
|
||||
- if: '$CI_JOB_NAME =~ /release_resource/'
|
||||
when: always
|
||||
# Always cleanup
|
||||
- if: '$CI_JOB_NAME =~ /cleanup/'
|
||||
when: always
|
||||
# Default is to run if previous stage succeeded
|
||||
- when: on_success
|
||||
|
||||
@@ -42,9 +45,11 @@ variables:
|
||||
extends: [.on_quartz]
|
||||
stage: build_and_test
|
||||
script:
|
||||
# THREADS is used by 'tests/gitlab/build_and_test', run below
|
||||
- export THREADS=12
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
# Jobs report
|
||||
.report_job_success:
|
||||
script:
|
||||
- echo ${MACHINE_NAME}
|
||||
- echo ${AUTOTEST}
|
||||
- echo ${AUTOTEST_COMMIT}
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- cd ${AUTOTEST_ROOT}
|
||||
- |
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/autotest.lock'"
|
||||
date
|
||||
# Report SUCCESS while holding the file lock on 'autotest.lock'.
|
||||
# The next script uses the following environment variables:
|
||||
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
|
||||
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
|
||||
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
|
||||
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_success
|
||||
err=$?
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
.report_job_failure:
|
||||
script:
|
||||
- echo ${MACHINE_NAME}
|
||||
- echo ${AUTOTEST}
|
||||
- echo ${AUTOTEST_COMMIT}
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- cd ${AUTOTEST_ROOT}
|
||||
- |
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/autotest.lock'"
|
||||
date
|
||||
# Report FAILURE while holding the file lock on 'autotest.lock'.
|
||||
# The next script uses the following environment variables:
|
||||
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
|
||||
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
|
||||
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
|
||||
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_failure
|
||||
err=$?
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
@@ -9,13 +9,6 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
|
||||
# when call the runtest script in MFEM test repo.
|
||||
# Note: the value must be consistent with what setup_baseline does.
|
||||
variables:
|
||||
TPLS_DIR: ${BUILD_ROOT}/tpls
|
||||
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_baseline
|
||||
|
||||
# The setup_baseline job doesn't rely on MFEM git repo. It prepares a
|
||||
# pipeline-wide working directory downloading/updating external repos.
|
||||
# TODO:
|
||||
@@ -30,13 +23,50 @@ setup_baseline:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
#
|
||||
# Setup ${BUILD_ROOT}/tpls and ${BUILD_ROOT}/tests:
|
||||
#
|
||||
- echo "MACHINE_NAME = ${MACHINE_NAME}"
|
||||
- echo "REBASELINE = ${REBASELINE}"
|
||||
- echo "AUTOTEST = ${AUTOTEST}"
|
||||
- echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}"
|
||||
- echo "BUILD_ROOT ${BUILD_ROOT}"
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi
|
||||
- if [ ! -d "tests" ]; then git clone ${TESTS_REPO}; fi
|
||||
- cd tpls && git pull && cd ..
|
||||
- cd tests && git pull origin && cd ..
|
||||
#
|
||||
# Setup ${AUTOTEST_ROOT}/autotest:
|
||||
#
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
- command -v flock || echo "Required command 'flock' not found"
|
||||
- |
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/autotest.lock'"
|
||||
date
|
||||
# clone/update the autotest repo while holding the file lock on
|
||||
# 'autotest.lock'
|
||||
err=0
|
||||
if [[ ! -d "autotest" ]]; then
|
||||
git clone ${AUTOTEST_REPO}
|
||||
else
|
||||
cd autotest && git pull && cd ..
|
||||
fi || err=1
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
variables:
|
||||
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_build_and_test
|
||||
|
||||
# setup clones the mfem/data repo in ${BUILD_ROOT}. The build_and_test script
|
||||
# then symlinks the repo to the parent directory of the MFEM source directory.
|
||||
# Unit tests that depend on the mfem/data repo will then detect that this
|
||||
# directory is present and be enabled.
|
||||
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
|
||||
# script then symlinks the repo to the parent directory of the MFEM source
|
||||
# directory. Unit tests that depend on the mfem/data repo will then detect that
|
||||
# this directory is present and be enabled.
|
||||
setup:
|
||||
tags:
|
||||
- shell
|
||||
@@ -24,11 +21,74 @@ setup:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- echo "BUILD_ROOT ${BUILD_ROOT}"
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
|
||||
#
|
||||
# Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml'
|
||||
# and '.gitlab/configs/<machine>-config.yml'
|
||||
#
|
||||
- echo "MACHINE_NAME = ${MACHINE_NAME}"
|
||||
- echo "AUTOTEST = ${AUTOTEST}"
|
||||
- echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}"
|
||||
- echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}"
|
||||
- mkdir -p ${SHARED_REPOS_DIR} && cd ${SHARED_REPOS_DIR}
|
||||
- command -v flock || echo "Required command 'flock' not found"
|
||||
- |
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/mfem-data.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (mfem-data.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/mfem-data.lock'"
|
||||
date
|
||||
# clone/update the mfem/data repo while holding the file lock on
|
||||
# 'mfem-data.lock'
|
||||
err=0
|
||||
if [[ ! -d "mfem-data" ]]; then
|
||||
git clone ${MFEM_DATA_REPO} "mfem-data"
|
||||
else
|
||||
cd "mfem-data" && git pull && cd ..
|
||||
fi || err=1
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> mfem-data.lock
|
||||
#
|
||||
# Setup ${AUTOTEST_ROOT}/autotest:
|
||||
#
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
|
||||
- |
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/autotest.lock'"
|
||||
date
|
||||
# clone/update the autotest repo while holding the file lock on
|
||||
# 'autotest.lock'
|
||||
err=0
|
||||
if [[ ! -d "autotest" ]]; then
|
||||
git clone ${AUTOTEST_REPO}
|
||||
else
|
||||
cd autotest && git pull && cd ..
|
||||
fi || err=1
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
@@ -22,6 +22,7 @@ allocate_resource:
|
||||
extends: .on_corona
|
||||
stage: allocate_resource
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
- salloc --exclusive --nodes=1 --partition=mi60 --time=30 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
needs: [setup]
|
||||
@@ -40,24 +41,27 @@ release_resource:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
|
||||
needs: [rocm_gcc_8.3.1]
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
extends:
|
||||
- .on_corona
|
||||
- .report_job_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
extends:
|
||||
- .on_corona
|
||||
- .report_job_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/corona-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
- local: .gitlab/configs/report-build-and-test.yml
|
||||
|
||||
@@ -21,18 +21,19 @@ opt_mpi_cuda_xl_16_1_1_8:
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
extends:
|
||||
- .on_lassen
|
||||
- .report_job_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
extends:
|
||||
- .on_lassen
|
||||
- .report_job_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/lassen-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
- local: .gitlab/configs/report-build-and-test.yml
|
||||
|
||||
+84
-15
@@ -16,12 +16,26 @@ stages:
|
||||
- setup
|
||||
- baseline_check
|
||||
- baseline_report
|
||||
- cleanup
|
||||
- baseline_publish
|
||||
|
||||
baselinecheck_mfem_intel_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_check
|
||||
variables:
|
||||
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
|
||||
# when call the runtest script in MFEM test repo.
|
||||
# Note: the value must be consistent with the setup performed in
|
||||
# .gitlab/configs/setup-baseline.yml.
|
||||
TPLS_DIR: ${BUILD_ROOT}/tpls
|
||||
script:
|
||||
- echo ${BUILD_ROOT}
|
||||
- echo ${TPLS_DIR}
|
||||
# Used by the tests in MFEM/tests:
|
||||
- export MFEM_TEST_NP=32
|
||||
# The next script uses the following environment variables:
|
||||
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
|
||||
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
|
||||
- .gitlab/scripts/baseline
|
||||
artifacts:
|
||||
when: always
|
||||
@@ -29,33 +43,88 @@ baselinecheck_mfem_intel_quartz:
|
||||
- ${ARTIFACTS_DIR}
|
||||
allow_failure: true
|
||||
|
||||
cleanup:
|
||||
extends: .on_quartz
|
||||
stage: cleanup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- echo "BUILD_ROOT=${BUILD_ROOT}"
|
||||
- rm -rf "${BUILD_ROOT}" || true
|
||||
|
||||
report_baseline:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_report
|
||||
script:
|
||||
- cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
- mkdir -p ${MACHINE_NAME}
|
||||
- rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
|
||||
- rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
|
||||
- cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
|
||||
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
|
||||
- echo ${MACHINE_NAME}
|
||||
- echo ${AUTOTEST}
|
||||
- echo ${AUTOTEST_COMMIT}
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- cd ${AUTOTEST_ROOT}
|
||||
- |
|
||||
if [[ -f ${rundir}/*.err ]]
|
||||
then
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/*.err
|
||||
cp ${rundir}/*.err ${rundir}/autotest-email.html
|
||||
fi
|
||||
- git add ${rundir}
|
||||
- git commit -am "GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} with intel ($(date +%Y-%m-%d))"
|
||||
- git push origin master
|
||||
(
|
||||
date
|
||||
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
|
||||
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
|
||||
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
|
||||
# command to hang indefinitely sometimes, so we use the timeout & retry
|
||||
# as a workaround; we may want to add a counter for the number of
|
||||
# retries to interrupt a potential infinite loop
|
||||
while ! flock -w 5 9; do
|
||||
true
|
||||
done
|
||||
echo "Aquired lock on '$PWD/autotest.lock'"
|
||||
date
|
||||
# ----------------------
|
||||
cd ${AUTOTEST_ROOT}/autotest || \
|
||||
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
|
||||
mkdir -p ${MACHINE_NAME}
|
||||
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
|
||||
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
|
||||
cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
|
||||
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
|
||||
if [[ -f ${rundir}/${BASELINE_TEST}.err ]]; then
|
||||
cp ${rundir}/${BASELINE_TEST}.err ${rundir}/autotest-email.html
|
||||
fi
|
||||
printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \
|
||||
>> ${rundir}/pipeline.txt
|
||||
msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
|
||||
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
|
||||
git pull && \
|
||||
git add ${rundir} && \
|
||||
git commit -m "${msg}" && \
|
||||
git push origin master
|
||||
else
|
||||
for file in ${rundir}/*; do
|
||||
echo "------------------------------"
|
||||
echo "Content of '$file'"
|
||||
echo "******************************"
|
||||
cat $file
|
||||
echo "******************************"
|
||||
done
|
||||
rm -rf ${rundir} || true
|
||||
fi
|
||||
err=$?
|
||||
# ----------------------
|
||||
# sleep for a period to allow NFS to propagate the above changes;
|
||||
# clearly, there is no guarantee that other NFS clients will see the
|
||||
# changes even after the timeout
|
||||
sleep 10
|
||||
exit $err
|
||||
) 9> autotest.lock
|
||||
|
||||
baselinepublish_mfem_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
- if: '$REBASELINE == "YES"'
|
||||
when: manual
|
||||
script:
|
||||
- echo ${BUILD_ROOT}
|
||||
- echo ${PWD}
|
||||
- echo ${ARTIFACTS_DIR}
|
||||
- ls -lA ${ARTIFACTS_DIR}
|
||||
- .gitlab/scripts/rebaseline
|
||||
|
||||
include:
|
||||
|
||||
@@ -22,6 +22,7 @@ allocate_resource:
|
||||
extends: .on_quartz
|
||||
stage: allocate_resource
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
- salloc --exclusive --nodes=1 --partition=pdebug --time=30 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
|
||||
@@ -73,23 +74,26 @@ release_resource:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
extends:
|
||||
- .on_quartz
|
||||
- .report_job_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
extends:
|
||||
- .on_quartz
|
||||
- .report_job_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/quartz-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
- local: .gitlab/configs/report-build-and-test.yml
|
||||
|
||||
@@ -20,7 +20,8 @@ base_out=${base}.out
|
||||
artifacts_path=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}
|
||||
|
||||
# prepare
|
||||
cd ${BUILD_ROOT}
|
||||
cd ${BUILD_ROOT} || \
|
||||
{ echo "Invalid BUILD_ROOT=$BUILD_ROOT"; exit 1; }
|
||||
ln -snf ${CI_PROJECT_DIR} mfem
|
||||
cd tests
|
||||
[[ -d _${BASELINE_TEST} ]] && rm -rf _${BASELINE_TEST}
|
||||
@@ -33,6 +34,9 @@ elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
srun --nodes=1 -t 60 -p mi60 ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
|
||||
lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# post
|
||||
@@ -60,6 +64,10 @@ then
|
||||
cp ${base_out} ${artifacts_path}/${base_out}
|
||||
fi
|
||||
|
||||
if [[ -f ${BASELINE_TEST}.out ]]; then
|
||||
cp ${BASELINE_TEST}.out ${artifacts_path}
|
||||
fi
|
||||
|
||||
# base_diff won't even exist if there is no difference.
|
||||
if [[ -f ${base_diff} ]]
|
||||
then
|
||||
|
||||
@@ -13,20 +13,33 @@
|
||||
|
||||
echo "Runs if there was at least one failure on ${MACHINE_NAME}"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
cd ${AUTOTEST_ROOT}/autotest || \
|
||||
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
|
||||
mkdir -p ${MACHINE_NAME}
|
||||
|
||||
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
|
||||
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
|
||||
|
||||
echo "There was an error while running CI on ${MACHINE_NAME}" > ${rundir}/gitlab.err
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
|
||||
printf "%s\n" "Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED." \
|
||||
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err
|
||||
|
||||
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
|
||||
|
||||
# Create 'autotest-email.html' to indicate failure:
|
||||
cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
|
||||
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
|
||||
git pull && \
|
||||
git add ${rundir} && \
|
||||
git commit -m "${msg}" && \
|
||||
git push origin master
|
||||
else
|
||||
for file in ${rundir}/*; do
|
||||
echo "------------------------------"
|
||||
echo "Content of '$file'"
|
||||
echo "******************************"
|
||||
cat $file
|
||||
echo "******************************"
|
||||
done
|
||||
rm -rf ${rundir} || true
|
||||
fi
|
||||
|
||||
@@ -13,18 +13,30 @@
|
||||
|
||||
echo "Can only run if all the ${MACHINE_NAME} jobs passed"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
cd ${AUTOTEST_ROOT}/autotest || \
|
||||
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
|
||||
mkdir -p ${MACHINE_NAME}
|
||||
|
||||
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
|
||||
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
|
||||
|
||||
echo "The ${MACHINE_NAME} jobs were successful" > ${rundir}/gitlab.out
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
|
||||
printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \
|
||||
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.out
|
||||
|
||||
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
|
||||
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
|
||||
git pull && \
|
||||
git add ${rundir} && \
|
||||
git commit -m "${msg}" && \
|
||||
git push origin master
|
||||
else
|
||||
for file in ${rundir}/*; do
|
||||
echo "------------------------------"
|
||||
echo "Content of '$file'"
|
||||
echo "******************************"
|
||||
cat $file
|
||||
echo "******************************"
|
||||
done
|
||||
rm -rf ${rundir} || true
|
||||
fi
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
|
||||
Version 4.3.1 (development)
|
||||
===========================
|
||||
- Added support for automatic differentiation. Users can select between
|
||||
native implementation and external library implementation at the
|
||||
configuration phase. A parallel and two serial examples are implemented
|
||||
in the autodiff miniapp directory.
|
||||
|
||||
- Added support for mesh preprocessing to resolve fine scale problem data
|
||||
before simulation. This feature uses adaptive mesh refinement to control the
|
||||
associated data oscillation error. See the new Example 30/30p.
|
||||
|
||||
- Switched from Artistic Style (astyle) version 2.05.1 to version 3.1 for code
|
||||
formatting. See the "make style" target.
|
||||
|
||||
@@ -18,6 +27,9 @@ Version 4.3.1 (development)
|
||||
|
||||
- Added support for hr-adaptivity using TMOP-based error estimator.
|
||||
|
||||
- Coefficient::SetTime now propagates the new time into internally stored
|
||||
Coefficient objects.
|
||||
|
||||
- Added initial support for google-benchmarks in the tests/benchmarks directory.
|
||||
It can be enabled with MFEM_USE_BENCHMARK=YES.
|
||||
|
||||
@@ -47,11 +59,26 @@ Version 4.3.1 (development)
|
||||
output format if no physical groups are defined) are now successfully loaded,
|
||||
and elements are reassigned attribute number 1.
|
||||
|
||||
- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the
|
||||
hierarchy of spaces created by the element-based AMG (AMGe) methodology in
|
||||
ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the
|
||||
miniapps/parelag directory for more details.
|
||||
|
||||
- Fixed several MinGW build issues on Windows.
|
||||
|
||||
- Remove the 'u' flag in the ar command, to update all files in the archive,
|
||||
avoiding file name collisions from different subdirectories.
|
||||
|
||||
- Added initial TMOP-based capabilities for surface fitting and tangential
|
||||
relaxation in the mesh-optimizer and pmesh-optimizer miniapps.
|
||||
|
||||
- Added ParMesh Adjaceny Set (adjset) creation support to the Conduit Mesh
|
||||
Blueprint MFEM wrapper functions in ConduitDataCollection.
|
||||
|
||||
- `HypreParVector` and `Vector` now support move semantics, and the copy
|
||||
constructor for `HypreParVector` now copies the local vector data.
|
||||
|
||||
|
||||
Version 4.3, released on July 29, 2021
|
||||
======================================
|
||||
|
||||
@@ -306,7 +333,7 @@ Miscellaneous
|
||||
* HYPRE >= 2.22.0 for CUDA support
|
||||
* libCEED >= 0.8
|
||||
* PETSc >= 3.15.0 for CUDA support
|
||||
* RAJA >= 0.14.0
|
||||
* RAJA >= 0.13.0
|
||||
see INSTALL for more details.
|
||||
|
||||
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
|
||||
@@ -320,11 +347,6 @@ Miscellaneous
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the
|
||||
hierarchy of spaces created by the element-based AMG (AMGe) methodology in
|
||||
ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the
|
||||
miniapps/parelag directory for more details.
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction`
|
||||
|
||||
+17
-2
@@ -252,6 +252,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
|
||||
endif()
|
||||
find_package(OpenMP REQUIRED)
|
||||
set(OPENMP_LIBRARIES ${OpenMP_CXX_LIBRARIES})
|
||||
if(APPLE)
|
||||
# On macOS, the compiler needs additional help to find the <omp.h> header.
|
||||
# See issue #2642 for more information.
|
||||
include_directories(${OpenMP_CXX_INCLUDE_DIRS})
|
||||
endif(APPLE)
|
||||
endif()
|
||||
|
||||
# SuiteSparse (before SUNDIALS which may depend on KLU)
|
||||
@@ -367,6 +372,12 @@ if (MFEM_USE_HIOP)
|
||||
# find_package updates HIOP_FOUND, HIOP_INCLUDE_DIRS, HIOP_LIBRARIES
|
||||
endif()
|
||||
|
||||
# CoDiPack package
|
||||
if (MFEM_USE_CODIPACK)
|
||||
find_package(CODIPACK REQUIRED)
|
||||
# find_package updates CODIPACK_FOUND, CODIPACK_INCLUDE_DIRS, CODIPACK_LIBRARIES
|
||||
endif()
|
||||
|
||||
# OCCA
|
||||
if (MFEM_USE_OCCA)
|
||||
find_package(OCCA REQUIRED)
|
||||
@@ -446,7 +457,7 @@ endif()
|
||||
set(MFEM_TPLS OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
|
||||
SLEPC MESQUITE MUMPS STRUMPACK AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB NETCDF
|
||||
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
|
||||
CUSPARSE MKL_CPARDISO AMGX CALIPER BENCHMARK PARELAG MPI_CXX)
|
||||
CUSPARSE MKL_CPARDISO AMGX CALIPER CODIPACK BENCHMARK PARELAG MPI_CXX)
|
||||
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
@@ -576,7 +587,11 @@ add_custom_target(${MFEM_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
# Create a target for all examples and, optionally, enable it.
|
||||
set(MFEM_ALL_EXAMPLES_TARGET_NAME examples)
|
||||
add_mfem_target(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${MFEM_ENABLE_EXAMPLES})
|
||||
add_subdirectory(examples EXCLUDE_FROM_ALL)
|
||||
if (MFEM_ENABLE_EXAMPLES)
|
||||
add_subdirectory(examples) #install examples if enabled
|
||||
else()
|
||||
add_subdirectory(examples EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# Create a target for all miniapps and, optionally, enable it.
|
||||
set(MFEM_ALL_MINIAPPS_TARGET_NAME miniapps)
|
||||
|
||||
+101
-5
@@ -42,6 +42,7 @@ back to them before issuing pull requests:
|
||||
- [New Feature Development](#new-feature-development)
|
||||
- [Developer Guidelines](#developer-guidelines)
|
||||
- [Pull Requests](#pull-requests)
|
||||
- [MFEM PR Rules](#mfem-pr-rules)
|
||||
- [Pull Request Checklist](#pull-request-checklist)
|
||||
- [Master/Next Workflow](#masternext-workflow)
|
||||
- [Releases](#releases)
|
||||
@@ -67,8 +68,9 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
with regards to documentation and code styling.
|
||||
- Please do not commit large/binary files to the central repository (use a fork
|
||||
instead).
|
||||
- Pull requests should be issued toward `mfem:master`. Make sure
|
||||
to check the items off the [Pull Request Checklist](#pull-request-checklist).
|
||||
- Pull requests should be issued toward `mfem:master`. Make sure
|
||||
to check the items off the [Pull Request Checklist](#pull-request-checklist) and
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
@@ -121,6 +123,7 @@ The MFEM source code has the following structure:
|
||||
├── mesh
|
||||
├── miniapps
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
@@ -326,8 +329,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
change the code by default.
|
||||
|
||||
- Code specifics
|
||||
- All significant new classes, methods and functions have Doxygen-style
|
||||
documentation in source comments.
|
||||
- All new public, protected, and private classes, methods, data members, and
|
||||
functions have Doxygen-style documentation in source comments.
|
||||
- In addition to arguments and functionality, documentation should include the
|
||||
current limitations of the code, any background information that is
|
||||
implicitly assumed in the implementation, and the ownership and lifetime
|
||||
of data.
|
||||
- Consistent code styling is enforced with `make style` in the top-level
|
||||
directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we
|
||||
specifically use version 3.1). See also the file `config/mfem.astylerc`.
|
||||
@@ -335,6 +342,9 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
internal library code. (You can use `std` in examples and miniapps.)
|
||||
- When manually resolving conflicts during a merge, make sure to mention the
|
||||
conflicted files in the commit message.
|
||||
- All significant new features and changes should be documented in CHANGELOG.
|
||||
- New examples and miniapps should have documentation on the MFEM webpage.
|
||||
|
||||
|
||||
### Pull Requests
|
||||
|
||||
@@ -400,6 +410,83 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- If triggered, track the status of the LLNL GitLab tests. If failing, ask
|
||||
one of the _LLNL developers_ for details.
|
||||
|
||||
|
||||
### MFEM PR Rules
|
||||
|
||||
The Pull Request (PR) approval process in MFEM is similar to the approval of papers in a peer-reviewed journal. In particular:
|
||||
|
||||
1. There is an MFEM board of "editors" that evaluates new PRs and assigns "reviewers" for each PR.
|
||||
|
||||
2. The assigned reviewers are responsible to carefully review and test the proposed PR.
|
||||
|
||||
3. A PR can be (manually) merged in the *next* branch only if 2 of the assigned reviewers have approved it and it has passed internal testing. This merge can be performed by any of the assigned reviewers or by any of the editors.
|
||||
|
||||
4. A PR can be merged in the *master* branch only if it has been tested successfully for a week in *next* and an editor has (optionally) taken a final look. This merge can be performed only by one of the editors.
|
||||
|
||||
#### Responsibilities of Editors
|
||||
|
||||
The current list of MFEM editors is:
|
||||
|
||||
- @v-dobrev (Veselin Dobrev)
|
||||
- @tzanio (Tzanio Kolev)
|
||||
- @pazner (Will Pazner)
|
||||
- @mlstowell (Mark Stowell)
|
||||
|
||||
**The responsibilities of the editors are:**
|
||||
|
||||
1. To assign appropriate milestone and labels for new PRs, e.g. *bugfix*, *minor*, *api-change*, *high-impact*, etc.
|
||||
|
||||
2. To assign at least 2 reviewers for new PRs. An editor can also be a reviewer. The editor, reviewers, and author should be listed as "Assignees" on the GitHub PR page. After assignment, the `in-review` label should be added.
|
||||
|
||||
3. To complete the initial PR evaluation and assignments in a timely manner: 1 week from submission.
|
||||
|
||||
4. To assist reviewers when they need help with their reviews (but also to stay out of the way when they don't).
|
||||
|
||||
5. To remind the reviewers about timely completion of their review.
|
||||
|
||||
6. To take a final look and complete the PR merge in *master*. The final look step is optional and shouldn't take more than 3 days.
|
||||
|
||||
7. The assignment of bugfixes should be expedited proportional to their importance, e.g. in some cases the editor can assign much shorter review window.
|
||||
|
||||
#### Responsibilities of Reviewers
|
||||
|
||||
Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their area of expertise.
|
||||
|
||||
**The responsibilities of the reviewers are:**
|
||||
|
||||
1. To let the editors know if the proposed assignment is not a good match for them.
|
||||
|
||||
2. To communicate with the PR author, provide feedback and work with them to resolve issues.
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
Authors should clearly indicate when a PR is ready for review (before that the PR should be marked as `Draft` or `[WIP]`).
|
||||
|
||||
**The responsibilities of the authors are:**
|
||||
|
||||
1. To follow the instructions and PR checklist in the `CONTRIBUTING.md` document in the MFEM repository.
|
||||
|
||||
2. To respond to reviewer feedback in a timely manner.
|
||||
|
||||
3. Authors are encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
4. Authors can use the "Reviewers" section of the GitHub PR page to suggest reviewers, but the "Assignees" section will show who the editor has assigned to do the reviews.
|
||||
|
||||
5. To indicate when the PR is ready for review by adding the `ready-for-review` label.
|
||||
|
||||
|
||||
### Pull Request Checklist
|
||||
|
||||
Before a PR can be merged, it should satisfy the following:
|
||||
@@ -453,7 +540,9 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All significant new classes, methods and functions have Doxygen-style documentation in source comments.
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
- [ ] Any new functions should include descriptions of their intended use e.g. for internal use only, user-facing, etc., along with references to example code whenever possible/appropriate.
|
||||
- [ ] Consider adding new sample runs in existing examples to highlight the new capability.
|
||||
- [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo.
|
||||
- [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*.
|
||||
@@ -464,6 +553,7 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] (LLNL only) After merging:
|
||||
- [ ] Update internal tests to include the new features.
|
||||
|
||||
|
||||
### Master/Next Workflow
|
||||
|
||||
MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
@@ -555,8 +645,10 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
|
||||
|
||||
## LLNL Workflow
|
||||
|
||||
|
||||
### Mirroring on Bitbucket
|
||||
|
||||
- The GitHub `master` and `next` branches are mirrored to the LLNL institutional
|
||||
@@ -576,6 +668,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- `mfem:gh-next` -- Bleeding-edge development version, may be broken, use at
|
||||
your own risk.
|
||||
|
||||
|
||||
### Mirroring on GitLab
|
||||
|
||||
- MFEM repository is also mirrored on the LLNL GitLab instance, in a
|
||||
@@ -598,6 +691,7 @@ In addition, developers can set local git hooks to run some quick checks on
|
||||
commit or push, see the [README](config/githooks/README.md) in the `config/githooks`
|
||||
directory.
|
||||
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
@@ -609,6 +703,7 @@ constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
@@ -618,6 +713,7 @@ build logs at
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
and debug build is performed with a simple run of `ex1` to verify the executable.
|
||||
|
||||
|
||||
### Tests at LLNL
|
||||
|
||||
- We mirror the `master` and `next` branches internally (to `gh-master` and
|
||||
|
||||
@@ -467,6 +467,14 @@ MFEM_USE_HIOP = YES/NO
|
||||
Enable the usage of HiOp (https://github.com/LLNL/hiop) in MFEM. HiOp is an
|
||||
HPC solver for nonlinear optimization problems.
|
||||
|
||||
MFEM_USE_CODIPACK = YES/NO
|
||||
Enable automatic differentiation using the CoDiPack library.
|
||||
www.scicomp.uni-kl.de/codi/
|
||||
|
||||
MFEM_USE_ADFORWARD = YES/NO
|
||||
Enable forward mode for AD packages. This option is valid
|
||||
only if the AD package supports two modes (backward/forward).
|
||||
|
||||
MFEM_USE_CUDA = YES/NO
|
||||
Enables support for CUDA devices in MFEM. CUDA is a parallel computing
|
||||
platform and programming model for general computing on graphical processing
|
||||
@@ -703,6 +711,11 @@ The specific libraries and their options are:
|
||||
Options: HIOP_OPT, HIOP_LIB.
|
||||
Versions: HIOP >= 0.4.6.
|
||||
|
||||
- CoDiPack (optiobal), used with MFEM_USE_CODIPACK = YES
|
||||
URL: https://www.scicomp.uni-kl.de/codi/
|
||||
Options: CODIPACK_OPT
|
||||
Versions: 1.9.3
|
||||
|
||||
- GSLIB (optional), used when MFEM_USE_GSLIB = YES. The gslib library must be
|
||||
built prior to the MFEM build, as follows: download gslib-1.0.7, untar it at
|
||||
the same level as MFEM and create a symbolic link: "ln -s gslib-1.0.7 gslib".
|
||||
@@ -739,10 +752,10 @@ The specific libraries and their options are:
|
||||
Versions: libCEED >= 0.8.
|
||||
|
||||
- RAJA (optional), used when MFEM_USE_RAJA = YES.
|
||||
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
|
||||
Beginning with MFEM v4.3, only RAJA v0.14.0+ is supported.
|
||||
URL: https://github.com/LLNL/RAJA
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 0.13.0.
|
||||
Versions: RAJA >= 0.14.0.
|
||||
|
||||
- Caliper (optional), used when MFEM_USE_CALIPER = YES.
|
||||
URL: https://github.com/LLNL/Caliper
|
||||
@@ -908,6 +921,8 @@ MFEM_USE_MPFR
|
||||
MFEM_USE_ZLIB
|
||||
MFEM_USE_PUMI
|
||||
MFEM_USE_HIOP
|
||||
MFEM_USE_CODIPACK
|
||||
MFEM_USE_ADFORWARD
|
||||
MFEM_USE_CUDA
|
||||
MFEM_USE_OCCA
|
||||
MFEM_USE_CEED
|
||||
@@ -967,6 +982,7 @@ The CMake build system adds auto-detection for the following packages/libraries:
|
||||
- POSIXCLOCKS
|
||||
- PUMI
|
||||
- HIOP
|
||||
- CoDiPack
|
||||
- OCCA
|
||||
- RAJA
|
||||
- UMPIRE
|
||||
|
||||
@@ -54,6 +54,8 @@ set(MFEM_USE_CEED @MFEM_USE_CEED@)
|
||||
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
|
||||
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
|
||||
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
|
||||
set(MFEM_USE_CODIPACK @MFEM_USE_CODIPACK@)
|
||||
set(MFEM_USE_ADFORWARD @MFEM_USE_ADFORWARD@)
|
||||
set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
|
||||
set(MFEM_USE_BENCHMARK @MFEM_USE_BENCHMARK@)
|
||||
set(MFEM_USE_PARELAG @MFEM_USE_PARELAG@)
|
||||
|
||||
@@ -175,6 +175,12 @@
|
||||
// Enable interface to the MKL CPardiso library.
|
||||
#cmakedefine MFEM_USE_MKL_CPARDISO
|
||||
|
||||
// Use forward mode for automatic differentiation
|
||||
#cmakedefine MFEM_USE_ADFORWARD
|
||||
|
||||
// Enable the use of the CoDiPack library for AD
|
||||
#cmakedefine MFEM_USE_CODIPACK
|
||||
|
||||
// Enable MFEM functionality based on the Google Benchmark library.
|
||||
#cmakedefine MFEM_USE_BENCHMARK
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
# Automatic differentiation using the CoDiPack library.
|
||||
# www.scicomp.uni-kl.de/codi/
|
||||
# Sets the following variables:
|
||||
# - CODIPACK_FOUND
|
||||
# - CODIPACK_INCLUDE_DIRS
|
||||
# - CODIPACK_LIBRARIES
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(CODIPACK CODIPACK CODIPACK_DIR
|
||||
"include" "codi.h"
|
||||
"lib" ""
|
||||
"Paths to headers required by CODIPACK."
|
||||
"Libraries required by CODIPACK.")
|
||||
@@ -100,6 +100,8 @@ macro(add_mfem_examples EXE_SRCS)
|
||||
|
||||
string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}")
|
||||
mfem_add_executable(${EXE_NAME} ${SRC_FILE})
|
||||
install(TARGETS ${EXE_NAME}
|
||||
RUNTIME DESTINATION examples)
|
||||
add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME})
|
||||
if (EXE_NEEDED_BY)
|
||||
add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME})
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
// Enable interface to the MKL CPardiso library.
|
||||
// #define MFEM_USE_MKL_CPARDISO
|
||||
|
||||
// Use forward mode for automatic differentiation
|
||||
// #define MFEM_USE_ADFORWARD
|
||||
|
||||
// Enable the use of the CoDiPack library for AD
|
||||
// #define MFEM_USE_CODIPACK
|
||||
|
||||
// Enable functionality based on the Google Benchmark library.
|
||||
// #define MFEM_USE_BENCHMARK
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
|
||||
MFEM_USE_SIMD = @MFEM_USE_SIMD@
|
||||
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
|
||||
MFEM_USE_MKL_CPARDISO = @MFEM_USE_MKL_CPARDISO@
|
||||
MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
|
||||
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
|
||||
MFEM_USE_BENCHMARK = @MFEM_USE_BENCHMARK@
|
||||
MFEM_USE_PARELAG = @MFEM_USE_PARELAG@
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
|
||||
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
|
||||
option(MFEM_USE_CALIPER "Enable Caliper support" OFF)
|
||||
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
|
||||
option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
|
||||
option(MFEM_USE_CODIPACK "Enable automatic differentiation (AD) using CoDiPack" OFF)
|
||||
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
|
||||
option(MFEM_USE_PARELAG "Enable ParELAG" OFF)
|
||||
|
||||
@@ -243,6 +245,9 @@ set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
|
||||
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
|
||||
set(LAPACK_LIBRARIES "" CACHE STRING "The LAPACK library.")
|
||||
|
||||
set(CODIPACK_INCLUDE_DIRS "${MFEM_DIR}/../CoDiPack/inlude" CACHE STRING "Path to CoDiPack headers.")
|
||||
set(CODIPACK_LIBRARIES "")
|
||||
|
||||
# Some useful variables:
|
||||
set(CMAKE_SKIP_PREPROCESSED_SOURCE_RULES ON) # Skip *.i rules
|
||||
set(CMAKE_SKIP_ASSEMBLY_SOURCE_RULES ON) # Skip *.s rules
|
||||
|
||||
@@ -59,6 +59,9 @@ HIP_FLAGS = --amdgpu-target=$(HIP_ARCH)
|
||||
HIP_XCOMPILER =
|
||||
HIP_XLINKER = -Wl,
|
||||
|
||||
# Flags for generating dependencies.
|
||||
DEP_FLAGS = -MM -MT
|
||||
|
||||
ifneq ($(NOTMAC),)
|
||||
AR = ar
|
||||
ARFLAGS = crv
|
||||
@@ -86,6 +89,9 @@ else
|
||||
BUILD_RPATH = $(XLINKER)-undefined,dynamic_lookup
|
||||
INSTALL_SOFLAGS = $(subst $1 ,,$(call MAKE_SOFLAGS,$(MFEM_LIB_DIR)))
|
||||
INSTALL_RPATH = $(XLINKER)-undefined,dynamic_lookup
|
||||
# Silence unused command line argument warnings when generating dependencies
|
||||
# with mpicxx and clang
|
||||
DEP_FLAGS := -Wno-unused-command-line-argument $(DEP_FLAGS)
|
||||
endif
|
||||
|
||||
# Set CXXFLAGS to overwrite the default selection of DEBUG_FLAGS/OPTIM_FLAGS
|
||||
@@ -151,6 +157,8 @@ MFEM_USE_UMPIRE = NO
|
||||
MFEM_USE_SIMD = NO
|
||||
MFEM_USE_ADIOS2 = NO
|
||||
MFEM_USE_MKL_CPARDISO = NO
|
||||
MFEM_USE_ADFORWARD = NO
|
||||
MFEM_USE_CODIPACK = NO
|
||||
MFEM_USE_BENCHMARK = NO
|
||||
MFEM_USE_PARELAG = NO
|
||||
|
||||
@@ -408,6 +416,11 @@ HIOP_DIR = @MFEM_DIR@/../hiop/install
|
||||
HIOP_OPT = -I$(HIOP_DIR)/include
|
||||
HIOP_LIB = -L$(HIOP_DIR)/lib -lhiop $(LAPACK_LIB)
|
||||
|
||||
# CoDiPack
|
||||
CODIPACK_DIR = @MFEM_DIR@/../CoDiPack
|
||||
CODIPACK_OPT = -I$(CODIPACK_DIR)
|
||||
CODIPACK_LIB =
|
||||
|
||||
# GSLIB library
|
||||
GSLIB_DIR = @MFEM_DIR@/../gslib/build
|
||||
GSLIB_OPT = -I$(GSLIB_DIR)/include
|
||||
|
||||
@@ -781,6 +781,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/gslib \
|
||||
@MFEM_SOURCE_DIR@/miniapps/meshing \
|
||||
@MFEM_SOURCE_DIR@/miniapps/mtop \
|
||||
@MFEM_SOURCE_DIR@/miniapps/autodiff \
|
||||
@MFEM_SOURCE_DIR@/miniapps/navier \
|
||||
@MFEM_SOURCE_DIR@/miniapps/nurbs \
|
||||
@MFEM_SOURCE_DIR@/miniapps/performance \
|
||||
|
||||
@@ -194,6 +194,8 @@ namespace mfem {
|
||||
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="par__example_8cpp_source.html">Parallel pLaplacian example using AD</a>: Parallel pLaplacian example
|
||||
* - <a class="el" href="seq__example_8cpp_source.html">Serial pLaplacian example using AD</a>: Serial pLaplacian example
|
||||
*
|
||||
* See also the <a class="el" href="https://mfem.org/examples/">examples documentation</a> online.
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex27.cpp
|
||||
ex28.cpp
|
||||
ex29.cpp
|
||||
ex30.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -70,6 +71,7 @@ if (MFEM_USE_MPI)
|
||||
ex27p.cpp
|
||||
ex28p.cpp
|
||||
ex29p.cpp
|
||||
ex30p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+2
-2
@@ -135,8 +135,8 @@ int main(int argc, char *argv[])
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
|
||||
+4
-4
@@ -199,8 +199,8 @@ int main(int argc, char *argv[])
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
@@ -221,7 +221,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
HyprePCG pcg(*A);
|
||||
pcg.SetTol(1e-12);
|
||||
pcg.SetMaxIter(200);
|
||||
pcg.SetMaxIter(500);
|
||||
pcg.SetPrintLevel(2);
|
||||
pcg.SetPreconditioner(*amg);
|
||||
pcg.Mult(*B, *X);
|
||||
@@ -232,7 +232,7 @@ int main(int argc, char *argv[])
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetRelTol(1e-12);
|
||||
gmres.SetMaxIter(200);
|
||||
gmres.SetMaxIter(500);
|
||||
gmres.SetKDim(10);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetOperator(*A);
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
// MFEM Example 30
|
||||
//
|
||||
// Compile with: make ex30
|
||||
//
|
||||
// Sample runs: ex30 -m ../data/square-disc.mesh -o 1
|
||||
// ex30 -m ../data/square-disc.mesh -o 2
|
||||
// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3
|
||||
// ex30 -m ../data/square-disc-nurbs.mesh -o 2
|
||||
// ex30 -m ../data/star.mesh -o 2 -eo 4
|
||||
// ex30 -m ../data/fichera.mesh -o 2 -me 1e4
|
||||
// ex30 -m ../data/disc-nurbs.mesh -o 2
|
||||
// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4
|
||||
// ex30 -m ../data/star-surf.mesh -o 2
|
||||
// ex30 -m ../data/square-disc-surf.mesh -o 2
|
||||
// ex30 -m ../data/amr-quad.mesh -l 2
|
||||
//
|
||||
// Description: This is an example of adaptive mesh refinement preprocessing
|
||||
// which lowers the data oscillation [1] to a user-defined
|
||||
// relative threshold. There is no PDE being solved.
|
||||
//
|
||||
// MFEM's capability to work with both conforming and
|
||||
// nonconforming meshes is demonstrated in example 6. In some
|
||||
// problems, the material data or loading data is not sufficiently
|
||||
// resolved on the initial mesh. This missing fine scale data
|
||||
// reduces the accuracy of the solution as well as the accuracy
|
||||
// of some local error estimators. By preprocessing the mesh
|
||||
// before solving the PDE, many issues can be avoided.
|
||||
//
|
||||
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
|
||||
// Data oscillation and convergence of adaptive FEM. SIAM
|
||||
// Journal on Numerical Analysis, 38(2), 466-488.
|
||||
//
|
||||
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
|
||||
// problems for testing adaptive grid refinement algorithms.
|
||||
// Applied mathematics and computation, 220, 350-364.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Piecewise-affine function which is sometimes mesh-conforming
|
||||
double affine_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
if (x < 0.0)
|
||||
{
|
||||
return 1.0 + x + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Piecewise-constant function which is never mesh-conforming
|
||||
double jump_function(const Vector &p)
|
||||
{
|
||||
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
|
||||
return 5.0;
|
||||
}
|
||||
|
||||
// Singular function derived from the Laplacian of the "steep wavefront"
|
||||
// problem in [2].
|
||||
double singular_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
double alpha = 1000.0;
|
||||
double xc = 0.75, yc = 0.5;
|
||||
double r0 = 0.7;
|
||||
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
|
||||
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
|
||||
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
|
||||
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
|
||||
denom = max(denom,1e-8);
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int nc_limit = 1;
|
||||
int max_elems = 1e5;
|
||||
double double_max_elems = double(max_elems);
|
||||
bool visualization = true;
|
||||
double osc_threshold = 1e-3;
|
||||
int enriched_order = 5;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&nc_limit, "-l", "--nc-limit",
|
||||
"Maximum level of hanging nodes.");
|
||||
args.AddOption(&double_max_elems, "-me", "--max-elems",
|
||||
"Stop after reaching this many elements.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&osc_threshold, "-e", "--error",
|
||||
"relative data oscillation threshold.");
|
||||
args.AddOption(&enriched_order, "-eo", "--enriched_order",
|
||||
"Enriched quadrature order.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
max_elems = int(double_max_elems);
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// convert it to a piecewise-polynomial curved mesh. First we refine the
|
||||
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
mesh.SetCurvature(2);
|
||||
}
|
||||
|
||||
// 3. Define functions and refiner.
|
||||
FunctionCoefficient affine_coeff(affine_function);
|
||||
FunctionCoefficient jump_coeff(jump_function);
|
||||
FunctionCoefficient singular_coeff(singular_function);
|
||||
CoefficientRefiner coeffrefiner(affine_coeff, order);
|
||||
|
||||
// 4. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost, visport);
|
||||
}
|
||||
|
||||
// 5. Define custom integration rule (optional).
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
int order_quad = 2*order + enriched_order;
|
||||
for (int i = 0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
// 6. Apply custom refiner settings.
|
||||
coeffrefiner.SetIntRule(irs);
|
||||
coeffrefiner.SetMaxElements(max_elems);
|
||||
coeffrefiner.SetThreshold(osc_threshold);
|
||||
coeffrefiner.SetNCLimit(nc_limit);
|
||||
coeffrefiner.PrintWarnings();
|
||||
|
||||
// 7. Preprocess mesh to control osc (piecewise-affine function).
|
||||
// This is mostly just a verification check. The oscillation should
|
||||
// be zero if the function is mesh-conforming and order > 0.
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 0 (affine) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
// 8. Preprocess mesh to control osc (jump function).
|
||||
coeffrefiner.ResetCoefficient(jump_coeff);
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 1 (discontinuous) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
// 9. Preprocess mesh to control osc (singular function).
|
||||
coeffrefiner.ResetCoefficient(singular_coeff);
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 2 (singular) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "mesh\n" << mesh << flush;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
// MFEM Example 30 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex30p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -me 1e3
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/star.mesh -o 2 -eo 4
|
||||
// mpirun -np 4 oscp -m ../data/fichera.mesh -o 2 -me 1e4
|
||||
// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2
|
||||
// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/amr-quad.mesh -l 2
|
||||
//
|
||||
// Description: This is an example of adaptive mesh refinement preprocessing
|
||||
// which lowers the data oscillation [1] to a user-defined
|
||||
// relative threshold. There is no PDE being solved.
|
||||
//
|
||||
// MFEM's capability to work with both conforming and
|
||||
// nonconforming meshes is demonstrated in example 6. In some
|
||||
// problems, the material data or loading data is not sufficiently
|
||||
// resolved on the initial mesh. This missing fine scale data
|
||||
// reduces the accuracy of the solution as well as the accuracy
|
||||
// of some local error estimators. By preprocessing the mesh
|
||||
// before solving the PDE, many issues can be avoided.
|
||||
//
|
||||
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
|
||||
// Data oscillation and convergence of adaptive FEM. SIAM
|
||||
// Journal on Numerical Analysis, 38(2), 466-488.
|
||||
//
|
||||
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
|
||||
// problems for testing adaptive grid refinement algorithms.
|
||||
// Applied mathematics and computation, 220, 350-364.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Piecewise-affine function which is sometimes mesh-conforming
|
||||
double affine_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
if (x < 0.0)
|
||||
{
|
||||
return 1.0 + x + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Piecewise-constant function which is never mesh-conforming
|
||||
double jump_function(const Vector &p)
|
||||
{
|
||||
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
|
||||
return 5.0;
|
||||
}
|
||||
|
||||
// Singular function derived from the Laplacian of the "steep wavefront"
|
||||
// problem in [2].
|
||||
double singular_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
double alpha = 1000.0;
|
||||
double xc = 0.75, yc = 0.5;
|
||||
double r0 = 0.7;
|
||||
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
|
||||
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
|
||||
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
|
||||
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
|
||||
denom = max(denom,1e-8);
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 0. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int nc_limit = 1;
|
||||
int max_elems = 1e5;
|
||||
double double_max_elems = double(max_elems);
|
||||
bool visualization = true;
|
||||
bool nc_simplices = true;
|
||||
double osc_threshold = 1e-3;
|
||||
int enriched_order = 5;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&nc_limit, "-l", "--nc-limit",
|
||||
"Maximum level of hanging nodes.");
|
||||
args.AddOption(&double_max_elems, "-me", "--max-elems",
|
||||
"Stop after reaching this many elements.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&osc_threshold, "-e", "--error",
|
||||
"relative data oscillation threshold.");
|
||||
args.AddOption(&enriched_order, "-eo", "--enriched_order",
|
||||
"Enriched quadrature order.");
|
||||
args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices",
|
||||
"-cs", "--conforming-simplices",
|
||||
"For simplicial meshes, enable/disable nonconforming"
|
||||
" refinement");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
max_elems = int(double_max_elems);
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// convert it to a piecewise-polynomial curved mesh. First we refine the
|
||||
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
mesh.SetCurvature(2);
|
||||
}
|
||||
|
||||
// 3. Make sure the mesh is in the non-conforming mode to enable local
|
||||
// refinement of quadrilaterals/hexahedra. Simplices can be refined
|
||||
// either in conforming or in non-conforming mode. The conforming
|
||||
// mode however does not support dynamic partitioning.
|
||||
mesh.EnsureNCMesh(nc_simplices);
|
||||
|
||||
// 4. Define a parallel mesh by partitioning the serial mesh.
|
||||
// Once the parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// 5. Define functions and refiner.
|
||||
FunctionCoefficient affine_coeff(affine_function);
|
||||
FunctionCoefficient jump_coeff(jump_function);
|
||||
FunctionCoefficient singular_coeff(singular_function);
|
||||
CoefficientRefiner coeffrefiner(affine_coeff,order);
|
||||
|
||||
// 6. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost, visport);
|
||||
}
|
||||
|
||||
// 7. Define custom integration rule (optional).
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
int order_quad = 2*order + enriched_order;
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
// 8. Apply custom refiner settings.
|
||||
coeffrefiner.SetIntRule(irs);
|
||||
coeffrefiner.SetMaxElements(max_elems);
|
||||
coeffrefiner.SetThreshold(osc_threshold);
|
||||
coeffrefiner.SetNCLimit(nc_limit);
|
||||
coeffrefiner.PrintWarnings();
|
||||
|
||||
// 9. Preprocess mesh to control osc (piecewise-affine function).
|
||||
// This is mostly just a verification check. The oscillation should
|
||||
// be zero if the function is mesh-conforming and order > 0.
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
int globalNE = pmesh.GetGlobalNE();
|
||||
double osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 0 (affine) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
// 10. Preprocess mesh to control osc (jump function).
|
||||
coeffrefiner.ResetCoefficient(jump_coeff);
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
globalNE = pmesh.GetGlobalNE();
|
||||
osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 1 (discontinuous) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
// 11. Preprocess mesh to control osc (singular function).
|
||||
coeffrefiner.ResetCoefficient(singular_coeff);
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
globalNE = pmesh.GetGlobalNE();
|
||||
osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 2 (singular) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock << "mesh\n" << pmesh << flush;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
+2
-2
@@ -22,10 +22,10 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \
|
||||
ex24p ex25p ex26p
|
||||
|
||||
@@ -969,6 +969,7 @@ void BilinearForm::EliminateVDofs(const Array<int> &vdofs,
|
||||
const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy)
|
||||
{
|
||||
vdofs.HostRead();
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
int vdof = vdofs[i];
|
||||
|
||||
+75
-31
@@ -985,6 +985,8 @@ void DiffusionIntegrator::ComputeElementFlux
|
||||
"Unexpected height for MatrixCoefficient");
|
||||
}
|
||||
|
||||
MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here");
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix dshape(nd,dim), invdfdx(dim, spaceDim);
|
||||
DenseMatrix M(MQ ? spaceDim : 0);
|
||||
@@ -997,7 +999,7 @@ void DiffusionIntegrator::ComputeElementFlux
|
||||
#endif
|
||||
vec.SetSize(dim);
|
||||
vecdxt.SetSize(spaceDim);
|
||||
pointflux.SetSize(MQ ? spaceDim : 0);
|
||||
pointflux.SetSize(MQ || VQ ? spaceDim : 0);
|
||||
|
||||
const IntegrationRule &ir = fluxelem.GetNodes();
|
||||
fnd = ir.GetNPoints();
|
||||
@@ -1013,36 +1015,45 @@ void DiffusionIntegrator::ComputeElementFlux
|
||||
CalcInverse(Trans.Jacobian(), invdfdx);
|
||||
invdfdx.MultTranspose(vec, vecdxt);
|
||||
|
||||
if (!MQ && !VQ)
|
||||
if (with_coef)
|
||||
{
|
||||
if (Q && with_coef)
|
||||
if (!MQ && !VQ)
|
||||
{
|
||||
vecdxt *= Q->Eval(Trans,ip);
|
||||
if (Q)
|
||||
{
|
||||
vecdxt *= Q->Eval(Trans,ip);
|
||||
}
|
||||
for (j = 0; j < spaceDim; j++)
|
||||
{
|
||||
flux(fnd*j+i) = vecdxt(j);
|
||||
}
|
||||
}
|
||||
for (j = 0; j < spaceDim; j++)
|
||||
else
|
||||
{
|
||||
flux(fnd*j+i) = vecdxt(j);
|
||||
if (MQ)
|
||||
{
|
||||
MQ->Eval(M, Trans, ip);
|
||||
M.Mult(vecdxt, pointflux);
|
||||
}
|
||||
else
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
for (int j=0; j<spaceDim; ++j)
|
||||
{
|
||||
pointflux[j] = D[j] * vecdxt[j];
|
||||
}
|
||||
}
|
||||
for (j = 0; j < spaceDim; j++)
|
||||
{
|
||||
flux(fnd*j+i) = pointflux(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MQ)
|
||||
{
|
||||
MQ->Eval(M, Trans, ip);
|
||||
M.Mult(vecdxt, pointflux);
|
||||
}
|
||||
else
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
for (int j=0; j<spaceDim; ++j)
|
||||
{
|
||||
pointflux[j] = D[j] * vecdxt[j];
|
||||
}
|
||||
|
||||
}
|
||||
for (j = 0; j < spaceDim; j++)
|
||||
{
|
||||
flux(fnd*j+i) = pointflux(j);
|
||||
flux(fnd*j+i) = vecdxt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1050,7 +1061,7 @@ void DiffusionIntegrator::ComputeElementFlux
|
||||
|
||||
double DiffusionIntegrator::ComputeFluxEnergy
|
||||
( const FiniteElement &fluxelem, ElementTransformation &Trans,
|
||||
Vector &flux, Vector* d_energy)
|
||||
Vector &flux, bool with_coef, Vector* d_energy)
|
||||
{
|
||||
int nd = fluxelem.GetDof();
|
||||
int dim = fluxelem.GetDim();
|
||||
@@ -1058,8 +1069,13 @@ double DiffusionIntegrator::ComputeFluxEnergy
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix M;
|
||||
Vector D(VQ ? VQ->GetVDim() : 0);
|
||||
#else
|
||||
D.SetSize(VQ ? VQ->GetVDim() : 0);
|
||||
#endif
|
||||
|
||||
MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here");
|
||||
|
||||
shape.SetSize(nd);
|
||||
pointflux.SetSize(spaceDim);
|
||||
if (d_energy) { vec.SetSize(spaceDim); }
|
||||
@@ -1088,16 +1104,42 @@ double DiffusionIntegrator::ComputeFluxEnergy
|
||||
Trans.SetIntPoint(&ip);
|
||||
double w = Trans.Weight() * ip.weight;
|
||||
|
||||
if (!MQ)
|
||||
if (MQ)
|
||||
{
|
||||
double e = (pointflux * pointflux);
|
||||
if (Q) { e *= Q->Eval(Trans, ip); }
|
||||
energy += w * e;
|
||||
MQ->Eval(M, Trans, ip);
|
||||
if (with_coef) { M.Invert(); }
|
||||
energy += w * M.InnerProduct(pointflux, pointflux);
|
||||
}
|
||||
else if (VQ)
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
if (with_coef)
|
||||
{
|
||||
Vector Dinv(D.Size());
|
||||
Dinv = 1.0;
|
||||
Dinv /= D;
|
||||
D = Dinv;
|
||||
}
|
||||
|
||||
D *= pointflux;
|
||||
|
||||
energy += w * (D * pointflux);
|
||||
}
|
||||
else
|
||||
{
|
||||
MQ->Eval(M, Trans, ip);
|
||||
energy += w * M.InnerProduct(pointflux, pointflux);
|
||||
double e = (pointflux * pointflux);
|
||||
if (Q)
|
||||
{
|
||||
if (with_coef)
|
||||
{
|
||||
e /= Q->Eval(Trans, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
e *= Q->Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
energy += w * e;
|
||||
}
|
||||
|
||||
if (d_energy)
|
||||
@@ -1108,7 +1150,7 @@ double DiffusionIntegrator::ComputeFluxEnergy
|
||||
{
|
||||
(*d_energy)[k] += w * vec[k] * vec[k];
|
||||
}
|
||||
// TODO: Q, MQ
|
||||
// TODO: Q, VQ, MQ
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1936,7 +1978,8 @@ void CurlCurlIntegrator
|
||||
|
||||
double CurlCurlIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy)
|
||||
Vector &flux, bool with_coef,
|
||||
Vector *d_energy)
|
||||
{
|
||||
int nd = fluxelem.GetDof();
|
||||
int dim = fluxelem.GetDim();
|
||||
@@ -2829,7 +2872,8 @@ void ElasticityIntegrator::ComputeElementFlux(
|
||||
|
||||
double ElasticityIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy)
|
||||
Vector &flux, bool with_coef,
|
||||
Vector *d_energy)
|
||||
{
|
||||
const int dof = fluxelem.GetDof();
|
||||
const int dim = fluxelem.GetDim();
|
||||
|
||||
+30
-9
@@ -234,6 +234,8 @@ public:
|
||||
position of the mesh element.
|
||||
@param[in] flux "Flux" coefficients representing the expansion of the
|
||||
"flux" function in the basis of @a fluxelem.
|
||||
@param[in] wcoef If true, @a flux includes the coefficient of this
|
||||
integrator.
|
||||
@param[out] d_energy If not NULL, the given Vector should be set to
|
||||
represent directional energy split that can be used
|
||||
for anisotropic error estimation.
|
||||
@@ -241,7 +243,8 @@ public:
|
||||
*/
|
||||
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy = NULL)
|
||||
Vector &flux, bool wcoef = true,
|
||||
Vector *d_energy = NULL)
|
||||
{ return 0.0; }
|
||||
|
||||
virtual ~BilinearFormIntegrator() { }
|
||||
@@ -2033,7 +2036,8 @@ public:
|
||||
|
||||
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy = NULL);
|
||||
Vector &flux, bool wcoef = true,
|
||||
Vector *d_energy = NULL);
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
|
||||
@@ -2452,7 +2456,8 @@ public:
|
||||
|
||||
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy = NULL);
|
||||
Vector &flux, bool wcoef = true,
|
||||
Vector *d_energy = NULL);
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
@@ -2785,7 +2790,8 @@ public:
|
||||
s_yz in 3D. */
|
||||
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy = NULL);
|
||||
Vector &flux, bool wcoef = true,
|
||||
Vector *d_energy = NULL);
|
||||
};
|
||||
|
||||
/** Integrator for the DG form:
|
||||
@@ -2938,10 +2944,11 @@ public:
|
||||
|
||||
sum_e eta (r_e([u]), r_e([v]))
|
||||
|
||||
where r_e is the lifting operator defined on each edge e. The parameter eta
|
||||
can be chosen to be one to obtain a stable discretization. The constructor
|
||||
for this integrator requires the finite element space because the lifting
|
||||
operator depends on the element-wise inverse mass matrix.
|
||||
where r_e is the lifting operator defined on each edge e (potentially
|
||||
weighted by a coefficient Q). The parameter eta can be chosen to be one to
|
||||
obtain a stable discretization. The constructor for this integrator requires
|
||||
the finite element space because the lifting operator depends on the
|
||||
element-wise inverse mass matrix.
|
||||
|
||||
BR2 stands for the second method of Bassi and Rebay:
|
||||
|
||||
@@ -2964,14 +2971,28 @@ protected:
|
||||
Array<int> ipiv;
|
||||
Array<int> ipiv_offsets, Minv_offsets;
|
||||
|
||||
Coefficient *Q;
|
||||
|
||||
Vector shape1, shape2;
|
||||
|
||||
DenseMatrix R11, R12, R21, R22;
|
||||
DenseMatrix MinvR11, MinvR12, MinvR21, MinvR22;
|
||||
DenseMatrix Re, MinvRe;
|
||||
|
||||
/// Precomputes the inverses (LU factorizations) of the local mass matrices.
|
||||
/** @a fes must be a DG space, so the mass matrix is block diagonal, and its
|
||||
inverse can be computed locally. This is required for the computation of
|
||||
the lifting operators @a r_e.
|
||||
*/
|
||||
void PrecomputeMassInverse(class FiniteElementSpace &fes);
|
||||
|
||||
public:
|
||||
DGDiffusionBR2Integrator(class FiniteElementSpace *fes, double e = 1.0);
|
||||
DGDiffusionBR2Integrator(class FiniteElementSpace &fes, double e = 1.0);
|
||||
DGDiffusionBR2Integrator(class FiniteElementSpace &fes, Coefficient &Q_,
|
||||
double e = 1.0);
|
||||
MFEM_DEPRECATED DGDiffusionBR2Integrator(class FiniteElementSpace *fes,
|
||||
double e = 1.0);
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
|
||||
+40
-18
@@ -16,20 +16,39 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
|
||||
double e) : eta(e)
|
||||
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
|
||||
FiniteElementSpace &fes, double e) : eta(e), Q(NULL)
|
||||
{
|
||||
PrecomputeMassInverse(fes);
|
||||
}
|
||||
|
||||
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
|
||||
FiniteElementSpace &fes, Coefficient &Q_, double e) : eta(e), Q(&Q_)
|
||||
{
|
||||
PrecomputeMassInverse(fes);
|
||||
}
|
||||
|
||||
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
|
||||
FiniteElementSpace *fes, double e) : eta(e), Q(NULL)
|
||||
{
|
||||
PrecomputeMassInverse(*fes);
|
||||
}
|
||||
|
||||
void DGDiffusionBR2Integrator::PrecomputeMassInverse(FiniteElementSpace &fes)
|
||||
{
|
||||
MFEM_VERIFY(fes.IsDGSpace(),
|
||||
"The BR2 integrator is only defined for DG spaces.");
|
||||
// Precompute local mass matrix inverses needed for the lifting operators
|
||||
// First compute offsets and total size needed (e.g. for mixed meshes or
|
||||
// p-refinement)
|
||||
int nel = fes->GetNE();
|
||||
int nel = fes.GetNE();
|
||||
Minv_offsets.SetSize(nel+1);
|
||||
ipiv_offsets.SetSize(nel+1);
|
||||
ipiv_offsets[0] = 0;
|
||||
Minv_offsets[0] = 0;
|
||||
for (int i=0; i<nel; ++i)
|
||||
{
|
||||
int dof = fes->GetFE(i)->GetDof();
|
||||
int dof = fes.GetFE(i)->GetDof();
|
||||
ipiv_offsets[i+1] = ipiv_offsets[i] + dof;
|
||||
Minv_offsets[i+1] = Minv_offsets[i] + dof*dof;
|
||||
}
|
||||
@@ -37,7 +56,7 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
|
||||
#ifdef MFEM_USE_MPI
|
||||
// When running in parallel, we also need to compute the local mass matrices
|
||||
// of face neighbor elements
|
||||
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(fes);
|
||||
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(&fes);
|
||||
if (pfes != NULL)
|
||||
{
|
||||
ParMesh *pmesh = pfes->GetParMesh();
|
||||
@@ -64,15 +83,15 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
|
||||
{
|
||||
const FiniteElement *fe = NULL;
|
||||
ElementTransformation *tr = NULL;
|
||||
if (i < fes->GetNE())
|
||||
if (i < fes.GetNE())
|
||||
{
|
||||
fe = fes->GetFE(i);
|
||||
tr = fes->GetElementTransformation(i);
|
||||
fe = fes.GetFE(i);
|
||||
tr = fes.GetElementTransformation(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
int inbr = i - fes->GetNE();
|
||||
int inbr = i - fes.GetNE();
|
||||
fe = pfes->GetFaceNbrFE(inbr);
|
||||
tr = pfes->GetParMesh()->GetFaceNbrElementTransformation(inbr);
|
||||
#endif
|
||||
@@ -151,21 +170,24 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix(
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
IntegrationPoint eip1, eip2;
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
const IntegrationPoint &eip1 = Trans.Elem1->GetIntPoint();
|
||||
el1.CalcShape(eip1, shape1);
|
||||
double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0;
|
||||
if (ndof2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
const IntegrationPoint &eip2 = Trans.Elem2->GetIntPoint();
|
||||
el2.CalcShape(eip2, shape2);
|
||||
// Set coefficient value q to the average of the values on either side
|
||||
if (Q) { q = 0.5*(q + Q->Eval(*Trans.Elem2, eip2)); }
|
||||
}
|
||||
|
||||
double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight();
|
||||
if (ndof2)
|
||||
{
|
||||
w /= 2;
|
||||
}
|
||||
// Take sqrt here because
|
||||
// eta (r_e([u]), r_e([v])) = (sqrt(eta) r_e([u]), sqrt(eta) r_e([v]))
|
||||
double w = sqrt((factor + 1)*eta*q)*ip.weight*Trans.Face->Weight();
|
||||
// r_e is defined by, (r_e([u]), tau) = <[u], {tau}>, so we pick up a
|
||||
// factor of 0.5 on interior faces from the average term.
|
||||
if (ndof2) { w *= 0.5; }
|
||||
|
||||
for (int i = 0; i < ndof1; i++)
|
||||
{
|
||||
|
||||
+152
-183
@@ -903,9 +903,11 @@ static void PADiffusionAssembleDiagonal(const int dim,
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPADiffusionDiagonal3D<2,2>(NE,symm,B,G,D,Y);
|
||||
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y);
|
||||
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y);
|
||||
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y);
|
||||
case 0x46: return SmemPADiffusionDiagonal3D<4,6>(NE,symm,B,G,D,Y);
|
||||
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y);
|
||||
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y);
|
||||
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y);
|
||||
@@ -1554,7 +1556,7 @@ static void SmemPADiffusionApply3D(const int NE,
|
||||
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
@@ -1583,118 +1585,102 @@ static void SmemPADiffusionApply3D(const int NE,
|
||||
double (*QDD0)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+0);
|
||||
double (*QDD1)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+1);
|
||||
double (*QDD2)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+2);
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
}
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
}
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
const int i = qi(qx,dy,Q1D);
|
||||
const int j = dj(qx,dy,D1D);
|
||||
const int k = qk(qx,dy,Q1D);
|
||||
const int l = dl(qx,dy,D1D);
|
||||
B[i][j] = b(qx,dy);
|
||||
G[k][l] = g(qx,dy) * sign(qx,dy);
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
const int i = qi(qx,dy,Q1D);
|
||||
const int j = dj(qx,dy,D1D);
|
||||
const int k = qk(qx,dy,Q1D);
|
||||
const int l = dl(qx,dy,D1D);
|
||||
B[i][j] = b(qx,dy);
|
||||
G[k][l] = g(qx,dy) * sign(qx,dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
double u[D1D], v[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; dz++) { u[dz] = v[dz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
const int i = qi(qx,dx,Q1D);
|
||||
const int j = dj(qx,dx,D1D);
|
||||
const int k = qk(qx,dx,Q1D);
|
||||
const int l = dl(qx,dx,D1D);
|
||||
const double s = sign(qx,dx);
|
||||
double u = 0.0, v = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const int i = qi(qx,dx,Q1D);
|
||||
const int j = dj(qx,dx,D1D);
|
||||
const int k = qk(qx,dx,Q1D);
|
||||
const int l = dl(qx,dx,D1D);
|
||||
const double s = sign(qx,dx);
|
||||
const double coords = X[dz][dy][dx];
|
||||
u += coords * B[i][j];
|
||||
v += coords * G[k][l] * s;
|
||||
}
|
||||
DDQ0[dz][dy][qx] = u;
|
||||
DDQ1[dz][dy][qx] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
const int i = qi(qy,dy,Q1D);
|
||||
const int j = dj(qy,dy,D1D);
|
||||
const int k = qk(qy,dy,Q1D);
|
||||
const int l = dl(qy,dy,D1D);
|
||||
const double s = sign(qy,dy);
|
||||
u += DDQ1[dz][dy][qx] * B[i][j];
|
||||
v += DDQ0[dz][dy][qx] * G[k][l] * s;
|
||||
w += DDQ0[dz][dy][qx] * B[i][j];
|
||||
}
|
||||
DQQ0[dz][qy][qx] = u;
|
||||
DQQ1[dz][qy][qx] = v;
|
||||
DQQ2[dz][qy][qx] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
const double coords = X[dz][dy][dx];
|
||||
u[dz] += coords * B[i][j];
|
||||
v[dz] += coords * G[k][l] * s;
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
DDQ0[dz][dy][qx] = u[dz];
|
||||
DDQ1[dz][dy][qx] = v[dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u[D1D], v[D1D], w[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; dz++) { u[dz] = v[dz] = w[dz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
const int i = qi(qy,dy,Q1D);
|
||||
const int j = dj(qy,dy,D1D);
|
||||
const int k = qk(qy,dy,Q1D);
|
||||
const int l = dl(qy,dy,D1D);
|
||||
const double s = sign(qy,dy);
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
u[dz] += DDQ1[dz][dy][qx] * B[i][j];
|
||||
v[dz] += DDQ0[dz][dy][qx] * G[k][l] * s;
|
||||
w[dz] += DDQ0[dz][dy][qx] * B[i][j];
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
DQQ0[dz][qy][qx] = u[dz];
|
||||
DQQ1[dz][qy][qx] = v[dz];
|
||||
DQQ2[dz][qy][qx] = w[dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u[Q1D], v[Q1D], w[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; qz++) { u[qz] = v[qz] = w[qz] = 0.0; }
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; qz++)
|
||||
{
|
||||
const int i = qi(qz,dz,Q1D);
|
||||
const int j = dj(qz,dz,D1D);
|
||||
const int k = qk(qz,dz,Q1D);
|
||||
const int l = dl(qz,dz,D1D);
|
||||
const double s = sign(qz,dz);
|
||||
u[qz] += DQQ0[dz][qy][qx] * B[i][j];
|
||||
v[qz] += DQQ1[dz][qy][qx] * B[i][j];
|
||||
w[qz] += DQQ2[dz][qy][qx] * G[k][l] * s;
|
||||
u += DQQ0[dz][qy][qx] * B[i][j];
|
||||
v += DQQ1[dz][qy][qx] * B[i][j];
|
||||
w += DQQ2[dz][qy][qx] * G[k][l] * s;
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; qz++)
|
||||
{
|
||||
const double O11 = d(qx,qy,qz,0,e);
|
||||
const double O12 = d(qx,qy,qz,1,e);
|
||||
const double O13 = d(qx,qy,qz,2,e);
|
||||
@@ -1704,9 +1690,9 @@ static void SmemPADiffusionApply3D(const int NE,
|
||||
const double O31 = symmetric ? O13 : d(qx,qy,qz,6,e);
|
||||
const double O32 = symmetric ? O23 : d(qx,qy,qz,7,e);
|
||||
const double O33 = symmetric ? d(qx,qy,qz,5,e) : d(qx,qy,qz,8,e);
|
||||
const double gX = u[qz];
|
||||
const double gY = v[qz];
|
||||
const double gZ = w[qz];
|
||||
const double gX = u;
|
||||
const double gY = v;
|
||||
const double gZ = w;
|
||||
QQQ0[qz][qy][qx] = (O11*gX) + (O12*gY) + (O13*gZ);
|
||||
QQQ1[qz][qy][qx] = (O21*gX) + (O22*gY) + (O23*gZ);
|
||||
QQQ2[qz][qy][qx] = (O31*gX) + (O32*gY) + (O33*gZ);
|
||||
@@ -1714,112 +1700,94 @@ static void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
const int i = qi(q,d,Q1D);
|
||||
const int j = dj(q,d,D1D);
|
||||
const int k = qk(q,d,Q1D);
|
||||
const int l = dl(q,d,D1D);
|
||||
Bt[j][i] = b(q,d);
|
||||
Gt[l][k] = g(q,d) * sign(q,d);
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
const int i = qi(q,d,Q1D);
|
||||
const int j = dj(q,d,D1D);
|
||||
const int k = qk(q,d,Q1D);
|
||||
const int l = dl(q,d,D1D);
|
||||
Bt[j][i] = b(q,d);
|
||||
Gt[l][k] = g(q,d) * sign(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
double u[Q1D], v[Q1D], w[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = v[qz] = w[qz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
const int i = qi(qx,dx,Q1D);
|
||||
const int j = dj(qx,dx,D1D);
|
||||
const int k = qk(qx,dx,Q1D);
|
||||
const int l = dl(qx,dx,D1D);
|
||||
const double s = sign(qx,dx);
|
||||
double u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const int i = qi(qx,dx,Q1D);
|
||||
const int j = dj(qx,dx,D1D);
|
||||
const int k = qk(qx,dx,Q1D);
|
||||
const int l = dl(qx,dx,D1D);
|
||||
const double s = sign(qx,dx);
|
||||
u += QQQ0[qz][qy][qx] * Gt[l][k] * s;
|
||||
v += QQQ1[qz][qy][qx] * Bt[j][i];
|
||||
w += QQQ2[qz][qy][qx] * Bt[j][i];
|
||||
}
|
||||
QQD0[qz][qy][dx] = u;
|
||||
QQD1[qz][qy][dx] = v;
|
||||
QQD2[qz][qy][dx] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(Q1D)
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const int i = qi(qy,dy,Q1D);
|
||||
const int j = dj(qy,dy,D1D);
|
||||
const int k = qk(qy,dy,Q1D);
|
||||
const int l = dl(qy,dy,D1D);
|
||||
const double s = sign(qy,dy);
|
||||
u += QQD0[qz][qy][dx] * Bt[j][i];
|
||||
v += QQD1[qz][qy][dx] * Gt[l][k] * s;
|
||||
w += QQD2[qz][qy][dx] * Bt[j][i];
|
||||
}
|
||||
QDD0[qz][dy][dx] = u;
|
||||
QDD1[qz][dy][dx] = v;
|
||||
QDD2[qz][dy][dx] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u[qz] += QQQ0[qz][qy][qx] * Gt[l][k] * s;
|
||||
v[qz] += QQQ1[qz][qy][qx] * Bt[j][i];
|
||||
w[qz] += QQQ2[qz][qy][qx] * Bt[j][i];
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
QQD0[qz][qy][dx] = u[qz];
|
||||
QQD1[qz][qy][dx] = v[qz];
|
||||
QQD2[qz][qy][dx] = w[qz];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u[Q1D], v[Q1D], w[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = v[qz] = w[qz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const int i = qi(qy,dy,Q1D);
|
||||
const int j = dj(qy,dy,D1D);
|
||||
const int k = qk(qy,dy,Q1D);
|
||||
const int l = dl(qy,dy,D1D);
|
||||
const double s = sign(qy,dy);
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u[qz] += QQD0[qz][qy][dx] * Bt[j][i];
|
||||
v[qz] += QQD1[qz][qy][dx] * Gt[l][k] * s;
|
||||
w[qz] += QQD2[qz][qy][dx] * Bt[j][i];
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
QDD0[qz][dy][dx] = u[qz];
|
||||
QDD1[qz][dy][dx] = v[qz];
|
||||
QDD2[qz][dy][dx] = w[qz];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u[D1D], v[D1D], w[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz) { u[dz] = v[dz] = w[dz] = 0.0; }
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
const int i = qi(qz,dz,Q1D);
|
||||
const int j = dj(qz,dz,D1D);
|
||||
const int k = qk(qz,dz,Q1D);
|
||||
const int l = dl(qz,dz,D1D);
|
||||
const double s = sign(qz,dz);
|
||||
u[dz] += QDD0[qz][dy][dx] * Bt[j][i];
|
||||
v[dz] += QDD1[qz][dy][dx] * Bt[j][i];
|
||||
w[dz] += QDD2[qz][dy][dx] * Gt[l][k] * s;
|
||||
u += QDD0[qz][dy][dx] * Bt[j][i];
|
||||
v += QDD1[qz][dy][dx] * Bt[j][i];
|
||||
w += QDD2[qz][dy][dx] * Gt[l][k] * s;
|
||||
}
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
y(dx,dy,dz,e) += (u[dz] + v[dz] + w[dz]);
|
||||
y(dx,dy,dz,e) += (u + v + w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1877,6 +1845,7 @@ static void PADiffusionApply(const int dim,
|
||||
{
|
||||
switch (ID)
|
||||
{
|
||||
case 0x22: return SmemPADiffusionApply3D<2,2>(NE,symm,B,G,D,X,Y);
|
||||
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y);
|
||||
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y);
|
||||
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y);
|
||||
|
||||
@@ -1203,8 +1203,10 @@ static void PAMassApply(const int dim,
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x22: return SmemPAMassApply3D<2,2>(NE,B,Bt,D,X,Y);
|
||||
case 0x23: return SmemPAMassApply3D<2,3>(NE,B,Bt,D,X,Y);
|
||||
case 0x24: return SmemPAMassApply3D<2,4>(NE,B,Bt,D,X,Y);
|
||||
case 0x26: return SmemPAMassApply3D<2,6>(NE,B,Bt,D,X,Y);
|
||||
case 0x34: return SmemPAMassApply3D<3,4>(NE,B,Bt,D,X,Y);
|
||||
case 0x35: return SmemPAMassApply3D<3,5>(NE,B,Bt,D,X,Y);
|
||||
case 0x36: return SmemPAMassApply3D<3,6>(NE,B,Bt,D,X,Y);
|
||||
|
||||
+7
-3
@@ -186,14 +186,18 @@ static void InitTensorBasis(const mfem::FiniteElementSpace &fes,
|
||||
const int ndofs = maps.ndof;
|
||||
const int nqpts = maps.nqpt;
|
||||
mfem::Vector qX(nqpts), qW(nqpts);
|
||||
const mfem::IntegrationRule &ir1d =
|
||||
IntRules.Get(Geometry::SEGMENT, ir.GetOrder());
|
||||
// The x-coordinates of the first `nqpts` points of the integration rule are
|
||||
// the points of the corresponding 1D rule. We also scale the weights
|
||||
// accordingly.
|
||||
double w_sum = 0.0;
|
||||
for (int i = 0; i < nqpts; i++)
|
||||
{
|
||||
const mfem::IntegrationPoint &ip = ir1d.IntPoint(i);
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(i);
|
||||
qX(i) = ip.x;
|
||||
qW(i) = ip.weight;
|
||||
w_sum += ip.weight;
|
||||
}
|
||||
qW *= 1.0/w_sum;
|
||||
CeedBasisCreateTensorH1(ceed, mesh->Dimension(), fes.GetVDim(), ndofs,
|
||||
nqpts, maps.Bt.GetData(),
|
||||
maps.Gt.GetData(), qX.GetData(),
|
||||
|
||||
@@ -52,6 +52,13 @@ double GridFunctionCoefficient::Eval (ElementTransformation &T,
|
||||
return GridF -> GetValue (T, ip, Component);
|
||||
}
|
||||
|
||||
void TransformedCoefficient::SetTime(double t)
|
||||
{
|
||||
if (Q1) { Q1->SetTime(t); }
|
||||
if (Q2) { Q2->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
double TransformedCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -66,6 +73,12 @@ double TransformedCoefficient::Eval(ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void DeltaCoefficient::SetTime(double t)
|
||||
{
|
||||
if (weight) { weight->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void DeltaCoefficient::SetDeltaCenter(const Vector& vcenter)
|
||||
{
|
||||
MFEM_VERIFY(vcenter.Size() <= 3,
|
||||
@@ -87,6 +100,12 @@ double DeltaCoefficient::EvalDelta(ElementTransformation &T,
|
||||
return weight ? weight->Eval(T, ip, GetTime())*w : w;
|
||||
}
|
||||
|
||||
void RestrictedCoefficient::SetTime(double t)
|
||||
{
|
||||
if (c) { c->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationRule &ir)
|
||||
{
|
||||
@@ -134,6 +153,15 @@ VectorArrayCoefficient::VectorArrayCoefficient (int dim)
|
||||
}
|
||||
}
|
||||
|
||||
void VectorArrayCoefficient::SetTime(double t)
|
||||
{
|
||||
for (int i = 0; i < vdim; i++)
|
||||
{
|
||||
if (Coeff[i]) { Coeff[i]->SetTime(t); }
|
||||
}
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorArrayCoefficient::Set(int i, Coefficient *c, bool own)
|
||||
{
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
@@ -247,6 +275,12 @@ double DivergenceGridFunctionCoefficient::Eval(ElementTransformation &T,
|
||||
return GridFunc->GetDivergence(T);
|
||||
}
|
||||
|
||||
void VectorDeltaCoefficient::SetTime(double t)
|
||||
{
|
||||
d.SetTime(t);
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorDeltaCoefficient::SetDirection(const Vector &d_)
|
||||
{
|
||||
dir = d_;
|
||||
@@ -261,6 +295,12 @@ void VectorDeltaCoefficient::EvalDelta(
|
||||
V *= d.EvalDelta(T, ip);
|
||||
}
|
||||
|
||||
void VectorRestrictedCoefficient::SetTime(double t)
|
||||
{
|
||||
if (c) { c->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorRestrictedCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -291,6 +331,12 @@ void VectorRestrictedCoefficient::Eval(
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixFunctionCoefficient::SetTime(double t)
|
||||
{
|
||||
if (Q) { Q->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -371,6 +417,12 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
|
||||
}
|
||||
}
|
||||
|
||||
void SymmetricMatrixFunctionCoefficient::SetTime(double t)
|
||||
{
|
||||
if (Q) { Q->SetTime(t); }
|
||||
this->SymmetricMatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -413,6 +465,15 @@ MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixArrayCoefficient::SetTime(double t)
|
||||
{
|
||||
for (int i=0; i < height*width; i++)
|
||||
{
|
||||
if (Coeff[i]) { Coeff[i]->SetTime(t); }
|
||||
}
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixArrayCoefficient::Set(int i, int j, Coefficient * c, bool own)
|
||||
{
|
||||
if (ownCoeff[i*width+j]) { delete Coeff[i*width+j]; }
|
||||
@@ -441,6 +502,12 @@ void MatrixArrayCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixRestrictedCoefficient::SetTime(double t)
|
||||
{
|
||||
if (c) { c->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -456,6 +523,33 @@ void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void SumCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void ProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void RatioCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void PowerCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A,
|
||||
VectorCoefficient &B)
|
||||
: a(&A), b(&B)
|
||||
@@ -465,6 +559,13 @@ InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A,
|
||||
"Arguments have incompatible dimensions.");
|
||||
}
|
||||
|
||||
void InnerProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
double InnerProductCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -482,6 +583,13 @@ VectorRotProductCoefficient::VectorRotProductCoefficient(VectorCoefficient &A,
|
||||
"Arguments must have dimension equal to two.");
|
||||
}
|
||||
|
||||
void VectorRotProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
double VectorRotProductCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -498,6 +606,12 @@ DeterminantCoefficient::DeterminantCoefficient(MatrixCoefficient &A)
|
||||
"Argument must be a square matrix.");
|
||||
}
|
||||
|
||||
void DeterminantCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
double DeterminantCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -546,6 +660,15 @@ VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &A_,
|
||||
"Arguments must have the same dimension.");
|
||||
}
|
||||
|
||||
void VectorSumCoefficient::SetTime(double t)
|
||||
{
|
||||
if (ACoef) { ACoef->SetTime(t); }
|
||||
if (BCoef) { BCoef->SetTime(t); }
|
||||
if (alphaCoef) { alphaCoef->SetTime(t); }
|
||||
if (betaCoef) { betaCoef->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorSumCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -569,6 +692,13 @@ ScalarVectorProductCoefficient::ScalarVectorProductCoefficient(
|
||||
: VectorCoefficient(B.GetVDim()), aConst(0.0), a(&A), b(&B)
|
||||
{}
|
||||
|
||||
void ScalarVectorProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void ScalarVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -582,6 +712,12 @@ NormalizedVectorCoefficient::NormalizedVectorCoefficient(VectorCoefficient &A,
|
||||
: VectorCoefficient(A.GetVDim()), a(&A), tol(tol_)
|
||||
{}
|
||||
|
||||
void NormalizedVectorCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void NormalizedVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -600,6 +736,13 @@ VectorCrossProductCoefficient::VectorCrossProductCoefficient(
|
||||
"Arguments must have dimension equal to three.");
|
||||
}
|
||||
|
||||
void VectorCrossProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void VectorCrossProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -621,6 +764,13 @@ MatrixVectorProductCoefficient::MatrixVectorProductCoefficient(
|
||||
"Arguments have incompatible dimensions.");
|
||||
}
|
||||
|
||||
void MatrixVectorProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->VectorCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -650,6 +800,13 @@ MatrixSumCoefficient::MatrixSumCoefficient(MatrixCoefficient &A,
|
||||
"Arguments must have the same dimensions.");
|
||||
}
|
||||
|
||||
void MatrixSumCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixSumCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -671,6 +828,13 @@ ScalarMatrixProductCoefficient::ScalarMatrixProductCoefficient(
|
||||
: MatrixCoefficient(B.GetHeight(), B.GetWidth()), aConst(0.0), a(&A), b(&B)
|
||||
{}
|
||||
|
||||
void ScalarMatrixProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void ScalarMatrixProductCoefficient::Eval(DenseMatrix &M,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -684,6 +848,12 @@ TransposeMatrixCoefficient::TransposeMatrixCoefficient(MatrixCoefficient &A)
|
||||
: MatrixCoefficient(A.GetWidth(), A.GetHeight()), a(&A)
|
||||
{}
|
||||
|
||||
void TransposeMatrixCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void TransposeMatrixCoefficient::Eval(DenseMatrix &M,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -700,6 +870,12 @@ InverseMatrixCoefficient::InverseMatrixCoefficient(MatrixCoefficient &A)
|
||||
"Argument must be a square matrix.");
|
||||
}
|
||||
|
||||
void InverseMatrixCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void InverseMatrixCoefficient::Eval(DenseMatrix &M,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -714,6 +890,13 @@ OuterProductCoefficient::OuterProductCoefficient(VectorCoefficient &A,
|
||||
va(A.GetVDim()), vb(B.GetVDim())
|
||||
{}
|
||||
|
||||
void OuterProductCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (b) { b->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void OuterProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -740,6 +923,13 @@ CrossCrossCoefficient::CrossCrossCoefficient(Coefficient &A,
|
||||
vk(K.GetVDim())
|
||||
{}
|
||||
|
||||
void CrossCrossCoefficient::SetTime(double t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
if (k) { k->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void CrossCrossCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
|
||||
+88
-4
@@ -45,7 +45,7 @@ public:
|
||||
Coefficient() { time = 0.; }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
void SetTime(double t) { time = t; }
|
||||
virtual void SetTime(double t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
double GetTime() { return time; }
|
||||
@@ -217,6 +217,9 @@ public:
|
||||
double (*F)(double,double))
|
||||
: Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
@@ -269,6 +272,9 @@ public:
|
||||
weight = NULL; sdim = 3; tdf = NULL;
|
||||
}
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Set the center location of the delta function.
|
||||
void SetDeltaCenter(const Vector& center);
|
||||
|
||||
@@ -333,6 +339,9 @@ public:
|
||||
RestrictedCoefficient(Coefficient &c_, Array<int> &attr)
|
||||
{ c = &c_; attr.Copy(active_attr); }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{ return active_attr[T.Attribute-1] ? c->Eval(T, ip, GetTime()) : 0.0; }
|
||||
@@ -350,7 +359,7 @@ public:
|
||||
VectorCoefficient(int vd) { vdim = vd; time = 0.; }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
void SetTime(double t) { time = t; }
|
||||
virtual void SetTime(double t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
double GetTime() { return time; }
|
||||
@@ -456,6 +465,9 @@ public:
|
||||
still need to be added with Set(). */
|
||||
explicit VectorArrayCoefficient(int dim);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Returns i'th coefficient.
|
||||
Coefficient* GetCoeff(int i) { return Coeff[i]; }
|
||||
|
||||
@@ -632,6 +644,9 @@ public:
|
||||
double s)
|
||||
: VectorCoefficient(dir_.Size()), dir(dir_), d(x,y,z,s) { }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Replace the associated DeltaCoefficient with a new DeltaCoefficient.
|
||||
/** The new DeltaCoefficient cannot have a specified weight Coefficient, i.e.
|
||||
DeltaCoefficient::Weight() should return NULL. */
|
||||
@@ -677,6 +692,9 @@ public:
|
||||
: VectorCoefficient(vc.GetVDim())
|
||||
{ c = &vc; attr.Copy(active_attr); }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -708,7 +726,7 @@ public:
|
||||
height(h), width(w), time(0.), symmetric(symm) { }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
void SetTime(double t) { time = t; }
|
||||
virtual void SetTime(double t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
double GetTime() { return time; }
|
||||
@@ -817,6 +835,9 @@ public:
|
||||
: MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -844,6 +865,9 @@ public:
|
||||
actual coefficients still need to be added with Set(). */
|
||||
explicit MatrixArrayCoefficient (int dim);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Get the coefficient located at (i,j) in the matrix.
|
||||
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
|
||||
|
||||
@@ -881,6 +905,9 @@ public:
|
||||
: MatrixCoefficient(mc.GetHeight(), mc.GetWidth())
|
||||
{ c = &mc; attr.Copy(active_attr); }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -911,6 +938,9 @@ public:
|
||||
double alpha_ = 1.0, double beta_ = 1.0)
|
||||
: aConst(0.0), a(&A), b(&B), alpha(alpha_), beta(beta_) { }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first term in the linear combination as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the linear combination
|
||||
@@ -959,7 +989,7 @@ public:
|
||||
{ dim = dimension; time = 0.; }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
void SetTime(double t) { time = t; }
|
||||
virtual void SetTime(double t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
double GetTime() { return time; }
|
||||
@@ -1037,6 +1067,9 @@ public:
|
||||
: SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1063,6 +1096,9 @@ public:
|
||||
ProductCoefficient(Coefficient &A, Coefficient &B)
|
||||
: aConst(0.0), a(&A), b(&B) { }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first term in the product as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the product
|
||||
@@ -1108,6 +1144,9 @@ public:
|
||||
RatioCoefficient(Coefficient &A, double B)
|
||||
: aConst(0.0), bConst(B), a(&A), b(NULL) { }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the numerator in the ratio as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the numerator of the ratio
|
||||
@@ -1151,6 +1190,9 @@ public:
|
||||
PowerCoefficient(Coefficient &A, double p_)
|
||||
: a(&A), p(p_) { }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the base coefficient
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the base coefficient
|
||||
@@ -1181,6 +1223,9 @@ public:
|
||||
/// Construct with the two vector coefficients. Result is \f$ A \cdot B \f$.
|
||||
InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first vector in the inner product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector coefficient in the inner product
|
||||
@@ -1210,6 +1255,9 @@ public:
|
||||
/// Constructor with two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$.
|
||||
VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first vector in the product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector of the product
|
||||
@@ -1237,6 +1285,9 @@ public:
|
||||
/// Construct with the matrix.
|
||||
DeterminantCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
@@ -1280,6 +1331,9 @@ public:
|
||||
VectorSumCoefficient(VectorCoefficient &A_, VectorCoefficient &B_,
|
||||
Coefficient &alpha_, Coefficient &beta_);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { ACoef = &A; }
|
||||
/// Return the first vector coefficient
|
||||
@@ -1341,6 +1395,9 @@ public:
|
||||
/// Constructor with two coefficients. Result is A * B.
|
||||
ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
@@ -1379,6 +1436,9 @@ public:
|
||||
*/
|
||||
NormalizedVectorCoefficient(VectorCoefficient &A, double tol = 1e-6);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the vector coefficient
|
||||
@@ -1404,6 +1464,9 @@ public:
|
||||
/// Construct with the two coefficients. Result is A x B.
|
||||
VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first term in the product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first term in the product
|
||||
@@ -1435,6 +1498,9 @@ public:
|
||||
/// Constructor with two coefficients. Result is A*B.
|
||||
MatrixVectorProductCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
@@ -1487,6 +1553,9 @@ public:
|
||||
MatrixSumCoefficient(MatrixCoefficient &A, MatrixCoefficient &B,
|
||||
double alpha_ = 1.0, double beta_ = 1.0);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the first matrix coefficient
|
||||
@@ -1528,6 +1597,9 @@ public:
|
||||
/// Constructor with two coefficients. Result is A*B.
|
||||
ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
@@ -1558,6 +1630,9 @@ public:
|
||||
/// Construct with the matrix coefficient. Result is \f$ A^T \f$.
|
||||
TransposeMatrixCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
@@ -1578,6 +1653,9 @@ public:
|
||||
/// Construct with the matrix coefficient. Result is \f$ A^{-1} \f$.
|
||||
InverseMatrixCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
@@ -1602,6 +1680,9 @@ public:
|
||||
/// Construct with two vector coefficients. Result is \f$ A B^T \f$.
|
||||
OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the first vector in the outer product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector coefficient in the outer product
|
||||
@@ -1637,6 +1718,9 @@ public:
|
||||
CrossCrossCoefficient(double A, VectorCoefficient &K);
|
||||
CrossCrossCoefficient(Coefficient &A, VectorCoefficient &K);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
|
||||
@@ -195,6 +195,15 @@ ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
if ( lfi_imag ) { lfi->AddDomainIntegrator(lfi_imag); }
|
||||
}
|
||||
|
||||
void
|
||||
ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag,
|
||||
Array<int> &elem_attr_marker)
|
||||
{
|
||||
if ( lfi_real ) { lfr->AddDomainIntegrator(lfi_real, elem_attr_marker); }
|
||||
if ( lfi_imag ) { lfi->AddDomainIntegrator(lfi_imag, elem_attr_marker); }
|
||||
}
|
||||
|
||||
void
|
||||
ComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag)
|
||||
@@ -317,6 +326,14 @@ void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
if (bfi_imag) { blfi->AddDomainIntegrator(bfi_imag); }
|
||||
}
|
||||
|
||||
void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag,
|
||||
Array<int> & elem_marker)
|
||||
{
|
||||
if (bfi_real) { blfr->AddDomainIntegrator(bfi_real, elem_marker); }
|
||||
if (bfi_imag) { blfi->AddDomainIntegrator(bfi_imag, elem_marker); }
|
||||
}
|
||||
|
||||
void
|
||||
SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag)
|
||||
@@ -879,6 +896,15 @@ ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
if ( lfi_imag ) { plfi->AddDomainIntegrator(lfi_imag); }
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag,
|
||||
Array<int> &elem_attr_marker)
|
||||
{
|
||||
if ( lfi_real ) { plfr->AddDomainIntegrator(lfi_real, elem_attr_marker); }
|
||||
if ( lfi_imag ) { plfi->AddDomainIntegrator(lfi_imag, elem_attr_marker); }
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag)
|
||||
@@ -1040,6 +1066,14 @@ void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
if (bfi_imag) { pblfi->AddDomainIntegrator(bfi_imag); }
|
||||
}
|
||||
|
||||
void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag,
|
||||
Array<int> & elem_marker)
|
||||
{
|
||||
if (bfi_real) { pblfr->AddDomainIntegrator(bfi_real, elem_marker); }
|
||||
if (bfi_imag) { pblfi->AddDomainIntegrator(bfi_imag, elem_marker); }
|
||||
}
|
||||
|
||||
void
|
||||
ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag)
|
||||
|
||||
@@ -128,6 +128,11 @@ public:
|
||||
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag);
|
||||
|
||||
/// Adds new Domain Integrator, restricted to the given attributes.
|
||||
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag,
|
||||
Array<int> &elem_attr_marker);
|
||||
|
||||
/// Adds new Boundary Integrator.
|
||||
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag);
|
||||
@@ -260,6 +265,11 @@ public:
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag);
|
||||
|
||||
/// Adds new Domain Integrator, restricted to the given attributes.
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag,
|
||||
Array<int> &elem_marker);
|
||||
|
||||
/// Adds new Boundary Integrator.
|
||||
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag);
|
||||
@@ -464,6 +474,11 @@ public:
|
||||
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag);
|
||||
|
||||
/// Adds new Domain Integrator, restricted to specific attributes.
|
||||
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag,
|
||||
Array<int> &elem_attr_marker);
|
||||
|
||||
/// Adds new Boundary Integrator.
|
||||
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
|
||||
LinearFormIntegrator *lfi_imag);
|
||||
@@ -598,6 +613,11 @@ public:
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag);
|
||||
|
||||
/// Adds new Domain Integrator, restricted to specific attributes.
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag,
|
||||
Array<int> &elem_marker);
|
||||
|
||||
/// Adds new Boundary Integrator.
|
||||
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
|
||||
BilinearFormIntegrator *bfi_imag);
|
||||
|
||||
@@ -645,7 +645,8 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
Node &n_mesh,
|
||||
const std::string &coordset_name,
|
||||
const std::string &main_topology_name,
|
||||
const std::string &boundary_topology_name)
|
||||
const std::string &boundary_topology_name,
|
||||
const std::string &main_adjset_name)
|
||||
{
|
||||
int dim = mesh->SpaceDimension();
|
||||
|
||||
@@ -815,6 +816,83 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
bndry_att_vals[i] = mesh->GetBdrAttribute(i);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Setup adjsets
|
||||
////////////////////////////////////////////
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh *pmesh = dynamic_cast<ParMesh*>(mesh);
|
||||
if (pmesh)
|
||||
{
|
||||
////////////////////////////////////////////
|
||||
// Setup main adjset
|
||||
////////////////////////////////////////////
|
||||
|
||||
Node &n_adjset = n_mesh["adjsets"][main_adjset_name];
|
||||
|
||||
n_adjset["association"] = "vertex";
|
||||
n_adjset["topology"] = main_topology_name;
|
||||
n_adjset["groups"].set(DataType::object());
|
||||
|
||||
const GroupTopology &pmesh_gtopo = pmesh->gtopo;
|
||||
const int local_rank = pmesh->GetMyRank();
|
||||
const int num_groups = pmesh_gtopo.NGroups();
|
||||
// NOTE: skip the first group since its the local-only group
|
||||
for (int i = 1; i < num_groups; i++)
|
||||
{
|
||||
const int num_group_nbrs = pmesh_gtopo.GetGroupSize(i);
|
||||
const int *group_nbrs = pmesh_gtopo.GetGroup(i);
|
||||
const int num_group_verts = pmesh->GroupNVertices(i);
|
||||
|
||||
// NOTE: 'neighbor' values are local to this processor, but Blueprint
|
||||
// expects global domain identifiers, so we collapse this layer of
|
||||
// indirection
|
||||
Array<int> group_ranks(num_group_nbrs);
|
||||
std::string group_name = "group";
|
||||
{
|
||||
for (int j = 0; j < num_group_nbrs; j++)
|
||||
{
|
||||
group_ranks[j] = pmesh_gtopo.GetNeighborRank(group_nbrs[j]);
|
||||
}
|
||||
group_ranks.Sort();
|
||||
for (int j = 0; j < num_group_nbrs; j++)
|
||||
{
|
||||
group_name += "_" + std::to_string(group_ranks[j]);
|
||||
}
|
||||
|
||||
// NOTE: Blueprint only wants remote ranks in its neighbor list,
|
||||
// so we remove the local rank after the canonicalized Blueprint
|
||||
// group name is formed
|
||||
group_ranks.DeleteFirst(local_rank);
|
||||
}
|
||||
Node &n_group = n_adjset["groups"][group_name];
|
||||
|
||||
n_group["neighbors"].set(group_ranks.GetData(), group_ranks.Size());
|
||||
n_group["values"].set(DataType::c_int(num_group_verts));
|
||||
|
||||
int_array group_vals = n_group["values"].value();
|
||||
for (int j = 0; j < num_group_verts; j++)
|
||||
{
|
||||
group_vals[j] = pmesh->GroupVertex(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We don't create an adjset for face neighbor data because
|
||||
// these faces aren't listed in the 'boundary_topology_name' topology
|
||||
// (this topology only covers the faces between 'main_topology_name'
|
||||
// elements and void). To include a face neighbor data adjset, this
|
||||
// function would need to export a topology with either (1) all faces
|
||||
// in the mesh topology or (2) all boundary faces, including neighbors.
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Setup distributed state
|
||||
////////////////////////////////////////////
|
||||
|
||||
Node &n_domid = n_mesh["state/domain_id"];
|
||||
n_domid.set(local_rank);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
@@ -166,7 +166,8 @@ public:
|
||||
conduit::Node &out,
|
||||
const std::string &coordset_name = "coords",
|
||||
const std::string &main_topology_name = "main",
|
||||
const std::string &boundary_topology_name = "boundary");
|
||||
const std::string &boundary_topology_name = "boundary",
|
||||
const std::string &main_adjset_name = "main_adjset");
|
||||
|
||||
/// Describes a MFEM grid function using the mesh blueprint
|
||||
/** Sets up passed conduit::Node out to describe the given grid function
|
||||
|
||||
@@ -6030,6 +6030,15 @@ void RT0PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
|
||||
shape(4,1) = - 0.5;
|
||||
shape(4,2) = 1.0;
|
||||
|
||||
if (!rt0)
|
||||
{
|
||||
for (int i=1; i<5; i++)
|
||||
for (int j=0; j<3; j++)
|
||||
{
|
||||
shape(i, j) *= 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+134
-3
@@ -106,8 +106,11 @@ void FiniteElementSpace::CopyProlongationAndRestriction(
|
||||
SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
|
||||
if (perm)
|
||||
{
|
||||
// Note: although n and fes.GetVSize() are typically equal, in
|
||||
// variable-order spaces they may differ, since nonconforming edges/faces
|
||||
// my have fictitious DOFs.
|
||||
int n = perm->Size();
|
||||
perm_mat = new SparseMatrix(n, n);
|
||||
perm_mat = new SparseMatrix(n, fes.GetVSize());
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
double s;
|
||||
@@ -124,11 +127,22 @@ void FiniteElementSpace::CopyProlongationAndRestriction(
|
||||
else { cP = new SparseMatrix(*fes.GetConformingProlongation()); }
|
||||
cP_is_set = true;
|
||||
}
|
||||
else if (perm != NULL)
|
||||
{
|
||||
cP = perm_mat;
|
||||
cP_is_set = true;
|
||||
perm_mat = NULL;
|
||||
}
|
||||
if (fes.GetConformingRestriction() != NULL)
|
||||
{
|
||||
if (perm) { cR = Mult(*fes.GetConformingRestriction(), *perm_mat_tr); }
|
||||
else { cR = new SparseMatrix(*fes.GetConformingRestriction()); }
|
||||
}
|
||||
else if (perm != NULL)
|
||||
{
|
||||
cR = perm_mat_tr;
|
||||
perm_mat_tr = NULL;
|
||||
}
|
||||
|
||||
delete perm_mat;
|
||||
delete perm_mat_tr;
|
||||
@@ -1723,6 +1737,122 @@ void FiniteElementSpace::RefinementOperator
|
||||
}
|
||||
}
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// Used in GetCoarseToFineMap() below.
|
||||
struct RefType
|
||||
{
|
||||
Geometry::Type geom;
|
||||
int num_children;
|
||||
const Pair<int,int> *children;
|
||||
|
||||
RefType(Geometry::Type g, int n, const Pair<int,int> *c)
|
||||
: geom(g), num_children(n), children(c) { }
|
||||
|
||||
bool operator<(const RefType &other) const
|
||||
{
|
||||
if (geom < other.geom) { return true; }
|
||||
if (geom > other.geom) { return false; }
|
||||
if (num_children < other.num_children) { return true; }
|
||||
if (num_children > other.num_children) { return false; }
|
||||
for (int i = 0; i < num_children; i++)
|
||||
{
|
||||
if (children[i].one < other.children[i].one) { return true; }
|
||||
if (children[i].one > other.children[i].one) { return false; }
|
||||
}
|
||||
return false; // everything is equal
|
||||
}
|
||||
};
|
||||
|
||||
void GetCoarseToFineMap(const CoarseFineTransformations &cft,
|
||||
const mfem::Mesh &fine_mesh,
|
||||
Table &coarse_to_fine,
|
||||
Array<int> &coarse_to_ref_type,
|
||||
Table &ref_type_to_matrix,
|
||||
Array<Geometry::Type> &ref_type_to_geom)
|
||||
{
|
||||
const int fine_ne = cft.embeddings.Size();
|
||||
int coarse_ne = -1;
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
coarse_ne = std::max(coarse_ne, cft.embeddings[i].parent);
|
||||
}
|
||||
coarse_ne++;
|
||||
|
||||
coarse_to_ref_type.SetSize(coarse_ne);
|
||||
coarse_to_fine.SetDims(coarse_ne, fine_ne);
|
||||
|
||||
Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
|
||||
Array<Pair<int,int> > cf_j(fine_ne);
|
||||
cf_i = 0;
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
cf_i[cft.embeddings[i].parent+1]++;
|
||||
}
|
||||
cf_i.PartialSum();
|
||||
MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
const Embedding &e = cft.embeddings[i];
|
||||
cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
|
||||
cf_j[cf_i[e.parent]].two = i;
|
||||
cf_i[e.parent]++;
|
||||
}
|
||||
std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
|
||||
cf_i[0] = 0;
|
||||
for (int i = 0; i < coarse_ne; i++)
|
||||
{
|
||||
std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
|
||||
}
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
coarse_to_fine.GetJ()[i] = cf_j[i].two;
|
||||
}
|
||||
|
||||
using std::map;
|
||||
using std::pair;
|
||||
|
||||
map<RefType,int> ref_type_map;
|
||||
for (int i = 0; i < coarse_ne; i++)
|
||||
{
|
||||
const int num_children = cf_i[i+1]-cf_i[i];
|
||||
MFEM_ASSERT(num_children > 0, "");
|
||||
const int fine_el = cf_j[cf_i[i]].two;
|
||||
// Assuming the coarse and the fine elements have the same geometry:
|
||||
const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
|
||||
const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
|
||||
pair<map<RefType,int>::iterator,bool> res =
|
||||
ref_type_map.insert(
|
||||
pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
|
||||
coarse_to_ref_type[i] = res.first->second;
|
||||
}
|
||||
|
||||
ref_type_to_matrix.MakeI((int)ref_type_map.size());
|
||||
ref_type_to_geom.SetSize((int)ref_type_map.size());
|
||||
for (map<RefType,int>::iterator it = ref_type_map.begin();
|
||||
it != ref_type_map.end(); ++it)
|
||||
{
|
||||
ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
|
||||
ref_type_to_geom[it->second] = it->first.geom;
|
||||
}
|
||||
|
||||
ref_type_to_matrix.MakeJ();
|
||||
for (map<RefType,int>::iterator it = ref_type_map.begin();
|
||||
it != ref_type_map.end(); ++it)
|
||||
{
|
||||
const RefType &rt = it->first;
|
||||
for (int j = 0; j < rt.num_children; j++)
|
||||
{
|
||||
ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
|
||||
}
|
||||
}
|
||||
ref_type_to_matrix.ShiftUpI();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
||||
/// TODO: Implement DofTransformation support
|
||||
FiniteElementSpace::DerefinementOperator::DerefinementOperator(
|
||||
const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
|
||||
@@ -1764,8 +1894,9 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator(
|
||||
}
|
||||
|
||||
Table ref_type_to_matrix;
|
||||
rtrans.GetCoarseToFineMap(*f_mesh, coarse_to_fine, coarse_to_ref_type,
|
||||
ref_type_to_matrix, ref_type_to_geom);
|
||||
internal::GetCoarseToFineMap(rtrans, *f_mesh, coarse_to_fine,
|
||||
coarse_to_ref_type, ref_type_to_matrix,
|
||||
ref_type_to_geom);
|
||||
MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), "");
|
||||
|
||||
const int total_ref_types = ref_type_to_geom.Size();
|
||||
|
||||
+14
-9
@@ -1341,21 +1341,20 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp)
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
|
||||
void GridFunction::AccumulateAndCountDerivativeValues(int comp, int der_comp,
|
||||
GridFunction &der,
|
||||
Array<int> &zones_per_dof)
|
||||
{
|
||||
FiniteElementSpace * der_fes = der.FESpace();
|
||||
ElementTransformation * transf;
|
||||
Array<int> overlap(der_fes->GetVSize());
|
||||
zones_per_dof.SetSize(der_fes->GetVSize());
|
||||
Array<int> der_dofs, vdofs;
|
||||
DenseMatrix dshape, inv_jac;
|
||||
Vector pt_grad, loc_func;
|
||||
int i, j, k, dim, dof, der_dof, ind;
|
||||
double a;
|
||||
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
overlap[i] = 0;
|
||||
}
|
||||
zones_per_dof = 0;
|
||||
der = 0.0;
|
||||
|
||||
comp--;
|
||||
@@ -1390,11 +1389,17 @@ void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
|
||||
a += inv_jac(j, der_comp) * pt_grad(j);
|
||||
}
|
||||
der(der_dofs[k]) += a;
|
||||
overlap[der_dofs[k]]++;
|
||||
zones_per_dof[der_dofs[k]]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
|
||||
{
|
||||
Array<int> overlap;
|
||||
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
|
||||
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
der(i) /= overlap[i];
|
||||
}
|
||||
@@ -3998,7 +4003,7 @@ double ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
fl -= fla;
|
||||
|
||||
double err = blfi.ComputeFluxEnergy(*ffes->GetFE(i), *Transf, fl,
|
||||
(aniso_flags ? &d_xyz : NULL));
|
||||
with_coeff, (aniso_flags ? &d_xyz : NULL));
|
||||
|
||||
error_estimates(i) = std::sqrt(err);
|
||||
total_error += err;
|
||||
|
||||
@@ -310,6 +310,16 @@ public:
|
||||
|
||||
void ProjectVectorFieldOn(GridFunction &vec_field, int comp = 0);
|
||||
|
||||
/** @brief Compute a certain derivative of a function's component.
|
||||
Derivatives of the function are computed at the DOF locations of @a der,
|
||||
and averaged over overlapping DOFs. Thus this function projects the
|
||||
derivative to the FiniteElementSpace of @a der.
|
||||
@param[in] comp Index of the function's component to be differentiated.
|
||||
The index is 1-based, i.e., use 1 for scalar functions.
|
||||
@param[in] der_comp Use 0/1/2 for derivatives in x/y/z directions.
|
||||
@param[out] der The resulting derivative (scalar function). The
|
||||
FiniteElementSpace of this function must be set
|
||||
before the call. */
|
||||
void GetDerivative(int comp, int der_comp, GridFunction &der);
|
||||
|
||||
double GetDivergence(ElementTransformation &tr) const;
|
||||
@@ -411,6 +421,12 @@ protected:
|
||||
void AccumulateAndCountZones(VectorCoefficient &vcoeff, AvgType type,
|
||||
Array<int> &zones_per_vdof);
|
||||
|
||||
/** @brief Used for the serial and parallel implementations of the
|
||||
GetDerivative() method; see its documentation. */
|
||||
void AccumulateAndCountDerivativeValues(int comp, int der_comp,
|
||||
GridFunction &der,
|
||||
Array<int> &zones_per_dof);
|
||||
|
||||
void AccumulateAndCountBdrValues(Coefficient *coeff[],
|
||||
VectorCoefficient *vcoeff, Array<int> &attr,
|
||||
Array<int> &values_counter);
|
||||
|
||||
+63
-43
@@ -34,7 +34,8 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
|
||||
BilinearForm &a_to,
|
||||
GetIntegratorsFn get_integrators,
|
||||
GetMarkersFn get_markers,
|
||||
AddIntegratorMarkersFn add_integrator,
|
||||
AddIntegratorMarkersFn add_integrator_marker,
|
||||
AddIntegratorFn add_integrator,
|
||||
const IntegrationRule *ir)
|
||||
{
|
||||
Array<BilinearFormIntegrator*> *integrators = (a_from.*get_integrators)();
|
||||
@@ -42,7 +43,14 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
|
||||
|
||||
for (int i=0; i<integrators->Size(); ++i)
|
||||
{
|
||||
(a_to.*add_integrator)((*integrators)[i], *(*markers[i]));
|
||||
if (*markers[i])
|
||||
{
|
||||
(a_to.*add_integrator_marker)((*integrators)[i], *(*markers[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
(a_to.*add_integrator)((*integrators)[i]);
|
||||
}
|
||||
ir_map[(*integrators)[i]] = ((*integrators)[i])->GetIntegrationRule();
|
||||
if (ir) { ((*integrators)[i])->SetIntegrationRule(*ir); }
|
||||
}
|
||||
@@ -92,13 +100,29 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
int dim = mesh_lor.Dimension();
|
||||
const CoarseFineTransformations &cf_tr = mesh_lor.GetRefinementTransforms();
|
||||
|
||||
using GeomRef = std::pair<Geometry::Type, int>;
|
||||
std::map<GeomRef, int> point_matrices_offsets;
|
||||
perm_.SetSize(fes_lor.GetVSize());
|
||||
|
||||
Array<int> vdof_ho, vdof_lor;
|
||||
for (int ilor=0; ilor<mesh_lor.GetNE(); ++ilor)
|
||||
{
|
||||
int iho = cf_tr.embeddings[ilor].parent;
|
||||
int p = fes_ho.GetOrder(iho);
|
||||
int lor_index = cf_tr.embeddings[ilor].matrix;
|
||||
// We use the point matrix index to identify the local LOR element index
|
||||
// within the high-order coarse element.
|
||||
//
|
||||
// In variable-order spaces, the point matrices for each order are
|
||||
// concatenated sequentially, so for the given element order, we need to
|
||||
// find the offset that will give us the point matrix index relative to
|
||||
// the current element order only.
|
||||
GeomRef id(mesh_lor.GetElementBaseGeometry(ilor), p);
|
||||
if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
|
||||
{
|
||||
point_matrices_offsets[id] = lor_index;
|
||||
}
|
||||
lor_index -= point_matrices_offsets[id];
|
||||
|
||||
fes_ho.GetElementVDofs(iho, vdof_ho);
|
||||
fes_lor.GetElementVDofs(ilor, vdof_lor);
|
||||
@@ -109,7 +133,6 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
continue;
|
||||
}
|
||||
|
||||
int p = fes_ho.GetOrder(iho);
|
||||
int p1 = p+1;
|
||||
int ndof_per_dim = (dim == 2) ? p*p1 : type == ND ? p*p1*p1 : p*p*p1;
|
||||
|
||||
@@ -181,7 +204,7 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
void LORBase::ConstructDofPermutation() const
|
||||
{
|
||||
FESpaceType type = GetFESpaceType();
|
||||
if (type == H1 || type == L2 || nonconforming)
|
||||
if (type == H1 || type == L2)
|
||||
{
|
||||
// H1 and L2: no permutation necessary, return identity
|
||||
perm.SetSize(fes->GetTrueVSize());
|
||||
@@ -226,10 +249,10 @@ const Array<int> &LORBase::GetDofPermutation() const
|
||||
return perm;
|
||||
}
|
||||
|
||||
bool LORBase::RequiresDofPermutation() const
|
||||
bool LORBase::HasSameDofNumbering() const
|
||||
{
|
||||
FESpaceType type = GetFESpaceType();
|
||||
return (type == H1 || type == L2 || nonconforming) ? false : true;
|
||||
return type == H1 || type == L2;
|
||||
}
|
||||
|
||||
const OperatorHandle &LORBase::GetAssembledSystem() const
|
||||
@@ -238,7 +261,7 @@ const OperatorHandle &LORBase::GetAssembledSystem() const
|
||||
return A;
|
||||
}
|
||||
|
||||
void LORBase::AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs)
|
||||
void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array<int> &ess_dofs)
|
||||
{
|
||||
a->UseExternalIntegrators();
|
||||
AddIntegrators(a_ho, *a, &BilinearForm::GetDBFI,
|
||||
@@ -247,40 +270,23 @@ void LORBase::AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs)
|
||||
&BilinearForm::AddInteriorFaceIntegrator, ir_face);
|
||||
AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBBFI,
|
||||
&BilinearForm::GetBBFI_Marker,
|
||||
&BilinearForm::AddBoundaryIntegrator,
|
||||
&BilinearForm::AddBoundaryIntegrator, ir_face);
|
||||
AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBFBFI,
|
||||
&BilinearForm::GetBFBFI_Marker,
|
||||
&BilinearForm::AddBdrFaceIntegrator,
|
||||
&BilinearForm::AddBdrFaceIntegrator, ir_face);
|
||||
a->Assemble();
|
||||
if (RequiresDofPermutation())
|
||||
{
|
||||
const Array<int> &p = GetDofPermutation();
|
||||
// Form inverse permutation: given high-order dof i, pi[i] is corresp. LO
|
||||
Array<int> pi(p.Size());
|
||||
for (int i=0; i<p.Size(); ++i)
|
||||
{
|
||||
pi[absdof(p[i])] = i;
|
||||
}
|
||||
Array<int> ess_dofs_perm(ess_dofs.Size());
|
||||
for (int i=0; i<ess_dofs.Size(); ++i)
|
||||
{
|
||||
ess_dofs_perm[i] = pi[ess_dofs[i]];
|
||||
}
|
||||
a->FormSystemMatrix(ess_dofs_perm, A);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->FormSystemMatrix(ess_dofs, A);
|
||||
}
|
||||
a->FormSystemMatrix(ess_dofs, A);
|
||||
ResetIntegrationRules(&BilinearForm::GetDBFI);
|
||||
ResetIntegrationRules(&BilinearForm::GetFBFI);
|
||||
ResetIntegrationRules(&BilinearForm::GetBBFI);
|
||||
ResetIntegrationRules(&BilinearForm::GetBFBFI);
|
||||
}
|
||||
|
||||
void LORBase::SetupNonconforming()
|
||||
void LORBase::SetupProlongationAndRestriction()
|
||||
{
|
||||
if (RequiresDofPermutation())
|
||||
if (!HasSameDofNumbering())
|
||||
{
|
||||
Array<int> p;
|
||||
ConstructLocalDofPermutation(p);
|
||||
@@ -290,7 +296,6 @@ void LORBase::SetupNonconforming()
|
||||
{
|
||||
fes->CopyProlongationAndRestriction(fes_ho, NULL);
|
||||
}
|
||||
nonconforming = true;
|
||||
}
|
||||
|
||||
template <typename FEC>
|
||||
@@ -373,7 +378,6 @@ LORDiscretization::LORDiscretization(BilinearForm &a_ho_,
|
||||
int ref_type)
|
||||
: LORDiscretization(*a_ho_.FESpace(), ref_type)
|
||||
{
|
||||
a = new BilinearForm(fes);
|
||||
AssembleSystem(a_ho_, ess_tdof_list);
|
||||
}
|
||||
|
||||
@@ -382,23 +386,32 @@ LORDiscretization::LORDiscretization(FiniteElementSpace &fes_ho,
|
||||
{
|
||||
CheckBasisType(fes_ho);
|
||||
|
||||
// TODO: support variable-order spaces
|
||||
MFEM_VERIFY(!fes_ho.IsVariableOrder(),
|
||||
"Cannot construct LOR operators on variable-order spaces");
|
||||
|
||||
int order = fes_ho.GetMaxElementOrder();
|
||||
if (GetFESpaceType() == L2) { ++order; }
|
||||
|
||||
Mesh &mesh_ho = *fes_ho.GetMesh();
|
||||
mesh = new Mesh(Mesh::MakeRefined(mesh_ho, order, ref_type));
|
||||
// For H1, ND and RT spaces, use refinement = element order, for DG spaces,
|
||||
// use refinement = element order + 1 (since LOR is p = 0 in this case).
|
||||
int increment = (GetFESpaceType() == L2) ? 1 : 0;
|
||||
Array<int> refinements(mesh_ho.GetNE());
|
||||
for (int i=0; i<refinements.Size(); ++i)
|
||||
{
|
||||
refinements[i] = fes_ho.GetOrder(i) + increment;
|
||||
}
|
||||
mesh = new Mesh(Mesh::MakeRefined(mesh_ho, refinements, ref_type));
|
||||
|
||||
fec = fes_ho.FEColl()->Clone(GetLOROrder());
|
||||
fes = new FiniteElementSpace(mesh, fec);
|
||||
if (fes_ho.Nonconforming()) { SetupNonconforming(); }
|
||||
SetupProlongationAndRestriction();
|
||||
|
||||
A.SetType(Operator::MFEM_SPARSEMAT);
|
||||
}
|
||||
|
||||
void LORDiscretization::AssembleSystem(BilinearForm &a_ho,
|
||||
const Array<int> &ess_dofs)
|
||||
{
|
||||
delete a;
|
||||
a = new BilinearForm(&GetFESpace());
|
||||
AssembleSystem_(a_ho, ess_dofs);
|
||||
}
|
||||
|
||||
SparseMatrix &LORDiscretization::GetAssembledMatrix() const
|
||||
{
|
||||
MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled");
|
||||
@@ -412,7 +425,6 @@ ParLORDiscretization::ParLORDiscretization(ParBilinearForm &a_ho_,
|
||||
int ref_type)
|
||||
: ParLORDiscretization(*a_ho_.ParFESpace(), ref_type)
|
||||
{
|
||||
a = new ParBilinearForm(static_cast<ParFiniteElementSpace*>(fes));
|
||||
AssembleSystem(a_ho_, ess_tdof_list);
|
||||
}
|
||||
|
||||
@@ -420,7 +432,7 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho,
|
||||
int ref_type) : LORBase(fes_ho)
|
||||
{
|
||||
if (fes_ho.GetMyRank() == 0) { CheckBasisType(fes_ho); }
|
||||
// TODO: support variable-order spaces
|
||||
// TODO: support variable-order spaces in parallel
|
||||
MFEM_VERIFY(!fes_ho.IsVariableOrder(),
|
||||
"Cannot construct LOR operators on variable-order spaces");
|
||||
|
||||
@@ -434,11 +446,19 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho,
|
||||
fec = fes_ho.FEColl()->Clone(GetLOROrder());
|
||||
ParFiniteElementSpace *pfes = new ParFiniteElementSpace(pmesh, fec);
|
||||
fes = pfes;
|
||||
if (fes_ho.Nonconforming()) { SetupNonconforming(); }
|
||||
SetupProlongationAndRestriction();
|
||||
|
||||
A.SetType(Operator::Hypre_ParCSR);
|
||||
}
|
||||
|
||||
void ParLORDiscretization::AssembleSystem(ParBilinearForm &a_ho,
|
||||
const Array<int> &ess_dofs)
|
||||
{
|
||||
delete a;
|
||||
a = new ParBilinearForm(&GetParFESpace());
|
||||
AssembleSystem_(a_ho, ess_dofs);
|
||||
}
|
||||
|
||||
HypreParMatrix &ParLORDiscretization::GetAssembledMatrix() const
|
||||
{
|
||||
MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled");
|
||||
|
||||
+37
-67
@@ -35,7 +35,7 @@ private:
|
||||
/// Adds all the integrators from the BilinearForm @a a_from to @a a_to. If
|
||||
/// the mesh consists of tensor product elements, temporarily changes the
|
||||
/// integration rules of the integrators to use collocated quadrature for
|
||||
/// better conditioning of the %LOR system.
|
||||
/// better conditioning of the LOR system.
|
||||
void AddIntegrators(BilinearForm &a_from,
|
||||
BilinearForm &a_to,
|
||||
GetIntegratorsFn get_integrators,
|
||||
@@ -49,11 +49,12 @@ private:
|
||||
BilinearForm &a_to,
|
||||
GetIntegratorsFn get_integrators,
|
||||
GetMarkersFn get_markers,
|
||||
AddIntegratorMarkersFn add_integrator,
|
||||
AddIntegratorMarkersFn add_integrator_marker,
|
||||
AddIntegratorFn add_integrator,
|
||||
const IntegrationRule *ir);
|
||||
|
||||
/// Resets the integration rules of the integrators of @a a to their original
|
||||
/// values (after temporarily changing them for %LOR assembly).
|
||||
/// values (after temporarily changing them for LOR assembly).
|
||||
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
@@ -68,37 +69,42 @@ protected:
|
||||
BilinearForm *a;
|
||||
OperatorHandle A;
|
||||
mutable Array<int> perm;
|
||||
bool nonconforming = false;
|
||||
|
||||
/// Constructs the local DOF (ldof) permutation. In parallel this is used as
|
||||
/// an intermediate step in computing the DOF permutation (see
|
||||
/// ConstructDofPermutation and GetDofPermutation).
|
||||
void ConstructLocalDofPermutation(Array<int> &perm_) const;
|
||||
|
||||
/// Construct the permutation that maps %LOR DOFs to high-order DOFs. See
|
||||
/// Construct the permutation that maps LOR DOFs to high-order DOFs. See
|
||||
/// GetDofPermutation.
|
||||
void ConstructDofPermutation() const;
|
||||
|
||||
/// Sets up the prolongation and restriction operators required for
|
||||
/// nonconforming spaces.
|
||||
void SetupNonconforming();
|
||||
/// Returns true if the LOR space and HO space have the same DOF numbering
|
||||
/// (H1 or L2 spaces), false otherwise (ND or RT spaces).
|
||||
bool HasSameDofNumbering() const;
|
||||
|
||||
/// Sets up the prolongation and restriction operators required in the case
|
||||
/// of different DOF numberings (ND or RT spaces) or nonconforming spaces.
|
||||
void SetupProlongationAndRestriction();
|
||||
|
||||
/// Returns the type of finite element space: H1, ND, RT or L2.
|
||||
FESpaceType GetFESpaceType() const;
|
||||
|
||||
/// Returns the order of the %LOR space. 1 for H1 or ND, 0 for L2 or RT.
|
||||
/// Returns the order of the LOR space. 1 for H1 or ND, 0 for L2 or RT.
|
||||
int GetLOROrder() const;
|
||||
|
||||
/// Assembles the LOR system (used internally by
|
||||
/// LORDiscretization::AssembleSystem and
|
||||
/// ParLORDiscretization::AssembleSystem).
|
||||
void AssembleSystem_(BilinearForm &a_ho, const Array<int> &ess_dofs);
|
||||
|
||||
LORBase(FiniteElementSpace &fes_ho_);
|
||||
|
||||
public:
|
||||
/// Returns the assembled %LOR system.
|
||||
/// Returns the assembled LOR system.
|
||||
const OperatorHandle &GetAssembledSystem() const;
|
||||
|
||||
/// Assembles the %LOR system.
|
||||
void AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs);
|
||||
|
||||
/// @brief Returns the permutation that maps %LOR DOFs to high-order DOFs.
|
||||
/// @brief Returns the permutation that maps LOR DOFs to high-order DOFs.
|
||||
///
|
||||
/// This permutation is constructed the first time it is requested, and then
|
||||
/// is cached. For H1 and L2 finite element spaces (or for nonconforming
|
||||
@@ -108,16 +114,9 @@ public:
|
||||
///
|
||||
/// For vector finite element spaces (ND and RT), the DOF permutation is
|
||||
/// nontrivial. Returns an array @a perm such that, given an index @a i of a
|
||||
/// %LOR dof, @a perm[i] is the index of the corresponding HO dof.
|
||||
/// LOR dof, @a perm[i] is the index of the corresponding HO dof.
|
||||
const Array<int> &GetDofPermutation() const;
|
||||
|
||||
/// Returns true if the %LOR spaces requires a DOF permutation (if the
|
||||
/// corresponding %LOR and HO DOFs are numbered differently), false
|
||||
/// otherwise. Note: permutations are not required in the case of
|
||||
/// nonconforming spaces, since the DOF numbering is incorporated into the
|
||||
/// prolongation operators.
|
||||
bool RequiresDofPermutation() const;
|
||||
|
||||
/// Returns the low-order refined finite element space.
|
||||
FiniteElementSpace &GetFESpace() const { return *fes; }
|
||||
|
||||
@@ -144,7 +143,10 @@ public:
|
||||
LORDiscretization(FiniteElementSpace &fes_ho,
|
||||
int ref_type=BasisType::GaussLobatto);
|
||||
|
||||
/// Return the assembled %LOR operator as a SparseMatrix.
|
||||
/// Assembles the LOR system corresponding to @a a_ho.
|
||||
void AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs);
|
||||
|
||||
/// Return the assembled LOR operator as a SparseMatrix.
|
||||
SparseMatrix &GetAssembledMatrix() const;
|
||||
};
|
||||
|
||||
@@ -170,10 +172,13 @@ public:
|
||||
ParLORDiscretization(ParFiniteElementSpace &fes_ho,
|
||||
int ref_type=BasisType::GaussLobatto);
|
||||
|
||||
/// Return the assembled %LOR operator as a HypreParMatrix.
|
||||
/// Assembles the LOR system corresponding to @a a_ho.
|
||||
void AssembleSystem(ParBilinearForm &a_ho, const Array<int> &ess_dofs);
|
||||
|
||||
/// Return the assembled LOR operator as a HypreParMatrix.
|
||||
HypreParMatrix &GetAssembledMatrix() const;
|
||||
|
||||
/// Return the %LOR ParFiniteElementSpace.
|
||||
/// Return the LOR ParFiniteElementSpace.
|
||||
ParFiniteElementSpace &GetParFESpace() const;
|
||||
};
|
||||
|
||||
@@ -192,12 +197,11 @@ class LORSolver : public Solver
|
||||
protected:
|
||||
LORBase *lor;
|
||||
bool own_lor = true;
|
||||
bool use_permutation = true;
|
||||
SolverType solver;
|
||||
mutable Vector px, py;
|
||||
public:
|
||||
/// @brief Create a solver of type @a SolverType, formed using the assembled
|
||||
/// SparseMatrix of the %LOR version of @a a_ho. @see LORDiscretization
|
||||
/// SparseMatrix of the LOR version of @a a_ho. @see LORDiscretization
|
||||
LORSolver(BilinearForm &a_ho, const Array<int> &ess_tdof_list,
|
||||
int ref_type=BasisType::GaussLobatto)
|
||||
{
|
||||
@@ -207,7 +211,7 @@ public:
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// @brief Create a solver of type @a SolverType, formed using the assembled
|
||||
/// HypreParMatrix of the %LOR version of @a a_ho. @see ParLORDiscretization
|
||||
/// HypreParMatrix of the LOR version of @a a_ho. @see ParLORDiscretization
|
||||
LORSolver(ParBilinearForm &a_ho, const Array<int> &ess_tdof_list,
|
||||
int ref_type=BasisType::GaussLobatto)
|
||||
{
|
||||
@@ -218,8 +222,6 @@ public:
|
||||
|
||||
/// @brief Create a solver of type @a SolverType using Operator @a op and
|
||||
/// arguments @a args.
|
||||
///
|
||||
/// The object @a lor_ will be used for DOF permutations.
|
||||
template <typename... Args>
|
||||
LORSolver(const Operator &op, LORBase &lor_, Args&&... args) : solver(args...)
|
||||
{
|
||||
@@ -228,7 +230,7 @@ public:
|
||||
SetOperator(op);
|
||||
}
|
||||
|
||||
/// @brief Create a solver of type @a SolverType using the assembled %LOR
|
||||
/// @brief Create a solver of type @a SolverType using the assembled LOR
|
||||
/// operator represented by @a lor_.
|
||||
///
|
||||
/// The given @a args will be used as arguments to the solver constructor.
|
||||
@@ -243,42 +245,7 @@ public:
|
||||
height = solver.Height();
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (use_permutation && lor->RequiresDofPermutation())
|
||||
{
|
||||
const Array<int> &p = lor->GetDofPermutation();
|
||||
px.SetSize(x.Size());
|
||||
py.SetSize(y.Size());
|
||||
for (int i=0; i<x.Size(); ++i)
|
||||
{ px[i] = p[i] < 0 ? -x[-1-p[i]] : x[p[i]]; }
|
||||
|
||||
solver.Mult(px, py);
|
||||
|
||||
for (int i=0; i<y.Size(); ++i)
|
||||
{
|
||||
int pi = p[i];
|
||||
int s = pi < 0 ? -1 : 1;
|
||||
y[pi < 0 ? -1-pi : pi] = s*py[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
solver.Mult(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Enable or disable the DOF permutation (enabled by default).
|
||||
///
|
||||
/// The corresponding %LOR and high-order DOFs may not have the same
|
||||
/// numbering (for example, when using ND or RT spaces), and so a permutation
|
||||
/// is required when applying the %LOR solver as a preconditioner for the
|
||||
/// high-order problem. This permutation can be disabled (for example, in
|
||||
/// order to precondition the low-order problem directly).
|
||||
void UsePermutation(bool use_permutation_)
|
||||
{
|
||||
use_permutation = use_permutation_;
|
||||
}
|
||||
void Mult(const Vector &x, Vector &y) const { solver.Mult(x, y); }
|
||||
|
||||
/// Access the underlying solver.
|
||||
SolverType &GetSolver() { return solver; }
|
||||
@@ -286,6 +253,9 @@ public:
|
||||
/// Access the underlying solver.
|
||||
const SolverType &GetSolver() const { return solver; }
|
||||
|
||||
/// Access the LOR discretization object.
|
||||
const LORBase &GetLOR() const { return *lor; }
|
||||
|
||||
~LORSolver() { if (own_lor) { delete lor; } }
|
||||
};
|
||||
|
||||
|
||||
+25
-6
@@ -96,6 +96,7 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
Vector el_x;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
double energy = 0.0;
|
||||
|
||||
if (dnfi.Size())
|
||||
@@ -103,9 +104,10 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fe = fes->GetFE(i);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
x.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
energy += dnfi[k]->GetElementEnergy(*fe, *T, el_x);
|
||||
@@ -166,6 +168,7 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
Vector el_x, el_y;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
|
||||
py = 0.0;
|
||||
@@ -175,12 +178,14 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fe = fes->GetFE(i);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
}
|
||||
}
|
||||
@@ -302,6 +307,7 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
DenseMatrix elmat;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
const Vector &px = Prolongate(x);
|
||||
|
||||
@@ -319,12 +325,14 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fe = fes->GetFE(i);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
// Grad->AddSubMatrix(vdofs, vdofs, elmat, 1);
|
||||
}
|
||||
@@ -583,6 +591,7 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
Array<const Vector *> el_x_const(fes.Size());
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
double energy = 0.0;
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
@@ -598,8 +607,9 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -645,6 +655,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
Array<const FiniteElement *> fe2(fes.Size());
|
||||
ElementTransformation *T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
|
||||
by.UseDevice(true);
|
||||
by = 0.0;
|
||||
@@ -664,9 +675,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetElementVDofs(i, *(vdofs[s]));
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *(vdofs[s]));
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -677,6 +689,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
if (el_y[s]->Size() == 0) { continue; }
|
||||
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
|
||||
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
|
||||
}
|
||||
}
|
||||
@@ -844,6 +857,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
Array<const FiniteElement *>fe(fes.Size());
|
||||
Array<const FiniteElement *>fe2(fes.Size());
|
||||
ElementTransformation * T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
{
|
||||
@@ -880,8 +894,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -893,6 +908,10 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
if (elmats(j,l)->Height() == 0) { continue; }
|
||||
if (doftrans[j] || doftrans[l])
|
||||
{
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
}
|
||||
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
|
||||
*elmats(j,l), skip_zeros);
|
||||
}
|
||||
|
||||
+19
-1
@@ -3195,8 +3195,11 @@ void ParFiniteElementSpace::CopyProlongationAndRestriction(
|
||||
SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
|
||||
if (perm)
|
||||
{
|
||||
// Note: although n and fes.GetVSize() are typically equal, in
|
||||
// variable-order spaces they may differ, since nonconforming edges/faces
|
||||
// my have fictitious DOFs.
|
||||
int n = perm->Size();
|
||||
perm_mat = new SparseMatrix(n, n);
|
||||
perm_mat = new SparseMatrix(n, fes.GetVSize());
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
double s;
|
||||
@@ -3213,11 +3216,26 @@ void ParFiniteElementSpace::CopyProlongationAndRestriction(
|
||||
else { P = new HypreParMatrix(*pfes->P); }
|
||||
nonconf_P = true;
|
||||
}
|
||||
else if (perm != NULL)
|
||||
{
|
||||
HYPRE_BigInt glob_nrows = GlobalVSize();
|
||||
HYPRE_BigInt glob_ncols = GlobalTrueVSize();
|
||||
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_BigInt *row_starts = GetDofOffsets();
|
||||
P = new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
|
||||
col_starts, perm_mat);
|
||||
nonconf_P = true;
|
||||
}
|
||||
if (pfes->R != NULL)
|
||||
{
|
||||
if (perm) { R = Mult(*pfes->R, *perm_mat_tr); }
|
||||
else { R = new SparseMatrix(*pfes->R); }
|
||||
}
|
||||
else if (perm != NULL)
|
||||
{
|
||||
R = perm_mat_tr;
|
||||
perm_mat_tr = NULL;
|
||||
}
|
||||
|
||||
delete perm_mat;
|
||||
delete perm_mat_tr;
|
||||
|
||||
@@ -481,6 +481,27 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::GetDerivative(int comp, int der_comp,
|
||||
ParGridFunction &der)
|
||||
{
|
||||
Array<int> overlap;
|
||||
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
|
||||
|
||||
// Count the zones globally.
|
||||
GroupCommunicator &gcomm = der.ParFESpace()->GroupComm();
|
||||
gcomm.Reduce<int>(overlap, GroupCommunicator::Sum);
|
||||
gcomm.Bcast(overlap);
|
||||
|
||||
// Accumulate for all dofs.
|
||||
gcomm.Reduce<double>(der.HostReadWrite(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<double>(der.HostReadWrite());
|
||||
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
der(i) /= overlap[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
int ne = fes->GetNE();
|
||||
|
||||
@@ -226,6 +226,9 @@ public:
|
||||
const IntegrationPoint &ip,
|
||||
Vector &val, Vector *tr = NULL) const;
|
||||
|
||||
/// Parallel version of GridFunction::GetDerivative(); see its documentation.
|
||||
void GetDerivative(int comp, int der_comp, ParGridFunction &der);
|
||||
|
||||
/** Sets the output vector @a dof_vals to the values of the degrees of
|
||||
freedom of element @a el. If @a el is greater than or equal to the number
|
||||
of local elements, it will be interpreted as a shifted index of a face
|
||||
|
||||
@@ -40,8 +40,6 @@ StaticCondensation::StaticCondensation(FiniteElementSpace *fespace)
|
||||
#endif
|
||||
S = S_e = NULL;
|
||||
symm = false;
|
||||
A_data.Reset();
|
||||
A_ipiv.Reset();
|
||||
|
||||
Array<int> vdofs;
|
||||
const int NE = fes->GetNE();
|
||||
|
||||
+323
-38
@@ -2326,6 +2326,8 @@ TMOP_Integrator::~TMOP_Integrator()
|
||||
{
|
||||
delete lim_func;
|
||||
delete zeta;
|
||||
delete sigma;
|
||||
delete sigma_bar;
|
||||
for (int i = 0; i < ElemDer.Size(); i++)
|
||||
{
|
||||
delete ElemDer[i];
|
||||
@@ -2393,6 +2395,87 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
|
||||
const Array<bool> &smarker,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
delete sigma;
|
||||
sigma = new GridFunction(s0);
|
||||
sigma_marker = &smarker;
|
||||
coeff_sigma = &coeff;
|
||||
sigma_eval = &ae;
|
||||
|
||||
// Compute the restricted sigma.
|
||||
delete sigma_bar;
|
||||
sigma_bar = new GridFunction(*sigma);
|
||||
for (int i = 0; i < sigma_marker->Size(); i++)
|
||||
{
|
||||
if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; }
|
||||
}
|
||||
|
||||
sigma_eval->SetSerialMetaInfo(*s0.FESpace()->GetMesh(),
|
||||
*s0.FESpace()->FEColl(), 1);
|
||||
sigma_eval->SetInitialField
|
||||
(*sigma->FESpace()->GetMesh()->GetNodes(), *sigma);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
delete sigma;
|
||||
sigma = new GridFunction(s0);
|
||||
sigma_marker = &smarker;
|
||||
coeff_sigma = &coeff;
|
||||
sigma_eval = &ae;
|
||||
|
||||
// Compute the restricted sigma.
|
||||
delete sigma_bar;
|
||||
sigma_bar = new GridFunction(*sigma);
|
||||
for (int i = 0; i < sigma_marker->Size(); i++)
|
||||
{
|
||||
if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; }
|
||||
}
|
||||
|
||||
sigma_eval->SetParMetaInfo(*s0.ParFESpace()->GetParMesh(),
|
||||
*s0.ParFESpace()->FEColl(), 1);
|
||||
sigma_eval->SetInitialField
|
||||
(*sigma->FESpace()->GetMesh()->GetNodes(), *sigma);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max)
|
||||
{
|
||||
MFEM_VERIFY(sigma, "Surface fitting has not been enabled.");
|
||||
|
||||
int loc_cnt = 0;
|
||||
double loc_max = 0.0, loc_sum = 0.0;
|
||||
for (int i = 0; i < sigma_marker->Size(); i++)
|
||||
{
|
||||
if ((*sigma_marker)[i] == true)
|
||||
{
|
||||
loc_cnt++;
|
||||
loc_max = std::max(loc_max, std::abs((*sigma_bar)(i)));
|
||||
loc_sum += std::abs((*sigma_bar)(i));
|
||||
}
|
||||
}
|
||||
err_avg = loc_sum / loc_cnt;
|
||||
err_max = loc_max;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (targetC->Parallel() == false) { return; }
|
||||
int glob_cnt;
|
||||
MPI_Comm comm = targetC->GetComm();
|
||||
MPI_Allreduce(&loc_max, &err_max, 1, MPI_DOUBLE, MPI_MAX, comm);
|
||||
MPI_Allreduce(&loc_cnt, &glob_cnt, 1, MPI_INT, MPI_SUM, comm);
|
||||
MPI_Allreduce(&loc_sum, &err_avg, 1, MPI_DOUBLE, MPI_SUM, comm);
|
||||
err_avg = err_avg / glob_cnt;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
|
||||
{
|
||||
if (zeta)
|
||||
@@ -2419,16 +2502,19 @@ void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun)
|
||||
{
|
||||
const int dof = el.GetDof(), dim = el.GetDim();
|
||||
const int el_id = T.ElementNo;
|
||||
double energy;
|
||||
|
||||
// No adaptive limiting terms if this is a FD computation.
|
||||
// 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 = (zeta && fd_call_flag == false);
|
||||
const bool surface_fit = (sigma && fd_call_flag == false);
|
||||
|
||||
DSh.SetSize(dof, dim);
|
||||
Jrt.SetSize(dim);
|
||||
@@ -2440,7 +2526,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
|
||||
energy = 0.0;
|
||||
DenseTensor Jtr(dim, dim, ir.GetNPoints());
|
||||
targetC->ComputeElementTargets(T.ElementNo, el, ir, elfun, Jtr);
|
||||
targetC->ComputeElementTargets(el_id, el, ir, elfun, Jtr);
|
||||
|
||||
// Limited case.
|
||||
Vector shape, p, p0, d_vals;
|
||||
@@ -2453,11 +2539,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
pos0.SetSize(dof, dim);
|
||||
Vector pos0V(pos0.Data(), dof * dim);
|
||||
Array<int> pos_dofs;
|
||||
nodes0->FESpace()->GetElementVDofs(T.ElementNo, pos_dofs);
|
||||
nodes0->FESpace()->GetElementVDofs(el_id, pos_dofs);
|
||||
nodes0->GetSubVector(pos_dofs, pos0V);
|
||||
if (lim_dist)
|
||||
{
|
||||
lim_dist->GetValues(T.ElementNo, ir, d_vals);
|
||||
lim_dist->GetValues(el_id, ir, d_vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2467,11 +2553,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (coeff1 || coeff0 || adaptive_limiting)
|
||||
if (coeff1 || coeff0 || adaptive_limiting || surface_fit)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
Tpr->ElementNo = T.ElementNo;
|
||||
Tpr->ElementNo = el_id;
|
||||
Tpr->ElementType = ElementTransformation::ELEMENT;
|
||||
Tpr->Attribute = T.Attribute;
|
||||
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
|
||||
@@ -2487,13 +2573,17 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
Vector zeta_q, zeta0_q;
|
||||
if (adaptive_limiting)
|
||||
{
|
||||
zeta->GetValues(T.ElementNo, ir, zeta_q);
|
||||
zeta_0->GetValues(T.ElementNo, ir, zeta0_q);
|
||||
zeta->GetValues(el_id, ir, zeta_q);
|
||||
zeta_0->GetValues(el_id, ir, zeta0_q);
|
||||
}
|
||||
|
||||
Vector sigma_bar_q;
|
||||
if (surface_fit) { sigma_bar->GetValues(el_id, ir, sigma_bar_q); }
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
const DenseMatrix &Jtr_i = Jtr(i);
|
||||
metric->SetTargetJacobian(Jtr_i);
|
||||
CalcInverse(Jtr_i, Jrt);
|
||||
@@ -2516,16 +2606,24 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
coeff0->Eval(*Tpr, ip);
|
||||
}
|
||||
|
||||
// Contribution from the adaptive limiting term.
|
||||
if (adaptive_limiting)
|
||||
{
|
||||
const double diff = zeta_q(i) - zeta0_q(i);
|
||||
val += coeff_zeta->Eval(*Tpr, ip) * lim_normal * diff * diff;
|
||||
}
|
||||
|
||||
// Contribution from the surface fitting term.
|
||||
if (surface_fit)
|
||||
{
|
||||
val += coeff_sigma->Eval(*Tpr, ip) * sigma_normal *
|
||||
sigma_bar_q(i) * sigma_bar_q(i);
|
||||
}
|
||||
|
||||
energy += weight * val;
|
||||
}
|
||||
delete Tpr;
|
||||
|
||||
delete Tpr;
|
||||
return energy;
|
||||
}
|
||||
|
||||
@@ -2747,7 +2845,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (coeff1 || coeff0 || zeta || exact_action)
|
||||
if (coeff1 || coeff0 || zeta || sigma || exact_action)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
@@ -2829,7 +2927,8 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
if (zeta) { AssembleElemVecAdaptLim(el, weights, *Tpr, ir, PMatO); }
|
||||
if (zeta) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
|
||||
if (sigma) { AssembleElemVecSurfFit(el, *Tpr, ir, weights, PMatO); }
|
||||
|
||||
delete Tpr;
|
||||
}
|
||||
@@ -2881,7 +2980,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (coeff1 || coeff0 || zeta)
|
||||
if (coeff1 || coeff0 || zeta || sigma)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
@@ -2935,21 +3034,20 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
if (zeta) { AssembleElemGradAdaptLim(el, weights, *Tpr, ir, elmat); }
|
||||
if (zeta) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
|
||||
if (sigma) { AssembleElemGradSurfFit(el, *Tpr, ir, weights, elmat); }
|
||||
|
||||
delete Tpr;
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
const Vector &weights,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &weights,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
if (zeta == NULL) { return; }
|
||||
|
||||
const int dof = el.GetDof(), dim = el.GetDim();
|
||||
Vector shape(dof), zeta_e, zeta_q, zeta0_q;
|
||||
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
|
||||
Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp);
|
||||
|
||||
Array<int> dofs;
|
||||
zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
@@ -2967,7 +3065,6 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
|
||||
Vector zeta_grad_q(dim);
|
||||
|
||||
const int nqp = weights.Size();
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
@@ -2980,15 +3077,13 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
const Vector &weights,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &weights,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
if (zeta == NULL) { return; }
|
||||
|
||||
const int dof = el.GetDof(), dim = el.GetDim();
|
||||
Vector shape(dof), zeta_e, zeta_q, zeta0_q;
|
||||
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
|
||||
Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp);
|
||||
|
||||
Array<int> dofs;
|
||||
zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
|
||||
@@ -3014,7 +3109,6 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
Vector zeta_grad_q(dim);
|
||||
DenseMatrix zeta_grad_grad_q(dim, dim);
|
||||
|
||||
const int nqp = weights.Size();
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
@@ -3043,6 +3137,169 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir_quad,
|
||||
const Vector &weights,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int el_id = Tpr.ElementNo;
|
||||
const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id);
|
||||
|
||||
const int dof_x = el_x.GetDof(), dim = el_x.GetDim(),
|
||||
dof_s = el_s.GetDof(), nqp = ir_quad.GetNPoints();
|
||||
|
||||
Vector sigma_e, sigma_bar_e;
|
||||
Vector sigma_bar_q;
|
||||
Array<int> dofs;
|
||||
sigma->FESpace()->GetElementDofs(el_id, dofs);
|
||||
sigma->GetSubVector(dofs, sigma_e);
|
||||
sigma_bar->GetSubVector(dofs, sigma_bar_e);
|
||||
sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q);
|
||||
|
||||
// Project the gradient of sigma in the same space.
|
||||
// The FE coefficients of the gradient go in sigma_grad_e.
|
||||
DenseMatrix sigma_grad_e(dof_s, dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
|
||||
// Gradient of sigma_bar.
|
||||
DenseMatrix sigma_bar_grad_e(dof_s, dim);
|
||||
Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim);
|
||||
grad_phys.Mult(sigma_bar_e, ptr);
|
||||
|
||||
Vector shape_x(dof_x), shape_s(dof_s), grad_q(dim);
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir_quad.IntPoint(q);
|
||||
Tpr.SetIntPoint(&ip);
|
||||
el_s.CalcShape(ip, shape_s);
|
||||
|
||||
// Grad of sigma_bar at the current quad point.
|
||||
sigma_bar_grad_e.MultTranspose(shape_s, grad_q);
|
||||
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
if ((*sigma_marker)[dofs[s]] == false) { continue; }
|
||||
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
// Grad of sigma must be taken at the active DOFs.
|
||||
grad_q(d) += sigma_grad_e(s, d) * shape_s(s);
|
||||
}
|
||||
}
|
||||
|
||||
grad_q *= 2.0 * sigma_normal * coeff_sigma->Eval(Tpr, ip) *
|
||||
weights(q) * sigma_bar_q(q);
|
||||
|
||||
el_x.CalcShape(ip, shape_x);
|
||||
AddMultVWt(shape_x, grad_q, mat);
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir_quad,
|
||||
const Vector &weights,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int el_id = Tpr.ElementNo, nqp = ir_quad.GetNPoints();
|
||||
const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id);
|
||||
|
||||
const int dof_x = el_x.GetDof(), dim = el_x.GetDim(),
|
||||
dof_s = el_s.GetDof();
|
||||
|
||||
Vector sigma_e, sigma_bar_e;
|
||||
Vector sigma_bar_q;
|
||||
|
||||
Array<int> dofs;
|
||||
sigma->FESpace()->GetElementDofs(el_id, dofs);
|
||||
sigma->GetSubVector(dofs, sigma_e);
|
||||
sigma_bar->GetSubVector(dofs, sigma_bar_e);
|
||||
sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q);
|
||||
|
||||
// Project the gradient of sigma in the same space.
|
||||
// The FE coefficients of the gradient go in sigma_grad_e.
|
||||
DenseMatrix sigma_grad_e(dof_s, dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
|
||||
// Gradient of sigma_bar.
|
||||
DenseMatrix sigma_bar_grad_e(dof_s, dim);
|
||||
Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim);
|
||||
grad_phys.Mult(sigma_bar_e, ptr);
|
||||
|
||||
// Project the gradient of each gradient of sigma in the same space.
|
||||
// The FE coefficients of the second derivatives go in sigma_grad_grad_e.
|
||||
DenseMatrix sigma_grad_grad_e(dof_s * dim, dim);
|
||||
Mult(grad_phys, sigma_grad_e, sigma_grad_grad_e);
|
||||
|
||||
// Project the gradient of each gradient of sigma in the same space.
|
||||
// The FE coefficients of the second derivatives go in sigma_grad_grad_e.
|
||||
DenseMatrix sigma_bar_grad_grad_e(dof_s * dim, dim);
|
||||
Mult(grad_phys, sigma_bar_grad_e, sigma_bar_grad_grad_e);
|
||||
// Reshape to be more convenient later (no change in the data).
|
||||
sigma_bar_grad_grad_e.SetSize(dof_s, dim * dim);
|
||||
|
||||
DenseMatrix sigma_bar_grad_grad_q(dim, dim);
|
||||
|
||||
Vector shape_x(dof_x), shape_s(dof_s), sigma_bar_grad_q(dim);
|
||||
DenseMatrix dshape_s(dof_s, dim);
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir_quad.IntPoint(q);
|
||||
Tpr.SetIntPoint(&ip);
|
||||
el_s.CalcShape(ip, shape_s);
|
||||
el_x.CalcShape(ip, shape_x);
|
||||
// We could reuse grad_phys, but this is more accurate.
|
||||
el_s.CalcPhysDShape(Tpr, dshape_s);
|
||||
|
||||
// Grad of sigma_bar at the current quad point.
|
||||
sigma_bar_grad_e.MultTranspose(shape_s, sigma_bar_grad_q);
|
||||
|
||||
// Grad-grad of sigma_bar at the current quad point.
|
||||
Vector gg_ptr(sigma_bar_grad_grad_q.GetData(), dim * dim);
|
||||
sigma_bar_grad_grad_e.MultTranspose(shape_s, gg_ptr);
|
||||
|
||||
// Loops over the local matrix.
|
||||
const double w = 2.0 * sigma_normal *
|
||||
coeff_sigma->Eval(Tpr, ip) * weights(q);
|
||||
for (int i = 0; i < dof_x * dim; i++)
|
||||
{
|
||||
const int idof = i % dof_x, idim = i / dof_x;
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
const int jdof = j % dof_x, jdim = j / dof_x;
|
||||
|
||||
double Di = sigma_bar_grad_q(idim),
|
||||
Dj = sigma_bar_grad_q(jdim),
|
||||
DD = sigma_bar_grad_grad_q(idim, jdim);
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
if ((*sigma_marker)[dofs[s]] == false) { continue; }
|
||||
|
||||
Di += sigma_grad_e(s, idim) * shape_s(s);
|
||||
Dj += sigma_grad_e(s, jdim) * shape_s(s);
|
||||
DD += sigma_grad_e(s, idim) * dshape_s(s, jdim) +
|
||||
sigma_grad_grad_e(dof_s * idim + s, jdim) * shape_s(s) +
|
||||
sigma_grad_e(s, jdim) * dshape_s(s, idim);
|
||||
}
|
||||
const double entry = w * (Di * Dj + sigma_bar_q(q) * DD) *
|
||||
shape_x(idof) * shape_x(jdof);
|
||||
|
||||
mat(i, j) += entry;
|
||||
if (i != j) { mat(j, i) += entry; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double TMOP_Integrator::GetFDDerivative(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
Vector &elfun, const int dofidx,
|
||||
@@ -3103,8 +3360,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
}
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting (exact derivatives).
|
||||
if (zeta)
|
||||
// Contributions from adaptive limiting, surface fitting (exact derivatives).
|
||||
if (zeta || sigma)
|
||||
{
|
||||
const IntegrationRule &ir = ActionIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -3125,7 +3382,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
}
|
||||
|
||||
PMatO.UseExternalData(elvect.GetData(), dof, dim);
|
||||
AssembleElemVecAdaptLim(el, weights, Tpr, ir, PMatO);
|
||||
if (zeta) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
|
||||
if (sigma) { AssembleElemVecSurfFit(el, Tpr, ir, weights, PMatO); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3200,7 +3458,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting.
|
||||
if (zeta)
|
||||
if (zeta || sigma)
|
||||
{
|
||||
const IntegrationRule &ir = GradientIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -3220,35 +3478,41 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
weights(q) = ir.IntPoint(q).weight * Jtr(q).Det();
|
||||
}
|
||||
|
||||
AssembleElemGradAdaptLim(el, weights, Tpr, ir, elmat);
|
||||
if (zeta) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
|
||||
if (sigma) { AssembleElemGradSurfFit(el, Tpr, ir, weights, elmat); }
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
|
||||
{
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal, sigma_normal);
|
||||
metric_normal = 1.0 / metric_normal;
|
||||
lim_normal = 1.0 / lim_normal;
|
||||
//if (sigma) { sigma_normal = 1.0 / sigma_normal; }
|
||||
if (sigma) { sigma_normal = lim_normal; }
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
|
||||
{
|
||||
double loc[2];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1]);
|
||||
double rdc[2];
|
||||
MPI_Allreduce(loc, rdc, 2, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm());
|
||||
double loc[3];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]);
|
||||
double rdc[3];
|
||||
MPI_Allreduce(loc, rdc, 3, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm());
|
||||
metric_normal = 1.0 / rdc[0];
|
||||
lim_normal = 1.0 / rdc[1];
|
||||
// if (sigma) { sigma_normal = 1.0 / rdc[2]; }
|
||||
if (sigma) { sigma_normal = lim_normal; }
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
double &metric_energy,
|
||||
double &lim_energy)
|
||||
double &lim_energy,
|
||||
double &sigma_energy)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
Vector x_vals;
|
||||
Vector x_vals, sigma_bar_q;
|
||||
const FiniteElementSpace* const fes = x.FESpace();
|
||||
|
||||
const int dim = fes->GetMesh()->Dimension();
|
||||
@@ -3258,6 +3522,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
|
||||
metric_energy = 0.0;
|
||||
lim_energy = 0.0;
|
||||
sigma_energy = 0.0;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -3273,6 +3538,8 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
|
||||
targetC->ComputeElementTargets(i, *fe, ir, x_vals, Jtr);
|
||||
|
||||
if (sigma) { sigma_bar->GetValues(i, ir, sigma_bar_q); }
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
@@ -3286,8 +3553,15 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
|
||||
metric_energy += weight * metric->EvalW(Jpt);
|
||||
lim_energy += weight;
|
||||
|
||||
// Normalization of the surface fitting term.
|
||||
if (sigma)
|
||||
{
|
||||
sigma_energy += weight * sigma_bar_q(q) * sigma_bar_q(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetC->ContainsVolumeInfo() == false)
|
||||
{
|
||||
// Special case when the targets don't contain volumetric information.
|
||||
@@ -3336,6 +3610,17 @@ void TMOP_Integrator::UpdateAfterMeshPositionChange(const Vector &new_x)
|
||||
}
|
||||
// Update zeta if adaptive limiting is enabled.
|
||||
if (zeta) { adapt_eval->ComputeAtNewPosition(new_x, *zeta); }
|
||||
|
||||
// Update sigma if surface fitting is enabled.
|
||||
if (sigma)
|
||||
{
|
||||
sigma_eval->ComputeAtNewPosition(new_x, *sigma);
|
||||
// Update the restricted sigma.
|
||||
for (int i = 0; i < sigma_marker->Size(); i++)
|
||||
{
|
||||
(*sigma_bar)(i) = ((*sigma_marker)[i] == true) ? (*sigma)(i) : 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Integrator::ComputeFDh(const Vector &x, const FiniteElementSpace &fes)
|
||||
|
||||
+105
-12
@@ -592,6 +592,27 @@ public:
|
||||
virtual int Id() const { return 321; }
|
||||
};
|
||||
|
||||
/// 3D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_328 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_Metric_328(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_Metric_301),
|
||||
sz_metric(new TMOP_Metric_316)
|
||||
{
|
||||
// (1-gamma) mu_301 + gamma mu_316
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual ~TMOP_Metric_328() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// 3D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_332 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
@@ -619,6 +640,7 @@ public:
|
||||
class TMOP_Metric_333 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
@@ -632,12 +654,30 @@ public:
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual int Id() const { return 333; }
|
||||
double GetGamma() const { return gamma; }
|
||||
|
||||
virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// 3D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_334 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_Metric_334(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_Metric_303),
|
||||
sz_metric(new TMOP_Metric_316)
|
||||
{
|
||||
// (1-gamma) mu_303 + gamma mu_316
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual ~TMOP_Metric_334() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// Shifted barrier form of 3D metric 16 (volume, ideal barrier metric), 3D
|
||||
class TMOP_Metric_352 : public TMOP_QualityMetric
|
||||
{
|
||||
@@ -897,9 +937,6 @@ protected:
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Comm comm;
|
||||
bool Parallel() const { return (comm != MPI_COMM_NULL); }
|
||||
#else
|
||||
bool Parallel() const { return false; }
|
||||
#endif
|
||||
|
||||
// should be called only if avg_volume == 0.0, i.e. avg_volume is not
|
||||
@@ -936,6 +973,13 @@ public:
|
||||
#endif
|
||||
virtual ~TargetConstructor() { }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
bool Parallel() const { return (comm != MPI_COMM_NULL); }
|
||||
MPI_Comm GetComm() const { return comm; }
|
||||
#else
|
||||
bool Parallel() const { return false; }
|
||||
#endif
|
||||
|
||||
/** @brief Set the nodes to be used in the target-matrix construction.
|
||||
|
||||
This method should be called every time the target nodes are updated
|
||||
@@ -1296,6 +1340,13 @@ protected:
|
||||
Coefficient *coeff_zeta; // Not owned.
|
||||
AdaptivityEvaluator *adapt_eval; // Not owned.
|
||||
|
||||
// Surface fitting.
|
||||
GridFunction *sigma, *sigma_bar; // Owned. Updated by sigma_eval.
|
||||
const Array<bool> *sigma_marker; // Not owned.
|
||||
Coefficient *coeff_sigma; // Not owned.
|
||||
AdaptivityEvaluator *sigma_eval; // Not owned.
|
||||
double sigma_normal;
|
||||
|
||||
DiscreteAdaptTC *discr_tc;
|
||||
|
||||
// Parameters for FD-based Gradient & Hessian calculation.
|
||||
@@ -1364,7 +1415,8 @@ protected:
|
||||
} PA;
|
||||
|
||||
void ComputeNormalizationEnergies(const GridFunction &x,
|
||||
double &metric_energy, double &lim_energy);
|
||||
double &metric_energy, double &lim_energy,
|
||||
double &sigma_energy);
|
||||
|
||||
void AssembleElementVectorExact(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
@@ -1383,12 +1435,25 @@ protected:
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun, DenseMatrix &elmat);
|
||||
|
||||
void AssembleElemVecAdaptLim(const FiniteElement &el, const Vector &weights,
|
||||
void AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir, DenseMatrix &m);
|
||||
void AssembleElemGradAdaptLim(const FiniteElement &el, const Vector &weights,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &weights, DenseMatrix &mat);
|
||||
void AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir, DenseMatrix &m);
|
||||
const IntegrationRule &ir,
|
||||
const Vector &weights, DenseMatrix &m);
|
||||
|
||||
// First derivative of the surface fitting term.
|
||||
void AssembleElemVecSurfFit(const FiniteElement &el_x,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir_quad,
|
||||
const Vector &weights, DenseMatrix &mat);
|
||||
// Second derivative of the surface fitting term.
|
||||
void AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
IsoparametricTransformation &Tpr,
|
||||
const IntegrationRule &ir_quad,
|
||||
const Vector &weights, DenseMatrix &mat);
|
||||
|
||||
double GetFDDerivative(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
@@ -1470,6 +1535,8 @@ public:
|
||||
nodes0(NULL), coeff0(NULL),
|
||||
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
|
||||
zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL),
|
||||
sigma(NULL), sigma_bar(NULL), sigma_marker(NULL), coeff_sigma(NULL),
|
||||
sigma_eval(NULL), sigma_normal(1.0),
|
||||
discr_tc(dynamic_cast<DiscreteAdaptTC *>(tc)),
|
||||
fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false)
|
||||
{ PA.enabled = false; }
|
||||
@@ -1522,7 +1589,7 @@ public:
|
||||
|
||||
Adds the term @f$ \int c (z(x) - z_0(x_0))^2 @f$, where z0(x0) is a given
|
||||
function on the starting mesh, and z(x) is its image on the new mesh.
|
||||
Minimizing this, means that a node at x0 is allowed to move to a
|
||||
Minimizing this term means that a node at x0 is allowed to move to a
|
||||
position x(x0) only if z(x) ~ z0(x0).
|
||||
Such term can be used for tangential mesh relaxation.
|
||||
|
||||
@@ -1537,6 +1604,32 @@ public:
|
||||
AdaptivityEvaluator &ae);
|
||||
#endif
|
||||
|
||||
/** @brief Fitting of certain DOFs to the zero level set of a function.
|
||||
|
||||
Having a level set function s0(x0) on the starting mesh, and a set of
|
||||
marked nodes (or DOFs), we move these nodes to the zero level set of s0.
|
||||
If s(x) is the image of s0(x0) on the current mesh, this function adds to
|
||||
the TMOP functional the term @f$ \int c \bar{s}(x))^2 @f$, where
|
||||
@f$\bar{s}(x)@f$ is the restriction of s(x) on the aligned DOFs.
|
||||
Minimizing this term means that a marked node at x0 is allowed to move to
|
||||
a position x(x0) only if s(x) ~ 0.
|
||||
Such term can be used for surface fitting and tangential relaxation.
|
||||
|
||||
@param[in] s0 The level set function on the initial mesh.
|
||||
@param[in] smarker Indicates which DOFs will be aligned.
|
||||
@param[in] coeff Coefficient c for the above integral.
|
||||
@param[in] ae AdaptivityEvaluator to compute s(x) from s0(x0). */
|
||||
void EnableSurfaceFitting(const GridFunction &s0,
|
||||
const Array<bool> &smarker, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae);
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for surface fitting.
|
||||
void EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae);
|
||||
#endif
|
||||
void GetSurfaceFittingErrors(double &err_avg, double &err_max);
|
||||
|
||||
/// Update the original/reference nodes used for limiting.
|
||||
void SetLimitingNodes(const GridFunction &n0) { nodes0 = &n0; }
|
||||
|
||||
|
||||
+8
-6
@@ -394,12 +394,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
|
||||
|
||||
const CoarseFineTransformations &dtrans =
|
||||
meshcopy.ncmesh->GetDerefinementTransforms();
|
||||
Table coarse_to_fine;
|
||||
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
|
||||
|
||||
Table coarse_to_fine;
|
||||
dtrans.MakeCoarseToFineTable(coarse_to_fine);
|
||||
|
||||
Array<int> tabrow;
|
||||
for (int pe = 0; pe < coarse_to_fine.Size(); pe++)
|
||||
{
|
||||
Array<int> tabrow;
|
||||
coarse_to_fine.GetRow(pe, tabrow);
|
||||
int nchild = tabrow.Size();
|
||||
double parent_energy = coarse_energy(pe);
|
||||
@@ -446,12 +447,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
|
||||
|
||||
const CoarseFineTransformations &dtrans =
|
||||
meshcopy.pncmesh->GetDerefinementTransforms();
|
||||
Table coarse_to_fine;
|
||||
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
|
||||
|
||||
Table coarse_to_fine;
|
||||
dtrans.MakeCoarseToFineTable(coarse_to_fine);
|
||||
|
||||
Array<int> tabrow;
|
||||
for (int pe = 0; pe < meshcopy.GetNE(); pe++)
|
||||
{
|
||||
Array<int> tabrow;
|
||||
coarse_to_fine.GetRow(pe, tabrow);
|
||||
int nchild = tabrow.Size();
|
||||
double parent_energy = coarse_energy(pe);
|
||||
|
||||
@@ -70,6 +70,10 @@ public:
|
||||
explicit inline Array(int asize)
|
||||
: size(asize) { asize > 0 ? data.New(asize) : data.Reset(); }
|
||||
|
||||
/// Creates array of @a asize elements with a given MemoryType
|
||||
inline Array(int asize, MemoryType mt)
|
||||
: size(asize) { asize > 0 ? data.New(asize, mt) : data.Reset(mt); }
|
||||
|
||||
/** @brief Creates array using an existing c-array of asize elements;
|
||||
allocsize is set to -asize to indicate that the data will not
|
||||
be deleted. */
|
||||
|
||||
+149
-12
@@ -183,6 +183,42 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct RajaCuWrap;
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap1D<BLCK>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap2D(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap3D(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
|
||||
@@ -248,6 +284,43 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
|
||||
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct RajaHipWrap;
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap1D<BLCK>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap2D(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap3D(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// RAJA OpenMP backend
|
||||
@@ -333,6 +406,42 @@ void CuWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct CuWrap;
|
||||
|
||||
template <>
|
||||
struct CuWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
CuWrap1D<BLCK>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CuWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
CuWrap2D(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CuWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
CuWrap3D(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_CUDA
|
||||
|
||||
|
||||
@@ -392,6 +501,42 @@ void HipWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct HipWrap;
|
||||
|
||||
template <>
|
||||
struct HipWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap1D<BLCK>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HipWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap2D(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HipWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap3D(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_HIP
|
||||
|
||||
|
||||
@@ -413,9 +558,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::RAJA_CUDA is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_CUDA))
|
||||
{
|
||||
if (DIM == 1) { return RajaCuWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z, G); }
|
||||
return RajaCuWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -423,9 +566,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::RAJA_HIP is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_HIP))
|
||||
{
|
||||
if (DIM == 1) { return RajaHipWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z, G); }
|
||||
return RajaHipWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -433,9 +574,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::CUDA is allowed, use it
|
||||
if (Device::Allows(Backend::CUDA))
|
||||
{
|
||||
if (DIM == 1) { return CuWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return CuWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return CuWrap3D(N, d_body, X, Y, Z, G); }
|
||||
return CuWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -443,9 +582,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::HIP is allowed, use it
|
||||
if (Device::Allows(Backend::HIP))
|
||||
{
|
||||
if (DIM == 1) { return HipWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return HipWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return HipWrap3D(N, d_body, X, Y, Z, G); }
|
||||
return HipWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -175,8 +175,9 @@ protected:
|
||||
// Copy{From,To}, {ReadWrite,Read,Write}.
|
||||
|
||||
public:
|
||||
/// Default constructor: no initialization.
|
||||
Memory() { }
|
||||
/** Default constructor, sets the host pointer to nullptr and the metadata to
|
||||
meaningful default values. */
|
||||
Memory() { Reset(); }
|
||||
|
||||
/// Copy constructor: default.
|
||||
Memory(const Memory &orig) = default;
|
||||
@@ -368,8 +369,7 @@ public:
|
||||
be updated as described above. */
|
||||
inline void SetDeviceMemoryType(MemoryType d_mt);
|
||||
|
||||
/** @brief Delete the owned pointers. The Memory is not reset by this method,
|
||||
i.e. it will, generally, not be Empty() after this call. */
|
||||
/** @brief Delete the owned pointers and reset the Memory object. */
|
||||
inline void Delete();
|
||||
|
||||
/** @brief Delete the device pointer, if owned. If @a copy_to_host is true
|
||||
@@ -986,6 +986,7 @@ inline void Memory<T>::Delete()
|
||||
{
|
||||
if (flags & OWNS_HOST) { delete [] h_ptr; }
|
||||
}
|
||||
Reset(h_mt);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -35,10 +35,6 @@ Table::Table(const Table &table)
|
||||
I.CopyFrom(table.I, size+1);
|
||||
J.CopyFrom(table.J, nnz);
|
||||
}
|
||||
else
|
||||
{
|
||||
I.Reset(); J.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
Table& Table::operator=(const Table &rhs)
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ protected:
|
||||
|
||||
public:
|
||||
/// Creates an empty table
|
||||
Table() { size = -1; I.Reset(); J.Reset(); }
|
||||
Table() { size = -1; }
|
||||
|
||||
/// Copy constructor
|
||||
Table(const Table &);
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
/** Create a table from a list of connections, see MakeFromList(). */
|
||||
Table(int nrows, Array<Connection> &list) : size(-1)
|
||||
{ I.Reset(); J.Reset(); MakeFromList(nrows, list); }
|
||||
{ MakeFromList(nrows, list); }
|
||||
|
||||
/** Create a table with one entry per row with column indices given
|
||||
by 'partitioning'. */
|
||||
|
||||
@@ -604,11 +604,16 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat)
|
||||
mfem_error("Hypre version 2.16+ is required when using AmgX \n");
|
||||
#endif
|
||||
|
||||
// Ensure HypreParMatrix is on the host
|
||||
A.HostRead();
|
||||
|
||||
hypre_ParCSRMatrix * A_ptr =
|
||||
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(A);
|
||||
|
||||
hypre_CSRMatrix *A_csr = hypre_MergeDiagAndOffd(A_ptr);
|
||||
|
||||
A.HypreRead();
|
||||
|
||||
Array<double> loc_A(A_csr->data, (int)A_csr->num_nonzeros);
|
||||
const Array<HYPRE_Int> loc_I(A_csr->i, (int)A_csr->num_rows+1);
|
||||
|
||||
|
||||
+1
-20
@@ -70,10 +70,7 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
DenseMatrix::DenseMatrix() : Matrix(0)
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
DenseMatrix::DenseMatrix() : Matrix(0) { }
|
||||
|
||||
DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
|
||||
{
|
||||
@@ -84,10 +81,6 @@ DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
|
||||
data.New(hw);
|
||||
std::memcpy(data, m.data, sizeof(double)*hw);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix::DenseMatrix(int s) : Matrix(s)
|
||||
@@ -98,10 +91,6 @@ DenseMatrix::DenseMatrix(int s) : Matrix(s)
|
||||
data.New(s*s);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
|
||||
@@ -114,10 +103,6 @@ DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
|
||||
data.New(capacity);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
|
||||
@@ -137,10 +122,6 @@ DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::SetSize(int h, int w)
|
||||
|
||||
@@ -753,7 +753,6 @@ public:
|
||||
DenseTensor()
|
||||
{
|
||||
nk = 0;
|
||||
tdata.Reset();
|
||||
}
|
||||
|
||||
DenseTensor(int i, int j, int k)
|
||||
@@ -787,10 +786,6 @@ public:
|
||||
tdata.New(size, other.tdata.GetMemoryType());
|
||||
tdata.CopyFrom(other.tdata, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
tdata.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
int SizeI() const { return Mk.Height(); }
|
||||
|
||||
+62
-11
@@ -127,18 +127,19 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : Vector()
|
||||
// Call the move constructor on the "compatible" temp vector
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : HypreParVector(
|
||||
y.CreateCompatibleVector())
|
||||
{
|
||||
x = hypre_ParVectorCreate(y.x -> comm, y.x -> global_size,
|
||||
y.x -> partitioning);
|
||||
hypre_ParVectorInitialize(x);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(x,0);
|
||||
#endif
|
||||
hypre_ParVectorSetDataOwner(x,1);
|
||||
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
|
||||
_SetDataAndSize_();
|
||||
own_ParVector = 1;
|
||||
// Deep copy the local data
|
||||
hypre_SeqVectorCopy(hypre_ParVectorLocalVector(y.x),
|
||||
hypre_ParVectorLocalVector(x));
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(HypreParVector &&y)
|
||||
{
|
||||
own_ParVector = 0;
|
||||
*this = std::move(y);
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(const HypreParMatrix &A,
|
||||
@@ -178,6 +179,23 @@ HypreParVector::HypreParVector(ParFiniteElementSpace *pfes)
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
HypreParVector HypreParVector::CreateCompatibleVector() const
|
||||
{
|
||||
HypreParVector result;
|
||||
result.x = hypre_ParVectorCreate(x -> comm, x -> global_size,
|
||||
x -> partitioning);
|
||||
hypre_ParVectorInitialize(result.x);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(result.x,0);
|
||||
#endif
|
||||
hypre_ParVectorSetDataOwner(result.x,1);
|
||||
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(result.x),1);
|
||||
result._SetDataAndSize_();
|
||||
result.own_ParVector = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
|
||||
{
|
||||
if (own_ParVector) { hypre_ParVectorDestroy(x); }
|
||||
@@ -216,6 +234,18 @@ HypreParVector& HypreParVector::operator=(const HypreParVector &y)
|
||||
return *this;
|
||||
}
|
||||
|
||||
HypreParVector& HypreParVector::operator=(HypreParVector &&y)
|
||||
{
|
||||
// If the argument vector owns its data, then the calling vector will as well
|
||||
WrapHypreParVector(static_cast<hypre_ParVector*>(y), y.own_ParVector);
|
||||
// Either way the argument vector will no longer own its data
|
||||
y.own_ParVector = 0;
|
||||
y.x = nullptr;
|
||||
y.data.Reset();
|
||||
y.size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void HypreParVector::SetData(double *data_)
|
||||
{
|
||||
hypre_VectorData(hypre_ParVectorLocalVector(x)) = data_;
|
||||
@@ -303,6 +333,18 @@ void HypreParVector::Print(const char *fname) const
|
||||
hypre_ParVectorPrint(x,fname);
|
||||
}
|
||||
|
||||
void HypreParVector::Read(MPI_Comm comm, const char *fname)
|
||||
{
|
||||
if (own_ParVector)
|
||||
{
|
||||
hypre_ParVectorDestroy(x);
|
||||
}
|
||||
data.Delete();
|
||||
x = hypre_ParVectorRead(comm, fname);
|
||||
own_ParVector = true;
|
||||
_SetDataAndSize_();
|
||||
}
|
||||
|
||||
HypreParVector::~HypreParVector()
|
||||
{
|
||||
if (own_ParVector)
|
||||
@@ -1561,9 +1603,16 @@ HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
|
||||
}
|
||||
|
||||
// Construct cpts_global array on hypre matrix structure
|
||||
#if (MFEM_HYPRE_VERSION > 22300) || (MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
|
||||
HYPRE_BigInt cpts_global[2];
|
||||
|
||||
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
|
||||
CF_marker, NULL, cpts_global);
|
||||
#else
|
||||
HYPRE_BigInt *cpts_global;
|
||||
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
|
||||
CF_marker, NULL, &cpts_global);
|
||||
#endif
|
||||
|
||||
// Extract submatrix into *submat
|
||||
#ifdef hypre_IntArrayData
|
||||
@@ -1575,7 +1624,9 @@ HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
|
||||
"FF", &submat, threshold);
|
||||
#endif
|
||||
|
||||
#if (MFEM_HYPRE_VERSION <= 22300) && !(MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
|
||||
mfem_hypre_TFree(cpts_global);
|
||||
#endif
|
||||
#ifdef hypre_IntArrayData
|
||||
hypre_IntArrayDestroy(CF_marker);
|
||||
#endif
|
||||
|
||||
+12
-1
@@ -141,8 +141,10 @@ public:
|
||||
allocated in the memory location HYPRE_MEMORY_DEVICE. */
|
||||
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, double *data_,
|
||||
HYPRE_BigInt *col, bool is_device_ptr = false);
|
||||
/// Creates vector compatible with y
|
||||
/// Creates a deep copy of @a y
|
||||
HypreParVector(const HypreParVector &y);
|
||||
/// Move constructor for HypreParVector. "Steals" data from its argument.
|
||||
HypreParVector(HypreParVector&& other);
|
||||
/// Creates vector compatible with (i.e. in the domain of) A or A^T
|
||||
explicit HypreParVector(const HypreParMatrix &A, int transpose = 0);
|
||||
/// Creates vector wrapping y
|
||||
@@ -150,6 +152,10 @@ public:
|
||||
/// Create a true dof parallel vector on a given ParFiniteElementSpace
|
||||
explicit HypreParVector(ParFiniteElementSpace *pfes);
|
||||
|
||||
/// \brief Constructs a @p HypreParVector *compatible* with the calling vector
|
||||
/// - meaning that it will be the same size and have the same partitioning.
|
||||
HypreParVector CreateCompatibleVector() const;
|
||||
|
||||
/// MPI communicator
|
||||
MPI_Comm GetComm() const { return x->comm; }
|
||||
|
||||
@@ -192,6 +198,8 @@ public:
|
||||
HypreParVector& operator= (double d);
|
||||
/// Define '=' for hypre vectors.
|
||||
HypreParVector& operator= (const HypreParVector &y);
|
||||
/// Move assignment
|
||||
HypreParVector& operator= (HypreParVector &&y);
|
||||
|
||||
using Vector::Read;
|
||||
|
||||
@@ -252,6 +260,9 @@ public:
|
||||
/// Prints the locally owned rows in parallel
|
||||
void Print(const char *fname) const;
|
||||
|
||||
/// Reads a HypreParVector from files saved with HypreParVector::Print
|
||||
void Read(MPI_Comm comm, const char *fname);
|
||||
|
||||
/// Calls hypre's destroy function
|
||||
~HypreParVector();
|
||||
|
||||
|
||||
+55
-2
@@ -160,7 +160,7 @@ double Norml2(const int size, const T *data)
|
||||
data of the input and output vectors. */
|
||||
template<typename TA, typename TX, typename TY>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
|
||||
void Mult(const int height, const int width, const TA *data, const TX *x, TY *y)
|
||||
{
|
||||
if (width == 0)
|
||||
{
|
||||
@@ -170,7 +170,7 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
|
||||
}
|
||||
return;
|
||||
}
|
||||
TA *d_col = data;
|
||||
const TA *d_col = data;
|
||||
TX x_col = x[0];
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
@@ -188,6 +188,35 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Matrix transpose vector multiplication: y = At x, where the matrix A
|
||||
is of size @a height x @a width with given @a data, while @a x and @a y
|
||||
specify the data of the input and output vectors. */
|
||||
template<typename TA, typename TX, typename TY>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void MultTranspose(const int height, const int width, const TA *data,
|
||||
const TX *x, TY *y)
|
||||
{
|
||||
if (height == 0)
|
||||
{
|
||||
for (int row = 0; row < width; row++)
|
||||
{
|
||||
y[row] = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
TY *y_off = y;
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
TY val = 0.0;
|
||||
for (int j = 0; j < height; ++j)
|
||||
{
|
||||
val += x[j] * data[i * height + j];
|
||||
}
|
||||
*y_off = val;
|
||||
y_off++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Symmetrize a square matrix with given @a size and @a data: A -> (A+A^T)/2.
|
||||
template<typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
@@ -353,6 +382,30 @@ void MultABt(const int Aheight, const int Awidth, const int Bheight,
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Multiply the transpose of a matrix of size @a Aheight x @a Awidth
|
||||
and data @a Adata with a matrix of size @a Aheight x @a Bwidth and data @a
|
||||
Bdata: At * B. Return the result in a matrix with data @a AtBdata. */
|
||||
template<typename TA, typename TB, typename TC>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void MultAtB(const int Aheight, const int Awidth, const int Bwidth,
|
||||
const TA *Adata, const TB *Bdata, TC *AtBdata)
|
||||
{
|
||||
TC *c = AtBdata;
|
||||
for (int i = 0; i < Bwidth; ++i)
|
||||
{
|
||||
for (int j = 0; j < Awidth; ++j)
|
||||
{
|
||||
TC val = 0.0;
|
||||
for (int k = 0; k < Aheight; ++k)
|
||||
{
|
||||
val += Adata[j * Aheight + k] * Bdata[i * Aheight + k];
|
||||
}
|
||||
*c = val;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the spectrum of the matrix of size dim with given @a data, returning
|
||||
/// the eigenvalues in the array @a lambda and the eigenvectors in the array @a
|
||||
/// vec (listed consecutively).
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ public:
|
||||
void FormDiscreteOperator(Operator* &A);
|
||||
|
||||
/// Prints operator with input size n and output size m in Matlab format.
|
||||
void PrintMatlab(std::ostream & out, int n = 0, int m = 0) const;
|
||||
void PrintMatlab(std::ostream & out, int n, int m = 0) const;
|
||||
|
||||
/// Prints operator in Matlab format.
|
||||
virtual void PrintMatlab(std::ostream & out) const;
|
||||
|
||||
@@ -558,8 +558,6 @@ PetscParVector::PetscParVector(MPI_Comm comm, const Operator &op,
|
||||
else /* Vector intended to be used with Place/ResetMemory calls */
|
||||
{
|
||||
size = loc;
|
||||
pdata.Reset();
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,8 +579,6 @@ PetscParVector::PetscParVector(const PetscParMatrix &A,
|
||||
PetscInt n;
|
||||
ierr = VecGetLocalSize(x,&n); PCHKERRQ(x,ierr);
|
||||
size = n;
|
||||
pdata.Reset();
|
||||
data.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -84,9 +84,9 @@ SparseMatrix::SparseMatrix(int nrows, int ncols)
|
||||
isSorted(false)
|
||||
{
|
||||
// We probably do not need to set the ownership flags here.
|
||||
I.Reset(); I.SetHostPtrOwner(true);
|
||||
J.Reset(); J.SetHostPtrOwner(true);
|
||||
A.Reset(); A.SetHostPtrOwner(true);
|
||||
I.SetHostPtrOwner(true);
|
||||
J.SetHostPtrOwner(true);
|
||||
A.SetHostPtrOwner(true);
|
||||
|
||||
for (int i = 0; i < nrows; i++)
|
||||
{
|
||||
@@ -229,9 +229,9 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph,
|
||||
}
|
||||
|
||||
// We probably do not need to set the ownership flags here.
|
||||
I.Reset(); I.SetHostPtrOwner(true);
|
||||
J.Reset(); J.SetHostPtrOwner(true);
|
||||
A.Reset(); A.SetHostPtrOwner(true);
|
||||
I.SetHostPtrOwner(true);
|
||||
J.SetHostPtrOwner(true);
|
||||
A.SetHostPtrOwner(true);
|
||||
}
|
||||
|
||||
current_row = -1;
|
||||
|
||||
+1
-8
@@ -17,10 +17,7 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0) { }
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
|
||||
{
|
||||
@@ -30,10 +27,6 @@ DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
|
||||
data.New((s*(s+1))/2);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DenseSymmetricMatrix::SetSize(int s)
|
||||
|
||||
+15
-6
@@ -39,21 +39,21 @@ namespace mfem
|
||||
Vector::Vector(const Vector &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
size = s;
|
||||
if (s > 0)
|
||||
{
|
||||
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
|
||||
size = s;
|
||||
data.New(s, v.data.GetMemoryType());
|
||||
data.CopyFrom(v.data, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = 0;
|
||||
data.Reset();
|
||||
}
|
||||
UseDevice(v.UseDevice());
|
||||
}
|
||||
|
||||
Vector::Vector(Vector &&v)
|
||||
{
|
||||
*this = std::move(v);
|
||||
}
|
||||
|
||||
void Vector::Load(std::istream **in, int np, int *dim)
|
||||
{
|
||||
int i, j, s;
|
||||
@@ -146,6 +146,15 @@ Vector &Vector::operator=(const Vector &v)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector &Vector::operator=(Vector &&v)
|
||||
{
|
||||
data = std::move(v.data);
|
||||
size = v.size;
|
||||
v.data.Reset();
|
||||
v.size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector &Vector::operator=(double value)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
|
||||
+11
-8
@@ -66,12 +66,16 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor for Vector. Sets size = 0 and data = NULL.
|
||||
Vector() { data.Reset(); size = 0; }
|
||||
/** Default constructor for Vector. Sets size = 0, and calls Memory::Reset on
|
||||
data through Memory<double>'s default constructor. */
|
||||
Vector(): size(0) { }
|
||||
|
||||
/// Copy constructor. Allocates a new data array and copies the data.
|
||||
Vector(const Vector &);
|
||||
|
||||
/// Move constructor. "Steals" data from its argument.
|
||||
Vector(Vector&& v);
|
||||
|
||||
/// @brief Creates vector of size s.
|
||||
/// @warning Entries are not initialized to zero!
|
||||
explicit Vector(int s);
|
||||
@@ -278,6 +282,9 @@ public:
|
||||
assignment operator. */
|
||||
Vector &operator=(const Vector &v);
|
||||
|
||||
/// Move assignment
|
||||
Vector &operator=(Vector&& v);
|
||||
|
||||
/// Redefine '=' for vector = constant.
|
||||
Vector &operator=(double value);
|
||||
|
||||
@@ -503,16 +510,12 @@ inline int CheckFinite(const double *v, const int n)
|
||||
|
||||
inline Vector::Vector(int s)
|
||||
{
|
||||
MFEM_ASSERT(s>=0,"Unexpected negative size.");
|
||||
size = s;
|
||||
if (s > 0)
|
||||
{
|
||||
size = s;
|
||||
data.New(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = 0;
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
inline void Vector::SetSize(int s)
|
||||
|
||||
@@ -123,7 +123,7 @@ EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu
|
||||
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
|
||||
EXAMPLE_TEST_DIRS := examples
|
||||
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib adjoint solvers shifted mtop parelag
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib adjoint solvers shifted mtop parelag autodiff
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools toys shifted)
|
||||
@@ -274,7 +274,7 @@ endif
|
||||
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
|
||||
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
|
||||
SUITESPARSE STRUMPACK GINKGO GNUTLS NETCDF PETSC SLEPC MPFR PUMI HIOP GSLIB\
|
||||
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER PARELAG BENCHMARK
|
||||
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER PARELAG BENCHMARK
|
||||
|
||||
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
|
||||
SLEPC_ERROR_MSG = $(if $(SLEPC_FOUND),,. SLEPC config not found: $(SLEPC_VARS))
|
||||
@@ -340,8 +340,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
|
||||
MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP\
|
||||
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD\
|
||||
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_AMGX MFEM_USE_MUMPS\
|
||||
MFEM_USE_CALIPER MFEM_USE_BENCHMARK MFEM_USE_PARELAG\
|
||||
MFEM_SOURCE_DIR MFEM_INSTALL_DIR
|
||||
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER MFEM_USE_BENCHMARK\
|
||||
MFEM_USE_PARELAG MFEM_SOURCE_DIR MFEM_INSTALL_DIR
|
||||
|
||||
# List of makefile variables that will be written to config.mk:
|
||||
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
|
||||
@@ -500,7 +500,7 @@ hpc:
|
||||
deps:
|
||||
rm -f $(BLD)deps.mk
|
||||
for i in $(RELSRC_FILES:.cpp=); do \
|
||||
$(DEP_CXX) $(MFEM_BUILD_FLAGS) -MM -MT $(BLD)$${i}.o $(SRC)$${i}.cpp\
|
||||
$(DEP_CXX) $(MFEM_BUILD_FLAGS) $(DEP_FLAGS) $(BLD)$${i}.o $(SRC)$${i}.cpp\
|
||||
>> $(BLD)deps.mk; done
|
||||
|
||||
check: lib
|
||||
@@ -679,6 +679,8 @@ status info:
|
||||
$(info MFEM_USE_SIMD = $(MFEM_USE_SIMD))
|
||||
$(info MFEM_USE_ADIOS2 = $(MFEM_USE_ADIOS2))
|
||||
$(info MFEM_USE_MKL_CPARDISO = $(MFEM_USE_MKL_CPARDISO))
|
||||
$(info MFEM_USE_ADFORWARD = $(MFEM_USE_ADFORWARD))
|
||||
$(info MFEM_USE_CODIPACK = $(MFEM_USE_CODIPACK))
|
||||
$(info MFEM_USE_BENCHMARK = $(MFEM_USE_BENCHMARK))
|
||||
$(info MFEM_USE_PARELAG = $(MFEM_USE_PARELAG))
|
||||
$(info MFEM_CXX = $(value MFEM_CXX))
|
||||
|
||||
+11
-11
@@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector ¢er)
|
||||
|
||||
double Mesh::GetElementSize(ElementTransformation *T, int type)
|
||||
{
|
||||
DenseMatrix J(spaceDim,Dim);
|
||||
DenseMatrix J(spaceDim, Dim);
|
||||
|
||||
Geometry::Type geom = T->GetGeometryType();
|
||||
T->SetIntPoint(&Geometries.GetCenter(geom));
|
||||
@@ -102,7 +102,7 @@ double Mesh::GetElementSize(int i, int type)
|
||||
|
||||
double Mesh::GetElementSize(int i, const Vector &dir)
|
||||
{
|
||||
DenseMatrix J(spaceDim,Dim);
|
||||
DenseMatrix J(spaceDim, Dim);
|
||||
Vector d_hat(Dim);
|
||||
GetElementJacobian(i, J);
|
||||
J.MultTranspose(dir, d_hat);
|
||||
@@ -8527,8 +8527,8 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
elements[new_e] = new Segment(new_v, vert[1], attr);
|
||||
vert[1] = new_v;
|
||||
|
||||
CoarseFineTr.embeddings[i] = Embedding(i, 1);
|
||||
CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
|
||||
CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1);
|
||||
CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2);
|
||||
}
|
||||
|
||||
static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
|
||||
@@ -9276,7 +9276,7 @@ void Mesh::Bisection(int i, const DSTable &v_to_v,
|
||||
|
||||
int coarse = FindCoarseElement(i);
|
||||
CoarseFineTr.embeddings[i].parent = coarse;
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
|
||||
|
||||
// 3. edge1 and edge2 may have to be changed for the second triangle.
|
||||
if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
|
||||
@@ -9396,7 +9396,7 @@ void Mesh::Bisection(int i, HashTable<Hashed2> &v_to_v)
|
||||
|
||||
int coarse = FindCoarseElement(i);
|
||||
CoarseFineTr.embeddings[i].parent = coarse;
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TETRAHEDRON));
|
||||
|
||||
// 3. Set the bisection flag
|
||||
switch (type)
|
||||
@@ -9534,10 +9534,10 @@ void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
|
||||
|
||||
// set parent indices
|
||||
int coarse = FindCoarseElement(i);
|
||||
CoarseFineTr.embeddings[i] = Embedding(coarse);
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse));
|
||||
CoarseFineTr.embeddings[i] = Embedding(coarse, Geometry::TRIANGLE);
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
|
||||
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
|
||||
|
||||
NumOfElements += 3;
|
||||
}
|
||||
@@ -9555,7 +9555,7 @@ void Mesh::InitRefinementTransforms()
|
||||
for (int i = 0; i < NumOfElements; i++)
|
||||
{
|
||||
elements[i]->ResetTransform(0);
|
||||
CoarseFineTr.embeddings[i] = Embedding(i);
|
||||
CoarseFineTr.embeddings[i] = Embedding(i, GetElementGeometry(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,142 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh)
|
||||
}
|
||||
|
||||
|
||||
int CoefficientRefiner::ApplyImpl(Mesh &mesh)
|
||||
{
|
||||
int max_it = 1;
|
||||
return PreprocessMesh(mesh, max_it);
|
||||
}
|
||||
|
||||
int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
|
||||
{
|
||||
int rank = 0;
|
||||
MFEM_VERIFY(max_it > 0, "max_it must be strictly positive")
|
||||
|
||||
int dim = mesh.Dimension();
|
||||
L2_FECollection l2fec(order, dim);
|
||||
FiniteElementSpace* l2fes = NULL;
|
||||
|
||||
bool par = false;
|
||||
GridFunction *gf = NULL;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh* pmesh = dynamic_cast<ParMesh*>(&mesh);
|
||||
if (pmesh && pmesh->Nonconforming())
|
||||
{
|
||||
par = true;
|
||||
l2fes = new ParFiniteElementSpace(pmesh, &l2fec);
|
||||
gf = new ParGridFunction(static_cast<ParFiniteElementSpace*>(l2fes));
|
||||
}
|
||||
#endif
|
||||
if (!par)
|
||||
{
|
||||
l2fes = new FiniteElementSpace(&mesh, &l2fec);
|
||||
gf = new GridFunction(l2fes);
|
||||
}
|
||||
|
||||
// If custom integration rule has not been set,
|
||||
// then use the default integration rule
|
||||
if (!irs)
|
||||
{
|
||||
int order_quad = 2*order + 3;
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
ir_default[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
irs = ir_default;
|
||||
}
|
||||
|
||||
for (int i = 0; i < max_it; i++)
|
||||
{
|
||||
// Compute number of elements and L2-norm of f.
|
||||
int NE = mesh.GetNE();
|
||||
int globalNE = 0;
|
||||
double norm_of_coeff = 0.0;
|
||||
if (par)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
globalNE = pmesh->GetGlobalNE();
|
||||
norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*pmesh,irs);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
globalNE = NE;
|
||||
norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs);
|
||||
}
|
||||
|
||||
// Compute average L2-norm of f
|
||||
double av_norm_of_coeff = norm_of_coeff / sqrt(globalNE);
|
||||
|
||||
// Compute element-wise L2-norms of (I - Π) f
|
||||
Vector element_norms_of_fine_scale(NE);
|
||||
gf->SetSpace(l2fes);
|
||||
gf->ProjectCoefficient(*coeff);
|
||||
gf->ComputeElementL2Errors(*coeff,element_norms_of_fine_scale,irs);
|
||||
|
||||
// Define osc_K(f) := || h ⋅ (I - Π) f ||_K and select elements
|
||||
// for refinement based on threshold. Also record relative osc(f).
|
||||
global_osc = 0.0;
|
||||
mesh_refinements.SetSize(0);
|
||||
element_oscs.Destroy();
|
||||
element_oscs.SetSize(NE);
|
||||
element_oscs = 0.0;
|
||||
for (int j = 0; j < NE; j++)
|
||||
{
|
||||
double h = mesh.GetElementSize(j);
|
||||
double element_osc = h * element_norms_of_fine_scale(j);
|
||||
if ( element_osc > threshold * av_norm_of_coeff )
|
||||
{
|
||||
mesh_refinements.Append(j);
|
||||
}
|
||||
element_oscs(j) = element_osc/(norm_of_coeff + 1e-10);
|
||||
global_osc += element_osc*element_osc;
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (par)
|
||||
{
|
||||
MPI_Comm comm = pmesh->GetComm();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &global_osc, 1, MPI_DOUBLE, MPI_SUM, comm);
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
}
|
||||
#endif
|
||||
global_osc = sqrt(global_osc)/(norm_of_coeff + 1e-10);
|
||||
|
||||
// Exit if the global threshold or maximum number of elements is reached.
|
||||
if (global_osc < threshold || globalNE > max_elements)
|
||||
{
|
||||
if (global_osc > threshold && globalNE > max_elements && rank == 0 &&
|
||||
print_level)
|
||||
{
|
||||
MFEM_WARNING("Reached maximum number of elements "
|
||||
"before resolving data to tolerance.");
|
||||
}
|
||||
delete l2fes;
|
||||
delete gf;
|
||||
return STOP;
|
||||
}
|
||||
|
||||
// Refine elements.
|
||||
mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit);
|
||||
l2fes->Update(false);
|
||||
gf->Update();
|
||||
|
||||
}
|
||||
delete l2fes;
|
||||
delete gf;
|
||||
return CONTINUE + REFINED;
|
||||
|
||||
}
|
||||
|
||||
void CoefficientRefiner::Reset()
|
||||
{
|
||||
element_oscs.Destroy();
|
||||
global_osc = 0.0;
|
||||
coeff = NULL;
|
||||
irs = NULL;
|
||||
}
|
||||
|
||||
|
||||
int Rebalancer::ApplyImpl(Mesh &mesh)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
@@ -308,6 +308,118 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** @brief Refinement operator to control data oscillation.
|
||||
|
||||
This class computes osc_K(f) := || h ⋅ (I - Π) f ||_K at each element K.
|
||||
Here, Π is the L2-projection and ||⋅||_K is the L2-norm, restricted to the
|
||||
element K. All elements satisfying the inequality
|
||||
\code
|
||||
osc_K(f) > threshold ⋅ ||f|| / sqrt(n_el),
|
||||
\endcode
|
||||
are refined. Here, threshold is a positive parameter, ||⋅|| is the L2-norm
|
||||
over the entire domain Ω, and n_el is the number of elements in the mesh.
|
||||
|
||||
Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, then
|
||||
\code
|
||||
osc(f) = sqrt(sum_K osc_K^2(f)) = threshold ⋅ ||f||.
|
||||
\endcode
|
||||
This is the reason for the 1/sqrt(n_el) factor.
|
||||
*/
|
||||
class CoefficientRefiner : public MeshOperator
|
||||
{
|
||||
protected:
|
||||
bool print_level = false;
|
||||
int nc_limit = 1;
|
||||
int nonconforming = -1;
|
||||
int order;
|
||||
long max_elements = std::numeric_limits<long>::max();
|
||||
double threshold = 1.0e-2;
|
||||
double global_osc = NAN;
|
||||
Array<int> mesh_refinements;
|
||||
Vector element_oscs;
|
||||
Coefficient *coeff = NULL;
|
||||
const IntegrationRule *ir_default[Geometry::NumGeom];
|
||||
const IntegrationRule **irs = NULL;
|
||||
|
||||
/** @brief Apply the operator to the mesh once.
|
||||
@return STOP if a stopping criterion is satisfied or no elements were
|
||||
marked for refinement; REFINED + CONTINUE otherwise. */
|
||||
virtual int ApplyImpl(Mesh &mesh);
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
CoefficientRefiner(Coefficient &coeff_, int order_)
|
||||
{
|
||||
// function f
|
||||
coeff = &coeff_;
|
||||
|
||||
// order of the projection Π
|
||||
order = order_;
|
||||
}
|
||||
|
||||
/** @brief Apply the operator to the mesh max_it times or until tolerance
|
||||
* achieved.
|
||||
@return STOP if a stopping criterion is satisfied or no elements were
|
||||
marked for refinement; REFINED + CONTINUE otherwise. */
|
||||
virtual int PreprocessMesh(Mesh &mesh, int max_it);
|
||||
|
||||
int PreprocessMesh(Mesh &mesh)
|
||||
{
|
||||
int max_it = 10;
|
||||
return PreprocessMesh(mesh, max_it);
|
||||
}
|
||||
|
||||
/// Set the refinement threshold. The default value is 1.0e-2.
|
||||
void SetThreshold(double threshold_) { threshold = threshold_; }
|
||||
|
||||
/** @brief Set the maximum number of elements stopping criterion: stop when
|
||||
the input mesh has num_elements >= max_elem. The default value is
|
||||
LONG_MAX. */
|
||||
void SetMaxElements(long max_elements_) { max_elements = max_elements_; }
|
||||
|
||||
/// Reset the function f
|
||||
void ResetCoefficient(Coefficient &coeff_)
|
||||
{
|
||||
element_oscs.Destroy();
|
||||
global_osc = NAN;
|
||||
coeff = &coeff_;
|
||||
}
|
||||
|
||||
/// Reset the oscillation order
|
||||
void SetOrder(double order_) { order = order_; }
|
||||
|
||||
/** @brief Set the maximum ratio of refinement levels of adjacent elements
|
||||
(0 = unlimited). The default value is 1, which helps ensure appropriate
|
||||
refinements in pathological situations where the default quadrature
|
||||
order is too low. */
|
||||
void SetNCLimit(int nc_limit_)
|
||||
{
|
||||
MFEM_ASSERT(nc_limit_ >= 0, "Invalid NC limit");
|
||||
nc_limit = nc_limit_;
|
||||
}
|
||||
|
||||
// Set a custom integration rule
|
||||
void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; }
|
||||
|
||||
// Set print level
|
||||
void PrintWarnings() { print_level = true; }
|
||||
|
||||
// Return the value of the global relative data oscillation
|
||||
double GetOsc() const { return global_osc; }
|
||||
|
||||
// Return the local relative data oscillation errors
|
||||
const Vector & GetLocalOscs() const
|
||||
{
|
||||
MFEM_ASSERT(element_oscs.Size() > 0,
|
||||
"Local oscillations have not been computed yet")
|
||||
return element_oscs;
|
||||
}
|
||||
|
||||
/// Reset
|
||||
virtual void Reset();
|
||||
};
|
||||
|
||||
|
||||
/** @brief ParMesh rebalancing operator.
|
||||
|
||||
If the mesh is a parallel mesh, perform rebalancing; otherwise, do nothing.
|
||||
|
||||
+28
-133
@@ -1865,8 +1865,12 @@ void NCMesh::InitDerefTransforms()
|
||||
transforms.embeddings.SetSize(nfine);
|
||||
for (int i = 0; i < nfine; i++)
|
||||
{
|
||||
transforms.embeddings[i].parent = -1;
|
||||
transforms.embeddings[i].matrix = 0;
|
||||
Embedding &emb = transforms.embeddings[i];
|
||||
emb.parent = -1;
|
||||
emb.matrix = 0;
|
||||
Element &el = elements[leaf_elements[i]];
|
||||
emb.geom = el.Geom();
|
||||
emb.ghost = IsGhost(el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1879,7 +1883,7 @@ void NCMesh::SetDerefMatrixCodes(int parent, Array<int> &fine_coarse)
|
||||
Element &ch = elements[prn.child[i]];
|
||||
if (ch.index >= 0)
|
||||
{
|
||||
int code = (prn.ref_type << 8) | (i << 4) | prn.geom;
|
||||
int code = (prn.ref_type << 4) | i;
|
||||
transforms.embeddings[ch.index].matrix = code;
|
||||
fine_coarse[ch.index] = parent;
|
||||
}
|
||||
@@ -4291,6 +4295,8 @@ void NCMesh::TraverseRefinements(int elem, int coarse_index,
|
||||
Embedding &emb = transforms.embeddings[el.index];
|
||||
emb.parent = coarse_index;
|
||||
emb.matrix = matrix - 1;
|
||||
emb.geom = el.Geom();
|
||||
emb.ghost = IsGhost(el);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4378,15 +4384,14 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms()
|
||||
// assign numbers to the different matrices used
|
||||
for (int i = 0; i < transforms.embeddings.Size(); i++)
|
||||
{
|
||||
int code = transforms.embeddings[i].matrix;
|
||||
Embedding &emb = transforms.embeddings[i];
|
||||
int code = emb.matrix; // see SetDerefMatrixCodes()
|
||||
if (code)
|
||||
{
|
||||
int geom = code & 0xf; // see SetDerefMatrixCodes()
|
||||
int ref_type_child = code >> 4;
|
||||
int &matrix = mat_no[emb.geom][code];
|
||||
if (!matrix) { matrix = mat_no[emb.geom].size(); }
|
||||
|
||||
int &matrix = mat_no[geom][ref_type_child];
|
||||
if (!matrix) { matrix = mat_no[geom].size(); }
|
||||
transforms.embeddings[i].matrix = matrix - 1;
|
||||
emb.matrix = matrix - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4421,136 +4426,26 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms()
|
||||
return transforms;
|
||||
}
|
||||
|
||||
namespace internal
|
||||
void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine,
|
||||
bool want_ghosts) const
|
||||
{
|
||||
Array<Connection> conn;
|
||||
conn.Reserve(embeddings.Size());
|
||||
|
||||
// Used in CoarseFineTransformations::GetCoarseToFineMap() below.
|
||||
struct RefType
|
||||
{
|
||||
Geometry::Type geom;
|
||||
int num_children;
|
||||
const Pair<int,int> *children;
|
||||
|
||||
RefType(Geometry::Type g, int n, const Pair<int,int> *c)
|
||||
: geom(g), num_children(n), children(c) { }
|
||||
|
||||
bool operator<(const RefType &other) const
|
||||
int max_parent = -1;
|
||||
for (int i = 0; i < embeddings.Size(); i++)
|
||||
{
|
||||
if (geom < other.geom) { return true; }
|
||||
if (geom > other.geom) { return false; }
|
||||
if (num_children < other.num_children) { return true; }
|
||||
if (num_children > other.num_children) { return false; }
|
||||
for (int i = 0; i < num_children; i++)
|
||||
const Embedding &emb = embeddings[i];
|
||||
if ((emb.parent >= 0) &&
|
||||
(!emb.ghost || want_ghosts))
|
||||
{
|
||||
if (children[i].one < other.children[i].one) { return true; }
|
||||
if (children[i].one > other.children[i].one) { return false; }
|
||||
}
|
||||
return false; // everything is equal
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void CoarseFineTransformations::GetCoarseToFineMap(
|
||||
const mfem::Mesh &fine_mesh, Table &coarse_to_fine,
|
||||
Array<int> &coarse_to_ref_type, Table &ref_type_to_matrix,
|
||||
Array<mfem::Geometry::Type> &ref_type_to_geom,
|
||||
bool get_coarse_to_fine_only) const
|
||||
{
|
||||
const int fine_ne = embeddings.Size();
|
||||
int coarse_ne = -1;
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
coarse_ne = std::max(coarse_ne, embeddings[i].parent);
|
||||
}
|
||||
coarse_ne++;
|
||||
|
||||
coarse_to_ref_type.SetSize(coarse_ne);
|
||||
coarse_to_fine.SetDims(coarse_ne, fine_ne);
|
||||
|
||||
Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
|
||||
Array<Pair<int,int> > cf_j(fine_ne);
|
||||
cf_i = 0;
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
cf_i[embeddings[i].parent+1]++;
|
||||
}
|
||||
cf_i.PartialSum();
|
||||
MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
const Embedding &e = embeddings[i];
|
||||
cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
|
||||
cf_j[cf_i[e.parent]].two = i;
|
||||
cf_i[e.parent]++;
|
||||
}
|
||||
std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
|
||||
cf_i[0] = 0;
|
||||
for (int i = 0; i < coarse_ne; i++)
|
||||
{
|
||||
std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
|
||||
}
|
||||
for (int i = 0; i < fine_ne; i++)
|
||||
{
|
||||
coarse_to_fine.GetJ()[i] = cf_j[i].two;
|
||||
}
|
||||
|
||||
if (get_coarse_to_fine_only) { return; }
|
||||
MFEM_VERIFY(fine_mesh.GetLastOperation() != Mesh::Operation::DEREFINE,
|
||||
"GetCoarseToFineMap is not fully supported for derefined meshes."
|
||||
" Set 'get_coarse_to_fine_only=true'.")
|
||||
|
||||
using internal::RefType;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
|
||||
map<RefType,int> ref_type_map;
|
||||
for (int i = 0; i < coarse_ne; i++)
|
||||
{
|
||||
const int num_children = cf_i[i+1]-cf_i[i];
|
||||
MFEM_ASSERT(num_children > 0, "");
|
||||
const int fine_el = cf_j[cf_i[i]].two;
|
||||
// Assuming the coarse and the fine elements have the same geometry:
|
||||
const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
|
||||
const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
|
||||
pair<map<RefType,int>::iterator,bool> res =
|
||||
ref_type_map.insert(
|
||||
pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
|
||||
coarse_to_ref_type[i] = res.first->second;
|
||||
}
|
||||
|
||||
ref_type_to_matrix.MakeI((int)ref_type_map.size());
|
||||
ref_type_to_geom.SetSize((int)ref_type_map.size());
|
||||
for (map<RefType,int>::iterator it = ref_type_map.begin();
|
||||
it != ref_type_map.end(); ++it)
|
||||
{
|
||||
ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
|
||||
ref_type_to_geom[it->second] = it->first.geom;
|
||||
}
|
||||
|
||||
ref_type_to_matrix.MakeJ();
|
||||
for (map<RefType,int>::iterator it = ref_type_map.begin();
|
||||
it != ref_type_map.end(); ++it)
|
||||
{
|
||||
const RefType &rt = it->first;
|
||||
for (int j = 0; j < rt.num_children; j++)
|
||||
{
|
||||
ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
|
||||
conn.Append(Connection(emb.parent, i));
|
||||
max_parent = std::max(emb.parent, max_parent);
|
||||
}
|
||||
}
|
||||
ref_type_to_matrix.ShiftUpI();
|
||||
}
|
||||
|
||||
void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &fine_mesh,
|
||||
Table &coarse_to_fine) const
|
||||
{
|
||||
Array<int> coarse_to_ref_type;
|
||||
Table ref_type_to_matrix;
|
||||
Array<mfem::Geometry::Type> ref_type_to_geom;
|
||||
bool get_coarse_to_fine_only = true;
|
||||
GetCoarseToFineMap(fine_mesh, coarse_to_fine, coarse_to_ref_type,
|
||||
ref_type_to_matrix, ref_type_to_geom,
|
||||
get_coarse_to_fine_only);
|
||||
conn.Sort(); // NOTE: unique is not necessary
|
||||
coarse_to_fine.MakeFromList(max_parent+1, conn);
|
||||
}
|
||||
|
||||
void NCMesh::ClearTransforms()
|
||||
@@ -4580,7 +4475,7 @@ bool CoarseFineTransformations::IsInitialized() const
|
||||
|
||||
void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b)
|
||||
{
|
||||
for (int g=0; g<Geometry::NumGeom; ++g)
|
||||
for (int g = 0; g < Geometry::NumGeom; ++g)
|
||||
{
|
||||
a.point_matrices[g].Swap(b.point_matrices[g]);
|
||||
}
|
||||
|
||||
+26
-17
@@ -38,45 +38,54 @@ struct Refinement
|
||||
char ref_type; ///< refinement XYZ bit mask (7 = full isotropic)
|
||||
|
||||
Refinement() = default;
|
||||
|
||||
Refinement(int index, int type = 7) : index(index), ref_type(type) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the position of a fine element within a coarse element.
|
||||
struct Embedding
|
||||
{
|
||||
/// %Element index in the coarse mesh.
|
||||
/// Coarse %Element index in the coarse mesh.
|
||||
int parent;
|
||||
/** @brief Index into the DenseTensor corresponding to the parent
|
||||
Geometry::Type stored in CoarseFineTransformations::point_matrices. */
|
||||
int matrix;
|
||||
|
||||
/** The (geom, matrix) pair determines the sub-element transformation for the
|
||||
fine element: CoarseFineTransformations::point_matrices[geom](matrix) is
|
||||
the point matrix of the region within the coarse element reference domain.*/
|
||||
unsigned geom : 4;
|
||||
unsigned matrix : 27;
|
||||
|
||||
/// For internal use: 0 if regular fine element, 1 if parallel ghost element.
|
||||
unsigned ghost : 1;
|
||||
|
||||
Embedding() = default;
|
||||
|
||||
Embedding(int elem, int matrix = 0) : parent(elem), matrix(matrix) {}
|
||||
Embedding(int elem, Geometry::Type geom, int matrix = 0, bool ghost = false)
|
||||
: parent(elem), geom(geom), matrix(matrix), ghost(ghost) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the coarse-fine transformations of all fine elements.
|
||||
struct CoarseFineTransformations
|
||||
{
|
||||
/// Matrices for IsoparametricTransformation organized by Geometry::Type
|
||||
DenseTensor point_matrices[Geometry::NumGeom];
|
||||
/// Fine element positions in their parents.
|
||||
Array<Embedding> embeddings;
|
||||
|
||||
void GetCoarseToFineMap(const Mesh &fine_mesh,
|
||||
Table &coarse_to_fine,
|
||||
Array<int> &coarse_to_ref_type,
|
||||
Table &ref_type_to_matrix,
|
||||
Array<Geometry::Type> &ref_type_to_geom,
|
||||
bool get_coarse_to_fine_only = false) const;
|
||||
/** A "dictionary" of matrices for IsoparametricTransformation. Use
|
||||
Embedding::{geom,matrix} to access a fine element point matrix. */
|
||||
DenseTensor point_matrices[Geometry::NumGeom];
|
||||
|
||||
void GetCoarseToFineMap(const Mesh &fine_mesh,
|
||||
Table &coarse_to_fine) const;
|
||||
/** Invert the 'embeddings' array: create a Table with coarse elements as
|
||||
rows and fine elements as columns. If 'want_ghosts' is false, parallel
|
||||
ghost fine elements are not included in the table. */
|
||||
void MakeCoarseToFineTable(Table &coarse_to_fine,
|
||||
bool want_ghosts = false) const;
|
||||
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
long MemoryUsage() const;
|
||||
|
||||
MFEM_DEPRECATED
|
||||
void GetCoarseToFineMap(const Mesh &fine_mesh, Table &coarse_to_fine) const
|
||||
{ MakeCoarseToFineTable(coarse_to_fine, true); (void) fine_mesh; }
|
||||
};
|
||||
|
||||
void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b);
|
||||
|
||||
+2
-2
@@ -3706,8 +3706,8 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
elements[new_e] = new Segment(new_v, vert[1], attr);
|
||||
vert[1] = new_v;
|
||||
|
||||
CoarseFineTr.embeddings[i] = Embedding(i, 1);
|
||||
CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
|
||||
CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1);
|
||||
CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2);
|
||||
}
|
||||
|
||||
static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
|
||||
|
||||
@@ -29,4 +29,5 @@ add_subdirectory(gslib)
|
||||
add_subdirectory(solvers)
|
||||
add_subdirectory(shifted)
|
||||
add_subdirectory(mtop)
|
||||
add_subdirectory(autodiff)
|
||||
add_subdirectory(parelag)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
list(APPEND SEQADIFF_COMMON_SOURCES)
|
||||
|
||||
list(APPEND SEQADIFF_COMMON_HEADERS
|
||||
fdual.hpp
|
||||
tadvector.hpp
|
||||
taddensemat.hpp
|
||||
admfem.hpp)
|
||||
|
||||
convert_filenames_to_full_paths(SEQADIFF_COMMON_SOURCES)
|
||||
convert_filenames_to_full_paths(SEQADIFF_COMMON_HEADERS)
|
||||
|
||||
set(SEQADIFF_COMMON_FILES
|
||||
EXTRA_SOURCES ${SEQADIFF_COMMON_SOURCES}
|
||||
EXTRA_HEADERS ${SEQADIFF_COMMON_HEADERS})
|
||||
|
||||
add_mfem_miniapp(seqadiff
|
||||
MAIN seq_example.cpp
|
||||
${SEQADIFF_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(seqtest
|
||||
MAIN seq_test.cpp
|
||||
${SEQADIFF_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
if(MFEM_USE_MPI)
|
||||
|
||||
list(APPEND PARADIFF_COMMON_SOURCES)
|
||||
list(APPEND PARADIFF_COMMON_HEADERS)
|
||||
|
||||
convert_filenames_to_full_paths(PARADIFF_COMMON_SOURCES)
|
||||
convert_filenames_to_full_paths(PARADIFF_COMMON_HEADERS)
|
||||
|
||||
set(PARADIFF_COMMON_FILES
|
||||
EXTRA_SOURCES ${PARADIFF_COMMON_SOURCES} ${SEQADIFF_COMMON_SOURCES}
|
||||
EXTRA_HEADERS ${PARADIFF_COMMON_HEADERS} ${SEQADIFF_COMMON_HEADERS})
|
||||
|
||||
message(STATUS "PARADIFF_COMMON_FILES: ${PARADIFF_COMMON_FILES}")
|
||||
message(STATUS "SEQADIFF_COMMON_FILES: ${SEQADIFF_COMMON_FILES}")
|
||||
|
||||
add_mfem_miniapp(paradiff
|
||||
MAIN par_example.cpp
|
||||
${PARADIFF_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
endif ()
|
||||
@@ -0,0 +1,712 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef ADMFEM_HPP
|
||||
#define ADMFEM_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "fdual.hpp"
|
||||
#include "tadvector.hpp"
|
||||
#include "taddensemat.hpp"
|
||||
|
||||
#ifdef MFEM_USE_CODIPACK
|
||||
#include <codi.hpp>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace ad
|
||||
{
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
/// Forward AD type declaration
|
||||
typedef codi::RealForward ADFloatType;
|
||||
/// Vector type for AD-numbers
|
||||
typedef TAutoDiffVector<ADFloatType> ADVectorType;
|
||||
/// Matrix type for AD-numbers
|
||||
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
|
||||
#else
|
||||
/// Reverse AD type declaration
|
||||
typedef codi::RealReverse ADFloatType;
|
||||
/// Vector type for AD-numbers
|
||||
typedef TAutoDiffVector<ADFloatType> ADVectorType;
|
||||
/// Matrix type for AD-numbers
|
||||
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The class provides an evaluation of the Jacobian of a templated vector
|
||||
/// function provided in the constructor. The Jacobian is evaluated with the
|
||||
/// help of automatic differentiation (AD). The template parameters specify the
|
||||
/// size of the return vector (vector_size), the size of the input vector
|
||||
/// (state_size), and the size of the parameters supplied to the function.
|
||||
template<int vector_size=1, int state_size=1, int param_size=0>
|
||||
class VectorFuncAutoDiff
|
||||
{
|
||||
public:
|
||||
/// F_ is user implemented function to be differentiated by
|
||||
/// VectorFuncAutoDiff. The signature of the function is: F_(mfem::Vector&
|
||||
/// parameters, ad::ADVectorType& state_vector, ad::ADVectorType& result).
|
||||
/// The parameters vector should have size param_size. The state_vector
|
||||
/// should have size state_size, and the result vector should have size
|
||||
/// vector_size. All size parameters are teplate parameters in
|
||||
/// VectorFuncAutoDiff.
|
||||
VectorFuncAutoDiff(
|
||||
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
|
||||
{
|
||||
F=F_;
|
||||
}
|
||||
|
||||
/// Evaluates the Jacobian of the vector function F_ for a set of parameters
|
||||
/// (vparam) and state vector vstate. The Jacobian (jac) has dimensions
|
||||
/// [vector_size x state_size].
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &vstate,
|
||||
mfem::DenseMatrix &jac)
|
||||
{
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
// use forward mode
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ad::ADVectorType ad_state(state_size);
|
||||
ad::ADVectorType ad_result(vector_size);
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
ad_state[i].setValue(vstate[i]);
|
||||
ad_state[i].setGradient(0.0);
|
||||
}
|
||||
for (int ii=0; ii<state_size; ii++)
|
||||
{
|
||||
ad_state[ii].setGradient(1.0);
|
||||
F(vparam,ad_state,ad_result);
|
||||
for (int jj=0; jj<vector_size; jj++)
|
||||
{
|
||||
jac(jj,ii)=ad_result[jj].getGradient();
|
||||
}
|
||||
ad_state[ii].setGradient(0.0);
|
||||
}
|
||||
}
|
||||
#else // use reverse mode
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ad::ADVectorType ad_state(state_size);
|
||||
ad::ADVectorType ad_result(vector_size);
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
ad_state[i]=vstate[i];
|
||||
}
|
||||
|
||||
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
|
||||
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
|
||||
|
||||
tape.setActive();
|
||||
for (int ii=0; ii<state_size; ii++) { tape.registerInput(ad_state[ii]); }
|
||||
F(vparam,ad_state,ad_result);
|
||||
for (int ii=0; ii<vector_size; ii++) { tape.registerOutput(ad_result[ii]); }
|
||||
tape.setPassive();
|
||||
|
||||
for (int jj=0; jj<vector_size; jj++)
|
||||
{
|
||||
ad_result[jj].setGradient(1.0);
|
||||
tape.evaluate();
|
||||
for (int ii=0; ii<state_size; ii++)
|
||||
{
|
||||
jac(jj,ii)=ad_state[ii].getGradient();
|
||||
}
|
||||
tape.clearAdjoints();
|
||||
ad_result[jj].setGradient(0.0);
|
||||
}
|
||||
tape.reset(pos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
|
||||
}; // VectorFuncAutoDiff
|
||||
|
||||
/// The class provides an evaluation of the Jacobian of a templated vector
|
||||
/// function provided as a functor TFunctor. The Jacobian is evaluated with the
|
||||
/// help of automatic differentiation (AD). The template parameters specify the
|
||||
/// size of the return vector (vector_size), the size of the input vector
|
||||
/// (state_size), and the size of the parameters supplied to the function. The
|
||||
/// TFunctor functor is a template class with parameters [Float data type],
|
||||
/// [Vector type for the additional parameters], [Vector type for the state
|
||||
/// vector and the return residual]. The integer template parameters are the
|
||||
/// same ones passed to QVectorFuncAutoDiff.
|
||||
template<template<typename, typename, typename, int, int, int> class TFunctor
|
||||
, int vector_size=1, int state_size=1, int param_size=0>
|
||||
class QVectorFuncAutoDiff
|
||||
{
|
||||
public:
|
||||
/// Evaluates the vector function for given set of parameters and state
|
||||
/// values in vector uu. The result is returned in vector rr.
|
||||
void VectorFunc(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector& rr)
|
||||
{
|
||||
rf(vparam,uu,rr);
|
||||
}
|
||||
|
||||
/// Returns the gradient of TFunctor(...) in the dense matrix jac. The
|
||||
/// dimensions of jac are vector_size x state_size, where state_size is the
|
||||
/// length of vector uu.
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
// use forward mode
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ad::ADVectorType aduu(state_size);
|
||||
ad::ADVectorType rr(vector_size);
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
aduu[i].setValue(uu[i]);
|
||||
aduu[i].setGradient(0.0);
|
||||
}
|
||||
|
||||
for (int ii=0; ii<state_size; ii++)
|
||||
{
|
||||
aduu[ii].setGradient(1.0);
|
||||
tf(vparam,aduu,rr);
|
||||
for (int jj=0; jj<vector_size; jj++)
|
||||
{
|
||||
jac(jj,ii)=rr[jj].getGradient();
|
||||
}
|
||||
aduu[ii].setGradient(0.0);
|
||||
}
|
||||
}
|
||||
#else // end MFEM_USE_ADFORWARD
|
||||
// use reverse mode
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ad::ADVectorType aduu(state_size);
|
||||
ad::ADVectorType rr(vector_size);
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
aduu[i]=uu[i];
|
||||
}
|
||||
|
||||
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
|
||||
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
|
||||
|
||||
tape.setActive();
|
||||
for (int ii=0; ii<state_size; ii++) { tape.registerInput(aduu[ii]); }
|
||||
tf(vparam,aduu,rr);
|
||||
for (int ii=0; ii<vector_size; ii++) { tape.registerOutput(rr[ii]); }
|
||||
tape.setPassive();
|
||||
for (int jj=0; jj<vector_size; jj++)
|
||||
{
|
||||
rr[jj].setGradient(1.0);
|
||||
tape.evaluate();
|
||||
for (int ii=0; ii<state_size; ii++)
|
||||
{
|
||||
jac(jj,ii)=aduu[ii].getGradient();
|
||||
}
|
||||
tape.clearAdjoints();
|
||||
rr[jj].setGradient(0.0);
|
||||
}
|
||||
tape.reset(pos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
|
||||
|
||||
TFunctor<ad::ADFloatType, const Vector, ad::ADVectorType,
|
||||
vector_size, state_size, param_size> tf;
|
||||
|
||||
TFunctor<double,const mfem::Vector, mfem::Vector,
|
||||
vector_size, state_size, param_size> rf;
|
||||
|
||||
};
|
||||
|
||||
/// The class provides an evaluation of the first derivatives and the Hessian of
|
||||
/// a templated scalar function provided as a functor TFunctor. Both the first
|
||||
/// and the second derivatives are evaluated with the help of automatic
|
||||
/// differentiation (AD). The template parameters specify the size of the input
|
||||
/// vector (state_size) and the size of the parameters supplied to the
|
||||
/// function. The TFunctor functor is a template class with parameters [Float
|
||||
/// data type], [Vector type for the additional parameters], [Vector type for
|
||||
/// the state vector and the return residual]. The integer template parameters
|
||||
/// are the same ones passed to QFunctionAutoDiff.
|
||||
template<template<typename, typename, typename, int, int> class TFunctor
|
||||
, int state_size=1, int param_size=0>
|
||||
class QFunctionAutoDiff
|
||||
{
|
||||
public:
|
||||
|
||||
/// Evaluates a function for arguments vparam and uu. The evaluation is
|
||||
/// based on the operator() in the user provided functor TFunctor.
|
||||
double Eval(const mfem::Vector &vparam, mfem::Vector &uu)
|
||||
{
|
||||
return rf(vparam,uu);
|
||||
}
|
||||
|
||||
/// Provides the same functionality as Grad.
|
||||
void VectorFunc(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector &rr)
|
||||
{
|
||||
Grad(vparam,uu,rr);
|
||||
}
|
||||
|
||||
/// Returns the first derivative of TFunctor(...) with respect to the active
|
||||
/// arguments proved in vector uu. The length of rr is the same as for uu.
|
||||
void Grad(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector &rr)
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
// use forward mode
|
||||
rr.SetSize(state_size);
|
||||
{
|
||||
ad::ADVectorType aduu(state_size);
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
aduu[i].setValue(uu[i]);
|
||||
aduu[i].setGradient(0.0);
|
||||
}
|
||||
|
||||
ad::ADFloatType rez;
|
||||
|
||||
for (int ii=0; ii<state_size; ii++)
|
||||
{
|
||||
aduu[ii].setGradient(1.0);
|
||||
rez=tf(vparam,aduu);
|
||||
rr[ii]=rez.getGradient();
|
||||
aduu[ii].setGradient(0.0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
ad::ADVectorType aduu(state_size);
|
||||
ad::ADFloatType rez;
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
aduu[i]=uu[i];
|
||||
}
|
||||
|
||||
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
|
||||
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
|
||||
|
||||
tape.setActive();
|
||||
for (int ii=0; ii<state_size; ii++) { tape.registerInput(aduu[ii]); }
|
||||
|
||||
rez=tf(vparam,aduu);
|
||||
tape.registerOutput(rez);
|
||||
tape.setPassive();
|
||||
|
||||
rez.setGradient(1.0);
|
||||
tape.evaluate();
|
||||
for (int i=0; i<state_size; i++)
|
||||
{
|
||||
rr[i]=aduu[i].getGradient();
|
||||
}
|
||||
tape.reset(pos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Provides same functionality as Hessian.
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
Hessian(vparam,uu,jac);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
// use forward-forward mode
|
||||
typedef codi::RealForwardGen<double> ADFType;
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
|
||||
|
||||
typedef codi::RealForwardGen<ADFType> ADSType;
|
||||
typedef TAutoDiffVector<ADSType> ADSVector;
|
||||
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
|
||||
#else
|
||||
//use mixed forward and reverse mode
|
||||
typedef codi::RealForwardGen<double> ADFType;
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
|
||||
|
||||
typedef codi::RealReverseGen<ADFType> ADSType;
|
||||
typedef TAutoDiffVector<ADSType> ADSVector;
|
||||
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
|
||||
#endif
|
||||
|
||||
|
||||
/// Returns the Hessian of TFunctor(...) in the dense matrix jac. The
|
||||
/// dimensions of jac are state_size x state_size, where state_size is the
|
||||
/// length of vector uu.
|
||||
void Hessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
// use forward-forward mode
|
||||
jac.SetSize(state_size);
|
||||
jac=0.0;
|
||||
{
|
||||
ADSVector aduu(state_size);
|
||||
for (int ii = 0; ii < state_size; ii++)
|
||||
{
|
||||
aduu[ii].value().value()=uu[ii];
|
||||
aduu[ii].value().gradient()=0.0;
|
||||
aduu[ii].gradient().value()=0.0;
|
||||
aduu[ii].gradient().gradient()=0.0;
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < state_size; ii++)
|
||||
{
|
||||
aduu[ii].value().gradient()=1.0;
|
||||
for (int jj=0; jj<(ii+1); jj++)
|
||||
{
|
||||
aduu[jj].gradient().value()=1.0;
|
||||
ADSType rez=sf(vparam,aduu);
|
||||
jac(ii,jj)=rez.gradient().gradient();
|
||||
jac(jj,ii)=jac(ii,jj);
|
||||
aduu[jj].gradient().value()=0.0;
|
||||
}
|
||||
aduu[ii].value().gradient()=0.0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// use mixed forward and reverse mode
|
||||
jac.SetSize(state_size);
|
||||
jac=0.0;
|
||||
{
|
||||
ADSVector aduu(state_size);
|
||||
for (int ii=0; ii < state_size ; ii++)
|
||||
{
|
||||
aduu[ii].value().value()=uu[ii];
|
||||
}
|
||||
|
||||
ADSType rez;
|
||||
|
||||
ADSType::TapeType& tape = ADSType::getGlobalTape();
|
||||
typename ADSType::TapeType::Position pos;
|
||||
for (int ii = 0; ii < state_size ; ii++)
|
||||
{
|
||||
pos=tape.getPosition();
|
||||
tape.setActive();
|
||||
|
||||
for (int jj=0; jj < state_size; jj++)
|
||||
{
|
||||
if (jj==ii) {aduu[jj].value().gradient()=1.0;}
|
||||
else {aduu[jj].value().gradient()=0.0;}
|
||||
tape.registerInput(aduu[jj]);
|
||||
}
|
||||
|
||||
rez=sf(vparam,aduu);
|
||||
tape.registerOutput(rez);
|
||||
tape.setPassive();
|
||||
|
||||
rez.gradient().value()=1.0;
|
||||
tape.evaluate();
|
||||
|
||||
for (int jj=0; jj<(ii+1); jj++)
|
||||
{
|
||||
jac(ii,jj)=aduu[jj].gradient().gradient();
|
||||
jac(jj,ii)=jac(ii,jj);
|
||||
}
|
||||
tape.reset(pos);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
TFunctor<double, const mfem::Vector,
|
||||
mfem::Vector, state_size, param_size> rf;
|
||||
|
||||
TFunctor<ad::ADFloatType, const mfem::Vector,
|
||||
ad::ADVectorType, state_size, param_size> tf;
|
||||
|
||||
TFunctor<ADSType, const mfem::Vector, ADSVector,
|
||||
state_size, param_size> sf;
|
||||
};
|
||||
|
||||
}
|
||||
#else // end MFEM_USE_CODIPACK
|
||||
|
||||
// USE NATIVE IMPLEMENTATION
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace ad
|
||||
{
|
||||
/// MFEM native forward AD-type
|
||||
typedef FDualNumber<double> ADFloatType;
|
||||
/// Vector type for AD-type numbers
|
||||
typedef TAutoDiffVector<ADFloatType> ADVectorType;
|
||||
/// Matrix type for AD-type numbers
|
||||
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
|
||||
}
|
||||
|
||||
/// The class provides an evaluation of the Jacobian of a templated vector
|
||||
/// function provided in the constructor. The Jacobian is evaluated with the
|
||||
/// help of automatic differentiation (AD). The template parameters specify the
|
||||
/// size of the return vector (vector_size), the size of the input vector
|
||||
/// (state_size), and the size of the parameters supplied to the function.
|
||||
template<int vector_size=1, int state_size=1, int param_size=0>
|
||||
class VectorFuncAutoDiff
|
||||
{
|
||||
public:
|
||||
/// F_ is user implemented function to be differentiated by
|
||||
/// VectorFuncAutoDiff. The signature of the function is: F_(mfem::Vector&
|
||||
/// parameters, ad::ADVectroType& state_vector, ad::ADVectorType& result).
|
||||
/// The parameters vector should have size param_size. The state_vector
|
||||
/// should have size state_size, and the result vector should have size
|
||||
/// vector_size. All size parameters are teplate parameters in
|
||||
/// VectorFuncAutoDiff.
|
||||
VectorFuncAutoDiff(
|
||||
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
|
||||
{
|
||||
F=F_;
|
||||
}
|
||||
|
||||
/// Evaluates the Jacobian of the vector function F_ for a set of parameters
|
||||
/// (vparam) and state vector uu. The Jacobian (jac) has dimensions
|
||||
/// [vector_size x state_size].
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ad::ADVectorType aduu(uu); // all dual numbers are initialized to zero
|
||||
ad::ADVectorType rr(vector_size);
|
||||
|
||||
for (int ii = 0; ii < state_size; ii++)
|
||||
{
|
||||
aduu[ii].dual(1.0);
|
||||
F(vparam,aduu,rr);
|
||||
for (int jj = 0; jj < vector_size; jj++)
|
||||
{
|
||||
jac(jj, ii) = rr[jj].dual();
|
||||
}
|
||||
aduu[ii].dual(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
|
||||
|
||||
};
|
||||
|
||||
/// The class provides an evaluation of the Jacobian of a templated vector
|
||||
/// function provided as a functor TFunctor. The Jacobian is evaluated with the
|
||||
/// help of automatic differentiation (AD). The template parameters specify the
|
||||
/// size of the return vector (vector_size), the size of the input vector
|
||||
/// (state_size), and the size of the parameters supplied to the function. The
|
||||
/// TFunctor functor is a template class with parameters [Float data type],
|
||||
/// [Vector type for the additional parameters], [Vector type for the state
|
||||
/// vector and the return residual].
|
||||
/// The integer template parameters are the same ones
|
||||
/// passed to QVectorFuncAutoDiff. \n
|
||||
/// Example: f={sin(a*x*y), cos(b*x*y*z), x*x+y*x} \n
|
||||
/// The vector function has vector_size=3, and state_size=3, i.e., it has
|
||||
/// three arguments [x,y,z]. The parameters [a,b] size is 2.
|
||||
/// The functor class will have the following form
|
||||
/// \code{.cpp}
|
||||
/// template<typename TDataType, typename TParamVector, typename TStateVector,
|
||||
/// int residual_size, int state_size, int param_size>
|
||||
/// class MyVectorFunction{
|
||||
/// public:
|
||||
/// TDataType operator() (TParamVector& vparam, TStateVector& uu, TStateVector& rr)
|
||||
/// {
|
||||
/// auto a=vparam[0];
|
||||
/// auto b=vparam[1];
|
||||
/// rr[0]=sin(a*uu[0]*uu[1]);
|
||||
/// rr[1]=cos(b*uu[0]*uu[1]*uu[2]);
|
||||
/// rr[2]=uu[0]*uu[0]+uu[0]*uu[1];
|
||||
/// }
|
||||
//
|
||||
/// };
|
||||
/// \endcode
|
||||
template<template<typename, typename, typename, int, int, int> class TFunctor
|
||||
, int vector_size=1, int state_size=1, int param_size=0>
|
||||
class QVectorFuncAutoDiff
|
||||
{
|
||||
private:
|
||||
/// MFEM native forward AD-type
|
||||
typedef ad::FDualNumber<double> ADFType;
|
||||
/// Vector type for AD-type numbers
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
/// Matrix type for AD-type numbers
|
||||
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
|
||||
|
||||
public:
|
||||
/// Returns a vector valued function rr for supplied passive arguments
|
||||
/// vparam and active arguments uu. The evaluation is based on the user
|
||||
/// supplied TFunctor template class.
|
||||
void VectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
|
||||
{
|
||||
func(vparam, uu, rr);
|
||||
}
|
||||
|
||||
/// Returns the gradient of TFunctor(...) residual in the dense matrix jac.
|
||||
/// The dimensions of jac are vector_size x state_size, where state_size is
|
||||
/// the length of vector uu.
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
// use native AD package
|
||||
jac.SetSize(vector_size, state_size);
|
||||
jac = 0.0;
|
||||
{
|
||||
ADFVector aduu(uu); // all dual numbers are initialized to zero
|
||||
ADFVector rr(vector_size);
|
||||
|
||||
for (int ii = 0; ii < state_size; ii++)
|
||||
{
|
||||
aduu[ii].dual(1.0);
|
||||
Eval(vparam, aduu, rr);
|
||||
for (int jj = 0; jj < vector_size; jj++)
|
||||
{
|
||||
jac(jj, ii) = rr[jj].dual();
|
||||
}
|
||||
aduu[ii].dual(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Evaluates the residual from TFunctor(...).
|
||||
/// Intended for internal use only.
|
||||
void Eval(const Vector &vparam, ADFVector &uu, ADFVector &rr)
|
||||
{
|
||||
tf(vparam, uu, rr);
|
||||
}
|
||||
|
||||
TFunctor<double, const Vector, Vector,
|
||||
vector_size, state_size, param_size> func;
|
||||
|
||||
TFunctor<ADFType, const Vector, ADFVector,
|
||||
vector_size, state_size, param_size> tf;
|
||||
|
||||
};
|
||||
|
||||
/// The class provides an evaluation of the first derivatives and the Hessian of
|
||||
/// a templated scalar function provided as a functor TFunctor. Both the first
|
||||
/// and the second derivatives are evaluated with the help of automatic
|
||||
/// differentiation (AD). The template parameters specify the size of the input
|
||||
/// vector (state_size) and the size of the parameters supplied to the
|
||||
/// function. The TFunctor functor is a template class with parameters [Float
|
||||
/// data type], [Vector type for the additional parameters], [Vector type for
|
||||
/// the state vector and the return residual]. The integer template parameters
|
||||
/// are the same ones passed to QFunctionAutoDiff. The class duplicates Grad and
|
||||
/// Hessian, i.e., VectorFunc calls Grad, and Jacobian calls Hessian. The main
|
||||
/// reason is to provide the same interface as the QVectorFuncAutoDiff class
|
||||
/// used to differentiate vector functions. Such compatibility allows users to
|
||||
/// start implementation of their problem based only on some energy or a weak
|
||||
/// form. The gradients, computed with Grad/VectorFunc, of the function will
|
||||
/// contribute to the FE residual. Computed with Hessian/Jacobian, the Hessian
|
||||
/// will contribute to the tangent matrix in Newton's iterations. Once the
|
||||
/// implementation is complete and tested, the users can start improving the
|
||||
/// performance by replacing Grad/VectorFunc with a hand-coded version. The
|
||||
/// gradient is a vector function and can be differentiated with the
|
||||
/// functionality implemented in QVectorFuncAutoDiff. Thus, the user can
|
||||
/// directly employ AD for computing the contributions to the global tangent
|
||||
/// matrix. The main code will not require changes as the names Grad/VectorFunc
|
||||
/// and Hessian/Jacobian are mirrored.
|
||||
template<template<typename, typename, typename, int, int> class TFunctor
|
||||
, int state_size=1, int param_size=0>
|
||||
class QFunctionAutoDiff
|
||||
{
|
||||
private:
|
||||
/// MFEM native AD-type for first derivatives
|
||||
typedef ad::FDualNumber<double> ADFType;
|
||||
/// Vector type for AD-numbers(first derivatives)
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
/// Matrix type for AD-numbers(first derivatives)
|
||||
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
|
||||
/// MFEM native AD-type for second derivatives
|
||||
typedef ad::FDualNumber<ADFType> ADSType;
|
||||
/// Vector type for AD-numbers (second derivatives)
|
||||
typedef TAutoDiffVector<ADSType> ADSVector;
|
||||
/// Vector type for AD-numbers (second derivatives)
|
||||
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
|
||||
|
||||
public:
|
||||
/// Evaluates a function for arguments vparam and uu. The evaluation is
|
||||
/// based on the operator() in the user provided functor TFunctor.
|
||||
double Eval(const Vector &vparam, Vector &uu)
|
||||
{
|
||||
return tf(vparam,uu);
|
||||
}
|
||||
|
||||
/// Provides the same functionality as Grad.
|
||||
void VectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
|
||||
{
|
||||
Grad(vparam,uu,rr);
|
||||
}
|
||||
|
||||
/// Returns the first derivative of TFunctor(...) with respect to the active
|
||||
/// arguments proved in vector uu. The length of rr is the same as for uu.
|
||||
void Grad(const Vector &vparam, Vector &uu, Vector &rr)
|
||||
{
|
||||
int n = uu.Size();
|
||||
rr.SetSize(n);
|
||||
ADFVector aduu(uu);
|
||||
ADFType rez;
|
||||
for (int ii = 0; ii < n; ii++)
|
||||
{
|
||||
aduu[ii].dual(1.0);
|
||||
rez = ff(vparam, aduu);
|
||||
rr[ii] = rez.dual();
|
||||
aduu[ii].dual(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides same functionality as Hessian.
|
||||
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
Hessian(vparam,uu,jac);
|
||||
}
|
||||
|
||||
/// Returns the Hessian of TFunctor(...) in the dense matrix jac. The
|
||||
/// dimensions of jac are state_size x state_size, where state_size is the
|
||||
/// length of vector uu.
|
||||
void Hessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
|
||||
{
|
||||
int n = uu.Size();
|
||||
jac.SetSize(n);
|
||||
jac = 0.0;
|
||||
{
|
||||
ADSVector aduu(n);
|
||||
for (int ii = 0; ii < n; ii++)
|
||||
{
|
||||
aduu[ii].real(ADFType(uu[ii], 0.0));
|
||||
aduu[ii].dual(ADFType(0.0, 0.0));
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < n; ii++)
|
||||
{
|
||||
aduu[ii].real(ADFType(uu[ii], 1.0));
|
||||
for (int jj = 0; jj < (ii + 1); jj++)
|
||||
{
|
||||
aduu[jj].dual(ADFType(1.0, 0.0));
|
||||
ADSType rez = sf(vparam, aduu);
|
||||
jac(ii, jj) = rez.dual().dual();
|
||||
jac(jj, ii) = rez.dual().dual();
|
||||
aduu[jj].dual(ADFType(0.0, 0.0));
|
||||
}
|
||||
aduu[ii].real(ADFType(uu[ii], 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TFunctor<double, const Vector, Vector, state_size, param_size> tf;
|
||||
TFunctor<ADFType, const Vector, ADFVector, state_size, param_size> ff;
|
||||
TFunctor<ADSType, const Vector, ADSVector, state_size, param_size> sf;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace mfem
|
||||
|
||||
#endif // NATIVE
|
||||
|
||||
#endif // ADMFEM_HPP
|
||||
@@ -0,0 +1,876 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef ADEXAMPLE_HPP
|
||||
#define ADEXAMPLE_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "admfem.hpp"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Example: Implementation of the residual evaluation for p-Laplacian
|
||||
/// problem. The residual is evaluated at the integration points for PDE
|
||||
/// parameters vparam and state fields (derivatives with respect to x,y,z and
|
||||
/// primal field) stored in vector uu.
|
||||
template<typename TDataType, typename TParamVector, typename TStateVector,
|
||||
int residual_size, int state_size, int param_size>
|
||||
class MyResidualFunctor
|
||||
{
|
||||
public:
|
||||
/// The operator returns the first derivative of the energy with respect to
|
||||
/// all state variables. These are set in vector uu and consist of the
|
||||
/// derivatives with respect to x,y,z and the primal field. The derivative is
|
||||
/// stored in vector rr with length equal to the length of vector uu.
|
||||
void operator()(TParamVector &vparam, TStateVector &uu, TStateVector &rr)
|
||||
{
|
||||
MFEM_ASSERT(residual_size==4,
|
||||
"PLaplacianResidual residual_size should be equal to 4!");
|
||||
double pp = vparam[0];
|
||||
double ee = vparam[1];
|
||||
double ff = vparam[2];
|
||||
|
||||
// The vector rr holds the gradients of the following expression:
|
||||
// (u_x^2+u_y^2+u_z^2+\varepsilon^2)^(p/2)-f.u,
|
||||
// where u_x,u_y,u_z are the gradients of the scalar field u.
|
||||
// The state vector is defined as uu=[u_x,u_y,u_z,u].
|
||||
|
||||
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
|
||||
TDataType tvar = pow(ee * ee + norm2, (pp - 2.0) / 2.0);
|
||||
|
||||
rr[0] = tvar * uu[0];
|
||||
rr[1] = tvar * uu[1];
|
||||
rr[2] = tvar * uu[2];
|
||||
rr[3] = -ff;
|
||||
}
|
||||
};
|
||||
|
||||
/// Defines template class (functor) for evaluating the energy of the
|
||||
/// p-Laplacian problem. The input parameters vparam are: vparam[0] - the
|
||||
/// p-Laplacian power, vparam[1] small value ensuring exciting of an unique
|
||||
/// solution, and vparam[2] - the distributed external input to the PDE. The
|
||||
/// template parameter TDataType will be replaced by the compiler with the
|
||||
/// appropriate AD type for automatic differentiation. The TParamVector
|
||||
/// represents the vector type used for the parameter vector, and TStateVector
|
||||
/// the vector type used for the state vector. The template parameters
|
||||
/// state_size and param_size provide information for the size of the state and
|
||||
/// the parameters vectors.
|
||||
template<typename TDataType, typename TParamVector, typename TStateVector
|
||||
, int state_size, int param_size>
|
||||
class MyEnergyFunctor
|
||||
{
|
||||
public:
|
||||
/// Returns the energy of a p-Laplacian for state field input provided in
|
||||
/// vector uu and parameters provided in vector vparam.
|
||||
TDataType operator()(TParamVector &vparam, TStateVector &uu)
|
||||
{
|
||||
MFEM_ASSERT(state_size==4,"MyEnergyFunctor state_size should be equal to 4!");
|
||||
MFEM_ASSERT(param_size==3,"MyEnergyFunctor param_size should be equal to 3!");
|
||||
double pp = vparam[0];
|
||||
double ee = vparam[1];
|
||||
double ff = vparam[2];
|
||||
|
||||
TDataType u = uu[3];
|
||||
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
|
||||
|
||||
TDataType rez = pow(ee * ee + norm2, pp / 2.0) / pp - ff * u;
|
||||
return rez;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Implements integrator for a p-Laplacian problem. The integrator is based on
|
||||
/// a class QFunction utilized for evaluating the energy, the first derivative
|
||||
/// (residual) and the Hessian of the energy (the Jacobian of the residual).
|
||||
/// The template parameter CQVectAutoDiff represents the automatically
|
||||
/// differentiated energy or residual implemented by the user.
|
||||
/// CQVectAutoDiff::VectorFunc(Vector parameters, Vector state,Vector residual)
|
||||
/// evaluates the residual at an integration point.
|
||||
/// CQVectAutoDiff::Jacobian(Vector parameters, Vector state, Matrix hessian)
|
||||
/// evaluates the Hessian of the energy(the Jacobian of the residual).
|
||||
template<class CQVectAutoDiff>
|
||||
class pLaplaceAD : public NonlinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *pp;
|
||||
Coefficient *coeff;
|
||||
Coefficient *load;
|
||||
|
||||
CQVectAutoDiff rdf;
|
||||
|
||||
public:
|
||||
pLaplaceAD()
|
||||
{
|
||||
coeff = nullptr;
|
||||
pp = nullptr;
|
||||
load = nullptr;
|
||||
|
||||
vparam.SetSize(3);
|
||||
vparam[0] = 2.0; // default power
|
||||
vparam[1] = 1e-8; // default epsilon
|
||||
vparam[2] = 1.0; // default load
|
||||
}
|
||||
|
||||
pLaplaceAD(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr)
|
||||
{
|
||||
vparam.SetSize(3);
|
||||
vparam[0] = 2.0; // default power
|
||||
vparam[1] = 1e-8; // default epsilon
|
||||
vparam[2] = 1.0; // default load
|
||||
|
||||
}
|
||||
|
||||
pLaplaceAD(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
|
||||
: pp(&pp_), coeff(&q), load(&ld_)
|
||||
{
|
||||
vparam.SetSize(3);
|
||||
vparam[0] = 2.0; // default power
|
||||
vparam[1] = 1e-8; // default epsilon
|
||||
vparam[2] = 1.0; // default load
|
||||
}
|
||||
|
||||
virtual ~pLaplaceAD() {}
|
||||
|
||||
virtual double GetElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun)
|
||||
{
|
||||
double energy = 0.0;
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
// derivatives in isoparametric coordinates
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
// derivatives in physical space
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
|
||||
Vector uu(4); //[diff_x,diff_y,diff_z,u]
|
||||
|
||||
uu = 0.0;
|
||||
|
||||
// Calculates the functional/energy at an integration point.
|
||||
MyEnergyFunctor<double,Vector,Vector,4,3> qfunc;
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
vparam[0] = pp->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
vparam[1] = coeff->Eval(trans, ip);
|
||||
}
|
||||
// add the contribution from the load
|
||||
if (load != nullptr)
|
||||
{
|
||||
vparam[2] = load->Eval(trans, ip);
|
||||
}
|
||||
// fill the values of vector uu
|
||||
for (int jj = 0; jj < spaceDim; jj++)
|
||||
{
|
||||
uu[jj] = grad[jj] / detJ;
|
||||
}
|
||||
uu[3] = shapef * elfun;
|
||||
// the energy is taken directly from the templated function
|
||||
energy = energy + w * qfunc(vparam,uu);
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
virtual void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
Vector &elvect)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementVector");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector lvec(ndof);
|
||||
elvect.SetSize(ndof);
|
||||
elvect = 0.0;
|
||||
|
||||
DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape]
|
||||
Vector uu(4); // [diff_x,diff_y,diff_z,u]
|
||||
Vector du(4);
|
||||
B = 0.0;
|
||||
uu = 0.0;
|
||||
double w;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
|
||||
|
||||
// set the matrix B
|
||||
for (int jj = 0; jj < spaceDim; jj++)
|
||||
{
|
||||
B.SetCol(jj, dshape_xyz.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3, shapef);
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
vparam[0] = pp->Eval(trans, ip);
|
||||
}
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
vparam[1] = coeff->Eval(trans, ip);
|
||||
}
|
||||
// add the contribution from the load
|
||||
if (load != nullptr)
|
||||
{
|
||||
vparam[2] = load->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// calculate uu
|
||||
B.MultTranspose(elfun, uu);
|
||||
// calculate derivative of the energy with respect to uu
|
||||
rdf.VectorFunc(vparam,uu,du);
|
||||
B.Mult(du, lvec);
|
||||
elvect.Add(w, lvec);
|
||||
} // end integration loop
|
||||
MFEM_PERF_END("AssembleElementVector");
|
||||
}
|
||||
|
||||
virtual void AssembleElementGrad(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementGrad");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
elmat.SetSize(ndof, ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape]
|
||||
DenseMatrix A(ndof, 4);
|
||||
Vector uu(4); // [diff_x,diff_y,diff_z,u]
|
||||
DenseMatrix duu(4, 4);
|
||||
B = 0.0;
|
||||
uu = 0.0;
|
||||
|
||||
double w;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
|
||||
|
||||
// set the matrix B
|
||||
for (int jj = 0; jj < spaceDim; jj++)
|
||||
{
|
||||
B.SetCol(jj, dshape_xyz.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3, shapef);
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
vparam[0] = pp->Eval(trans, ip);
|
||||
}
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
vparam[1] = coeff->Eval(trans, ip);
|
||||
}
|
||||
// add the contribution from the load
|
||||
if (load != nullptr)
|
||||
{
|
||||
vparam[2] = load->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// calculate uu
|
||||
B.MultTranspose(elfun, uu);
|
||||
// calculate derivative of the energy with respect to uu
|
||||
rdf.Jacobian(vparam,uu,duu);
|
||||
Mult(B, duu, A);
|
||||
AddMult_a_ABt(w, A, B, elmat);
|
||||
|
||||
} // end integration loop
|
||||
MFEM_PERF_END("AssembleElementGrad");
|
||||
}
|
||||
|
||||
private:
|
||||
Vector vparam; // [power, epsilon, load]
|
||||
|
||||
};
|
||||
|
||||
/// Implements hand-coded integrator for a p-Laplacian problem. Utilized as
|
||||
/// alternative for the pLaplaceAD class based on automatic differentiation.
|
||||
class pLaplace : public NonlinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *pp;
|
||||
Coefficient *coeff;
|
||||
Coefficient *load;
|
||||
|
||||
public:
|
||||
pLaplace()
|
||||
{
|
||||
coeff = nullptr;
|
||||
pp = nullptr;
|
||||
load = nullptr;
|
||||
}
|
||||
|
||||
pLaplace(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
|
||||
|
||||
pLaplace(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
|
||||
: pp(&pp_), coeff(&q), load(&ld_)
|
||||
{}
|
||||
|
||||
virtual ~pLaplace() {}
|
||||
|
||||
virtual double GetElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun)
|
||||
{
|
||||
double energy = 0.0;
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
double nrgrad2;
|
||||
double ppp = 2.0;
|
||||
double eee = 0.0;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
nrgrad2 = grad * grad / (detJ * detJ);
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
|
||||
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
|
||||
|
||||
// add the contribution from the load
|
||||
if (load != nullptr)
|
||||
{
|
||||
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
|
||||
}
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
virtual void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
Vector &elvect)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementVector");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
Vector lvec(ndof);
|
||||
elvect.SetSize(ndof);
|
||||
elvect = 0.0;
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
double nrgrad;
|
||||
double aa;
|
||||
double ppp = 2.0;
|
||||
double eee = 0.0;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
nrgrad = grad.Norml2() / detJ;
|
||||
// grad is not scaled so far, i.e., grad=grad/detJ
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
// compute (norm of the gradient)^2 + epsilon^2
|
||||
aa = nrgrad * nrgrad + eee * eee;
|
||||
aa = std::pow(aa, (ppp - 2.0) / 2.0);
|
||||
dshape_xyz.Mult(grad, lvec);
|
||||
elvect.Add(w * aa / (detJ * detJ), lvec);
|
||||
|
||||
// add loading
|
||||
if (load != nullptr)
|
||||
{
|
||||
elvect.Add(-w * load->Eval(trans, ip), shapef);
|
||||
}
|
||||
} // end integration loop
|
||||
MFEM_PERF_END("AssembleElementVector");
|
||||
}
|
||||
|
||||
virtual void AssembleElementGrad(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementGrad");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
Vector lvec(ndof);
|
||||
// set the size of the element matrix
|
||||
elmat.SetSize(ndof, ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
double w; // integration weight
|
||||
double detJ;
|
||||
double nrgrad; // norm of the gradient
|
||||
double aa0; // original nonlinear diffusion coefficient
|
||||
double aa1; // gradient of the above
|
||||
double ppp = 2.0; // power in the P-Laplacian
|
||||
double eee = 0.0; // regularization parameter
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
// grad is not scaled so far,i.e., grad=grad/detJ
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
nrgrad = grad.Norml2() / detJ;
|
||||
// (u_x^2+u_y^2+u_z^2+\varepsilon^2)
|
||||
aa0 = nrgrad * nrgrad + eee * eee;
|
||||
aa1 = std::pow(aa0, (ppp - 2.0) / 2.0);
|
||||
aa0 = (ppp - 2.0) * std::pow(aa0, (ppp - 4.0) / 2.0);
|
||||
dshape_xyz.Mult(grad, lvec);
|
||||
w = w / (detJ * detJ);
|
||||
AddMult_a_VVt(w * aa0 / (detJ * detJ), lvec, elmat);
|
||||
AddMult_a_AAt(w * aa1, dshape_xyz, elmat);
|
||||
|
||||
} // end integration loop
|
||||
MFEM_PERF_END("AssembleElementGrad");
|
||||
}
|
||||
};
|
||||
|
||||
/// Implements AD enabled integrator for a p-Laplacian problem. The tangent
|
||||
/// matrix is computed using the residual of the element. The template argument
|
||||
/// should be equal to the size of the residual vector (element vector), i.e.,
|
||||
/// the user should specify the size to match the exact vector size for the
|
||||
/// considered order of the shape functions.
|
||||
|
||||
template<int sizeres=10>
|
||||
class pLaplaceSL : public NonlinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *pp;
|
||||
Coefficient *coeff;
|
||||
Coefficient *load;
|
||||
|
||||
public:
|
||||
pLaplaceSL()
|
||||
{
|
||||
coeff = nullptr;
|
||||
pp = nullptr;
|
||||
load = nullptr;
|
||||
}
|
||||
|
||||
pLaplaceSL(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
|
||||
|
||||
pLaplaceSL(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
|
||||
: pp(&pp_), coeff(&q), load(&ld_)
|
||||
{}
|
||||
|
||||
virtual ~pLaplaceSL() {}
|
||||
|
||||
virtual double GetElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun)
|
||||
{
|
||||
double energy = 0.0;
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
double nrgrad2;
|
||||
double ppp = 2.0;
|
||||
double eee = 0.0;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
nrgrad2 = grad * grad / (detJ * detJ);
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
|
||||
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
|
||||
|
||||
// add the contribution from the load
|
||||
if (load != nullptr)
|
||||
{
|
||||
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
|
||||
}
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
virtual void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
Vector &elvect)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementVector");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
Vector shapef(ndof);
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
Vector grad(spaceDim);
|
||||
Vector lvec(ndof);
|
||||
elvect.SetSize(ndof);
|
||||
elvect = 0.0;
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
double nrgrad;
|
||||
double aa;
|
||||
double ppp = 2.0;
|
||||
double eee = 0.0;
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w; //w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
el.CalcShape(ip, shapef);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
|
||||
// calculate the gradient
|
||||
dshape_xyz.MultTranspose(elfun, grad);
|
||||
nrgrad = grad.Norml2() / detJ;
|
||||
// grad is not scaled so far, i.e., grad=grad/detJ
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
|
||||
aa = nrgrad * nrgrad + eee * eee;
|
||||
aa = std::pow(aa, (ppp - 2.0) / 2.0);
|
||||
dshape_xyz.Mult(grad, lvec);
|
||||
elvect.Add(w * aa / (detJ * detJ), lvec);
|
||||
|
||||
// add loading
|
||||
if (load != nullptr)
|
||||
{
|
||||
elvect.Add(-w * load->Eval(trans, ip), shapef);
|
||||
}
|
||||
} // end integration loop
|
||||
MFEM_PERF_END("AssembleElementVector");
|
||||
}
|
||||
|
||||
virtual void AssembleElementGrad(const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &elfun,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_PERF_BEGIN("AssembleElementGrad");
|
||||
const int ndof = el.GetDof();
|
||||
const int ndim = el.GetDim();
|
||||
const int spaceDim = trans.GetSpaceDim();
|
||||
bool square = (ndim == spaceDim);
|
||||
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
|
||||
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
|
||||
|
||||
DenseMatrix dshape_iso(ndof, ndim);
|
||||
DenseMatrix dshape_xyz(ndof, spaceDim);
|
||||
elmat.SetSize(ndof, ndof);
|
||||
elmat = 0.0;
|
||||
|
||||
double w;
|
||||
double detJ;
|
||||
double ppp = 2.0;
|
||||
double eee = 0.0;
|
||||
|
||||
mfem::Vector param(3); param=0.0;
|
||||
|
||||
// Computes the residual at an integration point. The implementation is a
|
||||
// copy of the integration loop in AssembleElementVector.
|
||||
auto resfun = [&](mfem::Vector& vparam, mfem::ad::ADVectorType& uu,
|
||||
mfem::ad::ADVectorType& vres)
|
||||
{
|
||||
|
||||
vres.SetSize(uu.Size()); vres=0.0;
|
||||
mfem::ad::ADVectorType grad(spaceDim);
|
||||
mfem::ad::ADFloatType nrgrad;
|
||||
mfem::ad::ADFloatType aa;
|
||||
mfem::ad::ADVectorType lvec(ndof);
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
lvec=0.0;
|
||||
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
trans.SetIntPoint(&ip);
|
||||
w = trans.Weight();
|
||||
detJ = (square ? w : w * w);
|
||||
w = ip.weight * w;
|
||||
|
||||
el.CalcDShape(ip, dshape_iso);
|
||||
// AdjugateJacobian = / adj(J), if J is square
|
||||
// \ adj(J^t.J).J^t, otherwise
|
||||
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
|
||||
// dshape_xyz should be divided by detJ for obtaining the real value
|
||||
// grad is not scaled so far,i.e., grad=grad/detJ
|
||||
|
||||
// set the power
|
||||
if (pp != nullptr)
|
||||
{
|
||||
ppp = pp->Eval(trans, ip);
|
||||
}
|
||||
// set the coefficient ensuring positiveness of the tangent matrix
|
||||
if (coeff != nullptr)
|
||||
{
|
||||
eee = coeff->Eval(trans, ip);
|
||||
}
|
||||
|
||||
grad=0.0;
|
||||
// calculate the gradient
|
||||
for (int i=0; i<spaceDim; i++)
|
||||
{
|
||||
for (int j=0; j<ndof; j++)
|
||||
{
|
||||
grad[i]= grad[i]+ dshape_xyz(j,i)*uu[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nrgrad= (grad*grad)/(detJ*detJ);
|
||||
|
||||
aa = nrgrad + eee * eee;
|
||||
aa = pow(aa, (ppp - 2.0) / 2.0);
|
||||
|
||||
for (int i=0; i<spaceDim; i++)
|
||||
{
|
||||
for (int j=0; j<ndof; j++)
|
||||
{
|
||||
lvec[j] = lvec[j] + dshape_xyz(j,i) * grad[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int j=0; j<ndof; j++)
|
||||
{
|
||||
vres[j]=vres[j] + lvec[j] * (w*aa/(detJ*detJ));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mfem::Vector bla(elfun);
|
||||
// calculate the gradient - only for a fixed ndof
|
||||
mfem::VectorFuncAutoDiff<sizeres,sizeres,3> fdr(resfun);
|
||||
fdr.Jacobian(param, bla, elmat);
|
||||
MFEM_PERF_END("AssembleElementGrad");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,650 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef FDUAL_H
|
||||
#define FDUAL_H
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace ad
|
||||
{
|
||||
/** The FDualNumber template class provides forward automatic differentiation
|
||||
(see https://en.wikipedia.org/wiki/Automatic_differentiation) implementation
|
||||
based on dual numbers.
|
||||
|
||||
|
||||
The derivative of an arbitrary function double f(double a) can be obtained
|
||||
by replacing the double type for the return value and the argument a with
|
||||
FDualNumber<double>, i.e., FDualNumber<double> f(FDualNumber<double> a). The
|
||||
derivative is evaluated automatically by calling the function r=f(a). The
|
||||
value of the function is stored in r.pr and the derivative in r.du. These
|
||||
can be extracted by the corresponding methods real()/prim() and dual().
|
||||
|
||||
Internally, the function f can be composed of standard functions predefined
|
||||
for FDualNumber type. These consist of a large set of functions replicating
|
||||
the functionality of the standard math library, i.e., sin, cos, exp, log,
|
||||
etc. New functions (non-member) can be equally added to the class. Example:
|
||||
|
||||
\code{.cpp}
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> cos(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
|
||||
}
|
||||
\endcode
|
||||
|
||||
The real part of the return value consists of the standard real value of the
|
||||
function, i.e., cos(f.real()).
|
||||
|
||||
The dual part of the return value consists of the first derivative of the
|
||||
function with respect to the real part of the argument -sin(f.reaf)
|
||||
multiplied with the dual part of the argument f.dual().
|
||||
*/
|
||||
template<typename tbase>
|
||||
class FDualNumber
|
||||
{
|
||||
private:
|
||||
/// Real value
|
||||
tbase pr;
|
||||
/// Dual value holding derivative information
|
||||
tbase du;
|
||||
|
||||
public:
|
||||
/// Standard constructor - both values are set to zero.
|
||||
FDualNumber() : pr(0), du(0) {}
|
||||
|
||||
/// The constructor utilized in nested definition of dual numbers. It is
|
||||
/// used for second and higher order derivatives.
|
||||
template<class fltyp,
|
||||
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
|
||||
FDualNumber(fltyp &f) : pr(f), du(0)
|
||||
{}
|
||||
|
||||
/// The constructor utilized in nested definition of dual numbers. It is
|
||||
/// used for second and higher order derivatives.
|
||||
template<class fltyp,
|
||||
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
|
||||
FDualNumber(const fltyp &f) : pr(f), du(0)
|
||||
{}
|
||||
|
||||
/// Standard constructor with user supplied input for both parts of the dual
|
||||
/// number.
|
||||
FDualNumber(tbase &pr_, tbase &du_) : pr(pr_), du(du_) {}
|
||||
|
||||
/// Standard constructor with user supplied input for both parts of the dual
|
||||
/// number.
|
||||
FDualNumber(const tbase &pr_, const tbase &du_) : pr(pr_), du(du_) {}
|
||||
|
||||
/// Standard constructor with user supplied dual number.
|
||||
FDualNumber(FDualNumber<tbase> &nm) : pr(nm.pr), du(nm.du) {}
|
||||
|
||||
/// Standard constructor with user supplied dual number.
|
||||
FDualNumber(const FDualNumber<tbase> &nm) : pr(nm.pr), du(nm.du) {}
|
||||
|
||||
/// Return the real value of the dual number.
|
||||
tbase prim() const { return pr; }
|
||||
|
||||
/// Same as prim(). Return the real value of the dual number.
|
||||
tbase real() const { return pr; }
|
||||
|
||||
/// Return the dual value of the dual number.
|
||||
tbase dual() const { return du; }
|
||||
|
||||
/// Set the primal and the dual values.
|
||||
void set(const tbase &pr_, const tbase &du_)
|
||||
{
|
||||
pr = pr_;
|
||||
du = du_;
|
||||
}
|
||||
|
||||
/// Set the primal value.
|
||||
void prim(const tbase &pr_) { pr = pr_; }
|
||||
|
||||
/// Set the primal value.
|
||||
void real(const tbase &pr_) { pr = pr_; }
|
||||
|
||||
/// Set the dual value.
|
||||
void dual(const tbase &du_) { du = du_; }
|
||||
|
||||
/// Set the primal value.
|
||||
void setReal(const tbase &pr_) { pr = pr_; }
|
||||
|
||||
/// Set the dual value.
|
||||
void setDual(const tbase &du_) { du = du_; }
|
||||
|
||||
/// operator =
|
||||
FDualNumber<tbase> &operator=(tbase sc_)
|
||||
{
|
||||
pr = sc_;
|
||||
du = tbase(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator +=
|
||||
FDualNumber<tbase> &operator+=(tbase sc_)
|
||||
{
|
||||
pr = pr + sc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator -=
|
||||
FDualNumber<tbase> &operator-=(tbase sc_)
|
||||
{
|
||||
pr = pr - sc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator *=
|
||||
FDualNumber<tbase> &operator*=(tbase sc_)
|
||||
{
|
||||
pr = pr * sc_;
|
||||
du = du * sc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator /=
|
||||
FDualNumber<tbase> &operator/=(tbase sc_)
|
||||
{
|
||||
pr = pr / sc_;
|
||||
du = du / sc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator =
|
||||
FDualNumber<tbase> &operator=(const FDualNumber<tbase> &f)
|
||||
{
|
||||
pr = f.real();
|
||||
du = f.dual();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator +=
|
||||
FDualNumber<tbase> &operator+=(const FDualNumber<tbase> &f)
|
||||
{
|
||||
pr += f.real();
|
||||
du += f.dual();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator -=
|
||||
FDualNumber<tbase> &operator-=(const FDualNumber<tbase> &f)
|
||||
{
|
||||
pr -= f.real();
|
||||
du -= f.dual();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator *=
|
||||
FDualNumber<tbase> &operator*=(const FDualNumber<tbase> &f)
|
||||
{
|
||||
du = du * f.real();
|
||||
du = du + pr * f.dual();
|
||||
pr = pr * f.real();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator /=
|
||||
FDualNumber<tbase> &operator/=(const FDualNumber<tbase> &f_)
|
||||
{
|
||||
pr = pr / f_.real();
|
||||
du = du - pr * f_.dual();
|
||||
du = du / f_.real();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// non-member functions
|
||||
/// boolean operation ==
|
||||
template<typename tbase>
|
||||
inline bool operator==(const FDualNumber<tbase> &a1,
|
||||
const FDualNumber<tbase> &a2)
|
||||
{
|
||||
return a1.real() == a2.real();
|
||||
}
|
||||
|
||||
/// boolean operation ==
|
||||
template<typename tbase>
|
||||
inline bool operator==(tbase a, const FDualNumber<tbase> &f_)
|
||||
{
|
||||
return a == f_.real();
|
||||
}
|
||||
|
||||
/// boolean operation ==
|
||||
template<typename tbase>
|
||||
inline bool operator==(const FDualNumber<tbase> &a, tbase b)
|
||||
{
|
||||
return a.real() == b;
|
||||
}
|
||||
|
||||
/// boolean operation <
|
||||
template<typename tbase>
|
||||
inline bool operator<(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
return f1.real() < f2.real();
|
||||
}
|
||||
|
||||
/// boolean operation <
|
||||
template<typename tbase>
|
||||
inline bool operator<(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return f.real() < a;
|
||||
}
|
||||
|
||||
/// boolean operation <
|
||||
template<typename tbase>
|
||||
inline bool operator<(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
return a < f.real();
|
||||
}
|
||||
|
||||
/// boolean operation >
|
||||
template<typename tbase>
|
||||
inline bool operator>(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
return f1.real() > f2.real();
|
||||
}
|
||||
|
||||
/// boolean operation >
|
||||
template<typename tbase>
|
||||
inline bool operator>(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return f.real() > a;
|
||||
}
|
||||
|
||||
/// boolean operation >
|
||||
template<typename tbase>
|
||||
inline bool operator>(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
return (a > f.real());
|
||||
}
|
||||
|
||||
/// Negate the real and the dual parts.
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(-f.real(), -f.dual());
|
||||
}
|
||||
|
||||
/// [dual number] - [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<tbase>(f.real() - a, f.dual());
|
||||
}
|
||||
|
||||
/// [dual number<dual number>] - [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator-(const
|
||||
FDualNumber<FDualNumber<tbase>> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(f.real() - a, f.dual());
|
||||
}
|
||||
|
||||
/// [dual number] + [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator+(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<tbase>(f.real() + a, f.dual());
|
||||
}
|
||||
|
||||
/// [dual number<dual number>] + [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator+(const
|
||||
FDualNumber<FDualNumber<tbase>> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(f.real() + a, f.dual());
|
||||
}
|
||||
|
||||
/// [dual number] * [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator*(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<tbase>(f.real() * a, f.dual() * a);
|
||||
}
|
||||
|
||||
/// [dual number] / [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator/(const FDualNumber<tbase> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<tbase>(f.real() / a, f.dual() / a);
|
||||
}
|
||||
|
||||
/// [dual number<dual number>] / [base number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator/(const
|
||||
FDualNumber<FDualNumber<tbase>> &f, tbase a)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(f.real() / a, f.dual() / a);
|
||||
}
|
||||
|
||||
/// [base number] + [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator+(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(a + f.real(), f.dual());
|
||||
}
|
||||
|
||||
/// [base number] + [dual number<dual number>]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator+(tbase a,
|
||||
const FDualNumber<FDualNumber<tbase>> &f)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(a + f.real(), f.dual());
|
||||
}
|
||||
|
||||
/// [base number] - [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator-(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(a - f.real(), -f.dual());
|
||||
}
|
||||
|
||||
/// [base number] - [dual number<dual number>]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator-(tbase a,
|
||||
const FDualNumber<FDualNumber<tbase>> &f)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(a - f.real(), -f.dual());
|
||||
}
|
||||
|
||||
/// [base number] * [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator*(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(f.real() * a, f.dual() * a);
|
||||
}
|
||||
|
||||
/// [base number] * [dual number<dual number>]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<FDualNumber<tbase>> operator*(tbase a,
|
||||
const FDualNumber<FDualNumber<tbase>> &f)
|
||||
{
|
||||
return FDualNumber<FDualNumber<tbase>>(f.real() * a, f.dual() * a);
|
||||
}
|
||||
|
||||
/// [base number] / [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator/(tbase a, const FDualNumber<tbase> &f)
|
||||
{
|
||||
a = a / f.real();
|
||||
return FDualNumber<tbase>(a, -a * f.dual() / f.real());
|
||||
}
|
||||
|
||||
/// [dual number] + [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator+(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
return FDualNumber<tbase>(f1.real() + f2.real(), f1.dual() + f2.dual());
|
||||
}
|
||||
|
||||
/// [dual number] - [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
return FDualNumber<tbase>(f1.real() - f2.real(), f1.dual() - f2.dual());
|
||||
}
|
||||
|
||||
/// [dual number] * [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator*(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
return FDualNumber<tbase>(f1.real() * f2.real(),
|
||||
f1.real() * f2.dual() + f1.dual() * f2.real());
|
||||
}
|
||||
|
||||
/// [dual number] / [dual number]
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> operator/(const FDualNumber<tbase> &f1,
|
||||
const FDualNumber<tbase> &f2)
|
||||
{
|
||||
tbase a = tbase(1) / f2.real();
|
||||
tbase b = f1.real() * a;
|
||||
return FDualNumber<tbase>(b, (f1.dual() - f2.dual() * b) * a);
|
||||
}
|
||||
|
||||
/// acos([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> acos(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(acos(f.real()),
|
||||
-f.dual() / sqrt(tbase(1) - f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// acos([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> acos(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::acos(f.real()),
|
||||
-f.dual() / std::sqrt(double(1) - f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// asin([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> asin(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(asin(f.real()),
|
||||
f.dual() / sqrt(tbase(1) - f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// asin([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> asin(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::asin(f.real()),
|
||||
f.dual() / std::sqrt(double(1) - f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// atan([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> atan(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(atan(f.real()),
|
||||
f.dual() / (tbase(1) + f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// atan([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> atan(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::atan(f.real()),
|
||||
f.dual() / (double(1) + f.real() * f.real()));
|
||||
}
|
||||
|
||||
/// cos([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> cos(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
|
||||
}
|
||||
|
||||
/// cos([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> cos(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::cos(f.real()), -f.dual() * std::sin(f.real()));
|
||||
}
|
||||
|
||||
/// cosh([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> cosh(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(cosh(f.real()), f.dual() * sinh(f.real()));
|
||||
}
|
||||
|
||||
/// cosh([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> cosh(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::cosh(f.real()), f.dual() * std::sinh(f.real()));
|
||||
}
|
||||
|
||||
/// exp([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> exp(const FDualNumber<tbase> &f)
|
||||
{
|
||||
tbase x = exp(f.real());
|
||||
return FDualNumber<tbase>(x, f.dual() * x);
|
||||
}
|
||||
|
||||
/// exp([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> exp(const FDualNumber<double> &f)
|
||||
{
|
||||
double x = std::exp(f.real());
|
||||
return FDualNumber<double>(x, f.dual() * x);
|
||||
}
|
||||
|
||||
/// log([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> log(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(log(f.real()), f.dual() / f.real());
|
||||
}
|
||||
|
||||
/// log([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> log(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::log(f.real()), f.dual() / f.real());
|
||||
}
|
||||
|
||||
/// log10([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> log10(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return log(f) / log(tbase(10));
|
||||
}
|
||||
|
||||
/// log10([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> log10(const FDualNumber<double> &f)
|
||||
{
|
||||
return log(f) / std::log(double(10));
|
||||
}
|
||||
|
||||
/// pow([dual number],[dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> pow(const FDualNumber<tbase> &a,
|
||||
const FDualNumber<tbase> &b)
|
||||
{
|
||||
return exp(log(a) * b);
|
||||
}
|
||||
|
||||
/// pow([dual number], [base number])
|
||||
template<typename tbase, typename tbase1>
|
||||
inline FDualNumber<tbase> pow(const FDualNumber<tbase> &a, const tbase1 &b)
|
||||
{
|
||||
return exp(log(a) * tbase(b));
|
||||
}
|
||||
|
||||
/// pow([base number], [dual number])
|
||||
template<typename tbase, typename tbase1>
|
||||
inline FDualNumber<tbase> pow(const tbase1 &a, const FDualNumber<tbase> &b)
|
||||
{
|
||||
return exp(log(tbase(a)) * b);
|
||||
}
|
||||
|
||||
/// pow([base number], [dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> pow(const double &a, const FDualNumber<double> &b)
|
||||
{
|
||||
return exp(std::log(a) * b);
|
||||
}
|
||||
|
||||
/// sin([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> sin(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(sin(f.real()), f.dual() * cos(f.real()));
|
||||
}
|
||||
|
||||
/// sin([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> sin(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::sin(f.real()), f.dual() * std::cos(f.real()));
|
||||
}
|
||||
|
||||
/// sinh([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> sinh(const FDualNumber<tbase> &f)
|
||||
{
|
||||
return FDualNumber<tbase>(sinh(f.real()), f.dual() * cosh(f.real()));
|
||||
}
|
||||
|
||||
/// sinh([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> sinh(const FDualNumber<double> &f)
|
||||
{
|
||||
return FDualNumber<double>(std::sinh(f.real()), f.dual() * std::cosh(f.real()));
|
||||
}
|
||||
|
||||
/// sqrt([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> sqrt(const FDualNumber<tbase> &f)
|
||||
{
|
||||
tbase a = sqrt(f.real());
|
||||
return FDualNumber<tbase>(a, f.dual() / (tbase(2) * a));
|
||||
}
|
||||
|
||||
/// sqrt([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> sqrt(const FDualNumber<double> &f)
|
||||
{
|
||||
double a = std::sqrt(f.real());
|
||||
return FDualNumber<double>(a, f.dual() / (double(2) * a));
|
||||
}
|
||||
|
||||
/// tan([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> tan(const FDualNumber<tbase> &f)
|
||||
{
|
||||
tbase a = tan(f.real());
|
||||
return FDualNumber<tbase>(a, f.dual() * (tbase(1) + a * a));
|
||||
}
|
||||
|
||||
/// tan([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> tan(const FDualNumber<double> &f)
|
||||
{
|
||||
double a = std::tan(f.real());
|
||||
return FDualNumber<double>(a, f.dual() * (double(1) + a * a));
|
||||
}
|
||||
|
||||
/// tanh([dual number])
|
||||
template<typename tbase>
|
||||
inline FDualNumber<tbase> tanh(const FDualNumber<tbase> &f)
|
||||
{
|
||||
tbase a = tanh(f.real());
|
||||
return FDualNumber<tbase>(a, f.dual() * (tbase(1) - a * a));
|
||||
}
|
||||
|
||||
/// tanh([dual number<double>])
|
||||
template<>
|
||||
inline FDualNumber<double> tanh(const FDualNumber<double> &f)
|
||||
{
|
||||
double a = std::tanh(f.real());
|
||||
return FDualNumber<double>(a, f.dual() * (double(1) - a * a));
|
||||
}
|
||||
|
||||
} // namespace ad
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/autodiff/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
# Include defaults.mk to get XLINKER
|
||||
DEFAULTS_MK = $(MFEM_DIR)/config/defaults.mk
|
||||
include $(DEFAULTS_MK)
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
ADIFF_COMMON_SRC =
|
||||
ADIFF_COMMON_OBJ = $(ADIFF_COMMON_SRC:.cpp=.o)
|
||||
|
||||
SEQ_MINIAPPS = seq_example seq_test
|
||||
PAR_MINIAPPS = par_example
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rules
|
||||
%: %.cpp
|
||||
%.o: %.cpp
|
||||
|
||||
%: %.o $(ADIFF_COMMON_OBJ)
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $^ -o $@ $(MFEM_LIBS)
|
||||
|
||||
%.o: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
|
||||
|
||||
all: $(MINIAPPS)
|
||||
|
||||
MFEM_TESTS = MINIAPPS
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
TEST_NAME := ADIFF miniapp
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TEST_NAME))
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, $(TEST_NAME))
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf Example*
|
||||
@@ -0,0 +1,553 @@
|
||||
// Copyright (c) 2010-2021, 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.
|
||||
//
|
||||
// MFEM AD Example - Parallel Version
|
||||
//
|
||||
// Compile with: make par_example
|
||||
//
|
||||
// Sample runs: mpirun -np 2 par_example -m ../data/beam-quad.mesh -pp 3.8
|
||||
// mpirun -np 2 par_example -m ../data/beam-tri.mesh -pp 7.2
|
||||
// mpirun -np 2 par_example -m ../data/beam-hex.mesh
|
||||
// mpirun -np 2 par_example -m ../data/beam-tet.mesh
|
||||
// mpirun -np 2 par_example -m ../data/beam-wedge.mesh
|
||||
//
|
||||
// Description: This examples solves a quasi-static nonlinear p-Laplacian
|
||||
// problem with zero Dirichlet boundary conditions applied on all
|
||||
// defined boundaries
|
||||
//
|
||||
// The example demonstrates the use of nonlinear operators
|
||||
// combined with automatic differentiation (AD). The integrators
|
||||
// are defined in example.hpp. Selecting integrator = 0 will use
|
||||
// the manually implemented integrator. Selecting integrator = 1
|
||||
// or 2 will utilize one of the AD integrators.
|
||||
//
|
||||
// We recommend viewing examples 1 and 19, before viewing this
|
||||
// example.
|
||||
|
||||
#include "example.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
enum IntegratorType
|
||||
{
|
||||
HandCodedIntegrator = 0,
|
||||
ADJacobianIntegrator = 1,
|
||||
ADHessianIntegrator = 2
|
||||
};
|
||||
|
||||
/// Non-linear solver for the p-Laplacian problem.
|
||||
class ParNLSolverPLaplacian
|
||||
{
|
||||
public:
|
||||
/// Constructor Input: imesh - FE mesh, finite element space, power for the
|
||||
/// p-Laplacian, external load (source, input), regularization parameter
|
||||
ParNLSolverPLaplacian(MPI_Comm comm, ParMesh& imesh,
|
||||
ParFiniteElementSpace& ifespace,
|
||||
double powerp=2,
|
||||
Coefficient* load=nullptr,
|
||||
double regularizationp=1e-7)
|
||||
{
|
||||
lcomm = comm;
|
||||
|
||||
// default parameters for the Newton solver
|
||||
newton_rtol = 1e-4;
|
||||
newton_atol = 1e-8;
|
||||
newton_iter = 10;
|
||||
|
||||
// linear solver
|
||||
linear_rtol = 1e-7;
|
||||
linear_atol = 1e-15;
|
||||
linear_iter = 500;
|
||||
|
||||
print_level = 0;
|
||||
|
||||
// set the mesh
|
||||
mesh=&imesh;
|
||||
|
||||
// set the fespace
|
||||
fespace=&ifespace;
|
||||
|
||||
// set the parameters
|
||||
plap_epsilon=new ConstantCoefficient(regularizationp);
|
||||
plap_power=new ConstantCoefficient(powerp);
|
||||
if (load==nullptr)
|
||||
{
|
||||
plap_input=new ConstantCoefficient(1.0);
|
||||
input_ownership=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
plap_input=load;
|
||||
input_ownership=false;
|
||||
}
|
||||
|
||||
nlform=nullptr;
|
||||
nsolver=nullptr;
|
||||
gmres=nullptr;
|
||||
prec=nullptr;
|
||||
|
||||
// set the default integrator
|
||||
integ=IntegratorType::HandCodedIntegrator;
|
||||
}
|
||||
|
||||
~ParNLSolverPLaplacian()
|
||||
{
|
||||
delete nlform;
|
||||
delete nsolver;
|
||||
delete prec;
|
||||
delete gmres;
|
||||
if (input_ownership) { delete plap_input;}
|
||||
delete plap_epsilon;
|
||||
delete plap_power;
|
||||
}
|
||||
|
||||
/// Set the integrator.
|
||||
/// 0 - hand coded, 1 - AD based (compute only Hessian by AD),
|
||||
/// 2 - AD based (compute residual and Hessian by AD)
|
||||
void SetIntegrator(IntegratorType intr)
|
||||
{
|
||||
integ=intr;
|
||||
}
|
||||
|
||||
// set relative tolerance for the Newton solver
|
||||
void SetNRRTol(double rtol)
|
||||
{
|
||||
newton_rtol=rtol;
|
||||
}
|
||||
|
||||
// set absolute tolerance for the Newton solver
|
||||
void SetNRATol(double atol)
|
||||
{
|
||||
newton_atol=atol;
|
||||
}
|
||||
|
||||
// set max iterations for the NR solver
|
||||
void SetMaxNRIter(int miter)
|
||||
{
|
||||
newton_iter=miter;
|
||||
}
|
||||
|
||||
void SetLSRTol(double rtol)
|
||||
{
|
||||
linear_rtol=rtol;
|
||||
}
|
||||
|
||||
void SetLSATol(double atol)
|
||||
{
|
||||
linear_atol=atol;
|
||||
}
|
||||
|
||||
// set max iterations for the linear solver
|
||||
void SetMaxLSIter(int miter)
|
||||
{
|
||||
linear_iter=miter;
|
||||
}
|
||||
|
||||
// set the print level
|
||||
void SetPrintLevel(int plev)
|
||||
{
|
||||
print_level=plev;
|
||||
}
|
||||
|
||||
/// The state vector is used as initial condition for the NR solver. On
|
||||
/// return the statev holds the solution to the problem.
|
||||
void Solve(Vector& statev)
|
||||
{
|
||||
if (nlform==nullptr)
|
||||
{
|
||||
AllocSolvers();
|
||||
}
|
||||
Vector b; // RHS is zero
|
||||
nsolver->Mult(b, statev);
|
||||
}
|
||||
|
||||
/// Compute the energy
|
||||
double GetEnergy(Vector& statev)
|
||||
{
|
||||
if (nlform==nullptr)
|
||||
{
|
||||
// allocate the solvers
|
||||
AllocSolvers();
|
||||
}
|
||||
return nlform->GetEnergy(statev);
|
||||
}
|
||||
|
||||
private:
|
||||
void AllocSolvers()
|
||||
{
|
||||
if (nlform!=nullptr) { delete nlform;}
|
||||
if (nsolver!=nullptr) { delete nsolver;}
|
||||
if (gmres!=nullptr) { delete gmres;}
|
||||
if (prec!=nullptr) { delete prec;}
|
||||
|
||||
// Define the essential boundary attributes
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
|
||||
nlform = new ParNonlinearForm(fespace);
|
||||
if (integ==IntegratorType::HandCodedIntegrator)
|
||||
{
|
||||
nlform->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,
|
||||
*plap_input));
|
||||
}
|
||||
else if (integ==IntegratorType::ADJacobianIntegrator)
|
||||
{
|
||||
// The template integrator is based on automatic differentiation. For
|
||||
// ADJacobianIntegrator the residual (vector function) at an
|
||||
// integration point is implemented as a functor by MyResidualFunctor.
|
||||
// The vector function has a return size of four(4), four state
|
||||
// arguments, and three(3) parameters. MyResidualFunctor is a template
|
||||
// argument to the actual template class performing the differentiation
|
||||
// - in this case, QVectorFuncAutoDiff. The derivatives are used in the
|
||||
// integration loop in the integrator pLaplaceAD.
|
||||
nlform->AddDomainIntegrator(new
|
||||
pLaplaceAD<mfem::QVectorFuncAutoDiff<MyResidualFunctor,4,4,3>>(*plap_power,
|
||||
*plap_epsilon,*plap_input));
|
||||
}
|
||||
else if (integ==IntegratorType::ADHessianIntegrator)
|
||||
{
|
||||
// The main difference from the previous case is that the user has to
|
||||
// implement only a functional evaluation at an integration point. The
|
||||
// implementation is in MyEnergyFunctor, which takes four state
|
||||
// arguments and three parameters. The residual vector is the first
|
||||
// derivative of the energy/functional with respect to the state
|
||||
// variables, and the Hessian is the second derivative. Automatic
|
||||
// differentiation is used for evaluating both of them.
|
||||
nlform->AddDomainIntegrator(new
|
||||
pLaplaceAD<mfem::QFunctionAutoDiff<MyEnergyFunctor,4,3>>(*plap_power,
|
||||
*plap_epsilon,*plap_input));
|
||||
}
|
||||
|
||||
nlform->SetEssentialBC(ess_bdr);
|
||||
|
||||
prec = new HypreBoomerAMG();
|
||||
prec->SetPrintLevel(print_level);
|
||||
|
||||
gmres = new GMRESSolver(lcomm);
|
||||
gmres->SetAbsTol(linear_atol);
|
||||
gmres->SetRelTol(linear_rtol);
|
||||
gmres->SetMaxIter(linear_iter);
|
||||
gmres->SetPrintLevel(print_level);
|
||||
gmres->SetPreconditioner(*prec);
|
||||
|
||||
nsolver = new NewtonSolver(lcomm);
|
||||
|
||||
nsolver->iterative_mode = true;
|
||||
nsolver->SetSolver(*gmres);
|
||||
nsolver->SetOperator(*nlform);
|
||||
nsolver->SetPrintLevel(print_level);
|
||||
nsolver->SetRelTol(newton_rtol);
|
||||
nsolver->SetAbsTol(newton_atol);
|
||||
nsolver->SetMaxIter(newton_iter);
|
||||
}
|
||||
|
||||
double newton_rtol;
|
||||
double newton_atol;
|
||||
int newton_iter;
|
||||
|
||||
double linear_rtol;
|
||||
double linear_atol;
|
||||
int linear_iter;
|
||||
|
||||
int print_level;
|
||||
|
||||
// power of the p-laplacian
|
||||
Coefficient* plap_power;
|
||||
// regularization parameter
|
||||
Coefficient* plap_epsilon;
|
||||
// load(input) parameter
|
||||
Coefficient* plap_input;
|
||||
// flag indicating the ownership of plap_input
|
||||
bool input_ownership;
|
||||
|
||||
MPI_Comm lcomm;
|
||||
|
||||
ParMesh *mesh;
|
||||
ParFiniteElementSpace *fespace;
|
||||
|
||||
ParNonlinearForm *nlform;
|
||||
|
||||
HypreBoomerAMG *prec;
|
||||
GMRESSolver *gmres;
|
||||
NewtonSolver *nsolver;
|
||||
IntegratorType integ;
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI
|
||||
int num_procs, myrank;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
|
||||
// Define Caliper ConfigManager
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
cali::ConfigManager mgr;
|
||||
#endif
|
||||
// Caliper instrumentation
|
||||
MFEM_PERF_FUNCTION;
|
||||
|
||||
// 2. Parse command-line options
|
||||
const char *mesh_file = "../../data/beam-tet.mesh";
|
||||
int ser_ref_levels = 3;
|
||||
int par_ref_levels = 1;
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
double newton_rel_tol = 1e-4;
|
||||
double newton_abs_tol = 1e-6;
|
||||
int newton_iter = 10;
|
||||
int print_level = 0;
|
||||
|
||||
double pp = 2.0; // p-Laplacian power
|
||||
|
||||
IntegratorType integrator = IntegratorType::ADHessianIntegrator;
|
||||
int int_integrator = integrator;
|
||||
// HandCodedIntegrator = 0 - do not use AD (hand coded)
|
||||
// ADJacobianIntegrator = 1 - use AD for Hessian only
|
||||
// ADHessianIntegrator = 2 - use AD for Residual and Hessian
|
||||
|
||||
const char* cali_config = "runtime-report";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels,
|
||||
"-rs",
|
||||
"--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels,
|
||||
"-rp",
|
||||
"--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order,
|
||||
"-o",
|
||||
"--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&visualization,
|
||||
"-vis",
|
||||
"--visualization",
|
||||
"-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&newton_rel_tol,
|
||||
"-rel",
|
||||
"--relative-tolerance",
|
||||
"Relative tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_abs_tol,
|
||||
"-abs",
|
||||
"--absolute-tolerance",
|
||||
"Absolute tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_iter,
|
||||
"-it",
|
||||
"--newton-iterations",
|
||||
"Maximum iterations for the Newton solve.");
|
||||
args.AddOption(&pp,
|
||||
"-pp",
|
||||
"--power-parameter",
|
||||
"Power parameter (>=2.0) for the p-Laplacian.");
|
||||
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
|
||||
args.AddOption(&int_integrator,
|
||||
"-int",
|
||||
"--integrator",
|
||||
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
|
||||
args.AddOption(&cali_config, "-p", "--caliper",
|
||||
"Caliper configuration string.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myrank == 0)
|
||||
{
|
||||
args.PrintUsage(std::cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myrank == 0)
|
||||
{
|
||||
args.PrintOptions(std::cout);
|
||||
}
|
||||
integrator = static_cast<IntegratorType>(int_integrator);
|
||||
|
||||
StopWatch *timer = new StopWatch();
|
||||
|
||||
// Caliper configuration
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
mgr.add(cali_config);
|
||||
mgr.start();
|
||||
#endif
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
|
||||
// with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define the load for the p-Laplacian
|
||||
ConstantCoefficient load(1.00);
|
||||
|
||||
// 7. Define the finite element spaces for the solution
|
||||
H1_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace fespace(pmesh, &fec, 1, Ordering::byVDIM);
|
||||
HYPRE_Int glob_size = fespace.GlobalTrueVSize();
|
||||
if (myrank == 0)
|
||||
{
|
||||
std::cout << "Number of finite element unknowns: " << glob_size
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// 8. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
HypreParVector *sv = x.GetTrueDofs();
|
||||
|
||||
// 9. Define ParaView DataCollection
|
||||
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example",
|
||||
pmesh);
|
||||
dacol->SetLevelsOfDetail(order);
|
||||
dacol->RegisterField("sol", &x);
|
||||
|
||||
// 10. Define the NR solver
|
||||
ParNLSolverPLaplacian* nr;
|
||||
|
||||
// 11. Start with linear diffusion - solvable for any initial guess
|
||||
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, 2.0, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
nr->SetPrintLevel(print_level);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(*sv);
|
||||
timer->Stop();
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
|
||||
<< std::endl;
|
||||
}
|
||||
// Compute the energy
|
||||
double energy = nr->GetEnergy(*sv);
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp=2] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
}
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(*sv);
|
||||
dacol->SetTime(2.0);
|
||||
dacol->SetCycle(2);
|
||||
dacol->Save();
|
||||
|
||||
// 12. Continue with powers higher than 2
|
||||
for (int i = 3; i < pp; i++)
|
||||
{
|
||||
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, (double)i, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
nr->SetPrintLevel(print_level);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(*sv);
|
||||
timer->Stop();
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp="<<i<<"] The solution time is: " << timer->RealTime()
|
||||
<< std::endl;
|
||||
}
|
||||
// Compute the energy
|
||||
double energy = nr->GetEnergy(*sv);
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp="<<i<<"] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
}
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(*sv);
|
||||
dacol->SetTime((double)i);
|
||||
dacol->SetCycle(i);
|
||||
dacol->Save();
|
||||
}
|
||||
|
||||
// 13. Continue with the final power
|
||||
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, pp, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
nr->SetPrintLevel(print_level);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(*sv);
|
||||
timer->Stop();
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp="<<pp<<"] The solution time is: " << timer->RealTime()
|
||||
<< std::endl;
|
||||
}
|
||||
// Compute the energy
|
||||
double energy = nr->GetEnergy(*sv);
|
||||
if (myrank==0)
|
||||
{
|
||||
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
}
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(*sv);
|
||||
dacol->SetTime(pp);
|
||||
if (pp < 2.0)
|
||||
{
|
||||
dacol->SetCycle(std::floor(pp));
|
||||
}
|
||||
else
|
||||
{
|
||||
dacol->SetCycle(std::ceil(pp));
|
||||
}
|
||||
dacol->Save();
|
||||
}
|
||||
|
||||
// 14. Free the used memory
|
||||
delete dacol;
|
||||
delete sv;
|
||||
delete pmesh;
|
||||
delete timer;
|
||||
|
||||
// Flush output before MPI_finalize
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
mgr.flush();
|
||||
#endif
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
// Copyright (c) 2010-2021, 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.
|
||||
//
|
||||
// MFEM AD Example - Serial Version
|
||||
//
|
||||
// Compile with: make seq_example
|
||||
//
|
||||
// Sample runs: seq_example -m ../data/beam-quad.mesh -pp 3.5
|
||||
// seq_example -m ../data/beam-tri.mesh -pp 4.6
|
||||
// seq_example -m ../data/beam-hex.mesh
|
||||
// seq_example -m ../data/beam-tet.mesh
|
||||
// seq_example -m ../data/beam-wedge.mesh
|
||||
//
|
||||
// Description: This examples solves a quasi-static nonlinear p-Laplacian
|
||||
// problem with zero Dirichlet boundary conditions applied on all
|
||||
// defined boundaries
|
||||
//
|
||||
// The example demonstrates the use of nonlinear operators
|
||||
// combined with automatic differentiation (AD). The integrators
|
||||
// are defined in example.hpp. Selecting integrator = 0 will use
|
||||
// the manually implemented integrator. Selecting integrator = 1
|
||||
// or 2 will utilize one of the AD integrators.
|
||||
//
|
||||
// We recommend viewing examples 1 and 19, before viewing this
|
||||
// example.
|
||||
|
||||
#include "example.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
enum IntegratorType
|
||||
{
|
||||
HandCodedIntegrator = 0,
|
||||
ADJacobianIntegrator = 1,
|
||||
ADHessianIntegrator = 2
|
||||
};
|
||||
|
||||
/// Non-linear solver for the p-Laplacian problem.
|
||||
class NLSolverPLaplacian
|
||||
{
|
||||
public:
|
||||
/// Constructor Input: imesh - FE mesh, finite element space, power for the
|
||||
/// p-Laplacian, external load (source, input), regularization parameter
|
||||
NLSolverPLaplacian(Mesh& imesh, FiniteElementSpace& ifespace,
|
||||
double powerp=2,
|
||||
Coefficient* load=nullptr,
|
||||
double regularizationp=1e-7)
|
||||
{
|
||||
// default parameters for the Newton solver
|
||||
newton_rtol = 1e-4;
|
||||
newton_atol = 1e-8;
|
||||
newton_iter = 10;
|
||||
|
||||
// linear solver
|
||||
linear_rtol = 1e-7;
|
||||
linear_atol = 1e-15;
|
||||
linear_iter = 500;
|
||||
|
||||
print_level = 0;
|
||||
|
||||
// set the mesh
|
||||
mesh=&imesh;
|
||||
|
||||
// set the fespace
|
||||
fespace=&ifespace;
|
||||
|
||||
// set the parameters
|
||||
plap_epsilon=new ConstantCoefficient(regularizationp);
|
||||
plap_power=new ConstantCoefficient(powerp);
|
||||
if (load==nullptr)
|
||||
{
|
||||
plap_input=new ConstantCoefficient(1.0);
|
||||
input_ownership=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
plap_input=load;
|
||||
input_ownership=false;
|
||||
}
|
||||
|
||||
// set the nonlinear form
|
||||
nlform=nullptr;
|
||||
lsolver=nullptr;
|
||||
prec=nullptr;
|
||||
nsolver=nullptr;
|
||||
|
||||
// set the default integrator
|
||||
integ=IntegratorType::HandCodedIntegrator; // hand coded
|
||||
}
|
||||
|
||||
~NLSolverPLaplacian()
|
||||
{
|
||||
if (nlform!=nullptr) { delete nlform;}
|
||||
if (nsolver!=nullptr) { delete nsolver;}
|
||||
if (prec!=nullptr) { delete prec;}
|
||||
if (lsolver!=nullptr) { delete lsolver;}
|
||||
if (input_ownership) { delete plap_input;}
|
||||
delete plap_epsilon;
|
||||
delete plap_power;
|
||||
}
|
||||
|
||||
/// Set the integrator.
|
||||
/// 0 - hand coded, 1 - AD based (compute only Hessian by AD),
|
||||
/// 2 - AD based (compute residual and Hessian by AD)
|
||||
void SetIntegrator(IntegratorType intr)
|
||||
{
|
||||
integ=intr;
|
||||
}
|
||||
|
||||
|
||||
// set relative tolerance for the Newton solver
|
||||
void SetNRRTol(double rtol)
|
||||
{
|
||||
newton_rtol=rtol;
|
||||
}
|
||||
|
||||
// set absolute tolerance for the Newton solver
|
||||
void SetNRATol(double atol)
|
||||
{
|
||||
newton_atol=atol;
|
||||
}
|
||||
|
||||
// set max iterations for the NR solver
|
||||
void SetMaxNRIter(int miter)
|
||||
{
|
||||
newton_iter=miter;
|
||||
}
|
||||
|
||||
void SetLSRTol(double rtol)
|
||||
{
|
||||
linear_rtol=rtol;
|
||||
}
|
||||
|
||||
void SetLSATol(double atol)
|
||||
{
|
||||
linear_atol=atol;
|
||||
}
|
||||
|
||||
// set max iterations for the linear solver
|
||||
void SetMaxLSIter(int miter)
|
||||
{
|
||||
linear_iter=miter;
|
||||
}
|
||||
|
||||
// set the print level
|
||||
void SetPrintLevel(int plev)
|
||||
{
|
||||
print_level=plev;
|
||||
}
|
||||
|
||||
/// The state vector is used as initial condition for the NR solver. On
|
||||
/// return the statev holds the solution to the problem.
|
||||
void Solve(Vector& statev)
|
||||
{
|
||||
if (nlform==nullptr)
|
||||
{
|
||||
AllocSolvers();
|
||||
}
|
||||
Vector b; // RHS is zero
|
||||
nsolver->Mult(b, statev);
|
||||
}
|
||||
|
||||
/// Compute the energy
|
||||
double GetEnergy(Vector& statev)
|
||||
{
|
||||
if (nlform==nullptr)
|
||||
{
|
||||
// allocate the solvers
|
||||
AllocSolvers();
|
||||
}
|
||||
return nlform->GetEnergy(statev);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void AllocSolvers()
|
||||
{
|
||||
if (nlform!=nullptr) { delete nlform;}
|
||||
if (nsolver!=nullptr) {delete nsolver;}
|
||||
if (prec!=nullptr) {delete prec;}
|
||||
if (lsolver!=nullptr) { delete lsolver;}
|
||||
|
||||
// Define the essential boundary attributes
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
|
||||
nlform = new NonlinearForm(fespace);
|
||||
|
||||
if (integ==IntegratorType::HandCodedIntegrator)
|
||||
{
|
||||
// standard hand coded integrator
|
||||
nlform->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,
|
||||
*plap_input));
|
||||
}
|
||||
else if (integ==IntegratorType::ADJacobianIntegrator)
|
||||
{
|
||||
// The template integrator is based on automatic differentiation. For
|
||||
// ADJacobianIntegrator the residual (vector function) at an
|
||||
// integration point is implemented as a functor by MyVFunctor. The
|
||||
// vector function has a return size of four(4), four state arguments,
|
||||
// and three(3) parameters. MyVFunctor is a template argument to the
|
||||
// actual template class performing the differentiation - in this case,
|
||||
// QVectorFuncAutoDiff. The derivatives are used in the integration
|
||||
// loop in the integrator pLaplaceAD.
|
||||
nlform->AddDomainIntegrator(new
|
||||
pLaplaceAD<mfem::QVectorFuncAutoDiff<MyResidualFunctor,4,4,3>>(*plap_power,
|
||||
*plap_epsilon,*plap_input));
|
||||
}
|
||||
else // IntegratorType::ADHessianIntegrator
|
||||
{
|
||||
// The main difference from the previous case is that the user has to
|
||||
// implement only a functional evaluation at an integration point. The
|
||||
// implementation is in MyQFunctor, which takes four state arguments
|
||||
// and three parameters. The residual vector is the first derivative of
|
||||
// the energy/functional with respect to the state variables, and the
|
||||
// Hessian is the second derivative. Automatic differentiation is used
|
||||
// for evaluating both of them.
|
||||
nlform->AddDomainIntegrator(new
|
||||
pLaplaceAD<mfem::QFunctionAutoDiff<MyEnergyFunctor,4,3>>(*plap_power,
|
||||
*plap_epsilon,*plap_input));
|
||||
}
|
||||
|
||||
nlform->SetEssentialBC(ess_bdr);
|
||||
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
prec = new UMFPackSolver();
|
||||
#else
|
||||
prec = new GSSmoother();
|
||||
#endif
|
||||
|
||||
// allocate the linear solver
|
||||
lsolver=new CGSolver();
|
||||
lsolver->SetRelTol(linear_rtol);
|
||||
lsolver->SetAbsTol(linear_atol);
|
||||
lsolver->SetMaxIter(linear_iter);
|
||||
lsolver->SetPrintLevel(print_level);
|
||||
lsolver->SetPreconditioner(*prec);
|
||||
|
||||
// allocate the NR solver
|
||||
nsolver = new NewtonSolver();
|
||||
nsolver->iterative_mode = true;
|
||||
nsolver->SetSolver(*lsolver);
|
||||
nsolver->SetOperator(*nlform);
|
||||
nsolver->SetPrintLevel(print_level);
|
||||
nsolver->SetRelTol(newton_rtol);
|
||||
nsolver->SetAbsTol(newton_atol);
|
||||
nsolver->SetMaxIter(newton_iter);
|
||||
}
|
||||
|
||||
double newton_rtol;
|
||||
double newton_atol;
|
||||
int newton_iter;
|
||||
|
||||
double linear_rtol;
|
||||
double linear_atol;
|
||||
int linear_iter;
|
||||
|
||||
int print_level;
|
||||
|
||||
// reference to the mesh
|
||||
Mesh* mesh;
|
||||
// reference to the fespace
|
||||
FiniteElementSpace *fespace;
|
||||
|
||||
// nonlinear form for the p-laplacian
|
||||
NonlinearForm *nlform;
|
||||
CGSolver *lsolver; // linear solver
|
||||
Solver *prec; // preconditioner for the linear solver
|
||||
NewtonSolver *nsolver; // NR solver
|
||||
IntegratorType integ;
|
||||
|
||||
// power of the p-laplacian
|
||||
Coefficient* plap_power;
|
||||
// regularization parameter
|
||||
Coefficient* plap_epsilon;
|
||||
// load(input) parameter
|
||||
Coefficient* plap_input;
|
||||
// flag indicating the ownership of plap_input
|
||||
bool input_ownership;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options
|
||||
const char *mesh_file = "../../data/beam-tet.mesh";
|
||||
int ser_ref_levels = 3;
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
double newton_rel_tol = 1e-4;
|
||||
double newton_abs_tol = 1e-6;
|
||||
int newton_iter = 10;
|
||||
int print_level = 0;
|
||||
|
||||
double pp = 2.0; // p-Laplacian power
|
||||
|
||||
IntegratorType integrator = IntegratorType::ADHessianIntegrator;
|
||||
int int_integrator = integrator;
|
||||
// HandCodedIntegrator = 0 - do not use AD (hand coded)
|
||||
// ADJacobianIntegrator = 1 - use AD for Hessian only
|
||||
// ADHessianIntegrator = 2 - use AD for Residual and Hessian
|
||||
StopWatch *timer = new StopWatch();
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels,
|
||||
"-rs",
|
||||
"--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&order,
|
||||
"-o",
|
||||
"--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&visualization,
|
||||
"-vis",
|
||||
"--visualization",
|
||||
"-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&newton_rel_tol,
|
||||
"-rel",
|
||||
"--relative-tolerance",
|
||||
"Relative tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_abs_tol,
|
||||
"-abs",
|
||||
"--absolute-tolerance",
|
||||
"Absolute tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_iter,
|
||||
"-it",
|
||||
"--newton-iterations",
|
||||
"Maximum iterations for the Newton solve.");
|
||||
args.AddOption(&pp,
|
||||
"-pp",
|
||||
"--power-parameter",
|
||||
"Power parameter (>=2.0) for the p-Laplacian.");
|
||||
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
|
||||
args.AddOption(&int_integrator,
|
||||
"-int",
|
||||
"--integrator",
|
||||
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(std::cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(std::cout);
|
||||
integrator = static_cast<IntegratorType>(int_integrator);
|
||||
|
||||
// 2. Read the (serial) mesh from the given mesh file.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 4. Define the load parameter for the p-Laplacian
|
||||
ConstantCoefficient load(1.00);
|
||||
|
||||
// 5. Define the finite element spaces for the solution
|
||||
H1_FECollection fec(order, dim);
|
||||
FiniteElementSpace fespace(mesh, &fec, 1, Ordering::byVDIM);
|
||||
int glob_size = fespace.GetTrueVSize();
|
||||
|
||||
std::cout << "Number of finite element unknowns: " << glob_size << std::endl;
|
||||
|
||||
// 6. Define the solution grid function
|
||||
GridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 7. Define the solution true vector
|
||||
Vector sv(fespace.GetTrueVSize());
|
||||
sv = 0.0;
|
||||
|
||||
// 8. Define ParaView DataCollection
|
||||
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example", mesh);
|
||||
dacol->SetLevelsOfDetail(order);
|
||||
dacol->RegisterField("sol", &x);
|
||||
|
||||
// 9. Define the nonlinear p-Laplacian solver
|
||||
NLSolverPLaplacian* nr;
|
||||
|
||||
// 10. Start with linear diffusion - solvable for any initial guess
|
||||
nr=new NLSolverPLaplacian(*mesh, fespace, 2.0, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(sv);
|
||||
timer->Stop();
|
||||
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
|
||||
<< std::endl;
|
||||
// Compute the energy
|
||||
double energy = nr->GetEnergy(sv);
|
||||
std::cout << "[pp=2] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(sv);
|
||||
dacol->SetTime(2.0);
|
||||
dacol->SetCycle(2);
|
||||
dacol->Save();
|
||||
|
||||
|
||||
// 11. Continue with powers higher than 2
|
||||
for (int i = 3; i < pp; i++)
|
||||
{
|
||||
nr=new NLSolverPLaplacian(*mesh, fespace, (double)i, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(sv);
|
||||
timer->Stop();
|
||||
std::cout << "[pp=" << i
|
||||
<< "] The solution time is: " << timer->RealTime() << std::endl;
|
||||
energy = nr->GetEnergy(sv);
|
||||
std::cout << "[pp="<< i<<"] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(sv);
|
||||
dacol->SetTime(i);
|
||||
dacol->SetCycle(i);
|
||||
dacol->Save();
|
||||
}
|
||||
|
||||
// 12. Continue with the final power
|
||||
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
nr=new NLSolverPLaplacian(*mesh, fespace, pp, &load);
|
||||
nr->SetIntegrator(integrator);
|
||||
nr->SetMaxNRIter(newton_iter);
|
||||
nr->SetNRATol(newton_abs_tol);
|
||||
nr->SetNRRTol(newton_rel_tol);
|
||||
timer->Clear();
|
||||
timer->Start();
|
||||
nr->Solve(sv);
|
||||
timer->Stop();
|
||||
std::cout << "[pp=" << pp
|
||||
<< "] The solution time is: " << timer->RealTime() << std::endl;
|
||||
energy = nr->GetEnergy(sv);
|
||||
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
|
||||
<< std::endl;
|
||||
delete nr;
|
||||
x.SetFromTrueDofs(sv);
|
||||
dacol->SetTime(pp);
|
||||
if (pp < 2.0)
|
||||
{
|
||||
dacol->SetCycle(std::floor(pp));
|
||||
}
|
||||
else
|
||||
{
|
||||
dacol->SetCycle(std::ceil(pp));
|
||||
}
|
||||
dacol->Save();
|
||||
}
|
||||
|
||||
// 13. Free the memory
|
||||
delete dacol;
|
||||
delete mesh;
|
||||
delete timer;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2010-2021, 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 "admfem.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
template<typename TDataType, typename TParamVector, typename TStateVector
|
||||
, int state_size, int param_size>
|
||||
class DiffusionFunctional
|
||||
{
|
||||
public:
|
||||
TDataType operator() (TParamVector& vparam, TStateVector& uu)
|
||||
{
|
||||
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
|
||||
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
|
||||
auto kappa = vparam[0]; // diffusion coefficient
|
||||
auto load = vparam[1]; // volumetric influx
|
||||
TDataType rez = kappa*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])/2.0 - load*uu[3];
|
||||
return rez;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TDataType, typename TParamVector, typename TStateVector,
|
||||
int residual_size, int state_size, int param_size>
|
||||
class DiffusionResidual
|
||||
{
|
||||
public:
|
||||
void operator ()(TParamVector& vparam, TStateVector& uu, TStateVector& rr)
|
||||
{
|
||||
MFEM_ASSERT(residual_size==4,
|
||||
"DiffusionResidual residual_size should be equal to 4!");
|
||||
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
|
||||
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
|
||||
auto kappa = vparam[0]; // diffusion coefficient
|
||||
auto load = vparam[1]; // volumetric influx
|
||||
|
||||
rr[0] = kappa * uu[0];
|
||||
rr[1] = kappa * uu[1];
|
||||
rr[2] = kappa * uu[2];
|
||||
rr[3] = -load;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_ADFORWARD
|
||||
std::cout<<"MFEM_USE_ADFORWARD == true"<<std::endl;
|
||||
#else
|
||||
std::cout<<"MFEM_USE_ADFORWARD == false"<<std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
cali::ConfigManager mgr;
|
||||
#endif
|
||||
// Caliper instrumentation
|
||||
MFEM_PERF_FUNCTION;
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
const char* cali_config = "runtime-report";
|
||||
mgr.add(cali_config);
|
||||
mgr.start();
|
||||
#endif
|
||||
mfem::Vector param(2);
|
||||
param[0]=3.0; // diffusion coefficient
|
||||
param[1]=2.0; // volumetric influx
|
||||
|
||||
mfem::Vector state(4);
|
||||
state[0]=1.0; // grad_x
|
||||
state[1]=2.0; // grad_y
|
||||
state[2]=3.0; // grad_z
|
||||
state[3]=4.0; // state value
|
||||
|
||||
mfem::QFunctionAutoDiff<DiffusionFunctional,4,2> adf;
|
||||
|
||||
mfem::Vector rr0(4);
|
||||
mfem::DenseMatrix hh0(4,4);
|
||||
|
||||
mfem::Vector rr1(4);
|
||||
mfem::DenseMatrix hh1(4,4);
|
||||
MFEM_PERF_BEGIN("Grad");
|
||||
adf.Grad(param,state,rr0);
|
||||
MFEM_PERF_END("Grad");
|
||||
MFEM_PERF_BEGIN("Hessian");
|
||||
adf.Hessian(param, state, hh0);
|
||||
MFEM_PERF_END("Hessian");
|
||||
// dump out the results
|
||||
std::cout<<"FunctionAutoDiff"<<std::endl;
|
||||
std::cout<< adf.Eval(param,state)<<std::endl;
|
||||
rr0.Print(std::cout);
|
||||
hh0.Print(std::cout);
|
||||
|
||||
mfem::QVectorFuncAutoDiff<DiffusionResidual,4,4,2> rdf;
|
||||
MFEM_PERF_BEGIN("Jacobian");
|
||||
rdf.Jacobian(param, state, hh1);
|
||||
MFEM_PERF_END("Jacobian");
|
||||
|
||||
std::cout<<"ResidualAutoDiff"<<std::endl;
|
||||
hh1.Print(std::cout);
|
||||
|
||||
// using lambda expression
|
||||
auto func = [](mfem::Vector& vparam,
|
||||
mfem::ad::ADVectorType& uu,
|
||||
mfem::ad::ADVectorType& vres)
|
||||
{
|
||||
// auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
|
||||
auto kappa = vparam[0]; // diffusion coefficient
|
||||
auto load = vparam[1]; // volumetric influx
|
||||
|
||||
vres[0] = kappa * uu[0];
|
||||
vres[1] = kappa * uu[1];
|
||||
vres[2] = kappa * uu[2];
|
||||
vres[3] = -load;
|
||||
};
|
||||
|
||||
mfem::VectorFuncAutoDiff<4,4,2> fdr(func);
|
||||
MFEM_PERF_BEGIN("JacobianV");
|
||||
fdr.Jacobian(param,state,
|
||||
hh1); // computes the gradient of func and stores the result in hh1
|
||||
MFEM_PERF_END("JacobianV");
|
||||
std::cout<<"LambdaAutoDiff"<<std::endl;
|
||||
hh1.Print(std::cout);
|
||||
|
||||
|
||||
double kappa = param[0];
|
||||
double load = param[1];
|
||||
// using lambda expression
|
||||
auto func01 = [&kappa,&load](mfem::Vector& vparam,
|
||||
mfem::ad::ADVectorType& uu,
|
||||
mfem::ad::ADVectorType& vres)
|
||||
{
|
||||
// auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
|
||||
|
||||
vres[0] = kappa * uu[0];
|
||||
vres[1] = kappa * uu[1];
|
||||
vres[2] = kappa * uu[2];
|
||||
vres[3] = -load;
|
||||
};
|
||||
|
||||
mfem::VectorFuncAutoDiff<4,4,2> fdr01(func01);
|
||||
MFEM_PERF_BEGIN("Jacobian1");
|
||||
fdr01.Jacobian(param,state,hh1);
|
||||
MFEM_PERF_END("Jacobian1");
|
||||
std::cout<<"LambdaAutoDiff 01"<<std::endl;
|
||||
hh1.Print(std::cout);
|
||||
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
mgr.flush();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef TADDENSEMATRIX_H
|
||||
#define TADDENSEMATRIX_H
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "tadvector.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// Templated dense matrix data type.
|
||||
/** The main goal of the TAutoDiffDenseMatrix class is to serve as a data
|
||||
container for representing dense matrices in classes, methods, and functions
|
||||
utilized with automatic differentiation (AD). The functionality/interface is
|
||||
copied from the standard MFEM dense matrix mfem::DenseMatrix. The basic idea
|
||||
is to utilize the templated vector class in combination with AD during the
|
||||
development phase. The AD parts can be replaced with optimized code once the
|
||||
initial development of the application is complete. The common interface
|
||||
between TAutoDiffDenseMatrix and DenseMatrix will ease the transition from
|
||||
AD to hand-optimized code as it does not require a change in the interface
|
||||
or the code structure. TAutoDiffDenseMatrix is intended to be utilized for
|
||||
dense serial matrices. The objects can be combined with TAutoDiffVector or
|
||||
standard Vector.*/
|
||||
template<typename dtype>
|
||||
class TAutoDiffDenseMatrix
|
||||
{
|
||||
private:
|
||||
int height; ///< Dimension of the output / number of rows in the matrix.
|
||||
int width; ///< Dimension of the input / number of columns in the matrix.
|
||||
dtype *data;
|
||||
int capacity; // zero or negative capacity means we do not own the data.
|
||||
|
||||
public:
|
||||
/// Get the height (size of output) of the Operator. Synonym with NumRows().
|
||||
inline int Height() const { return height; }
|
||||
/** @brief Get the number of rows (size of output) of the Operator. Synonym
|
||||
with Height(). */
|
||||
inline int NumRows() const { return height; }
|
||||
|
||||
/// Get the width (size of input) of the Operator. Synonym with NumCols().
|
||||
inline int Width() const { return width; }
|
||||
/** @brief Get the number of columns (size of input) of the Operator. Synonym
|
||||
with Width(). */
|
||||
inline int NumCols() const { return width; }
|
||||
|
||||
/** Default constructor for TAutoDiffDenseMatrix.
|
||||
Sets data = NULL and height = width = 0. */
|
||||
TAutoDiffDenseMatrix()
|
||||
{
|
||||
data = nullptr;
|
||||
capacity = 0;
|
||||
height = 0;
|
||||
width = 0;
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
template<typename idtype>
|
||||
TAutoDiffDenseMatrix(const TAutoDiffDenseMatrix<idtype> &m)
|
||||
{
|
||||
height = m.GetHeight();
|
||||
width = m.GetWidth();
|
||||
const int hw = height * width;
|
||||
if (hw > 0)
|
||||
{
|
||||
idtype *mdata = m.Data();
|
||||
MFEM_ASSERT(mdata, "invalid source matrix");
|
||||
data = new dtype[hw];
|
||||
capacity = hw;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = mdata[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = nullptr;
|
||||
capacity = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
}
|
||||
/// Copy constructor using standard DenseMatrix
|
||||
TAutoDiffDenseMatrix(const DenseMatrix &m)
|
||||
{
|
||||
height = m.Height();
|
||||
width = m.Width();
|
||||
const int hw = height * width;
|
||||
if (hw > 0)
|
||||
{
|
||||
double *mdata = m.Data();
|
||||
MFEM_ASSERT(mdata, "invalid source matrix");
|
||||
data = new dtype[hw];
|
||||
capacity = hw;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = mdata[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = nullptr;
|
||||
capacity = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates square matrix of size s.
|
||||
explicit TAutoDiffDenseMatrix(int s)
|
||||
{
|
||||
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
|
||||
height = s;
|
||||
width = s;
|
||||
capacity = s * s;
|
||||
if (capacity > 0)
|
||||
{
|
||||
data = new dtype[capacity](); // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates rectangular matrix of size m x n.
|
||||
TAutoDiffDenseMatrix(int m, int n)
|
||||
{
|
||||
MFEM_ASSERT(m >= 0 && n >= 0,
|
||||
"invalid DenseMatrix size: " << m << " x " << n);
|
||||
height = m;
|
||||
width = n;
|
||||
capacity = m * n;
|
||||
if (capacity > 0)
|
||||
{
|
||||
data = new dtype[capacity](); // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TAutoDiffDenseMatrix(const TAutoDiffDenseMatrix<dtype> &mat, char ch)
|
||||
{
|
||||
height = mat.Width();
|
||||
width = mat.Height();
|
||||
capacity = height * width;
|
||||
if (capacity > 0)
|
||||
{
|
||||
data = new dtype[capacity];
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
(*this)(i, j) = mat(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the size of the DenseMatrix to s x s.
|
||||
void SetSize(int s) { SetSize(s, s); }
|
||||
|
||||
/// Change the size of the DenseMatrix to h x w.
|
||||
void SetSize(int h, int w)
|
||||
{
|
||||
MFEM_ASSERT(h >= 0 && w >= 0,
|
||||
"invalid DenseMatrix size: " << h << " x " << w);
|
||||
if (Height() == h && Width() == w)
|
||||
{
|
||||
return;
|
||||
}
|
||||
height = h;
|
||||
width = w;
|
||||
const int hw = h * w;
|
||||
if (hw > std::abs(capacity))
|
||||
{
|
||||
if (capacity > 0)
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
capacity = hw;
|
||||
data = new dtype[hw](); // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline dtype *Data() const { return data; }
|
||||
/// Returns the matrix data array.
|
||||
inline dtype *GetData() const { return data; }
|
||||
|
||||
inline bool OwnsData() const { return (capacity > 0); }
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
dtype &operator()(int i, int j)
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
return data[i + j * height];
|
||||
}
|
||||
|
||||
const dtype &operator()(int i, int j) const
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
return data[i + j * height];
|
||||
}
|
||||
|
||||
dtype &Elem(int i, int j) { return (*this)(i, j); }
|
||||
|
||||
const dtype &Elem(int i, int j) const { return (*this)(i, j); }
|
||||
|
||||
void Mult(const dtype *x, dtype *y) const
|
||||
{
|
||||
if (width == 0)
|
||||
{
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
y[row] = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
dtype *d_col = data;
|
||||
dtype x_col = x[0];
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
y[row] = x_col * d_col[row];
|
||||
}
|
||||
d_col += height;
|
||||
for (int col = 1; col < width; col++)
|
||||
{
|
||||
x_col = x[col];
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
y[row] += x_col * d_col[row];
|
||||
}
|
||||
d_col += height;
|
||||
}
|
||||
}
|
||||
|
||||
void Mult(const TAutoDiffVector<dtype> &x, TAutoDiffVector<dtype> &y) const
|
||||
{
|
||||
MFEM_ASSERT(height == y.Size() && width == x.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
Mult((const dtype *) x, (dtype *) y);
|
||||
}
|
||||
|
||||
dtype operator*(const TAutoDiffDenseMatrix<dtype> &m) const
|
||||
{
|
||||
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const int hw = height * width;
|
||||
dtype a = 0.0;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
a += data[i] * m.data[i];
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void MultTranspose(const dtype *x, dtype *y) const
|
||||
{
|
||||
dtype *d_col = data;
|
||||
for (int col = 0; col < width; col++)
|
||||
{
|
||||
double y_col = 0.0;
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
y_col += x[row] * d_col[row];
|
||||
}
|
||||
y[col] = y_col;
|
||||
d_col += height;
|
||||
}
|
||||
}
|
||||
|
||||
void MultTranspose(const TAutoDiffVector<dtype> &x,
|
||||
TAutoDiffVector<dtype> &y) const
|
||||
{
|
||||
MFEM_ASSERT(height == x.Size() && width == y.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
MultTranspose((const dtype *) x, (dtype *) y);
|
||||
}
|
||||
|
||||
void Randomize(int seed)
|
||||
{
|
||||
// static unsigned int seed = time(0);
|
||||
const double max = (double) (RAND_MAX) + 1.;
|
||||
|
||||
if (seed == 0)
|
||||
{
|
||||
seed = (int) time(0);
|
||||
}
|
||||
|
||||
// srand(seed++);
|
||||
srand((unsigned) seed);
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
data[i] = (dtype)(std::abs(rand() / max));
|
||||
}
|
||||
}
|
||||
|
||||
void RandomizeDiag(int seed)
|
||||
{
|
||||
// static unsigned int seed = time(0);
|
||||
const double max = (double) (RAND_MAX) + 1.;
|
||||
|
||||
if (seed == 0)
|
||||
{
|
||||
seed = (int) time(0);
|
||||
}
|
||||
|
||||
// srand(seed++);
|
||||
srand((unsigned) seed);
|
||||
|
||||
for (int i = 0; i < std::min(height, width); i++)
|
||||
{
|
||||
Elem(i, i) = (dtype)(std::abs(rand() / max));
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates n x n diagonal matrix with diagonal elements c
|
||||
void Diag(dtype c, int n)
|
||||
{
|
||||
SetSize(n);
|
||||
|
||||
const int N = n * n;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
data[i] = (dtype) 0.0;
|
||||
}
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
data[i * (n + 1)] = c;
|
||||
}
|
||||
}
|
||||
/// Creates n x n diagonal matrix with diagonal given by diag
|
||||
template<typename itype>
|
||||
void Diag(itype *diag, int n)
|
||||
{
|
||||
SetSize(n);
|
||||
|
||||
int i, N = n * n;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
data[i] = 0.0;
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
data[i * (n + 1)] = (dtype) diag[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// (*this) = (*this)^t
|
||||
void Transpose()
|
||||
{
|
||||
int i, j;
|
||||
dtype t;
|
||||
|
||||
if (Width() == Height())
|
||||
{
|
||||
for (i = 0; i < Height(); i++)
|
||||
for (j = i + 1; j < Width(); j++)
|
||||
{
|
||||
t = (*this)(i, j);
|
||||
(*this)(i, j) = (*this)(j, i);
|
||||
(*this)(j, i) = t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TAutoDiffDenseMatrix<dtype> T(*this, 't');
|
||||
(*this) = T;
|
||||
}
|
||||
}
|
||||
/// (*this) = A^t
|
||||
template<typename itype>
|
||||
void Transpose(const TAutoDiffDenseMatrix<itype> &A)
|
||||
{
|
||||
SetSize(A.Width(), A.Height());
|
||||
|
||||
for (int i = 0; i < Height(); i++)
|
||||
for (int j = 0; j < Width(); j++)
|
||||
{
|
||||
(*this)(i, j) = (dtype) A(j, i);
|
||||
}
|
||||
}
|
||||
|
||||
/// (*this) = 1/2 ((*this) + (*this)^t)
|
||||
void Symmetrize()
|
||||
{
|
||||
#ifdef MFEM_DEBUG
|
||||
if (Width() != Height())
|
||||
{
|
||||
mfem_error("DenseMatrix::Symmetrize() : not a square matrix!");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < Height(); i++)
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
dtype a = 0.5 * ((*this)(i, j) + (*this)(j, i));
|
||||
(*this)(j, i) = (*this)(i, j) = a;
|
||||
}
|
||||
}
|
||||
|
||||
void Lump()
|
||||
{
|
||||
for (int i = 0; i < Height(); i++)
|
||||
{
|
||||
dtype L = 0.0;
|
||||
for (int j = 0; j < Width(); j++)
|
||||
{
|
||||
L += (*this)(i, j);
|
||||
(*this)(i, j) = (dtype) 0.0;
|
||||
}
|
||||
(*this)(i, i) = L;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename dtype>
|
||||
void CalcAdjugate(const TAutoDiffDenseMatrix<dtype> &a,
|
||||
TAutoDiffDenseMatrix<dtype> &adja)
|
||||
{
|
||||
#ifdef MFEM_DEBUG
|
||||
if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3)
|
||||
{
|
||||
mfem_error("CalcAdjugate(...)");
|
||||
}
|
||||
if (a.Width() != adja.Height() || a.Height() != adja.Width())
|
||||
{
|
||||
mfem_error("CalcAdjugate(...)");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (a.Width() < a.Height())
|
||||
{
|
||||
const dtype *d = a.Data();
|
||||
dtype *ad = adja.Data();
|
||||
if (a.Width() == 1)
|
||||
{
|
||||
// N x 1, N = 2,3
|
||||
ad[0] = d[0];
|
||||
ad[1] = d[1];
|
||||
if (a.Height() == 3)
|
||||
{
|
||||
ad[2] = d[2];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3 x 2
|
||||
double e, g, f;
|
||||
e = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
|
||||
g = d[3] * d[3] + d[4] * d[4] + d[5] * d[5];
|
||||
f = d[0] * d[3] + d[1] * d[4] + d[2] * d[5];
|
||||
|
||||
ad[0] = d[0] * g - d[3] * f;
|
||||
ad[1] = d[3] * e - d[0] * f;
|
||||
ad[2] = d[1] * g - d[4] * f;
|
||||
ad[3] = d[4] * e - d[1] * f;
|
||||
ad[4] = d[2] * g - d[5] * f;
|
||||
ad[5] = d[5] * e - d[2] * f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.Width() == 1)
|
||||
{
|
||||
adja(0, 0) = (dtype) 1.0;
|
||||
}
|
||||
else if (a.Width() == 2)
|
||||
{
|
||||
adja(0, 0) = a(1, 1);
|
||||
adja(0, 1) = -a(0, 1);
|
||||
adja(1, 0) = -a(1, 0);
|
||||
adja(1, 1) = a(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
adja(0, 0) = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);
|
||||
adja(0, 1) = a(0, 2) * a(2, 1) - a(0, 1) * a(2, 2);
|
||||
adja(0, 2) = a(0, 1) * a(1, 2) - a(0, 2) * a(1, 1);
|
||||
|
||||
adja(1, 0) = a(1, 2) * a(2, 0) - a(1, 0) * a(2, 2);
|
||||
adja(1, 1) = a(0, 0) * a(2, 2) - a(0, 2) * a(2, 0);
|
||||
adja(1, 2) = a(0, 2) * a(1, 0) - a(0, 0) * a(1, 2);
|
||||
|
||||
adja(2, 0) = a(1, 0) * a(2, 1) - a(1, 1) * a(2, 0);
|
||||
adja(2, 1) = a(0, 1) * a(2, 0) - a(0, 0) * a(2, 1);
|
||||
adja(2, 2) = a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,699 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_TADVECTOR
|
||||
#define MFEM_TADVECTOR
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
||||
#include <float.h>
|
||||
#define isfinite _finite
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// Templated vector data type.
|
||||
/** The main goal of the TAutoDiffVector class is to serve as a data container
|
||||
for representing vectors in classes, methods, and functions utilized with
|
||||
automatic differentiation (AD). The functionality/interface is copied from
|
||||
the standard MFEM dense vector mfem::Vector. The basic idea is to utilize
|
||||
the templated vector class in combination with AD during the development
|
||||
phase. The AD parts can be replaced with optimized code once the initial
|
||||
development of the application is complete. The common interface between
|
||||
TAutoDiffVector and Vector will ease the transition from AD to
|
||||
hand-optimized code as it does not require a change in the interface or the
|
||||
code structure. TAutoDiffVector is intended to be utilized for dense serial
|
||||
vectors. */
|
||||
template<typename dtype>
|
||||
class TAutoDiffVector
|
||||
{
|
||||
protected:
|
||||
dtype *data;
|
||||
int size;
|
||||
int capacity;
|
||||
|
||||
public:
|
||||
/// Default constructor for Vector. Sets size = 0 and data = NULL.
|
||||
TAutoDiffVector()
|
||||
{
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
}
|
||||
|
||||
/// Copy constructor. Allocates a new data array and copies the data.
|
||||
TAutoDiffVector(const TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
if (s > 0)
|
||||
{
|
||||
size = s;
|
||||
data = new dtype[s];
|
||||
capacity = s;
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = v[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TAutoDiffVector(const Vector &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
if (s > 0)
|
||||
{
|
||||
size = s;
|
||||
capacity = s;
|
||||
data = new dtype[s];
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = v[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Creates vector of size s.
|
||||
/// @warning Entries are not initialized to zero!
|
||||
explicit TAutoDiffVector(int s)
|
||||
{
|
||||
if (s > 0)
|
||||
{
|
||||
size = s;
|
||||
capacity = s;
|
||||
data = new dtype[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a vector referencing an array of doubles, owned by someone else.
|
||||
/** The pointer @a _data can be NULL. The data array can be replaced later
|
||||
with SetData(). */
|
||||
TAutoDiffVector(dtype *_data, int _size)
|
||||
{
|
||||
if (capacity > 0)
|
||||
{
|
||||
delete[] data;
|
||||
capacity = 0;
|
||||
}
|
||||
size = _size;
|
||||
data = _data;
|
||||
}
|
||||
|
||||
/// Reads a vector from multiple files
|
||||
void Load(std::istream **in, int np, int *dim)
|
||||
{
|
||||
int i, j, s;
|
||||
|
||||
s = 0;
|
||||
for (i = 0; i < np; i++)
|
||||
{
|
||||
s += dim[i];
|
||||
}
|
||||
SetSize(s);
|
||||
|
||||
int p = 0;
|
||||
double tmpd;
|
||||
for (i = 0; i < np; i++)
|
||||
{
|
||||
for (j = 0; j < dim[i]; j++)
|
||||
{
|
||||
*in[i] >> tmpd;
|
||||
data[p++] = dtype(tmpd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a vector from an input stream.
|
||||
void Load(std::istream &in, int Size)
|
||||
{
|
||||
SetSize(Size);
|
||||
double tmpd;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
in >> tmpd;
|
||||
data[i] = dtype(tmpd);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a vector from an input stream, reading the size from the stream.
|
||||
void Load(std::istream &in)
|
||||
{
|
||||
int s;
|
||||
in >> s;
|
||||
Load(in, s);
|
||||
}
|
||||
|
||||
/// @brief Resize the vector to size @a s.
|
||||
/** If the new size is less than or equal to Capacity() then the internal
|
||||
data array remains the same. Otherwise, the old array is deleted, if
|
||||
owned, and a new array of size @a s is allocated without copying the
|
||||
previous content of the Vector.
|
||||
@warning In the second case above (new size greater than current one),
|
||||
the vector will allocate new data array, even if it did not own the
|
||||
original data! Also, new entries are not initialized! */
|
||||
void SetSize(int s)
|
||||
{
|
||||
if (s == size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s <= capacity)
|
||||
{
|
||||
size = s;
|
||||
return;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
data = new dtype[s];
|
||||
size = s;
|
||||
capacity = s;
|
||||
}
|
||||
|
||||
/// Set the Vector data and size.
|
||||
/** The Vector does not assume ownership of the new data. The new size is
|
||||
@warning This method should be called only when OwnsData() is false.
|
||||
@sa NewDataAndSize(). */
|
||||
void SetDataAndSize(dtype *d, int s)
|
||||
{
|
||||
if (OwnsData())
|
||||
{
|
||||
delete[] data;
|
||||
capacity = 0;
|
||||
}
|
||||
data = d;
|
||||
size = s;
|
||||
}
|
||||
|
||||
/// Set the Vector data and size, deleting the old data, if owned.
|
||||
/** The Vector does not assume ownership of the new data. The new size is
|
||||
also used as the new Capacity().
|
||||
@sa SetDataAndSize(). */
|
||||
void NewDataAndSize(dtype *d, int s) { SetDataAndSize(d, s); }
|
||||
|
||||
/// Reset the Vector to be a reference to a sub-vector of @a base.
|
||||
inline void MakeRef(TAutoDiffVector<dtype> &base, int offset, int size_)
|
||||
{
|
||||
NewDataAndSize(base.GetData() + offset, size_);
|
||||
}
|
||||
|
||||
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
|
||||
without changing its current size. */
|
||||
inline void MakeRef(TAutoDiffVector<dtype> &base, int offset)
|
||||
{
|
||||
int tsiz = size;
|
||||
NewDataAndSize(base.GetData() + offset, tsiz);
|
||||
}
|
||||
|
||||
/// Destroy a vector
|
||||
void Destroy()
|
||||
{
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
/// Returns the size of the vector.
|
||||
inline int Size() const { return size; }
|
||||
|
||||
/// Return the size of the currently allocated data array.
|
||||
/** It is always true that Capacity() >= Size(). */
|
||||
inline int Capacity() const { return capacity; }
|
||||
|
||||
/// Return a pointer to the beginning of the Vector data.
|
||||
/** @warning This method should be used with caution as it gives write access
|
||||
to the data of const-qualified Vector%s. */
|
||||
inline dtype *GetData() const
|
||||
{
|
||||
return const_cast<dtype *>((const dtype *) data);
|
||||
}
|
||||
|
||||
/// Conversion to `double *`.
|
||||
/** @note This conversion function makes it possible to use [] for indexing
|
||||
in addition to the overloaded operator()(int). */
|
||||
inline operator dtype *() { return data; }
|
||||
|
||||
/// Conversion to `const double *`.
|
||||
/** @note This conversion function makes it possible to use [] for indexing
|
||||
in addition to the overloaded operator()(int). */
|
||||
inline operator const dtype *() const { return data; }
|
||||
|
||||
/// Read the Vector data (host pointer) ownership flag.
|
||||
inline bool OwnsData() const { return (capacity > 0); }
|
||||
|
||||
/// Changes the ownership of the data; after the call the Vector is empty
|
||||
inline void StealData(dtype **p)
|
||||
{
|
||||
*p = data;
|
||||
delete[] data;
|
||||
size = 0;
|
||||
capacity = 0;
|
||||
}
|
||||
|
||||
/// Changes the ownership of the data; after the call the Vector is empty
|
||||
inline dtype *StealData()
|
||||
{
|
||||
dtype *p;
|
||||
StealData(&p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Access Vector entries. Index i = 0 .. size-1.
|
||||
dtype &Elem(int i) { return operator()(i); }
|
||||
/// Read only access to Vector entries. Index i = 0 .. size-1.
|
||||
const dtype &Elem(int i) const { return operator()(i); }
|
||||
|
||||
/// Access Vector entries using () for 0-based indexing.
|
||||
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
|
||||
inline dtype &operator()(int i)
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < size,
|
||||
"index [" << i << "] is out of range [0," << size << ")");
|
||||
|
||||
return data[i];
|
||||
}
|
||||
|
||||
/// Read only access to Vector entries using () for 0-based indexing.
|
||||
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
|
||||
inline const dtype &operator()(int i) const
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < size,
|
||||
"index [" << i << "] is out of range [0," << size << ")");
|
||||
|
||||
return data[i];
|
||||
}
|
||||
|
||||
/// Dot product with a `dtype *` array.
|
||||
dtype operator*(const dtype *v) const
|
||||
{
|
||||
dtype dot = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dot += data[i] * v[i];
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
/// Return the inner-product.
|
||||
dtype operator*(const TAutoDiffVector<dtype> &v) const
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
dtype dot = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dot += data[i] * v[i];
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
dtype operator*(const Vector &v) const
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
dtype dot = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dot += data[i] * v[i];
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
/// Copy Size() entries from @a v.
|
||||
TAutoDiffVector<dtype> &operator=(const dtype *v)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Copy assignment.
|
||||
/** @note Defining this method overwrites the implicitly defined copy
|
||||
assignment operator. */
|
||||
TAutoDiffVector<dtype> &operator=(const TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
SetSize(v.Size());
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TAutoDiffVector<dtype> &operator=(const Vector &v)
|
||||
{
|
||||
SetSize(v.Size());
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Redefine '=' for vector = constant.
|
||||
template<typename ivtype>
|
||||
TAutoDiffVector &operator=(ivtype value)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = (dtype) value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ivtype>
|
||||
TAutoDiffVector &operator*=(ivtype c)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] * c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ivtype>
|
||||
TAutoDiffVector &operator/=(ivtype c)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] / c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TAutoDiffVector &operator-=(const TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] - v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ivtype>
|
||||
TAutoDiffVector &operator-=(ivtype v)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] - v;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TAutoDiffVector &operator+=(const TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] + v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ivtype>
|
||||
TAutoDiffVector &operator+=(ivtype v)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] + v;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// (*this) += a * Va
|
||||
template<typename ivtype, typename vtype>
|
||||
TAutoDiffVector &Add(const ivtype a, const vtype &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = data[i] + a * v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// (*this) = a * x
|
||||
template<typename ivtype, typename vtype>
|
||||
TAutoDiffVector &Set(const ivtype a, const vtype &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = a * v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename vtype>
|
||||
void SetVector(const vtype &v, int offset)
|
||||
{
|
||||
MFEM_ASSERT(v.Size() + offset <= size, "invalid sub-vector");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i + offset] = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// (*this) = -(*this)
|
||||
void Neg()
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = -data[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Swap the contents of two Vectors
|
||||
inline void Swap(TAutoDiffVector<dtype> &other)
|
||||
{
|
||||
Swap(data, other.data);
|
||||
Swap(size, other.size);
|
||||
Swap(capacity, other.capacity);
|
||||
}
|
||||
|
||||
/// Set v = v1 + v2.
|
||||
template<typename vtype1, typename vtype2>
|
||||
friend void add(const vtype1 &v1, const vtype2 &v2, TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
|
||||
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < v.Size(); i++)
|
||||
{
|
||||
v[i] = v1[i] + v2[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set v = v1 + alpha * v2.
|
||||
template<typename vtype1, typename vtype2>
|
||||
friend void add(const vtype1 &v1,
|
||||
dtype alpha,
|
||||
const vtype2 &v2,
|
||||
TAutoDiffVector<dtype> &v)
|
||||
{
|
||||
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
|
||||
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
|
||||
for (int i = 0; i < v.Size(); i++)
|
||||
{
|
||||
v[i] = v1[i] + alpha * v2[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename vtype1, typename vtype2>
|
||||
friend void add(const dtype a,
|
||||
const vtype1 &x,
|
||||
const dtype b,
|
||||
const vtype2 &y,
|
||||
TAutoDiffVector<dtype> &z)
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
|
||||
"incompatible Vectors!");
|
||||
|
||||
for (int i = 0; i < z.Size(); i++)
|
||||
{
|
||||
z[i] = a * x[i] + b * y[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename vtype1, typename vtype2>
|
||||
friend void add(const dtype a,
|
||||
const vtype1 &x,
|
||||
const vtype2 &y,
|
||||
TAutoDiffVector<dtype> &z)
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
|
||||
"incompatible Vectors!");
|
||||
|
||||
for (int i = 0; i < z.Size(); i++)
|
||||
{
|
||||
z[i] = a * x[i] + y[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename vtype1, typename vtype2>
|
||||
friend void subtract(const vtype1 &x, const vtype2 &y,
|
||||
TAutoDiffVector<dtype> &z)
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
|
||||
"incompatible Vectors!");
|
||||
for (int i = 0; i < z.Size(); i++)
|
||||
{
|
||||
z[i] = x[i] - y[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ivtype, typename vtype1, typename vtype2>
|
||||
friend void subtract(const ivtype a,
|
||||
const vtype1 &x,
|
||||
const vtype2 &y,
|
||||
TAutoDiffVector<dtype> &z)
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
|
||||
"incompatible Vectors!");
|
||||
for (int i = 0; i < z.Size(); i++)
|
||||
{
|
||||
z[i] = a * (x[i] - y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys vector.
|
||||
~TAutoDiffVector() { delete[] data; }
|
||||
|
||||
/// Prints vector to stream out.
|
||||
void Print(std::ostream &out = mfem::out, int width = 8) const
|
||||
{
|
||||
if (!size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; 1;)
|
||||
{
|
||||
out << data[i];
|
||||
i++;
|
||||
if (i == size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (i % width == 0)
|
||||
{
|
||||
out << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
out << ' ';
|
||||
}
|
||||
}
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
/// Set random values in the vector.
|
||||
void Randomize(int seed = 0)
|
||||
{
|
||||
// static unsigned int seed = time(0);
|
||||
const double max = (double) (RAND_MAX) + 1.;
|
||||
|
||||
if (seed == 0)
|
||||
{
|
||||
seed = (int) time(0);
|
||||
}
|
||||
|
||||
// srand(seed++);
|
||||
srand((unsigned) seed);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = std::abs(rand() / max);
|
||||
}
|
||||
}
|
||||
/// Returns the l2 norm of the vector.
|
||||
dtype Norml2() const
|
||||
{
|
||||
// Scale entries of Vector on the fly, using algorithms from std::hypot()
|
||||
// and LAPACK's drm2. This scaling ensures that the argument of each call
|
||||
// to std::pow is <= 1 to avoid overflow.
|
||||
if (0 == size)
|
||||
{
|
||||
return 0.0;
|
||||
} // end if 0 == size
|
||||
|
||||
if (1 == size)
|
||||
{
|
||||
return abs(data[0]);
|
||||
} // end if 1 == size
|
||||
|
||||
dtype scale = 0.0;
|
||||
dtype sum = 0.0;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (data[i] != 0.0)
|
||||
{
|
||||
const dtype absdata = abs(data[i]);
|
||||
if (scale <= absdata)
|
||||
{
|
||||
const dtype sqr_arg = scale / absdata;
|
||||
sum = 1.0 + sum * (sqr_arg * sqr_arg);
|
||||
scale = absdata;
|
||||
continue;
|
||||
} // end if scale <= absdata
|
||||
const dtype sqr_arg = absdata / scale;
|
||||
sum += (sqr_arg * sqr_arg); // else scale > absdata
|
||||
} // end if data[i] != 0
|
||||
}
|
||||
return scale * sqrt(sum);
|
||||
}
|
||||
|
||||
/// Returns the l_infinity norm of the vector.
|
||||
dtype Normlinf() const
|
||||
{
|
||||
dtype max = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
max = max(abs(data[i]), max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/// Returns the l_1 norm of the vector.
|
||||
dtype Norml1() const
|
||||
{
|
||||
dtype sum = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
sum += abs(data[i]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -69,6 +69,10 @@
|
||||
// Adaptive limiting through FD (requires GSLIB):
|
||||
// * mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
|
||||
//
|
||||
// Adaptive surface fitting:
|
||||
// mesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor
|
||||
// mesh-optimizer -m square01-tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor
|
||||
//
|
||||
// Blade shape:
|
||||
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
|
||||
// Blade shape with FD-based solver:
|
||||
@@ -96,7 +100,6 @@
|
||||
// mesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
|
||||
// 3D untangling (the mesh is in the mfem/data GitHub repository):
|
||||
// * mesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "../common/mfem-common.hpp"
|
||||
@@ -117,7 +120,8 @@ int main(int argc, char *argv[])
|
||||
int metric_id = 1;
|
||||
int target_id = 1;
|
||||
double lim_const = 0.0;
|
||||
double adapt_lim_const = 0.0;
|
||||
double adapt_lim_const = 0.0;
|
||||
double surface_fit_const = 0.0;
|
||||
int quad_type = 1;
|
||||
int quad_order = 8;
|
||||
int solver_type = 0;
|
||||
@@ -195,6 +199,8 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant.");
|
||||
args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const",
|
||||
"Adaptive limiting coefficient constant.");
|
||||
args.AddOption(&surface_fit_const, "-sfc", "--surface-fit-const",
|
||||
"Surface preservation constant.");
|
||||
args.AddOption(&quad_type, "-qt", "--quad-type",
|
||||
"Quadrature rule type:\n\t"
|
||||
"1: Gauss-Lobatto\n\t"
|
||||
@@ -283,10 +289,6 @@ int main(int argc, char *argv[])
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
|
||||
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
|
||||
const int dim = mesh->Dimension();
|
||||
cout << "Mesh curvature: ";
|
||||
if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); }
|
||||
else { cout << "(NONE)"; }
|
||||
cout << endl;
|
||||
|
||||
if (hradaptivity) { mesh->EnsureNCMesh(); }
|
||||
|
||||
@@ -722,8 +724,6 @@ int main(int argc, char *argv[])
|
||||
<< irules->Get(Geometry::PRISM, quad_order).GetNPoints() << endl;
|
||||
}
|
||||
|
||||
if (normalization) { he_nlf_integ->EnableNormalization(x0); }
|
||||
|
||||
// Limit the node movement.
|
||||
// The limiting distances can be given by a general function of space.
|
||||
FiniteElementSpace dist_fespace(mesh, fec); // scalar space
|
||||
@@ -765,6 +765,77 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// Surface fitting.
|
||||
L2_FECollection mat_coll(0, dim);
|
||||
H1_FECollection sigma_fec(mesh_poly_deg, dim);
|
||||
FiniteElementSpace sigma_fes(mesh, &sigma_fec);
|
||||
FiniteElementSpace mat_fes(mesh, &mat_coll);
|
||||
GridFunction mat(&mat_fes);
|
||||
GridFunction marker_gf(&sigma_fes);
|
||||
GridFunction ls_0(&sigma_fes);
|
||||
Array<bool> marker(ls_0.Size());
|
||||
ConstantCoefficient coef_ls(surface_fit_const);
|
||||
AdaptivityEvaluator *adapt_surface = NULL;
|
||||
if (surface_fit_const > 0.0)
|
||||
{
|
||||
MFEM_VERIFY(hradaptivity == false,
|
||||
"Surface fitting with HR is not implemented yet.");
|
||||
MFEM_VERIFY(pa == false,
|
||||
"Surface fitting with PA is not implemented yet.");
|
||||
|
||||
FunctionCoefficient ls_coeff(surface_level_set);
|
||||
ls_0.ProjectCoefficient(ls_coeff);
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
mat(i) = material_id(i, ls_0);
|
||||
mesh->SetAttribute(i, mat(i) + 1);
|
||||
}
|
||||
|
||||
GridFunctionCoefficient coeff_mat(&mat);
|
||||
marker_gf.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC);
|
||||
for (int j = 0; j < marker.Size(); j++)
|
||||
{
|
||||
if (marker_gf(j) > 0.1 && marker_gf(j) < 0.9)
|
||||
{
|
||||
marker[j] = true;
|
||||
marker_gf(j) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
marker[j] = false;
|
||||
marker_gf(j) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (adapt_eval == 0) { adapt_surface = new AdvectorCG; }
|
||||
else if (adapt_eval == 1)
|
||||
{
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
adapt_surface = new InterpolatorFP;
|
||||
#else
|
||||
MFEM_ABORT("MFEM is not built with GSLIB support!");
|
||||
#endif
|
||||
}
|
||||
else { MFEM_ABORT("Bad interpolation option."); }
|
||||
|
||||
he_nlf_integ->EnableSurfaceFitting(ls_0, marker, coef_ls, *adapt_surface);
|
||||
if (visualization)
|
||||
{
|
||||
socketstream vis1, vis2, vis3;
|
||||
common::VisualizeField(vis1, "localhost", 19916, ls_0, "Level Set 0",
|
||||
300, 600, 300, 300);
|
||||
common::VisualizeField(vis2, "localhost", 19916, mat, "Materials",
|
||||
600, 600, 300, 300);
|
||||
common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Dofs to Move",
|
||||
900, 600, 300, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Has to be after the enabling of the limiting / alignment, as it computes
|
||||
// normalization factors for these terms as well.
|
||||
if (normalization) { he_nlf_integ->EnableNormalization(x0); }
|
||||
|
||||
// 12. Setup the final NonlinearForm (which defines the integral of interest,
|
||||
// its first and second derivatives). Here we can use a combination of
|
||||
// metrics, i.e., optimize the sum of two integrals, where both are
|
||||
@@ -855,6 +926,18 @@ int main(int argc, char *argv[])
|
||||
// For HR tests, the energy is normalized by the number of elements.
|
||||
const double init_energy = a.GetGridFunctionEnergy(x) /
|
||||
(hradaptivity ? mesh->GetNE() : 1);
|
||||
double init_metric_energy = init_energy;
|
||||
if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0)
|
||||
{
|
||||
lim_coeff.constant = 0.0;
|
||||
coef_zeta.constant = 0.0;
|
||||
coef_ls.constant = 0.0;
|
||||
init_metric_energy = a.GetGridFunctionEnergy(x) /
|
||||
(hradaptivity ? mesh->GetNE() : 1);
|
||||
lim_coeff.constant = lim_const;
|
||||
coef_zeta.constant = adapt_lim_const;
|
||||
coef_ls.constant = surface_fit_const;
|
||||
}
|
||||
|
||||
// Visualize the starting mesh and metric values.
|
||||
// Note that for combinations of metrics, this only shows the first metric.
|
||||
@@ -1012,23 +1095,26 @@ int main(int argc, char *argv[])
|
||||
|
||||
const double fin_energy = a.GetGridFunctionEnergy(x) /
|
||||
(hradaptivity ? mesh->GetNE() : 1);
|
||||
double metric_part = fin_energy;
|
||||
double fin_metric_energy = fin_energy;
|
||||
if (lim_const > 0.0 || adapt_lim_const > 0.0)
|
||||
{
|
||||
lim_coeff.constant = 0.0;
|
||||
coef_zeta.constant = 0.0;
|
||||
metric_part = a.GetGridFunctionEnergy(x) /
|
||||
(hradaptivity ? mesh->GetNE() : 1);
|
||||
coef_ls.constant = 0.0;
|
||||
fin_metric_energy = a.GetGridFunctionEnergy(x) /
|
||||
(hradaptivity ? mesh->GetNE() : 1);
|
||||
lim_coeff.constant = lim_const;
|
||||
coef_zeta.constant = adapt_lim_const;
|
||||
coef_ls.constant = surface_fit_const;
|
||||
}
|
||||
std::cout << std::scientific << std::setprecision(4);
|
||||
cout << "Initial strain energy: " << init_energy
|
||||
<< " = metrics: " << init_energy
|
||||
<< " + limiting term: " << 0.0 << endl;
|
||||
<< " = metrics: " << init_metric_energy
|
||||
<< " + extra terms: " << init_energy - init_metric_energy << endl;
|
||||
cout << " Final strain energy: " << fin_energy
|
||||
<< " = metrics: " << metric_part
|
||||
<< " + limiting term: " << fin_energy - metric_part << endl;
|
||||
cout << "The strain energy decreased by: " << setprecision(12)
|
||||
<< " = metrics: " << fin_metric_energy
|
||||
<< " + extra terms: " << fin_energy - fin_metric_energy << endl;
|
||||
cout << "The strain energy decreased by: "
|
||||
<< (init_energy - fin_energy) * 100.0 / init_energy << " %." << endl;
|
||||
|
||||
// 16. Visualize the final mesh and metric values.
|
||||
@@ -1045,6 +1131,22 @@ int main(int argc, char *argv[])
|
||||
600, 600, 300, 300);
|
||||
}
|
||||
|
||||
if (surface_fit_const > 0.0)
|
||||
{
|
||||
if (visualization)
|
||||
{
|
||||
socketstream vis2, vis3;
|
||||
common::VisualizeField(vis2, "localhost", 19916, mat, "Materials",
|
||||
600, 900, 300, 300);
|
||||
common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof",
|
||||
900, 900, 300, 300);
|
||||
}
|
||||
double err_avg, err_max;
|
||||
he_nlf_integ->GetSurfaceFittingErrors(err_avg, err_max);
|
||||
std::cout << "Avg fitting error: " << err_avg << std::endl
|
||||
<< "Max fitting error: " << err_max << std::endl;
|
||||
}
|
||||
|
||||
// 17. Visualize the mesh displacement.
|
||||
if (visualization)
|
||||
{
|
||||
@@ -1066,6 +1168,7 @@ int main(int argc, char *argv[])
|
||||
delete metric2;
|
||||
delete coeff1;
|
||||
delete adapt_evaluator;
|
||||
delete adapt_surface;
|
||||
delete target_c;
|
||||
delete hr_adapt_coeff;
|
||||
delete adapt_coeff;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user