Compare commits

..
Author SHA1 Message Date
Joseph Signorelli 86ba47cbb9 style 2024-03-17 18:20:51 -05:00
Joseph Signorelli 04ea231d3b Backward compatibility for SymmetricMatrixCoefficient 2024-03-17 18:20:22 -05:00
Joseph Signorelli 0aaa0b4353 Prevent overloaded-virtual warning 2024-03-17 17:53:32 -05:00
Joseph Signorelli fd8b529c11 sundials + petsc examples changes propagated through 2024-03-17 17:45:48 -05:00
Joseph Signorelli 94b5f2dad8 style 2024-03-14 18:18:32 -05:00
Joseph Signorelli 6de569ce93 Backward compatibility for codes w/ custom coeffs 2024-03-14 18:17:41 -05:00
Joseph Signorelli 13d4976b3e Merge branch 'master' into constcoeff-dev 2024-03-14 15:33:13 -05:00
Joseph Signorelli 596923be2c Propogate changes through miniapps 2024-03-05 17:03:12 -06:00
Joseph Signorelli 8d612acd5e Propogate changes through examples 2024-03-05 16:42:05 -06:00
Joseph Signorelli 094542ac8b Propogate changes through fem + mesh
- Make const and add `mutable` to appropriate member vars
2024-03-05 16:41:28 -06:00
Joseph Signorelli 841a546bfa Make VectorSumCoefficient member variables mutable to ensure backward compatibility
Not clean but there may be situations where one calls GetAlpha, GetBeta, GetA, or GetB specifically after an Eval call to get those variables at a given point (when ACoeff,BCoeff, AlphaCoeff, BetaCoeff exist).

An alternative option is to use separate auxiliary variables for coefficient evaluations instead.
2024-03-05 16:23:46 -06:00
Joseph Signorelli af29df2b4d *Make access to internal coefficients + member function const-ness consistent
**This may be a breaking change for existing codes. Ex:

```
const SumCoefficient* coeff = ....;
Coefficient* a_coeff = coeff->GetACoef(); // Cannot do this anymore unless a_coeff is also const
```

