Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c6e5091a | ||
|
|
6bce9ab9cf | ||
|
|
abbbf64bc2 | ||
|
|
d6da6eb322 | ||
|
|
d6334ac0da | ||
|
|
ce1b110505 | ||
|
|
81da10f904 | ||
|
|
db28bea31a |
@@ -82,9 +82,9 @@ jobs:
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
target: optim
|
||||
codecov: NO
|
||||
mpi: par
|
||||
mpi: parallel
|
||||
build-system: make
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
@@ -225,14 +225,6 @@ 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
|
||||
|
||||
+4
-36
@@ -29,34 +29,12 @@ 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:
|
||||
# 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}"
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/quartz-build-and-test.yml
|
||||
strategy: depend
|
||||
@@ -64,11 +42,7 @@ quartz-build-and-test:
|
||||
quartz-baseline:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# 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}"
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/quartz-baseline.yml
|
||||
strategy: depend
|
||||
@@ -76,10 +50,7 @@ quartz-baseline:
|
||||
lassen-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# 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}"
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/lassen-build-and-test.yml
|
||||
strategy: depend
|
||||
@@ -87,10 +58,7 @@ lassen-build-and-test:
|
||||
corona-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
# 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}"
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/corona-build-and-test.yml
|
||||
strategy: depend
|
||||
|
||||
@@ -18,13 +18,19 @@ 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: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
|
||||
BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${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
|
||||
@@ -34,3 +40,5 @@ variables:
|
||||
# Directory used to place artifacts.
|
||||
ARTIFACTS_DIR: artifacts
|
||||
SLURM_OVERLAP: 1
|
||||
|
||||
|
||||
|
||||
@@ -26,20 +26,17 @@ 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
|
||||
|
||||
@@ -49,11 +46,9 @@ 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}
|
||||
- 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
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
|
||||
@@ -21,17 +21,14 @@ 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
|
||||
@@ -42,8 +39,5 @@ variables:
|
||||
extends: [.on_lassen]
|
||||
stage: build_and_test
|
||||
script:
|
||||
- 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
|
||||
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
needs: [setup]
|
||||
|
||||
@@ -22,20 +22,17 @@ 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
|
||||
|
||||
@@ -45,11 +42,9 @@ 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}
|
||||
- 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
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
# 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,6 +9,13 @@
|
||||
# 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:
|
||||
@@ -23,50 +30,13 @@ 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}
|
||||
- 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
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# 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.
|
||||
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:
|
||||
tags:
|
||||
- shell
|
||||
@@ -21,74 +24,11 @@ setup:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
#
|
||||
# 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 "BUILD_ROOT ${BUILD_ROOT}"
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- mkdir -p ${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
|
||||
# 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
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ 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]
|
||||
@@ -41,27 +40,24 @@ 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
|
||||
extends:
|
||||
- .on_corona
|
||||
- .report_job_success
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
extends:
|
||||
- .on_corona
|
||||
- .report_job_failure
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_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,19 +21,18 @@ opt_mpi_cuda_xl_16_1_1_8:
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
extends:
|
||||
- .on_lassen
|
||||
- .report_job_success
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
extends:
|
||||
- .on_lassen
|
||||
- .report_job_failure
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_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
|
||||
|
||||
+15
-84
@@ -16,26 +16,12 @@ 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
|
||||
@@ -43,88 +29,33 @@ 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:
|
||||
- echo ${MACHINE_NAME}
|
||||
- echo ${AUTOTEST}
|
||||
- echo ${AUTOTEST_COMMIT}
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- cd ${AUTOTEST_ROOT}
|
||||
- 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).
|
||||
- |
|
||||
(
|
||||
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
|
||||
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
|
||||
|
||||
baselinepublish_mfem_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
- if: '$REBASELINE == "YES"'
|
||||
- if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
when: manual
|
||||
script:
|
||||
- echo ${BUILD_ROOT}
|
||||
- echo ${PWD}
|
||||
- echo ${ARTIFACTS_DIR}
|
||||
- ls -lA ${ARTIFACTS_DIR}
|
||||
- .gitlab/scripts/rebaseline
|
||||
|
||||
include:
|
||||
|
||||
@@ -22,7 +22,6 @@ 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
|
||||
|
||||
@@ -74,26 +73,23 @@ 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
|
||||
extends:
|
||||
- .on_quartz
|
||||
- .report_job_success
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
extends:
|
||||
- .on_quartz
|
||||
- .report_job_failure
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_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,8 +20,7 @@ base_out=${base}.out
|
||||
artifacts_path=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}
|
||||
|
||||
# prepare
|
||||
cd ${BUILD_ROOT} || \
|
||||
{ echo "Invalid BUILD_ROOT=$BUILD_ROOT"; exit 1; }
|
||||
cd ${BUILD_ROOT}
|
||||
ln -snf ${CI_PROJECT_DIR} mfem
|
||||
cd tests
|
||||
[[ -d _${BASELINE_TEST} ]] && rm -rf _${BASELINE_TEST}
|
||||
@@ -34,9 +33,6 @@ 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
|
||||
@@ -64,10 +60,6 @@ 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,33 +13,20 @@
|
||||
|
||||
echo "Runs if there was at least one failure on ${MACHINE_NAME}"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest || \
|
||||
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
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)
|
||||
|
||||
printf "%s\n" "Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED." \
|
||||
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
|
||||
@@ -13,30 +13,18 @@
|
||||
|
||||
echo "Can only run if all the ${MACHINE_NAME} jobs passed"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest || \
|
||||
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
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)
|
||||
|
||||
printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \
|
||||
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.out
|
||||
echo "The ${MACHINE_NAME} jobs were successful" > ${rundir}/gitlab.out
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
|
||||
|
||||
msg="GitLab CI log for build-and-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
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
|
||||
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.
|
||||
@@ -71,12 +66,6 @@ Version 4.3.1 (development)
|
||||
|
||||
- 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
|
||||
|
||||
+1
-12
@@ -252,11 +252,6 @@ 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)
|
||||
@@ -372,12 +367,6 @@ 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)
|
||||
@@ -457,7 +446,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 CODIPACK BENCHMARK PARELAG MPI_CXX)
|
||||
CUSPARSE MKL_CPARDISO AMGX CALIPER BENCHMARK PARELAG MPI_CXX)
|
||||
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
|
||||
@@ -123,7 +123,6 @@ The MFEM source code has the following structure:
|
||||
├── mesh
|
||||
├── miniapps
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
|
||||
@@ -467,14 +467,6 @@ 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
|
||||
@@ -711,11 +703,6 @@ 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".
|
||||
@@ -921,8 +908,6 @@ 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
|
||||
@@ -982,7 +967,6 @@ The CMake build system adds auto-detection for the following packages/libraries:
|
||||
- POSIXCLOCKS
|
||||
- PUMI
|
||||
- HIOP
|
||||
- CoDiPack
|
||||
- OCCA
|
||||
- RAJA
|
||||
- UMPIRE
|
||||
|
||||
@@ -54,8 +54,6 @@ 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,12 +175,6 @@
|
||||
// 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
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# 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.")
|
||||
@@ -180,12 +180,6 @@
|
||||
// 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,8 +58,6 @@ 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,8 +58,6 @@ 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)
|
||||
|
||||
@@ -245,9 +243,6 @@ 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,9 +59,6 @@ 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
|
||||
@@ -89,9 +86,6 @@ 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
|
||||
@@ -157,8 +151,6 @@ 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
|
||||
|
||||
@@ -416,11 +408,6 @@ 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,7 +781,6 @@ 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,8 +194,6 @@ 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.
|
||||
*/
|
||||
|
||||
+60
-62
@@ -82,80 +82,78 @@ include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
add_mfem_examples(ALL_EXE_SRCS)
|
||||
|
||||
# Add a test for each example
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
if (${TEST_NAME} MATCHES "ex0p?")
|
||||
set(THIS_TEST_OPTIONS)
|
||||
endif()
|
||||
if (${TEST_NAME} MATCHES "ex10p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-tf" "5")
|
||||
elseif(${TEST_NAME} MATCHES "ex15p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-e" "1")
|
||||
elseif(${TEST_NAME} MATCHES "ex27p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-dg")
|
||||
endif()
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
if (${TEST_NAME} MATCHES "ex0p?")
|
||||
set(THIS_TEST_OPTIONS)
|
||||
endif()
|
||||
if (${TEST_NAME} MATCHES "ex10p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-tf" "5")
|
||||
elseif(${TEST_NAME} MATCHES "ex15p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-e" "1")
|
||||
elseif(${TEST_NAME} MATCHES "ex27p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-dg")
|
||||
endif()
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Add CUDA/HIP tests.
|
||||
set(DEVICE_EXAMPLES
|
||||
# serial examples with device support:
|
||||
ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
|
||||
# parallel examples with device support:
|
||||
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p ex24p ex25p ex26p)
|
||||
set(MFEM_TEST_DEVICE)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_TEST_DEVICE "cuda")
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_TEST_DEVICE "hip")
|
||||
endif()
|
||||
if (MFEM_TEST_DEVICE)
|
||||
foreach(TEST_NAME ${DEVICE_EXAMPLES})
|
||||
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
elseif (MFEM_USE_MPI)
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Add CUDA/HIP tests.
|
||||
set(DEVICE_EXAMPLES
|
||||
# serial examples with device support:
|
||||
ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
|
||||
# parallel examples with device support:
|
||||
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p ex24p ex25p ex26p)
|
||||
set(MFEM_TEST_DEVICE)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_TEST_DEVICE "cuda")
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_TEST_DEVICE "hip")
|
||||
endif()
|
||||
if (MFEM_TEST_DEVICE)
|
||||
foreach(TEST_NAME ${DEVICE_EXAMPLES})
|
||||
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
elseif (MFEM_USE_MPI)
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
# If STRUMPACK is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_STRUMPACK)
|
||||
add_test(NAME ex11p_strumpack_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--strumpack"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
|
||||
# If STRUMPACK is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_STRUMPACK)
|
||||
add_test(NAME ex11p_strumpack_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--strumpack"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
|
||||
# If SuperLU_DIST is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_SUPERLU)
|
||||
add_test(NAME ex11p_superlu_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--superlu"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
# If SuperLU_DIST is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_SUPERLU)
|
||||
add_test(NAME ex11p_superlu_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--superlu"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
|
||||
# Include the examples/amgx directory if AmgX is enabled
|
||||
|
||||
@@ -50,32 +50,30 @@ add_mfem_examples(AMGX_EXAMPLES_SRCS ${PFX} copy_amgx_json_files test_amgx)
|
||||
# which builds the examples and runs:
|
||||
# ctest -R amgx
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# Example 1/1p:
|
||||
set(EX1_TEST_OPTS)
|
||||
set(EX1P_TEST_OPTS)
|
||||
# Command line options for the tests.
|
||||
# Example 1/1p:
|
||||
set(EX1_TEST_OPTS)
|
||||
set(EX1P_TEST_OPTS)
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${AMGX_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${AMGX_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -30,22 +30,22 @@ set(PREFIX caliper_)
|
||||
add_mfem_examples(CALIPER_EXE_SRCS ${PREFIX})
|
||||
|
||||
# Add a test for each example
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
foreach(SRC_FILE ${CALIPER_EXE_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
foreach(SRC_FILE ${CALIPER_EXE_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
// MFEM Example 1
|
||||
//
|
||||
// Compile with: make ex1
|
||||
//
|
||||
// Sample runs: ex1 -m ../data/square-disc.mesh
|
||||
// ex1 -m ../data/star.mesh
|
||||
// ex1 -m ../data/escher.mesh
|
||||
// ex1 -m ../data/fichera.mesh
|
||||
// ex1 -m ../data/square-disc-p2.vtk -o 2
|
||||
// ex1 -m ../data/square-disc-p3.mesh -o 3
|
||||
// ex1 -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// ex1 -m ../data/disc-nurbs.mesh -o -1
|
||||
// ex1 -m ../data/pipe-nurbs.mesh -o -1
|
||||
// ex1 -m ../data/star-surf.mesh
|
||||
// ex1 -m ../data/square-disc-surf.mesh
|
||||
// ex1 -m ../data/inline-segment.mesh
|
||||
// ex1 -m ../data/amr-quad.mesh
|
||||
// ex1 -m ../data/amr-hex.mesh
|
||||
// ex1 -m ../data/fichera-amr.mesh
|
||||
// ex1 -m ../data/mobius-strip.mesh
|
||||
// ex1 -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "./star-set.mesh";
|
||||
int order = 1;
|
||||
int rs = -1;
|
||||
int ra = 0;
|
||||
int bt = EntitySets::INVALID;
|
||||
const char *bs = "Origin";
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&rs, "-rs", "--refine-serial",
|
||||
"Number of serial refinement levels");
|
||||
args.AddOption(&ra, "-ra", "--refine-adaptive",
|
||||
"Number of adaptive refinement levels");
|
||||
args.AddOption(&bt, "-bt", "--bc-entity-type",
|
||||
"");
|
||||
args.AddOption(&bs, "-bs", "--bc-entity-set-name",
|
||||
"");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
// largest number that gives a final mesh with no more than 50,000
|
||||
// elements.
|
||||
{
|
||||
int ref_levels = ( rs >= 0 ) ? rs :
|
||||
(int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
|
||||
if ( ra > 0 )
|
||||
{
|
||||
cout << "calling EnsureNCMesh" << endl;
|
||||
mesh->EnsureNCMesh();
|
||||
cout << "back from EnsureNCMesh" << endl;
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
cout << "Calling RandomRefinement " << ra << " times." << endl;
|
||||
for (int l = 0; l < ra; l++)
|
||||
{
|
||||
mesh->RandomRefinement(0.2);
|
||||
}
|
||||
cout << "Done with refinement" << endl;
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
if ( mesh->ncmesh )
|
||||
{
|
||||
mesh->ncmesh->PrintStats(cout);
|
||||
|
||||
ofstream ofsV("vp.out");
|
||||
ofstream ofsE("ce.out");
|
||||
mesh->ncmesh->PrintVertexParents(ofsV);
|
||||
mesh->ncmesh->PrintCoarseElements(ofsE);
|
||||
}
|
||||
|
||||
// 4. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (mesh->GetNodes())
|
||||
{
|
||||
fec = mesh->GetNodes()->OwnFEC();
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace->GetTrueVSize() << endl;
|
||||
|
||||
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined by marking all
|
||||
// the boundary attributes from the mesh as essential (Dirichlet) and
|
||||
// converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace->GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
|
||||
cout << "Number of Dirichlet dofs: " << ess_tdof_list.Size() << endl;
|
||||
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
||||
// the basis functions in the finite element fespace.
|
||||
LinearForm *b = new LinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 7. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
GridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 8. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 9. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
SparseMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
cout << "Size of linear system: " << A.Height() << endl;
|
||||
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
// solve the system A X = B with PCG.
|
||||
GSSmoother M(A);
|
||||
PCG(A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// 10. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
|
||||
// 11. Recover the solution as a finite element grid function.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 12. Save the refined mesh and the solution. This output can be viewed later
|
||||
// using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
ofstream sol_ofs("sol.gf");
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
|
||||
// 13. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 14. Free the used memory.
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// The following are examples of using EntitySets to define
|
||||
// homogeneous Dirichlet boundary condition. These examples
|
||||
// require a modified mesh file and a specialized version of
|
||||
// example 1 called "ex1p_es".
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh -bt 0 -bs Origin
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh -bt 1 -bs Axes
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh
|
||||
// -bt 1 -bs "Negative Axes"
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh
|
||||
// -bt 2 -bs "Interior Corner"
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh
|
||||
// -bt 2 -bs "Exterior Corner"
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh
|
||||
// -bt 3 -bs "Interior Corner"
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh
|
||||
// -bt 3 -bs "Exterior Corner"
|
||||
// mpirun -np 4 ex1p_es -m ./fichera-set.mesh -bt 3 -bs "Steps"
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "./star-set.mesh";
|
||||
int order = 1;
|
||||
int rs = -1;
|
||||
int rp = 2;
|
||||
int ra = 0;
|
||||
int bt = EntitySets::INVALID;
|
||||
const char *bs = "Origin";
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&rs, "-rs", "--refine-serial",
|
||||
"Number of serial refinement levels");
|
||||
args.AddOption(&rp, "-rp", "--refine-parallel",
|
||||
"Number of parallel refinement levels");
|
||||
args.AddOption(&ra, "-ra", "--refine-adaptive",
|
||||
"Number of adaptive refinement levels");
|
||||
args.AddOption(&bt, "-bt", "--bc-entity-type",
|
||||
"");
|
||||
args.AddOption(&bs, "-bs", "--bc-entity-set-name",
|
||||
"");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels = ( rs >= 0 ) ? rs :
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in serial..."; }
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
if ( myid == 0 && rs > 0 ) { cout << "Done" << endl; }
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
At this point we have a serial mesh containing an EntitySets
|
||||
object which stores the current node/edge/face/element indices
|
||||
for each entity in each set. This data is duplicated on each MPI
|
||||
rank.
|
||||
*/
|
||||
if ( ra > 0 )
|
||||
{
|
||||
cout << "calling EnsureNCMesh" << endl;
|
||||
mesh->EnsureNCMesh();
|
||||
cout << "back from EnsureNCMesh" << endl;
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
We now have an NCEntitySets object which stores the node indices
|
||||
describing each enity in each node/edge/face set and the element
|
||||
indices for the elements in each element set. This data is
|
||||
duplicated on each MPI rank.
|
||||
*/
|
||||
|
||||
// 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.
|
||||
cout << "creating ParMesh from serial mesh" << endl;
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
cout << "done creating ParMesh from serial mesh" << endl;
|
||||
delete mesh;
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
We now have a ParEntitySets object which marshals the data stored
|
||||
in EntitySets objects. The data has now been pruned so that each
|
||||
rank only contains indices of local entities.
|
||||
|
||||
The NCEntitySets object remains unchanged...
|
||||
|
||||
If we have an NC mesh a different path is taken and the
|
||||
EntitySets are ignored.
|
||||
|
||||
1) ParNCMesh is created from NCMesh
|
||||
a) Creates a ParNCEntitySets object from ncmesh (every rank contains
|
||||
information to find every entity)
|
||||
2) ParNCMesh is pruned which involves renumbering elements and vertices
|
||||
3) ParMesh is initialized from ParNCMesh
|
||||
4) ParNCMesh::OnMeshUpdated is called
|
||||
5) Mesh::GenerateNCFaceInfo is called
|
||||
*/
|
||||
{
|
||||
int par_ref_levels = rp;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in parallel..."; }
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
if ( myid == 0 && rs > 0 ) { cout << "Done" << endl; }
|
||||
}
|
||||
/*
|
||||
RandomRefinement will end up calling
|
||||
ParMesh::NonconformingRefinement which will create a new ParMesh
|
||||
object using the ParNCMesh object and then call
|
||||
ParMesh::OnMeshUpdated on this new mesh.
|
||||
*/
|
||||
|
||||
for (int l = 0; l < ra; l++)
|
||||
{
|
||||
pmesh->RandomRefinement(0.2);
|
||||
}
|
||||
if ( ra > 0 )
|
||||
{
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL post random refinement" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL post random refinement" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace->GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
for (int i=0; i<num_procs; i++)
|
||||
{
|
||||
if (myid == i)
|
||||
{
|
||||
cout << "Number of Dirichlet dofs on proc " << i << ": "
|
||||
<< ess_tdof_list.Size() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 9. 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;
|
||||
|
||||
// 10. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
HypreSolver *amg = new HypreBoomerAMG(A);
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(200);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 16. Free the used memory.
|
||||
delete pcg;
|
||||
delete amg;
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic diffusion
|
||||
// problem corresponding to the second order definite Maxwell
|
||||
// equation curl curl E + E = f with boundary condition
|
||||
// E x n = <given tangential field>. Here, we use a given exact
|
||||
// solution E and compute the corresponding r.h.s. f.
|
||||
// We discretize with Nedelec finite elements in 2D or 3D.
|
||||
//
|
||||
// The example demonstrates the use of H(curl) finite element
|
||||
// spaces with the curl-curl and the (vector finite element) mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Static condensation is
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-2 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
//void E_exact(const Vector &, Vector &);
|
||||
//void f_exact(const Vector &, Vector &);
|
||||
//double freq = 1.0, kappa;
|
||||
void f_const(const Vector &, Vector &);
|
||||
|
||||
int dim;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
int order = 1;
|
||||
int rs = -1;
|
||||
int rp = 2;
|
||||
int ra = 0;
|
||||
int bt = EntitySets::INVALID;
|
||||
const char *bs = "Origin";
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
/*
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
*/
|
||||
args.AddOption(&rs, "-rs", "--refine-serial",
|
||||
"Number of serial refinement levels");
|
||||
args.AddOption(&rp, "-rp", "--refine-parallel",
|
||||
"Number of parallel refinement levels");
|
||||
args.AddOption(&ra, "-ra", "--refine-adaptive",
|
||||
"Number of adaptive refinement levels");
|
||||
args.AddOption(&bt, "-bt", "--bc-entity-type",
|
||||
"");
|
||||
args.AddOption(&bs, "-bs", "--bc-entity-set-name",
|
||||
"");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
// kappa = freq * M_PI;
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
{
|
||||
int ref_levels = ( rs >= 0 ) ? rs :
|
||||
(int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in serial..."; }
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
if ( myid == 0 && rs > 0 ) { cout << "Done" << endl; }
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
At this point we have a serial mesh containing an EntitySets
|
||||
object which stores the current node/edge/face/element indices
|
||||
for each entity in each set. This data is duplicated on each MPI
|
||||
rank.
|
||||
*/
|
||||
if ( ra > 0 )
|
||||
{
|
||||
cout << "calling EnsureNCMesh" << endl;
|
||||
mesh->EnsureNCMesh();
|
||||
cout << "back from EnsureNCMesh" << endl;
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
We now have an NCEntitySets object which stores the node indices
|
||||
describing each enity in each node/edge/face set and the element
|
||||
indices for the elements in each element set. This data is
|
||||
duplicated on each MPI rank.
|
||||
*/
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL" << endl;
|
||||
}
|
||||
{
|
||||
int par_ref_levels = rp;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in parallel..."; }
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
if ( myid == 0 && rs > 0 ) { cout << "Done" << endl; }
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
pmesh->ent_sets->PrintSetInfo(cout);
|
||||
|
||||
for (int l = 0; l < ra; l++)
|
||||
{
|
||||
pmesh->RandomRefinement(0.2);
|
||||
}
|
||||
if ( ra > 0 )
|
||||
{
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL post random refinement" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL post random refinement" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace->GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of Dirichlet dofs: " << ess_tdof_list.Size() << endl;
|
||||
}
|
||||
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
VectorFunctionCoefficient f(sdim, f_const);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
// VectorFunctionCoefficient E(sdim, E_exact);
|
||||
// x.ProjectCoefficient(E);
|
||||
x = 0.0;
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *sigma = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
|
||||
// preconditioner from hypre.
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
HypreSolver *ams = new HypreAMS(A, prec_fespace);
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*ams);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
/*
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(E);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete pcg;
|
||||
delete ams;
|
||||
delete a;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
E(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { E(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
|
||||
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
*/
|
||||
void f_const(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
f(0) = 1.0;
|
||||
f(1) = 1.0;
|
||||
f(2) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = 1.0;
|
||||
f(1) = 1.0;
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
@@ -1,438 +0,0 @@
|
||||
// MFEM Example 4 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex4p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex4p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex-nurbs.mesh -o 3
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-square.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D H(div) diffusion
|
||||
// problem corresponding to the second order definite equation
|
||||
// -grad(alpha div F) + beta F = f with boundary condition F dot n
|
||||
// = <given normal field>. Here, we use a given exact solution F
|
||||
// and compute the corresponding r.h.s. f. We discretize with
|
||||
// Raviart-Thomas finite elements.
|
||||
//
|
||||
// The example demonstrates the use of H(div) finite element
|
||||
// spaces with the grad-div and H(div) vector finite element mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Bilinear form
|
||||
// hybridization and static condensation are also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-3 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution, F, and r.h.s., f. See below for implementation.
|
||||
//void F_exact(const Vector &, Vector &);
|
||||
//void f_exact(const Vector &, Vector &);
|
||||
//double freq = 1.0, kappa;
|
||||
void f_const(const Vector &, Vector &);
|
||||
|
||||
int dim;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int rs = -1;
|
||||
int rp = 2;
|
||||
int ra = 0;
|
||||
int bt = EntitySets::INVALID;
|
||||
const char *bs = "Origin";
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
|
||||
"Impose or not essential boundary conditions.");
|
||||
args.AddOption(&rs, "-rs", "--refine-serial",
|
||||
"Number of serial refinement levels");
|
||||
args.AddOption(&rp, "-rp", "--refine-parallel",
|
||||
"Number of parallel refinement levels");
|
||||
args.AddOption(&ra, "-ra", "--refine-adaptive",
|
||||
"Number of adaptive refinement levels");
|
||||
args.AddOption(&bt, "-bt", "--bc-entity-type",
|
||||
"");
|
||||
args.AddOption(&bs, "-bs", "--bc-entity-set-name",
|
||||
"");
|
||||
// args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
// " solution.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
// kappa = freq * M_PI;
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume, as well as periodic meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
{
|
||||
int ref_levels = ( rs >= 0 ) ? rs :
|
||||
(int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in serial..."; }
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
if ( myid == 0 && rs > 0 ) { cout << "Done" << endl; }
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
At this point we have a serial mesh containing an EntitySets
|
||||
object which stores the current node/edge/face/element indices
|
||||
for each entity in each set. This data is duplicated on each MPI
|
||||
rank.
|
||||
*/
|
||||
if ( ra > 0 )
|
||||
{
|
||||
cout << "calling EnsureNCMesh" << endl;
|
||||
mesh->EnsureNCMesh();
|
||||
cout << "back from EnsureNCMesh" << endl;
|
||||
}
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
/*
|
||||
We now have an NCEntitySets object which stores the node indices
|
||||
describing each enity in each node/edge/face set and the element
|
||||
indices for the elements in each element set. This data is
|
||||
duplicated on each MPI rank.
|
||||
*/
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them (this is needed in the ADS solver below).
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL" << endl;
|
||||
}
|
||||
{
|
||||
int par_ref_levels = rp;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
if ( myid == 0 ) { cout << "Uniform refinement in parallel..."; }
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
for (int l = 0; l < ra; l++)
|
||||
{
|
||||
pmesh->RandomRefinement(0.2);
|
||||
}
|
||||
if ( ra > 0 )
|
||||
{
|
||||
if ( pmesh->pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL post random refinement" << endl;
|
||||
pmesh->pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL post random refinement" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = set_bc ? 1 : 0;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace->GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of Dirichlet dofs: " << ess_tdof_list.Size() << endl;
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
VectorFunctionCoefficient f(sdim, f_const);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary faces will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
// VectorFunctionCoefficient F(sdim, F_exact);
|
||||
// x.ProjectCoefficient(F);
|
||||
x = 0.0;
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the H(div)
|
||||
// diffusion operator grad alpha div + beta I, by adding the div-div and
|
||||
// the mass domain integrators.
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation,
|
||||
// hybridization, etc.
|
||||
FiniteElementCollection *hfec = NULL;
|
||||
ParFiniteElementSpace *hfes = NULL;
|
||||
if (static_cond)
|
||||
{
|
||||
a->EnableStaticCondensation();
|
||||
}
|
||||
else if (hybridization)
|
||||
{
|
||||
hfec = new DG_Interface_FECollection(order-1, dim);
|
||||
hfes = new ParFiniteElementSpace(pmesh, hfec);
|
||||
a->EnableHybridization(hfes, new NormalTraceJumpIntegrator(),
|
||||
ess_tdof_list);
|
||||
}
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
HYPRE_Int glob_size = A.GetGlobalNumRows();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << glob_size << endl;
|
||||
}
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
|
||||
// the 3D ADS preconditioners from hypre. If using hybridization, the
|
||||
// system is preconditioned with hypre's BoomerAMG.
|
||||
HypreSolver *prec = NULL;
|
||||
CGSolver *pcg = new CGSolver(A.GetComm());
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(1e-12);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(A); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
if (dim == 2) { prec = new HypreAMS(A, prec_fespace); }
|
||||
else { prec = new HypreADS(A, prec_fespace); }
|
||||
}
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
/*
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(F);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| F_h - F ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete pcg;
|
||||
delete prec;
|
||||
delete hfes;
|
||||
delete hfec;
|
||||
delete a;
|
||||
delete alpha;
|
||||
delete beta;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
// The exact solution (for non-surface meshes)
|
||||
void F_exact(const Vector &p, Vector &F)
|
||||
{
|
||||
int dim = p.Size();
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
F(0) = cos(kappa*x)*sin(kappa*y);
|
||||
F(1) = cos(kappa*y)*sin(kappa*x);
|
||||
if (dim == 3)
|
||||
{
|
||||
F(2) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// The right hand side
|
||||
void f_exact(const Vector &p, Vector &f)
|
||||
{
|
||||
int dim = p.Size();
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
double temp = 1 + 2*kappa*kappa;
|
||||
|
||||
f(0) = temp*cos(kappa*x)*sin(kappa*y);
|
||||
f(1) = temp*cos(kappa*y)*sin(kappa*x);
|
||||
if (dim == 3)
|
||||
{
|
||||
f(2) = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
void f_const(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
f(0) = 1.0;
|
||||
f(1) = 1.0;
|
||||
f(2) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = 1.0;
|
||||
f(1) = 1.0;
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
// MFEM Example 6 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex6p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 1
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/star.mesh -o 3
|
||||
// mpirun -np 4 ex6p -m ../data/escher.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/fichera.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/ball-nurbs.mesh
|
||||
// mpirun -np 4 ex6p -m ../data/pipe-nurbs.mesh
|
||||
// mpirun -np 4 ex6p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/square-disc-surf.mesh -o 2
|
||||
// mpirun -np 4 ex6p -m ../data/amr-quad.mesh
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the Laplace
|
||||
// equation -Delta u = 1 with homogeneous Dirichlet boundary
|
||||
// conditions. The problem is solved on a sequence of meshes which
|
||||
// are locally refined in a conforming (triangles, tetrahedrons)
|
||||
// or non-conforming (quadrilaterals, hexahedra) manner according
|
||||
// to a simple ZZ error estimator.
|
||||
//
|
||||
// The example demonstrates MFEM's capability to work with both
|
||||
// conforming and nonconforming refinements, in 2D and 3D, on
|
||||
// linear, curved and surface meshes. Interpolation of functions
|
||||
// from coarse to fine meshes, as well as persistent GLVis
|
||||
// visualization are also illustrated.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
static int max_dofs = 100000;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "./star-set.mesh";
|
||||
int order = 1;
|
||||
int bt = EntitySets::INVALID;
|
||||
const char *bs = "";
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&max_dofs, "-md", "--max-dofs",
|
||||
"Maximum number of degrees of freedom.");
|
||||
args.AddOption(&bt, "-bt", "--bc-entity-type",
|
||||
"");
|
||||
args.AddOption(&bs, "-bs", "--bc-entity-set-name",
|
||||
"");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution.
|
||||
// Also project a NURBS mesh to a piecewise-quadratic curved mesh. Make
|
||||
// sure that the mesh is non-conforming.
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
mesh->SetCurvature(2);
|
||||
}
|
||||
mesh->EnsureNCMesh();
|
||||
if ( mesh->ent_sets )
|
||||
{
|
||||
cout << "mesh->ent_sets is non NULL" << endl;
|
||||
mesh->ent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "mesh->ent_sets is NULL" << endl;
|
||||
}
|
||||
|
||||
// 5. 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);
|
||||
delete mesh;
|
||||
if ( pmesh.pent_sets )
|
||||
{
|
||||
cout << "pmesh->pent_sets is non NULL" << endl;
|
||||
pmesh.pent_sets->PrintSetInfo(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "pmesh->pent_sets is NULL" << endl;
|
||||
}
|
||||
|
||||
// 6. Define a finite element space on the mesh. The polynomial order is
|
||||
// one (linear) by default, but this can be changed on the command line.
|
||||
H1_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace.GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
ParBilinearForm a(&fespace);
|
||||
ParLinearForm b(&fespace);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
|
||||
BilinearFormIntegrator *integ = new DiffusionIntegrator(one);
|
||||
a.AddDomainIntegrator(integ);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
|
||||
// 8. The solution vector x and the associated finite element grid function
|
||||
// will be maintained over the AMR iterations. We initialize it to zero.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0;
|
||||
|
||||
// 9. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
visualization = false;
|
||||
}
|
||||
|
||||
sout.precision(8);
|
||||
}
|
||||
|
||||
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
|
||||
// with L2 projection in the smoothing step to better handle hanging
|
||||
// nodes and parallel partitioning. We need to supply a space for the
|
||||
// discontinuous flux (L2) and a space for the smoothed flux (H(div) is
|
||||
// used here).
|
||||
L2_FECollection flux_fec(order, dim);
|
||||
ParFiniteElementSpace flux_fes(&pmesh, &flux_fec, sdim);
|
||||
RT_FECollection smooth_flux_fec(order-1, dim);
|
||||
ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec);
|
||||
// Another possible option for the smoothed flux space:
|
||||
// H1_FECollection smooth_flux_fec(order, dim);
|
||||
// ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec, dim);
|
||||
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
|
||||
|
||||
// 11. A refiner selects and refines elements based on a refinement strategy.
|
||||
// The strategy here is to refine elements with errors larger than a
|
||||
// fraction of the maximum element error. Other strategies are possible.
|
||||
// The refiner will call the given error estimator.
|
||||
ThresholdRefiner refiner(estimator);
|
||||
refiner.SetTotalErrorFraction(0.7);
|
||||
|
||||
// 12. The main AMR loop. In each iteration we solve the problem on the
|
||||
// current mesh, visualize the solution, and refine the mesh.
|
||||
// const int max_dofs = 100000;
|
||||
for (int it = 0; ; it++)
|
||||
{
|
||||
HYPRE_Int global_dofs = fespace.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\nAMR iteration " << it << endl;
|
||||
cout << "Number of unknowns: " << global_dofs << endl;
|
||||
}
|
||||
|
||||
// 13. Assemble the stiffness matrix and the right-hand side. Note that
|
||||
// MFEM doesn't care at this point that the mesh is nonconforming
|
||||
// and parallel. The FE space is considered 'cut' along hanging
|
||||
// edges/faces, and also across processor boundaries.
|
||||
a.Assemble();
|
||||
b.Assemble();
|
||||
|
||||
// 14. Create the parallel linear system: eliminate boundary conditions,
|
||||
// constrain hanging nodes and nodes across processor boundaries.
|
||||
// The system will be solved for true (unconstrained/unique) DOFs only.
|
||||
// Array<int> ess_tdof_list;
|
||||
if ( bt == EntitySets::INVALID )
|
||||
{
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace.GetEssentialTrueDofs((EntitySets::EntityType)bt, bs,
|
||||
ess_tdof_list);
|
||||
}
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
const int copy_interior = 1;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
|
||||
|
||||
// 15. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
HypreBoomerAMG amg;
|
||||
amg.SetPrintLevel(0);
|
||||
CGSolver pcg(A.GetComm());
|
||||
pcg.SetPreconditioner(amg);
|
||||
pcg.SetOperator(A);
|
||||
pcg.SetRelTol(1e-6);
|
||||
pcg.SetMaxIter(200);
|
||||
pcg.SetPrintLevel(3); // print the first and the last iterations only
|
||||
pcg.Mult(B, X);
|
||||
|
||||
// 16. Extract the parallel grid function corresponding to the finite element
|
||||
// approximation X. This is the local solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << pmesh << x << flush;
|
||||
}
|
||||
|
||||
if (global_dofs > max_dofs)
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Reached the maximum number of dofs. Stop." << endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 18. Call the refiner to modify the mesh. The refiner calls the error
|
||||
// estimator to obtain element errors, then it selects elements to be
|
||||
// refined and finally it modifies the mesh. The Stop() method can be
|
||||
// used to determine if a stopping criterion was met.
|
||||
refiner.Apply(pmesh);
|
||||
if (refiner.Stop())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Stopping criterion satisfied. Stop." << endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 19. Update the finite element space (recalculate the number of DOFs,
|
||||
// etc.) and create a grid function update matrix. Apply the matrix
|
||||
// to any GridFunctions over the space. In this case, the update
|
||||
// matrix is an interpolation matrix so the updated GridFunction will
|
||||
// still represent the same function as before refinement.
|
||||
fespace.Update();
|
||||
x.Update();
|
||||
|
||||
// 20. Load balance the mesh, and update the space and solution. Currently
|
||||
// available only for nonconforming meshes.
|
||||
if (pmesh.Nonconforming())
|
||||
{
|
||||
pmesh.Rebalance();
|
||||
|
||||
// Update the space and the GridFunction. This time the update matrix
|
||||
// redistributes the GridFunction among the processors.
|
||||
fespace.Update();
|
||||
x.Update();
|
||||
}
|
||||
|
||||
// 21. Inform also the bilinear and linear forms that the space has
|
||||
// changed.
|
||||
a.Update();
|
||||
b.Update();
|
||||
}
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
14
|
||||
1 4 13 15 21 25
|
||||
1 4 12 13 15 21
|
||||
1 4 13 21 22 25
|
||||
1 4 15 24 21 25
|
||||
1 4 13 15 25 16
|
||||
1 5 0 1 4 3 9 10 13 12
|
||||
1 5 8 9 12 11 17 18 21 20
|
||||
1 5 2 3 6 5 11 12 15 14
|
||||
1 6 3 4 6 12 13 15
|
||||
1 6 4 7 6 13 16 15
|
||||
1 6 12 13 21 9 10 18
|
||||
1 6 13 22 21 10 19 18
|
||||
1 6 11 14 20 12 15 21
|
||||
1 6 15 21 24 14 20 23
|
||||
|
||||
boundary
|
||||
30
|
||||
1 3 5 6 3 2
|
||||
2 2 3 6 4
|
||||
2 2 4 6 7
|
||||
3 3 3 4 1 0
|
||||
4 3 11 12 9 8
|
||||
5 3 2 3 12 11
|
||||
6 3 0 1 10 9
|
||||
7 2 9 10 18
|
||||
7 2 10 19 18
|
||||
8 3 8 9 18 17
|
||||
9 3 1 4 13 10
|
||||
10 3 4 7 16 13
|
||||
11 2 13 16 25
|
||||
11 2 13 25 22
|
||||
12 3 10 13 22 19
|
||||
13 3 7 6 15 16
|
||||
14 3 6 5 14 15
|
||||
15 3 15 14 23 24
|
||||
16 2 16 15 25
|
||||
16 2 15 24 25
|
||||
17 3 5 2 11 14
|
||||
18 3 3 0 9 12
|
||||
19 3 11 8 17 20
|
||||
20 2 11 20 14
|
||||
20 2 14 20 23
|
||||
21 3 17 18 21 20
|
||||
22 3 18 19 22 21
|
||||
23 2 21 22 25
|
||||
23 2 21 25 24
|
||||
24 3 20 21 24 23
|
||||
|
||||
vertices
|
||||
26
|
||||
3
|
||||
0 -1 -1
|
||||
1 -1 -1
|
||||
-1 0 -1
|
||||
0 0 -1
|
||||
1 0 -1
|
||||
-1 1 -1
|
||||
0 1 -1
|
||||
1 1 -1
|
||||
-1 -1 0
|
||||
0 -1 0
|
||||
1 -1 0
|
||||
-1 0 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
-1 1 0
|
||||
0 1 0
|
||||
1 1 0
|
||||
-1 -1 1
|
||||
0 -1 1
|
||||
1 -1 1
|
||||
-1 0 1
|
||||
0 0 1
|
||||
1 0 1
|
||||
-1 1 1
|
||||
0 1 1
|
||||
1 1 1
|
||||
|
||||
MFEM sets v1.0
|
||||
|
||||
vertex_sets
|
||||
1
|
||||
|
||||
Origin
|
||||
1
|
||||
12
|
||||
|
||||
edge_sets
|
||||
2
|
||||
|
||||
Axes
|
||||
3
|
||||
12 13
|
||||
12 15
|
||||
12 21
|
||||
|
||||
Negative Axes
|
||||
3
|
||||
12 9
|
||||
12 11
|
||||
12 3
|
||||
|
||||
face_sets
|
||||
2
|
||||
|
||||
Interior Corner
|
||||
3
|
||||
3 11 12 9 8
|
||||
3 2 3 12 11
|
||||
3 3 0 9 12
|
||||
|
||||
Exterior Corner
|
||||
15
|
||||
2 13 16 25
|
||||
2 13 25 22
|
||||
2 16 15 25
|
||||
2 15 24 25
|
||||
2 21 22 25
|
||||
2 21 25 24
|
||||
3 10 13 22 19
|
||||
3 4 7 16 13
|
||||
3 1 4 13 10
|
||||
3 7 6 15 16
|
||||
3 6 5 14 15
|
||||
3 15 14 23 24
|
||||
3 20 21 24 23
|
||||
3 18 19 22 21
|
||||
3 17 18 21 20
|
||||
|
||||
element_sets
|
||||
3
|
||||
|
||||
Interior Corner
|
||||
3
|
||||
5 6 7
|
||||
|
||||
Exterior Corner
|
||||
5
|
||||
0 1 2 3 4
|
||||
|
||||
Steps
|
||||
3
|
||||
6 8 9
|
||||
@@ -1,145 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
7
|
||||
1 5 0 1 4 3 9 10 13 12
|
||||
1 5 3 4 7 6 12 13 16 15
|
||||
1 5 2 3 6 5 11 12 15 14
|
||||
1 5 8 9 12 11 17 18 21 20
|
||||
1 5 9 10 13 12 18 19 22 21
|
||||
1 5 12 13 16 15 21 22 25 24
|
||||
1 5 11 12 15 14 20 21 24 23
|
||||
|
||||
boundary
|
||||
24
|
||||
1 3 5 6 3 2
|
||||
2 3 6 7 4 3
|
||||
3 3 3 4 1 0
|
||||
4 3 11 12 9 8
|
||||
5 3 2 3 12 11
|
||||
6 3 0 1 10 9
|
||||
7 3 9 10 19 18
|
||||
8 3 8 9 18 17
|
||||
9 3 1 4 13 10
|
||||
10 3 4 7 16 13
|
||||
11 3 13 16 25 22
|
||||
12 3 10 13 22 19
|
||||
13 3 7 6 15 16
|
||||
14 3 6 5 14 15
|
||||
15 3 15 14 23 24
|
||||
16 3 16 15 24 25
|
||||
17 3 5 2 11 14
|
||||
18 3 3 0 9 12
|
||||
19 3 11 8 17 20
|
||||
20 3 14 11 20 23
|
||||
21 3 17 18 21 20
|
||||
22 3 18 19 22 21
|
||||
23 3 21 22 25 24
|
||||
24 3 20 21 24 23
|
||||
|
||||
vertices
|
||||
26
|
||||
3
|
||||
0 -1 -1
|
||||
1 -1 -1
|
||||
-1 0 -1
|
||||
0 0 -1
|
||||
1 0 -1
|
||||
-1 1 -1
|
||||
0 1 -1
|
||||
1 1 -1
|
||||
-1 -1 0
|
||||
0 -1 0
|
||||
1 -1 0
|
||||
-1 0 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
-1 1 0
|
||||
0 1 0
|
||||
1 1 0
|
||||
-1 -1 1
|
||||
0 -1 1
|
||||
1 -1 1
|
||||
-1 0 1
|
||||
0 0 1
|
||||
1 0 1
|
||||
-1 1 1
|
||||
0 1 1
|
||||
1 1 1
|
||||
|
||||
MFEM sets v1.0
|
||||
|
||||
vertex_sets
|
||||
1
|
||||
|
||||
Origin
|
||||
1
|
||||
12
|
||||
|
||||
edge_sets
|
||||
2
|
||||
|
||||
Axes
|
||||
3
|
||||
12 13
|
||||
12 15
|
||||
12 21
|
||||
|
||||
Negative Axes
|
||||
3
|
||||
12 9
|
||||
12 11
|
||||
12 3
|
||||
|
||||
face_sets
|
||||
2
|
||||
|
||||
Interior Corner
|
||||
3
|
||||
3 11 12 9 8
|
||||
3 2 3 12 11
|
||||
3 3 0 9 12
|
||||
|
||||
Exterior Corner
|
||||
12
|
||||
3 13 16 25 22
|
||||
3 16 15 24 25
|
||||
3 21 22 25 24
|
||||
3 10 13 22 19
|
||||
3 4 7 16 13
|
||||
3 1 4 13 10
|
||||
3 7 6 15 16
|
||||
3 6 5 14 15
|
||||
3 15 14 23 24
|
||||
3 20 21 24 23
|
||||
3 18 19 22 21
|
||||
3 17 18 21 20
|
||||
|
||||
element_sets
|
||||
3
|
||||
|
||||
Interior Corner
|
||||
3
|
||||
0 2 3
|
||||
|
||||
Exterior Corner
|
||||
1
|
||||
5
|
||||
|
||||
Steps
|
||||
2
|
||||
1 3
|
||||
@@ -31,31 +31,29 @@ add_mfem_examples(GINKGO_EXAMPLES_SRCS ${PFX} "" test_ginkgo)
|
||||
# which builds the examples and runs:
|
||||
# ctest -R ginkgo
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
set(EX1_COMMON_OPTS ex1 -m ../data/star.mesh --use_gko_solver)
|
||||
set(EX1_TEST_OPTS ${EX9_COMMON_OPTS})
|
||||
# Command line options for the tests.
|
||||
set(EX1_COMMON_OPTS ex1 -m ../data/star.mesh --use_gko_solver)
|
||||
set(EX1_TEST_OPTS ${EX9_COMMON_OPTS})
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${GINKGO_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${GINKGO_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -33,33 +33,31 @@ add_mfem_examples(HIOP_EXAMPLES_SRCS ${PFX} "" test_hiop)
|
||||
# which builds the examples and runs:
|
||||
# ctest -R hiop
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# Example 9:
|
||||
set(EX9_COMMON_OPTS -m ../../data/periodic-segment.mesh -p 0 -dt 0.005)
|
||||
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 )
|
||||
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS})
|
||||
# Command line options for the tests.
|
||||
# Example 9:
|
||||
set(EX9_COMMON_OPTS -m ../../data/periodic-segment.mesh -p 0 -dt 0.005)
|
||||
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 )
|
||||
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS})
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${HIOP_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${HIOP_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -94,32 +94,30 @@ if (MFEM_USE_SLEPC)
|
||||
endif()
|
||||
|
||||
# Add the tests: one test per command-line-variable.
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
set(TEST_OPTIONS_VARS
|
||||
EX1_ARGS_W EX1_ARGS_P EX2_ARGS EX3_ARGS EX4_ARGS EX4_HYB_ARGS
|
||||
EX5_BDDC_LB_ARGS EX5_BDDC_GB_ARGS EX5_FSPL_ARGS EX6_ARGS EX6_NONOVL_ARGS
|
||||
EX9_E_ARGS EX9_ES_ARGS EX9_IS_ARGS EX10_ARGS)
|
||||
if (MFEM_USE_SLEPC)
|
||||
list(APPEND TEST_OPTIONS_VARS EX11_ARGS_SINV EX11_ARGS_LOBPCG EX11_ARGS_GD)
|
||||
endif()
|
||||
|
||||
foreach(TEST_OPTIONS_VAR ${TEST_OPTIONS_VARS})
|
||||
string(REGEX REPLACE "^(.+)_ARGS" "\\1" TEST_NAME_UC ${TEST_OPTIONS_VAR})
|
||||
string(REGEX REPLACE "^([^_]+)" "\\1P" TEST_NAME_UC ${TEST_NAME_UC})
|
||||
string(TOLOWER ${TEST_NAME_UC} TEST_NAME_FULL)
|
||||
string(REGEX REPLACE "^([^_]+).*" "\\1" TEST_NAME ${TEST_NAME_FULL})
|
||||
set(TEST_NAME_FULL ${PFX}${TEST_NAME_FULL})
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
set(TEST_OPTIONS "-no-vis" ${${TEST_OPTIONS_VAR}})
|
||||
# message(STATUS "${TEST_NAME_FULL} --> ${TEST_NAME} ${TEST_OPTIONS}")
|
||||
|
||||
# All PETSC tests are parallel.
|
||||
if (MFEM_USE_MPI)
|
||||
add_test(NAME ${TEST_NAME_FULL}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
set(TEST_OPTIONS_VARS
|
||||
EX1_ARGS_W EX1_ARGS_P EX2_ARGS EX3_ARGS EX4_ARGS EX4_HYB_ARGS
|
||||
EX5_BDDC_LB_ARGS EX5_BDDC_GB_ARGS EX5_FSPL_ARGS EX6_ARGS EX6_NONOVL_ARGS
|
||||
EX9_E_ARGS EX9_ES_ARGS EX9_IS_ARGS EX10_ARGS)
|
||||
if (MFEM_USE_SLEPC)
|
||||
list(APPEND TEST_OPTIONS_VARS EX11_ARGS_SINV EX11_ARGS_LOBPCG EX11_ARGS_GD)
|
||||
endif()
|
||||
|
||||
foreach(TEST_OPTIONS_VAR ${TEST_OPTIONS_VARS})
|
||||
string(REGEX REPLACE "^(.+)_ARGS" "\\1" TEST_NAME_UC ${TEST_OPTIONS_VAR})
|
||||
string(REGEX REPLACE "^([^_]+)" "\\1P" TEST_NAME_UC ${TEST_NAME_UC})
|
||||
string(TOLOWER ${TEST_NAME_UC} TEST_NAME_FULL)
|
||||
string(REGEX REPLACE "^([^_]+).*" "\\1" TEST_NAME ${TEST_NAME_FULL})
|
||||
set(TEST_NAME_FULL ${PFX}${TEST_NAME_FULL})
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
set(TEST_OPTIONS "-no-vis" ${${TEST_OPTIONS_VAR}})
|
||||
# message(STATUS "${TEST_NAME_FULL} --> ${TEST_NAME} ${TEST_OPTIONS}")
|
||||
|
||||
# All PETSC tests are parallel.
|
||||
if (MFEM_USE_MPI)
|
||||
add_test(NAME ${TEST_NAME_FULL}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -37,39 +37,37 @@ add_mfem_examples(PUMI_EXAMPLES_SRCS ${PFX} "" test_pumi)
|
||||
# which builds the examples and runs:
|
||||
# ctest -R pumi
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# TODO...
|
||||
# Command line options for the tests.
|
||||
# TODO...
|
||||
|
||||
# Set the number of processors for the parallel examples. The value of
|
||||
# MFEM_MPI_NP is ignored.
|
||||
set(EX1_TEST_NP 1)
|
||||
set(EX1P_TEST_NP 8)
|
||||
set(EX2_TEST_NP 1)
|
||||
set(EX6P_TEST_NP 8)
|
||||
# Set the number of processors for the parallel examples. The value of
|
||||
# MFEM_MPI_NP is ignored.
|
||||
set(EX1_TEST_NP 1)
|
||||
set(EX1P_TEST_NP 8)
|
||||
set(EX2_TEST_NP 1)
|
||||
set(EX6P_TEST_NP 8)
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${PUMI_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${PUMI_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
# All PUMI examples require MPI
|
||||
if (FALSE)
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
set(TEST_NP ${${UP_TEST_NAME}_TEST_NP})
|
||||
add_test(NAME ${TEST_NAME}_np=${TEST_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
# All PUMI examples require MPI
|
||||
if (FALSE)
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
set(TEST_NP ${${UP_TEST_NAME}_TEST_NP})
|
||||
add_test(NAME ${TEST_NAME}_np=${TEST_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
30
|
||||
1 3 0 11 26 14
|
||||
1 3 0 14 27 17
|
||||
1 3 0 17 28 20
|
||||
1 3 0 20 29 23
|
||||
1 3 0 23 30 11
|
||||
1 2 11 1 26
|
||||
1 2 1 12 26
|
||||
1 3 26 12 3 13
|
||||
1 2 26 13 2
|
||||
1 2 14 26 2
|
||||
1 2 14 2 27
|
||||
1 2 2 15 27
|
||||
1 3 27 15 5 16
|
||||
1 2 27 16 4
|
||||
1 2 17 27 4
|
||||
1 2 17 4 28
|
||||
1 2 4 18 28
|
||||
1 3 28 18 7 19
|
||||
1 2 28 19 6
|
||||
1 2 20 28 6
|
||||
1 2 20 6 29
|
||||
1 2 6 21 29
|
||||
1 3 29 21 9 22
|
||||
1 2 29 22 8
|
||||
1 2 23 29 8
|
||||
1 2 23 8 30
|
||||
1 2 8 24 30
|
||||
1 3 30 24 10 25
|
||||
1 2 30 25 1
|
||||
1 2 11 30 1
|
||||
|
||||
boundary
|
||||
20
|
||||
1 1 13 2
|
||||
1 1 12 3
|
||||
1 1 16 4
|
||||
1 1 15 5
|
||||
1 1 19 6
|
||||
1 1 18 7
|
||||
1 1 22 8
|
||||
1 1 21 9
|
||||
1 1 25 1
|
||||
1 1 24 10
|
||||
1 1 3 13
|
||||
1 1 1 12
|
||||
1 1 5 16
|
||||
1 1 2 15
|
||||
1 1 7 19
|
||||
1 1 4 18
|
||||
1 1 9 22
|
||||
1 1 6 21
|
||||
1 1 10 25
|
||||
1 1 8 24
|
||||
|
||||
vertices
|
||||
31
|
||||
2
|
||||
0 0
|
||||
1 0
|
||||
0.309017 0.951057
|
||||
1.30902 0.951057
|
||||
-0.809017 0.587785
|
||||
-0.5 1.53884
|
||||
-0.809017 -0.587785
|
||||
-1.61803 0
|
||||
0.309017 -0.951057
|
||||
-0.5 -1.53884
|
||||
1.30902 -0.951057
|
||||
0.5 0
|
||||
1.15451 0.475529
|
||||
0.809019 0.951057
|
||||
0.154508 0.475529
|
||||
-0.0954915 1.24495
|
||||
-0.654508 1.06331
|
||||
-0.404508 0.293893
|
||||
-1.21352 0.293893
|
||||
-1.21352 -0.293892
|
||||
-0.404508 -0.293893
|
||||
-0.654508 -1.06331
|
||||
-0.0954915 -1.24495
|
||||
0.154508 -0.475529
|
||||
0.809019 -0.951057
|
||||
1.15451 -0.475529
|
||||
0.654509 0.475529
|
||||
-0.25 0.769421
|
||||
-0.809016 0
|
||||
-0.25 -0.76942
|
||||
0.654509 -0.475529
|
||||
|
||||
MFEM sets v1.0
|
||||
|
||||
vertex_sets
|
||||
3
|
||||
|
||||
Origin
|
||||
1
|
||||
0
|
||||
|
||||
Tent
|
||||
5
|
||||
1 2 4 6 8
|
||||
|
||||
Gazebo
|
||||
5
|
||||
3 5 7 9 10
|
||||
|
||||
edge_sets
|
||||
2
|
||||
|
||||
Columbine
|
||||
5
|
||||
1 11
|
||||
2 14
|
||||
4 17
|
||||
6 20
|
||||
8 23
|
||||
|
||||
Lily
|
||||
5
|
||||
0 11
|
||||
0 14
|
||||
0 17
|
||||
0 20
|
||||
0 23
|
||||
|
||||
element_sets
|
||||
3
|
||||
|
||||
Flying Squirrel
|
||||
3
|
||||
7 17 27
|
||||
|
||||
Sea Lion
|
||||
4
|
||||
12 17 22 27
|
||||
|
||||
Pinwheel
|
||||
5
|
||||
8 13 18 23 28
|
||||
@@ -1,143 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
20
|
||||
1 3 0 11 26 14
|
||||
1 3 0 14 27 17
|
||||
1 3 0 17 28 20
|
||||
1 3 0 20 29 23
|
||||
1 3 0 23 30 11
|
||||
1 3 11 1 12 26
|
||||
1 3 26 12 3 13
|
||||
1 3 14 26 13 2
|
||||
1 3 14 2 15 27
|
||||
1 3 27 15 5 16
|
||||
1 3 17 27 16 4
|
||||
1 3 17 4 18 28
|
||||
1 3 28 18 7 19
|
||||
1 3 20 28 19 6
|
||||
1 3 20 6 21 29
|
||||
1 3 29 21 9 22
|
||||
1 3 23 29 22 8
|
||||
1 3 23 8 24 30
|
||||
1 3 30 24 10 25
|
||||
1 3 11 30 25 1
|
||||
|
||||
boundary
|
||||
20
|
||||
1 1 13 2
|
||||
1 1 12 3
|
||||
1 1 16 4
|
||||
1 1 15 5
|
||||
1 1 19 6
|
||||
1 1 18 7
|
||||
1 1 22 8
|
||||
1 1 21 9
|
||||
1 1 25 1
|
||||
1 1 24 10
|
||||
1 1 3 13
|
||||
1 1 1 12
|
||||
1 1 5 16
|
||||
1 1 2 15
|
||||
1 1 7 19
|
||||
1 1 4 18
|
||||
1 1 9 22
|
||||
1 1 6 21
|
||||
1 1 10 25
|
||||
1 1 8 24
|
||||
|
||||
vertices
|
||||
31
|
||||
2
|
||||
0 0
|
||||
1 0
|
||||
0.309017 0.951057
|
||||
1.30902 0.951057
|
||||
-0.809017 0.587785
|
||||
-0.5 1.53884
|
||||
-0.809017 -0.587785
|
||||
-1.61803 0
|
||||
0.309017 -0.951057
|
||||
-0.5 -1.53884
|
||||
1.30902 -0.951057
|
||||
0.5 0
|
||||
1.15451 0.475529
|
||||
0.809019 0.951057
|
||||
0.154508 0.475529
|
||||
-0.0954915 1.24495
|
||||
-0.654508 1.06331
|
||||
-0.404508 0.293893
|
||||
-1.21352 0.293893
|
||||
-1.21352 -0.293892
|
||||
-0.404508 -0.293893
|
||||
-0.654508 -1.06331
|
||||
-0.0954915 -1.24495
|
||||
0.154508 -0.475529
|
||||
0.809019 -0.951057
|
||||
1.15451 -0.475529
|
||||
0.654509 0.475529
|
||||
-0.25 0.769421
|
||||
-0.809016 0
|
||||
-0.25 -0.76942
|
||||
0.654509 -0.475529
|
||||
|
||||
MFEM sets v1.0
|
||||
|
||||
vertex_sets
|
||||
3
|
||||
|
||||
Origin
|
||||
1
|
||||
0
|
||||
|
||||
Tent
|
||||
5
|
||||
1 2 4 6 8
|
||||
|
||||
Gazebo
|
||||
5
|
||||
3 5 7 9 10
|
||||
|
||||
edge_sets
|
||||
2
|
||||
|
||||
Columbine
|
||||
5
|
||||
1 11
|
||||
2 14
|
||||
4 17
|
||||
6 20
|
||||
8 23
|
||||
|
||||
Lily
|
||||
5
|
||||
0 11
|
||||
0 14
|
||||
0 17
|
||||
0 20
|
||||
0 23
|
||||
|
||||
element_sets
|
||||
2
|
||||
|
||||
Flying Squirrel
|
||||
3
|
||||
6 12 18
|
||||
|
||||
Sea Lion
|
||||
4
|
||||
9 12 15 18
|
||||
@@ -41,38 +41,36 @@ add_mfem_examples(SUNDIALS_EXAMPLES_SRCS ${PFX} "" test_sundials)
|
||||
# which builds the examples and runs:
|
||||
# ctest -R sundials
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# Example 9: test CVODE with CV_ADAMS (non-stiff implicit) time stepping
|
||||
set(EX9_COMMON_OPTS -m ../../data/periodic-hexagon.mesh -p 0 -s 7)
|
||||
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 -dt 0.0018 -vs 25)
|
||||
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS} -rp 1 -dt 0.0009 -vs 50)
|
||||
# Example 10: test CVODE with CV_BDF (stiff implicit) time stepping
|
||||
set(EX10_COMMON_OPTS -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -tf 6 -vs 10)
|
||||
set(EX10_TEST_OPTS ${EX10_COMMON_OPTS} -r 2)
|
||||
set(EX10P_TEST_OPTS ${EX10_COMMON_OPTS} -rp 1)
|
||||
# Example 16: use the default options
|
||||
# Command line options for the tests.
|
||||
# Example 9: test CVODE with CV_ADAMS (non-stiff implicit) time stepping
|
||||
set(EX9_COMMON_OPTS -m ../../data/periodic-hexagon.mesh -p 0 -s 7)
|
||||
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 -dt 0.0018 -vs 25)
|
||||
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS} -rp 1 -dt 0.0009 -vs 50)
|
||||
# Example 10: test CVODE with CV_BDF (stiff implicit) time stepping
|
||||
set(EX10_COMMON_OPTS -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -tf 6 -vs 10)
|
||||
set(EX10_TEST_OPTS ${EX10_COMMON_OPTS} -r 2)
|
||||
set(EX10P_TEST_OPTS ${EX10_COMMON_OPTS} -rp 1)
|
||||
# Example 16: use the default options
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${SUNDIALS_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${SUNDIALS_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -32,32 +32,31 @@ add_mfem_examples(SUPERLU_EXAMPLES_SRCS ${PFX} "" test_superlu)
|
||||
# The SuperLU tests can be run separately using the target "test_superlu"
|
||||
# which builds the examples and runs:
|
||||
# ctest -R superlu
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# Example 1: Test SuperLU on the simple Poisson problem
|
||||
set(EX1_COMMON_OPTS -m ../../data/star.mesh -p 2)
|
||||
set(EX1P_TEST_OPTS ${EX1_COMMON_OPTS})
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${SUPERLU_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
# Command line options for the tests.
|
||||
# Example 1: Test SuperLU on the simple Poisson problem
|
||||
set(EX1_COMMON_OPTS -m ../../data/star.mesh -p 2)
|
||||
set(EX1P_TEST_OPTS ${EX1_COMMON_OPTS})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${SUPERLU_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
+183
-149
@@ -1556,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, Q1D,
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
@@ -1585,102 +1585,118 @@ 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(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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 += DQQ0[dz][qy][qx] * B[i][j];
|
||||
v += DQQ1[dz][qy][qx] * B[i][j];
|
||||
w += DQQ2[dz][qy][qx] * G[k][l] * s;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -1690,9 +1706,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;
|
||||
const double gY = v;
|
||||
const double gZ = w;
|
||||
const double gX = u[qz];
|
||||
const double gY = v[qz];
|
||||
const double gZ = w[qz];
|
||||
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);
|
||||
@@ -1700,94 +1716,112 @@ static void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,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 qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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 += QDD0[qz][dy][dx] * Bt[j][i];
|
||||
v += QDD1[qz][dy][dx] * Bt[j][i];
|
||||
w += QDD2[qz][dy][dx] * Gt[l][k] * s;
|
||||
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;
|
||||
}
|
||||
y(dx,dy,dz,e) += (u + v + w);
|
||||
}
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
y(dx,dy,dz,e) += (u[dz] + v[dz] + w[dz]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,52 +28,6 @@ double PWConstCoefficient::Eval(ElementTransformation & T,
|
||||
return (constants(att-1));
|
||||
}
|
||||
|
||||
void PWCoefficient::InitMap(const Array<int> & attr,
|
||||
const Array<Coefficient*> & coefs)
|
||||
{
|
||||
MFEM_VERIFY(attr.Size() == coefs.Size(),
|
||||
"PWCoefficient: "
|
||||
"Attribute and coefficient arrays have incompatible "
|
||||
"dimensions.");
|
||||
|
||||
for (int i=0; i<attr.Size(); i++)
|
||||
{
|
||||
if (coefs[i] != NULL)
|
||||
{
|
||||
UpdateCoefficient(attr[i], *coefs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PWCoefficient::SetTime(double t)
|
||||
{
|
||||
Coefficient::SetTime(t);
|
||||
|
||||
std::map<int, Coefficient*>::iterator p = pieces.begin();
|
||||
for (; p != pieces.end(); p++)
|
||||
{
|
||||
if (p->second != NULL)
|
||||
{
|
||||
p->second->SetTime(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double PWCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
const int att = T.Attribute;
|
||||
std::map<int, Coefficient*>::const_iterator p = pieces.find(att);
|
||||
if (p != pieces.end())
|
||||
{
|
||||
if ( p->second != NULL)
|
||||
{
|
||||
return p->second->Eval(T, ip);
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double FunctionCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
@@ -166,63 +120,6 @@ void VectorCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void PWVectorCoefficient::InitMap(const Array<int> & attr,
|
||||
const Array<VectorCoefficient*> & coefs)
|
||||
{
|
||||
MFEM_VERIFY(attr.Size() == coefs.Size(),
|
||||
"PWVectorCoefficient: "
|
||||
"Attribute and coefficient arrays have incompatible "
|
||||
"dimensions.");
|
||||
|
||||
for (int i=0; i<attr.Size(); i++)
|
||||
{
|
||||
if (coefs[i] != NULL)
|
||||
{
|
||||
UpdateCoefficient(attr[i], *coefs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PWVectorCoefficient::UpdateCoefficient(int attr, VectorCoefficient & coef)
|
||||
{
|
||||
MFEM_VERIFY(coef.GetVDim() == vdim,
|
||||
"PWVectorCoefficient::UpdateCoefficient: "
|
||||
"VectorCoefficient has incompatible dimension.");
|
||||
pieces[attr] = &coef;
|
||||
}
|
||||
|
||||
void PWVectorCoefficient::SetTime(double t)
|
||||
{
|
||||
VectorCoefficient::SetTime(t);
|
||||
|
||||
std::map<int, VectorCoefficient*>::iterator p = pieces.begin();
|
||||
for (; p != pieces.end(); p++)
|
||||
{
|
||||
if (p->second != NULL)
|
||||
{
|
||||
p->second->SetTime(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PWVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
const int att = T.Attribute;
|
||||
std::map<int, VectorCoefficient*>::const_iterator p = pieces.find(att);
|
||||
if (p != pieces.end())
|
||||
{
|
||||
if ( p->second != NULL)
|
||||
{
|
||||
p->second->Eval(V, T, ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
V.SetSize(vdim);
|
||||
V = 0.0;
|
||||
}
|
||||
|
||||
void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
@@ -434,72 +331,6 @@ void VectorRestrictedCoefficient::Eval(
|
||||
}
|
||||
}
|
||||
|
||||
void PWMatrixCoefficient::InitMap(const Array<int> & attr,
|
||||
const Array<MatrixCoefficient*> & coefs)
|
||||
{
|
||||
MFEM_VERIFY(attr.Size() == coefs.Size(),
|
||||
"PWMatrixCoefficient: "
|
||||
"Attribute and coefficient arrays have incompatible "
|
||||
"dimensions.");
|
||||
|
||||
for (int i=0; i<attr.Size(); i++)
|
||||
{
|
||||
if (coefs[i] != NULL)
|
||||
{
|
||||
UpdateCoefficient(attr[i], *coefs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PWMatrixCoefficient::UpdateCoefficient(int attr, MatrixCoefficient & coef)
|
||||
{
|
||||
MFEM_VERIFY(coef.GetHeight() == height,
|
||||
"PWMatrixCoefficient::UpdateCoefficient: "
|
||||
"MatrixCoefficient has incompatible height.");
|
||||
MFEM_VERIFY(coef.GetWidth() == width,
|
||||
"PWMatrixCoefficient::UpdateCoefficient: "
|
||||
"MatrixCoefficient has incompatible width.");
|
||||
if (symmetric)
|
||||
{
|
||||
MFEM_VERIFY(coef.IsSymmetric(),
|
||||
"PWMatrixCoefficient::UpdateCoefficient: "
|
||||
"MatrixCoefficient has incompatible symmetry.");
|
||||
}
|
||||
pieces[attr] = &coef;
|
||||
}
|
||||
|
||||
void PWMatrixCoefficient::SetTime(double t)
|
||||
{
|
||||
MatrixCoefficient::SetTime(t);
|
||||
|
||||
std::map<int, MatrixCoefficient*>::iterator p = pieces.begin();
|
||||
for (; p != pieces.end(); p++)
|
||||
{
|
||||
if (p->second != NULL)
|
||||
{
|
||||
p->second->SetTime(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PWMatrixCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
const int att = T.Attribute;
|
||||
std::map<int, MatrixCoefficient*>::const_iterator p = pieces.find(att);
|
||||
if (p != pieces.end())
|
||||
{
|
||||
if ( p->second != NULL)
|
||||
{
|
||||
p->second->Eval(K, T, ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
K.SetSize(height, width);
|
||||
K = 0.0;
|
||||
}
|
||||
|
||||
void MatrixFunctionCoefficient::SetTime(double t)
|
||||
{
|
||||
if (Q) { Q->SetTime(t); }
|
||||
|
||||
@@ -125,87 +125,6 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/** @brief A piecewise coefficient with the pieces keyed off the element
|
||||
attribute numbers.
|
||||
|
||||
A value of zero will be returned for any missing attribute numbers.
|
||||
|
||||
This object will not assume ownership of any Coefficient objects
|
||||
passed to it. Consequently, the caller must ensure that the
|
||||
individual Coefficient objects are not deleted while this
|
||||
PWCoefficient is still in use.
|
||||
|
||||
\note The keys may either be domain attribute numbers or boundary
|
||||
attribute numbers. If the PWCoefficient is used with a domain
|
||||
integrator the keys are assumed to be domain attribute
|
||||
numbers. Similarly, if the PWCoefficient is used with a boundary
|
||||
integrator the keys are assumed to be boundary attribute numbers.
|
||||
*/
|
||||
class PWCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
/** Internal data structure to store pointers to the appropriate
|
||||
coefficients for different regions of the mesh. The keys used
|
||||
in the map are the mesh attribute numbers (either element
|
||||
attribute or boundary element attribute depending upon
|
||||
context). The values returned for any missing attributes will
|
||||
be zero. The coefficient pointers may be NULL in which case a
|
||||
value of zero is returned.
|
||||
|
||||
The Coefficient objects contained in this map are NOT owned by
|
||||
this PWCoefficient object. This means that they will not be
|
||||
deleted when this object is deleted also the caller must ensure
|
||||
that the various Coefficient objects are not deleted while this
|
||||
PWCoefficient is still needed.
|
||||
*/
|
||||
std::map<int, Coefficient*> pieces;
|
||||
|
||||
/** Convenience function to check for compatible array lengths,
|
||||
loop over the arrays, and add their attribute/Coefficient pairs
|
||||
to the internal data structure.
|
||||
*/
|
||||
void InitMap(const Array<int> & attr,
|
||||
const Array<Coefficient*> & coefs);
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs a piecewise coefficient
|
||||
explicit PWCoefficient() {}
|
||||
|
||||
/// Construct the coefficient using arrays describing the pieces
|
||||
/** \param attr - an array of attribute numbers for each piece
|
||||
\param coefs - the corresponding array of Coefficient pointers
|
||||
Any missing attributes or NULL coefficient pointers will result in a
|
||||
value of zero being returned for that attribute.
|
||||
|
||||
\note Ownership of the Coefficient objects will NOT be
|
||||
transferred to this object.
|
||||
*/
|
||||
PWCoefficient(const Array<int> & attr,
|
||||
const Array<Coefficient*> & coefs)
|
||||
{ InitMap(attr, coefs); }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(double t);
|
||||
|
||||
/// Replace a set of coefficients
|
||||
void UpdateCoefficients(const Array<int> & attr,
|
||||
const Array<Coefficient*> & coefs)
|
||||
{ InitMap(attr, coefs); }
|
||||
|
||||
/// Replace a single Coefficient for a particular attribute
|
||||
void UpdateCoefficient(int attr, Coefficient & coef)
|
||||
{ pieces[attr] = &coef; }
|
||||
|
||||
/// Remove a single Coefficient for a particular attribute
|
||||
void ZeroCoefficient(int attr)
|
||||
{ pieces.erase(attr); }
|
||||
|
||||
/// Evaluate the coefficient.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// A general function coefficient
|
||||
class FunctionCoefficient : public Coefficient
|
||||
{
|
||||
@@ -494,88 +413,6 @@ public:
|
||||
const Vector& GetVec() { return vec; }
|
||||
};
|
||||
|
||||
/** @brief A piecewise vector-valued coefficient with the pieces keyed off the
|
||||
element attribute numbers.
|
||||
|
||||
A value of zero will be returned for any missing attribute numbers.
|
||||
|
||||
This object will not assume ownership of any VectorCoefficient
|
||||
objects passed to it. Consequently, the caller must ensure that
|
||||
the individual VectorCoefficient objects are not deleted while
|
||||
this PWVectorCoefficient is still in use.
|
||||
|
||||
\note The keys may either be domain attribute numbers or boundary
|
||||
attribute numbers. If the PWVectorCoefficient is used with a
|
||||
domain integrator the keys are assumed to be domain attribute
|
||||
numbers. Similarly, if the PWVectorCoefficient is used with a
|
||||
boundary integrator the keys are assumed to be boundary attribute
|
||||
numbers.
|
||||
*/
|
||||
class PWVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
/** Internal data structure to store pointers to the appropriate
|
||||
coefficients for different regions of the mesh. The keys used
|
||||
in the map are the mesh attribute numbers (either element
|
||||
attribute or boundary element attribute depending upon
|
||||
context). The values returned for any missing attributes will
|
||||
be zero. The coefficient pointers may be NULL in which case a
|
||||
value of zero is returned.
|
||||
|
||||
The VectorCoefficient objects contained in this map are NOT
|
||||
owned by this PWVectorCoefficient object. This means that they
|
||||
will not be deleted when this object is deleted also the caller
|
||||
must ensure that the various VectorCoefficient objects are not
|
||||
deleted while this PWVectorCoefficient is still needed.
|
||||
*/
|
||||
std::map<int, VectorCoefficient*> pieces;
|
||||
|
||||
/** Convenience function to check for compatible array lengths,
|
||||
loop over the arrays, and add their attribute/VectorCoefficient
|
||||
pairs to the internal data structure.
|
||||
*/
|
||||
void InitMap(const Array<int> & attr,
|
||||
const Array<VectorCoefficient*> & coefs);
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs a piecewise vector coefficient of dimension vd
|
||||
explicit PWVectorCoefficient(int vd): VectorCoefficient(vd) {}
|
||||
|
||||
/// Construct the coefficient using arrays describing the pieces
|
||||
/** \param vd - dimension of the vector-valued result
|
||||
\param attr - an array of attribute numbers for each piece
|
||||
\param coefs - the corresponding array of VectorCoefficient pointers
|
||||
Any missing attributes or NULL coefficient pointers will result in a
|
||||
zero vector being returned for that attribute.
|
||||
|
||||
\note Ownership of the VectorCoefficient objects will NOT be
|
||||
transferred to this object.
|
||||
*/
|
||||
PWVectorCoefficient(int vd, const Array<int> & attr,
|
||||
const Array<VectorCoefficient*> & coefs)
|
||||
: VectorCoefficient(vd) { InitMap(attr, coefs); }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(double t);
|
||||
|
||||
/// Replace a set of coefficients
|
||||
void UpdateCoefficients(const Array<int> & attr,
|
||||
const Array<VectorCoefficient*> & coefs)
|
||||
{ InitMap(attr, coefs); }
|
||||
|
||||
/// Replace a single Coefficient for a particular attribute
|
||||
void UpdateCoefficient(int attr, VectorCoefficient & coef);
|
||||
|
||||
/// Remove a single VectorCoefficient for a particular attribute
|
||||
void ZeroCoefficient(int attr)
|
||||
{ pieces.erase(attr); }
|
||||
|
||||
/// Evaluate the coefficient.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// A general vector function coefficient
|
||||
class VectorFunctionCoefficient : public VectorCoefficient
|
||||
{
|
||||
@@ -945,112 +782,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** @brief A piecewise matrix-valued coefficient with the pieces keyed off the
|
||||
element attribute numbers.
|
||||
|
||||
A value of zero will be returned for any missing attribute numbers.
|
||||
|
||||
This object will not assume ownership of any MatrixCoefficient
|
||||
objects passed to it. Consequently, the caller must ensure that
|
||||
the individual MatrixCoefficient objects are not deleted while
|
||||
this PWMatrixCoefficient is still in use.
|
||||
|
||||
\note The keys may either be domain attribute numbers or boundary
|
||||
attribute numbers. If the PWMatrixCoefficient is used with a
|
||||
domain integrator the keys are assumed to be domain attribute
|
||||
numbers. Similarly, if the PWMatrixCoefficient is used with a
|
||||
boundary integrator the keys are assumed to be boundary attribute
|
||||
numbers.
|
||||
*/
|
||||
class PWMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
/** Internal data structure to store pointers to the appropriate
|
||||
coefficients for different regions of the mesh. The keys used
|
||||
in the map are the mesh attribute numbers (either element
|
||||
attribute or boundary element attribute depending upon
|
||||
context). The values returned for any missing attributes will
|
||||
be zero. The coefficient pointers may be NULL in which case a
|
||||
value of zero is returned.
|
||||
|
||||
The MatrixCoefficient objects contained in this map are NOT
|
||||
owned by this PWMatrixCoefficient object. This means that they
|
||||
will not be deleted when this object is deleted also the caller
|
||||
must ensure that the various MatrixCoefficient objects are not
|
||||
deleted while this PWMatrixCoefficient is still needed.
|
||||
*/
|
||||
std::map<int, MatrixCoefficient*> pieces;
|
||||
|
||||
/** Convenience function to check for compatible array lengths,
|
||||
loop over the arrays, and add their attribute/MatrixCoefficient
|
||||
pairs to the internal data structure.
|
||||
*/
|
||||
void InitMap(const Array<int> & attr,
|
||||
const Array<MatrixCoefficient*> & coefs);
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs a piecewise matrix coefficient of dimension dim by dim
|
||||
explicit PWMatrixCoefficient(int dim, bool symm = false)
|
||||
: MatrixCoefficient(dim, symm) {}
|
||||
|
||||
/// Constructs a piecewise matrix coefficient of dimension h by w
|
||||
explicit PWMatrixCoefficient(int h, int w, bool symm = false)
|
||||
: MatrixCoefficient(h, w, symm) {}
|
||||
|
||||
/// Construct the coefficient using arrays describing the pieces
|
||||
/** \param dim - size of the square matrix-valued result
|
||||
\param attr - an array of attribute numbers for each piece
|
||||
\param coefs - the corresponding array of MatrixCoefficient pointers
|
||||
\param symm - true if the result will be symmetric, false otherwise
|
||||
Any missing attributes or NULL coefficient pointers will result in a
|
||||
zero matrix being returned.
|
||||
|
||||
\note Ownership of the MatrixCoefficient objects will NOT be
|
||||
transferred to this object.
|
||||
*/
|
||||
PWMatrixCoefficient(int dim, const Array<int> & attr,
|
||||
const Array<MatrixCoefficient*> & coefs,
|
||||
bool symm=false)
|
||||
: MatrixCoefficient(dim, symm) { InitMap(attr, coefs); }
|
||||
|
||||
/// Construct the coefficient using arrays describing the pieces
|
||||
/** \param h - height of the matrix-valued result
|
||||
\param w - width of the matrix-valued result
|
||||
\param attr - an array of attribute numbers for each piece
|
||||
\param coefs - the corresponding array of MatrixCoefficient pointers
|
||||
\param symm - true if the result will be symmetric, false otherwise
|
||||
Any missing attributes or NULL coefficient pointers will result in a
|
||||
zero matrix being returned for that attribute.
|
||||
|
||||
\note Ownership of the MatrixCoefficient objects will NOT be
|
||||
transferred to this object.
|
||||
*/
|
||||
PWMatrixCoefficient(int h, int w, const Array<int> & attr,
|
||||
const Array<MatrixCoefficient*> & coefs,
|
||||
bool symm=false)
|
||||
: MatrixCoefficient(h, w, symm) { InitMap(attr, coefs); }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(double t);
|
||||
|
||||
/// Replace a set of coefficients
|
||||
void UpdateCoefficients(const Array<int> & attr,
|
||||
const Array<MatrixCoefficient*> & coefs)
|
||||
{ InitMap(attr, coefs); }
|
||||
|
||||
/// Replace a single coefficient for a particular attribute
|
||||
void UpdateCoefficient(int attr, MatrixCoefficient & coef);
|
||||
|
||||
/// Remove a single MatrixCoefficient for a particular attribute
|
||||
void ZeroCoefficient(int attr)
|
||||
{ pieces.erase(attr); }
|
||||
|
||||
/// Evaluate the coefficient.
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/** @brief A matrix coefficient with an optional scalar coefficient multiplier
|
||||
\a q. The matrix function can either be represented by a std function or
|
||||
a constant matrix provided when constructing this object. */
|
||||
|
||||
@@ -195,15 +195,6 @@ 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)
|
||||
@@ -326,14 +317,6 @@ 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)
|
||||
@@ -896,15 +879,6 @@ 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)
|
||||
@@ -1066,14 +1040,6 @@ 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,11 +128,6 @@ 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);
|
||||
@@ -265,11 +260,6 @@ 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);
|
||||
@@ -474,11 +464,6 @@ 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);
|
||||
@@ -613,11 +598,6 @@ 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,8 +645,7 @@ 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 &main_adjset_name)
|
||||
const std::string &boundary_topology_name)
|
||||
{
|
||||
int dim = mesh->SpaceDimension();
|
||||
|
||||
@@ -816,83 +815,6 @@ 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,8 +166,7 @@ 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 &main_adjset_name = "main_adjset");
|
||||
const std::string &boundary_topology_name = "boundary");
|
||||
|
||||
/// Describes a MFEM grid function using the mesh blueprint
|
||||
/** Sets up passed conduit::Node out to describe the given grid function
|
||||
|
||||
@@ -85,11 +85,6 @@ void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::InvTransformDual(Vector &v) const
|
||||
{
|
||||
InvTransformDual(v.GetData());
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
@@ -200,35 +195,6 @@ void VDofTransformation::TransformDual(double *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void VDofTransformation::InvTransformDual(double *v) const
|
||||
{
|
||||
int size = doftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
doftrans_->InvTransformDual(&v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
doftrans_->InvTransformDual(vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double ND_DofTransformation::T_data[24] =
|
||||
{
|
||||
1.0, 0.0, 0.0, 1.0,
|
||||
@@ -325,26 +291,6 @@ ND_TriDofTransformation::TransformDual(double *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TriDofTransformation::InvTransformDual(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<1; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[3*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).MultTranspose(v2, &v[3*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ND_TetDofTransformation::ND_TetDofTransformation(int p)
|
||||
: ND_DofTransformation(p*(p + 2)*(p + 3)/2, p)
|
||||
{
|
||||
@@ -409,24 +355,4 @@ ND_TetDofTransformation::TransformDual(double *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TetDofTransformation::InvTransformDual(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<4; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[6*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).MultTranspose(v2, &v[6*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -102,10 +102,6 @@ public:
|
||||
virtual void TransformDual(double *v) const = 0;
|
||||
virtual void TransformDual(Vector &v) const;
|
||||
|
||||
/** Inverse Transform dual DoFs */
|
||||
virtual void InvTransformDual(double *v) const = 0;
|
||||
virtual void InvTransformDual(Vector &v) const;
|
||||
|
||||
/** Transform a matrix of dual DoFs entries as computed by a
|
||||
BilinearFormIntegrator before summing into a BilinearForm object. */
|
||||
virtual void TransformDual(DenseMatrix &V) const;
|
||||
@@ -187,12 +183,10 @@ public:
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
void InvTransformPrimal(double *v) const;
|
||||
void TransformDual(double *v) const;
|
||||
void InvTransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
@@ -241,8 +235,6 @@ public:
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
|
||||
void InvTransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on tetrahedra
|
||||
@@ -254,15 +246,12 @@ public:
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
|
||||
void InvTransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on wedge elements
|
||||
@@ -275,16 +264,12 @@ public:
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
|
||||
void InvTransformDual(double *v) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+5
-183
@@ -561,155 +561,6 @@ void FiniteElementSpace::GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEssentialVDofs(EntitySets::EntityType type,
|
||||
int set_index,
|
||||
Array<int> &ess_vdofs,
|
||||
int component) const
|
||||
{
|
||||
Array<int> vdofs, dofs;
|
||||
|
||||
ess_vdofs.SetSize(GetVSize());
|
||||
ess_vdofs = 0;
|
||||
|
||||
MFEM_VERIFY(mesh->ent_sets != NULL, "Mesh object contains no "
|
||||
"entity set information");
|
||||
if (!mesh->ent_sets->SetExists(type, set_index))
|
||||
{
|
||||
ostringstream oss; oss << "Entity set of type \""
|
||||
<< EntitySets::GetTypeName(type)
|
||||
<< "\" and index " << set_index
|
||||
<< " was not found.";
|
||||
|
||||
MFEM_VERIFY(false, oss.str().c_str());
|
||||
}
|
||||
|
||||
set<int>::iterator it;
|
||||
for (it=(*mesh->ent_sets)(type, set_index).begin();
|
||||
it!=(*mesh->ent_sets)(type, set_index).end(); it++)
|
||||
{
|
||||
int ent_index = *it;
|
||||
cout << "collecting vdofs for entity " << ent_index << "->";
|
||||
if (component < 0)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EntitySets::VERTEX:
|
||||
GetVertexVDofs(ent_index, vdofs);
|
||||
break;
|
||||
case EntitySets::EDGE:
|
||||
GetEdgeVDofs(ent_index, vdofs);
|
||||
break;
|
||||
case EntitySets::FACE:
|
||||
GetFaceVDofs(ent_index, vdofs);
|
||||
break;
|
||||
case EntitySets::ELEMENT:
|
||||
GetElementVDofs(ent_index, vdofs);
|
||||
break;
|
||||
default:
|
||||
mfem_error("GetEssentialVDofs: Invalid entity type");
|
||||
}
|
||||
vdofs.Print(cout);
|
||||
mark_dofs(vdofs, ess_vdofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EntitySets::VERTEX:
|
||||
GetVertexDofs(ent_index, dofs);
|
||||
break;
|
||||
case EntitySets::EDGE:
|
||||
GetEdgeDofs(ent_index, dofs);
|
||||
break;
|
||||
case EntitySets::FACE:
|
||||
GetFaceDofs(ent_index, dofs);
|
||||
break;
|
||||
case EntitySets::ELEMENT:
|
||||
GetElementDofs(ent_index, dofs);
|
||||
break;
|
||||
default:
|
||||
mfem_error("GetEssentialDofs: Invalid entity type");
|
||||
}
|
||||
for (int d = 0; d < dofs.Size(); d++)
|
||||
{ dofs[d] = DofToVDof(dofs[d], component); }
|
||||
mark_dofs(dofs, ess_vdofs);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh->ncmesh)
|
||||
{
|
||||
Array<int> es_verts, es_edges, es_faces;
|
||||
mesh->ncmesh->GetEntitySetClosure(type, set_index,
|
||||
es_verts, es_edges, es_faces);
|
||||
cout << "returned from get closure" << endl;
|
||||
for (int i = 0; i < es_verts.Size(); i++)
|
||||
{
|
||||
if (es_verts[i] < GetNV())
|
||||
{
|
||||
if (component < 0)
|
||||
{
|
||||
GetVertexVDofs(es_verts[i], vdofs);
|
||||
mark_dofs(vdofs, ess_vdofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetVertexDofs(es_verts[i], dofs);
|
||||
for (int d = 0; d < dofs.Size(); d++)
|
||||
{ dofs[d] = DofToVDof(dofs[d], component); }
|
||||
mark_dofs(dofs, ess_vdofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < es_edges.Size(); i++)
|
||||
{
|
||||
if (es_edges[i] < GetMesh()->GetNEdges())
|
||||
{
|
||||
if (component < 0)
|
||||
{
|
||||
GetEdgeVDofs(es_edges[i], vdofs);
|
||||
mark_dofs(vdofs, ess_vdofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEdgeDofs(es_edges[i], dofs);
|
||||
for (int d = 0; d < dofs.Size(); d++)
|
||||
{ dofs[d] = DofToVDof(dofs[d], component); }
|
||||
mark_dofs(dofs, ess_vdofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < es_faces.Size(); i++)
|
||||
{
|
||||
if (es_faces[i] < GetMesh()->GetNFaces())
|
||||
{
|
||||
if (component < 0)
|
||||
{
|
||||
GetFaceVDofs(es_faces[i], vdofs);
|
||||
mark_dofs(vdofs, ess_vdofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetFaceDofs(es_faces[i], dofs);
|
||||
for (int d = 0; d < dofs.Size(); d++)
|
||||
{ dofs[d] = DofToVDof(dofs[d], component); }
|
||||
mark_dofs(dofs, ess_vdofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEssentialVDofs(EntitySets::EntityType type,
|
||||
const string & set_name,
|
||||
Array<int> &ess_vdofs,
|
||||
int component) const
|
||||
{
|
||||
MFEM_VERIFY(mesh->ent_sets != NULL, "Mesh object contains no "
|
||||
"entity set information");
|
||||
GetEssentialVDofs(type, mesh->ent_sets->GetSetIndex(type, set_name),
|
||||
ess_vdofs, component);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component)
|
||||
@@ -728,36 +579,6 @@ void FiniteElementSpace::GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
|
||||
MarkerToList(ess_tdofs, ess_tdof_list);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
int set_index,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component)
|
||||
{
|
||||
Array<int> ess_vdofs, ess_tdofs;
|
||||
GetEssentialVDofs(type, set_index, ess_vdofs, component);
|
||||
const SparseMatrix *R = GetConformingRestriction();
|
||||
if (!R)
|
||||
{
|
||||
ess_tdofs.MakeRef(ess_vdofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
R->BooleanMult(ess_vdofs, ess_tdofs);
|
||||
}
|
||||
MarkerToList(ess_tdofs, ess_tdof_list);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
const string & set_name,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component)
|
||||
{
|
||||
MFEM_VERIFY(mesh->ent_sets != NULL, "Mesh object contains no "
|
||||
"entity set information");
|
||||
GetEssentialTrueDofs(type, mesh->ent_sets->GetSetIndex(type, set_name),
|
||||
ess_tdof_list, component);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetBoundaryTrueDofs(Array<int> &boundary_dofs,
|
||||
int component)
|
||||
{
|
||||
@@ -1888,7 +1709,8 @@ void FiniteElementSpace::RefinementOperator
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
doftrans->InvTransformDual(subX);
|
||||
old_DoFTrans[geom]->InvTransformPrimal(subX);
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
@@ -1897,9 +1719,9 @@ void FiniteElementSpace::RefinementOperator
|
||||
}
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subYt);
|
||||
old_DoFTrans[geom]->TransformDual(subYt);
|
||||
y.AddElementVector(c_vdofs, subYt);
|
||||
lP.MultTranspose(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
y.AddElementVector(c_vdofs, subY);
|
||||
}
|
||||
|
||||
if (vdoftrans)
|
||||
|
||||
@@ -778,19 +778,6 @@ public:
|
||||
Array<int> &ess_vdofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** Mark degrees of freedom associated with the entity set with the
|
||||
specified entity type and set index. */
|
||||
virtual void GetEssentialVDofs(EntitySets::EntityType type, int set_index,
|
||||
Array<int> &ess_vdofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** Mark degrees of freedom associated with the entity set with the
|
||||
specified entity type and set index. */
|
||||
virtual void GetEssentialVDofs(EntitySets::EntityType type,
|
||||
const std::string & set_name,
|
||||
Array<int> &ess_vdofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** @brief Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
boundary attributes marked in the array bdr_attr_is_ess.
|
||||
For spaces with 'vdim' > 1, the 'component' parameter can be used
|
||||
@@ -799,19 +786,6 @@ public:
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
entity set specified by the given entity type and set index. */
|
||||
virtual void GetEssentialTrueDofs(EntitySets::EntityType type, int set_index,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
entity set specified by the given entity type and set name. */
|
||||
virtual void GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
const std::string & set_name,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** @brief Get a list of all boundary true dofs, @a boundary_dofs. For spaces
|
||||
with 'vdim' > 1, the 'component' parameter can be used to restricts the
|
||||
marked tDOFs to the specified component. Equivalent to
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <limits>
|
||||
#include <list>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -1020,30 +1018,6 @@ void ParFiniteElementSpace::GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetEssentialVDofs(EntitySets::EntityType type,
|
||||
int set_index,
|
||||
Array<int> &ess_dofs,
|
||||
int component) const
|
||||
{
|
||||
FiniteElementSpace::GetEssentialVDofs(type, set_index, ess_dofs, component);
|
||||
|
||||
if (Conforming())
|
||||
{
|
||||
// Make sure that processors without boundary elements mark
|
||||
// their boundary dofs (if they have any).
|
||||
Synchronize(ess_dofs);
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetEssentialVDofs(EntitySets::EntityType type,
|
||||
const string & set_name,
|
||||
Array<int> &ess_vdofs,
|
||||
int component) const
|
||||
{
|
||||
GetEssentialVDofs(type, pmesh->ent_sets->GetSetIndex(type, set_name),
|
||||
ess_vdofs, component);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetEssentialTrueDofs(const Array<int>
|
||||
&bdr_attr_is_ess,
|
||||
Array<int> &ess_tdof_list,
|
||||
@@ -1073,27 +1047,6 @@ void ParFiniteElementSpace::GetEssentialTrueDofs(const Array<int>
|
||||
MarkerToList(true_ess_dofs, ess_tdof_list);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
int set_index,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component)
|
||||
{
|
||||
Array<int> ess_dofs, true_ess_dofs;
|
||||
|
||||
GetEssentialVDofs(type, set_index, ess_dofs, component);
|
||||
GetRestrictionMatrix()->BooleanMult(ess_dofs, true_ess_dofs);
|
||||
MarkerToList(true_ess_dofs, ess_tdof_list);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
const string & set_name,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component)
|
||||
{
|
||||
GetEssentialTrueDofs(type, pmesh->ent_sets->GetSetIndex(type, set_name),
|
||||
ess_tdof_list, component);
|
||||
}
|
||||
|
||||
int ParFiniteElementSpace::GetLocalTDofNumber(int ldof) const
|
||||
{
|
||||
if (Nonconforming())
|
||||
|
||||
@@ -355,38 +355,12 @@ public:
|
||||
Array<int> &ess_dofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** Mark degrees of freedom associated with the entity set with the
|
||||
specified entity type and set index. */
|
||||
virtual void GetEssentialVDofs(EntitySets::EntityType type, int set_index,
|
||||
Array<int> &ess_vdofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** Mark degrees of freedom associated with the entity set with the
|
||||
specified entity type and set index. */
|
||||
virtual void GetEssentialVDofs(EntitySets::EntityType type,
|
||||
const std::string & set_name,
|
||||
Array<int> &ess_vdofs,
|
||||
int component = -1) const;
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
boundary attributes marked in the array bdr_attr_is_ess. */
|
||||
virtual void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
entity set specified by the given entity type and set index. */
|
||||
virtual void GetEssentialTrueDofs(EntitySets::EntityType type, int set_index,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
entity set specified by the given entity type and set name. */
|
||||
virtual void GetEssentialTrueDofs(EntitySets::EntityType type,
|
||||
const std::string & set_name,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
|
||||
/** If the given ldof is owned by the current processor, return its local
|
||||
tdof number, otherwise return -1 */
|
||||
int GetLocalTDofNumber(int ldof) const;
|
||||
|
||||
@@ -40,6 +40,8 @@ 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();
|
||||
|
||||
+6
-27
@@ -913,11 +913,7 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
|
||||
else if (lFESpace_.GetMesh()->GetNE() > 0
|
||||
&& hFESpace_.GetMesh()->GetNE() > 0
|
||||
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetFE(0))
|
||||
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetFE(0))
|
||||
&& (hFESpace_.FEColl()->GetContType() ==
|
||||
mfem::FiniteElementCollection::CONTINUOUS ||
|
||||
hFESpace_.FEColl()->GetContType() ==
|
||||
mfem::FiniteElementCollection::DISCONTINUOUS))
|
||||
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetFE(0)))
|
||||
{
|
||||
opr = new TensorProductPRefinementTransferOperator(lFESpace_, hFESpace_);
|
||||
}
|
||||
@@ -965,8 +961,8 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs);
|
||||
lFESpace.GetElementDofs(i, l_dofs);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom)
|
||||
@@ -986,15 +982,7 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
h_dofs.Copy(h_vdofs);
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
x.GetSubVector(l_vdofs, subX);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->InvTransformPrimal(subX);
|
||||
}
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->TransformPrimal(subY);
|
||||
}
|
||||
y.SetSubVector(h_vdofs, subY);
|
||||
}
|
||||
}
|
||||
@@ -1022,8 +1010,8 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs);
|
||||
lFESpace.GetElementDofs(i, l_dofs);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom)
|
||||
@@ -1045,10 +1033,6 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
|
||||
x.GetSubVector(h_vdofs, subX);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->InvTransformDual(subX);
|
||||
}
|
||||
for (int p = 0; p < h_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[lFESpace.DecodeDof(h_dofs[p])])
|
||||
@@ -1058,10 +1042,6 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
}
|
||||
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->TransformDual(subY);
|
||||
}
|
||||
y.AddElementVector(l_vdofs, subY);
|
||||
}
|
||||
|
||||
@@ -1105,8 +1085,7 @@ TensorProductPRefinementTransferOperator(
|
||||
// must be sorted in lexicographical order
|
||||
for (int i = 0; i < ir.GetNPoints(); ++i)
|
||||
{
|
||||
int j = hdofmap[i] >=0 ? hdofmap[i] : -1 - hdofmap[i];
|
||||
irLex.IntPoint(i) = ir.IntPoint(j);
|
||||
irLex.IntPoint(i) = ir.IntPoint(hdofmap[i]);
|
||||
}
|
||||
|
||||
NE = lFESpace.GetNE();
|
||||
|
||||
@@ -175,9 +175,8 @@ protected:
|
||||
// Copy{From,To}, {ReadWrite,Read,Write}.
|
||||
|
||||
public:
|
||||
/** Default constructor, sets the host pointer to nullptr and the metadata to
|
||||
meaningful default values. */
|
||||
Memory() { Reset(); }
|
||||
/// Default constructor: no initialization.
|
||||
Memory() { }
|
||||
|
||||
/// Copy constructor: default.
|
||||
Memory(const Memory &orig) = default;
|
||||
|
||||
@@ -69,7 +69,6 @@ void IntegerSet::Recreate(const int n, const int *p)
|
||||
|
||||
me.Sort();
|
||||
|
||||
// Remove duplicate entries
|
||||
for (j = 0, i = 1; i < n; i++)
|
||||
if (me[i] != me[j])
|
||||
{
|
||||
|
||||
+2
-7
@@ -36,7 +36,7 @@ public:
|
||||
IntegerSet(const int n, const int *p) { Recreate(n, p); }
|
||||
|
||||
/// Return the size of the set.
|
||||
int Size() const { return me.Size(); }
|
||||
int Size() { return me.Size(); }
|
||||
|
||||
/// Return a reference to the sorted array of all the set entries.
|
||||
operator Array<int>& () { return me; }
|
||||
@@ -50,8 +50,6 @@ public:
|
||||
/// Return 1 if the sets are equal and 0 otherwise.
|
||||
int operator==(IntegerSet &s);
|
||||
|
||||
inline const int & operator[](int i) const { return me[i]; }
|
||||
|
||||
/** @brief Create an integer set from C-array 'p' of 'n' integers.
|
||||
Overwrites any existing set data. */
|
||||
void Recreate(const int n, const int *p);
|
||||
@@ -66,7 +64,7 @@ private:
|
||||
public:
|
||||
|
||||
/// Return the number of integer sets in the list.
|
||||
int Size() const { return TheList.Size(); }
|
||||
int Size() { return TheList.Size(); }
|
||||
|
||||
/// Return the value of the first element of the ith set.
|
||||
int PickElementInSet(int i) { return TheList[i]->PickElement(); }
|
||||
@@ -86,9 +84,6 @@ public:
|
||||
/// Write the list of sets into table 't'.
|
||||
void AsTable(Table &t);
|
||||
|
||||
inline const IntegerSet & operator[](int i) const { return *TheList[i]; }
|
||||
inline IntegerSet & operator[](int i) { return *TheList[i]; }
|
||||
|
||||
~ListOfIntegerSets();
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ inline void Sort3 (int &r, int &c, int &f)
|
||||
}
|
||||
}
|
||||
|
||||
int STable3D::Push (int r, int c, int f, int t)
|
||||
int STable3D::Push (int r, int c, int f)
|
||||
{
|
||||
STable3DNode *node;
|
||||
|
||||
@@ -86,7 +86,6 @@ int STable3D::Push (int r, int c, int f, int t)
|
||||
#endif
|
||||
node->Column = c;
|
||||
node->Floor = f;
|
||||
node->Tier = t;
|
||||
node->Number = NElem;
|
||||
node->Prev = Rows[r];
|
||||
Rows[r] = node;
|
||||
@@ -110,9 +109,9 @@ int STable3D::operator() (int r, int c, int f) const
|
||||
}
|
||||
}
|
||||
|
||||
// MFEM_ABORT("(r,c,f) = (" << r << "," << c << "," << f << ")");
|
||||
MFEM_ABORT("(r,c,f) = (" << r << "," << c << "," << f << ")");
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int STable3D::Index (int r, int c, int f) const
|
||||
@@ -153,13 +152,13 @@ int STable3D::Push4 (int r, int c, int f, int t)
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return Push (c,f,t,r);
|
||||
return Push (c,f,t);
|
||||
case 1:
|
||||
return Push (r,f,t,c);
|
||||
return Push (r,f,t);
|
||||
case 2:
|
||||
return Push (r,c,t,f);
|
||||
return Push (r,c,t);
|
||||
case 3:
|
||||
return Push (r,c,f,t);
|
||||
return Push (r,c,f);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -219,7 +218,6 @@ void STable3D::Print(std::ostream & out) const
|
||||
out << row
|
||||
<< ' ' << node_p->Column
|
||||
<< ' ' << node_p->Floor
|
||||
<< ' ' << node_p->Tier
|
||||
<< ' ' << node_p->Number
|
||||
<< endl;
|
||||
node_p = node_p->Prev;
|
||||
|
||||
+3
-22
@@ -15,8 +15,6 @@
|
||||
#include "mem_alloc.hpp"
|
||||
#include "../general/globals.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -24,7 +22,7 @@ class STable3DNode
|
||||
{
|
||||
public:
|
||||
STable3DNode *Prev;
|
||||
int Column, Floor, Tier, Number;
|
||||
int Column, Floor, Number;
|
||||
};
|
||||
|
||||
/** @brief Symmetric 3D Table stored as an array of rows each of which has a
|
||||
@@ -49,7 +47,7 @@ public:
|
||||
|
||||
/** @brief Check to see if this entry is in the table and add it to the table
|
||||
if it is not there. Returns the number assigned to the table entry. */
|
||||
int Push (int r, int c, int f, int t = -1);
|
||||
int Push (int r, int c, int f);
|
||||
|
||||
/// Return the number assigned to the table entry. Abort if it's not there.
|
||||
int operator() (int r, int c, int f) const;
|
||||
@@ -68,30 +66,13 @@ public:
|
||||
not there. */
|
||||
int operator() (int r, int c, int f, int t) const;
|
||||
|
||||
/// Return the number of rows added to the table.
|
||||
int NumberOfRows() const { return Size; }
|
||||
|
||||
/// Return the number of elements added to the table.
|
||||
int NumberOfElements() const { return NElem; }
|
||||
int NumberOfElements() { return NElem; }
|
||||
|
||||
/// Print out all of the table elements.
|
||||
void Print(std::ostream &out = mfem::out) const;
|
||||
|
||||
~STable3D ();
|
||||
|
||||
class RowIterator
|
||||
{
|
||||
private:
|
||||
STable3DNode *n;
|
||||
public:
|
||||
RowIterator (const STable3D &t, int r) { n = t.Rows[r]; }
|
||||
int operator!() { return (n != NULL); }
|
||||
void operator++() { n = n->Prev; }
|
||||
int Column() { return (n->Column); }
|
||||
int Floor() { return (n->Floor); }
|
||||
int Tier() { return (n->Tier); }
|
||||
int Index() { return (n->Number); }
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ 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; }
|
||||
Table() { size = -1; I.Reset(); J.Reset(); }
|
||||
|
||||
/// 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)
|
||||
{ MakeFromList(nrows, list); }
|
||||
{ I.Reset(); J.Reset(); MakeFromList(nrows, list); }
|
||||
|
||||
/** Create a table with one entry per row with column indices given
|
||||
by 'partitioning'. */
|
||||
|
||||
+20
-1
@@ -70,7 +70,10 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
DenseMatrix::DenseMatrix() : Matrix(0) { }
|
||||
DenseMatrix::DenseMatrix() : Matrix(0)
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
|
||||
DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
|
||||
{
|
||||
@@ -81,6 +84,10 @@ 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)
|
||||
@@ -91,6 +98,10 @@ 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)
|
||||
@@ -103,6 +114,10 @@ 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)
|
||||
@@ -122,6 +137,10 @@ DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::SetSize(int h, int w)
|
||||
|
||||
@@ -753,6 +753,7 @@ public:
|
||||
DenseTensor()
|
||||
{
|
||||
nk = 0;
|
||||
tdata.Reset();
|
||||
}
|
||||
|
||||
DenseTensor(int i, int j, int k)
|
||||
@@ -786,6 +787,10 @@ public:
|
||||
tdata.New(size, other.tdata.GetMemoryType());
|
||||
tdata.CopyFrom(other.tdata, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
tdata.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
int SizeI() const { return Mk.Height(); }
|
||||
|
||||
+11
-50
@@ -127,19 +127,18 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
// Call the move constructor on the "compatible" temp vector
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : HypreParVector(
|
||||
y.CreateCompatibleVector())
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : Vector()
|
||||
{
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(const HypreParMatrix &A,
|
||||
@@ -179,23 +178,6 @@ 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); }
|
||||
@@ -234,18 +216,6 @@ 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_;
|
||||
@@ -1603,16 +1573,9 @@ 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
|
||||
@@ -1624,9 +1587,7 @@ 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
|
||||
|
||||
+1
-9
@@ -141,10 +141,8 @@ 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 a deep copy of @a y
|
||||
/// Creates vector compatible with 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
|
||||
@@ -152,10 +150,6 @@ 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; }
|
||||
|
||||
@@ -198,8 +192,6 @@ public:
|
||||
HypreParVector& operator= (double d);
|
||||
/// Define '=' for hypre vectors.
|
||||
HypreParVector& operator= (const HypreParVector &y);
|
||||
/// Move assignment
|
||||
HypreParVector& operator= (HypreParVector &&y);
|
||||
|
||||
using Vector::Read;
|
||||
|
||||
|
||||
@@ -558,6 +558,8 @@ PetscParVector::PetscParVector(MPI_Comm comm, const Operator &op,
|
||||
else /* Vector intended to be used with Place/ResetMemory calls */
|
||||
{
|
||||
size = loc;
|
||||
pdata.Reset();
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,6 +581,8 @@ 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.SetHostPtrOwner(true);
|
||||
J.SetHostPtrOwner(true);
|
||||
A.SetHostPtrOwner(true);
|
||||
I.Reset(); I.SetHostPtrOwner(true);
|
||||
J.Reset(); J.SetHostPtrOwner(true);
|
||||
A.Reset(); 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.SetHostPtrOwner(true);
|
||||
J.SetHostPtrOwner(true);
|
||||
A.SetHostPtrOwner(true);
|
||||
I.Reset(); I.SetHostPtrOwner(true);
|
||||
J.Reset(); J.SetHostPtrOwner(true);
|
||||
A.Reset(); A.SetHostPtrOwner(true);
|
||||
}
|
||||
|
||||
current_row = -1;
|
||||
|
||||
+8
-1
@@ -17,7 +17,10 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0) { }
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
|
||||
{
|
||||
@@ -27,6 +30,10 @@ 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)
|
||||
|
||||
+6
-15
@@ -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,15 +146,6 @@ 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();
|
||||
|
||||
+8
-11
@@ -66,16 +66,12 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor for Vector. Sets size = 0, and calls Memory::Reset on
|
||||
data through Memory<double>'s default constructor. */
|
||||
Vector(): size(0) { }
|
||||
/// Default constructor for Vector. Sets size = 0 and data = NULL.
|
||||
Vector() { data.Reset(); 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);
|
||||
@@ -282,9 +278,6 @@ public:
|
||||
assignment operator. */
|
||||
Vector &operator=(const Vector &v);
|
||||
|
||||
/// Move assignment
|
||||
Vector &operator=(Vector&& v);
|
||||
|
||||
/// Redefine '=' for vector = constant.
|
||||
Vector &operator=(double value);
|
||||
|
||||
@@ -510,12 +503,16 @@ 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 autodiff
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib adjoint solvers shifted mtop parelag
|
||||
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_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER MFEM_USE_BENCHMARK\
|
||||
MFEM_USE_PARELAG MFEM_SOURCE_DIR MFEM_INSTALL_DIR
|
||||
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) $(DEP_FLAGS) $(BLD)$${i}.o $(SRC)$${i}.cpp\
|
||||
$(DEP_CXX) $(MFEM_BUILD_FLAGS) -MM -MT $(BLD)$${i}.o $(SRC)$${i}.cpp\
|
||||
>> $(BLD)deps.mk; done
|
||||
|
||||
check: lib
|
||||
@@ -679,8 +679,6 @@ 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))
|
||||
|
||||
-1295
File diff suppressed because it is too large
Load Diff
@@ -1,219 +0,0 @@
|
||||
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
||||
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
||||
// reserved. See file COPYRIGHT for details.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability see http://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#ifndef MFEM_ENTITY_SETS
|
||||
#define MFEM_ENTITY_SETS
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/table.hpp"
|
||||
#include "../general/stable3d.hpp"
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class Mesh;
|
||||
class NCMesh;
|
||||
class NCEntitySets;
|
||||
|
||||
class EntitySets
|
||||
{
|
||||
friend class Mesh;
|
||||
friend class NCMesh;
|
||||
friend class NCEntitySets;
|
||||
|
||||
public:
|
||||
enum EntityType {INVALID = -1, VERTEX = 0, EDGE = 1, FACE = 2, ELEMENT = 3};
|
||||
|
||||
static std::map<EntityType,std::string> EntityTypeNames;
|
||||
|
||||
EntitySets(Mesh & mesh);
|
||||
EntitySets(const EntitySets & ent_sets);
|
||||
EntitySets(Mesh & mesh, NCMesh &ncmesh);
|
||||
|
||||
virtual ~EntitySets();
|
||||
|
||||
static const std::string & GetTypeName(EntityType t);
|
||||
|
||||
bool SetExists(EntityType t, unsigned int s) const;
|
||||
bool SetExists(EntityType t, const std::string & s) const;
|
||||
|
||||
void Load(std::istream &input);
|
||||
void Print(std::ostream &output) const;
|
||||
virtual void PrintSetInfo(std::ostream &output) const;
|
||||
|
||||
inline Mesh *GetMesh() const { return mesh_; }
|
||||
|
||||
unsigned int GetNumSets(EntityType t) const;
|
||||
|
||||
const std::string & GetSetName(EntityType t, unsigned int s) const;
|
||||
unsigned int GetNumEntities(EntityType t, unsigned int s) const;
|
||||
|
||||
int GetSetIndex(EntityType t, const std::string & s) const;
|
||||
unsigned int GetNumEntities(EntityType t, const std::string & s) const;
|
||||
|
||||
inline std::set<int> & operator()(EntityType t, unsigned int s)
|
||||
{ return sets_[t][s]; }
|
||||
inline const std::set<int> & operator()(EntityType t, unsigned int s) const
|
||||
{ return sets_[t][s]; }
|
||||
|
||||
const Table * GetEdgeVertexTable() const { return edge_vertex_; }
|
||||
const Table * GetFaceVertexTable() const { return face_vertex_; }
|
||||
const Table * GetFaceEdgeTable() const { return face_edge_; }
|
||||
|
||||
// void Prune(int nelems);
|
||||
|
||||
protected:
|
||||
|
||||
void SetNumSets(EntityType t, unsigned int n)
|
||||
{ sets_[t].resize(n); set_names_[t].resize(n); }
|
||||
void SetSetName(EntityType t, int s, const std::string & name)
|
||||
{ set_names_[t][s] = name; set_index_by_name_[t][name] = s; }
|
||||
|
||||
/// Make local copies of edge_vertex, face_vertex, and face_edge tables.
|
||||
void CopyMeshTables();
|
||||
|
||||
/// Refine quadrilateral mesh.
|
||||
virtual void QuadUniformRefinement();
|
||||
|
||||
/// Refine hexahedral mesh.
|
||||
virtual void HexUniformRefinement();
|
||||
|
||||
/// Refine 2D mesh.
|
||||
virtual void UniformRefinement2D();
|
||||
|
||||
/// Refine 3D mesh.
|
||||
virtual void UniformRefinement3D();
|
||||
|
||||
private:
|
||||
|
||||
static void skip_comment_lines(std::istream &is, const char comment_char)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
is >> std::ws;
|
||||
if (is.peek() != comment_char) { break; }
|
||||
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
}
|
||||
}
|
||||
// Check for, and remove, a trailing '\r'.
|
||||
static void filter_dos(std::string &line)
|
||||
{
|
||||
if (!line.empty() && *line.rbegin() == '\r')
|
||||
{ line.resize(line.size()-1); }
|
||||
}
|
||||
|
||||
static std::map<EntityType,std::string> init_type_names();
|
||||
|
||||
void LoadEntitySets(std::istream &input, EntityType t,
|
||||
const std::string & header);
|
||||
|
||||
void PrintEntitySets(std::ostream &output, EntityType t,
|
||||
const std::string & header) const;
|
||||
|
||||
void PrintEdgeSets(std::ostream &output) const;
|
||||
|
||||
void PrintFaceSets(std::ostream &output) const;
|
||||
|
||||
void PrintEntitySetInfo(std::ostream & output, EntityType t,
|
||||
const std::string & ent_name) const;
|
||||
|
||||
void CopyEntitySets(const EntitySets & ent_sets, EntityType t);
|
||||
void BuildEntitySets(NCMesh &ncmesh, EntityType t);
|
||||
|
||||
protected:
|
||||
|
||||
Mesh * mesh_;
|
||||
Table * edge_vertex_;
|
||||
Table * face_vertex_;
|
||||
Table * face_edge_;
|
||||
|
||||
int NumOfVertices_;
|
||||
int NumOfEdges_;
|
||||
int NumOfElements_;
|
||||
|
||||
/** The node/edge/face/element indices needed by the finite element
|
||||
space to look up DoFs. */
|
||||
std::vector<std::vector<std::set<int> > > sets_;
|
||||
|
||||
/// Names of each entity set
|
||||
std::vector<std::vector<std::string> > set_names_;
|
||||
|
||||
/// Indices of each entity set indexed by set name
|
||||
std::vector<std::map<std::string, int> > set_index_by_name_;
|
||||
};
|
||||
|
||||
class NCEntitySets
|
||||
{
|
||||
friend class EntitySets;
|
||||
|
||||
public:
|
||||
NCEntitySets(const EntitySets & ent_sets, NCMesh &ncmesh);
|
||||
NCEntitySets(const NCEntitySets & ncent_sets);
|
||||
|
||||
bool SetExists(EntitySets::EntityType t, unsigned int s) const;
|
||||
bool SetExists(EntitySets::EntityType t, const std::string & s) const;
|
||||
|
||||
unsigned int GetNumSets(EntitySets::EntityType t) const;
|
||||
|
||||
static int GetEntitySize(EntitySets::EntityType t);
|
||||
|
||||
const std::string & GetSetName(EntitySets::EntityType t, int s) const;
|
||||
unsigned int GetNumEntities(EntitySets::EntityType t, int s) const;
|
||||
void GetEntityIndex(EntitySets::EntityType t, int s,
|
||||
int i, Array<int> & inds) const;
|
||||
|
||||
int GetSetIndex(EntitySets::EntityType t,
|
||||
const std::string & s) const;
|
||||
unsigned int GetNumEntities(EntitySets::EntityType t,
|
||||
const std::string & s) const;
|
||||
void GetEntityIndex(EntitySets::EntityType t,
|
||||
const std::string & s, int i,
|
||||
Array<int> & inds) const;
|
||||
|
||||
inline std::vector<int> & operator()(EntitySets::EntityType t, int s)
|
||||
{ return sets_[t][s]; }
|
||||
inline const std::vector<int> & operator()(EntitySets::EntityType t,
|
||||
int s) const
|
||||
{ return sets_[t][s]; }
|
||||
inline int & operator()(EntitySets::EntityType t, int s, int i)
|
||||
{ return sets_[t][s][i]; }
|
||||
inline int operator()(EntitySets::EntityType t, int s, int i) const
|
||||
{ return sets_[t][s][i]; }
|
||||
|
||||
private:
|
||||
void CopyNCEntitySets(const NCEntitySets & ncent_sets,
|
||||
EntitySets::EntityType t);
|
||||
|
||||
protected:
|
||||
|
||||
NCMesh * ncmesh_;
|
||||
|
||||
/// The nodes defining the node/edge/face/element sets
|
||||
std::vector<std::vector<std::vector<int> > > sets_;
|
||||
|
||||
/// Names of each entity set
|
||||
std::vector<std::vector<std::string> > set_names_;
|
||||
|
||||
/// Indices of each entity set indexed by set name
|
||||
std::vector<std::map<std::string, int> > set_index_by_name_;
|
||||
|
||||
/// Number of indices per entity
|
||||
static const int entity_size_[4];
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_ENTITY_SETS
|
||||
+5
-71
@@ -1090,7 +1090,8 @@ FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
|
||||
int fn = GetBdrFace(BdrElemNo);
|
||||
|
||||
// Check if the face is interior, shared, or non-conforming.
|
||||
if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
|
||||
// if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
|
||||
if (faces_info[fn].NCFace >= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -1177,15 +1178,13 @@ void Mesh::Init()
|
||||
own_nodes = 1;
|
||||
NURBSext = NULL;
|
||||
ncmesh = NULL;
|
||||
ent_sets = NULL;
|
||||
last_operation = Mesh::NONE;
|
||||
}
|
||||
|
||||
void Mesh::InitTables()
|
||||
{
|
||||
el_to_edge =
|
||||
el_to_face = el_to_el = bel_to_edge = face_edge =
|
||||
face_vertex = edge_vertex = NULL;
|
||||
el_to_face = el_to_el = bel_to_edge = face_edge = edge_vertex = NULL;
|
||||
}
|
||||
|
||||
void Mesh::SetEmpty()
|
||||
@@ -1207,7 +1206,6 @@ void Mesh::DestroyTables()
|
||||
}
|
||||
|
||||
delete face_edge;
|
||||
delete face_vertex;
|
||||
delete edge_vertex;
|
||||
}
|
||||
|
||||
@@ -1215,8 +1213,6 @@ void Mesh::DestroyPointers()
|
||||
{
|
||||
if (own_nodes) { delete Nodes; }
|
||||
|
||||
delete ent_sets;
|
||||
|
||||
delete ncmesh;
|
||||
|
||||
delete NURBSext;
|
||||
@@ -3351,12 +3347,6 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
|
||||
// Copy the edge-to-vertex Table, edge_vertex
|
||||
edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
|
||||
|
||||
// Copy the face-to-vertex Table, edge_vertex
|
||||
face_vertex = (mesh.face_vertex) ? new Table(*mesh.face_vertex) : NULL;
|
||||
|
||||
// Do not copy any of the coarse (c_*), fine (f_*) or fine/coarse (fc_*)
|
||||
// data members.
|
||||
|
||||
// Copy the attributes and bdr_attributes
|
||||
mesh.attributes.Copy(attributes);
|
||||
mesh.bdr_attributes.Copy(bdr_attributes);
|
||||
@@ -3407,9 +3397,6 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
|
||||
Nodes = mesh.Nodes;
|
||||
own_nodes = 0;
|
||||
}
|
||||
|
||||
// Copy entity sets if present in the input mesh
|
||||
ent_sets = (mesh.ent_sets) ? new EntitySets(*mesh.ent_sets) : NULL;
|
||||
}
|
||||
|
||||
Mesh::Mesh(Mesh &&mesh) : Mesh()
|
||||
@@ -5782,38 +5769,6 @@ Table *Mesh::GetEdgeVertexTable() const
|
||||
return edge_vertex;
|
||||
}
|
||||
|
||||
Table *Mesh::GetFaceVertexTable() const
|
||||
{
|
||||
if (face_vertex)
|
||||
{
|
||||
return face_vertex;
|
||||
}
|
||||
|
||||
STable3D * faces_tbl = GetFacesTable();
|
||||
|
||||
int nfaces = faces_tbl->NumberOfElements();
|
||||
face_vertex = new Table(nfaces, 4);
|
||||
for (int i = 0; i < NumOfVertices; i++)
|
||||
{
|
||||
for (STable3D::RowIterator it(*faces_tbl, i); !it; ++it)
|
||||
{
|
||||
int j = it.Index();
|
||||
face_vertex->Push(j, i);
|
||||
face_vertex->Push(j, it.Column());
|
||||
face_vertex->Push(j, it.Floor());
|
||||
if ( it.Tier() > 0 )
|
||||
{
|
||||
face_vertex->Push(j, it.Tier());
|
||||
}
|
||||
}
|
||||
}
|
||||
face_vertex->Finalize();
|
||||
|
||||
delete faces_tbl;
|
||||
|
||||
return face_vertex;
|
||||
}
|
||||
|
||||
Table *Mesh::GetVertexToElementTable()
|
||||
{
|
||||
int i, j, nv, *v;
|
||||
@@ -6448,7 +6403,7 @@ void Mesh::GenerateNCFaceInfo()
|
||||
}
|
||||
}
|
||||
|
||||
STable3D *Mesh::GetFacesTable() const
|
||||
STable3D *Mesh::GetFacesTable()
|
||||
{
|
||||
STable3D *faces_tbl = new STable3D(NumOfVertices);
|
||||
for (int i = 0; i < NumOfElements; i++)
|
||||
@@ -7703,11 +7658,6 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
}
|
||||
|
||||
if ( ent_sets )
|
||||
{
|
||||
ent_sets->CopyMeshTables();
|
||||
}
|
||||
|
||||
int quad_counter = 0;
|
||||
for (int i = 0; i < NumOfElements; i++)
|
||||
{
|
||||
@@ -7843,11 +7793,6 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
|
||||
|
||||
if (update_nodes) { UpdateNodes(); }
|
||||
|
||||
if ( ent_sets )
|
||||
{
|
||||
ent_sets->UniformRefinement2D();
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (!Nodes || update_nodes)
|
||||
{
|
||||
@@ -7878,11 +7823,6 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
GetElementToFaceTable();
|
||||
}
|
||||
|
||||
if ( ent_sets )
|
||||
{
|
||||
ent_sets->CopyMeshTables();
|
||||
}
|
||||
|
||||
Array<int> f2qf_loc;
|
||||
Array<int> &f2qf = f2qf_ptr ? *f2qf_ptr : f2qf_loc;
|
||||
f2qf.SetSize(0);
|
||||
@@ -8209,6 +8149,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
}
|
||||
AverageVertices(vv, 4, oface + f2qf[f[fi]]);
|
||||
}
|
||||
|
||||
for (int ei = 0; ei < 9; ei++)
|
||||
{
|
||||
for (int k = 0; k < 2; k++)
|
||||
@@ -8552,11 +8493,6 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
sequence++;
|
||||
|
||||
if (update_nodes) { UpdateNodes(); }
|
||||
|
||||
if (ent_sets)
|
||||
{
|
||||
ent_sets->UniformRefinement3D();
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
@@ -9026,8 +8962,6 @@ void Mesh::Swap(Mesh& other, bool non_geometry)
|
||||
|
||||
mfem::Swap(geom_factors, other.geom_factors);
|
||||
|
||||
mfem::Swap(ent_sets, other.ent_sets);
|
||||
|
||||
#ifdef MFEM_USE_MEMALLOC
|
||||
TetMemory.Swap(other.TetMemory);
|
||||
#endif
|
||||
|
||||
+2
-11
@@ -20,7 +20,6 @@
|
||||
#include "vertex.hpp"
|
||||
#include "vtk.hpp"
|
||||
#include "ncmesh.hpp"
|
||||
#include "entsets.hpp"
|
||||
#include "../fem/eltrans.hpp"
|
||||
#include "../fem/coefficient.hpp"
|
||||
#include "../general/zstr.hpp"
|
||||
@@ -55,11 +54,9 @@ class Mesh
|
||||
#ifdef MFEM_USE_MPI
|
||||
friend class ParMesh;
|
||||
friend class ParNCMesh;
|
||||
friend class ParEntitySets;
|
||||
#endif
|
||||
friend class NCMesh;
|
||||
friend class NURBSExtension;
|
||||
friend class EntitySets;
|
||||
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
friend class adios2stream;
|
||||
@@ -169,7 +166,6 @@ protected:
|
||||
Array<int> be_to_face;
|
||||
mutable Table *face_edge;
|
||||
mutable Table *edge_vertex;
|
||||
mutable Table *face_vertex;
|
||||
|
||||
IsoparametricTransformation Transformation, Transformation2;
|
||||
IsoparametricTransformation BdrTransformation;
|
||||
@@ -220,8 +216,6 @@ public:
|
||||
Array<FaceGeometricFactors*>
|
||||
face_geom_factors; ///< Optional face geometric factors.
|
||||
|
||||
EntitySets *ent_sets;
|
||||
|
||||
// Global parameter that can be used to control the removal of unused
|
||||
// vertices performed when reading a mesh in MFEM format. The default value
|
||||
// (true) is set in mesh_readers.cpp.
|
||||
@@ -293,7 +287,7 @@ protected:
|
||||
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert);
|
||||
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert);
|
||||
|
||||
STable3D *GetFacesTable() const;
|
||||
STable3D *GetFacesTable();
|
||||
STable3D *GetElementToFaceTable(int ret_ftbl = 0);
|
||||
|
||||
/** Red refinement. Element with index i is refined. The default
|
||||
@@ -1073,12 +1067,9 @@ public:
|
||||
/// Returns the face-to-edge Table (3D)
|
||||
Table *GetFaceEdgeTable() const;
|
||||
|
||||
/// Returns the edge-to-vertex Table (2D or 3D)
|
||||
/// Returns the edge-to-vertex Table (3D)
|
||||
Table *GetEdgeVertexTable() const;
|
||||
|
||||
/// Returns the face-to-vertex Table (2d or 3D)
|
||||
Table *GetFaceVertexTable() const;
|
||||
|
||||
/// Return the indices and the orientations of all faces of element i.
|
||||
void GetElementFaces(int i, Array<int> &faces, Array<int> &ori) const;
|
||||
|
||||
|
||||
@@ -100,14 +100,6 @@ void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
|
||||
curved = 1;
|
||||
}
|
||||
|
||||
ent_sets = new EntitySets(*this);
|
||||
ent_sets->Load(input);
|
||||
if ( ent_sets->GetNumSets(EntitySets::FACE) > 0 && faces.Size() == 0 )
|
||||
{
|
||||
GetElementToFaceTable();
|
||||
GenerateFaces();
|
||||
}
|
||||
|
||||
// When visualizing solutions on non-conforming grids, PETSc
|
||||
// may dump additional vertices
|
||||
if (remove_unused_vertices) { RemoveUnusedVertices(); }
|
||||
|
||||
+4
-404
@@ -185,10 +185,6 @@ NCMesh::NCMesh(const Mesh *mesh)
|
||||
face->attribute = be->GetAttribute();
|
||||
}
|
||||
|
||||
// Store entity set information if present in the Mesh
|
||||
ncent_sets = (mesh->ent_sets) ?
|
||||
new NCEntitySets(*mesh->ent_sets, *this) : NULL;
|
||||
|
||||
// copy top-level vertex coordinates (leave empty if the mesh is curved)
|
||||
if (!mesh->Nodes)
|
||||
{
|
||||
@@ -220,10 +216,6 @@ NCMesh::NCMesh(const NCMesh &other)
|
||||
other.free_element_ids.Copy(free_element_ids);
|
||||
other.root_state.Copy(root_state);
|
||||
other.coordinates.Copy(coordinates);
|
||||
|
||||
// Copy the entity set information
|
||||
ncent_sets = (other.ncent_sets) ? new NCEntitySets(*other.ncent_sets) : NULL;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -262,11 +254,8 @@ NCMesh::~NCMesh()
|
||||
DeleteUnusedFaces(elemFaces);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: in release mode, we just throw away all faces and nodes at once
|
||||
#endif
|
||||
|
||||
delete ncent_sets;
|
||||
}
|
||||
|
||||
NCMesh::Node::~Node()
|
||||
@@ -2515,42 +2504,6 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
if (face->index < 0) { face->index = NFaces + (nghosts++); }
|
||||
}
|
||||
MFEM_ASSERT(nghosts == NGhostFaces, "");
|
||||
|
||||
if (ncent_sets)
|
||||
{
|
||||
std::cout << "NCMesh::OnMeshUpdated ncent_sets is non NULL" << std::endl;
|
||||
if (!mesh->ent_sets)
|
||||
{
|
||||
std::cout << "NCMesh::OnMeshUpdated creating ent_sets from NCMesh" << std::endl;
|
||||
mesh->ent_sets = new EntitySets(*mesh, *this);
|
||||
std::cout << "NCMesh::OnMeshUpdated done creating ent_sets from NCMesh" <<
|
||||
std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream ossN;
|
||||
ossN << "node_on_mesh_updated.out";
|
||||
std::ofstream ofsN(ossN.str().c_str());
|
||||
ofsN << nodes.Size() << std::endl;
|
||||
for (int i=0; i<nodes.Size(); i++)
|
||||
{
|
||||
ofsN << i
|
||||
// << " " << nodes[i].vert_refc
|
||||
// << " " << nodes[i].edge_refc
|
||||
<< " " << nodes[i].HasVertex()
|
||||
<< " " << nodes[i].HasEdge()
|
||||
<< " " << nodes[i].vert_index
|
||||
<< " " << nodes[i].edge_index
|
||||
<< " " << nodes[i].p1
|
||||
<< " " << nodes[i].p2
|
||||
<< " " << nodes[i].next << std::endl;
|
||||
}
|
||||
ofsN.close();
|
||||
|
||||
NEdges = mesh->GetNEdges();
|
||||
NFaces = mesh->GetNumFaces();
|
||||
|
||||
std::cout << "Leaving NCMesh::OnMeshUpdated" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -3353,15 +3306,12 @@ const NCMesh::MeshId& NCMesh::NCList::LookUp(int index, int *type) const
|
||||
void NCMesh::CollectEdgeVertices(int v0, int v1, Array<int> &indices)
|
||||
{
|
||||
int mid = nodes.FindId(v0, v1);
|
||||
if (mid >= 0)
|
||||
if (mid >= 0 && nodes[mid].HasVertex())
|
||||
{
|
||||
if (nodes[mid].HasVertex())
|
||||
{
|
||||
indices.Append(mid);
|
||||
indices.Append(mid);
|
||||
|
||||
CollectEdgeVertices(v0, mid, indices);
|
||||
CollectEdgeVertices(mid, v1, indices);
|
||||
}
|
||||
CollectEdgeVertices(v0, mid, indices);
|
||||
CollectEdgeVertices(mid, v1, indices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3423,78 +3373,6 @@ void NCMesh::CollectQuadFaceVertices(int v0, int v1, int v2, int v3,
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::CollectElementVertices(int elem_id, Array<int> &indices)
|
||||
{
|
||||
Element &el = elements[elem_id];
|
||||
|
||||
if (el.ref_type != 0)
|
||||
{
|
||||
// This element has been refined so recurse into its children
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (el.child[i] >= 0 && el.child[i] < elements.Size())
|
||||
{
|
||||
CollectElementVertices(el.child[i], indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This element has not been refined so add its vertices
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
if (el.node[i] >= 0 && el.node[i] < nodes.Size())
|
||||
{
|
||||
indices.Append(el.node[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::CollectElementEdges(int elem_id, Array<int> &indices)
|
||||
{
|
||||
Element &el = elements[elem_id];
|
||||
|
||||
if (el.ref_type != 0)
|
||||
{
|
||||
// This element has been refined so recurse into its children
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (el.child[i] >= 0 && el.child[i] < elements.Size())
|
||||
{
|
||||
CollectElementEdges(el.child[i], indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int* node = el.node;
|
||||
GeomInfo& gi = GI[(int) el.geom];
|
||||
|
||||
for (int i = 0; i < gi.nv; i++)
|
||||
{
|
||||
if (nodes[node[i]].HasEdge())
|
||||
{
|
||||
indices.Append(node[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < gi.ne; i++)
|
||||
{
|
||||
const int* ev = gi.edges[i];
|
||||
int index = nodes.FindId(node[ev[0]], node[ev[1]]);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
if (nodes[index].HasEdge())
|
||||
{
|
||||
indices.Append(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::BuildElementToVertexTable()
|
||||
{
|
||||
int nrows = leaf_elements.Size();
|
||||
@@ -4996,107 +4874,6 @@ int NCMesh::GetElementDepth(int i) const
|
||||
return depth;
|
||||
}
|
||||
|
||||
void NCMesh::GetRefinedEdges(int vn0, int vn1, BlockArray<int> & edges)
|
||||
{
|
||||
std::cout << "entering NCMesh::GetRefinedEdges "
|
||||
<<"searching for edge with vertices: " << vn0 << " and " << vn1
|
||||
<< std::endl;
|
||||
int mid = nodes.FindId(vn0, vn1);
|
||||
if (mid < 0) { return; }
|
||||
|
||||
Node &nd = nodes[mid];
|
||||
|
||||
// if ( nd.edge_index < 0 ) { return; }
|
||||
|
||||
// edges.Append(nd.edge_index);
|
||||
if ( nd.HasEdge() )
|
||||
{
|
||||
std::cout << " found node " << mid << std::endl;
|
||||
edges.Append(mid);
|
||||
}
|
||||
|
||||
GetRefinedEdges(vn0, mid, edges);
|
||||
GetRefinedEdges(mid, vn1, edges);
|
||||
}
|
||||
|
||||
void NCMesh::GetRefinedFaces(int vn0, int vn1, int vn2, int vn3,
|
||||
BlockArray<int> & face_ids)
|
||||
{
|
||||
// Face* fa = faces.Find(vn0, vn1, vn2, vn3);
|
||||
int face = faces.FindId(vn0, vn1, vn2, vn3);
|
||||
/*
|
||||
if (fa)
|
||||
{
|
||||
if ( fa->index >= 0 )
|
||||
{
|
||||
face_ids.Append(fa->index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
*/
|
||||
if (face>=0)
|
||||
{
|
||||
if ( faces[face].index >= 0 )
|
||||
{
|
||||
face_ids.Append(face);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to recurse deeper
|
||||
int mid[4];
|
||||
int split = QuadFaceSplitType(vn0, vn1, vn2, vn3, mid);
|
||||
|
||||
if (split == 1) // "X" split face
|
||||
{
|
||||
GetRefinedFaces(vn0, mid[0], mid[2], vn3, face_ids);
|
||||
GetRefinedFaces(mid[0], vn1, vn2, mid[2], face_ids);
|
||||
}
|
||||
else if (split == 2) // "Y" split face
|
||||
{
|
||||
GetRefinedFaces(vn0, vn1, mid[1], mid[3], face_ids);
|
||||
GetRefinedFaces(mid[3], mid[1], vn2, vn3, face_ids);
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::GetRefinedElements(int elem_id, BlockArray<int> & elem_ids)
|
||||
{
|
||||
// std::cout << "entering NCMesh::GetRefinedElements searching for element id: "
|
||||
// << elem_id << std::endl;
|
||||
Element &el = elements[elem_id];
|
||||
/*
|
||||
if (el.index >= 0 && el.rank >= 0)
|
||||
{
|
||||
elem_ids.Append(el.index);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (el.child[i] >= 0 && el.child[i] < elements.Size() )
|
||||
{
|
||||
GetRefinedElements(el.child[i], elem_ids);
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (el.ref_type != 0)
|
||||
{
|
||||
// This element has been refined so recurse into its children
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (el.child[i] >= 0 && el.child[i] < elements.Size() )
|
||||
{
|
||||
GetRefinedElements(el.child[i], elem_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This element has not been refined so add it
|
||||
elem_ids.Append(elem_id);
|
||||
}
|
||||
}
|
||||
|
||||
int NCMesh::GetElementSizeReduction(int i) const
|
||||
{
|
||||
int elem = leaf_elements[i];
|
||||
@@ -5216,183 +4993,6 @@ void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
bdr_edges.Unique();
|
||||
}
|
||||
|
||||
void NCMesh::GetEntitySetClosure(EntitySets::EntityType type,
|
||||
int set_index,
|
||||
Array<int> &es_vertices,
|
||||
Array<int> &es_edges,
|
||||
Array<int> &es_faces)
|
||||
{
|
||||
es_vertices.SetSize(0);
|
||||
es_edges.SetSize(0);
|
||||
es_faces.SetSize(0);
|
||||
|
||||
MFEM_VERIFY(ncent_sets != NULL, "NCMesh object contains no "
|
||||
"entity set information");
|
||||
if (!ncent_sets->SetExists(type, set_index))
|
||||
{
|
||||
std::ostringstream oss; oss << "Entity set of type \""
|
||||
<< EntitySets::GetTypeName(type)
|
||||
<< "\" and index " << set_index
|
||||
<< " was not found.";
|
||||
|
||||
MFEM_VERIFY(false, oss.str().c_str());
|
||||
}
|
||||
|
||||
int ni = ncent_sets->GetNumEntities(type ,set_index);
|
||||
Array<int> inds;
|
||||
Array<int> coll_inds;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EntitySets::VERTEX:
|
||||
{
|
||||
/// Do nothing because vertices cannot hide
|
||||
}
|
||||
break;
|
||||
case EntitySets::EDGE:
|
||||
{
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
ncent_sets->GetEntityIndex(type, set_index, i, inds);
|
||||
|
||||
// collect vertices
|
||||
inds.Copy(coll_inds);
|
||||
this->CollectEdgeVertices(inds[0], inds[1], coll_inds);
|
||||
for (int j=0; j<coll_inds.Size(); j++)
|
||||
{
|
||||
int index = nodes[coll_inds[j]].vert_index;
|
||||
if (index >= 0)
|
||||
{
|
||||
es_vertices.Append(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EntitySets::FACE:
|
||||
{
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
ncent_sets->GetEntityIndex(type, set_index, i, inds);
|
||||
|
||||
// collect vertices
|
||||
inds.Copy(coll_inds);
|
||||
if (inds.Size() == 4)
|
||||
{
|
||||
this->CollectQuadFaceVertices(inds[0], inds[1], inds[2], inds[3],
|
||||
coll_inds);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->CollectTriFaceVertices(inds[0], inds[1], inds[2],
|
||||
coll_inds);
|
||||
}
|
||||
for (int j=0; j<coll_inds.Size(); j++)
|
||||
{
|
||||
int index = nodes[coll_inds[j]].vert_index;
|
||||
if (index >= 0)
|
||||
{
|
||||
es_vertices.Append(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EntitySets::ELEMENT:
|
||||
{
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
int elem_id = (*ncent_sets)(type, set_index, i);
|
||||
std::cout << "examining element " << elem_id << std::endl;
|
||||
|
||||
// collect vertices
|
||||
coll_inds.SetSize(0);
|
||||
this->CollectElementVertices(elem_id, coll_inds);
|
||||
for (int j=0; j<coll_inds.Size(); j++)
|
||||
{
|
||||
int index = nodes[coll_inds[j]].vert_index;
|
||||
if (index >= 0)
|
||||
{
|
||||
es_vertices.Append(index);
|
||||
}
|
||||
}
|
||||
|
||||
// collect edges
|
||||
coll_inds.SetSize(0);
|
||||
this->CollectElementEdges(elem_id, coll_inds);
|
||||
for (int j=0; j<coll_inds.Size(); j++)
|
||||
{
|
||||
int index = nodes[coll_inds[j]].edge_index;
|
||||
if (index >= 0)
|
||||
{
|
||||
es_edges.Append(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("GetEnitySetClosure - Unknown entity set type: \""
|
||||
<< EntitySets::GetTypeName(type) << "\"");
|
||||
}
|
||||
/*
|
||||
if (Dim == 3)
|
||||
{
|
||||
GetFaceList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int i = 0; i < boundary_faces.Size(); i++)
|
||||
{
|
||||
int face = boundary_faces[i];
|
||||
if (bdr_attr_is_ess[faces[face].attribute - 1])
|
||||
{
|
||||
int node[4];
|
||||
FindFaceNodes(face, node);
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
bdr_vertices.Append(nodes[node[j]].vert_index);
|
||||
|
||||
int enode = nodes.FindId(node[j], node[(j+1) % 4]);
|
||||
MFEM_ASSERT(enode >= 0 && nodes[enode].HasEdge(), "Edge not found.");
|
||||
bdr_edges.Append(nodes[enode].edge_index);
|
||||
|
||||
while ((enode = GetEdgeMaster(enode)) >= 0)
|
||||
{
|
||||
// append master edges that may not be accessible from any
|
||||
// boundary element, this happens in 3D in re-entrant corners
|
||||
bdr_edges.Append(nodes[enode].edge_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
GetEdgeList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int i = 0; i < boundary_faces.Size(); i++)
|
||||
{
|
||||
int face = boundary_faces[i];
|
||||
Face &fc = faces[face];
|
||||
if (bdr_attr_is_ess[fc.attribute - 1])
|
||||
{
|
||||
bdr_vertices.Append(nodes[fc.p1].vert_index);
|
||||
bdr_vertices.Append(nodes[fc.p3].vert_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
es_vertices.Sort();
|
||||
es_vertices.Unique();
|
||||
|
||||
es_edges.Sort();
|
||||
es_edges.Unique();
|
||||
|
||||
es_faces.Sort();
|
||||
es_faces.Unique();
|
||||
}
|
||||
|
||||
static int max4(int a, int b, int c, int d)
|
||||
{
|
||||
return std::max(std::max(a, b), std::max(c, d));
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "../linalg/densemat.hpp"
|
||||
#include "element.hpp"
|
||||
#include "vertex.hpp"
|
||||
#include "entsets.hpp"
|
||||
#include "../fem/geom.hpp"
|
||||
|
||||
#include <vector>
|
||||
@@ -118,9 +117,6 @@ struct MatrixMap; // for internal use
|
||||
*/
|
||||
class NCMesh
|
||||
{
|
||||
friend class EntitySets;
|
||||
friend class NCEntitySets;
|
||||
|
||||
public:
|
||||
//// Initialize with elements from an existing 'mesh'.
|
||||
explicit NCMesh(const Mesh *mesh);
|
||||
@@ -347,16 +343,6 @@ public:
|
||||
Array<int> &bdr_vertices,
|
||||
Array<int> &bdr_edges);
|
||||
|
||||
/** Get a list of vertices (2D/3D), edges (2D/3D), and faces (3D) that
|
||||
coincide with members of the specified entity set. In 3D this function
|
||||
also reveals "hidden" edges or faces. In parallel it helps identifying
|
||||
vertices/edges/faces affected by non-local entities. */
|
||||
virtual void GetEntitySetClosure(EntitySets::EntityType t,
|
||||
int set_index,
|
||||
Array<int> &es_vertices,
|
||||
Array<int> &es_edges,
|
||||
Array<int> &es_faces);
|
||||
|
||||
/// Return element geometry type. @a index is the Mesh element number.
|
||||
Geometry::Type GetElementGeometry(int index) const
|
||||
{ return elements[leaf_elements[index]].Geom(); }
|
||||
@@ -371,19 +357,6 @@ public:
|
||||
/// Return the distance of leaf 'i' from the root.
|
||||
int GetElementDepth(int i) const;
|
||||
|
||||
/** Collect edge indices of all refined edges which are children of
|
||||
the coarse edge defined by the given vertices. */
|
||||
void GetRefinedEdges(int vn0, int vn1, BlockArray<int> & edge_ids);
|
||||
|
||||
/** Collect face indices of all refined faces which are children of
|
||||
the coarse face defined by the given vertices. */
|
||||
void GetRefinedFaces(int vn0, int vn1, int vn2, int vn3,
|
||||
BlockArray<int> & face_ids);
|
||||
|
||||
/** Collect element indices of all refined elements which are children of
|
||||
the coarse element defined by the given element index. */
|
||||
void GetRefinedElements(int elem_id, BlockArray<int> & elem_ids);
|
||||
|
||||
/** Return the size reduction compared to the root element (ignoring local
|
||||
stretching and curvature). */
|
||||
int GetElementSizeReduction(int i) const;
|
||||
@@ -528,7 +501,6 @@ protected: // implementation
|
||||
Array<double> coordinates;
|
||||
|
||||
|
||||
|
||||
// secondary data
|
||||
|
||||
/** Apart from the primary data structure, which is the element/node/face
|
||||
@@ -558,8 +530,6 @@ protected: // implementation
|
||||
|
||||
Table element_vertex; ///< leaf-element to vertex table, see FindSetNeighbors
|
||||
|
||||
// Node/edge/Face/Element sets defined on the coarse mesh
|
||||
NCEntitySets * ncent_sets;
|
||||
|
||||
void UpdateLeafElements();
|
||||
void UpdateVertices(); ///< update Vertex::index and vertex_nodeId
|
||||
@@ -741,10 +711,6 @@ protected: // implementation
|
||||
void CollectTriFaceVertices(int v0, int v1, int v2, Array<int> &indices);
|
||||
void CollectQuadFaceVertices(int v0, int v1, int v2, int v3,
|
||||
Array<int> &indices);
|
||||
void CollectElementVertices(int elem_id, Array<int> &indices);
|
||||
|
||||
void CollectElementEdges(int elem_id, Array<int> &indices);
|
||||
|
||||
void BuildElementToVertexTable();
|
||||
|
||||
void UpdateElementToVertexTable()
|
||||
@@ -960,7 +926,6 @@ public:
|
||||
#endif
|
||||
|
||||
friend class ParNCMesh; // for ParNCMesh::ElementSet
|
||||
friend class ParNCEntitySets;
|
||||
friend struct MatrixMap;
|
||||
friend struct PointMatrixHash;
|
||||
};
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
||||
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
||||
// reserved. See file COPYRIGHT for details.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability see http://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pentsets.hpp"
|
||||
#include "pmesh.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
ParEntitySets::ParEntitySets(const ParEntitySets & ent_sets)
|
||||
: EntitySets(ent_sets),
|
||||
pmesh_(ent_sets.GetParMesh())
|
||||
{
|
||||
MPI_Comm_size(pmesh_->GetComm(), &NRanks_);
|
||||
MPI_Comm_rank(pmesh_->GetComm(), &MyRank_);
|
||||
cout << MyRank_ << ": Entering ParEntitySets copy c'tor" << endl;
|
||||
cout << MyRank_ << ": Leaving ParEntitySets copy c'tor" << endl;
|
||||
}
|
||||
|
||||
ParEntitySets::ParEntitySets(ParMesh & pmesh, const EntitySets & ent_sets,
|
||||
int * partitioning,
|
||||
const Array<int> & vert_global_local)
|
||||
: EntitySets(ent_sets),
|
||||
pmesh_(&pmesh)
|
||||
{
|
||||
// The copy constructor for EntitySets will initialize this object's
|
||||
// data with the correct set names, and numbers of sets. However,
|
||||
// the set entries themselves will need to be recomputed based on
|
||||
// local numberings and the paritioning.
|
||||
//
|
||||
// The EntitySets object will be a copy of the serial object. This
|
||||
// constructor will have to prune and renumber the data. Once this
|
||||
// is done the mesh pointer stored in the EntitySets object can be
|
||||
// replaced with the local portion of the parallel mesh.
|
||||
|
||||
MPI_Comm MyComm = pmesh_->GetComm();
|
||||
|
||||
MPI_Comm_size(MyComm, &NRanks_);
|
||||
MPI_Comm_rank(MyComm, &MyRank_);
|
||||
cout << MyRank_ << ": Entering ParEntitySets(ParMesh, EntitySets, ...) c'tor" <<
|
||||
endl;
|
||||
|
||||
int nelem = mesh_->GetNE();
|
||||
|
||||
DSTable v_to_v(vert_global_local.Size());
|
||||
pmesh_->GetVertexToVertexTable(v_to_v);
|
||||
|
||||
STable3D * faces_tbl = NULL;
|
||||
|
||||
const Table * serial_edge_vertex = NULL;
|
||||
const Table * serial_face_vertex = NULL;
|
||||
|
||||
if ( ent_sets.GetNumSets(EDGE) > 0 )
|
||||
{
|
||||
serial_edge_vertex = ent_sets.GetEdgeVertexTable();
|
||||
}
|
||||
if ( ent_sets.GetNumSets(FACE) > 0 )
|
||||
{
|
||||
serial_face_vertex = ent_sets.GetFaceVertexTable();
|
||||
faces_tbl = pmesh_->GetFacesTable();
|
||||
}
|
||||
|
||||
Array<int> elem_global_local(nelem);
|
||||
elem_global_local = -1;
|
||||
int elem_counter = 0;
|
||||
for (int i=0; i<nelem; i++)
|
||||
{
|
||||
if ( partitioning[i] == MyRank_ )
|
||||
{
|
||||
elem_global_local[i] = elem_counter;
|
||||
elem_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
EntityType t;
|
||||
unsigned int ns;
|
||||
|
||||
t = VERTEX;
|
||||
ns = ent_sets.GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
set<int>::iterator it;
|
||||
sets_[t][s].clear();
|
||||
for (it=ent_sets(t,s).begin(); it!=ent_sets(t,s).end(); it++)
|
||||
{
|
||||
int v0 = vert_global_local[*it];
|
||||
if ( v0 >= 0 )
|
||||
{
|
||||
sets_[t][s].insert(v0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( pmesh_->Dimension() > 1 )
|
||||
{
|
||||
t = EDGE;
|
||||
ns = ent_sets.GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
set<int>::iterator it;
|
||||
sets_[t][s].clear();
|
||||
for (it=ent_sets(t,s).begin(); it!=ent_sets(t,s).end(); it++)
|
||||
{
|
||||
int old_edge = *it;
|
||||
const int *v = serial_edge_vertex->GetRow(old_edge);
|
||||
int v0 = vert_global_local[v[0]];
|
||||
int v1 = vert_global_local[v[1]];
|
||||
if ( v0 >= 0 && v1 >= 0 )
|
||||
{
|
||||
int new_edge = v_to_v(v0,v1);
|
||||
if ( new_edge >= 0 )
|
||||
{
|
||||
sets_[t][s].insert(new_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( pmesh_->Dimension() > 2 )
|
||||
{
|
||||
Array<int> v;
|
||||
t = FACE;
|
||||
ns = ent_sets.GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
set<int>::iterator it;
|
||||
sets_[t][s].clear();
|
||||
for (it=ent_sets(t,s).begin(); it!=ent_sets(t,s).end(); it++)
|
||||
{
|
||||
int old_face = *it;
|
||||
int numv = serial_face_vertex->RowSize(old_face);
|
||||
const int *v = serial_face_vertex->GetRow(old_face);
|
||||
if ( vert_global_local[v[0]] >= 0 &&
|
||||
vert_global_local[v[1]] >= 0 &&
|
||||
vert_global_local[v[2]] >= 0 )
|
||||
{
|
||||
int new_face = -1;
|
||||
if ( numv == 3 )
|
||||
{
|
||||
new_face = (*faces_tbl)(vert_global_local[v[0]],
|
||||
vert_global_local[v[1]],
|
||||
vert_global_local[v[2]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_face = (*faces_tbl)(vert_global_local[v[0]],
|
||||
vert_global_local[v[1]],
|
||||
vert_global_local[v[2]],
|
||||
vert_global_local[v[3]]);
|
||||
}
|
||||
if ( new_face >= 0 )
|
||||
{
|
||||
sets_[t][s].insert(new_face);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete faces_tbl;
|
||||
}
|
||||
|
||||
t = ELEMENT;
|
||||
ns = ent_sets.GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
set<int>::iterator it;
|
||||
sets_[t][s].clear();
|
||||
for (it=ent_sets(t,s).begin(); it!=ent_sets(t,s).end(); it++)
|
||||
{
|
||||
if ( partitioning[*it] == MyRank_ )
|
||||
{
|
||||
sets_[t][s].insert(elem_global_local[*it]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->mesh_ = (Mesh*)this->pmesh_;
|
||||
|
||||
this->CopyMeshTables();
|
||||
cout << MyRank_ << ": Leaving ParEntitySets(ParMesh, EntitySets, ...) c'tor" <<
|
||||
endl;
|
||||
}
|
||||
|
||||
ParEntitySets::ParEntitySets(ParMesh & pmesh, ParNCMesh &pncmesh)
|
||||
: EntitySets(pmesh),
|
||||
pmesh_(&pmesh)
|
||||
{
|
||||
MPI_Comm MyComm = pmesh_->GetComm();
|
||||
|
||||
MPI_Comm_size(MyComm, &NRanks_);
|
||||
MPI_Comm_rank(MyComm, &MyRank_);
|
||||
cout << MyRank_ << ": Entering ParEntitySets(ParMesh, ParNCMesh) c'tor" << endl;
|
||||
|
||||
this->BuildEntitySets(pncmesh, VERTEX);
|
||||
this->BuildEntitySets(pncmesh, EDGE);
|
||||
this->BuildEntitySets(pncmesh, FACE);
|
||||
this->BuildEntitySets(pncmesh, ELEMENT);
|
||||
cout << MyRank_ << ": Leaving ParEntitySets(ParMesh, ParNCMesh) c'tor" << endl;
|
||||
}
|
||||
|
||||
ParEntitySets::~ParEntitySets()
|
||||
{
|
||||
cout << MyRank_ << ": Entering ParEntitySets d'tor" << endl;
|
||||
cout << MyRank_ << ": Leaving ParEntitySets d'tor" << endl;
|
||||
}
|
||||
|
||||
void
|
||||
ParEntitySets::PrintSetInfo(std::ostream & output) const
|
||||
{
|
||||
if ( MyRank_ == 0 &&
|
||||
( GetNumSets(VERTEX) > 0 || GetNumSets(EDGE) > 0 ||
|
||||
GetNumSets(FACE) > 0 || GetNumSets(ELEMENT) > 0 ) )
|
||||
{
|
||||
output << "\nMFEM Parallel Entity Sets:\n";
|
||||
}
|
||||
this->PrintEntitySetInfo(output, VERTEX, "Vertex");
|
||||
this->PrintEntitySetInfo(output, EDGE, "Edge");
|
||||
this->PrintEntitySetInfo(output, FACE, "Face");
|
||||
this->PrintEntitySetInfo(output, ELEMENT, "Element");
|
||||
}
|
||||
|
||||
void
|
||||
ParEntitySets::PrintEntitySetInfo(std::ostream & output, EntityType t,
|
||||
const string & ent_name) const
|
||||
{
|
||||
if ( sets_[t].size() > 0 )
|
||||
{
|
||||
if ( MyRank_ == 0 )
|
||||
{
|
||||
output << " " << ent_name
|
||||
<< " Sets (Index, Set Name, Global Size):\n";
|
||||
}
|
||||
for (unsigned int s=0; s<sets_[t].size(); s++)
|
||||
{
|
||||
int loc_size = sets_[t][s].size();
|
||||
int glb_size = -1;
|
||||
MPI_Reduce(&loc_size, &glb_size, 1, MPI_INT, MPI_SUM, 0,
|
||||
pmesh_->GetComm());
|
||||
if ( MyRank_ == 0 )
|
||||
{
|
||||
output << '\t' << s
|
||||
<< '\t' << set_names_[t][s]
|
||||
<< '\t' << glb_size
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
if ( MyRank_ == 0 )
|
||||
{
|
||||
output << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParEntitySets::BuildEntitySets(ParNCMesh &pncmesh, EntityType t)
|
||||
{
|
||||
cout << MyRank_ << ": BuildEntitySets for type " << GetTypeName(t) << endl;
|
||||
int es = pncmesh.pncent_sets->GetEntitySize(t);
|
||||
unsigned int ns = pncmesh.pncent_sets->GetNumSets(t);
|
||||
cout << MyRank_ << ": num sets " << ns << endl;
|
||||
|
||||
Array<int> inds(es);
|
||||
|
||||
sets_[t].resize(ns);
|
||||
set_names_[t].resize(ns);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
int ni = pncmesh.pncent_sets->GetNumEntities(t, s);
|
||||
set_names_[t][s] = pncmesh.pncent_sets->GetSetName(t, s);
|
||||
set_index_by_name_[t][set_names_[t][s]] = s;
|
||||
|
||||
switch (t)
|
||||
{
|
||||
case VERTEX:
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
int node = (*pncmesh.pncent_sets)(t, s, i);
|
||||
int index = pncmesh.nodes[node].vert_index;
|
||||
if (!pncmesh.IsGhost(0,index))
|
||||
{
|
||||
sets_[t][s].insert(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EDGE:
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
pncmesh.pncent_sets->GetEntityIndex(t, s, i, inds);
|
||||
BlockArray<int> ind_coll;
|
||||
pncmesh.GetRefinedEdges(inds[0], inds[1],
|
||||
ind_coll);
|
||||
|
||||
for (int j=0; j<ind_coll.Size(); j++)
|
||||
{
|
||||
int edge = ind_coll[j];
|
||||
int index = pncmesh.nodes[edge].edge_index;
|
||||
if (index >= 0 && !pncmesh.IsGhost(1, index))
|
||||
{
|
||||
sets_[t][s].insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FACE:
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
pncmesh.pncent_sets->GetEntityIndex(t, s, i, inds);
|
||||
BlockArray<int> ind_coll;
|
||||
pncmesh.GetRefinedFaces(inds[0], inds[1], inds[2], inds[3],
|
||||
ind_coll);
|
||||
|
||||
for (int j=0; j<ind_coll.Size(); j++)
|
||||
{
|
||||
int face = ind_coll[j];
|
||||
int index = pncmesh.faces[face].index;
|
||||
if (index >= 0 && !pncmesh.IsGhost(2, index))
|
||||
{
|
||||
sets_[t][s].insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ELEMENT:
|
||||
for (int i=0; i<ni; i++)
|
||||
{
|
||||
int elem = (*pncmesh.pncent_sets)(t, s, i);
|
||||
BlockArray<int> ind_coll;
|
||||
pncmesh.GetRefinedElements(elem, ind_coll);
|
||||
|
||||
for (int j=0; j<ind_coll.Size(); j++)
|
||||
{
|
||||
sets_[t][s].insert(pncmesh.elements[ind_coll[j]].index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Unknown entity set type: \"" << GetTypeName(t) << "\"");
|
||||
}
|
||||
cout << MyRank_ << ": " << set_names_[t][s] << " " << s << " set size " <<
|
||||
sets_[t][s].size() << "{";
|
||||
for (set<int>::iterator it=sets_[t][s].begin(); it!=sets_[t][s].end(); it++)
|
||||
{
|
||||
cout << " " << *it;
|
||||
}
|
||||
cout << "}" << endl;
|
||||
}
|
||||
map<string,int>::iterator it;
|
||||
cout << MyRank_ << ": set index by name ";
|
||||
for (it=set_index_by_name_[t].begin(); it != set_index_by_name_[t].end(); it++)
|
||||
{
|
||||
cout << " " << it->first << "->" << it->second;
|
||||
}
|
||||
cout << endl;
|
||||
cout << MyRank_ << ": done BuildEntitySets for type " << GetTypeName(t) << endl;
|
||||
}
|
||||
|
||||
ParNCEntitySets::ParNCEntitySets(MPI_Comm comm, const NCMesh &ncmesh)
|
||||
: NCEntitySets(*ncmesh.ncent_sets)
|
||||
{
|
||||
MyComm_ = comm;
|
||||
MPI_Comm_size(MyComm_, &NRanks_);
|
||||
MPI_Comm_rank(MyComm_, &MyRank_);
|
||||
|
||||
if ( MyRank_ == 0 )
|
||||
{
|
||||
cout << "Entering ParNCEntitySets(NCMesh) c'tor" << endl;
|
||||
}
|
||||
|
||||
if ( MyRank_ == 0 )
|
||||
{
|
||||
cout << "Leaving ParNCEntitySets(NCMesh) c'tor" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
||||
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
||||
// reserved. See file COPYRIGHT for details.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability see http://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#ifndef MFEM_PAR_ENTITY_SETS
|
||||
#define MFEM_PAR_ENTITY_SETS
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "entsets.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ParMesh;
|
||||
class ParNCMesh;
|
||||
|
||||
class ParEntitySets : public EntitySets
|
||||
{
|
||||
friend class ParMesh;
|
||||
|
||||
public:
|
||||
ParEntitySets(const ParEntitySets & ent_sets);
|
||||
ParEntitySets(ParMesh & _mesh, const EntitySets & ent_sets, int * part,
|
||||
const Array<int> & vert_global_local);
|
||||
ParEntitySets(ParMesh & mesh, ParNCMesh &ncmesh);
|
||||
|
||||
virtual ~ParEntitySets();
|
||||
|
||||
virtual void PrintSetInfo(std::ostream &output) const;
|
||||
|
||||
inline ParMesh *GetParMesh() const { return pmesh_; }
|
||||
|
||||
private:
|
||||
|
||||
void PrintEntitySetInfo(std::ostream & output, EntityType t,
|
||||
const std::string & ent_name) const;
|
||||
|
||||
void BuildEntitySets(ParNCMesh &pncmesh, EntityType t);
|
||||
|
||||
ParMesh * pmesh_;
|
||||
int NRanks_;
|
||||
int MyRank_;
|
||||
};
|
||||
|
||||
class ParNCEntitySets : public NCEntitySets
|
||||
{
|
||||
public:
|
||||
// ParNCEntitySets(MPI_Comm comm, EntitySets &ent_sets, NCMesh &ncmesh);
|
||||
ParNCEntitySets(MPI_Comm comm, const NCMesh &ncmesh);
|
||||
// ParNCEntitySets(const ParMesh & pmesh, const ParNCMesh &pncmesh);
|
||||
// ParNCEntitySets(const ParNCEntitySets & pncent_sets);
|
||||
|
||||
private:
|
||||
MPI_Comm MyComm_;
|
||||
int NRanks_;
|
||||
int MyRank_;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#endif // MFEM_PAR_ENTITY_SETS
|
||||
+3
-37
@@ -91,10 +91,6 @@ ParMesh::ParMesh(const ParMesh &pmesh, bool copy_nodes)
|
||||
*Nodes = *pmesh.Nodes;
|
||||
own_nodes = 1;
|
||||
}
|
||||
|
||||
// Copy entity sets if present in the input mesh
|
||||
ent_sets = pent_sets =
|
||||
(pmesh.pent_sets) ? new ParEntitySets(*pmesh.pent_sets) : NULL;
|
||||
}
|
||||
|
||||
ParMesh::ParMesh(ParMesh &&mesh) : ParMesh()
|
||||
@@ -114,7 +110,6 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
, glob_elem_offset(-1)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(comm)
|
||||
, pent_sets(NULL)
|
||||
{
|
||||
int *partitioning = NULL;
|
||||
Array<bool> activeBdrElem;
|
||||
@@ -123,8 +118,6 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
MPI_Comm_size(MyComm, &NRanks);
|
||||
MPI_Comm_rank(MyComm, &MyRank);
|
||||
|
||||
Array<int> vert_global_local;
|
||||
|
||||
if (mesh.Nonconforming())
|
||||
{
|
||||
if (partitioning_)
|
||||
@@ -155,10 +148,6 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
mesh.bdr_attributes.Copy(bdr_attributes);
|
||||
|
||||
GenerateNCFaceInfo();
|
||||
|
||||
// if (mesh.ent_sets)
|
||||
// NumOfVertices = BuildLocalVertices(mesh, partitioning,
|
||||
// vert_global_local);
|
||||
}
|
||||
else // mesh.Conforming()
|
||||
{
|
||||
@@ -179,6 +168,7 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
// re-enumerate the partitions to better map to actual processor
|
||||
// interconnect topology !?
|
||||
|
||||
Array<int> vert_global_local;
|
||||
NumOfVertices = BuildLocalVertices(mesh, partitioning, vert_global_local);
|
||||
NumOfElements = BuildLocalElements(mesh, partitioning, vert_global_local);
|
||||
|
||||
@@ -250,12 +240,6 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
|
||||
SetMeshGen();
|
||||
meshgen = mesh.meshgen; // copy the global 'meshgen'
|
||||
|
||||
ent_sets = pent_sets =
|
||||
(mesh.ent_sets) ? new ParEntitySets(*this, *mesh.ent_sets,
|
||||
partitioning,
|
||||
vert_global_local)
|
||||
: NULL;
|
||||
}
|
||||
|
||||
if (mesh.NURBSext)
|
||||
@@ -305,12 +289,7 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
// for compatibility (e.g., Mesh::GetVertex())
|
||||
SetVerticesFromNodes(Nodes);
|
||||
}
|
||||
/*
|
||||
ent_sets = pent_sets =
|
||||
(mesh.ent_sets) ? new ParEntitySets(*this, *mesh.ent_sets,
|
||||
partitioning,
|
||||
vert_global_local) : NULL;
|
||||
*/
|
||||
|
||||
if (partitioning != partitioning_)
|
||||
{
|
||||
delete [] partitioning;
|
||||
@@ -880,7 +859,6 @@ ParMesh::ParMesh(const ParNCMesh &pncmesh)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(MyComm)
|
||||
, pncmesh(NULL)
|
||||
, pent_sets(NULL)
|
||||
{
|
||||
Mesh::InitFromNCMesh(pncmesh);
|
||||
ReduceMeshGen();
|
||||
@@ -947,7 +925,6 @@ ParMesh::ParMesh(MPI_Comm comm, istream &input, bool refine)
|
||||
, glob_elem_offset(-1)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(comm)
|
||||
, pent_sets(NULL)
|
||||
{
|
||||
MyComm = comm;
|
||||
MPI_Comm_size(MyComm, &NRanks);
|
||||
@@ -1162,8 +1139,7 @@ void ParMesh::MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type)
|
||||
gtopo = orig_mesh.gtopo;
|
||||
have_face_nbr_data = false;
|
||||
pncmesh = NULL;
|
||||
pent_sets = NULL;
|
||||
|
||||
|
||||
Array<int> ref_factors(orig_mesh.GetNE());
|
||||
ref_factors = ref_factor;
|
||||
Mesh::MakeRefined_(orig_mesh, ref_factors, ref_type);
|
||||
@@ -3792,13 +3768,6 @@ void ParMesh::NonconformingRefinement(const Array<Refinement> &refinements,
|
||||
// and this mesh will be the new fine mesh
|
||||
Mesh::Swap(*pmesh2, false);
|
||||
|
||||
// swap entity set information if present
|
||||
mfem::Swap(pmesh2->pent_sets, this->pent_sets);
|
||||
if (this->pent_sets)
|
||||
{
|
||||
this->pent_sets->pmesh_ = this;
|
||||
}
|
||||
|
||||
delete pmesh2; // NOTE: old face neighbors destroyed here
|
||||
|
||||
pncmesh->GetConformingSharedStructures(*this);
|
||||
@@ -6202,9 +6171,6 @@ void ParMesh::Destroy()
|
||||
delete pncmesh;
|
||||
ncmesh = pncmesh = NULL;
|
||||
|
||||
delete pent_sets;
|
||||
ent_sets = pent_sets = NULL;
|
||||
|
||||
DeleteFaceNbrData();
|
||||
|
||||
for (int i = 0; i < shared_edges.Size(); i++)
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "../general/globals.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "pncmesh.hpp"
|
||||
#include "pentsets.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace mfem
|
||||
@@ -321,7 +320,6 @@ public:
|
||||
Table send_face_nbr_vertices;
|
||||
|
||||
ParNCMesh* pncmesh;
|
||||
ParEntitySets* pent_sets;
|
||||
|
||||
int GetNGroups() const { return gtopo.NGroups(); }
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#include <map>
|
||||
#include <climits> // INT_MIN, INT_MAX
|
||||
|
||||
#include <fstream> // MLS Debugging
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -29,7 +27,6 @@ using namespace bin_io;
|
||||
|
||||
ParNCMesh::ParNCMesh(MPI_Comm comm, const NCMesh &ncmesh, int *part)
|
||||
: NCMesh(ncmesh)
|
||||
, pncent_sets(NULL)
|
||||
{
|
||||
MyComm = comm;
|
||||
MPI_Comm_size(MyComm, &NRanks);
|
||||
@@ -44,40 +41,6 @@ ParNCMesh::ParNCMesh(MPI_Comm comm, const NCMesh &ncmesh, int *part)
|
||||
|
||||
Update();
|
||||
|
||||
std::ostringstream oss; oss << "elements_" << MyRank << ".out";
|
||||
std::ofstream ofs(oss.str().c_str());
|
||||
|
||||
for (int i=0; i<elements.Size(); i++)
|
||||
{
|
||||
ofs << i
|
||||
<< '\t' << elements[i].index
|
||||
<< '\t' << elements[i].rank
|
||||
<< '\t' << elements[i].attribute
|
||||
<< '\t' << elements[i].parent;
|
||||
if ( elements[i].ref_type == 0 )
|
||||
{
|
||||
ofs << " nodes {";
|
||||
for (int j=0; j<8; j++)
|
||||
{
|
||||
ofs << " " << elements[i].node[j];
|
||||
}
|
||||
ofs << "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
ofs << " children {";
|
||||
for (int j=0; j<8; j++)
|
||||
{
|
||||
ofs << " " << elements[i].child[j];
|
||||
}
|
||||
ofs << "}";
|
||||
}
|
||||
ofs << std::endl;
|
||||
}
|
||||
|
||||
ncent_sets = pncent_sets =
|
||||
(ncmesh.ncent_sets) ? new ParNCEntitySets(comm, ncmesh) : NULL;
|
||||
|
||||
// note that at this point all processors still have all the leaf elements;
|
||||
// we however may now start pruning the refinement tree to get rid of
|
||||
// branches that only contain someone else's leaves (see Prune())
|
||||
@@ -122,9 +85,6 @@ ParNCMesh::ParNCMesh(const ParNCMesh &other)
|
||||
ParNCMesh::~ParNCMesh()
|
||||
{
|
||||
ClearAuxPM();
|
||||
|
||||
delete pncent_sets;
|
||||
ncent_sets = pncent_sets = NULL;
|
||||
}
|
||||
|
||||
void ParNCMesh::Update()
|
||||
@@ -155,386 +115,6 @@ void ParNCMesh::Update()
|
||||
boundary_layer.SetSize(0);
|
||||
}
|
||||
|
||||
/*
|
||||
void ParNCMesh::AssignLeafIndices()
|
||||
{
|
||||
// This is an override of NCMesh::AssignLeafIndices(). The difference is
|
||||
// that we shift all elements we own to the beginning of the array
|
||||
// 'leaf_elements' and assign all ghost elements indices >= NElements.
|
||||
|
||||
// Also note that the ordering of ghosts and non-ghosts is preserved here,
|
||||
// which is important for ParNCMesh::GetFaceNeighbors.
|
||||
|
||||
// We store the original leaf ordering in 'leaf_glob_order'. This is later
|
||||
// used (and deleted) in GetConformingSharedStructures
|
||||
|
||||
NCMesh::AssignLeafIndices(); // original numbering, for 'leaf_glob_order'
|
||||
|
||||
int nleafs = leaf_elements.Size();
|
||||
|
||||
Array<int> ghosts;
|
||||
ghosts.Reserve(nleafs);
|
||||
|
||||
NElements = 0;
|
||||
for (int i = 0; i < nleafs; i++)
|
||||
{
|
||||
int elem = leaf_elements[i];
|
||||
if (elements[elem].rank == MyRank)
|
||||
{
|
||||
leaf_elements[NElements++] = elem;
|
||||
}
|
||||
else
|
||||
{
|
||||
ghosts.Append(elem);
|
||||
}
|
||||
}
|
||||
NGhostElements = ghosts.Size();
|
||||
|
||||
leaf_elements.SetSize(NElements);
|
||||
leaf_elements.Append(ghosts);
|
||||
|
||||
// store original (globally consistent) numbering in 'leaf_glob_order'
|
||||
leaf_glob_order.SetSize(nleafs);
|
||||
for (int i = 0; i < nleafs; i++)
|
||||
{
|
||||
leaf_glob_order[i] = elements[leaf_elements[i]].index;
|
||||
}
|
||||
|
||||
// new numbering with ghost shifted to the back
|
||||
NCMesh::AssignLeafIndices();
|
||||
}
|
||||
|
||||
void ParNCMesh::UpdateVertices()
|
||||
{
|
||||
// This is an override of NCMesh::UpdateVertices. This version first
|
||||
// assigns vert_index to vertices of elements of our rank. Only these
|
||||
// vertices then make it to the Mesh in NCMesh::GetMeshComponents.
|
||||
// The remaining (ghost) vertices are assigned indices greater or equal to
|
||||
// Mesh::GetNV().
|
||||
|
||||
for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
if (node->HasVertex()) { node->vert_index = -1; }
|
||||
}
|
||||
|
||||
NVertices = 0;
|
||||
for (int i = 0; i < leaf_elements.Size(); i++)
|
||||
{
|
||||
Element &el = elements[leaf_elements[i]];
|
||||
if (el.rank == MyRank)
|
||||
{
|
||||
for (int j = 0; j < GI[el.Geom()].nv; j++)
|
||||
{
|
||||
int &vindex = nodes[el.node[j]].vert_index;
|
||||
if (vindex < 0) { vindex = NVertices++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertex_nodeId.SetSize(NVertices);
|
||||
for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
if (node->HasVertex() && node->vert_index >= 0)
|
||||
{
|
||||
vertex_nodeId[node->vert_index] = node.index();
|
||||
}
|
||||
}
|
||||
|
||||
NGhostVertices = 0;
|
||||
for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
if (node->HasVertex() && node->vert_index < 0)
|
||||
{
|
||||
node->vert_index = NVertices + (NGhostVertices++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParNCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
{
|
||||
std::cout << MyRank << ": Entering ParNCMesh::OnMeshUpdated" << std::endl;
|
||||
// This is an override (or extension of) NCMesh::OnMeshUpdated().
|
||||
// In addition to getting edge/face indices from 'mesh', we also
|
||||
// assign indices to ghost edges/faces that don't exist in the 'mesh'.
|
||||
|
||||
// clear edge_index and Face::index
|
||||
for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
if (node->HasEdge()) { node->edge_index = -1; }
|
||||
}
|
||||
for (face_iterator face = faces.begin(); face != faces.end(); ++face)
|
||||
{
|
||||
face->index = -1;
|
||||
}
|
||||
|
||||
// go assign existing edge/face indices
|
||||
NCMesh::OnMeshUpdated(mesh);
|
||||
|
||||
std::cout << MyRank << ": NVertices = " << NVertices << std::endl;
|
||||
|
||||
std::ostringstream ossN;
|
||||
ossN << "node_on_mesh_updated_" << MyRank << ".out";
|
||||
std::ofstream ofsN(ossN.str().c_str());
|
||||
ofsN << nodes.Size() << std::endl;
|
||||
for (int i=0; i<nodes.Size(); i++)
|
||||
{
|
||||
ofsN << i
|
||||
// << " " << nodes[i].vert_refc
|
||||
// << " " << nodes[i].edge_refc
|
||||
<< " " << nodes[i].HasVertex()
|
||||
<< " " << nodes[i].HasEdge()
|
||||
<< " " << nodes[i].vert_index
|
||||
<< " " << nodes[i].edge_index
|
||||
<< " " << nodes[i].p1
|
||||
<< " " << nodes[i].p2
|
||||
<< " " << nodes[i].next << std::endl;
|
||||
}
|
||||
ofsN.close();
|
||||
|
||||
// count ghost edges and assign their indices
|
||||
NEdges = mesh->GetNEdges();
|
||||
NGhostEdges = 0;
|
||||
for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
if (node->HasEdge() && node->edge_index < 0)
|
||||
{
|
||||
node->edge_index = NEdges + (NGhostEdges++);
|
||||
}
|
||||
}
|
||||
|
||||
// count ghost faces
|
||||
NFaces = mesh->GetNumFaces();
|
||||
NGhostFaces = 0;
|
||||
for (face_iterator face = faces.begin(); face != faces.end(); ++face)
|
||||
{
|
||||
if (face->index < 0) { NGhostFaces++; }
|
||||
}
|
||||
|
||||
if (Dim == 2)
|
||||
{
|
||||
// in 2D we have fake faces because of DG
|
||||
MFEM_ASSERT(NFaces == NEdges, "");
|
||||
MFEM_ASSERT(NGhostFaces == NGhostEdges, "");
|
||||
}
|
||||
|
||||
// resize face_geom (default_geom is for slave faces beyond the ghost layer)
|
||||
Geometry::Type default_geom = Geometry::SQUARE;
|
||||
face_geom.SetSize(NFaces + NGhostFaces, default_geom);
|
||||
|
||||
// update 'face_geom' for ghost faces, assign ghost face indices
|
||||
int nghosts = 0;
|
||||
for (int i = 0; i < NGhostElements; i++)
|
||||
{
|
||||
Element &el = elements[leaf_elements[NElements + i]]; // ghost element
|
||||
GeomInfo &gi = GI[el.Geom()];
|
||||
|
||||
for (int j = 0; j < gi.nf; j++)
|
||||
{
|
||||
const int *fv = gi.faces[j];
|
||||
Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
|
||||
el.node[fv[2]], el.node[fv[3]]);
|
||||
MFEM_ASSERT(face, "face not found!");
|
||||
|
||||
if (face->index < 0)
|
||||
{
|
||||
face->index = NFaces + (nghosts++);
|
||||
|
||||
// store the face geometry
|
||||
static const Geometry::Type types[5] =
|
||||
{
|
||||
Geometry::INVALID, Geometry::INVALID,
|
||||
Geometry::SEGMENT, Geometry::TRIANGLE, Geometry::SQUARE
|
||||
};
|
||||
face_geom[face->index] = types[gi.nfv[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign valid indices also to faces beyond the ghost layer
|
||||
for (face_iterator face = faces.begin(); face != faces.end(); ++face)
|
||||
{
|
||||
if (face->index < 0) { face->index = NFaces + (nghosts++); }
|
||||
}
|
||||
MFEM_ASSERT(nghosts == NGhostFaces, "");
|
||||
|
||||
{
|
||||
/// Debugging output
|
||||
std::ostringstream oss; oss << "elements_on_mesh_updated_"
|
||||
<< MyRank << ".out";
|
||||
std::ofstream ofs(oss.str().c_str());
|
||||
|
||||
for (int i=0; i<elements.Size(); i++)
|
||||
{
|
||||
ofs << i
|
||||
<< '\t' << elements[i].index
|
||||
<< '\t' << elements[i].rank
|
||||
<< '\t' << elements[i].attribute
|
||||
<< '\t' << elements[i].parent;
|
||||
if ( elements[i].ref_type == 0 )
|
||||
{
|
||||
ofs << " nodes {";
|
||||
for (int j=0; j<8; j++)
|
||||
{
|
||||
ofs << " " << elements[i].node[j];
|
||||
}
|
||||
ofs << "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
ofs << " children {";
|
||||
for (int j=0; j<8; j++)
|
||||
{
|
||||
ofs << " " << elements[i].child[j];
|
||||
}
|
||||
ofs << "}";
|
||||
}
|
||||
ofs << std::endl;
|
||||
}
|
||||
|
||||
if (pncent_sets)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated pncent_sets is non NULL" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated pncent_sets is NULL" << std::endl;
|
||||
}
|
||||
if (ncent_sets)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated ncent_sets is non NULL" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated ncent_sets is NULL" << std::endl;
|
||||
}
|
||||
if (mesh->ent_sets)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated mesh->ent_sets is non NULL" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated mesh->ent_sets is NULL" << std::endl;
|
||||
}
|
||||
ParMesh * pmesh = dynamic_cast<ParMesh*>(mesh);
|
||||
if (pmesh)
|
||||
{
|
||||
std::cout << "dynamic cast succeeded: mesh is a ParMesh" << std::endl;
|
||||
|
||||
if (pmesh->pent_sets != NULL)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated deleting ParEntitySets object in ParMesh"
|
||||
<< std::endl;
|
||||
delete pmesh->pent_sets;
|
||||
}
|
||||
else if (pmesh->ent_sets != NULL)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated deleting EntitySets object in ParMesh" <<
|
||||
std::endl;
|
||||
delete pmesh->ent_sets;
|
||||
}
|
||||
std::cout << "ParNCMesh::OnMeshUpdated creating ParEntitySets object in ParMesh"
|
||||
<< std::endl;
|
||||
pmesh->ent_sets = pmesh->pent_sets =
|
||||
(pncent_sets) ? new ParEntitySets(*pmesh, *this): NULL;
|
||||
*/
|
||||
/*
|
||||
if (pmesh->ent_sets)
|
||||
{
|
||||
std::cout << MyRank << ": ParNCMesh::OnMeshUpdated pmesh->ent_sets is non NULL" << std::endl;
|
||||
pmesh->ent_sets->PrintSetInfo(std::cout);
|
||||
|
||||
std::ostringstream oss; oss << "ent_sets_" << MyRank << ".out";
|
||||
std::ofstream ofs(oss.str().c_str());
|
||||
pmesh->ent_sets->Print(ofs);
|
||||
MPI_Barrier(MyComm);
|
||||
|
||||
std::cout << MyRank << ": testing " << NElements << std::endl;
|
||||
//pmesh->ent_sets->Prune(NElements);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated pmesh->ent_sets is NULL" << std::endl;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (pmesh->pent_sets)
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated pmesh->pent_sets is non NULL" <<
|
||||
std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ParNCMesh::OnMeshUpdated pmesh->pent_sets is NULL" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "dynamic cast failed: mesh is not a ParMesh" << std::endl;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (pncent_sets)
|
||||
{
|
||||
if (!pmesh->pent_sets)
|
||||
{
|
||||
pmesh->pent_sets = new ParEntitySets(*pmesh, *this);
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
// Prune the Entity Sets
|
||||
if ( entity_sets )
|
||||
{
|
||||
EntitySets::EntityType t = EntitySets::INVALID;
|
||||
unsigned int ns = -1;
|
||||
|
||||
std::cout << "Processing node sets" << std::endl;
|
||||
|
||||
t = EntitySets::VERTEX;
|
||||
ns = entity_sets->GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
unsigned int ni = entity_sets->GetNumEntities(t, s);
|
||||
int e = 0;
|
||||
for (unsigned int i=0; i<ni; i++)
|
||||
{
|
||||
if ( (*mesh->ent_sets)(t, s, i) < NVertices )
|
||||
{
|
||||
(*mesh->ent_sets)(t, s, e) = (*mesh->ent_sets)(t, s, i);
|
||||
e++;
|
||||
}
|
||||
}
|
||||
(*mesh->ent_sets)(t, s).resize(e);
|
||||
}
|
||||
|
||||
t = EntitySets::EDGE;
|
||||
ns = entity_sets->GetNumSets(t);
|
||||
for (unsigned int s=0; s<ns; s++)
|
||||
{
|
||||
unsigned int ni = entity_sets->GetNumEntities(t, s);
|
||||
BlockArray<int> ids;
|
||||
|
||||
for (unsigned int i=0; i<ni; i++)
|
||||
{
|
||||
if ( (*mesh->ent_sets)(t, s, i) < NEdges )
|
||||
{
|
||||
ids.Append((*mesh->ent_sets)(t, s, i));
|
||||
}
|
||||
}
|
||||
(*mesh->ent_sets)(t, s).resize(ids.Size());
|
||||
for (int i=0; i<ids.Size(); i++)
|
||||
{
|
||||
(*mesh->ent_sets)(t, s, i) = ids[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
std::cout << MyRank << ": Leaving ParNCMesh::OnMeshUpdated" << std::endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void ParNCMesh::ElementSharesFace(int elem, int local, int face)
|
||||
{
|
||||
// Analogous to ElementSharesEdge.
|
||||
@@ -3152,94 +2732,6 @@ void ParNCMesh::GetDebugMesh(Mesh &debug_mesh) const
|
||||
debug_mesh.ncmesh = copy;
|
||||
}
|
||||
|
||||
void ParNCMesh::GetRefinedEdges(int vn0, int vn1, BlockArray<int> & edges)
|
||||
{
|
||||
std::cout << MyRank
|
||||
<< ": entering ParNCMesh::GetRefinedEdges "
|
||||
<<"searching for edge with vertices: " << vn0 << " and " << vn1
|
||||
<< std::endl;
|
||||
return this->NCMesh::GetRefinedEdges(vn0, vn1, edges);
|
||||
|
||||
int mid = nodes.FindId(vn0, vn1);
|
||||
if (mid < 0) { return; }
|
||||
|
||||
/*
|
||||
Node &nd = nodes[mid];
|
||||
|
||||
if ( nd.edge_index < 0 ) { return; }
|
||||
|
||||
edges.Append(nd.edge_index);
|
||||
|
||||
GetRefinedEdges(vn0, mid, edges);
|
||||
GetRefinedEdges(mid, vn1, edges);
|
||||
*/
|
||||
edges.Append(mid);
|
||||
|
||||
GetRefinedEdges(vn0, mid, edges);
|
||||
GetRefinedEdges(mid, vn1, edges);
|
||||
}
|
||||
|
||||
void ParNCMesh::GetRefinedFaces(int vn0, int vn1, int vn2, int vn3,
|
||||
BlockArray<int> & face_ids)
|
||||
{
|
||||
return this->NCMesh::GetRefinedFaces(vn0, vn1, vn2, vn3, face_ids);
|
||||
/*
|
||||
Face* fa = faces.Find(vn0, vn1, vn2, vn3);
|
||||
|
||||
if (fa)
|
||||
{
|
||||
if ( fa->index >= 0 )
|
||||
{
|
||||
face_ids.Append(fa->index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to recurse deeper
|
||||
int mid[4];
|
||||
int split = FaceSplitType(vn0, vn1, vn2, vn3, mid);
|
||||
|
||||
if (split == 1) // "X" split face
|
||||
{
|
||||
GetRefinedFaces(vn0, mid[0], mid[2], vn3, face_ids);
|
||||
GetRefinedFaces(mid[0], vn1, vn2, mid[2], face_ids);
|
||||
}
|
||||
else if (split == 2) // "Y" split face
|
||||
{
|
||||
GetRefinedFaces(vn0, vn1, mid[1], mid[3], face_ids);
|
||||
GetRefinedFaces(mid[3], mid[1], vn2, vn3, face_ids);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void ParNCMesh::GetRefinedElements(int elem_id, BlockArray<int> & elem_ids)
|
||||
{
|
||||
// std::cout << MyRank
|
||||
// << ": entering ParNCMesh::GetRefinedElements "
|
||||
// <<"searching for element id: " << elem_id << std::endl;
|
||||
Element &el = elements[elem_id];
|
||||
|
||||
if (el.ref_type != 0)
|
||||
{
|
||||
// This element has been refined so recurse into its children
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (el.child[i] >= 0 && el.child[i] < elements.Size() )
|
||||
{
|
||||
GetRefinedElements(el.child[i], elem_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This element has not been refined so add it if it's a local element
|
||||
if (el.rank == MyRank)
|
||||
{
|
||||
elem_ids.Append(elem_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParNCMesh::Trim()
|
||||
{
|
||||
NCMesh::Trim();
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <set>
|
||||
|
||||
#include "ncmesh.hpp"
|
||||
#include "pentsets.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
#include "../general/sort_pairs.hpp"
|
||||
|
||||
@@ -249,29 +248,9 @@ public:
|
||||
The debug mesh will have element attributes set to element rank + 1. */
|
||||
void GetDebugMesh(Mesh &debug_mesh) const;
|
||||
|
||||
/** Collect edge indices of all refined edges which are children of
|
||||
the coarse edge defined by the given vertices. This method
|
||||
overrides a method in NCMesh and only returns locally owned
|
||||
edges. */
|
||||
void GetRefinedEdges(int vn0, int vn1, BlockArray<int> & edge_ids);
|
||||
|
||||
/** Collect face indices of all refined faces which are children of
|
||||
the coarse face defined by the given vertices. This method
|
||||
overrides a method in NCMesh and only returns locally owned
|
||||
faces. */
|
||||
void GetRefinedFaces(int vn0, int vn1, int vn2, int vn3,
|
||||
BlockArray<int> & face_ids);
|
||||
|
||||
/** Collect element indices of all refined elements which are
|
||||
children of the coarse element defined by the given element
|
||||
index. This method overrides a method in NCMesh and only
|
||||
returns locally owned elements. */
|
||||
void GetRefinedElements(int elem_id, BlockArray<int> & elem_ids);
|
||||
|
||||
protected: // interface for ParMesh
|
||||
|
||||
friend class ParMesh;
|
||||
friend class ParEntitySets;
|
||||
|
||||
/** For compatibility with conforming code in ParMesh and ParFESpace.
|
||||
Initializes shared structures in ParMesh: gtopo, shared_*, group_s*, s*_l*.
|
||||
@@ -561,8 +540,6 @@ protected: // implementation
|
||||
Array<DenseMatrix*> aux_pm_store;
|
||||
void ClearAuxPM();
|
||||
|
||||
ParNCEntitySets * pncent_sets;
|
||||
|
||||
long GroupsMemoryUsage() const;
|
||||
|
||||
friend class NeighborRowMessage;
|
||||
|
||||
@@ -29,5 +29,4 @@ add_subdirectory(gslib)
|
||||
add_subdirectory(solvers)
|
||||
add_subdirectory(shifted)
|
||||
add_subdirectory(mtop)
|
||||
add_subdirectory(autodiff)
|
||||
add_subdirectory(parelag)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# 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 ()
|
||||
@@ -1,712 +0,0 @@
|
||||
// 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
|
||||
@@ -1,876 +0,0 @@
|
||||
// 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
|
||||
@@ -1,650 +0,0 @@
|
||||
// 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
|
||||
@@ -1,79 +0,0 @@
|
||||
# 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*
|
||||
@@ -1,553 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user