GetACoef was previously declared const, so it makes more sense to not allow non-const calls to ACoef when this is called.
2024-03-05 16:18:07 -06:00
Joseph Signorelli 88d43f0b97 Add SetTime for VectorFunctionCoefficient 2024-03-05 16:03:00 -06:00
Joseph Signorelli 316572693a Remove any calls setting internal coefficient times' in Eval
These internal coefficients' times' should already have been set in SetTime for each given class, so it is not necessary
2024-03-05 16:00:46 -06:00
Joseph Signorelli f8335862ac Appropriate member functions of coefficients made const
- Eval, Project, + more specific ones
2024-03-05 15:49:58 -06:00
771 changed files with 20463 additions and 58283 deletions
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
- name: GHCR Login
if: (github.event_name != 'pull_request')
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
+9 -36
View File
@@ -33,7 +33,6 @@ env:
HYPRE_ARCHIVE: v2.19.0.tar.gz
HYPRE_TOP_DIR: hypre-2.19.0
METIS_ARCHIVE: metis-4.0.3.tar.gz
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
METIS_TOP_DIR: metis-4.0.3
MFEM_TOP_DIR: mfem
@@ -53,7 +52,6 @@ jobs:
mpi: [seq, par]
build-system: [make, cmake]
hypre-target: [int32]
precision: [fp64]
exclude:
- os: ubuntu-latest
build-system: cmake
@@ -77,8 +75,6 @@ jobs:
- os: ubuntu-latest
target: dbg
config-opts: 'CPPFLAGS+=-Og'
- os: macos-latest
codecov: NO
- os: windows-latest
codecov: NO
- os: windows-latest
@@ -91,7 +87,6 @@ jobs:
mpi: par
build-system: cmake
hypre-target: int32
precision: fp64
# This option can be set to pass additional configuration options to
# the MFEM configuration command.
# config-opts: '-DCMAKE_VERBOSE_MAKEFILE=ON'
@@ -101,15 +96,7 @@ jobs:
mpi: par
build-system: make
hypre-target: int64
precision: fp64
- os: ubuntu-latest
target: opt
codecov: NO
mpi: par
build-system: make
hypre-target: int32
precision: fp32
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
runs-on: ${{ matrix.os }}
@@ -139,17 +126,6 @@ jobs:
# Fetch the complete history for codecov to access commits ID
fetch-depth: 0
- name: Xcode version setup (MacOS)
if: matrix.os == 'macos-latest'
run: |
XCODE_PATH="/Applications/Xcode_15.3.app"
echo "> sudo xcode-select -s ${XCODE_PATH}"
sudo xcode-select -s ${XCODE_PATH}
echo "> g++ -v"
g++ -v
echo "> clang++ -v"
clang++ -v
# Only get MPI if defined for the job.
# TODO: It would be nice to have only one step, e.g. with a dedicated
# action, but I (@adrienbernede) don't see how at the moment.
@@ -193,27 +169,25 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-v2.2
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.4
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: ${{ matrix.hypre-target }}
build-system: make
precision: ${{ matrix.precision }}
- name: get hypre (Windows)
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.4
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: ${{ matrix.hypre-target }}
build-system: cmake
precision: ${{ matrix.precision }}
# Get Metis through cache, or build it.
# Install will only run on cache miss.
@@ -223,13 +197,13 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.2
- name: install metis
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
uses: mfem/github-actions/build-metis@v2.4
with:
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
- name: cache vcpkg (Windows)
@@ -254,7 +228,7 @@ jobs:
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.5
uses: mfem/github-actions/build-mfem@v2.4
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
with:
@@ -266,7 +240,6 @@ jobs:
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
metis-dir: ${{ env.METIS_TOP_DIR }}
mfem-dir: ${{ env.MFEM_TOP_DIR }}
precision: ${{ matrix.precision }}
config-options: ${{ matrix.config-opts }}
library-only: ${{ matrix.target == 'dbg' && matrix.os != 'ubuntu-latest' }}
@@ -309,7 +282,7 @@ jobs:
# Code coverage (process and upload reports)
- name: codecov
if: matrix.codecov == 'YES'
uses: mfem/github-actions/upload-coverage@v2.5
uses: mfem/github-actions/upload-coverage@v2.4
with:
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
project_dir: ${{ env.MFEM_TOP_DIR }}
+5 -5
View File
@@ -53,11 +53,11 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.5
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.2
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.4
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -68,18 +68,18 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.2
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
uses: mfem/github-actions/build-metis@v2.4
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@v2.5
uses: mfem/github-actions/build-mfem@v2.4
with:
os: ${{ runner.os }}
target: opt
+1 -7
View File
@@ -27,12 +27,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Temporary workaround for sanitizer crashes
# See https://github.com/actions/runner-images/issues/9491
# The issue should be fixed in the next runner image for Ubuntu 22.04,
# see https://github.com/actions/runner-images/pull/9513
run: sudo sysctl vm.mmap_rnd_bits=28
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.12.1
with:
@@ -44,7 +38,7 @@ jobs:
path: mfem
- name: MFEM Build
uses: mfem/github-actions/build-mfem@v2.5
uses: mfem/github-actions/build-mfem@v2.4
with:
os: ${{ runner.os }}
target: opt
+5 -20
View File
@@ -57,8 +57,6 @@ examples/ex2[0-9]
examples/ex2[0-9]p
examples/ex3[0-9]
examples/ex3[0-9]p
examples/ex4[0-9]
examples/ex4[0-9]p
examples/refined.mesh
examples/displaced.mesh
@@ -93,6 +91,10 @@ examples/ex16.mesh
examples/ex16-mesh.*
examples/ex16-init.*
examples/ex16-final.*
examples/vortex-mesh.*
examples/vortex.mesh
examples/vortex-?-init.*
examples/vortex-?-final.*
examples/deformation.*
examples/pressure.*
examples/ex20.dat
@@ -118,8 +120,6 @@ examples/cond_mesh.*
examples/port_mesh.*
examples/port_mode.*
examples/euler-*
examples/amgx/ex1
examples/amgx/ex1p
examples/amgx/.logamgx
@@ -234,7 +234,7 @@ miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
miniapps/meshing/twist-*.mesh
miniapps/meshing/mesh-explorer.mesh*
miniapps/meshing/mesh-explorer.mesh
miniapps/meshing/partitioning.txt
miniapps/meshing/mesh-explorer-visit*
miniapps/meshing/mesh-explorer-paraview/
@@ -272,27 +272,16 @@ miniapps/navier/*_output
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex3
miniapps/nurbs/nurbs_ex5
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/nurbs_ex24
miniapps/nurbs/nurbs_solenoidal
miniapps/nurbs/nurbs_printfunc
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol_?.gf
miniapps/nurbs/sol.*
miniapps/nurbs/mode_*
miniapps/nurbs/Example1*
miniapps/nurbs/Example3*
miniapps/nurbs/Example5*
miniapps/nurbs/Solenoidal*
miniapps/nurbs/ParaView
miniapps/nurbs/sin-fit.mesh
miniapps/nurbs/ex5.mesh
miniapps/nurbs/exsol.mesh
miniapps/nurbs/CurveInt
miniapps/nurbs/nurbs_naca_cmesh
miniapps/nurbs/naca-cmesh.mesh
@@ -365,8 +354,6 @@ miniapps/parelag/MultilevelHcurlHdivSolver
miniapps/parelag/*.mesh
miniapps/multidomain/multidomain
miniapps/multidomain/multidomain_nd
miniapps/multidomain/multidomain_rt
miniapps/hooke/hooke
miniapps/dpg/diffusion
@@ -382,8 +369,6 @@ miniapps/dpg/ParaView
miniapps/spde/generate_random_field
miniapps/spde/ParaView
miniapps/tribol/contact-patch-test
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
+5 -8
View File
@@ -13,16 +13,13 @@
# at Lawrence Livermore National Laboratory (LLNL). This entire pipeline is
# LLNL-specific!
include:
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# The pipeline is divided into stages. Usually, jobs in a given stage wait for
# the preceding stages to complete before to start. However, we sometimes use
# the "needs" keyword and express the DAG of jobs for more efficiency.
# - We use setup and setup_baseline phases to download content outside of mfem
# directory.
# - Allocate/Release is where ruby resource are allocated/released once for all.
# - Allocate/Release is where quartz resource are allocated/released once for all.
# - Build and Test is where we build and MFEM for multiple toolchains.
# - Baseline_checks gathers baseline-type test suites execution
# - Baseline_publish, only available on master, allows to update baseline
@@ -53,7 +50,7 @@ variables:
AUTOTEST_COMMIT: "YES"
# Trigger subpipelines:
ruby-build-and-test:
quartz-build-and-test:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -61,10 +58,10 @@ ruby-build-and-test:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/ruby-build-and-test.yml
include: .gitlab/quartz-build-and-test.yml
strategy: depend
ruby-baseline:
quartz-baseline:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -73,7 +70,7 @@ ruby-baseline:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/ruby-baseline.yml
include: .gitlab/quartz-baseline.yml
strategy: depend
lassen-build-and-test:
+3 -3
View File
@@ -24,7 +24,7 @@ and `test type`.
Machines typically include:
* Ruby: 2nd Gen Intel Xeon (Cascade Lake)
* Quartz: Intel bi-socket x86
* Lassen: Power9 + Nvidia GPU
* Corona: AMD GPU
@@ -76,13 +76,13 @@ with a spack spec of MFEM, within the limits permitted by the MFEM spack
package.
In any build-and-test sub-pipeline a job basically consists in defining the
spack spec to use. Adding a job on ruby for example resumes to:
spack spec to use. Adding a job on quartz for example resumes to:
```yaml
<job_name>:
variables:
SPEC: "<spack_spec>"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
```
The remaining and non trivial work is to make sure this spec is working. To
+1 -5
View File
@@ -9,10 +9,6 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
include:
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# We define the following GitLab pipeline variables:
variables:
@@ -24,7 +20,7 @@ variables:
# TODO: add a clean-up mechanism
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
# On LLNL's ruby, there is only one allocation shared among jobs in order to
# 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}
+4 -3
View File
@@ -35,8 +35,9 @@ variables:
- when: on_success
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
# pre-allocation the same way slurm does. We use the pci queue on lassen
# to speed-up the allocation.
# pre-allocation the same way slurm does. We use pdebug queue on lassen
# to speed-up the allocation. However this would not be scalable to
# multiple builds.
.build_and_test_on_lassen:
extends: [.on_lassen]
stage: build_and_test
@@ -44,5 +45,5 @@ variables:
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
# Next script uses 'THREADS': leaving it empty --> it uses 'make all -j'
- lalloc 1 -W 45 -q pci --atsdisable tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
- lalloc 1 -W 45 -q pdebug --atsdisable tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
needs: [setup]
@@ -9,17 +9,17 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipelines configurations for the Ruby machine at LLNL
# GitLab pipelines configurations for the Quartz machine at LLNL
variables:
MACHINE_NAME: ruby
MACHINE_NAME: quartz
.on_ruby:
.on_quartz:
tags:
- shell
- ruby
- quartz
rules:
# Don't run ruby jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_RUBY == "OFF"'
# Don't run quartz jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
@@ -40,16 +40,16 @@ variables:
- when: on_success
# Spack helped builds
# Generic ruby build job, extending build script
.build_and_test_on_ruby:
extends: [.on_ruby]
# Generic quartz build job, extending build script
.build_and_test_on_quartz:
extends: [.on_quartz]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
- export THREADS=16
- 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}" ) --reservation=ci -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
+1 -1
View File
@@ -18,7 +18,7 @@
setup_baseline:
tags:
- shell
- ruby
- quartz
stage: setup
variables:
GIT_STRATEGY: none
+1 -1
View File
@@ -16,7 +16,7 @@
setup:
tags:
- shell
- ruby
- quartz
stage: setup
variables:
GIT_STRATEGY: none
+4 -4
View File
@@ -14,14 +14,14 @@ stages:
- build_and_test
- report
opt_mpi_cuda_gcc:
opt_mpi_cuda_xl_16_1_1_12:
variables:
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70"
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70"
extends: .build_and_test_on_lassen
opt_mpi_cuda_hypre_cuda_gcc:
opt_mpi_cuda_hypre_cuda_xl:
variables:
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
extends: .build_and_test_on_lassen
# Jobs report
@@ -19,8 +19,8 @@ stages:
- cleanup
- baseline_publish
baselinecheck_mfem_intel_ruby:
extends: [.on_ruby]
baselinecheck_mfem_intel_quartz:
extends: [.on_quartz]
stage: baseline_check
variables:
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
@@ -32,7 +32,7 @@ baselinecheck_mfem_intel_ruby:
- echo ${BUILD_ROOT}
- echo ${TPLS_DIR}
# Used by the tests in MFEM/tests:
- export MFEM_TEST_NP=48
- 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
@@ -44,16 +44,18 @@ baselinecheck_mfem_intel_ruby:
allow_failure: true
cleanup:
extends: .on_ruby
extends: .on_quartz
stage: cleanup
variables:
GIT_STRATEGY: none
script:
- echo "BUILD_ROOT=${BUILD_ROOT}"
- rm -rf "${BUILD_ROOT}" || true
- echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}"
- make -C "${CI_PROJECT_DIR}" distclean
report_baseline:
extends: [.on_ruby]
extends: [.on_quartz]
stage: baseline_report
script:
- echo ${MACHINE_NAME}
@@ -85,7 +87,7 @@ report_baseline:
# We create an autotest-email.html file, because that's how we signal
# that there was an error / diff (temporary).
if [[ -f ${rundir}/${BASELINE_TEST}.err ]] || \
[[ -f ${rundir}/${BASELINE_TEST}-${MACHINE_NAME}.diff ]]; then
[[ -f ${rundir}/${BASELINE_TEST}-${SYS_TYPE}.diff ]]; then
cp ${rundir}/pipeline.txt ${rundir}/autotest-email.html
fi
msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
@@ -113,8 +115,8 @@ report_baseline:
exit $err
) 9> autotest.lock
baselinepublish_mfem_ruby:
extends: [.on_ruby]
baselinepublish_mfem_quartz:
extends: [.on_quartz]
stage: baseline_publish
rules:
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
@@ -129,5 +131,5 @@ baselinepublish_mfem_ruby:
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/quartz-config.yml
- local: .gitlab/configs/setup-baseline.yml
@@ -19,54 +19,54 @@ stages:
allocate_resource:
variables:
GIT_STRATEGY: none
extends: .on_ruby
extends: .on_quartz
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --reservation=ci --time=60 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
# GitLab jobs for the Ruby machine at LLNL
# GitLab jobs for the Quartz machine at LLNL
debug_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
debug_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
opt_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 ~mpi"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
opt_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
opt_par_gcc_10_sundials:
variables:
SPEC: "%gcc@10.3.1 +sundials"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
opt_par_gcc_10_petsc:
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
opt_par_gcc_10_pumi:
variables:
SPEC: "%gcc@10.3.1 +pumi"
extends: .build_and_test_on_ruby
extends: .build_and_test_on_quartz
# Release
release_resource:
variables:
GIT_STRATEGY: none
extends: .on_ruby
extends: .on_quartz
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
@@ -78,17 +78,17 @@ release_resource:
report_job_success:
stage: release_resource_and_report
extends:
- .on_ruby
- .on_quartz
- .report_job_success
report_job_failure:
stage: release_resource_and_report
extends:
- .on_ruby
- .on_quartz
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/quartz-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+4 -7
View File
@@ -14,9 +14,6 @@
# locals
glob_err=${BASELINE_TEST}.err
base=${BASELINE_TEST}-${SYS_TYPE}
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
base="${BASELINE_TEST}-${MACHINE_NAME}"
fi
base_diff=${base}.diff
base_patch=${base}.patch
base_out=${base}.out
@@ -31,21 +28,21 @@ cd tests
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
if [[ "${MACHINE_NAME}" == "quartz" || "${MACHINE_NAME}" == "ruby" ]]; then
salloc --nodes=1 -p pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
lalloc 1 -q pci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
else
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
exit 1
fi
status="$?"
# post
mkdir ${artifacts_path}
status=0
if [[ -f ${BASELINE_TEST}.out ]]; then
cp ${BASELINE_TEST}.out ${artifacts_path}
fi
+2 -2
View File
@@ -11,7 +11,7 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# There will be collision between corona and ruby baselines.
# There will be collision between corona and quartz baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}
@@ -21,7 +21,7 @@ PATCH_FILE=${ARTIFACT_PATH}.patch
FULL_FILE=${ARTIFACT_PATH}.out
DIFF_FILE=${ARTIFACT_PATH}.diff
# There will be collision between corona and ruby baselines.
# There will be collision between corona and quartz baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
SAVED_NAME=baseline-${SYS_TYPE}.saved
+27 -122
View File
@@ -8,151 +8,53 @@
https://mfem.org
Version 4.7.1 (development)
Version 4.6.1 (development)
===========================
Discretization improvements
---------------------------
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Only on single
patch meshes. Only implemented for serial computations.
- Added support for boundary constraints to the hybridization class.
Meshing improvements
--------------------
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
are also supported.
New and updated examples and miniapps
-------------------------------------
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
- Added an MFEM example for the eikonal equation. This new solver is based on
the proximal Galerkin method introduced by Keith and Surowiec.
GPU computing
-------------
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
Miscellaneous
-------------
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
`TimeDependentOperator::Mult` only when the associated ODE operator is
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
otherwise `TimeDependentOperator::ExplicitMult` is used. A check has been
added to `ARKStepSolver` to verify that the associated ODE operator is not in
explicit form when a mass matrix solver is enabled via a call to either the
`UseMFEMMassLinearSolver` or `UseSundialsMassLinearSolver` methods. This is
because enabling a mass matrix solver assumes that F(u,k,t) = M k in the
associated ODE operator.
- Added support for custom interpolation procedure in FindPointsGSLIB.
API changes
-----------
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
Version 4.7, released on May 7, 2024
====================================
- Added support for single precision (with corresponding hypre build). The MFEM
floating point type was generalized from `double` to `real_t`. For details see
https://github.com/orgs/mfem/discussions/4207.
Meshing improvements
--------------------
- Added the capability to partition (big) serial meshes in serial code, see the
new classes MeshPartitioner and MeshPart. This capability is also exposed as a
menu option in the mesh-explorer miniapp in miniapps/meshing.
- Added named attribute sets and basic supporting methods to the Mesh class as a
convenient means of referring to sets of domain or boundary attribute numbers.
See the new Example 39/39p and data/compass.mesh.
- Introduced formulas for refinement of patches in NURBS meshes. Refinement by
arbitrary integer factors is also enabled, e.g. in the mesh-explorer miniapp.
NURBS coarsening and knot removal are also introduced.
- Added support for internal boundary elements in nonconforming meshes.
- Added ExodusII output capability. The writer can handle first-order (Pyramid5,
Wedge6, Hex8, Tet4) and second-order FE types (Pyramid14, Wedge18, Hex27, Tet10).
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
Discretization improvements
---------------------------
- Added a new nonlinear integrator, `HyperbolicFormIntegrator` that implements
both element-wise weak divergence and face-wise numerical flux for a general
system of hyperbolic conservation laws. To use the integrator for a specific
flux function, users can define a derived class of `FluxFunction`. Currently,
advection, Burgers, shallow-water and Euler equations (see Example 18/18p) are
available.
- Introduced support for higher order non conformal Nedelec elements on
simplices in ParMesh.
- Introduced support for internal boundary elements in nonconformal adapted
meshes.
- Added a capability to construct cut-surface and cut-volume IntegrationRules
through a moment-fitting approach. The cut is specified by the zero level set
of a Coefficient. See fem/intrules_cut.hpp and the new Example 38.
- Introduced support for high-order nonconforming Nedelec elements on simplices.
GPU computing
-------------
- Added partial assembly and GPU support for the DG diffusion integrator.
- Efficient GPU-accelerated LOR assembly is now supported on surface meshes.
- Added functionality to automatically configure hypre's compute policy to match
MFEM's compute policy when hypre is built with GPU support. Requires version
hypre-2.31.0 or later.
- Added functionality for construction of cut-surface and cut-volume
IntegrationRules through a moment-fitting approach. The cut is specified by
the zero level set of a Coefficient. See fem/intrules_cut.hpp and Example 38.
GPU support
----------------------------
- Added support for full assembly on simplices.
- Added partial assembly for linear elasticity (no sum factorization for now).
- Added functionality for BilinearFormIntegrators to use kernels that work for
both tensor and unstructured elements.
- The RAJA backend will use `seq_exec` for serial loop execution when RAJA
v2023.06.00 and beyond is detected as `loop_exec` is deprecated.
- API change: The macro MFEM_HYPRE_FORALL (from hypre.hpp) which was intended
for internal use, has been removed and replaced by the function template
mfem::hypre_forall in general/forall.hpp.
- Added functionality for BilinearFormIntegrators to use kernels that work for both
tensor and unstructured elements.
- Added partial assembly for linear elasticity. Does not use sum factorization for now.
New and updated examples and miniapps
-------------------------------------
- Added a new miniapp illustrating elastic contact based on the Tribol library,
(https://github.com/LLNL/Tribol). See miniapps/tribol.
- Added a new block solver in miniapp/solvers for the Darcy problem.
The new solver is based on a Bramble-Pasciak preconditioning. User can
use and implement their own preconditioner for the mass matrix.
- Added a miniapp to demonstrate low order refined (LOR) block preconditioning
for linear elasticity on GPUs. See miniapps/solvers/lor_elast.
- Added a new block solver in miniapp/solvers for the Darcy problem. The new
solver is based on a Bramble-Pasciak preconditioning. User can use and
implement their own preconditioner for the mass matrix.
- Added a small miniapp for printing the shape functions of a KnotVector. See
miniapps/nurbs/nurbs_printfunc.cpp.
- Added two new example codes: 38 and 39/39p described above. Substantially
updated Example 18/18p.
- Added miniapp to demonstrate new elasticity integrator and unstructured element GPU support,
and a block diagonal preconditioner using low order refinement. Allows comparison with
currently existing legacy mode integrator. See miniapps/solvers/lor_elast.
Miscellaneous
-------------
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
- Updated the Doxygen documentation style, which now requires Doxygen version
1.9.8 or later. See the doc/ directory.
- Improved thread safety for global variables in the library, e.g. for IntRules,
RefinedIntRules, GlobGeometryRefiner, and FiniteElement::dof2quad_array.
- PETSc integration now generally requires PETSc version 3.21 or later, though
depending on the functionality older versions may still work.
- Various other simplifications, extensions, and bugfixes in the code.
- Added GSLIB-based gather-scatter operator.
- Improved thread safety for global variables in the library, for example
IntegrationRules IntRules, RefinedIntRules, GeometryRefiner
GlobGeometryRefiner, and FiniteElement::dof2quad_array.
Version 4.6, released on September 27, 2023
@@ -174,6 +76,7 @@ Meshing improvements
* The edge to knot map for NURBS meshes can be determined automatically. It is
no longer needed to specify this in the NURBS mesh.
* Added curve interpolation method for NURBS.
* Added new small miniapp for printing of shape functions of a KnotVector
* See miniapps/nurbs for example meshes and miniapps.
Discretization improvements
@@ -220,6 +123,8 @@ Linear and nonlinear solvers
- Added HIP support to the PETSc and SUNDIALS interfaces.
- Efficient GPU-accelerated LOR assembly now supports surface meshes.
New and updated examples and miniapps
-------------------------------------
- Added a new H(div) solver miniapp demonstrating the use of a matrix-free
+7 -46
View File
@@ -58,7 +58,7 @@ project(mfem NONE)
# Current version of MFEM, see also `makefile`.
# mfem_VERSION = (string)
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
set(${PROJECT_NAME}_VERSION 4.7.1)
set(${PROJECT_NAME}_VERSION 4.6.1)
# Prohibit in-source build
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
@@ -87,11 +87,10 @@ if (MFEM_USE_STRUMPACK OR MFEM_USE_MUMPS)
# Just needed to find the MPI_Fortran libraries to link with
set(XSDK_ENABLE_Fortran ON)
endif()
# SUNDIALS, STRUMPACK, Ginkgo, Tribol, RAJA and Umpire require C++14:
# SUNDIALS, STRUMPACK, Ginkgo, RAJA and Umpire require C++14:
if ((MFEM_USE_SUNDIALS OR
MFEM_USE_STRUMPACK OR
MFEM_USE_GINKGO OR
MFEM_USE_TRIBOL OR
MFEM_USE_RAJA OR
MFEM_USE_UMPIRE) AND
("${CMAKE_CXX_STANDARD}" LESS "14"))
@@ -146,9 +145,7 @@ if (MFEM_USE_CUDA)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
find_package(CUDAToolkit REQUIRED)
set(CUSPARSE_FOUND TRUE)
set(CUBLAS_FOUND TRUE)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
endif()
if (XSDK_ENABLE_C)
@@ -186,19 +183,6 @@ endif()
# Process configuration options
#-------------------------------------------------------------------------------
# MFEM_PRECISION -> MFEM_USE_SINGLE, MFEM_USE_DOUBLE
if (MFEM_PRECISION MATCHES "^(double|Double|DOUBLE)$")
set(MFEM_USE_SINGLE OFF)
set(MFEM_USE_DOUBLE ON)
elseif (MFEM_PRECISION MATCHES "^(single|Single|SINGLE)$")
set(MFEM_USE_SINGLE ON)
set(MFEM_USE_DOUBLE OFF)
else()
message(FATAL_ERROR " *** Invalid floating-point precision: "
"MFEM_PRECISION = ${MFEM_PRECISION}")
endif()
message(STATUS "Floating-point precision: MFEM_PRECISION = ${MFEM_PRECISION}")
# MFEM_DEBUG
if (CMAKE_BUILD_TYPE MATCHES "Debug|debug|DEBUG")
set(MFEM_DEBUG ON)
@@ -233,7 +217,6 @@ if (MFEM_USE_HIP)
list(INSERT CMAKE_PREFIX_PATH 0 ${ROCM_PATH})
endif()
find_package(HIP REQUIRED)
find_package(HIPBLAS REQUIRED)
find_package(HIPSPARSE REQUIRED)
endif()
@@ -399,10 +382,6 @@ if (MFEM_USE_AMGX)
find_package(AMGX REQUIRED)
endif()
if (MFEM_USE_MAGMA)
find_package(MAGMA REQUIRED)
endif()
if (MFEM_USE_CONDUIT)
find_package(Conduit REQUIRED conduit relay blueprint)
endif()
@@ -511,21 +490,9 @@ if (MFEM_USE_PARELAG)
find_package(PARELAG REQUIRED)
endif()
# Tribol
if (MFEM_USE_TRIBOL)
if (MFEM_USE_MPI)
find_package(Tribol REQUIRED tribol redecomp)
else()
message(FATAL_ERROR " *** Tribol requires that MPI be enabled.")
endif()
endif()
# Enzyme
if (MFEM_USE_ENZYME)
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
set(ENZYME_FOUND 1)
find_package(ENZYME REQUIRED)
endif()
# MFEM_TIMER_TYPE
@@ -567,9 +534,8 @@ find_package(Threads REQUIRED)
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
ALGOIM ENZYME)
ADIOS2 CUSPARSE MKL_CPARDISO MKL_PARDISO AMGX CALIPER CODIPACK
BENCHMARK PARELAG MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM ENZYME)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
@@ -632,11 +598,6 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
if (MFEM_USE_ENZYME)
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
endif()
if (MINGW)
target_link_libraries(mfem PRIVATE ws2_32)
endif()
@@ -689,7 +650,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_BINARY_DIR}/${Header}.tmp"
"${PROJECT_BINARY_DIR}/${Header}"
)
@@ -703,7 +664,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
#include \"mfem/${Header}\"
")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
)
+1 -4
View File
@@ -151,8 +151,7 @@ The MFEM source code has the following structure:
│ ├── solvers
│ ├── spde
│ ├── tools
── toys
│ └── tribol
── toys
└── tests
├── benchmarks
├── convergence
@@ -363,8 +362,6 @@ Before you can start, you need a GitHub account, here are a few suggestions:
conflicted files in the commit message.
- All significant new features and changes should be documented in CHANGELOG.
- New examples and miniapps should have documentation on the MFEM webpage.
- The general floating-point type `real_t` should be used, rather than
`float` or `double`, except in special cases where only one is possible.
### Pull Requests
+8 -51
View File
@@ -75,8 +75,6 @@ and miniapps. See https://glvis.org and https://mfem.org/building.
Quick start with GNU make
=========================
See also: https://mfem.org/building
Serial build:
make serial -j 4
@@ -85,7 +83,6 @@ Parallel build:
(build METIS 4 in ../metis-4.0 relative to mfem/)
(build hypre in ../hypre relative to mfem/)
make parallel -j 4
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
CUDA build:
make cuda -j 4
@@ -119,7 +116,6 @@ Parallel build:
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
make -j 4
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
CUDA build:
(this build requires CMake 3.8 or newer)
@@ -273,13 +269,7 @@ Installation options:
PREFIX - Specify the installation directory. The library (libmfem.a) will be
installed in $(PREFIX)/lib, the headers in $(PREFIX)/include, and
the configuration makefile (config.mk) in $(PREFIX)/share/mfem.
INSTALL - Specify the install program, default = /usr/bin/install
INSTALL_DEF_PERM - Specify the default install permissions. This affects
headers and configuration makefiles, default = 644
INSTALL_BIN_PERM - Specify the install permissions for binaries. This only
affects the shared version of the library, default = 755
INSTALL_DIR_PERM - Specify the install permissions for directories and,
on macOS/BSD, for symlinks as well, default = 755
INSTALL - Specify the install program, e.g /usr/bin/install
MFEM library features/options (GNU make)
----------------------------------------
@@ -294,15 +284,6 @@ MFEM_USE_METIS = YES/NO
option in the library will be Cartesian partitioning with box meshes, and
thus most of the parallel examples and miniapps will fail.
MFEM_PRECISION = double/Double/DOUBLE/single/Single/SINGLE
Use single (float type) or double floating-point precision. In the
configuration header 'config/_config.hpp' this option is represented by
defining exactly one of the macros: MFEM_USE_DOUBLE, or MFEM_USE_SINGLE.
In the exported config files 'config.mk' and 'MFEMConfig.cmake', the option
is represented by the variables MFEM_USE_DOUBLE and MFEM_USE_SINGLE defined
as YES/NO (make) or ON/OFF (cmake). For more details see
https://github.com/orgs/mfem/discussions/4207
MFEM_DEBUG = YES/NO
Choose debug/optimized build. The debug build enables a number of messages
and consistency checks that may simplify bug-hunting.
@@ -394,11 +375,6 @@ MFEM_USE_AMGX = YES/NO
Allows the user to use SparseMatrices and HypreParMatrices to solve linear
systems with the routines from the AmgX library.
MFEM_USE_MAGMA = YES/NO
Enable MFEM functionality based on the MAGMA high-performance linear algebra
library. The MAGMA library provides a BLAS/LAPACK interface, with
implementations that have been optimized for Nvidia and AMD GPUs.
MFEM_USE_GNUTLS = YES/NO
Enable secure socket support in class socketstream, using the auxiliary
GnuTLS_* classes, based on the GnuTLS library. This option may be useful in
@@ -586,11 +562,6 @@ MFEM_USE_PARELAG = YES/NO
use ParELAG. In fact, ParELAG is dependent on MFEM. Therefore, this option
currently only concerns the miniapps.
MFEM_USE_TRIBOL = YES/NO
Enables the miniapps that use the Tribol library. MFEM does not currently
use Tribol. In fact, Tribol is dependent on MFEM. Therefore, this option
currently only concerns the miniapps.
MFEM_USE_ENZYME = YES/NO
Enables automatic differentiation support through the LLVM plugin Enzyme.
This requires the compiler to be set to clang (>=14.0.0). We also advise to
@@ -627,13 +598,9 @@ The specific libraries and their options are:
HYPRE >= 2.20.0 (HYPRE built with '--enable-mixedint')
HYPRE >= 2.22.1 (HYPRE built with CUDA)
HYPRE >= 2.23.0 (HYPRE built with HIP)
HYPRE >= 2.31.0 (runtime selectable HYPRE execution on CPU/GPU)
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
MFEM_USE_METIS_5 = YES (default is to use METIS 4). For building instructions,
see the following:
- METIS 4.0.3: https://mfem.org/building/#parallel-mpi-version-of-mfem
- METIS 5.1.0: https://mfem.org/building/#parallel-build-using-metis-5
MFEM_USE_METIS_5 = YES (default is to use METIS 4).
URL: https://github.com/mfem/tpls (MFEM mirror, see above)
Options: METIS_OPT, METIS_LIB.
Versions: METIS 4.0.3 or 5.1.0.
@@ -710,11 +677,6 @@ The specific libraries and their options are:
Options: AMGX_OPT, AMGX_LIB.
Versions: AmgX >= 2.1, older versions may work too.
- MAGMA (optional), used with MFEM_USE_MAGMA = YES.
URL: https://icl.utk.edu/magma/
Options: MAGMA_OPT, MAGMA_LIB
Versions: MAGMA >= 2.8.0
- GnuTLS (optional), used when MFEM_USE_GNUTLS = YES. On most Linux systems,
GnuTLS is available as a development package, e.g. gnutls-devel. On Mac OS X,
one can get the library through the Homebrew package manager (http://brew.sh).
@@ -730,10 +692,9 @@ The specific libraries and their options are:
Options: NETCDF_OPT, NETCDF_LIB.
Versions: NetCDF >= 4.4.0.
- PETSc (optional), used when MFEM_USE_PETSC = YES. Version 3.21 or higher of
the PETSC dev branch is required, though depending on the functionality older
versions may work too. The MFEM and PETSc builds can share common libraries,
e.g., hypre and SUNDIALS. Here's an example configuration, assuming
- PETSc (optional), used when MFEM_USE_PETSC = YES. Version 3.8 or higher of
the PETSC dev branch is required. The MFEM and PETSc builds can share common
libraries, e.g., hypre and SUNDIALS. Here's an example configuration, assuming
PETSc has been cloned on the same level as mfem and hypre:
./configure --download-fblaslapack=yes --download-scalapack=yes \
--download-mumps=yes --download-suitesparse=yes \
@@ -743,7 +704,9 @@ The specific libraries and their options are:
CFLAGS to allow proper parsing of the hipsparse header under C.
URL: https://www.mcs.anl.gov/petsc
Options: PETSC_OPT, PETSC_LIB.
Versions: PETSc >= 3.21.0, older versions may work too.
Versions: PETSc >= 3.8.0 (PETSc build without CUDA/HIP)
PETSc >= 3.15.0 (PETSc built with CUDA)
PETSc >= 3.19.0 (PETSc built with HIP, older versions may work too)
- SLEPc (optional), used when MFEM_USE_SLEPC = YES. SLEPc depends on PETSc and
uses some of the PETSc options when compiled.
@@ -886,10 +849,6 @@ The specific libraries and their options are:
URL: https://github.com/LLNL/parelag
Options: PARELAG_DIR, PARELAG_OPT, PARELAG_LIB.
- Tribol, used when MFEM_USE_TRIBOL = YES.
URL: https://github.com/LLNL/Tribol
Options: TRIBOL_DIR, TRIBOL_OPT, TRIBOL_LIB.
- Enzyme, used when MFEM_USE_ENZYME = YES. Requires LLVM/Clang >= 14.0.0.
URL: https://github.com/EnzymeAD/Enzyme
Options: ENZYME_DIR, ENZYME_OPT, ENZYME_LIB.
@@ -1000,7 +959,6 @@ The following options are equivalent to the GNU make options with the same name:
MFEM_USE_MPI
MFEM_USE_METIS - Set to ${MFEM_USE_MPI}, can be overwritten.
MFEM_PRECISION
MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK
MFEM_THREAD_SAFE
@@ -1034,7 +992,6 @@ MFEM_USE_CALIPER
MFEM_USE_FMS
MFEM_USE_BENCHMARK
MFEM_USE_PARELAG
MFEM_USE_TRIBOL
MFEM_USE_ENZYME
The following options are CMake specific:
-4
View File
@@ -287,7 +287,3 @@ ENDIF()
IF (DEFINED TPL_ENABLE_PARELAG)
SET(MFEM_USE_PARELAG ${TPL_ENABLE_PARELAG} CACHE BOOL "Enable ParELAG" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_TRIBOL)
SET(MFEM_USE_TRIBOL ${TPL_ENABLE_TRIBOL} CACHE BOOL "Enable Tribol" FORCE)
ENDIF()
-4
View File
@@ -18,8 +18,6 @@ set(MFEM_GIT_STRING "@MFEM_GIT_STRING@")
set(MFEM_USE_MPI @MFEM_USE_MPI@)
set(MFEM_USE_METIS @MFEM_USE_METIS@)
set(MFEM_USE_METIS_5 @MFEM_USE_METIS_5@)
set(MFEM_USE_DOUBLE @MFEM_USE_DOUBLE@)
set(MFEM_USE_SINGLE @MFEM_USE_SINGLE@)
set(MFEM_DEBUG @MFEM_DEBUG@)
set(MFEM_USE_EXCEPTIONS @MFEM_USE_EXCEPTIONS@)
set(MFEM_USE_ZLIB @MFEM_USE_ZLIB@)
@@ -37,7 +35,6 @@ set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
set(MFEM_USE_HIOP @MFEM_USE_HIOP@)
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
@@ -65,7 +62,6 @@ set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
set(MFEM_USE_ALGOIM @MFEM_USE_ALGOIM@)
set(MFEM_USE_BENCHMARK @MFEM_USE_BENCHMARK@)
set(MFEM_USE_PARELAG @MFEM_USE_PARELAG@)
set(MFEM_USE_TRIBOL @MFEM_USE_TRIBOL@)
set(MFEM_USE_ENZYME @MFEM_USE_ENZYME@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
-9
View File
@@ -46,12 +46,6 @@
// Requires an MPI compiler, and the libraries HYPRE and METIS.
#cmakedefine MFEM_USE_MPI
// Use double-precision floating point type
#cmakedefine MFEM_USE_DOUBLE
// Use single-precision floating point type
#cmakedefine MFEM_USE_SINGLE
// Enable debug checks in MFEM.
#cmakedefine MFEM_DEBUG
@@ -114,9 +108,6 @@
// Enable MFEM functionality based on the AmgX library.
#cmakedefine MFEM_USE_AMGX
// Enable MFEM functionality based on the MAGMA library.
#cmakedefine MFEM_USE_MAGMA
// Enable secure socket streams based on the GNUTLS library.
#cmakedefine MFEM_USE_GNUTLS
+1 -10
View File
@@ -18,13 +18,4 @@ include(MfemCmakeUtilities)
# Note: components are enabled based on the find_package() parameters.
mfem_find_package(Axom AXOM AXOM_DIR "include" "" "lib" ""
"Paths to headers required by Axom." "Libraries required by Axom."
ADD_COMPONENT core "include" axom/core.hpp "lib" axom_core
ADD_COMPONENT inlet "include" axom/inlet.hpp "lib" axom_inlet
ADD_COMPONENT klee "include" axom/klee.hpp "lib" axom_klee
ADD_COMPONENT lumberjack "include" axom/lumberjack.hpp "lib" axom_lumberjack
ADD_COMPONENT mint "include" axom/mint.hpp "lib" axom_mint
ADD_COMPONENT multimat "include" axom/multimat.hpp "lib" axom_multimat
ADD_COMPONENT quest "include" axom/quest.hpp "lib" axom_quest
ADD_COMPONENT sidre "include" axom/sidre.hpp "lib" axom_sidre
ADD_COMPONENT slam "include" axom/slam.hpp "lib" axom_slam
ADD_COMPONENT slic "include" axom/slic.hpp "lib" axom_slic)
ADD_COMPONENT Axom "include" axom/config.hpp "lib" axom)
+2 -6
View File
@@ -36,11 +36,7 @@ include(MfemCmakeUtilities)
mfem_find_package(Conduit CONDUIT CONDUIT_DIR
"include;include/conduit" conduit.hpp "lib" conduit
"Paths to headers required by Conduit." "Libraries required by Conduit."
ADD_COMPONENT blueprint
"include;include/conduit" conduit_blueprint.hpp "lib" conduit_blueprint
ADD_COMPONENT blueprint_mpi
"include;include/conduit" conduit_blueprint_mpi.hpp "lib" conduit_blueprint_mpi
ADD_COMPONENT relay
"include;include/conduit" conduit_relay.hpp "lib" conduit_relay
ADD_COMPONENT relay_mpi
"include;include/conduit" conduit_relay_mpi.hpp "lib" conduit_relay_mpi)
ADD_COMPONENT blueprint
"include;include/conduit" conduit_blueprint.hpp "lib" conduit_blueprint)
+27
View File
@@ -0,0 +1,27 @@
# Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
message(STATUS "Looking for ENZYME ...")
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
# Make sure the directory and version combination works. Do nothing otherwise.
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
# Set ENZYME_FOUND
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
# Set CXX flags to accommodate the Enzyme Clang plugin
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
set(MFEM_USE_ENZYME YES)
else()
endif()
+1 -3
View File
@@ -79,9 +79,7 @@ if (HYPRE_FOUND AND HYPRE_USING_CUDA)
find_package(CUDAToolkit REQUIRED)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CURAND_LIBRARIES CUDA::curand LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
${CUBLAS_LIBRARIES})
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES})
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
"HYPRE libraries + dependencies." FORCE)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
-37
View File
@@ -1,37 +0,0 @@
# Copyright (c) 2010-2024, 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.
# Defines the following variables:
# - MAGMA_FOUND
# - MAGMA_LIBRARIES
# - MAGMA_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(MAGMA MAGMA MAGMA_DIR "include" "magma.h" "lib" "magma"
"Paths to headers required by MAGMA." "Libraries required by MAGMA.")
if (MAGMA_FOUND AND MFEM_USE_CUDA)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
list(APPEND MAGMA_LIBRARIES ${CUSPARSE_LIBRARIES} ${CUBLAS_LIBRARIES})
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
"MAGMA libraries + dependencies." FORCE)
message(STATUS "Updated MAGMA_LIBRARIES: ${MAGMA_LIBRARIES}")
endif()
if (MAGMA_FOUND AND MFEM_USE_HIP)
find_package(HIPBLAS REQUIRED)
find_package(HIPSPARSE REQUIRED)
list(APPEND MAGMA_LIBRARIES ${HIPBLAS_LIBRARIES} ${HIPSPARSE_LIBRARIES})
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
"MAGMA libraries + dependencies." FORCE)
message(STATUS "Updated MAGMA_LIBRARIES: ${MAGMA_LIBRARIES}")
endif()
+3 -13
View File
@@ -16,22 +16,12 @@
# - MUMPS_VERSION
include(MfemCmakeUtilities)
# Toggle which precision of MUMPS to use depending on the precision of MFEM.
if (MFEM_USE_DOUBLE)
set(_mumps_header dmumps_c.h)
set(_mumps_lib dmumps)
elseif(MFEM_USE_SINGLE)
set(_mumps_header smumps_c.h)
set(_mumps_lib smumps)
endif()
mfem_find_package(MUMPS MUMPS MUMPS_DIR
"include" ${_mumps_header} "lib" ${_mumps_lib}
"include" dmumps_c.h "lib" dmumps
"Paths to headers required by MUMPS."
"Libraries required by MUMPS."
ADD_COMPONENT mumps_common "include" ${_mumps_header} "lib" mumps_common
ADD_COMPONENT pord "include" ${_mumps_header} "lib" pord)
ADD_COMPONENT mumps_common "include" dmumps_c.h "lib" mumps_common
ADD_COMPONENT pord "include" dmumps_c.h "lib" pord)
if (MUMPS_FOUND AND (NOT MUMPS_VERSION))
try_run(MUMPS_VERSION_RUN_RESULT MUMPS_VERSION_COMPILE_RESULT
-22
View File
@@ -1,22 +0,0 @@
# Copyright (c) 2010-2024, 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.
# Defines the following variables:
# - TRIBOL_FOUND
# - TRIBOL_LIBRARIES
# - TRIBOL_INCLUDE_DIRS
include(MfemCmakeUtilities)
# Note: components are enabled based on the find_package() parameters.
mfem_find_package(Tribol TRIBOL TRIBOL_DIR "include" tribol/config.hpp "lib" tribol
"Paths to headers required by Tribol." "Libraries required by Tribol."
ADD_COMPONENT redecomp
"include" redecomp/redecomp.hpp "lib" redecomp)
+10 -11
View File
@@ -842,18 +842,17 @@ function(mfem_export_mk_files)
# Convert Boolean vars to YES/NO without writing the values to cache
set(CONFIG_MK_BOOL_VARS MFEM_USE_MPI MFEM_USE_METIS MFEM_USE_METIS_5
MFEM_USE_SINGLE MFEM_USE_DOUBLE MFEM_DEBUG MFEM_USE_EXCEPTIONS
MFEM_USE_ZLIB MFEM_USE_LIBUNWIND MFEM_USE_LAPACK MFEM_THREAD_SAFE
MFEM_USE_LEGACY_OPENMP MFEM_USE_OPENMP MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS
MFEM_USE_SUITESPARSE MFEM_USE_SUPERLU MFEM_USE_SUPERLU5 MFEM_USE_MUMPS
MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX MFEM_USE_MAGMA
MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC
MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS MFEM_USE_CONDUIT MFEM_USE_PUMI
MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_RAJA
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
MFEM_DEBUG MFEM_USE_EXCEPTIONS MFEM_USE_ZLIB MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_LEGACY_OPENMP MFEM_USE_OPENMP
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU MFEM_USE_SUPERLU5 MFEM_USE_MUMPS MFEM_USE_STRUMPACK
MFEM_USE_GINKGO MFEM_USE_AMGX MFEM_USE_GNUTLS MFEM_USE_NETCDF
MFEM_USE_PETSC MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA
MFEM_USE_HIP MFEM_USE_RAJA MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER
MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
-65
View File
@@ -23,62 +23,6 @@
#include "_config.hpp"
#endif
namespace mfem
{
#if (defined(MFEM_USE_CUDA) && defined(__CUDACC__)) || \
(defined(MFEM_USE_HIP) && defined(__HIPCC__))
#define MFEM_HOST_DEVICE __host__ __device__
#else
#define MFEM_HOST_DEVICE
#endif
// MFEM precision configuration
#if defined MFEM_USE_SINGLE && defined MFEM_USE_DOUBLE
#error "DOUBLE and SINGLE precision cannot both be specified"
#endif
#ifdef MFEM_USE_SINGLE
typedef float real_t;
#elif defined MFEM_USE_DOUBLE
typedef double real_t;
#else
#error "Either DOUBLE or SINGLE precision must be specified"
#endif
MFEM_HOST_DEVICE
constexpr real_t operator""_r(long double v)
{
return static_cast<real_t>(v);
}
MFEM_HOST_DEVICE
constexpr real_t operator""_r(unsigned long long v)
{
return static_cast<real_t>(v);
}
} // namespace mfem
// Return value for main function in examples that should be skipped by testing
// in some case. This return value prevents failures in testing.
#define MFEM_SKIP_RETURN_VALUE 242
// Request a global object to be instantiated for each thread in its TLS.
#define MFEM_THREAD_LOCAL thread_local
// MFEM_DEPRECATED macro to mark obsolete functions and methods
// see https://stackoverflow.com/questions/295120/c-mark-as-deprecated
#if defined(__GNUC__) || defined(__clang__)
#define MFEM_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define MFEM_DEPRECATED __declspec(deprecated)
#else
#pragma message("WARNING: You need to implement MFEM_DEPRECATED for this compiler")
#define MFEM_DEPRECATED
#endif
// Common configuration macros
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) || defined(__clang__)
@@ -120,15 +64,6 @@ constexpr real_t operator""_r(unsigned long long v)
// Check dependencies:
// Define MFEM_MPI_REAL_T to be the appropriate MPI real type
#ifdef MFEM_USE_MPI
#ifdef MFEM_USE_SINGLE
#define MFEM_MPI_REAL_T MPI_FLOAT
#elif defined MFEM_USE_DOUBLE
#define MFEM_MPI_REAL_T MPI_DOUBLE
#endif
#endif
// Options that require MPI
#ifndef MFEM_USE_MPI
#ifdef MFEM_USE_SUPERLU
-9
View File
@@ -46,12 +46,6 @@
// Requires an MPI compiler, and the libraries HYPRE and METIS.
// #define MFEM_USE_MPI
// Use double-precision floating point type
// #define MFEM_USE_DOUBLE
// Use single-precision floating point type
// #define MFEM_USE_SINGLE
// Enable debug checks in MFEM.
// #define MFEM_DEBUG
@@ -114,9 +108,6 @@
// Enable MFEM functionality based on the AmgX library.
// #define MFEM_USE_AMGX
// Enable MFEM functionality based on the MAGMA library.
// #define MFEM_USE_MAGMA
// Enable secure socket streams based on the GNUTLS library.
// #define MFEM_USE_GNUTLS
-4
View File
@@ -18,8 +18,6 @@ MFEM_GIT_STRING = @MFEM_GIT_STRING@
MFEM_USE_MPI = @MFEM_USE_MPI@
MFEM_USE_METIS = @MFEM_USE_METIS@
MFEM_USE_METIS_5 = @MFEM_USE_METIS_5@
MFEM_USE_DOUBLE = @MFEM_USE_DOUBLE@
MFEM_USE_SINGLE = @MFEM_USE_SINGLE@
MFEM_DEBUG = @MFEM_DEBUG@
MFEM_USE_EXCEPTIONS = @MFEM_USE_EXCEPTIONS@
MFEM_USE_ZLIB = @MFEM_USE_ZLIB@
@@ -38,7 +36,6 @@ MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
@@ -66,7 +63,6 @@ MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
MFEM_USE_BENCHMARK = @MFEM_USE_BENCHMARK@
MFEM_USE_PARELAG = @MFEM_USE_PARELAG@
MFEM_USE_TRIBOL = @MFEM_USE_TRIBOL@
MFEM_USE_ENZYME = @MFEM_USE_ENZYME@
# Compiler, compile options, and link options
+2 -21
View File
@@ -22,8 +22,6 @@ endif()
option(BUILD_SHARED_LIBS "Enable shared library build of MFEM" OFF)
option(MFEM_USE_MPI "Enable MPI parallel build" OFF)
option(MFEM_USE_METIS "Enable METIS usage" ${MFEM_USE_MPI})
set(MFEM_PRECISION "double" CACHE STRING
"Floating-point precision to use: single, or double")
option(MFEM_USE_EXCEPTIONS "Enable the use of exceptions" OFF)
option(MFEM_USE_ZLIB "Enable zlib for compressed data streams." OFF)
option(MFEM_USE_LIBUNWIND "Enable backtrace for errors." OFF)
@@ -40,7 +38,6 @@ option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
@@ -68,7 +65,6 @@ option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
option(MFEM_USE_CODIPACK "Enable automatic differentiation (AD) using CoDiPack" OFF)
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
option(MFEM_USE_PARELAG "Enable ParELAG" OFF)
option(MFEM_USE_TRIBOL "Enable Tribol" OFF)
option(MFEM_USE_ENZYME "Enable Enzyme" OFF)
# Optional overrides for autodetected MPIEXEC and MPIEXEC_NUMPROC_FLAG
@@ -184,10 +180,6 @@ set(Ginkgo_DIR "${MFEM_DIR}/../ginkgo" CACHE PATH "Path to the Ginkgo library.")
set(AMGX_DIR "${MFEM_DIR}/../amgx" CACHE PATH "Path to AmgX")
set(MAGMA_DIR "${MFEM_DIR}/../magma" CACHE PATH "Path to MAGMA")
set(MAGMA_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
"Additional packages required by MAGMA.")
set(GNUTLS_DIR "" CACHE PATH "Path to the GnuTLS library.")
set(GSLIB_DIR "" CACHE PATH "Path to the GSLIB library.")
@@ -218,15 +210,8 @@ set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
set(AXOM_DIR "${MFEM_DIR}/../axom" CACHE PATH "Path to the Axom library.")
# May need to add "Boost" as requirement.
if (MFEM_USE_SIDRE)
if (MFEM_USE_MPI)
set(Axom_REQUIRED_PACKAGES "Conduit/blueprint/blueprint_mpi/relay/relay_mpi" CACHE STRING
"Additional packages required by Axom.")
elseif()
set(Axom_REQUIRED_PACKAGES "Conduit/blueprint/relay" CACHE STRING
"Additional packages required by Axom.")
endif()
endif()
set(Axom_REQUIRED_PACKAGES "Conduit/relay/blueprint" CACHE STRING
"Additional packages required by Axom.")
set(PUMI_DIR "${MFEM_DIR}/../pumi-2.1.0" CACHE STRING
"Directory where PUMI is installed")
@@ -263,10 +248,6 @@ set(PARELAG_INCLUDE_DIRS "${PARELAG_DIR}/src;${PARELAG_DIR}/build/src" CACHE
set(PARELAG_LIBRARIES "${PARELAG_DIR}/build/src/libParELAG.a" CACHE STRING
"The ParELAG library.")
set(TRIBOL_DIR "${MFEM_DIR}/../tribol" CACHE PATH "Path to Tribol")
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
"Additional packages required by Tribol")
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
+5 -44
View File
@@ -95,10 +95,6 @@ else
# Silence unused command line argument warnings when generating dependencies
# with mpicxx and clang
DEP_FLAGS := -Wno-unused-command-line-argument $(DEP_FLAGS)
# Silence "ignoring duplicate libraries" warnings on new (Xcode 15) linker
ifneq (,$(findstring PROJECT:dyld,$(shell ld -v 2>&1)))
LDFLAGS_INTERNAL = -Xlinker -no_warn_duplicate_libraries
endif
endif
# Set CXXFLAGS to overwrite the default selection of DEBUG_FLAGS/OPTIM_FLAGS
@@ -124,7 +120,6 @@ MFEM_MPI_NP = 4
MFEM_USE_MPI = NO
MFEM_USE_METIS = $(MFEM_USE_MPI)
MFEM_USE_METIS_5 = NO
MFEM_PRECISION = double
MFEM_DEBUG = NO
MFEM_USE_EXCEPTIONS = NO
MFEM_USE_ZLIB = NO
@@ -143,7 +138,6 @@ MFEM_USE_MUMPS = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
MFEM_USE_MAGMA = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
@@ -172,21 +166,8 @@ MFEM_USE_ADFORWARD = NO
MFEM_USE_CODIPACK = NO
MFEM_USE_BENCHMARK = NO
MFEM_USE_PARELAG = NO
MFEM_USE_TRIBOL = NO
MFEM_USE_ENZYME = NO
# Process MFEM_PRECISION -> MFEM_USE_SINGLE, MFEM_USE_DOUBLE
ifneq ($(filter double Double DOUBLE,$(MFEM_PRECISION)),)
MFEM_USE_DOUBLE = YES
MFEM_USE_SINGLE = NO
else ifneq ($(filter single Single SINGLE,$(MFEM_PRECISION)),)
MFEM_USE_DOUBLE = NO
MFEM_USE_SINGLE = YES
else ifeq ($(MAKECMDGOALS),config)
$(error Invalid floating-point precision: \
MFEM_PRECISION = $(MFEM_PRECISION))
endif
# MPI library compile and link flags
# These settings are used only when building MFEM with MPI + HIP
ifeq ($(MFEM_USE_MPI)$(MFEM_USE_HIP),YESYES)
@@ -336,13 +317,8 @@ MPI_FORTRAN_LIB = -lmpifort
# MUMPS library configuration
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.5.0
MUMPS_OPT = -I$(MUMPS_DIR)/include
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib
ifeq ($(MFEM_USE_SINGLE),YES)
MUMPS_LIB += -lsmumps
else
MUMPS_LIB += -ldmumps
endif
MUMPS_LIB += -lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
# STRUMPACK library configuration
STRUMPACK_DIR = @MFEM_DIR@/../STRUMPACK-build
@@ -393,12 +369,7 @@ GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LINK_LIB_DIR) -L$(GINKGO_LINK_LIB_DIR)\
# AmgX library configuration
AMGX_DIR = @MFEM_DIR@/../amgx
AMGX_OPT = -I$(AMGX_DIR)/include
AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
# MAGMA library configuration
MAGMA_DIR = @MFEM_DIR@/../magma
MAGMA_OPT = -I$(MAGMA_DIR)/include
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
AMGX_LIB = -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx
# GnuTLS library configuration
GNUTLS_OPT =
@@ -507,11 +478,11 @@ GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
# CUDA library configuration
CUDA_OPT =
CUDA_LIB = -lcusparse -lcublas
CUDA_LIB = -lcusparse
# HIP library configuration
HIP_OPT =
HIP_LIB = -L$(HIP_DIR)/lib $(XLINKER)-rpath,$(HIP_DIR)/lib -lhipsparse -lhipblas
HIP_LIB = -L$(HIP_DIR)/lib $(XLINKER)-rpath,$(HIP_DIR)/lib -lhipsparse
# OCCA library configuration
OCCA_DIR = @MFEM_DIR@/../occa
@@ -599,16 +570,6 @@ PARELAG_DIR = @MFEM_DIR@/../parelag
PARELAG_OPT = -I$(PARELAG_DIR)/src -I$(PARELAG_DIR)/build/src
PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
# Tribol library configuration
ifeq ($(MFEM_USE_TRIBOL),YES)
BASE_FLAGS = -std=c++14
endif
AXOM_DIR = @MFEM_DIR@/../axom
TRIBOL_DIR = @MFEM_DIR@/../tribol
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
-laxom_slam -laxom_slic -laxom_core
# Enzyme configuration
# If you want to enable automatic differentiation at compile time, use the
-4
View File
@@ -9,11 +9,7 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifdef MFEM_USE_SINGLE
#include "smumps_c.h"
#else
#include "dmumps_c.h"
#endif
#include <string>
#include <iostream>
#include <algorithm>
+1 -1
View File
@@ -110,4 +110,4 @@ config-mk:
clean:
rm -f $(CONFIG_HPP) $(CONFIG_MK) sample-runs-build.log
rm -f $(GHV) $(GHV).out $(GMV) $(GMV).out *.dSYM
rm -f $(GHV) $(GHV).out $(GMV) $(GMV).out
+13 -83
View File
@@ -32,7 +32,7 @@ groups_serial=(
'"examples"
"Examples:"
"examples"
"ex{,[1-9]}[0-9].cpp"'
"ex{,1,2,3}[0-9].cpp"'
# "ex1.cpp"'
'"sundials"
"SUNDIALS examples:"
@@ -58,10 +58,6 @@ groups_serial=(
"HiOp examples:"
"examples/hiop"
"ex9.cpp"'
'"moonolith"
"Moonolith examples:"
"examples/moonolith"
"ex1.cpp"'
'"pumi"
"PUMI examples:"
"examples/pumi"
@@ -70,38 +66,25 @@ groups_serial=(
'"meshing"
"Meshing miniapps:"
"miniapps/meshing"
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp mesh-quality.cpp
polar-nc.cpp reflector.cpp shaper.cpp trimmer.cpp twist.cpp
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
mesh-optimizer.cpp minimal-surface.cpp"'
'"adjoint"
"Adjoint miniapps:"
"miniapps/adjoint"
"cvsRoberts_ASAi_dns.cpp"'
'"autodiff"
"Autodiff miniapps:"
"miniapps/autodiff"
"seq_example.cpp seq_test.cpp"' # 'seq_test.cpp' has no sample runs
'"dpg"
"DPG miniapps:"
"miniapps/dpg"
"{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
'"gslib"
"GSLIB miniapps:"
"miniapps/gslib"
"field-diff.cpp field-interp.cpp findpts.cpp schwarz_ex1.cpp "'
# todo: miniapps/mtop
'"nurbs"
"NURBS miniapps:"
"miniapps/nurbs"
"nurbs_ex1.cpp"'
# todo: add other nurbs miniapps
# todo: miniapps/solvers (serial)
'"tools"
"Tools miniapps:"
"miniapps/tools"
"convert-dc.cpp display-basis.cpp get-values.cpp load-dc.cpp
lor-transfer.cpp"'
# todo: add other tools miniapps
'"toys"
"Toys miniapps:"
"miniapps/toys"
@@ -117,7 +100,7 @@ groups_parallel=(
'"examples"
"Examples:"
"examples"
"ex{,[1-9]}[0-9]p.cpp"'
"ex{,1,2,3}[0-9]p.cpp"'
# "ex1p.cpp"'
'"sundials"
"SUNDIALS examples:"
@@ -143,10 +126,6 @@ groups_parallel=(
"HiOp examples:"
"examples/hiop"
"ex9p.cpp"'
'"moonolith"
"Moonolith examples:"
"examples/moonolith"
"ex{1,2}p.cpp"'
'"pumi"
"PUMI examples:"
"examples/pumi"
@@ -159,41 +138,24 @@ groups_parallel=(
'"meshing"
"Meshing miniapps:"
"miniapps/meshing"
"pmesh-optimizer.cpp pmesh-fitting.cpp pminimal-surface.cpp
fit-node-position.cpp"'
"pmesh-optimizer.cpp pmesh-fitting.cpp pminimal-surface.cpp"'
'"electromagnetics"
"Electromagnetics miniapps:"
"miniapps/electromagnetics"
"joule.cpp"'
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
'"adjoint"
"Adjoint miniapps:"
"miniapps/adjoint"
"adjoint_advection_diffusion.cpp"'
'"autodiff"
"Autodiff miniapps:"
"miniapps/autodiff"
"par_example.cpp"'
'"dpg"
"DPG miniapps:"
"miniapps/dpg"
"p{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
'"gslib"
"GSLIB miniapps:"
"miniapps/gslib"
"pfindpts.cpp schwarz_ex1p.cpp"'
'"hdiv-linear-solver"
"H(div) linear solver miniapps:"
"miniapps/hdiv-linear-solver"
"grad_div.cpp darcy.cpp"'
# 'miniapps/hooke/hooke.cpp' has no sample runs
# todo: miniapps/mtop
# todo: miniapps/multidomain
'"navier"
"Navier miniapps:"
"miniapps/navier"
"navier_cht.cpp"'
# todo: add other navier miniapps
'"nurbs"
"NURBS miniapps:"
"miniapps/nurbs"
@@ -202,18 +164,14 @@ groups_parallel=(
"Shifted miniapps:"
"miniapps/shifted"
"distance.cpp"'
# todo: add other shifted miniapps
'"solvers"
"Solvers miniapps:"
"miniapps/solvers"
"block-solvers.cpp"'
# todo: add other solvers miniapps
# todo: miniapps/spde
'"tools"
"Tools miniapps:"
"miniapps/tools"
"convert-dc.cpp get-values.cpp load-dc.cpp"'
# todo: add other tools miniapps
"convert-cd.cpp get-values.cpp load-dc.cpp"'
'"convergence"
"Convergence tests:"
"tests/convergence"
@@ -228,7 +186,7 @@ groups_all=(
'"examples"
"Examples:"
"examples"
"ex\"{,[1-9]}[0-9]\"{,p}.cpp"'
"ex\"{,1,2,3}[0-9]\"{,p}.cpp"'
'"sundials"
"SUNDIALS examples:"
"examples/sundials"
@@ -257,14 +215,10 @@ groups_all=(
"HiOp examples:"
"examples/hiop"
"ex9.cpp ex9p.cpp"'
'"moonolith"
"Moonolith examples:"
"examples/moonolith"
"ex1.cpp ex{1,2}p.cpp"'
'"pumi"
"PUMI examples:"
"examples/pumi"
"ex1.cpp ex2.cpp ex1p.cpp ex6p.cpp"'
"ex1.cpp ex1p.cpp ex2.cpp ex6p.cpp"'
'"superlu"
"Superlu examples:"
"examples/superlu"
@@ -272,67 +226,43 @@ groups_all=(
'"meshing"
"Meshing miniapps:"
"miniapps/meshing"
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp mesh-quality.cpp
polar-nc.cpp reflector.cpp shaper.cpp trimmer.cpp twist.cpp
{,p}mesh-optimizer.cpp pmesh-fitting.cpp {,p}minimal-surface.cpp
fit-node-position.cpp"'
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
{,p}mesh-optimizer.cpp pmesh-fitting.cpp {,p}minimal-surface.cpp"'
'"electromagnetics"
"Electromagnetics miniapps:"
"miniapps/electromagnetics"
"joule.cpp"'
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
'"adjoint"
"Adjoint miniapps:"
"miniapps/adjoint"
"cvsRoberts_ASAi_dns.cpp adjoint_advection_diffusion.cpp"'
'"autodiff"
"Autodiff miniapps:"
"miniapps/autodiff"
"seq_example.cpp seq_test.cpp par_example.cpp"'
# 'seq_test.cpp' has no sample runs
'"dpg"
"DPG miniapps:"
"miniapps/dpg"
"{,p}{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
"adjoint_advection_diffusion.cpp cvsRoberts_ASAi_dns.cpp"'
'"gslib"
"GSLIB miniapps:"
"miniapps/gslib"
"field-diff.cpp field-interp.cpp findpts.cpp schwarz_ex1.cpp pfindpts.cpp
schwarz_ex1p.cpp"'
'"hdiv-linear-solver"
"H(div) linear solver miniapps:"
"miniapps/hdiv-linear-solver"
"grad_div.cpp darcy.cpp"'
# 'miniapps/hooke/hooke.cpp' has no sample runs
# todo: miniapps/mtop
# todo: miniapps/multidomain
'"navier"
"Navier miniapps:"
"miniapps/navier"
"navier_cht.cpp"'
# todo: add other navier miniapps
'"nurbs"
"NURBS miniapps:"
"miniapps/nurbs"
"nurbs_ex1.cpp nurbs_ex1p.cpp nurbs_ex11p.cpp"'
# todo: add other nurbs miniapps
'"shifted"
"Shifted miniapps:"
"miniapps/shifted"
"distance.cpp"'
# todo: add other shifted miniapps
'"solvers"
"Solvers miniapps:"
"miniapps/solvers"
"block-solvers.cpp"'
# todo: add other solvers miniapps
# todo: miniapps/spde
'"tools"
"Tools miniapps:"
"miniapps/tools"
"convert-dc.cpp display-basis.cpp get-values.cpp load-dc.cpp
lor-transfer.cpp"'
# todo: add other tools miniapps
'"toys"
"Toys miniapps:"
"miniapps/toys"
@@ -456,7 +386,7 @@ function help_message()
mfem_config [${mfem_config}]
Set MFEM configuration options
make [${make}], mpiexec [${mpiexec}], mpiexec_np [${mpiexec_np}]
Their values can also be set using the respective uppercase environment
Their values can also set using the respective uppercase environment
variable
mfem_build_dir [${mfem_build_dir}]
Same as '-d': set this variable to something different from <mfem_dir>
-118
View File
@@ -1,118 +0,0 @@
SetFactory("OpenCASCADE");
order = 1;
R = 1;
r = 0.2;
Point(1) = {0,0,0};
Point(2) = {r/Sqrt(2),r/Sqrt(2),0};
Point(3) = {-r/Sqrt(2),r/Sqrt(2),0};
Point(4) = {-r/Sqrt(2),-r/Sqrt(2),0};
Point(5) = {r/Sqrt(2),-r/Sqrt(2),0};
Point(6) = {R,0,0};
Point(7) = {R/Sqrt(2),R/Sqrt(2),0};
Point(8) = {0,R,0};
Point(9) = {-R/Sqrt(2),R/Sqrt(2),0};
Point(10) = {-R,0,0};
Point(11) = {-R/Sqrt(2),-R/Sqrt(2),0};
Point(12) = {0,-R,0};
Point(13) = {R/Sqrt(2),-R/Sqrt(2),0};
Line(1) = {1,2};
Line(2) = {1,3};
Line(3) = {1,4};
Line(4) = {1,5};
Line(5) = {1,6};
Line(6) = {1,8};
Line(7) = {1,10};
Line(8) = {1,12};
Line(9) = {2,6};
Line(10) = {2,8};
Line(11) = {3,8};
Line(12) = {3,10};
Line(13) = {4,10};
Line(14) = {4,12};
Line(15) = {5,12};
Line(16) = {5,6};
Line(17) = {6,7};
Line(18) = {7,8};
Line(19) = {8,9};
Line(20) = {9,10};
Line(21) = {10,11};
Line(22) = {11,12};
Line(23) = {12,13};
Line(24) = {13,6};
Transfinite Curve{1:24} = 2;
Physical Curve("ENE") = {17};
Physical Curve("NNE") = {18};
Physical Curve("NNW") = {19};
Physical Curve("WNW") = {20};
Physical Curve("WSW") = {21};
Physical Curve("SSW") = {22};
Physical Curve("SSE") = {23};
Physical Curve("ESE") = {24};
Curve Loop(1) = {9,17,18,-10};
Curve Loop(2) = {11,19,20,-12};
Curve Loop(3) = {13,21,22,-14};
Curve Loop(4) = {15,23,24,-16};
Plane Surface(1) = {1};
Plane Surface(2) = {2};
Plane Surface(3) = {3};
Plane Surface(4) = {4};
Transfinite Surface{1} = {2,6,7,8};
Transfinite Surface{2} = {3,8,9,10};
Transfinite Surface{3} = {4,10,11,12};
Transfinite Surface{4} = {5,12,13,6};
Recombine Surface{1:4};
Physical Surface("Base") = {1,2,3,4};
Curve Loop(5) = {1,10,-6};
Plane Surface(5) = {5};
Physical Surface("N Even") = {5};
Curve Loop(6) = {6,-11,-2};
Plane Surface(6) = {6};
Physical Surface("N Odd") = {6};
Curve Loop(7) = {2,12,-7};
Plane Surface(7) = {7};
Physical Surface("W Even") = {7};
Curve Loop(8) = {7,-13,-3};
Plane Surface(8) = {8};
Physical Surface("W Odd") = {8};
Curve Loop(9) = {3,14,-8};
Plane Surface(9) = {9};
Physical Surface("S Even") = {9};
Curve Loop(10) = {8,-15,-4};
Plane Surface(10) = {10};
Physical Surface("S Odd") = {10};
Curve Loop(11) = {4,16,-5};
Plane Surface(11) = {11};
Physical Surface("E Even") = {11};
Curve Loop(12) = {5,-9,-1};
Plane Surface(12) = {12};
Physical Surface("E Odd") = {12};
// Generate 2D mesh
Mesh 2;
SetOrder order;
Mesh.MshFileVersion = 2.2;
Save "compass.msh";
-96
View File
@@ -1,96 +0,0 @@
MFEM mesh v1.3
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
12
10 2 7 0 1
11 2 0 7 2
12 2 9 0 2
13 2 0 9 3
14 2 11 0 3
15 2 0 11 4
16 2 5 0 4
17 2 0 5 1
9 3 1 5 6 7
9 3 2 7 8 9
9 3 3 9 10 11
9 3 4 11 12 5
attribute_sets
16
"Base" 1 9
"E Even" 1 16
"E Odd" 1 17
"East" 2 16 17
"N Even" 1 10
"N Odd" 1 11
"North" 2 10 11
"Rose" 8 10 11 12 13 14 15 16 17
"Rose Even" 4 10 12 14 16
"Rose Odd" 4 11 13 15 17
"S Even" 1 14
"S Odd" 1 15
"South" 2 14 15
"W Even" 1 12
"W Odd" 1 13
"West" 2 12 13
boundary
8
1 1 5 6
2 1 6 7
3 1 7 8
4 1 8 9
5 1 9 10
6 1 10 11
7 1 11 12
8 1 12 5
bdr_attribute_sets
13
"Boundary" 8 1 2 3 4 5 6 7 8
"ENE" 1 1
"ESE" 1 8
"Eastern Boundary" 2 1 8
"NNE" 1 2
"NNW" 1 3
"Northern Boundary" 2 2 3
"SSE" 1 7
"SSW" 1 6
"Southern Boundary" 2 6 7
"WNW" 1 4
"WSW" 1 5
"Western Boundary" 2 4 5
vertices
13
2
0 0
0.14142136 0.14142136
-0.14142136 0.14142136
-0.14142136 -0.14142136
0.14142136 -0.14142136
1 0
0.70710678 0.70710678
0 1
-0.70710678 0.70710678
-1 0
-0.70710678 -0.70710678
0 -1
0.70710678 -0.70710678
mfem_mesh_end
-62
View File
@@ -1,62 +0,0 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$PhysicalNames
17
1 1 "ENE"
1 2 "NNE"
1 3 "NNW"
1 4 "WNW"
1 5 "WSW"
1 6 "SSW"
1 7 "SSE"
1 8 "ESE"
2 9 "Base"
2 10 "N Even"
2 11 "N Odd"
2 12 "W Even"
2 13 "W Odd"
2 14 "S Even"
2 15 "S Odd"
2 16 "E Even"
2 17 "E Odd"
$EndPhysicalNames
$Nodes
13
1 0 0 0
2 0.1414213562373095 0.1414213562373095 0
3 -0.1414213562373095 0.1414213562373095 0
4 -0.1414213562373095 -0.1414213562373095 0
5 0.1414213562373095 -0.1414213562373095 0
6 1 0 0
7 0.7071067811865475 0.7071067811865475 0
8 0 1 0
9 -0.7071067811865475 0.7071067811865475 0
10 -1 0 0
11 -0.7071067811865475 -0.7071067811865475 0
12 0 -1 0
13 0.7071067811865475 -0.7071067811865475 0
$EndNodes
$Elements
20
1 1 2 1 17 6 7
2 1 2 2 18 7 8
3 1 2 3 19 8 9
4 1 2 4 20 9 10
5 1 2 5 21 10 11
6 1 2 6 22 11 12
7 1 2 7 23 12 13
8 1 2 8 24 13 6
9 2 2 10 5 1 2 8
10 2 2 11 6 1 8 3
11 2 2 12 7 1 3 10
12 2 2 13 8 1 10 4
13 2 2 14 9 1 4 12
14 2 2 15 10 1 12 5
15 2 2 16 11 1 5 6
16 2 2 17 12 1 6 2
17 3 2 9 1 2 6 7 8
18 3 2 9 2 3 8 9 10
19 3 2 9 3 4 10 11 12
20 3 2 9 4 5 12 13 6
$EndElements
+3 -3
View File
@@ -18,9 +18,9 @@ elements
boundary
4
1 1 0 1
2 1 2 3
3 1 3 0
4 1 1 2
1 1 2 3
1 1 3 0
1 1 1 2
edges
4
-35
View File
@@ -1,35 +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
1
1 3 0 1 2 3
boundary
4
1 1 0 1
2 1 1 2
3 1 2 3
4 1 3 0
vertices
4
2
0 0
1 0.3
1.4 1.2
0.25 1.34
+2 -5
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v4.7.1
PROJECT_NUMBER = v4.6.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -938,7 +938,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/config \
@MFEM_SOURCE_DIR@/general \
@MFEM_SOURCE_DIR@/linalg \
@MFEM_SOURCE_DIR@/linalg/batched \
@MFEM_SOURCE_DIR@/linalg/simd \
@MFEM_SOURCE_DIR@/mesh \
@MFEM_SOURCE_DIR@/mesh/submesh \
@@ -988,7 +987,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/solvers \
@MFEM_SOURCE_DIR@/miniapps/tools \
@MFEM_SOURCE_DIR@/miniapps/toys \
@MFEM_SOURCE_DIR@/miniapps/tribol \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util
@@ -1050,8 +1048,7 @@ RECURSIVE = NO
EXCLUDE = @MFEM_SOURCE_DIR@/config/_config.hpp \
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp \
@MFEM_SOURCE_DIR@/general/tinyxml2.h \
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp \
@MFEM_SOURCE_DIR@/linalg/lapack.hpp
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
+1 -22
View File
@@ -110,13 +110,9 @@ namespace mfem {
* - <a class="el" href="ex35p_8cpp_source.html">Example 35p</a>: parallel multi-domain damped harmonic oscillators
* - <a class="el" href="ex36_8cpp_source.html">Example 36</a>: Proximal Galerkin FEM for the obstacle problem
* - <a class="el" href="ex36p_8cpp_source.html">Example 36p</a>: parallel Proximal Galerkin FEM for the obstacle problem
* - <a class="el" href="ex37_8cpp_source.html">Example 37</a>: topology optimization
* - <a class="el" href="ex37_8cpp_source.html">Example 37</a>: Topology optimization
* - <a class="el" href="ex37p_8cpp_source.html">Example 37p</a>: parallel topology optimization
* - <a class="el" href="ex38_8cpp_source.html">Example 38</a>: cut-surface and cut-volume integration
* - <a class="el" href="ex39_8cpp_source.html">Example 39</a>: named mesh attributes
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
*
* <H4>AmgX Examples</H4>
* - Variants of Examples
@@ -182,21 +178,6 @@ namespace mfem {
* <a class="el" href="examples_2superlu_2ex1p_8cpp_source.html">1p</a>,
* demonstrating the use of MFEM's \link superlu.hpp SuperLU integration\endlink.
*
* <H4>NURBS Examples</H4>
* - Variants of Examples
* <a class="el" href="nurbs__ex1_8cpp_source.html">1</a>,
* <a class="el" href="nurbs__ex1p_8cpp_source.html">1p</a>,
* <a class="el" href="nurbs__ex3_8cpp_source.html">3</a>,
* <a class="el" href="nurbs__ex5_8cpp_source.html">5</a>,
* <a class="el" href="nurbs__ex11p_8cpp_source.html">11p</a>, and
* <a class="el" href="nurbs__ex24_8cpp_source.html">24</a>,
* demonstrating howto perform NURBS-based Isogeometric Analysis.
* - Variant of Example <a class="el" href="nurbs__patch__ex1_8cpp_source.html">1</a>: demonstrates the use of patch integration
* - <a class="el" href="nurbs__solenoidal_8cpp_source.html">NURBS Divergence-free</a>: solve a solenoidal vector projection with NURBS-based H(div) elements
* - <a class="el" href="nurbs__curveint_8cpp_source.html">NURBS Interpolation</a>: NURBS interpolation of given geometry
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
*
* <H3>Miniapps</H3>
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
* - <a class="el" href="tesla_8cpp_source.html">Tesla</a>: simple magnetostatics simulation code
@@ -233,8 +214,6 @@ namespace mfem {
* - <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="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
* - <a class="el" href="lor__elast_8cpp_source.html">LOR Elasticity</a>: solve linear elasticity with LOR preconditioning on GPUs
+2 -2
View File
@@ -46,7 +46,7 @@ class DoxygenAwesomeDarkModeToggle extends HTMLElement {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
@@ -97,7 +97,7 @@ class DoxygenAwesomeDarkModeToggle extends HTMLElement {
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
+10 -62
View File
@@ -43,35 +43,16 @@ list(APPEND ALL_EXE_SRCS
ex34.cpp
ex36.cpp
ex37.cpp
ex38.cpp
ex39.cpp
ex40.cpp
)
if(MFEM_USE_LAPACK)
list(APPEND ALL_EXE_SRCS
ex38.cpp
)
endif()
if (MFEM_USE_MPI)
list(APPEND ALL_EXE_SRCS
dfem_poisson.cpp
dfem_stokes.cpp
enzyme_interface_smoketest.cpp
test_dfem_dual.cpp
test_dfem.cpp
dfem_laghos.cpp
dfem_minimal_example.cpp
dfem_test_diffusion_2d.cpp
dfem_test_diffusion_3d.cpp
dfem_test_diffusion_3d_refactor.cpp
dfem_test_ordering.cpp
dfem_test_vector_diffusion.cpp
dfem_test_elasticity.cpp
dfem_test_nonlinear_elasticity_3d.cpp
dfem_test_nonlinear_diffusion_3d.cpp
dfem_test_interpolate_linear_scalar.cpp
dfem_test_interpolate_linear_scalar_3d.cpp
dfem_test_interpolate_gradient_linear_scalar_3d.cpp
dfem_test_mass_scalar_3d.cpp
dfem_test_mass_scalar_2d.cpp
dfem_test_interpolate_linear_vector.cpp
dfem_test_interpolate_linear_vector_3d.cpp
ex0p.cpp
ex1p.cpp
ex2p.cpp
@@ -109,21 +90,7 @@ if (MFEM_USE_MPI)
ex35p.cpp
ex36p.cpp
ex37p.cpp
ex39p.cpp
ex40p.cpp
)
endif()
# Examples that return MFEM_SKIP_RETURN_VALUE in some cases:
set(SKIP_TESTS)
if (HYPRE_USING_CUDA OR HYPRE_USING_HIP)
list(APPEND SKIP_TESTS ex19p.cpp ex28p.cpp)
endif()
if (MFEM_USE_SINGLE)
list(APPEND SKIP_TESTS ex33.cpp ex33p.cpp)
endif()
if (NOT MFEM_USE_LAPACK)
list(APPEND SKIP_TESTS ex38.cpp)
)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
@@ -132,23 +99,9 @@ include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add one executable per cpp file
add_mfem_examples(ALL_EXE_SRCS)
target_link_libraries(dfem_poisson ClangEnzymeFlags)
target_link_libraries(dfem_stokes ClangEnzymeFlags)
target_link_libraries(enzyme_interface_smoketest ClangEnzymeFlags)
target_link_libraries(test_dfem ClangEnzymeFlags)
target_link_libraries(dfem_laghos ClangEnzymeFlags)
target_link_libraries(dfem_minimal_example ClangEnzymeFlags)
target_link_libraries(dfem_test_diffusion_3d ClangEnzymeFlags)
target_link_libraries(dfem_test_diffusion_3d_refactor ClangEnzymeFlags)
target_link_libraries(dfem_test_nonlinear_diffusion_3d ClangEnzymeFlags)
target_link_libraries(dfem_test_nonlinear_elasticity_3d ClangEnzymeFlags)
# Add a test for each example
if (MFEM_ENABLE_TESTING)
foreach(SRC_FILE ${ALL_EXE_SRCS})
if (SRC_FILE IN_LIST SKIP_TESTS)
continue()
endif()
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
@@ -181,10 +134,10 @@ if (MFEM_ENABLE_TESTING)
# Add CUDA/HIP tests.
set(DEVICE_EXAMPLES
# serial examples with device support:
ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26 ex34
# parallel examples with device support:
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p ex22p ex24p ex25p
ex26p ex34p ex35p)
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p ex24p ex25p ex26p
ex34p ex35p)
set(MFEM_TEST_DEVICE)
if (MFEM_USE_CUDA)
set(MFEM_TEST_DEVICE "cuda")
@@ -194,11 +147,6 @@ if (MFEM_ENABLE_TESTING)
if (MFEM_TEST_DEVICE)
foreach(TEST_NAME ${DEVICE_EXAMPLES})
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
if (${TEST_NAME} MATCHES "ex14p")
list(APPEND THIS_TEST_OPTIONS "-rs" "2" "-rp" "0" "-pa")
elseif (${TEST_NAME} MATCHES "ex14")
list(APPEND THIS_TEST_OPTIONS "-r" "2" "-pa")
endif()
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
+4 -3
View File
@@ -12,10 +12,11 @@
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
MFEM_INSTALL_DIR ?= ../../mfem
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/amgx/,)
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
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
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
+4 -3
View File
@@ -12,10 +12,11 @@
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
MFEM_INSTALL_DIR ?= ../../mfem
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/caliper,)
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
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
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
-184
View File
@@ -1,184 +0,0 @@
/*
MIT License
Copyright (c) 2017 André L. Maravilha
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CXX_TIMER_HPP
#define CXX_TIMER_HPP
#include <chrono>
namespace cxxtimer {
/**
* This class works as a stopwatch.
*/
class Timer {
public:
/**
* Constructor.
*
* @param start
* If true, the timer is started just after construction.
* Otherwise, it will not be automatically started.
*/
Timer(bool start = false);
/**
* Copy constructor.
*
* @param other
* The object to be copied.
*/
Timer(const Timer& other) = default;
/**
* Transfer constructor.
*
* @param other
* The object to be transferred.
*/
Timer(Timer&& other) = default;
/**
* Destructor.
*/
virtual ~Timer() = default;
/**
* Assignment operator by copy.
*
* @param other
* The object to be copied.
*
* @return A reference to this object.
*/
Timer& operator=(const Timer& other) = default;
/**
* Assignment operator by transfer.
*
* @param other
* The object to be transferred.
*
* @return A reference to this object.
*/
Timer& operator=(Timer&& other) = default;
/**
* Start/resume the timer.
*/
void start();
/**
* Stop/pause the timer.
*/
void stop();
/**
* Reset the timer.
*/
void reset();
/**
* Return the elapsed time.
*
* @param duration_t
* The duration type used to return the time elapsed. If not
* specified, it returns the time as represented by
* std::chrono::milliseconds.
*
* @return The elapsed time.
*/
template <class duration_t = std::chrono::milliseconds>
typename duration_t::rep count() const;
private:
bool started_;
bool paused_;
std::chrono::steady_clock::time_point reference_;
std::chrono::duration<long double> accumulated_;
};
}
inline cxxtimer::Timer::Timer(bool start) :
started_(false), paused_(false),
reference_(std::chrono::steady_clock::now()),
accumulated_(std::chrono::duration<long double>(0)) {
if (start) {
this->start();
}
}
inline void cxxtimer::Timer::start() {
if (!started_) {
started_ = true;
paused_ = false;
accumulated_ = std::chrono::duration<long double>(0);
reference_ = std::chrono::steady_clock::now();
} else if (paused_) {
reference_ = std::chrono::steady_clock::now();
paused_ = false;
}
}
inline void cxxtimer::Timer::stop() {
if (started_ && !paused_) {
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
accumulated_ = accumulated_ + std::chrono::duration_cast< std::chrono::duration<long double> >(now - reference_);
paused_ = true;
}
}
inline void cxxtimer::Timer::reset() {
if (started_) {
started_ = false;
paused_ = false;
reference_ = std::chrono::steady_clock::now();
accumulated_ = std::chrono::duration<long double>(0);
}
}
template <class duration_t>
typename duration_t::rep cxxtimer::Timer::count() const {
if (started_) {
if (paused_) {
return std::chrono::duration_cast<duration_t>(accumulated_).count();
} else {
return std::chrono::duration_cast<duration_t>(
accumulated_ + (std::chrono::steady_clock::now() - reference_)).count();
}
} else {
return duration_t(0).count();
}
}
#endif
-4
View File
@@ -1,4 +0,0 @@
#pragma once
#include "dfem_differentiable_operator.hpp"
#include "dfem_element_operator.hpp"
-232
View File
@@ -1,232 +0,0 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Action::create_action_callback(
kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// All solutions T-vector sizes make up the width of the operator, since
// they are explicitly provided in Mult() for example.
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
op.height = 1;
}
else
{
op.height = op.residual_lsize;
}
residual_l.SetSize(op.residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
op.element_dof_ordering);
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
op.element_dof_ordering,
op.solutions.size());
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
wrapped_fields_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// tic();
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
// tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
residual_l = ye_mem;
}
else
{
R->MultTranspose(ye_mem, residual_l);
}
};
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
else if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
@@ -1,308 +0,0 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::assemble_hypreparmatrix_impl(
kernel_t kernel, HypreParMatrix &A)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs,
std::make_index_sequence<kernel.num_koutputs> {});
auto output_fop = std::get<0>(kernel.outputs);
constexpr int hardcoded_output_idx = 0;
int num_qp = op.integration_rule.GetNPoints();;
int num_el = 0;
int dimension = 0;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
num_el = op.mesh.GetNE();
dimension = op.dim;
}
else if (std::is_same_v<entity_t, Entity::Face>)
{
num_el = op.mesh.GetNumFacesWithGhost();
dimension = op.dim - 1;
}
else
{
static_assert(always_false<entity_t>, "not implemented");
}
std::vector<const DofToQuad*> dtqmaps;
for (const auto &field : op.fields)
{
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
// Allocate memory for fields on quadrature points
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (auto &d_qp_mem : directions_qp_mem)
{
d_qp_mem = 0.0;
}
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_kinput_is_dependent)
{
return;
}
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
DeviceTensor<1, const double> integration_weights(
this->op.integration_rule.GetWeights().Read(), num_qp);
Vector zero;
GeometricFactorMaps geometric_factors
{
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
};
// fields interpolated to the quadrature points in the order of
// kernel function arguments
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
kinput_to_field);
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
kernel.inputs,
dtqmaps,
kinput_to_field,
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
koutput_to_field);
constexpr int fixed_output_idx = 0;
auto Bv = output_dtq_ops[fixed_output_idx];
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
const int test_vdim = std::get<0>(kernel.outputs).vdim;
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
int trial_vdim = 0;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_is_dependent[i])
{
trial_vdim = GetVDim(op.fields[kinput_to_field[i]]);
break;
}
}
// All trial operators dimensions accumulated
int total_trial_op_dim = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
}
Vector a_qp_mem(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim *
num_qp *
num_el);
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp,
num_el);
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim * num_el);
Ae_mem = 0.0;
auto A_e = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
trial_vdim, num_el);
for (int e = 0; e < num_el; e++)
{
map_fields_to_quadrature_data(
input_qp, e, this->fields_e,
kinput_to_field, input_dtq_ops,
integration_weights, geometric_factors, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (int q = 0; q < num_qp; q++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
Vector f_qp = apply_kernel_fwddiff_enzyme(
kernel.func,
kernel_args,
input_qp,
kernel_shadow_args,
directions_qp,
q);
// Vector f_qp = apply_kernel_fwddiff_dual(
// kernel.func,
// kernel_args,
// input_qp,
// directions_qp,
// q);
d_qp(j, m, q) = 0.0;
auto f = Reshape(f_qp.Read(), test_vdim, test_op_dim);
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
a_qp(i, k, j, m + m_offset, q, e) = f(i, k);
}
}
}
m_offset += trial_op_dim;
}
}
}
Vector fhat_mem(test_op_dim * num_qp * dimension);
auto fhat = Reshape(fhat_mem.ReadWrite(), test_vdim, test_op_dim, num_qp);
for (int J = 0; J < num_trial_dof; J++)
{
for (int j = 0; j < trial_vdim; j++)
{
fhat_mem = 0.0;
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
for (int q = 0; q < num_qp; q++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
for (int m = 0; m < trial_op_dim; m++)
{
fhat(i, k, q) += a_qp(i, k, j, m + m_offset, q, e) * Bu(q, m, J);
}
}
}
}
m_offset += trial_op_dim;
}
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(bvtfhat, fhat, output_fop,
output_dtq_ops[hardcoded_output_idx]);
}
}
}
bool same_test_and_trial = false;
if (koutput_to_field[0] ==
kinput_to_field[dependent_input_dtq_ops[0].which_input])
{
same_test_and_trial = true;
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]].data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&op.fields[koutput_to_field[0]].data);
SparseMatrix mat(test_fes->GlobalVSize(), trial_fes->GlobalVSize());
if (test_fes == nullptr)
{
MFEM_ABORT("error");
}
for (int e = 0; e < num_el; e++)
{
auto tmp = Reshape(Ae_mem.ReadWrite(), num_test_dof * test_vdim,
num_trial_dof * trial_vdim,
num_el);
DenseMatrix A_e(&tmp(0, 0, e), num_test_dof * test_vdim,
num_trial_dof * trial_vdim);
Array<int> test_vdofs, trial_vdofs;
test_fes->GetElementVDofs(e, test_vdofs);
GetElementVDofs(
op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]], e,
trial_vdofs);
mat.AddSubMatrix(test_vdofs, trial_vdofs, A_e, 1);
}
mat.Finalize();
if (same_test_and_trial)
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
&mat);
A = *RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
}
else
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
trial_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
trial_fes->GetDofOffsets(),
&mat);
A = *RAP(test_fes->Dof_TrueDof_Matrix(), &tmp, trial_fes->Dof_TrueDof_Matrix());
// A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
}
}
-233
View File
@@ -1,233 +0,0 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::assemble_vector_impl(
kernel_t kernel, Vector &v)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs,
std::make_index_sequence<kernel.num_koutputs> {});
auto output_fop = std::get<0>(kernel.outputs);
constexpr int hardcoded_output_idx = 0;
int num_qp = op.integration_rule.GetNPoints();;
int num_el = 0;
int dimension = 0;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
num_el = op.mesh.GetNE();
dimension = op.dim;
}
else if (std::is_same_v<entity_t, Entity::Face>)
{
num_el = op.mesh.GetNumFacesWithGhost();
dimension = op.dim - 1;
}
else
{
static_assert(always_false<entity_t>, "not implemented");
}
std::vector<const DofToQuad*> dtqmaps;
for (const auto &field : op.fields)
{
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
// Allocate memory for fields on quadrature points
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (auto &d_qp_mem : directions_qp_mem)
{
d_qp_mem = 0.0;
}
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_kinput_is_dependent)
{
return;
}
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
DeviceTensor<1, const double> integration_weights(
this->op.integration_rule.GetWeights().Read(), num_qp);
Vector zero;
GeometricFactorMaps geometric_factors
{
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
};
// fields interpolated to the quadrature points in the order of
// kernel function arguments
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
kinput_to_field);
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
kernel.inputs,
dtqmaps,
kinput_to_field,
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
koutput_to_field);
constexpr int fixed_output_idx = 0;
auto Bv = output_dtq_ops[fixed_output_idx];
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
const int test_vdim = std::get<0>(kernel.outputs).vdim;
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
int trial_vdim = 0;
int dependent_field_idx = -1;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_is_dependent[i])
{
dependent_field_idx = kinput_to_field[i];
break;
}
}
trial_vdim = GetVDim(op.fields[dependent_field_idx]);
// All trial operators dimensions accumulated
int total_trial_op_dim = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
}
Vector a_qp_mem(trial_vdim * total_trial_op_dim * num_qp * num_el);
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), trial_vdim,
total_trial_op_dim, num_qp, num_el);
Vector ve_mem(num_trial_dof * trial_vdim * num_el);
ve_mem = 0.0;
for (int e = 0; e < num_el; e++)
{
map_fields_to_quadrature_data(
input_qp, e, this->fields_e,
kinput_to_field, input_dtq_ops,
integration_weights, geometric_factors, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (int q = 0; q < num_qp; q++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
// Vector f_qp = apply_kernel_fwddiff_dual(
// kernel.func,
// kernel_args,
// input_qp,
// directions_qp,
// q);
Vector f_qp = apply_kernel_fwddiff_enzyme(
kernel.func,
kernel_args,
input_qp,
kernel_shadow_args,
directions_qp,
q);
d_qp(j, m, q) = 0.0;
auto f = Reshape(f_qp.Read(), test_vdim);
a_qp(j, m + m_offset, q, e) = f(0);
}
m_offset += trial_op_dim;
}
}
}
auto shat = Reshape(ve_mem.ReadWrite(), num_trial_dof, trial_vdim, num_el);
for (int J = 0; J < num_trial_dof; J++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
for (int q = 0; q < num_qp; q++)
{
for (int m = 0; m < trial_op_dim; m++)
{
shat(J, j, e) += a_qp(j, m + m_offset, q, e) * Bu(q, m, J);
}
}
m_offset += trial_op_dim;
}
}
}
}
auto R = get_element_restriction(op.fields[dependent_field_idx],
element_dof_ordering);
Vector ve(R->Width());
R->MultTranspose(ve_mem, ve);
get_prolongation(op.fields[dependent_field_idx])->MultTranspose(ve, v);
}
-244
View File
@@ -1,244 +0,0 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
bool with_derivatives = true;
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
if (no_kinput_is_dependent)
{
return;
}
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering);
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.Read(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
wrapped_fields_e,
e);
auto direction_shmem = load_direction_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::DIRECTION],
shmem_info.direction_size,
wrapped_direction_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto shadow_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::SHADOW],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
scratch_mem, kinput_is_dependent,
std::make_index_sequence<kernel.num_kinputs> {});
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
apply_kernel_fwddiff_enzyme(
r,
kernel.func,
kernel_args,
input_shmem,
kernel_shadow_args,
shadow_shmem,
q);
// printf(">>>>> WARNING: AD DISABLED\n");
}
}
}
MFEM_SYNC_THREAD;
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
@@ -1,820 +0,0 @@
#pragma once
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <utility>
#include <variant>
#include <vector>
#include <type_traits>
#include <mfem.hpp>
#include <type_traits>
#include "dfem_fieldoperator.hpp"
#include "dfem_parametricspace.hpp"
#include "general/tic_toc.hpp"
#include "tuple.hpp"
#include <linalg/tensor.hpp>
#include <enzyme/utils>
#include <enzyme/enzyme>
#include "dfem_util.hpp"
#include "dfem_interpolate.hpp"
#include "dfem_qfunction.hpp"
#include "dfem_qfunction_dual.hpp"
#include "dfem_integrate.hpp"
namespace mfem
{
using mult_func_t = std::function<void(Vector &)>;
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields = num_solutions + num_parameters,
size_t num_kernels = mfem::tuple_size<kernels_tuple>::value,
typename autodiff_t = AutoDiff::NativeDualNumber
>
class DifferentiableOperator : public Operator
{
public:
DifferentiableOperator(DifferentiableOperator&) = delete;
DifferentiableOperator(DifferentiableOperator&&) = delete;
class Action : public Operator
{
public:
template <typename kernel_t>
void create_action_callback(kernel_t kernel, mult_func_t &func);
template<std::size_t... idx>
void materialize_callbacks(kernels_tuple &ks,
std::array<mult_func_t, num_kernels>,
std::index_sequence<idx...> const&)
{
(create_action_callback(mfem::get<idx>(ks), funcs[idx]), ...);
}
Action(DifferentiableOperator &op, kernels_tuple &ks) : op(op)
{
materialize_callbacks(ks, funcs,
std::make_index_sequence<mfem::tuple_size<kernels_tuple>::value>());
}
void Mult(const Vector &x, Vector &y) const
{
prolongation(op.solutions, x, solutions_l);
residual_e = 0.0;
for (const auto &f : funcs)
{
f(residual_e);
}
prolongation_transpose(residual_l, y);
y.SetSubVector(op.ess_tdof_list, 0.0);
}
void SetParameters(std::vector<Vector *> p) const
{
MFEM_ASSERT(num_parameters == p.size(),
"number of parameters doesn't match descriptors");
for (int i = 0; i < num_parameters; i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
// parameters_l[i].MakeRef(p[i], 0, p[i]->Size());
}
}
protected:
DifferentiableOperator &op;
std::array<mult_func_t, num_kernels> funcs;
std::function<void(Vector &, Vector &)> prolongation_transpose;
mutable std::array<Vector, num_solutions> solutions_l;
mutable std::array<Vector, num_parameters> parameters_l;
mutable Vector residual_l;
mutable std::array<Vector, num_fields> fields_e;
mutable Vector residual_e;
};
template <size_t derivative_idx>
class Derivative : public Operator
{
public:
template <typename kernel_t>
void create_callback(kernel_t kernel, mult_func_t &func);
template<std::size_t... idx>
void materialize_callbacks(kernels_tuple &ks,
std::array<mult_func_t, num_kernels>,
std::index_sequence<idx...> const&)
{
(create_callback(mfem::get<idx>(ks), funcs[idx]), ...);
}
Derivative(
DifferentiableOperator &op,
std::array<Vector *, num_solutions> &solutions,
std::array<Vector *, num_parameters> &parameters,
kernels_tuple &ks) : op(op), ks(ks)
{
for (int i = 0; i < num_solutions; i++)
{
solutions_l[i] = *solutions[i];
}
for (int i = 0; i < num_parameters; i++)
{
parameters_l[i] = *parameters[i];
}
// G
// if constexpr (std::is_same_v<OperatesOn, OperatesOnElement>)
// {
element_restriction(op.solutions, solutions_l, fields_e,
op.element_dof_ordering);
element_restriction(op.parameters, parameters_l, fields_e,
op.element_dof_ordering,
op.solutions.size());
// }
// else
// {
// MFEM_ABORT("restriction not implemented for OperatesOn");
// }
direction = op.fields[derivative_idx];
size_t derivative_action_l_size = 0;
for (auto &s : op.solutions)
{
derivative_action_l_size += GetVSize(s);
this->width += GetTrueVSize(s);
}
this->height = derivative_action_l_size;
derivative_action_l.SetSize(derivative_action_l_size);
materialize_callbacks(ks, funcs,
std::make_index_sequence<num_kernels>());
}
void Mult(const Vector &x, Vector &y) const override
{
current_direction_t = x;
current_direction_t.SetSubVector(op.ess_tdof_list, 0.0);
prolongation(direction, current_direction_t, direction_l);
derivative_action_e = 0.0;
for (const auto &f : funcs)
{
f(derivative_action_e);
}
prolongation_transpose(derivative_action_l, y);
y.SetSubVector(op.ess_tdof_list, 0.0);
}
template <typename kernel_t>
void assemble_vector_impl(kernel_t kernel, Vector &v);
template<std::size_t... idx>
void assemble_vector(
kernels_tuple &ks,
Vector &v,
std::index_sequence<idx...> const&)
{
(assemble_vector_impl(mfem::get<idx>(ks), v), ...);
}
void Assemble(Vector &v)
{
assemble_vector(ks, v, std::make_index_sequence<num_kernels>());
}
template <typename kernel_t>
void assemble_hypreparmatrix_impl(kernel_t kernel, HypreParMatrix &A);
template<std::size_t... idx>
void assemble_hypreparmatrix(
kernels_tuple &ks,
HypreParMatrix &A,
std::index_sequence<idx...> const&)
{
(assemble_hypreparmatrix_impl(mfem::get<idx>(ks), A), ...);
}
void Assemble(HypreParMatrix &A)
{
assemble_hypreparmatrix(ks, A, std::make_index_sequence<num_kernels>());
}
void AssembleDiagonal(Vector &d) const override {}
protected:
DifferentiableOperator &op;
kernels_tuple &ks;
std::array<mult_func_t, num_kernels> funcs;
std::function<void(Vector &, Vector &)> prolongation_transpose;
FieldDescriptor direction;
std::array<Vector, num_solutions> solutions_l;
std::array<Vector, num_parameters> parameters_l;
mutable Vector direction_l;
mutable Vector derivative_action_l;
mutable std::array<Vector, num_fields> fields_e;
mutable Vector direction_e;
mutable Vector derivative_action_e;
mutable Vector current_direction_t;
};
DifferentiableOperator(std::array<FieldDescriptor, num_solutions> s,
std::array<FieldDescriptor, num_parameters> p,
kernels_tuple ks,
ParMesh &m,
autodiff_t ad = AutoDiff::NativeDualNumber{}) :
kernels(ks),
mesh(m),
dim(mesh.Dimension()),
solutions(s),
parameters(p)
{
for (int i = 0; i < num_solutions; i++)
{
fields[i] = solutions[i];
}
for (int i = 0; i < num_parameters; i++)
{
fields[i + num_solutions] = parameters[i];
}
residual.reset(new Action(*this, kernels));
}
void SetParameters(std::vector<Vector *> p) const
{
residual->SetParameters(p);
}
void Mult(const Vector &x, Vector &y) const override
{
residual->Mult(x, y);
}
template <int derivative_idx>
std::shared_ptr<Derivative<derivative_idx>>
GetDerivativeWrt(std::array<Vector *, num_solutions> solutions,
std::array<Vector *, num_parameters> parameters)
{
return std::shared_ptr<Derivative<derivative_idx>>(
new Derivative<derivative_idx>(*this, solutions, parameters, kernels));
}
void SetEssentialTrueDofs(const Array<int> &l)
{
l.Copy(ess_tdof_list);
}
kernels_tuple kernels;
ParMesh &mesh;
const int dim;
std::array<FieldDescriptor, num_solutions> solutions;
std::array<FieldDescriptor, num_parameters> parameters;
// solutions and parameters
std::array<FieldDescriptor, num_fields> fields;
int residual_lsize = 0;
mutable std::array<Vector, num_solutions> current_state_l;
mutable Vector direction_l;
mutable Vector current_direction_t;
Array<int> ess_tdof_list;
static constexpr ElementDofOrdering element_dof_ordering =
ElementDofOrdering::LEXICOGRAPHIC;
static constexpr DofToQuad::Mode doftoquad_mode =
DofToQuad::Mode::TENSOR;
// static constexpr ElementDofOrdering element_dof_ordering =
// ElementDofOrdering::NATIVE;
// static constexpr DofToQuad::Mode doftoquad_mode =
// DofToQuad::Mode::FULL;
std::shared_ptr<Action> residual;
};
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels,
typename autodiff_t
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels,
autodiff_t>::Action::create_action_callback(
kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = kernel.integration_rule.GetNPoints();
// All solutions T-vector sizes make up the width of the operator, since
// they are explicitly provided in Mult() for example.
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
op.height = 1;
}
else
{
op.height = op.residual_lsize;
}
residual_l.SetSize(op.residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
op.element_dof_ordering);
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
op.element_dof_ordering,
op.solutions.size());
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
input_fops,
wrapped_fields_e,
e,
std::make_index_sequence<kernel.num_kinputs> {});
// These functions don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// tic();
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
// tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
residual_l = ye_mem;
}
else
{
R->MultTranspose(ye_mem, residual_l);
}
};
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
else if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels,
typename autodiff_t
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels,
autodiff_t>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = kernel.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
bool with_derivatives = true;
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
if (no_kinput_is_dependent)
{
return;
}
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering);
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
input_fops,
wrapped_fields_e,
e,
std::make_index_sequence<kernel.num_kinputs> {});
auto direction_shmem = load_direction_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::DIRECTION],
shmem_info.direction_size,
wrapped_direction_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto shadow_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::SHADOW],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
scratch_mem, kinput_is_dependent,
std::make_index_sequence<kernel.num_kinputs> {});
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
if constexpr (std::is_same_v<autodiff_t, AutoDiff::EnzymeForward>)
{
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
apply_kernel_fwddiff_enzyme(
r,
kernel.func,
kernel_args,
kernel_shadow_args,
input_shmem,
shadow_shmem,
q);
}
else if constexpr (std::is_same_v<autodiff_t, AutoDiff::NativeDualNumber>)
{
apply_kernel_native_dual(
r,
kernel.func,
kernel_args,
input_shmem,
shadow_shmem,
q);
}
else
{
static_assert(always_false<autodiff_t>, "unknown autodiff type");
}
}
}
}
MFEM_SYNC_THREAD;
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
} // namespace mfem
-79
View File
@@ -1,79 +0,0 @@
#include "dfem_util.hpp"
namespace mfem
{
template <typename func_t, typename input_t, typename output_t, typename dependency_map_t>
struct ElementOperator;
template <typename func_t, typename... input_ts, typename... output_ts, typename dependency_map_t>
struct ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>, dependency_map_t>
{
using entity_t = Entity::Element;
func_t qfunc;
mfem::tuple<input_ts...> inputs;
mfem::tuple<output_ts...> outputs;
dependency_map_t dependency_map;
using qf_param_ts = typename create_function_signature<
decltype(&func_t::operator())>::type::parameter_ts;
using qf_output_t = typename create_function_signature<
decltype(&func_t::operator())>::type::return_t;
static constexpr size_t num_inputs =
mfem::tuple_size<decltype(inputs)>::value;
static constexpr size_t num_outputs =
mfem::tuple_size<decltype(outputs)>::value;
ElementOperator(func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs)
: qfunc(qfunc), inputs(inputs), outputs(outputs),
dependency_map(make_dependency_map(inputs))
{
// Consistency checks
if constexpr (num_outputs > 1)
{
static_assert(always_false<func_t>,
"more than one output per kernel is not supported right now");
}
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
static_assert(num_qfinputs == num_inputs,
"kernel function inputs and descriptor inputs have to match");
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
static_assert(num_qf_outputs == num_qf_outputs,
"kernel function outputs and descriptor outputs have to match");
}
};
template <typename func_t, typename... input_ts, typename... output_ts>
ElementOperator(func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>)
-> ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>,
decltype(make_dependency_map(std::declval<mfem::tuple<input_ts...>>()))>;
// template <typename func_t, typename input_t, typename output_t>
// struct BoundaryElementOperator : public
// ElementOperator<func_t, input_t, output_t>
// {
// public:
// using entity_t = Entity::BoundaryElement;
// BoundaryElementOperator(func_t func, input_t inputs, output_t outputs)
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
// };
// template <typename func_t, typename input_t, typename output_t>
// struct FaceOperator : public
// ElementOperator<func_t, input_t, output_t>
// {
// public:
// using entity_t = Entity::Face;
// FaceOperator(func_t func, input_t inputs, output_t outputs)
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
// };
} // namespace mfem
-227
View File
@@ -1,227 +0,0 @@
#pragma once
#include <string>
namespace mfem
{
template <int FIELD_ID = -1>
class FieldOperator
{
public:
constexpr FieldOperator(int size_on_qp = 0) :
size_on_qp(size_on_qp) {};
static constexpr int GetFieldId() { return FIELD_ID; }
int size_on_qp = -1;
int dim = -1;
int vdim = -1;
};
template <int FIELD_ID = -1>
class None : public FieldOperator<FIELD_ID>
{
public:
constexpr None() : FieldOperator<FIELD_ID>() {}
};
template< typename T >
struct is_none_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_none_fop<None<FIELD_ID>>
{
static const bool value = true;
};
template <typename T>
struct DisableAD
{
T& operator()() const { return fop; }
T fop;
};
class Weight : public FieldOperator<-1>
{
public:
constexpr Weight() : FieldOperator<-1>() {};
};
template< typename T >
struct is_weight_fop
{
static const bool value = false;
};
template <>
struct is_weight_fop<Weight>
{
static const bool value = true;
};
template <int FIELD_ID = -1>
class Value : public FieldOperator<FIELD_ID>
{
public:
constexpr Value() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_value_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_value_fop<Value<FIELD_ID>>
{
static const bool value = true;
};
template <typename T>
struct is_value_fop<DisableAD<T>>
{
static const bool value = is_value_fop<T>::value;
};
template <int FIELD_ID = -1>
class Gradient : public FieldOperator<FIELD_ID>
{
public:
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_gradient_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_gradient_fop<Gradient<FIELD_ID>>
{
static const bool value = true;
};
// class FieldOperator
// {
// public:
// FieldOperator(std::string field_label = "", int size_on_qp = 0) :
// field_label(field_label),
// size_on_qp(size_on_qp) {};
// std::string field_label;
// int size_on_qp = -1;
// int dim = -1;
// int vdim = -1;
// };
// class None : public FieldOperator
// {
// public:
// None(std::string field_label) :
// FieldOperator(field_label) {}
// };
// class Weight : public FieldOperator
// {
// public:
// Weight() : FieldOperator("quadrature_weights") {};
// };
// class Value : public FieldOperator
// {
// public:
// Value(std::string field_label) : FieldOperator(field_label) {};
// };
// class Gradient : public FieldOperator
// {
// public:
// Gradient(std::string field_label) : FieldOperator(field_label) {};
// };
// class Curl : public FieldOperator
// {
// public:
// Curl(std::string field_label) : FieldOperator(field_label) {};
// };
// class Div : public FieldOperator
// {
// public:
// Div(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceValueLeft : public FieldOperator
// {
// public:
// FaceValueLeft(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceValueRight : public FieldOperator
// {
// public:
// FaceValueRight(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceNormal : public FieldOperator
// {
// public:
// FaceNormal(std::string field_label) : FieldOperator(field_label) {};
// };
// class One : public FieldOperator
// {
// public:
// One(std::string field_label) : FieldOperator(field_label) {};
// };
// namespace BareFieldOperator
// {
// struct Base
// {
// Base(FieldOperator &o)
// {
// size_on_qp = o.size_on_qp;
// dim = o.dim;
// vdim = o.vdim;
// };
// int size_on_qp = -1;
// int dim = -1;
// int vdim = -1;
// };
// struct None : Base
// {
// None(FieldOperator &o) : Base(o) {}
// };
// struct Weight : Base
// {
// Weight(FieldOperator &o) : Base(o) {}
// };
// struct Value : Base
// {
// Value(FieldOperator &o) : Base(o) {}
// };
// struct Gradient : Base
// {
// Gradient(FieldOperator &o) : Base(o) {}
// };
// }
} // namespace mfem
-290
View File
@@ -1,290 +0,0 @@
#pragma once
#include "dfem_util.hpp"
#include <type_traits>
namespace mfem
{
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_impl(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq)
{
auto B = dtq.B;
auto G = dtq.G;
// assuming the quadrature point residual has to "play nice with
// the test function"
if constexpr (std::is_same_v<std::decay_t<output_t>, Value<>>)
{
const auto [num_qp, cdim, num_dof] = B.GetShape();
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
double acc = 0.0;
for (int qp = 0; qp < num_qp; qp++)
{
acc += B(qp, 0, dof) * f(vd, 0, qp);
}
y(dof, vd) += acc;
}
}
}
else if constexpr (
std::is_same_v<std::decay_t<output_t>, Gradient<>>)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = output.vdim;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
double acc = 0.0;
for (int d = 0; d < dim; d++)
{
for (int qp = 0; qp < num_qp; qp++)
{
acc += G(qp, d, dof) * f(vd, d, qp);
}
}
y(dof, vd) += acc;
}
}
}
// else if constexpr (std::is_same_v<std::decay_t<output_t>, One>)
// {
// // This is the "integral over all quadrature points type" applying
// // B = 1 s.t. B^T * C \in R^1.
// const auto [a, b, num_qp] = B.GetShape();
// auto cc = Reshape(&c(0, 0, 0), num_qp);
// for (int i = 0; i < num_qp; i++)
// {
// y(0, 0) += cc(i);
// }
// }
else if constexpr (
std::is_same_v<std::decay_t<output_t>, None<>>)
{
const auto [vdim, dim, num_qp] = G.GetShape();
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
auto yy = Reshape(&y(0, 0), num_qp * vdim);
for (int i = 0; i < num_qp * vdim; i++)
{
yy(i) = cc(i);
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq,
std::array<DeviceTensor<1>, 6> &scratch_mem)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
double acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
double acc = 0.0;
for (int qy = 0; qy < q1d; qy++)
{
acc += s0(qz, qy, dx) * B(qy, 0, dy);
}
s1(qz, dy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
double acc = 0.0;
for (int qz = 0; qz < q1d; qz++)
{
acc += s1(qz, dy, dx) * B(qz, 0, dz);
}
yd(dx, dy, dz, vd) += acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = G.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qx = 0; qx < q1d; qx++)
{
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = uvw[0];
s1(qz, qy, dx) = uvw[1];
s2(qz, qy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qy = 0; qy < q1d; qy++)
{
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
}
s3(qz, dy, dx) = uvw[0];
s4(qz, dy, dx) = uvw[1];
s5(qz, dy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qz = 0; qz < q1d; qz++)
{
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
}
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_none_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
for (int sq = 0; sq < output.size_on_qp; sq++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
}
}
template <typename T = NonTensorProduct, typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq,
std::array<DeviceTensor<1>, 6> &scratch_mem)
{
if constexpr (std::is_same_v<T, NonTensorProduct>)
{
map_quadrature_data_to_fields_impl(y, f, output, dtq);
}
else if constexpr (std::is_same_v<T, TensorProduct>)
{
map_quadrature_data_to_fields_tensor_impl(y, f, output, dtq, scratch_mem);
}
}
}
-403
View File
@@ -1,403 +0,0 @@
#pragma once
#include "dfem_util.hpp"
namespace mfem
{
template <typename field_operator_t>
MFEM_HOST_DEVICE inline
void map_field_to_quadrature_data_tensor_product(
DeviceTensor<2> &field_qp,
const DofToQuadMap &dtq,
const DeviceTensor<1> &field_e,
const field_operator_t &input,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
{
auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
double acc = 0.0;
for (int dx = 0; dx < d1d; dx++)
{
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
}
s0(dz, dy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
double acc = 0.0;
for (int dy = 0; dy < d1d; dy++)
{
acc += s0(dz, dy, qx) * B(qy, 0, dy);
}
s1(dz, qy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
double acc = 0.0;
for (int dz = 0; dz < d1d; dz++)
{
acc += s1(dz, qy, qx) * B(qz, 0, dz);
}
fqp(vd, qx, qy, qz) = acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uv[2] = {0.0, 0.0};
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, dz, vd);
uv[0] += f * B(qx, 0, dx);
uv[1] += f * G(qx, 0, dx);
}
s0(dz, dy, qx) = uv[0];
s1(dz, dy, qx) = uv[1];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dy = 0; dy < d1d; dy++)
{
const real_t s0i = s0(dz, dy, qx);
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
uvw[1] += s0i * G(qy, 0, dy);
uvw[2] += s0i * B(qy, 0, dy);
}
s2(dz, qy, qx) = uvw[0];
s3(dz, qy, qx) = uvw[1];
s4(dz, qy, qx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dz = 0; dz < d1d; dz++)
{
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
}
fqp(vd, 0, qx, qy, qz) = uvw[0];
fqp(vd, 1, qx, qy, qz) = uvw[1];
fqp(vd, 2, qx, qy, qz) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
// TODO: Create separate function for clarity
else if constexpr (
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
// TODO: eeek
const int q1d = (int)floor(pow(num_qp, 1.0/input.dim) + 0.5);
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
f(qx, qy, qz) = w(qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_none_fop<std::decay_t<field_operator_t>>::value)
{
const int q1d = B.GetShape()[0];
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
field_qp = field;
}
else
{
static_assert(always_false<std::decay_t<field_operator_t>>,
"can't map field to quadrature data");
}
}
template <typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data(
DeviceTensor<2> field_qp,
const DofToQuadMap &dtq,
const DeviceTensor<1, const double> &field_e,
field_operator_t &input,
DeviceTensor<1, const double> integration_weights)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<field_operator_t>::value)
{
auto [num_qp, dim, num_dof] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
for (int vd = 0; vd < vdim; vd++)
{
for (int qp = 0; qp < num_qp; qp++)
{
double acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += B(qp, 0, dof) * field(dof, vd);
}
field_qp(vd, qp) = acc;
}
}
}
else if constexpr (is_gradient_fop<field_operator_t>::value)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
for (int qp = 0; qp < num_qp; qp++)
{
for (int vd = 0; vd < vdim; vd++)
{
for (int d = 0; d < dim; d++)
{
double acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += G(qp, d, dof) * field(dof, vd);
}
f(vd, d, qp) = acc;
}
}
}
}
// else if constexpr (std::is_same_v<field_operator_t, FaceNormal>)
// {
// auto normal = geometric_factors.normal;
// auto [num_qp, dim, num_entities] = normal.GetShape();
// auto f = Reshape(&field_qp[0], dim, num_qp);
// for (int qp = 0; qp < num_qp; qp++)
// {
// for (int d = 0; d < dim; d++)
// {
// f(d, qp) = normal(qp, d, entity_idx);
// }
// }
// }
// TODO: Create separate function for clarity
else if constexpr (std::is_same_v<field_operator_t, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
auto f = Reshape(&field_qp[0], num_qp);
for (int qp = 0; qp < num_qp; qp++)
{
f(qp) = integration_weights(qp);
}
}
else if constexpr (is_none_fop<field_operator_t>::value)
{
auto [num_qp, unused, num_dof] = B.GetShape();
const int size_on_qp = input.size_on_qp;
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
for (int i = 0; i < size_on_qp * num_qp; i++)
{
f(i) = field(i);
}
}
else
{
static_assert(always_false<field_operator_t>,
"can't map field to quadrature data");
}
}
template <typename T = NonTensorProduct, typename field_operator_ts, size_t num_inputs, size_t num_fields>
MFEM_HOST_DEVICE inline
void map_fields_to_quadrature_data(
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
const std::array<DeviceTensor<1>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
const std::array<int, num_inputs> &input_to_field,
const field_operator_ts &fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem)
{
for_constexpr<num_inputs>([&](auto i)
{
if constexpr (std::is_same_v<T, TensorProduct>)
{
map_field_to_quadrature_data_tensor_product(
fields_qp[i],
dtqmaps[i],
fields_e[input_to_field[i]],
mfem::get<i>(fops),
integration_weights,
scratch_mem);
}
else
{
map_field_to_quadrature_data(
fields_qp[i],
dtqmaps[i],
fields_e[i],
mfem::get<i>(fops),
integration_weights);
}
});
}
template <typename T, typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data_conditional(
DeviceTensor<2> &field_qp,
const DeviceTensor<1> &field_e,
const DofToQuadMap &dtqmap,
field_operator_t &fop,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const bool &condition)
{
if (condition)
{
if constexpr (std::is_same_v<T, TensorProduct>)
{
map_field_to_quadrature_data_tensor_product(field_qp, dtqmap,
field_e, fop,
integration_weights,
scratch_mem);
}
else
{
map_field_to_quadrature_data(field_qp, dtqmap, field_e, fop,
integration_weights);
}
}
}
template <typename T = NonTensorProduct, size_t num_fields, size_t num_kinputs, typename field_operator_ts, std::size_t... i>
MFEM_HOST_DEVICE
void map_fields_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_kinputs> &fields_qp,
const std::array<DeviceTensor<1, const double>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_kinputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_kinputs> &conditions,
std::index_sequence<i...>)
{
(map_field_to_quadrature_data_conditional<T>(fields_qp[i],
fields_e[i],
dtqmaps[i],
mfem::get<i>(fops),
integration_weights,
scratch_mem,
conditions[i]),
...);
}
template <typename T = NonTensorProduct, size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
const DeviceTensor<1> &direction_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_inputs> &conditions)
{
for_constexpr<num_inputs>([&](auto i)
{
map_field_to_quadrature_data_conditional<T>(directions_qp[i],
direction_e,
dtqmaps[i],
mfem::get<i>(fops),
integration_weights,
scratch_mem,
conditions[i]);
});
}
}
-99
View File
@@ -1,99 +0,0 @@
#pragma once
#include <mfem.hpp>
namespace mfem
{
class ParametricSpace
{
public:
ParametricSpace(int spatial_dim, int local_size, int element_size,
int total_size) :
spatial_dim(spatial_dim),
local_size(local_size),
element_size(element_size),
total_size(total_size),
identity(total_size)
{
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
dtq.nqpt = dtq.ndof;
}
ParametricSpace(int local_size) :
local_size(local_size),
element_size(local_size),
total_size(local_size),
identity(local_size)
{
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
dtq.nqpt = dtq.ndof;
}
int Dimension() const
{
return spatial_dim;
}
int GetLocalSize() const
{
return local_size;
}
int GetElementSize() const
{
return element_size;
}
int GetTotalSize() const
{
return total_size;
}
const DofToQuad &GetDofToQuad() const
{
return dtq;
}
const Operator *GetProlongation() const
{
return &identity;
}
const Operator *GetRestriction() const
{
return &identity;
}
private:
int spatial_dim;
// Hint for the local dimension. E.g. the size on the quadrature point or vdim.
int local_size;
// Size of the data on an element
int element_size;
int total_size;
IdentityOperator identity;
DofToQuad dtq;
};
class ParametricFunction : public Vector
{
public:
ParametricFunction(ParametricSpace &space) :
Vector(space.GetTotalSize()),
space(space)
{}
ParametricSpace &space;
using Vector::operator=;
};
}
-253
View File
@@ -1,253 +0,0 @@
#pragma once
#include "dfem_util.hpp"
#ifdef MFEM_USE_ENZYME
#include <enzyme/utils>
#include <enzyme/enzyme>
#endif
namespace mfem
{
template <typename T0, typename T1>
MFEM_HOST_DEVICE
void process_kf_arg(const T0 &, T1 &)
{
static_assert(always_false<T0, T1>,
"process_kf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
T &arg)
{
arg = u(0);
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
internal::tensor<T> &arg)
{
arg(0) = u(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<T, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i) = u(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE
void process_kf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
process_kf_arg(u_qp, arg);
}
template <size_t num_fields, typename kf_args, std::size_t... i>
MFEM_HOST_DEVICE
void process_kf_args(
const std::array<DeviceTensor<2>, num_fields> &u,
kf_args &args,
const int &qp,
std::index_sequence<i...>)
{
(process_kf_arg(u[i], mfem::get<i>(args), qp), ...);
}
template <typename T0, typename T1> inline
Vector process_kf_result(T0, T1)
{
static_assert(always_false<T0, T1>,
"process_kf_result not implemented for result type");
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const double &x)
{
r(0) = x;
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T> &x)
{
r(0) = x(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j);
}
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
double &arg)
{
arg = u(0);
}
template <int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<double, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
template <typename kernel_func_t, typename kernel_args_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel(
DeviceTensor<1, double> &f_qp,
const kernel_func_t &kf,
kernel_args_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
int qp)
{
process_kf_args(u, args, qp,
std::make_index_sequence<mfem::tuple_size<kernel_args_ts>::value> {});
process_kf_result(f_qp, mfem::get<0>(mfem::apply(kf, args)));
}
#ifdef MFEM_USE_ENZYME
// Version for active function arguments only
//
// This is an Enzyme regression and can be removed in later versions.
template <typename kernel_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<>)
{
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
return __enzyme_fwddiff<kf_return_t>(
+kernel, enzyme_dup, &mfem::get<Is>(args)..., enzyme_interleave,
&mfem::get<Is>(shadow_args)...);
}
// Interleave function arguments for enzyme
template <typename kernel_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts, std::size_t... Js>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<Js...>)
{
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
return __enzyme_fwddiff<kf_return_t>(
+kernel, enzyme_dup, &std::get<Is>(args)..., enzyme_const,
&mfem::get<Js>(inactive_args)..., enzyme_interleave,
&mfem::get<Is>(shadow_args)...);
}
template <typename kernel_t, typename arg_ts, typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
inactive_arg_ts &&inactive_args)
{
auto arg_indices = std::make_index_sequence<
mfem::tuple_size<std::remove_reference_t<arg_ts>>::value> {};
auto inactive_arg_indices = std::make_index_sequence<
mfem::tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
return fwddiff_apply_enzyme_indexed(kernel, args, shadow_args, arg_indices,
inactive_args, inactive_arg_indices);
}
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_fwddiff_enzyme(
DeviceTensor<1, double> &f_qp,
const kf_t &kf,
kernel_arg_ts &args,
kernel_arg_ts &shadow_args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
int qp_idx)
{
process_kf_args(u, args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
process_kf_args(v, shadow_args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
process_kf_result(f_qp,
mfem::get<0>(fwddiff_apply_enzyme(kf, args, shadow_args, mfem::tuple<> {})));
}
#endif // MFEM_USE_ENZYME
} // namespace mfem
-187
View File
@@ -1,187 +0,0 @@
#pragma once
#include "dfem_util.hpp"
#include "dfem_qfunction.hpp"
namespace mfem
{
MFEM_HOST_DEVICE
template <typename T0, typename T1, typename T2>
void process_kf_arg(const T0 &, const T1 &, T2 &)
{
static_assert(always_false<T0, T1, T2>,
"process_kf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
T &arg)
{
arg = u(0);
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<internal::dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
}
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
internal::dual<T, T> &arg)
{
arg.value = u(0);
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::dual<T, T> &arg)
{
arg.value = u(0);
arg.gradient = v(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<internal::dual<T, T>, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i).value = u(i);
arg(i).gradient = v(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<internal::dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
arg(j, i).gradient = v((i * m) + j);
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).value;
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).value;
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<2> &u,
const DeviceTensor<2> &v,
arg_type &arg,
const int &qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
process_kf_arg(u_qp, v_qp, arg);
}
template <size_t num_args, typename kf_args, std::size_t... Is>
MFEM_HOST_DEVICE inline
void process_kf_args(
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
kf_args &args,
const int &qp,
std::index_sequence<Is...>)
{
(process_kf_arg(u[Is], v[Is], mfem::get<Is>(args), qp), ...);
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).gradient;
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).gradient;
}
}
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_native_dual(
DeviceTensor<1, double> &f_qp,
const kf_t &kf,
kernel_arg_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
const int &qp_idx)
{
process_kf_args(u, v, args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
auto r = mfem::get<0>(mfem::apply(kf, args));
process_derivative_from_native_dual(f_qp, r);
}
} // namespace mfem
-530
View File
@@ -1,530 +0,0 @@
#pragma once
#include <mfem.hpp>
#include <utility>
#include "dfem_interpolate.hpp"
#include "dfem_integrate.hpp"
#include "dfem_qfunction.hpp"
#include "dfem_qfunction_dual.hpp"
#include "examples/dfem/dfem_util.hpp"
namespace mfem
{
class DerivativeOperator : public Operator
{
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
std::vector<Vector> &)>;
public:
DerivativeOperator(
const std::vector<derivative_action_t> &derivative_actions,
const FieldDescriptor &direction,
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_l,
const std::vector<restriction_callback_t> &restriction_callbacks,
const std::function<void(Vector &, Vector &)> prolongation_transpose) :
derivative_actions(derivative_actions),
direction(direction),
restriction_callbacks(restriction_callbacks),
derivative_action_l(GetVSize(direction)),
prolongation_transpose(prolongation_transpose)
{
MFEM_ASSERT(derivative_actions.size() == restriction_callbacks.size(),
"internal error");
derivative_action_l = 0.0;
this->solutions_l.resize(solutions_l.size());
this->parameters_l.resize(parameters_l.size());
for (int i = 0; i < solutions_l.size(); i++)
{
this->solutions_l[i] = *solutions_l[i];
}
for (int i = 0; i < parameters_l.size(); i++)
{
this->parameters_l[i] = *parameters_l[i];
}
fields_e.resize(solutions_l.size() + parameters_l.size());
}
void Mult(const Vector &x, Vector &y) const override
{
direction_t = x;
direction_t.SetSubVector(ess_tdof_list, 0.0);
prolongation(direction, direction_t, direction_l);
for (int i = 0; i < derivative_actions.size(); i++)
{
restriction_callbacks[i](solutions_l, parameters_l, fields_e);
derivative_actions[i](fields_e, direction_l, derivative_action_l);
}
prolongation_transpose(derivative_action_l, y);
y.SetSubVector(ess_tdof_list, 0.0);
};
private:
std::vector<derivative_action_t> derivative_actions;
mutable std::vector<Vector> solutions_l;
std::vector<Vector> parameters_l;
FieldDescriptor direction;
mutable Vector direction_t;
mutable Vector direction_e;
mutable Vector direction_l;
mutable Vector derivative_action_e;
mutable Vector derivative_action_l;
mutable std::vector<Vector> fields_e;
Array<int> ess_tdof_list;
std::vector<restriction_callback_t> restriction_callbacks;
std::function<void(Vector &, Vector &)> prolongation_transpose;
};
class DifferentiableOperator : public Operator
{
using action_t =
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
std::vector<Vector> &)>;
public:
DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh);
void Mult(const Vector &x, Vector &y) const override
{
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
prolongation(solutions, x, solutions_l);
for (auto &action : action_callbacks)
{
action(solutions_l, parameters_l, residual_l);
}
prolongation_transpose(residual_l, y);
y.SetSubVector(ess_tdof_list, 0.0);
}
template <
typename func_t,
typename... input_ts,
typename... output_ts,
typename derivative_indices_t>
void AddDomainIntegrator(
func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs,
const IntegrationRule &integration_rule,
const derivative_indices_t derivative_indices = {});
void SetParameters(std::vector<Vector *> p) const;
std::shared_ptr<DerivativeOperator> GetDerivative(
size_t derivative_idx,
std::vector<Vector *> solutions_l,
std::vector<Vector *> parameters_l)
{
MFEM_ASSERT(derivative_action_callbacks.find(derivative_idx) !=
derivative_action_callbacks.end(),
"no derivative action has been found for index " << derivative_idx);
return std::make_shared<DerivativeOperator>(
derivative_action_callbacks[derivative_idx],
fields[derivative_idx],
solutions_l,
parameters_l,
restriction_callbacks,
prolongation_transpose);
}
private:
const ParMesh &mesh;
std::vector<action_t> action_callbacks;
std::map<size_t, std::vector<derivative_action_t>> derivative_action_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
// solutions and parameters
std::vector<FieldDescriptor> fields;
Array<int> ess_tdof_list;
mutable std::vector<Vector> solutions_l;
mutable std::vector<Vector> parameters_l;
mutable Vector residual_l;
mutable std::vector<Vector> fields_e;
mutable Vector residual_e;
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::vector<restriction_callback_t> restriction_callbacks;
};
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
{
MFEM_ASSERT(parameters.size() == p.size(),
"number of parameters doesn't match descriptors");
for (int i = 0; i < parameters.size(); i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
}
}
DifferentiableOperator::DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh) :
mesh(mesh),
solutions(solutions),
parameters(parameters)
{
fields.resize(solutions.size() + parameters.size());
fields_e.resize(fields.size());
solutions_l.resize(solutions.size());
parameters_l.resize(parameters.size());
for (int i = 0; i < solutions.size(); i++)
{
fields[i] = solutions[i];
}
for (int i = 0; i < parameters.size(); i++)
{
fields[i + solutions.size()] = parameters[i];
}
}
template <
typename func_t,
typename... input_ts,
typename... output_ts,
typename derivative_indices_t = std::make_index_sequence<0>>
void DifferentiableOperator::AddDomainIntegrator(
func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs,
const IntegrationRule &integration_rule,
const derivative_indices_t derivative_indices)
{
using entity_t = Entity::Element;
static constexpr size_t num_inputs =
mfem::tuple_size<decltype(inputs)>::value;
static constexpr size_t num_outputs =
mfem::tuple_size<decltype(outputs)>::value;
using qf_param_ts = typename create_function_signature<
decltype(&func_t::operator())>::type::parameter_ts;
using qf_output_t = typename create_function_signature<
decltype(&func_t::operator())>::type::return_t;
// Consistency checks
if constexpr (num_outputs > 1)
{
static_assert(always_false<func_t>,
"more than one output per kernel is not supported right now");
}
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
static_assert(num_qfinputs == num_inputs,
"kernel function inputs and descriptor inputs have to match");
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
static_assert(num_qf_outputs == num_qf_outputs,
"kernel function outputs and descriptor outputs have to match");
constexpr auto field_tuple = std::tuple_cat(std::tuple<input_ts...> {},
std::tuple<output_ts...> {});
constexpr auto filtered_field_tuple = filter_fields(field_tuple);
constexpr size_t num_fields = count_unique_field_ids(filtered_field_tuple);
constexpr auto dependency_map = make_dependency_map(mfem::tuple<input_ts...> {});
// Create the action callback
auto input_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
inputs,
std::make_index_sequence<num_inputs> {});
auto output_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
outputs,
std::make_index_sequence<num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = output_to_field[hardcoded_output_idx];
ElementDofOrdering element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::TENSOR;
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
// The explicit captures are necessary to avoid dependency on
// the specific instance of this class (this pointer).
auto restriction_callback =
[=, solutions = this->solutions, parameters = this->parameters]
(std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
std::vector<Vector> &fields_e)
{
restriction<entity_t>(solutions, solutions_l, fields_e,
element_dof_ordering);
restriction<entity_t>(parameters, parameters_l, fields_e,
element_dof_ordering,
solutions.size());
};
restriction_callbacks.push_back(restriction_callback);
auto output_fop = mfem::get<hardcoded_output_idx>(outputs);
if constexpr (is_none_fop<decltype(output_fop)>::value)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// prolongation_transpose = [&](Vector &r_local, Vector &y)
// {
// double local_sum = r_local.Sum();
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
// op.mesh.GetComm());
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
// };
// }
else
{
auto P = get_prolongation(fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
// if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// this->width = 1;
// }
// else
{
width = residual_lsize;
}
residual_l.SetSize(residual_lsize);
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp =
GetSizeOnQP<entity_t>(mfem::get<hardcoded_output_idx>(outputs),
fields[test_space_field_idx]);
auto input_dtq_maps =
create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
auto output_dtq_maps =
create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
const int test_vdim = mfem::get<hardcoded_output_idx>(outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp =
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
auto action_shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp);
Vector shmem_cache(action_shmem_info.total_size);
// print_shared_memory_info(action_shmem_info);
action_callbacks.push_back(
[=](std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
Vector &residual_l) mutable
{
restriction_callback(solutions_l, parameters_l, fields_e);
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e,
action_shmem_info.field_sizes,
num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
residual_shmem, scratch_shmem] =
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
wrapped_fields_e, num_qp, e);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem);
call_qfunction<TensorProduct, qf_param_ts>(
qfunc, input_shmem, residual_shmem,
residual_size_on_qp, num_qp, q1d);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_shmem);
}, num_entities, q1d, q1d, q1d, action_shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (is_none_fop<decltype(output_fop)>::value)
{
residual_l = residual_e;
}
else
{
R->MultTranspose(residual_e, residual_l);
}
});
for_constexpr([&](auto derivative_idx)
{
// bool is_dependent = false;
// for_constexpr<num_inputs>([&](auto input_idx)
// {
// constexpr auto input_is_dependent_on_field_idx =
// std::get<derivative_idx>(std::get<input_idx>(dependency_map));
// if constexpr (input_is_dependent_on_field_idx == 1)
// {
// is_dependent = true;
// }
// });
// if (!is_dependent)
// {
// derivative_action_callbacks[derivative_idx].push_back(
// [=](const Vector &direction_l, Vector &y) mutable
// {
// y += 0.0;
// });
// return;
// }
auto direction = fields[derivative_idx];
size_t derivative_action_l_size = GetVSize(direction);
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(outputs),
fields[test_space_field_idx]);
auto shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, derivative_idx);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
Vector direction_e;
Vector derivative_action_e(R->Height());
derivative_action_e = 0.0;
auto input_is_dependent = get_array_from_tuple(std::get<derivative_idx>
(dependency_map));
derivative_action_callbacks[derivative_idx].push_back(
[=](std::vector<Vector> &fields_e, const Vector &direction_l,
Vector &derivative_action_l) mutable
{
restriction<entity_t>(direction, direction_l, direction_e, element_dof_ordering);
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, direction_shmem,
input_shmem, shadow_shmem, residual_shmem, scratch_shmem] =
unpack_shmem(shmem, shmem_info, input_dtq_maps,
output_dtq_maps, wrapped_fields_e, wrapped_direction_e, num_qp, e);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem);
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, inputs, ir_weights,
scratch_shmem, input_is_dependent);
call_qfunction_derivative_action<TensorProduct, qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem,
da_size_on_qp, num_qp, q1d);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_shmem);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(derivative_action_e, derivative_action_l);
});
}, derivative_indices);
}
} // namespace mfem
// #include "dfem_refactor_action.hpp"
// #include "dfem_refactor_derivatives.hpp"
-232
View File
@@ -1,232 +0,0 @@
#pragma once
#include "dfem_refactor.hpp"
namespace mfem
{
template <typename element_operator_t, size_t num_fields>
void DifferentiableOperator::instantiate_action(
element_operator_t element_operator, action_t &action)
{
using entity_t = typename element_operator_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.outputs,
std::make_index_sequence<element_operator.num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
this->width = GetTrueVSize(fields[test_space_field_idx]);
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
// if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// this->width = 1;
// }
// else
{
this->width = residual_lsize;
}
residual_l.SetSize(residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(element_operator.outputs),
fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
koutput_to_field);
// auto input_fops = create_bare_fops(element_operator.inputs);
// auto output_fops = create_bare_fops(element_operator.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>
(element_operator.outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto shmem_info =
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
(input_dtq_maps,
output_dtq_maps,
fields,
num_entities,
element_operator.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
action = [=](const Vector &x, Vector &y) mutable
{
prolongation(solutions, x, solutions_l);
restriction<entity_t>(solutions, solutions_l, this->fields_e,
element_dof_ordering);
restriction<entity_t>(parameters, parameters_l, this->fields_e,
element_dof_ordering,
solutions.size());
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof,
num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e,
shmem_info.field_sizes,
num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
element_operator.inputs,
wrapped_fields_e,
e,
std::make_index_sequence<element_operator.num_inputs> {});
// These functions don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// // printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// // tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, element_operator.inputs, ir_weights,
scratch_mem,
std::make_index_sequence<element_operator.num_inputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// // tic();
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto qf_args = decay_tuple<typename element_operator_t::qf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, element_operator.qfunc, qf_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// // printf("qf elapsed: %.1fus\n", toc() * 1e6);
// // tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(element_operator.outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
{
residual_l = y;
}
else
{
R->MultTranspose(residual_e, residual_l);
}
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
{
y = residual_l;
}
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// double local_sum = residual_l.Sum();
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
// }
else
{
get_prolongation(fields[test_space_field_idx])->MultTranspose(residual_l, y);
}
};
}
}
-132
View File
@@ -1,132 +0,0 @@
#pragma once
#include "dfem_refactor.hpp"
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>)
{
((std::cout << Ints << " "), ...);
std::cout << std::endl;
}
namespace mfem
{
template <
typename element_operator_t,
size_t num_solutions,
size_t num_parameters,
size_t derivative_idx>
DerivativeOperator::DerivativeOperator(
element_operator_t element_operator,
const std::array<FieldDescriptor, num_solutions> &solutions,
const std::array<FieldDescriptor, num_parameters> &parameters,
const std::vector<FieldDescriptor> &fields,
ParMesh &mesh,
const IntegrationRule &integration_rule,
const ElementDofOrdering &element_dof_ordering,
const DofToQuad::Mode &doftoquad_mode,
std::integral_constant<size_t, derivative_idx>)
{
direction = fields[derivative_idx];
size_t derivative_action_l_size = 0;
for (auto &s : solutions)
{
derivative_action_l_size += GetVSize(s);
this->width += GetTrueVSize(s);
}
this->height = derivative_action_l_size;
derivative_action_l.SetSize(derivative_action_l_size);
constexpr size_t num_fields = num_solutions + num_parameters;
using entity_t = typename element_operator_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.outputs,
std::make_index_sequence<element_operator.num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(element_operator.outputs),
fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
koutput_to_field);
const int test_vdim = mfem::get<hardcoded_output_idx>
(element_operator.outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto input_is_dependent = std::get<derivative_idx>
(element_operator.dependency_map);
constexpr bool with_derivatives = true;
auto shmem_info =
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
(input_dtq_maps,
output_dtq_maps,
fields,
num_entities,
element_operator.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
action_callback = [=](const Vector &x, Vector &y) mutable
{
restriction<entity_t>(direction, direction_l, direction_e,
element_dof_ordering);
};
}
} // namespace mfem
-116
View File
@@ -1,116 +0,0 @@
#pragma once
#include <mfem.hpp>
class SharedMemoryManager
{
private:
struct MemoryBlock
{
char* ptr;
int size;
bool used;
};
MFEM_HOST_DEVICE static const int MAX_BLOCKS = 16;
MFEM_HOST_DEVICE static MemoryBlock blocks[MAX_BLOCKS];
MFEM_HOST_DEVICE static int num_blocks;
MFEM_HOST_DEVICE static char* base_ptr;
public:
MFEM_HOST_DEVICE static void init(void* shmem, int total_size)
{
base_ptr = static_cast<char*>(shmem);
num_blocks = 1;
blocks[0] = {base_ptr, total_size, false};
}
template<typename T>
MFEM_HOST_DEVICE static T* reserve(int n)
{
int size_bytes = n * sizeof(T);
for (int i = 0; i < num_blocks; ++i)
{
if (!blocks[i].used && blocks[i].size >= size_bytes)
{
blocks[i].used = true;
if (blocks[i].size > size_bytes)
{
// Split block
if (num_blocks < MAX_BLOCKS)
{
blocks[num_blocks] = {blocks[i].ptr + size_bytes, blocks[i].size - size_bytes, false};
++num_blocks;
blocks[i].size = size_bytes;
}
}
return reinterpret_cast<T*>(blocks[i].ptr);
}
}
return nullptr; // Allocation failed
}
MFEM_HOST_DEVICE static void release(void* ptr)
{
for (int i = 0; i < num_blocks; ++i)
{
if (blocks[i].ptr == ptr)
{
blocks[i].used = false;
return;
}
}
}
MFEM_HOST_DEVICE static void release_and_try_merge(void* ptr)
{
for (int i = 0; i < num_blocks; ++i)
{
if (blocks[i].ptr == ptr)
{
blocks[i].used = false;
merge_adjacent_free_blocks();
return;
}
}
}
private:
MFEM_HOST_DEVICE static void merge_adjacent_free_blocks()
{
// Simple bubble sort for simplicity (can be optimized)
for (int i = 0; i < num_blocks - 1; ++i)
{
for (int j = 0; j < num_blocks - i - 1; ++j)
{
if (blocks[j].ptr > blocks[j + 1].ptr)
{
MemoryBlock temp = blocks[j];
blocks[j] = blocks[j + 1];
blocks[j + 1] = temp;
}
}
}
for (int i = 0; i < num_blocks - 1; ++i)
{
if (!blocks[i].used && !blocks[i + 1].used)
{
blocks[i].size += blocks[i + 1].size;
for (int j = i + 1; j < num_blocks - 1; ++j)
{
blocks[j] = blocks[j + 1];
}
--num_blocks;
--i;
}
}
}
};
MFEM_HOST_DEVICE SharedMemoryManager::MemoryBlock
SharedMemoryManager::blocks[SharedMemoryManager::MAX_BLOCKS];
MFEM_HOST_DEVICE int SharedMemoryManager::num_blocks;
MFEM_HOST_DEVICE char* SharedMemoryManager::base_ptr;
-39
View File
@@ -1,39 +0,0 @@
#pragma once
#include "dfem_refactor.hpp"
#define DFEM_TEST_MAIN(function) \
int main(int argc, char* argv[]) \
{ \
Mpi::Init(); \
\
const char* device_config = "cpu"; \
const char* mesh_file = "../data/ref-square.mesh"; \
int polynomial_order = 1; \
int ir_order = 2; \
int refinements = 0; \
\
OptionsParser args(argc, argv); \
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); \
args.AddOption(&polynomial_order, "-o", "--order", ""); \
args.AddOption(&refinements, "-r", "--r", ""); \
args.AddOption(&ir_order, "-iro", "--iro", ""); \
args.AddOption(&device_config, "-d", "--device", \
"Device configuration string, see Device::Configure()."); \
args.ParseCheck(); \
\
Device device(device_config); \
if (Mpi::Root() == 0) \
{ \
device.Print(); \
} \
\
out << std::setprecision(12); \
\
int ret; \
\
ret = function(mesh_file, refinements, polynomial_order); \
out << #function; \
ret ? out << " FAILURE\n" : out << " OK\n"; \
\
return ret; \
}\
File diff suppressed because it is too large Load Diff
-130
View File
@@ -1,130 +0,0 @@
// SPDX-ArtifactOfProjectName: noisy
// SPDX-ArtifactOfProjectHomePage: https://github.com/VincentZalzal/noisy
// SPDX-FileCopyrightText: Copyright 2024 Vincent Zalzal
// SPDX-License-Identifier: MIT
#pragma once
#include <iomanip>
#include <iostream>
namespace vz {
struct Counters {
unsigned m_def_ctor = 0;
unsigned m_copy_ctor = 0;
unsigned m_move_ctor = 0;
unsigned m_copy_assign = 0;
unsigned m_move_assign = 0;
unsigned m_dtor = 0;
void reset() {
*this = {};
}
bool leaks() const {
return m_def_ctor + m_copy_ctor + m_move_ctor != m_dtor;
}
friend std::ostream& operator<<(std::ostream& os, const Counters& c) {
stream_counter(os, "Default constructor count: ", c.m_def_ctor );
stream_counter(os, "Copy constructor count: ", c.m_copy_ctor );
stream_counter(os, "Move constructor count: ", c.m_move_ctor );
stream_counter(os, "Copy assignment count: ", c.m_copy_assign);
stream_counter(os, "Move assignment count: ", c.m_move_assign);
stream_counter(os, "Destructor count: ", c.m_dtor );
return os;
}
friend bool operator==(const Counters& lhs, const Counters& rhs) {
return
lhs.m_def_ctor == rhs.m_def_ctor &&
lhs.m_copy_ctor == rhs.m_copy_ctor &&
lhs.m_move_ctor == rhs.m_move_ctor &&
lhs.m_copy_assign == rhs.m_copy_assign &&
lhs.m_move_assign == rhs.m_move_assign &&
lhs.m_dtor == rhs.m_dtor ;
}
friend bool operator!=(const Counters& lhs, const Counters& rhs) { return !(lhs == rhs); }
private:
static void stream_counter(std::ostream& os, const char* msg, unsigned value) {
if (value != 0)
os << msg << std::setw(2) << value << '\n';
}
};
namespace detail {
struct Globals {
~Globals() {
if (m_verbose)
std::cout << "\n===== Noisy counters =====\n" << m_counters;
}
Counters m_counters;
unsigned m_next_id = 0;
bool m_verbose = true;
};
}
class Noisy {
private:
static detail::Globals& globals() {
static detail::Globals s_globals;
return s_globals;
}
public:
static Counters& counters() { return globals().m_counters; }
static void set_verbose(bool verbose) { globals().m_verbose = verbose; }
Noisy() {
if (globals().m_verbose)
std::cout << *this << ": default constructor\n";
globals().m_counters.m_def_ctor++;
}
Noisy(const Noisy& other) {
if (globals().m_verbose)
std::cout << *this << ": copy constructor from " << other << '\n';
globals().m_counters.m_copy_ctor++;
}
Noisy(Noisy&& other) noexcept {
if (globals().m_verbose)
std::cout << *this << ": move constructor from " << other << '\n';
globals().m_counters.m_move_ctor++;
}
~Noisy() {
if (globals().m_verbose)
std::cout << *this << ": destructor\n";
globals().m_counters.m_dtor++;
}
Noisy& operator=(const Noisy& other) {
if (globals().m_verbose)
std::cout << *this << ": copy assignment from " << other << '\n';
globals().m_counters.m_copy_assign++;
return *this;
}
Noisy& operator=(Noisy&& other) noexcept {
if (globals().m_verbose)
std::cout << *this << ": move assignment from " << other << '\n';
globals().m_counters.m_move_assign++;
return *this;
}
unsigned id() const { return m_id; }
friend std::ostream& operator<<(std::ostream& os, const Noisy& noisy) { return os << "Noisy(" << std::setw(2) << noisy.m_id << ')'; }
private:
unsigned m_id = globals().m_next_id++;
};
}
@@ -1,188 +0,0 @@
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering, derivative_idx);
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_qfinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_qfinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_qfinput_is_dependent)
{
return;
}
// auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
// auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
// DeviceTensor<1, const double> integration_weights(
// this->op.integration_rule.GetWeights().Read(), num_qp);
// Vector zero;
// GeometricFactorMaps geometric_factors
// {
// DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
// };
// // Fields interpolated to the quadrature points in the order of
// // kernel function arguments
// auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// constexpr int fixed_output_idx = 0;
// auto Bv = output_dtq_maps[fixed_output_idx];
// auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
// const int test_vdim = mfem::get<0>(kernel.outputs).vdim;
// DeviceTensor<3> ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
// map_fields_to_quadrature_data(
// input_qp, e, this->fields_e,
// kinput_to_field, input_dtq_maps,
// integration_weights, geometric_factors, kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// map_fields_to_quadrature_data_conditional(
// directions_qp, e,
// directions_e, kinput_to_field,
// input_dtq_maps,
// integration_weights,
// geometric_factors,
// kinput_is_dependent,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// for (int qp = 0; qp < num_qp; qp++)
// {
// auto f_qp = apply_kernel_fwddiff_enzyme(
// kernel.func,
// kernel_args,
// input_qp,
// kernel_shadow_args,
// directions_qp,
// qp);
// auto r_qp = Reshape(&da_qp(0, qp, e), da_size_on_qp);
// for (int i = 0; i < da_size_on_qp; i++)
// {
// r_qp(i) = f_qp(i);
// }
// }
// DeviceTensor<3> fhat = Reshape(&da_qp(0, 0, e), test_vdim, test_op_dim, num_qp);
// DeviceTensor<2> y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
// map_quadrature_data_to_fields(y, fhat,
// output_fop,
// output_dtq_maps[hardcoded_output_idx]);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.GetData());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](Vector &r_l, Vector &y)
{
P->MultTranspose(r_l, y);
};
}
-49
View File
@@ -1,49 +0,0 @@
* Calculate shared memory requirements
* Interpolation and integration
---
* If grad involved, need B and G
* Fit largest field, depends on polynomial order (#dofs)
-> vdim is irrelevant
* Temporaries for each sum
- DDQ (d1d x d1d x q1d) x 2 -> DDQ0, DDQ1
- DQQ (d1d x q1d x q1d) x 3 -> DQQ0, DQQ1, DQQ2
- QQQ (q1d x q1d x q1d) x 3 -> QQQ0, QQQ1, QQQ2
We need the following combinations at the same time
(1) DDQ0 + DDQ1 + DQQ0 + DQQ1 + DQQ2
(2) DQQ0 + DQQ1 + DQQ2 + QQQ0 + QQQ1 + QQQ2
(3) QQQ0 + QQQ1 + QQQ2 + QQD0 + QQD1 + QQD2
(4) QQD0 + QQD1 + QQD2 + QDD0 + QDD1 + QDD2
Allocate largest memory footprint from 2, 3 or 4 and
add memory footprint of fields and B/G.
Annotations with NR and R mean "not reusable" and
"reusable", respectively. This means the memory location is
reused for _all_ e.g. interpolation of a value etc.
----
For the action of nonlinear diffusion in 2D we have
(rho * |u|^2 \nabla u, \nabla v)
* Load
RHO (D x D) | R (after interpolation)
U (D x D x VDIM) | R (after interpolation)
B (Q x D) | NR
G (Q x D) | NR
* Interpolate Value
Temporary (Q x D) | R
R (Q x Q) | NR
U (Q x Q x VDIM) | NR
* Interpolate Grad
Temporaries (Q x D) + (Q x D) | R
U (Q x Q x DIM x VDIM) | NR
Quadrature point function
-> purely thread local
* Integrate Grad
R | temp from Interpolation
R | U from Load
-845
View File
@@ -1,845 +0,0 @@
// This is serac's tuple implementation
#pragma once
#include "general/backends.hpp"
#include <utility>
#include <mfem.hpp>
#include <tuple>
namespace mfem
{
/**
* @tparam T the types stored in the tuple
* @brief This is a class that mimics most of std::tuple's interface,
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
*
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
*/
template <typename... T>
struct tuple
{
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
*/
template <typename T0>
struct tuple<T0>
{
T0 v0; ///< The first member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
*/
template <typename T0, typename T1>
struct tuple<T0, T1>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
*/
template <typename T0, typename T1, typename T2>
struct tuple<T0, T1, T2>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3>
struct tuple<T0, T1, T2, T3>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct tuple<T0, T1, T2, T3, T4>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
struct tuple<T0, T1, T2, T3, T4, T5>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
struct tuple<T0, T1, T2, T3, T4, T5, T6>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
* @tparam T7 The eighth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
T8 v8;
};
/**
* @brief Class template argument deduction rule for tuples
* @tparam T The variadic template parameter for tuple types
*/
template <typename... T>
MFEM_HOST_DEVICE
tuple(T...) -> tuple<T...>;
/**
* @brief helper function for combining a list of values into a tuple
* @tparam T types of the values to be tuple-d
* @param args the actual values to be put into a tuple
*/
template <typename... T>
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
{
return tuple<T...> {args...};
}
template <class... Types>
struct tuple_size
{
};
template <class... Types>
struct tuple_size<mfem::tuple<Types...>> :
std::integral_constant<std::size_t, sizeof...(Types)>
{
};
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a reference to the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a copy of the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @brief a function intended to be used for extracting the ith type from a tuple.
*
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
*
* @tparam i the index of the tuple to query
* @tparam T the types stored in the tuple
* @param values the tuple of values
* @return a copy of the ith entry of the input
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @brief A helper function for the + operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple sum
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) + get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise sum of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return plus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the += operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be incremented
* @param y tuple of increment values
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) += get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
const tuple<T...>& y)
{
return plus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the -= operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be subracted from
* @param y tuple of values to subtract from x
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) -= get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
const tuple<T...>& y)
{
return minus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple difference
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) - get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise difference of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return minus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @return the returned tuple difference
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
std::integer_sequence<int, i...>)
{
return tuple{-get<i>(x)...};
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
{
return unary_minus_helper(x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple ratio
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise division of x by y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return div_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant numerator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const double a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a / get<i>(x)...};
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant denomenator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
const double a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / a...};
}
/**
* @tparam T the types stored in the tuple x
* @param a the numerator
* @param x a tuple of denominator values
* @brief return a tuple of values defined by division of a by the elements of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const double a, const tuple<T...>& x)
{
return div_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of numerator values
* @param a a denominator
* @brief return a tuple of values defined by elementwise division of x by a
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const double a)
{
return div_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple product
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise multiplication of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return mult_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const double a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a * get<i>(x)...};
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
const double a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * a...};
}
/**
* @tparam T the types stored in the tuple
* @param a a scaling factor
* @param x the tuple object
* @brief multiply each component of x by the value a on the left
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const double a, const tuple<T...>& x)
{
return mult_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @param x the tuple object
* @param a a scaling factor
* @brief multiply each component of x by the value a on the right
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const double a)
{
return mult_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @tparam i a list of indices used to acces each element of the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief helper used to implement printing a tuple of values
*/
template <typename... T, std::size_t... i>
auto& print_helper(std::ostream& out, const mfem::tuple<T...>& A,
std::integer_sequence<size_t, i...>)
{
out << "tuple{";
(..., (out << (i == 0 ? "" : ", ") << mfem::get<i>(A)));
out << "}";
return out;
}
/**
* @tparam T the types stored in the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief print a tuple of values
*/
template <typename... T>
auto& operator<<(std::ostream& out, const mfem::tuple<T...>& A)
{
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
}
/**
* @brief A helper to apply a lambda to a tuple
*
* @tparam lambda The functor type
* @tparam T The tuple types
* @tparam i The integer sequence to i
* @param f The functor to apply to the tuple
* @param args The input tuple
* @return The functor output
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @overload
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief a struct used to determine the type at index I of a tuple
*
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
*
* @tparam I the index of the desired type
* @tparam T a tuple of different types
*/
template <size_t I, class T>
struct tuple_element;
// recursive case
/// @overload
template <size_t I, class Head, class... Tail>
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
tuple<Tail...>>
{
};
// base case
/// @overload
template <class Head, class... Tail>
struct tuple_element<0, tuple<Head, Tail...>>
{
using type = Head; ///< the type at the specified index
};
/**
* @brief Trait for checking if a type is a @p mfem::tuple
*/
template <typename T>
struct is_tuple : std::false_type
{
};
/// @overload
template <typename... T>
struct is_tuple<mfem::tuple<T...>> : std::true_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename T>
struct is_tuple_of_tuples : std::false_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename... T>
struct is_tuple_of_tuples<mfem::tuple<T...>>
{
static constexpr bool value = (is_tuple<T>::value &&
...); ///< true/false result of type check
};
} // namespace mfem
-123
View File
@@ -1,123 +0,0 @@
#include "dfem/dfem_refactor.hpp"
#include "fem/bilininteg.hpp"
#include "fem/coefficient.hpp"
#include "linalg/auxiliary.hpp"
#include "linalg/hypre.hpp"
using namespace mfem;
using mfem::internal::tensor;
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-square.mesh";
int polynomial_order = 1;
int ir_order = 2;
int refinements = 1;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
L2_FECollection fec(polynomial_order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace fes(&mesh, &fec);
const IntegrationRule &ir = IntRules.Get(fes.GetFE(0)->GetGeomType(),
ir_order * fec.GetOrder());
const IntegrationRule &ir_face = IntRules.Get(
fes.GetTraceElement(0, fes.GetMesh()->GetFaceGeometry(0))->GetGeomType(),
ir_order * fec.GetOrder());
ParGridFunction u(&fes);
// // -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
// auto advection_kernel = [](const tensor<double, 2> &dudxi,
// const tensor<double, 2, 2> &J,
// const double &w)
// {
// constexpr tensor<double, 2> b{1.0, 1.0};
// return std::tuple{dot(b, dudxi * inv(J)) * det(J) * w};
// };
// std::tuple argument_operators_0{Gradient{"quantity"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
// std::tuple output_operator_0{Value{"quantity"}};
// ElementOperator op_0{advection_kernel, argument_operators_0, output_operator_0};
// std::array solutions{FieldDescriptor{&fes, "quantity"}};
// std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
// DifferentiableOperator advection_op{solutions, parameters, std::tuple{op_0}, mesh, ir};
// auto adv_du = advection_op.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
// HypreParMatrix A;
// adv_du->Assemble(A);
// std::ofstream mmatofs("dfem_mat.dat");
// A.PrintMatlab(mmatofs);
// mmatofs.close();
auto trace_kernel = [](const double &uL, const double &uR, const double &J,
const double &w)
{
return std::tuple{1.0 / J * w};
};
std::tuple argument_operators_0
{
FaceValueLeft{"quantity"},
FaceValueRight{"quantity"},
Gradient{"coordinates"},
Weight{"integration_weights"}
};
std::tuple output_operator_0{Value{"quantity"}};
FaceElementOperator op_0{trace_kernel, argument_operators_0, output_operator_0};
std::array solutions{FieldDescriptor{&fes, "quantity"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator trace_op{solutions, parameters, std::tuple{op_0}, mesh, ir_face};
auto vector_func = [](const Vector &, Vector &u)
{
u = 1.0;
};
VectorFunctionCoefficient vel_coeff(dim, vector_func);
ParBilinearForm adv_form(&fes);
constexpr double alpha = 1.0;
auto integ = new ConvectionIntegrator(vel_coeff, alpha);
integ->SetIntRule(&ir);
adv_form.AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(vel_coeff, alpha));
// adv_form.AddDomainIntegrator(integ);
adv_form.Assemble();
adv_form.Finalize();
auto K = adv_form.ParallelAssemble();
std::ofstream kmatofs("mfem_mat.dat");
K->PrintMatlab(kmatofs);
kmatofs.close();
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" << mesh << u << std::flush;
return 0;
}
-150
View File
@@ -1,150 +0,0 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 2;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x + y;
u(1) = x + 0.5*y*y;
};
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
auto elasticity_kernel = [](tensor<double, 2, 2> &dudxi,
tensor<double, 2, 2> &J,
double &w)
{
using mfem::internal::tensor;
using mfem::internal::IsotropicIdentity;
double lambda, mu;
{
lambda = 1.0;
mu = 1.0;
}
static constexpr auto I = IsotropicIdentity<2>();
auto eps = sym(dudxi * inv(J));
auto JxW = transpose(inv(J)) * det(J) * w;
auto r = (lambda * tr(eps) * I + 2.0 * mu * eps) * JxW;
return r;
};
tensor<double, 2, 2> dudxi, s_dudxi, J;
double w = 1.0;
enzyme::get<0>
(enzyme::autodiff<enzyme::Forward,
enzyme::DuplicatedNoNeed<tensor<double, 2, 2>>>
(+elasticity_kernel,
enzyme::Duplicated<tensor<double, 2, 2> *>(&dudxi, &s_dudxi),
enzyme::Const<tensor<double, 2, 2>*>(&J),
enzyme::Const<double*>(&w)));
// std::tuple input_descriptors = {Gradient{"displacement"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
// std::tuple output_descriptors = {Gradient{"displacement"}};
// ElementOperator qf {elasticity_kernel, input_descriptors, output_descriptors};
// ElementOperator forcing_qf
// {
// [](tensor<double, 2> x, tensor<double, 2, 2> J, double w)
// {
// double lambda, mu;
// {
// lambda = 1.0;
// mu = 1.0;
// }
// auto f = x;
// f(0) = 4.0*mu + 2.0*lambda;
// f(1) = 2.0*mu + lambda;
// return f * det(J) * w;
// },
// // inputs
// std::tuple{
// Value{"coordinates"},
// Gradient{"coordinates"},
// Weight{"integration_weight"}},
// // outputs
// std::tuple{
// Value{"displacement"}}
// };
// std::vector<Field> solutions{{&u, "displacement"}};
// std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
// std::vector<Field> dependent_fields{{&u, "displacement"}};
// DifferentiableForm dop(solutions, parameters, dependent_fields, mesh);
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
// dop.SetEssentialTrueDofs(ess_tdof_list);
// GMRESSolver gmres(MPI_COMM_WORLD);
// gmres.SetRelTol(1e-12);
// gmres.SetMaxIter(5000);
// gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
// NewtonSolver newton(MPI_COMM_WORLD);
// newton.SetSolver(gmres);
// newton.SetOperator(dop);
// newton.SetRelTol(1e-12);
// newton.SetMaxIter(100);
// newton.SetPrintLevel(1);
// u = 1e-6;
// u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
// Vector x;
// u.GetTrueDofs(x);
// Vector zero;
// newton.Mult(zero, x);
// u.Distribute(x);
// std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
return 0;
}
File diff suppressed because it is too large Load Diff
-115
View File
@@ -1,115 +0,0 @@
#include <tuple>
#include <type_traits>
#include <iostream>
#include <enzyme/enzyme>
template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
constexpr auto prefix = std::string_view {"[T = "};
constexpr auto suffix = "]";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
constexpr auto prefix = std::string_view {"with T = "};
constexpr auto suffix = "; ";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
constexpr auto prefix = std::string_view {"get_type_name<"};
constexpr auto suffix = ">(void)";
constexpr auto function = std::string_view{__FUNCSIG__};
#else
#error Unsupported compiler
#endif
const auto start = function.find(prefix) + prefix.size();
const auto end = function.find(suffix);
const auto size = end - start;
return function.substr(start, size);
}
template <typename ... Ts>
constexpr auto decay_types(std::tuple<Ts...> const &)
-> std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
template <typename T>
using decay_tuple = decltype(decay_types(std::declval<T>()));
template <class F> struct FunctionSignature;
template <typename output_t, typename... input_ts>
struct FunctionSignature<output_t(input_ts...)>
{
using return_t = output_t;
using parameter_ts = std::tuple<input_ts...>;
};
template <class T> struct create_function_signature;
template <typename output_t, typename T, typename... input_ts>
struct create_function_signature<output_t (T::*)(input_ts...) const>
{
using type = FunctionSignature<output_t(input_ts...)>;
};
template <typename arg_ts, std::size_t... Is>
auto create_enzyme_args(arg_ts &args,
arg_ts &shadow_args,
std::index_sequence<Is...>)
{
((std::cout << std::get<Is>(shadow_args) << "\n"), ...);
return std::tuple<enzyme::Duplicated<decltype(std::get<Is>(args))>...>
{
{ std::get<Is>(args), std::get<Is>(shadow_args) }...
};
}
template <typename kernel_t, typename arg_ts>
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args, arg_ts &&shadow_args)
{
auto arg_indices =
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<arg_ts>>> {};
auto enzyme_args = create_enzyme_args(args, shadow_args, arg_indices);
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
std::cout << "args is " << get_type_name<decltype(args)>() << "\n\n";
std::cout << "enzyme_args type is " << get_type_name<decltype(enzyme_args)>() <<
"\n\n";
std::cout << "return type is " << get_type_name<decltype(kf_return_t{})>() <<
"\n\n";
return std::apply([&](auto &&...args)
{
return enzyme::get<0>(
enzyme::autodiff<enzyme::Forward>
(+kernel, args...));
},
enzyme_args);
}
int main()
{
auto func = [](const double &x)
{
return x*x;
};
using kf_param_ts = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::parameter_ts;
using kf_output_t = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::return_t;
auto kernel_args = decay_tuple<kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<kf_param_ts> {};
std::get<0>(kernel_args) = 3;
std::get<0>(kernel_shadow_args) = 1;
const auto res = fwddiff_apply_enzyme(func, kernel_args, kernel_shadow_args);
std::cout << res << " == 6\n";
return 0;
}
-114
View File
@@ -1,114 +0,0 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 2;
// test_partial_assembly_setup_qf(mesh, 1, polynomial_order);
// exit(0);
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
ParGridFunction g(&h1fes);
ParGridFunction rho(&h1fes);
auto exact_solution = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x + y;
u(1) = x + 0.5*y*y;
};
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
auto objective = [](tensor<double, 2> u, double rho,
tensor<double, 2, 2> J,
double w)
{
return sqnorm(u) * det(J) * w;
};
std::tuple inputs{Value{"displacement"}, Value{"density"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
std::tuple outputs{ One{"integral"} };
ElementOperator objective_eop { objective, inputs, outputs };
std::vector<Field> solution_fields{{&u, "displacement"}};
std::vector<Field> parameter_fields{{mesh.GetNodes(), "coordinates"}, {&rho, "density"}};
std::vector<Field> dependent_variables{{&u, "displacement"}};
DifferentiableForm dop(solution_fields, parameter_fields, dependent_variables,
mesh);
dop.AddElementOperator(objective_eop, ir);
u.ProjectCoefficient(exact_solution_coeff);
Vector zero;
Vector y(1);
Vector utdof;
u.GetTrueDofs(utdof);
dop.Mult(utdof, y);
// finite difference test
Vector dgdu(u.Size());
Vector fx(y);
out << "g: ";
print_vector(fx);
out << "\n";
for (int i = 0; i < u.Size(); i++)
{
double h = 1e-6;
u(i) += h;
dop.Mult(u, y);
u(i) -= h;
y -= fx;
y /= h;
dgdu(i) = y(0);
}
out << "dgdu: ";
print_vector(dgdu);
// Vector dgdu = dop.GetGradientWrt({&u, "displacement"});
return 0;
}
-138
View File
@@ -1,138 +0,0 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 1;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
// PRESENT
return pow(x,2) + 0.5*x*pow(y,2);
};
FunctionCoefficient exact_solution_coeff(exact_solution);
auto plaplacian = [](double u,
tensor<double, 2> dudxi,
tensor<double, 2, 2> J,
double w)
{
using mfem::internal::tensor;
auto dudx = dudxi * inv(J);
auto JxW = transpose(inv(J)) * det(J) * w;
// PRESENT: Implement (1+u^2) * ∇u
return (1.0 + u*u) * dudx * JxW;
};
// PRESENT: Implement descriptors
std::tuple input_descriptors = {Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
// PRESENT: Implement descriptors
std::tuple output_descriptors = {Gradient{"potential"}};
ElementOperator qf {plaplacian, input_descriptors, output_descriptors};
ElementOperator forcing_qf
{
[](tensor<double, 2> coords, tensor<double, 2, 2> J, double w)
{
int p = 2;
double x = coords(0);
double y = coords(1);
// *INDENT-OFF*
double mathematica_please_help_me = 2.*pow(x,2)*pow(y,2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*pow(2*x + 0.5*pow(y,2),2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2)) + 1.*x*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2));
return mathematica_please_help_me * det(J) * w;
// *INDENT-ON*
},
// inputs
std::tuple{
Value{"coordinates"},
Gradient{"coordinates"},
Weight{"integration_weight"}},
// outputs
std::tuple{
Value{"potential"}}
};
std::tuple list_of_qfs{qf_1, qf_2, qf_n};
std::vector<Field> solutions{{&u, "potential"}};
std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
DifferentiableForm dop(solutions, parameters, mesh);
dop.SetEssentialTrueDofs(ess_tdof_list);
auto R = dop.GetResidual(list_of_qfs, ir);
auto Jacobian_aka_dRdu = dop.GetDerivative<0>(list_of_qfs, ir);
// R(u) = (\grad u, \grad v) + (f, v)
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(5000);
gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetSolver(gmres);
newton.SetOperator(dop);
newton.SetRelTol(1e-12);
newton.SetMaxIter(100);
newton.SetPrintLevel(1);
u = 1e-6;
u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
Vector x;
u.GetTrueDofs(x);
Vector zero;
newton.Mult(zero, x);
u.Distribute(x);
std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
return 0;
}
-192
View File
@@ -1,192 +0,0 @@
#include "dfem/dfem_refactor.hpp"
#include "linalg/hypre.hpp"
using namespace mfem;
using mfem::internal::tensor;
template <typename diffusion_t, typename force_t>
class DiffusionOperator : public Operator
{
template <typename diffusion_du_t>
class DiffusionJacobianOperator : public Operator
{
public:
DiffusionJacobianOperator(const DiffusionOperator *diffusion,
std::shared_ptr<diffusion_du_t> diff_du) :
Operator(diffusion->Height()), s(diffusion)
{
diff_du->Assemble(A);
A.EliminateBC(s->ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
}
void Mult(const Vector &x, Vector &y) const override
{
A.Mult(x, y);
}
const DiffusionOperator *s;
HypreParMatrix A;
};
public:
DiffusionOperator(diffusion_t &diffusion, force_t &force,
Array<int> &ess_tdofs) :
Operator(diffusion.Height()), diffusion(diffusion),
force(force), ess_tdofs(ess_tdofs), f(force.Height()) {}
void SetParameters(ParGridFunction &mesh_nodes)
{
diffusion.SetParameters({&mesh_nodes});
force.SetParameters({&mesh_nodes});
Vector zero;
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
this->mesh_nodes = mesh_nodes;
}
void Mult(const Vector &x, Vector &r) const override
{
diffusion.Mult(x, r);
force.Mult(x, f);
r -= f;
r.SetSubVector(ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&diffusion.solutions[0].data)));
u.SetFromTrueDofs(x);
auto dfdu = diffusion.template GetDerivativeWrt<0>({&u}, {&mesh_nodes});
dfdu->Assemble(A);
A.EliminateBC(ess_tdofs, DiagonalPolicy::DIAG_ONE);
return A;
// delete jacobian_operator;
// jacobian_operator = new
// DiffusionJacobianOperator<typename std::remove_pointer<decltype(dfdu.get())>::type>
// (this, dfdu);
// return *jacobian_operator;
}
diffusion_t &diffusion;
force_t &force;
const Array<int> ess_tdofs;
mutable Vector f;
mutable ParGridFunction mesh_nodes;
mutable Operator *jacobian_operator = nullptr;
mutable HypreParMatrix A;
};
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-square.mesh";
int polynomial_order = 2;
int ir_order = 2;
int refinements = 4;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection potential_fec(polynomial_order, dim);
ParFiniteElementSpace potential_fes(&mesh, &potential_fec);
const IntegrationRule &potential_ir =
IntRules.Get(potential_fes.GetFE(0)->GetGeomType(),
ir_order * potential_fec.GetOrder());
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
bdr_attr_is_ess = 1;
Array<int> ess_tdofs;
potential_fes.GetEssentialTrueDofs(bdr_attr_is_ess, ess_tdofs);
ParGridFunction u(&potential_fes);
u = 0.0;
auto diffusion_kernel = [](const internal::dual<double, double> &u,
const tensor<internal::dual<double, double>, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
auto invJ = inv(J);
auto dudx = dudxi * invJ;
return std::tuple{(1.0 + u * u) * dudx * det(J) * w * transpose(invJ)};
};
std::tuple argument_operators_0{Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator_0{Gradient{"potential"}};
ElementOperator op_0{diffusion_kernel, argument_operators_0, output_operator_0};
auto force_kernel = [](const tensor<double, 2, 2> &J,
const double &w)
{
return std::tuple{1.0 * det(J) * w};
};
std::tuple argument_operators_1{Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator_1{Value{"potential"}};
ElementOperator op_1{force_kernel, argument_operators_1, output_operator_1};
std::array solutions{FieldDescriptor{&potential_fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator diffusion_op{solutions, parameters, std::tuple{op_0}, mesh, potential_ir};
DifferentiableOperator force_op{solutions, parameters, std::tuple{op_1}, mesh, potential_ir};
DiffusionOperator diffusion(diffusion_op, force_op, ess_tdofs);
diffusion.SetParameters({*mesh_nodes});
HypreBoomerAMG amg;
amg.SetPrintLevel(0);
CGSolver solver(MPI_COMM_WORLD);
solver.SetAbsTol(1e-12);
solver.SetRelTol(1e-12);
solver.SetMaxIter(500);
solver.SetPrintLevel(2);
solver.SetPreconditioner(amg);
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetOperator(diffusion);
newton.SetSolver(solver);
newton.SetRelTol(1e-8);
newton.SetMaxIter(10);
newton.SetPrintLevel(1);
Vector zero;
Vector x(potential_fes.GetTrueVSize());
u.ParallelProject(x);
newton.Mult(zero, x);
u.SetFromTrueDofs(x);
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" << mesh << u << std::flush;
return 0;
}
-102
View File
@@ -1,102 +0,0 @@
#include "mfem.hpp"
#include "dfem/dfem_refactor.hpp"
using namespace mfem;
auto main(int argc, char *argv[]) -> int
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
constexpr int vdim = 1;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + y;
};
FunctionCoefficient exact_solution_coeff(exact_solution);
u.ProjectCoefficient(exact_solution_coeff);
auto domain_qf = [](const double &u,
const tensor<double, 2, 2> &J,
const double &w)
{
out << u << "\n" << J << "\n" << w << "\n\n";
return std::tuple{u * det(J) * w};
};
std::tuple input_descriptors = {Value{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_descriptors = {Value{"potential"}};
ElementOperator eop{domain_qf, input_descriptors, output_descriptors};
auto ops = std::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, ops, mesh, ir};
Vector x(h1fes.GetTrueVSize()), y(h1fes.GetTrueVSize());
u.GetTrueDofs(x);
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
// Derivative wrt "potential", indicated by the index 0 of the set {solutions} \cup {parameters}
auto dFd0 = dop.GetDerivativeWrt<0>({&u}, {mesh_nodes});
dFd0->Mult(x, y);
Vector dFd0_vec;
dFd0->Assemble(dFd0_vec);
// Derivative wrt "coordinates", indicated by the index 1 of the set {solutions} \cup {parameters}
auto dFd1 = dop.GetDerivativeWrt<1>({&u}, {mesh_nodes});
dFd1->Mult(x, y);
return 0;
}
-302
View File
@@ -1,302 +0,0 @@
#include "dfem/dfem.hpp"
using namespace mfem;
using mfem::internal::tensor;
template <typename momentum_t, typename mass_conservation_t>
class NavierStokesOperator : public Operator
{
template <typename momentum_du_t, typename momentum_dp_t>
class NavierStokesJacobianOperator : public Operator
{
public:
NavierStokesJacobianOperator(const NavierStokesOperator *ns,
std::shared_ptr<momentum_du_t> mom_du,
std::shared_ptr<momentum_dp_t> mom_dp) :
Operator(ns->Height()), ns(ns), block_op(ns->block_offsets)
{
mom_du->Assemble(A);
A.EliminateBC(ns->vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
mom_dp->Assemble(D);
D.EliminateRows(ns->vel_ess_tdofs);
Dt = new TransposeOperator(D);
block_op.SetBlock(0, 0, &A);
block_op.SetBlock(0, 1, &D);
block_op.SetBlock(1, 0, Dt);
// std::ofstream amatofs("dfem_mat.dat");
// block_op.PrintMatlab(amatofs);
// amatofs.close();
}
void Mult(const Vector &x, Vector &y) const override
{
block_op.Mult(x, y);
}
~NavierStokesJacobianOperator()
{
delete Dt;
}
const NavierStokesOperator *ns = nullptr;
HypreParMatrix A, D;
TransposeOperator *Dt = nullptr;
BlockOperator block_op;
};
public:
NavierStokesOperator(momentum_t &momentum,
mass_conservation_t &mass_conservation,
Array<int> &offsets, Array<int> &vel_ess_tdofs) :
Operator(offsets.Last()), momentum(momentum),
mass_conservation(mass_conservation),
block_offsets(offsets), vel_ess_tdofs(vel_ess_tdofs) {}
void SetParameters(ParGridFunction &mesh_nodes)
{
momentum.SetParameters({&mesh_nodes});
mass_conservation.SetParameters({&mesh_nodes});
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
this->mesh_nodes = mesh_nodes;
}
void Mult(const Vector &x, Vector &r) const override
{
Vector ru(r.ReadWrite() + block_offsets[0],
block_offsets[1] - block_offsets[0]);
Vector rp(r.ReadWrite() + block_offsets[1],
block_offsets[2] - block_offsets[1]);
momentum.Mult(x, ru);
mass_conservation.Mult(x, rp);
ru.SetSubVector(vel_ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
xtmp = x;
BlockVector xb(xtmp.ReadWrite(), block_offsets);
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&momentum.solutions[0].data)));
ParGridFunction p(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&momentum.solutions[1].data)));
u.SetFromTrueDofs(xb.GetBlock(0));
p.SetFromTrueDofs(xb.GetBlock(1));
auto mom_du = momentum.template GetDerivativeWrt<0>({&u, &p}, {&mesh_nodes});
auto mom_dp = momentum.template GetDerivativeWrt<1>({&u, &p}, {&mesh_nodes});
delete jacobian_operator;
jacobian_operator = new NavierStokesJacobianOperator<
typename std::remove_pointer<decltype(mom_du.get())>::type,
typename std::remove_pointer<decltype(mom_dp.get())>::type>(this, mom_du,
mom_dp);
return *jacobian_operator;
}
momentum_t &momentum;
mass_conservation_t &mass_conservation;
const Array<int> block_offsets;
const Array<int> vel_ess_tdofs;
mutable Vector xtmp;
mutable ParGridFunction mesh_nodes;
mutable Operator *jacobian_operator = nullptr;
};
double reynolds = 10.0;
int main(int argc, char *argv[])
{
constexpr int dim = 3;
constexpr int vdim = dim;
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-cube.mesh";
int polynomial_order = 2;
int ir_order = 2;
int refinements = 2;
OptionsParser args(argc, argv);
args.AddOption(&refinements, "-r", "--refinements", "");
args.AddOption(&reynolds, "-rey", "--reynolds", "");
args.ParseCheck();
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection velocity_fec(polynomial_order, dim);
ParFiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim);
H1_FECollection pressure_fec(polynomial_order - 1, dim);
ParFiniteElementSpace pressure_fes(&mesh, &pressure_fec);
const IntegrationRule &velocity_ir =
IntRules.Get(velocity_fes.GetFE(0)->GetGeomType(),
ir_order * velocity_fec.GetOrder());
const IntegrationRule &pressure_ir =
IntRules.Get(pressure_fes.GetFE(0)->GetGeomType(),
ir_order * pressure_fec.GetOrder());
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
bdr_attr_is_ess = 1;
Array<int> vel_ess_tdofs;
velocity_fes.GetEssentialTrueDofs(bdr_attr_is_ess, vel_ess_tdofs);
ParGridFunction u(&velocity_fes);
ParGridFunction p(&pressure_fes);
auto u_f = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double z = coords(2);
if (z >= 1.0)
{
u(0) = 1.0;
}
else
{
u(0) = 0.0;
}
u(1) = 0.0;
u(2) = 0.0;
};
auto u_coef = VectorFunctionCoefficient(dim, u_f);
u.ProjectCoefficient(u_coef);
p = 0.0;
// -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
auto momentum_kernel = [](const tensor<double, dim> &u,
const tensor<double, dim, dim> &dudxi,
const double &p,
const tensor<double, dim, dim> &J,
const double &w)
{
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
auto invJ = inv(J);
auto dudx = dudxi * invJ;
double Re = reynolds;
return mfem::tuple{(outer(u, u) - 1.0 / Re * dudx + p * I) * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators_0{Value{"velocity"}, Gradient{"velocity"}, Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator_0{Gradient{"velocity"}};
ElementOperator op_0{momentum_kernel, argument_operators_0, output_operator_0};
// (\nabla \cdot u, q)
auto mass_conservation_kernel = [](const tensor<double, dim, dim> &dudxi,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{tr(dudxi * inv(J)) * det(J) * w};
};
mfem::tuple argument_operators_1{Gradient{"velocity"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator_1{Value{"pressure"}};
ElementOperator op_1{mass_conservation_kernel, argument_operators_1, output_operator_1};
std::array solutions{FieldDescriptor{&velocity_fes, "velocity"}, FieldDescriptor{&pressure_fes, "pressure"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator momentum_op{solutions, parameters, mfem::tuple{op_0}, mesh, velocity_ir};
DifferentiableOperator mass_conservation_op{solutions, parameters, mfem::tuple{op_1}, mesh, pressure_ir};
// Preconditioner form
auto pressure_mass_kernel = [](const double &p,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{p * det(J) * w};
};
mfem::tuple pms_args{Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple pms_outs{Value{"pressure"}};
ElementOperator pressure_mass{pressure_mass_kernel, pms_args, pms_outs};
std::array pms_sols{FieldDescriptor{&pressure_fes, "pressure"}};
std::array pms_params{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator pressure_mass_op{pms_sols, pms_params, mfem::tuple{pressure_mass}, mesh, pressure_ir};
Array<int> block_offsets(3);
block_offsets[0] = 0;
block_offsets[1] = velocity_fes.GetTrueVSize();
block_offsets[2] = pressure_fes.GetTrueVSize();
block_offsets.PartialSum();
NavierStokesOperator navierstokes(momentum_op, mass_conservation_op,
block_offsets,
vel_ess_tdofs);
BlockVector x(block_offsets), y(block_offsets);
u.ParallelProject(x.GetBlock(0));
// p.ParallelProject(x.GetBlock(1));
navierstokes.SetParameters(*mesh_nodes);
HypreParMatrix A;
momentum_op.template GetDerivativeWrt<0>({&u, &p}, {mesh_nodes})->Assemble(A);
A.EliminateBC(vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
HypreBoomerAMG amg(A);
amg.SetMaxLevels(50);
amg.SetPrintLevel(0);
HypreParMatrix Mp;
pressure_mass_op.template GetDerivativeWrt<0>({&p}, {mesh_nodes})->Assemble(Mp);
HypreDiagScale Mp_inv(Mp);
BlockDiagonalPreconditioner prec(block_offsets);
prec.SetDiagonalBlock(0, &amg);
prec.SetDiagonalBlock(1, &Mp_inv);
GMRESSolver solver(MPI_COMM_WORLD);
solver.SetAbsTol(0.0);
solver.SetRelTol(1e-8);
solver.SetKDim(100);
solver.SetMaxIter(500);
solver.SetPrintLevel(2);
solver.SetPreconditioner(prec);
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetOperator(navierstokes);
newton.SetSolver(solver);
newton.SetRelTol(1e-8);
newton.SetMaxIter(50);
newton.SetPrintLevel(1);
Vector zero;
newton.Mult(zero, x);
u.SetFromTrueDofs(x.GetBlock(0));
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" << mesh << u << std::flush;
return 0;
}
-174
View File
@@ -1,174 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_diffusion(
std::string mesh_file, int refinements, int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == 2, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
const double& w, const tensor<double, 2>& dudxi)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators =
{
Gradient{"coordinates"}, Weight{}, Gradient{"potential"}
};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + 0.25 * x * x * y + y * y * x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
ParBilinearForm a(&h1fes);
a.AddDomainIntegrator(new DiffusionIntegrator);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(y2);
print_vector(y);
return 1;
}
// // Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion);
-296
View File
@@ -1,296 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_parametricspace.hpp"
#include "fem/bilininteg.hpp"
#include "general/tic_toc.hpp"
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
int test_diffusion_3d(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int num_samples = 10;
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
dim * dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(qdata_space);
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.GetTrueVSize());
{
auto diffusion_mf_kernel =
[] MFEM_HOST_DEVICE (
const tensor<dual<real_t, real_t>, dim>& dudxi,
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {diffusion_mf_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({mesh_nodes});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
{
auto diffusion_setup_kernel =
[] MFEM_HOST_DEVICE (
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {None{"qdata"}};
ElementOperator eop = {diffusion_setup_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"},
FieldDescriptor{&qdata_space, "qdata"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({mesh_nodes, &qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, qdata);
}
sw.Stop();
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
qdata.HostRead();
}
// printf("qdata: ");
// print_vector(qdata);
{
auto diffusion_apply_kernel =
[] MFEM_HOST_DEVICE (
const tensor<dual<real_t, real_t>, dim>& dudxi,
const tensor<double, dim, dim>& qdata)
{
return mfem::tuple{dudxi * qdata};
};
mfem::tuple argument_operators = {Gradient{"potential"}, None{"qdata"}};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {diffusion_apply_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&qdata_space, "qdata"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({&qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
// printf("y: ");
// print_vector(y);
Vector y2(h1fes.TrueVSize());
{
ParBilinearForm a(&h1fes);
auto diff_integ = new DiffusionIntegrator;
diff_integ->SetIntRule(&ir);
a.AddDomainIntegrator(diff_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
OperatorPtr A;
StopWatch sw;
sw.Start();
a.Assemble();
a.Finalize();
Array<int> empty;
a.FormSystemMatrix(empty, A);
sw.Stop();
printf("mfem pa setup: %fs\n", sw.RealTime());
sw.Clear();
sw.Start();
for (int i = 0; i < num_samples; i++)
{
A->Mult(x, y2);
}
sw.Stop();
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
y2.HostRead();
}
// printf("y2: ");
// print_vector(y2);
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
// printf("y ");
// print_vector(y);
// printf("y2: ");
// print_vector(y2);
// printf("diff: ");
// print_vector(diff);
return 1;
}
// Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion_3d);
@@ -1,309 +0,0 @@
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_fieldoperator.hpp"
#include "examples/dfem/dfem_refactor.hpp"
#include "fem/bilininteg.hpp"
#include "general/tic_toc.hpp"
#include <utility>
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
int test_diffusion_3d(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int num_samples = 100;
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
dim * dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(qdata_space);
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.GetTrueVSize());
{
std::shared_ptr<DerivativeOperator> dpotential;
{
auto diffusion_mf_kernel =
[] MFEM_HOST_DEVICE (
const tensor<real_t, dim>& dudxi,
const tensor<real_t, dim, dim>& J,
const real_t& w)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
constexpr int Potential = 0;
constexpr int Coordinates = 1;
auto input_operators = mfem::tuple{Gradient<Potential>{}, Gradient<Coordinates>{}, Weight{}};
auto output_operator = mfem::tuple{Gradient<Potential>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes}};
DifferentiableOperator dop(solutions, parameters, mesh);
auto derivatives = std::integer_sequence<size_t, Potential> {};
dop.AddDomainIntegrator(
diffusion_mf_kernel, input_operators, output_operator, ir, derivatives);
dop.SetParameters({mesh_nodes});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
dpotential = dop.GetDerivative(Potential, {&f1_g}, {mesh_nodes});
}
dpotential->Mult(x, y);
}
{
auto diffusion_setup_kernel =
[] MFEM_HOST_DEVICE (
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
};
constexpr int Potential = 0;
constexpr int Coordinates = 1;
constexpr int QData = 2;
auto input_operators = mfem::tuple{Gradient<Coordinates>{}, Weight{}};
auto output_operator = mfem::tuple{None<QData>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes},
FieldDescriptor{QData, &qdata_space}};
DifferentiableOperator dop(solutions, parameters, mesh);
dop.AddDomainIntegrator(
diffusion_setup_kernel, input_operators, output_operator, ir);
dop.SetParameters({mesh_nodes, &qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, qdata);
}
sw.Stop();
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
qdata.HostRead();
}
// printf("qdata: ");
// print_vector(qdata);
{
auto diffusion_apply_kernel =
[] MFEM_HOST_DEVICE (
const tensor<real_t, dim>& dudxi,
const tensor<double, dim, dim>& qdata)
{
return mfem::tuple{dudxi * qdata};
};
constexpr int Potential = 0;
constexpr int QData = 1;
auto input_operators = mfem::tuple{Gradient<Potential>{}, None<QData>{}};
auto output_operator = mfem::tuple{Gradient<Potential>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{QData, &qdata_space}};
DifferentiableOperator dop(solutions, parameters, mesh);
dop.AddDomainIntegrator(
diffusion_apply_kernel, input_operators, output_operator, ir);
dop.SetParameters({&qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
// printf("y: ");
// print_vector(y);
Vector y2(h1fes.TrueVSize());
{
ParBilinearForm a(&h1fes);
auto diff_integ = new DiffusionIntegrator;
diff_integ->SetIntRule(&ir);
a.AddDomainIntegrator(diff_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
OperatorPtr A;
StopWatch sw;
sw.Start();
a.Assemble();
a.Finalize();
Array<int> empty;
a.FormSystemMatrix(empty, A);
sw.Stop();
printf("mfem pa setup: %fs\n", sw.RealTime());
sw.Clear();
sw.Start();
y2 = 0.0;
for (int i = 0; i < num_samples; i++)
{
A->Mult(x, y2);
}
sw.Stop();
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
y2.HostRead();
}
// printf("y2: ");
// print_vector(y2);
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y: ");
print_vector(y);
printf("y2: ");
print_vector(y2);
printf("diff: ");
print_vector(diff);
return 1;
}
// Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion_3d);
-109
View File
@@ -1,109 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_elasticity(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
const int vdim = dim;
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_bdr(mesh.bdr_attributes.Max());
Array<int> ess_tdof;
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 6 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
out << "#dof_el: " << h1fes.GetRestrictionMatrix()->Height() / mesh.GetNE() <<
"\n";
ParGridFunction u(&h1fes);
auto f1 = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
};
VectorFunctionCoefficient u_c(dim, f1);
u.ProjectCoefficient(u_c);
ConstantCoefficient l_coeff(0.5), m_coeff(0.25);
ParBilinearForm A_form(&h1fes);
auto A_integ = new ElasticityIntegrator(l_coeff, m_coeff);
A_integ->SetIntegrationRule(ir);
A_form.AddDomainIntegrator(A_integ);
A_form.Assemble();
A_form.Finalize();
auto elasticity_kernel = [](const tensor<double, 2, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
constexpr double lambda = 0.5;
constexpr double mu = 0.25;
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
auto invJ = inv(J);
auto eps = sym(dudxi * invJ);
return mfem::tuple{transpose(lambda * tr(eps) * I + 2.0 * mu * eps) * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"displacement"}};
ElementOperator op{elasticity_kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector x(u), y1(h1fes.GetTrueVSize()),
y2(h1fes.GetTrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y1);
y1.HostRead();
A_form.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y1;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y1);
print_vector(y2);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_elasticity);
@@ -1,115 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_parametricspace.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_gradient_linear_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
// const IntegrationRule &ir =
// IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
2 * polynomial_order - 1);
ParGridFunction f1_g(&h1fes);
ParametricSpace pspace(dim, dim, ir.GetNPoints(),
dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(pspace);
auto kernel = [](const tensor<double, dim> &dudxi,
const tensor<double, dim, dim> &J)
{
return mfem::tuple{dudxi * inv(J)};
};
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}};
mfem::tuple output_operator = {None{"qdata"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"},
FieldDescriptor{&pspace, "qdata"}
};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x * y * z + y * z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize() * dim);
dop.SetParameters({mesh_nodes, &qdata});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height() * dim);
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector g(dim);
f1_g.GetGradient(*T, g);
// printf("(%f, %f, %f): (%f, %f, %f)\n", ip.x, ip.y, ip.z, g(0), g(1), g(2));
for (int d = 0; d < dim; d++)
{
int qpo = qp * dim;
int eo = e * (ir.GetNPoints() * dim);
f_test(d + qpo + eo) = g(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_gradient_linear_scalar_3d);
@@ -1,91 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_scalar(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u, const tensor<double, 2, 2> &J,
const double &w)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + y;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_scalar);
@@ -1,93 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + y + 1.25 * z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_scalar_3d);
@@ -1,100 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_vector(std::string mesh_file, int refinements,
int polynomial_order)
{
constexpr int vdim = 2;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
QuadratureSpace qspace(mesh, ir);
QuadratureFunction qf(&qspace, vdim);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const tensor<double, 2> &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop{kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + x + y;
u(1) = 12.345 + x + y;
};
VectorFunctionCoefficient f1_c(vdim, f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(f1_g.Size());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(qf.Size());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector f(vdim);
f1_g.GetVectorValue(*T, ip, f);
for (int d = 0; d < vdim; d++)
{
int qpo = qp * vdim;
int eo = e * (ir.GetNPoints() * vdim);
f_test(d + qpo + eo) = f(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_vector);
@@ -1,105 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_vector_3d(std::string mesh_file, int refinements,
int polynomial_order)
{
constexpr int dim = 3;
constexpr int vdim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
QuadratureSpace qspace(mesh, ir);
QuadratureFunction qf(&qspace, vdim);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const tensor<double, vdim> &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop{kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
u(0) = 2.345 + x + y + 3.0 * z;
u(1) = 12.345 + x + y + 2.0 * z;
u(2) = 5.345 + x + y + 1.0 * z;
};
VectorFunctionCoefficient f1_c(vdim, f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(f1_g.Size());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(qf.Size());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector f(vdim);
f1_g.GetVectorValue(*T, ip, f);
for (int d = 0; d < vdim; d++)
{
int qpo = qp * vdim;
int eo = e * (ir.GetNPoints() * vdim);
f_test(d + qpo + eo) = f(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_vector_3d);
-113
View File
@@ -1,113 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/bilininteg.hpp"
#include "fem/normal_deriv_restriction.hpp"
#include <fstream>
using namespace mfem;
using mfem::internal::tensor;
int dfem_test_mass_scalar_2d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 2;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
// 2 * polynomial_order - 1);
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double& u,
const tensor<double, dim> x,
const tensor<double, dim, dim> J,
const double& w)
{
out << x << ": " << u << "\n";
return mfem::tuple{u * w * det(J)};
};
mfem::tuple argument_operators = {Value{"potential"}, Value{"coordinates"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Value{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + x*y + 1.25 * x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector f1_g_e(f1_g.Size());
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
// R->Mult(f1_g, f1_g_e);
auto r_out = std::ofstream("r_mat.mtx");
R->PrintMatlab(r_out);
r_out.close();
print_vector(f1_g);
// print_vector(f1_g_e);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
ParBilinearForm a(&h1fes);
auto mass_integ = new MassIntegrator;
mass_integ->SetIntRule(&ir);
a.AddDomainIntegrator(mass_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(y2);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(dfem_test_mass_scalar_2d);
-147
View File
@@ -1,147 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/bilininteg.hpp"
#include "fem/fe/fe_base.hpp"
using namespace mfem;
using mfem::internal::tensor;
int dfem_test_mass_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
// 2 * polynomial_order - 1);
auto dtq = h1fes.GetFE(0)->GetDofToQuad(ir, DofToQuad::TENSOR);
// printf("\n B: ");
// dtq.B.Print(out, dtq.B.Size());
// printf("\n G: ");
// dtq.G.Print(out, dtq.G.Size());
// printf("\n w: ");
// ir.GetWeights().Print(out, ir.GetWeights().Size());
// printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
// printf("#nqp = %d\n", ir.GetNPoints());
// printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
// printf("nodes: ");
// print_vector(*mesh_nodes);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{u * det(J) * w};
};
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Value{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
// printf("\nf1_g: ");
// print_vector(f1_g);
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
// Vector f1_g_e(R->Height());
// R->Mult(f1_g, f1_g_e);
// printf("\nf1_g_e: ");
// print_vector(f1_g_e);
// auto r_out = std::ofstream("r_mat.mtx");
// R->PrintMatlab(r_out);
// r_out.close();
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
ParBilinearForm a(&h1fes);
auto mass_integ = new MassIntegrator;
mass_integ->SetIntRule(&ir);
a.AddDomainIntegrator(mass_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y ");
print_vector(y);
printf("y2: ");
print_vector(y2);
printf("diff: ");
print_vector(diff);
return 1;
}
Vector y3(h1fes.TrueVSize());
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
dFdu->Mult(x, y3);
diff = y2;
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y2 ");
print_vector(y2);
printf("y3: ");
print_vector(y3);
printf("diff: ");
print_vector(diff);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(dfem_test_mass_scalar_3d);
@@ -1,114 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_neo_hookean_elasticity_2d(
std::string mesh_file, int refinements, int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
MFEM_ASSERT(dim == 2, "This test is for 2D meshes only");
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, dim);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction u_g(&h1fes);
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
const double& w,
const tensor<double, 2, 2>& dudxi)
{
// Neo-Hookean parameters
const double lambda = 1.0;
const double mu = 0.5;
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
auto F = I + (dudxi * inv(J));
auto E = 0.5 * (transpose(F) * F - I);
auto invF = inv(F);
// 2D plane strain formulation
auto P = mu * (F - transpose(invF)) + lambda * log(det(F)) * transpose(invF);
return mfem::tuple{P * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{},
Gradient{"displacement"}
};
mfem::tuple output_operator = {Gradient{"displacement"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "displacement"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto displacement = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 0.1 * x * y;
u(1) = 0.1 * y * x;
};
VectorFunctionCoefficient disp_coeff(2, displacement);
u_g.ProjectCoefficient(disp_coeff);
Vector x(u_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
// Test linearization
auto dFdu = dop.GetDerivativeWrt<0>({&u_g}, {mesh_nodes});
dFdu->Mult(x, y);
// Finite difference Jacobian test
{
double eps = 1.0e-6;
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
v *= eps;
xpv += v;
xmv -= v;
dop.Mult(xpv, fxpv);
dop.Mult(xmv, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
fxpv -= y;
if (fxpv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
return 1;
}
}
return 0;
}
DFEM_TEST_MAIN(test_neo_hookean_elasticity_2d);
@@ -1,169 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_nonlinear_diffusion(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction f1_g(&h1fes);
bool inactive_derivative = false;
auto kernel = [] MFEM_HOST_DEVICE(
const tensor<double, dim, dim>& J,
const double& w,
const tensor<double, dim>& dudxi,
const double& u)
{
auto invJ = inv(J);
return mfem::tuple{(u * u) * dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators =
{
Gradient{"coordinates"},
Weight{},
Gradient{"potential"},
Value{"potential"}
};
mfem::tuple output_operator =
{
Gradient{"potential"}
};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array
{
FieldDescriptor{&h1fes, "potential"}
};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"}
};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + 0.25 * x * x * y + y * y * x + z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
ParBilinearForm a(&h1fes);
GridFunctionCoefficient f1gc(&f1_g);
TransformedCoefficient tf_c(&f1gc, [](double f) { return f * f; });
a.AddDomainIntegrator(new DiffusionIntegrator(tf_c));
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize()), diff(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
diff = y2;
diff -= y;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y);
print_vector(y2);
return 1;
}
// Test linearization here as well
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
dFdu->Mult(x, y);
// fd jacobian test
{
double eps = 1.0e-6;
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
v *= eps;
xpv += v;
xmv -= v;
dop.Mult(xpv, fxpv);
dop.Mult(xmv, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
fxpv -= y;
if (fxpv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
return 1;
}
}
// ParBilinearForm da(&h1fes);
// TransformedCoefficient dtf_c(&f1gc, [](double f) { return 2.0 * f; });
// da.AddDomainIntegrator(new DiffusionIntegrator(dtf_c));
// da.SetAssemblyLevel(AssemblyLevel::PARTIAL);
// da.Assemble();
// da.Finalize();
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// print_vector(y);
// da.Mult(x, y2);
// print_vector(y2);
// y2 -= y;
// out << "||dFdu x - A x||_l2 = " << y2.Norml2() << "\n";
// if (y2.Norml2() > 1e-10)
// {
// out << "||dFdu u^* - ex||_l2 = " << y2.Norml2() << "\n";
// }
return 0;
}
DFEM_TEST_MAIN(test_nonlinear_diffusion);
@@ -1,267 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include <fstream>
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
class FDJacobian : public Operator
{
public:
FDJacobian(const Operator &op, const Vector &x) :
Operator(op.Height()),
op(op),
x(x)
{
f.SetSize(Height());
xpev.SetSize(Height());
op.Mult(x, f);
xnorm = x.Norml2();
}
void Mult(const Vector &v, Vector &y) const override
{
x.HostRead();
// See [1] for choice of eps.
//
// [1] Woodward, C.S., Gardner, D.J. and Evans, K.J., 2015. On the use of
// finite difference matrix-vector products in Newton-Krylov solvers for
// implicit climate dynamics with spectral elements. Procedia Computer
// Science, 51, pp.2036-2045.
real_t eps = lambda * (lambda + xnorm / v.Norml2());
for (int i = 0; i < x.Size(); i++)
{
xpev(i) = x(i) + eps * v(i);
}
// y = f(x + eps * v)
op.Mult(xpev, y);
// y = (f(x + eps * v) - f(x)) / eps
for (int i = 0; i < x.Size(); i++)
{
y(i) = (y(i) - f(i)) / eps;
}
}
virtual MemoryClass GetMemoryClass() const override
{
return Device::GetDeviceMemoryClass();
}
private:
const Operator &op;
Vector x, f;
mutable Vector xpev;
real_t lambda = 1.0e-6;
real_t xnorm;
};
template <typename elasticity_t>
class ElasticityOperator : public Operator
{
template <typename elasticity_du_t>
class ElasticityJacobianOperator : public Operator
{
public:
ElasticityJacobianOperator(const ElasticityOperator *elasticity,
std::shared_ptr<elasticity_du_t> dRdu) :
Operator(elasticity->Height()),
elasticity(elasticity),
dRdu(dRdu),
x_ess(dRdu->Height())
{
}
void Mult(const Vector &x, Vector &y) const override
{
x_ess = x;
x_ess.SetSubVector(elasticity->ess_tdofs, 0.0);
dRdu->Mult(x_ess, y);
for (int i = 0; i < elasticity->ess_tdofs.Size(); i++)
{
y[elasticity->ess_tdofs[i]] = x[elasticity->ess_tdofs[i]];
}
}
const ElasticityOperator *elasticity = nullptr;
std::shared_ptr<elasticity_du_t> dRdu;
mutable Vector x_ess;
};
public:
ElasticityOperator(ParFiniteElementSpace &fes, elasticity_t &elasticity,
Array<int> &ess_tdofs) :
Operator(fes.GetTrueVSize()),
fes(fes),
elasticity(elasticity),
ess_tdofs(ess_tdofs) {}
void Mult(const Vector &x, Vector &r) const override
{
elasticity.Mult(x, r);
r.SetSubVector(ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&elasticity.solutions[0].data)));
u.SetFromTrueDofs(x);
auto dRdu = elasticity.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
jacobian.reset(
new ElasticityJacobianOperator<
typename std::remove_pointer<decltype(dRdu.get())>::type> (this, dRdu));
// jacobian.reset(new FDJacobian(*this, x));
return *jacobian;
}
void SetParameters(ParGridFunction &mesh_nodes)
{
elasticity.SetParameters({&mesh_nodes});
this->mesh_nodes = &mesh_nodes;
}
ParFiniteElementSpace &fes;
elasticity_t &elasticity;
Array<int> ess_tdofs;
mutable ParGridFunction *mesh_nodes = nullptr;
mutable std::shared_ptr<Operator> jacobian;
};
int test_nonlinear_elasticity_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
constexpr int vdim = dim;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list, ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
out << "#qp: " << ir.GetNPoints() << "\n";
out << "#dof: " << h1fes.GetNDofs() << "\n";
ParGridFunction u(&h1fes);
auto elasticity_kernel = [] MFEM_HOST_DEVICE
(const tensor<dual<real_t, real_t>, dim, dim> &dudxi,
const tensor<real_t, dim, dim> &J,
const real_t &w)
{
// shear modulus
real_t D1{0.1e6};
// bulk modulus
real_t C1{1.0e6};
constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
auto invJ = inv(J);
auto dudx = dudxi * invJ;
auto F = det(I + dudx);
auto p = -2.0 * D1 * F * (F - 1);
auto devB = dev(dudx + transpose(dudx) + dot(dudx, transpose(dudx)));
auto sigma = -(p / F) * I + 2.0 * (C1 / pow(F, 5.0 / 3.0)) * devB;
return mfem::tuple{sigma * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"displacement"}};
// B^T D(B0*dudxi, B1*J, B2*w)
ElementOperator op(elasticity_kernel, argument_operators, output_operator, ir);
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, mfem::tuple{op}, mesh,
AutoDiff::NativeDualNumber{});
ElasticityOperator elasticity(h1fes, dop, ess_tdof_list);
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
{
f.Set(i, new ConstantCoefficient(0.0));
}
{
Vector pull_force(mesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
}
ParLinearForm b(&h1fes);
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
b.UseFastAssembly(true);
b.Assemble();
auto B = b.ParallelAssemble();
Vector X = u.GetTrueVector();
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-8);
cg.SetMaxIter(1000);
cg.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetSolver(cg);
newton.SetOperator(elasticity);
newton.SetRelTol(1e-6);
newton.SetMaxIter(100);
// newton.SetAdaptiveLinRtol();
newton.SetPrintLevel(IterativeSolver::PrintLevel().Iterations());
elasticity.SetParameters(*mesh_nodes);
// Vector zero;
newton.Mult(*B, X);
u.SetFromTrueDofs(X);
ParaViewDataCollection paraview_dc("dfem", &mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(polynomial_order);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("displacement", &u);
paraview_dc.Save();
return 0;
}
DFEM_TEST_MAIN(test_nonlinear_elasticity_3d);
-82
View File
@@ -1,82 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/coefficient.hpp"
#include "fem/pgridfunc.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_ordering(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 2;
constexpr int vdim = dim;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
const IntegrationRule &ir =
IntRules.Get(mesh_fes.GetFE(0)->GetGeomType(),
2 * mesh_fes.FEColl()->GetOrder() - 1);
for (int q = 0; q < ir.GetNPoints(); q++)
{
out << "(" << ir.IntPoint(q).x << ", " << ir.IntPoint(q).y << ")\n";
}
ParGridFunction u(&mesh_fes);
auto f = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x*y + 1.0;
u(1) = y*y*x*x + 2.0;
};
VectorFunctionCoefficient uc(dim, f);
u.ProjectCoefficient(uc);
auto kernel = [](const tensor<double, dim> &xi,
const tensor<double, vdim, dim> &J,
const tensor<double, dim> &u,
const tensor<double, vdim, dim> &dudxi)
{
out << "xi: " << xi << "\n";
out << "J: " << J << "\n";
out << "u: " << u << "\n";
out << "dudxi: " << dudxi << "\n\n";
return mfem::tuple{J};
};
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"coordinates"}, Value{"potential"}, Gradient{"potential"}};
mfem::tuple output_operator{Gradient{"potential"}};
ElementOperator op{kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&mesh_fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector y(u);
dop.SetParameters({mesh_nodes});
dop.Mult(u, y);
print_vector(y);
return 0;
}
DFEM_TEST_MAIN(test_ordering);
-102
View File
@@ -1,102 +0,0 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_vector_diffusion(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
const int vdim = dim;
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_bdr(mesh.bdr_attributes.Max());
Array<int> ess_tdof;
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() - 1);
ParGridFunction u(&h1fes);
auto f1 = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
};
VectorFunctionCoefficient u_c(dim, f1);
u.ProjectCoefficient(u_c);
auto vector_diffusion_kernel = [](const tensor<double, 2> &xi,
const tensor<double, 2, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
out << "xi: " << xi << "\n";
out << "dudxi: " << dudxi << "\n";
return mfem::tuple{dudxi * inv(J) * det(J) * w * transpose(inv(J))};
// return mfem::tuple{dudxi};
};
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"potential"}};
ElementOperator op{vector_diffusion_kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&h1fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector x(u), y1(h1fes.GetTrueVSize()),
y2(h1fes.GetTrueVSize());
ParBilinearForm A_form(&h1fes);
auto A_integ = new VectorDiffusionIntegrator(vdim);
A_integ->SetIntegrationRule(ir);
A_form.AddDomainIntegrator(A_integ);
A_form.Assemble();
A_form.Finalize();
dop.SetParameters({mesh_nodes});
dop.Mult(x, y1);
y1.HostRead();
A_form.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y1;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y1);
print_vector(y2);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_vector_diffusion);

Some files were not shown because too many files have changed in this diff Show More